check

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 20, 2026 License: Apache-2.0, MIT Imports: 16 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Cmd = &cobra.Command{
	Use:   "check [space] [source-path-or-name]",
	Short: "Check upload integrity and completeness",
	Long: wordwrap.WrapString(`Checks uploads for data inconsistencies and incompleteness that may have occurred due to interrupted uploads or shutdowns.
Performs the following checks:
	1. Upload Scanned Check - Verifies FS and DAG scans completed
	2. File System Integrity Check - Validates FS structure and DAG integrity
	3. Node Integrity Check - Ensures all nodes have upload records
	4. Node Completeness Check - Verifies all nodes are in shards
	5. Shard Completeness Check - Verifies all shards are uploaded and indexed
	6. Index Completeness Check - Verifies all indexes are uploaded

By default, runs in dry-run mode (reports issues without fixing).
Use --repair to automatically fix issues where possible.

If no arguments are provided, checks all uploads.
Specify a space to check only uploads for that space.
Specify both space and source to check a specific upload.`,
		80),
	Args: cobra.MaximumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {
		ctx := cmd.Context()

		cfg, err := config.Load[config.Config]()
		if err != nil {
			return err
		}

		repo, err := preparation.OpenRepo(ctx, cfg.Repo)
		if err != nil {
			return err
		}
		defer repo.Close()

		client := cmdutil.MustGetClient(cfg.Repo.Dir, cfg.Network)

		// Determine which uploads to check
		var uploadsToCheck []uploadInfo
		switch len(args) {
		case 0:

			uploadsToCheck, err = getAllUploads(ctx, repo)
			if err != nil {
				return fmt.Errorf("getting all uploads: %w", err)
			}
			if len(uploadsToCheck) == 0 {
				cmd.Println("No uploads found.")
				return nil
			}
			cmd.Printf("Checking %d upload(s)...\n", len(uploadsToCheck))

		case 1:

			spaceArg := args[0]
			spaceDID, err := cmdutil.ResolveSpace(client, spaceArg)
			if err != nil {
				return err
			}
			uploadsToCheck, err = getUploadsForSpace(ctx, repo, spaceDID)
			if err != nil {
				return fmt.Errorf("getting uploads for space %s: %w", spaceDID, err)
			}
			if len(uploadsToCheck) == 0 {
				cmd.Printf("No uploads found for space %s.\n", spaceDID)
				return nil
			}
			cmd.Printf("Checking %d upload(s) for space %s...\n", len(uploadsToCheck), spaceDID)

		case 2:

			spaceArg := args[0]
			sourceArg := args[1]
			spaceDID, err := cmdutil.ResolveSpace(client, spaceArg)
			if err != nil {
				return err
			}
			upload, err := getUploadForSpaceAndSource(ctx, repo, spaceDID, sourceArg)
			if err != nil {
				return fmt.Errorf("getting upload for space %s and source %s: %w", spaceDID, sourceArg, err)
			}
			uploadsToCheck = []uploadInfo{upload}
			cmd.Printf("Checking upload for space %s, source %s...\n", spaceDID, sourceArg)
		}

		sourcesAPI := sources.API{
			Repo: repo,
			GetLocalFSForPathFn: func(path string) (fs.FS, error) {
				absPath, err := filepath.Abs(path)
				if err != nil {
					return nil, fmt.Errorf("resolving absolute path: %w", err)
				}
				info, err := os.Stat(absPath)
				if err != nil {
					return nil, fmt.Errorf("statting path: %w", err)
				}

				if info.IsDir() {
					return os.DirFS(absPath), nil
				}
				dir := filepath.Dir(absPath)
				base := filepath.Base(absPath)
				fsys, err := fs.Sub(os.DirFS(dir), base)
				if err != nil {
					return nil, fmt.Errorf("getting sub fs: %w", err)
				}
				return fsys, nil
			},
		}

		nodeReaderOpener, err := nodereader.NewNodeReaderOpener(
			repo.LinksForCID,
			func(ctx context.Context, sourceID id.SourceID, path string) (fs.File, error) {
				source, err := repo.GetSourceByID(ctx, sourceID)
				if err != nil {
					return nil, fmt.Errorf("failed to get source by ID %s: %w", sourceID, err)
				}

				fsys, err := sourcesAPI.Access(source)
				if err != nil {
					return nil, fmt.Errorf("failed to access source %s: %w", sourceID, err)
				}

				f, err := fsys.Open(path)
				if err != nil {
					return nil, fmt.Errorf("failed to open file %s in source %s: %w", path, sourceID, err)
				}
				return f, nil
			},
			true,
		)
		if err != nil {
			return fmt.Errorf("creating node reader opener: %w", err)
		}

		checker := &prepcheck.Checker{
			Repo:           repo,
			OpenNodeReader: nodeReaderOpener.OpenNodeReader,
		}

		var opts []prepcheck.Option
		if checkFlags.repair {
			opts = append(opts, prepcheck.WithRepairs())
		}

		allPassed := true
		totalRepairs := 0
		for i, uploadInfo := range uploadsToCheck {
			if len(uploadsToCheck) > 1 {
				cmd.Printf("\n[%d/%d] ", i+1, len(uploadsToCheck))
			}
			cmd.Printf("Upload: %s\n", uploadInfo.displayName)

			report, err := checker.CheckUpload(ctx, uploadInfo.uploadID, opts...)
			if err != nil {
				return fmt.Errorf("checking upload %s: %w", uploadInfo.uploadID, err)
			}

			printReport(cmd, report)

			if !report.OverallPass {
				allPassed = false
			}
			totalRepairs += report.RepairsApplied
		}

		if len(uploadsToCheck) > 1 {
			cmd.Println("\n" + strings.Repeat("=", 60))
			cmd.Printf("Summary: Checked %d upload(s)\n", len(uploadsToCheck))
			if allPassed {
				cmd.Println("✓ All uploads passed all checks")
			} else {
				cmd.Println("✗ Some uploads have issues")
			}
			if totalRepairs > 0 {
				cmd.Printf("Applied %d repair(s)\n", totalRepairs)
			}
		}

		if !allPassed {
			return cmdutil.NewHandledCliError(fmt.Errorf("upload check failed"))
		}
		return nil
	},
}

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL