2019-05-31 09:45:11 +00:00
/ *
Copyright 2017 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package image
import (
2022-05-05 02:47:06 +00:00
"bufio"
"bytes"
2021-06-25 04:59:51 +00:00
"crypto/sha256"
"encoding/base64"
2019-05-31 09:45:11 +00:00
"fmt"
2022-05-05 02:47:06 +00:00
"io"
"net/http"
2019-05-31 09:45:11 +00:00
"os"
2021-06-25 04:59:51 +00:00
"regexp"
2020-01-14 10:38:55 +00:00
"strings"
2019-05-31 09:45:11 +00:00
2021-08-09 07:19:24 +00:00
"gopkg.in/yaml.v2"
2019-05-31 09:45:11 +00:00
)
// RegistryList holds public and private image registries
type RegistryList struct {
2021-08-09 07:19:24 +00:00
GcAuthenticatedRegistry string ` yaml:"gcAuthenticatedRegistry" `
PromoterE2eRegistry string ` yaml:"promoterE2eRegistry" `
BuildImageRegistry string ` yaml:"buildImageRegistry" `
InvalidRegistry string ` yaml:"invalidRegistry" `
GcEtcdRegistry string ` yaml:"gcEtcdRegistry" `
GcRegistry string ` yaml:"gcRegistry" `
SigStorageRegistry string ` yaml:"sigStorageRegistry" `
PrivateRegistry string ` yaml:"privateRegistry" `
MicrosoftRegistry string ` yaml:"microsoftRegistry" `
DockerLibraryRegistry string ` yaml:"dockerLibraryRegistry" `
CloudProviderGcpRegistry string ` yaml:"cloudProviderGcpRegistry" `
2019-05-31 09:45:11 +00:00
}
// Config holds an images registry, name, and version
type Config struct {
registry string
name string
version string
}
// SetRegistry sets an image registry in a Config struct
func ( i * Config ) SetRegistry ( registry string ) {
i . registry = registry
}
// SetName sets an image name in a Config struct
func ( i * Config ) SetName ( name string ) {
i . name = name
}
// SetVersion sets an image version in a Config struct
func ( i * Config ) SetVersion ( version string ) {
i . version = version
}
2023-05-29 21:03:29 +00:00
func Init ( repoList string ) {
registry , imageConfigs , originalImageConfigs = readRepoList ( repoList )
}
func readRepoList ( repoList string ) ( RegistryList , map [ ImageID ] Config , map [ ImageID ] Config ) {
2021-08-09 07:19:24 +00:00
registry := initRegistry
2019-05-31 09:45:11 +00:00
if repoList == "" {
2023-05-29 21:03:29 +00:00
imageConfigs , originalImageConfigs := initImageConfigs ( registry )
return registry , imageConfigs , originalImageConfigs
2019-05-31 09:45:11 +00:00
}
2022-05-05 02:47:06 +00:00
var fileContent [ ] byte
var err error
if strings . HasPrefix ( repoList , "https://" ) || strings . HasPrefix ( repoList , "http://" ) {
var b bytes . Buffer
err = readFromURL ( repoList , bufio . NewWriter ( & b ) )
if err != nil {
panic ( fmt . Errorf ( "error reading '%v' url contents: %v" , repoList , err ) )
}
fileContent = b . Bytes ( )
} else {
fileContent , err = os . ReadFile ( repoList )
if err != nil {
panic ( fmt . Errorf ( "error reading '%v' file contents: %v" , repoList , err ) )
}
2019-05-31 09:45:11 +00:00
}
err = yaml . Unmarshal ( fileContent , & registry )
if err != nil {
2023-05-29 21:03:29 +00:00
panic ( fmt . Errorf ( "error unmarshalling '%v' YAML file: %v" , repoList , err ) )
2019-05-31 09:45:11 +00:00
}
2023-05-29 21:03:29 +00:00
imageConfigs , originalImageConfigs := initImageConfigs ( registry )
return registry , imageConfigs , originalImageConfigs
2019-05-31 09:45:11 +00:00
}
2022-05-05 02:47:06 +00:00
// Essentially curl url | writer
func readFromURL ( url string , writer io . Writer ) error {
httpTransport := new ( http . Transport )
httpTransport . Proxy = http . ProxyFromEnvironment
c := & http . Client { Transport : httpTransport }
r , err := c . Get ( url )
if err != nil {
return err
}
defer r . Body . Close ( )
if r . StatusCode >= 400 {
return fmt . Errorf ( "%v returned %d" , url , r . StatusCode )
}
_ , err = io . Copy ( writer , r . Body )
if err != nil {
return err
}
return nil
}
2019-05-31 09:45:11 +00:00
var (
2021-08-09 07:19:24 +00:00
initRegistry = RegistryList {
GcAuthenticatedRegistry : "gcr.io/authenticated-image-pulling" ,
2022-08-24 02:24:25 +00:00
PromoterE2eRegistry : "registry.k8s.io/e2e-test-images" ,
BuildImageRegistry : "registry.k8s.io/build-image" ,
2022-05-05 02:47:06 +00:00
InvalidRegistry : "invalid.registry.k8s.io/invalid" ,
2022-08-24 02:24:25 +00:00
GcEtcdRegistry : "registry.k8s.io" ,
GcRegistry : "registry.k8s.io" ,
SigStorageRegistry : "registry.k8s.io/sig-storage" ,
2021-08-09 07:19:24 +00:00
PrivateRegistry : "gcr.io/k8s-authenticated-test" ,
MicrosoftRegistry : "mcr.microsoft.com" ,
DockerLibraryRegistry : "docker.io/library" ,
2022-08-24 02:24:25 +00:00
CloudProviderGcpRegistry : "registry.k8s.io/cloud-provider-gcp" ,
2021-08-09 07:19:24 +00:00
}
2021-06-25 04:59:51 +00:00
2023-05-29 21:03:29 +00:00
registry , imageConfigs , originalImageConfigs = readRepoList ( os . Getenv ( "KUBE_TEST_REPO_LIST" ) )
2019-05-31 09:45:11 +00:00
)
2022-08-24 02:24:25 +00:00
type ImageID int
2019-05-31 09:45:11 +00:00
const (
2020-12-17 12:28:29 +00:00
// None is to be used for unset/default images
2022-08-24 02:24:25 +00:00
None ImageID = iota
2019-06-24 09:08:09 +00:00
// Agnhost image
2020-12-17 12:28:29 +00:00
Agnhost
2020-01-14 10:38:55 +00:00
// AgnhostPrivate image
AgnhostPrivate
2019-05-31 09:45:11 +00:00
// APIServer image
APIServer
// AppArmorLoader image
AppArmorLoader
2020-01-14 10:38:55 +00:00
// AuthenticatedAlpine image
AuthenticatedAlpine
// AuthenticatedWindowsNanoServer image
AuthenticatedWindowsNanoServer
2019-05-31 09:45:11 +00:00
// BusyBox image
BusyBox
// CudaVectorAdd image
CudaVectorAdd
// CudaVectorAdd2 image
CudaVectorAdd2
2022-08-24 02:24:25 +00:00
// DistrolessIptables Image
DistrolessIptables
2019-05-31 09:45:11 +00:00
// Etcd image
Etcd
2020-01-14 10:38:55 +00:00
// Httpd image
Httpd
// HttpdNew image
HttpdNew
// InvalidRegistryImage image
InvalidRegistryImage
2019-05-31 09:45:11 +00:00
// IpcUtils image
IpcUtils
// JessieDnsutils image
JessieDnsutils
// Kitten image
Kitten
// Nautilus image
Nautilus
2020-01-14 10:38:55 +00:00
// NFSProvisioner image
NFSProvisioner
2019-05-31 09:45:11 +00:00
// Nginx image
Nginx
// NginxNew image
NginxNew
2021-06-25 04:59:51 +00:00
// NodePerfNpbEp image
NodePerfNpbEp
// NodePerfNpbIs image
NodePerfNpbIs
// NodePerfTfWideDeep image
NodePerfTfWideDeep
2019-05-31 09:45:11 +00:00
// Nonewprivs image
Nonewprivs
2019-06-24 09:08:09 +00:00
// NonRoot runs with a default user of 1234
NonRoot
2019-05-31 09:45:11 +00:00
// Pause - when these values are updated, also update cmd/kubelet/app/options/container_runtime.go
// Pause image
Pause
2019-06-24 09:08:09 +00:00
// Perl image
Perl
2020-01-14 10:38:55 +00:00
// PrometheusDummyExporter image
PrometheusDummyExporter
// PrometheusToSd image
PrometheusToSd
2019-05-31 09:45:11 +00:00
// Redis image
Redis
2020-01-14 10:38:55 +00:00
// RegressionIssue74839 image
RegressionIssue74839
2019-05-31 09:45:11 +00:00
// ResourceConsumer image
ResourceConsumer
2020-01-14 10:38:55 +00:00
// SdDummyExporter image
SdDummyExporter
2019-05-31 09:45:11 +00:00
// VolumeNFSServer image
VolumeNFSServer
// VolumeISCSIServer image
VolumeISCSIServer
// VolumeRBDServer image
VolumeRBDServer
2021-06-25 04:59:51 +00:00
// WindowsServer image
WindowsServer
2019-05-31 09:45:11 +00:00
)
2022-08-24 02:24:25 +00:00
func initImageConfigs ( list RegistryList ) ( map [ ImageID ] Config , map [ ImageID ] Config ) {
configs := map [ ImageID ] Config { }
2023-08-17 05:15:28 +00:00
configs [ Agnhost ] = Config { list . PromoterE2eRegistry , "agnhost" , "2.45" }
2021-08-09 07:19:24 +00:00
configs [ AgnhostPrivate ] = Config { list . PrivateRegistry , "agnhost" , "2.6" }
configs [ AuthenticatedAlpine ] = Config { list . GcAuthenticatedRegistry , "alpine" , "3.7" }
configs [ AuthenticatedWindowsNanoServer ] = Config { list . GcAuthenticatedRegistry , "windows-nanoserver" , "v1" }
2023-02-01 17:06:36 +00:00
configs [ APIServer ] = Config { list . PromoterE2eRegistry , "sample-apiserver" , "1.17.7" }
2021-12-08 13:50:47 +00:00
configs [ AppArmorLoader ] = Config { list . PromoterE2eRegistry , "apparmor-loader" , "1.4" }
2023-12-18 20:31:00 +00:00
configs [ BusyBox ] = Config { list . PromoterE2eRegistry , "busybox" , "1.36.1-1" }
2021-08-09 07:19:24 +00:00
configs [ CudaVectorAdd ] = Config { list . PromoterE2eRegistry , "cuda-vector-add" , "1.0" }
2023-08-17 05:15:28 +00:00
configs [ CudaVectorAdd2 ] = Config { list . PromoterE2eRegistry , "cuda-vector-add" , "2.3" }
2024-02-19 20:58:29 +00:00
configs [ DistrolessIptables ] = Config { list . BuildImageRegistry , "distroless-iptables" , "v0.4.5" }
2023-12-18 20:31:00 +00:00
configs [ Etcd ] = Config { list . GcEtcdRegistry , "etcd" , "3.5.10-0" }
2023-02-01 17:06:36 +00:00
configs [ Httpd ] = Config { list . PromoterE2eRegistry , "httpd" , "2.4.38-4" }
configs [ HttpdNew ] = Config { list . PromoterE2eRegistry , "httpd" , "2.4.39-4" }
2021-08-09 07:19:24 +00:00
configs [ InvalidRegistryImage ] = Config { list . InvalidRegistry , "alpine" , "3.1" }
2021-12-08 13:50:47 +00:00
configs [ IpcUtils ] = Config { list . PromoterE2eRegistry , "ipc-utils" , "1.3" }
2023-02-01 17:06:36 +00:00
configs [ JessieDnsutils ] = Config { list . PromoterE2eRegistry , "jessie-dnsutils" , "1.7" }
configs [ Kitten ] = Config { list . PromoterE2eRegistry , "kitten" , "1.7" }
configs [ Nautilus ] = Config { list . PromoterE2eRegistry , "nautilus" , "1.7" }
2022-05-05 02:47:06 +00:00
configs [ NFSProvisioner ] = Config { list . SigStorageRegistry , "nfs-provisioner" , "v3.0.1" }
2023-02-01 17:06:36 +00:00
configs [ Nginx ] = Config { list . PromoterE2eRegistry , "nginx" , "1.14-4" }
configs [ NginxNew ] = Config { list . PromoterE2eRegistry , "nginx" , "1.15-4" }
2021-12-08 13:50:47 +00:00
configs [ NodePerfNpbEp ] = Config { list . PromoterE2eRegistry , "node-perf/npb-ep" , "1.2" }
configs [ NodePerfNpbIs ] = Config { list . PromoterE2eRegistry , "node-perf/npb-is" , "1.2" }
2023-02-01 17:06:36 +00:00
configs [ NodePerfTfWideDeep ] = Config { list . PromoterE2eRegistry , "node-perf/tf-wide-deep" , "1.3" }
2021-08-09 07:19:24 +00:00
configs [ Nonewprivs ] = Config { list . PromoterE2eRegistry , "nonewprivs" , "1.3" }
2023-02-01 17:06:36 +00:00
configs [ NonRoot ] = Config { list . PromoterE2eRegistry , "nonroot" , "1.4" }
2019-05-31 09:45:11 +00:00
// Pause - when these values are updated, also update cmd/kubelet/app/options/container_runtime.go
2023-02-01 17:06:36 +00:00
configs [ Pause ] = Config { list . GcRegistry , "pause" , "3.9" }
2021-08-09 07:19:24 +00:00
configs [ Perl ] = Config { list . PromoterE2eRegistry , "perl" , "5.26" }
configs [ PrometheusDummyExporter ] = Config { list . GcRegistry , "prometheus-dummy-exporter" , "v0.1.0" }
configs [ PrometheusToSd ] = Config { list . GcRegistry , "prometheus-to-sd" , "v0.5.0" }
2023-02-01 17:06:36 +00:00
configs [ Redis ] = Config { list . PromoterE2eRegistry , "redis" , "5.0.5-3" }
2021-08-09 07:19:24 +00:00
configs [ RegressionIssue74839 ] = Config { list . PromoterE2eRegistry , "regression-issue-74839" , "1.2" }
2023-02-01 17:06:36 +00:00
configs [ ResourceConsumer ] = Config { list . PromoterE2eRegistry , "resource-consumer" , "1.13" }
2021-08-09 07:19:24 +00:00
configs [ SdDummyExporter ] = Config { list . GcRegistry , "sd-dummy-exporter" , "v0.2.0" }
2021-12-08 13:50:47 +00:00
configs [ VolumeNFSServer ] = Config { list . PromoterE2eRegistry , "volume/nfs" , "1.3" }
2023-08-17 05:15:28 +00:00
configs [ VolumeISCSIServer ] = Config { list . PromoterE2eRegistry , "volume/iscsi" , "2.6" }
configs [ VolumeRBDServer ] = Config { list . PromoterE2eRegistry , "volume/rbd" , "1.0.6" }
2021-08-09 07:19:24 +00:00
configs [ WindowsServer ] = Config { list . MicrosoftRegistry , "windows" , "1809" }
2021-06-25 04:59:51 +00:00
2022-08-24 02:24:25 +00:00
// This adds more config entries. Those have no pre-defined ImageID number,
2022-05-05 02:47:06 +00:00
// but will be used via ReplaceRegistryInImageURL when deploying
// CSI drivers (test/e2e/storage/util/create.go).
appendCSIImageConfigs ( configs )
2021-06-25 04:59:51 +00:00
// if requested, map all the SHAs into a known format based on the input
originalImageConfigs := configs
if repo := os . Getenv ( "KUBE_TEST_REPO" ) ; len ( repo ) > 0 {
configs = GetMappedImageConfigs ( originalImageConfigs , repo )
}
return configs , originalImageConfigs
}
// GetMappedImageConfigs returns the images if they were mapped to the provided
// image repository.
2022-08-24 02:24:25 +00:00
func GetMappedImageConfigs ( originalImageConfigs map [ ImageID ] Config , repo string ) map [ ImageID ] Config {
configs := make ( map [ ImageID ] Config )
2021-06-25 04:59:51 +00:00
for i , config := range originalImageConfigs {
switch i {
case InvalidRegistryImage , AuthenticatedAlpine ,
AuthenticatedWindowsNanoServer , AgnhostPrivate :
// These images are special and can't be run out of the cloud - some because they
// are authenticated, and others because they are not real images. Tests that depend
// on these images can't be run without access to the public internet.
configs [ i ] = config
continue
}
2022-08-24 02:24:25 +00:00
// Build a new tag with the ImageID, a hash of the image spec (to be unique) and
2021-06-25 04:59:51 +00:00
// shorten and make the pull spec "safe" so it will fit in the tag
configs [ i ] = getRepositoryMappedConfig ( i , config , repo )
}
2019-05-31 09:45:11 +00:00
return configs
}
2021-06-25 04:59:51 +00:00
var (
reCharSafe = regexp . MustCompile ( ` [^\w] ` )
reDashes = regexp . MustCompile ( ` -+ ` )
)
// getRepositoryMappedConfig maps an existing image to the provided repo, generating a
2022-08-24 02:24:25 +00:00
// tag that is unique with the input config. The tag will contain the imageID, a hash of
2021-06-25 04:59:51 +00:00
// the image spec (to be unique) and shorten and make the pull spec "safe" so it will
2022-08-24 02:24:25 +00:00
// fit in the tag to allow a human to recognize the value. If imageID is None, then no
// imageID will be added to the tag.
func getRepositoryMappedConfig ( imageID ImageID , config Config , repo string ) Config {
2021-06-25 04:59:51 +00:00
parts := strings . SplitN ( repo , "/" , 2 )
registry , name := parts [ 0 ] , parts [ 1 ]
pullSpec := config . GetE2EImage ( )
h := sha256 . New ( )
h . Write ( [ ] byte ( pullSpec ) )
2021-08-09 07:19:24 +00:00
hash := base64 . RawURLEncoding . EncodeToString ( h . Sum ( nil ) ) [ : 16 ]
2021-06-25 04:59:51 +00:00
shortName := reCharSafe . ReplaceAllLiteralString ( pullSpec , "-" )
shortName = reDashes . ReplaceAllLiteralString ( shortName , "-" )
maxLength := 127 - 16 - 6 - 10
if len ( shortName ) > maxLength {
shortName = shortName [ len ( shortName ) - maxLength : ]
}
var version string
2022-08-24 02:24:25 +00:00
if imageID == None {
2021-06-25 04:59:51 +00:00
version = fmt . Sprintf ( "e2e-%s-%s" , shortName , hash )
} else {
2022-08-24 02:24:25 +00:00
version = fmt . Sprintf ( "e2e-%d-%s-%s" , imageID , shortName , hash )
2021-06-25 04:59:51 +00:00
}
return Config {
registry : registry ,
name : name ,
version : version ,
}
}
// GetOriginalImageConfigs returns the configuration before any mapping rules.
2022-08-24 02:24:25 +00:00
func GetOriginalImageConfigs ( ) map [ ImageID ] Config {
2021-06-25 04:59:51 +00:00
return originalImageConfigs
}
2019-05-31 09:45:11 +00:00
// GetImageConfigs returns the map of imageConfigs
2022-08-24 02:24:25 +00:00
func GetImageConfigs ( ) map [ ImageID ] Config {
2019-05-31 09:45:11 +00:00
return imageConfigs
}
// GetConfig returns the Config object for an image
2022-08-24 02:24:25 +00:00
func GetConfig ( image ImageID ) Config {
2019-05-31 09:45:11 +00:00
return imageConfigs [ image ]
}
// GetE2EImage returns the fully qualified URI to an image (including version)
2022-08-24 02:24:25 +00:00
func GetE2EImage ( image ImageID ) string {
2019-05-31 09:45:11 +00:00
return fmt . Sprintf ( "%s/%s:%s" , imageConfigs [ image ] . registry , imageConfigs [ image ] . name , imageConfigs [ image ] . version )
}
// GetE2EImage returns the fully qualified URI to an image (including version)
func ( i * Config ) GetE2EImage ( ) string {
return fmt . Sprintf ( "%s/%s:%s" , i . registry , i . name , i . version )
}
// GetPauseImageName returns the pause image name with proper version
func GetPauseImageName ( ) string {
return GetE2EImage ( Pause )
}
2020-01-14 10:38:55 +00:00
2021-08-09 07:19:24 +00:00
// ReplaceRegistryInImageURL replaces the registry in the image URL with a custom one based
// on the configured registries.
2020-01-14 10:38:55 +00:00
func ReplaceRegistryInImageURL ( imageURL string ) ( string , error ) {
2021-08-09 07:19:24 +00:00
return replaceRegistryInImageURLWithList ( imageURL , registry )
}
// replaceRegistryInImageURLWithList replaces the registry in the image URL with a custom one based
// on the given registry list.
func replaceRegistryInImageURLWithList ( imageURL string , reg RegistryList ) ( string , error ) {
2020-01-14 10:38:55 +00:00
parts := strings . Split ( imageURL , "/" )
countParts := len ( parts )
registryAndUser := strings . Join ( parts [ : countParts - 1 ] , "/" )
2021-06-25 04:59:51 +00:00
if repo := os . Getenv ( "KUBE_TEST_REPO" ) ; len ( repo ) > 0 {
2022-08-24 02:24:25 +00:00
imageID := None
2021-06-25 04:59:51 +00:00
for i , v := range originalImageConfigs {
if v . GetE2EImage ( ) == imageURL {
2022-08-24 02:24:25 +00:00
imageID = i
2021-06-25 04:59:51 +00:00
break
}
}
last := strings . SplitN ( parts [ countParts - 1 ] , ":" , 2 )
2021-08-09 07:19:24 +00:00
if len ( last ) == 1 {
return "" , fmt . Errorf ( "image %q is required to be in an image:tag format" , imageURL )
}
2022-08-24 02:24:25 +00:00
config := getRepositoryMappedConfig ( imageID , Config {
2021-06-25 04:59:51 +00:00
registry : parts [ 0 ] ,
name : strings . Join ( [ ] string { strings . Join ( parts [ 1 : countParts - 1 ] , "/" ) , last [ 0 ] } , "/" ) ,
version : last [ 1 ] ,
} , repo )
return config . GetE2EImage ( ) , nil
}
2020-01-14 10:38:55 +00:00
switch registryAndUser {
2021-08-09 07:19:24 +00:00
case initRegistry . GcRegistry :
registryAndUser = reg . GcRegistry
case initRegistry . SigStorageRegistry :
registryAndUser = reg . SigStorageRegistry
case initRegistry . PrivateRegistry :
registryAndUser = reg . PrivateRegistry
case initRegistry . InvalidRegistry :
registryAndUser = reg . InvalidRegistry
case initRegistry . MicrosoftRegistry :
registryAndUser = reg . MicrosoftRegistry
case initRegistry . PromoterE2eRegistry :
registryAndUser = reg . PromoterE2eRegistry
case initRegistry . BuildImageRegistry :
registryAndUser = reg . BuildImageRegistry
case initRegistry . GcAuthenticatedRegistry :
registryAndUser = reg . GcAuthenticatedRegistry
case initRegistry . DockerLibraryRegistry :
registryAndUser = reg . DockerLibraryRegistry
case initRegistry . CloudProviderGcpRegistry :
registryAndUser = reg . CloudProviderGcpRegistry
2020-01-14 10:38:55 +00:00
default :
if countParts == 1 {
// We assume we found an image from docker hub library
// e.g. openjdk -> docker.io/library/openjdk
2021-08-09 07:19:24 +00:00
registryAndUser = reg . DockerLibraryRegistry
2020-01-14 10:38:55 +00:00
break
}
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 )
}
return fmt . Sprintf ( "%s/%s" , registryAndUser , parts [ countParts - 1 ] ) , nil
}