diff --git a/krm-functions/lib/interface/v1alpha1/interface.go b/krm-functions/lib/interface/v1alpha1/interface.go index 534fbd04..56bcd2ac 100644 --- a/krm-functions/lib/interface/v1alpha1/interface.go +++ b/krm-functions/lib/interface/v1alpha1/interface.go @@ -17,224 +17,49 @@ package v1alpha1 import ( - "fmt" - "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn" nephioreqv1alpha1 "github.com/nephio-project/api/nf_requirements/v1alpha1" - "sigs.k8s.io/yaml" -) - -const ( - // errors - errKubeObjectNotInitialized = "KubeObject not initialized" + "github.com/nephio-project/nephio/krm-functions/lib/kubeobject" ) -var ( - attachmentType = []string{"spec", "attachmentType"} - cniType = []string{"spec", "cniType"} - networkInstanceName = []string{"spec", "networkInstance", "name"} -) +type Interface struct { + kubeobject.KubeObjectExt[*nephioreqv1alpha1.Interface] +} -type Interface interface { - // GetKubeObject returns the present kubeObject - GetKubeObject() *fn.KubeObject - // GetGoStruct returns a go struct representing the present KRM resource - GetGoStruct() (*nephioreqv1alpha1.Interface, error) - // GetAttachmentType returns the attachmentType from the spec - // if an error occurs or the attribute is not present an empty string is returned - GetAttachmentType() string - // GetCNIType returns the cniType from the spec - // if an error occurs or the attribute is not present an empty string is returned - GetCNIType() string - // GetNetworkInstanceName returns the name of the networkInstance from the spec - // if an error occurs or the attribute is not present an empty string is returned - GetNetworkInstanceName() string - // SetAttachmentType sets the attachmentType in the spec - SetAttachmentType(s string) error - // SetCNIType sets the cniType in the spec - SetCNIType(s string) error - // SetNetworkInstanceName sets the name of the networkInstance in the spec - SetNetworkInstanceName(s string) error - // SetSpec sets the spec attributes in the kubeobject according the go struct - SetSpec(*nephioreqv1alpha1.InterfaceSpec) error - // DeleteAttachmentType deletes the attachmentType from the spec - DeleteAttachmentType() error - // DeleteAttachmentType deletes the attachmentType from the spec - DeleteCNIType() error +// NewFromKubeObject creates a new parser interface +// It expects a *fn.KubeObject as input representing the serialized yaml file +func NewFromKubeObject(o *fn.KubeObject) (*Interface, error) { + r, err := kubeobject.NewFromKubeObject[*nephioreqv1alpha1.Interface](o) + if err != nil { + return nil, err + } + return &Interface{*r}, nil } // NewFromYAML creates a new parser interface // It expects a raw byte slice as input representing the serialized yaml file -func NewFromYAML(b []byte) (Interface, error) { - o, err := fn.ParseKubeObject(b) +func NewFromYAML(b []byte) (*Interface, error) { + r, err := kubeobject.NewFromYaml[*nephioreqv1alpha1.Interface](b) if err != nil { return nil, err } - return &itfce{ - o: o, - }, nil + return &Interface{*r}, nil } // NewFromGoStruct creates a new parser interface // It expects a go struct representing the interface krm resource -func NewFromGoStruct(x *nephioreqv1alpha1.Interface) (Interface, error) { - b, err := yaml.Marshal(x) +func NewFromGoStruct(x *nephioreqv1alpha1.Interface) (*Interface, error) { + r, err := kubeobject.NewFromGoStruct[*nephioreqv1alpha1.Interface](x) if err != nil { return nil, err } - return NewFromYAML(b) -} - -type itfce struct { - o *fn.KubeObject -} - -// GetKubeObject returns the present kubeObject -func (r *itfce) GetKubeObject() *fn.KubeObject { - return r.o -} - -// GetGoStruct returns a go struct representing the present KRM resource -func (r *itfce) GetGoStruct() (*nephioreqv1alpha1.Interface, error) { - x := &nephioreqv1alpha1.Interface{} - if err := yaml.Unmarshal([]byte(r.o.String()), x); err != nil { - return nil, err - } - return x, nil -} - -// GetAttachmentType returns the attachmentType from the spec -// if an error occurs or the attribute is not present an empty string is returned -func (r *itfce) GetAttachmentType() string { - return r.getStringValue(attachmentType...) -} - -// GetCNIType returns the cniType from the spec -// if an error occurs or the attribute is not present an empty string is returned -func (r *itfce) GetCNIType() string { - return r.getStringValue(cniType...) -} - -// GetNetworkInstanceName returns the name of the networkInstance from the spec -// if an error occurs or the attribute is not present an empty string is returned -func (r *itfce) GetNetworkInstanceName() string { - return r.getStringValue(networkInstanceName...) + return &Interface{*r}, nil } -// SetAttachmentType sets the attachmentType in the spec -func (r *itfce) SetAttachmentType(s string) error { - - // validation -> should be part of the interface api repo - switch s { - case string(nephioreqv1alpha1.AttachmentTypeNone): - case string(nephioreqv1alpha1.AttachmentTypeVLAN): - default: - return fmt.Errorf("unknown attachmentType") - } - - return r.setNestedField(s, attachmentType...) +func (r *Interface) SetSpec(spec nephioreqv1alpha1.InterfaceSpec) error { + return r.KubeObjectExt.SetSpec(spec) } -// SetCNIType sets the cniType in the spec -func (r *itfce) SetCNIType(s string) error { - - // validation -> should be part of the interface api repo - switch s { - case string(nephioreqv1alpha1.CNITypeIPVLAN): - case string(nephioreqv1alpha1.CNITypeSRIOV): - case string(nephioreqv1alpha1.CNITypeMACVLAN): - default: - return fmt.Errorf("unknown cniType") - } - - return r.setNestedField(s, cniType...) -} - -// SetNetworkInstanceName sets the name of the networkInstance in the spec -func (r *itfce) SetNetworkInstanceName(s string) error { - return r.setNestedField(s, networkInstanceName...) -} - -// SetSpec sets the spec attributes in the kubeobject according the go struct -func (r *itfce) SetSpec(spec *nephioreqv1alpha1.InterfaceSpec) error { - if spec == nil { - return nil - } - if spec.AttachmentType != "" { - if err := r.SetAttachmentType(string(spec.AttachmentType)); err != nil { - return err - } - } else { - if err := r.DeleteAttachmentType(); err != nil { - return err - } - } - if spec.CNIType != "" { - if err := r.SetCNIType(string(spec.CNIType)); err != nil { - return err - } - } else { - if err := r.DeleteCNIType(); err != nil { - return err - } - } - if spec.NetworkInstance != nil { - if err := r.SetNetworkInstanceName(string(spec.NetworkInstance.Name)); err != nil { - return err - } - } else { - return fmt.Errorf("networkInstance is required") - } - return nil -} - -// DeleteAttachmentType deletes the attachmentType from the spec -func (r *itfce) DeleteAttachmentType() error { - return r.deleteNestedField(attachmentType...) -} - -// DeleteAttachmentType deletes the attachmentType from the spec -func (r *itfce) DeleteCNIType() error { - return r.deleteNestedField(cniType...) -} - -// getStringValue is a generic utility function that returns a string from -// a string slice representing the path in the yaml doc -func (r *itfce) 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 -} - -// setNestedField is a generic utility function that sets a string on -// a string slice representing the path in the yaml doc -func (r *itfce) setNestedField(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 -} - -// deleteNestedField is a generic utility function that deletes -// a string slice representing the path from the yaml doc -func (r *itfce) deleteNestedField(fields ...string) error { - if r.o == nil { - return fmt.Errorf(errKubeObjectNotInitialized) - } - _, err := r.o.RemoveNestedField(fields...) - if err != nil { - return err - } - return nil +func (r *Interface) SetStatus(spec nephioreqv1alpha1.InterfaceStatus) error { + return r.KubeObjectExt.SetStatus(spec) } diff --git a/krm-functions/lib/interface/v1alpha1/interface_test.go b/krm-functions/lib/interface/v1alpha1/interface_test.go index 4d858446..47ba0649 100644 --- a/krm-functions/lib/interface/v1alpha1/interface_test.go +++ b/krm-functions/lib/interface/v1alpha1/interface_test.go @@ -21,6 +21,8 @@ import ( "github.com/google/go-cmp/cmp" nephioreqv1alpha1 "github.com/nephio-project/api/nf_requirements/v1alpha1" + ipamv1alpha1 "github.com/nokia/k8s-ipam/apis/alloc/ipam/v1alpha1" + vlanv1alpha1 "github.com/nokia/k8s-ipam/apis/alloc/vlan/v1alpha1" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -104,7 +106,7 @@ func TestNewFromGoStruct(t *testing.T) { }, "TestNewFromGoStructNil": { input: nil, - errExpected: true, + errExpected: false, // new approach does not return an error }, } @@ -140,10 +142,10 @@ func TestGetKubeObject(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - if diff := cmp.Diff(tc.wantKind, i.GetKubeObject().GetKind()); diff != "" { + if diff := cmp.Diff(tc.wantKind, i.GetKind()); diff != "" { t.Errorf("TestGetKubeObject: -want, +got:\n%s", diff) } - if diff := cmp.Diff(tc.wantName, i.GetKubeObject().GetName()); diff != "" { + if diff := cmp.Diff(tc.wantName, i.GetName()); diff != "" { t.Errorf("TestGetKubeObject: -want, +got:\n%s", diff) } }) @@ -152,16 +154,24 @@ func TestGetKubeObject(t *testing.T) { func TestGetGoStruct(t *testing.T) { cases := map[string]struct { - file string - want string + file string + wantAT nephioreqv1alpha1.AttachmentType + wantCT nephioreqv1alpha1.CNIType + wantNI *corev1.ObjectReference }{ - "TestGetGoStructNormal": { - file: itface, - want: "vlan", + "Normal": { + file: itface, + wantAT: nephioreqv1alpha1.AttachmentTypeVLAN, + wantCT: nephioreqv1alpha1.CNITypeSRIOV, + wantNI: &corev1.ObjectReference{ + Name: "vpc-ran", + }, }, - "TestGetGoStructEmpty": { - file: itfaceEmpty, - want: "", + "Empty": { + file: itfaceEmpty, + wantAT: "", + wantCT: "", + wantNI: nil, }, } @@ -174,227 +184,18 @@ func TestGetGoStruct(t *testing.T) { t.Run(name, func(t *testing.T) { g, err := i.GetGoStruct() assert.NoError(t, err) - got := g.Spec.AttachmentType - if diff := cmp.Diff(tc.want, string(got)); diff != "" { - t.Errorf("TestGetAttachmentType: -want, +got:\n%s", diff) - } - }) - } -} - -func TestGetAttachmentType(t *testing.T) { - cases := map[string]struct { - file string - want string - }{ - "GetAttachmentTypeNormal": { - file: itface, - want: "vlan", - }, - "GetAttachmentTypeEmpty": { - file: itfaceEmpty, - want: "", - }, - } - - for name, tc := range cases { - i, err := NewFromYAML([]byte(tc.file)) - if err != nil { - t.Errorf("cannot unmarshal file: %s", err.Error()) - } - - t.Run(name, func(t *testing.T) { - got := i.GetAttachmentType() - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("TestGetAttachmentType: -want, +got:\n%s", diff) - } - }) - } -} - -func TestGetCNIType(t *testing.T) { - cases := map[string]struct { - file string - want string - }{ - "GetCNITypeNormal": { - file: itface, - want: "sriov", - }, - "GetCNITypeEmpty": { - file: itfaceEmpty, - want: "", - }, - } - - for name, tc := range cases { - i, err := NewFromYAML([]byte(tc.file)) - if err != nil { - t.Errorf("cannot unmarshal file: %s", err.Error()) - } - - t.Run(name, func(t *testing.T) { - got := i.GetCNIType() - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("TestGetCNIType: -want, +got:\n%s", diff) - } - }) - } -} - -func TestGetNetworkInstanceName(t *testing.T) { - cases := map[string]struct { - file string - want string - }{ - "GetNetworkInstanceNameNormal": { - file: itface, - want: "vpc-ran", - }, - "GetNetworkInstanceNameEmpty": { - file: itfaceEmpty, - want: "", - }, - } - - for name, tc := range cases { - i, err := NewFromYAML([]byte(tc.file)) - if err != nil { - t.Errorf("cannot unmarshal file: %s", err.Error()) - } - - t.Run(name, func(t *testing.T) { - got := i.GetNetworkInstanceName() - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("TestGetNetworkInstanceName: -want, +got:\n%s", diff) - } - }) - } -} - -func TestSetAttachmentType(t *testing.T) { - cases := map[string]struct { - file string - value string - errExpected bool - }{ - "SetAttachmentTypeNormal": { - file: itface, - value: "none", - errExpected: false, - }, - "SetAttachmentTypeEmpty": { - file: itfaceEmpty, - value: "vlan", - errExpected: false, - }, - "SetAttachmentTypeUnknown": { - file: itfaceEmpty, - value: "a", - errExpected: true, - }, - } - - for name, tc := range cases { - i, err := NewFromYAML([]byte(tc.file)) - if err != nil { - t.Errorf("cannot unmarshal file: %s", err.Error()) - } - t.Run(name, func(t *testing.T) { - err := i.SetAttachmentType(tc.value) - if tc.errExpected { - assert.Error(t, err) - } else { - assert.NoError(t, err) - got := i.GetAttachmentType() - if diff := cmp.Diff(tc.value, got); diff != "" { - t.Errorf("TestSetAttachmentType: -want, +got:\n%s", diff) - } + gotAT := g.Spec.AttachmentType + if diff := cmp.Diff(tc.wantAT, gotAT); diff != "" { + t.Errorf("AttachmentType: -want, +got:\n%s", diff) } - - }) - } -} - -func TestSetCNIType(t *testing.T) { - cases := map[string]struct { - file string - value string - errExpected bool - }{ - "SetCNITypeNormal": { - file: itface, - value: "ipvlan", - errExpected: false, - }, - "SetCNITypeEmpty": { - file: itfaceEmpty, - value: "sriov", - errExpected: false, - }, - "SetCNITypeUnknown": { - file: itfaceEmpty, - value: "a", - errExpected: true, - }, - } - - for name, tc := range cases { - i, err := NewFromYAML([]byte(tc.file)) - if err != nil { - t.Errorf("cannot unmarshal file: %s", err.Error()) - } - - t.Run(name, func(t *testing.T) { - err := i.SetCNIType(tc.value) - if tc.errExpected { - assert.Error(t, err) - } else { - assert.NoError(t, err) - got := i.GetCNIType() - if diff := cmp.Diff(tc.value, got); diff != "" { - t.Errorf("TestSetCNIType: -want, +got:\n%s", diff) - } + gotCT := g.Spec.CNIType + if diff := cmp.Diff(tc.wantCT, gotCT); diff != "" { + t.Errorf("CNIType: -want, +got:\n%s", diff) } - }) - } -} - -func TestSetNetworkInstanceName(t *testing.T) { - cases := map[string]struct { - file string - value string - errExpected bool - }{ - "SetNetworkInstanceNameNormal": { - file: itface, - value: "a", - errExpected: false, - }, - "SetNetworkInstanceNameEmpty": { - file: itfaceEmpty, - value: "b", - errExpected: false, - }, - } - - for name, tc := range cases { - i, err := NewFromYAML([]byte(tc.file)) - if err != nil { - t.Errorf("cannot unmarshal file: %s", err.Error()) - } - - t.Run(name, func(t *testing.T) { - err := i.SetNetworkInstanceName(tc.value) - if tc.errExpected { - assert.Error(t, err) - } else { - assert.NoError(t, err) - got := i.GetNetworkInstanceName() - if diff := cmp.Diff(tc.value, got); diff != "" { - t.Errorf("TestSetNetworkInstanceName: -want, +got:\n%s", diff) - } + gotNI := g.Spec.NetworkInstance + if diff := cmp.Diff(tc.wantNI, gotNI); diff != "" { + t.Errorf("NetworkInstance: -want, +got:\n%s", diff) } }) } @@ -402,46 +203,36 @@ func TestSetNetworkInstanceName(t *testing.T) { func TestSetSpec(t *testing.T) { cases := map[string]struct { - file string - spec *nephioreqv1alpha1.InterfaceSpec - errExpected bool - defaultCNIType string - defaultAttachmentType string + file string + t nephioreqv1alpha1.InterfaceSpec }{ - "SetInterfaceSpecNormal": { - file: itface, - defaultCNIType: "sriov", - defaultAttachmentType: "vlan", - spec: &nephioreqv1alpha1.InterfaceSpec{ + "Override": { + file: itface, + t: nephioreqv1alpha1.InterfaceSpec{ NetworkInstance: &corev1.ObjectReference{ - Name: "test", + Name: "a", + Namespace: "b", }, AttachmentType: nephioreqv1alpha1.AttachmentTypeNone, CNIType: nephioreqv1alpha1.CNITypeIPVLAN, }, - errExpected: false, }, - "SetInterfaceSpecDefault": { - file: itface, - defaultCNIType: "", - defaultAttachmentType: "", - spec: &nephioreqv1alpha1.InterfaceSpec{ - NetworkInstance: &corev1.ObjectReference{ - Name: "test", - }, + "Change": { + file: itface, + t: nephioreqv1alpha1.InterfaceSpec{ + CNIType: nephioreqv1alpha1.CNITypeIPVLAN, }, - errExpected: false, }, - "SetInterfaceSpecEmpty": { - file: itfaceEmpty, - defaultCNIType: "", - defaultAttachmentType: "", - spec: &nephioreqv1alpha1.InterfaceSpec{ + "Empty": { + file: itfaceEmpty, + t: nephioreqv1alpha1.InterfaceSpec{ NetworkInstance: &corev1.ObjectReference{ - Name: "test", + Name: "a", + Namespace: "b", }, + AttachmentType: nephioreqv1alpha1.AttachmentTypeNone, + CNIType: nephioreqv1alpha1.CNITypeIPVLAN, }, - errExpected: false, }, } @@ -452,79 +243,35 @@ func TestSetSpec(t *testing.T) { } t.Run(name, func(t *testing.T) { - err := i.SetSpec(tc.spec) - if tc.errExpected { - assert.Error(t, err) - } else { - assert.NoError(t, err) - if tc.spec.NetworkInstance != nil { - got := i.GetNetworkInstanceName() - if diff := cmp.Diff(tc.spec.NetworkInstance.Name, got); diff != "" { - t.Errorf("TestSetInterfaceSpec: -want, +got:\n%s", diff) - } - } - if tc.spec.AttachmentType != "" { - got := i.GetAttachmentType() - if diff := cmp.Diff(string(tc.spec.AttachmentType), got); diff != "" { - t.Errorf("TestSetInterfaceSpec: -want, +got:\n%s", diff) - } - } else { - got := i.GetAttachmentType() - if diff := cmp.Diff(tc.defaultAttachmentType, got); diff != "" { - t.Errorf("TestSetInterfaceSpec: -want, +got:\n%s", diff) - } - } - if tc.spec.CNIType != "" { - got := i.GetCNIType() - if diff := cmp.Diff(string(tc.spec.CNIType), got); diff != "" { - t.Errorf("TestSetInterfaceSpec: -want, +got:\n%s", diff) - } - } else { - got := i.GetCNIType() - if diff := cmp.Diff(tc.defaultCNIType, got); diff != "" { - t.Errorf("TestSetInterfaceSpec: -want, +got:\n%s", diff) - } - } - } - }) - } -} - -func TestDeleteCNIType(t *testing.T) { - cases := map[string]struct { - file string - }{ - "DeleteCNIType": { - file: itface, - }, - "DeleteCNITypeEmpty": { - file: itfaceEmpty, - }, - } - - for name, tc := range cases { - i, err := NewFromYAML([]byte(tc.file)) - if err != nil { - t.Errorf("cannot unmarshal file: %s", err.Error()) - } + err = i.SetSpec(tc.t) + assert.NoError(t, err) - t.Run(name, func(t *testing.T) { - err := i.DeleteCNIType() + got, err := i.GetGoStruct() assert.NoError(t, err) + if diff := cmp.Diff(tc.t, got.Spec); diff != "" { + t.Errorf("-want, +got:\n%s", diff) + } }) } } -func TestDeleteAttachmentType(t *testing.T) { +func TestSetStatus(t *testing.T) { cases := map[string]struct { file string + t nephioreqv1alpha1.InterfaceStatus }{ - "TestDeleteAttachmentType": { + "Override": { file: itface, - }, - "TestDeleteAttachmentTypeEmpty": { - file: itfaceEmpty, + t: nephioreqv1alpha1.InterfaceStatus{ + IPAllocationStatus: &ipamv1alpha1.IPAllocationStatus{ + AllocatedPrefix: "10.0.0.2/24", + Gateway: "10.0.0.1", + }, + VLANAllocationStatus: &vlanv1alpha1.VLANAllocationStatus{ + AllocatedVlanID: 10, + }, + }, }, } @@ -535,57 +282,14 @@ func TestDeleteAttachmentType(t *testing.T) { } t.Run(name, func(t *testing.T) { - err := i.DeleteAttachmentType() + err = i.SetStatus(tc.t) assert.NoError(t, err) - }) - } -} - -func TestYamlComments(t *testing.T) { - cases := map[string]struct { - input []byte - errExpected bool - }{ - "TestNewFromYAMLNormal": { - input: []byte(itface), - errExpected: false, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - i, err := NewFromYAML(tc.input) - - if tc.errExpected { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - if err := i.SetCNIType("ipvlan"); err != nil { - assert.NoError(t, err) - } - if err := i.SetAttachmentType("none"); err != nil { - assert.NoError(t, err) - } - if err := i.SetNetworkInstanceName("new"); err != nil { - assert.NoError(t, err) - } - - if err := i.SetCNIType("sriov"); err != nil { - assert.NoError(t, err) - } - if err := i.SetAttachmentType("vlan"); err != nil { - assert.NoError(t, err) - } - if err := i.SetNetworkInstanceName("vpc-ran"); err != nil { - assert.NoError(t, err) - } - - o := i.GetKubeObject() + got, err := i.GetGoStruct() + assert.NoError(t, err) - if o.String() != string(tc.input) { - t.Errorf("expected output to be %q, but got %q", string(tc.input), o.String()) + if diff := cmp.Diff(tc.t, got.Status); diff != "" { + t.Errorf("-want, +got:\n%s", diff) } }) }