Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions internal/pkg/cli/svc_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package cli
import (
"errors"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"path/filepath"
"strings"

Expand Down Expand Up @@ -312,6 +313,8 @@ func buildArgs(name, imageTag, copilotDir string, unmarshaledManifest interface{
Context: *args.Context,
Args: args.Args,
ImageTag: imageTag,
CacheFrom: args.CacheFrom,
Target: aws.StringValue(args.Target),
}, nil
}

Expand Down
12 changes: 12 additions & 0 deletions internal/pkg/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type BuildArguments struct {
ImageTag string // Required. Tag to pass to `docker build` via -t flag. Usually Git commit short ID.
Dockerfile string // Required. Dockerfile to pass to `docker build` via --file flag.
Context string // Optional. Build context directory to pass to `docker build`
Target string // Optional. The target build stage to pass to `docker build`
CacheFrom []string // Optional. Images to consider as cache sources to pass to `docker build`
Args map[string]string // Optional. Build args to pass via `--build-arg` flags. Equivalent to ARG directives in dockerfile.
AdditionalTags []string // Optional. Additional image tags to pass to docker.
}
Expand All @@ -53,6 +55,16 @@ func (r Runner) Build(in *BuildArguments) error {
args = append(args, "-t", imageName(in.URI, tag))
}

// Add cache from options
for _, imageFrom := range in.CacheFrom {
args = append(args, "--cache-from", imageFrom)
}

// Add target option
if in.Target != "" {
args = append(args, "--target", in.Target)
}

// Add the "args:" override section from manifest to the docker build call

// Collect the keys in a slice to sort for test stability
Expand Down
18 changes: 18 additions & 0 deletions internal/pkg/docker/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ func TestBuild(t *testing.T) {
context string
additionalTags []string
args map[string]string
target string
cacheFrom []string
setupMocks func(controller *gomock.Controller)

wantedError error
Expand Down Expand Up @@ -103,6 +105,20 @@ func TestBuild(t *testing.T) {
"mockPath/to", "-f", "mockPath/to/mockDockerfile"}).Return(nil)
},
},
"runs with cache_from and target fields": {
path: mockPath,
target: "foobar",
cacheFrom: []string{"foo/bar:latest", "foo/bar/baz:1.2.3"},
setupMocks: func(c *gomock.Controller) {
mockRunner = mocks.NewMockrunner(c)
mockRunner.EXPECT().Run("docker", []string{"build",
"-t", mockURI + ":" + mockTag1,
"--cache-from", "foo/bar:latest",
"--cache-from", "foo/bar/baz:1.2.3",
"--target", "foobar",
"mockPath/to", "-f", "mockPath/to/mockDockerfile"}).Return(nil)
},
},
}

