func setupImageStreamAdmissionTest(t *testing.T) (*kclient.Client, *client.Client) { testutil.RequireEtcd(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } kClient, err := testutil.GetClusterAdminKubeClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } client, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } err = testutil.CreateNamespace(clusterAdminKubeConfig, testutil.Namespace()) if err != nil { t.Errorf("unexpected error: %v", err) } _, err = client.ImageStreams(testutil.Namespace()).Create(&imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Name: "src", }, }) if err != nil { t.Fatal(err) } return kClient, client }
func setupImageStreamAdmissionTest(t *testing.T) (*kclient.Client, *client.Client) { testutil.RequireEtcd(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } kClient, err := testutil.GetClusterAdminKubeClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } client, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } err = testutil.CreateNamespace(clusterAdminKubeConfig, testutil.Namespace()) if err != nil { t.Errorf("unexpected error: %v", err) } for { _, err = client.ImageStreams(testutil.Namespace()).Create(newImageStreamWithSpecTags("src", nil)) t.Logf("initing: %v", err) if err != nil { if errForbiddenWithRetry(err) { t.Logf("waiting for limit ranger to catch up: %v", err) continue } t.Fatalf("err: %#v", err) } break } return kClient, client }
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 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 *manifest.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(payload), }, } if err := r.registryClient.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 { log.Errorf("Error creating ImageStreamMapping: %s", err) return err } status := statusErr.ErrStatus if status.Code != http.StatusNotFound || status.Details.Kind != "imageStream" || status.Details.Name != r.name { log.Errorf("Error creating ImageStreamMapping: %s", err) return err } stream := imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Name: r.name, }, } client, ok := UserClientFrom(ctx) if !ok { log.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 { log.Errorf("Error auto provisioning image stream: %s", err) return statusErr } // try to create the ISM again if err := unversioned.RetryOnConflict(unversioned.DefaultRetry, func() error { return r.registryClient.ImageStreamMappings(r.namespace).Create(&ism) }); err != nil { log.Errorf("Error creating image stream mapping: %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 { log.Errorf("Error storing signature: %s", err) return err } } return nil }
// 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.registryClient.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 kerrors.IsForbidden(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 { context.GetLogger(r.ctx).Errorf("Error auto provisioning image stream: %s", err) return statusErr } // try to create the ISM again if err := r.registryClient.ImageStreamMappings(r.namespace).Create(&ism); err != nil { context.GetLogger(r.ctx).Errorf("Error creating image stream mapping: %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 }