Skip to content

Commit

Permalink
feat: allow setting custom transformer function as option
Browse files Browse the repository at this point in the history
Signed-off-by: KevFan <[email protected]>
  • Loading branch information
KevFan committed Nov 29, 2024
1 parent a1fb8dc commit 168c27d
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 20 deletions.
71 changes: 51 additions & 20 deletions controller/runnable.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type RunnableBuilderOptions[T Object] struct {
FieldSelector string
Predicates []ctrlruntimepredicate.TypedPredicate[T]
Builder func(obj T, resource schema.GroupVersionResource, namespace string, options ...RunnableBuilderOption[T]) RunnableBuilder
TransformFunc cache.TransformFunc
}

type RunnableBuilderOption[T Object] func(*RunnableBuilderOptions[T])
Expand All @@ -55,6 +56,12 @@ func FilterResourcesByField[T Object](selector string) RunnableBuilderOption[T]
}
}

func WithTransformerFunc[T Object](transformer cache.TransformFunc) RunnableBuilderOption[T] {
return func(o *RunnableBuilderOptions[T]) {
o.TransformFunc = transformer
}
}

func WithPredicates[T Object](predicates ...ctrlruntimepredicate.TypedPredicate[T]) RunnableBuilderOption[T] {
return func(o *RunnableBuilderOptions[T]) {
o.Predicates = append(o.Predicates, predicates...)
Expand All @@ -77,8 +84,10 @@ func Watch[T Object](obj T, resource schema.GroupVersionResource, namespace stri
return o.Builder(obj, resource, namespace, options...)
}

func IncrementalInformer[T Object](obj T, resource schema.GroupVersionResource, namespace string, options ...RunnableBuilderOption[T]) RunnableBuilder {
o := &RunnableBuilderOptions[T]{}
func IncrementalInformer[T Object](_ T, resource schema.GroupVersionResource, namespace string, options ...RunnableBuilderOption[T]) RunnableBuilder {
o := &RunnableBuilderOptions[T]{
TransformFunc: Restructure[T],
}
for _, f := range options {
f(o)
}
Expand Down Expand Up @@ -107,7 +116,7 @@ func IncrementalInformer[T Object](obj T, resource schema.GroupVersionResource,
&unstructured.Unstructured{},
time.Minute*10,
)
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
_, err := informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(o any) {
obj := o.(T)
controller.add(obj)
Expand All @@ -122,13 +131,20 @@ func IncrementalInformer[T Object](obj T, resource schema.GroupVersionResource,
controller.delete(obj)
},
})
informer.SetTransform(Restructure[T])
if err != nil {
fmt.Print(err.Error())
}
if err := informer.SetTransform(o.TransformFunc); err != nil {
fmt.Print(err.Error())
}
return informer
}
}

func StateReconciler[T Object](obj T, resource schema.GroupVersionResource, namespace string, options ...RunnableBuilderOption[T]) RunnableBuilder {
o := &RunnableBuilderOptions[T]{}
o := &RunnableBuilderOptions[T]{
TransformFunc: Restructure[T],
}
for _, f := range options {
f(o)
}
Expand All @@ -154,8 +170,8 @@ func StateReconciler[T Object](obj T, resource schema.GroupVersionResource, name
controller.logger.Error(err, "failed to list resources", "kind", kind)
return nil
}
return lo.Map(objs.Items, func(o unstructured.Unstructured, _ int) Object {
obj, err := Restructure[T](&o)
return lo.Map(objs.Items, func(u unstructured.Unstructured, _ int) Object {
obj, err := o.TransformFunc(&u)
if err != nil {
controller.logger.Error(err, "failed to restructure object", "kind", kind)
return nil
Expand Down Expand Up @@ -220,20 +236,35 @@ func (r *stateReconciler) HasSynced() bool {
return r.synced
}

func Restructure[T any](obj any) (any, error) {
unstructuredObj, ok := obj.(*unstructured.Unstructured)
if !ok {
return nil, fmt.Errorf("unexpected object type: %T", obj)
}
j, err := unstructuredObj.MarshalJSON()
if err != nil {
return nil, err
}
o := new(T)
if err := json.Unmarshal(j, o); err != nil {
return nil, err
// TransformFunc returns a cache.TransformFunc that converts unstructured data into a typed object.
// It accepts a variable number of mutate functions that are applied to the unstructured
// object before it is converted to the target type. This allows for pre-processing or modification
// of the unstructured data before it is transformed.
func TransformFunc[T any](mutateFns ...func(unstructuredObj *unstructured.Unstructured)) cache.TransformFunc {
return func(obj any) (any, error) {
unstructuredObj, ok := obj.(*unstructured.Unstructured)
if !ok {
return nil, fmt.Errorf("unexpected object type: %T", obj)
}

for _, fn := range mutateFns {
fn(unstructuredObj)
}

j, err := unstructuredObj.MarshalJSON()
if err != nil {
return nil, err
}
o := new(T)
if err := json.Unmarshal(j, o); err != nil {
return nil, err
}
return *o, nil
}
return *o, nil
}

func Restructure[T any](obj any) (any, error) {
return TransformFunc[T]()(obj)
}

func Destruct[T any](obj T) (*unstructured.Unstructured, error) {
Expand Down
5 changes: 5 additions & 0 deletions examples/kuadrant/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/samber/lo"
istiov1 "istio.io/client-go/pkg/apis/security/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
Expand Down Expand Up @@ -125,6 +126,10 @@ func main() {
&gwapiv1.Gateway{},
controller.GatewaysResource,
metav1.NamespaceAll,
// Example of using custom transformer function
controller.WithTransformerFunc[*gwapiv1.Gateway](controller.TransformFunc[*gwapiv1.Gateway](func(unstructuredObj *unstructured.Unstructured) {
unstructuredObj.SetManagedFields(nil)
})),
controller.WithPredicates(&ctrlruntimepredicate.TypedGenerationChangedPredicate[*gwapiv1.Gateway]{})),
),
controller.WithRunnable("httproute watcher", buildWatcher(
Expand Down

0 comments on commit 168c27d

Please sign in to comment.