From fcd0d84ca0c6d6a1a29213cd2929e1e95796b638 Mon Sep 17 00:00:00 2001 From: jiajliu Date: Fri, 13 Mar 2026 14:33:12 +0800 Subject: [PATCH] migrate ocp-53906 from otp to cvo repo --- ...hift_payload_cluster-version-operator.json | 10 +++ test/cvo/cvo.go | 81 +++++++++++++++++++ test/oc/api/api.go | 12 +++ test/oc/cli/cli.go | 47 +++++++++++ 4 files changed, 150 insertions(+) diff --git a/.openshift-tests-extension/openshift_payload_cluster-version-operator.json b/.openshift-tests-extension/openshift_payload_cluster-version-operator.json index e9be447240..daf25aae01 100644 --- a/.openshift-tests-extension/openshift_payload_cluster-version-operator.json +++ b/.openshift-tests-extension/openshift_payload_cluster-version-operator.json @@ -72,5 +72,15 @@ "source": "openshift:payload:cluster-version-operator", "lifecycle": "blocking", "environmentSelector": {} + }, + { + "name": "[Jira:\"Cluster Version Operator\"] cluster-version-operator should have correct architecture info in clusterversion status", + "labels": {}, + "resources": { + "isolation": {} + }, + "source": "openshift:payload:cluster-version-operator", + "lifecycle": "blocking", + "environmentSelector": {} } ] \ No newline at end of file diff --git a/test/cvo/cvo.go b/test/cvo/cvo.go index 7b3bf8ed89..3c4b9dc33c 100644 --- a/test/cvo/cvo.go +++ b/test/cvo/cvo.go @@ -4,14 +4,18 @@ package cvo import ( "context" + "encoding/json" + "time" g "github.com/onsi/ginkgo/v2" o "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" + "github.com/openshift/cluster-version-operator/lib/resourcemerge" "github.com/openshift/cluster-version-operator/pkg/external" "github.com/openshift/cluster-version-operator/test/oc" ocapi "github.com/openshift/cluster-version-operator/test/oc/api" @@ -99,4 +103,81 @@ var _ = g.Describe(`[Jira:"Cluster Version Operator"] cluster-version-operator`, sccAnnotation := cvoPod.Annotations["openshift.io/scc"] o.Expect(sccAnnotation).To(o.Equal("hostaccess"), "Expected the annotation 'openshift.io/scc annotation' on pod %s to have the value 'hostaccess', but got %s", cvoPod.Name, sccAnnotation) }) + + // Migrated from case Author:jiajliu-Medium-53906-The architecture info in clusterversion's status should be correct + // Refer to https://github.com/jiajliu/openshift-tests-private/blob/1ac5f94ee596419194ff7b0070732cb7930fe39e/test/extended/ota/cvo/cvo.go#L1775 + g.It("should have correct architecture info in clusterversion status", func() { + const heterogeneousArchKeyword = "multi" + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + err := util.SkipIfMicroshift(ctx, restCfg) + o.Expect(err).NotTo(o.HaveOccurred(), "Failed to determine if cluster is MicroShift") + + g.By("Getting release architecture from release info") + ocClient, err := oc.NewOC(ocapi.Options{ + Logger: logger, + Timeout: 2 * time.Minute, + }) + o.Expect(err).NotTo(o.HaveOccurred(), "Failed to create oc client") + releaseInfoJSON, err := ocClient.AdmReleaseInfo(ocapi.ReleaseInfoOptions{}) + o.Expect(err).NotTo(o.HaveOccurred(), "Failed to get release info") + + // Extract architecture from release info: metadata.metadata."release.openshift.io/architecture" + // Valid payloads: + // - Stable single-arch: metadata.metadata exists but without "release.openshift.io/architecture" + // - Stable multi-arch: metadata.metadata."release.openshift.io/architecture" = "multi" + // - Nightly single-arch: metadata.metadata not exists + // - Nightly multi-arch: metadata.metadata."release.openshift.io/architecture" = "multi" + var releaseInfo struct { + Metadata *struct { + Metadata map[string]string `json:"metadata"` + } `json:"metadata"` + } + o.Expect(json.Unmarshal([]byte(releaseInfoJSON), &releaseInfo)).To(o.Succeed(), "Failed to unmarshal release info JSON") + if releaseInfo.Metadata == nil { + g.Skip("Release info missing top-level 'metadata' field, cannot determine payload architecture type") + } + + releaseArch := releaseInfo.Metadata.Metadata["release.openshift.io/architecture"] + logger.Info("Release architecture from release info", "architecture", releaseArch) + if releaseArch != "" && releaseArch != heterogeneousArchKeyword { + g.Skip("Unknown architecture value in release info: " + releaseArch) + } + + g.By("Check the arch info in ClusterVersion status is expected") + configClient, err := util.GetConfigClient(restCfg) + o.Expect(err).NotTo(o.HaveOccurred(), "Failed to create config client") + cv, err := configClient.ConfigV1().ClusterVersions().Get(ctx, external.DefaultClusterVersionName, metav1.GetOptions{}) + o.Expect(err).NotTo(o.HaveOccurred(), "Failed to get ClusterVersion") + releaseAcceptedCond := resourcemerge.FindOperatorStatusCondition(cv.Status.Conditions, "ReleaseAccepted") + o.Expect(releaseAcceptedCond).NotTo(o.BeNil(), "ReleaseAccepted condition not found in ClusterVersion status") + cvArchInfo := releaseAcceptedCond.Message + logger.Info("ClusterVersion ReleaseAccepted message", "message", cvArchInfo) + + if releaseArch == "" { + // For non-heterogeneous payload, the architecture info in ClusterVersion status should be consistent with node architectures + g.By("Verifying all nodes have the same architecture") + nodes, err := kubeClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) + o.Expect(err).NotTo(o.HaveOccurred(), "Failed to list nodes") + o.Expect(nodes.Items).NotTo(o.BeEmpty(), "No nodes found in cluster") + + nodeArchs := sets.New[string]() + for _, node := range nodes.Items { + nodeArchs.Insert(node.Status.NodeInfo.Architecture) + } + logger.Info("Node architectures found", "architectures", sets.List(nodeArchs)) + o.Expect(nodeArchs).To(o.HaveLen(1), "Expected all nodes to have the same architecture in non-heterogeneous cluster, but found: %v", sets.List(nodeArchs)) + + expectedArch := sets.List(nodeArchs)[0] + g.By("Verifying ClusterVersion status architecture matches node architecture") + o.Expect(cvArchInfo).To(o.ContainSubstring(expectedArch), "ClusterVersion ReleaseAccepted message should contain node architecture %q, but got: %s", expectedArch, cvArchInfo) + } else { + // For heterogeneous payload, the architecture info in ClusterVersion status should be Multi + g.By("Verifying ClusterVersion status architecture includes architecture=\"Multi\"") + expectedArchMsg := `architecture="Multi"` + o.Expect(cvArchInfo).To(o.ContainSubstring(expectedArchMsg), "ClusterVersion ReleaseAccepted message should contain %q for heterogeneous payload, but got: %s", expectedArchMsg, cvArchInfo) + } + }) }) diff --git a/test/oc/api/api.go b/test/oc/api/api.go index 050cbea148..e9e086703a 100644 --- a/test/oc/api/api.go +++ b/test/oc/api/api.go @@ -10,10 +10,20 @@ type ReleaseExtractOptions struct { To string } +type ReleaseInfoOptions struct { + Image string +} + type VersionOptions struct { Client bool } +type ExtractOptions struct { + Resource string + Namespace string + To string +} + type Options struct { Logger logr.Logger Timeout time.Duration @@ -21,5 +31,7 @@ type Options struct { type OC interface { AdmReleaseExtract(o ReleaseExtractOptions) error + AdmReleaseInfo(o ReleaseInfoOptions) (string, error) Version(o VersionOptions) (string, error) + Extract(o ExtractOptions) error } diff --git a/test/oc/cli/cli.go b/test/oc/cli/cli.go index 7e6d4509ee..2db5b129e4 100644 --- a/test/oc/cli/cli.go +++ b/test/oc/cli/cli.go @@ -4,7 +4,9 @@ import ( "context" "errors" "fmt" + "os" "os/exec" + "path/filepath" "strings" "time" @@ -90,6 +92,51 @@ func (c *client) AdmReleaseExtract(o api.ReleaseExtractOptions) error { return nil } +func (c *client) AdmReleaseInfo(o api.ReleaseInfoOptions) (string, error) { + // Create temp directory to extract pull-secret + tempDir, err := os.MkdirTemp("", "cvo-test-") + if err != nil { + return "", fmt.Errorf("failed to create temp directory: %w", err) + } + defer func() { + if err := os.RemoveAll(tempDir); err != nil { + c.logger.Error(err, "failed to clean up temp directory", "path", tempDir) + } + }() + + // Extract pull-secret from cluster + extractErr := c.Extract(api.ExtractOptions{ + Resource: "secret/pull-secret", + Namespace: "openshift-config", + To: tempDir, + }) + if extractErr != nil { + return "", fmt.Errorf("failed to extract pull-secret: %w", extractErr) + } + + // Use the extracted pull-secret + pullSecret := filepath.Join(tempDir, ".dockerconfigjson") + if _, err := os.Stat(pullSecret); err != nil { + return "", fmt.Errorf("pull-secret file not found at %s: %w", pullSecret, err) + } + + args := []string{"adm", "release", "info", "-a", pullSecret, "--insecure", "-o", "json"} + if o.Image != "" { + args = append(args, o.Image) + } + output, err := c.executor.Run(args...) + if err != nil { + return "", err + } + return string(output), nil +} + +func (c *client) Extract(o api.ExtractOptions) error { + args := []string{"extract", o.Resource, "-n", o.Namespace, "--confirm", fmt.Sprintf("--to=%s", o.To)} + _, err := c.executor.Run(args...) + return err +} + func (c *client) Version(o api.VersionOptions) (string, error) { args := []string{"version", fmt.Sprintf("--client=%t", o.Client)} output, err := c.executor.Run(args...)