@@ -131,7 +131,14 @@ func (r *ServiceMetadataWatcherReconciler) Reconcile(ctx context.Context, req ct
131131 for _ , field := range wso .WatchedFields {
132132 // TODO: validate field Source & Destination somewhere
133133
134- value , found , err := getNestedString (obj .Object , strings .Split (field .Source , "." ))
134+ path , err := parsePath (field .Source )
135+ if err != nil {
136+ // TODO: update status with error
137+ log .Error (err , "cannot parse path" , "field" , field .Source )
138+ continue
139+ }
140+
141+ value , found , err := getNestedString (obj .Object , path )
135142 if err != nil {
136143 // TODO: update status with error
137144 log .Error (err , "cannot get field" , "field" , field .Source )
@@ -165,6 +172,15 @@ func (r *ServiceMetadataWatcherReconciler) Reconcile(ctx context.Context, req ct
165172 return noRequeue ()
166173}
167174
175+ func parsePath (path string ) ([]string , error ) {
176+ re := regexp .MustCompile (`(?:[^.\[]+|\[.*?\])` )
177+ p := re .FindAllString (path , - 1 )
178+ if len (p ) > 0 {
179+ return p , nil
180+ }
181+ return nil , fmt .Errorf ("invalid path" )
182+ }
183+
168184// SetupWithManager sets up the controller with the Manager.
169185func (r * ServiceMetadataWatcherReconciler ) SetupWithManager (ctx context.Context , mgr ctrl.Manager ) error {
170186 options := controller.Options {MaxConcurrentReconciles : 10 }
@@ -318,32 +334,40 @@ func createServiceMetadataPatch(serviceId string, namespace string, field string
318334}
319335
320336// getNestedString returns the value of a nested field in the provided object
321- // path is a list of keys separated by dots, e.g. "spec.template.spec.containers[0].image"
322- // if the field is a slice, the last key must be in the form of "key[index]"
337+ // path is a list of strings separated by dots, e.g. "spec.template.spec.containers[0].image"
338+ // If the field is a slice, the last string must be in the form of "field[index]", where index is an integer
339+ // If the field is a map, the last string must be in the form of "field[key]", where key is a string
323340func getNestedString (object interface {}, path []string ) (string , bool , error ) {
324- re := regexp .MustCompile (`^(.*)\[(\d+|[a-z]+)]$` )
325341 var cpath []string
326342 for i , key := range path {
327- m := re .FindStringSubmatch (key )
328- if len (m ) > 0 {
329- cpath = append (cpath , m [1 ])
330- slice , found , err := unstructured .NestedSlice (object .(map [string ]interface {}), cpath ... )
331- if ! found || err != nil {
332- return "" , false , err
333- }
334- index , err := strconv .Atoi (m [2 ])
335- if err != nil && m [2 ] != "last" {
336- return "" , false , fmt .Errorf ("invalid array index: %s" , m [2 ])
337- }
338- if m [2 ] == "last" {
339- index = len (slice ) - 1
343+ if strings .HasPrefix (key , "[" ) && strings .HasSuffix (key , "]" ) {
344+ k := key [1 : len (key )- 1 ]
345+
346+ sliceObj , found , err := unstructured .NestedSlice (object .(map [string ]interface {}), cpath ... )
347+ if err == nil && found {
348+ index , err := strconv .Atoi (k )
349+ if err != nil {
350+ return "" , false , fmt .Errorf ("invalid array index: %s" , k )
351+ }
352+ if index < 0 {
353+ index = len (sliceObj ) + index
354+ }
355+ if index >= len (sliceObj ) || index < 0 {
356+ return "" , false , fmt .Errorf ("index out of range" )
357+ }
358+ return getNestedString (sliceObj [index ], path [i + 1 :])
340359 }
341- if len (slice ) <= index {
342- return "" , false , fmt .Errorf ("index out of range" )
360+
361+ mapObj , found , err := unstructured .NestedMap (object .(map [string ]interface {}), cpath ... )
362+ if err == nil && found {
363+ if _ , ok := mapObj [k ]; ! ok {
364+ return "" , false , fmt .Errorf ("key not found: %s" , k )
365+ }
366+ return getNestedString (mapObj [k ], path [i + 1 :])
343367 }
344- return getNestedString (slice [index ], path [i + 1 :])
368+ } else {
369+ cpath = append (cpath , key )
345370 }
346- cpath = append (cpath , key )
347371 }
348372
349373 if reflect .TypeOf (object ).String () == "string" {
@@ -364,7 +388,15 @@ func getNestedString(object interface{}, path []string) (string, bool, error) {
364388 return strconv .FormatBool (boolVal ), found , err
365389 }
366390
367- // TODO: handle additional types?
391+ intVal , found , err := unstructured .NestedInt64 (object .(map [string ]interface {}), path ... )
392+ if found && err == nil {
393+ return strconv .FormatInt (intVal , 10 ), found , err
394+ }
395+
396+ floatVal , found , err := unstructured .NestedFloat64 (object .(map [string ]interface {}), path ... )
397+ if found && err == nil {
398+ return strconv .FormatFloat (floatVal , 'g' , - 1 , 64 ), found , err
399+ }
368400
369401 return "" , found , fmt .Errorf ("invalid field type" )
370402}
0 commit comments