diff options
-rw-r--r-- | build/instructions.go | 13 | ||||
-rw-r--r-- | build/phases.go | 14 | ||||
-rw-r--r-- | config/apt.go | 25 | ||||
-rw-r--r-- | config/common.go | 19 | ||||
-rw-r--r-- | config/config.go | 4 | ||||
-rw-r--r-- | config/npm.go | 39 | ||||
-rw-r--r-- | config/runs.go | 40 | ||||
-rw-r--r-- | docker/compiler.go | 29 |
8 files changed, 131 insertions, 52 deletions
diff --git a/build/instructions.go b/build/instructions.go new file mode 100644 index 0000000..2676a75 --- /dev/null +++ b/build/instructions.go @@ -0,0 +1,13 @@ +package build + +type InstructionType int + +const ( + Run InstructionType = iota + Copy +) + +type Instruction struct { + Type InstructionType + Arguments []string +} diff --git a/build/phases.go b/build/phases.go new file mode 100644 index 0000000..095263c --- /dev/null +++ b/build/phases.go @@ -0,0 +1,14 @@ +package build + +type Phase int + +const ( + PhasePrivileged Phase = iota + PhasePrivilegeDropped + PhasePreInstall + PhasePostInstall +) + +type PhaseCompileable interface { + InstructionsForPhase(phase Phase) []Instruction +} diff --git a/config/apt.go b/config/apt.go index f9e631f..65dad9c 100644 --- a/config/apt.go +++ b/config/apt.go @@ -1,8 +1,8 @@ package config import ( - "bytes" "strings" + "github.com/marxarelli/blubber/build" ) type AptConfig struct { @@ -13,16 +13,19 @@ func (apt *AptConfig) Merge(apt2 AptConfig) { apt.Packages = append(apt.Packages, apt2.Packages...) } -func (apt AptConfig) Commands() []string { - if len(apt.Packages) < 1 { - return []string{} +func (apt AptConfig) InstructionsForPhase(phase build.Phase) []build.Instruction { + if len(apt.Packages) > 0 { + switch phase { + case build.PhasePrivileged: + return []build.Instruction{ + {build.Run, []string{ + "apt-get update && apt-get install -y ", + strings.Join(apt.Packages, " "), + " && rm -rf /var/lib/apt/lists/*", + }}, + } + } } - buffer := new(bytes.Buffer) - - buffer.WriteString("apt-get update && apt-get install -y ") - buffer.WriteString(strings.Join(apt.Packages, " ")) - buffer.WriteString(" && rm -rf /var/lib/apt/lists/*") - - return []string{buffer.String()} + return []build.Instruction{} } diff --git a/config/common.go b/config/common.go index c4dc2db..1073b18 100644 --- a/config/common.go +++ b/config/common.go @@ -1,5 +1,9 @@ package config +import ( + "github.com/marxarelli/blubber/build" +) + type CommonConfig struct { Base string `yaml:"base"` Apt AptConfig `yaml:"apt"` @@ -24,3 +28,18 @@ func (cc1 *CommonConfig) Merge(cc2 CommonConfig) { cc1.EntryPoint = cc2.EntryPoint } } + + +func (cc *CommonConfig) PhaseCompileableConfig() []build.PhaseCompileable { + return []build.PhaseCompileable{cc.Apt, cc.Npm, cc.Runs} +} + +func (cc *CommonConfig) InstructionsForPhase(phase build.Phase) []build.Instruction { + instructions := []build.Instruction{} + + for _, phaseCompileable := range cc.PhaseCompileableConfig() { + instructions = append(instructions, phaseCompileable.InstructionsForPhase(phase)...) + } + + return instructions +} diff --git a/config/config.go b/config/config.go index dcf1adb..ee38ea8 100644 --- a/config/config.go +++ b/config/config.go @@ -4,7 +4,3 @@ type Config struct { CommonConfig `yaml:",inline"` Variants map[string]VariantConfig `yaml:"variants"` } - -type CommandCompileable interface { - Commands() []string -} diff --git a/config/npm.go b/config/npm.go index c4a52c1..78307b6 100644 --- a/config/npm.go +++ b/config/npm.go @@ -2,8 +2,12 @@ package config import ( "bytes" + "path" + "github.com/marxarelli/blubber/build" ) +const TempNpmInstallDir = "/tmp/node-deps/" + type NpmConfig struct { Install bool `yaml:"install"` Env string `yaml:"env"` @@ -17,18 +21,29 @@ func (npm *NpmConfig) Merge(npm2 NpmConfig) { } } -func (npm NpmConfig) Commands() []string { - if !npm.Install { - return []string{} - } - - buffer := new(bytes.Buffer) - - buffer.WriteString("npm install") - - if npm.Env == "production" { - buffer.WriteString(" --production && npm dedupe") +func (npm NpmConfig) InstructionsForPhase(phase build.Phase) []build.Instruction{ + if npm.Install { + switch phase { + case build.PhasePreInstall: + npmCmd := new(bytes.Buffer) + + npmCmd.WriteString("npm install") + + if npm.Env == "production" { + npmCmd.WriteString(" --production && npm dedupe") + } + + return []build.Instruction{ + {build.Run, []string{"mkdir -p ", TempNpmInstallDir}}, + {build.Copy, []string{"package.json", TempNpmInstallDir}}, + {build.Run, []string{"cd ", TempNpmInstallDir, " && ", npmCmd.String()}}, + } + case build.PhasePostInstall: + return []build.Instruction{ + {build.Run, []string{"mv ", path.Join(TempNpmInstallDir, "node_modules"), " ./"}}, + } + } } - return []string{buffer.String()} + return []build.Instruction{} } diff --git a/config/runs.go b/config/runs.go index 70f03a9..1d1f63a 100644 --- a/config/runs.go +++ b/config/runs.go @@ -2,7 +2,7 @@ package config import ( "strconv" - "strings" + "github.com/marxarelli/blubber/build" ) type RunsConfig struct { @@ -19,26 +19,32 @@ func (run *RunsConfig) Merge(run2 RunsConfig) { if run2.Gid != 0 { run.Gid = run2.Gid } } -func (run RunsConfig) Commands() []string { - cmds := []string{} +func (run RunsConfig) InstructionsForPhase(phase build.Phase) []build.Instruction { + ins := []build.Instruction{} - if run.In != "" { - cmds = append(cmds, strings.Join([]string{"mkdir -p", run.In}, " ")) - } - - if run.As != "" { - cmd := []string{ - "groupadd -o -g", strconv.Itoa(run.Gid), "-r", run.As, "&&", - "useradd -o -m -r -g", run.As, "-u", strconv.Itoa(run.Uid), run.As, + switch phase { + case build.PhasePrivileged: + if run.In != "" { + ins = append(ins, []build.Instruction{{build.Run, []string{"mkdir -p ", run.In}}}...) } - cmds = append(cmds, strings.Join(cmd, " ")) - - if run.In != "" { - owner := strings.Join([]string{run.As, ":", run.As}, "") - cmds = append(cmds, strings.Join([]string{"chown", owner, run.In}, " ")) + if run.As != "" { + ins = append(ins, []build.Instruction{ + {build.Run, []string{ + "groupadd -o -g ", strconv.Itoa(run.Gid), " -r ", run.As, " && ", + "useradd -o -m -r -g ", run.As, " -u ", strconv.Itoa(run.Uid), " ", run.As, + }}, + }...) + + if run.In != "" { + ins = append(ins, []build.Instruction{ + {build.Run, []string{ + "chown ", run.As, ":", run.As, " ", run.In, + }}, + }...) + } } } - return cmds + return ins } diff --git a/docker/compiler.go b/docker/compiler.go index f5114c4..6b08355 100644 --- a/docker/compiler.go +++ b/docker/compiler.go @@ -3,6 +3,7 @@ package docker import ( "bytes" "strings" + "github.com/marxarelli/blubber/build" "github.com/marxarelli/blubber/config" ) @@ -33,26 +34,27 @@ func CompileStage(buffer *bytes.Buffer, stage string, vcfg *config.VariantConfig Writeln(buffer, "FROM ", vcfg.Base, " AS ", stage) Writeln(buffer, "USER root") - Writeln(buffer, "WORKDIR /srv") - CompileToCommands(buffer, vcfg.Apt) - CompileToCommands(buffer, vcfg.Runs) + + CompilePhase(buffer, vcfg, build.PhasePrivileged) if vcfg.Runs.As != "" { Writeln(buffer, "USER ", vcfg.Runs.As) } + CompilePhase(buffer, vcfg, build.PhasePrivilegeDropped) + if vcfg.Runs.In != "" { Writeln(buffer, "WORKDIR ", vcfg.Runs.In) } + CompilePhase(buffer, vcfg, build.PhasePreInstall) + if vcfg.SharedVolume { Writeln(buffer, "VOLUME [\"", vcfg.Runs.In, "\"]") } else { Writeln(buffer, "COPY . \"", vcfg.Runs.In, "\"") } - CompileToCommands(buffer, vcfg.Npm) - // Artifact copying for _, artifact := range vcfg.Artifacts { Write(buffer, "COPY ") @@ -64,14 +66,25 @@ func CompileStage(buffer *bytes.Buffer, stage string, vcfg *config.VariantConfig Writeln(buffer, artifact.Source, " ", artifact.Destination) } + CompilePhase(buffer, vcfg, build.PhasePostInstall) + if len(vcfg.EntryPoint) > 0 { Writeln(buffer, "ENTRYPOINT [\"", strings.Join(vcfg.EntryPoint, "\", \""), "\"]") } } -func CompileToCommands(buffer *bytes.Buffer, compileable config.CommandCompileable) { - for _, command := range compileable.Commands() { - Writeln(buffer, "RUN ", command) +func CompilePhase(buffer *bytes.Buffer, vcfg *config.VariantConfig, phase build.Phase) { + for _, instruction := range vcfg.InstructionsForPhase(phase) { + CompileInstruction(buffer, instruction) + } +} + +func CompileInstruction(buffer *bytes.Buffer, instruction build.Instruction) { + switch instruction.Type { + case build.Run: + Writeln(buffer, append([]string{"RUN "}, instruction.Arguments...)...) + case build.Copy: + Writeln(buffer, "COPY \"", instruction.Arguments[0], "\" \"", instruction.Arguments[1], "\"") } } |