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  }