func setupRecoveryContext()

in internal/cli/gitaly/subcmd_recovery.go [476:646]


func setupRecoveryContext(ctx context.Context, cmd *cli.Command) (rc recoveryContext, returnErr error) {
	recoveryContext := recoveryContext{
		cmd:          cmd,
		partitions:   make([]storage.PartitionID, 0),
		cleanupFuncs: list.New(),
	}
	defer func() {
		if returnErr != nil {
			returnErr = errors.Join(returnErr, recoveryContext.Cleanup())
		}
	}()

	parallel := cmd.Int(flagParallel)
	if parallel < 1 {
		parallel = 1
	}
	recoveryContext.parallel = int(parallel)

	logger := log.ConfigureCommand()

	cfg, err := loadConfig(cmd.String(flagConfig))
	if err != nil {
		return recoveryContext, fmt.Errorf("load config: %w", err)
	}

	if cfg.Backup.WALGoCloudURL == "" {
		return recoveryContext, fmt.Errorf("write-ahead log backup is not configured")
	}
	sink, err := backup.ResolveSink(ctx, cfg.Backup.WALGoCloudURL)
	if err != nil {
		return recoveryContext, fmt.Errorf("resolve sink: %w", err)
	}
	recoveryContext.logEntryStore = backup.NewLogEntryStore(sink)

	runtimeDir, err := os.MkdirTemp("", "gitaly-recovery-*")
	if err != nil {
		return recoveryContext, fmt.Errorf("creating runtime dir: %w", err)
	}
	recoveryContext.cleanupFuncs.PushFront(func() error {
		return os.RemoveAll(runtimeDir)
	})

	cfg.RuntimeDir = runtimeDir
	if err := gitaly.UnpackAuxiliaryBinaries(cfg.RuntimeDir, func(binaryName string) bool {
		return strings.HasPrefix(binaryName, "gitaly-git")
	}); err != nil {
		return recoveryContext, fmt.Errorf("unpack auxiliary binaries: %w", err)
	}

	dbMgr, err := databasemgr.NewDBManager(
		ctx,
		cfg.Storages,
		keyvalue.NewBadgerStore,
		helper.NewTimerTickerFactory(time.Minute),
		logger,
	)
	if err != nil {
		return recoveryContext, fmt.Errorf("new db manager: %w", err)
	}
	recoveryContext.cleanupFuncs.PushFront(func() error {
		dbMgr.Close()
		return nil
	})

	gitCmdFactory, cleanup, err := gitcmd.NewExecCommandFactory(cfg, logger)
	if err != nil {
		return recoveryContext, fmt.Errorf("creating Git command factory: %w", err)
	}
	recoveryContext.cleanupFuncs.PushFront(func() error {
		cleanup()
		return nil
	})

	catfileCache := catfile.NewCache(cfg)
	recoveryContext.cleanupFuncs.PushFront(func() error {
		catfileCache.Stop()
		return nil
	})

	partitionFactoryOptions := []partition.FactoryOption{
		partition.WithCmdFactory(gitCmdFactory),
		partition.WithRepoFactory(localrepo.NewFactory(logger, config.NewLocator(cfg), gitCmdFactory, catfileCache)),
		partition.WithMetrics(partition.NewMetrics(housekeeping.NewMetrics(cfg.Prometheus))),
		partition.WithRaftConfig(cfg.Raft),
	}

	node, err := nodeimpl.NewManager(
		cfg.Storages,
		storagemgr.NewFactory(
			logger,
			dbMgr,
			migration.NewFactory(
				partition.NewFactory(partitionFactoryOptions...),
				migration.NewMetrics(),
				[]migration.Migration{},
			),
			1,
			storagemgr.NewMetrics(cfg.Prometheus),
		),
	)
	if err != nil {
		return recoveryContext, fmt.Errorf("new node: %w", err)
	}
	recoveryContext.cleanupFuncs.PushFront(func() error {
		node.Close()
		return nil
	})

	storageName := cmd.String(flagStorage)
	if storageName == "" {
		if len(cfg.Storages) != 1 {
			return recoveryContext, fmt.Errorf("multiple storages configured: use --storage to specify the one you want")
		}

		storageName = cfg.Storages[0].Name
	}
	nodeStorage, err := node.GetStorage(storageName)
	if err != nil {
		return recoveryContext, fmt.Errorf("get storage: %w", err)
	}
	recoveryContext.storageName = storageName
	recoveryContext.nodeStorage = nodeStorage

	if cmd.Bool("all") {
		iter, err := nodeStorage.ListPartitions(storage.PartitionID(0))
		if err != nil {
			return recoveryContext, fmt.Errorf("list partitions: %w", err)
		}
		defer iter.Close()

		for iter.Next() {
			recoveryContext.partitions = append(recoveryContext.partitions, iter.GetPartitionID())
		}

		if err := iter.Err(); err != nil {
			return recoveryContext, fmt.Errorf("partition iterator: %w", err)
		}
	} else {
		partitionString := cmd.String(flagPartition)
		repositoryPath := cmd.String(flagRepository)

		if partitionString != "" && repositoryPath != "" {
			return recoveryContext, fmt.Errorf("--partition and --repository flags can not be provided at the same time")
		}

		if partitionString == "" && repositoryPath == "" {
			return recoveryContext, fmt.Errorf("this command requires one of --all, --partition or --repository flags")
		}

		var err error
		var partitionID storage.PartitionID
		if partitionString != "" {
			if err = parsePartitionID(&partitionID, partitionString); err != nil {
				return recoveryContext, fmt.Errorf("parse partition ID: %w", err)
			}
		} else {
			partitionID, err = nodeStorage.GetAssignedPartitionID(repositoryPath)
			if err != nil {
				return recoveryContext, fmt.Errorf("partition ID not found for the given relative path: %w", err)
			}
		}

		if partitionID == storage.PartitionID(0) {
			return recoveryContext, fmt.Errorf("invalid partition ID %s", partitionID)
		}

		recoveryContext.partitions = append(recoveryContext.partitions, partitionID)
	}

	return recoveryContext, nil
}