dispatchers.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. package builder
  2. // This file contains the dispatchers for each command. Note that
  3. // `nullDispatch` is not actually a command, but support for commands we parse
  4. // but do nothing with.
  5. //
  6. // See evaluator.go for a higher level discussion of the whole evaluator
  7. // package.
  8. import (
  9. "fmt"
  10. "io/ioutil"
  11. "path/filepath"
  12. "strings"
  13. "github.com/docker/docker/nat"
  14. "github.com/docker/docker/pkg/log"
  15. flag "github.com/docker/docker/pkg/mflag"
  16. "github.com/docker/docker/runconfig"
  17. )
  18. // dispatch with no layer / parsing. This is effectively not a command.
  19. func nullDispatch(b *Builder, args []string, attributes map[string]bool) error {
  20. return nil
  21. }
  22. // ENV foo bar
  23. //
  24. // Sets the environment variable foo to bar, also makes interpolation
  25. // in the dockerfile available from the next statement on via ${foo}.
  26. //
  27. func env(b *Builder, args []string, attributes map[string]bool) error {
  28. if len(args) != 2 {
  29. return fmt.Errorf("ENV accepts two arguments")
  30. }
  31. fullEnv := fmt.Sprintf("%s=%s", args[0], args[1])
  32. for i, envVar := range b.Config.Env {
  33. envParts := strings.SplitN(envVar, "=", 2)
  34. if args[0] == envParts[0] {
  35. b.Config.Env[i] = fullEnv
  36. return b.commit("", b.Config.Cmd, fmt.Sprintf("ENV %s", fullEnv))
  37. }
  38. }
  39. b.Config.Env = append(b.Config.Env, fullEnv)
  40. return b.commit("", b.Config.Cmd, fmt.Sprintf("ENV %s", fullEnv))
  41. }
  42. // MAINTAINER some text <maybe@an.email.address>
  43. //
  44. // Sets the maintainer metadata.
  45. func maintainer(b *Builder, args []string, attributes map[string]bool) error {
  46. if len(args) != 1 {
  47. return fmt.Errorf("MAINTAINER requires only one argument")
  48. }
  49. b.maintainer = args[0]
  50. return b.commit("", b.Config.Cmd, fmt.Sprintf("MAINTAINER %s", b.maintainer))
  51. }
  52. // ADD foo /path
  53. //
  54. // Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling
  55. // exist here. If you do not wish to have this automatic handling, use COPY.
  56. //
  57. func add(b *Builder, args []string, attributes map[string]bool) error {
  58. if len(args) != 2 {
  59. return fmt.Errorf("ADD requires two arguments")
  60. }
  61. return b.runContextCommand(args, true, true, "ADD")
  62. }
  63. // COPY foo /path
  64. //
  65. // Same as 'ADD' but without the tar and remote url handling.
  66. //
  67. func dispatchCopy(b *Builder, args []string, attributes map[string]bool) error {
  68. if len(args) != 2 {
  69. return fmt.Errorf("COPY requires two arguments")
  70. }
  71. return b.runContextCommand(args, false, false, "COPY")
  72. }
  73. // FROM imagename
  74. //
  75. // This sets the image the dockerfile will build on top of.
  76. //
  77. func from(b *Builder, args []string, attributes map[string]bool) error {
  78. if len(args) != 1 {
  79. return fmt.Errorf("FROM requires one argument")
  80. }
  81. name := args[0]
  82. image, err := b.Daemon.Repositories().LookupImage(name)
  83. if err != nil {
  84. if b.Daemon.Graph().IsNotExist(err) {
  85. image, err = b.pullImage(name)
  86. }
  87. // note that the top level err will still be !nil here if IsNotExist is
  88. // not the error. This approach just simplifies hte logic a bit.
  89. if err != nil {
  90. return err
  91. }
  92. }
  93. return b.processImageFrom(image)
  94. }
  95. // ONBUILD RUN echo yo
  96. //
  97. // ONBUILD triggers run when the image is used in a FROM statement.
  98. //
  99. // ONBUILD handling has a lot of special-case functionality, the heading in
  100. // evaluator.go and comments around dispatch() in the same file explain the
  101. // special cases. search for 'OnBuild' in internals.go for additional special
  102. // cases.
  103. //
  104. func onbuild(b *Builder, args []string, attributes map[string]bool) error {
  105. triggerInstruction := strings.ToUpper(strings.TrimSpace(args[0]))
  106. switch triggerInstruction {
  107. case "ONBUILD":
  108. return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
  109. case "MAINTAINER", "FROM":
  110. return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction)
  111. }
  112. trigger := strings.Join(args, " ")
  113. b.Config.OnBuild = append(b.Config.OnBuild, trigger)
  114. return b.commit("", b.Config.Cmd, fmt.Sprintf("ONBUILD %s", trigger))
  115. }
  116. // WORKDIR /tmp
  117. //
  118. // Set the working directory for future RUN/CMD/etc statements.
  119. //
  120. func workdir(b *Builder, args []string, attributes map[string]bool) error {
  121. if len(args) != 1 {
  122. return fmt.Errorf("WORKDIR requires exactly one argument")
  123. }
  124. workdir := args[0]
  125. if workdir[0] == '/' {
  126. b.Config.WorkingDir = workdir
  127. } else {
  128. if b.Config.WorkingDir == "" {
  129. b.Config.WorkingDir = "/"
  130. }
  131. b.Config.WorkingDir = filepath.Join(b.Config.WorkingDir, workdir)
  132. }
  133. return b.commit("", b.Config.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
  134. }
  135. // RUN some command yo
  136. //
  137. // run a command and commit the image. Args are automatically prepended with
  138. // 'sh -c' in the event there is only one argument. The difference in
  139. // processing:
  140. //
  141. // RUN echo hi # sh -c echo hi
  142. // RUN [ "echo", "hi" ] # echo hi
  143. //
  144. func run(b *Builder, args []string, attributes map[string]bool) error {
  145. if b.image == "" {
  146. return fmt.Errorf("Please provide a source image with `from` prior to run")
  147. }
  148. args = handleJsonArgs(args, attributes)
  149. if len(args) == 1 {
  150. args = append([]string{"/bin/sh", "-c"}, args[0])
  151. }
  152. runCmd := flag.NewFlagSet("run", flag.ContinueOnError)
  153. runCmd.SetOutput(ioutil.Discard)
  154. runCmd.Usage = nil
  155. config, _, _, err := runconfig.Parse(runCmd, append([]string{b.image}, args...), nil)
  156. if err != nil {
  157. return err
  158. }
  159. cmd := b.Config.Cmd
  160. // set Cmd manually, this is special case only for Dockerfiles
  161. b.Config.Cmd = config.Cmd
  162. runconfig.Merge(b.Config, config)
  163. defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
  164. log.Debugf("Command to be executed: %v", b.Config.Cmd)
  165. hit, err := b.probeCache()
  166. if err != nil {
  167. return err
  168. }
  169. if hit {
  170. return nil
  171. }
  172. c, err := b.create()
  173. if err != nil {
  174. return err
  175. }
  176. // Ensure that we keep the container mounted until the commit
  177. // to avoid unmounting and then mounting directly again
  178. c.Mount()
  179. defer c.Unmount()
  180. err = b.run(c)
  181. if err != nil {
  182. return err
  183. }
  184. if err := b.commit(c.ID, cmd, "run"); err != nil {
  185. return err
  186. }
  187. return nil
  188. }
  189. // CMD foo
  190. //
  191. // Set the default command to run in the container (which may be empty).
  192. // Argument handling is the same as RUN.
  193. //
  194. func cmd(b *Builder, args []string, attributes map[string]bool) error {
  195. b.Config.Cmd = handleJsonArgs(args, attributes)
  196. if !attributes["json"] && len(b.Config.Entrypoint) == 0 {
  197. b.Config.Entrypoint = []string{"/bin/sh", "-c"}
  198. }
  199. if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %v", b.Config.Cmd)); err != nil {
  200. return err
  201. }
  202. if len(args) != 0 {
  203. b.cmdSet = true
  204. }
  205. return nil
  206. }
  207. // ENTRYPOINT /usr/sbin/nginx
  208. //
  209. // Set the entrypoint (which defaults to sh -c) to /usr/sbin/nginx. Will
  210. // accept the CMD as the arguments to /usr/sbin/nginx.
  211. //
  212. // Handles command processing similar to CMD and RUN, only b.Config.Entrypoint
  213. // is initialized at NewBuilder time instead of through argument parsing.
  214. //
  215. func entrypoint(b *Builder, args []string, attributes map[string]bool) error {
  216. b.Config.Entrypoint = handleJsonArgs(args, attributes)
  217. if len(b.Config.Entrypoint) == 0 && len(b.Config.Cmd) == 0 {
  218. b.Config.Entrypoint = []string{"/bin/sh", "-c"}
  219. } else if !b.cmdSet {
  220. b.Config.Cmd = nil
  221. }
  222. if err := b.commit("", b.Config.Cmd, fmt.Sprintf("ENTRYPOINT %v", b.Config.Entrypoint)); err != nil {
  223. return err
  224. }
  225. return nil
  226. }
  227. // EXPOSE 6667/tcp 7000/tcp
  228. //
  229. // Expose ports for links and port mappings. This all ends up in
  230. // b.Config.ExposedPorts for runconfig.
  231. //
  232. func expose(b *Builder, args []string, attributes map[string]bool) error {
  233. portsTab := args
  234. if b.Config.ExposedPorts == nil {
  235. b.Config.ExposedPorts = make(nat.PortSet)
  236. }
  237. ports, _, err := nat.ParsePortSpecs(append(portsTab, b.Config.PortSpecs...))
  238. if err != nil {
  239. return err
  240. }
  241. for port := range ports {
  242. if _, exists := b.Config.ExposedPorts[port]; !exists {
  243. b.Config.ExposedPorts[port] = struct{}{}
  244. }
  245. }
  246. b.Config.PortSpecs = nil
  247. return b.commit("", b.Config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
  248. }
  249. // USER foo
  250. //
  251. // Set the user to 'foo' for future commands and when running the
  252. // ENTRYPOINT/CMD at container run time.
  253. //
  254. func user(b *Builder, args []string, attributes map[string]bool) error {
  255. if len(args) != 1 {
  256. return fmt.Errorf("USER requires exactly one argument")
  257. }
  258. b.Config.User = args[0]
  259. return b.commit("", b.Config.Cmd, fmt.Sprintf("USER %v", args))
  260. }
  261. // VOLUME /foo
  262. //
  263. // Expose the volume /foo for use. Will also accept the JSON form, but either
  264. // way requires exactly one argument.
  265. //
  266. func volume(b *Builder, args []string, attributes map[string]bool) error {
  267. if len(args) != 1 {
  268. return fmt.Errorf("Volume cannot be empty")
  269. }
  270. volume := args
  271. if b.Config.Volumes == nil {
  272. b.Config.Volumes = map[string]struct{}{}
  273. }
  274. for _, v := range volume {
  275. b.Config.Volumes[v] = struct{}{}
  276. }
  277. if err := b.commit("", b.Config.Cmd, fmt.Sprintf("VOLUME %s", args)); err != nil {
  278. return err
  279. }
  280. return nil
  281. }
  282. // INSERT is no longer accepted, but we still parse it.
  283. func insert(b *Builder, args []string, attributes map[string]bool) error {
  284. return fmt.Errorf("INSERT has been deprecated. Please use ADD instead")
  285. }