commands.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  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. // NewLabelCommand creates a new 'LABEL' command
  94. func NewLabelCommand(k string, v string, NoExp bool) *LabelCommand {
  95. kvp := KeyValuePair{Key: k, Value: v}
  96. c := "LABEL "
  97. c += kvp.String()
  98. nc := withNameAndCode{code: c, name: "label"}
  99. cmd := &LabelCommand{
  100. withNameAndCode: nc,
  101. Labels: KeyValuePairs{
  102. kvp,
  103. },
  104. noExpand: NoExp,
  105. }
  106. return cmd
  107. }
  108. // LabelCommand : LABEL some json data describing the image
  109. //
  110. // Sets the Label variable foo to bar,
  111. //
  112. type LabelCommand struct {
  113. withNameAndCode
  114. Labels KeyValuePairs // kvp slice instead of map to preserve ordering
  115. noExpand bool
  116. }
  117. // Expand variables
  118. func (c *LabelCommand) Expand(expander SingleWordExpander) error {
  119. if c.noExpand {
  120. return nil
  121. }
  122. return expandKvpsInPlace(c.Labels, expander)
  123. }
  124. // SourcesAndDest represent a list of source files and a destination
  125. type SourcesAndDest []string
  126. // Sources list the source paths
  127. func (s SourcesAndDest) Sources() []string {
  128. res := make([]string, len(s)-1)
  129. copy(res, s[:len(s)-1])
  130. return res
  131. }
  132. // Dest path of the operation
  133. func (s SourcesAndDest) Dest() string {
  134. return s[len(s)-1]
  135. }
  136. // AddCommand : ADD foo /path
  137. //
  138. // Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling
  139. // exist here. If you do not wish to have this automatic handling, use COPY.
  140. //
  141. type AddCommand struct {
  142. withNameAndCode
  143. SourcesAndDest
  144. Chown string
  145. }
  146. // Expand variables
  147. func (c *AddCommand) Expand(expander SingleWordExpander) error {
  148. return expandSliceInPlace(c.SourcesAndDest, expander)
  149. }
  150. // CopyCommand : COPY foo /path
  151. //
  152. // Same as 'ADD' but without the tar and remote url handling.
  153. //
  154. type CopyCommand struct {
  155. withNameAndCode
  156. SourcesAndDest
  157. From string
  158. Chown string
  159. }
  160. // Expand variables
  161. func (c *CopyCommand) Expand(expander SingleWordExpander) error {
  162. return expandSliceInPlace(c.SourcesAndDest, expander)
  163. }
  164. // OnbuildCommand : ONBUILD <some other command>
  165. type OnbuildCommand struct {
  166. withNameAndCode
  167. Expression string
  168. }
  169. // WorkdirCommand : WORKDIR /tmp
  170. //
  171. // Set the working directory for future RUN/CMD/etc statements.
  172. //
  173. type WorkdirCommand struct {
  174. withNameAndCode
  175. Path string
  176. }
  177. // Expand variables
  178. func (c *WorkdirCommand) Expand(expander SingleWordExpander) error {
  179. p, err := expander(c.Path)
  180. if err != nil {
  181. return err
  182. }
  183. c.Path = p
  184. return nil
  185. }
  186. // ShellDependantCmdLine represents a cmdline optionally prepended with the shell
  187. type ShellDependantCmdLine struct {
  188. CmdLine strslice.StrSlice
  189. PrependShell bool
  190. }
  191. // RunCommand : RUN some command yo
  192. //
  193. // run a command and commit the image. Args are automatically prepended with
  194. // the current SHELL which defaults to 'sh -c' under linux or 'cmd /S /C' under
  195. // Windows, in the event there is only one argument The difference in processing:
  196. //
  197. // RUN echo hi # sh -c echo hi (Linux)
  198. // RUN echo hi # cmd /S /C echo hi (Windows)
  199. // RUN [ "echo", "hi" ] # echo hi
  200. //
  201. type RunCommand struct {
  202. withNameAndCode
  203. ShellDependantCmdLine
  204. }
  205. // CmdCommand : CMD foo
  206. //
  207. // Set the default command to run in the container (which may be empty).
  208. // Argument handling is the same as RUN.
  209. //
  210. type CmdCommand struct {
  211. withNameAndCode
  212. ShellDependantCmdLine
  213. }
  214. // HealthCheckCommand : HEALTHCHECK foo
  215. //
  216. // Set the default healthcheck command to run in the container (which may be empty).
  217. // Argument handling is the same as RUN.
  218. //
  219. type HealthCheckCommand struct {
  220. withNameAndCode
  221. Health *container.HealthConfig
  222. }
  223. // EntrypointCommand : ENTRYPOINT /usr/sbin/nginx
  224. //
  225. // Set the entrypoint to /usr/sbin/nginx. Will accept the CMD as the arguments
  226. // to /usr/sbin/nginx. Uses the default shell if not in JSON format.
  227. //
  228. // Handles command processing similar to CMD and RUN, only req.runConfig.Entrypoint
  229. // is initialized at newBuilder time instead of through argument parsing.
  230. //
  231. type EntrypointCommand struct {
  232. withNameAndCode
  233. ShellDependantCmdLine
  234. }
  235. // ExposeCommand : EXPOSE 6667/tcp 7000/tcp
  236. //
  237. // Expose ports for links and port mappings. This all ends up in
  238. // req.runConfig.ExposedPorts for runconfig.
  239. //
  240. type ExposeCommand struct {
  241. withNameAndCode
  242. Ports []string
  243. }
  244. // UserCommand : USER foo
  245. //
  246. // Set the user to 'foo' for future commands and when running the
  247. // ENTRYPOINT/CMD at container run time.
  248. //
  249. type UserCommand struct {
  250. withNameAndCode
  251. User string
  252. }
  253. // Expand variables
  254. func (c *UserCommand) Expand(expander SingleWordExpander) error {
  255. p, err := expander(c.User)
  256. if err != nil {
  257. return err
  258. }
  259. c.User = p
  260. return nil
  261. }
  262. // VolumeCommand : VOLUME /foo
  263. //
  264. // Expose the volume /foo for use. Will also accept the JSON array form.
  265. //
  266. type VolumeCommand struct {
  267. withNameAndCode
  268. Volumes []string
  269. }
  270. // Expand variables
  271. func (c *VolumeCommand) Expand(expander SingleWordExpander) error {
  272. return expandSliceInPlace(c.Volumes, expander)
  273. }
  274. // StopSignalCommand : STOPSIGNAL signal
  275. //
  276. // Set the signal that will be used to kill the container.
  277. type StopSignalCommand struct {
  278. withNameAndCode
  279. Signal string
  280. }
  281. // Expand variables
  282. func (c *StopSignalCommand) Expand(expander SingleWordExpander) error {
  283. p, err := expander(c.Signal)
  284. if err != nil {
  285. return err
  286. }
  287. c.Signal = p
  288. return nil
  289. }
  290. // CheckPlatform checks that the command is supported in the target platform
  291. func (c *StopSignalCommand) CheckPlatform(platform string) error {
  292. if platform == "windows" {
  293. return errors.New("The daemon on this platform does not support the command stopsignal")
  294. }
  295. return nil
  296. }
  297. // ArgCommand : ARG name[=value]
  298. //
  299. // Adds the variable foo to the trusted list of variables that can be passed
  300. // to builder using the --build-arg flag for expansion/substitution or passing to 'run'.
  301. // Dockerfile author may optionally set a default value of this variable.
  302. type ArgCommand struct {
  303. withNameAndCode
  304. Key string
  305. Value *string
  306. }
  307. // Expand variables
  308. func (c *ArgCommand) Expand(expander SingleWordExpander) error {
  309. p, err := expander(c.Key)
  310. if err != nil {
  311. return err
  312. }
  313. c.Key = p
  314. if c.Value != nil {
  315. p, err = expander(*c.Value)
  316. if err != nil {
  317. return err
  318. }
  319. c.Value = &p
  320. }
  321. return nil
  322. }
  323. // ShellCommand : SHELL powershell -command
  324. //
  325. // Set the non-default shell to use.
  326. type ShellCommand struct {
  327. withNameAndCode
  328. Shell strslice.StrSlice
  329. }
  330. // Stage represents a single stage in a multi-stage build
  331. type Stage struct {
  332. Name string
  333. Commands []Command
  334. BaseName string
  335. SourceCode string
  336. Platform specs.Platform
  337. }
  338. // AddCommand to the stage
  339. func (s *Stage) AddCommand(cmd Command) {
  340. // todo: validate cmd type
  341. s.Commands = append(s.Commands, cmd)
  342. }
  343. // IsCurrentStage check if the stage name is the current stage
  344. func IsCurrentStage(s []Stage, name string) bool {
  345. if len(s) == 0 {
  346. return false
  347. }
  348. return s[len(s)-1].Name == name
  349. }
  350. // CurrentStage return the last stage in a slice
  351. func CurrentStage(s []Stage) (*Stage, error) {
  352. if len(s) == 0 {
  353. return nil, errors.New("No build stage in current context")
  354. }
  355. return &s[len(s)-1], nil
  356. }
  357. // HasStage looks for the presence of a given stage name
  358. func HasStage(s []Stage, name string) (int, bool) {
  359. for i, stage := range s {
  360. // Stage name is case-insensitive by design
  361. if strings.EqualFold(stage.Name, name) {
  362. return i, true
  363. }
  364. }
  365. return -1, false
  366. }