builder.go 3.0 KB

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