sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/client/config/imagemeta_client.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package config 18 19 import ( 20 "fmt" 21 "strings" 22 23 "github.com/pkg/errors" 24 25 "sigs.k8s.io/cluster-api/util/container" 26 ) 27 28 const ( 29 // CertManagerImageComponent define the name of the cert-manager component in image overrides. 30 CertManagerImageComponent = "cert-manager" 31 32 imagesConfigKey = "images" 33 allImageConfig = "all" 34 ) 35 36 // ImageMetaClient has methods to work with image meta configurations. 37 type ImageMetaClient interface { 38 // AlterImage alters an image name according to the current image override configurations. 39 AlterImage(component, image string) (string, error) 40 } 41 42 // imageMetaClient implements ImageMetaClient. 43 type imageMetaClient struct { 44 reader Reader 45 imageMetaCache map[string]*imageMeta 46 } 47 48 // ensure imageMetaClient implements ImageMetaClient. 49 var _ ImageMetaClient = &imageMetaClient{} 50 51 func newImageMetaClient(reader Reader) *imageMetaClient { 52 return &imageMetaClient{ 53 reader: reader, 54 imageMetaCache: map[string]*imageMeta{}, 55 } 56 } 57 58 func (p *imageMetaClient) AlterImage(component, imageString string) (string, error) { 59 image, err := container.ImageFromString(imageString) 60 if err != nil { 61 return "", err 62 } 63 64 // Gets the image meta that applies to the selected component/image; if none, returns early 65 meta, err := p.getImageMeta(component, image.Name) 66 if err != nil { 67 return "", err 68 } 69 if meta == nil { 70 return imageString, nil 71 } 72 73 // Apply the image meta to image name 74 return meta.ApplyToImage(image), nil 75 } 76 77 // getImageMeta returns the image meta that applies to the selected component/image. 78 func (p *imageMetaClient) getImageMeta(component, imageName string) (*imageMeta, error) { 79 // if the image meta for the component is already known, return it 80 if im, ok := p.imageMetaCache[imageMetaCacheKey(component, imageName)]; ok { 81 return im, nil 82 } 83 84 // Otherwise read the image override configurations. 85 var meta map[string]imageMeta 86 if err := p.reader.UnmarshalKey(imagesConfigKey, &meta); err != nil { 87 return nil, errors.Wrap(err, "failed to unmarshal image override configurations") 88 } 89 90 // If there are not image override configurations, return. 91 if meta == nil { 92 p.imageMetaCache[imageMetaCacheKey(component, imageName)] = nil 93 return nil, nil 94 } 95 96 // Gets the image configuration for: 97 // - all the components, 98 // - the component (and to all its images) 99 // - the selected component/image 100 // and returns the union of all the above. 101 m := &imageMeta{} 102 if allMeta, ok := meta[allImageConfig]; ok { 103 m.Union(&allMeta) 104 } 105 106 if componentMeta, ok := meta[component]; ok { 107 m.Union(&componentMeta) 108 } 109 p.imageMetaCache[component] = m 110 111 if imageNameMeta, ok := meta[imageMetaCacheKey(component, imageName)]; ok { 112 m.Union(&imageNameMeta) 113 } 114 p.imageMetaCache[imageMetaCacheKey(component, imageName)] = m 115 116 return m, nil 117 } 118 119 func imageMetaCacheKey(component, imageName string) string { 120 return fmt.Sprintf("%s/%s", component, imageName) 121 } 122 123 // imageMeta allows to define transformations to apply to the image contained in the YAML manifests. 124 type imageMeta struct { 125 // repository sets the container registry to pull images from. 126 Repository string `json:"repository,omitempty"` 127 128 // Tag allows to specify a tag for the images. 129 Tag string `json:"tag,omitempty"` 130 } 131 132 // Union allows to merge two imageMeta transformation; in case both the imageMeta defines new values for the same field, 133 // the other transformation takes precedence on the existing one. 134 func (i *imageMeta) Union(other *imageMeta) { 135 if other.Repository != "" { 136 i.Repository = other.Repository 137 } 138 if other.Tag != "" { 139 i.Tag = other.Tag 140 } 141 } 142 143 // ApplyToImage changes an image name applying the transformations defined in the current imageMeta. 144 func (i *imageMeta) ApplyToImage(image container.Image) string { 145 // apply transformations 146 if i.Repository != "" { 147 image.Repository = strings.TrimSuffix(i.Repository, "/") 148 } 149 if i.Tag != "" { 150 image.Tag = i.Tag 151 } 152 153 // returns the resulting image name 154 return image.String() 155 }