commands.go 9.4 KB

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