diff options
author | Dan Duvall <dduvall@wikimedia.org> | 2017-08-30 09:47:41 -0700 |
---|---|---|
committer | Dan Duvall <dduvall@wikimedia.org> | 2017-09-07 10:01:34 -0700 |
commit | 410085e1f5be759b6a2bfbe08a51dca84aa18e3c (patch) | |
tree | 9c0b1e24953082cfe96e84b0924ba0ac90c67100 /config | |
parent | 2a19f04679b042567dd7ed9c0208eacbb63b8d26 (diff) | |
download | blubber-410085e1f5be759b6a2bfbe08a51dca84aa18e3c.tar.gz |
Support `copies` config entry for multi-stage builds
Summary:
Support a `copies` variant config entry that will result in a
multi-stage build, copying both shared library files and application
directory from a previously defined variant. This is essentially a
shorthand for two `artifacts` entries that are likely to be idiomatic to
multi-stage build/prod configurations.
Defined a new abstract `build.CopyFrom` instruction and corresponding
`docker.DockerCopyFrom` instruction and refactored the writing of these
Dockerfile lines to be accomplished using an `InstructionsForPhase`
method on `config.ArtifactsConfig`.
Implemented new support for `copies` configuration in
`config.VariantConfig` and an `InstructionsForPhase` method that returns
`build.CopyFrom` instructions for both the shared library and
application directories.
Fixes T174622
Depends on D759
Test Plan:
Run `go test ./...`. Run `blubber blubber.example.yaml production` and ensure
the right `COPY --from` lines are included for the final stage.
Reviewers: thcipriani, mobrovac, hashar, mmodell, #release-engineering-team
Reviewed By: thcipriani, #release-engineering-team
Tags: #release-engineering-team
Maniphest Tasks: T174622
Differential Revision: https://phabricator.wikimedia.org/D768
Diffstat (limited to 'config')
-rw-r--r-- | config/artifacts.go | 15 | ||||
-rw-r--r-- | config/artifacts_test.go | 31 | ||||
-rw-r--r-- | config/variant.go | 55 | ||||
-rw-r--r-- | config/variant_test.go | 66 |
4 files changed, 167 insertions, 0 deletions
diff --git a/config/artifacts.go b/config/artifacts.go index a8108d0..c3fad69 100644 --- a/config/artifacts.go +++ b/config/artifacts.go @@ -1,7 +1,22 @@ package config +import ( + "phabricator.wikimedia.org/source/blubber.git/build" +) + type ArtifactsConfig struct { From string `yaml:"from"` Source string `yaml:"source"` Destination string `yaml:"destination"` } + +func (ac ArtifactsConfig) InstructionsForPhase(phase build.Phase) []build.Instruction { + switch phase { + case build.PhasePostInstall: + return []build.Instruction{ + build.CopyFrom{ac.From, build.Copy{[]string{ac.Source}, ac.Destination}}, + } + } + + return []build.Instruction{} +} diff --git a/config/artifacts_test.go b/config/artifacts_test.go index 09f2ae4..9198d96 100644 --- a/config/artifacts_test.go +++ b/config/artifacts_test.go @@ -5,6 +5,7 @@ import ( "gopkg.in/stretchr/testify.v1/assert" + "phabricator.wikimedia.org/source/blubber.git/build" "phabricator.wikimedia.org/source/blubber.git/config" ) @@ -38,3 +39,33 @@ func TestArtifactsConfig(t *testing.T) { config.ArtifactsConfig{From: "build", Source: "/bar/src", Destination: "/bar/dst"}, ) } + +func TestArtifactsConfigInstructions(t *testing.T) { + cfg := config.ArtifactsConfig{ + From: "foo", + Source: "/source/path", + Destination: "/destination/path", + } + + t.Run("PhasePrivileged", func(t *testing.T) { + assert.Empty(t, cfg.InstructionsForPhase(build.PhasePrivileged)) + }) + + t.Run("PhasePrivilegeDropped", func(t *testing.T) { + assert.Empty(t, cfg.InstructionsForPhase(build.PhasePrivilegeDropped)) + }) + + t.Run("PhasePreInstall", func(t *testing.T) { + assert.Empty(t, cfg.InstructionsForPhase(build.PhasePreInstall)) + }) + + t.Run("PhasePostInstall", func(t *testing.T) { + assert.Equal(t, + []build.Instruction{build.CopyFrom{ + "foo", + build.Copy{[]string{"/source/path"}, "/destination/path"}, + }}, + cfg.InstructionsForPhase(build.PhasePostInstall), + ) + }) +} diff --git a/config/variant.go b/config/variant.go index 9309b9d..cc2cbdb 100644 --- a/config/variant.go +++ b/config/variant.go @@ -1,12 +1,67 @@ package config +import ( + "phabricator.wikimedia.org/source/blubber.git/build" +) + type VariantConfig struct { Includes []string `yaml:"includes"` + Copies string `yaml:"copies"` Artifacts []ArtifactsConfig `yaml:"artifacts"` CommonConfig `yaml:",inline"` } func (vc *VariantConfig) Merge(vc2 VariantConfig) { + vc.Copies = vc2.Copies vc.Artifacts = append(vc.Artifacts, vc2.Artifacts...) vc.CommonConfig.Merge(vc2.CommonConfig) } + +func (vc *VariantConfig) InstructionsForPhase(phase build.Phase) []build.Instruction { + instructions := vc.CommonConfig.InstructionsForPhase(phase) + ainstructions := []build.Instruction{} + + for _, artifact := range vc.allArtifacts() { + ainstructions = append(ainstructions, artifact.InstructionsForPhase(phase)...) + } + + return append(ainstructions, instructions...) +} + +func (vc *VariantConfig) VariantDependencies() []string { + // get unique set of variant dependencies based on artifacts + existing := map[string]bool{} + dependencies := []string{} + + for _, artifact := range vc.allArtifacts() { + if dependency := artifact.From; dependency != "" && !existing[dependency] { + existing[dependency] = true + dependencies = append(dependencies, dependency) + } + } + + return dependencies +} + +func (vc *VariantConfig) allArtifacts() []ArtifactsConfig { + return append(vc.defaultArtifacts(), vc.Artifacts...) +} + +func (vc *VariantConfig) defaultArtifacts() []ArtifactsConfig { + if vc.Copies != "" { + return []ArtifactsConfig{ + { + From: vc.Copies, + Source: vc.CommonConfig.Runs.In, + Destination: vc.CommonConfig.Runs.In, + }, + { + From: vc.Copies, + Source: LocalLibPrefix, + Destination: LocalLibPrefix, + }, + } + } + + return []ArtifactsConfig{} +} diff --git a/config/variant_test.go b/config/variant_test.go new file mode 100644 index 0000000..0a98828 --- /dev/null +++ b/config/variant_test.go @@ -0,0 +1,66 @@ +package config_test + +import ( + "testing" + + "gopkg.in/stretchr/testify.v1/assert" + + "phabricator.wikimedia.org/source/blubber.git/build" + "phabricator.wikimedia.org/source/blubber.git/config" +) + +func TestVariantConfig(t *testing.T) { + cfg, err := config.ReadConfig([]byte(`--- + variants: + build: {} + production: + copies: build + artifacts: + - from: build + source: /foo/src + destination: /foo/dst + - from: build + source: /bar/src + destination: /bar/dst`)) + + assert.Nil(t, err) + + variant, err := config.ExpandVariant(cfg, "production") + + assert.Nil(t, err) + + assert.Equal(t, "build", variant.Copies) + assert.Len(t, variant.Artifacts, 2) +} + +func TestVariantDependencies(t *testing.T) { + cfg := config.VariantConfig{ + Copies: "foo", + Artifacts: []config.ArtifactsConfig{ + {From: "build", Source: "/foo/src", Destination: "/foo/dst"}, + }, + } + + assert.Equal(t, []string{"foo", "build"}, cfg.VariantDependencies()) +} + +func TestVariantConfigInstructions(t *testing.T) { + cfg := config.VariantConfig{ + CommonConfig: config.CommonConfig{Runs: config.RunsConfig{In: "/srv/service"}}, + Copies: "foo", + Artifacts: []config.ArtifactsConfig{ + {From: "build", Source: "/foo/src", Destination: "/foo/dst"}, + }, + } + + t.Run("PhasePostInstall", func(t *testing.T) { + assert.Equal(t, + []build.Instruction{ + build.CopyFrom{"foo", build.Copy{[]string{"/srv/service"}, "/srv/service"}}, + build.CopyFrom{"foo", build.Copy{[]string{config.LocalLibPrefix}, config.LocalLibPrefix}}, + build.CopyFrom{"build", build.Copy{[]string{"/foo/src"}, "/foo/dst"}}, + }, + cfg.InstructionsForPhase(build.PhasePostInstall), + ) + }) +} |