1
16
17 package image
18
19 import (
20 "bufio"
21 "bytes"
22 "crypto/sha256"
23 "encoding/base64"
24 "fmt"
25 "io"
26 "net/http"
27 "os"
28 "regexp"
29 "strings"
30
31 "gopkg.in/yaml.v2"
32 )
33
34
35 type RegistryList struct {
36 GcAuthenticatedRegistry string `yaml:"gcAuthenticatedRegistry"`
37 PromoterE2eRegistry string `yaml:"promoterE2eRegistry"`
38 BuildImageRegistry string `yaml:"buildImageRegistry"`
39 InvalidRegistry string `yaml:"invalidRegistry"`
40 GcEtcdRegistry string `yaml:"gcEtcdRegistry"`
41 GcRegistry string `yaml:"gcRegistry"`
42 SigStorageRegistry string `yaml:"sigStorageRegistry"`
43 PrivateRegistry string `yaml:"privateRegistry"`
44 DockerLibraryRegistry string `yaml:"dockerLibraryRegistry"`
45 CloudProviderGcpRegistry string `yaml:"cloudProviderGcpRegistry"`
46 }
47
48
49 type Config struct {
50 registry string
51 name string
52 version string
53 }
54
55
56 func (i *Config) SetRegistry(registry string) {
57 i.registry = registry
58 }
59
60
61 func (i *Config) SetName(name string) {
62 i.name = name
63 }
64
65
66 func (i *Config) SetVersion(version string) {
67 i.version = version
68 }
69
70 func Init(repoList string) {
71 registry, imageConfigs, originalImageConfigs = readRepoList(repoList)
72 }
73
74 func readRepoList(repoList string) (RegistryList, map[ImageID]Config, map[ImageID]Config) {
75 registry := initRegistry
76
77 if repoList == "" {
78 imageConfigs, originalImageConfigs := initImageConfigs(registry)
79 return registry, imageConfigs, originalImageConfigs
80 }
81
82 var fileContent []byte
83 var err error
84 if strings.HasPrefix(repoList, "https://") || strings.HasPrefix(repoList, "http://") {
85 var b bytes.Buffer
86 err = readFromURL(repoList, bufio.NewWriter(&b))
87 if err != nil {
88 panic(fmt.Errorf("error reading '%v' url contents: %v", repoList, err))
89 }
90 fileContent = b.Bytes()
91 } else {
92 fileContent, err = os.ReadFile(repoList)
93 if err != nil {
94 panic(fmt.Errorf("error reading '%v' file contents: %v", repoList, err))
95 }
96 }
97
98 err = yaml.Unmarshal(fileContent, ®istry)
99 if err != nil {
100 panic(fmt.Errorf("error unmarshalling '%v' YAML file: %v", repoList, err))
101 }
102
103 imageConfigs, originalImageConfigs := initImageConfigs(registry)
104
105 return registry, imageConfigs, originalImageConfigs
106
107 }
108
109
110 func readFromURL(url string, writer io.Writer) error {
111 httpTransport := new(http.Transport)
112 httpTransport.Proxy = http.ProxyFromEnvironment
113
114 c := &http.Client{Transport: httpTransport}
115 r, err := c.Get(url)
116 if err != nil {
117 return err
118 }
119 defer r.Body.Close()
120 if r.StatusCode >= 400 {
121 return fmt.Errorf("%v returned %d", url, r.StatusCode)
122 }
123 _, err = io.Copy(writer, r.Body)
124 if err != nil {
125 return err
126 }
127 return nil
128 }
129
130 var (
131 initRegistry = RegistryList{
132 GcAuthenticatedRegistry: "gcr.io/authenticated-image-pulling",
133 PromoterE2eRegistry: "registry.k8s.io/e2e-test-images",
134 BuildImageRegistry: "registry.k8s.io/build-image",
135 InvalidRegistry: "invalid.registry.k8s.io/invalid",
136 GcEtcdRegistry: "registry.k8s.io",
137 GcRegistry: "registry.k8s.io",
138 SigStorageRegistry: "registry.k8s.io/sig-storage",
139 PrivateRegistry: "gcr.io/k8s-authenticated-test",
140 DockerLibraryRegistry: "docker.io/library",
141 CloudProviderGcpRegistry: "registry.k8s.io/cloud-provider-gcp",
142 }
143
144 registry, imageConfigs, originalImageConfigs = readRepoList(os.Getenv("KUBE_TEST_REPO_LIST"))
145 )
146
147 type ImageID int
148
149 const (
150
151 None ImageID = iota
152
153 Agnhost
154
155 AgnhostPrivate
156
157 APIServer
158
159 AppArmorLoader
160
161 AuthenticatedAlpine
162
163 AuthenticatedWindowsNanoServer
164
165 BusyBox
166
167 CudaVectorAdd
168
169 CudaVectorAdd2
170
171 DistrolessIptables
172
173 Etcd
174
175 Httpd
176
177 HttpdNew
178
179 InvalidRegistryImage
180
181 IpcUtils
182
183 JessieDnsutils
184
185 Kitten
186
187 Nautilus
188
189 NFSProvisioner
190
191 Nginx
192
193 NginxNew
194
195 NodePerfNpbEp
196
197 NodePerfNpbIs
198
199 NodePerfTfWideDeep
200
201 Nonewprivs
202
203 NonRoot
204
205
206 Pause
207
208 Perl
209
210 PrometheusDummyExporter
211
212 PrometheusToSd
213
214 Redis
215
216 RegressionIssue74839
217
218 ResourceConsumer
219
220 SdDummyExporter
221
222 VolumeNFSServer
223
224 VolumeISCSIServer
225
226 VolumeRBDServer
227 )
228
229 func initImageConfigs(list RegistryList) (map[ImageID]Config, map[ImageID]Config) {
230 configs := map[ImageID]Config{}
231 configs[Agnhost] = Config{list.PromoterE2eRegistry, "agnhost", "2.47"}
232 configs[AgnhostPrivate] = Config{list.PrivateRegistry, "agnhost", "2.6"}
233 configs[AuthenticatedAlpine] = Config{list.GcAuthenticatedRegistry, "alpine", "3.7"}
234 configs[AuthenticatedWindowsNanoServer] = Config{list.GcAuthenticatedRegistry, "windows-nanoserver", "v1"}
235 configs[APIServer] = Config{list.PromoterE2eRegistry, "sample-apiserver", "1.29.2"}
236 configs[AppArmorLoader] = Config{list.PromoterE2eRegistry, "apparmor-loader", "1.4"}
237 configs[BusyBox] = Config{list.PromoterE2eRegistry, "busybox", "1.36.1-1"}
238 configs[CudaVectorAdd] = Config{list.PromoterE2eRegistry, "cuda-vector-add", "1.0"}
239 configs[CudaVectorAdd2] = Config{list.PromoterE2eRegistry, "cuda-vector-add", "2.3"}
240 configs[DistrolessIptables] = Config{list.BuildImageRegistry, "distroless-iptables", "v0.5.3"}
241 configs[Etcd] = Config{list.GcEtcdRegistry, "etcd", "3.5.12-0"}
242 configs[Httpd] = Config{list.PromoterE2eRegistry, "httpd", "2.4.38-4"}
243 configs[HttpdNew] = Config{list.PromoterE2eRegistry, "httpd", "2.4.39-4"}
244 configs[InvalidRegistryImage] = Config{list.InvalidRegistry, "alpine", "3.1"}
245 configs[IpcUtils] = Config{list.PromoterE2eRegistry, "ipc-utils", "1.3"}
246 configs[JessieDnsutils] = Config{list.PromoterE2eRegistry, "jessie-dnsutils", "1.7"}
247 configs[Kitten] = Config{list.PromoterE2eRegistry, "kitten", "1.7"}
248 configs[Nautilus] = Config{list.PromoterE2eRegistry, "nautilus", "1.7"}
249 configs[NFSProvisioner] = Config{list.SigStorageRegistry, "nfs-provisioner", "v4.0.8"}
250 configs[Nginx] = Config{list.PromoterE2eRegistry, "nginx", "1.14-4"}
251 configs[NginxNew] = Config{list.PromoterE2eRegistry, "nginx", "1.15-4"}
252 configs[NodePerfNpbEp] = Config{list.PromoterE2eRegistry, "node-perf/npb-ep", "1.2"}
253 configs[NodePerfNpbIs] = Config{list.PromoterE2eRegistry, "node-perf/npb-is", "1.2"}
254 configs[NodePerfTfWideDeep] = Config{list.PromoterE2eRegistry, "node-perf/tf-wide-deep", "1.3"}
255 configs[Nonewprivs] = Config{list.PromoterE2eRegistry, "nonewprivs", "1.3"}
256 configs[NonRoot] = Config{list.PromoterE2eRegistry, "nonroot", "1.4"}
257
258 configs[Pause] = Config{list.GcRegistry, "pause", "3.9"}
259 configs[Perl] = Config{list.PromoterE2eRegistry, "perl", "5.26"}
260 configs[PrometheusDummyExporter] = Config{list.GcRegistry, "prometheus-dummy-exporter", "v0.1.0"}
261 configs[PrometheusToSd] = Config{list.GcRegistry, "prometheus-to-sd", "v0.5.0"}
262 configs[Redis] = Config{list.PromoterE2eRegistry, "redis", "5.0.5-3"}
263 configs[RegressionIssue74839] = Config{list.PromoterE2eRegistry, "regression-issue-74839", "1.2"}
264 configs[ResourceConsumer] = Config{list.PromoterE2eRegistry, "resource-consumer", "1.13"}
265 configs[SdDummyExporter] = Config{list.GcRegistry, "sd-dummy-exporter", "v0.2.0"}
266 configs[VolumeNFSServer] = Config{list.PromoterE2eRegistry, "volume/nfs", "1.4"}
267 configs[VolumeISCSIServer] = Config{list.PromoterE2eRegistry, "volume/iscsi", "2.6"}
268 configs[VolumeRBDServer] = Config{list.PromoterE2eRegistry, "volume/rbd", "1.0.6"}
269
270
271
272
273 appendCSIImageConfigs(configs)
274
275
276 originalImageConfigs := configs
277 if repo := os.Getenv("KUBE_TEST_REPO"); len(repo) > 0 {
278 configs = GetMappedImageConfigs(originalImageConfigs, repo)
279 }
280
281 return configs, originalImageConfigs
282 }
283
284
285
286 func GetMappedImageConfigs(originalImageConfigs map[ImageID]Config, repo string) map[ImageID]Config {
287 configs := make(map[ImageID]Config)
288 for i, config := range originalImageConfigs {
289 switch i {
290 case InvalidRegistryImage, AuthenticatedAlpine,
291 AuthenticatedWindowsNanoServer, AgnhostPrivate:
292
293
294
295 configs[i] = config
296 continue
297 }
298
299
300
301 configs[i] = getRepositoryMappedConfig(i, config, repo)
302 }
303 return configs
304 }
305
306 var (
307 reCharSafe = regexp.MustCompile(`[^\w]`)
308 reDashes = regexp.MustCompile(`-+`)
309 )
310
311
312
313
314
315
316 func getRepositoryMappedConfig(imageID ImageID, config Config, repo string) Config {
317 parts := strings.SplitN(repo, "/", 2)
318 registry, name := parts[0], parts[1]
319
320 pullSpec := config.GetE2EImage()
321
322 h := sha256.New()
323 h.Write([]byte(pullSpec))
324 hash := base64.RawURLEncoding.EncodeToString(h.Sum(nil))[:16]
325
326 shortName := reCharSafe.ReplaceAllLiteralString(pullSpec, "-")
327 shortName = reDashes.ReplaceAllLiteralString(shortName, "-")
328 maxLength := 127 - 16 - 6 - 10
329 if len(shortName) > maxLength {
330 shortName = shortName[len(shortName)-maxLength:]
331 }
332 var version string
333 if imageID == None {
334 version = fmt.Sprintf("e2e-%s-%s", shortName, hash)
335 } else {
336 version = fmt.Sprintf("e2e-%d-%s-%s", imageID, shortName, hash)
337 }
338
339 return Config{
340 registry: registry,
341 name: name,
342 version: version,
343 }
344 }
345
346
347 func GetOriginalImageConfigs() map[ImageID]Config {
348 return originalImageConfigs
349 }
350
351
352 func GetImageConfigs() map[ImageID]Config {
353 return imageConfigs
354 }
355
356
357 func GetConfig(image ImageID) Config {
358 return imageConfigs[image]
359 }
360
361
362 func GetE2EImage(image ImageID) string {
363 return fmt.Sprintf("%s/%s:%s", imageConfigs[image].registry, imageConfigs[image].name, imageConfigs[image].version)
364 }
365
366
367 func (i *Config) GetE2EImage() string {
368 return fmt.Sprintf("%s/%s:%s", i.registry, i.name, i.version)
369 }
370
371
372 func GetPauseImageName() string {
373 return GetE2EImage(Pause)
374 }
375
376
377
378 func ReplaceRegistryInImageURL(imageURL string) (string, error) {
379 return replaceRegistryInImageURLWithList(imageURL, registry)
380 }
381
382
383
384 func replaceRegistryInImageURLWithList(imageURL string, reg RegistryList) (string, error) {
385 parts := strings.Split(imageURL, "/")
386 countParts := len(parts)
387 registryAndUser := strings.Join(parts[:countParts-1], "/")
388
389 if repo := os.Getenv("KUBE_TEST_REPO"); len(repo) > 0 {
390 imageID := None
391 for i, v := range originalImageConfigs {
392 if v.GetE2EImage() == imageURL {
393 imageID = i
394 break
395 }
396 }
397 last := strings.SplitN(parts[countParts-1], ":", 2)
398 if len(last) == 1 {
399 return "", fmt.Errorf("image %q is required to be in an image:tag format", imageURL)
400 }
401 config := getRepositoryMappedConfig(imageID, Config{
402 registry: parts[0],
403 name: strings.Join([]string{strings.Join(parts[1:countParts-1], "/"), last[0]}, "/"),
404 version: last[1],
405 }, repo)
406 return config.GetE2EImage(), nil
407 }
408
409 switch registryAndUser {
410 case initRegistry.GcRegistry:
411 registryAndUser = reg.GcRegistry
412 case initRegistry.SigStorageRegistry:
413 registryAndUser = reg.SigStorageRegistry
414 case initRegistry.PrivateRegistry:
415 registryAndUser = reg.PrivateRegistry
416 case initRegistry.InvalidRegistry:
417 registryAndUser = reg.InvalidRegistry
418 case initRegistry.PromoterE2eRegistry:
419 registryAndUser = reg.PromoterE2eRegistry
420 case initRegistry.BuildImageRegistry:
421 registryAndUser = reg.BuildImageRegistry
422 case initRegistry.GcAuthenticatedRegistry:
423 registryAndUser = reg.GcAuthenticatedRegistry
424 case initRegistry.DockerLibraryRegistry:
425 registryAndUser = reg.DockerLibraryRegistry
426 case initRegistry.CloudProviderGcpRegistry:
427 registryAndUser = reg.CloudProviderGcpRegistry
428 default:
429 if countParts == 1 {
430
431
432 registryAndUser = reg.DockerLibraryRegistry
433 break
434 }
435
436 return "", fmt.Errorf("Registry: %s is missing in test/utils/image/manifest.go, please add the registry, otherwise the test will fail on air-gapped clusters", registryAndUser)
437 }
438
439 return fmt.Sprintf("%s/%s", registryAndUser, parts[countParts-1]), nil
440 }
441
View as plain text