2
0

builder.go 3.6 KB

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