github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/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  	"errors"
    12  	"fmt"
    13  	"regexp"
    14  	"runtime"
    15  	"sort"
    16  	"strconv"
    17  	"strings"
    18  	"time"
    19  
    20  	"github.com/Sirupsen/logrus"
    21  	"github.com/docker/docker/api"
    22  	"github.com/docker/docker/api/types"
    23  	"github.com/docker/docker/api/types/container"
    24  	"github.com/docker/docker/api/types/strslice"
    25  	"github.com/docker/docker/builder"
    26  	"github.com/docker/docker/pkg/signal"
    27  	runconfigopts "github.com/docker/docker/runconfig/opts"
    28  	"github.com/docker/go-connections/nat"
    29  )
    30  
    31  // ENV foo bar
    32  //
    33  // Sets the environment variable foo to bar, also makes interpolation
    34  // in the dockerfile available from the next statement on via ${foo}.
    35  //
    36  func env(b *Builder, args []string, attributes map[string]bool, original string) error {
    37  	if len(args) == 0 {
    38  		return errAtLeastOneArgument("ENV")
    39  	}
    40  
    41  	if len(args)%2 != 0 {
    42  		// should never get here, but just in case
    43  		return errTooManyArguments("ENV")
    44  	}
    45  
    46  	if err := b.flags.Parse(); err != nil {
    47  		return err
    48  	}
    49  
    50  	// TODO/FIXME/NOT USED
    51  	// Just here to show how to use the builder flags stuff within the
    52  	// context of a builder command. Will remove once we actually add
    53  	// a builder command to something!
    54  	/*
    55  		flBool1 := b.flags.AddBool("bool1", false)
    56  		flStr1 := b.flags.AddString("str1", "HI")
    57  
    58  		if err := b.flags.Parse(); err != nil {
    59  			return err
    60  		}
    61  
    62  		fmt.Printf("Bool1:%v\n", flBool1)
    63  		fmt.Printf("Str1:%v\n", flStr1)
    64  	*/
    65  
    66  	commitStr := "ENV"
    67  
    68  	for j := 0; j < len(args); j++ {
    69  		// name  ==> args[j]
    70  		// value ==> args[j+1]
    71  
    72  		if len(args[j]) == 0 {
    73  			return errBlankCommandNames("ENV")
    74  		}
    75  		newVar := args[j] + "=" + args[j+1] + ""
    76  		commitStr += " " + newVar
    77  
    78  		gotOne := false
    79  		for i, envVar := range b.runConfig.Env {
    80  			envParts := strings.SplitN(envVar, "=", 2)
    81  			compareFrom := envParts[0]
    82  			compareTo := args[j]
    83  			if runtime.GOOS == "windows" {
    84  				// Case insensitive environment variables on Windows
    85  				compareFrom = strings.ToUpper(compareFrom)
    86  				compareTo = strings.ToUpper(compareTo)
    87  			}
    88  			if compareFrom == compareTo {
    89  				b.runConfig.Env[i] = newVar
    90  				gotOne = true
    91  				break
    92  			}
    93  		}
    94  		if !gotOne {
    95  			b.runConfig.Env = append(b.runConfig.Env, newVar)
    96  		}
    97  		j++
    98  	}
    99  
   100  	return b.commit("", b.runConfig.Cmd, commitStr)
   101  }
   102  
   103  // MAINTAINER some text <maybe@an.email.address>
   104  //
   105  // Sets the maintainer metadata.
   106  func maintainer(b *Builder, args []string, attributes map[string]bool, original string) error {
   107  	if len(args) != 1 {
   108  		return errExactlyOneArgument("MAINTAINER")
   109  	}
   110  
   111  	if err := b.flags.Parse(); err != nil {
   112  		return err
   113  	}
   114  
   115  	b.maintainer = args[0]
   116  	return b.commit("", b.runConfig.Cmd, fmt.Sprintf("MAINTAINER %s", b.maintainer))
   117  }
   118  
   119  // LABEL some json data describing the image
   120  //
   121  // Sets the Label variable foo to bar,
   122  //
   123  func label(b *Builder, args []string, attributes map[string]bool, original string) error {
   124  	if len(args) == 0 {
   125  		return errAtLeastOneArgument("LABEL")
   126  	}
   127  	if len(args)%2 != 0 {
   128  		// should never get here, but just in case
   129  		return errTooManyArguments("LABEL")
   130  	}
   131  
   132  	if err := b.flags.Parse(); err != nil {
   133  		return err
   134  	}
   135  
   136  	commitStr := "LABEL"
   137  
   138  	if b.runConfig.Labels == nil {
   139  		b.runConfig.Labels = map[string]string{}
   140  	}
   141  
   142  	for j := 0; j < len(args); j++ {
   143  		// name  ==> args[j]
   144  		// value ==> args[j+1]
   145  
   146  		if len(args[j]) == 0 {
   147  			return errBlankCommandNames("LABEL")
   148  		}
   149  
   150  		newVar := args[j] + "=" + args[j+1] + ""
   151  		commitStr += " " + newVar
   152  
   153  		b.runConfig.Labels[args[j]] = args[j+1]
   154  		j++
   155  	}
   156  	return b.commit("", b.runConfig.Cmd, commitStr)
   157  }
   158  
   159  // ADD foo /path
   160  //
   161  // Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling
   162  // exist here. If you do not wish to have this automatic handling, use COPY.
   163  //
   164  func add(b *Builder, args []string, attributes map[string]bool, original string) error {
   165  	if len(args) < 2 {
   166  		return errAtLeastTwoArguments("ADD")
   167  	}
   168  
   169  	if err := b.flags.Parse(); err != nil {
   170  		return err
   171  	}
   172  
   173  	return b.runContextCommand(args, true, true, "ADD")
   174  }
   175  
   176  // COPY foo /path
   177  //
   178  // Same as 'ADD' but without the tar and remote url handling.
   179  //
   180  func dispatchCopy(b *Builder, args []string, attributes map[string]bool, original string) error {
   181  	if len(args) < 2 {
   182  		return errAtLeastTwoArguments("COPY")
   183  	}
   184  
   185  	if err := b.flags.Parse(); err != nil {
   186  		return err
   187  	}
   188  
   189  	return b.runContextCommand(args, false, false, "COPY")
   190  }
   191  
   192  // FROM imagename
   193  //
   194  // This sets the image the dockerfile will build on top of.
   195  //
   196  func from(b *Builder, args []string, attributes map[string]bool, original string) error {
   197  	if len(args) != 1 {
   198  		return errExactlyOneArgument("FROM")
   199  	}
   200  
   201  	if err := b.flags.Parse(); err != nil {
   202  		return err
   203  	}
   204  
   205  	name := args[0]
   206  
   207  	var image builder.Image
   208  
   209  	// Windows cannot support a container with no base image.
   210  	if name == api.NoBaseImageSpecifier {
   211  		if runtime.GOOS == "windows" {
   212  			return errors.New("Windows does not support FROM scratch")
   213  		}
   214  		b.image = ""
   215  		b.noBaseImage = true
   216  	} else {
   217  		// TODO: don't use `name`, instead resolve it to a digest
   218  		if !b.options.PullParent {
   219  			image, _ = b.docker.GetImageOnBuild(name)
   220  			// TODO: shouldn't we error out if error is different from "not found" ?
   221  		}
   222  		if image == nil {
   223  			var err error
   224  			image, err = b.docker.PullOnBuild(b.clientCtx, name, b.options.AuthConfigs, b.Output)
   225  			if err != nil {
   226  				return err
   227  			}
   228  		}
   229  	}
   230  	b.from = image
   231  
   232  	return b.processImageFrom(image)
   233  }
   234  
   235  // ONBUILD RUN echo yo
   236  //
   237  // ONBUILD triggers run when the image is used in a FROM statement.
   238  //
   239  // ONBUILD handling has a lot of special-case functionality, the heading in
   240  // evaluator.go and comments around dispatch() in the same file explain the
   241  // special cases. search for 'OnBuild' in internals.go for additional special
   242  // cases.
   243  //
   244  func onbuild(b *Builder, args []string, attributes map[string]bool, original string) error {
   245  	if len(args) == 0 {
   246  		return errAtLeastOneArgument("ONBUILD")
   247  	}
   248  
   249  	if err := b.flags.Parse(); err != nil {
   250  		return err
   251  	}
   252  
   253  	triggerInstruction := strings.ToUpper(strings.TrimSpace(args[0]))
   254  	switch triggerInstruction {
   255  	case "ONBUILD":
   256  		return errors.New("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
   257  	case "MAINTAINER", "FROM":
   258  		return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction)
   259  	}
   260  
   261  	original = regexp.MustCompile(`(?i)^\s*ONBUILD\s*`).ReplaceAllString(original, "")
   262  
   263  	b.runConfig.OnBuild = append(b.runConfig.OnBuild, original)
   264  	return b.commit("", b.runConfig.Cmd, fmt.Sprintf("ONBUILD %s", original))
   265  }
   266  
   267  // WORKDIR /tmp
   268  //
   269  // Set the working directory for future RUN/CMD/etc statements.
   270  //
   271  func workdir(b *Builder, args []string, attributes map[string]bool, original string) error {
   272  	if len(args) != 1 {
   273  		return errExactlyOneArgument("WORKDIR")
   274  	}
   275  
   276  	err := b.flags.Parse()
   277  	if err != nil {
   278  		return err
   279  	}
   280  
   281  	// This is from the Dockerfile and will not necessarily be in platform
   282  	// specific semantics, hence ensure it is converted.
   283  	b.runConfig.WorkingDir, err = normaliseWorkdir(b.runConfig.WorkingDir, args[0])
   284  	if err != nil {
   285  		return err
   286  	}
   287  
   288  	// For performance reasons, we explicitly do a create/mkdir now
   289  	// This avoids having an unnecessary expensive mount/unmount calls
   290  	// (on Windows in particular) during each container create.
   291  	// Prior to 1.13, the mkdir was deferred and not executed at this step.
   292  	if b.disableCommit {
   293  		// Don't call back into the daemon if we're going through docker commit --change "WORKDIR /foo".
   294  		// We've already updated the runConfig and that's enough.
   295  		return nil
   296  	}
   297  	b.runConfig.Image = b.image
   298  
   299  	cmd := b.runConfig.Cmd
   300  	comment := "WORKDIR " + b.runConfig.WorkingDir
   301  	// reset the command for cache detection
   302  	b.runConfig.Cmd = strslice.StrSlice(append(getShell(b.runConfig), "#(nop) "+comment))
   303  	defer func(cmd strslice.StrSlice) { b.runConfig.Cmd = cmd }(cmd)
   304  
   305  	if hit, err := b.probeCache(); err != nil {
   306  		return err
   307  	} else if hit {
   308  		return nil
   309  	}
   310  
   311  	container, err := b.docker.ContainerCreate(types.ContainerCreateConfig{
   312  		Config: b.runConfig,
   313  		// Set a log config to override any default value set on the daemon
   314  		HostConfig: &container.HostConfig{LogConfig: defaultLogConfig},
   315  	})
   316  	if err != nil {
   317  		return err
   318  	}
   319  	b.tmpContainers[container.ID] = struct{}{}
   320  	if err := b.docker.ContainerCreateWorkdir(container.ID); err != nil {
   321  		return err
   322  	}
   323  
   324  	return b.commit(container.ID, cmd, comment)
   325  }
   326  
   327  // RUN some command yo
   328  //
   329  // run a command and commit the image. Args are automatically prepended with
   330  // the current SHELL which defaults to 'sh -c' under linux or 'cmd /S /C' under
   331  // Windows, in the event there is only one argument The difference in processing:
   332  //
   333  // RUN echo hi          # sh -c echo hi       (Linux)
   334  // RUN echo hi          # cmd /S /C echo hi   (Windows)
   335  // RUN [ "echo", "hi" ] # echo hi
   336  //
   337  func run(b *Builder, args []string, attributes map[string]bool, original string) error {
   338  	if b.image == "" && !b.noBaseImage {
   339  		return errors.New("Please provide a source image with `from` prior to run")
   340  	}
   341  
   342  	if err := b.flags.Parse(); err != nil {
   343  		return err
   344  	}
   345  
   346  	args = handleJSONArgs(args, attributes)
   347  
   348  	if !attributes["json"] {
   349  		args = append(getShell(b.runConfig), args...)
   350  	}
   351  	config := &container.Config{
   352  		Cmd:   strslice.StrSlice(args),
   353  		Image: b.image,
   354  	}
   355  
   356  	// stash the cmd
   357  	cmd := b.runConfig.Cmd
   358  	if len(b.runConfig.Entrypoint) == 0 && len(b.runConfig.Cmd) == 0 {
   359  		b.runConfig.Cmd = config.Cmd
   360  	}
   361  
   362  	// stash the config environment
   363  	env := b.runConfig.Env
   364  
   365  	defer func(cmd strslice.StrSlice) { b.runConfig.Cmd = cmd }(cmd)
   366  	defer func(env []string) { b.runConfig.Env = env }(env)
   367  
   368  	// derive the net build-time environment for this run. We let config
   369  	// environment override the build time environment.
   370  	// This means that we take the b.buildArgs list of env vars and remove
   371  	// any of those variables that are defined as part of the container. In other
   372  	// words, anything in b.Config.Env. What's left is the list of build-time env
   373  	// vars that we need to add to each RUN command - note the list could be empty.
   374  	//
   375  	// We don't persist the build time environment with container's config
   376  	// environment, but just sort and prepend it to the command string at time
   377  	// of commit.
   378  	// This helps with tracing back the image's actual environment at the time
   379  	// of RUN, without leaking it to the final image. It also aids cache
   380  	// lookup for same image built with same build time environment.
   381  	cmdBuildEnv := []string{}
   382  	configEnv := runconfigopts.ConvertKVStringsToMap(b.runConfig.Env)
   383  	for key, val := range b.options.BuildArgs {
   384  		if !b.isBuildArgAllowed(key) {
   385  			// skip build-args that are not in allowed list, meaning they have
   386  			// not been defined by an "ARG" Dockerfile command yet.
   387  			// This is an error condition but only if there is no "ARG" in the entire
   388  			// Dockerfile, so we'll generate any necessary errors after we parsed
   389  			// the entire file (see 'leftoverArgs' processing in evaluator.go )
   390  			continue
   391  		}
   392  		if _, ok := configEnv[key]; !ok && val != nil {
   393  			cmdBuildEnv = append(cmdBuildEnv, fmt.Sprintf("%s=%s", key, *val))
   394  		}
   395  	}
   396  
   397  	// derive the command to use for probeCache() and to commit in this container.
   398  	// Note that we only do this if there are any build-time env vars.  Also, we
   399  	// use the special argument "|#" at the start of the args array. This will
   400  	// avoid conflicts with any RUN command since commands can not
   401  	// start with | (vertical bar). The "#" (number of build envs) is there to
   402  	// help ensure proper cache matches. We don't want a RUN command
   403  	// that starts with "foo=abc" to be considered part of a build-time env var.
   404  	saveCmd := config.Cmd
   405  	if len(cmdBuildEnv) > 0 {
   406  		sort.Strings(cmdBuildEnv)
   407  		tmpEnv := append([]string{fmt.Sprintf("|%d", len(cmdBuildEnv))}, cmdBuildEnv...)
   408  		saveCmd = strslice.StrSlice(append(tmpEnv, saveCmd...))
   409  	}
   410  
   411  	b.runConfig.Cmd = saveCmd
   412  	hit, err := b.probeCache()
   413  	if err != nil {
   414  		return err
   415  	}
   416  	if hit {
   417  		return nil
   418  	}
   419  
   420  	// set Cmd manually, this is special case only for Dockerfiles
   421  	b.runConfig.Cmd = config.Cmd
   422  	// set build-time environment for 'run'.
   423  	b.runConfig.Env = append(b.runConfig.Env, cmdBuildEnv...)
   424  	// set config as already being escaped, this prevents double escaping on windows
   425  	b.runConfig.ArgsEscaped = true
   426  
   427  	logrus.Debugf("[BUILDER] Command to be executed: %v", b.runConfig.Cmd)
   428  
   429  	cID, err := b.create()
   430  	if err != nil {
   431  		return err
   432  	}
   433  
   434  	if err := b.run(cID); err != nil {
   435  		return err
   436  	}
   437  
   438  	// revert to original config environment and set the command string to
   439  	// have the build-time env vars in it (if any) so that future cache look-ups
   440  	// properly match it.
   441  	b.runConfig.Env = env
   442  	b.runConfig.Cmd = saveCmd
   443  	return b.commit(cID, cmd, "run")
   444  }
   445  
   446  // CMD foo
   447  //
   448  // Set the default command to run in the container (which may be empty).
   449  // Argument handling is the same as RUN.
   450  //
   451  func cmd(b *Builder, args []string, attributes map[string]bool, original string) error {
   452  	if err := b.flags.Parse(); err != nil {
   453  		return err
   454  	}
   455  
   456  	cmdSlice := handleJSONArgs(args, attributes)
   457  
   458  	if !attributes["json"] {
   459  		cmdSlice = append(getShell(b.runConfig), cmdSlice...)
   460  	}
   461  
   462  	b.runConfig.Cmd = strslice.StrSlice(cmdSlice)
   463  	// set config as already being escaped, this prevents double escaping on windows
   464  	b.runConfig.ArgsEscaped = true
   465  
   466  	if err := b.commit("", b.runConfig.Cmd, fmt.Sprintf("CMD %q", cmdSlice)); err != nil {
   467  		return err
   468  	}
   469  
   470  	if len(args) != 0 {
   471  		b.cmdSet = true
   472  	}
   473  
   474  	return nil
   475  }
   476  
   477  // parseOptInterval(flag) is the duration of flag.Value, or 0 if
   478  // empty. An error is reported if the value is given and is not positive.
   479  func parseOptInterval(f *Flag) (time.Duration, error) {
   480  	s := f.Value
   481  	if s == "" {
   482  		return 0, nil
   483  	}
   484  	d, err := time.ParseDuration(s)
   485  	if err != nil {
   486  		return 0, err
   487  	}
   488  	if d <= 0 {
   489  		return 0, fmt.Errorf("Interval %#v must be positive", f.name)
   490  	}
   491  	return d, nil
   492  }
   493  
   494  // HEALTHCHECK foo
   495  //
   496  // Set the default healthcheck command to run in the container (which may be empty).
   497  // Argument handling is the same as RUN.
   498  //
   499  func healthcheck(b *Builder, args []string, attributes map[string]bool, original string) error {
   500  	if len(args) == 0 {
   501  		return errAtLeastOneArgument("HEALTHCHECK")
   502  	}
   503  	typ := strings.ToUpper(args[0])
   504  	args = args[1:]
   505  	if typ == "NONE" {
   506  		if len(args) != 0 {
   507  			return errors.New("HEALTHCHECK NONE takes no arguments")
   508  		}
   509  		test := strslice.StrSlice{typ}
   510  		b.runConfig.Healthcheck = &container.HealthConfig{
   511  			Test: test,
   512  		}
   513  	} else {
   514  		if b.runConfig.Healthcheck != nil {
   515  			oldCmd := b.runConfig.Healthcheck.Test
   516  			if len(oldCmd) > 0 && oldCmd[0] != "NONE" {
   517  				fmt.Fprintf(b.Stdout, "Note: overriding previous HEALTHCHECK: %v\n", oldCmd)
   518  			}
   519  		}
   520  
   521  		healthcheck := container.HealthConfig{}
   522  
   523  		flInterval := b.flags.AddString("interval", "")
   524  		flTimeout := b.flags.AddString("timeout", "")
   525  		flRetries := b.flags.AddString("retries", "")
   526  
   527  		if err := b.flags.Parse(); err != nil {
   528  			return err
   529  		}
   530  
   531  		switch typ {
   532  		case "CMD":
   533  			cmdSlice := handleJSONArgs(args, attributes)
   534  			if len(cmdSlice) == 0 {
   535  				return errors.New("Missing command after HEALTHCHECK CMD")
   536  			}
   537  
   538  			if !attributes["json"] {
   539  				typ = "CMD-SHELL"
   540  			}
   541  
   542  			healthcheck.Test = strslice.StrSlice(append([]string{typ}, cmdSlice...))
   543  		default:
   544  			return fmt.Errorf("Unknown type %#v in HEALTHCHECK (try CMD)", typ)
   545  		}
   546  
   547  		interval, err := parseOptInterval(flInterval)
   548  		if err != nil {
   549  			return err
   550  		}
   551  		healthcheck.Interval = interval
   552  
   553  		timeout, err := parseOptInterval(flTimeout)
   554  		if err != nil {
   555  			return err
   556  		}
   557  		healthcheck.Timeout = timeout
   558  
   559  		if flRetries.Value != "" {
   560  			retries, err := strconv.ParseInt(flRetries.Value, 10, 32)
   561  			if err != nil {
   562  				return err
   563  			}
   564  			if retries < 1 {
   565  				return fmt.Errorf("--retries must be at least 1 (not %d)", retries)
   566  			}
   567  			healthcheck.Retries = int(retries)
   568  		} else {
   569  			healthcheck.Retries = 0
   570  		}
   571  
   572  		b.runConfig.Healthcheck = &healthcheck
   573  	}
   574  
   575  	return b.commit("", b.runConfig.Cmd, fmt.Sprintf("HEALTHCHECK %q", b.runConfig.Healthcheck))
   576  }
   577  
   578  // ENTRYPOINT /usr/sbin/nginx
   579  //
   580  // Set the entrypoint to /usr/sbin/nginx. Will accept the CMD as the arguments
   581  // to /usr/sbin/nginx. Uses the default shell if not in JSON format.
   582  //
   583  // Handles command processing similar to CMD and RUN, only b.runConfig.Entrypoint
   584  // is initialized at NewBuilder time instead of through argument parsing.
   585  //
   586  func entrypoint(b *Builder, args []string, attributes map[string]bool, original string) error {
   587  	if err := b.flags.Parse(); err != nil {
   588  		return err
   589  	}
   590  
   591  	parsed := handleJSONArgs(args, attributes)
   592  
   593  	switch {
   594  	case attributes["json"]:
   595  		// ENTRYPOINT ["echo", "hi"]
   596  		b.runConfig.Entrypoint = strslice.StrSlice(parsed)
   597  	case len(parsed) == 0:
   598  		// ENTRYPOINT []
   599  		b.runConfig.Entrypoint = nil
   600  	default:
   601  		// ENTRYPOINT echo hi
   602  		b.runConfig.Entrypoint = strslice.StrSlice(append(getShell(b.runConfig), parsed[0]))
   603  	}
   604  
   605  	// when setting the entrypoint if a CMD was not explicitly set then
   606  	// set the command to nil
   607  	if !b.cmdSet {
   608  		b.runConfig.Cmd = nil
   609  	}
   610  
   611  	if err := b.commit("", b.runConfig.Cmd, fmt.Sprintf("ENTRYPOINT %q", b.runConfig.Entrypoint)); err != nil {
   612  		return err
   613  	}
   614  
   615  	return nil
   616  }
   617  
   618  // EXPOSE 6667/tcp 7000/tcp
   619  //
   620  // Expose ports for links and port mappings. This all ends up in
   621  // b.runConfig.ExposedPorts for runconfig.
   622  //
   623  func expose(b *Builder, args []string, attributes map[string]bool, original string) error {
   624  	portsTab := args
   625  
   626  	if len(args) == 0 {
   627  		return errAtLeastOneArgument("EXPOSE")
   628  	}
   629  
   630  	if err := b.flags.Parse(); err != nil {
   631  		return err
   632  	}
   633  
   634  	if b.runConfig.ExposedPorts == nil {
   635  		b.runConfig.ExposedPorts = make(nat.PortSet)
   636  	}
   637  
   638  	ports, _, err := nat.ParsePortSpecs(portsTab)
   639  	if err != nil {
   640  		return err
   641  	}
   642  
   643  	// instead of using ports directly, we build a list of ports and sort it so
   644  	// the order is consistent. This prevents cache burst where map ordering
   645  	// changes between builds
   646  	portList := make([]string, len(ports))
   647  	var i int
   648  	for port := range ports {
   649  		if _, exists := b.runConfig.ExposedPorts[port]; !exists {
   650  			b.runConfig.ExposedPorts[port] = struct{}{}
   651  		}
   652  		portList[i] = string(port)
   653  		i++
   654  	}
   655  	sort.Strings(portList)
   656  	return b.commit("", b.runConfig.Cmd, fmt.Sprintf("EXPOSE %s", strings.Join(portList, " ")))
   657  }
   658  
   659  // USER foo
   660  //
   661  // Set the user to 'foo' for future commands and when running the
   662  // ENTRYPOINT/CMD at container run time.
   663  //
   664  func user(b *Builder, args []string, attributes map[string]bool, original string) error {
   665  	if len(args) != 1 {
   666  		return errExactlyOneArgument("USER")
   667  	}
   668  
   669  	if err := b.flags.Parse(); err != nil {
   670  		return err
   671  	}
   672  
   673  	b.runConfig.User = args[0]
   674  	return b.commit("", b.runConfig.Cmd, fmt.Sprintf("USER %v", args))
   675  }
   676  
   677  // VOLUME /foo
   678  //
   679  // Expose the volume /foo for use. Will also accept the JSON array form.
   680  //
   681  func volume(b *Builder, args []string, attributes map[string]bool, original string) error {
   682  	if len(args) == 0 {
   683  		return errAtLeastOneArgument("VOLUME")
   684  	}
   685  
   686  	if err := b.flags.Parse(); err != nil {
   687  		return err
   688  	}
   689  
   690  	if b.runConfig.Volumes == nil {
   691  		b.runConfig.Volumes = map[string]struct{}{}
   692  	}
   693  	for _, v := range args {
   694  		v = strings.TrimSpace(v)
   695  		if v == "" {
   696  			return errors.New("VOLUME specified can not be an empty string")
   697  		}
   698  		b.runConfig.Volumes[v] = struct{}{}
   699  	}
   700  	if err := b.commit("", b.runConfig.Cmd, fmt.Sprintf("VOLUME %v", args)); err != nil {
   701  		return err
   702  	}
   703  	return nil
   704  }
   705  
   706  // STOPSIGNAL signal
   707  //
   708  // Set the signal that will be used to kill the container.
   709  func stopSignal(b *Builder, args []string, attributes map[string]bool, original string) error {
   710  	if len(args) != 1 {
   711  		return errExactlyOneArgument("STOPSIGNAL")
   712  	}
   713  
   714  	sig := args[0]
   715  	_, err := signal.ParseSignal(sig)
   716  	if err != nil {
   717  		return err
   718  	}
   719  
   720  	b.runConfig.StopSignal = sig
   721  	return b.commit("", b.runConfig.Cmd, fmt.Sprintf("STOPSIGNAL %v", args))
   722  }
   723  
   724  // ARG name[=value]
   725  //
   726  // Adds the variable foo to the trusted list of variables that can be passed
   727  // to builder using the --build-arg flag for expansion/substitution or passing to 'run'.
   728  // Dockerfile author may optionally set a default value of this variable.
   729  func arg(b *Builder, args []string, attributes map[string]bool, original string) error {
   730  	if len(args) != 1 {
   731  		return errExactlyOneArgument("ARG")
   732  	}
   733  
   734  	var (
   735  		name       string
   736  		newValue   string
   737  		hasDefault bool
   738  	)
   739  
   740  	arg := args[0]
   741  	// 'arg' can just be a name or name-value pair. Note that this is different
   742  	// from 'env' that handles the split of name and value at the parser level.
   743  	// The reason for doing it differently for 'arg' is that we support just
   744  	// defining an arg and not assign it a value (while 'env' always expects a
   745  	// name-value pair). If possible, it will be good to harmonize the two.
   746  	if strings.Contains(arg, "=") {
   747  		parts := strings.SplitN(arg, "=", 2)
   748  		if len(parts[0]) == 0 {
   749  			return errBlankCommandNames("ARG")
   750  		}
   751  
   752  		name = parts[0]
   753  		newValue = parts[1]
   754  		hasDefault = true
   755  	} else {
   756  		name = arg
   757  		hasDefault = false
   758  	}
   759  	// add the arg to allowed list of build-time args from this step on.
   760  	b.allowedBuildArgs[name] = true
   761  
   762  	// If there is a default value associated with this arg then add it to the
   763  	// b.buildArgs if one is not already passed to the builder. The args passed
   764  	// to builder override the default value of 'arg'. Note that a 'nil' for
   765  	// a value means that the user specified "--build-arg FOO" and "FOO" wasn't
   766  	// defined as an env var - and in that case we DO want to use the default
   767  	// value specified in the ARG cmd.
   768  	if baValue, ok := b.options.BuildArgs[name]; (!ok || baValue == nil) && hasDefault {
   769  		b.options.BuildArgs[name] = &newValue
   770  	}
   771  
   772  	return b.commit("", b.runConfig.Cmd, fmt.Sprintf("ARG %s", arg))
   773  }
   774  
   775  // SHELL powershell -command
   776  //
   777  // Set the non-default shell to use.
   778  func shell(b *Builder, args []string, attributes map[string]bool, original string) error {
   779  	if err := b.flags.Parse(); err != nil {
   780  		return err
   781  	}
   782  	shellSlice := handleJSONArgs(args, attributes)
   783  	switch {
   784  	case len(shellSlice) == 0:
   785  		// SHELL []
   786  		return errAtLeastOneArgument("SHELL")
   787  	case attributes["json"]:
   788  		// SHELL ["powershell", "-command"]
   789  		b.runConfig.Shell = strslice.StrSlice(shellSlice)
   790  	default:
   791  		// SHELL powershell -command - not JSON
   792  		return errNotJSON("SHELL", original)
   793  	}
   794  	return b.commit("", b.runConfig.Cmd, fmt.Sprintf("SHELL %v", shellSlice))
   795  }
   796  
   797  func errAtLeastOneArgument(command string) error {
   798  	return fmt.Errorf("%s requires at least one argument", command)
   799  }
   800  
   801  func errExactlyOneArgument(command string) error {
   802  	return fmt.Errorf("%s requires exactly one argument", command)
   803  }
   804  
   805  func errAtLeastTwoArguments(command string) error {
   806  	return fmt.Errorf("%s requires at least two arguments", command)
   807  }
   808  
   809  func errBlankCommandNames(command string) error {
   810  	return fmt.Errorf("%s names can not be blank", command)
   811  }
   812  
   813  func errTooManyArguments(command string) error {
   814  	return fmt.Errorf("Bad input to %s, too many arguments", command)
   815  }
   816  
   817  // getShell is a helper function which gets the right shell for prefixing the
   818  // shell-form of RUN, ENTRYPOINT and CMD instructions
   819  func getShell(c *container.Config) []string {
   820  	if 0 == len(c.Shell) {
   821  		return defaultShell[:]
   822  	}
   823  	return c.Shell[:]
   824  }