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