added recursiveness and new progbar
parent
f859235f45
commit
7973a6fc33
@ -0,0 +1,138 @@
|
|||||||
|
package chunk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.cheetah.cat/worksucc/gma-puzzles/common"
|
||||||
|
"github.com/klauspost/compress/zstd"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PoolRecoveryData struct {
|
||||||
|
PoolID string `json:"_key"`
|
||||||
|
Size uint64 `json:"size"`
|
||||||
|
Created time.Time `json:"date"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
|
||||||
|
ItemCount int `json:"itemCount"`
|
||||||
|
Items []string `json:"items"`
|
||||||
|
RecoveryData []common.DB_File `json:"recoveryData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChunkReader struct {
|
||||||
|
FileHandle *os.File
|
||||||
|
ExpectedHash *string
|
||||||
|
ExpectedSize *uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChunkReader(fileName string) (_ ChunkReader, err error) {
|
||||||
|
return ChunkReader{}.NewReader(fileName)
|
||||||
|
}
|
||||||
|
func (r ChunkReader) NewReader(fileName string) (_ ChunkReader, err error) {
|
||||||
|
r.FileHandle, err = os.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
func (r ChunkReader) NewReaderFrom(fileHandle *os.File) (_ ChunkReader, err error) {
|
||||||
|
r.FileHandle = fileHandle
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
func (r *ChunkReader) LoadRecoveryFile(fileName string) (err error) {
|
||||||
|
var poolRecoveryData PoolRecoveryData
|
||||||
|
|
||||||
|
readJSONFile, err := os.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer readJSONFile.Close()
|
||||||
|
|
||||||
|
readBytes, err := io.ReadAll(readJSONFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(readBytes, &poolRecoveryData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.ExpectedHash = &poolRecoveryData.Hash
|
||||||
|
r.ExpectedSize = &poolRecoveryData.Size
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ChunkReader) CheckIntegrity() (err error) {
|
||||||
|
// re-open and check
|
||||||
|
r.FileHandle.Seek(0, 0)
|
||||||
|
shaHasher := sha256.New()
|
||||||
|
hashedBytes, err := io.Copy(shaHasher, r.FileHandle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
readHash := fmt.Sprintf("%x", shaHasher.Sum(nil))
|
||||||
|
//fmt.Printf("PackPoolTar hash is %s\n", readHash)
|
||||||
|
if readHash != *r.ExpectedHash {
|
||||||
|
return fmt.Errorf("WORM Hash %s != Hash %s", readHash, *r.ExpectedHash)
|
||||||
|
}
|
||||||
|
packFileStats, err := r.FileHandle.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
readSize := packFileStats.Size()
|
||||||
|
if readSize != int64(*r.ExpectedSize) {
|
||||||
|
return fmt.Errorf("WORM Copy FileSize %d != FileSize %d", readSize, *r.ExpectedSize)
|
||||||
|
}
|
||||||
|
// validate written tar-chunk
|
||||||
|
_, err = r.FileHandle.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
decompressor, err := zstd.NewReader(r.FileHandle, zstd.WithDecoderConcurrency(8))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer decompressor.Close()
|
||||||
|
tarFileCheckReader := tar.NewReader(decompressor)
|
||||||
|
|
||||||
|
//filenamesReadBackList := []string{}
|
||||||
|
for {
|
||||||
|
header, err := tarFileCheckReader.Next()
|
||||||
|
//header.PAXRecords
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hasher := sha256.New()
|
||||||
|
hashedBytes, err := io.Copy(hasher, tarFileCheckReader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
readBackChecksum := fmt.Sprintf("%x", hasher.Sum(nil))
|
||||||
|
if hashedBytes != header.Size {
|
||||||
|
return fmt.Errorf("validation on output archive, incorrect size file %s has %d should be %d", header.Name, hashedBytes, header.Size)
|
||||||
|
}
|
||||||
|
if header.Name != readBackChecksum {
|
||||||
|
return fmt.Errorf("validation on output archive, incorrect checksum file %s has %s", header.Name, readBackChecksum)
|
||||||
|
}
|
||||||
|
//filenamesReadBackList = append(filenamesReadBackList, header.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hashedBytes != int64(*r.ExpectedSize) {
|
||||||
|
return fmt.Errorf("WORM Copy HashedBytes %d != FileSize %d", hashedBytes, *r.ExpectedSize)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ChunkReader) Close() {
|
||||||
|
r.FileHandle.Close()
|
||||||
|
}
|
@ -0,0 +1,228 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.cheetah.cat/worksucc/gma-puzzles/chunk"
|
||||||
|
"git.cheetah.cat/worksucc/gma-puzzles/common"
|
||||||
|
adriver "github.com/arangodb/go-driver"
|
||||||
|
ahttp "github.com/arangodb/go-driver/http"
|
||||||
|
"github.com/schollz/progressbar/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
arangoDB adriver.Database
|
||||||
|
arangoCTX context.Context
|
||||||
|
colChunk adriver.Collection
|
||||||
|
colFile adriver.Collection
|
||||||
|
colFile2Chunk adriver.Collection
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConnectDB(baseURL string, arangoUser string, arangoPWD string, arangoDatabase string) (driver adriver.Database, ctx context.Context, err error) {
|
||||||
|
log.Println("connectDB:", "Starting Connection Process...")
|
||||||
|
|
||||||
|
// Retry Loop for Failed Connections
|
||||||
|
for i := 0; i < 6; i++ {
|
||||||
|
if i == 5 {
|
||||||
|
return driver, ctx, fmt.Errorf("connectdb unable to connect to database %d times", i)
|
||||||
|
} else if i > 0 {
|
||||||
|
time.Sleep(30 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to ArangoDB URL
|
||||||
|
conn, err := ahttp.NewConnection(ahttp.ConnectionConfig{
|
||||||
|
Endpoints: []string{baseURL},
|
||||||
|
TLSConfig: &tls.Config{ /*...*/ },
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("connectDB:", "Cannot Connect to ArangoDB!", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect Driver to User
|
||||||
|
client, err := adriver.NewClient(adriver.ClientConfig{
|
||||||
|
Connection: conn,
|
||||||
|
Authentication: adriver.BasicAuthentication(arangoUser, arangoPWD),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Println("connectDB:", "Cannot Authenticate ArangoDB User!", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Context for Database Access
|
||||||
|
ctx = context.Background()
|
||||||
|
driver, err = client.Database(ctx, arangoDatabase)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("connectDB:", "Cannot Load ArangoDB Database!", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Println("connectDB:", "Connection Sucessful!")
|
||||||
|
return driver, ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return driver, ctx, fmt.Errorf("connectDB: FUCK HOW DID THIS EXCUTE?")
|
||||||
|
}
|
||||||
|
func InitDatabase() (err error) {
|
||||||
|
arangoDB, arangoCTX, err = ConnectDB("http://192.168.45.8:8529/", "gma-inator", "gma-inator", "gma-inator")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
colChunk, err = arangoDB.Collection(arangoCTX, "chunk")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
colFile, err = arangoDB.Collection(arangoCTX, "file")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
colFile2Chunk, err = arangoDB.Collection(arangoCTX, "file_chunk_map")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckIntegrity(tarPath string) (err error) {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PoolRecoveryData struct {
|
||||||
|
PoolID string `json:"_key"`
|
||||||
|
Size uint64 `json:"size"`
|
||||||
|
Created time.Time `json:"date"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
|
||||||
|
ItemCount int `json:"itemCount"`
|
||||||
|
Items []string `json:"items"`
|
||||||
|
RecoveryData []common.DB_File `json:"recoveryData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var DoneTaskCount chan int
|
||||||
|
var TotalTaskCount chan int
|
||||||
|
var DoneTaskCountV int
|
||||||
|
var TotalTaskCountV int
|
||||||
|
var ConcurrencyLimit int = 12
|
||||||
|
var WorkerJobPool chan string
|
||||||
|
|
||||||
|
func bla() error {
|
||||||
|
WorkerJobPool = make(chan string)
|
||||||
|
sem := common.NewSemaphore(ConcurrencyLimit)
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
|
entries, err := os.ReadDir("/mnt/SC9000/storagePools/")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
chunkNames := []string{}
|
||||||
|
for _, e := range entries {
|
||||||
|
if strings.Contains(e.Name(), ".json") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !e.IsDir() {
|
||||||
|
chunkNames = append(chunkNames, e.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
TotalTaskCount = make(chan int)
|
||||||
|
DoneTaskCount = make(chan int)
|
||||||
|
validationBar := progressbar.Default(int64(len(chunkNames)), "Validating Chunks")
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-TotalTaskCount:
|
||||||
|
TotalTaskCountV++
|
||||||
|
case <-DoneTaskCount:
|
||||||
|
DoneTaskCountV++
|
||||||
|
validationBar.Add(1)
|
||||||
|
if TotalTaskCountV == DoneTaskCountV {
|
||||||
|
fmt.Println("APPARENTLY WE are done")
|
||||||
|
close(TotalTaskCount)
|
||||||
|
close(DoneTaskCount)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for _, chunkName := range chunkNames {
|
||||||
|
wg.Add(1)
|
||||||
|
TotalTaskCount <- 1
|
||||||
|
go func(job string, wg *sync.WaitGroup) (err error) {
|
||||||
|
sem.Acquire() // Wait for worker to have slot open
|
||||||
|
defer sem.Release() // Release the slot
|
||||||
|
defer wg.Done() // Finish job
|
||||||
|
defer func() {
|
||||||
|
DoneTaskCount <- 1
|
||||||
|
}()
|
||||||
|
|
||||||
|
//fmt.Printf("Scanning For Local Pools, found %s:", job)
|
||||||
|
tarFinalPath := filepath.Join("/mnt/SC9000/storagePools/", job)
|
||||||
|
_, err = os.Stat(tarFinalPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
parts := strings.Split(job, ".")
|
||||||
|
jsonPath := filepath.Join("/mnt/SC9000/storagePools/", fmt.Sprintf("%s.json", parts[0]))
|
||||||
|
_, err = os.Stat(jsonPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var dboChunk common.DB_Chunk
|
||||||
|
_, err = colChunk.ReadDocument(arangoCTX, parts[0], &dboChunk)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
log.Printf("Chunk %s does exist on disk but not in database\n", job)
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkReader, err := chunk.NewChunkReader(tarFinalPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = chunkReader.LoadRecoveryFile(jsonPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = chunkReader.CheckIntegrity()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}(chunkName, &wg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Wait for all jobs to finish
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func main() {
|
||||||
|
err := InitDatabase()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bla()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue