2
0

builder.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package docker
  2. import (
  3. "archive/tar"
  4. "bytes"
  5. "context"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "os"
  10. "time"
  11. "github.com/docker/docker/api/types"
  12. "github.com/docker/docker/pkg/archive"
  13. "github.com/docker/docker/pkg/fileutils"
  14. "github.com/moby/buildkit/frontend/dockerfile/dockerignore"
  15. "github.com/moby/moby/pkg/jsonmessage"
  16. "github.com/moby/moby/pkg/stringid"
  17. "github.com/moby/term"
  18. "github.com/pkg/errors"
  19. )
  20. type BuildOpts struct {
  21. ImageRepo string
  22. Tag string
  23. CurrentTag string
  24. BuildContext string
  25. DockerfilePath string
  26. IsDockerfileInCtx bool
  27. UseCache bool
  28. Env map[string]string
  29. }
  30. // BuildLocal
  31. func (a *Agent) BuildLocal(ctx context.Context, opts *BuildOpts) (err error) {
  32. dockerfilePath := opts.DockerfilePath
  33. // attempt to read dockerignore file and paths
  34. dockerIgnoreBytes, _ := ioutil.ReadFile(".dockerignore")
  35. var excludes []string
  36. if len(dockerIgnoreBytes) != 0 {
  37. excludes, err = dockerignore.ReadAll(bytes.NewBuffer(dockerIgnoreBytes))
  38. if err != nil {
  39. return err
  40. }
  41. }
  42. excludes = trimBuildFilesFromExcludes(excludes, dockerfilePath)
  43. tar, err := archive.TarWithOptions(opts.BuildContext, &archive.TarOptions{
  44. ExcludePatterns: excludes,
  45. })
  46. if err != nil {
  47. return err
  48. }
  49. if !opts.IsDockerfileInCtx {
  50. dockerfileCtx, err := os.Open(dockerfilePath)
  51. if err != nil {
  52. return errors.Errorf("unable to open Dockerfile: %v", err)
  53. }
  54. defer dockerfileCtx.Close()
  55. // add the dockerfile to the build context
  56. tar, dockerfilePath, err = AddDockerfileToBuildContext(dockerfileCtx, tar)
  57. if err != nil {
  58. return err
  59. }
  60. }
  61. buildArgs := make(map[string]*string)
  62. for key, val := range opts.Env {
  63. valCopy := val
  64. buildArgs[key] = &valCopy
  65. }
  66. // attach BUILDKIT_INLINE_CACHE=1 by default, to take advantage of caching
  67. inlineCacheVal := "1"
  68. buildArgs["BUILDKIT_INLINE_CACHE"] = &inlineCacheVal
  69. out, err := a.ImageBuild(ctx, tar, types.ImageBuildOptions{
  70. Dockerfile: dockerfilePath,
  71. BuildArgs: buildArgs,
  72. Tags: []string{
  73. fmt.Sprintf("%s:%s", opts.ImageRepo, opts.Tag),
  74. },
  75. CacheFrom: []string{
  76. fmt.Sprintf("%s:%s", opts.ImageRepo, opts.CurrentTag),
  77. },
  78. Remove: true,
  79. Platform: "linux/amd64",
  80. })
  81. if err != nil {
  82. return err
  83. }
  84. defer out.Body.Close()
  85. termFd, isTerm := term.GetFdInfo(os.Stderr)
  86. return jsonmessage.DisplayJSONMessagesStream(out.Body, os.Stderr, termFd, isTerm, nil)
  87. }
  88. func trimBuildFilesFromExcludes(excludes []string, dockerfile string) []string {
  89. if keep, _ := fileutils.Matches(".dockerignore", excludes); keep {
  90. excludes = append(excludes, "!.dockerignore")
  91. }
  92. if keep, _ := fileutils.Matches(dockerfile, excludes); keep {
  93. excludes = append(excludes, "!"+dockerfile)
  94. }
  95. return excludes
  96. }
  97. // AddDockerfileToBuildContext from a ReadCloser, returns a new archive and
  98. // the relative path to the dockerfile in the context.
  99. func AddDockerfileToBuildContext(dockerfileCtx io.ReadCloser, buildCtx io.ReadCloser) (io.ReadCloser, string, error) {
  100. file, err := ioutil.ReadAll(dockerfileCtx)
  101. dockerfileCtx.Close()
  102. if err != nil {
  103. return nil, "", err
  104. }
  105. now := time.Now()
  106. hdrTmpl := &tar.Header{
  107. Mode: 0o600,
  108. Uid: 0,
  109. Gid: 0,
  110. ModTime: now,
  111. Typeflag: tar.TypeReg,
  112. AccessTime: now,
  113. ChangeTime: now,
  114. }
  115. randomName := ".dockerfile." + stringid.GenerateRandomID()[:20]
  116. buildCtx = archive.ReplaceFileTarWrapper(buildCtx, map[string]archive.TarModifierFunc{
  117. // Add the dockerfile with a random filename
  118. randomName: func(_ string, h *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
  119. return hdrTmpl, file, nil
  120. },
  121. // Update .dockerignore to include the random filename
  122. ".dockerignore": func(_ string, h *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
  123. if h == nil {
  124. h = hdrTmpl
  125. }
  126. b := &bytes.Buffer{}
  127. if content != nil {
  128. if _, err := b.ReadFrom(content); err != nil {
  129. return nil, nil, err
  130. }
  131. } else {
  132. b.WriteString(".dockerignore")
  133. }
  134. b.WriteString("\n" + randomName + "\n")
  135. return h, b.Bytes(), nil
  136. },
  137. })
  138. return buildCtx, randomName, nil
  139. }