Skip to content
This repository has been archived by the owner on Aug 28, 2024. It is now read-only.

Commit

Permalink
Handle pointer items in ObjectList with ChildReconciler
Browse files Browse the repository at this point in the history
Canonically, Items in an ObjectList are a slice of structs. Some types,
like Istio, use a slice of pointers intead. We now detect and handle
both.

Signed-off-by: Scott Andrews <[email protected]>
  • Loading branch information
scothis committed Feb 13, 2024
1 parent e22ffba commit bb43c32
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 13 deletions.
19 changes: 19 additions & 0 deletions internal/resources/resource_list_with_pointer_items.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
Copyright 2024 VMware, Inc.
SPDX-License-Identifier: Apache-2.0
*/

package resources

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +kubebuilder:object:root=true

type TestResourcePointerList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`

Items []*TestResource `json:"items"`
}
36 changes: 36 additions & 0 deletions internal/resources/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 0 additions & 13 deletions reconcilers/child.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"context"
"errors"
"fmt"
"reflect"
"sync"

"github.com/go-logr/logr"
Expand Down Expand Up @@ -381,15 +380,3 @@ func (r *ChildReconciler[T, CT, CLT]) ourChild(resource T, obj CT) bool {
}
return r.OurChild(resource, obj)
}

// extractItems returns a typed slice of objects from an object list
func extractItems[T client.Object](list client.ObjectList) []T {
items := []T{}
listValue := reflect.ValueOf(list).Elem()
itemsValue := listValue.FieldByName("Items")
for i := 0; i < itemsValue.Len(); i++ {
item := itemsValue.Index(i).Addr().Interface().(T)
items = append(items, item)
}
return items
}
36 changes: 36 additions & 0 deletions reconcilers/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Copyright 2024 VMware, Inc.
SPDX-License-Identifier: Apache-2.0
*/

package reconcilers

import (
"fmt"
"reflect"

"sigs.k8s.io/controller-runtime/pkg/client"
)

// extractItems returns a typed slice of objects from an object list
func extractItems[T client.Object](list client.ObjectList) []T {
items := []T{}
listValue := reflect.ValueOf(list).Elem()
itemsValue := listValue.FieldByName("Items")
for i := 0; i < itemsValue.Len(); i++ {
itemValue := itemsValue.Index(i)
var item T
switch itemValue.Kind() {
case reflect.Pointer:
item = itemValue.Interface().(T)
case reflect.Interface:
item = itemValue.Interface().(T)
case reflect.Struct:
item = itemValue.Addr().Interface().(T)
default:
panic(fmt.Errorf("unknown type %s, expected Pointer or Struct", itemValue.Kind().String()))
}
items = append(items, item)
}
return items
}
95 changes: 95 additions & 0 deletions reconcilers/util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
Copyright 2024 VMware, Inc.
SPDX-License-Identifier: Apache-2.0
*/

package reconcilers

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/vmware-labs/reconciler-runtime/internal/resources"
corev1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func TestExtractItems(t *testing.T) {
tests := map[string]struct {
list client.ObjectList
expected []*resources.TestResource
}{
"empty": {
list: &resources.TestResourceList{
Items: []resources.TestResource{},
},
expected: []*resources.TestResource{},
},
"struct items": {
list: &resources.TestResourceList{
Items: []resources.TestResource{
{
ObjectMeta: corev1.ObjectMeta{
Name: "obj1",
},
},
{
ObjectMeta: corev1.ObjectMeta{
Name: "obj2",
},
},
},
},
expected: []*resources.TestResource{
{
ObjectMeta: corev1.ObjectMeta{
Name: "obj1",
},
},
{
ObjectMeta: corev1.ObjectMeta{
Name: "obj2",
},
},
},
},
"struct-pointer items": {
list: &resources.TestResourcePointerList{
Items: []*resources.TestResource{
{
ObjectMeta: corev1.ObjectMeta{
Name: "obj1",
},
},
{
ObjectMeta: corev1.ObjectMeta{
Name: "obj2",
},
},
},
},
expected: []*resources.TestResource{
{
ObjectMeta: corev1.ObjectMeta{
Name: "obj1",
},
},
{
ObjectMeta: corev1.ObjectMeta{
Name: "obj2",
},
},
},
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
actual := extractItems[*resources.TestResource](tc.list)
expected := tc.expected
if diff := cmp.Diff(expected, actual); diff != "" {
t.Errorf("expected items to match actual items: %s", diff)
}
})
}
}

0 comments on commit bb43c32

Please sign in to comment.