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  }