github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/builder/dockerfile/dispatchers.go (about) 1 package dockerfile 2 3 // This file contains the dispatchers for each command. Note that 4 // `nullDispatch` is not actually a command, but support for commands we parse 5 // but do nothing with. 6 // 7 // See evaluator.go for a higher level discussion of the whole evaluator 8 // package. 9 10 import ( 11 "fmt" 12 "os" 13 "path/filepath" 14 "regexp" 15 "runtime" 16 "sort" 17 "strings" 18 19 "github.com/Sirupsen/logrus" 20 "github.com/docker/docker/api" 21 "github.com/docker/docker/builder" 22 derr "github.com/docker/docker/errors" 23 "github.com/docker/docker/pkg/signal" 24 "github.com/docker/docker/pkg/system" 25 runconfigopts "github.com/docker/docker/runconfig/opts" 26 "github.com/docker/engine-api/types/container" 27 "github.com/docker/engine-api/types/strslice" 28 "github.com/docker/go-connections/nat" 29 ) 30 31 // dispatch with no layer / parsing. This is effectively not a command. 32 func nullDispatch(b *Builder, args []string, attributes map[string]bool, original string) error { 33 return nil 34 } 35 36 // ENV foo bar 37 // 38 // Sets the environment variable foo to bar, also makes interpolation 39 // in the dockerfile available from the next statement on via ${foo}. 40 // 41 func env(b *Builder, args []string, attributes map[string]bool, original string) error { 42 if len(args) == 0 { 43 return derr.ErrorCodeAtLeastOneArg.WithArgs("ENV") 44 } 45 46 if len(args)%2 != 0 { 47 // should never get here, but just in case 48 return derr.ErrorCodeTooManyArgs.WithArgs("ENV") 49 } 50 51 if err := b.flags.Parse(); err != nil { 52 return err 53 } 54 55 // TODO/FIXME/NOT USED 56 // Just here to show how to use the builder flags stuff within the 57 // context of a builder command. Will remove once we actually add 58 // a builder command to something! 59 /* 60 flBool1 := b.flags.AddBool("bool1", false) 61 flStr1 := b.flags.AddString("str1", "HI") 62 63 if err := b.flags.Parse(); err != nil { 64 return err 65 } 66 67 fmt.Printf("Bool1:%v\n", flBool1) 68 fmt.Printf("Str1:%v\n", flStr1) 69 */ 70 71 commitStr := "ENV" 72 73 for j := 0; j < len(args); j++ { 74 // name ==> args[j] 75 // value ==> args[j+1] 76 newVar := args[j] + "=" + args[j+1] + "" 77 commitStr += " " + newVar 78 79 gotOne := false 80 for i, envVar := range b.runConfig.Env { 81 envParts := strings.SplitN(envVar, "=", 2) 82 if envParts[0] == args[j] { 83 b.runConfig.Env[i] = newVar 84 gotOne = true 85 break 86 } 87 } 88 if !gotOne { 89 b.runConfig.Env = append(b.runConfig.Env, newVar) 90 } 91 j++ 92 } 93 94 return b.commit("", b.runConfig.Cmd, commitStr) 95 } 96 97 // MAINTAINER some text <maybe@an.email.address> 98 // 99 // Sets the maintainer metadata. 100 func maintainer(b *Builder, args []string, attributes map[string]bool, original string) error { 101 if len(args) != 1 { 102 return derr.ErrorCodeExactlyOneArg.WithArgs("MAINTAINER") 103 } 104 105 if err := b.flags.Parse(); err != nil { 106 return err 107 } 108 109 b.maintainer = args[0] 110 return b.commit("", b.runConfig.Cmd, fmt.Sprintf("MAINTAINER %s", b.maintainer)) 111 } 112 113 // LABEL some json data describing the image 114 // 115 // Sets the Label variable foo to bar, 116 // 117 func label(b *Builder, args []string, attributes map[string]bool, original string) error { 118 if len(args) == 0 { 119 return derr.ErrorCodeAtLeastOneArg.WithArgs("LABEL") 120 } 121 if len(args)%2 != 0 { 122 // should never get here, but just in case 123 return derr.ErrorCodeTooManyArgs.WithArgs("LABEL") 124 } 125 126 if err := b.flags.Parse(); err != nil { 127 return err 128 } 129 130 commitStr := "LABEL" 131 132 if b.runConfig.Labels == nil { 133 b.runConfig.Labels = map[string]string{} 134 } 135 136 for j := 0; j < len(args); j++ { 137 // name ==> args[j] 138 // value ==> args[j+1] 139 newVar := args[j] + "=" + args[j+1] + "" 140 commitStr += " " + newVar 141 142 b.runConfig.Labels[args[j]] = args[j+1] 143 j++ 144 } 145 return b.commit("", b.runConfig.Cmd, commitStr) 146 } 147 148 // ADD foo /path 149 // 150 // Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling 151 // exist here. If you do not wish to have this automatic handling, use COPY. 152 // 153 func add(b *Builder, args []string, attributes map[string]bool, original string) error { 154 if len(args) < 2 { 155 return derr.ErrorCodeAtLeastTwoArgs.WithArgs("ADD") 156 } 157 158 if err := b.flags.Parse(); err != nil { 159 return err 160 } 161 162 return b.runContextCommand(args, true, true, "ADD") 163 } 164 165 // COPY foo /path 166 // 167 // Same as 'ADD' but without the tar and remote url handling. 168 // 169 func dispatchCopy(b *Builder, args []string, attributes map[string]bool, original string) error { 170 if len(args) < 2 { 171 return derr.ErrorCodeAtLeastTwoArgs.WithArgs("COPY") 172 } 173 174 if err := b.flags.Parse(); err != nil { 175 return err 176 } 177 178 return b.runContextCommand(args, false, false, "COPY") 179 } 180 181 // FROM imagename 182 // 183 // This sets the image the dockerfile will build on top of. 184 // 185 func from(b *Builder, args []string, attributes map[string]bool, original string) error { 186 if len(args) != 1 { 187 return derr.ErrorCodeExactlyOneArg.WithArgs("FROM") 188 } 189 190 if err := b.flags.Parse(); err != nil { 191 return err 192 } 193 194 name := args[0] 195 196 var ( 197 image builder.Image 198 err error 199 ) 200 201 // Windows cannot support a container with no base image. 202 if name == api.NoBaseImageSpecifier { 203 if runtime.GOOS == "windows" { 204 return fmt.Errorf("Windows does not support FROM scratch") 205 } 206 b.image = "" 207 b.noBaseImage = true 208 } else { 209 // TODO: don't use `name`, instead resolve it to a digest 210 if !b.options.PullParent { 211 image, err = b.docker.GetImage(name) 212 // TODO: shouldn't we error out if error is different from "not found" ? 213 } 214 if image == nil { 215 image, err = b.docker.Pull(name) 216 if err != nil { 217 return err 218 } 219 } 220 } 221 222 return b.processImageFrom(image) 223 } 224 225 // ONBUILD RUN echo yo 226 // 227 // ONBUILD triggers run when the image is used in a FROM statement. 228 // 229 // ONBUILD handling has a lot of special-case functionality, the heading in 230 // evaluator.go and comments around dispatch() in the same file explain the 231 // special cases. search for 'OnBuild' in internals.go for additional special 232 // cases. 233 // 234 func onbuild(b *Builder, args []string, attributes map[string]bool, original string) error { 235 if len(args) == 0 { 236 return derr.ErrorCodeAtLeastOneArg.WithArgs("ONBUILD") 237 } 238 239 if err := b.flags.Parse(); err != nil { 240 return err 241 } 242 243 triggerInstruction := strings.ToUpper(strings.TrimSpace(args[0])) 244 switch triggerInstruction { 245 case "ONBUILD": 246 return derr.ErrorCodeChainOnBuild 247 case "MAINTAINER", "FROM": 248 return derr.ErrorCodeBadOnBuildCmd.WithArgs(triggerInstruction) 249 } 250 251 original = regexp.MustCompile(`(?i)^\s*ONBUILD\s*`).ReplaceAllString(original, "") 252 253 b.runConfig.OnBuild = append(b.runConfig.OnBuild, original) 254 return b.commit("", b.runConfig.Cmd, fmt.Sprintf("ONBUILD %s", original)) 255 } 256 257 // WORKDIR /tmp 258 // 259 // Set the working directory for future RUN/CMD/etc statements. 260 // 261 func workdir(b *Builder, args []string, attributes map[string]bool, original string) error { 262 if len(args) != 1 { 263 return derr.ErrorCodeExactlyOneArg.WithArgs("WORKDIR") 264 } 265 266 if err := b.flags.Parse(); err != nil { 267 return err 268 } 269 270 // This is from the Dockerfile and will not necessarily be in platform 271 // specific semantics, hence ensure it is converted. 272 workdir := filepath.FromSlash(args[0]) 273 274 if !system.IsAbs(workdir) { 275 current := filepath.FromSlash(b.runConfig.WorkingDir) 276 workdir = filepath.Join(string(os.PathSeparator), current, workdir) 277 } 278 279 b.runConfig.WorkingDir = workdir 280 281 return b.commit("", b.runConfig.Cmd, fmt.Sprintf("WORKDIR %v", workdir)) 282 } 283 284 // RUN some command yo 285 // 286 // run a command and commit the image. Args are automatically prepended with 287 // 'sh -c' under linux or 'cmd /S /C' under Windows, in the event there is 288 // only one argument. The difference in processing: 289 // 290 // RUN echo hi # sh -c echo hi (Linux) 291 // RUN echo hi # cmd /S /C echo hi (Windows) 292 // RUN [ "echo", "hi" ] # echo hi 293 // 294 func run(b *Builder, args []string, attributes map[string]bool, original string) error { 295 if b.image == "" && !b.noBaseImage { 296 return derr.ErrorCodeMissingFrom 297 } 298 299 if err := b.flags.Parse(); err != nil { 300 return err 301 } 302 303 args = handleJSONArgs(args, attributes) 304 305 if !attributes["json"] { 306 if runtime.GOOS != "windows" { 307 args = append([]string{"/bin/sh", "-c"}, args...) 308 } else { 309 args = append([]string{"cmd", "/S", "/C"}, args...) 310 } 311 } 312 313 config := &container.Config{ 314 Cmd: strslice.New(args...), 315 Image: b.image, 316 } 317 318 // stash the cmd 319 cmd := b.runConfig.Cmd 320 if b.runConfig.Entrypoint.Len() == 0 && b.runConfig.Cmd.Len() == 0 { 321 b.runConfig.Cmd = config.Cmd 322 } 323 324 // stash the config environment 325 env := b.runConfig.Env 326 327 defer func(cmd *strslice.StrSlice) { b.runConfig.Cmd = cmd }(cmd) 328 defer func(env []string) { b.runConfig.Env = env }(env) 329 330 // derive the net build-time environment for this run. We let config 331 // environment override the build time environment. 332 // This means that we take the b.buildArgs list of env vars and remove 333 // any of those variables that are defined as part of the container. In other 334 // words, anything in b.Config.Env. What's left is the list of build-time env 335 // vars that we need to add to each RUN command - note the list could be empty. 336 // 337 // We don't persist the build time environment with container's config 338 // environment, but just sort and prepend it to the command string at time 339 // of commit. 340 // This helps with tracing back the image's actual environment at the time 341 // of RUN, without leaking it to the final image. It also aids cache 342 // lookup for same image built with same build time environment. 343 cmdBuildEnv := []string{} 344 configEnv := runconfigopts.ConvertKVStringsToMap(b.runConfig.Env) 345 for key, val := range b.options.BuildArgs { 346 if !b.isBuildArgAllowed(key) { 347 // skip build-args that are not in allowed list, meaning they have 348 // not been defined by an "ARG" Dockerfile command yet. 349 // This is an error condition but only if there is no "ARG" in the entire 350 // Dockerfile, so we'll generate any necessary errors after we parsed 351 // the entire file (see 'leftoverArgs' processing in evaluator.go ) 352 continue 353 } 354 if _, ok := configEnv[key]; !ok { 355 cmdBuildEnv = append(cmdBuildEnv, fmt.Sprintf("%s=%s", key, val)) 356 } 357 } 358 359 // derive the command to use for probeCache() and to commit in this container. 360 // Note that we only do this if there are any build-time env vars. Also, we 361 // use the special argument "|#" at the start of the args array. This will 362 // avoid conflicts with any RUN command since commands can not 363 // start with | (vertical bar). The "#" (number of build envs) is there to 364 // help ensure proper cache matches. We don't want a RUN command 365 // that starts with "foo=abc" to be considered part of a build-time env var. 366 saveCmd := config.Cmd 367 if len(cmdBuildEnv) > 0 { 368 sort.Strings(cmdBuildEnv) 369 tmpEnv := append([]string{fmt.Sprintf("|%d", len(cmdBuildEnv))}, cmdBuildEnv...) 370 saveCmd = strslice.New(append(tmpEnv, saveCmd.Slice()...)...) 371 } 372 373 b.runConfig.Cmd = saveCmd 374 hit, err := b.probeCache() 375 if err != nil { 376 return err 377 } 378 if hit { 379 return nil 380 } 381 382 // set Cmd manually, this is special case only for Dockerfiles 383 b.runConfig.Cmd = config.Cmd 384 // set build-time environment for 'run'. 385 b.runConfig.Env = append(b.runConfig.Env, cmdBuildEnv...) 386 // set config as already being escaped, this prevents double escaping on windows 387 b.runConfig.ArgsEscaped = true 388 389 logrus.Debugf("[BUILDER] Command to be executed: %v", b.runConfig.Cmd) 390 391 cID, err := b.create() 392 if err != nil { 393 return err 394 } 395 396 if err := b.run(cID); err != nil { 397 return err 398 } 399 400 // revert to original config environment and set the command string to 401 // have the build-time env vars in it (if any) so that future cache look-ups 402 // properly match it. 403 b.runConfig.Env = env 404 b.runConfig.Cmd = saveCmd 405 return b.commit(cID, cmd, "run") 406 } 407 408 // CMD foo 409 // 410 // Set the default command to run in the container (which may be empty). 411 // Argument handling is the same as RUN. 412 // 413 func cmd(b *Builder, args []string, attributes map[string]bool, original string) error { 414 if err := b.flags.Parse(); err != nil { 415 return err 416 } 417 418 cmdSlice := handleJSONArgs(args, attributes) 419 420 if !attributes["json"] { 421 if runtime.GOOS != "windows" { 422 cmdSlice = append([]string{"/bin/sh", "-c"}, cmdSlice...) 423 } else { 424 cmdSlice = append([]string{"cmd", "/S", "/C"}, cmdSlice...) 425 } 426 } 427 428 b.runConfig.Cmd = strslice.New(cmdSlice...) 429 430 if err := b.commit("", b.runConfig.Cmd, fmt.Sprintf("CMD %q", cmdSlice)); err != nil { 431 return err 432 } 433 434 if len(args) != 0 { 435 b.cmdSet = true 436 } 437 438 return nil 439 } 440 441 // ENTRYPOINT /usr/sbin/nginx 442 // 443 // Set the entrypoint (which defaults to sh -c on linux, or cmd /S /C on Windows) to 444 // /usr/sbin/nginx. Will accept the CMD as the arguments to /usr/sbin/nginx. 445 // 446 // Handles command processing similar to CMD and RUN, only b.runConfig.Entrypoint 447 // is initialized at NewBuilder time instead of through argument parsing. 448 // 449 func entrypoint(b *Builder, args []string, attributes map[string]bool, original string) error { 450 if err := b.flags.Parse(); err != nil { 451 return err 452 } 453 454 parsed := handleJSONArgs(args, attributes) 455 456 switch { 457 case attributes["json"]: 458 // ENTRYPOINT ["echo", "hi"] 459 b.runConfig.Entrypoint = strslice.New(parsed...) 460 case len(parsed) == 0: 461 // ENTRYPOINT [] 462 b.runConfig.Entrypoint = nil 463 default: 464 // ENTRYPOINT echo hi 465 if runtime.GOOS != "windows" { 466 b.runConfig.Entrypoint = strslice.New("/bin/sh", "-c", parsed[0]) 467 } else { 468 b.runConfig.Entrypoint = strslice.New("cmd", "/S", "/C", parsed[0]) 469 } 470 } 471 472 // when setting the entrypoint if a CMD was not explicitly set then 473 // set the command to nil 474 if !b.cmdSet { 475 b.runConfig.Cmd = nil 476 } 477 478 if err := b.commit("", b.runConfig.Cmd, fmt.Sprintf("ENTRYPOINT %q", b.runConfig.Entrypoint)); err != nil { 479 return err 480 } 481 482 return nil 483 } 484 485 // EXPOSE 6667/tcp 7000/tcp 486 // 487 // Expose ports for links and port mappings. This all ends up in 488 // b.runConfig.ExposedPorts for runconfig. 489 // 490 func expose(b *Builder, args []string, attributes map[string]bool, original string) error { 491 portsTab := args 492 493 if len(args) == 0 { 494 return derr.ErrorCodeAtLeastOneArg.WithArgs("EXPOSE") 495 } 496 497 if err := b.flags.Parse(); err != nil { 498 return err 499 } 500 501 if b.runConfig.ExposedPorts == nil { 502 b.runConfig.ExposedPorts = make(nat.PortSet) 503 } 504 505 ports, _, err := nat.ParsePortSpecs(portsTab) 506 if err != nil { 507 return err 508 } 509 510 // instead of using ports directly, we build a list of ports and sort it so 511 // the order is consistent. This prevents cache burst where map ordering 512 // changes between builds 513 portList := make([]string, len(ports)) 514 var i int 515 for port := range ports { 516 if _, exists := b.runConfig.ExposedPorts[port]; !exists { 517 b.runConfig.ExposedPorts[port] = struct{}{} 518 } 519 portList[i] = string(port) 520 i++ 521 } 522 sort.Strings(portList) 523 return b.commit("", b.runConfig.Cmd, fmt.Sprintf("EXPOSE %s", strings.Join(portList, " "))) 524 } 525 526 // USER foo 527 // 528 // Set the user to 'foo' for future commands and when running the 529 // ENTRYPOINT/CMD at container run time. 530 // 531 func user(b *Builder, args []string, attributes map[string]bool, original string) error { 532 if len(args) != 1 { 533 return derr.ErrorCodeExactlyOneArg.WithArgs("USER") 534 } 535 536 if err := b.flags.Parse(); err != nil { 537 return err 538 } 539 540 b.runConfig.User = args[0] 541 return b.commit("", b.runConfig.Cmd, fmt.Sprintf("USER %v", args)) 542 } 543 544 // VOLUME /foo 545 // 546 // Expose the volume /foo for use. Will also accept the JSON array form. 547 // 548 func volume(b *Builder, args []string, attributes map[string]bool, original string) error { 549 if len(args) == 0 { 550 return derr.ErrorCodeAtLeastOneArg.WithArgs("VOLUME") 551 } 552 553 if err := b.flags.Parse(); err != nil { 554 return err 555 } 556 557 if b.runConfig.Volumes == nil { 558 b.runConfig.Volumes = map[string]struct{}{} 559 } 560 for _, v := range args { 561 v = strings.TrimSpace(v) 562 if v == "" { 563 return derr.ErrorCodeVolumeEmpty 564 } 565 b.runConfig.Volumes[v] = struct{}{} 566 } 567 if err := b.commit("", b.runConfig.Cmd, fmt.Sprintf("VOLUME %v", args)); err != nil { 568 return err 569 } 570 return nil 571 } 572 573 // STOPSIGNAL signal 574 // 575 // Set the signal that will be used to kill the container. 576 func stopSignal(b *Builder, args []string, attributes map[string]bool, original string) error { 577 if len(args) != 1 { 578 return fmt.Errorf("STOPSIGNAL requires exactly one argument") 579 } 580 581 sig := args[0] 582 _, err := signal.ParseSignal(sig) 583 if err != nil { 584 return err 585 } 586 587 b.runConfig.StopSignal = sig 588 return b.commit("", b.runConfig.Cmd, fmt.Sprintf("STOPSIGNAL %v", args)) 589 } 590 591 // ARG name[=value] 592 // 593 // Adds the variable foo to the trusted list of variables that can be passed 594 // to builder using the --build-arg flag for expansion/subsitution or passing to 'run'. 595 // Dockerfile author may optionally set a default value of this variable. 596 func arg(b *Builder, args []string, attributes map[string]bool, original string) error { 597 if len(args) != 1 { 598 return fmt.Errorf("ARG requires exactly one argument definition") 599 } 600 601 var ( 602 name string 603 value string 604 hasDefault bool 605 ) 606 607 arg := args[0] 608 // 'arg' can just be a name or name-value pair. Note that this is different 609 // from 'env' that handles the split of name and value at the parser level. 610 // The reason for doing it differently for 'arg' is that we support just 611 // defining an arg and not assign it a value (while 'env' always expects a 612 // name-value pair). If possible, it will be good to harmonize the two. 613 if strings.Contains(arg, "=") { 614 parts := strings.SplitN(arg, "=", 2) 615 name = parts[0] 616 value = parts[1] 617 hasDefault = true 618 } else { 619 name = arg 620 hasDefault = false 621 } 622 // add the arg to allowed list of build-time args from this step on. 623 b.allowedBuildArgs[name] = true 624 625 // If there is a default value associated with this arg then add it to the 626 // b.buildArgs if one is not already passed to the builder. The args passed 627 // to builder override the default value of 'arg'. 628 if _, ok := b.options.BuildArgs[name]; !ok && hasDefault { 629 b.options.BuildArgs[name] = value 630 } 631 632 return b.commit("", b.runConfig.Cmd, fmt.Sprintf("ARG %s", arg)) 633 }