func (t tagService) Tag(ctx context.Context, tag string, dgst distribution.Descriptor) error { imageStream, err := t.repo.getImageStream() if err != nil { context.GetLogger(ctx).Errorf("error retrieving ImageStream %s/%s: %v", t.repo.namespace, t.repo.name, err) return distribution.ErrRepositoryUnknown{Name: t.repo.Named().Name()} } image, err := t.repo.registryOSClient.Images().Get(dgst.Digest.String()) if err != nil { context.GetLogger(ctx).Errorf("unable to get image: %s", dgst.Digest.String()) return err } image.SetResourceVersion("") if !t.repo.pullthrough && !isImageManaged(image) { return distribution.ErrRepositoryUnknown{Name: t.repo.Named().Name()} } ism := imageapi.ImageStreamMapping{ ObjectMeta: kapi.ObjectMeta{ Namespace: imageStream.Namespace, Name: imageStream.Name, }, Tag: tag, Image: *image, } err = t.repo.registryOSClient.ImageStreamMappings(imageStream.Namespace).Create(&ism) if quotautil.IsErrorQuotaExceeded(err) { context.GetLogger(ctx).Errorf("denied creating ImageStreamMapping: %v", err) return distribution.ErrAccessDenied } return err }
// TestOriginQuotaAdmissionIsErrorQuotaExceeded verifies that if a resource exceeds allowed usage, the // admission will return error we can recognize. func TestOriginQuotaAdmissionIsErrorQuotaExceeded(t *testing.T) { resourceQuota := &kapi.ResourceQuota{ ObjectMeta: kapi.ObjectMeta{Name: "quota", Namespace: "test", ResourceVersion: "124"}, Status: kapi.ResourceQuotaStatus{ Hard: kapi.ResourceList{ imageapi.ResourceImageStreams: resource.MustParse("0"), }, Used: kapi.ResourceList{ imageapi.ResourceImageStreams: resource.MustParse("0"), }, }, } kubeClient := kfake.NewSimpleClientset(resourceQuota) osClient := testclient.NewSimpleFake(&imageapi.ImageStream{}) plugin := NewOriginResourceQuota(kubeClient).(*originQuotaAdmission) plugin.SetOriginQuotaRegistry(quota.NewOriginQuotaRegistry(osClient)) if err := plugin.Validate(); err != nil { t.Fatalf("unexpected error: %v", err) } newIS := &imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "is", }, } err := plugin.Admit(admission.NewAttributesRecord(newIS, nil, imageapi.Kind("ImageStream").WithVersion("version"), newIS.Namespace, newIS.Name, kapi.Resource("imageStreams").WithVersion("version"), "", admission.Create, nil)) if err == nil { t.Fatalf("Expected an error exceeding quota") } if !quotautil.IsErrorQuotaExceeded(err) { t.Fatalf("Expected error %q to be matched by IsErrorQuotaExceeded()", err.Error()) } }
func retryOnQuotaExceeded(t *testing.T, retries int, fn func() error) error { var err error for i := 0; i < retries; i++ { err = fn() if !quotautil.IsErrorQuotaExceeded(err) { return err } t.Logf("got quota exceeded error, retrying after sleep; number of retries remaining: %d", retries-i-1) time.Sleep(time.Second) } return err }
// transformUnsupported converts specific error conditions to unsupported func transformUnsupported(err error) error { if err == nil { return nil } if apierrs.IsNotFound(err) { status, ok := err.(apierrs.APIStatus) if !ok { return ErrImageStreamImportUnsupported } if status.Status().Details == nil || status.Status().Details.Kind == "" { return ErrImageStreamImportUnsupported } } // The ImageStreamImport resource exists in v1.1.1 of origin but is not yet // enabled by policy. A create request will return a Forbidden(403) error. // We want to return ErrImageStreamImportUnsupported to allow fallback behavior // in clients. if apierrs.IsForbidden(err) && !quotautil.IsErrorQuotaExceeded(err) { return ErrImageStreamImportUnsupported } return err }
g.By(fmt.Sprintf("trying to tag a docker image exceeding limit %v", limit)) is, err := oc.Client().ImageStreams(oc.Namespace()).Get("stream") o.Expect(err).NotTo(o.HaveOccurred()) is.Spec.Tags["foo"] = imageapi.TagReference{ Name: "foo", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: tag2Image["tag2"].DockerImageReference, }, ImportPolicy: imageapi.TagImportPolicy{ Insecure: true, }, } _, err = oc.Client().ImageStreams(oc.Namespace()).Update(is) o.Expect(err).To(o.HaveOccurred()) o.Expect(quotautil.IsErrorQuotaExceeded(err)).Should(o.Equal(true)) g.By("re-tagging the image under different tag") is, err = oc.Client().ImageStreams(oc.Namespace()).Get("stream") o.Expect(err).NotTo(o.HaveOccurred()) is.Spec.Tags["duplicate"] = imageapi.TagReference{ Name: "duplicate", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: tag2Image["tag1"].DockerImageReference, }, ImportPolicy: imageapi.TagImportPolicy{ Insecure: true, }, } _, err = oc.Client().ImageStreams(oc.Namespace()).Update(is)
func TestImageStreamAdmitStatusUpdate(t *testing.T) { defer testutil.DumpEtcdOnFailure(t) kClient, client := setupImageStreamAdmissionTest(t) images := []*imageapi.Image{} for _, name := range []string{imagetest.BaseImageWith1LayerDigest, imagetest.BaseImageWith2LayersDigest} { imageReference := fmt.Sprintf("openshift/test@%s", name) image := &imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: name, }, DockerImageReference: imageReference, } images = append(images, image) _, err := client.Images().Create(image) if err != nil { t.Fatal(err) } } limit := kapi.ResourceList{ imageapi.ResourceImageStreamTags: resource.MustParse("0"), imageapi.ResourceImageStreamImages: resource.MustParse("0"), } lrClient := kClient.LimitRanges(testutil.Namespace()) createLimitRangeOfType(t, lrClient, limitRangeName, imageapi.LimitTypeImageStream, limit) t.Logf("trying to create a new image stream with a tag exceeding limit %v", limit) _, err := client.ImageStreams(testutil.Namespace()).Create(&imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Name: "is", }, }) if err != nil { t.Fatalf("unexpected error: %v", err) } t.Logf("adding new tag to image stream status exceeding limit %v", limit) err = kclient.RetryOnConflict(kclient.DefaultRetry, func() error { is, err := client.ImageStreams(testutil.Namespace()).Get("is") if err != nil { return err } is.Status.Tags["tag1"] = imageapi.TagEventList{ Items: []imageapi.TagEvent{ { DockerImageReference: images[0].DockerImageReference, Image: images[0].Name, }, }, } _, err = client.ImageStreams(testutil.Namespace()).UpdateStatus(is) return err }) if err == nil { t.Fatalf("unexpected non-error") } if !quotautil.IsErrorQuotaExceeded(err) { t.Errorf("expected quota exceeded error, got instead: %v", err) } if !strings.Contains(err.Error(), string(imageapi.ResourceImageStreamImages)) { t.Errorf("expected resource %q in error string: %v", imageapi.ResourceImageStreamImages, err) } limit = bumpLimit(t, lrClient, limitRangeName, imageapi.ResourceImageStreamImages, "1") t.Logf("adding new tag to image stream status below limit %v", limit) err = kclient.RetryOnConflict(kclient.DefaultRetry, func() error { is, err := client.ImageStreams(testutil.Namespace()).Get("is") if err != nil { return err } is.Status.Tags["tag1"] = imageapi.TagEventList{ Items: []imageapi.TagEvent{ { DockerImageReference: images[0].DockerImageReference, Image: images[0].Name, }, }, } _, err = client.ImageStreams(testutil.Namespace()).UpdateStatus(is) return err }) if err != nil { t.Fatalf("unexpected error: %v", err) } t.Logf("adding new tag to image stream status exceeding limit %v", limit) err = kclient.RetryOnConflict(kclient.DefaultRetry, func() error { is, err := client.ImageStreams(testutil.Namespace()).Get("is") if err != nil { return err } is.Status.Tags["tag2"] = imageapi.TagEventList{ Items: []imageapi.TagEvent{ { DockerImageReference: images[1].DockerImageReference, Image: images[1].Name, }, }, } _, err = client.ImageStreams(testutil.Namespace()).UpdateStatus(is) return err }) if err == nil { t.Fatalf("unexpected non-error") } if !quotautil.IsErrorQuotaExceeded(err) { t.Errorf("expected quota exceeded error, got instead: %v", err) } if !strings.Contains(err.Error(), string(imageapi.ResourceImageStreamImages)) { t.Errorf("expected resource %q in error string: %v", imageapi.ResourceImageStreamImages, err) } t.Logf("re-tagging the image under different tag") err = kclient.RetryOnConflict(kclient.DefaultRetry, func() error { is, err := client.ImageStreams(testutil.Namespace()).Get("is") if err != nil { return err } is.Status.Tags["1again"] = imageapi.TagEventList{ Items: []imageapi.TagEvent{ { DockerImageReference: images[0].DockerImageReference, Image: images[0].Name, }, }, } _, err = client.ImageStreams(testutil.Namespace()).UpdateStatus(is) return err }) if err != nil { t.Fatalf("unexpected error: %v", err) } }
func TestImageStreamTagsAdmission(t *testing.T) { defer testutil.DumpEtcdOnFailure(t) kClient, client := setupImageStreamAdmissionTest(t) for i, name := range []string{imagetest.BaseImageWith1LayerDigest, imagetest.BaseImageWith2LayersDigest, imagetest.MiscImageDigest} { imageReference := fmt.Sprintf("openshift/test@%s", name) image := &imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: name, }, DockerImageReference: imageReference, } tag := fmt.Sprintf("tag%d", i+1) err := client.ImageStreamMappings(testutil.Namespace()).Create(&imageapi.ImageStreamMapping{ ObjectMeta: kapi.ObjectMeta{ Name: "src", }, Tag: tag, Image: *image, }) if err != nil { t.Fatal(err) } } limit := kapi.ResourceList{imageapi.ResourceImageStreamTags: resource.MustParse("0")} lrClient := kClient.LimitRanges(testutil.Namespace()) createLimitRangeOfType(t, lrClient, limitRangeName, imageapi.LimitTypeImageStream, limit) t.Logf("trying to create ImageStreamTag referencing isimage exceeding quota %v", limit) ist := &imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Name: "dest:tag1", }, Tag: &imageapi.TagReference{ Name: "1", From: &kapi.ObjectReference{ Kind: "ImageStreamImage", Name: "src@" + imagetest.BaseImageWith1LayerDigest, }, }, } _, err := client.ImageStreamTags(testutil.Namespace()).Update(ist) if err == nil { t.Fatalf("expected error") } if !quotautil.IsErrorQuotaExceeded(err) { t.Errorf("expected quota exceeded error, got instead: %v", err) } limit = bumpLimit(t, lrClient, limitRangeName, imageapi.ResourceImageStreamTags, "1") t.Logf("trying to create ImageStreamTag referencing isimage below quota %v", limit) ist = &imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Name: "dest:tag1", }, Tag: &imageapi.TagReference{ Name: "1", From: &kapi.ObjectReference{ Kind: "ImageStreamImage", Name: "src@" + imagetest.BaseImageWith1LayerDigest, }, }, } // we may hit cache with old limit, let's retry in such a case err = retryOnQuotaExceeded(t, 1, func() error { ist, err = client.ImageStreamTags(testutil.Namespace()).Update(ist) return err }) if err != nil { t.Fatalf("unexpected error: %v", err) } t.Logf("trying to create ImageStreamTag exceeding quota %v", limit) ist = &imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Name: "dest:tag2", }, Tag: &imageapi.TagReference{ Name: "2", From: &kapi.ObjectReference{ Kind: "ImageStreamImage", Name: "src@" + imagetest.BaseImageWith2LayersDigest, }, }, } ist, err = client.ImageStreamTags(testutil.Namespace()).Update(ist) if err == nil { t.Fatalf("expected error") } if !quotautil.IsErrorQuotaExceeded(err) { t.Errorf("expected quota exceeded error, got instead: %v", err) } t.Log("trying to create ImageStreamTag referencing isimage already referenced") ist = &imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Name: "dest:tag1again", }, Tag: &imageapi.TagReference{ Name: "tag1again", From: &kapi.ObjectReference{ Kind: "ImageStreamImage", Name: "src@" + imagetest.BaseImageWith1LayerDigest, }, }, } _, err = client.ImageStreamTags(testutil.Namespace()).Update(ist) if err != nil { t.Fatalf("unexpected error: %v", err) } t.Log("trying to create ImageStreamTag in a new image stream") ist = &imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Name: "new:misc", }, Tag: &imageapi.TagReference{ Name: "misc", From: &kapi.ObjectReference{ Kind: "ImageStreamImage", Name: "src@" + imagetest.MiscImageDigest, }, }, } _, err = client.ImageStreamTags(testutil.Namespace()).Update(ist) if err != nil { t.Fatalf("unexpected error: %v", err) } limit = bumpLimit(t, lrClient, limitRangeName, imageapi.ResourceImageStreamTags, "2") t.Logf("trying to create ImageStreamTag referencing istag below quota %v", limit) ist = &imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Name: "dest:tag2", }, Tag: &imageapi.TagReference{ Name: "2", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "src:tag2", }, }, } // we may hit cache with old limit, let's retry in such a case err = retryOnQuotaExceeded(t, 1, func() error { ist, err = client.ImageStreamTags(testutil.Namespace()).Update(ist) return err }) if err != nil { t.Fatalf("unexpected error: %v", err) } t.Logf("trying to create ImageStreamTag referencing istag exceeding quota %v", limit) ist = &imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Name: "dest:tag3", }, Tag: &imageapi.TagReference{ Name: "3", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "src:tag3", }, }, } _, err = client.ImageStreamTags(testutil.Namespace()).Update(ist) if err == nil { t.Fatal("creating image stream tag should have failed") } if !quotautil.IsErrorQuotaExceeded(err) { t.Fatalf("expected quota exceeded error, not: %v", err) } t.Log("trying to create ImageStreamTag referencing istag already referenced") ist = &imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{ Name: "dest:tag2again", }, Tag: &imageapi.TagReference{ Name: "tag2again", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "src:tag2", }, }, } _, err = client.ImageStreamTags(testutil.Namespace()).Update(ist) if err != nil { t.Fatal(err) } }
func TestImageStreamAdmitSpecUpdate(t *testing.T) { defer testutil.DumpEtcdOnFailure(t) kClient, client := setupImageStreamAdmissionTest(t) for i, name := range []string{imagetest.BaseImageWith1LayerDigest, imagetest.BaseImageWith2LayersDigest} { imageReference := fmt.Sprintf("openshift/test@%s", name) image := &imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: name, }, DockerImageReference: imageReference, } tag := fmt.Sprintf("tag%d", i+1) err := client.ImageStreamMappings(testutil.Namespace()).Create(&imageapi.ImageStreamMapping{ ObjectMeta: kapi.ObjectMeta{ Name: "src", }, Tag: tag, Image: *image, }) if err != nil { t.Fatal(err) } } limit := kapi.ResourceList{ imageapi.ResourceImageStreamTags: resource.MustParse("0"), imageapi.ResourceImageStreamImages: resource.MustParse("0"), } lrClient := kClient.LimitRanges(testutil.Namespace()) createLimitRangeOfType(t, lrClient, limitRangeName, imageapi.LimitTypeImageStream, limit) t.Logf("trying to create a new image stream with a tag exceeding limit %v", limit) _, err := client.ImageStreams(testutil.Namespace()).Create(&imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Name: "is", }, Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "tag1": { Name: "tag1", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "src:tag1", }, }, }, }, }) if err == nil { t.Fatal("unexpected non-error") } if !quotautil.IsErrorQuotaExceeded(err) { t.Errorf("expected quota exceeded error, got instead: %v", err) } for _, res := range []kapi.ResourceName{imageapi.ResourceImageStreamTags, imageapi.ResourceImageStreamImages} { if !strings.Contains(err.Error(), string(res)) { t.Errorf("expected resource %q in error string: %v", res, err) } } limit = bumpLimit(t, lrClient, limitRangeName, imageapi.ResourceImageStreamTags, "1") limit = bumpLimit(t, lrClient, limitRangeName, imageapi.ResourceImageStreamImages, "1") t.Logf("trying to create a new image stream with a tag below limit %v", limit) // we may hit cache with old limit, let's retry in such a case err = retryOnQuotaExceeded(t, 1, func() error { _, err = client.ImageStreams(testutil.Namespace()).Create(&imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Name: "is", }, Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "tag1": { Name: "tag1", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "src:tag1", }, }, }, }, }) return err }) if err != nil { t.Fatalf("unexpected error: %v", err) } t.Logf("adding new tag to image stream spec exceeding limit %v", limit) err = kclient.RetryOnConflict(kclient.DefaultRetry, func() error { is, err := client.ImageStreams(testutil.Namespace()).Get("is") if err != nil { return err } is.Spec.Tags["tag2"] = imageapi.TagReference{ Name: "tag2", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "src:tag2", }, } _, err = client.ImageStreams(testutil.Namespace()).Update(is) return err }) if err == nil { t.Fatalf("unexpected non-error") } if !quotautil.IsErrorQuotaExceeded(err) { t.Errorf("expected quota exceeded error, got instead: %v", err) } for _, res := range []kapi.ResourceName{imageapi.ResourceImageStreamTags, imageapi.ResourceImageStreamImages} { if !strings.Contains(err.Error(), string(res)) { t.Errorf("expected resource %q in error string: %v", res, err) } } t.Logf("re-tagging the image under different tag") err = kclient.RetryOnConflict(kclient.DefaultRetry, func() error { is, err := client.ImageStreams(testutil.Namespace()).Get("is") if err != nil { return err } is.Spec.Tags["1again"] = imageapi.TagReference{ Name: "1again", From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "src:tag1", }, } _, err = client.ImageStreams(testutil.Namespace()).Update(is) return err }) if err != nil { t.Fatalf("unexpected error: %v", err) } }
// Put creates or updates the named manifest. func (r *repository) Put(ctx context.Context, manifest distribution.Manifest, options ...distribution.ManifestServiceOption) (digest.Digest, error) { if err := r.checkPendingErrors(ctx); err != nil { return "", err } var canonical []byte // Resolve the payload in the manifest. mediatype, payload, err := manifest.Payload() if err != nil { return "", err } switch manifest.(type) { case *schema1.SignedManifest: canonical = manifest.(*schema1.SignedManifest).Canonical case *schema2.DeserializedManifest: canonical = payload default: err = fmt.Errorf("unrecognized manifest type %T", manifest) return "", regapi.ErrorCodeManifestInvalid.WithDetail(err) } if !r.acceptschema2 { if _, ok := manifest.(*schema1.SignedManifest); !ok { err = fmt.Errorf("schema version 2 disabled") return "", regapi.ErrorCodeManifestInvalid.WithDetail(err) } } // Calculate digest dgst := digest.FromBytes(canonical) // Upload to openshift ism := imageapi.ImageStreamMapping{ ObjectMeta: kapi.ObjectMeta{ Namespace: r.namespace, Name: r.name, }, Image: imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: dgst.String(), Annotations: map[string]string{ imageapi.ManagedByOpenShiftAnnotation: "true", }, }, DockerImageReference: fmt.Sprintf("%s/%s/%s@%s", r.registryAddr, r.namespace, r.name, dgst.String()), DockerImageManifest: string(payload), DockerImageManifestMediaType: mediatype, }, } for _, option := range options { if opt, ok := option.(distribution.WithTagOption); ok { ism.Tag = opt.Tag break } } if err = r.fillImageWithMetadata(manifest, &ism.Image); err != nil { return "", err } if err = r.registryOSClient.ImageStreamMappings(r.namespace).Create(&ism); err != nil { // if the error was that the image stream wasn't found, try to auto provision it statusErr, ok := err.(*kerrors.StatusError) if !ok { context.GetLogger(r.ctx).Errorf("error creating ImageStreamMapping: %s", err) return "", err } if quotautil.IsErrorQuotaExceeded(statusErr) { context.GetLogger(r.ctx).Errorf("denied creating ImageStreamMapping: %v", statusErr) return "", distribution.ErrAccessDenied } status := statusErr.ErrStatus if status.Code != http.StatusNotFound || (strings.ToLower(status.Details.Kind) != "imagestream" /*pre-1.2*/ && strings.ToLower(status.Details.Kind) != "imagestreams") || status.Details.Name != r.name { context.GetLogger(r.ctx).Errorf("error creating ImageStreamMapping: %s", err) return "", err } stream := imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Name: r.name, }, } uclient, ok := UserClientFrom(r.ctx) if !ok { context.GetLogger(r.ctx).Errorf("error creating user client to auto provision image stream: Origin user client unavailable") return "", statusErr } if _, err := uclient.ImageStreams(r.namespace).Create(&stream); err != nil { if quotautil.IsErrorQuotaExceeded(err) { context.GetLogger(r.ctx).Errorf("denied creating ImageStream: %v", err) return "", distribution.ErrAccessDenied } context.GetLogger(r.ctx).Errorf("error auto provisioning ImageStream: %s", err) return "", statusErr } // try to create the ISM again if err := r.registryOSClient.ImageStreamMappings(r.namespace).Create(&ism); err != nil { if quotautil.IsErrorQuotaExceeded(err) { context.GetLogger(r.ctx).Errorf("denied a creation of ImageStreamMapping: %v", err) return "", distribution.ErrAccessDenied } context.GetLogger(r.ctx).Errorf("error creating ImageStreamMapping: %s", err) return "", err } } return dgst, nil }
func TestVerifyLimits(t *testing.T) { for _, tc := range []struct { name string maxUsage kapi.ResourceList is imageapi.ImageStream exceededResources []kapi.ResourceName }{ { name: "no limits", is: imageapi.ImageStream{ Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), Image: imagetest.BaseImageWith1LayerDigest, }, }, }, }, }, }, }, { name: "zero limits", maxUsage: kapi.ResourceList{ imageapi.ResourceImageStreamImages: resource.MustParse("0"), imageapi.ResourceImageStreamTags: resource.MustParse("0"), }, }, { name: "exceed images", maxUsage: kapi.ResourceList{ imageapi.ResourceImageStreamImages: resource.MustParse("0"), imageapi.ResourceImageStreamTags: resource.MustParse("0"), }, is: imageapi.ImageStream{ Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), Image: imagetest.BaseImageWith1LayerDigest, }, }, }, }, }, }, exceededResources: []kapi.ResourceName{imageapi.ResourceImageStreamImages}, }, { name: "exceed tags", maxUsage: kapi.ResourceList{ imageapi.ResourceImageStreamTags: resource.MustParse("0"), }, is: imageapi.ImageStream{ Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "new": { Name: "new", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: imagetest.MakeDockerImageReference("test", "noshared", imagetest.ChildImageWith2LayersDigest), }, }, }, }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), Image: imagetest.BaseImageWith1LayerDigest, }, }, }, }, }, }, exceededResources: []kapi.ResourceName{imageapi.ResourceImageStreamTags}, }, { name: "exceed tags and images", maxUsage: kapi.ResourceList{ imageapi.ResourceImageStreamTags: resource.MustParse("1"), imageapi.ResourceImageStreamImages: resource.MustParse("0"), }, is: imageapi.ImageStream{ Spec: imageapi.ImageStreamSpec{ Tags: map[string]imageapi.TagReference{ "new": { Name: "new", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: imagetest.MakeDockerImageReference("test", "noshared", imagetest.ChildImageWith2LayersDigest), }, }, "good": { Name: "good", From: &kapi.ObjectReference{ Kind: "DockerImage", Name: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), }, }, }, }, Status: imageapi.ImageStreamStatus{ Tags: map[string]imageapi.TagEventList{ "latest": { Items: []imageapi.TagEvent{ { DockerImageReference: imagetest.MakeDockerImageReference("test", "is", imagetest.BaseImageWith1LayerDigest), Image: imagetest.BaseImageWith1LayerDigest, }, }, }, }, }, }, exceededResources: []kapi.ResourceName{ imageapi.ResourceImageStreamTags, imageapi.ResourceImageStreamImages, }, }, } { limitRange := &kapi.LimitRange{ ObjectMeta: kapi.ObjectMeta{ Namespace: "test", Name: "limitrange", }, Spec: kapi.LimitRangeSpec{ Limits: []kapi.LimitRangeItem{ { Type: imageapi.LimitTypeImageStream, Max: tc.maxUsage, }, }, }, } verifier := &limitVerifier{ limiter: LimitRangesForNamespaceFunc(func(ns string) ([]*kapi.LimitRange, error) { return []*kapi.LimitRange{limitRange}, nil }), } err := verifier.VerifyLimits("test", &tc.is) if len(tc.exceededResources) > 0 && err == nil { t.Errorf("[%s] unexpected non-error while following resources should fail: %v", tc.name, tc.exceededResources) continue } if len(tc.exceededResources) == 0 && err != nil { t.Errorf("[%s] unexpected error: %v", tc.name, err) continue } for _, r := range tc.exceededResources { if !strings.Contains(err.Error(), string(r)) { t.Errorf("[%s] expected resource %q not found in error message: %v", tc.name, r, err) } if !quotautil.IsErrorQuotaExceeded(err) { t.Errorf("[%s] error %q not matched by IsErrorQuotaExceeded", tc.name, err) } } } }
// Put creates or updates the named manifest. func (r *repository) Put(manifest *schema1.SignedManifest) error { // Resolve the payload in the manifest. payload, err := manifest.Payload() if err != nil { return err } // Calculate digest dgst, err := digest.FromBytes(payload) if err != nil { return err } // Upload to openshift ism := imageapi.ImageStreamMapping{ ObjectMeta: kapi.ObjectMeta{ Namespace: r.namespace, Name: r.name, }, Tag: manifest.Tag, Image: imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: dgst.String(), Annotations: map[string]string{ imageapi.ManagedByOpenShiftAnnotation: "true", }, }, DockerImageReference: fmt.Sprintf("%s/%s/%s@%s", r.registryAddr, r.namespace, r.name, dgst.String()), DockerImageManifest: string(manifest.Raw), }, } if err := r.fillImageWithMetadata(manifest, &ism.Image); err != nil { return err } if err := r.registryOSClient.ImageStreamMappings(r.namespace).Create(&ism); err != nil { // if the error was that the image stream wasn't found, try to auto provision it statusErr, ok := err.(*kerrors.StatusError) if !ok { context.GetLogger(r.ctx).Errorf("error creating ImageStreamMapping: %s", err) return err } if quotautil.IsErrorQuotaExceeded(statusErr) { context.GetLogger(r.ctx).Errorf("denied creating ImageStreamMapping: %v", statusErr) return distribution.ErrAccessDenied } status := statusErr.ErrStatus if status.Code != http.StatusNotFound || (strings.ToLower(status.Details.Kind) != "imagestream" /*pre-1.2*/ && strings.ToLower(status.Details.Kind) != "imagestreams") || status.Details.Name != r.name { context.GetLogger(r.ctx).Errorf("error creating ImageStreamMapping: %s", err) return err } stream := imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Name: r.name, }, } client, ok := UserClientFrom(r.ctx) if !ok { context.GetLogger(r.ctx).Errorf("error creating user client to auto provision image stream: Origin user client unavailable") return statusErr } if _, err := client.ImageStreams(r.namespace).Create(&stream); err != nil { if quotautil.IsErrorQuotaExceeded(err) { context.GetLogger(r.ctx).Errorf("denied creating ImageStream: %v", err) return distribution.ErrAccessDenied } context.GetLogger(r.ctx).Errorf("error auto provisioning ImageStream: %s", err) return statusErr } // try to create the ISM again if err := r.registryOSClient.ImageStreamMappings(r.namespace).Create(&ism); err != nil { if quotautil.IsErrorQuotaExceeded(err) { context.GetLogger(r.ctx).Errorf("denied a creation of ImageStreamMapping: %v", err) return distribution.ErrAccessDenied } context.GetLogger(r.ctx).Errorf("error creating ImageStreamMapping: %s", err) return err } } // Grab each json signature and store them. signatures, err := manifest.Signatures() if err != nil { return err } for _, signature := range signatures { if err := r.Signatures().Put(dgst, signature); err != nil { context.GetLogger(r.ctx).Errorf("error storing signature: %s", err) return err } } return nil }