Skip to content

Commit

Permalink
feat: integrates ECR generation into docker release and adds deployme…
Browse files Browse the repository at this point in the history
…nt runtime (#121)
  • Loading branch information
jmgilman authored Jan 18, 2025
1 parent a0516ff commit ed2eaaf
Show file tree
Hide file tree
Showing 21 changed files with 385 additions and 161 deletions.
5 changes: 4 additions & 1 deletion blueprint.cue
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ global: {
]
}
deployment: {
registry: ci.providers.aws.ecr.registry + "/catalyst-deployments"
registries: {
containers: "ghcr.io/input-output-hk/catalyst-forge"
modules: ci.providers.aws.ecr.registry + "/catalyst-deployments"
}
repo: {
url: "https://github.com/input-output-hk/catalyst-world"
ref: "master"
Expand Down
11 changes: 7 additions & 4 deletions cli/pkg/deployment/gitops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/input-output-hk/catalyst-forge/lib/project/schema"
"github.com/input-output-hk/catalyst-forge/lib/project/secrets"
"github.com/input-output-hk/catalyst-forge/lib/project/secrets/mocks"
"github.com/input-output-hk/catalyst-forge/lib/tools/pointers"
"github.com/input-output-hk/catalyst-forge/lib/tools/testutils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -50,7 +49,9 @@ func TestDeploy(t *testing.T) {
defaultParams := projectParams{
projectName: "test",
globalDeploy: schema.GlobalDeployment{
Registry: "registry.myserver.com",
Registries: schema.GlobalDeploymentRegistries{
Modules: "registry.myserver.com",
},
Repo: schema.GlobalDeploymentRepo{
Ref: "main",
Url: "https://github.com/foo/bar",
Expand Down Expand Up @@ -191,7 +192,9 @@ func TestLoad(t *testing.T) {
defaultParams := projectParams{
projectName: "test",
globalDeploy: schema.GlobalDeployment{
Registry: "registry.myserver.com",
Registries: schema.GlobalDeploymentRegistries{
Modules: "registry.myserver.com",
},
Repo: schema.GlobalDeploymentRepo{
Ref: "main",
Url: "https://github.com/foo/bar",
Expand Down Expand Up @@ -294,7 +297,7 @@ func newTestProject(p projectParams) *project.Project {
Environment: p.enviroment,
Modules: &schema.DeploymentModules{
Main: schema.Module{
Container: pointers.String(p.container),
Name: p.container,
Namespace: p.namespace,
Values: ctx.CompileString(p.values),
Version: p.version,
Expand Down
12 changes: 6 additions & 6 deletions cli/pkg/deployment/kcl.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ type KCLRunner struct {
func (k *KCLRunner) GetMainValues(p *project.Project) (string, error) {
if p.Blueprint.Project.Deployment.Modules == nil {
return "", fmt.Errorf("no deployment modules found in project blueprint")
} else if p.Blueprint.Global.Deployment.Registry == "" {
return "", fmt.Errorf("no deployment registry found in project blueprint")
} else if p.Blueprint.Global.Deployment.Registries.Modules == "" {
return "", fmt.Errorf("no module deployment registry found in project blueprint")
}

ctx := cuecontext.New()
Expand All @@ -86,8 +86,8 @@ func (k *KCLRunner) RunDeployment(p *project.Project) (map[string]KCLRunResult,
ctx := cuecontext.New()
if p.Blueprint.Project.Deployment.Modules == nil {
return nil, fmt.Errorf("no deployment modules found in project blueprint")
} else if p.Blueprint.Global.Deployment.Registry == "" {
return nil, fmt.Errorf("no deployment registry found in project blueprint")
} else if p.Blueprint.Global.Deployment.Registries.Modules == "" {
return nil, fmt.Errorf("no module deployment registry found in project blueprint")
}

modules := map[string]schema.Module{"main": p.Blueprint.Project.Deployment.Modules.Main}
Expand All @@ -109,10 +109,10 @@ func (k *KCLRunner) RunDeployment(p *project.Project) (map[string]KCLRunResult,
Version: module.Version,
}

container := fmt.Sprintf("%s/%s", strings.TrimSuffix(p.Blueprint.Global.Deployment.Registry, "/"), module.Module)
container := fmt.Sprintf("%s/%s", strings.TrimSuffix(p.Blueprint.Global.Deployment.Registries.Modules, "/"), module.Name)
out, err := k.run(container, args)
if err != nil {
k.logger.Error("Failed to run KCL module", "module", module.Module, "error", err, "output", string(out))
k.logger.Error("Failed to run KCL module", "module", module.Name, "error", err, "output", string(out))
return nil, fmt.Errorf("failed to run KCL module: %w", err)
}

Expand Down
16 changes: 10 additions & 6 deletions cli/pkg/deployment/kcl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ func TestKCLRunnerGetMainValues(t *testing.T) {
},
Global: schema.Global{
Deployment: schema.GlobalDeployment{
Registry: "test",
Registries: schema.GlobalDeploymentRegistries{
Modules: "test.com",
},
},
},
},
Expand All @@ -43,7 +45,7 @@ func TestKCLRunnerGetMainValues(t *testing.T) {
"test",
&schema.DeploymentModules{
Main: schema.Module{
Module: "module",
Name: "module",
Namespace: "default",
Values: map[string]string{
"key": "value",
Expand Down Expand Up @@ -91,7 +93,9 @@ func TestKCLRunnerRunDeployment(t *testing.T) {
},
Global: schema.Global{
Deployment: schema.GlobalDeployment{
Registry: registry,
Registries: schema.GlobalDeploymentRegistries{
Modules: registry,
},
},
},
},
Expand All @@ -113,7 +117,7 @@ func TestKCLRunnerRunDeployment(t *testing.T) {
"test.com",
&schema.DeploymentModules{
Main: schema.Module{
Module: "module",
Name: "module",
Namespace: "default",
Values: map[string]string{
"key": "value",
Expand All @@ -122,7 +126,7 @@ func TestKCLRunnerRunDeployment(t *testing.T) {
},
Support: map[string]schema.Module{
"support": {
Module: "module1",
Name: "module1",
Namespace: "default",
Values: map[string]string{
"key1": "value1",
Expand Down Expand Up @@ -152,7 +156,7 @@ func TestKCLRunnerRunDeployment(t *testing.T) {
"test.com",
&schema.DeploymentModules{
Main: schema.Module{
Module: "module",
Name: "module",
Namespace: "default",
Values: map[string]string{
"key": "value",
Expand Down
29 changes: 0 additions & 29 deletions cli/pkg/release/providers/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,40 +34,11 @@ func createECRRepoIfNotExists(client aws.ECRClient, p *project.Project, registry
return nil
}

// generateContainerName generates the container name for the project.
// If the name is not provided, the project name is used.
func generateContainerName(p *project.Project, name string, registry string) string {
var n string
if name == "" {
n = p.Name
} else {
n = name
}

if isGHCRRegistry(registry) {
return fmt.Sprintf("%s/%s", strings.TrimSuffix(registry, "/"), n)
} else {
var repo string
if strings.Contains(p.Blueprint.Global.Repo.Name, "/") {
repo = strings.Split(p.Blueprint.Global.Repo.Name, "/")[1]
} else {
repo = p.Blueprint.Global.Repo.Name
}

return fmt.Sprintf("%s/%s/%s", strings.TrimSuffix(registry, "/"), repo, n)
}
}

// isECRRegistry checks if the registry is an ECR registry.
func isECRRegistry(registry string) bool {
return regexp.MustCompile(`^\d{12}\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com`).MatchString(registry)
}

// isGHCRRegistry checks if the registry is a GHCR registry.
func isGHCRRegistry(registry string) bool {
return regexp.MustCompile(`^ghcr\.io/[a-zA-Z0-9](?:-?[a-zA-Z0-9])*$`).MatchString(registry)
}

// parseConfig parses the configuration for the release.
func parseConfig(p *project.Project, release string, config any) error {
err := p.Raw().DecodePath(fmt.Sprintf("project.release.%s.config", release), &config)
Expand Down
30 changes: 26 additions & 4 deletions cli/pkg/release/providers/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/input-output-hk/catalyst-forge/cli/pkg/earthly"
"github.com/input-output-hk/catalyst-forge/cli/pkg/events"
"github.com/input-output-hk/catalyst-forge/cli/pkg/executor"
"github.com/input-output-hk/catalyst-forge/cli/pkg/providers/aws"
"github.com/input-output-hk/catalyst-forge/cli/pkg/run"
"github.com/input-output-hk/catalyst-forge/lib/project/project"
"github.com/input-output-hk/catalyst-forge/lib/project/schema"
Expand All @@ -26,6 +27,7 @@ type DockerReleaserConfig struct {
type DockerReleaser struct {
config DockerReleaserConfig
docker executor.WrappedExecuter
ecr aws.ECRClient
force bool
handler events.EventHandler
logger *slog.Logger
Expand Down Expand Up @@ -56,9 +58,7 @@ func (r *DockerReleaser) Release() error {
return fmt.Errorf("no registries found")
}

container := r.project.Blueprint.Project.Container
registries := r.project.Blueprint.Global.CI.Registries

imageTag := r.config.Tag
if imageTag == "" {
return fmt.Errorf("no image tag specified")
Expand All @@ -69,10 +69,18 @@ func (r *DockerReleaser) Release() error {
for _, registry := range registries {
var pushed []string

container := project.GenerateContainerName(&r.project, r.project.Blueprint.Project.Container, registry)
if isECRRegistry(registry) {
r.logger.Info("Detected ECR registry, checking if repository exists", "repository", container)
if err := createECRRepoIfNotExists(r.ecr, &r.project, container, r.logger); err != nil {
return fmt.Errorf("failed to create ECR repository: %w", err)
}
}

for _, platform := range platforms {
platformSuffix := strings.Replace(platform, "/", "_", -1)
curImage := fmt.Sprintf("%s:%s_%s", CONTAINER_NAME, TAG_NAME, platformSuffix)
newImage := fmt.Sprintf("%s/%s:%s_%s", registry, container, imageTag, platformSuffix)
newImage := fmt.Sprintf("%s:%s_%s", container, imageTag, platformSuffix)

r.logger.Debug("Tagging image", "tag", newImage)
if err := r.tagImage(curImage, newImage); err != nil {
Expand All @@ -95,8 +103,16 @@ func (r *DockerReleaser) Release() error {
}
} else {
for _, registry := range registries {
container := project.GenerateContainerName(&r.project, r.project.Blueprint.Project.Container, registry)
if isECRRegistry(registry) {
r.logger.Info("Detected ECR registry, checking if repository exists", "repository", container)
if err := createECRRepoIfNotExists(r.ecr, &r.project, container, r.logger); err != nil {
return fmt.Errorf("failed to create ECR repository: %w", err)
}
}

curImage := fmt.Sprintf("%s:%s", CONTAINER_NAME, TAG_NAME)
newImage := fmt.Sprintf("%s/%s:%s", registry, container, imageTag)
newImage := fmt.Sprintf("%s:%s", container, imageTag)

r.logger.Info("Tagging image", "old", curImage, "new", newImage)
if err := r.tagImage(curImage, newImage); err != nil {
Expand Down Expand Up @@ -215,12 +231,18 @@ func NewDockerReleaser(
return nil, fmt.Errorf("failed to parse release config: %w", err)
}

ecr, err := aws.NewECRClient(ctx.Logger)
if err != nil {
return nil, fmt.Errorf("failed to create ECR client: %w", err)
}

docker := executor.NewLocalWrappedExecutor(exec, "docker")
handler := events.NewDefaultEventHandler(ctx.Logger)
runner := run.NewDefaultProjectRunner(ctx, &project)
return &DockerReleaser{
config: config,
docker: docker,
ecr: ecr,
force: force,
handler: &handler,
logger: ctx.Logger,
Expand Down
Loading

0 comments on commit ed2eaaf

Please sign in to comment.