summaryrefslogtreecommitdiff
path: root/docker
diff options
context:
space:
mode:
authorDan Duvall <dduvall@wikimedia.org>2017-10-16 11:58:00 -0700
committerDan Duvall <dduvall@wikimedia.org>2017-10-19 09:39:49 -0700
commit818a0fcc0ffcda1c80a33c4f1ff6e1cc4d52abe3 (patch)
tree9ee6e4067414506e6bd422888ea0d08dc7eefef7 /docker
parentd53a06a138106c1652fff0b8576ae907a29a727c (diff)
downloadblubber-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.go49
-rw-r--r--docker/compiler_test.go19
-rw-r--r--docker/instructions.go14
-rw-r--r--docker/instructions_test.go12
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"}