for name, tc := range tests {
Expand All @@ -119,6 +135,8 @@ func TestBuild(t *testing.T) {
ImageTag: mockTag1,
AdditionalTags: tc.additionalTags,
Args: tc.args,
Target: tc.target,
CacheFrom: tc.cacheFrom,
}
got := s.Build(&buildInput)

Expand Down
3 changes: 2 additions & 1 deletion internal/pkg/manifest/testdata/backend-svc-nohealthcheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ name: subscribers
type: Backend Service

image:
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments.
# For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/backend-service/#image-build
build: ./subscribers/Dockerfile

# Number of CPU units for the task.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ name: cuteness-aggregator
type: Scheduled Job

image:
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments.
# For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/scheduled-job/#image-build
build: ./cuteness-aggregator/Dockerfile

# Number of CPU units for the task.
Expand Down
3 changes: 2 additions & 1 deletion internal/pkg/manifest/testdata/scheduled-job-no-retries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ name: cuteness-aggregator
type: Scheduled Job

image:
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments.
# For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/scheduled-job/#image-build
build: ./cuteness-aggregator/Dockerfile

# Number of CPU units for the task.
Expand Down
3 changes: 2 additions & 1 deletion internal/pkg/manifest/testdata/scheduled-job-no-timeout.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ name: cuteness-aggregator
type: Scheduled Job

image:
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments.
# For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/scheduled-job/#image-build
build: ./cuteness-aggregator/Dockerfile

# Number of CPU units for the task.
Expand Down
45 changes: 27 additions & 18 deletions internal/pkg/manifest/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,31 +58,27 @@ func (i Image) GetLocation() string {
func (i *Image) BuildConfig(rootDirectory string) *DockerBuildArgs {
df := i.dockerfile()
ctx := i.context()
dockerfile := aws.String(filepath.Join(rootDirectory, dockerfileDefaultName))
context := aws.String(rootDirectory)

if df != "" && ctx != "" {
return &DockerBuildArgs{
Dockerfile: aws.String(filepath.Join(rootDirectory, df)),
Context: aws.String(filepath.Join(rootDirectory, ctx)),
Args: i.args(),
}
dockerfile = aws.String(filepath.Join(rootDirectory, df))
context = aws.String(filepath.Join(rootDirectory, ctx))
}
if df != "" && ctx == "" {
return &DockerBuildArgs{
Dockerfile: aws.String(filepath.Join(rootDirectory, df)),
Context: aws.String(filepath.Join(rootDirectory, filepath.Dir(df))),
Args: i.args(),
}
dockerfile = aws.String(filepath.Join(rootDirectory, df))
context = aws.String(filepath.Join(rootDirectory, filepath.Dir(df)))
}
if df == "" && ctx != "" {
return &DockerBuildArgs{
Dockerfile: aws.String(filepath.Join(rootDirectory, ctx, dockerfileDefaultName)),
Context: aws.String(filepath.Join(rootDirectory, ctx)),
Args: i.args(),
}
dockerfile = aws.String(filepath.Join(rootDirectory, ctx, dockerfileDefaultName))
context = aws.String(filepath.Join(rootDirectory, ctx))
}
return &DockerBuildArgs{
Dockerfile: aws.String(filepath.Join(rootDirectory, dockerfileDefaultName)),
Context: aws.String(rootDirectory),
Dockerfile: dockerfile,
Context: context,
Args: i.args(),
Target: i.target(),
CacheFrom: i.cacheFrom(),
}
}

Expand Down Expand Up @@ -114,6 +110,17 @@ func (i *Image) args() map[string]string {
return i.Build.BuildArgs.Args
}

// target returns the build target stage if it exists, otherwise nil.
func (i *Image) target() *string {
return i.Build.BuildArgs.Target
}

// cacheFrom returns the cache from build section, if it exists.
// Otherwise it returns nil.
func (i *Image) cacheFrom() []string {
return i.Build.BuildArgs.CacheFrom
}

// BuildArgsOrString is a custom type which supports unmarshaling yaml which
// can either be of type string or type DockerBuildArgs.
type BuildArgsOrString struct {
Expand Down Expand Up @@ -159,10 +166,12 @@ type DockerBuildArgs struct {
Context *string `yaml:"context,omitempty"`
Dockerfile *string `yaml:"dockerfile,omitempty"`
Args map[string]string `yaml:"args,omitempty"`
Target *string `yaml:"target,omitempty"`
CacheFrom []string `yaml:"cache_from,omitempty"`
}

func (b *DockerBuildArgs) isEmpty() bool {
if b.Context == nil && b.Dockerfile == nil && b.Args == nil {
if b.Context == nil && b.Dockerfile == nil && b.Args == nil && b.Target == nil && b.CacheFrom == nil {
return true
}
return false
Expand Down
38 changes: 38 additions & 0 deletions internal/pkg/manifest/workload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@ func TestBuildArgs_UnmarshalYAML(t *testing.T) {
},
},
},
"Dockerfile with cache from and target build opts": {
inContent: []byte(`build:
cache_from:
- foo/bar:latest
- foo/bar/baz:1.2.3
target: foobar`),
wantedStruct: BuildArgsOrString{
BuildArgs: DockerBuildArgs{
Target: aws.String("foobar"),
CacheFrom: []string{
"foo/bar:latest",
"foo/bar/baz:1.2.3",
},
},
},
},
"Error if unmarshalable": {
inContent: []byte(`build:
badfield: OH NOES
Expand All @@ -76,6 +92,8 @@ func TestBuildArgs_UnmarshalYAML(t *testing.T) {
require.Equal(t, tc.wantedStruct.BuildArgs.Context, b.Build.BuildArgs.Context)
require.Equal(t, tc.wantedStruct.BuildArgs.Dockerfile, b.Build.BuildArgs.Dockerfile)
require.Equal(t, tc.wantedStruct.BuildArgs.Args, b.Build.BuildArgs.Args)
require.Equal(t, tc.wantedStruct.BuildArgs.Target, b.Build.BuildArgs.Target)
require.Equal(t, tc.wantedStruct.BuildArgs.CacheFrom, b.Build.BuildArgs.CacheFrom)
}
})
}
Expand Down Expand Up @@ -154,6 +172,26 @@ func TestBuildConfig(t *testing.T) {
},
},
},
"including build options": {
inBuild: BuildArgsOrString{
BuildArgs: DockerBuildArgs{
Target: aws.String("foobar"),
CacheFrom: []string{
"foo/bar:latest",
"foo/bar/baz:1.2.3",
},
},
},
wantedBuild: DockerBuildArgs{
Dockerfile: aws.String(filepath.Join(mockWsRoot, "Dockerfile")),
Context: aws.String(mockWsRoot),
Target: aws.String("foobar"),
CacheFrom: []string{
"foo/bar:latest",
"foo/bar/baz:1.2.3",
},
},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
Expand Down
5 changes: 4 additions & 1 deletion site/content/docs/manifest/backend-service.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,13 @@ image:
build:
dockerfile: path/to/dockerfile
context: context/dir
target: build-stage
cache_from:
- image:tag
args:
key: value
```
In this case, copilot will use the context directory you specified and convert the key-value pairs under args to --build-arg overrides. The equivalent docker build call will be: `$ docker build --file path/to/dockerfile --build-arg key=value context/dir`.
In this case, copilot will use the context directory you specified and convert the key-value pairs under args to --build-arg overrides. The equivalent docker build call will be: `$ docker build --file path/to/dockerfile --target build-stage --cache-from image:tag --build-arg key=value context/dir`.

You can omit fields and Copilot will do its best to understand what you mean. For example, if you specify `context` but not `dockerfile`, Copilot will run Docker in the context directory and assume that your Dockerfile is named "Dockerfile." If you specify `dockerfile` but no `context`, Copilot assumes you want to run Docker in the directory that contains `dockerfile`.

Expand Down
5 changes: 4 additions & 1 deletion site/content/docs/manifest/lb-web-service.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,13 @@ image:
build:
dockerfile: path/to/dockerfile
context: context/dir
target: build-stage
cache_from:
- image:tag
args:
key: value
```
In this case, copilot will use the context directory you specified and convert the key-value pairs under args to --build-arg overrides. The equivalent docker build call will be: `$ docker build --file path/to/dockerfile --build-arg key=value context/dir`.
In this case, copilot will use the context directory you specified and convert the key-value pairs under args to --build-arg overrides. The equivalent docker build call will be: `$ docker build --file path/to/dockerfile --target build-stage --cache-from image:tag --build-arg key=value context/dir`.

You can omit fields and Copilot will do its best to understand what you mean. For example, if you specify `context` but not `dockerfile`, Copilot will run Docker in the context directory and assume that your Dockerfile is named "Dockerfile." If you specify `dockerfile` but no `context`, Copilot assumes you want to run Docker in the directory that contains `dockerfile`.

Expand Down
5 changes: 4 additions & 1 deletion site/content/docs/manifest/scheduled-job.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,13 @@ image:
build:
dockerfile: path/to/dockerfile
context: context/dir
target: build-stage
cache_from:
- image:tag
args:
key: value
```
In this case, Copilot will use the context directory you specified and convert the key-value pairs under args to --build-arg overrides. The equivalent docker build call will be: `$ docker build --file path/to/dockerfile --build-arg key=value context/dir`.
In this case, Copilot will use the context directory you specified and convert the key-value pairs under args to --build-arg overrides. The equivalent docker build call will be: `$ docker build --file path/to/dockerfile --target build-stage --cache-from image:tag --build-arg key=value context/dir`.

You can omit fields and Copilot will do its best to understand what you mean. For example, if you specify `context` but not `dockerfile`, Copilot will run Docker in the context directory and assume that your Dockerfile is named "Dockerfile." If you specify `dockerfile` but no `context`, Copilot assumes you want to run Docker in the directory that contains `dockerfile`.

Expand Down
10 changes: 10 additions & 0 deletions templates/cicd/buildspec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ phases:
base_dockerfile=$(echo $manifest | jq '.image.build')
build_dockerfile=$(echo $manifest| jq 'if .image.build?.dockerfile? then .image.build.dockerfile else "" end' | sed 's/"//g')
build_context=$(echo $manifest| jq 'if .image.build?.context? then .image.build.context else "" end' | sed 's/"//g')
build_target=$(echo $manifest| jq 'if .image.build?.target? then .image.build.target else "" end' | sed 's/"//g')
dockerfile_args=$(echo $manifest | jq 'if .image.build?.args? then .image.build.args else "" end | to_entries?')
build_cache_from=$(echo $manifest | jq 'if .image.build?.cache_from? then .image.build.cache_from else "" end')
df_rel_path=$( echo $base_dockerfile | sed 's/"//g')
if [ -n "$build_dockerfile" ]; then
df_rel_path=$build_dockerfile
Expand All @@ -86,6 +88,14 @@ phases:
build_args="$build_args--build-arg $arg "
done
fi
if [ -n "$build_target" ]; then
build_args="$build_args--target $build_target "
fi
if [ -n "$build_cache_from" ]; then
for arg in $(echo $build_cache_from | jq -r '.[]'); do
build_args="$build_args--cache-from $arg "
done
fi
echo "Name: $workload"
echo "Relative Dockerfile path: $df_rel_path"
echo "Docker build context: $df_dir_path"
Expand Down
3 changes: 2 additions & 1 deletion templates/workloads/jobs/scheduled-job/manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ type: {{.Type}}

image:
{{- if .ImageConfig.Build.BuildArgs.Dockerfile}}
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments.
# For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/scheduled-job/#image-build
build: {{.ImageConfig.Build.BuildArgs.Dockerfile}}
{{- end}}
{{- if .ImageConfig.Location}}
Expand Down
3 changes: 2 additions & 1 deletion templates/workloads/services/backend/manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ type: {{.Type}}

image:
{{- if .ImageConfig.Build.BuildArgs.Dockerfile}}
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments.
# For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/backend-service/#image-build
build: {{.ImageConfig.Build.BuildArgs.Dockerfile}}
{{- end}}
{{- if .ImageConfig.Image.Location}}
Expand Down
3 changes: 2 additions & 1 deletion templates/workloads/services/lb-web/manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ type: {{.Type}}

image:
{{- if .ImageConfig.Build.BuildArgs.Dockerfile}}
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments.
# For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/#image-build
build: {{.ImageConfig.Build.BuildArgs.Dockerfile}}
{{- end}}
{{- if .ImageConfig.Image.Location}}
Expand Down