frozen.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package load
  2. import (
  3. "bufio"
  4. "bytes"
  5. "os"
  6. "os/exec"
  7. "path/filepath"
  8. "strings"
  9. "sync"
  10. "github.com/pkg/errors"
  11. )
  12. var frozenImgDir = "/docker-frozen-images"
  13. // FrozenImagesLinux loads the frozen image set for the integration suite
  14. // If the images are not available locally it will download them
  15. // TODO: This loads whatever is in the frozen image dir, regardless of what
  16. // images were passed in. If the images need to be downloaded, then it will respect
  17. // the passed in images
  18. func FrozenImagesLinux(dockerBinary string, images ...string) error {
  19. imgNS := os.Getenv("TEST_IMAGE_NAMESPACE")
  20. var loadImages []struct{ srcName, destName string }
  21. for _, img := range images {
  22. if err := exec.Command(dockerBinary, "inspect", "--type=image", img).Run(); err != nil {
  23. srcName := img
  24. // hello-world:latest gets re-tagged as hello-world:frozen
  25. // there are some tests that use hello-world:latest specifically so it pulls
  26. // the image and hello-world:frozen is used for when we just want a super
  27. // small image
  28. if img == "hello-world:frozen" {
  29. srcName = "hello-world:latest"
  30. }
  31. if imgNS != "" {
  32. srcName = imgNS + "/" + srcName
  33. }
  34. loadImages = append(loadImages, struct{ srcName, destName string }{
  35. srcName: srcName,
  36. destName: img,
  37. })
  38. }
  39. }
  40. if len(loadImages) == 0 {
  41. // everything is loaded, we're done
  42. return nil
  43. }
  44. fi, err := os.Stat(frozenImgDir)
  45. if err != nil || !fi.IsDir() {
  46. srcImages := make([]string, 0, len(loadImages))
  47. for _, img := range loadImages {
  48. srcImages = append(srcImages, img.srcName)
  49. }
  50. if err := pullImages(dockerBinary, srcImages); err != nil {
  51. return errors.Wrap(err, "error pulling image list")
  52. }
  53. } else {
  54. if err := loadFrozenImages(dockerBinary); err != nil {
  55. return err
  56. }
  57. }
  58. for _, img := range loadImages {
  59. if img.srcName != img.destName {
  60. if out, err := exec.Command(dockerBinary, "tag", img.srcName, img.destName).CombinedOutput(); err != nil {
  61. return errors.Errorf("%v: %s", err, string(out))
  62. }
  63. if out, err := exec.Command(dockerBinary, "rmi", img.srcName).CombinedOutput(); err != nil {
  64. return errors.Errorf("%v: %s", err, string(out))
  65. }
  66. }
  67. }
  68. return nil
  69. }
  70. func loadFrozenImages(dockerBinary string) error {
  71. tar, err := exec.LookPath("tar")
  72. if err != nil {
  73. return errors.Wrap(err, "could not find tar binary")
  74. }
  75. tarCmd := exec.Command(tar, "-cC", frozenImgDir, ".")
  76. out, err := tarCmd.StdoutPipe()
  77. if err != nil {
  78. return errors.Wrap(err, "error getting stdout pipe for tar command")
  79. }
  80. errBuf := bytes.NewBuffer(nil)
  81. tarCmd.Stderr = errBuf
  82. tarCmd.Start()
  83. defer tarCmd.Wait()
  84. cmd := exec.Command(dockerBinary, "load")
  85. cmd.Stdin = out
  86. if out, err := cmd.CombinedOutput(); err != nil {
  87. return errors.Errorf("%v: %s", err, string(out))
  88. }
  89. return nil
  90. }
  91. func pullImages(dockerBinary string, images []string) error {
  92. cwd, err := os.Getwd()
  93. if err != nil {
  94. return errors.Wrap(err, "error getting path to dockerfile")
  95. }
  96. dockerfile := os.Getenv("DOCKERFILE")
  97. if dockerfile == "" {
  98. dockerfile = "Dockerfile"
  99. }
  100. dockerfilePath := filepath.Join(filepath.Dir(filepath.Clean(cwd)), dockerfile)
  101. pullRefs, err := readFrozenImageList(dockerfilePath, images)
  102. if err != nil {
  103. return errors.Wrap(err, "error reading frozen image list")
  104. }
  105. var wg sync.WaitGroup
  106. chErr := make(chan error, len(images))
  107. for tag, ref := range pullRefs {
  108. wg.Add(1)
  109. go func(tag, ref string) {
  110. defer wg.Done()
  111. if out, err := exec.Command(dockerBinary, "pull", ref).CombinedOutput(); err != nil {
  112. chErr <- errors.Errorf("%v: %s", string(out), err)
  113. return
  114. }
  115. if out, err := exec.Command(dockerBinary, "tag", ref, tag).CombinedOutput(); err != nil {
  116. chErr <- errors.Errorf("%v: %s", string(out), err)
  117. return
  118. }
  119. if out, err := exec.Command(dockerBinary, "rmi", ref).CombinedOutput(); err != nil {
  120. chErr <- errors.Errorf("%v: %s", string(out), err)
  121. return
  122. }
  123. }(tag, ref)
  124. }
  125. wg.Wait()
  126. close(chErr)
  127. return <-chErr
  128. }
  129. func readFrozenImageList(dockerfilePath string, images []string) (map[string]string, error) {
  130. f, err := os.Open(dockerfilePath)
  131. if err != nil {
  132. return nil, errors.Wrap(err, "error reading dockerfile")
  133. }
  134. defer f.Close()
  135. ls := make(map[string]string)
  136. scanner := bufio.NewScanner(f)
  137. for scanner.Scan() {
  138. line := strings.Fields(scanner.Text())
  139. if len(line) < 3 {
  140. continue
  141. }
  142. if !(line[0] == "RUN" && line[1] == "./contrib/download-frozen-image-v2.sh") {
  143. continue
  144. }
  145. frozenImgDir = line[2]
  146. if line[2] == frozenImgDir {
  147. frozenImgDir = filepath.Join(os.Getenv("DEST"), "frozen-images")
  148. }
  149. for scanner.Scan() {
  150. img := strings.TrimSpace(scanner.Text())
  151. img = strings.TrimSuffix(img, "\\")
  152. img = strings.TrimSpace(img)
  153. split := strings.Split(img, "@")
  154. if len(split) < 2 {
  155. break
  156. }
  157. for _, i := range images {
  158. if split[0] == i {
  159. ls[i] = img
  160. break
  161. }
  162. }
  163. }
  164. }
  165. return ls, nil
  166. }