From c5ef180bb0304ddfdc0e6c0ea0d927136a2b225a Mon Sep 17 00:00:00 2001 From: Yamunadevi Shanmugam Date: Mon, 11 May 2026 12:11:41 +0530 Subject: [PATCH 1/7] fixed e2e flakiness for resource quota_ban creating new images --- test/extended/quota/resourcequota.go | 6 +++-- test/extended/util/search_latest_image.go | 29 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 test/extended/util/search_latest_image.go diff --git a/test/extended/quota/resourcequota.go b/test/extended/quota/resourcequota.go index 179f1ffe5cd0..d1ef0ac88933 100644 --- a/test/extended/quota/resourcequota.go +++ b/test/extended/quota/resourcequota.go @@ -256,17 +256,19 @@ var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { return nil }) o.Expect(err).NotTo(o.HaveOccurred()) + cliImage, _ := exutil.SearchLatestImage(oc, "cli") + toolsImage, _ := exutil.SearchLatestImage(oc, "tools") images := []struct { Image string Tag string }{ { - Image: "quay.io/openshifttest/hello-openshift@sha256:4200f438cf2e9446f6bcff9d67ceea1f69ed07a2f83363b7fb52529f7ddd8a83", + Image: cliImage, Tag: "v1", }, { - Image: "quay.io/openshifttest/base-alpine@sha256:3126e4eed4a3ebd8bf972b2453fa838200988ee07c01b2251e3ea47e4b1f245c", + Image: toolsImage, Tag: "v2", }, { diff --git a/test/extended/util/search_latest_image.go b/test/extended/util/search_latest_image.go new file mode 100644 index 000000000000..c3555aa6a33a --- /dev/null +++ b/test/extended/util/search_latest_image.go @@ -0,0 +1,29 @@ +package util + +import ( + "context" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const openshiftPayloadImageNamespace = "openshift" + +// SearchLatestImage returns the resolved docker pull spec for imageName:latest in the openshift +// namespace (payload imagestreams such as cli, tools, must-gather). The cluster serves the +// architecture-appropriate image; callers must not hardcode digests. +func SearchLatestImage(oc *CLI, imageName string) (string, error) { + if imageName == "" { + return "", fmt.Errorf("imageName is empty") + } + ctx := context.Background() + istag, err := oc.AdminImageClient().ImageV1().ImageStreamTags(openshiftPayloadImageNamespace).Get(ctx, imageName+":latest", metav1.GetOptions{}) + if err != nil { + return "", err + } + ref := istag.Image.DockerImageReference + if ref == "" { + return "", fmt.Errorf("empty DockerImageReference for %s/%s:latest", openshiftPayloadImageNamespace, imageName) + } + return ref, nil +} From d3cd554cc22248bdf904c578d0850535d780283c Mon Sep 17 00:00:00 2001 From: Yamunadevi Shanmugam Date: Mon, 11 May 2026 17:56:12 +0530 Subject: [PATCH 2/7] Updated the test to run in disconnected clusters --- test/extended/quota/resourcequota.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/extended/quota/resourcequota.go b/test/extended/quota/resourcequota.go index d1ef0ac88933..b573c84b1a4a 100644 --- a/test/extended/quota/resourcequota.go +++ b/test/extended/quota/resourcequota.go @@ -217,7 +217,7 @@ var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { o.Expect(err).NotTo(o.HaveOccurred()) }) - g.It("when exceed openshift.io/image-tags will ban to create new image references in the project [Skipped:Disconnected]", func() { + g.It("when exceed openshift.io/image-tags will ban to create new image references in the project", func() { testProject := oc.Namespace() testResourceQuotaName := "my-image-tag-quota" clusterAdminKubeClient := oc.AdminKubeClient() From 4a6d4bf98cbedd216f442ed7d9a61063bbd64b28 Mon Sep 17 00:00:00 2001 From: Yamunadevi Shanmugam Date: Mon, 11 May 2026 18:48:58 +0530 Subject: [PATCH 3/7] Fixed requested changes by coderabbit --- test/extended/quota/resourcequota.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/extended/quota/resourcequota.go b/test/extended/quota/resourcequota.go index b573c84b1a4a..02512c13ca87 100644 --- a/test/extended/quota/resourcequota.go +++ b/test/extended/quota/resourcequota.go @@ -256,8 +256,10 @@ var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { return nil }) o.Expect(err).NotTo(o.HaveOccurred()) - cliImage, _ := exutil.SearchLatestImage(oc, "cli") - toolsImage, _ := exutil.SearchLatestImage(oc, "tools") + cliImage, err := exutil.SearchLatestImage(oc, "cli") + o.Expect(err).NotTo(o.HaveOccurred()) + toolsImage, err := exutil.SearchLatestImage(oc, "tools") + o.Expect(err).NotTo(o.HaveOccurred()) images := []struct { Image string From b1ca2e2c3c6635d663212fa20666c96f4eaa6914 Mon Sep 17 00:00:00 2001 From: Yamunadevi Shanmugam Date: Wed, 13 May 2026 14:18:00 +0530 Subject: [PATCH 4/7] updated changes for hypershift specifically --- tatus | 133 ++++++++++++++++++++++ test/extended/quota/resourcequota.go | 14 +-- test/extended/util/search_latest_image.go | 29 ----- 3 files changed, 138 insertions(+), 38 deletions(-) create mode 100644 tatus delete mode 100644 test/extended/util/search_latest_image.go diff --git a/tatus b/tatus new file mode 100644 index 000000000000..ae54f2491920 --- /dev/null +++ b/tatus @@ -0,0 +1,133 @@ +diff --git a/test/extended/quota/resourcequota.go b/test/extended/quota/resourcequota.go +index 179f1ffe5c..2f42505f09 100644 +--- a/test/extended/quota/resourcequota.go ++++ b/test/extended/quota/resourcequota.go +@@ -15,10 +15,16 @@ import ( + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ++ "k8s.io/apimachinery/pkg/util/wait" + utilwait "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + ) +  ++// defaultIntegratedRegistryService is the in-cluster Service DNS for the default OpenShift image ++// registry. Use this for import-image --from so importer traffic stays on the pod network instead ++// of following an external route that InternalRegistryHostname may advertise. ++const defaultIntegratedRegistryService = "image-registry.openshift-image-registry.svc:5000" ++ + var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { + defer g.GinkgoRecover() + oc := exutil.NewCLI("object-count-rq") +@@ -165,7 +171,8 @@ var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { + o.Expect(err).NotTo(o.HaveOccurred()) + }) +  +- g.It("check the quota after import-image with --all option [Skipped:Disconnected]", func() { ++ g.It("check the quota after import-image with --all option", func() { ++ ctx := context.Background() + testProject := oc.SetupProject() + testResourceQuotaName := "my-imagestream-quota-" + testProject + clusterAdminKubeClient := oc.AdminKubeClient() +@@ -194,11 +201,89 @@ var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { + }) + o.Expect(err).NotTo(o.HaveOccurred()) +  +- g.By("trying to tag a container image") +- err = oc.AsAdmin().WithoutNamespace().Run("import-image").Args("centos", "--from=quay.io/openshifttest/alpine", "--confirm=true", "--all=true", "-n", testProject).Execute() ++ g.By("waiting until the integrated registry is published (readiness for import-image)") ++ _, err = exutil.WaitForInternalRegistryHostname(oc) ++ o.Expect(err).NotTo(o.HaveOccurred()) ++ ++ // Build a multi-tag ImageStream in this namespace by referencing openshift/cli:latest only ++ // (disconnected-safe). The integrated registry exposes it as one repository with several tags. ++ sourceISName := "rq-local-multi-src" ++ sourceTags := []string{"alpha", "beta", "gamma"} ++ for _, tag := range sourceTags { ++ err = oc.AsAdmin().WithoutNamespace().Run("tag").Args( ++ "openshift/cli:latest", ++ fmt.Sprintf("%s:%s", sourceISName, tag), ++ "-n", testProject, ++ ).Execute() ++ o.Expect(err).NotTo(o.HaveOccurred()) ++ err = exutil.WaitForAnImageStreamTag(oc, testProject, sourceISName, tag) ++ o.Expect(err).NotTo(o.HaveOccurred()) ++ } ++ ++ g.By("checking quota after creating one local ImageStream with multiple tags") ++ err = waitForResourceQuotaStatus(clusterAdminKubeClient, testResourceQuotaName, testProject, func(actualResourceQuota *corev1.ResourceQuota) error { ++ expectedUsedStatus := corev1.ResourceList{ ++ "openshift.io/imagestreams": resource.MustParse("1"), ++ } ++ if !equality.Semantic.DeepEqual(actualResourceQuota.Status.Used, expectedUsedStatus) { ++ return fmt.Errorf("unexpected current total usage: actual: %#v, expected: %#v", actualResourceQuota.Status.Used, expectedUsedStatus) ++ } ++ return nil ++ }) ++ o.Expect(err).NotTo(o.HaveOccurred()) ++ ++ sourceIS, err := oc.AdminImageClient().ImageV1().ImageStreams(testProject).Get(ctx, sourceISName, metav1.GetOptions{}) ++ o.Expect(err).NotTo(o.HaveOccurred()) ++ o.Expect(len(sourceIS.Spec.Tags)).To(o.Equal(len(sourceTags))) ++ ++ bulkISName := "rq-bulk-import-is" ++ internalSource := fmt.Sprintf("%s/%s/%s", defaultIntegratedRegistryService, testProject, sourceISName) ++ ++ g.By("importing all tags from the local repository into one new ImageStream (bulk import adds exactly one ImageStream)") ++ err = oc.AsAdmin().WithoutNamespace().Run("import-image").Args( ++ bulkISName, ++ "--from="+internalSource, ++ "--confirm=true", ++ "--all=true", ++ "--request-timeout=5m", ++ "-n", testProject, ++ ).Execute() ++ o.Expect(err).NotTo(o.HaveOccurred()) ++ ++ var imported *imagev1.ImageStream ++ // wait.PollUntilContextTimeout replaces PollImmediate ++ // Parameters: ctx, interval, timeout, immediate (true = PollImmediate behavior), condition ++ err = wait.PollUntilContextTimeout(ctx, 1*time.Second, 1*time.Minute, true, func(ctx context.Context) (bool, error) { ++ var getErr error ++ imported, getErr = oc.AdminImageClient().ImageV1().ImageStreams(testProject).Get(ctx, bulkISName, metav1.GetOptions{}) ++ if getErr != nil { ++ // Return false, nil to keep retrying if the IS isn't found yet ++ return false, nil ++ } ++ // Return true only when the tags are actually there ++ return len(imported.Spec.Tags) >= 2, nil ++ }) ++ ++ o.Expect(err).NotTo(o.HaveOccurred(), ++ "import-image --all should copy multiple tags onto a single ImageStream (timed out waiting for tags to populate)") ++ g.By("checking that bulk import increased ImageStream quota by exactly one") ++ err = waitForResourceQuotaStatus(clusterAdminKubeClient, testResourceQuotaName, testProject, func(actualResourceQuota *corev1.ResourceQuota) error { ++ expectedUsedStatus := corev1.ResourceList{ ++ "openshift.io/imagestreams": resource.MustParse("2"), ++ } ++ if !equality.Semantic.DeepEqual(actualResourceQuota.Status.Used, expectedUsedStatus) { ++ return fmt.Errorf("unexpected current total usage: actual: %#v, expected: %#v", actualResourceQuota.Status.Used, expectedUsedStatus) ++ } ++ return nil ++ }) + o.Expect(err).NotTo(o.HaveOccurred()) +  +- err = oc.AsAdmin().WithoutNamespace().Run("tag").Args("quay.io/openshifttest/base-alpine@sha256:3126e4eed4a3ebd8bf972b2453fa838200988ee07c01b2251e3ea47e4b1f245c", "--source=docker", "mystream:latest", "-n", testProject).Execute() ++ g.By("tagging another ImageStream from an in-cluster ImageStreamTag") ++ err = oc.AsAdmin().WithoutNamespace().Run("tag").Args( ++ "openshift/cli:latest", ++ "mystream:latest", ++ "-n", testProject, ++ ).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) +  + err = exutil.WaitForAnImageStreamTag(oc, testProject, "mystream", "latest") +@@ -207,7 +292,7 @@ var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { + g.By("checking the imagestream usage again") + err = waitForResourceQuotaStatus(clusterAdminKubeClient, testResourceQuotaName, testProject, func(actualResourceQuota *corev1.ResourceQuota) error { + expectedUsedStatus := corev1.ResourceList{ +- "openshift.io/imagestreams": resource.MustParse("2"), ++ "openshift.io/imagestreams": resource.MustParse("3"), + } + if !equality.Semantic.DeepEqual(actualResourceQuota.Status.Used, expectedUsedStatus) { + return fmt.Errorf("unexpected current total usage: actual: %#v, expected: %#v", actualResourceQuota.Status.Used, expectedUsedStatus) diff --git a/test/extended/quota/resourcequota.go b/test/extended/quota/resourcequota.go index 02512c13ca87..97cc2e18be22 100644 --- a/test/extended/quota/resourcequota.go +++ b/test/extended/quota/resourcequota.go @@ -218,7 +218,7 @@ var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { }) g.It("when exceed openshift.io/image-tags will ban to create new image references in the project", func() { - testProject := oc.Namespace() + testProject := oc.SetupProject() testResourceQuotaName := "my-image-tag-quota" clusterAdminKubeClient := oc.AdminKubeClient() @@ -256,32 +256,28 @@ var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { return nil }) o.Expect(err).NotTo(o.HaveOccurred()) - cliImage, err := exutil.SearchLatestImage(oc, "cli") - o.Expect(err).NotTo(o.HaveOccurred()) - toolsImage, err := exutil.SearchLatestImage(oc, "tools") - o.Expect(err).NotTo(o.HaveOccurred()) images := []struct { Image string Tag string }{ { - Image: cliImage, + Image: "openshift/cli:latest", Tag: "v1", }, { - Image: toolsImage, + Image: "openshift/tools:latest", Tag: "v2", }, { - Image: "openshift/hello-openshift", + Image: "openshift/must-gather:latest", Tag: "v3", }, } for _, u := range images { g.By("trying to tag a container image with " + u.Tag) - err = oc.Run("tag").Args(u.Image, "--source=docker", "mystream:"+u.Tag, "-n", testProject).Execute() + err = oc.Run("tag").Args(u.Image, "--source=istag", "mystream:"+u.Tag, "-n", testProject).Execute() if u.Tag != "v3" { o.Expect(err).NotTo(o.HaveOccurred()) err = exutil.WaitForAnImageStreamTag(oc, testProject, "mystream", u.Tag) diff --git a/test/extended/util/search_latest_image.go b/test/extended/util/search_latest_image.go deleted file mode 100644 index c3555aa6a33a..000000000000 --- a/test/extended/util/search_latest_image.go +++ /dev/null @@ -1,29 +0,0 @@ -package util - -import ( - "context" - "fmt" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const openshiftPayloadImageNamespace = "openshift" - -// SearchLatestImage returns the resolved docker pull spec for imageName:latest in the openshift -// namespace (payload imagestreams such as cli, tools, must-gather). The cluster serves the -// architecture-appropriate image; callers must not hardcode digests. -func SearchLatestImage(oc *CLI, imageName string) (string, error) { - if imageName == "" { - return "", fmt.Errorf("imageName is empty") - } - ctx := context.Background() - istag, err := oc.AdminImageClient().ImageV1().ImageStreamTags(openshiftPayloadImageNamespace).Get(ctx, imageName+":latest", metav1.GetOptions{}) - if err != nil { - return "", err - } - ref := istag.Image.DockerImageReference - if ref == "" { - return "", fmt.Errorf("empty DockerImageReference for %s/%s:latest", openshiftPayloadImageNamespace, imageName) - } - return ref, nil -} From 39c6d0b052567431b7c6bfbae5aeb639f02f37ee Mon Sep 17 00:00:00 2001 From: Yamunadevi Shanmugam Date: Wed, 13 May 2026 15:17:30 +0530 Subject: [PATCH 5/7] fixed samples unavailable in hypershift --- test/extended/quota/resourcequota.go | 110 ++++++++++++++++++++------- 1 file changed, 83 insertions(+), 27 deletions(-) diff --git a/test/extended/quota/resourcequota.go b/test/extended/quota/resourcequota.go index 97cc2e18be22..53611222640b 100644 --- a/test/extended/quota/resourcequota.go +++ b/test/extended/quota/resourcequota.go @@ -3,12 +3,14 @@ package quota import ( "context" "fmt" + "sort" "time" g "github.com/onsi/ginkgo/v2" o "github.com/onsi/gomega" imagev1 "github.com/openshift/api/image/v1" + "github.com/openshift/library-go/pkg/image/imageutil" exutil "github.com/openshift/origin/test/extended/util" corev1 "k8s.io/api/core/v1" @@ -257,40 +259,94 @@ var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { }) o.Expect(err).NotTo(o.HaveOccurred()) - images := []struct { - Image string - Tag string - }{ - { - Image: "openshift/cli:latest", - Tag: "v1", - }, - { - Image: "openshift/tools:latest", - Tag: "v2", - }, - { - Image: "openshift/must-gather:latest", - Tag: "v3", - }, + sourceISTags, ok := pickOpenshiftSourceISTagsForTagQuota(oc) + if !ok { + g.Skip("need three openshift ImageStreams with at least one resolved tag each for oc tag --source=istag; skipping image-tag quota enforcement check") } - for _, u := range images { - g.By("trying to tag a container image with " + u.Tag) - err = oc.Run("tag").Args(u.Image, "--source=istag", "mystream:"+u.Tag, "-n", testProject).Execute() - if u.Tag != "v3" { - o.Expect(err).NotTo(o.HaveOccurred()) - err = exutil.WaitForAnImageStreamTag(oc, testProject, "mystream", u.Tag) - o.Expect(err).NotTo(o.HaveOccurred()) - } else { - o.Expect(err).To(o.HaveOccurred()) - o.Expect(err.Error()).To(o.MatchRegexp(`.*forbidden.*[Ee]xceed`)) - } + tags := []string{"v1", "v2", "v3"} + for i := range 2 { + g.By("trying to tag a container image with " + tags[i] + " from " + sourceISTags[i]) + err = oc.Run("tag").Args(sourceISTags[i], "--source=istag", "mystream:"+tags[i], "-n", testProject).Execute() + o.Expect(err).NotTo(o.HaveOccurred()) + err = exutil.WaitForAnImageStreamTag(oc, testProject, "mystream", tags[i]) + o.Expect(err).NotTo(o.HaveOccurred()) } + + g.By("waiting until mystream records both tags so limit enforcement sees tag count before v3") + err = waitForImageStreamStatusTagsPopulated(oc, testProject, "mystream", tags[:2]) + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("trying to tag a container image with v3 from " + sourceISTags[2]) + err = oc.Run("tag").Args(sourceISTags[2], "--source=istag", "mystream:v3", "-n", testProject).Execute() + o.Expect(err).To(o.HaveOccurred()) + o.Expect(err.Error()).To(o.MatchRegexp(`.*forbidden.*[Ee]xceed`)) }) }) }) +func pickOpenshiftSourceISTagsForTagQuota(oc *exutil.CLI) ([]string, bool) { + const ns = "openshift" + list, err := oc.AdminImageClient().ImageV1().ImageStreams(ns).List(context.Background(), metav1.ListOptions{}) + if err != nil { + return nil, false + } + sort.Slice(list.Items, func(i, j int) bool { return list.Items[i].Name < list.Items[j].Name }) + + var refs []string + for i := range list.Items { + is := &list.Items[i] + if len(is.Spec.Tags) == 0 { + continue + } + names := make([]string, len(is.Spec.Tags)) + for j := range is.Spec.Tags { + names[j] = is.Spec.Tags[j].Name + } + sort.Slice(names, func(i, j int) bool { + a, b := names[i], names[j] + aLatest, bLatest := a == imagev1.DefaultImageTag, b == imagev1.DefaultImageTag + if aLatest != bLatest { + return aLatest + } + return a < b + }) + tag := "" + for _, n := range names { + ev, ok := imageutil.StatusHasTag(is, n) + if ok && len(ev.Items) > 0 { + tag = n + break + } + } + if tag == "" { + continue + } + refs = append(refs, fmt.Sprintf("%s/%s:%s", ns, is.Name, tag)) + if len(refs) == 3 { + return refs, true + } + } + return nil, false +} + +func waitForImageStreamStatusTagsPopulated(oc *exutil.CLI, namespace, name string, tags []string) error { + streams := oc.AdminImageClient().ImageV1().ImageStreams(namespace) + return utilwait.PollImmediate(200*time.Millisecond, 2*time.Minute, func() (bool, error) { + is, err := streams.Get(context.Background(), name, metav1.GetOptions{}) + if err != nil { + return false, nil + } + for _, t := range tags { + st, ok := imageutil.StatusHasTag(is, t) + if !ok || len(st.Items) == 0 { + return false, nil + } + } + return true, nil + }) +} + func waitForResourceQuotaStatus(clusterAdminKubeClient kubernetes.Interface, name string, namespace string, conditionFn func(*corev1.ResourceQuota) error) error { var pollErr error err := utilwait.PollImmediate(100*time.Millisecond, QuotaWaitTimeout, func() (done bool, err error) { From b96e4f79f92bcf885b23b7b22e6356cefe829970 Mon Sep 17 00:00:00 2001 From: Yamunadevi Shanmugam Date: Wed, 13 May 2026 15:20:08 +0530 Subject: [PATCH 6/7] fixed samples unavailable in hypershift --- tatus | 133 ---------------------------------------------------------- 1 file changed, 133 deletions(-) delete mode 100644 tatus diff --git a/tatus b/tatus deleted file mode 100644 index ae54f2491920..000000000000 --- a/tatus +++ /dev/null @@ -1,133 +0,0 @@ -diff --git a/test/extended/quota/resourcequota.go b/test/extended/quota/resourcequota.go -index 179f1ffe5c..2f42505f09 100644 ---- a/test/extended/quota/resourcequota.go -+++ b/test/extended/quota/resourcequota.go -@@ -15,10 +15,16 @@ import ( - "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -+ "k8s.io/apimachinery/pkg/util/wait" - utilwait "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - ) -  -+// defaultIntegratedRegistryService is the in-cluster Service DNS for the default OpenShift image -+// registry. Use this for import-image --from so importer traffic stays on the pod network instead -+// of following an external route that InternalRegistryHostname may advertise. -+const defaultIntegratedRegistryService = "image-registry.openshift-image-registry.svc:5000" -+ - var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { - defer g.GinkgoRecover() - oc := exutil.NewCLI("object-count-rq") -@@ -165,7 +171,8 @@ var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { - o.Expect(err).NotTo(o.HaveOccurred()) - }) -  -- g.It("check the quota after import-image with --all option [Skipped:Disconnected]", func() { -+ g.It("check the quota after import-image with --all option", func() { -+ ctx := context.Background() - testProject := oc.SetupProject() - testResourceQuotaName := "my-imagestream-quota-" + testProject - clusterAdminKubeClient := oc.AdminKubeClient() -@@ -194,11 +201,89 @@ var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { - }) - o.Expect(err).NotTo(o.HaveOccurred()) -  -- g.By("trying to tag a container image") -- err = oc.AsAdmin().WithoutNamespace().Run("import-image").Args("centos", "--from=quay.io/openshifttest/alpine", "--confirm=true", "--all=true", "-n", testProject).Execute() -+ g.By("waiting until the integrated registry is published (readiness for import-image)") -+ _, err = exutil.WaitForInternalRegistryHostname(oc) -+ o.Expect(err).NotTo(o.HaveOccurred()) -+ -+ // Build a multi-tag ImageStream in this namespace by referencing openshift/cli:latest only -+ // (disconnected-safe). The integrated registry exposes it as one repository with several tags. -+ sourceISName := "rq-local-multi-src" -+ sourceTags := []string{"alpha", "beta", "gamma"} -+ for _, tag := range sourceTags { -+ err = oc.AsAdmin().WithoutNamespace().Run("tag").Args( -+ "openshift/cli:latest", -+ fmt.Sprintf("%s:%s", sourceISName, tag), -+ "-n", testProject, -+ ).Execute() -+ o.Expect(err).NotTo(o.HaveOccurred()) -+ err = exutil.WaitForAnImageStreamTag(oc, testProject, sourceISName, tag) -+ o.Expect(err).NotTo(o.HaveOccurred()) -+ } -+ -+ g.By("checking quota after creating one local ImageStream with multiple tags") -+ err = waitForResourceQuotaStatus(clusterAdminKubeClient, testResourceQuotaName, testProject, func(actualResourceQuota *corev1.ResourceQuota) error { -+ expectedUsedStatus := corev1.ResourceList{ -+ "openshift.io/imagestreams": resource.MustParse("1"), -+ } -+ if !equality.Semantic.DeepEqual(actualResourceQuota.Status.Used, expectedUsedStatus) { -+ return fmt.Errorf("unexpected current total usage: actual: %#v, expected: %#v", actualResourceQuota.Status.Used, expectedUsedStatus) -+ } -+ return nil -+ }) -+ o.Expect(err).NotTo(o.HaveOccurred()) -+ -+ sourceIS, err := oc.AdminImageClient().ImageV1().ImageStreams(testProject).Get(ctx, sourceISName, metav1.GetOptions{}) -+ o.Expect(err).NotTo(o.HaveOccurred()) -+ o.Expect(len(sourceIS.Spec.Tags)).To(o.Equal(len(sourceTags))) -+ -+ bulkISName := "rq-bulk-import-is" -+ internalSource := fmt.Sprintf("%s/%s/%s", defaultIntegratedRegistryService, testProject, sourceISName) -+ -+ g.By("importing all tags from the local repository into one new ImageStream (bulk import adds exactly one ImageStream)") -+ err = oc.AsAdmin().WithoutNamespace().Run("import-image").Args( -+ bulkISName, -+ "--from="+internalSource, -+ "--confirm=true", -+ "--all=true", -+ "--request-timeout=5m", -+ "-n", testProject, -+ ).Execute() -+ o.Expect(err).NotTo(o.HaveOccurred()) -+ -+ var imported *imagev1.ImageStream -+ // wait.PollUntilContextTimeout replaces PollImmediate -+ // Parameters: ctx, interval, timeout, immediate (true = PollImmediate behavior), condition -+ err = wait.PollUntilContextTimeout(ctx, 1*time.Second, 1*time.Minute, true, func(ctx context.Context) (bool, error) { -+ var getErr error -+ imported, getErr = oc.AdminImageClient().ImageV1().ImageStreams(testProject).Get(ctx, bulkISName, metav1.GetOptions{}) -+ if getErr != nil { -+ // Return false, nil to keep retrying if the IS isn't found yet -+ return false, nil -+ } -+ // Return true only when the tags are actually there -+ return len(imported.Spec.Tags) >= 2, nil -+ }) -+ -+ o.Expect(err).NotTo(o.HaveOccurred(), -+ "import-image --all should copy multiple tags onto a single ImageStream (timed out waiting for tags to populate)") -+ g.By("checking that bulk import increased ImageStream quota by exactly one") -+ err = waitForResourceQuotaStatus(clusterAdminKubeClient, testResourceQuotaName, testProject, func(actualResourceQuota *corev1.ResourceQuota) error { -+ expectedUsedStatus := corev1.ResourceList{ -+ "openshift.io/imagestreams": resource.MustParse("2"), -+ } -+ if !equality.Semantic.DeepEqual(actualResourceQuota.Status.Used, expectedUsedStatus) { -+ return fmt.Errorf("unexpected current total usage: actual: %#v, expected: %#v", actualResourceQuota.Status.Used, expectedUsedStatus) -+ } -+ return nil -+ }) - o.Expect(err).NotTo(o.HaveOccurred()) -  -- err = oc.AsAdmin().WithoutNamespace().Run("tag").Args("quay.io/openshifttest/base-alpine@sha256:3126e4eed4a3ebd8bf972b2453fa838200988ee07c01b2251e3ea47e4b1f245c", "--source=docker", "mystream:latest", "-n", testProject).Execute() -+ g.By("tagging another ImageStream from an in-cluster ImageStreamTag") -+ err = oc.AsAdmin().WithoutNamespace().Run("tag").Args( -+ "openshift/cli:latest", -+ "mystream:latest", -+ "-n", testProject, -+ ).Execute() - o.Expect(err).NotTo(o.HaveOccurred()) -  - err = exutil.WaitForAnImageStreamTag(oc, testProject, "mystream", "latest") -@@ -207,7 +292,7 @@ var _ = g.Describe("[sig-api-machinery][Feature:ResourceQuota]", func() { - g.By("checking the imagestream usage again") - err = waitForResourceQuotaStatus(clusterAdminKubeClient, testResourceQuotaName, testProject, func(actualResourceQuota *corev1.ResourceQuota) error { - expectedUsedStatus := corev1.ResourceList{ -- "openshift.io/imagestreams": resource.MustParse("2"), -+ "openshift.io/imagestreams": resource.MustParse("3"), - } - if !equality.Semantic.DeepEqual(actualResourceQuota.Status.Used, expectedUsedStatus) { - return fmt.Errorf("unexpected current total usage: actual: %#v, expected: %#v", actualResourceQuota.Status.Used, expectedUsedStatus) From 96733ba4e9ab44f986e8b8f95a2fe842fd52e6ce Mon Sep 17 00:00:00 2001 From: Yamunadevi Shanmugam Date: Thu, 14 May 2026 16:38:29 +0530 Subject: [PATCH 7/7] addressed review comments not to use deprecated methods --- test/extended/quota/resourcequota.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/extended/quota/resourcequota.go b/test/extended/quota/resourcequota.go index 53611222640b..6d88e2f54004 100644 --- a/test/extended/quota/resourcequota.go +++ b/test/extended/quota/resourcequota.go @@ -349,8 +349,8 @@ func waitForImageStreamStatusTagsPopulated(oc *exutil.CLI, namespace, name strin func waitForResourceQuotaStatus(clusterAdminKubeClient kubernetes.Interface, name string, namespace string, conditionFn func(*corev1.ResourceQuota) error) error { var pollErr error - err := utilwait.PollImmediate(100*time.Millisecond, QuotaWaitTimeout, func() (done bool, err error) { - quota, err := clusterAdminKubeClient.CoreV1().ResourceQuotas(namespace).Get(context.Background(), name, metav1.GetOptions{}) + err := utilwait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, QuotaWaitTimeout, true, func(ctx context.Context) (done bool, err error) { + quota, err := clusterAdminKubeClient.CoreV1().ResourceQuotas(namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { pollErr = err return false, nil