diff --git a/modules/common/networkattachment/networkattachment.go b/modules/common/networkattachment/networkattachment.go index dcaa0e59..ce9102ee 100644 --- a/modules/common/networkattachment/networkattachment.go +++ b/modules/common/networkattachment/networkattachment.go @@ -204,3 +204,37 @@ func EnsureNetworksAnnotation( return annotationString, nil } + +// GetJSONPathFromConfig - returns the result of the jsonPath as string +// from the NetworkAttachmentDefinition config. +// if the NAD has no config, an empty string is returned. +// The jsonPath must be in the format e.g. ".ipam" +func GetJSONPathFromConfig(netAtt networkv1.NetworkAttachmentDefinition, path string) (string, error) { + var data interface{} + buf := new(bytes.Buffer) + + if netAtt.Spec.Config == "" { + return buf.String(), nil + } + + if err := json.Unmarshal([]byte(netAtt.Spec.Config), &data); err != nil { + return "", fmt.Errorf("failed to unmarshal JSON data: %w", err) + } + + // use jsonpath to parse the cni config + jp := jsonpath.New(netAtt.Name) + + // Parse the JSONPath template to get the ipam + err := jp.Parse(fmt.Sprintf(`{.%s}`, path)) + if err != nil { + return "", fmt.Errorf("parse template error: %w", err) + } + + // get the ipam from the config + err = jp.Execute(buf, data) + if err != nil { + return "", fmt.Errorf("parse execute template against nad %+v error: %w", netAtt.Spec.Config, err) + } + + return buf.String(), nil +} diff --git a/modules/common/networkattachment/networkattachment_test.go b/modules/common/networkattachment/networkattachment_test.go index ff558719..180f47be 100644 --- a/modules/common/networkattachment/networkattachment_test.go +++ b/modules/common/networkattachment/networkattachment_test.go @@ -291,3 +291,79 @@ func TestEnsureNetworksAnnotation(t *testing.T) { }) } } + +func TestGetJSONPathFromConfig(t *testing.T) { + + tests := []struct { + name string + nad networkv1.NetworkAttachmentDefinition + path string + want string + }{ + { + name: "No config", + nad: networkv1.NetworkAttachmentDefinition{}, + path: ".ipam", + want: "", + }, + { + name: "get .name", + nad: networkv1.NetworkAttachmentDefinition{ + ObjectMeta: metav1.ObjectMeta{Name: "one", Namespace: "foo"}, + Spec: networkv1.NetworkAttachmentDefinitionSpec{ + Config: ` + { + "cniVersion": "0.3.1", + "name": "internalapi", + "type": "macvlan", + "master": "internalapi", + "ipam": { + "type": "whereabouts", + "range": "172.17.0.0/24", + "range_start": "172.17.0.30", + "range_end": "172.17.0.70" + } + } + `, + }, + }, + path: ".name", + want: "internalapi", + }, + { + name: "get .ipam.range", + nad: networkv1.NetworkAttachmentDefinition{ + ObjectMeta: metav1.ObjectMeta{Name: "one", Namespace: "foo"}, + Spec: networkv1.NetworkAttachmentDefinitionSpec{ + Config: ` + { + "cniVersion": "0.3.1", + "name": "internalapi", + "type": "macvlan", + "master": "internalapi", + "ipam": { + "type": "whereabouts", + "range": "172.17.0.0/24", + "range_start": "172.17.0.30", + "range_end": "172.17.0.70" + } + } + `, + }, + }, + path: ".ipam.range", + want: "172.17.0.0/24", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + + networkAnnotation, err := GetJSONPathFromConfig(tt.nad, tt.path) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(networkAnnotation).To(HaveLen(len(tt.want))) + g.Expect(networkAnnotation).To(BeEquivalentTo(tt.want)) + }) + } +}