Skip to content

Commit

Permalink
Add support for podman image digests
Browse files Browse the repository at this point in the history
  • Loading branch information
mjrlee committed Feb 13, 2024
1 parent f3b1576 commit fc2b9bd
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 8 deletions.
1 change: 1 addition & 0 deletions internal/pkg/cli/deploy/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ func buildArgsPerContainer(name, workspacePath string, img ContainerImageIdentif
CacheFrom: buildArgs.CacheFrom,
Target: aws.StringValue(buildArgs.Target),
Platform: mf.ContainerPlatform(),
Engine: aws.StringValue(buildArgs.Engine),
Tags: tags,
Labels: labels,
}
Expand Down
21 changes: 20 additions & 1 deletion internal/pkg/docker/dockerengine/dockerengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type BuildArguments struct {
Platform string // Optional. OS/Arch to pass to `docker build`.
Args map[string]string // Optional. Build args to pass via `--build-arg` flags. Equivalent to ARG directives in dockerfile.
Labels map[string]string // Required. Set metadata for an image.
Engine string // Optional. Currently supported options are "docker" and "podman". Defaults to "docker".
}

// RunOptions holds the options for running a Docker container.
Expand Down Expand Up @@ -232,12 +233,22 @@ func (c DockerCmdClient) Exec(ctx context.Context, container string, out io.Writ
}

// Push pushes the images with the specified tags and ecr repository URI, and returns the image digest on success.
func (c DockerCmdClient) Push(ctx context.Context, uri string, w io.Writer, tags ...string) (digest string, err error) {
func (c DockerCmdClient) Push(ctx context.Context, uri string, engine string, w io.Writer, tags ...string) (digest string, err error) {
images := []string{}
for _, tag := range tags {
images = append(images, imageName(uri, tag))
}
var args []string
// Podman image digests are based on the compressed image, so need to be gathered from podman.
var digestFile *os.File
if engine == "podman" {
digestFile, err = os.CreateTemp("", "copilot-digest")
if err != nil {
return "", fmt.Errorf("create temp file for digest: %w", err)
}
defer os.Remove(digestFile.Name())
args = append(args, "--digestfile", digestFile.Name())
}
if ci, _ := c.lookupEnv("CI"); ci == "true" {
args = append(args, "--quiet")
}
Expand All @@ -247,6 +258,14 @@ func (c DockerCmdClient) Push(ctx context.Context, uri string, w io.Writer, tags
return "", fmt.Errorf("docker push %s: %w", img, err)
}
}
if engine == "podman" {
digest, err := os.ReadFile(digestFile.Name())
if err != nil {
return "", fmt.Errorf("read digest file: %w", err)
}
return string(digest), nil
}

buf := new(strings.Builder)
// The container image will have the same digest regardless of the associated tag.
// Pick the first tag and get the image's digest.
Expand Down
11 changes: 6 additions & 5 deletions internal/pkg/docker/dockerengine/dockerengine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func TestDockerCommand_Build(t *testing.T) {
args map[string]string
target string
cacheFrom []string
engine string
envVars map[string]string
labels map[string]string
setupMocks func(controller *gomock.Controller)
Expand Down Expand Up @@ -313,7 +314,7 @@ func TestDockerCommand_Push(t *testing.T) {
lookupEnv: emptyLookupEnv,
}
buf := new(strings.Builder)
digest, err := cmd.Push(ctx, "aws_account_id.dkr.ecr.region.amazonaws.com/my-web-app", buf, "latest", "g123bfc")
digest, err := cmd.Push(ctx, "aws_account_id.dkr.ecr.region.amazonaws.com/my-web-app", "docker", buf, "latest", "g123bfc")

// THEN
require.NoError(t, err)
Expand Down Expand Up @@ -343,7 +344,7 @@ func TestDockerCommand_Push(t *testing.T) {
},
}
buf := new(strings.Builder)
digest, err := cmd.Push(context.Background(), "aws_account_id.dkr.ecr.region.amazonaws.com/my-web-app", buf, "latest")
digest, err := cmd.Push(context.Background(), "aws_account_id.dkr.ecr.region.amazonaws.com/my-web-app", "docker", buf, "latest")

// THEN
require.NoError(t, err)
Expand All @@ -362,7 +363,7 @@ func TestDockerCommand_Push(t *testing.T) {
lookupEnv: emptyLookupEnv,
}
buf := new(strings.Builder)
_, err := cmd.Push(ctx, "uri", buf, "latest")
_, err := cmd.Push(ctx, "uri", "docker", buf, "latest")

// THEN
require.EqualError(t, err, "docker push uri:latest: some error")
Expand All @@ -381,7 +382,7 @@ func TestDockerCommand_Push(t *testing.T) {
lookupEnv: emptyLookupEnv,
}
buf := new(strings.Builder)
_, err := cmd.Push(ctx, "uri", buf, "latest")
_, err := cmd.Push(ctx, "uri", "docker", buf, "latest")

// THEN
require.EqualError(t, err, "inspect image digest for uri: some error")
Expand All @@ -406,7 +407,7 @@ func TestDockerCommand_Push(t *testing.T) {
lookupEnv: emptyLookupEnv,
}
buf := new(strings.Builder)
_, err := cmd.Push(ctx, "aws_account_id.dkr.ecr.region.amazonaws.com/my-web-app", buf, "latest", "g123bfc")
_, err := cmd.Push(ctx, "aws_account_id.dkr.ecr.region.amazonaws.com/my-web-app", "docker", buf, "latest", "g123bfc")

// THEN
require.EqualError(t, err, "parse the digest from the repo digest ''")
Expand Down
10 changes: 10 additions & 0 deletions internal/pkg/manifest/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ func (i *ImageLocationOrBuild) BuildConfig(rootDirectory string) *DockerBuildArg
Args: i.args(),
Target: i.target(),
CacheFrom: i.cacheFrom(),
Engine: i.engine(),
}
}

