2
0

builder.go 2.8 KB

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