package promote import ( "context" "errors" "flag" "fmt" "github.com/go-logr/logr" "edge-infra.dev/pkg/edge/api/types" "edge-infra.dev/pkg/f8n/warehouse/packagelock" "edge-infra.dev/pkg/lib/gcp/pubsub" ) const ( PromoteTag = "promote" Latest = "latest" ) func Run(ctx context.Context, log logr.Logger) error { cfg, err := newConfig() if err != nil { if errors.Is(err, flag.ErrHelp) { return nil } log.Error(err, "failed to load config") return err } log.Info("loaded config", "config", cfg) log.Info("promotion started", "description", cfg.Description()) ps, err := pubsub.New(ctx, cfg.ForwarderProjectID) if err != nil { log.Error(err, "failed to create Pub/Sub connection to project", "project-id", cfg.ForwarderProjectID) return err } plr, err := packagelock.NewRules() if err != nil { log.Error(err, "failed to create package lock rules") return err } pl, err := plr.ParsePackageLock(cfg.LockFile) if err != nil { log.Error(err, "failed to parse package lock") return err } digests, err := Promote(ctx, cfg, pl, ps) if err != nil { log.Error(err, "promotion failed") return err } // TODO(aj185259): This is printing "digests" as eg "us-east1-docker.pkg.dev/ret-edge-stage1-foreman/warehouse-dk185217/banner-infra-cluster:latest" // based on old packages.lock format. Update to more accurate message eventually log.Info("promotion complete", "digests", digests) return nil } func Promote(ctx context.Context, cfg *Config, pl packagelock.PackageLock, ps types.PubSubService) ([]string, error) { sourceRepo := cfg.SourceRepo destinationRepo := cfg.DestinationRepo digests := []string{} for _, lockPkg := range pl.Packages { for _, version := range lockPkg.Versions { if err := sourceRepo.EnsurePackageDigestExists(lockPkg.Name, version.Digest); err != nil { return nil, fmt.Errorf("failed to get hash for package: %s, reason: %v", lockPkg.Name, err) } } } for _, lockPkg := range pl.Packages { pkgDigests, err := promotePackage(ctx, lockPkg, sourceRepo, destinationRepo, ps) if err != nil { return nil, err } digests = append(digests, pkgDigests...) } return digests, nil } func promotePackage(ctx context.Context, lockPkg packagelock.LockPackage, sourceRepo, destinationRepo Repository, ps types.PubSubService) ([]string, error) { tagsSent := []string{} for _, version := range lockPkg.Versions { // send all tags for _, tag := range version.Tags { // message format requires source repo to build digest sourceDigest, err := sourceRepo.NewPackageDigest(lockPkg.Name, version.Digest) if err != nil { return nil, err } // message format requires destination repo to build tag destinationTag, err := destinationRepo.NewPackageTag(lockPkg.Name, tag) if err != nil { return nil, err } msg := newPromotionMessage(sourceDigest, destinationTag, destinationRepo) if err := msg.Send(ctx, ps); err != nil { return nil, fmt.Errorf("failed to publish promotion message to topic: %s, reason: %v", PromotionsTopic, err) } tagsSent = append(tagsSent, tag) } } return destinationRepo.PackageTagRefs(lockPkg.Name, tagsSent), nil }