github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/builder/dockerfile/internals.go (about) 1 package dockerfile 2 3 // internals for handling commands. Covers many areas and a lot of 4 // non-contiguous functionality. Please read the comments. 5 6 import ( 7 "crypto/sha256" 8 "encoding/hex" 9 "fmt" 10 "strings" 11 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/api/types/backend" 14 "github.com/docker/docker/api/types/container" 15 "github.com/docker/docker/pkg/stringid" 16 "github.com/pkg/errors" 17 ) 18 19 func (b *Builder) commit(dispatchState *dispatchState, comment string) error { 20 if b.disableCommit { 21 return nil 22 } 23 if !dispatchState.hasFromImage() { 24 return errors.New("Please provide a source image with `from` prior to commit") 25 } 26 27 runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment)) 28 hit, err := b.probeCache(dispatchState, runConfigWithCommentCmd) 29 if err != nil || hit { 30 return err 31 } 32 id, err := b.create(runConfigWithCommentCmd) 33 if err != nil { 34 return err 35 } 36 37 return b.commitContainer(dispatchState, id, runConfigWithCommentCmd) 38 } 39 40 // TODO: see if any args can be dropped 41 func (b *Builder) commitContainer(dispatchState *dispatchState, id string, containerConfig *container.Config) error { 42 if b.disableCommit { 43 return nil 44 } 45 46 commitCfg := &backend.ContainerCommitConfig{ 47 ContainerCommitConfig: types.ContainerCommitConfig{ 48 Author: dispatchState.maintainer, 49 Pause: true, 50 // TODO: this should be done by Commit() 51 Config: copyRunConfig(dispatchState.runConfig), 52 }, 53 ContainerConfig: containerConfig, 54 } 55 56 // Commit the container 57 imageID, err := b.docker.Commit(id, commitCfg) 58 if err != nil { 59 return err 60 } 61 62 dispatchState.imageID = imageID 63 b.buildStages.update(imageID, dispatchState.runConfig) 64 return nil 65 } 66 67 func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error { 68 srcHash := getSourceHashFromInfos(inst.infos) 69 70 // TODO: should this have been using origPaths instead of srcHash in the comment? 71 runConfigWithCommentCmd := copyRunConfig( 72 state.runConfig, 73 withCmdCommentString(fmt.Sprintf("%s %s in %s ", inst.cmdName, srcHash, inst.dest))) 74 containerID, err := b.probeAndCreate(state, runConfigWithCommentCmd) 75 if err != nil || containerID == "" { 76 return err 77 } 78 79 // Twiddle the destination when it's a relative path - meaning, make it 80 // relative to the WORKINGDIR 81 dest, err := normaliseDest(inst.cmdName, state.runConfig.WorkingDir, inst.dest) 82 if err != nil { 83 return err 84 } 85 86 for _, info := range inst.infos { 87 if err := b.docker.CopyOnBuild(containerID, dest, info.root, info.path, inst.allowLocalDecompression); err != nil { 88 return err 89 } 90 } 91 return b.commitContainer(state, containerID, runConfigWithCommentCmd) 92 } 93 94 // For backwards compat, if there's just one info then use it as the 95 // cache look-up string, otherwise hash 'em all into one 96 func getSourceHashFromInfos(infos []copyInfo) string { 97 if len(infos) == 1 { 98 return infos[0].hash 99 } 100 var hashs []string 101 for _, info := range infos { 102 hashs = append(hashs, info.hash) 103 } 104 return hashStringSlice("multi", hashs) 105 } 106 107 func hashStringSlice(prefix string, slice []string) string { 108 hasher := sha256.New() 109 hasher.Write([]byte(strings.Join(slice, ","))) 110 return prefix + ":" + hex.EncodeToString(hasher.Sum(nil)) 111 } 112 113 type runConfigModifier func(*container.Config) 114 115 func copyRunConfig(runConfig *container.Config, modifiers ...runConfigModifier) *container.Config { 116 copy := *runConfig 117 for _, modifier := range modifiers { 118 modifier(©) 119 } 120 return © 121 } 122 123 func withCmd(cmd []string) runConfigModifier { 124 return func(runConfig *container.Config) { 125 runConfig.Cmd = cmd 126 } 127 } 128 129 // withCmdComment sets Cmd to a nop comment string. See withCmdCommentString for 130 // why there are two almost identical versions of this. 131 func withCmdComment(comment string) runConfigModifier { 132 return func(runConfig *container.Config) { 133 runConfig.Cmd = append(getShell(runConfig), "#(nop) ", comment) 134 } 135 } 136 137 // withCmdCommentString exists to maintain compatibility with older versions. 138 // A few instructions (workdir, copy, add) used a nop comment that is a single arg 139 // where as all the other instructions used a two arg comment string. This 140 // function implements the single arg version. 141 func withCmdCommentString(comment string) runConfigModifier { 142 return func(runConfig *container.Config) { 143 runConfig.Cmd = append(getShell(runConfig), "#(nop) "+comment) 144 } 145 } 146 147 func withEnv(env []string) runConfigModifier { 148 return func(runConfig *container.Config) { 149 runConfig.Env = env 150 } 151 } 152 153 // withEntrypointOverride sets an entrypoint on runConfig if the command is 154 // not empty. The entrypoint is left unmodified if command is empty. 155 // 156 // The dockerfile RUN instruction expect to run without an entrypoint 157 // so the runConfig entrypoint needs to be modified accordingly. ContainerCreate 158 // will change a []string{""} entrypoint to nil, so we probe the cache with the 159 // nil entrypoint. 160 func withEntrypointOverride(cmd []string, entrypoint []string) runConfigModifier { 161 return func(runConfig *container.Config) { 162 if len(cmd) > 0 { 163 runConfig.Entrypoint = entrypoint 164 } 165 } 166 } 167 168 // getShell is a helper function which gets the right shell for prefixing the 169 // shell-form of RUN, ENTRYPOINT and CMD instructions 170 func getShell(c *container.Config) []string { 171 if 0 == len(c.Shell) { 172 return append([]string{}, defaultShell[:]...) 173 } 174 return append([]string{}, c.Shell[:]...) 175 } 176 177 func (b *Builder) probeCache(dispatchState *dispatchState, runConfig *container.Config) (bool, error) { 178 cachedID, err := b.imageProber.Probe(dispatchState.imageID, runConfig) 179 if cachedID == "" || err != nil { 180 return false, err 181 } 182 fmt.Fprint(b.Stdout, " ---> Using cache\n") 183 184 dispatchState.imageID = string(cachedID) 185 b.buildStages.update(dispatchState.imageID, runConfig) 186 return true, nil 187 } 188 189 var defaultLogConfig = container.LogConfig{Type: "none"} 190 191 func (b *Builder) probeAndCreate(dispatchState *dispatchState, runConfig *container.Config) (string, error) { 192 if hit, err := b.probeCache(dispatchState, runConfig); err != nil || hit { 193 return "", err 194 } 195 // Set a log config to override any default value set on the daemon 196 hostConfig := &container.HostConfig{LogConfig: defaultLogConfig} 197 container, err := b.containerManager.Create(runConfig, hostConfig) 198 return container.ID, err 199 } 200 201 func (b *Builder) create(runConfig *container.Config) (string, error) { 202 hostConfig := hostConfigFromOptions(b.options) 203 container, err := b.containerManager.Create(runConfig, hostConfig) 204 if err != nil { 205 return "", err 206 } 207 // TODO: could this be moved into containerManager.Create() ? 208 for _, warning := range container.Warnings { 209 fmt.Fprintf(b.Stdout, " ---> [Warning] %s\n", warning) 210 } 211 fmt.Fprintf(b.Stdout, " ---> Running in %s\n", stringid.TruncateID(container.ID)) 212 return container.ID, nil 213 } 214 215 func hostConfigFromOptions(options *types.ImageBuildOptions) *container.HostConfig { 216 resources := container.Resources{ 217 CgroupParent: options.CgroupParent, 218 CPUShares: options.CPUShares, 219 CPUPeriod: options.CPUPeriod, 220 CPUQuota: options.CPUQuota, 221 CpusetCpus: options.CPUSetCPUs, 222 CpusetMems: options.CPUSetMems, 223 Memory: options.Memory, 224 MemorySwap: options.MemorySwap, 225 Ulimits: options.Ulimits, 226 } 227 228 return &container.HostConfig{ 229 SecurityOpt: options.SecurityOpt, 230 Isolation: options.Isolation, 231 ShmSize: options.ShmSize, 232 Resources: resources, 233 NetworkMode: container.NetworkMode(options.NetworkMode), 234 // Set a log config to override any default value set on the daemon 235 LogConfig: defaultLogConfig, 236 ExtraHosts: options.ExtraHosts, 237 } 238 }