2016-08-31 19:31:06 +00:00
|
|
|
package load
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bytes"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
var frozenImgDir = "/docker-frozen-images"
|
|
|
|
|
|
|
|
// FrozenImagesLinux loads the frozen image set for the integration suite
|
|
|
|
// If the images are not available locally it will download them
|
|
|
|
// TODO: This loads whatever is in the frozen image dir, regardless of what
|
|
|
|
// images were passed in. If the images need to be downloaded, then it will respect
|
|
|
|
// the passed in images
|
|
|
|
func FrozenImagesLinux(dockerBinary string, images ...string) error {
|
|
|
|
imgNS := os.Getenv("TEST_IMAGE_NAMESPACE")
|
2016-09-12 18:59:10 +00:00
|
|
|
var loadImages []struct{ srcName, destName string }
|
2016-08-31 19:31:06 +00:00
|
|
|
for _, img := range images {
|
|
|
|
if err := exec.Command(dockerBinary, "inspect", "--type=image", img).Run(); err != nil {
|
2016-09-12 18:59:10 +00:00
|
|
|
srcName := img
|
|
|
|
// hello-world:latest gets re-tagged as hello-world:frozen
|
|
|
|
// there are some tests that use hello-world:latest specifically so it pulls
|
|
|
|
// the image and hello-world:frozen is used for when we just want a super
|
|
|
|
// small image
|
|
|
|
if img == "hello-world:frozen" {
|
|
|
|
srcName = "hello-world:latest"
|
|
|
|
}
|
|
|
|
if imgNS != "" {
|
|
|
|
srcName = imgNS + "/" + srcName
|
|
|
|
}
|
|
|
|
loadImages = append(loadImages, struct{ srcName, destName string }{
|
|
|
|
srcName: srcName,
|
|
|
|
destName: img,
|
|
|
|
})
|
2016-08-31 19:31:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(loadImages) == 0 {
|
|
|
|
// everything is loaded, we're done
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
fi, err := os.Stat(frozenImgDir)
|
|
|
|
if err != nil || !fi.IsDir() {
|
2016-09-12 18:59:10 +00:00
|
|
|
srcImages := make([]string, 0, len(loadImages))
|
|
|
|
for _, img := range loadImages {
|
|
|
|
srcImages = append(srcImages, img.srcName)
|
|
|
|
}
|
|
|
|
if err := pullImages(dockerBinary, srcImages); err != nil {
|
2016-08-31 19:31:06 +00:00
|
|
|
return errors.Wrap(err, "error pulling image list")
|
|
|
|
}
|
|
|
|
} else {
|
2016-09-12 18:59:10 +00:00
|
|
|
if err := loadFrozenImages(dockerBinary); err != nil {
|
2016-08-31 19:31:06 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-12 18:59:10 +00:00
|
|
|
for _, img := range loadImages {
|
|
|
|
if img.srcName != img.destName {
|
|
|
|
if out, err := exec.Command(dockerBinary, "tag", img.srcName, img.destName).CombinedOutput(); err != nil {
|
|
|
|
return errors.Errorf("%v: %s", err, string(out))
|
|
|
|
}
|
|
|
|
if out, err := exec.Command(dockerBinary, "rmi", img.srcName).CombinedOutput(); err != nil {
|
|
|
|
return errors.Errorf("%v: %s", err, string(out))
|
2016-08-31 19:31:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-09-12 18:59:10 +00:00
|
|
|
func loadFrozenImages(dockerBinary string) error {
|
2016-08-31 19:31:06 +00:00
|
|
|
tar, err := exec.LookPath("tar")
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "could not find tar binary")
|
|
|
|
}
|
|
|
|
tarCmd := exec.Command(tar, "-cC", frozenImgDir, ".")
|
|
|
|
out, err := tarCmd.StdoutPipe()
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error getting stdout pipe for tar command")
|
|
|
|
}
|
|
|
|
|
|
|
|
errBuf := bytes.NewBuffer(nil)
|
|
|
|
tarCmd.Stderr = errBuf
|
|
|
|
tarCmd.Start()
|
|
|
|
defer tarCmd.Wait()
|
|
|
|
|
|
|
|
cmd := exec.Command(dockerBinary, "load")
|
|
|
|
cmd.Stdin = out
|
|
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
|
|
return errors.Errorf("%v: %s", err, string(out))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func pullImages(dockerBinary string, images []string) error {
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error getting path to dockerfile")
|
|
|
|
}
|
|
|
|
dockerfile := os.Getenv("DOCKERFILE")
|
|
|
|
if dockerfile == "" {
|
|
|
|
dockerfile = "Dockerfile"
|
|
|
|
}
|
|
|
|
dockerfilePath := filepath.Join(filepath.Dir(filepath.Clean(cwd)), dockerfile)
|
|
|
|
pullRefs, err := readFrozenImageList(dockerfilePath, images)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error reading frozen image list")
|
|
|
|
}
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
chErr := make(chan error, len(images))
|
|
|
|
for tag, ref := range pullRefs {
|
|
|
|
wg.Add(1)
|
|
|
|
go func(tag, ref string) {
|
|
|
|
defer wg.Done()
|
|
|
|
if out, err := exec.Command(dockerBinary, "pull", ref).CombinedOutput(); err != nil {
|
|
|
|
chErr <- errors.Errorf("%v: %s", string(out), err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if out, err := exec.Command(dockerBinary, "tag", ref, tag).CombinedOutput(); err != nil {
|
|
|
|
chErr <- errors.Errorf("%v: %s", string(out), err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if out, err := exec.Command(dockerBinary, "rmi", ref).CombinedOutput(); err != nil {
|
|
|
|
chErr <- errors.Errorf("%v: %s", string(out), err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}(tag, ref)
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
close(chErr)
|
|
|
|
return <-chErr
|
|
|
|
}
|
|
|
|
|
|
|
|
func readFrozenImageList(dockerfilePath string, images []string) (map[string]string, error) {
|
|
|
|
f, err := os.Open(dockerfilePath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "error reading dockerfile")
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
ls := make(map[string]string)
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
|
|
for scanner.Scan() {
|
|
|
|
line := strings.Fields(scanner.Text())
|
|
|
|
if len(line) < 3 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !(line[0] == "RUN" && line[1] == "./contrib/download-frozen-image-v2.sh") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
frozenImgDir = line[2]
|
|
|
|
if line[2] == frozenImgDir {
|
|
|
|
frozenImgDir = filepath.Join(os.Getenv("DEST"), "frozen-images")
|
|
|
|
}
|
|
|
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
img := strings.TrimSpace(scanner.Text())
|
|
|
|
img = strings.TrimSuffix(img, "\\")
|
|
|
|
img = strings.TrimSpace(img)
|
|
|
|
split := strings.Split(img, "@")
|
|
|
|
if len(split) < 2 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, i := range images {
|
|
|
|
if split[0] == i {
|
|
|
|
ls[i] = img
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ls, nil
|
|
|
|
}
|