Skip to content

Commit 1c4b0a0

Browse files
aalexandruaalexand
andauthored
Feature/service metadata fixes (#79)
* New release 1.5.0-unstable * Add support for referencing array indices in watchedField source --------- Co-authored-by: aalexand <[email protected]>
1 parent f6b753f commit 1c4b0a0

File tree

1 file changed

+50
-1
lines changed

1 file changed

+50
-1
lines changed

pkg/client/controllers/servicemetadatawatcher_controller.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)