@@ -25,12 +25,15 @@ import (
2525 "k8s.io/apimachinery/pkg/runtime/schema"
2626 "k8s.io/apimachinery/pkg/types"
2727 "k8s.io/apimachinery/pkg/util/json"
28+ "reflect"
29+ "regexp"
2830 "sigs.k8s.io/controller-runtime/pkg/builder"
2931 "sigs.k8s.io/controller-runtime/pkg/controller"
3032 crevent "sigs.k8s.io/controller-runtime/pkg/event"
3133 "sigs.k8s.io/controller-runtime/pkg/handler"
3234 "sigs.k8s.io/controller-runtime/pkg/predicate"
3335 "sigs.k8s.io/controller-runtime/pkg/reconcile"
36+ "strconv"
3437 "strings"
3538 "time"
3639
@@ -127,7 +130,7 @@ func (r *ServiceMetadataWatcherReconciler) Reconcile(ctx context.Context, req ct
127130 for _ , field := range wso .WatchedFields {
128131 // TODO: validate field Source & Destination somewhere
129132
130- value , found , err := unstructured . NestedString (obj .Object , strings .Split (field .Source , "." )... )
133+ value , found , err := getNestedString (obj .Object , strings .Split (field .Source , "." ))
131134 if err != nil {
132135 // TODO: update status with error
133136 log .Error (err , "cannot get field" , "field" , field .Source )
@@ -312,3 +315,49 @@ func createServiceMetadataPatch(serviceId string, namespace string, field string
312315
313316 return jsonpatch .CreateMergePatch (oldClusterJSON , newClusterJSON )
314317}
318+
319+ // getNestedString returns the value of a nested field in the provided object
320+ // path is a list of keys separated by dots, e.g. "spec.template.spec.containers[0].image"
321+ // if the field is a slice, the last key must be in the form of "key[index]"
322+ func getNestedString (object interface {}, path []string ) (string , bool , error ) {
323+ re := regexp .MustCompile (`^(.*)\[(\d+)]$` )
324+ var cpath []string
325+ for i , key := range path {
326+ m := re .FindStringSubmatch (key )
327+ if len (m ) > 0 {
328+ cpath = append (cpath , m [1 ])
329+ index , _ := strconv .Atoi (m [2 ])
330+ slice , found , err := unstructured .NestedSlice (object .(map [string ]interface {}), cpath ... )
331+ if ! found || err != nil {
332+ return "" , false , err
333+ }
334+ if len (slice ) <= index {
335+ return "" , false , fmt .Errorf ("index out of range" )
336+ }
337+ return getNestedString (slice [index ], path [i + 1 :])
338+ }
339+ cpath = append (cpath , key )
340+ }
341+
342+ if reflect .TypeOf (object ).String () == "string" {
343+ return object .(string ), true , nil
344+ }
345+
346+ if reflect .TypeOf (object ).String () == "bool" {
347+ return strconv .FormatBool (object .(bool )), true , nil
348+ }
349+
350+ stringVal , found , err := unstructured .NestedString (object .(map [string ]interface {}), path ... )
351+ if found && err == nil {
352+ return stringVal , found , err
353+ }
354+
355+ boolVal , found , err := unstructured .NestedBool (object .(map [string ]interface {}), path ... )
356+ if found && err == nil {
357+ return strconv .FormatBool (boolVal ), found , err
358+ }
359+
360+ // TODO: handle additional types?
361+
362+ return "" , found , fmt .Errorf ("invalid field type" )
363+ }
0 commit comments