evaluator.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // Package dockerfile is the evaluation step in the Dockerfile parse/evaluate pipeline.
  2. //
  3. // It incorporates a dispatch table based on the parser.Node values (see the
  4. // parser package for more information) that are yielded from the parser itself.
  5. // Calling newBuilder with the BuildOpts struct can be used to customize the
  6. // experience for execution purposes only. Parsing is controlled in the parser
  7. // package, and this division of responsibility should be respected.
  8. //
  9. // Please see the jump table targets for the actual invocations, most of which
  10. // will call out to the functions in internals.go to deal with their tasks.
  11. //
  12. // ONBUILD is a special case, which is covered in the onbuild() func in
  13. // dispatchers.go.
  14. //
  15. // The evaluator uses the concept of "steps", which are usually each processable
  16. // line in the Dockerfile. Each step is numbered and certain actions are taken
  17. // before and after each step, such as creating an image ID and removing temporary
  18. // containers and images. Note that ONBUILD creates a kinda-sorta "sub run" which
  19. // includes its own set of steps (usually only one of them).
  20. package dockerfile // import "github.com/docker/docker/builder/dockerfile"
  21. import (
  22. "context"
  23. "reflect"
  24. "strconv"
  25. "strings"
  26. "github.com/docker/docker/api/types/container"
  27. "github.com/docker/docker/builder"
  28. "github.com/docker/docker/errdefs"
  29. "github.com/docker/docker/image"
  30. "github.com/docker/docker/oci"
  31. "github.com/docker/docker/runconfig/opts"
  32. "github.com/moby/buildkit/frontend/dockerfile/instructions"
  33. "github.com/moby/buildkit/frontend/dockerfile/shell"
  34. "github.com/pkg/errors"
  35. )
  36. func dispatch(ctx context.Context, d dispatchRequest, cmd instructions.Command) (err error) {
  37. if c, ok := cmd.(instructions.PlatformSpecific); ok {
  38. err := c.CheckPlatform(d.state.operatingSystem)
  39. if err != nil {
  40. return errdefs.InvalidParameter(err)
  41. }
  42. }
  43. runConfigEnv := d.state.runConfig.Env
  44. envs := append(runConfigEnv, d.state.buildArgs.FilterAllowed(runConfigEnv)...)
  45. if ex, ok := cmd.(instructions.SupportsSingleWordExpansion); ok {
  46. err := ex.Expand(func(word string) (string, error) {
  47. return d.shlex.ProcessWord(word, envs)
  48. })
  49. if err != nil {
  50. return errdefs.InvalidParameter(err)
  51. }
  52. }
  53. defer func() {
  54. if d.builder.options.ForceRemove {
  55. d.builder.containerManager.RemoveAll(d.builder.Stdout)
  56. return
  57. }
  58. if d.builder.options.Remove && err == nil {
  59. d.builder.containerManager.RemoveAll(d.builder.Stdout)
  60. return
  61. }
  62. }()
  63. switch c := cmd.(type) {
  64. case *instructions.EnvCommand:
  65. return dispatchEnv(ctx, d, c)
  66. case *instructions.MaintainerCommand:
  67. return dispatchMaintainer(ctx, d, c)
  68. case *instructions.LabelCommand:
  69. return dispatchLabel(ctx, d, c)
  70. case *instructions.AddCommand:
  71. return dispatchAdd(ctx, d, c)
  72. case *instructions.CopyCommand:
  73. return dispatchCopy(ctx, d, c)
  74. case *instructions.OnbuildCommand:
  75. return dispatchOnbuild(ctx, d, c)
  76. case *instructions.WorkdirCommand:
  77. return dispatchWorkdir(ctx, d, c)
  78. case *instructions.RunCommand:
  79. return dispatchRun(ctx, d, c)
  80. case *instructions.CmdCommand:
  81. return dispatchCmd(ctx, d, c)
  82. case *instructions.HealthCheckCommand:
  83. return dispatchHealthcheck(ctx, d, c)
  84. case *instructions.EntrypointCommand:
  85. return dispatchEntrypoint(ctx, d, c)
  86. case *instructions.ExposeCommand:
  87. return dispatchExpose(ctx, d, c, envs)
  88. case *instructions.UserCommand:
  89. return dispatchUser(ctx, d, c)
  90. case *instructions.VolumeCommand:
  91. return dispatchVolume(ctx, d, c)
  92. case *instructions.StopSignalCommand:
  93. return dispatchStopSignal(ctx, d, c)
  94. case *instructions.ArgCommand:
  95. return dispatchArg(ctx, d, c)
  96. case *instructions.ShellCommand:
  97. return dispatchShell(ctx, d, c)
  98. }
  99. return errors.Errorf("unsupported command type: %v", reflect.TypeOf(cmd))
  100. }
  101. // dispatchState is a data object which is modified by dispatchers
  102. type dispatchState struct {
  103. runConfig *container.Config
  104. maintainer string
  105. cmdSet bool
  106. imageID string
  107. baseImage builder.Image
  108. stageName string
  109. buildArgs *BuildArgs
  110. operatingSystem string
  111. }
  112. func newDispatchState(baseArgs *BuildArgs) *dispatchState {
  113. args := baseArgs.Clone()
  114. args.ResetAllowed()
  115. return &dispatchState{runConfig: &container.Config{}, buildArgs: args}
  116. }
  117. type stagesBuildResults struct {
  118. flat []*container.Config
  119. indexed map[string]*container.Config
  120. }
  121. func newStagesBuildResults() *stagesBuildResults {
  122. return &stagesBuildResults{
  123. indexed: make(map[string]*container.Config),
  124. }
  125. }
  126. func (r *stagesBuildResults) getByName(name string) (*container.Config, bool) {
  127. c, ok := r.indexed[strings.ToLower(name)]
  128. return c, ok
  129. }
  130. func (r *stagesBuildResults) validateIndex(i int) error {
  131. if i == len(r.flat) {
  132. return errors.New("refers to current build stage")
  133. }
  134. if i < 0 || i > len(r.flat) {
  135. return errors.New("index out of bounds")
  136. }
  137. return nil
  138. }
  139. func (r *stagesBuildResults) get(nameOrIndex string) (*container.Config, error) {
  140. if c, ok := r.getByName(nameOrIndex); ok {
  141. return c, nil
  142. }
  143. ix, err := strconv.ParseInt(nameOrIndex, 10, 0)
  144. if err != nil {
  145. return nil, nil
  146. }
  147. if err := r.validateIndex(int(ix)); err != nil {
  148. return nil, err
  149. }
  150. return r.flat[ix], nil
  151. }
  152. func (r *stagesBuildResults) checkStageNameAvailable(name string) error {
  153. if name != "" {
  154. if _, ok := r.getByName(name); ok {
  155. return errors.Errorf("%s stage name already used", name)
  156. }
  157. }
  158. return nil
  159. }
  160. func (r *stagesBuildResults) commitStage(name string, config *container.Config) error {
  161. if name != "" {
  162. if _, ok := r.getByName(name); ok {
  163. return errors.Errorf("%s stage name already used", name)
  164. }
  165. r.indexed[strings.ToLower(name)] = config
  166. }
  167. r.flat = append(r.flat, config)
  168. return nil
  169. }
  170. func commitStage(state *dispatchState, stages *stagesBuildResults) error {
  171. return stages.commitStage(state.stageName, state.runConfig)
  172. }
  173. type dispatchRequest struct {
  174. state *dispatchState
  175. shlex *shell.Lex
  176. builder *Builder
  177. source builder.Source
  178. stages *stagesBuildResults
  179. }
  180. func newDispatchRequest(builder *Builder, escapeToken rune, source builder.Source, buildArgs *BuildArgs, stages *stagesBuildResults) dispatchRequest {
  181. return dispatchRequest{
  182. state: newDispatchState(buildArgs),
  183. shlex: shell.NewLex(escapeToken),
  184. builder: builder,
  185. source: source,
  186. stages: stages,
  187. }
  188. }
  189. func (s *dispatchState) updateRunConfig() {
  190. s.runConfig.Image = s.imageID
  191. }
  192. // hasFromImage returns true if the builder has processed a `FROM <image>` line
  193. func (s *dispatchState) hasFromImage() bool {
  194. return s.imageID != "" || (s.baseImage != nil && s.baseImage.ImageID() == "")
  195. }
  196. func (s *dispatchState) beginStage(stageName string, img builder.Image) error {
  197. s.stageName = stageName
  198. s.imageID = img.ImageID()
  199. s.operatingSystem = img.OperatingSystem()
  200. if err := image.CheckOS(s.operatingSystem); err != nil {
  201. return err
  202. }
  203. if img.RunConfig() != nil {
  204. // copy avoids referencing the same instance when 2 stages have the same base
  205. s.runConfig = copyRunConfig(img.RunConfig())
  206. } else {
  207. s.runConfig = &container.Config{}
  208. }
  209. s.baseImage = img
  210. s.setDefaultPath()
  211. s.runConfig.OpenStdin = false
  212. s.runConfig.StdinOnce = false
  213. return nil
  214. }
  215. // Add the default PATH to runConfig.ENV if one exists for the operating system and there
  216. // is no PATH set. Note that Windows containers on Windows won't have one as it's set by HCS
  217. func (s *dispatchState) setDefaultPath() {
  218. // TODO(thaJeztah): use github.com/moby/buildkit/util/system.DefaultPathEnv() once https://github.com/moby/buildkit/pull/3158 is resolved.
  219. defaultPath := oci.DefaultPathEnv(s.operatingSystem)
  220. if defaultPath == "" {
  221. return
  222. }
  223. envMap := opts.ConvertKVStringsToMap(s.runConfig.Env)
  224. if _, ok := envMap["PATH"]; !ok {
  225. s.runConfig.Env = append(s.runConfig.Env, "PATH="+defaultPath)
  226. }
  227. }