in internal/cli/praefect/subcmd_track_repositories.go [60:194]
func trackRepositoriesAction(ctx context.Context, cmd *cli.Command) error {
logger := log.ConfigureCommand()
conf, err := readConfig(cmd.String(configFlagName))
if err != nil {
return err
}
db, clean, err := openDB(conf.DB, cmd.ErrWriter)
if err != nil {
return fmt.Errorf("connect to database: %w", err)
}
defer clean()
ctx = correlation.ContextWithCorrelation(ctx, correlation.SafeRandomID())
logger = logger.WithField("correlation_id", correlation.ExtractFromContext(ctx))
store := datastore.NewPostgresRepositoryStore(db, conf.StorageNames())
inputPath := cmd.String(paramInputPath)
f, err := os.Open(inputPath)
if err != nil {
return fmt.Errorf("open input: %w", err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
fmt.Fprintf(cmd.Writer, "Validating repository information in %q\n", inputPath)
var requests []trackRepositoryRequest
var line int
var repoErrs []invalidRequest
pathLines := make(map[string][]int)
// Read in and validate all requests from input file before executing. This prevents us from
// partially executing a file, which makes it difficult to tell which repos were actually
// tracked.
for scanner.Scan() {
line++
request := trackRepositoryRequest{}
badReq := invalidRequest{line: line}
if err := json.Unmarshal(scanner.Bytes(), &request); err != nil {
badReq.errs = append(badReq.errs, err)
repoErrs = append(repoErrs, badReq)
// Invalid request, nothing to validate.
continue
}
if request.RelativePath == "" {
badReq.errs = append(badReq.errs, requiredParameterError(paramRelativePath))
}
badReq.relativePath = request.RelativePath
if request.ReplicaPath == "" {
badReq.errs = append(badReq.errs, requiredParameterError(paramReplicaPath))
}
badReq.replicaPath = request.ReplicaPath
if request.VirtualStorage == "" {
badReq.errs = append(badReq.errs, requiredParameterError(paramVirtualStorage))
}
if request.AuthoritativeStorage == "" {
badReq.errs = append(badReq.errs, requiredParameterError(paramAuthoritativeStorage))
}
if len(badReq.errs) > 0 {
repoErrs = append(repoErrs, badReq)
// Incomplete request, no further validation possible.
continue
}
// Repo paths are globally unique, any attempt to add the same path multiple virtual storages
// is invalid and must be rejected.
prevLines, exists := pathLines[request.RelativePath]
if exists {
badReq.errs = append(badReq.errs, &dupPathError{path: request.RelativePath})
repoErrs = append(repoErrs, badReq)
prevLines = append(prevLines, line)
pathLines[request.RelativePath] = prevLines
// We've already checked this path, no need to run further checks.
continue
}
pathLines[request.RelativePath] = []int{line}
repoInDB, err := store.RepositoryExists(ctx, request.VirtualStorage, request.RelativePath)
if err != nil {
// Bail out if we're having trouble contacting the DB, nothing is going to work if this fails.
return fmt.Errorf("checking database: %w", err)
}
if repoInDB {
badReq.errs = append(badReq.errs, fmt.Errorf("repository is already tracked by Praefect"))
repoErrs = append(repoErrs, badReq)
// Repo already in Praefect DB, we can skip it.
continue
}
authoritativeRepoExists, err := request.authoritativeRepositoryExists(ctx, conf, logger, cmd.Writer, request.AuthoritativeStorage)
if err != nil {
badReq.errs = append(badReq.errs, fmt.Errorf("checking repository on disk: %w", err))
} else if !authoritativeRepoExists {
badReq.errs = append(badReq.errs, fmt.Errorf("not a valid git repository"))
}
if len(badReq.errs) > 0 {
repoErrs = append(repoErrs, badReq)
continue
}
requests = append(requests, request)
}
if len(repoErrs) > 0 {
printInvalidRequests(cmd.Writer, repoErrs, pathLines, inputPath)
return fmt.Errorf("invalid entries found, aborting")
}
if len(requests) == 0 {
return fmt.Errorf("no repository information found in %q", inputPath)
}
fmt.Fprintf(cmd.Writer, "All repository details are correctly formatted\n")
fmt.Fprintf(cmd.Writer, "Tracking %v repositories in Praefect DB...\n", line)
replicateImmediately := cmd.Bool("replicate-immediately")
for _, request := range requests {
if err := request.execRequest(ctx, db, conf, cmd.Writer, logger, replicateImmediately); err != nil {
return fmt.Errorf("tracking repository %q: %w", request.RelativePath, err)
}
}
return nil
}