From f07371de19756a95a4a1f09c236ace004261be22 Mon Sep 17 00:00:00 2001 From: fabriziopandini Date: Fri, 6 Mar 2020 21:14:01 +0100 Subject: [PATCH] clusterctl allow cert-manager image overrides --- cmd/clusterctl/client/client_test.go | 4 + cmd/clusterctl/client/cluster/cert_manager.go | 52 ++++-- cmd/clusterctl/client/cluster/client.go | 2 +- cmd/clusterctl/client/config/client.go | 10 +- .../client/config/imagemeta_client.go | 171 ++++++++++++++++++ .../client/config/imagemeta_client_test.go | 146 +++++++++++++++ cmd/clusterctl/internal/test/fake_reader.go | 23 ++- cmd/clusterctl/internal/util/objs.go | 48 +++++ cmd/clusterctl/internal/util/objs_test.go | 69 +++++++ cmd/clusterctl/test/e2e/go.sum | 4 + go.mod | 2 + go.sum | 4 + test/infrastructure/docker/go.sum | 2 + 13 files changed, 517 insertions(+), 20 deletions(-) create mode 100644 cmd/clusterctl/client/config/imagemeta_client.go create mode 100644 cmd/clusterctl/client/config/imagemeta_client_test.go diff --git a/cmd/clusterctl/client/client_test.go b/cmd/clusterctl/client/client_test.go index 42c92bdff3af..bf1971d3780c 100644 --- a/cmd/clusterctl/client/client_test.go +++ b/cmd/clusterctl/client/client_test.go @@ -283,6 +283,10 @@ func (f fakeConfigClient) Variables() config.VariablesClient { return f.internalclient.Variables() } +func (f fakeConfigClient) ImageMeta() config.ImageMetaClient { + return f.internalclient.ImageMeta() +} + func (f *fakeConfigClient) WithVar(key, value string) *fakeConfigClient { f.fakeReader.WithVar(key, value) return f diff --git a/cmd/clusterctl/client/cluster/cert_manager.go b/cmd/clusterctl/client/cluster/cert_manager.go index 7c3d2e787c93..004e64b61091 100644 --- a/cmd/clusterctl/client/cluster/cert_manager.go +++ b/cmd/clusterctl/client/cluster/cert_manager.go @@ -23,7 +23,8 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3" - "sigs.k8s.io/cluster-api/cmd/clusterctl/config" + "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config" + manifests "sigs.k8s.io/cluster-api/cmd/clusterctl/config" "sigs.k8s.io/cluster-api/cmd/clusterctl/internal/util" logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log" "sigs.k8s.io/controller-runtime/pkg/client" @@ -37,6 +38,8 @@ const ( retryCreateCertManagerObject = 3 retryIntervalCreateCertManagerObject = 1 * time.Second + + certManagerImageComponent = "cert-manager" ) // CertManagerClient has methods to work with cert-manager components in the cluster. @@ -51,6 +54,7 @@ type CertManagerClient interface { // certManagerClient implements CertManagerClient . type certManagerClient struct { + configClient config.Client proxy Proxy pollImmediateWaiter PollImmediateWaiter } @@ -59,8 +63,9 @@ type certManagerClient struct { var _ CertManagerClient = &certManagerClient{} // newCertMangerClient returns a certManagerClient. -func newCertMangerClient(proxy Proxy, pollImmediateWaiter PollImmediateWaiter) *certManagerClient { +func newCertMangerClient(configClient config.Client, proxy Proxy, pollImmediateWaiter PollImmediateWaiter) *certManagerClient { return &certManagerClient{ + configClient: configClient, proxy: proxy, pollImmediateWaiter: pollImmediateWaiter, } @@ -76,14 +81,10 @@ func (cm *certManagerClient) Images() ([]string, error) { return []string{}, nil } - yaml, err := config.Asset(embeddedCertManagerManifestPath) + // Gets the cert-manager objects from the embedded assets. + objs, err := cm.getManifestObjs() if err != nil { - return nil, err - } - - objs, err := util.ToUnstructured(yaml) - if err != nil { - return nil, errors.Wrap(err, "failed to parse yaml for cert-manager manifest") + return []string{}, nil } images, err := util.InspectImages(objs) @@ -112,15 +113,10 @@ func (cm *certManagerClient) EnsureWebhook() error { // Otherwise install cert-manager log.Info("Installing cert-manager") - // Gets the cert-manager manifest from the embedded assets and apply it. - yaml, err := config.Asset(embeddedCertManagerManifestPath) + // Gets the cert-manager objects from the embedded assets. + objs, err := cm.getManifestObjs() if err != nil { - return err - } - - objs, err := util.ToUnstructured(yaml) - if err != nil { - return errors.Wrap(err, "failed to parse yaml for cert-manager manifest") + return nil } // installs the web-hook @@ -163,6 +159,28 @@ func (cm *certManagerClient) EnsureWebhook() error { return nil } +// getManifestObjs gets the cert-manager manifest, convert to unstructured objects, and fix images +func (cm *certManagerClient) getManifestObjs() ([]unstructured.Unstructured, error) { + yaml, err := manifests.Asset(embeddedCertManagerManifestPath) + if err != nil { + return nil, err + } + + objs, err := util.ToUnstructured(yaml) + if err != nil { + return nil, errors.Wrap(err, "failed to parse yaml for cert-manager manifest") + } + + objs, err = util.FixImages(objs, func(image string) (string, error) { + return cm.configClient.ImageMeta().AlterImage(certManagerImageComponent, image) + }) + if err != nil { + return nil, errors.Wrap(err, "failed to apply image override to the cert-manager manifest") + } + + return objs, nil +} + func (cm *certManagerClient) createObj(o unstructured.Unstructured) error { c, err := cm.proxy.NewClient() if err != nil { diff --git a/cmd/clusterctl/client/cluster/client.go b/cmd/clusterctl/client/cluster/client.go index 38d5fdb95d8a..8e59eba9ffe7 100644 --- a/cmd/clusterctl/client/cluster/client.go +++ b/cmd/clusterctl/client/cluster/client.go @@ -97,7 +97,7 @@ func (c *clusterClient) Proxy() Proxy { } func (c *clusterClient) CertManager() CertManagerClient { - return newCertMangerClient(c.proxy, c.pollImmediateWaiter) + return newCertMangerClient(c.configClient, c.proxy, c.pollImmediateWaiter) } func (c *clusterClient) ProviderComponents() ComponentsClient { diff --git a/cmd/clusterctl/client/config/client.go b/cmd/clusterctl/client/config/client.go index d6687f286645..6b344f95bade 100644 --- a/cmd/clusterctl/client/config/client.go +++ b/cmd/clusterctl/client/config/client.go @@ -22,15 +22,19 @@ import ( ) // Client is used to interact with the clusterctl configurations. -// Clusterctl v2 handles two types of configs: +// Clusterctl v2 handles the following configurations: // 1. The configuration of the providers (name, type and URL of the provider repository) // 2. Variables used when installing providers/creating clusters. Variables can be read from the environment or from the config file +// 3. The configuration about image overrides type Client interface { // Providers provide access to provider configurations. Providers() ProvidersClient // Variables provide access to environment variables and/or variables defined in the clusterctl configuration file. Variables() VariablesClient + + // ImageMeta provide access to to image meta configurations. + ImageMeta() ImageMetaClient } // configClient implements Client. @@ -49,6 +53,10 @@ func (c *configClient) Variables() VariablesClient { return newVariablesClient(c.reader) } +func (c *configClient) ImageMeta() ImageMetaClient { + return newImageMetaClient(c.reader) +} + // Option is a configuration option supplied to New type Option func(*configClient) diff --git a/cmd/clusterctl/client/config/imagemeta_client.go b/cmd/clusterctl/client/config/imagemeta_client.go new file mode 100644 index 000000000000..334bc282bda6 --- /dev/null +++ b/cmd/clusterctl/client/config/imagemeta_client.go @@ -0,0 +1,171 @@ +/* +Copyright 2020 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 config + +import ( + "fmt" + "strings" + + "github.com/docker/distribution/reference" + "github.com/pkg/errors" +) + +const ( + imagesConfigKey = "images" + allImageConfig = "all" +) + +// ImageMetaClient has methods to work with image meta configurations. +type ImageMetaClient interface { + // AlterImage alters an image name according to the current image override configurations. + AlterImage(component, image string) (string, error) +} + +// imageMetaClient implements ImageMetaClient. +type imageMetaClient struct { + reader Reader + imageMetaCache map[string]*imageMeta +} + +// ensure imageMetaClient implements ImageMetaClient. +var _ ImageMetaClient = &imageMetaClient{} + +func newImageMetaClient(reader Reader) *imageMetaClient { + return &imageMetaClient{ + reader: reader, + imageMetaCache: map[string]*imageMeta{}, + } +} + +func (p *imageMetaClient) AlterImage(component, image string) (string, error) { + // Gets the image meta that applies to the selected component; if none, returns early + meta, err := p.getImageMetaByComponent(component) + if err != nil { + return "", err + } + if meta == nil { + return image, nil + } + + // Apply the image meta to image name + return meta.ApplyToImage(image) +} + +// getImageMetaByComponent returns the image meta that applies to the selected component +func (p *imageMetaClient) getImageMetaByComponent(component string) (*imageMeta, error) { + // if the image meta for the component is already known, return it + if im, ok := p.imageMetaCache[component]; ok { + return im, nil + } + + // Otherwise read the image override configurations. + var meta map[string]imageMeta + if err := p.reader.UnmarshalKey(imagesConfigKey, &meta); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal image override configurations") + } + + // If there are not image override configurations, return. + if meta == nil { + p.imageMetaCache[component] = nil + return nil, nil + } + + // Gets the image configuration and to the specific component, and returns the union of the two. + m := &imageMeta{} + if allMeta, ok := meta[allImageConfig]; ok { + m.Union(&allMeta) + } + if componentMeta, ok := meta[component]; ok { + m.Union(&componentMeta) + } + + p.imageMetaCache[component] = m + return m, nil +} + +// imageMeta allows to define transformations to apply to the image contained in the YAML manifests. +type imageMeta struct { + // repository sets the container registry to pull images from. + Repository string `json:"repository,omitempty"` + + // Tag allows to specify a tag for the images. + Tag string `json:"tag,omitempty"` +} + +// Union allows to merge two imageMeta transformation; in case both the imageMeta defines new values for the same field, +// the other transformation takes precedence on the existing one. +func (i *imageMeta) Union(other *imageMeta) { + if other.Repository != "" { + i.Repository = other.Repository + } + if other.Tag != "" { + i.Tag = other.Tag + } +} + +// ApplyToImage changes an image name applying the transformations defined in the current imageMeta. +func (i *imageMeta) ApplyToImage(image string) (string, error) { + // Splits the image name into its own composing parts + ref, err := reference.ParseNormalizedNamed(image) + if err != nil { + return "", err + } + + // apply transformations + if i.Repository != "" { + // store tag & digest for rebuilding the image name + tagged, hasTag := ref.(reference.Tagged) + digested, hasDigest := ref.(reference.Digested) + + // detect the image name, dropping host and path if any + name := ref.Name() + imageNameIndex := strings.LastIndex(name, "/") + if imageNameIndex != -1 { + name = strings.TrimPrefix(name[imageNameIndex+1:], "/") + } + + // parse the new image resulting by concatenating the new repository and the image name + ref, err = reference.ParseNormalizedNamed(fmt.Sprintf("%s/%s", strings.TrimSuffix(i.Repository, "/"), name)) + if err != nil { + return "", err + } + + // applies back tag & digest + if hasTag { + ref, err = reference.WithTag(ref, tagged.Tag()) + if err != nil { + return "", err + } + } + + if hasDigest { + ref, err = reference.WithDigest(ref, digested.Digest()) + if err != nil { + return "", err + } + } + } + if i.Tag != "" { + ref, err = reference.WithTag(reference.TrimNamed(ref), i.Tag) + if err != nil { + return "", err + } + } + + // returns the resulting image name + return ref.String(), nil +} diff --git a/cmd/clusterctl/client/config/imagemeta_client_test.go b/cmd/clusterctl/client/config/imagemeta_client_test.go new file mode 100644 index 000000000000..e3cdddac0704 --- /dev/null +++ b/cmd/clusterctl/client/config/imagemeta_client_test.go @@ -0,0 +1,146 @@ +/* +Copyright 2020 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 config + +import ( + "testing" + + "sigs.k8s.io/cluster-api/cmd/clusterctl/internal/test" +) + +func Test_imageMetaClient_AlterImage(t *testing.T) { + type fields struct { + reader Reader + } + type args struct { + component string + image string + } + tests := []struct { + name string + fields fields + args args + want string + wantErr bool + }{ + { + name: "no image config, image should not be changes", + fields: fields{ + reader: test.NewFakeReader(), + }, + args: args{ + component: "any", + image: "quay.io/jetstack/cert-manager-cainjector:v0.11.0", + }, + want: "quay.io/jetstack/cert-manager-cainjector:v0.11.0", + wantErr: false, + }, + { + name: "image config for cert-manager, image for the cert-manager should be changed", + fields: fields{ + reader: test.NewFakeReader().WithImageMeta("cert-manager", "foo-repository.io", "foo-tag"), + }, + args: args{ + component: "cert-manager", + image: "quay.io/jetstack/cert-manager-cainjector:v0.11.0", + }, + want: "foo-repository.io/cert-manager-cainjector:foo-tag", + wantErr: false, + }, + { + name: "image config for all, image for the cert-manager should be changed", + fields: fields{ + reader: test.NewFakeReader().WithImageMeta(allImageConfig, "foo-repository.io", "foo-tag"), + }, + args: args{ + component: "cert-manager", + image: "quay.io/jetstack/cert-manager-cainjector:v0.11.0", + }, + want: "foo-repository.io/cert-manager-cainjector:foo-tag", + wantErr: false, + }, + { + name: "image config for all and image config for cert-manager, image for the cert-manager should be changed according to the most specific", + fields: fields{ + reader: test.NewFakeReader(). + WithImageMeta(allImageConfig, "foo-repository.io", "foo-tag"). + WithImageMeta("cert-manager", "bar-repository.io", "bar-tag"), + }, + args: args{ + component: "cert-manager", + image: "quay.io/jetstack/cert-manager-cainjector:v0.11.0", + }, + want: "bar-repository.io/cert-manager-cainjector:bar-tag", + wantErr: false, + }, + { + name: "image config for all and image config for cert-manager, image for the cert-manager should be changed according to the most specific (mixed case)", + fields: fields{ + reader: test.NewFakeReader(). + WithImageMeta(allImageConfig, "foo-repository.io", ""). + WithImageMeta("cert-manager", "", "bar-tag"), + }, + args: args{ + component: "cert-manager", + image: "quay.io/jetstack/cert-manager-cainjector:v0.11.0", + }, + want: "foo-repository.io/cert-manager-cainjector:bar-tag", + wantErr: false, + }, + { + name: "fails if wrong image config", + fields: fields{ + reader: test.NewFakeReader().WithVar(imagesConfigKey, "invalid"), + }, + args: args{ + component: "any", + image: "any", + }, + want: "", + wantErr: true, + }, + { + name: "fails if wrong image name", + fields: fields{ + reader: test.NewFakeReader().WithImageMeta(allImageConfig, "foo-Repository.io", ""), + }, + args: args{ + component: "any", + image: "invalid:invalid:invalid", + }, + want: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := newImageMetaClient(tt.fields.reader) + + got, err := p.AlterImage(tt.args.component, tt.args.image) + if (err != nil) != tt.wantErr { + t.Fatalf("error = %v, wantErr %v", err, tt.wantErr) + } + if tt.wantErr { + return + } + + if got != tt.want { + t.Errorf("got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cmd/clusterctl/internal/test/fake_reader.go b/cmd/clusterctl/internal/test/fake_reader.go index 2c30cace6ecf..fde313d03cba 100644 --- a/cmd/clusterctl/internal/test/fake_reader.go +++ b/cmd/clusterctl/internal/test/fake_reader.go @@ -27,6 +27,7 @@ type FakeReader struct { initialized bool variables map[string]string providers []configProvider + imageMetas map[string]imageMeta } // configProvider is a mirror of config.Provider, re-implemented here in order to @@ -37,6 +38,13 @@ type configProvider struct { Type clusterctlv1.ProviderType `json:"type,omitempty"` } +// imageMeta is a mirror of config.imageMeta, re-implemented here in order to +// avoid circular dependencies between pkg/client/config and pkg/internal/test +type imageMeta struct { + Repository string `json:"repository,omitempty"` + Tag string `json:"tag,omitempty"` +} + func (f *FakeReader) Init(config string) error { f.initialized = true return nil @@ -63,7 +71,8 @@ func (f *FakeReader) UnmarshalKey(key string, rawval interface{}) error { func NewFakeReader() *FakeReader { return &FakeReader{ - variables: map[string]string{}, + variables: map[string]string{}, + imageMetas: map[string]imageMeta{}, } } @@ -84,3 +93,15 @@ func (f *FakeReader) WithProvider(name string, ttype clusterctlv1.ProviderType, return f } + +func (f *FakeReader) WithImageMeta(component, repository, tag string) *FakeReader { + f.imageMetas[component] = imageMeta{ + Repository: repository, + Tag: tag, + } + + yaml, _ := yaml.Marshal(f.imageMetas) + f.variables["images"] = string(yaml) + + return f +} diff --git a/cmd/clusterctl/internal/util/objs.go b/cmd/clusterctl/internal/util/objs.go index 1d7923fe896e..ebcd464cc4c8 100644 --- a/cmd/clusterctl/internal/util/objs.go +++ b/cmd/clusterctl/internal/util/objs.go @@ -17,6 +17,7 @@ limitations under the License. package util import ( + "github.com/pkg/errors" appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3" @@ -96,3 +97,50 @@ func IsSharedResource(o unstructured.Unstructured) bool { } return false } + +// FixImages alters images using the give alter func +// NB. The implemented approach is specific for the provider components YAML & for the cert-manager manifest; it is not +// intended to cover all the possible objects used to deploy containers existing in Kubernetes. +func FixImages(objs []unstructured.Unstructured, alterImageFunc func(image string) (string, error)) ([]unstructured.Unstructured, error) { + // look for resources of kind Deployment and alter the image + for i := range objs { + o := &objs[i] + if o.GetKind() != deploymentKind { + continue + } + + // Convert Unstructured into a typed object + d := &appsv1.Deployment{} + if err := scheme.Scheme.Convert(o, d, nil); err != nil { + return nil, err + } + + // Alter the image + for j := range d.Spec.Template.Spec.Containers { + container := d.Spec.Template.Spec.Containers[j] + image, err := alterImageFunc(container.Image) + if err != nil { + return nil, errors.Wrapf(err, "failed to fix image for container %s in deployment %s", container.Name, d.Name) + } + container.Image = image + d.Spec.Template.Spec.Containers[j] = container + } + + for j := range d.Spec.Template.Spec.InitContainers { + container := d.Spec.Template.Spec.InitContainers[j] + image, err := alterImageFunc(container.Image) + if err != nil { + return nil, errors.Wrapf(err, "failed to fix image for init container %s in deployment %s", container.Name, d.Name) + } + container.Image = image + d.Spec.Template.Spec.InitContainers[j] = container + } + + // Convert typed object back to Unstructured + if err := scheme.Scheme.Convert(d, o, nil); err != nil { + return nil, err + } + objs[i] = *o + } + return objs, nil +} diff --git a/cmd/clusterctl/internal/util/objs_test.go b/cmd/clusterctl/internal/util/objs_test.go index 2f43b10a9e48..05669fdd08fb 100644 --- a/cmd/clusterctl/internal/util/objs_test.go +++ b/cmd/clusterctl/internal/util/objs_test.go @@ -17,6 +17,7 @@ limitations under the License. package util import ( + "fmt" "reflect" "testing" @@ -141,3 +142,71 @@ func Test_inspectImages(t *testing.T) { }) } } + +func TestFixImages(t *testing.T) { + type args struct { + objs []unstructured.Unstructured + alterImageFunc func(image string) (string, error) + } + tests := []struct { + name string + args args + want []string + wantErr bool + }{ + { + name: "fix deployment containers images", + args: args{ + objs: []unstructured.Unstructured{ + { + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": deploymentKind, + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []map[string]interface{}{ + { + "image": "container-image", + }, + }, + "initContainers": []map[string]interface{}{ + { + "image": "init-container-image", + }, + }, + }, + }, + }, + }, + }, + }, + alterImageFunc: func(image string) (string, error) { + return fmt.Sprintf("foo-%s", image), nil + }, + }, + want: []string{"foo-container-image", "foo-init-container-image"}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := FixImages(tt.args.objs, tt.args.alterImageFunc) + if (err != nil) != tt.wantErr { + t.Fatalf("error = %v, wantErr %v", err, tt.wantErr) + } + if tt.wantErr { + return + } + + gotImages, err := InspectImages(got) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(gotImages, tt.want) { + t.Errorf("got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cmd/clusterctl/test/e2e/go.sum b/cmd/clusterctl/test/e2e/go.sum index 3586533f1a06..c548bf5b1124 100644 --- a/cmd/clusterctl/test/e2e/go.sum +++ b/cmd/clusterctl/test/e2e/go.sum @@ -65,6 +65,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -273,6 +275,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= diff --git a/go.mod b/go.mod index d618abf66102..3e9869b96693 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/MakeNowJust/heredoc v1.0.0 github.com/blang/semver v3.5.1+incompatible github.com/davecgh/go-spew v1.1.1 + github.com/docker/distribution v2.7.1+incompatible github.com/go-logr/logr v0.1.0 github.com/gogo/protobuf v1.3.1 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect @@ -16,6 +17,7 @@ require ( github.com/imdario/mergo v0.3.8 // indirect github.com/onsi/ginkgo v1.12.0 github.com/onsi/gomega v1.9.0 + github.com/opencontainers/go-digest v1.0.0-rc1 // indirect github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.5.0 github.com/prometheus/client_model v0.2.0 diff --git a/go.sum b/go.sum index 7325843eebe1..fbdc99945b10 100644 --- a/go.sum +++ b/go.sum @@ -72,6 +72,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -290,6 +292,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= diff --git a/test/infrastructure/docker/go.sum b/test/infrastructure/docker/go.sum index 2dc39f04f8c6..55c284c10fe8 100644 --- a/test/infrastructure/docker/go.sum +++ b/test/infrastructure/docker/go.sum @@ -66,6 +66,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -265,6 +266,7 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=