From ddc1c42ac72bb2eb34a49eda980a7c4bbef4e197 Mon Sep 17 00:00:00 2001 From: Evan Cordell Date: Wed, 8 Oct 2025 10:56:12 -0400 Subject: [PATCH] typed: prevent panics on missing objects the previous implementation would try to deref nil values --- typed/typedindexer.go | 20 ++++++++++---------- typed/typedindexer_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/typed/typedindexer.go b/typed/typedindexer.go index a42c65e..18f05e7 100644 --- a/typed/typedindexer.go +++ b/typed/typedindexer.go @@ -40,37 +40,37 @@ func (t Indexer[K]) ListKeys() []string { } func (t Indexer[K]) Get(obj K) (item K, exists bool, err error) { - var typedObj *K + var zero K gotItem, gotExists, gotErr := t.indexer.Get(obj) - if err != nil || !gotExists { - return *typedObj, gotExists, gotErr + if gotErr != nil || !gotExists { + return zero, gotExists, gotErr } gotRObj, ok := gotItem.(runtime.Object) if !ok { - return *typedObj, gotExists, fmt.Errorf("%v is not a runtime.Object", gotItem) + return zero, gotExists, fmt.Errorf("%v is not a runtime.Object", gotItem) } gotTypedObj, err := UnstructuredObjToTypedObj[K](gotRObj) if err != nil { - return *typedObj, gotExists, fmt.Errorf("could not convert %s to %T", gotItem, *typedObj) + return zero, gotExists, fmt.Errorf("could not convert %s to %T", gotItem, zero) } return gotTypedObj, gotExists, gotErr } func (t Indexer[K]) GetByKey(key string) (item K, exists bool, err error) { - var typedObj *K + var zero K gotItem, gotExists, gotErr := t.indexer.GetByKey(key) - if err != nil || !gotExists { - return *typedObj, gotExists, gotErr + if gotErr != nil || !gotExists { + return zero, gotExists, gotErr } gotRObj, ok := gotItem.(runtime.Object) if !ok { - return *typedObj, gotExists, fmt.Errorf("%v is not a runtime.Object", gotItem) + return zero, gotExists, fmt.Errorf("%v is not a runtime.Object", gotItem) } gotTypedObj, err := UnstructuredObjToTypedObj[K](gotRObj) if err != nil { - return *typedObj, gotExists, fmt.Errorf("could not convert %s to %T", gotItem, *typedObj) + return zero, gotExists, fmt.Errorf("could not convert %s to %T", gotItem, zero) } return gotTypedObj, gotExists, gotErr } diff --git a/typed/typedindexer_test.go b/typed/typedindexer_test.go index 290b8e5..f6c189c 100644 --- a/typed/typedindexer_test.go +++ b/typed/typedindexer_test.go @@ -3,6 +3,7 @@ package typed import ( "context" "fmt" + "testing" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" @@ -25,3 +26,26 @@ func ExampleIndexer() { fmt.Printf("%T", secrets) // Output: []*v1.Secret } + +func TestGetByKeyErrorHandling(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + client := fake.NewSimpleDynamicClient(runtime.NewScheme()) + informerFactory := dynamicinformer.NewDynamicSharedInformerFactory(client, 0) + informerFactory.Start(ctx.Done()) + informerFactory.WaitForCacheSync(ctx.Done()) + + indexer := NewIndexer[*corev1.Secret](informerFactory.ForResource(corev1.SchemeGroupVersion.WithResource("secrets")).Informer().GetIndexer()) + + secret, exists, err := indexer.GetByKey("nonexistent/key") + if secret != nil { + t.Errorf("Expected nil secret for non-existent key, got %v", secret) + } + if exists { + t.Errorf("Expected exists to be false for non-existent key") + } + if err != nil { + t.Errorf("Expected no error for non-existent key, got %v", err) + } +}