Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Normalize the hardcoded images used for warmpool pre-pulling #17144

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 51 additions & 47 deletions pkg/assets/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func (a *AssetBuilder) RemapManifest(data []byte) ([]byte, error) {
}

// RemapImage normalizes a containers location if a user sets the AssetsLocation ContainerRegistry location.
func (a *AssetBuilder) RemapImage(image string) (string, error) {
func (a *AssetBuilder) RemapImage(image string) string {
asset := &ImageAsset{
DownloadLocation: image,
CanonicalLocation: image,
Expand Down Expand Up @@ -179,66 +179,27 @@ func (a *AssetBuilder) RemapImage(image string) (string, error) {
}
}

if a.AssetsLocation != nil && a.AssetsLocation.ContainerProxy != nil {
containerProxy := strings.TrimSuffix(*a.AssetsLocation.ContainerProxy, "/")
normalized := image

// If the image name contains only a single / we need to determine if the image is located on docker-hub or if it's using a convenient URL,
// like registry.k8s.io/<image-name> or registry.k8s.io/<image-name>
// In case of a hub image it should be sufficient to just prepend the proxy url, producing eg docker-proxy.example.com/weaveworks/weave-kube
if strings.Count(normalized, "/") <= 1 && !strings.ContainsAny(strings.Split(normalized, "/")[0], ".:") {
normalized = containerProxy + "/" + normalized
} else {
re := regexp.MustCompile(`^[^/]+`)
normalized = re.ReplaceAllString(normalized, containerProxy)
}

asset.DownloadLocation = normalized

// Run the new image
image = asset.DownloadLocation
}

if a.AssetsLocation != nil && a.AssetsLocation.ContainerRegistry != nil {
registryMirror := *a.AssetsLocation.ContainerRegistry
normalized := image

// Remove the 'standard' kubernetes image prefixes, just for sanity
normalized = strings.TrimPrefix(normalized, "registry.k8s.io/")

// When assembling the cluster spec, kops may call the option more then once until the config converges
// This means that this function may me called more than once on the same image
// It this is pass is the second one, the image will already have been normalized with the containerRegistry settings
// If this is the case, passing though the process again will re-prepend the container registry again
// and again, causing the spec to never converge and the config build to fail.
if !strings.HasPrefix(normalized, registryMirror+"/") {
// We can't nest arbitrarily
// Some risk of collisions, but also -- and __ in the names appear to be blocked by docker hub
normalized = strings.Replace(normalized, "/", "-", -1)
asset.DownloadLocation = registryMirror + "/" + normalized
}

// Run the new image
image = asset.DownloadLocation
}
normalized := NormalizeImage(a, image)
image = normalized
asset.DownloadLocation = normalized

a.ImageAssets = append(a.ImageAssets, asset)

if !featureflag.ImageDigest.Enabled() || os.Getenv("KOPS_BASE_URL") != "" {
return image, nil
return image
}

if strings.Contains(image, "@") {
return image, nil
return image
}

digest, err := crane.Digest(image, crane.WithAuthFromKeychain(authn.DefaultKeychain))
if err != nil {
klog.Warningf("failed to digest image %q: %s", image, err)
return image, nil
return image
}

return image + "@" + digest, nil
return image + "@" + digest
}

// RemapFile returns a remapped URL for the file, if AssetsLocation is defined.
Expand Down Expand Up @@ -378,3 +339,46 @@ func (a *AssetBuilder) remapURL(canonicalURL *url.URL) (*url.URL, error) {

return fileRepo, nil
}

func NormalizeImage(a *AssetBuilder, image string) string {
if a.AssetsLocation != nil && a.AssetsLocation.ContainerProxy != nil {
containerProxy := strings.TrimSuffix(*a.AssetsLocation.ContainerProxy, "/")
normalized := image

// If the image name contains only a single / we need to determine if the image is located on docker-hub or if it's using a convenient URL,
// like registry.k8s.io/<image-name> or registry.k8s.io/<image-name>
// In case of a hub image it should be sufficient to just prepend the proxy url, producing eg docker-proxy.example.com/weaveworks/weave-kube
if strings.Count(normalized, "/") <= 1 && !strings.ContainsAny(strings.Split(normalized, "/")[0], ".:") {
normalized = containerProxy + "/" + normalized
} else {
re := regexp.MustCompile(`^[^/]+`)
normalized = re.ReplaceAllString(normalized, containerProxy)
}

// Run the new image
image = normalized
}

if a.AssetsLocation != nil && a.AssetsLocation.ContainerRegistry != nil {
registryMirror := *a.AssetsLocation.ContainerRegistry
normalized := image

// Remove the 'standard' kubernetes image prefixes, just for sanity
normalized = strings.TrimPrefix(normalized, "registry.k8s.io/")

// When assembling the cluster spec, kops may call the option more then once until the config converges
// This means that this function may me called more than once on the same image
// It this is pass is the second one, the image will already have been normalized with the containerRegistry settings
// If this is the case, passing though the process again will re-prepend the container registry again
// and again, causing the spec to never converge and the config build to fail.
if !strings.HasPrefix(normalized, registryMirror+"/") {
// We can't nest arbitrarily
// Some risk of collisions, but also -- and __ in the names appear to be blocked by docker hub
normalized = strings.Replace(normalized, "/", "-", -1)
normalized = registryMirror + "/" + normalized
}
image = normalized
}
// Run the new image
return image
}
36 changes: 6 additions & 30 deletions pkg/assets/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,7 @@ func TestValidate_RemapImage_ContainerProxy_AppliesToDockerHub(t *testing.T) {

builder.AssetsLocation.ContainerProxy = &proxyURL

remapped, err := builder.RemapImage(image)
if err != nil {
t.Error("Error remapping image", err)
}

remapped := builder.RemapImage(image)
if remapped != expected {
t.Errorf("Error remapping image (Expecting: %s, got %s)", expected, remapped)
}
Expand All @@ -61,11 +57,7 @@ func TestValidate_RemapImage_ContainerProxy_AppliesToSimplifiedDockerHub(t *test

builder.AssetsLocation.ContainerProxy = &proxyURL

remapped, err := builder.RemapImage(image)
if err != nil {
t.Error("Error remapping image", err)
}

remapped := builder.RemapImage(image)
if remapped != expected {
t.Errorf("Error remapping image (Expecting: %s, got %s)", expected, remapped)
}
Expand All @@ -80,11 +72,7 @@ func TestValidate_RemapImage_ContainerProxy_AppliesToSimplifiedKubernetesURL(t *

builder.AssetsLocation.ContainerProxy = &proxyURL

remapped, err := builder.RemapImage(image)
if err != nil {
t.Error("Error remapping image", err)
}

remapped := builder.RemapImage(image)
if remapped != expected {
t.Errorf("Error remapping image (Expecting: %s, got %s)", expected, remapped)
}
Expand All @@ -99,11 +87,7 @@ func TestValidate_RemapImage_ContainerProxy_AppliesToLegacyKubernetesURL(t *test

builder.AssetsLocation.ContainerProxy = &proxyURL

remapped, err := builder.RemapImage(image)
if err != nil {
t.Error("Error remapping image", err)
}

remapped := builder.RemapImage(image)
if remapped != expected {
t.Errorf("Error remapping image (Expecting: %s, got %s)", expected, remapped)
}
Expand All @@ -118,11 +102,7 @@ func TestValidate_RemapImage_ContainerProxy_AppliesToImagesWithTags(t *testing.T

builder.AssetsLocation.ContainerProxy = &proxyURL

remapped, err := builder.RemapImage(image)
if err != nil {
t.Error("Error remapping image", err)
}

remapped := builder.RemapImage(image)
if remapped != expected {
t.Errorf("Error remapping image (Expecting: %s, got %s)", expected, remapped)
}
Expand All @@ -140,11 +120,7 @@ func TestValidate_RemapImage_ContainerRegistry_MappingMultipleTimesConverges(t *
remapped := image
iterations := make([]map[int]int, 2)
for i := range iterations {
remapped, err := builder.RemapImage(remapped)
if err != nil {
t.Errorf("Error remapping image (iteration %d): %s", i, err)
}

remapped := builder.RemapImage(remapped)
if remapped != expected {
t.Errorf("Error remapping image (Expecting: %s, got %s, iteration: %d)", expected, remapped, i)
}
Expand Down
8 changes: 2 additions & 6 deletions pkg/kubemanifest/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@ limitations under the License.
package kubemanifest

import (
"fmt"
"strings"

"k8s.io/klog/v2"
)

type ImageRemapFunction func(image string) (string, error)
type ImageRemapFunction func(image string) string

func (m *Object) RemapImages(mapper ImageRemapFunction) error {
visitor := &imageRemapVisitor{
Expand Down Expand Up @@ -57,10 +56,7 @@ func (m *imageRemapVisitor) VisitString(path []string, v string, mutator func(st

image := v
klog.V(4).Infof("Consider image for re-mapping: %q", image)
remapped, err := m.mapper(v)
if err != nil {
return fmt.Errorf("error remapping image %q: %v", image, err)
}
remapped := m.mapper(v)
if remapped != image {
mutator(remapped)
}
Expand Down
6 changes: 1 addition & 5 deletions pkg/model/components/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,7 @@ func Image(component string, clusterSpec *kops.ClusterSpec, assetsBuilder *asset
if !kopsmodel.IsBaseURL(clusterSpec.KubernetesVersion) {
image := "registry.k8s.io/" + imageName + ":" + "v" + kubernetesVersion.String()

image, err := assetsBuilder.RemapImage(image)
if err != nil {
return "", fmt.Errorf("unable to remap container %q: %v", image, err)
}
return image, nil
return assetsBuilder.RemapImage(image), nil
}

// The simple name is valid when pulling. But if we
Expand Down
12 changes: 2 additions & 10 deletions pkg/model/components/etcdmanager/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,11 +314,7 @@ func (b *EtcdManagerBuilder) buildPod(etcdCluster kops.EtcdClusterSpec, instance
// Remap image via AssetBuilder
for i := range pod.Spec.InitContainers {
initContainer := &pod.Spec.InitContainers[i]
remapped, err := b.AssetBuilder.RemapImage(initContainer.Image)
if err != nil {
return nil, fmt.Errorf("unable to remap init container image %q: %w", container.Image, err)
}
initContainer.Image = remapped
initContainer.Image = b.AssetBuilder.RemapImage(initContainer.Image)
}
}

Expand All @@ -334,11 +330,7 @@ func (b *EtcdManagerBuilder) buildPod(etcdCluster kops.EtcdClusterSpec, instance
}

// Remap image via AssetBuilder
remapped, err := b.AssetBuilder.RemapImage(container.Image)
if err != nil {
return nil, fmt.Errorf("unable to remap container image %q: %w", container.Image, err)
}
container.Image = remapped
container.Image = b.AssetBuilder.RemapImage(container.Image)
}

var clientHost string
Expand Down
8 changes: 1 addition & 7 deletions pkg/model/components/kubeapiserver/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,7 @@ func (b *KubeApiserverBuilder) buildHealthcheckSidecar() (*corev1.Pod, error) {
}

// Remap image via AssetBuilder
{
remapped, err := b.AssetBuilder.RemapImage(container.Image)
if err != nil {
return nil, fmt.Errorf("unable to remap container image %q: %v", container.Image, err)
}
container.Image = remapped
}
container.Image = b.AssetBuilder.RemapImage(container.Image)

return pod, nil
}
6 changes: 1 addition & 5 deletions pkg/model/components/kubelet.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,7 @@ func (b *KubeletOptionsBuilder) configureKubelet(cluster *kops.Cluster, kubelet
// Prevent image GC from pruning the pause image
// https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2040-kubelet-cri#pinned-images
image := "registry.k8s.io/pause:3.9"
var err error
if image, err = b.AssetBuilder.RemapImage(image); err != nil {
return err
}
kubelet.PodInfraContainerImage = image
kubelet.PodInfraContainerImage = b.AssetBuilder.RemapImage(image)

if kubelet.FeatureGates == nil {
kubelet.FeatureGates = make(map[string]string)
Expand Down
3 changes: 2 additions & 1 deletion pkg/nodemodel/nodeupconfigbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,8 @@ func (n *nodeUpConfigBuilder) buildWarmPoolImages(ig *kops.InstanceGroup) []stri
if assetBuilder != nil {
for _, image := range assetBuilder.ImageAssets {
for _, prefix := range desiredImagePrefixes {
if strings.HasPrefix(image.DownloadLocation, prefix) {
remappedPrefix := assets.NormalizeImage(assetBuilder, prefix)
if strings.HasPrefix(image.DownloadLocation, remappedPrefix) {
images[image.DownloadLocation] = true
}
}
Expand Down
Loading