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) + } + }) + } +}