From fb2defe2281231ed2f5f503bd7106b6654e2383c Mon Sep 17 00:00:00 2001 From: Xiaopei Liu Date: Thu, 21 Nov 2024 18:12:24 +0800 Subject: [PATCH] Add validations for SubnetPort CRD Add validations for SubnetPort CR to specify one of subnet or subnetSet under spec. --- .../vpc/crd.nsx.vmware.com_subnetports.yaml | 4 ++ .../yaml/samples/nsx_v1alpha1_subnetport.yaml | 43 +++++++++++++++++++ pkg/apis/vpc/v1alpha1/subnetport_types.go | 1 + .../subnetport/subnetport_controller.go | 5 --- .../subnetport/subnetport_controller_test.go | 25 +++-------- 5 files changed, 55 insertions(+), 23 deletions(-) create mode 100644 build/yaml/samples/nsx_v1alpha1_subnetport.yaml diff --git a/build/yaml/crd/vpc/crd.nsx.vmware.com_subnetports.yaml b/build/yaml/crd/vpc/crd.nsx.vmware.com_subnetports.yaml index 9fd7d29bc..fb430bdb5 100644 --- a/build/yaml/crd/vpc/crd.nsx.vmware.com_subnetports.yaml +++ b/build/yaml/crd/vpc/crd.nsx.vmware.com_subnetports.yaml @@ -59,6 +59,10 @@ spec: description: SubnetSet defines the parent SubnetSet name of the SubnetPort. type: string type: object + x-kubernetes-validations: + - message: Only one of subnet or subnetSet can be specified or both set + to empty in which case default SubnetSet for VM will be used + rule: '!has(self.subnetSet) || !has(self.subnet)' status: description: SubnetPortStatus defines the observed state of SubnetPort. properties: diff --git a/build/yaml/samples/nsx_v1alpha1_subnetport.yaml b/build/yaml/samples/nsx_v1alpha1_subnetport.yaml new file mode 100644 index 000000000..5a5abba80 --- /dev/null +++ b/build/yaml/samples/nsx_v1alpha1_subnetport.yaml @@ -0,0 +1,43 @@ +apiVersion: crd.nsx.vmware.com/v1alpha1 +kind: SubnetPort +metadata: + name: subnetport-sample-a +spec: + subnetSet: vm-subnetset +status: + attachment: + id: 35323036-6439-4932-ad36-3930372d3438 + conditions: + - lastTransitionTime: "2024-11-20T22:23:10Z" + message: NSX subnet port has been successfully created/updated + reason: SubnetPortReady + status: "True" + type: Ready + networkInterfaceConfig: + ipAddresses: + - gateway: 172.26.0.1 + ipAddress: 172.26.0.3/28 + logicalSwitchUUID: 49fa0a2d-8fd2-4c85-87ca-2495e8a86d06 + macAddress: 04:50:56:00:94:00 +--- +# SubnetPort CR sample without specifying subnet or subnetSet +apiVersion: crd.nsx.vmware.com/v1alpha1 +kind: SubnetPort +metadata: + name: subnetport-sample-b +spec: +status: + attachment: + id: 35323036-6439-4932-ad36-3930372d3438 + conditions: + - lastTransitionTime: "2024-11-20T22:23:10Z" + message: NSX subnet port has been successfully created/updated + reason: SubnetPortReady + status: "True" + type: Ready + networkInterfaceConfig: + ipAddresses: + - gateway: 172.26.0.1 + ipAddress: 172.26.0.3/28 + logicalSwitchUUID: 49fa0a2d-8fd2-4c85-87ca-2495e8a86d06 + macAddress: 04:50:56:00:94:00 \ No newline at end of file diff --git a/pkg/apis/vpc/v1alpha1/subnetport_types.go b/pkg/apis/vpc/v1alpha1/subnetport_types.go index 34b592f9c..0e4ff910d 100644 --- a/pkg/apis/vpc/v1alpha1/subnetport_types.go +++ b/pkg/apis/vpc/v1alpha1/subnetport_types.go @@ -7,6 +7,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// +kubebuilder:validation:XValidation:rule="!has(self.subnetSet) || !has(self.subnet)",message="Only one of subnet or subnetSet can be specified or both set to empty in which case default SubnetSet for VM will be used" // SubnetPortSpec defines the desired state of SubnetPort. type SubnetPortSpec struct { // Subnet defines the parent Subnet name of the SubnetPort. diff --git a/pkg/controllers/subnetport/subnetport_controller.go b/pkg/controllers/subnetport/subnetport_controller.go index bf7a78904..c42ee1e68 100644 --- a/pkg/controllers/subnetport/subnetport_controller.go +++ b/pkg/controllers/subnetport/subnetport_controller.go @@ -89,11 +89,6 @@ func (r *SubnetPortReconciler) Reconcile(ctx context.Context, req ctrl.Request) log.Error(err, "unable to fetch SubnetPort CR", "SubnetPort", req.NamespacedName) return common.ResultRequeue, err } - if len(subnetPort.Spec.SubnetSet) > 0 && len(subnetPort.Spec.Subnet) > 0 { - err := errors.New("subnet and subnetset should not be configured at the same time") - r.StatusUpdater.UpdateFail(ctx, subnetPort, err, "Failed to get Subnet/SubnetSet of the SubnetPort", setSubnetPortReadyStatusFalse, r.SubnetPortService) - return common.ResultNormal, err - } if subnetPort.ObjectMeta.DeletionTimestamp.IsZero() { r.StatusUpdater.IncreaseUpdateTotal() diff --git a/pkg/controllers/subnetport/subnetport_controller_test.go b/pkg/controllers/subnetport/subnetport_controller_test.go index 60f26f2ae..ec69d98ea 100644 --- a/pkg/controllers/subnetport/subnetport_controller_test.go +++ b/pkg/controllers/subnetport/subnetport_controller_test.go @@ -76,6 +76,7 @@ func TestSubnetPortReconciler_Reconcile(t *testing.T) { }, }, }, + SubnetPortStore: &subnetport.SubnetPortStore{}, } subnetService := &subnet.SubnetService{ Service: servicecommon.Service{ @@ -133,31 +134,19 @@ func TestSubnetPortReconciler_Reconcile(t *testing.T) { _, ret = r.Reconcile(ctx, req) assert.Equal(t, err, ret) - patches := gomonkey.ApplyFunc(setAddressBindingStatusBySubnetPort, func(client client.Client, ctx context.Context, subnetPort *v1alpha1.SubnetPort, subnetPortService *subnetport.SubnetPortService, transitionTime metav1.Time, e error) { - }) - defer patches.Reset() - - // both subnet and subnetset are configured - sp := &v1alpha1.SubnetPort{} - k8sClient.EXPECT().Get(ctx, gomock.Any(), sp).Return(nil).Do( - func(_ context.Context, _ client.ObjectKey, obj client.Object, option ...client.GetOption) error { - v1sp := obj.(*v1alpha1.SubnetPort) - v1sp.Spec.Subnet = "subnet1" - v1sp.Spec.SubnetSet = "subnetset2" - return nil - }) - err = errors.New("subnet and subnetset should not be configured at the same time") - k8sClient.EXPECT().Status().Return(fakewriter) - _, ret = r.Reconcile(ctx, req) - assert.Equal(t, err, ret) - // CheckAndGetSubnetPathForSubnetPort fails + sp := &v1alpha1.SubnetPort{} err = errors.New("CheckAndGetSubnetPathForSubnetPort failed") patchesCheckAndGetSubnetPathForSubnetPort := gomonkey.ApplyFunc((*SubnetPortReconciler).CheckAndGetSubnetPathForSubnetPort, func(r *SubnetPortReconciler, ctx context.Context, obj *v1alpha1.SubnetPort) (bool, string, error) { return false, "", err }) defer patchesCheckAndGetSubnetPathForSubnetPort.Reset() + patchesGetByKey := gomonkey.ApplyFunc((*subnetport.SubnetPortStore).GetByKey, + func(s *subnetport.SubnetPortStore, key string) *model.VpcSubnetPort { + return nil + }) + defer patchesGetByKey.Reset() k8sClient.EXPECT().Get(ctx, gomock.Any(), sp).Return(nil).Do( func(_ context.Context, _ client.ObjectKey, obj client.Object, option ...client.GetOption) error { v1sp := obj.(*v1alpha1.SubnetPort)