Expand Down Expand Up @@ -238,6 +239,14 @@ func (i *ImageLocationOrBuild) cacheFrom() []string {
return i.Build.BuildArgs.CacheFrom
}

// engine returns the engine to use for building the image if it exists, otherwise "docker"
func (i *ImageLocationOrBuild) engine() *string {
if i.Build.BuildArgs.Engine != nil {
return i.Build.BuildArgs.Engine
}
return aws.String("docker")
}

// ImageOverride holds fields that override Dockerfile image defaults.
type ImageOverride struct {
EntryPoint EntryPointOverride `yaml:"entrypoint"`
Expand Down Expand Up @@ -398,6 +407,7 @@ type DockerBuildArgs struct {
Args map[string]string `yaml:"args,omitempty"`
Target *string `yaml:"target,omitempty"`
CacheFrom []string `yaml:"cache_from,omitempty"`
Engine *string `yaml:"engine,omitempty"`
}

func (b *DockerBuildArgs) isEmpty() bool {
Expand Down
5 changes: 3 additions & 2 deletions internal/pkg/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
type ContainerLoginBuildPusher interface {
Build(ctx context.Context, args *dockerengine.BuildArguments, w io.Writer) error
Login(uri, username, password string) error
Push(ctx context.Context, uri string, w io.Writer, tags ...string) (digest string, err error)
Push(ctx context.Context, uri string, engine string, w io.Writer, tags ...string) (digest string, err error)
IsEcrCredentialHelperEnabled(uri string) bool
}

Expand Down Expand Up @@ -77,10 +77,11 @@ func (r *Repository) BuildAndPush(ctx context.Context, args *dockerengine.BuildA
return "", fmt.Errorf("build Dockerfile at %s: %w", args.Dockerfile, err)
}

digest, err = r.docker.Push(ctx, args.URI, w, args.Tags...)
digest, err = r.docker.Push(ctx, args.URI, args.Engine, w, args.Tags...)
if err != nil {
return "", fmt.Errorf("push to repo %s: %w", r.name, err)
}

return digest, nil
}

Expand Down

0 comments on commit fc2b9bd

Please sign in to comment.