diff --git a/controllers/gateway_controller.go b/controllers/gateway_controller.go index 91cab32a..bf327cec 100644 --- a/controllers/gateway_controller.go +++ b/controllers/gateway_controller.go @@ -249,7 +249,9 @@ func (r *GatewayReconciler) updateGatewayStatus(ctx context.Context, serviceNetw //if gw.Status.Conditions[0].LastTransitionTime == eventhandlers.ZeroTransitionTime { glog.V(6).Infof("updateGatewayStatus: updating last transition time \n") - gw.Status.Conditions[0].LastTransitionTime = metav1.NewTime(time.Now()) + if gw.Status.Conditions[0].LastTransitionTime == eventhandlers.ZeroTransitionTime { + gw.Status.Conditions[0].LastTransitionTime = metav1.NewTime(time.Now()) + } //} gw.Status.Conditions[0].Status = "True" gw.Status.Conditions[0].Message = fmt.Sprintf("aws-gateway-arn: %s", serviceNetworkStatus.ARN) diff --git a/controllers/httproute_controller.go b/controllers/httproute_controller.go index 838ad58a..1f3dec24 100644 --- a/controllers/httproute_controller.go +++ b/controllers/httproute_controller.go @@ -270,12 +270,6 @@ func (r *HTTPRouteReconciler) updateHTTPRouteStatus(ctx context.Context, dns str glog.V(6).Infof("updateHTTPRouteStatus: httproute %v, dns %v\n", httproute, dns) httprouteOld := httproute.DeepCopy() - if len(httproute.Status.RouteStatus.Parents) == 0 { - httproute.Status.RouteStatus.Parents = make([]v1alpha2.RouteParentStatus, 1) - httproute.Status.RouteStatus.Parents[0].Conditions = make([]metav1.Condition, 1) - httproute.Status.RouteStatus.Parents[0].Conditions[0].LastTransitionTime = eventhandlers.ZeroTransitionTime - } - if len(httproute.ObjectMeta.Annotations) == 0 { httproute.ObjectMeta.Annotations = make(map[string]string) } @@ -288,7 +282,13 @@ func (r *HTTPRouteReconciler) updateHTTPRouteStatus(ctx context.Context, dns str } httprouteOld = httproute.DeepCopy() + if len(httproute.Status.RouteStatus.Parents) == 0 { + httproute.Status.RouteStatus.Parents = make([]v1alpha2.RouteParentStatus, 1) + httproute.Status.RouteStatus.Parents[0].Conditions = make([]metav1.Condition, 1) + httproute.Status.RouteStatus.Parents[0].Conditions[0].LastTransitionTime = eventhandlers.ZeroTransitionTime + } + fmt.Printf("liwwu >>>>httproute :%v \n", httproute) httproute.Status.RouteStatus.Parents[0].ControllerName = config.LatticeGatewayControllerName httproute.Status.RouteStatus.Parents[0].Conditions[0].Type = "httproute" diff --git a/docs/configure.md b/docs/configure.md index 94a55d8e..7727d9dc 100644 --- a/docs/configure.md +++ b/docs/configure.md @@ -18,6 +18,19 @@ This example creates a single cluster in a single VPC, then configures two route ```bash kubectl apply -f examples/my-hotel-gateway.yaml ``` + ***Note*** + + By default, the gateway(lattice service network) is not associated with cluster's VPC. To associate a gateway(lattice service network) to VPC, gateway object must have following annotation. + + + apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: Gateway + metadata: + name: my-hotel + annotations: + application-networking.k8s.aws/lattice-vpc-association: "true" + + 1. Verify that `my-hotel` gateway is created (this could take about five minutes): ```bash kubectl get gateway diff --git a/examples/my-hotel-gateway-multi-listeners.yaml b/examples/my-hotel-gateway-multi-listeners.yaml index 86f4f51c..acb68947 100644 --- a/examples/my-hotel-gateway-multi-listeners.yaml +++ b/examples/my-hotel-gateway-multi-listeners.yaml @@ -2,6 +2,8 @@ apiVersion: gateway.networking.k8s.io/v1alpha2 kind: Gateway metadata: name: my-hotel + annotations: + application-networking.k8s.aws/lattice-vpc-association: "true" spec: gatewayClassName: amazon-vpc-lattice listeners: diff --git a/examples/my-hotel-gateway-tls.yaml b/examples/my-hotel-gateway-tls.yaml index 48861296..15dd29d8 100644 --- a/examples/my-hotel-gateway-tls.yaml +++ b/examples/my-hotel-gateway-tls.yaml @@ -2,6 +2,8 @@ apiVersion: gateway.networking.k8s.io/v1alpha2 kind: Gateway metadata: name: my-hotel + annotations: + application-networking.k8s.aws/lattice-vpc-association: "true" spec: gatewayClassName: amazon-vpc-lattice listeners: diff --git a/examples/my-hotel-gateway.yaml b/examples/my-hotel-gateway.yaml index ded0f2cc..04b99622 100644 --- a/examples/my-hotel-gateway.yaml +++ b/examples/my-hotel-gateway.yaml @@ -2,6 +2,8 @@ apiVersion: gateway.networking.k8s.io/v1alpha2 kind: Gateway metadata: name: my-hotel + annotations: + application-networking.k8s.aws/lattice-vpc-association: "true" spec: gatewayClassName: amazon-vpc-lattice listeners: diff --git a/pkg/deploy/lattice/service_network_manager.go b/pkg/deploy/lattice/service_network_manager.go index accf9d0f..2eb56f99 100644 --- a/pkg/deploy/lattice/service_network_manager.go +++ b/pkg/deploy/lattice/service_network_manager.go @@ -19,6 +19,11 @@ type ServiceNetworkManager interface { Delete(ctx context.Context, service_network string) error } +type serviceNetworkOutput struct { + snSummary *vpclattice.ServiceNetworkSummary + snTags *vpclattice.ListTagsForResourceOutput +} + func NewDefaultServiceNetworkManager(cloud lattice_aws.Cloud) *defaultServiceNetworkManager { return &defaultServiceNetworkManager{ cloud: cloud, @@ -56,58 +61,94 @@ func (m *defaultServiceNetworkManager) Create(ctx context.Context, service_netwo var service_networkID string var service_networkArn string var isServiceNetworkAssociatedWithVPC bool + var service_networkAssociatedWithCurrentVPCId *string vpcLatticeSess := m.cloud.Lattice() if service_networkSummary == nil { - glog.V(2).Infof("ServiceNetwork Create API here, service_network[%v] vpciID[%s]\n", service_network, config.VpcID) + glog.V(2).Infof("Create ServiceNetwork, service_network[%v] and tag it with vpciID[%s]\n", service_network, config.VpcID) + // Add tag to show this is the VPC create this servicenetwork + // This means, the servicenetwork can only be deleted by the controller running in this VPC + service_networkInput := vpclattice.CreateServiceNetworkInput{ Name: &service_network.Spec.Name, + Tags: make(map[string]*string), } + service_networkInput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &config.VpcID + + glog.V(2).Infof("Create service_network >>>> req[%v]", service_networkInput) resp, err := vpcLatticeSess.CreateServiceNetworkWithContext(ctx, &service_networkInput) + glog.V(2).Infof("Create service_network >>>> resp[%v], err : %v", resp, err) if err != nil { - glog.V(6).Infoln("Failed to create service_network, err: ", err) + glog.V(2).Infof("Failed to create service_network[%v], err: %v", service_network, err) return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, err } service_networkID = aws.StringValue(resp.Id) service_networkArn = aws.StringValue(resp.Arn) isServiceNetworkAssociatedWithVPC = false - glog.V(2).Infof(" ServiceNetwork Create API resp [%v]\n", resp) + glog.V(6).Infof(" ServiceNetwork Create API resp [%v]\n", resp) + } else { - glog.V(6).Infoln("service_network exists, further check association") - service_networkID = aws.StringValue(service_networkSummary.Id) - service_networkArn = aws.StringValue(service_networkSummary.Arn) - isServiceNetworkAssociatedWithVPC, _, _, err = m.isServiceNetworkAssociatedWithVPC(ctx, service_networkID) + glog.V(6).Infof("service_network[%v] exists, further check association", service_network) + service_networkID = aws.StringValue(service_networkSummary.snSummary.Id) + service_networkArn = aws.StringValue(service_networkSummary.snSummary.Arn) + isServiceNetworkAssociatedWithVPC, service_networkAssociatedWithCurrentVPCId, _, err = m.isServiceNetworkAssociatedWithVPC(ctx, service_networkID) if err != nil { return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, err } } - if isServiceNetworkAssociatedWithVPC == false { - createServiceNetworkVpcAssociationInput := vpclattice.CreateServiceNetworkVpcAssociationInput{ - ServiceNetworkIdentifier: &service_networkID, - VpcIdentifier: &config.VpcID, + if service_network.Spec.AssociateToVPC == true { + if isServiceNetworkAssociatedWithVPC == false { + // current state: service network is associated to VPC + // desired state: associate this service network to VPC + createServiceNetworkVpcAssociationInput := vpclattice.CreateServiceNetworkVpcAssociationInput{ + ServiceNetworkIdentifier: &service_networkID, + VpcIdentifier: &config.VpcID, + } + glog.V(2).Infof("Create service_network/vpc association >>>> req[%v]", createServiceNetworkVpcAssociationInput) + resp, err := vpcLatticeSess.CreateServiceNetworkVpcAssociationWithContext(ctx, &createServiceNetworkVpcAssociationInput) + glog.V(2).Infof("Create service_network and vpc association here >>>> resp[%v] err [%v]\n", resp, err) + // Associate service_network with vpc + if err != nil { + glog.V(2).Infof("Failed to associate service_network[%v] and vpc, err: %v", service_network, err) + return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, err + } else { + service_networkVPCAssociationStatus := aws.StringValue(resp.Status) + switch service_networkVPCAssociationStatus { + case vpclattice.ServiceNetworkVpcAssociationStatusCreateInProgress: + return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, errors.New(LATTICE_RETRY) + case vpclattice.ServiceNetworkVpcAssociationStatusActive: + return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: service_networkArn, ServiceNetworkID: service_networkID}, nil + case vpclattice.ServiceNetworkVpcAssociationStatusCreateFailed: + return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, errors.New(LATTICE_RETRY) + case vpclattice.ServiceNetworkVpcAssociationStatusDeleteFailed: + return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, errors.New(LATTICE_RETRY) + case vpclattice.ServiceNetworkVpcAssociationStatusDeleteInProgress: + return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, errors.New(LATTICE_RETRY) + } + } } - glog.V(2).Infof("Create service_network/vpc association >>>> req[%v]\n", createServiceNetworkVpcAssociationInput) - resp, err := vpcLatticeSess.CreateServiceNetworkVpcAssociationWithContext(ctx, &createServiceNetworkVpcAssociationInput) - glog.V(2).Infof("Create service_network and vpc association here >>>> resp[%v] err [%v]\n", resp, err) - // Associate service_network with vpc - if err != nil { - glog.V(6).Infoln("ServiceNetwork is created successfully, but failed to associate service_network and vpc, err: ", err) - return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, err - } else { - service_networkVPCAssociationStatus := aws.StringValue(resp.Status) - switch service_networkVPCAssociationStatus { - case vpclattice.ServiceNetworkVpcAssociationStatusCreateInProgress: - return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, errors.New(LATTICE_RETRY) - case vpclattice.ServiceNetworkVpcAssociationStatusActive: - return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: service_networkArn, ServiceNetworkID: service_networkID}, nil - case vpclattice.ServiceNetworkVpcAssociationStatusCreateFailed: - return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, errors.New(LATTICE_RETRY) - case vpclattice.ServiceNetworkVpcAssociationStatusDeleteFailed: - return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, errors.New(LATTICE_RETRY) - case vpclattice.ServiceNetworkVpcAssociationStatusDeleteInProgress: - return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, errors.New(LATTICE_RETRY) + } else { + if isServiceNetworkAssociatedWithVPC == true { + // current state: service network is associated to VPC + // desired state: not to associate this service network to VPC + glog.V(6).Infof("Disassociate service_network(%v) from vpc association", service_network.Spec.Name) + + deleteServiceNetworkVpcAssociationInput := vpclattice.DeleteServiceNetworkVpcAssociationInput{ + ServiceNetworkVpcAssociationIdentifier: service_networkAssociatedWithCurrentVPCId, } + + glog.V(2).Infof("Delete service_network association >>>> req[%v]", deleteServiceNetworkVpcAssociationInput) + resp, err := vpcLatticeSess.DeleteServiceNetworkVpcAssociationWithContext(ctx, &deleteServiceNetworkVpcAssociationInput) + glog.V(2).Infof("Delete service_network association >>>> resp[%v],err [%v]", resp, err) + if err != nil { + glog.V(2).Infof("Failed to delete association for %v err=%v , resp = %v\n", service_network.Spec.Name, err, resp) + } + + // return retry and check later if disassociation workflow finishes + return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, errors.New(LATTICE_RETRY) + } + glog.V(2).Infof("Created service_network(%v) without vpc association", service_network.Spec.Name) } return latticemodel.ServiceNetworkStatus{ServiceNetworkARN: service_networkArn, ServiceNetworkID: service_networkID}, nil } @@ -135,71 +176,80 @@ func (m *defaultServiceNetworkManager) Delete(ctx context.Context, service_netwo return err } - var ifServiceNetworkExist bool if service_networkSummary == nil { - ifServiceNetworkExist = false - } else { - ifServiceNetworkExist = true - } - - if ifServiceNetworkExist == false { - glog.V(6).Infof("ServiceNetworkManager-Delete, successfully deleting unknown service_network %v\n", service_network) + glog.V(2).Infof("Successfully deleted unknown service_network %v\n", service_network) return nil } vpcLatticeSess := m.cloud.Lattice() - service_networkID := aws.StringValue(service_networkSummary.Id) - deleteNeedRetry := false + service_networkID := aws.StringValue(service_networkSummary.snSummary.Id) - _, service_networkAssociatedWithCurrentVPCId, service_networkVPCAssociations, err := m.isServiceNetworkAssociatedWithVPC(ctx, service_networkID) + _, service_networkAssociatedWithCurrentVPCId, assocResp, err := m.isServiceNetworkAssociatedWithVPC(ctx, service_networkID) if err != nil { return err } - totalAssociation := len(service_networkVPCAssociations) - if totalAssociation == 0 { - deleteNeedRetry = false - } else if service_networkAssociatedWithCurrentVPCId != nil { - if err == errors.New(LATTICE_RETRY) { - // The current association is in progress - deleteNeedRetry = true - return errors.New(LATTICE_RETRY) + if service_networkAssociatedWithCurrentVPCId != nil { + // current VPC is associated with this service network + + // Happy case, disassociate the VPC from service network + deleteServiceNetworkVpcAssociationInput := vpclattice.DeleteServiceNetworkVpcAssociationInput{ + ServiceNetworkVpcAssociationIdentifier: service_networkAssociatedWithCurrentVPCId, + } + glog.V(2).Infof("DeleteServiceNetworkVpcAssociationInput >>>> %v\n", deleteServiceNetworkVpcAssociationInput) + resp, err := vpcLatticeSess.DeleteServiceNetworkVpcAssociationWithContext(ctx, &deleteServiceNetworkVpcAssociationInput) + glog.V(2).Infof("DeleteServiceNetworkVPCAssociationResp: service_network %v , resp %v, err %v \n", service_network, resp, err) + if err != nil { + glog.V(2).Infof("Failed to delete association for %v, err: %v \n", service_network, err) + } + // retry later to check if VPC disassociation workflow finishes + return errors.New(LATTICE_RETRY) + + } + + // check if this VPC is the one created the service network + needToDelete := false + if service_networkSummary.snTags != nil && service_networkSummary.snTags.Tags != nil { + snTags := service_networkSummary.snTags + vpcOwner, ok := snTags.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] + if ok && *vpcOwner == config.VpcID { + needToDelete = true } else { - // Happy case, this is the last association - deleteNeedRetry = true - deleteServiceNetworkVpcAssociationInput := vpclattice.DeleteServiceNetworkVpcAssociationInput{ - ServiceNetworkVpcAssociationIdentifier: service_networkAssociatedWithCurrentVPCId, - } - glog.V(2).Infof("DeleteServiceNetworkVpcAssociationInput >>>> %v\n", deleteServiceNetworkVpcAssociationInput) - resp, err := vpcLatticeSess.DeleteServiceNetworkVpcAssociationWithContext(ctx, &deleteServiceNetworkVpcAssociationInput) - if err != nil { - glog.V(6).Infof("Failed to delete association %v \n", err) + if ok { + glog.V(2).Infof("Skip deleting, the service network[%v] is created by VPC %v", service_network, *vpcOwner) + } else { + glog.V(2).Infof("Skip deleting, the service network[%v] is not created by K8S, since there is no tag", service_network) } - glog.V(2).Infof("DeleteServiceNetworkVPCAssociationResp: service_network %v , resp %v, err %v \n", service_network, resp, err) } - } else { - // there are other Associations left for this service_network, could not delete - deleteNeedRetry = true - return nil } - deleteInput := vpclattice.DeleteServiceNetworkInput{ - ServiceNetworkIdentifier: &service_networkID, - } - _, err = vpcLatticeSess.DeleteServiceNetworkWithContext(ctx, &deleteInput) - if err != nil { + if needToDelete { + + if len(assocResp) != 0 { + glog.V(2).Infof("Retry deleting %v later, due to service network still has VPCs associated", service_network) + return errors.New(LATTICE_RETRY) + } + + deleteInput := vpclattice.DeleteServiceNetworkInput{ + ServiceNetworkIdentifier: &service_networkID, + } + glog.V(2).Infof("DeleteServiceNetworkWithContext: service_network %v", service_network) + resp, err := vpcLatticeSess.DeleteServiceNetworkWithContext(ctx, &deleteInput) + glog.V(2).Infof("DeleteServiceNetworkWithContext: service_network %v , resp %v, err %v \n", service_network, resp, err) + if err != nil { + return errors.New(LATTICE_RETRY) + } + + glog.V(2).Infof("Successfully delete service_network %v\n", service_network) return err - } - if deleteNeedRetry { - return errors.New(LATTICE_RETRY) } else { - glog.V(6).Infof("Successfully delete service_network %v\n", service_network) - return err + glog.V(2).Infof("Deleting service_network (%v) Skipped, since it is owned by different VPC ", service_network) + return nil } } // Find service_network by name return service_network,err if service_network exists, otherwise return nil, nil. -func (m *defaultServiceNetworkManager) findServiceNetworkByName(ctx context.Context, targetServiceNetwork string) (*vpclattice.ServiceNetworkSummary, error) { +func (m *defaultServiceNetworkManager) findServiceNetworkByName(ctx context.Context, targetServiceNetwork string) (*serviceNetworkOutput, error) { vpcLatticeSess := m.cloud.Lattice() service_networkListInput := vpclattice.ListServiceNetworksInput{} resp, err := vpcLatticeSess.ListServiceNetworksAsList(ctx, &service_networkListInput) @@ -207,7 +257,22 @@ func (m *defaultServiceNetworkManager) findServiceNetworkByName(ctx context.Cont for _, r := range resp { if aws.StringValue(r.Name) == targetServiceNetwork { glog.V(6).Infoln("Found ServiceNetwork named ", targetServiceNetwork) - return r, err + + tagsInput := vpclattice.ListTagsForResourceInput{ + ResourceArn: r.Arn, + } + tagsOutput, err := vpcLatticeSess.ListTagsForResourceWithContext(ctx, &tagsInput) + + if err != nil { + tagsOutput = nil + } + + snOutput := serviceNetworkOutput{ + snSummary: r, + snTags: tagsOutput, + } + + return &snOutput, err } } return nil, err diff --git a/pkg/deploy/lattice/service_network_manager_test.go b/pkg/deploy/lattice/service_network_manager_test.go index 600fcfba..e016535f 100644 --- a/pkg/deploy/lattice/service_network_manager_test.go +++ b/pkg/deploy/lattice/service_network_manager_test.go @@ -15,11 +15,12 @@ import ( ) // ServiceNetwork does not exist before,happy case. -func Test_CreateServiceNetwork_MeshNotExist(t *testing.T) { +func Test_CreateServiceNetwork_MeshNotExist_NoNeedToAssociate(t *testing.T) { meshCreateInput := latticemodel.ServiceNetwork{ Spec: latticemodel.ServiceNetworkSpec{ - Name: "test", - Account: "123456789", + Name: "test", + Account: "123456789", + AssociateToVPC: false, }, Status: &latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, } @@ -32,17 +33,54 @@ func Test_CreateServiceNetwork_MeshNotExist(t *testing.T) { Name: &name, } listServiceNetworkOutput := []*vpclattice.ServiceNetworkSummary{} - status := vpclattice.ServiceNetworkVpcAssociationStatusActive - createServiceNetworkVPCAssociationOutput := &vpclattice.CreateServiceNetworkVpcAssociationOutput{ - Status: &status, - } + createServiceNetworkInput := &vpclattice.CreateServiceNetworkInput{ Name: &name, + Tags: make(map[string]*string), } - createServiceNetworkVpcAssociationInput := &vpclattice.CreateServiceNetworkVpcAssociationInput{ - ServiceNetworkIdentifier: &id, - VpcIdentifier: &config.VpcID, + createServiceNetworkInput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &config.VpcID + + c := gomock.NewController(t) + defer c.Finish() + ctx := context.TODO() + mockCloud := mocks_aws.NewMockCloud(c) + mockVpcLatticeSess := mocks.NewMockLattice(c) + mockVpcLatticeSess.EXPECT().ListServiceNetworksAsList(ctx, gomock.Any()).Return(listServiceNetworkOutput, nil) + mockVpcLatticeSess.EXPECT().CreateServiceNetworkWithContext(ctx, createServiceNetworkInput).Return(meshCreateOutput, nil) + mockCloud.EXPECT().Lattice().Return(mockVpcLatticeSess).AnyTimes() + + meshManager := NewDefaultServiceNetworkManager(mockCloud) + resp, err := meshManager.Create(ctx, &meshCreateInput) + + assert.Nil(t, err) + assert.Equal(t, resp.ServiceNetworkARN, arn) + assert.Equal(t, resp.ServiceNetworkID, id) +} + +func Test_CreateServiceNetwork_MeshNotExist_NeedToAssociate(t *testing.T) { + meshCreateInput := latticemodel.ServiceNetwork{ + Spec: latticemodel.ServiceNetworkSpec{ + Name: "test", + Account: "123456789", + AssociateToVPC: true, + }, + Status: &latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, } + arn := "12345678912345678912" + id := "12345678912345678912" + name := "test" + meshCreateOutput := &vpclattice.CreateServiceNetworkOutput{ + Arn: &arn, + Id: &id, + Name: &name, + } + listServiceNetworkOutput := []*vpclattice.ServiceNetworkSummary{} + + createServiceNetworkInput := &vpclattice.CreateServiceNetworkInput{ + Name: &name, + Tags: make(map[string]*string), + } + createServiceNetworkInput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &config.VpcID c := gomock.NewController(t) defer c.Finish() @@ -51,6 +89,15 @@ func Test_CreateServiceNetwork_MeshNotExist(t *testing.T) { mockVpcLatticeSess := mocks.NewMockLattice(c) mockVpcLatticeSess.EXPECT().ListServiceNetworksAsList(ctx, gomock.Any()).Return(listServiceNetworkOutput, nil) mockVpcLatticeSess.EXPECT().CreateServiceNetworkWithContext(ctx, createServiceNetworkInput).Return(meshCreateOutput, nil) + meshId := "12345678912345678912" + createServiceNetworkVpcAssociationInput := &vpclattice.CreateServiceNetworkVpcAssociationInput{ + ServiceNetworkIdentifier: &meshId, + VpcIdentifier: &config.VpcID, + } + associationStatus := vpclattice.ServiceNetworkVpcAssociationStatusUpdateInProgress + createServiceNetworkVPCAssociationOutput := &vpclattice.CreateServiceNetworkVpcAssociationOutput{ + Status: &associationStatus, + } mockVpcLatticeSess.EXPECT().CreateServiceNetworkVpcAssociationWithContext(ctx, createServiceNetworkVpcAssociationInput).Return(createServiceNetworkVPCAssociationOutput, nil) mockCloud.EXPECT().Lattice().Return(mockVpcLatticeSess).AnyTimes() @@ -103,8 +150,9 @@ func Test_CreateServiceNetwork_ListFailed(t *testing.T) { func Test_CreateServiceNetwork_MeshAlreadyExist_ServiceNetworkVpcAssociationStatusCreateInProgress(t *testing.T) { meshCreateInput := latticemodel.ServiceNetwork{ Spec: latticemodel.ServiceNetworkSpec{ - Name: "test", - Account: "123456789", + Name: "test", + Account: "123456789", + AssociateToVPC: true, }, Status: &latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, } @@ -136,6 +184,7 @@ func Test_CreateServiceNetwork_MeshAlreadyExist_ServiceNetworkVpcAssociationStat mockCloud := mocks_aws.NewMockCloud(c) mockVpcLatticeSess.EXPECT().ListServiceNetworksAsList(ctx, gomock.Any()).Return(listServiceNetworkOutput, nil) mockVpcLatticeSess.EXPECT().ListServiceNetworkVpcAssociationsAsList(ctx, gomock.Any()).Return(statusServiceNetworkVPCOutput, nil) + mockVpcLatticeSess.EXPECT().ListTagsForResourceWithContext(ctx, gomock.Any()).Return(nil, nil) mockCloud.EXPECT().Lattice().Return(mockVpcLatticeSess).AnyTimes() meshManager := NewDefaultServiceNetworkManager(mockCloud) @@ -185,6 +234,7 @@ func Test_CreateServiceNetwork_MeshAlreadyExist_ServiceNetworkVpcAssociationStat mockCloud := mocks_aws.NewMockCloud(c) mockVpcLatticeSess.EXPECT().ListServiceNetworksAsList(ctx, gomock.Any()).Return(listServiceNetworkOutput, nil) mockVpcLatticeSess.EXPECT().ListServiceNetworkVpcAssociationsAsList(ctx, gomock.Any()).Return(statusServiceNetworkVPCOutput, nil) + mockVpcLatticeSess.EXPECT().ListTagsForResourceWithContext(ctx, gomock.Any()).Return(nil, nil) mockCloud.EXPECT().Lattice().Return(mockVpcLatticeSess).AnyTimes() meshManager := NewDefaultServiceNetworkManager(mockCloud) @@ -200,8 +250,9 @@ func Test_CreateServiceNetwork_MeshAlreadyExist_ServiceNetworkVpcAssociationStat func Test_CreateServiceNetwork_MeshAlreadyExist_ServiceNetworkVpcAssociationStatusActive(t *testing.T) { meshCreateInput := latticemodel.ServiceNetwork{ Spec: latticemodel.ServiceNetworkSpec{ - Name: "test", - Account: "123456789", + Name: "test", + Account: "123456789", + AssociateToVPC: true, }, Status: &latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, } @@ -232,6 +283,7 @@ func Test_CreateServiceNetwork_MeshAlreadyExist_ServiceNetworkVpcAssociationStat mockCloud := mocks_aws.NewMockCloud(c) mockVpcLatticeSess.EXPECT().ListServiceNetworksAsList(ctx, gomock.Any()).Return(listServiceNetworkOutput, nil) mockVpcLatticeSess.EXPECT().ListServiceNetworkVpcAssociationsAsList(ctx, gomock.Any()).Return(statusServiceNetworkVPCOutput, nil) + mockVpcLatticeSess.EXPECT().ListTagsForResourceWithContext(ctx, gomock.Any()).Return(nil, nil) mockCloud.EXPECT().Lattice().Return(mockVpcLatticeSess).AnyTimes() meshManager := NewDefaultServiceNetworkManager(mockCloud) @@ -242,12 +294,65 @@ func Test_CreateServiceNetwork_MeshAlreadyExist_ServiceNetworkVpcAssociationStat assert.Equal(t, resp.ServiceNetworkID, meshId) } +func Test_CreateServiceNetwork_MeshAlreadyExist_AssociateToNotAssociate(t *testing.T) { + meshCreateInput := latticemodel.ServiceNetwork{ + Spec: latticemodel.ServiceNetworkSpec{ + Name: "test", + Account: "123456789", + AssociateToVPC: false, + }, + Status: &latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, + } + meshId := "12345678912345678912" + meshArn := "12345678912345678912" + name := "test" + item := vpclattice.ServiceNetworkSummary{ + Arn: &meshArn, + Id: &meshId, + Name: &name, + } + listServiceNetworkOutput := []*vpclattice.ServiceNetworkSummary{&item} + + status := vpclattice.ServiceNetworkVpcAssociationStatusActive + items := vpclattice.ServiceNetworkVpcAssociationSummary{ + ServiceNetworkArn: &meshArn, + ServiceNetworkId: &meshId, + ServiceNetworkName: &meshId, + Status: &status, + VpcId: &config.VpcID, + } + statusServiceNetworkVPCOutput := []*vpclattice.ServiceNetworkVpcAssociationSummary{&items} + + c := gomock.NewController(t) + defer c.Finish() + ctx := context.TODO() + mockVpcLatticeSess := mocks.NewMockLattice(c) + mockCloud := mocks_aws.NewMockCloud(c) + mockVpcLatticeSess.EXPECT().ListServiceNetworksAsList(ctx, gomock.Any()).Return(listServiceNetworkOutput, nil) + mockVpcLatticeSess.EXPECT().ListServiceNetworkVpcAssociationsAsList(ctx, gomock.Any()).Return(statusServiceNetworkVPCOutput, nil) + mockVpcLatticeSess.EXPECT().ListTagsForResourceWithContext(ctx, gomock.Any()).Return(nil, nil) + deleteInProgressStatus := vpclattice.ServiceNetworkVpcAssociationStatusDeleteInProgress + deleteServiceNetworkVpcAssociationOutput := &vpclattice.DeleteServiceNetworkVpcAssociationOutput{Status: &deleteInProgressStatus} + + mockVpcLatticeSess.EXPECT().DeleteServiceNetworkVpcAssociationWithContext(ctx, gomock.Any()).Return(deleteServiceNetworkVpcAssociationOutput, nil) + + mockCloud.EXPECT().Lattice().Return(mockVpcLatticeSess).AnyTimes() + + meshManager := NewDefaultServiceNetworkManager(mockCloud) + _, err := meshManager.Create(ctx, &meshCreateInput) + + assert.Equal(t, err, errors.New(LATTICE_RETRY)) + +} + // ServiceNetwork already exists, association is ServiceNetworkVpcAssociationStatusCreateFailed. + func Test_CreateServiceNetwork_MeshAlreadyExist_ServiceNetworkVpcAssociationStatusCreateFailed(t *testing.T) { meshCreateInput := latticemodel.ServiceNetwork{ Spec: latticemodel.ServiceNetworkSpec{ - Name: "test", - Account: "123456789", + Name: "test", + Account: "123456789", + AssociateToVPC: true, }, Status: &latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, } @@ -287,6 +392,11 @@ func Test_CreateServiceNetwork_MeshAlreadyExist_ServiceNetworkVpcAssociationStat mockCloud := mocks_aws.NewMockCloud(c) mockVpcLatticeSess.EXPECT().ListServiceNetworksAsList(ctx, gomock.Any()).Return(listServiceNetworkOutput, nil) mockVpcLatticeSess.EXPECT().ListServiceNetworkVpcAssociationsAsList(ctx, gomock.Any()).Return(statusServiceNetworkVPCOutput, nil) + snTagsOuput := &vpclattice.ListTagsForResourceOutput{ + Tags: make(map[string]*string), + } + snTagsOuput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &config.VpcID + mockVpcLatticeSess.EXPECT().ListTagsForResourceWithContext(ctx, gomock.Any()).Return(snTagsOuput, nil) mockVpcLatticeSess.EXPECT().CreateServiceNetworkVpcAssociationWithContext(ctx, createServiceNetworkVpcAssociationInput).Return(createServiceNetworkVPCAssociationOutput, nil) mockCloud.EXPECT().Lattice().Return(mockVpcLatticeSess).AnyTimes() @@ -302,8 +412,9 @@ func Test_CreateServiceNetwork_MeshAlreadyExist_ServiceNetworkVpcAssociationStat func Test_CreateServiceNetwork_MeshAlreadyExist_MeshAssociatedWithOtherVPC(t *testing.T) { meshCreateInput := latticemodel.ServiceNetwork{ Spec: latticemodel.ServiceNetworkSpec{ - Name: "test", - Account: "123456789", + Name: "test", + Account: "123456789", + AssociateToVPC: true, }, Status: &latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, } @@ -344,6 +455,12 @@ func Test_CreateServiceNetwork_MeshAlreadyExist_MeshAssociatedWithOtherVPC(t *te mockCloud := mocks_aws.NewMockCloud(c) mockVpcLatticeSess.EXPECT().ListServiceNetworksAsList(ctx, gomock.Any()).Return(listServiceNetworkOutput, nil) mockVpcLatticeSess.EXPECT().ListServiceNetworkVpcAssociationsAsList(ctx, gomock.Any()).Return(statusServiceNetworkVPCOutput, nil) + snTagsOuput := &vpclattice.ListTagsForResourceOutput{ + Tags: make(map[string]*string), + } + dummy_vpc := "dummy-vpc-id" + snTagsOuput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &dummy_vpc + mockVpcLatticeSess.EXPECT().ListTagsForResourceWithContext(ctx, gomock.Any()).Return(snTagsOuput, nil) mockVpcLatticeSess.EXPECT().CreateServiceNetworkVpcAssociationWithContext(ctx, createServiceNetworkVpcAssociationInput).Return(createServiceNetworkVPCAssociationOutput, nil) mockCloud.EXPECT().Lattice().Return(mockVpcLatticeSess).AnyTimes() @@ -359,8 +476,9 @@ func Test_CreateServiceNetwork_MeshAlreadyExist_MeshAssociatedWithOtherVPC(t *te func Test_CreateServiceNetwork_MeshNotExist_ServiceNetworkVpcAssociationStatusFailed(t *testing.T) { CreateInput := latticemodel.ServiceNetwork{ Spec: latticemodel.ServiceNetworkSpec{ - Name: "test", - Account: "123456789", + Name: "test", + Account: "123456789", + AssociateToVPC: true, }, Status: &latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, } @@ -380,7 +498,10 @@ func Test_CreateServiceNetwork_MeshNotExist_ServiceNetworkVpcAssociationStatusFa } meshCreateInput := &vpclattice.CreateServiceNetworkInput{ Name: &name, + Tags: make(map[string]*string), } + meshCreateInput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &config.VpcID + createServiceNetworkVpcAssociationInput := &vpclattice.CreateServiceNetworkVpcAssociationInput{ ServiceNetworkIdentifier: &meshId, VpcIdentifier: &config.VpcID, @@ -409,8 +530,9 @@ func Test_CreateServiceNetwork_MeshNotExist_ServiceNetworkVpcAssociationStatusFa func Test_CreateServiceNetwork_MeshNOTExist_ServiceNetworkVpcAssociationStatusCreateInProgress(t *testing.T) { CreateInput := latticemodel.ServiceNetwork{ Spec: latticemodel.ServiceNetworkSpec{ - Name: "test", - Account: "123456789", + Name: "test", + Account: "123456789", + AssociateToVPC: true, }, Status: &latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, } @@ -429,7 +551,9 @@ func Test_CreateServiceNetwork_MeshNOTExist_ServiceNetworkVpcAssociationStatusCr } meshCreateInput := &vpclattice.CreateServiceNetworkInput{ Name: &name, + Tags: make(map[string]*string), } + meshCreateInput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &config.VpcID createServiceNetworkVpcAssociationInput := &vpclattice.CreateServiceNetworkVpcAssociationInput{ ServiceNetworkIdentifier: &meshId, VpcIdentifier: &config.VpcID, @@ -458,8 +582,9 @@ func Test_CreateServiceNetwork_MeshNOTExist_ServiceNetworkVpcAssociationStatusCr func Test_CreateServiceNetwork_MeshNotExist_ServiceNetworkVpcAssociationStatusDeleteInProgress(t *testing.T) { CreateInput := latticemodel.ServiceNetwork{ Spec: latticemodel.ServiceNetworkSpec{ - Name: "test", - Account: "123456789", + Name: "test", + Account: "123456789", + AssociateToVPC: true, }, Status: &latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, } @@ -478,7 +603,9 @@ func Test_CreateServiceNetwork_MeshNotExist_ServiceNetworkVpcAssociationStatusDe } meshCreateInput := &vpclattice.CreateServiceNetworkInput{ Name: &name, + Tags: make(map[string]*string), } + meshCreateInput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &config.VpcID createServiceNetworkVpcAssociationInput := &vpclattice.CreateServiceNetworkVpcAssociationInput{ ServiceNetworkIdentifier: &meshId, VpcIdentifier: &config.VpcID, @@ -507,8 +634,9 @@ func Test_CreateServiceNetwork_MeshNotExist_ServiceNetworkVpcAssociationStatusDe func Test_CreateServiceNetwork_MeshNotExist_ServiceNetworkVpcAssociationReturnsError(t *testing.T) { CreateInput := latticemodel.ServiceNetwork{ Spec: latticemodel.ServiceNetworkSpec{ - Name: "test", - Account: "123456789", + Name: "test", + Account: "123456789", + AssociateToVPC: true, }, Status: &latticemodel.ServiceNetworkStatus{ServiceNetworkARN: "", ServiceNetworkID: ""}, } @@ -524,7 +652,9 @@ func Test_CreateServiceNetwork_MeshNotExist_ServiceNetworkVpcAssociationReturnsE } meshCreateInput := &vpclattice.CreateServiceNetworkInput{ Name: &name, + Tags: make(map[string]*string), } + meshCreateInput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &config.VpcID createServiceNetworkVpcAssociationInput := &vpclattice.CreateServiceNetworkVpcAssociationInput{ ServiceNetworkIdentifier: &meshId, VpcIdentifier: &config.VpcID, @@ -569,8 +699,11 @@ func Test_CreateMesh_MeshNotExist_MeshCreateFailed(t *testing.T) { listServiceNetworkOutput := []*vpclattice.ServiceNetworkSummary{} meshCreateInput := &vpclattice.CreateServiceNetworkInput{ Name: &name, + Tags: make(map[string]*string), } + meshCreateInput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &config.VpcID + c := gomock.NewController(t) defer c.Finish() ctx := context.TODO() @@ -606,6 +739,7 @@ func Test_DeleteMesh_MeshNotExist(t *testing.T) { assert.Nil(t, err) } +// delte a service network, which has no association and also was created by this VPC func Test_DeleteMesh_MeshExistsNoAssociation(t *testing.T) { arn := "123456789" id := "123456789" @@ -629,6 +763,12 @@ func Test_DeleteMesh_MeshExistsNoAssociation(t *testing.T) { mockCloud := mocks_aws.NewMockCloud(c) mockVpcLatticeSess.EXPECT().ListServiceNetworksAsList(ctx, gomock.Any()).Return(listServiceNetworkOutput, nil) mockVpcLatticeSess.EXPECT().ListServiceNetworkVpcAssociationsAsList(ctx, gomock.Any()).Return(statusServiceNetworkVPCOutput, nil) + + snTagsOuput := &vpclattice.ListTagsForResourceOutput{ + Tags: make(map[string]*string), + } + snTagsOuput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &config.VpcID + mockVpcLatticeSess.EXPECT().ListTagsForResourceWithContext(ctx, gomock.Any()).Return(snTagsOuput, nil) mockVpcLatticeSess.EXPECT().DeleteServiceNetworkWithContext(ctx, deleteMeshInout).Return(deleteMeshOutput, nil) mockCloud.EXPECT().Lattice().Return(mockVpcLatticeSess).AnyTimes() @@ -638,22 +778,38 @@ func Test_DeleteMesh_MeshExistsNoAssociation(t *testing.T) { assert.Nil(t, err) } -func Test_DeleteMesh_MeshExists_AssociationsWithOtherVPCExists(t *testing.T) { +// Deleting a service netwrok, when +// * the service network is associated with current VPC +// * and it is this VPC creates this service network +func Test_DeleteMesh_MeshExistsAssociatedWithVPC_Deleting(t *testing.T) { arn := "123456789" id := "123456789" name := "test" - vpcId := "123456789" - item := vpclattice.ServiceNetworkSummary{ + itemMesh := vpclattice.ServiceNetworkSummary{ Arn: &arn, Id: &id, Name: &name, } - listServiceNetworkOutput := []*vpclattice.ServiceNetworkSummary{&item} + listServiceNetworkOutput := []*vpclattice.ServiceNetworkSummary{&itemMesh} - ServiceNetworkVpcAssociationSummaryItem := vpclattice.ServiceNetworkVpcAssociationSummary{ - VpcId: &vpcId, + associationArn := "123456789" + associationID := "123456789" + associationStatus := vpclattice.ServiceNetworkVpcAssociationStatusActive + associationVPCId := config.VpcID + itemAssociation := vpclattice.ServiceNetworkVpcAssociationSummary{ + Arn: &associationArn, + Id: &associationID, + ServiceNetworkArn: &arn, + ServiceNetworkId: &id, + ServiceNetworkName: &name, + Status: &associationStatus, + VpcId: &associationVPCId, } - statusServiceNetworkVPCOutput := []*vpclattice.ServiceNetworkVpcAssociationSummary{&ServiceNetworkVpcAssociationSummaryItem} + statusServiceNetworkVPCOutput := []*vpclattice.ServiceNetworkVpcAssociationSummary{&itemAssociation} + + deleteInProgressStatus := vpclattice.ServiceNetworkVpcAssociationStatusDeleteInProgress + deleteServiceNetworkVpcAssociationOutput := &vpclattice.DeleteServiceNetworkVpcAssociationOutput{Status: &deleteInProgressStatus} + deleteServiceNetworkVpcAssociationInput := &vpclattice.DeleteServiceNetworkVpcAssociationInput{ServiceNetworkVpcAssociationIdentifier: &associationID} c := gomock.NewController(t) defer c.Finish() @@ -662,15 +818,23 @@ func Test_DeleteMesh_MeshExists_AssociationsWithOtherVPCExists(t *testing.T) { mockCloud := mocks_aws.NewMockCloud(c) mockVpcLatticeSess.EXPECT().ListServiceNetworksAsList(ctx, gomock.Any()).Return(listServiceNetworkOutput, nil) mockVpcLatticeSess.EXPECT().ListServiceNetworkVpcAssociationsAsList(ctx, gomock.Any()).Return(statusServiceNetworkVPCOutput, nil) + + snTagsOuput := &vpclattice.ListTagsForResourceOutput{ + Tags: make(map[string]*string), + } + snTagsOuput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &config.VpcID + mockVpcLatticeSess.EXPECT().ListTagsForResourceWithContext(ctx, gomock.Any()).Return(snTagsOuput, nil) + mockVpcLatticeSess.EXPECT().DeleteServiceNetworkVpcAssociationWithContext(ctx, deleteServiceNetworkVpcAssociationInput).Return(deleteServiceNetworkVpcAssociationOutput, nil) mockCloud.EXPECT().Lattice().Return(mockVpcLatticeSess).AnyTimes() meshManager := NewDefaultServiceNetworkManager(mockCloud) err := meshManager.Delete(ctx, "test") - assert.Nil(t, err) + assert.NotNil(t, err) + assert.Equal(t, err, errors.New(LATTICE_RETRY)) } -func Test_DeleteMesh_MeshExistsAssociatedWithVPC_Deleting(t *testing.T) { +func Test_DeleteMesh_MeshExistsAssociatedWithOtherVPC(t *testing.T) { arn := "123456789" id := "123456789" name := "test" @@ -684,7 +848,7 @@ func Test_DeleteMesh_MeshExistsAssociatedWithVPC_Deleting(t *testing.T) { associationArn := "123456789" associationID := "123456789" associationStatus := vpclattice.ServiceNetworkVpcAssociationStatusActive - associationVPCId := config.VpcID + associationVPCId := "other-vpc-id" itemAssociation := vpclattice.ServiceNetworkVpcAssociationSummary{ Arn: &associationArn, Id: &associationID, @@ -696,12 +860,6 @@ func Test_DeleteMesh_MeshExistsAssociatedWithVPC_Deleting(t *testing.T) { } statusServiceNetworkVPCOutput := []*vpclattice.ServiceNetworkVpcAssociationSummary{&itemAssociation} - deleteInProgressStatus := vpclattice.ServiceNetworkVpcAssociationStatusDeleteInProgress - deleteServiceNetworkVpcAssociationOutput := &vpclattice.DeleteServiceNetworkVpcAssociationOutput{Status: &deleteInProgressStatus} - deleteMeshOutput := &vpclattice.DeleteServiceNetworkOutput{} - deleteServiceNetworkVpcAssociationInput := &vpclattice.DeleteServiceNetworkVpcAssociationInput{ServiceNetworkVpcAssociationIdentifier: &associationID} - deleteMeshInput := &vpclattice.DeleteServiceNetworkInput{ServiceNetworkIdentifier: &id} - c := gomock.NewController(t) defer c.Finish() ctx := context.TODO() @@ -709,8 +867,12 @@ func Test_DeleteMesh_MeshExistsAssociatedWithVPC_Deleting(t *testing.T) { mockCloud := mocks_aws.NewMockCloud(c) mockVpcLatticeSess.EXPECT().ListServiceNetworksAsList(ctx, gomock.Any()).Return(listServiceNetworkOutput, nil) mockVpcLatticeSess.EXPECT().ListServiceNetworkVpcAssociationsAsList(ctx, gomock.Any()).Return(statusServiceNetworkVPCOutput, nil) - mockVpcLatticeSess.EXPECT().DeleteServiceNetworkVpcAssociationWithContext(ctx, deleteServiceNetworkVpcAssociationInput).Return(deleteServiceNetworkVpcAssociationOutput, nil) - mockVpcLatticeSess.EXPECT().DeleteServiceNetworkWithContext(ctx, deleteMeshInput).Return(deleteMeshOutput, nil) + + snTagsOuput := &vpclattice.ListTagsForResourceOutput{ + Tags: make(map[string]*string), + } + snTagsOuput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &config.VpcID + mockVpcLatticeSess.EXPECT().ListTagsForResourceWithContext(ctx, gomock.Any()).Return(snTagsOuput, nil) mockCloud.EXPECT().Lattice().Return(mockVpcLatticeSess).AnyTimes() meshManager := NewDefaultServiceNetworkManager(mockCloud) @@ -720,7 +882,7 @@ func Test_DeleteMesh_MeshExistsAssociatedWithVPC_Deleting(t *testing.T) { assert.Equal(t, err, errors.New(LATTICE_RETRY)) } -func Test_DeleteMesh_MeshExistsAssociatedWithOtherVPC(t *testing.T) { +func Test_DeleteMesh_MeshExistsAssociatedWithOtherVPC_NotCreatedByVPC(t *testing.T) { arn := "123456789" id := "123456789" name := "test" @@ -753,6 +915,7 @@ func Test_DeleteMesh_MeshExistsAssociatedWithOtherVPC(t *testing.T) { mockCloud := mocks_aws.NewMockCloud(c) mockVpcLatticeSess.EXPECT().ListServiceNetworksAsList(ctx, gomock.Any()).Return(listServiceNetworkOutput, nil) mockVpcLatticeSess.EXPECT().ListServiceNetworkVpcAssociationsAsList(ctx, gomock.Any()).Return(statusServiceNetworkVPCOutput, nil) + mockVpcLatticeSess.EXPECT().ListTagsForResourceWithContext(ctx, gomock.Any()).Return(nil, nil) mockCloud.EXPECT().Lattice().Return(mockVpcLatticeSess).AnyTimes() meshManager := NewDefaultServiceNetworkManager(mockCloud) @@ -761,6 +924,54 @@ func Test_DeleteMesh_MeshExistsAssociatedWithOtherVPC(t *testing.T) { assert.Nil(t, err) } +func Test_DeleteMesh_MeshExistsAssociatedWithOtherVPC_CreatedByVPC(t *testing.T) { + arn := "123456789" + id := "123456789" + name := "test" + itemMesh := vpclattice.ServiceNetworkSummary{ + Arn: &arn, + Id: &id, + Name: &name, + } + listServiceNetworkOutput := []*vpclattice.ServiceNetworkSummary{&itemMesh} + + associationArn := "123456789" + associationID := "123456789" + associationStatus := vpclattice.ServiceNetworkVpcAssociationStatusActive + associationVPCId := "123456789" + itemAssociation := vpclattice.ServiceNetworkVpcAssociationSummary{ + Arn: &associationArn, + Id: &associationID, + ServiceNetworkArn: &arn, + ServiceNetworkId: &id, + ServiceNetworkName: &name, + Status: &associationStatus, + VpcId: &associationVPCId, + } + statusServiceNetworkVPCOutput := []*vpclattice.ServiceNetworkVpcAssociationSummary{&itemAssociation} + + c := gomock.NewController(t) + defer c.Finish() + ctx := context.TODO() + mockVpcLatticeSess := mocks.NewMockLattice(c) + mockCloud := mocks_aws.NewMockCloud(c) + mockVpcLatticeSess.EXPECT().ListServiceNetworksAsList(ctx, gomock.Any()).Return(listServiceNetworkOutput, nil) + mockVpcLatticeSess.EXPECT().ListServiceNetworkVpcAssociationsAsList(ctx, gomock.Any()).Return(statusServiceNetworkVPCOutput, nil) + snTagsOutput := &vpclattice.ListTagsForResourceOutput{ + Tags: make(map[string]*string), + } + snTagsOutput.Tags[latticemodel.K8SServiceNetworkOwnedByVPC] = &config.VpcID + mockVpcLatticeSess.EXPECT().ListTagsForResourceWithContext(ctx, gomock.Any()).Return(snTagsOutput, nil) + + mockCloud.EXPECT().Lattice().Return(mockVpcLatticeSess).AnyTimes() + + meshManager := NewDefaultServiceNetworkManager(mockCloud) + err := meshManager.Delete(ctx, "test") + + assert.NotNil(t, err) + assert.Equal(t, err, errors.New(LATTICE_RETRY)) +} + func Test_ListMesh_MeshExists(t *testing.T) { arn := "123456789" id := "123456789" diff --git a/pkg/deploy/lattice/service_network_synthesizer.go b/pkg/deploy/lattice/service_network_synthesizer.go index 2b32294d..ba903d5e 100644 --- a/pkg/deploy/lattice/service_network_synthesizer.go +++ b/pkg/deploy/lattice/service_network_synthesizer.go @@ -39,21 +39,16 @@ func (s *serviceNetworkSynthesizer) Synthesize(ctx context.Context) error { ret = LATTICE_RETRY } - /* TODO - * support following when we have tagging enabled, so that K8S will NOT accidently delete - * other serviceNetworkes - - if err := s.synthesizeSDKServiceNetworks(ctx); err != nil { ret = LATTICE_RETRY } - */ //TODO // https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/main/pkg/deploy/elbv2/load_balancer_synthesizer.go#L46 if ret != "" { return errors.New(ret) } else { + glog.V(6).Infof("Finish synthesizing ServiceNetworks/Gateways ... \n") return nil } @@ -109,7 +104,7 @@ func (s *serviceNetworkSynthesizer) synthesizeSDKServiceNetworks(ctx context.Con var ret = "" sdkServiceNetworks, err := s.serviceNetworkManager.List(ctx) if err != nil { - glog.V(6).Infof("Synthesize failed on List lattice serviceNetworkes %v\n", err) + glog.V(2).Infof("Synthesize failed on List lattice serviceNetworkes %v\n", err) return err } glog.V(6).Infof("SDK List: %v \n", sdkServiceNetworks) @@ -136,16 +131,18 @@ func (s *serviceNetworkSynthesizer) synthesizeSDKServiceNetworks(ctx context.Con if toBeDeleted { - glog.V(6).Infof("Synthesizing Gateway: Delete stale sdkServiceNetwork %v\n", sdkServiceNetwork) + glog.V(2).Infof("Synthesizing Gateway: Delete stale sdkServiceNetwork %v\n", sdkServiceNetwork) err := s.serviceNetworkManager.Delete(ctx, sdkServiceNetwork) if err != nil { + glog.V(6).Infof("Need to retry synthesizing err %v", err) ret = LATTICE_RETRY } } } if ret != "" { + glog.V(2).Infof("Need to retry synthesizing, reg = %v", ret) return errors.New(ret) } else { return nil diff --git a/pkg/gateway/model_build_service_network_test.go b/pkg/gateway/model_build_service_network_test.go index 899ea6f7..41b19853 100644 --- a/pkg/gateway/model_build_service_network_test.go +++ b/pkg/gateway/model_build_service_network_test.go @@ -13,22 +13,50 @@ import ( func Test_MeshModelBuild(t *testing.T) { now := metav1.Now() tests := []struct { - name string - gw *v1alpha2.Gateway - wantErr error - wantName string - wantIsDeleted bool + name string + gw *v1alpha2.Gateway + wantErr error + wantName string + wantIsDeleted bool + associateToVPC bool }{ { - name: "Adding Mesh", + name: "Adding Mesh, no annotation on VPC association", gw: &v1alpha2.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "mesh1", }, }, - wantErr: nil, - wantName: "mesh1", - wantIsDeleted: false, + wantErr: nil, + wantName: "mesh1", + wantIsDeleted: false, + associateToVPC: false, + }, + { + name: "Adding Mesh, and need VPC association", + gw: &v1alpha2.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mesh1", + Annotations: map[string]string{LatticeVPCAssociationAnnotation: "true"}, + }, + }, + wantErr: nil, + wantName: "mesh1", + wantIsDeleted: false, + associateToVPC: true, + }, + { + name: "Adding Mesh, and need VPC association", + gw: &v1alpha2.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mesh1", + Annotations: map[string]string{LatticeVPCAssociationAnnotation: "false"}, + }, + }, + wantErr: nil, + wantName: "mesh1", + wantIsDeleted: false, + associateToVPC: false, }, { name: "Deleting Mesh", @@ -56,6 +84,7 @@ func Test_MeshModelBuild(t *testing.T) { } else { assert.Equal(t, tt.wantName, got.Spec.Name) assert.Equal(t, tt.wantIsDeleted, got.Spec.IsDeleted) + assert.Equal(t, tt.associateToVPC, got.Spec.AssociateToVPC) } }) diff --git a/pkg/gateway/model_build_servicenetwork.go b/pkg/gateway/model_build_servicenetwork.go index 1ed5b027..f883ca3b 100644 --- a/pkg/gateway/model_build_servicenetwork.go +++ b/pkg/gateway/model_build_servicenetwork.go @@ -13,7 +13,8 @@ import ( ) const ( - ResourceIDServiceNetwork = "ServiceNetwork" + ResourceIDServiceNetwork = "ServiceNetwork" + LatticeVPCAssociationAnnotation = "application-networking.k8s.aws/lattice-vpc-association" ) // ModelBuilder builds the model stack for the mesh resource. @@ -61,8 +62,19 @@ func (t *serviceNetworkModelBuildTask) buildModel(ctx context.Context) error { func (t *serviceNetworkModelBuildTask) buildServiceNetwork(ctx context.Context) error { spec := latticemodel.ServiceNetworkSpec{ - Name: t.gateway.Name, - Account: config.AccountID, + Name: t.gateway.Name, + Account: config.AccountID, + AssociateToVPC: false, + } + + if len(t.gateway.ObjectMeta.Annotations) > 0 { + if value, exist := t.gateway.Annotations[LatticeVPCAssociationAnnotation]; exist { + if value == "true" { + spec.AssociateToVPC = true + } + + } + } if !t.gateway.DeletionTimestamp.IsZero() { diff --git a/pkg/model/lattice/servicenetwork.go b/pkg/model/lattice/servicenetwork.go index b13c973e..63b1c543 100644 --- a/pkg/model/lattice/servicenetwork.go +++ b/pkg/model/lattice/servicenetwork.go @@ -4,6 +4,10 @@ import ( "github.com/aws/aws-application-networking-k8s/pkg/model/core" ) +const ( + K8SServiceNetworkOwnedByVPC = "K8SServiceNetworkOwnedByVPC" +) + type ServiceNetwork struct { core.ResourceMeta `json:"-"` @@ -16,9 +20,10 @@ type ServiceNetwork struct { type ServiceNetworkSpec struct { // The name of the ServiceNetwork - Name string `json:"name"` - Account string `json:"account"` - IsDeleted bool + Name string `json:"name"` + Account string `json:"account"` + AssociateToVPC bool + IsDeleted bool } type ServiceNetworkStatus struct {