builder.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  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. Remove: true,
  57. })
  58. if err != nil {
  59. return err
  60. }
  61. defer out.Body.Close()
  62. termFd, isTerm := term.GetFdInfo(os.Stderr)
  63. return jsonmessage.DisplayJSONMessagesStream(out.Body, os.Stderr, termFd, isTerm, nil)
  64. }
  65. // AddDockerfileToBuildContext from a ReadCloser, returns a new archive and
  66. // the relative path to the dockerfile in the context.
  67. func AddDockerfileToBuildContext(dockerfileCtx io.ReadCloser, buildCtx io.ReadCloser) (io.ReadCloser, string, error) {
  68. file, err := ioutil.ReadAll(dockerfileCtx)
  69. dockerfileCtx.Close()
  70. if err != nil {
  71. return nil, "", err
  72. }
  73. now := time.Now()
  74. hdrTmpl := &tar.Header{
  75. Mode: 0600,
  76. Uid: 0,
  77. Gid: 0,
  78. ModTime: now,
  79. Typeflag: tar.TypeReg,
  80. AccessTime: now,
  81. ChangeTime: now,
  82. }
  83. randomName := ".dockerfile." + stringid.GenerateRandomID()[:20]
  84. buildCtx = archive.ReplaceFileTarWrapper(buildCtx, map[string]archive.TarModifierFunc{
  85. // Add the dockerfile with a random filename
  86. randomName: func(_ string, h *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
  87. return hdrTmpl, file, nil
  88. },
  89. // Update .dockerignore to include the random filename
  90. ".dockerignore": func(_ string, h *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
  91. if h == nil {
  92. h = hdrTmpl
  93. }
  94. b := &bytes.Buffer{}
  95. if content != nil {
  96. if _, err := b.ReadFrom(content); err != nil {
  97. return nil, nil, err
  98. }
  99. } else {
  100. b.WriteString(".dockerignore")
  101. }
  102. b.WriteString("\n" + randomName + "\n")
  103. return h, b.Bytes(), nil
  104. },
  105. })
  106. return buildCtx, randomName, nil
  107. }