From 0ac02c94a5f5f720c7633783859b15d94780d0c7 Mon Sep 17 00:00:00 2001 From: Arpit Jain Date: Sun, 28 Jun 2026 12:30:33 +0900 Subject: [PATCH 1/2] fix(etcd): initialize ResourceVersion in NewObjectStoreCluster The cluster store's inner objectStoreNamespaced literal left the embedded *versioning.ResourceVersion nil, so the first UseResourceVersion call made during Create of a cluster-scoped report dereferenced a nil pointer and panicked at versioning.go:33. This crashed the apiserver request handler for any ClusterEphemeralReport/ClusterPolicyReport/ClusterReport on the etcd backend. NewObjectStoreNamespaced already sets this field; mirror it in the cluster constructor and add a regression test. Fixes #352 Signed-off-by: Arpit Jain --- pkg/storage/etcd/store.go | 9 +++++---- pkg/storage/etcd/store_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 pkg/storage/etcd/store_test.go diff --git a/pkg/storage/etcd/store.go b/pkg/storage/etcd/store.go index 12776fc6..d64136d6 100644 --- a/pkg/storage/etcd/store.go +++ b/pkg/storage/etcd/store.go @@ -183,10 +183,11 @@ type objectStoreCluster[T metav1.Object] struct { func NewObjectStoreCluster[T metav1.Object](client clientv3.KV, gvk schema.GroupVersionKind, gr schema.GroupResource) api.GenericClusterIface[T] { return &objectStoreCluster[T]{ store: &objectStoreNamespaced[T]{ - namespaced: false, - etcdclient: client, - gvk: gvk, - gr: gr, + ResourceVersion: versioning.NewVersioning(), + namespaced: false, + etcdclient: client, + gvk: gvk, + gr: gr, }, } } diff --git a/pkg/storage/etcd/store_test.go b/pkg/storage/etcd/store_test.go new file mode 100644 index 00000000..86f6b199 --- /dev/null +++ b/pkg/storage/etcd/store_test.go @@ -0,0 +1,26 @@ +package etcd + +import ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// TestClusterStoreUseResourceVersionNoPanic guards against a regression where +// NewObjectStoreCluster left the embedded *versioning.ResourceVersion nil. The +// first call to UseResourceVersion (made during Create of a cluster-scoped +// report) then dereferenced that nil pointer and panicked, taking down the +// apiserver request handler. See kyverno/reports-server#352. +func TestClusterStoreUseResourceVersionNoPanic(t *testing.T) { + gvk := schema.GroupVersionKind{Group: "wgpolicyk8s.io", Version: "v1alpha2", Kind: "ClusterPolicyReport"} + gr := schema.GroupResource{Group: "wgpolicyk8s.io", Resource: "clusterpolicyreports"} + + // A nil KV client is fine: UseResourceVersion only touches the in-memory + // versioning struct and never talks to etcd. + store := NewObjectStoreCluster[metav1.Object](nil, gvk, gr) + + if got := store.UseResourceVersion(); got == "" { + t.Fatalf("expected a non-empty resource version, got %q", got) + } +} From 707f9d2c29613178f9ab3656bb64a4a606e63306 Mon Sep 17 00:00:00 2001 From: Arpit Jain Date: Wed, 8 Jul 2026 09:30:03 +0900 Subject: [PATCH 2/2] test(etcd): drop the explanatory comment on the cluster-store regression test Signed-off-by: Arpit Jain --- pkg/storage/etcd/store_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pkg/storage/etcd/store_test.go b/pkg/storage/etcd/store_test.go index 86f6b199..00446f09 100644 --- a/pkg/storage/etcd/store_test.go +++ b/pkg/storage/etcd/store_test.go @@ -7,11 +7,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" ) -// TestClusterStoreUseResourceVersionNoPanic guards against a regression where -// NewObjectStoreCluster left the embedded *versioning.ResourceVersion nil. The -// first call to UseResourceVersion (made during Create of a cluster-scoped -// report) then dereferenced that nil pointer and panicked, taking down the -// apiserver request handler. See kyverno/reports-server#352. func TestClusterStoreUseResourceVersionNoPanic(t *testing.T) { gvk := schema.GroupVersionKind{Group: "wgpolicyk8s.io", Version: "v1alpha2", Kind: "ClusterPolicyReport"} gr := schema.GroupResource{Group: "wgpolicyk8s.io", Resource: "clusterpolicyreports"}