diff options
author | Dan Duvall <dduvall@wikimedia.org> | 2017-10-16 11:58:00 -0700 |
---|---|---|
committer | Dan Duvall <dduvall@wikimedia.org> | 2017-10-19 09:39:49 -0700 |
commit | 818a0fcc0ffcda1c80a33c4f1ff6e1cc4d52abe3 (patch) | |
tree | 9ee6e4067414506e6bd422888ea0d08dc7eefef7 /docker | |
parent | d53a06a138106c1652fff0b8576ae907a29a727c (diff) | |
download | blubber-818a0fcc0ffcda1c80a33c4f1ff6e1cc4d52abe3.tar.gz |
Include meta data as labels in Dockerfile output
Summary:
Certain meta data including the Blubber version and variant used at
invocation may be useful in downstream tracking.
Implemented abstract and Docker-specific instructions for labels and
modified the Docker compiler to include these instructions at the end of
the output for the final stage.
Depends on D816
Fixes T178022
Test Plan:
Run unit tests. Run `make && bin/blubber blubber.example.yaml production` and
verify that both `blubber.version` and `blubber.variant` labels are present
and accurate.
Reviewers: thcipriani, hashar, #release-engineering-team
Reviewed By: thcipriani, #release-engineering-team
Tags: #release-engineering-team
Maniphest Tasks: T178022
Differential Revision: https://phabricator.wikimedia.org/D818
Diffstat (limited to 'docker')
-rw-r--r-- | docker/compiler.go | 49 | ||||
-rw-r--r-- | docker/compiler_test.go | 19 | ||||
-rw-r--r-- | docker/instructions.go | 14 | ||||
-rw-r--r-- | docker/instructions_test.go | 12 |
4 files changed, 75 insertions, 19 deletions
diff --git a/docker/compiler.go b/docker/compiler.go index 79f179e..0a2120b 100644 --- a/docker/compiler.go +++ b/docker/compiler.go @@ -6,6 +6,7 @@ import ( "phabricator.wikimedia.org/source/blubber/build" "phabricator.wikimedia.org/source/blubber/config" + "phabricator.wikimedia.org/source/blubber/meta" ) // Compile blubber yaml file into Dockerfile @@ -28,63 +29,73 @@ func Compile(cfg *config.Config, variant string) (*bytes.Buffer, error) { if err != nil { return nil, err } - CompileStage(buffer, stage, dependency) + compileStage(buffer, stage, dependency) mainStage = variant } - CompileStage(buffer, mainStage, vcfg) + compileStage(buffer, mainStage, vcfg) + + // add meta-data labels to the final stage + compileInstructions(buffer, build.Label{map[string]string{ + "blubber.variant": variant, + "blubber.version": meta.FullVersion(), + }}) return buffer, nil } -func CompileStage(buffer *bytes.Buffer, stage string, vcfg *config.VariantConfig) { +func compileStage(buffer *bytes.Buffer, stage string, vcfg *config.VariantConfig) { baseAndStage := vcfg.Base if stage != "" { baseAndStage += " AS " + stage } - Writeln(buffer, "FROM ", baseAndStage) + writeln(buffer, "FROM ", baseAndStage) - Writeln(buffer, "USER root") + writeln(buffer, "USER root") - CompilePhase(buffer, vcfg, build.PhasePrivileged) + compilePhase(buffer, vcfg, build.PhasePrivileged) if vcfg.Runs.As != "" { - Writeln(buffer, "USER ", vcfg.Runs.As) + writeln(buffer, "USER ", vcfg.Runs.As) } - CompilePhase(buffer, vcfg, build.PhasePrivilegeDropped) + compilePhase(buffer, vcfg, build.PhasePrivilegeDropped) if vcfg.Runs.In != "" { - Writeln(buffer, "WORKDIR ", vcfg.Runs.In) + writeln(buffer, "WORKDIR ", vcfg.Runs.In) } - CompilePhase(buffer, vcfg, build.PhasePreInstall) + compilePhase(buffer, vcfg, build.PhasePreInstall) - CompilePhase(buffer, vcfg, build.PhaseInstall) + compilePhase(buffer, vcfg, build.PhaseInstall) - CompilePhase(buffer, vcfg, build.PhasePostInstall) + compilePhase(buffer, vcfg, build.PhasePostInstall) if len(vcfg.EntryPoint) > 0 { - Writeln(buffer, "ENTRYPOINT [\"", strings.Join(vcfg.EntryPoint, "\", \""), "\"]") + writeln(buffer, "ENTRYPOINT [\"", strings.Join(vcfg.EntryPoint, "\", \""), "\"]") } } -func CompilePhase(buffer *bytes.Buffer, vcfg *config.VariantConfig, phase build.Phase) { - for _, instruction := range vcfg.InstructionsForPhase(phase) { +func compileInstructions(buffer *bytes.Buffer, instructions ...build.Instruction) { + for _, instruction := range instructions { dockerInstruction, _ := NewDockerInstruction(instruction) - Write(buffer, dockerInstruction.Compile()) + write(buffer, dockerInstruction.Compile()) } } -func Write(buffer *bytes.Buffer, strings ...string) { +func compilePhase(buffer *bytes.Buffer, vcfg *config.VariantConfig, phase build.Phase) { + compileInstructions(buffer, vcfg.InstructionsForPhase(phase)...) +} + +func write(buffer *bytes.Buffer, strings ...string) { for _, str := range strings { buffer.WriteString(str) } } -func Writeln(buffer *bytes.Buffer, strings ...string) { - Write(buffer, strings...) +func writeln(buffer *bytes.Buffer, strings ...string) { + write(buffer, strings...) buffer.WriteString("\n") } diff --git a/docker/compiler_test.go b/docker/compiler_test.go index 04454b4..846304b 100644 --- a/docker/compiler_test.go +++ b/docker/compiler_test.go @@ -8,6 +8,7 @@ import ( "phabricator.wikimedia.org/source/blubber/config" "phabricator.wikimedia.org/source/blubber/docker" + "phabricator.wikimedia.org/source/blubber/meta" ) func TestSingleStageHasNoName(t *testing.T) { @@ -72,3 +73,21 @@ func TestMultipleArtifactsFromSameStage(t *testing.T) { assert.Equal(t, 1, strings.Count(dockerfile, "FROM foo/bar AS build\n")) assert.Equal(t, 1, strings.Count(dockerfile, "FROM foo/bar AS production\n")) } + +func TestMetaDataLabels(t *testing.T) { + cfg, err := config.ReadConfig([]byte(`--- + base: foo/bar + variants: + development: {}`)) + + assert.Nil(t, err) + + dockerOut, _ := docker.Compile(cfg, "development") + dockerfile := dockerOut.String() + + version := meta.FullVersion() + + assert.Contains(t, dockerfile, + "LABEL blubber.variant=\"development\" blubber.version=\""+version+"\"\n", + ) +} diff --git a/docker/instructions.go b/docker/instructions.go index fa566cb..eba6731 100644 --- a/docker/instructions.go +++ b/docker/instructions.go @@ -26,6 +26,10 @@ func NewDockerInstruction(instruction build.Instruction) (DockerInstruction, err var dockerInstruction DockerEnv dockerInstruction.arguments = instruction.Compile() return dockerInstruction, nil + case build.Label: + var dockerInstruction DockerLabel + dockerInstruction.arguments = instruction.Compile() + return dockerInstruction, nil case build.Volume: var dockerInstruction DockerVolume dockerInstruction.arguments = instruction.Compile() @@ -81,6 +85,16 @@ func (de DockerEnv) Compile() string { join(de.arguments, " ")) } +// DockerLabel represents a concrete LABEL instruction +type DockerLabel struct{ abstractDockerInstruction } + +// Compile returns multiple key="value" arguments as a single LABEL string +func (dl DockerLabel) Compile() string { + return fmt.Sprintf( + "LABEL %s\n", + join(dl.arguments, " ")) +} + type DockerVolume struct{ abstractDockerInstruction } func (dv DockerVolume) Compile() string { diff --git a/docker/instructions_test.go b/docker/instructions_test.go index 87dfada..a4f648b 100644 --- a/docker/instructions_test.go +++ b/docker/instructions_test.go @@ -71,6 +71,18 @@ func TestEnv(t *testing.T) { assert.Equal(t, "ENV bar=\"foo\" foo=\"bar\"\n", di.Compile()) } +func TestLabel(t *testing.T) { + i := build.Label{map[string]string{"foo": "bar", "bar": "foo"}} + + di, err := docker.NewDockerInstruction(i) + + var dockerLabel docker.DockerLabel + + assert.Nil(t, err) + assert.IsType(t, dockerLabel, di) + assert.Equal(t, "LABEL bar=\"foo\" foo=\"bar\"\n", di.Compile()) +} + func TestVolume(t *testing.T) { i := build.Volume{"/foo/dir"} |