-
Notifications
You must be signed in to change notification settings - Fork 0
/
frozen.go
182 lines (166 loc) · 4.83 KB
/
frozen.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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")
var loadImages []struct{ srcName, destName string }
for _, img := range images {
if err := exec.Command(dockerBinary, "inspect", "--type=image", img).Run(); err != nil {
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,
})
}
}
if len(loadImages) == 0 {
// everything is loaded, we're done
return nil
}
fi, err := os.Stat(frozenImgDir)
if err != nil || !fi.IsDir() {
srcImages := make([]string, 0, len(loadImages))
for _, img := range loadImages {
srcImages = append(srcImages, img.srcName)
}
if err := pullImages(dockerBinary, srcImages); err != nil {
return errors.Wrap(err, "error pulling image list")
}
} else {
if err := loadFrozenImages(dockerBinary); err != nil {
return err
}
}
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))
}
}
}
return nil
}
func loadFrozenImages(dockerBinary string) error {
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
}