From 129eab896d4def9717d9db30bd8772b6475e2bad Mon Sep 17 00:00:00 2001 From: Wim Henderickx Date: Sun, 16 Apr 2023 16:04:33 +0200 Subject: [PATCH 1/7] added new generic parser interface --- krm-functions/lib/parser/parser.go | 215 ++++++++++++++++++++++++++++- 1 file changed, 211 insertions(+), 4 deletions(-) diff --git a/krm-functions/lib/parser/parser.go b/krm-functions/lib/parser/parser.go index 2fda2e75..bf73736f 100644 --- a/krm-functions/lib/parser/parser.go +++ b/krm-functions/lib/parser/parser.go @@ -17,11 +17,218 @@ limitations under the License. package parser import ( + "fmt" + "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn" + "sigs.k8s.io/yaml" +) + +const ( + // errors + errKubeObjectNotInitialized = "KubeObject not initialized" ) -type Parser interface { - // ParseKubeObject returns a fn sdk KubeObject; if something failed an error - // is returned - ParseKubeObject() (*fn.KubeObject, error) +type Parser[T1 any] interface { + // GetKubeObject returns the present kubeObject + GetKubeObject() *fn.KubeObject + // GetGoStruct returns a go struct representing the present KRM resource + GetGoStruct() (T1, error) + // GetStringValue is a generic utility function that returns a string from + // a string slice representing the path in the yaml doc + GetStringValue(fields ...string) string + // GetIntValue is a generic utility function that returns a int from + // a string slice representing the path in the yaml doc + GetIntValue(fields ...string) int + // GetBoolValue is a generic utility function that returns a bool from + // a string slice representing the path in the yaml doc + GetBoolValue(fields ...string) bool + // GetStringMap is a generic utility function that returns a map[string]string from + // a string slice representing the path in the yaml doc + GetStringMap(fields ...string) map[string]string + // SetNestedString is a generic utility function that sets a string on + // a string slice representing the path in the yaml doc + SetNestedString(s string, fields ...string) error + // SetNestedInt is a generic utility function that sets a int on + // a string slice representing the path in the yaml doc + SetNestedInt(s int, fields ...string) error + // SetNestedBool is a generic utility function that sets a bool on + // a string slice representing the path in the yaml doc + SetNestedBool(s bool, fields ...string) error + // SetNestedMap is a generic utility function that sets a map[string]string on + // a string slice representing the path in the yaml doc + SetNestedMap(s map[string]string, fields ...string) error + // DeleteNestedField is a generic utility function that deletes + // a string slice representing the path from the yaml doc + DeleteNestedField(fields ...string) error +} + +// NewFromKubeObject creates a new parser interface +// It expects a *fn.KubeObject as input representing the serialized yaml file +func NewFromKubeObject[T1 any](o *fn.KubeObject) Parser[T1] { + return &obj[T1]{ + o: o, + } +} + +// NewFromYaml creates a new parser interface +// It expects raw byte slice as input representing the serialized yaml file +func NewFromYaml[T1 any](b []byte) (Parser[T1], error) { + o, err := fn.ParseKubeObject(b) + if err != nil { + return nil, err + } + return NewFromKubeObject[T1](o), nil +} + +// NewFromGoStruct creates a new parser interface +// It expects a go struct representing the interface krm resource +func NewFromGoStruct[T1 any](x any) (Parser[T1], error) { + b, err := yaml.Marshal(x) + if err != nil { + return nil, err + } + return NewFromYaml[T1](b) +} + +type obj[T1 any] struct { + o *fn.KubeObject +} + +// GetKubeObject returns the present kubeObject +func (r *obj[T1]) GetKubeObject() *fn.KubeObject { + return r.o +} + +// GetGoStruct returns a go struct representing the present KRM resource +func (r *obj[T1]) GetGoStruct() (T1, error) { + var x T1 + if err := yaml.Unmarshal([]byte(r.o.String()), &x); err != nil { + return x, err + } + return x, nil +} + +// GetStringValue is a generic utility function that returns a string from +// a string slice representing the path in the yaml doc +func (r *obj[T1]) GetStringValue(fields ...string) string { + if r.o == nil { + return "" + } + s, ok, err := r.o.NestedString(fields...) + if err != nil { + return "" + } + if !ok { + return "" + } + return s +} + +// GetIntValue is a generic utility function that returns a int from +// a string slice representing the path in the yaml doc +func (r *obj[T1]) GetIntValue(fields ...string) int { + if r.o == nil { + return 0 + } + i, ok, err := r.o.NestedInt(fields...) + if err != nil { + return 0 + } + if !ok { + return 0 + } + return i +} + +// GetBoolValue is a generic utility function that returns a bool from +// a string slice representing the path in the yaml doc +func (r *obj[T1]) GetBoolValue(fields ...string) bool { + if r.o == nil { + return false + } + b, ok, err := r.o.NestedBool(fields...) + if err != nil { + return false + } + if !ok { + return false + } + return b +} + +// GetStringMap is a generic utility function that returns a map[string]string from +// a string slice representing the path in the yaml doc +func (r *obj[T1]) GetStringMap(fields ...string) map[string]string { + if r.o == nil { + return map[string]string{} + } + m, ok, err := r.o.NestedStringMap(fields...) + if err != nil { + return map[string]string{} + } + if !ok { + return map[string]string{} + } + return m +} + +// SetNestedField is a generic utility function that sets a string on +// a string slice representing the path in the yaml doc +func (r *obj[T1]) SetNestedString(s string, fields ...string) error { + if r.o == nil { + return fmt.Errorf(errKubeObjectNotInitialized) + } + if err := r.o.SetNestedField(s, fields...); err != nil { + return err + } + return nil +} + +// SetNestedInt is a generic utility function that sets a int on +// a string slice representing the path in the yaml doc +func (r *obj[T1]) SetNestedInt(s int, fields ...string) error { + if r.o == nil { + return fmt.Errorf(errKubeObjectNotInitialized) + } + if err := r.o.SetNestedInt(s, fields...); err != nil { + return err + } + return nil +} + +// SetNestedBool is a generic utility function that sets a bool on +// a string slice representing the path in the yaml doc +func (r *obj[T1]) SetNestedBool(s bool, fields ...string) error { + if r.o == nil { + return fmt.Errorf(errKubeObjectNotInitialized) + } + if err := r.o.SetNestedBool(s, fields...); err != nil { + return err + } + return nil +} + +// SetNestedMap is a generic utility function that sets a map[string]string on +// a string slice representing the path in the yaml doc +func (r *obj[T1]) SetNestedMap(s map[string]string, fields ...string) error { + if r.o == nil { + return fmt.Errorf(errKubeObjectNotInitialized) + } + if err := r.o.SetNestedStringMap(s, fields...); err != nil { + return err + } + return nil +} + +// DeleteNestedField is a generic utility function that deletes +// a string slice representing the path from the yaml doc +func (r *obj[T1]) DeleteNestedField(fields ...string) error { + if r.o == nil { + return fmt.Errorf(errKubeObjectNotInitialized) + } + _, err := r.o.RemoveNestedField(fields...) + if err != nil { + return err + } + return nil } From 7efd8128070a6649dec7386fd442eb33d1519936 Mon Sep 17 00:00:00 2001 From: Ganesh Chandrasekaran Date: Tue, 18 Apr 2023 21:46:59 +0530 Subject: [PATCH 2/7] Updated linting with proper comments --- krm-functions/lib/parser/parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/krm-functions/lib/parser/parser.go b/krm-functions/lib/parser/parser.go index bf73736f..6b204f3b 100644 --- a/krm-functions/lib/parser/parser.go +++ b/krm-functions/lib/parser/parser.go @@ -172,7 +172,7 @@ func (r *obj[T1]) GetStringMap(fields ...string) map[string]string { return m } -// SetNestedField is a generic utility function that sets a string on +// SetNestedString is a generic utility function that sets a string on // a string slice representing the path in the yaml doc func (r *obj[T1]) SetNestedString(s string, fields ...string) error { if r.o == nil { From 941c717ade184a24901cd1b704e655928d2e7ec5 Mon Sep 17 00:00:00 2001 From: Ganesh Chandrasekaran Date: Tue, 18 Apr 2023 21:49:12 +0530 Subject: [PATCH 3/7] testing for parser --- krm-functions/lib/parser/parser_test.go | 489 ++++++++++++++++++++++++ 1 file changed, 489 insertions(+) create mode 100644 krm-functions/lib/parser/parser_test.go diff --git a/krm-functions/lib/parser/parser_test.go b/krm-functions/lib/parser/parser_test.go new file mode 100644 index 00000000..eb88dbde --- /dev/null +++ b/krm-functions/lib/parser/parser_test.go @@ -0,0 +1,489 @@ +package parser + +import ( + "fmt" + "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn" + v1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/yaml" + "testing" +) + +type objParser struct { + p Parser[v1.Deployment] +} + +func TestNewFromKubeObject(t *testing.T) { + type object struct { + namespace string + overwrittenNamespace string + config string + name string + gv string + kind string + replicas int32 + overwrittenReplicas int32 + paused bool + overwrittenPaused bool + selector map[string]string + overwrittenSelector map[string]string + } + testItems := []struct { + input object + }{ + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "a", + namespace: "b", + overwrittenNamespace: "new", + config: "c", + replicas: 3, + overwrittenReplicas: 10, + paused: true, + overwrittenPaused: false, + selector: map[string]string{"install": "output"}, + overwrittenSelector: map[string]string{"install": "large", "network": "CONF"}, + }, + }, + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "d", + namespace: "e", + overwrittenNamespace: "old", + config: "f", + replicas: 10, + overwrittenReplicas: 3, + paused: false, + overwrittenPaused: true, + selector: map[string]string{"flavor": "large"}, + overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, + }, + }, + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "", + namespace: "", + overwrittenNamespace: "", + config: "", + replicas: 0, + overwrittenReplicas: 0, + paused: false, + overwrittenPaused: true, + selector: map[string]string{"flavor": "large"}, + overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, + }, + }, + } + for _, tt := range testItems { + deploymentReceived := v1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: tt.input.gv, + Kind: tt.input.kind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: tt.input.name, + Namespace: tt.input.namespace, + }, + Spec: v1.DeploymentSpec{ + Replicas: &tt.input.replicas, + Paused: tt.input.paused, + Selector: &metav1.LabelSelector{ + MatchLabels: tt.input.selector, + }, + }, + } + b, err := yaml.Marshal(deploymentReceived) + if err != nil { + t.Errorf("YAML Marshal error: %s", err) + } + deploymentKubeObject, _ := fn.ParseKubeObject(b) + deploymentKubeObjectParser := NewFromKubeObject[v1.Deployment](deploymentKubeObject) + deploymentKubeObjectParserObject := &objParser{ + p: deploymentKubeObjectParser, + } + fmt.Println(deploymentKubeObjectParserObject.p.GetKubeObject().String()) + if deploymentKubeObjectParser.GetKubeObject() != deploymentKubeObjectParserObject.p.GetKubeObject() { + t.Errorf("TestNewFromKubeObject: -want%s, +got:\n%s", deploymentKubeObjectParserObject.p.GetKubeObject(), deploymentKubeObjectParser.GetKubeObject()) + } + deploymentGoStruct, _ := deploymentKubeObjectParser.GetGoStruct() + if deploymentGoStruct.Name != deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...) { + t.Errorf("TestNewFromKubeObject: -want%s, +got:\n%s", deploymentKubeObjectParserObject.p.GetKubeObject(), deploymentKubeObjectParser.GetKubeObject()) + } + if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...) != tt.input.name { + t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.name, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...)) + } + if deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...) != int(tt.input.replicas) { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", tt.input.replicas, deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...)) + } + if deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...) != tt.input.paused { + t.Errorf("TestNewFromKubeObject: -want:%t, +got:%t", tt.input.paused, deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...)) + } + if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"metadata", "name"}...)) != 0 { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 0, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"metadata", "name"}...))) + } + if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != len(tt.input.selector) { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", len(tt.input.selector), len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) + } + if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...) != tt.input.namespace { + t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.namespace, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...)) + } + + err = deploymentKubeObjectParserObject.p.SetNestedString(tt.input.overwrittenNamespace, []string{"metadata", "namespace"}...) + if err != nil { + t.Errorf("SetNestedString error: %s", err) + } + if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...) != tt.input.overwrittenNamespace { + t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.overwrittenNamespace, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...)) + } + + err = deploymentKubeObjectParserObject.p.SetNestedInt(int(tt.input.overwrittenReplicas), []string{"spec", "replicas"}...) + if err != nil { + t.Errorf("SetNestedInt error: %s", err) + } + if deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...) != int(tt.input.overwrittenReplicas) { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", tt.input.overwrittenReplicas, deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...)) + } + + err = deploymentKubeObjectParserObject.p.SetNestedBool(tt.input.overwrittenPaused, []string{"spec", "paused"}...) + if err != nil { + t.Errorf("SetNestedBool error: %s", err) + } + if deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...) != tt.input.overwrittenPaused { + t.Errorf("TestNewFromKubeObject: -want:%t, +got:%t", tt.input.overwrittenPaused, deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...)) + } + + err = deploymentKubeObjectParserObject.p.SetNestedMap(tt.input.overwrittenSelector, []string{"spec", "selector", "matchLabels"}...) + if err != nil { + t.Errorf("SetNestedBool error: %s", err) + } + if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != len(tt.input.overwrittenSelector) { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 2, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) + } + + err = deploymentKubeObjectParserObject.p.DeleteNestedField([]string{"spec", "selector", "matchLabels"}...) + if err != nil { + t.Errorf("SetNestedBool error: %s", err) + } + if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != 0 { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 0, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) + } + } +} + +func TestNewFromYaml(t *testing.T) { + type object struct { + namespace string + overwrittenNamespace string + config string + name string + gv string + kind string + replicas int32 + overwrittenReplicas int32 + paused bool + overwrittenPaused bool + selector map[string]string + overwrittenSelector map[string]string + } + testItems := []struct { + input object + }{ + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "a", + namespace: "b", + overwrittenNamespace: "new", + config: "c", + replicas: 3, + overwrittenReplicas: 10, + paused: true, + overwrittenPaused: false, + selector: map[string]string{"install": "output"}, + overwrittenSelector: map[string]string{"install": "large", "network": "CONF"}, + }, + }, + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "d", + namespace: "e", + overwrittenNamespace: "old", + config: "f", + replicas: 10, + overwrittenReplicas: 3, + paused: false, + overwrittenPaused: true, + selector: map[string]string{"flavor": "large"}, + overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, + }, + }, + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "", + namespace: "", + overwrittenNamespace: "", + config: "", + replicas: 0, + overwrittenReplicas: 0, + paused: false, + overwrittenPaused: true, + selector: map[string]string{"flavor": "large"}, + overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, + }, + }, + } + for _, tt := range testItems { + deploymentReceived := v1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: tt.input.gv, + Kind: tt.input.kind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: tt.input.name, + Namespace: tt.input.namespace, + }, + Spec: v1.DeploymentSpec{ + Replicas: &tt.input.replicas, + Paused: tt.input.paused, + Selector: &metav1.LabelSelector{ + MatchLabels: tt.input.selector, + }, + }, + } + b, err := yaml.Marshal(deploymentReceived) + if err != nil { + t.Errorf("YAML Marshal error: %s", err) + } + deploymentKubeObjectParser, _ := NewFromYaml[v1.Deployment](b) + deploymentKubeObjectParserObject := &objParser{ + p: deploymentKubeObjectParser, + } + fmt.Println(deploymentKubeObjectParserObject.p.GetKubeObject().String()) + if deploymentKubeObjectParser.GetKubeObject() != deploymentKubeObjectParserObject.p.GetKubeObject() { + t.Errorf("TestNewFromKubeObject: -want%s, +got:\n%s", deploymentKubeObjectParserObject.p.GetKubeObject(), deploymentKubeObjectParser.GetKubeObject()) + } + if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...) != tt.input.name { + t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.name, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...)) + } + if deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...) != int(tt.input.replicas) { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", tt.input.replicas, deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...)) + } + if deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...) != tt.input.paused { + t.Errorf("TestNewFromKubeObject: -want:%t, +got:%t", tt.input.paused, deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...)) + } + if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"metadata", "name"}...)) != 0 { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 0, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"metadata", "name"}...))) + } + if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != len(tt.input.selector) { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", len(tt.input.selector), len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) + } + if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...) != tt.input.namespace { + t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.namespace, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...)) + } + + err = deploymentKubeObjectParserObject.p.SetNestedString(tt.input.overwrittenNamespace, []string{"metadata", "namespace"}...) + if err != nil { + t.Errorf("SetNestedString error: %s", err) + } + if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...) != tt.input.overwrittenNamespace { + t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.overwrittenNamespace, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...)) + } + + err = deploymentKubeObjectParserObject.p.SetNestedInt(int(tt.input.overwrittenReplicas), []string{"spec", "replicas"}...) + if err != nil { + t.Errorf("SetNestedInt error: %s", err) + } + if deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...) != int(tt.input.overwrittenReplicas) { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", tt.input.overwrittenReplicas, deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...)) + } + + err = deploymentKubeObjectParserObject.p.SetNestedBool(tt.input.overwrittenPaused, []string{"spec", "paused"}...) + if err != nil { + t.Errorf("SetNestedBool error: %s", err) + } + if deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...) != tt.input.overwrittenPaused { + t.Errorf("TestNewFromKubeObject: -want:%t, +got:%t", tt.input.overwrittenPaused, deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...)) + } + + err = deploymentKubeObjectParserObject.p.SetNestedMap(tt.input.overwrittenSelector, []string{"spec", "selector", "matchLabels"}...) + if err != nil { + t.Errorf("SetNestedBool error: %s", err) + } + if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != len(tt.input.overwrittenSelector) { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 2, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) + } + + err = deploymentKubeObjectParserObject.p.DeleteNestedField([]string{"spec", "selector", "matchLabels"}...) + if err != nil { + t.Errorf("SetNestedBool error: %s", err) + } + if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != 0 { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 0, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) + } + } +} + +func TestNewFromGoStruct(t *testing.T) { + type object struct { + namespace string + overwrittenNamespace string + config string + name string + gv string + kind string + replicas int32 + overwrittenReplicas int32 + paused bool + overwrittenPaused bool + selector map[string]string + overwrittenSelector map[string]string + } + testItems := []struct { + input object + }{ + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "a", + namespace: "b", + overwrittenNamespace: "new", + config: "c", + replicas: 3, + overwrittenReplicas: 10, + paused: true, + overwrittenPaused: false, + selector: map[string]string{"install": "output"}, + overwrittenSelector: map[string]string{"install": "large", "network": "CONF"}, + }, + }, + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "d", + namespace: "e", + overwrittenNamespace: "old", + config: "f", + replicas: 10, + overwrittenReplicas: 3, + paused: false, + overwrittenPaused: true, + selector: map[string]string{"flavor": "large"}, + overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, + }, + }, + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "", + namespace: "", + overwrittenNamespace: "", + config: "", + replicas: 0, + overwrittenReplicas: 0, + paused: false, + overwrittenPaused: true, + selector: map[string]string{"flavor": "large"}, + overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, + }, + }, + } + for _, tt := range testItems { + deploymentReceived := v1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: tt.input.gv, + Kind: tt.input.kind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: tt.input.name, + Namespace: tt.input.namespace, + }, + Spec: v1.DeploymentSpec{ + Replicas: &tt.input.replicas, + Paused: tt.input.paused, + Selector: &metav1.LabelSelector{ + MatchLabels: tt.input.selector, + }, + }, + } + deploymentKubeObjectParser, _ := NewFromGoStruct[v1.Deployment](deploymentReceived) + deploymentKubeObjectParserObject := &objParser{ + p: deploymentKubeObjectParser, + } + fmt.Println(deploymentKubeObjectParserObject.p.GetKubeObject().String()) + if deploymentKubeObjectParser.GetKubeObject() != deploymentKubeObjectParserObject.p.GetKubeObject() { + t.Errorf("TestNewFromKubeObject: -want%s, +got:\n%s", deploymentKubeObjectParserObject.p.GetKubeObject(), deploymentKubeObjectParser.GetKubeObject()) + } + if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...) != tt.input.name { + t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.name, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...)) + } + if deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...) != int(tt.input.replicas) { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", tt.input.replicas, deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...)) + } + if deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...) != tt.input.paused { + t.Errorf("TestNewFromKubeObject: -want:%t, +got:%t", tt.input.paused, deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...)) + } + if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"metadata", "name"}...)) != 0 { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 0, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"metadata", "name"}...))) + } + if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != len(tt.input.selector) { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", len(tt.input.selector), len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) + } + if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...) != tt.input.namespace { + t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.namespace, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...)) + } + + err := deploymentKubeObjectParserObject.p.SetNestedString(tt.input.overwrittenNamespace, []string{"metadata", "namespace"}...) + if err != nil { + t.Errorf("SetNestedString error: %s", err) + } + if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...) != tt.input.overwrittenNamespace { + t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.overwrittenNamespace, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...)) + } + + err = deploymentKubeObjectParserObject.p.SetNestedInt(int(tt.input.overwrittenReplicas), []string{"spec", "replicas"}...) + if err != nil { + t.Errorf("SetNestedInt error: %s", err) + } + if deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...) != int(tt.input.overwrittenReplicas) { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", tt.input.overwrittenReplicas, deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...)) + } + + err = deploymentKubeObjectParserObject.p.SetNestedBool(tt.input.overwrittenPaused, []string{"spec", "paused"}...) + if err != nil { + t.Errorf("SetNestedBool error: %s", err) + } + if deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...) != tt.input.overwrittenPaused { + t.Errorf("TestNewFromKubeObject: -want:%t, +got:%t", tt.input.overwrittenPaused, deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...)) + } + + err = deploymentKubeObjectParserObject.p.SetNestedMap(tt.input.overwrittenSelector, []string{"spec", "selector", "matchLabels"}...) + if err != nil { + t.Errorf("SetNestedBool error: %s", err) + } + if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != len(tt.input.overwrittenSelector) { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 2, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) + } + + err = deploymentKubeObjectParserObject.p.DeleteNestedField([]string{"spec", "selector", "matchLabels"}...) + if err != nil { + t.Errorf("SetNestedBool error: %s", err) + } + if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != 0 { + t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 0, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) + } + } +} From 17176860c5ddceadc1760edfefc2e5cf1b019bf1 Mon Sep 17 00:00:00 2001 From: Ganesh Chandrasekaran Date: Wed, 19 Apr 2023 15:28:56 +0530 Subject: [PATCH 4/7] removed debugging depandencies in testing --- krm-functions/lib/parser/parser_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/krm-functions/lib/parser/parser_test.go b/krm-functions/lib/parser/parser_test.go index eb88dbde..07c4e646 100644 --- a/krm-functions/lib/parser/parser_test.go +++ b/krm-functions/lib/parser/parser_test.go @@ -1,7 +1,6 @@ package parser import ( - "fmt" "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn" v1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -107,7 +106,6 @@ func TestNewFromKubeObject(t *testing.T) { deploymentKubeObjectParserObject := &objParser{ p: deploymentKubeObjectParser, } - fmt.Println(deploymentKubeObjectParserObject.p.GetKubeObject().String()) if deploymentKubeObjectParser.GetKubeObject() != deploymentKubeObjectParserObject.p.GetKubeObject() { t.Errorf("TestNewFromKubeObject: -want%s, +got:\n%s", deploymentKubeObjectParserObject.p.GetKubeObject(), deploymentKubeObjectParser.GetKubeObject()) } @@ -269,7 +267,6 @@ func TestNewFromYaml(t *testing.T) { deploymentKubeObjectParserObject := &objParser{ p: deploymentKubeObjectParser, } - fmt.Println(deploymentKubeObjectParserObject.p.GetKubeObject().String()) if deploymentKubeObjectParser.GetKubeObject() != deploymentKubeObjectParserObject.p.GetKubeObject() { t.Errorf("TestNewFromKubeObject: -want%s, +got:\n%s", deploymentKubeObjectParserObject.p.GetKubeObject(), deploymentKubeObjectParser.GetKubeObject()) } @@ -423,7 +420,6 @@ func TestNewFromGoStruct(t *testing.T) { deploymentKubeObjectParserObject := &objParser{ p: deploymentKubeObjectParser, } - fmt.Println(deploymentKubeObjectParserObject.p.GetKubeObject().String()) if deploymentKubeObjectParser.GetKubeObject() != deploymentKubeObjectParserObject.p.GetKubeObject() { t.Errorf("TestNewFromKubeObject: -want%s, +got:\n%s", deploymentKubeObjectParserObject.p.GetKubeObject(), deploymentKubeObjectParser.GetKubeObject()) } From 0ca3db383cb6a99c0d018751546081eff01eb072 Mon Sep 17 00:00:00 2001 From: Wim Henderickx Date: Thu, 20 Apr 2023 19:20:24 +0200 Subject: [PATCH 5/7] updated strategy --- krm-functions/lib/kubeobject/kubeobject.go | 60 +++ .../lib/kubeobject/kubeobject_test.go | 317 ++++++++++++ krm-functions/lib/parser/parser.go | 234 --------- krm-functions/lib/parser/parser_test.go | 485 ------------------ 4 files changed, 377 insertions(+), 719 deletions(-) create mode 100644 krm-functions/lib/kubeobject/kubeobject.go create mode 100644 krm-functions/lib/kubeobject/kubeobject_test.go delete mode 100644 krm-functions/lib/parser/parser.go delete mode 100644 krm-functions/lib/parser/parser_test.go diff --git a/krm-functions/lib/kubeobject/kubeobject.go b/krm-functions/lib/kubeobject/kubeobject.go new file mode 100644 index 00000000..5b89a141 --- /dev/null +++ b/krm-functions/lib/kubeobject/kubeobject.go @@ -0,0 +1,60 @@ +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package parser + +import ( + "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn" + "sigs.k8s.io/yaml" +) + +type KubeObject[T1 any] struct { + fn.KubeObject +} + +func (r *KubeObject[T1]) GetGoStruct() (T1, error) { + var x T1 + if err := yaml.Unmarshal([]byte(r.String()), &x); err != nil { + return x, err + } + return x, nil +} + +// NewFromKubeObject returns a KubeObject struct +// It expects a fn.KubeObject as input representing the serialized yaml file +func NewFromKubeObject[T1 any](o fn.KubeObject) *KubeObject[T1] { + return &KubeObject[T1]{o} +} + +// NewFromYaml returns a KubeObject struct +// It expects raw byte slice as input representing the serialized yaml file +func NewFromYaml[T1 any](b []byte) (*KubeObject[T1], error) { + o, err := fn.ParseKubeObject(b) + if err != nil { + return nil, err + } + return NewFromKubeObject[T1](*o), nil +} + +// NewFromGoStruct returns a KubeObject struct +// It expects a go struct representing the interface krm resource +func NewFromGoStruct[T1 any](x any) (*KubeObject[T1], error) { + b, err := yaml.Marshal(x) + if err != nil { + return nil, err + } + return NewFromYaml[T1](b) +} diff --git a/krm-functions/lib/kubeobject/kubeobject_test.go b/krm-functions/lib/kubeobject/kubeobject_test.go new file mode 100644 index 00000000..ba4b8b02 --- /dev/null +++ b/krm-functions/lib/kubeobject/kubeobject_test.go @@ -0,0 +1,317 @@ +package parser + +import ( + "testing" + + "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn" + v1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/yaml" +) + +func TestNewFromKubeObject(t *testing.T) { + type object struct { + namespace string + overwrittenNamespace string + config string + name string + gv string + kind string + replicas int32 + overwrittenReplicas int32 + paused bool + overwrittenPaused bool + selector map[string]string + overwrittenSelector map[string]string + } + testItems := []struct { + input object + }{ + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "a", + namespace: "b", + overwrittenNamespace: "new", + config: "c", + replicas: 3, + overwrittenReplicas: 10, + paused: true, + overwrittenPaused: false, + selector: map[string]string{"install": "output"}, + overwrittenSelector: map[string]string{"install": "large", "network": "CONF"}, + }, + }, + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "d", + namespace: "e", + overwrittenNamespace: "old", + config: "f", + replicas: 10, + overwrittenReplicas: 3, + paused: false, + overwrittenPaused: true, + selector: map[string]string{"flavor": "large"}, + overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, + }, + }, + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "", + namespace: "", + overwrittenNamespace: "", + config: "", + replicas: 0, + overwrittenReplicas: 0, + paused: false, + overwrittenPaused: true, + selector: map[string]string{"flavor": "large"}, + overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, + }, + }, + } + for _, tt := range testItems { + deploymentReceived := v1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: tt.input.gv, + Kind: tt.input.kind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: tt.input.name, + Namespace: tt.input.namespace, + }, + Spec: v1.DeploymentSpec{ + Replicas: &tt.input.replicas, + Paused: tt.input.paused, + Selector: &metav1.LabelSelector{ + MatchLabels: tt.input.selector, + }, + }, + } + b, err := yaml.Marshal(deploymentReceived) + if err != nil { + t.Errorf("YAML Marshal error: %s", err) + } + deploymentKubeObject, _ := fn.ParseKubeObject(b) + deploymentKubeObjectParser := NewFromKubeObject[v1.Deployment](*deploymentKubeObject) + if deploymentKubeObjectParser.SubObject != deploymentKubeObject.SubObject { + t.Errorf("-want%s, +got:\n%s", deploymentKubeObjectParser.String(), deploymentKubeObject.String()) + } + deploymentGoStruct, _ := deploymentKubeObjectParser.GetGoStruct() + s, _, err := deploymentKubeObjectParser.NestedString([]string{"metadata", "name"}...) + if err != nil { + t.Errorf("unexpected error: %v\n", err) + } + if deploymentGoStruct.Name != s { + t.Errorf("-want%s, +got:\n%s", deploymentGoStruct.Name, s) + } + } +} + +func TestNewFromYaml(t *testing.T) { + type object struct { + namespace string + overwrittenNamespace string + config string + name string + gv string + kind string + replicas int32 + overwrittenReplicas int32 + paused bool + overwrittenPaused bool + selector map[string]string + overwrittenSelector map[string]string + } + testItems := []struct { + input object + }{ + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "a", + namespace: "b", + overwrittenNamespace: "new", + config: "c", + replicas: 3, + overwrittenReplicas: 10, + paused: true, + overwrittenPaused: false, + selector: map[string]string{"install": "output"}, + overwrittenSelector: map[string]string{"install": "large", "network": "CONF"}, + }, + }, + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "d", + namespace: "e", + overwrittenNamespace: "old", + config: "f", + replicas: 10, + overwrittenReplicas: 3, + paused: false, + overwrittenPaused: true, + selector: map[string]string{"flavor": "large"}, + overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, + }, + }, + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "", + namespace: "", + overwrittenNamespace: "", + config: "", + replicas: 0, + overwrittenReplicas: 0, + paused: false, + overwrittenPaused: true, + selector: map[string]string{"flavor": "large"}, + overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, + }, + }, + } + for _, tt := range testItems { + deploymentReceived := v1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: tt.input.gv, + Kind: tt.input.kind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: tt.input.name, + Namespace: tt.input.namespace, + }, + Spec: v1.DeploymentSpec{ + Replicas: &tt.input.replicas, + Paused: tt.input.paused, + Selector: &metav1.LabelSelector{ + MatchLabels: tt.input.selector, + }, + }, + } + b, err := yaml.Marshal(deploymentReceived) + if err != nil { + t.Errorf("YAML Marshal error: %s", err) + } + deploymentKubeObjectParser, _ := NewFromYaml[v1.Deployment](b) + + if deploymentKubeObjectParser.String() != string(b) { + t.Errorf("-want%s, +got:\n%s", string(b), deploymentKubeObjectParser.String()) + } + deploymentGoStruct, _ := deploymentKubeObjectParser.GetGoStruct() + s, _, err := deploymentKubeObjectParser.NestedString([]string{"metadata", "name"}...) + if err != nil { + t.Errorf("unexpected error: %v\n", err) + } + if deploymentGoStruct.Name != s { + t.Errorf("-want%s, +got:\n%s", deploymentGoStruct.Name, s) + } + } +} + +func TestNewFromGoStruct(t *testing.T) { + type object struct { + namespace string + overwrittenNamespace string + config string + name string + gv string + kind string + replicas int32 + overwrittenReplicas int32 + paused bool + overwrittenPaused bool + selector map[string]string + overwrittenSelector map[string]string + } + testItems := []struct { + input object + }{ + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "a", + namespace: "b", + overwrittenNamespace: "new", + config: "c", + replicas: 3, + overwrittenReplicas: 10, + paused: true, + overwrittenPaused: false, + selector: map[string]string{"install": "output"}, + overwrittenSelector: map[string]string{"install": "large", "network": "CONF"}, + }, + }, + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "d", + namespace: "e", + overwrittenNamespace: "old", + config: "f", + replicas: 10, + overwrittenReplicas: 3, + paused: false, + overwrittenPaused: true, + selector: map[string]string{"flavor": "large"}, + overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, + }, + }, + { + input: object{ + gv: "apps/v1", + kind: "Deployment", + name: "", + namespace: "", + overwrittenNamespace: "", + config: "", + replicas: 0, + overwrittenReplicas: 0, + paused: false, + overwrittenPaused: true, + selector: map[string]string{"flavor": "large"}, + overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, + }, + }, + } + for _, tt := range testItems { + deploymentReceived := v1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: tt.input.gv, + Kind: tt.input.kind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: tt.input.name, + Namespace: tt.input.namespace, + }, + Spec: v1.DeploymentSpec{ + Replicas: &tt.input.replicas, + Paused: tt.input.paused, + Selector: &metav1.LabelSelector{ + MatchLabels: tt.input.selector, + }, + }, + } + deploymentKubeObjectParser, _ := NewFromGoStruct[v1.Deployment](deploymentReceived) + + s, _, err := deploymentKubeObjectParser.NestedString([]string{"metadata", "name"}...) + if err != nil { + t.Errorf("unexpected error: %v\n", err) + } + if deploymentReceived.Name != s { + t.Errorf("-want%s, +got:\n%s", deploymentReceived.Name, s) + } + } +} diff --git a/krm-functions/lib/parser/parser.go b/krm-functions/lib/parser/parser.go deleted file mode 100644 index 6b204f3b..00000000 --- a/krm-functions/lib/parser/parser.go +++ /dev/null @@ -1,234 +0,0 @@ -/* -Copyright 2023 The Nephio Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package parser - -import ( - "fmt" - - "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn" - "sigs.k8s.io/yaml" -) - -const ( - // errors - errKubeObjectNotInitialized = "KubeObject not initialized" -) - -type Parser[T1 any] interface { - // GetKubeObject returns the present kubeObject - GetKubeObject() *fn.KubeObject - // GetGoStruct returns a go struct representing the present KRM resource - GetGoStruct() (T1, error) - // GetStringValue is a generic utility function that returns a string from - // a string slice representing the path in the yaml doc - GetStringValue(fields ...string) string - // GetIntValue is a generic utility function that returns a int from - // a string slice representing the path in the yaml doc - GetIntValue(fields ...string) int - // GetBoolValue is a generic utility function that returns a bool from - // a string slice representing the path in the yaml doc - GetBoolValue(fields ...string) bool - // GetStringMap is a generic utility function that returns a map[string]string from - // a string slice representing the path in the yaml doc - GetStringMap(fields ...string) map[string]string - // SetNestedString is a generic utility function that sets a string on - // a string slice representing the path in the yaml doc - SetNestedString(s string, fields ...string) error - // SetNestedInt is a generic utility function that sets a int on - // a string slice representing the path in the yaml doc - SetNestedInt(s int, fields ...string) error - // SetNestedBool is a generic utility function that sets a bool on - // a string slice representing the path in the yaml doc - SetNestedBool(s bool, fields ...string) error - // SetNestedMap is a generic utility function that sets a map[string]string on - // a string slice representing the path in the yaml doc - SetNestedMap(s map[string]string, fields ...string) error - // DeleteNestedField is a generic utility function that deletes - // a string slice representing the path from the yaml doc - DeleteNestedField(fields ...string) error -} - -// NewFromKubeObject creates a new parser interface -// It expects a *fn.KubeObject as input representing the serialized yaml file -func NewFromKubeObject[T1 any](o *fn.KubeObject) Parser[T1] { - return &obj[T1]{ - o: o, - } -} - -// NewFromYaml creates a new parser interface -// It expects raw byte slice as input representing the serialized yaml file -func NewFromYaml[T1 any](b []byte) (Parser[T1], error) { - o, err := fn.ParseKubeObject(b) - if err != nil { - return nil, err - } - return NewFromKubeObject[T1](o), nil -} - -// NewFromGoStruct creates a new parser interface -// It expects a go struct representing the interface krm resource -func NewFromGoStruct[T1 any](x any) (Parser[T1], error) { - b, err := yaml.Marshal(x) - if err != nil { - return nil, err - } - return NewFromYaml[T1](b) -} - -type obj[T1 any] struct { - o *fn.KubeObject -} - -// GetKubeObject returns the present kubeObject -func (r *obj[T1]) GetKubeObject() *fn.KubeObject { - return r.o -} - -// GetGoStruct returns a go struct representing the present KRM resource -func (r *obj[T1]) GetGoStruct() (T1, error) { - var x T1 - if err := yaml.Unmarshal([]byte(r.o.String()), &x); err != nil { - return x, err - } - return x, nil -} - -// GetStringValue is a generic utility function that returns a string from -// a string slice representing the path in the yaml doc -func (r *obj[T1]) GetStringValue(fields ...string) string { - if r.o == nil { - return "" - } - s, ok, err := r.o.NestedString(fields...) - if err != nil { - return "" - } - if !ok { - return "" - } - return s -} - -// GetIntValue is a generic utility function that returns a int from -// a string slice representing the path in the yaml doc -func (r *obj[T1]) GetIntValue(fields ...string) int { - if r.o == nil { - return 0 - } - i, ok, err := r.o.NestedInt(fields...) - if err != nil { - return 0 - } - if !ok { - return 0 - } - return i -} - -// GetBoolValue is a generic utility function that returns a bool from -// a string slice representing the path in the yaml doc -func (r *obj[T1]) GetBoolValue(fields ...string) bool { - if r.o == nil { - return false - } - b, ok, err := r.o.NestedBool(fields...) - if err != nil { - return false - } - if !ok { - return false - } - return b -} - -// GetStringMap is a generic utility function that returns a map[string]string from -// a string slice representing the path in the yaml doc -func (r *obj[T1]) GetStringMap(fields ...string) map[string]string { - if r.o == nil { - return map[string]string{} - } - m, ok, err := r.o.NestedStringMap(fields...) - if err != nil { - return map[string]string{} - } - if !ok { - return map[string]string{} - } - return m -} - -// SetNestedString is a generic utility function that sets a string on -// a string slice representing the path in the yaml doc -func (r *obj[T1]) SetNestedString(s string, fields ...string) error { - if r.o == nil { - return fmt.Errorf(errKubeObjectNotInitialized) - } - if err := r.o.SetNestedField(s, fields...); err != nil { - return err - } - return nil -} - -// SetNestedInt is a generic utility function that sets a int on -// a string slice representing the path in the yaml doc -func (r *obj[T1]) SetNestedInt(s int, fields ...string) error { - if r.o == nil { - return fmt.Errorf(errKubeObjectNotInitialized) - } - if err := r.o.SetNestedInt(s, fields...); err != nil { - return err - } - return nil -} - -// SetNestedBool is a generic utility function that sets a bool on -// a string slice representing the path in the yaml doc -func (r *obj[T1]) SetNestedBool(s bool, fields ...string) error { - if r.o == nil { - return fmt.Errorf(errKubeObjectNotInitialized) - } - if err := r.o.SetNestedBool(s, fields...); err != nil { - return err - } - return nil -} - -// SetNestedMap is a generic utility function that sets a map[string]string on -// a string slice representing the path in the yaml doc -func (r *obj[T1]) SetNestedMap(s map[string]string, fields ...string) error { - if r.o == nil { - return fmt.Errorf(errKubeObjectNotInitialized) - } - if err := r.o.SetNestedStringMap(s, fields...); err != nil { - return err - } - return nil -} - -// DeleteNestedField is a generic utility function that deletes -// a string slice representing the path from the yaml doc -func (r *obj[T1]) DeleteNestedField(fields ...string) error { - if r.o == nil { - return fmt.Errorf(errKubeObjectNotInitialized) - } - _, err := r.o.RemoveNestedField(fields...) - if err != nil { - return err - } - return nil -} diff --git a/krm-functions/lib/parser/parser_test.go b/krm-functions/lib/parser/parser_test.go deleted file mode 100644 index 07c4e646..00000000 --- a/krm-functions/lib/parser/parser_test.go +++ /dev/null @@ -1,485 +0,0 @@ -package parser - -import ( - "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn" - v1 "k8s.io/api/apps/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/yaml" - "testing" -) - -type objParser struct { - p Parser[v1.Deployment] -} - -func TestNewFromKubeObject(t *testing.T) { - type object struct { - namespace string - overwrittenNamespace string - config string - name string - gv string - kind string - replicas int32 - overwrittenReplicas int32 - paused bool - overwrittenPaused bool - selector map[string]string - overwrittenSelector map[string]string - } - testItems := []struct { - input object - }{ - { - input: object{ - gv: "apps/v1", - kind: "Deployment", - name: "a", - namespace: "b", - overwrittenNamespace: "new", - config: "c", - replicas: 3, - overwrittenReplicas: 10, - paused: true, - overwrittenPaused: false, - selector: map[string]string{"install": "output"}, - overwrittenSelector: map[string]string{"install": "large", "network": "CONF"}, - }, - }, - { - input: object{ - gv: "apps/v1", - kind: "Deployment", - name: "d", - namespace: "e", - overwrittenNamespace: "old", - config: "f", - replicas: 10, - overwrittenReplicas: 3, - paused: false, - overwrittenPaused: true, - selector: map[string]string{"flavor": "large"}, - overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, - }, - }, - { - input: object{ - gv: "apps/v1", - kind: "Deployment", - name: "", - namespace: "", - overwrittenNamespace: "", - config: "", - replicas: 0, - overwrittenReplicas: 0, - paused: false, - overwrittenPaused: true, - selector: map[string]string{"flavor": "large"}, - overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, - }, - }, - } - for _, tt := range testItems { - deploymentReceived := v1.Deployment{ - TypeMeta: metav1.TypeMeta{ - APIVersion: tt.input.gv, - Kind: tt.input.kind, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: tt.input.name, - Namespace: tt.input.namespace, - }, - Spec: v1.DeploymentSpec{ - Replicas: &tt.input.replicas, - Paused: tt.input.paused, - Selector: &metav1.LabelSelector{ - MatchLabels: tt.input.selector, - }, - }, - } - b, err := yaml.Marshal(deploymentReceived) - if err != nil { - t.Errorf("YAML Marshal error: %s", err) - } - deploymentKubeObject, _ := fn.ParseKubeObject(b) - deploymentKubeObjectParser := NewFromKubeObject[v1.Deployment](deploymentKubeObject) - deploymentKubeObjectParserObject := &objParser{ - p: deploymentKubeObjectParser, - } - if deploymentKubeObjectParser.GetKubeObject() != deploymentKubeObjectParserObject.p.GetKubeObject() { - t.Errorf("TestNewFromKubeObject: -want%s, +got:\n%s", deploymentKubeObjectParserObject.p.GetKubeObject(), deploymentKubeObjectParser.GetKubeObject()) - } - deploymentGoStruct, _ := deploymentKubeObjectParser.GetGoStruct() - if deploymentGoStruct.Name != deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...) { - t.Errorf("TestNewFromKubeObject: -want%s, +got:\n%s", deploymentKubeObjectParserObject.p.GetKubeObject(), deploymentKubeObjectParser.GetKubeObject()) - } - if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...) != tt.input.name { - t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.name, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...)) - } - if deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...) != int(tt.input.replicas) { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", tt.input.replicas, deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...)) - } - if deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...) != tt.input.paused { - t.Errorf("TestNewFromKubeObject: -want:%t, +got:%t", tt.input.paused, deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...)) - } - if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"metadata", "name"}...)) != 0 { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 0, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"metadata", "name"}...))) - } - if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != len(tt.input.selector) { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", len(tt.input.selector), len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) - } - if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...) != tt.input.namespace { - t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.namespace, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...)) - } - - err = deploymentKubeObjectParserObject.p.SetNestedString(tt.input.overwrittenNamespace, []string{"metadata", "namespace"}...) - if err != nil { - t.Errorf("SetNestedString error: %s", err) - } - if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...) != tt.input.overwrittenNamespace { - t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.overwrittenNamespace, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...)) - } - - err = deploymentKubeObjectParserObject.p.SetNestedInt(int(tt.input.overwrittenReplicas), []string{"spec", "replicas"}...) - if err != nil { - t.Errorf("SetNestedInt error: %s", err) - } - if deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...) != int(tt.input.overwrittenReplicas) { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", tt.input.overwrittenReplicas, deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...)) - } - - err = deploymentKubeObjectParserObject.p.SetNestedBool(tt.input.overwrittenPaused, []string{"spec", "paused"}...) - if err != nil { - t.Errorf("SetNestedBool error: %s", err) - } - if deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...) != tt.input.overwrittenPaused { - t.Errorf("TestNewFromKubeObject: -want:%t, +got:%t", tt.input.overwrittenPaused, deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...)) - } - - err = deploymentKubeObjectParserObject.p.SetNestedMap(tt.input.overwrittenSelector, []string{"spec", "selector", "matchLabels"}...) - if err != nil { - t.Errorf("SetNestedBool error: %s", err) - } - if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != len(tt.input.overwrittenSelector) { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 2, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) - } - - err = deploymentKubeObjectParserObject.p.DeleteNestedField([]string{"spec", "selector", "matchLabels"}...) - if err != nil { - t.Errorf("SetNestedBool error: %s", err) - } - if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != 0 { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 0, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) - } - } -} - -func TestNewFromYaml(t *testing.T) { - type object struct { - namespace string - overwrittenNamespace string - config string - name string - gv string - kind string - replicas int32 - overwrittenReplicas int32 - paused bool - overwrittenPaused bool - selector map[string]string - overwrittenSelector map[string]string - } - testItems := []struct { - input object - }{ - { - input: object{ - gv: "apps/v1", - kind: "Deployment", - name: "a", - namespace: "b", - overwrittenNamespace: "new", - config: "c", - replicas: 3, - overwrittenReplicas: 10, - paused: true, - overwrittenPaused: false, - selector: map[string]string{"install": "output"}, - overwrittenSelector: map[string]string{"install": "large", "network": "CONF"}, - }, - }, - { - input: object{ - gv: "apps/v1", - kind: "Deployment", - name: "d", - namespace: "e", - overwrittenNamespace: "old", - config: "f", - replicas: 10, - overwrittenReplicas: 3, - paused: false, - overwrittenPaused: true, - selector: map[string]string{"flavor": "large"}, - overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, - }, - }, - { - input: object{ - gv: "apps/v1", - kind: "Deployment", - name: "", - namespace: "", - overwrittenNamespace: "", - config: "", - replicas: 0, - overwrittenReplicas: 0, - paused: false, - overwrittenPaused: true, - selector: map[string]string{"flavor": "large"}, - overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, - }, - }, - } - for _, tt := range testItems { - deploymentReceived := v1.Deployment{ - TypeMeta: metav1.TypeMeta{ - APIVersion: tt.input.gv, - Kind: tt.input.kind, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: tt.input.name, - Namespace: tt.input.namespace, - }, - Spec: v1.DeploymentSpec{ - Replicas: &tt.input.replicas, - Paused: tt.input.paused, - Selector: &metav1.LabelSelector{ - MatchLabels: tt.input.selector, - }, - }, - } - b, err := yaml.Marshal(deploymentReceived) - if err != nil { - t.Errorf("YAML Marshal error: %s", err) - } - deploymentKubeObjectParser, _ := NewFromYaml[v1.Deployment](b) - deploymentKubeObjectParserObject := &objParser{ - p: deploymentKubeObjectParser, - } - if deploymentKubeObjectParser.GetKubeObject() != deploymentKubeObjectParserObject.p.GetKubeObject() { - t.Errorf("TestNewFromKubeObject: -want%s, +got:\n%s", deploymentKubeObjectParserObject.p.GetKubeObject(), deploymentKubeObjectParser.GetKubeObject()) - } - if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...) != tt.input.name { - t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.name, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...)) - } - if deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...) != int(tt.input.replicas) { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", tt.input.replicas, deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...)) - } - if deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...) != tt.input.paused { - t.Errorf("TestNewFromKubeObject: -want:%t, +got:%t", tt.input.paused, deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...)) - } - if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"metadata", "name"}...)) != 0 { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 0, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"metadata", "name"}...))) - } - if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != len(tt.input.selector) { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", len(tt.input.selector), len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) - } - if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...) != tt.input.namespace { - t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.namespace, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...)) - } - - err = deploymentKubeObjectParserObject.p.SetNestedString(tt.input.overwrittenNamespace, []string{"metadata", "namespace"}...) - if err != nil { - t.Errorf("SetNestedString error: %s", err) - } - if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...) != tt.input.overwrittenNamespace { - t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.overwrittenNamespace, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...)) - } - - err = deploymentKubeObjectParserObject.p.SetNestedInt(int(tt.input.overwrittenReplicas), []string{"spec", "replicas"}...) - if err != nil { - t.Errorf("SetNestedInt error: %s", err) - } - if deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...) != int(tt.input.overwrittenReplicas) { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", tt.input.overwrittenReplicas, deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...)) - } - - err = deploymentKubeObjectParserObject.p.SetNestedBool(tt.input.overwrittenPaused, []string{"spec", "paused"}...) - if err != nil { - t.Errorf("SetNestedBool error: %s", err) - } - if deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...) != tt.input.overwrittenPaused { - t.Errorf("TestNewFromKubeObject: -want:%t, +got:%t", tt.input.overwrittenPaused, deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...)) - } - - err = deploymentKubeObjectParserObject.p.SetNestedMap(tt.input.overwrittenSelector, []string{"spec", "selector", "matchLabels"}...) - if err != nil { - t.Errorf("SetNestedBool error: %s", err) - } - if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != len(tt.input.overwrittenSelector) { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 2, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) - } - - err = deploymentKubeObjectParserObject.p.DeleteNestedField([]string{"spec", "selector", "matchLabels"}...) - if err != nil { - t.Errorf("SetNestedBool error: %s", err) - } - if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != 0 { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 0, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) - } - } -} - -func TestNewFromGoStruct(t *testing.T) { - type object struct { - namespace string - overwrittenNamespace string - config string - name string - gv string - kind string - replicas int32 - overwrittenReplicas int32 - paused bool - overwrittenPaused bool - selector map[string]string - overwrittenSelector map[string]string - } - testItems := []struct { - input object - }{ - { - input: object{ - gv: "apps/v1", - kind: "Deployment", - name: "a", - namespace: "b", - overwrittenNamespace: "new", - config: "c", - replicas: 3, - overwrittenReplicas: 10, - paused: true, - overwrittenPaused: false, - selector: map[string]string{"install": "output"}, - overwrittenSelector: map[string]string{"install": "large", "network": "CONF"}, - }, - }, - { - input: object{ - gv: "apps/v1", - kind: "Deployment", - name: "d", - namespace: "e", - overwrittenNamespace: "old", - config: "f", - replicas: 10, - overwrittenReplicas: 3, - paused: false, - overwrittenPaused: true, - selector: map[string]string{"flavor": "large"}, - overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, - }, - }, - { - input: object{ - gv: "apps/v1", - kind: "Deployment", - name: "", - namespace: "", - overwrittenNamespace: "", - config: "", - replicas: 0, - overwrittenReplicas: 0, - paused: false, - overwrittenPaused: true, - selector: map[string]string{"flavor": "large"}, - overwrittenSelector: map[string]string{"flavor": "large", "network": "VLAN"}, - }, - }, - } - for _, tt := range testItems { - deploymentReceived := v1.Deployment{ - TypeMeta: metav1.TypeMeta{ - APIVersion: tt.input.gv, - Kind: tt.input.kind, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: tt.input.name, - Namespace: tt.input.namespace, - }, - Spec: v1.DeploymentSpec{ - Replicas: &tt.input.replicas, - Paused: tt.input.paused, - Selector: &metav1.LabelSelector{ - MatchLabels: tt.input.selector, - }, - }, - } - deploymentKubeObjectParser, _ := NewFromGoStruct[v1.Deployment](deploymentReceived) - deploymentKubeObjectParserObject := &objParser{ - p: deploymentKubeObjectParser, - } - if deploymentKubeObjectParser.GetKubeObject() != deploymentKubeObjectParserObject.p.GetKubeObject() { - t.Errorf("TestNewFromKubeObject: -want%s, +got:\n%s", deploymentKubeObjectParserObject.p.GetKubeObject(), deploymentKubeObjectParser.GetKubeObject()) - } - if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...) != tt.input.name { - t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.name, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "name"}...)) - } - if deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...) != int(tt.input.replicas) { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", tt.input.replicas, deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...)) - } - if deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...) != tt.input.paused { - t.Errorf("TestNewFromKubeObject: -want:%t, +got:%t", tt.input.paused, deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...)) - } - if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"metadata", "name"}...)) != 0 { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 0, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"metadata", "name"}...))) - } - if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != len(tt.input.selector) { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", len(tt.input.selector), len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) - } - if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...) != tt.input.namespace { - t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.namespace, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...)) - } - - err := deploymentKubeObjectParserObject.p.SetNestedString(tt.input.overwrittenNamespace, []string{"metadata", "namespace"}...) - if err != nil { - t.Errorf("SetNestedString error: %s", err) - } - if deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...) != tt.input.overwrittenNamespace { - t.Errorf("TestNewFromKubeObject: -want:%s, +got:%s", tt.input.overwrittenNamespace, deploymentKubeObjectParserObject.p.GetStringValue([]string{"metadata", "namespace"}...)) - } - - err = deploymentKubeObjectParserObject.p.SetNestedInt(int(tt.input.overwrittenReplicas), []string{"spec", "replicas"}...) - if err != nil { - t.Errorf("SetNestedInt error: %s", err) - } - if deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...) != int(tt.input.overwrittenReplicas) { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", tt.input.overwrittenReplicas, deploymentKubeObjectParserObject.p.GetIntValue([]string{"spec", "replicas"}...)) - } - - err = deploymentKubeObjectParserObject.p.SetNestedBool(tt.input.overwrittenPaused, []string{"spec", "paused"}...) - if err != nil { - t.Errorf("SetNestedBool error: %s", err) - } - if deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...) != tt.input.overwrittenPaused { - t.Errorf("TestNewFromKubeObject: -want:%t, +got:%t", tt.input.overwrittenPaused, deploymentKubeObjectParserObject.p.GetBoolValue([]string{"spec", "paused"}...)) - } - - err = deploymentKubeObjectParserObject.p.SetNestedMap(tt.input.overwrittenSelector, []string{"spec", "selector", "matchLabels"}...) - if err != nil { - t.Errorf("SetNestedBool error: %s", err) - } - if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != len(tt.input.overwrittenSelector) { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 2, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) - } - - err = deploymentKubeObjectParserObject.p.DeleteNestedField([]string{"spec", "selector", "matchLabels"}...) - if err != nil { - t.Errorf("SetNestedBool error: %s", err) - } - if len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...)) != 0 { - t.Errorf("TestNewFromKubeObject: -want:%v, +got:%v", 0, len(deploymentKubeObjectParserObject.p.GetStringMap([]string{"spec", "selector", "matchLabels"}...))) - } - } -} From 7fc62fcacda2f09014976cae855a4f674c6f6564 Mon Sep 17 00:00:00 2001 From: Wim Henderickx Date: Thu, 20 Apr 2023 20:18:44 +0200 Subject: [PATCH 6/7] new NewFromKubeObject with nil check --- krm-functions/lib/kubeobject/kubeobject.go | 29 +++++++++++-------- .../lib/kubeobject/kubeobject_test.go | 21 ++++++++++++-- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/krm-functions/lib/kubeobject/kubeobject.go b/krm-functions/lib/kubeobject/kubeobject.go index 5b89a141..2d56928a 100644 --- a/krm-functions/lib/kubeobject/kubeobject.go +++ b/krm-functions/lib/kubeobject/kubeobject.go @@ -14,18 +14,20 @@ See the License for the specific language governing permissions and limitations under the License. */ -package parser +package kubeobject import ( + "fmt" + "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn" "sigs.k8s.io/yaml" ) -type KubeObject[T1 any] struct { +type KubeObjectExt[T1 any] struct { fn.KubeObject } -func (r *KubeObject[T1]) GetGoStruct() (T1, error) { +func (r *KubeObjectExt[T1]) GetGoStruct() (T1, error) { var x T1 if err := yaml.Unmarshal([]byte(r.String()), &x); err != nil { return x, err @@ -33,28 +35,31 @@ func (r *KubeObject[T1]) GetGoStruct() (T1, error) { return x, nil } -// NewFromKubeObject returns a KubeObject struct +// NewFromKubeObject returns a KubeObjectExt struct // It expects a fn.KubeObject as input representing the serialized yaml file -func NewFromKubeObject[T1 any](o fn.KubeObject) *KubeObject[T1] { - return &KubeObject[T1]{o} +func NewFromKubeObject[T1 any](o *fn.KubeObject) (*KubeObjectExt[T1], error) { + if o == nil { + return nil, fmt.Errorf("cannot initialize with a nil object") + } + return &KubeObjectExt[T1]{*o}, nil } -// NewFromYaml returns a KubeObject struct +// NewFromYaml returns a KubeObjectExt struct // It expects raw byte slice as input representing the serialized yaml file -func NewFromYaml[T1 any](b []byte) (*KubeObject[T1], error) { +func NewFromYaml[T1 any](b []byte) (*KubeObjectExt[T1], error) { o, err := fn.ParseKubeObject(b) if err != nil { return nil, err } - return NewFromKubeObject[T1](*o), nil + return NewFromKubeObject[T1](o) } -// NewFromGoStruct returns a KubeObject struct +// NewFromGoStruct returns a KubeObjectExt struct // It expects a go struct representing the interface krm resource -func NewFromGoStruct[T1 any](x any) (*KubeObject[T1], error) { +func NewFromGoStruct[T1 any](x any) (*KubeObjectExt[T1], error) { b, err := yaml.Marshal(x) if err != nil { return nil, err } return NewFromYaml[T1](b) -} +} \ No newline at end of file diff --git a/krm-functions/lib/kubeobject/kubeobject_test.go b/krm-functions/lib/kubeobject/kubeobject_test.go index ba4b8b02..e2d61e77 100644 --- a/krm-functions/lib/kubeobject/kubeobject_test.go +++ b/krm-functions/lib/kubeobject/kubeobject_test.go @@ -1,4 +1,21 @@ -package parser +/* +Copyright 2023 The Nephio Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + +package kubeobject import ( "testing" @@ -99,7 +116,7 @@ func TestNewFromKubeObject(t *testing.T) { t.Errorf("YAML Marshal error: %s", err) } deploymentKubeObject, _ := fn.ParseKubeObject(b) - deploymentKubeObjectParser := NewFromKubeObject[v1.Deployment](*deploymentKubeObject) + deploymentKubeObjectParser, _ := NewFromKubeObject[v1.Deployment](deploymentKubeObject) if deploymentKubeObjectParser.SubObject != deploymentKubeObject.SubObject { t.Errorf("-want%s, +got:\n%s", deploymentKubeObjectParser.String(), deploymentKubeObject.String()) } From 0bedbb38ae3e1562dd1020411be859fa7be7853d Mon Sep 17 00:00:00 2001 From: Wim Henderickx Date: Thu, 20 Apr 2023 20:40:28 +0200 Subject: [PATCH 7/7] fixed go fmt --- krm-functions/lib/kubeobject/kubeobject.go | 2 +- krm-functions/lib/kubeobject/kubeobject_test.go | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/krm-functions/lib/kubeobject/kubeobject.go b/krm-functions/lib/kubeobject/kubeobject.go index 2d56928a..3c093e3f 100644 --- a/krm-functions/lib/kubeobject/kubeobject.go +++ b/krm-functions/lib/kubeobject/kubeobject.go @@ -62,4 +62,4 @@ func NewFromGoStruct[T1 any](x any) (*KubeObjectExt[T1], error) { return nil, err } return NewFromYaml[T1](b) -} \ No newline at end of file +} diff --git a/krm-functions/lib/kubeobject/kubeobject_test.go b/krm-functions/lib/kubeobject/kubeobject_test.go index e2d61e77..78bc82a9 100644 --- a/krm-functions/lib/kubeobject/kubeobject_test.go +++ b/krm-functions/lib/kubeobject/kubeobject_test.go @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ - package kubeobject import ( @@ -221,7 +220,7 @@ func TestNewFromYaml(t *testing.T) { t.Errorf("YAML Marshal error: %s", err) } deploymentKubeObjectParser, _ := NewFromYaml[v1.Deployment](b) - + if deploymentKubeObjectParser.String() != string(b) { t.Errorf("-want%s, +got:\n%s", string(b), deploymentKubeObjectParser.String()) } @@ -322,7 +321,7 @@ func TestNewFromGoStruct(t *testing.T) { }, } deploymentKubeObjectParser, _ := NewFromGoStruct[v1.Deployment](deploymentReceived) - + s, _, err := deploymentKubeObjectParser.NestedString([]string{"metadata", "name"}...) if err != nil { t.Errorf("unexpected error: %v\n", err)