// Package build defines types and interfaces that could potentially be
							 | 
						|
								// compiled to various external build-tool scripts but share a general
							 | 
						|
								// internal abstraction and rules for escaping.
							 | 
						|
								//
							 | 
						|
								package build
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"fmt"
							 | 
						|
									"sort"
							 | 
						|
									"strconv"
							 | 
						|
									"strings"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								// Instruction defines a common interface that all concrete build types must
							 | 
						|
								// implement.
							 | 
						|
								//
							 | 
						|
								type Instruction interface {
							 | 
						|
									Compile() []string
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Run is a concrete build instruction for passing any number of arguments to
							 | 
						|
								// a shell command.
							 | 
						|
								//
							 | 
						|
								// The command string may contain inner argument placeholders using the "%s"
							 | 
						|
								// format verb and will be appended with the quoted values of any arguments
							 | 
						|
								// that remain after interpolation of the command string.
							 | 
						|
								//
							 | 
						|
								type Run struct {
							 | 
						|
									Command   string   // command string (e.g. "useradd -d %s -u %s")
							 | 
						|
									Arguments []string // command arguments both inner and final (e.g. ["/home/user", "123", "user"])
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Compile quotes all arguments, interpolates the command string with inner
							 | 
						|
								// arguments, and appends the final arguments.
							 | 
						|
								//
							 | 
						|
								func (run Run) Compile() []string {
							 | 
						|
									numInnerArgs := strings.Count(run.Command, `%`) - strings.Count(run.Command, `%%`)
							 | 
						|
									command := sprintf(run.Command, run.Arguments[0:numInnerArgs])
							 | 
						|
								
							 | 
						|
									if len(run.Arguments) > numInnerArgs {
							 | 
						|
										command += " " + strings.Join(quoteAll(run.Arguments[numInnerArgs:]), " ")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return []string{command}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// RunAll is a concrete build instruction for declaring multiple Run
							 | 
						|
								// instructions that will be executed together in a `cmd1 && cmd2` chain.
							 | 
						|
								//
							 | 
						|
								type RunAll struct {
							 | 
						|
									Runs []Run // multiple Run instructions to be executed together
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Compile concatenates all individually compiled Run instructions into a
							 | 
						|
								// single command.
							 | 
						|
								//
							 | 
						|
								func (runAll RunAll) Compile() []string {
							 | 
						|
									commands := make([]string, len(runAll.Runs))
							 | 
						|
								
							 | 
						|
									for i, run := range runAll.Runs {
							 | 
						|
										commands[i] = run.Compile()[0]
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return []string{strings.Join(commands, " && ")}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Copy is a concrete build instruction for copying source files/directories
							 | 
						|
								// from the build host into the image.
							 | 
						|
								//
							 | 
						|
								type Copy struct {
							 | 
						|
									Sources     []string // source file/directory paths
							 | 
						|
									Destination string   // destination path
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Compile quotes the defined source files/directories and destination.
							 | 
						|
								//
							 | 
						|
								func (copy Copy) Compile() []string {
							 | 
						|
									dest := copy.Destination
							 | 
						|
								
							 | 
						|
									// If there is more than 1 file being copied, the destination must be a
							 | 
						|
									// directory ending with "/"
							 | 
						|
									if len(copy.Sources) > 1 && !strings.HasSuffix(copy.Destination, "/") {
							 | 
						|
										dest = dest + "/"
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return append(quoteAll(copy.Sources), quote(dest))
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// CopyAs is a concrete build instruction for copying source
							 | 
						|
								// files/directories and setting their ownership to the given UID/GID.
							 | 
						|
								//
							 | 
						|
								// While it can technically wrap any build.Instruction, it is meant to be used
							 | 
						|
								// with build.Copy and build.CopyFrom to enforce file/directory ownership.
							 | 
						|
								//
							 | 
						|
								type CopyAs struct {
							 | 
						|
									UID uint // owner UID
							 | 
						|
									GID uint // owner GID
							 | 
						|
									Instruction
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Compile returns the variant name unquoted and all quoted CopyAs instruction
							 | 
						|
								// fields.
							 | 
						|
								//
							 | 
						|
								func (ca CopyAs) Compile() []string {
							 | 
						|
									return append([]string{fmt.Sprintf("%d:%d", ca.UID, ca.GID)}, ca.Instruction.Compile()...)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// CopyFrom is a concrete build instruction for copying source
							 | 
						|
								// files/directories from one variant image to another.
							 | 
						|
								//
							 | 
						|
								type CopyFrom struct {
							 | 
						|
									From string // source variant name
							 | 
						|
									Copy
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Compile returns the variant name unquoted and all quoted Copy instruction
							 | 
						|
								// fields.
							 | 
						|
								//
							 | 
						|
								func (cf CopyFrom) Compile() []string {
							 | 
						|
									return append([]string{cf.From}, cf.Copy.Compile()...)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// EntryPoint is a build instruction for declaring a container's default
							 | 
						|
								// runtime process.
							 | 
						|
								type EntryPoint struct {
							 | 
						|
									Command []string // command and arguments
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Compile returns the quoted entrypoint command and arguments.
							 | 
						|
								//
							 | 
						|
								func (ep EntryPoint) Compile() []string {
							 | 
						|
									return quoteAll(ep.Command)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Env is a concrete build instruction for declaring a container's runtime
							 | 
						|
								// environment variables.
							 | 
						|
								//
							 | 
						|
								type Env struct {
							 | 
						|
									Definitions map[string]string // number of key/value pairs
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Compile returns the key/value pairs as a number of `key="value"` strings
							 | 
						|
								// where the values are properly quoted and the slice is ordered by the keys.
							 | 
						|
								//
							 | 
						|
								func (env Env) Compile() []string {
							 | 
						|
									return compileSortedKeyValues(env.Definitions)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Label is a concrete build instruction for declaring a number of meta-data
							 | 
						|
								// key/value pairs to be included in the image.
							 | 
						|
								//
							 | 
						|
								type Label struct {
							 | 
						|
									Definitions map[string]string // number of meta-data key/value pairs
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Compile returns the key/value pairs as a number of `key="value"` strings
							 | 
						|
								// where the values are properly quoted and the slice is ordered by the keys.
							 | 
						|
								//
							 | 
						|
								func (label Label) Compile() []string {
							 | 
						|
									return compileSortedKeyValues(label.Definitions)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// User is a build instruction for setting which user will run future
							 | 
						|
								// commands.
							 | 
						|
								//
							 | 
						|
								type User struct {
							 | 
						|
									Name string // user name
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Compile returns the quoted user name.
							 | 
						|
								//
							 | 
						|
								func (user User) Compile() []string {
							 | 
						|
									return []string{quote(user.Name)}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// WorkingDirectory is a build instruction for defining the working directory
							 | 
						|
								// for future command and entrypoint instructions.
							 | 
						|
								//
							 | 
						|
								type WorkingDirectory struct {
							 | 
						|
									Path string // working directory path
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Compile returns the quoted working directory path.
							 | 
						|
								//
							 | 
						|
								func (wd WorkingDirectory) Compile() []string {
							 | 
						|
									return []string{quote(wd.Path)}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func compileSortedKeyValues(keyValues map[string]string) []string {
							 | 
						|
									defs := make([]string, 0, len(keyValues))
							 | 
						|
									names := make([]string, 0, len(keyValues))
							 | 
						|
								
							 | 
						|
									for name := range keyValues {
							 | 
						|
										names = append(names, name)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									sort.Strings(names)
							 | 
						|
								
							 | 
						|
									for _, name := range names {
							 | 
						|
										defs = append(defs, name+"="+quote(keyValues[name]))
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return defs
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func quote(arg string) string {
							 | 
						|
									return strconv.Quote(arg)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func quoteAll(arguments []string) []string {
							 | 
						|
									quoted := make([]string, len(arguments))
							 | 
						|
								
							 | 
						|
									for i, arg := range arguments {
							 | 
						|
										quoted[i] = quote(arg)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return quoted
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func sprintf(format string, arguments []string) string {
							 | 
						|
									args := make([]interface{}, len(arguments))
							 | 
						|
								
							 | 
						|
									for i, v := range arguments {
							 | 
						|
										args[i] = quote(v)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return fmt.Sprintf(format, args...)
							 | 
						|
								}
							 |