Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e96dd9f
Define field in service network model to indicate whether needs to
liwenwu-amazon Feb 28, 2023
8760c56
Update service network create call to not to associate if the spec says
liwenwu-amazon Feb 28, 2023
8abcff4
Add resource tag to indicate which VPC's controller create this service
liwenwu-amazon Feb 28, 2023
f347046
Fixed unit test failure for service network creation
liwenwu-amazon Feb 28, 2023
a57d218
Add logic to retrieve tags on service network lattice object
liwenwu-amazon Mar 1, 2023
16fd5ca
Only delete service network if it is created by this VPC
liwenwu-amazon Mar 1, 2023
0b88ab7
Update to have better log msgs
liwenwu-amazon Mar 1, 2023
d7f67e3
Move this close to where we update httproute status
liwenwu-amazon Mar 1, 2023
247ce6e
Add logic to associate or disassociate based on annotation value
liwenwu-amazon Mar 1, 2023
a75eb40
Fixed unit test to align with latest business logic
liwenwu-amazon Mar 1, 2023
a00f930
Update unit test
liwenwu-amazon Mar 2, 2023
7022e5c
Refact delete logic and unit test(s)
liwenwu-amazon Mar 2, 2023
18cfa4f
minor refactor delete routine
liwenwu-amazon Mar 2, 2023
2249f50
Update debug msgs in create routine
liwenwu-amazon Mar 2, 2023
3f6f7dd
Update debug msg and also avoid call delete API if there are VPC
liwenwu-amazon Mar 2, 2023
374ed34
Add some more unit test(s)
liwenwu-amazon Mar 2, 2023
2b3ed18
Re-enable servicenetowrk reconcile deleting staled SDK SNs
liwenwu-amazon Mar 2, 2023
07e2a41
Update not to update timestamp in every reconcile loop which cause
liwenwu-amazon Mar 2, 2023
f70d9e5
Update debug msgs
liwenwu-amazon Mar 2, 2023
730277c
Update gateway example object to specify they needs to associated to
liwenwu-amazon Mar 2, 2023
ee42c90
Update document on new annotation
liwenwu-amazon Mar 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion controllers/gateway_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic seems wrong. Don't you want to change this every time the condition transitions? Right now, it only checks when it goes from unset -> set. Is it ever possible for this condition to be false?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we reset it whenever there is a change and need controller to reconcile it

}
//}
gw.Status.Conditions[0].Status = "True"
gw.Status.Conditions[0].Message = fmt.Sprintf("aws-gateway-arn: %s", serviceNetworkStatus.ARN)
Expand Down
12 changes: 6 additions & 6 deletions controllers/httproute_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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"
Expand Down
13 changes: 13 additions & 0 deletions docs/configure.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions examples/my-hotel-gateway-multi-listeners.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions examples/my-hotel-gateway-tls.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions examples/my-hotel-gateway.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
217 changes: 141 additions & 76 deletions pkg/deploy/lattice/service_network_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get the idea, that if service_network.Spec.AssociateToVPC=false, and ServiceNetwork is currently AssociatedWithVPC, we want to disassociate vpcAndServiceNetwork. I feel it is a bit wired that this is doing delete work while in create section.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, the associationToVPC or disassociatefromVPC is kind of like an attribute of a service-network/gateway here. The K8S intend is to create gateway==servicenetwork, with attribute whether to associate/dis-associate to VPC

// 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
}
Expand Down Expand Up @@ -135,79 +176,103 @@ 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)
if err == nil {
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
Expand Down
Loading