diff --git a/cmd/bee/cmd/db.go b/cmd/bee/cmd/db.go index dc04b442634..ec94ff7f6d8 100644 --- a/cmd/bee/cmd/db.go +++ b/cmd/bee/cmd/db.go @@ -27,8 +27,10 @@ import ( ) const ( - optionNameValidation = "validate" - optionNameValidationPin = "validate-pin" + optionNameValidation = "validate" + optionNameValidationPin = "validate-pin" + optionNameCollectionPin = "pin" + optionNameOutputLocation = "output" ) func (c *command) initDBCmd() { @@ -193,14 +195,19 @@ func dbValidatePinsCmd(cmd *cobra.Command) { return errors.New("no data-dir provided") } - providedPin, err := cmd.Flags().GetString("pin") + providedPin, err := cmd.Flags().GetString(optionNameCollectionPin) if err != nil { - return fmt.Errorf("get pin: %w", err) + return fmt.Errorf("read pin option: %w", err) + } + + outputLoc, err := cmd.Flags().GetString(optionNameOutputLocation) + if err != nil { + return fmt.Errorf("read location option: %w", err) } localstorePath := path.Join(dataDir, "localstore") - err = storer.ValidatePinCollectionChunks(context.Background(), localstorePath, providedPin, &storer.Options{ + err = storer.ValidatePinCollectionChunks(context.Background(), localstorePath, providedPin, outputLoc, &storer.Options{ Logger: logger, RadiusSetter: noopRadiusSetter{}, Batchstore: new(postage.NoOpBatchStore), @@ -215,7 +222,8 @@ func dbValidatePinsCmd(cmd *cobra.Command) { } c.Flags().String(optionNameDataDir, "", "data directory") c.Flags().String(optionNameVerbosity, "info", "verbosity level") - c.Flags().String("pin", "", "only validate given pin") + c.Flags().String(optionNameCollectionPin, "", "only validate given pin") + c.Flags().String(optionNameOutputLocation, "", "location and name of the output file") cmd.AddCommand(c) } diff --git a/pkg/storer/validate.go b/pkg/storer/validate.go index 3a2e9a9b460..2cf92d79ce9 100644 --- a/pkg/storer/validate.go +++ b/pkg/storer/validate.go @@ -148,7 +148,7 @@ func validateWork(logger log.Logger, store storage.Store, readFn func(context.Co // ValidatePinCollectionChunks collects all chunk addresses that are present in a pin collection but // are either invalid or missing altogether. -func ValidatePinCollectionChunks(ctx context.Context, basePath, pin string, opts *Options) error { +func ValidatePinCollectionChunks(ctx context.Context, basePath, pin, location string, opts *Options) error { logger := opts.Logger store, err := initStore(basePath, opts) @@ -173,12 +173,12 @@ func ValidatePinCollectionChunks(ctx context.Context, basePath, pin string, opts }() logger.Info("performing chunk validation") - validatePins(logger, store, pin, sharky.Read) + validatePins(logger, store, pin, location, sharky.Read) return nil } -func validatePins(logger log.Logger, store storage.Store, pin string, readFn func(context.Context, sharky.Location, []byte) error) { +func validatePins(logger log.Logger, store storage.Store, pin, location string, readFn func(context.Context, sharky.Location, []byte) error) { var stats struct { total, read, invalid atomic.Int32 } @@ -230,9 +230,25 @@ func validatePins(logger log.Logger, store storage.Store, pin string, readFn fun logger.Info("got a total number of pins", "size", len(pins)) - f, err := os.OpenFile("address.csv", os.O_CREATE|os.O_WRONLY, 0644) + var ( + fileName = "address.csv" + fileLoc = "." + ) + + if location != "" { + if path.Ext(location) != "" { + fileName = path.Base(location) + } + fileLoc = path.Dir(location) + } + + logger.Info("saving stats to", "location", fileLoc, "name", fileName) + + location = path.Join(fileLoc, fileName) + + f, err := os.OpenFile(location, os.O_CREATE|os.O_WRONLY, 0644) if err != nil { - logger.Error(err, "open file for writing") + logger.Error(err, "open output file for writing") return }