From a706c53de4629b31fd5e404ba42ead0430010803 Mon Sep 17 00:00:00 2001 From: Leonardo Ochoa-Aday Date: Mon, 26 Jan 2026 20:46:38 +0100 Subject: [PATCH 1/2] Fix API resource discovery to properly parse GroupVersion The getSupportedResourceTypes function was attempting to access res.Group and res.Version fields that don't exist on APIResource. The group and version are part of the APIResourceList, not the individual resources. This fix parses the GroupVersion from list.GroupVersion and applies it to all resources in that list, resolving the "missing group/version" warnings for cluster-scoped resources like Config, OperatorGroup, etc. Signed-off-by: Leonardo Ochoa-Aday --- pkg/compare/compare.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/compare/compare.go b/pkg/compare/compare.go index d7b3d3c3..0c969937 100644 --- a/pkg/compare/compare.go +++ b/pkg/compare/compare.go @@ -490,8 +490,12 @@ func getSupportedResourceTypes(client discovery.CachedDiscoveryInterface) (map[s } for _, list := range lists { if len(list.APIResources) != 0 { + gv, err := schema.ParseGroupVersion(list.GroupVersion) + if err != nil { + klog.Warningf("Failed to parse GroupVersion %s: %v", list.GroupVersion, err) + continue + } for _, res := range list.APIResources { - gv := schema.GroupVersion{Group: res.Group, Version: res.Version} if !slices.Contains(resources[res.Kind], gv) { resources[res.Kind] = append(resources[res.Kind], gv) } From c4323952739e217314fb4138847a937f7322b3b0 Mon Sep 17 00:00:00 2001 From: Leonardo Ochoa-Aday Date: Sat, 7 Feb 2026 13:09:53 +0100 Subject: [PATCH 2/2] Fix test mock to set GroupVersion on APIResourceList Update test discovery client to properly group resources by GroupVersion and set the GroupVersion field on each APIResourceList. This matches real Kubernetes API behavior where resources are returned in separate lists per group/version. This fixes the test failures caused by the API resource discovery fix and eliminates duplicate resource counting (27 down to 14 unique resources). Signed-off-by: Leonardo Ochoa-Aday --- ...dUnmatchedCRsAndDiffTestsSuitesWhenNoDiffs | 2 +- ...hedCRsAndDiffTestsSuitesWhenNoDiffs.golden | 49 ++----------------- pkg/compare/compare_test.go | 17 +++++-- 3 files changed, 20 insertions(+), 48 deletions(-) diff --git a/addon-tools/report-creator/report/testdata/CreationOfMissingCRsAndUnmatchedCRsAndDiffTestsSuitesWhenNoDiffs b/addon-tools/report-creator/report/testdata/CreationOfMissingCRsAndUnmatchedCRsAndDiffTestsSuitesWhenNoDiffs index dcfe8370..11930799 100644 --- a/addon-tools/report-creator/report/testdata/CreationOfMissingCRsAndUnmatchedCRsAndDiffTestsSuitesWhenNoDiffs +++ b/addon-tools/report-creator/report/testdata/CreationOfMissingCRsAndUnmatchedCRsAndDiffTestsSuitesWhenNoDiffs @@ -1 +1 @@ -{"Summary":{"ValidationIssuses":{},"NumMissing":0,"UnmatchedCRS":[],"NumDiffCRs":0,"TotalCRs":27,"MetadataHash":"933892b7ae8a4f5232734acc34f6c93fc223844d836b37af390cfeaecf0b7a99","patchedCRs":0},"Diffs":[{"DiffOutput":"","CorrelatedTemplate":"cm.yaml","CRName":"v1_ConfigMap_kubernetes-dashboard_kubernetes-dashboard-settings"},{"DiffOutput":"","CorrelatedTemplate":"cr.yaml","CRName":"rbac.authorization.k8s.io/v1_ClusterRole_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"crb.yaml","CRName":"rbac.authorization.k8s.io/v1_ClusterRoleBinding_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"deploymentDashboard.yaml","CRName":"apps/v1_Deployment_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"deploymentMetrics.yaml","CRName":"apps/v1_Deployment_kubernetes-dashboard_dashboard-metrics-scraper"},{"DiffOutput":"","CorrelatedTemplate":"ns.yaml","CRName":"v1_Namespace_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"rb.yaml","CRName":"rbac.authorization.k8s.io/v1_RoleBinding_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"role.yaml","CRName":"rbac.authorization.k8s.io/v1_Role_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"sa.yaml","CRName":"v1_ServiceAccount_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"secret.yaml","CRName":"v1_Secret_kubernetes-dashboard_kubernetes-dashboard-certs"},{"DiffOutput":"","CorrelatedTemplate":"secret.yaml","CRName":"v1_Secret_kubernetes-dashboard_kubernetes-dashboard-csrf"},{"DiffOutput":"","CorrelatedTemplate":"secret.yaml","CRName":"v1_Secret_kubernetes-dashboard_kubernetes-dashboard-key-holder"},{"DiffOutput":"","CorrelatedTemplate":"service.yaml","CRName":"v1_Service_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"ns.yaml","CRName":"v1_Namespace_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"sa.yaml","CRName":"v1_ServiceAccount_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"service.yaml","CRName":"v1_Service_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"secret.yaml","CRName":"v1_Secret_kubernetes-dashboard_kubernetes-dashboard-certs"},{"DiffOutput":"","CorrelatedTemplate":"secret.yaml","CRName":"v1_Secret_kubernetes-dashboard_kubernetes-dashboard-csrf"},{"DiffOutput":"","CorrelatedTemplate":"secret.yaml","CRName":"v1_Secret_kubernetes-dashboard_kubernetes-dashboard-key-holder"},{"DiffOutput":"","CorrelatedTemplate":"cm.yaml","CRName":"v1_ConfigMap_kubernetes-dashboard_kubernetes-dashboard-settings"},{"DiffOutput":"","CorrelatedTemplate":"role.yaml","CRName":"rbac.authorization.k8s.io/v1_Role_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"cr.yaml","CRName":"rbac.authorization.k8s.io/v1_ClusterRole_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"rb.yaml","CRName":"rbac.authorization.k8s.io/v1_RoleBinding_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"crb.yaml","CRName":"rbac.authorization.k8s.io/v1_ClusterRoleBinding_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"deploymentDashboard.yaml","CRName":"apps/v1_Deployment_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"service.yaml","CRName":"v1_Service_kubernetes-dashboard_dashboard-metrics-scraper"},{"DiffOutput":"","CorrelatedTemplate":"deploymentMetrics.yaml","CRName":"apps/v1_Deployment_kubernetes-dashboard_dashboard-metrics-scraper"}]} +{"Summary":{"ValidationIssuses":{},"NumMissing":0,"UnmatchedCRS":[],"NumDiffCRs":0,"TotalCRs":14,"MetadataHash":"933892b7ae8a4f5232734acc34f6c93fc223844d836b37af390cfeaecf0b7a99","patchedCRs":0},"Diffs":[{"DiffOutput":"","CorrelatedTemplate":"cm.yaml","CRName":"v1_ConfigMap_kubernetes-dashboard_kubernetes-dashboard-settings"},{"DiffOutput":"","CorrelatedTemplate":"cr.yaml","CRName":"rbac.authorization.k8s.io/v1_ClusterRole_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"crb.yaml","CRName":"rbac.authorization.k8s.io/v1_ClusterRoleBinding_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"deploymentDashboard.yaml","CRName":"apps/v1_Deployment_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"deploymentMetrics.yaml","CRName":"apps/v1_Deployment_kubernetes-dashboard_dashboard-metrics-scraper"},{"DiffOutput":"","CorrelatedTemplate":"ns.yaml","CRName":"v1_Namespace_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"rb.yaml","CRName":"rbac.authorization.k8s.io/v1_RoleBinding_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"role.yaml","CRName":"rbac.authorization.k8s.io/v1_Role_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"sa.yaml","CRName":"v1_ServiceAccount_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"secret.yaml","CRName":"v1_Secret_kubernetes-dashboard_kubernetes-dashboard-certs"},{"DiffOutput":"","CorrelatedTemplate":"secret.yaml","CRName":"v1_Secret_kubernetes-dashboard_kubernetes-dashboard-csrf"},{"DiffOutput":"","CorrelatedTemplate":"secret.yaml","CRName":"v1_Secret_kubernetes-dashboard_kubernetes-dashboard-key-holder"},{"DiffOutput":"","CorrelatedTemplate":"service.yaml","CRName":"v1_Service_kubernetes-dashboard_kubernetes-dashboard"},{"DiffOutput":"","CorrelatedTemplate":"service.yaml","CRName":"v1_Service_kubernetes-dashboard_dashboard-metrics-scraper"}]} diff --git a/addon-tools/report-creator/report/testdata/CreationOfMissingCRsAndUnmatchedCRsAndDiffTestsSuitesWhenNoDiffs.golden b/addon-tools/report-creator/report/testdata/CreationOfMissingCRsAndUnmatchedCRsAndDiffTestsSuitesWhenNoDiffs.golden index 2bcb0451..d8070983 100644 --- a/addon-tools/report-creator/report/testdata/CreationOfMissingCRsAndUnmatchedCRsAndDiffTestsSuitesWhenNoDiffs.golden +++ b/addon-tools/report-creator/report/testdata/CreationOfMissingCRsAndUnmatchedCRsAndDiffTestsSuitesWhenNoDiffs.golden @@ -1,9 +1,9 @@ - - + + - + @@ -44,53 +44,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -99,7 +60,7 @@ - + diff --git a/pkg/compare/compare_test.go b/pkg/compare/compare_test.go index fe4590a2..232e597c 100644 --- a/pkg/compare/compare_test.go +++ b/pkg/compare/compare_test.go @@ -848,8 +848,19 @@ func getResources(t *testing.T, test Test, resourcesDir string) ([]v1.APIResourc func updateTestDiscoveryClient(tf *cmdtesting.TestFactory, discoveryResources []v1.APIResource) { discoveryClient := cmdtesting.NewFakeCachedDiscoveryClient() - ResourceList := v1.APIResourceList{APIResources: discoveryResources} - discoveryClient.Resources = append(discoveryClient.Resources, &ResourceList) - discoveryClient.PreferredResources = append(discoveryClient.PreferredResources, &ResourceList) + // Group resources by GroupVersion to match real Kubernetes API behavior + resourcesByGV := make(map[string][]v1.APIResource) + for _, res := range discoveryResources { + gv := res.Version + if res.Group != "" { + gv = res.Group + "/" + res.Version + } + resourcesByGV[gv] = append(resourcesByGV[gv], res) + } + for gv, resources := range resourcesByGV { + list := &v1.APIResourceList{GroupVersion: gv, APIResources: resources} + discoveryClient.Resources = append(discoveryClient.Resources, list) + discoveryClient.PreferredResources = append(discoveryClient.PreferredResources, list) + } tf.WithDiscoveryClient(discoveryClient) }