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
}