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  }