github.com/lazyboychen7/engine@v17.12.1-ce-rc2+incompatible/builder/dockerfile/instructions/commands.go (about) 1 package instructions 2 3 import ( 4 "errors" 5 6 "strings" 7 8 "github.com/docker/docker/api/types/container" 9 "github.com/docker/docker/api/types/strslice" 10 ) 11 12 // KeyValuePair represent an arbitrary named value (useful in slice insted of map[string] string to preserve ordering) 13 type KeyValuePair struct { 14 Key string 15 Value string 16 } 17 18 func (kvp *KeyValuePair) String() string { 19 return kvp.Key + "=" + kvp.Value 20 } 21 22 // Command is implemented by every command present in a dockerfile 23 type Command interface { 24 Name() string 25 } 26 27 // KeyValuePairs is a slice of KeyValuePair 28 type KeyValuePairs []KeyValuePair 29 30 // withNameAndCode is the base of every command in a Dockerfile (String() returns its source code) 31 type withNameAndCode struct { 32 code string 33 name string 34 } 35 36 func (c *withNameAndCode) String() string { 37 return c.code 38 } 39 40 // Name of the command 41 func (c *withNameAndCode) Name() string { 42 return c.name 43 } 44 45 func newWithNameAndCode(req parseRequest) withNameAndCode { 46 return withNameAndCode{code: strings.TrimSpace(req.original), name: req.command} 47 } 48 49 // SingleWordExpander is a provider for variable expansion where 1 word => 1 output 50 type SingleWordExpander func(word string) (string, error) 51 52 // SupportsSingleWordExpansion interface marks a command as supporting variable expansion 53 type SupportsSingleWordExpansion interface { 54 Expand(expander SingleWordExpander) error 55 } 56 57 // PlatformSpecific adds platform checks to a command 58 type PlatformSpecific interface { 59 CheckPlatform(platform string) error 60 } 61 62 func expandKvp(kvp KeyValuePair, expander SingleWordExpander) (KeyValuePair, error) { 63 key, err := expander(kvp.Key) 64 if err != nil { 65 return KeyValuePair{}, err 66 } 67 value, err := expander(kvp.Value) 68 if err != nil { 69 return KeyValuePair{}, err 70 } 71 return KeyValuePair{Key: key, Value: value}, nil 72 } 73 func expandKvpsInPlace(kvps KeyValuePairs, expander SingleWordExpander) error { 74 for i, kvp := range kvps { 75 newKvp, err := expandKvp(kvp, expander) 76 if err != nil { 77 return err 78 } 79 kvps[i] = newKvp 80 } 81 return nil 82 } 83 84 func expandSliceInPlace(values []string, expander SingleWordExpander) error { 85 for i, v := range values { 86 newValue, err := expander(v) 87 if err != nil { 88 return err 89 } 90 values[i] = newValue 91 } 92 return nil 93 } 94 95 // EnvCommand : ENV key1 value1 [keyN valueN...] 96 type EnvCommand struct { 97 withNameAndCode 98 Env KeyValuePairs // kvp slice instead of map to preserve ordering 99 } 100 101 // Expand variables 102 func (c *EnvCommand) Expand(expander SingleWordExpander) error { 103 return expandKvpsInPlace(c.Env, expander) 104 } 105 106 // MaintainerCommand : MAINTAINER maintainer_name 107 type MaintainerCommand struct { 108 withNameAndCode 109 Maintainer string 110 } 111 112 // LabelCommand : LABEL some json data describing the image 113 // 114 // Sets the Label variable foo to bar, 115 // 116 type LabelCommand struct { 117 withNameAndCode 118 Labels KeyValuePairs // kvp slice instead of map to preserve ordering 119 } 120 121 // Expand variables 122 func (c *LabelCommand) Expand(expander SingleWordExpander) error { 123 return expandKvpsInPlace(c.Labels, expander) 124 } 125 126 // SourcesAndDest represent a list of source files and a destination 127 type SourcesAndDest []string 128 129 // Sources list the source paths 130 func (s SourcesAndDest) Sources() []string { 131 res := make([]string, len(s)-1) 132 copy(res, s[:len(s)-1]) 133 return res 134 } 135 136 // Dest path of the operation 137 func (s SourcesAndDest) Dest() string { 138 return s[len(s)-1] 139 } 140 141 // AddCommand : ADD foo /path 142 // 143 // Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling 144 // exist here. If you do not wish to have this automatic handling, use COPY. 145 // 146 type AddCommand struct { 147 withNameAndCode 148 SourcesAndDest 149 Chown string 150 } 151 152 // Expand variables 153 func (c *AddCommand) Expand(expander SingleWordExpander) error { 154 return expandSliceInPlace(c.SourcesAndDest, expander) 155 } 156 157 // CopyCommand : COPY foo /path 158 // 159 // Same as 'ADD' but without the tar and remote url handling. 160 // 161 type CopyCommand struct { 162 withNameAndCode 163 SourcesAndDest 164 From string 165 Chown string 166 } 167 168 // Expand variables 169 func (c *CopyCommand) Expand(expander SingleWordExpander) error { 170 return expandSliceInPlace(c.SourcesAndDest, expander) 171 } 172 173 // OnbuildCommand : ONBUILD <some other command> 174 type OnbuildCommand struct { 175 withNameAndCode 176 Expression string 177 } 178 179 // WorkdirCommand : WORKDIR /tmp 180 // 181 // Set the working directory for future RUN/CMD/etc statements. 182 // 183 type WorkdirCommand struct { 184 withNameAndCode 185 Path string 186 } 187 188 // Expand variables 189 func (c *WorkdirCommand) Expand(expander SingleWordExpander) error { 190 p, err := expander(c.Path) 191 if err != nil { 192 return err 193 } 194 c.Path = p 195 return nil 196 } 197 198 // ShellDependantCmdLine represents a cmdline optionaly prepended with the shell 199 type ShellDependantCmdLine struct { 200 CmdLine strslice.StrSlice 201 PrependShell bool 202 } 203 204 // RunCommand : RUN some command yo 205 // 206 // run a command and commit the image. Args are automatically prepended with 207 // the current SHELL which defaults to 'sh -c' under linux or 'cmd /S /C' under 208 // Windows, in the event there is only one argument The difference in processing: 209 // 210 // RUN echo hi # sh -c echo hi (Linux) 211 // RUN echo hi # cmd /S /C echo hi (Windows) 212 // RUN [ "echo", "hi" ] # echo hi 213 // 214 type RunCommand struct { 215 withNameAndCode 216 ShellDependantCmdLine 217 } 218 219 // CmdCommand : CMD foo 220 // 221 // Set the default command to run in the container (which may be empty). 222 // Argument handling is the same as RUN. 223 // 224 type CmdCommand struct { 225 withNameAndCode 226 ShellDependantCmdLine 227 } 228 229 // HealthCheckCommand : HEALTHCHECK foo 230 // 231 // Set the default healthcheck command to run in the container (which may be empty). 232 // Argument handling is the same as RUN. 233 // 234 type HealthCheckCommand struct { 235 withNameAndCode 236 Health *container.HealthConfig 237 } 238 239 // EntrypointCommand : ENTRYPOINT /usr/sbin/nginx 240 // 241 // Set the entrypoint to /usr/sbin/nginx. Will accept the CMD as the arguments 242 // to /usr/sbin/nginx. Uses the default shell if not in JSON format. 243 // 244 // Handles command processing similar to CMD and RUN, only req.runConfig.Entrypoint 245 // is initialized at newBuilder time instead of through argument parsing. 246 // 247 type EntrypointCommand struct { 248 withNameAndCode 249 ShellDependantCmdLine 250 } 251 252 // ExposeCommand : EXPOSE 6667/tcp 7000/tcp 253 // 254 // Expose ports for links and port mappings. This all ends up in 255 // req.runConfig.ExposedPorts for runconfig. 256 // 257 type ExposeCommand struct { 258 withNameAndCode 259 Ports []string 260 } 261 262 // UserCommand : USER foo 263 // 264 // Set the user to 'foo' for future commands and when running the 265 // ENTRYPOINT/CMD at container run time. 266 // 267 type UserCommand struct { 268 withNameAndCode 269 User string 270 } 271 272 // Expand variables 273 func (c *UserCommand) Expand(expander SingleWordExpander) error { 274 p, err := expander(c.User) 275 if err != nil { 276 return err 277 } 278 c.User = p 279 return nil 280 } 281 282 // VolumeCommand : VOLUME /foo 283 // 284 // Expose the volume /foo for use. Will also accept the JSON array form. 285 // 286 type VolumeCommand struct { 287 withNameAndCode 288 Volumes []string 289 } 290 291 // Expand variables 292 func (c *VolumeCommand) Expand(expander SingleWordExpander) error { 293 return expandSliceInPlace(c.Volumes, expander) 294 } 295 296 // StopSignalCommand : STOPSIGNAL signal 297 // 298 // Set the signal that will be used to kill the container. 299 type StopSignalCommand struct { 300 withNameAndCode 301 Signal string 302 } 303 304 // Expand variables 305 func (c *StopSignalCommand) Expand(expander SingleWordExpander) error { 306 p, err := expander(c.Signal) 307 if err != nil { 308 return err 309 } 310 c.Signal = p 311 return nil 312 } 313 314 // CheckPlatform checks that the command is supported in the target platform 315 func (c *StopSignalCommand) CheckPlatform(platform string) error { 316 if platform == "windows" { 317 return errors.New("The daemon on this platform does not support the command stopsignal") 318 } 319 return nil 320 } 321 322 // ArgCommand : ARG name[=value] 323 // 324 // Adds the variable foo to the trusted list of variables that can be passed 325 // to builder using the --build-arg flag for expansion/substitution or passing to 'run'. 326 // Dockerfile author may optionally set a default value of this variable. 327 type ArgCommand struct { 328 withNameAndCode 329 Key string 330 Value *string 331 } 332 333 // Expand variables 334 func (c *ArgCommand) Expand(expander SingleWordExpander) error { 335 p, err := expander(c.Key) 336 if err != nil { 337 return err 338 } 339 c.Key = p 340 if c.Value != nil { 341 p, err = expander(*c.Value) 342 if err != nil { 343 return err 344 } 345 c.Value = &p 346 } 347 return nil 348 } 349 350 // ShellCommand : SHELL powershell -command 351 // 352 // Set the non-default shell to use. 353 type ShellCommand struct { 354 withNameAndCode 355 Shell strslice.StrSlice 356 } 357 358 // Stage represents a single stage in a multi-stage build 359 type Stage struct { 360 Name string 361 Commands []Command 362 BaseName string 363 SourceCode string 364 } 365 366 // AddCommand to the stage 367 func (s *Stage) AddCommand(cmd Command) { 368 // todo: validate cmd type 369 s.Commands = append(s.Commands, cmd) 370 } 371 372 // IsCurrentStage check if the stage name is the current stage 373 func IsCurrentStage(s []Stage, name string) bool { 374 if len(s) == 0 { 375 return false 376 } 377 return s[len(s)-1].Name == name 378 } 379 380 // CurrentStage return the last stage in a slice 381 func CurrentStage(s []Stage) (*Stage, error) { 382 if len(s) == 0 { 383 return nil, errors.New("No build stage in current context") 384 } 385 return &s[len(s)-1], nil 386 } 387 388 // HasStage looks for the presence of a given stage name 389 func HasStage(s []Stage, name string) (int, bool) { 390 for i, stage := range s { 391 if stage.Name == name { 392 return i, true 393 } 394 } 395 return -1, false 396 }