From e47833c1e2395178ae0ec319ae073ad1ece1b8a4 Mon Sep 17 00:00:00 2001 From: Brandon Palm Date: Tue, 25 Aug 2026 14:38:37 -0500 Subject: [PATCH] reconciler: fix correctImages panic when pod has no containers pod.Spec.Containers[0] was accessed without a length guard in the non-ExtractContent path of correctImages, causing a panic for any pod with an empty containers slice (e.g. evicted or malformed pods). The ExtractContent path already had an equivalent guard. Add TestCorrectImages covering both paths including the empty-containers case and the ExtractContent serving-image mismatch case. --- pkg/controller/registry/reconciler/grpc.go | 3 + .../registry/reconciler/grpc_test.go | 72 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/pkg/controller/registry/reconciler/grpc.go b/pkg/controller/registry/reconciler/grpc.go index 182dc4288e..2e2d394d84 100644 --- a/pkg/controller/registry/reconciler/grpc.go +++ b/pkg/controller/registry/reconciler/grpc.go @@ -273,6 +273,9 @@ func correctImages(source grpcCatalogSourceDecorator, pod *corev1.Pod) bool { pod.Spec.InitContainers[1].Image == source.CatalogSource.Spec.Image && pod.Spec.Containers[0].Image == source.opmImage } + if len(pod.Spec.Containers) == 0 { + return false + } return pod.Spec.Containers[0].Image == source.CatalogSource.Spec.Image } diff --git a/pkg/controller/registry/reconciler/grpc_test.go b/pkg/controller/registry/reconciler/grpc_test.go index 89b1719ce0..b28686f6c2 100644 --- a/pkg/controller/registry/reconciler/grpc_test.go +++ b/pkg/controller/registry/reconciler/grpc_test.go @@ -816,3 +816,75 @@ func TestUpdatePodByDigest(t *testing.T) { require.Equal(t, tt.result, imageChanged(logrus.NewEntry(logrus.New()), tt.updatePod, tt.servingPods), table[i].description) } } + +func TestCorrectImages(t *testing.T) { + const catalogImage = "quay.io/test/catalog:v1" + const opmImage = "quay.io/test/opm:v1" + const utilImage = "quay.io/test/util:v1" + + makeSource := func(image string, extractContent *v1alpha1.ExtractContentConfig) grpcCatalogSourceDecorator { + cs := validGrpcCatalogSource(image, "") + if extractContent != nil { + cs.Spec.GrpcPodConfig = &v1alpha1.GrpcPodConfig{ExtractContent: extractContent} + } + return grpcCatalogSourceDecorator{CatalogSource: cs, opmImage: opmImage, utilImage: utilImage} + } + + for _, tt := range []struct { + name string + source grpcCatalogSourceDecorator + pod *corev1.Pod + want bool + }{ + { + name: "non-ExtractContent/empty containers does not panic", + source: makeSource(catalogImage, nil), + pod: &corev1.Pod{}, + want: false, + }, + { + name: "non-ExtractContent/matching image", + source: makeSource(catalogImage, nil), + pod: &corev1.Pod{Spec: corev1.PodSpec{ + Containers: []corev1.Container{{Image: catalogImage}}, + }}, + want: true, + }, + { + name: "non-ExtractContent/wrong image", + source: makeSource(catalogImage, nil), + pod: &corev1.Pod{Spec: corev1.PodSpec{ + Containers: []corev1.Container{{Image: "quay.io/test/catalog:v2"}}, + }}, + want: false, + }, + { + name: "ExtractContent/correct init and serving containers", + source: makeSource(catalogImage, &v1alpha1.ExtractContentConfig{CatalogDir: "/catalog"}), + pod: &corev1.Pod{Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{{Image: utilImage}, {Image: catalogImage}}, + Containers: []corev1.Container{{Image: opmImage}}, + }}, + want: true, + }, + { + name: "ExtractContent/wrong number of init containers", + source: makeSource(catalogImage, &v1alpha1.ExtractContentConfig{CatalogDir: "/catalog"}), + pod: &corev1.Pod{Spec: corev1.PodSpec{Containers: []corev1.Container{{Image: opmImage}}}}, + want: false, + }, + { + name: "ExtractContent/wrong serving container image", + source: makeSource(catalogImage, &v1alpha1.ExtractContentConfig{CatalogDir: "/catalog"}), + pod: &corev1.Pod{Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{{Image: utilImage}, {Image: catalogImage}}, + Containers: []corev1.Container{{Image: "quay.io/test/opm:wrong"}}, + }}, + want: false, + }, + } { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, correctImages(tt.source, tt.pod)) + }) + } +}