From cf8b4e27301b5ce64fd5edc67ec85e4e53e47c7e Mon Sep 17 00:00:00 2001 From: Mihai IDU Date: Thu, 23 Jul 2026 13:34:03 +0200 Subject: [PATCH 1/6] Fix undefined csvs in mustgather and add CSV annotation warning --- pkg/cli/admin/mustgather/mustgather.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/cli/admin/mustgather/mustgather.go b/pkg/cli/admin/mustgather/mustgather.go index 52b553f390..3677dadb92 100644 --- a/pkg/cli/admin/mustgather/mustgather.go +++ b/pkg/cli/admin/mustgather/mustgather.go @@ -350,12 +350,18 @@ func (o *MustGatherOptions) completeImages(ctx context.Context) error { if err != nil { return err } + var missingAnnotation []string for _, item := range cos.Items { ann := item.GetAnnotations() if v, ok := ann[mgAnnotation]; ok { pluginImages[v] = struct{}{} + } else { + missingAnnotation = append(missingAnnotation, item.GetName()) } } + if len(missingAnnotation) > 0 { + o.log("WARNING: ClusterOperators without %s annotation: %s", mgAnnotation, strings.Join(missingAnnotation, ", ")) + } // delete the default image to avoid duplication in case an Operator had it in its annotation delete(pluginImages, o.Images[0]) for i := range pluginImages { @@ -380,12 +386,18 @@ func (o *MustGatherOptions) annotatedCSVs(ctx context.Context) (map[string]struc return nil, err } + var missingAnnotation []string for _, item := range csvs.Items { ann := item.GetAnnotations() if v, ok := ann[mgAnnotation]; ok { pluginImages[v] = struct{}{} + } else { + missingAnnotation = append(missingAnnotation, item.GetName()) } } + if len(missingAnnotation) > 0 { + o.log("WARNING: CSVs without %s annotation: %s", mgAnnotation, strings.Join(missingAnnotation, ", ")) + } return pluginImages, nil } From 2c804f35f7ed84c6227cdee6fa0fae58894b0010 Mon Sep 17 00:00:00 2001 From: Mihai Idu Date: Wed, 29 Jul 2026 14:57:57 +0200 Subject: [PATCH 2/6] must-gather: move missing-annotation logging to caller Return missing annotation names from annotatedCSVs instead of logging internally, so both ClusterOperator and CSV messages are logged at the same level in completeImages. Drop the WARNING prefix since the messages are informational. --- pkg/cli/admin/mustgather/mustgather.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/cli/admin/mustgather/mustgather.go b/pkg/cli/admin/mustgather/mustgather.go index 3677dadb92..6ab69affa3 100644 --- a/pkg/cli/admin/mustgather/mustgather.go +++ b/pkg/cli/admin/mustgather/mustgather.go @@ -341,7 +341,7 @@ func (o *MustGatherOptions) completeImages(ctx context.Context) error { } if o.AllImages { // find all csvs and clusteroperators with the annotation "operators.openshift.io/must-gather-image" - pluginImages, err := o.annotatedCSVs(ctx) + pluginImages, missingCSVs, err := o.annotatedCSVs(ctx) if err != nil { return err } @@ -350,17 +350,20 @@ func (o *MustGatherOptions) completeImages(ctx context.Context) error { if err != nil { return err } - var missingAnnotation []string + var missingCOs []string for _, item := range cos.Items { ann := item.GetAnnotations() if v, ok := ann[mgAnnotation]; ok { pluginImages[v] = struct{}{} } else { - missingAnnotation = append(missingAnnotation, item.GetName()) + missingCOs = append(missingCOs, item.GetName()) } } - if len(missingAnnotation) > 0 { - o.log("WARNING: ClusterOperators without %s annotation: %s", mgAnnotation, strings.Join(missingAnnotation, ", ")) + if len(missingCOs) > 0 { + o.log("ClusterOperators without %s annotation: %s", mgAnnotation, strings.Join(missingCOs, ", ")) + } + if len(missingCSVs) > 0 { + o.log("CSVs without %s annotation: %s", mgAnnotation, strings.Join(missingCSVs, ", ")) } // delete the default image to avoid duplication in case an Operator had it in its annotation delete(pluginImages, o.Images[0]) @@ -373,7 +376,7 @@ func (o *MustGatherOptions) completeImages(ctx context.Context) error { return nil } -func (o *MustGatherOptions) annotatedCSVs(ctx context.Context) (map[string]struct{}, error) { +func (o *MustGatherOptions) annotatedCSVs(ctx context.Context) (map[string]struct{}, []string, error) { csvGVR := schema.GroupVersionResource{ Group: "operators.coreos.com", Version: "v1alpha1", @@ -383,7 +386,7 @@ func (o *MustGatherOptions) annotatedCSVs(ctx context.Context) (map[string]struc csvs, err := o.DynamicClient.Resource(csvGVR).List(ctx, metav1.ListOptions{}) if err != nil { - return nil, err + return nil, nil, err } var missingAnnotation []string @@ -395,10 +398,7 @@ func (o *MustGatherOptions) annotatedCSVs(ctx context.Context) (map[string]struc missingAnnotation = append(missingAnnotation, item.GetName()) } } - if len(missingAnnotation) > 0 { - o.log("WARNING: CSVs without %s annotation: %s", mgAnnotation, strings.Join(missingAnnotation, ", ")) - } - return pluginImages, nil + return pluginImages, missingAnnotation, nil } func (o *MustGatherOptions) resolveImageStreamTagString(ctx context.Context, s string) (string, error) { From 4b4339a5c4ca36253a86a0994f93d72487ad0c5b Mon Sep 17 00:00:00 2001 From: Mihai Idu Date: Wed, 29 Jul 2026 15:07:54 +0200 Subject: [PATCH 3/6] must-gather: improve missing-annotation variable naming Rename missingCOs/missingCSVs to annotationMissingCOs/annotationMissingCSVs to clarify these track resources missing the must-gather annotation. --- pkg/cli/admin/mustgather/mustgather.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/cli/admin/mustgather/mustgather.go b/pkg/cli/admin/mustgather/mustgather.go index 6ab69affa3..0a8914de64 100644 --- a/pkg/cli/admin/mustgather/mustgather.go +++ b/pkg/cli/admin/mustgather/mustgather.go @@ -341,7 +341,7 @@ func (o *MustGatherOptions) completeImages(ctx context.Context) error { } if o.AllImages { // find all csvs and clusteroperators with the annotation "operators.openshift.io/must-gather-image" - pluginImages, missingCSVs, err := o.annotatedCSVs(ctx) + pluginImages, annotationMissingCSVs, err := o.annotatedCSVs(ctx) if err != nil { return err } @@ -350,20 +350,20 @@ func (o *MustGatherOptions) completeImages(ctx context.Context) error { if err != nil { return err } - var missingCOs []string + var annotationMissingCOs []string for _, item := range cos.Items { ann := item.GetAnnotations() if v, ok := ann[mgAnnotation]; ok { pluginImages[v] = struct{}{} } else { - missingCOs = append(missingCOs, item.GetName()) + annotationMissingCOs = append(annotationMissingCOs, item.GetName()) } } - if len(missingCOs) > 0 { - o.log("ClusterOperators without %s annotation: %s", mgAnnotation, strings.Join(missingCOs, ", ")) + if len(annotationMissingCOs) > 0 { + o.log("ClusterOperators without %s annotation: %s", mgAnnotation, strings.Join(annotationMissingCOs, ", ")) } - if len(missingCSVs) > 0 { - o.log("CSVs without %s annotation: %s", mgAnnotation, strings.Join(missingCSVs, ", ")) + if len(annotationMissingCSVs) > 0 { + o.log("CSVs without %s annotation: %s", mgAnnotation, strings.Join(annotationMissingCSVs, ", ")) } // delete the default image to avoid duplication in case an Operator had it in its annotation delete(pluginImages, o.Images[0]) From afbcfb7cf061adfce6ae88b6d9eab7c78c45944d Mon Sep 17 00:00:00 2001 From: Mihai Idu Date: Wed, 29 Jul 2026 15:42:25 +0200 Subject: [PATCH 4/6] must-gather: rename missingAnnotation to annotationMissingImages Align internal variable naming in annotatedCSVs with the caller-side convention (annotationMissingCSVs, annotationMissingCOs) per review feedback. --- pkg/cli/admin/mustgather/mustgather.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/cli/admin/mustgather/mustgather.go b/pkg/cli/admin/mustgather/mustgather.go index 0a8914de64..c080d082fc 100644 --- a/pkg/cli/admin/mustgather/mustgather.go +++ b/pkg/cli/admin/mustgather/mustgather.go @@ -389,16 +389,16 @@ func (o *MustGatherOptions) annotatedCSVs(ctx context.Context) (map[string]struc return nil, nil, err } - var missingAnnotation []string + var annotationMissingImages []string for _, item := range csvs.Items { ann := item.GetAnnotations() if v, ok := ann[mgAnnotation]; ok { pluginImages[v] = struct{}{} } else { - missingAnnotation = append(missingAnnotation, item.GetName()) + annotationMissingImages = append(annotationMissingImages, item.GetName()) } } - return pluginImages, missingAnnotation, nil + return pluginImages, annotationMissingImages, nil } func (o *MustGatherOptions) resolveImageStreamTagString(ctx context.Context, s string) (string, error) { From 5009c1e6a636931657360cb977cdb18e4b3884d9 Mon Sep 17 00:00:00 2001 From: Mihai Idu Date: Fri, 31 Jul 2026 16:23:02 +0200 Subject: [PATCH 5/6] must-gather: deduplicate CSV warning and remove CO warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ClusterOperators are platform-level components that never carry the must-gather-image annotation—listing all 34 of them on every --all-images run is noise. Remove the CO warning entirely and keep the annotation check only for day-2 operators (CSVs). CSVs exist in every namespace, producing 100+ duplicated entries in the warning line. Use a map[string]struct{} set to deduplicate names and slices.Sorted(maps.Keys(...)) for deterministic, sorted output. --- pkg/cli/admin/mustgather/mustgather.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/pkg/cli/admin/mustgather/mustgather.go b/pkg/cli/admin/mustgather/mustgather.go index c080d082fc..8f1ac0c81b 100644 --- a/pkg/cli/admin/mustgather/mustgather.go +++ b/pkg/cli/admin/mustgather/mustgather.go @@ -6,11 +6,13 @@ import ( "errors" "fmt" "io" + "maps" "math/rand" "os" "os/signal" "path" "regexp" + "slices" "sort" "strconv" "strings" @@ -350,18 +352,12 @@ func (o *MustGatherOptions) completeImages(ctx context.Context) error { if err != nil { return err } - var annotationMissingCOs []string for _, item := range cos.Items { ann := item.GetAnnotations() if v, ok := ann[mgAnnotation]; ok { pluginImages[v] = struct{}{} - } else { - annotationMissingCOs = append(annotationMissingCOs, item.GetName()) } } - if len(annotationMissingCOs) > 0 { - o.log("ClusterOperators without %s annotation: %s", mgAnnotation, strings.Join(annotationMissingCOs, ", ")) - } if len(annotationMissingCSVs) > 0 { o.log("CSVs without %s annotation: %s", mgAnnotation, strings.Join(annotationMissingCSVs, ", ")) } @@ -389,16 +385,16 @@ func (o *MustGatherOptions) annotatedCSVs(ctx context.Context) (map[string]struc return nil, nil, err } - var annotationMissingImages []string + annotationMissingImages := make(map[string]struct{}) for _, item := range csvs.Items { ann := item.GetAnnotations() if v, ok := ann[mgAnnotation]; ok { pluginImages[v] = struct{}{} } else { - annotationMissingImages = append(annotationMissingImages, item.GetName()) + annotationMissingImages[item.GetName()] = struct{}{} } } - return pluginImages, annotationMissingImages, nil + return pluginImages, slices.Sorted(maps.Keys(annotationMissingImages)), nil } func (o *MustGatherOptions) resolveImageStreamTagString(ctx context.Context, s string) (string, error) { From ac0495e8a92cc971c98dec0d61e55f10394c2e20 Mon Sep 17 00:00:00 2001 From: Mihai Idu Date: Fri, 31 Jul 2026 16:40:14 +0200 Subject: [PATCH 6/6] must-gather: use sets.Set for CSV deduplication Replace map[string]struct{} with k8s.io/apimachinery/pkg/util/sets.Set for deduplicating CSV names missing the must-gather-image annotation. sets.List() returns a sorted slice, replacing slices.Sorted(maps.Keys()). --- pkg/cli/admin/mustgather/mustgather.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pkg/cli/admin/mustgather/mustgather.go b/pkg/cli/admin/mustgather/mustgather.go index 8f1ac0c81b..d44c18d8ba 100644 --- a/pkg/cli/admin/mustgather/mustgather.go +++ b/pkg/cli/admin/mustgather/mustgather.go @@ -6,13 +6,11 @@ import ( "errors" "fmt" "io" - "maps" "math/rand" "os" "os/signal" "path" "regexp" - "slices" "sort" "strconv" "strings" @@ -28,6 +26,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" kutilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericiooptions" @@ -385,16 +384,16 @@ func (o *MustGatherOptions) annotatedCSVs(ctx context.Context) (map[string]struc return nil, nil, err } - annotationMissingImages := make(map[string]struct{}) + annotationMissingImages := sets.New[string]() for _, item := range csvs.Items { ann := item.GetAnnotations() if v, ok := ann[mgAnnotation]; ok { pluginImages[v] = struct{}{} } else { - annotationMissingImages[item.GetName()] = struct{}{} + annotationMissingImages.Insert(item.GetName()) } } - return pluginImages, slices.Sorted(maps.Keys(annotationMissingImages)), nil + return pluginImages, sets.List(annotationMissingImages), nil } func (o *MustGatherOptions) resolveImageStreamTagString(ctx context.Context, s string) (string, error) {