Skip to content

Commit

Permalink
feat(manifest): add target and cache from build options
Browse files Browse the repository at this point in the history
  • Loading branch information
mRoca committed Oct 22, 2020
1 parent 2256074 commit c38908d
Show file tree
Hide file tree
Showing 15 changed files with 123 additions and 27 deletions.
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)
},
},
"success with options": {
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ name: subscribers
type: Backend Service

image:
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args, target, cache_from.
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,7 @@ name: cuteness-aggregator
type: Scheduled Job

image:
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args, target, cache_from.
build: ./cuteness-aggregator/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,7 @@ name: cuteness-aggregator
type: Scheduled Job

image:
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args, target, cache_from.
build: ./cuteness-aggregator/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,7 @@ name: cuteness-aggregator
type: Scheduled Job

image:
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args, target, cache_from.
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 @@ -55,31 +55,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 @@ -111,6 +107,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 an empty array.
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 @@ -156,10 +163,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
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
2 changes: 1 addition & 1 deletion templates/workloads/jobs/scheduled-job/manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type: {{.Type}}

image:
{{- if .ImageConfig.Build.BuildArgs.Dockerfile}}
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args, target, cache_from.
build: {{.ImageConfig.Build.BuildArgs.Dockerfile}}
{{- end}}
{{- if .ImageConfig.Location}}
Expand Down
2 changes: 1 addition & 1 deletion templates/workloads/services/backend/manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type: {{.Type}}

image:
{{- if .ImageConfig.Build.BuildArgs.Dockerfile}}
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args, target, cache_from.
build: {{.ImageConfig.Build.BuildArgs.Dockerfile}}
{{- end}}
{{- if .ImageConfig.Image.Location}}
Expand Down
2 changes: 1 addition & 1 deletion templates/workloads/services/lb-web/manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type: {{.Type}}

image:
{{- if .ImageConfig.Build.BuildArgs.Dockerfile}}
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args.
# Docker build arguments. You can specify additional overrides here. Supported: dockerfile, context, args, target, cache_from.
build: {{.ImageConfig.Build.BuildArgs.Dockerfile}}
{{- end}}
{{- if .ImageConfig.Image.Location}}
Expand Down

0 comments on commit c38908d

Please sign in to comment.