commands.go 9.1 KB

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