summaryrefslogtreecommitdiff
path: root/docker/compiler.go
blob: 4d60a226121749b2621b9fb671d44b41c5e62e8f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Package docker implements a compiler for turning Blubber configuration into
// a valid single- or multi-stage Dockerfile.
//
package docker

import (
	"bytes"

	"gerrit.wikimedia.org/r/blubber/build"
	"gerrit.wikimedia.org/r/blubber/config"
	"gerrit.wikimedia.org/r/blubber/meta"
)

// Compile takes a parsed config.Config and a configured variant name and
// returns the bytes of a resulting Dockerfile. In the case where artifacts
// are defined or the shorthand "copies" configured is set, a multi-stage
// Dockerfile will be returned.
//
func Compile(cfg *config.Config, variant string) (*bytes.Buffer, error) {
	buffer := new(bytes.Buffer)

	vcfg, err := config.ExpandVariant(cfg, variant)

	if err != nil {
		return nil, err
	}

	// omit the main stage name unless multi-stage is required below
	mainStage := ""

	// write multi-stage sections for each variant dependency
	for _, stage := range vcfg.VariantDependencies() {
		dependency, err := config.ExpandVariant(cfg, stage)

		if err != nil {
			return nil, err
		}

		compileStage(buffer, stage, dependency)
		mainStage = variant
	}

	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) {
	baseAndStage := vcfg.Base

	if stage != "" {
		baseAndStage += " AS " + stage
	}

	writeln(buffer, "FROM ", baseAndStage)

	for _, phase := range build.Phases() {
		compilePhase(buffer, vcfg, phase)
	}
}

func compileInstructions(buffer *bytes.Buffer, instructions ...build.Instruction) {
	for _, instruction := range instructions {
		dockerInstruction, _ := NewInstruction(instruction)
		write(buffer, dockerInstruction.Compile())
	}
}

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...)
	buffer.WriteString("\n")
}