diff --git a/pkg/dockerregistry/server/client/client.go b/pkg/dockerregistry/server/client/client.go index aa3016be6e..88a188b6a2 100644 --- a/pkg/dockerregistry/server/client/client.go +++ b/pkg/dockerregistry/server/client/client.go @@ -72,6 +72,10 @@ func (c *apiClient) ImageDigestMirrorSet() cfgv1.ImageDigestMirrorSetInterface { return c.config.ImageDigestMirrorSets() } +func (c *apiClient) ImageTagMirrorSet() cfgv1.ImageTagMirrorSetInterface { + return c.config.ImageTagMirrorSets() +} + func (c *apiClient) Users() UserInterface { return c.user.Users() } diff --git a/pkg/dockerregistry/server/client/interfaces.go b/pkg/dockerregistry/server/client/interfaces.go index 51c4849a0f..dc39156337 100644 --- a/pkg/dockerregistry/server/client/interfaces.go +++ b/pkg/dockerregistry/server/client/interfaces.go @@ -26,6 +26,7 @@ type UsersInterfacer interface { type ImageContentSourcePolicyInterfacer interface { ImageContentSourcePolicy() operatorclientv1alpha1.ImageContentSourcePolicyInterface ImageDigestMirrorSet() cfgv1.ImageDigestMirrorSetInterface + ImageTagMirrorSet() cfgv1.ImageTagMirrorSetInterface } type ImagesInterfacer interface { diff --git a/pkg/dockerregistry/server/client/test.go b/pkg/dockerregistry/server/client/test.go index 9d708661a4..f3b3bc679b 100644 --- a/pkg/dockerregistry/server/client/test.go +++ b/pkg/dockerregistry/server/client/test.go @@ -23,8 +23,8 @@ func NewFakeRegistryClient(imageclient imageclientv1.ImageV1Interface) RegistryC func (c *fakeRegistryClient) Client() (Interface, error) { icsp := operatorfake.NewSimpleClientset().OperatorV1alpha1() - idms := cfgfake.NewSimpleClientset().ConfigV1() - return newAPIClient(nil, nil, c.images, nil, icsp, idms), nil + cfgclient := cfgfake.NewSimpleClientset().ConfigV1() + return newAPIClient(nil, nil, c.images, nil, icsp, cfgclient), nil } func NewFakeRegistryAPIClient(kc coreclientv1.CoreV1Interface, imageclient imageclientv1.ImageV1Interface) Interface { diff --git a/pkg/dockerregistry/server/pullthroughblobstore_test.go b/pkg/dockerregistry/server/pullthroughblobstore_test.go index 94e70c561f..6a380417a6 100644 --- a/pkg/dockerregistry/server/pullthroughblobstore_test.go +++ b/pkg/dockerregistry/server/pullthroughblobstore_test.go @@ -37,6 +37,7 @@ import ( func TestPullthroughServeBlob(t *testing.T) { icsp := operatorfake.NewSimpleClientset().OperatorV1alpha1().ImageContentSourcePolicies() idms := cfgfake.NewSimpleClientset().ConfigV1().ImageDigestMirrorSets() + itms := cfgfake.NewSimpleClientset().ConfigV1().ImageTagMirrorSets() ctx := context.Background() ctx = testutil.WithTestLogger(ctx, t) @@ -174,6 +175,7 @@ func TestPullthroughServeBlob(t *testing.T) { metrics.NewNoopMetrics(), icsp, idms, + itms, ) ptbs := &pullthroughBlobStore{ @@ -335,6 +337,7 @@ func TestPullthroughServeNotSeekableBlob(t *testing.T) { func TestPullthroughServeBlobInsecure(t *testing.T) { icsp := operatorfake.NewSimpleClientset().OperatorV1alpha1().ImageContentSourcePolicies() idms := cfgfake.NewSimpleClientset().ConfigV1().ImageDigestMirrorSets() + itms := cfgfake.NewSimpleClientset().ConfigV1().ImageTagMirrorSets() namespace := "user" repo1 := "app1" repo2 := "app2" @@ -613,6 +616,7 @@ func TestPullthroughServeBlobInsecure(t *testing.T) { metrics.NewNoopMetrics(), icsp, idms, + itms, ) ptbs := &pullthroughBlobStore{ @@ -681,6 +685,7 @@ func TestPullthroughServeBlobInsecure(t *testing.T) { func TestPullthroughMetrics(t *testing.T) { icsp := operatorfake.NewSimpleClientset().OperatorV1alpha1().ImageContentSourcePolicies() idms := cfgfake.NewSimpleClientset().ConfigV1().ImageDigestMirrorSets() + itms := cfgfake.NewSimpleClientset().ConfigV1().ImageTagMirrorSets() ctx := context.Background() ctx = testutil.WithTestLogger(ctx, t) @@ -742,6 +747,7 @@ func TestPullthroughMetrics(t *testing.T) { metrics.NewMetrics(sink), icsp, idms, + itms, ) ptbs := &pullthroughBlobStore{ diff --git a/pkg/dockerregistry/server/pullthroughmanifestservice.go b/pkg/dockerregistry/server/pullthroughmanifestservice.go index fa1a70e5a9..ffa9f9cebc 100644 --- a/pkg/dockerregistry/server/pullthroughmanifestservice.go +++ b/pkg/dockerregistry/server/pullthroughmanifestservice.go @@ -34,6 +34,7 @@ type pullthroughManifestService struct { registryAddr string metrics metrics.Pullthrough idms cfgv1.ImageDigestMirrorSetInterface + itms cfgv1.ImageTagMirrorSetInterface icsp operatorv1alpha1.ImageContentSourcePolicyInterface } @@ -131,7 +132,7 @@ func (m *pullthroughManifestService) getRemoteRepositoryClient(ctx context.Conte dcontext.GetLogger(ctx).Errorf("error getting secrets: %v", err) } - retriever, impErr := getImportContext(ctx, ref, secrets, m.metrics, m.icsp, m.idms) + retriever, impErr := getImportContext(ctx, ref, secrets, m.metrics, m.icsp, m.idms, m.itms) if impErr != nil { return nil, impErr } diff --git a/pkg/dockerregistry/server/pullthroughmanifestservice_test.go b/pkg/dockerregistry/server/pullthroughmanifestservice_test.go index 91be3da785..c07fc9a0f9 100644 --- a/pkg/dockerregistry/server/pullthroughmanifestservice_test.go +++ b/pkg/dockerregistry/server/pullthroughmanifestservice_test.go @@ -57,6 +57,7 @@ func createTestRegistryServer(t *testing.T, ctx context.Context) *httptest.Serve func TestPullthroughManifests(t *testing.T) { icsp := operatorfake.NewSimpleClientset().OperatorV1alpha1().ImageContentSourcePolicies() idms := cfgfake.NewSimpleClientset().ConfigV1().ImageDigestMirrorSets() + itms := cfgfake.NewSimpleClientset().ConfigV1().ImageTagMirrorSets() namespace := "fuser" repo := "zapp" repoName := fmt.Sprintf("%s/%s", namespace, repo) @@ -191,6 +192,7 @@ func TestPullthroughManifests(t *testing.T) { registryAddr: "localhost:5000", metrics: metrics.NewNoopMetrics(), idms: idms, + itms: itms, icsp: icsp, } @@ -232,6 +234,7 @@ func TestPullthroughManifests(t *testing.T) { func TestPullthroughManifestInsecure(t *testing.T) { icsp := operatorfake.NewSimpleClientset().OperatorV1alpha1().ImageContentSourcePolicies() idms := cfgfake.NewSimpleClientset().ConfigV1().ImageDigestMirrorSets() + itms := cfgfake.NewSimpleClientset().ConfigV1().ImageTagMirrorSets() namespace := "fuser" repo := "zapp" repoName := fmt.Sprintf("%s/%s", namespace, repo) @@ -436,6 +439,7 @@ func TestPullthroughManifestInsecure(t *testing.T) { cache: cache, metrics: metrics.NewNoopMetrics(), idms: idms, + itms: itms, icsp: icsp, } @@ -479,6 +483,7 @@ func TestPullthroughManifestInsecure(t *testing.T) { func TestPullthroughManifestDockerReference(t *testing.T) { icsp := operatorfake.NewSimpleClientset().OperatorV1alpha1().ImageContentSourcePolicies() idms := cfgfake.NewSimpleClientset().ConfigV1().ImageDigestMirrorSets() + itms := cfgfake.NewSimpleClientset().ConfigV1().ImageTagMirrorSets() namespace := "user" repo1 := "repo1" repo2 := "repo2" @@ -579,6 +584,7 @@ func TestPullthroughManifestDockerReference(t *testing.T) { imageStream: imageStream, metrics: metrics.NewNoopMetrics(), idms: idms, + itms: itms, icsp: icsp, } @@ -676,6 +682,7 @@ func (ms *putWaiterManifestService) Put(ctx context.Context, manifest distributi func TestPullthroughManifestMirroring(t *testing.T) { icsp := operatorfake.NewSimpleClientset().OperatorV1alpha1().ImageContentSourcePolicies() idms := cfgfake.NewSimpleClientset().ConfigV1().ImageDigestMirrorSets() + itms := cfgfake.NewSimpleClientset().ConfigV1().ImageTagMirrorSets() const timeout = 5 * time.Second namespace := "myproject" @@ -741,6 +748,7 @@ func TestPullthroughManifestMirroring(t *testing.T) { mirror: true, metrics: metrics.NewNoopMetrics(), idms: idms, + itms: itms, icsp: icsp, } @@ -759,6 +767,7 @@ func TestPullthroughManifestMirroring(t *testing.T) { func TestPullthroughManifestMetrics(t *testing.T) { icsp := operatorfake.NewSimpleClientset().OperatorV1alpha1().ImageContentSourcePolicies() idms := cfgfake.NewSimpleClientset().ConfigV1().ImageDigestMirrorSets() + itms := cfgfake.NewSimpleClientset().ConfigV1().ImageTagMirrorSets() namespace := "myproject" repo := "myapp" repoName := fmt.Sprintf("%s/%s", namespace, repo) @@ -821,6 +830,7 @@ func TestPullthroughManifestMetrics(t *testing.T) { imageStream: imageStream, metrics: metrics.NewMetrics(sink), idms: idms, + itms: itms, icsp: icsp, } diff --git a/pkg/dockerregistry/server/remoteblobgetter.go b/pkg/dockerregistry/server/remoteblobgetter.go index 046fab90c3..0f97c2b21a 100644 --- a/pkg/dockerregistry/server/remoteblobgetter.go +++ b/pkg/dockerregistry/server/remoteblobgetter.go @@ -74,6 +74,7 @@ type remoteBlobGetterService struct { metrics metrics.Pullthrough icsp operatorv1alpha1.ImageContentSourcePolicyInterface idms cfgv1.ImageDigestMirrorSetInterface + itms cfgv1.ImageTagMirrorSetInterface } var _ BlobGetterService = &remoteBlobGetterService{} @@ -87,6 +88,7 @@ func NewBlobGetterService( m metrics.Pullthrough, icsp operatorv1alpha1.ImageContentSourcePolicyInterface, idms cfgv1.ImageDigestMirrorSetInterface, + itms cfgv1.ImageTagMirrorSetInterface, ) BlobGetterService { return &remoteBlobGetterService{ imageStream: imageStream, @@ -96,6 +98,7 @@ func NewBlobGetterService( metrics: m, icsp: icsp, idms: idms, + itms: itms, } } @@ -299,7 +302,7 @@ func (rbgs *remoteBlobGetterService) findCandidateRepository( continue } - retriever, impErr := getImportContext(ctx, spec.DockerImageReference, secrets, rbgs.metrics, rbgs.icsp, rbgs.idms) + retriever, impErr := getImportContext(ctx, spec.DockerImageReference, secrets, rbgs.metrics, rbgs.icsp, rbgs.idms, rbgs.itms) if impErr != nil { return distribution.Descriptor{}, nil, impErr } @@ -321,7 +324,7 @@ func (rbgs *remoteBlobGetterService) findCandidateRepository( continue } - retriever, impErr := getImportContext(ctx, spec.DockerImageReference, secrets, rbgs.metrics, rbgs.icsp, rbgs.idms) + retriever, impErr := getImportContext(ctx, spec.DockerImageReference, secrets, rbgs.metrics, rbgs.icsp, rbgs.idms, rbgs.itms) if impErr != nil { return distribution.Descriptor{}, nil, impErr } diff --git a/pkg/dockerregistry/server/repository.go b/pkg/dockerregistry/server/repository.go index 0120e18b9e..5585b08988 100644 --- a/pkg/dockerregistry/server/repository.go +++ b/pkg/dockerregistry/server/repository.go @@ -49,6 +49,7 @@ type repository struct { imageStream imagestream.ImageStream icsp operatorv1alpha1.ImageContentSourcePolicyInterface idms cfgv1.ImageDigestMirrorSetInterface + itms cfgv1.ImageTagMirrorSetInterface // remoteBlobGetter is used to fetch blobs from remote registries if pullthrough is enabled. remoteBlobGetter BlobGetterService @@ -78,6 +79,7 @@ func (app *App) Repository(ctx context.Context, repo distribution.Repository, cr cache: cache.NewRepositoryDigest(app.cache), icsp: registryOSClient.ImageContentSourcePolicy(), idms: registryOSClient.ImageDigestMirrorSet(), + itms: registryOSClient.ImageTagMirrorSet(), } r.remoteBlobGetter = NewBlobGetterService( @@ -87,6 +89,7 @@ func (app *App) Repository(ctx context.Context, repo distribution.Repository, cr r.app.metrics, r.icsp, r.idms, + r.itms, ) repo = distribution.Repository(r) @@ -128,6 +131,7 @@ func (r *repository) Manifests(ctx context.Context, options ...distribution.Mani metrics: r.app.metrics, idms: r.idms, icsp: r.icsp, + itms: r.itms, } ms = newPendingErrorsManifestService(ms, r) diff --git a/pkg/dockerregistry/server/simplelookupicsp.go b/pkg/dockerregistry/server/simplelookupicsp.go index 447fa2cd04..11d1e68345 100644 --- a/pkg/dockerregistry/server/simplelookupicsp.go +++ b/pkg/dockerregistry/server/simplelookupicsp.go @@ -17,28 +17,31 @@ import ( "github.com/openshift/library-go/pkg/image/registryclient" ) -// simpleLookupICSP holds ImageContentSourcePolicy variables to look up image sources. Satisfies +// simpleLookupImageMirrorSets holds ImageContentSourcePolicy, ImageDigestMirrorSet, and ImageTagMirrorSet variables to look up image sources. Satisfies // *Context AlternativeBlobSourceStrategy interface. -type simpleLookupICSP struct { +type simpleLookupImageMirrorSets struct { icspClient operatorv1alpha1client.ImageContentSourcePolicyInterface idmsClient cfgv1client.ImageDigestMirrorSetInterface + itmsClient cfgv1client.ImageTagMirrorSetInterface } -// NewSimpleLookupICSPStrategy returns a new entity of simpleLookupICSP using provided client -// to obtain cluster wide ICSP or IDMS configuration. -func NewSimpleLookupICSPStrategy( +// NewSimpleLookupImageMirrorSetsStrategy returns a new entity of simpleLookupImageMirrorSets using provided client +// to obtain cluster wide ICSP or IDMS and ITMS configuration. +func NewSimpleLookupImageMirrorSetsStrategy( icspcli operatorv1alpha1client.ImageContentSourcePolicyInterface, idmscli cfgv1client.ImageDigestMirrorSetInterface, + itmscli cfgv1client.ImageTagMirrorSetInterface, ) registryclient.AlternateBlobSourceStrategy { - return &simpleLookupICSP{ + return &simpleLookupImageMirrorSets{ icspClient: icspcli, idmsClient: idmscli, + itmsClient: itmscli, } } // FirstRequest returns a list of sources to use when searching for a given repository. Returns // the whole list of mirrors followed by the original image reference. -func (s *simpleLookupICSP) FirstRequest( +func (s *simpleLookupImageMirrorSets) FirstRequest( ctx context.Context, ref reference.DockerImageReference, ) ([]reference.DockerImageReference, error) { klog.V(5).Infof("reading ICSP from cluster") @@ -54,12 +57,23 @@ func (s *simpleLookupICSP) FirstRequest( return []reference.DockerImageReference{ref.AsRepository()}, nil } + itmsList, err := s.itmsClient.List(ctx, metav1.ListOptions{}) + if err != nil { + klog.Errorf("unable to list ITMS config: %s", err) + return []reference.DockerImageReference{ref.AsRepository()}, nil + } + if len(icspList.Items) > 0 && len(idmsList.Items) > 0 { err := fmt.Errorf("found both ICSP and IDMS resources, but only one or the other is supported") return []reference.DockerImageReference{ref.AsRepository()}, err } - imageRefList, err := s.alternativeImageSources(ref, icspList.Items, idmsList.Items) + if len(icspList.Items) > 0 && len(itmsList.Items) > 0 { + err := fmt.Errorf("found both ICSP and ITMS resources, but only one or the other is supported") + return []reference.DockerImageReference{ref.AsRepository()}, err + } + + imageRefList, err := s.alternativeImageSources(ref, icspList.Items, idmsList.Items, itmsList.Items) if err != nil { klog.Errorf("error looking for alternate repositories: %s", err) return []reference.DockerImageReference{ref.AsRepository()}, nil @@ -69,7 +83,7 @@ func (s *simpleLookupICSP) FirstRequest( return imageRefList, nil } -func (s *simpleLookupICSP) OnFailure( +func (s *simpleLookupImageMirrorSets) OnFailure( ctx context.Context, ref reference.DockerImageReference, ) ([]reference.DockerImageReference, error) { return nil, nil @@ -91,10 +105,10 @@ type mirrorSource struct { } // alternativeImageSources returns unique list of DockerImageReference objects from list of -// ImageContentSourcePolicy or ImageDigestMirrorSet objects -func (s *simpleLookupICSP) alternativeImageSources( +// ImageContentSourcePolicy or ImageDigestMirrorSet, ImageTagMirrorSet objects +func (s *simpleLookupImageMirrorSets) alternativeImageSources( ref reference.DockerImageReference, icspList []operatorv1alpha1.ImageContentSourcePolicy, - idmsList []cfgv1.ImageDigestMirrorSet, + idmsList []cfgv1.ImageDigestMirrorSet, itmsList []cfgv1.ImageTagMirrorSet, ) ([]reference.DockerImageReference, error) { repo := ref.AsRepository().Exact() @@ -118,6 +132,17 @@ func (s *simpleLookupICSP) alternativeImageSources( } } + for _, itms := range itmsList { + s := mirrorSource{} + for _, itm := range itms.Spec.ImageTagMirrors { + s.source = itm.Source + for _, m := range itm.Mirrors { + s.mirrors = append(s.mirrors, string(m)) + } + mirrorSources = append(mirrorSources, s) + } + } + imageSources := []reference.DockerImageReference{} uniqueMirrors := map[reference.DockerImageReference]bool{} diff --git a/pkg/dockerregistry/server/simplelookupicsp_test.go b/pkg/dockerregistry/server/simplelookupicsp_test.go index 38d48db492..5f29f70c17 100644 --- a/pkg/dockerregistry/server/simplelookupicsp_test.go +++ b/pkg/dockerregistry/server/simplelookupicsp_test.go @@ -304,9 +304,10 @@ func TestFirstRequest(t *testing.T) { } cli := fake.NewSimpleClientset(icspRules...) cfgcli := cfgfake.NewSimpleClientset() - lookup := NewSimpleLookupICSPStrategy( + lookup := NewSimpleLookupImageMirrorSetsStrategy( cli.OperatorV1alpha1().ImageContentSourcePolicies(), cfgcli.ConfigV1().ImageDigestMirrorSets(), + cfgcli.ConfigV1().ImageTagMirrorSets(), ) ref, err := reference.Parse(tt.ref) @@ -332,9 +333,39 @@ func TestFirstRequest(t *testing.T) { } cli := fake.NewSimpleClientset() cfgcli := cfgfake.NewSimpleClientset(idmsRules...) - lookup := NewSimpleLookupICSPStrategy( + lookup := NewSimpleLookupImageMirrorSetsStrategy( cli.OperatorV1alpha1().ImageContentSourcePolicies(), cfgcli.ConfigV1().ImageDigestMirrorSets(), + cfgcli.ConfigV1().ImageTagMirrorSets(), + ) + + ref, err := reference.Parse(tt.ref) + if err != nil { + t.Fatalf("unexpected error parsing reference: %s", err) + } + + alternates, err := lookup.FirstRequest(context.Background(), ref) + if err != nil { + t.Fatalf("FirstRequest does not return error, received: %s", err) + } + + if !reflect.DeepEqual(alternates, tt.res) { + t.Errorf("expected %+v, received %+v", tt.res, alternates) + } + }) + + t.Run(tt.name, func(t *testing.T) { + tt.name = "itms-test-" + tt.name + itmsRules := []runtime.Object{} + for _, rule := range tt.rules { + itmsRules = append(itmsRules, newITMSRule(rule)) + } + cli := fake.NewSimpleClientset() + cfgcli := cfgfake.NewSimpleClientset(itmsRules...) + lookup := NewSimpleLookupImageMirrorSetsStrategy( + cli.OperatorV1alpha1().ImageContentSourcePolicies(), + cfgcli.ConfigV1().ImageDigestMirrorSets(), + cfgcli.ConfigV1().ImageTagMirrorSets(), ) ref, err := reference.Parse(tt.ref) @@ -391,3 +422,23 @@ func newIDMSRule(r rule) runtime.Object { }, } } + +func newITMSRule(r rule) runtime.Object { + itms := []v1.ImageTagMirrors{} + for _, e := range r.ruleElement { + itm := v1.ImageTagMirrors{} + itm.Source = e.source + for _, m := range e.mirrors { + itm.Mirrors = append(itm.Mirrors, v1.ImageMirror(m)) + } + itms = append(itms, itm) + } + return &v1.ImageTagMirrorSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: r.name, + }, + Spec: v1.ImageTagMirrorSetSpec{ + ImageTagMirrors: itms, + }, + } +} diff --git a/pkg/dockerregistry/server/util.go b/pkg/dockerregistry/server/util.go index 8a0a648eb5..d8639ea642 100644 --- a/pkg/dockerregistry/server/util.go +++ b/pkg/dockerregistry/server/util.go @@ -51,7 +51,7 @@ func getNamespaceName(resourceName string) (string, string, error) { // getImportContext loads secrets and returns a context for getting // distribution clients to remote repositories. -func getImportContext(ctx context.Context, ref *reference.DockerImageReference, secrets []corev1.Secret, m metrics.Pullthrough, icsp operatorv1alpha1.ImageContentSourcePolicyInterface, idms apicfgv1.ImageDigestMirrorSetInterface) (registryclient.RepositoryRetriever, error) { +func getImportContext(ctx context.Context, ref *reference.DockerImageReference, secrets []corev1.Secret, m metrics.Pullthrough, icsp operatorv1alpha1.ImageContentSourcePolicyInterface, idms apicfgv1.ImageDigestMirrorSetInterface, itms apicfgv1.ImageTagMirrorSetInterface) (registryclient.RepositoryRetriever, error) { req, err := dcontext.GetRequest(ctx) if err != nil { dcontext.GetLogger(ctx).Errorf("unable to get request from context: %v", err) @@ -79,7 +79,7 @@ func getImportContext(ctx context.Context, ref *reference.DockerImageReference, ).WithRequestModifiers( requesttrace.New(ctx, req), ).WithAlternateBlobSourceStrategy( - NewSimpleLookupICSPStrategy(icsp, idms), + NewSimpleLookupImageMirrorSetsStrategy(icsp, idms, itms), ).WithCredentialsFactory( &credentialStoreFactory{ keyring: keyring, diff --git a/pkg/dockerregistry/server/util_test.go b/pkg/dockerregistry/server/util_test.go index d3487e4d6a..7013f6eba8 100644 --- a/pkg/dockerregistry/server/util_test.go +++ b/pkg/dockerregistry/server/util_test.go @@ -30,6 +30,7 @@ func (m *mockMetricsPullThrough) DigestBlobStoreCache() metrics.Cache { func Test_getImportContext(t *testing.T) { icsp := operatorfake.NewSimpleClientset().OperatorV1alpha1().ImageContentSourcePolicies() idms := cfgfake.NewSimpleClientset().ConfigV1().ImageDigestMirrorSets() + itms := cfgfake.NewSimpleClientset().ConfigV1().ImageTagMirrorSets() tmpCredDir, err := ioutil.TempDir("", "credentials") if err != nil { t.Fatalf("error creating temp dir: %v", err) @@ -143,7 +144,7 @@ func Test_getImportContext(t *testing.T) { } retriever, err := getImportContext( - ctx, tt.ref, tt.secrets, &mockMetricsPullThrough{}, icsp, idms, + ctx, tt.ref, tt.secrets, &mockMetricsPullThrough{}, icsp, idms, itms, ) if err != nil { if len(tt.err) == 0 { diff --git a/test/integration/pullthrough/pullthrough_test.go b/test/integration/pullthrough/pullthrough_test.go index 69f5e76623..3e808063ad 100644 --- a/test/integration/pullthrough/pullthrough_test.go +++ b/test/integration/pullthrough/pullthrough_test.go @@ -630,3 +630,178 @@ func TestPullThroughIDMS(t *testing.T) { } } } + +func TestPullThroughITMS(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + namespace := "image-registry-test-integration-itms" + reponame := "testrepo" + repotag := "testtag" + isname := "test/" + reponame + + master := testframework.NewMaster(t) + defer master.Close() + + testuser := master.CreateUser("testuser", "testp@ssw0rd") + master.CreateProject(namespace, testuser.Name) + + cfgclient := cfgclientv1.NewForConfigOrDie(master.AdminKubeConfig()) + imgclient := imageclientv1.NewForConfigOrDie(testuser.KubeConfig()) + + imageData, err := testframework.NewSchema2ImageData() + if err != nil { + t.Fatal(err) + } + imgdgst := imageData.ManifestDigest.String() + + descriptors := map[string]int64{ + string(imageData.ConfigDigest): int64(len(imageData.Config)), + string(imageData.LayerDigest): int64(len(imageData.Layer)), + } + + remoteRegistryAddr, _, _ := testframework.CreateEphemeralRegistry( + t, master.AdminKubeConfig(), namespace, nil, + ) + + remoteRepo, err := testutil.NewInsecureRepository(remoteRegistryAddr+"/"+isname, nil) + if err != nil { + t.Fatal(err) + } + + if _, err := testframework.PushSchema2ImageData( + ctx, remoteRepo, repotag, imageData, + ); err != nil { + t.Fatal(err) + } + + itmsRule, err := cfgclient.ImageTagMirrorSets().Create( + ctx, + &cfgv1.ImageTagMirrorSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "image-registry-itms-testing", + }, + Spec: cfgv1.ImageTagMirrorSetSpec{ + ImageTagMirrors: []cfgv1.ImageTagMirrors{ + { + Source: "does.not.exist/repo/image", + Mirrors: []cfgv1.ImageMirror{ + cfgv1.ImageMirror(remoteRegistryAddr + "/" + isname), + }, + }, + }, + }, + }, + metav1.CreateOptions{}, + ) + if err != nil { + t.Fatalf("error creating ITMS rule: %s", err) + } + defer func() { + if err := cfgclient.ImageTagMirrorSets().Delete( + ctx, itmsRule.Name, metav1.DeleteOptions{}, + ); err != nil { + t.Errorf("error deleting ITMS rule: %s", err) + } + }() + + imgsrc := fmt.Sprintf("does.not.exist/repo/image:%s", repotag) + isi := &imageapiv1.ImageStreamImport{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: "myimagestream", + Annotations: map[string]string{ + imageapiv1.InsecureRepositoryAnnotation: "true", + }, + }, + Spec: imageapiv1.ImageStreamImportSpec{ + Import: true, + Images: []imageapiv1.ImageImportSpec{ + { + From: corev1.ObjectReference{ + Kind: "DockerImage", + Name: imgsrc, + }, + To: &corev1.LocalObjectReference{ + Name: repotag, + }, + ImportPolicy: imageapiv1.TagImportPolicy{ + Insecure: true, + }, + }, + }, + }, + } + + if isi, err = imgclient.ImageStreamImports(namespace).Create( + ctx, isi, metav1.CreateOptions{}, + ); err != nil { + t.Fatal(err) + } + + if len(isi.Status.Images) != 1 { + t.Fatalf("imported unexpected number of images (%d != 1)", len(isi.Status.Images)) + } + for i, image := range isi.Status.Images { + if image.Status.Status != metav1.StatusSuccess { + t.Fatalf("unexpected status %d: %#v", i, image.Status) + } + + if image.Image == nil { + t.Fatalf("unexpected empty image %d", i) + } + + if image.Image.Name != imgdgst { + t.Fatalf( + "unexpected image %d: %#v (expect %q)", + i, image.Image.Name, imgdgst, + ) + } + } + + istream, err := imgclient.ImageStreams(isi.Namespace).Get( + ctx, isi.Name, metav1.GetOptions{}, + ) + if err != nil { + t.Fatal(err) + } + + if istream.Annotations == nil { + istream.Annotations = make(map[string]string) + } + istream.Annotations[imageapiv1.InsecureRepositoryAnnotation] = "true" + + istream, err = imgclient.ImageStreams(istream.Namespace).Update( + ctx, istream, metav1.UpdateOptions{}, + ) + if err != nil { + t.Fatal(err) + } + + t.Logf("Run registry...") + registry := master.StartRegistry(t, testframework.DisableMirroring{}) + defer registry.Close() + + t.Logf("Pulling manifest by tag...") + if err := testPullThroughGetManifest( + registry.BaseURL(), isi, testuser.Name, testuser.Token, repotag, + ); err != nil { + t.Fatal(err) + } + + t.Logf("Pulling manifest by digest...") + if err := testPullThroughGetManifest( + registry.BaseURL(), isi, testuser.Name, testuser.Token, imgdgst, + ); err != nil { + t.Fatal(err) + } + + t.Logf("Pulling image blobs...") + for digest := range descriptors { + if err := testPullThroughStatBlob( + registry.BaseURL(), isi, testuser.Name, testuser.Token, digest, + ); err != nil { + t.Fatal(err) + } + } +}