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