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