From bbb2e85882786277b1ba8d20df42377df085e23f Mon Sep 17 00:00:00 2001 From: Deng Yun Date: Sun, 26 Jan 2025 17:43:36 +0800 Subject: [PATCH] Support showing NSX LB SNAT IP in networkinfo CR Getting VPC Ppolicy Tier1 uplink port IP as NSX LB SNAT IP in order to present it in networkinfo CR loadBalancerIPAddresses field. --- .../networkinfo/networkinfo_controller.go | 30 ++- .../networkinfo_controller_test.go | 189 +++++++++++++----- .../services/realizestate/realize_state.go | 35 +++- .../realizestate/realize_state_test.go | 150 ++++++++++++++ pkg/nsx/services/vpc/vpc.go | 18 +- pkg/nsx/services/vpc/vpc_test.go | 119 +++++++++-- 6 files changed, 458 insertions(+), 83 deletions(-) diff --git a/pkg/controllers/networkinfo/networkinfo_controller.go b/pkg/controllers/networkinfo/networkinfo_controller.go index e616ba9a0..bd6e5a00d 100644 --- a/pkg/controllers/networkinfo/networkinfo_controller.go +++ b/pkg/controllers/networkinfo/networkinfo_controller.go @@ -58,6 +58,7 @@ var ( nsMsgVPCCreateUpdateError = newNsUnreadyMessage("Error happened to create or update VPC: %v", NSReasonVPCNotReady) nsMsgVPCNsxLBSNotReady = newNsUnreadyMessage("Error happened to get NSX LBS path in VPC: %v", NSReasonVPCNotReady) nsMsgVPCAviSubnetError = newNsUnreadyMessage("Error happened to get Avi Load balancer Subnet info: %v", NSReasonVPCNotReady) + nsMsgVPCNSXLBSNATIPError = newNsUnreadyMessage("Error happened to get NSX Load balancer SNAT IP info: %v", NSReasonVPCNotReady) nsMsgVPCGetExtIPBlockError = newNsUnreadyMessage("Error happened to get external IP blocks: %v", NSReasonVPCNotReady) nsMsgVPCNoExternalIPBlock = newNsUnreadyMessage("System VPC has no external IP blocks", NSReasonVPCNotReady) nsMsgVPCAutoSNATDisabled = newNsUnreadyMessage("SNAT is not enabled in System VPC", NSReasonVPCSnatNotReady) @@ -125,6 +126,7 @@ func (r *NetworkInfoReconciler) GetVpcConnectivityProfilePathByVpcPath(vpcPath s return "", err } } + func (r *NetworkInfoReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { startTime := time.Now() defer func() { @@ -252,7 +254,7 @@ func (r *NetworkInfoReconciler) Reconcile(ctx context.Context, req ctrl.Request) nsxLBSPath = r.Service.GetDefaultNSXLBSPathByVPC(*createdVpc.Id) } - snatIP, path, cidr := "", "", "" + snatIP, aviSubnetPath, aviSECIDR, nsxLBSNATIP, lbIP := "", "", "", "", "" vpcConnectivityProfile, err := r.Service.GetVpcConnectivityProfile(&nc, vpcConnectivityProfilePath) if err != nil { @@ -302,30 +304,46 @@ func (r *NetworkInfoReconciler) Reconcile(ctx context.Context, req ctrl.Request) // nsx bug, if set LoadBalancerVpcEndpoint.Enabled to false, when read this VPC back, // LoadBalancerVpcEndpoint.Enabled will become a nil pointer. if lbProvider == vpc.AVILB && createdVpc.LoadBalancerVpcEndpoint != nil && createdVpc.LoadBalancerVpcEndpoint.Enabled != nil && *createdVpc.LoadBalancerVpcEndpoint.Enabled { - path, cidr, err = r.Service.GetAVISubnetInfo(*createdVpc) + aviSubnetPath, aviSECIDR, err = r.Service.GetAVISubnetInfo(*createdVpc) if err != nil { - log.Error(err, "Failed to read LB Subnet path and CIDR", "VPC", createdVpc.Id) + log.Error(err, "Failed to read AVI LB Subnet path and CIDR", "VPC", createdVpc.Id) state := &v1alpha1.VPCState{ Name: *createdVpc.DisplayName, DefaultSNATIP: snatIP, LoadBalancerIPAddresses: "", PrivateIPs: privateIPs, } - r.StatusUpdater.UpdateFail(ctx, networkInfoCR, err, fmt.Sprintf("Failed to read LB Subnet path and CIDR, VPC: %s", *createdVpc.Id), setNetworkInfoVPCStatusWithError, state) + r.StatusUpdater.UpdateFail(ctx, networkInfoCR, err, fmt.Sprintf("Failed to read AVI LB Subnet path and CIDR, VPC: %s", *createdVpc.Id), setNetworkInfoVPCStatusWithError, state) setNSNetworkReadyCondition(ctx, r.Client, req.Namespace, nsMsgVPCAviSubnetError.getNSNetworkCondition(err)) return common.ResultRequeueAfter10sec, err } + lbIP = aviSECIDR + } else if lbProvider == vpc.NSXLB { + nsxLBSNATIP, err = r.Service.GetNSXLBSNATIP(*createdVpc) + if err != nil { + log.Error(err, "Failed to read NSX LB SNAT IP", "VPC", createdVpc.Id) + state := &v1alpha1.VPCState{ + Name: *createdVpc.DisplayName, + DefaultSNATIP: snatIP, + LoadBalancerIPAddresses: "", + PrivateIPs: privateIPs, + } + r.StatusUpdater.UpdateFail(ctx, networkInfoCR, err, fmt.Sprintf("Failed to read NSX LB Subnet path and CIDR, VPC: %s", *createdVpc.Id), setNetworkInfoVPCStatusWithError, state) + setNSNetworkReadyCondition(ctx, r.Client, req.Namespace, nsMsgVPCNSXLBSNATIPError.getNSNetworkCondition(err)) + return common.ResultRequeueAfter10sec, err + } + lbIP = nsxLBSNATIP } state := &v1alpha1.VPCState{ Name: *createdVpc.DisplayName, DefaultSNATIP: snatIP, - LoadBalancerIPAddresses: cidr, + LoadBalancerIPAddresses: lbIP, PrivateIPs: privateIPs, } // AKO needs to know the AVI subnet path created by NSX - setVPCNetworkConfigurationStatusWithLBS(ctx, r.Client, ncName, state.Name, path, nsxLBSPath, *createdVpc.Path) + setVPCNetworkConfigurationStatusWithLBS(ctx, r.Client, ncName, state.Name, aviSubnetPath, nsxLBSPath, *createdVpc.Path) r.StatusUpdater.UpdateSuccess(ctx, networkInfoCR, setNetworkInfoVPCStatus, state) if retryWithSystemVPC { diff --git a/pkg/controllers/networkinfo/networkinfo_controller_test.go b/pkg/controllers/networkinfo/networkinfo_controller_test.go index 44ee95ea7..6efb7ed4c 100644 --- a/pkg/controllers/networkinfo/networkinfo_controller_test.go +++ b/pkg/controllers/networkinfo/networkinfo_controller_test.go @@ -73,6 +73,7 @@ func (c *fakeVpcAttachmentClient) Get(orgIdParam string, projectIdParam string, func (c *fakeVpcAttachmentClient) Patch(orgIdParam string, projectIdParam string, vpcIdParam string, vpcAttachmentIdParam string, vpcAttachmentParam model.VpcAttachment) error { return nil } + func (c *fakeVpcAttachmentClient) Update(orgIdParam string, projectIdParam string, vpcIdParam string, vpcAttachmentIdParam string, vpcAttachmentParam model.VpcAttachment) (model.VpcAttachment, error) { return model.VpcAttachment{}, nil } @@ -180,7 +181,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { })) patches = gomonkey.ApplyMethod(reflect.TypeOf(r.Service), "GetNetworkconfigNameFromNS", func(_ *vpc.VPCService, _ context.Context, _ string) (string, error) { return servicecommon.SystemVPCNetworkConfigurationName, nil - }) patches.ApplyMethod(reflect.TypeOf(&vpc.VPCService{}), "GetVPCNetworkConfig", func(_ *vpc.VPCService, _ string) (servicecommon.VPCNetworkConfigInfo, bool) { return servicecommon.VPCNetworkConfigInfo{ @@ -213,10 +213,11 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { patches.ApplyMethod(reflect.TypeOf(r.Service), "GetLBProvider", func(_ *vpc.VPCService) vpc.LBProvider { return vpc.NSXLB }) - + patches.ApplyMethod(reflect.TypeOf(r.Service), "GetNSXLBSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { + return "100.64.0.3", nil + }) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetDefaultNSXLBSPathByVPC", func(_ *vpc.VPCService, _ string) string { return "lbs-path" - }) patches.ApplyFunc(r.StatusUpdater.UpdateSuccess, func(_ context.Context, _ client.Object, _ common.UpdateSuccessStatusFn, _ ...interface{}) { @@ -247,7 +248,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { })) patches = gomonkey.ApplyMethod(reflect.TypeOf(r.Service), "GetNetworkconfigNameFromNS", func(_ *vpc.VPCService, _ context.Context, _ string) (string, error) { return "non-system", nil - }) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetVPCNetworkConfig", func(_ *vpc.VPCService, _ string) (servicecommon.VPCNetworkConfigInfo, bool) { return servicecommon.VPCNetworkConfigInfo{ @@ -256,7 +256,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { Org: "default", NSXProject: "project-quality", }, true - }) patches.ApplyFunc(getGatewayConnectionStatus, func(_ context.Context, _ *v1alpha1.VPCNetworkConfiguration) (bool, string) { return true, "" @@ -282,16 +281,17 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { patches.ApplyMethod(reflect.TypeOf(r.Service), "GetLBProvider", func(_ *vpc.VPCService) vpc.LBProvider { return vpc.NSXLB }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "GetNSXLBSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { + return "100.64.0.3", nil + }) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetDefaultNSXLBSPathByVPC", func(_ *vpc.VPCService, _ string) string { return "lbs-path" - }) patches.ApplyFunc(setNSNetworkReadyCondition, func(ctx context.Context, client client.Client, nsName string, condition *corev1.NamespaceCondition) { require.True(t, nsConditionEquals(*condition, *nsMsgVPCIsReady.getNSNetworkCondition())) }) return patches - }, args: requestArgs, want: common.ResultNormal, @@ -313,7 +313,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { })) patches = gomonkey.ApplyMethod(reflect.TypeOf(r.Service), "GetNetworkconfigNameFromNS", func(_ *vpc.VPCService, ctx context.Context, _ string) (string, error) { return "non-system", nil - }) patches.ApplyMethod(reflect.TypeOf(r), "GetVpcConnectivityProfilePathByVpcPath", func(_ *NetworkInfoReconciler, _ string) (string, error) { return "connectivity_profile", nil @@ -326,7 +325,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { Org: "default", NSXProject: "project-quality", }, true - }) patches.ApplyFunc(getGatewayConnectionStatus, func(_ context.Context, _ *v1alpha1.VPCNetworkConfiguration) (bool, string) { return false, "" @@ -344,7 +342,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { require.True(t, nsConditionEquals(*condition, *nsMsgVPCGwConnectionNotReady.getNSNetworkCondition())) }) return patches - }, args: requestArgs, want: common.ResultRequeueAfter60sec, @@ -366,7 +363,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { })) patches = gomonkey.ApplyMethod(reflect.TypeOf(r.Service), "GetNetworkconfigNameFromNS", func(_ *vpc.VPCService, ctx context.Context, _ string) (string, error) { return servicecommon.SystemVPCNetworkConfigurationName, nil - }) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetVPCNetworkConfig", func(_ *vpc.VPCService, _ string) (servicecommon.VPCNetworkConfigInfo, bool) { return servicecommon.VPCNetworkConfigInfo{ @@ -375,7 +371,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { Org: "default", NSXProject: "project-quality", }, true - }) patches.ApplyFunc(getGatewayConnectionStatus, func(_ context.Context, _ *v1alpha1.VPCNetworkConfiguration) (bool, string) { return false, "" @@ -386,6 +381,9 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { patches.ApplyMethod(reflect.TypeOf(r.Service), "GetLBProvider", func(_ *vpc.VPCService) vpc.LBProvider { return vpc.NSXLB }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "GetNSXLBSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { + return "100.64.0.3", nil + }) patches.ApplyMethod(reflect.TypeOf(r.Service), "CreateOrUpdateVPC", func(_ *vpc.VPCService, ctx context.Context, _ *v1alpha1.NetworkInfo, _ *servicecommon.VPCNetworkConfigInfo, _ vpc.LBProvider) (*model.Vpc, error) { return &model.Vpc{ DisplayName: servicecommon.String("vpc-name"), @@ -410,14 +408,15 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { }}) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetDefaultNSXLBSPathByVPC", func(_ *vpc.VPCService, _ string) string { return "lbs-path" - }) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetLBProvider", func(_ *vpc.VPCService) vpc.LBProvider { return vpc.NSXLB }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "GetNSXLBSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { + return "100.64.0.3", nil + }) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetDefaultSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { return "snat-ip", nil - }) patches.ApplyFunc(setVPCNetworkConfigurationStatusWithSnatEnabled, func(_ context.Context, _ client.Client, _ *v1alpha1.VPCNetworkConfiguration, autoSnatEnabled bool) { @@ -430,7 +429,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { require.True(t, nsConditionEquals(*condition, *nsMsgVPCIsReady.getNSNetworkCondition())) }) return patches - }, args: requestArgs, want: common.ResultNormal, @@ -452,7 +450,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { })) patches = gomonkey.ApplyMethod(reflect.TypeOf(r.Service), "GetNetworkconfigNameFromNS", func(_ *vpc.VPCService, ctx context.Context, _ string) (string, error) { return servicecommon.SystemVPCNetworkConfigurationName, nil - }) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetVPCNetworkConfig", func(_ *vpc.VPCService, _ string) (servicecommon.VPCNetworkConfigInfo, bool) { return servicecommon.VPCNetworkConfigInfo{ @@ -461,7 +458,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { Org: "default", NSXProject: "project-quality", }, true - }) patches.ApplyFunc(getGatewayConnectionStatus, func(_ context.Context, _ *v1alpha1.VPCNetworkConfiguration) (bool, string) { return false, "" @@ -472,6 +468,9 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { patches.ApplyMethod(reflect.TypeOf(r.Service), "GetLBProvider", func(_ *vpc.VPCService) vpc.LBProvider { return vpc.NSXLB }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "GetNSXLBSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { + return "100.64.0.3", nil + }) patches.ApplyMethod(reflect.TypeOf(r.Service), "CreateOrUpdateVPC", func(_ *vpc.VPCService, ctx context.Context, _ *v1alpha1.NetworkInfo, _ *servicecommon.VPCNetworkConfigInfo, _ vpc.LBProvider) (*model.Vpc, error) { return &model.Vpc{ DisplayName: servicecommon.String("vpc-name"), @@ -491,12 +490,10 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { }}) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetDefaultNSXLBSPathByVPC", func(_ *vpc.VPCService, _ string) string { return "lbs-path" - }) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetDefaultSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { assert.FailNow(t, "should not be called") return "", nil - }) patches.ApplyFunc(setVPCNetworkConfigurationStatusWithSnatEnabled, func(_ context.Context, _ client.Client, _ *v1alpha1.VPCNetworkConfiguration, autoSnatEnabled bool) { @@ -509,7 +506,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { require.True(t, nsConditionEquals(*condition, *nsMsgVPCAutoSNATDisabled.getNSNetworkCondition())) }) return patches - }, args: requestArgs, want: common.ResultRequeueAfter60sec, @@ -531,7 +527,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { })) patches = gomonkey.ApplyMethod(reflect.TypeOf(r.Service), "GetNetworkconfigNameFromNS", func(_ *vpc.VPCService, ctx context.Context, _ string) (string, error) { return "non-system", nil - }) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetVPCNetworkConfig", func(_ *vpc.VPCService, _ string) (servicecommon.VPCNetworkConfigInfo, bool) { return servicecommon.VPCNetworkConfigInfo{ @@ -540,7 +535,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { Org: "default", NSXProject: "project-quality", }, true - }) patches.ApplyFunc(getGatewayConnectionStatus, func(_ context.Context, _ *v1alpha1.VPCNetworkConfiguration) (bool, string) { return true, "" @@ -551,6 +545,9 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { patches.ApplyMethod(reflect.TypeOf(r.Service), "GetLBProvider", func(_ *vpc.VPCService) vpc.LBProvider { return vpc.NSXLB }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "GetNSXLBSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { + return "100.64.0.3", nil + }) patches.ApplyMethod(reflect.TypeOf(r.Service), "CreateOrUpdateVPC", func(_ *vpc.VPCService, ctx context.Context, _ *v1alpha1.NetworkInfo, _ *servicecommon.VPCNetworkConfigInfo, _ vpc.LBProvider) (*model.Vpc, error) { return &model.Vpc{ DisplayName: servicecommon.String("vpc-name"), @@ -575,11 +572,12 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { }}) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetDefaultNSXLBSPathByVPC", func(_ *vpc.VPCService, _ string) string { return "lbs-path" - + }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "GetNSXLBSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { + return "100.64.0.3", nil }) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetDefaultSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { return "snat-ip", nil - }) patches.ApplyFunc(setVPCNetworkConfigurationStatusWithSnatEnabled, func(_ context.Context, _ client.Client, _ *v1alpha1.VPCNetworkConfiguration, autoSnatEnabled bool) { @@ -590,7 +588,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { require.True(t, nsConditionEquals(*condition, *nsMsgVPCIsReady.getNSNetworkCondition())) }) return patches - }, args: requestArgs, want: common.ResultNormal, @@ -612,7 +609,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { })) patches = gomonkey.ApplyMethod(reflect.TypeOf(r.Service), "GetNetworkconfigNameFromNS", func(_ *vpc.VPCService, ctx context.Context, _ string) (string, error) { return servicecommon.SystemVPCNetworkConfigurationName, nil - }) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetVPCNetworkConfig", func(_ *vpc.VPCService, _ string) (servicecommon.VPCNetworkConfigInfo, bool) { return servicecommon.VPCNetworkConfigInfo{ @@ -621,7 +617,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { Org: "default", NSXProject: "project-quality", }, true - }) patches.ApplyFunc(getGatewayConnectionStatus, func(_ context.Context, _ *v1alpha1.VPCNetworkConfiguration) (bool, string) { return false, "" @@ -632,6 +627,9 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { patches.ApplyMethod(reflect.TypeOf(r.Service), "GetLBProvider", func(_ *vpc.VPCService) vpc.LBProvider { return vpc.NSXLB }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "GetNSXLBSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { + return "100.64.0.3", nil + }) patches.ApplyMethod(reflect.TypeOf(r.Service), "CreateOrUpdateVPC", func(_ *vpc.VPCService, ctx context.Context, _ *v1alpha1.NetworkInfo, _ *servicecommon.VPCNetworkConfigInfo, _ vpc.LBProvider) (*model.Vpc, error) { return &model.Vpc{ DisplayName: servicecommon.String("vpc-name"), @@ -655,11 +653,9 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { patches.ApplyMethod(reflect.TypeOf(r.Service), "GetDefaultNSXLBSPathByVPC", func(_ *vpc.VPCService, _ string) string { return "lbs-path" - }) patches.ApplyMethod(reflect.TypeOf(r.Service), "GetDefaultSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { return "snat-ip", nil - }) patches.ApplyFunc(setVPCNetworkConfigurationStatusWithSnatEnabled, func(_ context.Context, _ client.Client, _ *v1alpha1.VPCNetworkConfiguration, autoSnatEnabled bool) { @@ -676,7 +672,8 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { args: requestArgs, want: common.ResultRequeueAfter60sec, wantErr: false, - }, { + }, + { name: "Pre-create VPC success case", prepareFunc: func(t *testing.T, r *NetworkInfoReconciler, ctx context.Context) (patches *gomonkey.Patches) { assert.NoError(t, r.Client.Create(ctx, &v1alpha1.NetworkInfo{ @@ -702,7 +699,6 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { NSXProject: "project-quality", VPCPath: "/orgs/default/projects/nsx_operator_e2e_test/vpcs/pre-vpc", }, true - }) patches.ApplyFunc(getGatewayConnectionStatus, func(_ context.Context, _ *v1alpha1.VPCNetworkConfiguration) (bool, string) { return true, "" @@ -737,6 +733,83 @@ func TestNetworkInfoReconciler_Reconcile(t *testing.T) { want: common.ResultNormal, wantErr: false, }, + { + name: "NSX LB SNAT IP failure", + prepareFunc: func(t *testing.T, r *NetworkInfoReconciler, ctx context.Context) (patches *gomonkey.Patches) { + assert.NoError(t, r.Client.Create(ctx, &v1alpha1.NetworkInfo{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: requestArgs.req.Namespace, + Name: requestArgs.req.Name, + }, + })) + assert.NoError(t, r.Client.Create(ctx, &v1alpha1.VPCNetworkConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "system", + }, + })) + patches = gomonkey.ApplyMethod(reflect.TypeOf(r.Service), "GetNetworkconfigNameFromNS", func(_ *vpc.VPCService, ctx context.Context, _ string) (string, error) { + return servicecommon.SystemVPCNetworkConfigurationName, nil + }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "GetVPCNetworkConfig", func(_ *vpc.VPCService, _ string) (servicecommon.VPCNetworkConfigInfo, bool) { + return servicecommon.VPCNetworkConfigInfo{ + Name: servicecommon.SystemVPCNetworkConfigurationName, + VPCConnectivityProfile: "/orgs/default/projects/nsx_operator_e2e_test/vpc-connectivity-profiles/default", + Org: "default", + NSXProject: "project-quality", + }, true + }) + patches.ApplyFunc(getGatewayConnectionStatus, func(_ context.Context, _ *v1alpha1.VPCNetworkConfiguration) (bool, string) { + return false, "" + }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "ValidateGatewayConnectionStatus", func(_ *vpc.VPCService, _ *servicecommon.VPCNetworkConfigInfo) (bool, string, error) { + return true, "", nil + }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "GetLBProvider", func(_ *vpc.VPCService) vpc.LBProvider { + return vpc.NSXLB + }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "CreateOrUpdateVPC", func(_ *vpc.VPCService, ctx context.Context, _ *v1alpha1.NetworkInfo, _ *servicecommon.VPCNetworkConfigInfo, _ vpc.LBProvider) (*model.Vpc, error) { + return &model.Vpc{ + DisplayName: servicecommon.String("vpc-name"), + Path: servicecommon.String("/orgs/default/projects/project-quality/vpcs/fake-vpc"), + Id: servicecommon.String("vpc-id"), + }, nil + }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "IsSharedVPCNamespaceByNS", func(_ *vpc.VPCService, ctx context.Context, _ string) (bool, error) { + return false, nil + }) + patches.ApplyMethodSeq(reflect.TypeOf(r.Service.Service.NSXClient.VPCConnectivityProfilesClient), "Get", []gomonkey.OutputCell{{ + Values: gomonkey.Params{model.VpcConnectivityProfile{ + ExternalIpBlocks: []string{"fake-ip-block"}, + ServiceGateway: nil, + }, nil}, + Times: 1, + }}) + patches.ApplyMethod(reflect.TypeOf(r.Service), "GetDefaultNSXLBSPathByVPC", func(_ *vpc.VPCService, _ string) string { + return "lbs-path" + }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "GetDefaultSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { + assert.FailNow(t, "should not be called") + return "", nil + }) + patches.ApplyFunc(setVPCNetworkConfigurationStatusWithSnatEnabled, + func(_ context.Context, _ client.Client, _ *v1alpha1.VPCNetworkConfiguration, autoSnatEnabled bool) { + if autoSnatEnabled { + assert.FailNow(t, "should set VPCNetworkConfiguration status with AutoSnatEnabled=false") + } + }) + patches.ApplyMethod(reflect.TypeOf(r.Service), "GetNSXLBSNATIP", func(_ *vpc.VPCService, _ model.Vpc) (string, error) { + return "", fmt.Errorf("tier1 uplink port IP not found") + }) + patches.ApplyFunc(setNSNetworkReadyCondition, + func(ctx context.Context, client client.Client, nsName string, condition *corev1.NamespaceCondition) { + require.True(t, nsConditionEquals(*condition, *nsMsgVPCNSXLBSNATIPError.getNSNetworkCondition(fmt.Errorf("tier1 uplink port IP not found")))) + }) + return patches + }, + args: requestArgs, + want: common.ResultRequeueAfter10sec, + wantErr: true, + }, } for _, tt := range tests { @@ -775,13 +848,16 @@ func TestNetworkInfoReconciler_deleteStaleVPCs(t *testing.T) { }) return patches }, - }, { + }, + { name: "NSX VPC is used by a valid Namespace,, skip deletion", prepareFuncs: func(r *NetworkInfoReconciler) *gomonkey.Patches { patches := gomonkey.ApplyMethod(reflect.TypeOf(r.Service), "GetVPCsByNamespace", func(_ *vpc.VPCService, _ string) []*model.Vpc { return []*model.Vpc{ {Tags: []model.Tag{ - {Scope: servicecommon.String(servicecommon.TagScopeNamespaceUID), Tag: servicecommon.String("vpc1")}}}} + {Scope: servicecommon.String(servicecommon.TagScopeNamespaceUID), Tag: servicecommon.String("vpc1")}, + }}, + } }) patches.ApplyPrivateMethod(reflect.TypeOf(r), "listNamespaceCRsNameIDSet", func(_ *NetworkInfoReconciler, _ context.Context) (sets.Set[string], sets.Set[string], error) { return sets.Set[string]{}, sets.New[string]("vpc1"), nil @@ -799,7 +875,8 @@ func TestNetworkInfoReconciler_deleteStaleVPCs(t *testing.T) { {Scope: servicecommon.String(servicecommon.TagScopeNamespaceUID), Tag: servicecommon.String("vpc1")}, }, Path: servicecommon.String("/orgs/default/projects/default/vpcs/vpc1"), - }} + }, + } }) patches.ApplyPrivateMethod(reflect.TypeOf(r), "listNamespaceCRsNameIDSet", func(_ *NetworkInfoReconciler, _ context.Context) (sets.Set[string], sets.Set[string], error) { return sets.Set[string]{}, sets.Set[string]{}, nil @@ -821,7 +898,8 @@ func TestNetworkInfoReconciler_deleteStaleVPCs(t *testing.T) { {Scope: servicecommon.String(servicecommon.TagScopeNamespaceUID), Tag: servicecommon.String("vpc1")}, }, Path: servicecommon.String("/orgs/default/projects/default/vpcs/vpc1"), - }} + }, + } }) patches.ApplyPrivateMethod(reflect.TypeOf(r), "listNamespaceCRsNameIDSet", func(_ *NetworkInfoReconciler, _ context.Context) (sets.Set[string], sets.Set[string], error) { return sets.Set[string]{}, sets.Set[string]{}, nil @@ -841,15 +919,16 @@ func TestNetworkInfoReconciler_deleteStaleVPCs(t *testing.T) { TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{Name: "fakeNetworkconfigName"}, Spec: v1alpha1.VPCNetworkConfigurationSpec{}, - Status: v1alpha1.VPCNetworkConfigurationStatus{VPCs: []v1alpha1.VPCInfo{ - { - Name: "fakeDisplayName2", - }, - { - Name: "fakeDisplayName1", + Status: v1alpha1.VPCNetworkConfigurationStatus{ + VPCs: []v1alpha1.VPCInfo{ + { + Name: "fakeDisplayName2", + }, + { + Name: "fakeDisplayName1", + }, }, }, - }, }, prepareFuncs: func(r *NetworkInfoReconciler) *gomonkey.Patches { patches := gomonkey.ApplyMethod(reflect.TypeOf(r.Service), "GetVPCsByNamespace", func(_ *vpc.VPCService, _ string) []*model.Vpc { @@ -860,7 +939,8 @@ func TestNetworkInfoReconciler_deleteStaleVPCs(t *testing.T) { }, Path: servicecommon.String("/orgs/default/projects/default/vpcs/vpc1"), DisplayName: servicecommon.String("fakeDisplayName1"), - }} + }, + } }) patches.ApplyPrivateMethod(reflect.TypeOf(r), "listNamespaceCRsNameIDSet", func(_ *NetworkInfoReconciler, _ context.Context) (sets.Set[string], sets.Set[string], error) { return sets.Set[string]{}, sets.Set[string]{}, nil @@ -952,7 +1032,8 @@ func TestNetworkInfoReconciler_DeleteNetworkInfo(t *testing.T) { {Scope: servicecommon.String(servicecommon.TagScopeNamespaceUID), Tag: servicecommon.String("vpc1")}, }, Path: servicecommon.String("/orgs/default/projects/default/vpcs/vpc1"), - }} + }, + } }) patches.ApplyMethod(reflect.TypeOf(r.Service), "DeleteVPC", func(_ *vpc.VPCService, _ string) error { return fmt.Errorf("delete failed") @@ -970,8 +1051,10 @@ func TestNetworkInfoReconciler_DeleteNetworkInfo(t *testing.T) { expectErrStr: "", existingNetworkInfo: &v1alpha1.NetworkInfo{ TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{Name: "testNetworkInfo", Namespace: "testNamespace", - DeletionTimestamp: &metav1.Time{Time: time.Now()}, Finalizers: []string{"test-Finalizers"}}, + ObjectMeta: metav1.ObjectMeta{ + Name: "testNetworkInfo", Namespace: "testNamespace", + DeletionTimestamp: &metav1.Time{Time: time.Now()}, Finalizers: []string{"test-Finalizers"}, + }, VPCs: nil, }, prepareFuncs: func(r *NetworkInfoReconciler) *gomonkey.Patches { @@ -1017,8 +1100,10 @@ func TestNetworkInfoReconciler_DeleteNetworkInfo(t *testing.T) { expectErrStr: "failed to get VPCNetworkConfiguration for Namespace when deleting stale VPCs", existingNetworkInfo: &v1alpha1.NetworkInfo{ TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{Name: "testNetworkInfo", Namespace: "testNamespace", UID: "fakeNamespaceUID", - DeletionTimestamp: &metav1.Time{Time: time.Now()}, Finalizers: []string{"test-Finalizers"}}, + ObjectMeta: metav1.ObjectMeta{ + Name: "testNetworkInfo", Namespace: "testNamespace", UID: "fakeNamespaceUID", + DeletionTimestamp: &metav1.Time{Time: time.Now()}, Finalizers: []string{"test-Finalizers"}, + }, VPCs: nil, }, expectRes: common.ResultRequeue, @@ -1219,9 +1304,11 @@ func TestNetworkInfoReconciler_GetVpcConnectivityProfilePathByVpcPath(t *testing patches := gomonkey.ApplyMethod(reflect.TypeOf(fakeAttachmentClient), "List", func(_ *fakeVpcAttachmentClient, _ string, _ string, _ string, _ *string, _ *bool, _ *string, _ *int64, _ *bool, _ *string) (model.VpcAttachmentListResult, error) { return model.VpcAttachmentListResult{ Results: []model.VpcAttachment{ - {VpcConnectivityProfile: servicecommon.String("/orgs/default/projects/project-quality/vpc-connectivity-profiles/default"), - ParentPath: servicecommon.String("/orgs/default/projects/project-quality/vpcs/fake-vpc"), - Path: servicecommon.String("/orgs/default/projects/project-quality/vpcs/fake-vpc/attachments/default")}, + { + VpcConnectivityProfile: servicecommon.String("/orgs/default/projects/project-quality/vpc-connectivity-profiles/default"), + ParentPath: servicecommon.String("/orgs/default/projects/project-quality/vpcs/fake-vpc"), + Path: servicecommon.String("/orgs/default/projects/project-quality/vpcs/fake-vpc/attachments/default"), + }, }, }, nil }) diff --git a/pkg/nsx/services/realizestate/realize_state.go b/pkg/nsx/services/realizestate/realize_state.go index b7f3c3b16..4bbfd1751 100644 --- a/pkg/nsx/services/realizestate/realize_state.go +++ b/pkg/nsx/services/realizestate/realize_state.go @@ -5,6 +5,7 @@ package realizestate import ( "fmt" + "strings" "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" "k8s.io/apimachinery/pkg/util/wait" @@ -15,9 +16,7 @@ import ( nsxutil "github.com/vmware-tanzu/nsx-operator/pkg/nsx/util" ) -var ( - log = &logger.Log -) +var log = &logger.Log type RealizeStateService struct { common.Service @@ -76,3 +75,33 @@ func (service *RealizeStateService) CheckRealizeState(backoff wait.Backoff, inte return fmt.Errorf("%s not realized", intentPath) }) } + +func (service *RealizeStateService) GetPolicyTier1UplinkPortIP(intentPath string) (string, error) { + results, err := service.NSXClient.RealizedEntitiesClient.List(intentPath, nil) + err = nsxutil.TransNSXApiError(err) + if err != nil { + return "", err + } + + for _, result := range results.Results { + extendAttributes := result.ExtendedAttributes + if len(extendAttributes) == 0 { + continue + } + for i := range extendAttributes { + if extendAttributes[i].Key != nil && *extendAttributes[i].Key == "IpAddresses" { + if len(result.IntentPaths) == 1 { + for _, ip := range extendAttributes[i].Values { + parts := strings.Split(ip, "/") + if len(parts) != 2 { + log.Info("Invalid tier1 uplink port IP found", "IP", ip, "Path", intentPath) + continue + } + return parts[0], nil + } + } + } + } + } + return "", fmt.Errorf("%s tier1 uplink port IP not found", intentPath) +} diff --git a/pkg/nsx/services/realizestate/realize_state_test.go b/pkg/nsx/services/realizestate/realize_state_test.go index 36be10cf7..b4706af9a 100644 --- a/pkg/nsx/services/realizestate/realize_state_test.go +++ b/pkg/nsx/services/realizestate/realize_state_test.go @@ -6,7 +6,10 @@ import ( "github.com/agiledragon/gomonkey/v2" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + apierrors "github.com/vmware/vsphere-automation-sdk-go/lib/vapi/std/errors" "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" + "k8s.io/apimachinery/pkg/util/wait" "github.com/vmware-tanzu/nsx-operator/pkg/config" @@ -229,5 +232,152 @@ func TestRealizeStateService_CheckRealizeState(t *testing.T) { _, ok = err.(*nsxutil.RealizeStateError) assert.Equal(t, ok, false) patches.Reset() +} + +func TestRealizeStateService_GetPolicyTier1UplinkPortIP(t *testing.T) { + commonService := common.Service{ + NSXClient: &nsx.Client{ + RealizedEntitiesClient: &fakeRealizedEntitiesClient{}, + NsxConfig: &config.NSXOperatorConfig{ + CoeConfig: &config.CoeConfig{ + Cluster: "k8scl-one:test", + }, + }, + }, + NSXConfig: &config.NSXOperatorConfig{ + CoeConfig: &config.CoeConfig{ + Cluster: "k8scl-one:test", + }, + }, + } + s := &RealizeStateService{ + Service: commonService, + } + testCases := []struct { + name string + intentPath string + prepareFuncs func() *gomonkey.Patches + wantObj string + wantErr string + }{ + { + name: "Test normal case", + intentPath: "/orgs/default/projects/project-quality/vpcs/ns-vpc-uid-1", + wantObj: "100.64.0.3", + prepareFuncs: func() *gomonkey.Patches { + patches := gomonkey.ApplyFunc((*fakeRealizedEntitiesClient).List, func(c *fakeRealizedEntitiesClient, intentPathParam string, sitePathParam *string) (model.GenericPolicyRealizedResourceListResult, error) { + return model.GenericPolicyRealizedResourceListResult{ + Results: []model.GenericPolicyRealizedResource{ + { + State: common.String(model.GenericPolicyRealizedResource_STATE_REALIZED), + EntityType: common.String("RealizedLogicalPort"), + IntentPaths: []string{ + "/orgs/default/projects/project-quality/vpcs/ns-vpc-uid-1", + }, + }, + { + State: common.String(model.GenericPolicyRealizedResource_STATE_REALIZED), + ExtendedAttributes: []model.AttributeVal{ + { + DataType: common.String("STRING"), + Key: common.String("IpAddresses"), + Values: []string{"100.64.0.3/31"}, + }, + { + DataType: common.String("STRING"), + Key: common.String("MacAddress"), + Values: []string{"02:50:56:56:44:52"}, + }, + }, + EntityType: common.String("RealizedLogicalPort"), + IntentPaths: []string{ + "/orgs/default/projects/project-quality/vpcs/ns-vpc-uid-1", + }, + }, + }, + }, nil + }) + return patches + }, + }, + { + name: "Empty list result", + intentPath: "/orgs/default/projects/project-quality/vpcs/ns-vpc-uid-1", + wantErr: "tier1 uplink port IP not found", + prepareFuncs: func() *gomonkey.Patches { + patches := gomonkey.ApplyFunc((*fakeRealizedEntitiesClient).List, func(c *fakeRealizedEntitiesClient, intentPathParam string, sitePathParam *string) (model.GenericPolicyRealizedResourceListResult, error) { + return model.GenericPolicyRealizedResourceListResult{ + Results: []model.GenericPolicyRealizedResource{ + {}, + }, + }, nil + }) + return patches + }, + }, + { + name: "Invalid tier1 uplink port IP", + intentPath: "/orgs/default/projects/project-quality/vpcs/ns-vpc-uid-1", + wantErr: "tier1 uplink port IP not found", + prepareFuncs: func() *gomonkey.Patches { + patches := gomonkey.ApplyFunc((*fakeRealizedEntitiesClient).List, func(c *fakeRealizedEntitiesClient, intentPathParam string, sitePathParam *string) (model.GenericPolicyRealizedResourceListResult, error) { + return model.GenericPolicyRealizedResourceListResult{ + Results: []model.GenericPolicyRealizedResource{ + { + State: common.String(model.GenericPolicyRealizedResource_STATE_REALIZED), + ExtendedAttributes: []model.AttributeVal{ + { + DataType: common.String("STRING"), + Key: common.String("IpAddresses"), + Values: []string{"100.64.0.3/31/33"}, + }, + }, + EntityType: common.String("RealizedLogicalPort"), + IntentPaths: []string{ + "/orgs/default/projects/project-quality/vpcs/ns-vpc-uid-1", + }, + }, + }, + }, nil + }) + return patches + }, + }, + { + name: "Realized error", + intentPath: "/orgs/default/projects/project-quality/vpcs/ns-vpc-uid-1", + wantErr: "com.vmware.vapi.std.errors.service_unavailable", + prepareFuncs: func() *gomonkey.Patches { + patches := gomonkey.ApplyFunc((*fakeRealizedEntitiesClient).List, func(c *fakeRealizedEntitiesClient, intentPathParam string, sitePathParam *string) (model.GenericPolicyRealizedResourceListResult, error) { + return model.GenericPolicyRealizedResourceListResult{ + Results: []model.GenericPolicyRealizedResource{ + { + State: common.String(model.GenericPolicyRealizedResource_STATE_ERROR), + EntityType: common.String("RealizedLogicalPort"), + }, + }, + }, apierrors.NewServiceUnavailable() + }) + return patches + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + if testCase.prepareFuncs != nil { + patches := testCase.prepareFuncs() + defer patches.Reset() + } + + got, err := s.GetPolicyTier1UplinkPortIP(testCase.intentPath) + if testCase.wantErr != "" { + assert.ErrorContains(t, err, testCase.wantErr) + } else { + require.NoError(t, err) + assert.Equal(t, testCase.wantObj, got) + } + }) + } } diff --git a/pkg/nsx/services/vpc/vpc.go b/pkg/nsx/services/vpc/vpc.go index a170e62e5..50fbeb904 100644 --- a/pkg/nsx/services/vpc/vpc.go +++ b/pkg/nsx/services/vpc/vpc.go @@ -643,7 +643,6 @@ func (s *VPCService) GetAVISubnetInfo(vpc model.Vpc) (string, string, error) { subnetsClient := s.NSXClient.SubnetsClient statusClient := s.NSXClient.SubnetStatusClient info, err := common.ParseVPCResourcePath(*vpc.Path) - if err != nil { return "", "", err } @@ -679,6 +678,22 @@ func (s *VPCService) GetAVISubnetInfo(vpc model.Vpc) (string, string, error) { return path, cidr, nil } +func (s *VPCService) GetNSXLBSNATIP(vpc model.Vpc) (string, error) { + log.V(2).Info("Get VPC NSX LB SNAT IP", "VPC", *vpc.Id) + _, err := common.ParseVPCResourcePath(*vpc.Path) + if err != nil { + return "", err + } + + realizeService := realizestate.InitializeRealizeState(s.Service) + tier1UpLinkIP, err := realizeService.GetPolicyTier1UplinkPortIP(*vpc.Path) + if err != nil { + log.Error(err, "Failed to get VPC NSX LB SNAT IP", "VPC", *vpc.Id) + return "", err + } + return tier1UpLinkIP, nil +} + func (s *VPCService) GetVpcConnectivityProfile(nc *common.VPCNetworkConfigInfo, vpcConnectivityProfilePath string) (*model.VpcConnectivityProfile, error) { parts := strings.Split(vpcConnectivityProfilePath, "/") if len(parts) < 1 { @@ -953,7 +968,6 @@ func (s *VPCService) ValidateGatewayConnectionStatus(nc *common.VPCNetworkConfig transitGatewayId := parts[len(parts)-1] res, err := s.NSXClient.TransitGatewayAttachmentClient.List(nc.Org, nc.NSXProject, transitGatewayId, nil, &markedForDelete, nil, nil, nil, nil) err = nsxutil.TransNSXApiError(err) - if err != nil { return false, "", err } diff --git a/pkg/nsx/services/vpc/vpc_test.go b/pkg/nsx/services/vpc/vpc_test.go index 350e416ec..aff3ab99f 100644 --- a/pkg/nsx/services/vpc/vpc_test.go +++ b/pkg/nsx/services/vpc/vpc_test.go @@ -14,6 +14,7 @@ import ( "k8s.io/apimachinery/pkg/types" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/agiledragon/gomonkey/v2" @@ -35,6 +36,7 @@ import ( "github.com/vmware-tanzu/nsx-operator/pkg/nsx" "github.com/vmware-tanzu/nsx-operator/pkg/nsx/ratelimiter" "github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/common" + "github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/realizestate" nsxUtil "github.com/vmware-tanzu/nsx-operator/pkg/nsx/util" ) @@ -270,15 +272,19 @@ func TestGetSharedVPCNamespaceFromNS(t *testing.T) { existingNames: []*v1.Namespace{ { TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{Name: "test-ns-1", - Annotations: map[string]string{"nsx.vmware.com/vpc_network_config": "default", "nsx.vmware.com/shared_vpc_namespace": "test-ns-2"}}, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ns-1", + Annotations: map[string]string{"nsx.vmware.com/vpc_network_config": "default", "nsx.vmware.com/shared_vpc_namespace": "test-ns-2"}, + }, Spec: v1.NamespaceSpec{}, Status: v1.NamespaceStatus{}, }, { TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{Name: "test-ns-2", - Annotations: map[string]string{"nsx.vmware.com/vpc_network_config": "default"}}, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ns-2", + Annotations: map[string]string{"nsx.vmware.com/vpc_network_config": "default"}, + }, Spec: v1.NamespaceSpec{}, Status: v1.NamespaceStatus{}, }, @@ -677,9 +683,11 @@ func TestEdgeClusterEnabled(t *testing.T) { } vpcConnPrfile := model.VpcConnectivityProfile{ - ServiceGateway: &model.VpcServiceGatewayConfig{Enable: common.Bool(false), + ServiceGateway: &model.VpcServiceGatewayConfig{ + Enable: common.Bool(false), NatConfig: &model.VpcNatConfig{EnableDefaultSnat: common.Bool(true)}, - }} + }, + } patch := gomonkey.ApplyMethod(reflect.TypeOf(vpcService), "GetVpcConnectivityProfile", func(_ *VPCService, _ *common.VPCNetworkConfigInfo, _ string) (*model.VpcConnectivityProfile, error) { return &vpcConnPrfile, nil @@ -1208,7 +1216,6 @@ func TestVPCService_RegisterVPCNetworkConfig(t *testing.T) { got, exist := service.GetVPCNetworkConfig("fake-name") assert.True(t, exist) reflect.DeepEqual(got, info) - } func TestVPCService_UnRegisterVPCNetworkConfig(t *testing.T) { @@ -1237,11 +1244,9 @@ func TestVPCService_UnRegisterVPCNetworkConfig(t *testing.T) { got, exist = service.GetVPCNetworkConfig("fake-name") assert.False(t, exist) assert.Equal(t, common.VPCNetworkConfigInfo{}, got) - } func TestVPCService_RegisterNamespaceNetworkconfigBinding(t *testing.T) { - service, _, _ := createService(t) info := common.VPCNetworkConfigInfo{ @@ -1261,11 +1266,9 @@ func TestVPCService_RegisterNamespaceNetworkconfigBinding(t *testing.T) { reflect.DeepEqual(info, got) got = service.GetVPCNetworkConfigByNamespace("non-exist") assert.Nil(t, got) - } func TestVPCService_UnRegisterNamespaceNetworkconfigBinding(t *testing.T) { - service, _, _ := createService(t) info := common.VPCNetworkConfigInfo{ @@ -1288,7 +1291,6 @@ func TestVPCService_UnRegisterNamespaceNetworkconfigBinding(t *testing.T) { service.UnRegisterNamespaceNetworkconfigBinding("fake-ns") got = service.GetVPCNetworkConfigByNamespace("fake-ns") assert.Nil(t, got) - } func TestVPCService_GetNamespacesByNetworkconfigName(t *testing.T) { @@ -1417,7 +1419,8 @@ func TestVPCService_DeleteVPC(t *testing.T) { checkLBStore: false, checkVPCStore: false, }, - {name: "lb in store but vpc not", + { + name: "lb in store but vpc not", prepareFunc: func(_ *testing.T, service *VPCService) (patches *gomonkey.Patches) { patches = gomonkey.ApplyMethodSeq(reflect.TypeOf(service.NSXClient.VPCClient), "Delete", []gomonkey.OutputCell{{ Values: gomonkey.Params{ @@ -1438,7 +1441,8 @@ func TestVPCService_DeleteVPC(t *testing.T) { checkLBStore: true, checkVPCStore: false, }, - {name: "delete vpc store fail", + { + name: "delete vpc store fail", prepareFunc: func(_ *testing.T, service *VPCService) (patches *gomonkey.Patches) { patches = gomonkey.ApplyMethodSeq(reflect.TypeOf(service.NSXClient.VPCClient), "Delete", []gomonkey.OutputCell{{ Values: gomonkey.Params{ @@ -1467,7 +1471,8 @@ func TestVPCService_DeleteVPC(t *testing.T) { checkLBStore: true, checkVPCStore: false, }, - {name: "happy pass", + { + name: "happy pass", prepareFunc: func(_ *testing.T, service *VPCService) (patches *gomonkey.Patches) { patches = gomonkey.ApplyMethodSeq(reflect.TypeOf(service.NSXClient.VPCClient), "Delete", []gomonkey.OutputCell{{ Values: gomonkey.Params{ @@ -1501,7 +1506,6 @@ func TestVPCService_DeleteVPC(t *testing.T) { // nolint: copylocks for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if tt.prepareFunc != nil { patches := tt.prepareFunc(t, service) defer patches.Reset() @@ -1535,7 +1539,6 @@ func TestVPCService_DeleteVPC(t *testing.T) { lb := service.LbsStore.GetByKey(mockLBKey) assert.Nil(t, lb) } - }) } } @@ -2046,8 +2049,7 @@ func TestVPCService_CreateOrUpdateVPC(t *testing.T) { } } -type fakeOrgRootClient struct { -} +type fakeOrgRootClient struct{} func (f fakeOrgRootClient) Get(basePathParam *string, filterParam *string, typeFilterParam *string) (model.OrgRoot, error) { return model.OrgRoot{}, nil @@ -2057,8 +2059,7 @@ func (f fakeOrgRootClient) Patch(orgRootParam model.OrgRoot, enforceRevisionChec return nil } -type fakeRealizedEntitiesClient struct { -} +type fakeRealizedEntitiesClient struct{} func (f fakeRealizedEntitiesClient) List(intentPathParam string, sitePathParam *string) (model.GenericPolicyRealizedResourceListResult, error) { state := model.GenericPolicyRealizedResource_STATE_REALIZED @@ -2133,3 +2134,79 @@ func TestInitializeVPC(t *testing.T) { assert.Equal(t, tc.expectAllVPCNum, len(allVPCs)) } } + +func TestGetNSXLBSNATIP(t *testing.T) { + vpcService := &VPCService{ + Service: common.Service{ + NSXConfig: &config.NSXOperatorConfig{ + NsxConfig: &config.NsxConfig{ + UseAVILoadBalancer: false, + UseNSXLoadBalancer: ptr.To(true), + }, + }, + NSXClient: &nsx.Client{ + Cluster: &nsx.Cluster{}, + }, + }, + LbsStore: &LBSStore{ResourceStore: common.ResourceStore{ + Indexer: cache.NewIndexer(keyFunc, cache.Indexers{}), + BindingType: model.LBServiceBindingType(), + }}, + } + + vpc1 := model.Vpc{ + DisplayName: &vpcName1, + Id: &vpcID1, + Path: ptr.To("/orgs/default/projects/project-quality/vpcs/ns-vpc-uid-1"), + } + + testCases := []struct { + name string + vpc model.Vpc + prepareFuncs func() *gomonkey.Patches + wantObj string + wantErr string + }{ + { + name: "Test normal case", + vpc: vpc1, + wantObj: "100.64.0.3", + prepareFuncs: func() *gomonkey.Patches { + patches := gomonkey.ApplyFunc((*realizestate.RealizeStateService).GetPolicyTier1UplinkPortIP, + func(_ *realizestate.RealizeStateService, _ string) (string, error) { + return "100.64.0.3", nil + }) + return patches + }, + }, + { + name: "nsx lb uplink port IP not found error", + vpc: vpc1, + prepareFuncs: func() *gomonkey.Patches { + patches := gomonkey.ApplyFunc((*realizestate.RealizeStateService).GetPolicyTier1UplinkPortIP, + func(_ *realizestate.RealizeStateService, _ string) (string, error) { + return "", fmt.Errorf("fake-vpc tier1 uplink port IP not found") + }) + return patches + }, + wantErr: "tier1 uplink port IP not found", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + if testCase.prepareFuncs != nil { + patches := testCase.prepareFuncs() + defer patches.Reset() + } + + got, err := vpcService.GetNSXLBSNATIP(testCase.vpc) + if testCase.wantErr != "" { + assert.ErrorContains(t, err, testCase.wantErr) + } else { + require.NoError(t, err) + assert.Equal(t, testCase.wantObj, got) + } + }) + } +}