commands.go 11 KB

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