diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index 193bd35d..a4045910 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,13 +1,13 @@ ack_generate_info: - build_date: "2022-04-15T17:13:39Z" - build_hash: 50c64871bcaf88b9ee200eb8d6b8245fa8f675eb - go_version: go1.17.5 - version: v0.18.4 -api_directory_checksum: 35ef0e4da69ded8c1fa7a4a6029510864a1069af + build_date: "2022-05-10T23:41:20Z" + build_hash: c651d2bb60694df1f7a5dad823258472a1a6fc8a + go_version: go1.18.1 + version: v0.18.4-12-gc651d2b +api_directory_checksum: 1d50f8d633cac0c132fe1f1fb8dad566a20c44db api_version: v1alpha1 aws_sdk_go_version: v1.42.0 generator_config_info: - file_checksum: 32301fc646db64b7beb3541310c727da6b6d8dc5 + file_checksum: 0dadc8ea417c3f3bacb05c27d0988207733d4e0f original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index 6dde027f..d8c76846 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -117,39 +117,6 @@ resources: DHCPConfigurations.Values: set: - from: AttributeValue.Value - RouteTable: - exceptions: - terminal_codes: - - InvalidVpcID.Malformed - - InvalidVpcID.NotFound - - InvalidParameterValue - fields: - # RouteStatuses as Route to ensure - # fields set server-side (active, origin) - # are exposed in Status - RouteStatuses: - from: - operation: DescribeRouteTables - path: RouteTables.Routes - is_read_only: true - # Routes as CreateRouteInput to ensure only - # user-editable fields are exposed in Spec - Routes: - custom_field: - list_of: CreateRouteInput - VpcId: - references: - resource: VPC - path: Status.VPCID - hooks: - sdk_create_post_set_output: - template_path: hooks/route_table/sdk_create_post_set_output.go.tpl - sdk_read_many_post_set_output: - template_path: hooks/route_table/sdk_read_many_post_set_output.go.tpl - sdk_file_end: - template_path: hooks/route_table/sdk_file_end.go.tpl - update_operation: - custom_method_name: customUpdateRouteTable ElasticIPAddress: exceptions: terminal_codes: @@ -224,6 +191,57 @@ resources: - path: Status.State in: - available + RouteTable: + exceptions: + terminal_codes: + - InvalidVpcID.Malformed + - InvalidVpcID.NotFound + - InvalidParameterValue + fields: + # RouteStatuses as Route to ensure + # fields set server-side (active, origin) + # are exposed in Status + RouteStatuses: + from: + operation: DescribeRouteTables + path: RouteTables.Routes + is_read_only: true + # Routes as CreateRouteInput to ensure only + # user-editable fields are exposed in Spec + Routes: + custom_field: + list_of: CreateRouteInput + VpcId: + references: + resource: VPC + path: Status.VPCID + Routes.GatewayId: + references: + resource: InternetGateway + path: Status.InternetGatewayID + Routes.NatGatewayId: + references: + resource: NATGateway + path: Status.NATGatewayID + Routes.TransitGatewayId: + references: + resource: TransitGateway + path: Status.TransitGatewayID + Routes.VpcEndpointId: + references: + resource: VPCEndpoint + path: Status.VPCEndpointID + hooks: + delta_pre_compare: + code: customPreCompare(a, b) + sdk_create_post_set_output: + template_path: hooks/route_table/sdk_create_post_set_output.go.tpl + sdk_read_many_post_set_output: + template_path: hooks/route_table/sdk_read_many_post_set_output.go.tpl + sdk_file_end: + template_path: hooks/route_table/sdk_file_end.go.tpl + update_operation: + custom_method_name: customUpdateRouteTable SecurityGroup: fields: # support EC2-VPC only @@ -281,6 +299,9 @@ resources: update_operation: custom_method_name: customUpdate exceptions: + errors: + 404: + code: InvalidVpcID.NotFound terminal_codes: - InvalidParameterCombination fields: diff --git a/apis/v1alpha1/types.go b/apis/v1alpha1/types.go index f89e5dfb..1269754f 100644 --- a/apis/v1alpha1/types.go +++ b/apis/v1alpha1/types.go @@ -566,13 +566,21 @@ type CreateRouteInput struct { DestinationPrefixListID *string `json:"destinationPrefixListID,omitempty"` EgressOnlyInternetGatewayID *string `json:"egressOnlyInternetGatewayID,omitempty"` GatewayID *string `json:"gatewayID,omitempty"` - InstanceID *string `json:"instanceID,omitempty"` - LocalGatewayID *string `json:"localGatewayID,omitempty"` - NATGatewayID *string `json:"natGatewayID,omitempty"` - NetworkInterfaceID *string `json:"networkInterfaceID,omitempty"` - TransitGatewayID *string `json:"transitGatewayID,omitempty"` - VPCEndpointID *string `json:"vpcEndpointID,omitempty"` - VPCPeeringConnectionID *string `json:"vpcPeeringConnectionID,omitempty"` + // Reference field for GatewayID + GatewayRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"gatewayRef,omitempty"` + InstanceID *string `json:"instanceID,omitempty"` + LocalGatewayID *string `json:"localGatewayID,omitempty"` + NATGatewayID *string `json:"natGatewayID,omitempty"` + // Reference field for NATGatewayID + NATGatewayRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"natGatewayRef,omitempty"` + NetworkInterfaceID *string `json:"networkInterfaceID,omitempty"` + TransitGatewayID *string `json:"transitGatewayID,omitempty"` + // Reference field for TransitGatewayID + TransitGatewayRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"transitGatewayRef,omitempty"` + VPCEndpointID *string `json:"vpcEndpointID,omitempty"` + // Reference field for VPCEndpointID + VPCEndpointRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"vpcEndpointRef,omitempty"` + VPCPeeringConnectionID *string `json:"vpcPeeringConnectionID,omitempty"` } // Describes the options for a VPC attachment. @@ -1294,9 +1302,9 @@ type Image struct { OwnerID *string `json:"ownerID,omitempty"` PlatformDetails *string `json:"platformDetails,omitempty"` Public *bool `json:"public,omitempty"` - RamdiskID *string `json:"ramdiskID,omitempty"` + RAMDiskID *string `json:"ramDiskID,omitempty"` RootDeviceName *string `json:"rootDeviceName,omitempty"` - SriovNetSupport *string `json:"sriovNetSupport,omitempty"` + SRIOVNetSupport *string `json:"sriovNetSupport,omitempty"` Tags []*Tag `json:"tags,omitempty"` UsageOperation *string `json:"usageOperation,omitempty"` } @@ -1392,11 +1400,11 @@ type Instance struct { PrivateIPAddress *string `json:"privateIPAddress,omitempty"` PublicDNSName *string `json:"publicDNSName,omitempty"` PublicIPAddress *string `json:"publicIPAddress,omitempty"` - RamdiskID *string `json:"ramdiskID,omitempty"` + RAMDiskID *string `json:"ramDiskID,omitempty"` RootDeviceName *string `json:"rootDeviceName,omitempty"` SourceDestCheck *bool `json:"sourceDestCheck,omitempty"` SpotInstanceRequestID *string `json:"spotInstanceRequestID,omitempty"` - SriovNetSupport *string `json:"sriovNetSupport,omitempty"` + SRIOVNetSupport *string `json:"sriovNetSupport,omitempty"` StateTransitionReason *string `json:"stateTransitionReason,omitempty"` SubnetID *string `json:"subnetID,omitempty"` Tags []*Tag `json:"tags,omitempty"` @@ -1706,7 +1714,7 @@ type LaunchSpecification struct { ImageID *string `json:"imageID,omitempty"` KernelID *string `json:"kernelID,omitempty"` KeyName *string `json:"keyName,omitempty"` - RamdiskID *string `json:"ramdiskID,omitempty"` + RAMDiskID *string `json:"ramDiskID,omitempty"` SubnetID *string `json:"subnetID,omitempty"` UserData *string `json:"userData,omitempty"` } @@ -2679,7 +2687,7 @@ type ResponseLaunchTemplateData struct { ImageID *string `json:"imageID,omitempty"` KernelID *string `json:"kernelID,omitempty"` KeyName *string `json:"keyName,omitempty"` - RamDiskID *string `json:"ramDiskID,omitempty"` + RAMDiskID *string `json:"ramDiskID,omitempty"` SecurityGroupIDs []*string `json:"securityGroupIDs,omitempty"` SecurityGroups []*string `json:"securityGroups,omitempty"` UserData *string `json:"userData,omitempty"` @@ -3055,7 +3063,7 @@ type SpotFleetLaunchSpecification struct { AddressingType *string `json:"addressingType,omitempty"` EBSOptimized *bool `json:"ebsOptimized,omitempty"` KernelID *string `json:"kernelID,omitempty"` - RamdiskID *string `json:"ramdiskID,omitempty"` + RAMDiskID *string `json:"ramDiskID,omitempty"` SpotPrice *string `json:"spotPrice,omitempty"` SubnetID *string `json:"subnetID,omitempty"` UserData *string `json:"userData,omitempty"` diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index b37551fc..d0fffa60 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -2314,6 +2314,11 @@ func (in *CreateRouteInput) DeepCopyInto(out *CreateRouteInput) { *out = new(string) **out = **in } + if in.GatewayRef != nil { + in, out := &in.GatewayRef, &out.GatewayRef + *out = new(corev1alpha1.AWSResourceReferenceWrapper) + (*in).DeepCopyInto(*out) + } if in.InstanceID != nil { in, out := &in.InstanceID, &out.InstanceID *out = new(string) @@ -2329,6 +2334,11 @@ func (in *CreateRouteInput) DeepCopyInto(out *CreateRouteInput) { *out = new(string) **out = **in } + if in.NATGatewayRef != nil { + in, out := &in.NATGatewayRef, &out.NATGatewayRef + *out = new(corev1alpha1.AWSResourceReferenceWrapper) + (*in).DeepCopyInto(*out) + } if in.NetworkInterfaceID != nil { in, out := &in.NetworkInterfaceID, &out.NetworkInterfaceID *out = new(string) @@ -2339,11 +2349,21 @@ func (in *CreateRouteInput) DeepCopyInto(out *CreateRouteInput) { *out = new(string) **out = **in } + if in.TransitGatewayRef != nil { + in, out := &in.TransitGatewayRef, &out.TransitGatewayRef + *out = new(corev1alpha1.AWSResourceReferenceWrapper) + (*in).DeepCopyInto(*out) + } if in.VPCEndpointID != nil { in, out := &in.VPCEndpointID, &out.VPCEndpointID *out = new(string) **out = **in } + if in.VPCEndpointRef != nil { + in, out := &in.VPCEndpointRef, &out.VPCEndpointRef + *out = new(corev1alpha1.AWSResourceReferenceWrapper) + (*in).DeepCopyInto(*out) + } if in.VPCPeeringConnectionID != nil { in, out := &in.VPCPeeringConnectionID, &out.VPCPeeringConnectionID *out = new(string) @@ -5710,8 +5730,8 @@ func (in *Image) DeepCopyInto(out *Image) { *out = new(bool) **out = **in } - if in.RamdiskID != nil { - in, out := &in.RamdiskID, &out.RamdiskID + if in.RAMDiskID != nil { + in, out := &in.RAMDiskID, &out.RAMDiskID *out = new(string) **out = **in } @@ -5720,8 +5740,8 @@ func (in *Image) DeepCopyInto(out *Image) { *out = new(string) **out = **in } - if in.SriovNetSupport != nil { - in, out := &in.SriovNetSupport, &out.SriovNetSupport + if in.SRIOVNetSupport != nil { + in, out := &in.SRIOVNetSupport, &out.SRIOVNetSupport *out = new(string) **out = **in } @@ -6167,8 +6187,8 @@ func (in *Instance) DeepCopyInto(out *Instance) { *out = new(string) **out = **in } - if in.RamdiskID != nil { - in, out := &in.RamdiskID, &out.RamdiskID + if in.RAMDiskID != nil { + in, out := &in.RAMDiskID, &out.RAMDiskID *out = new(string) **out = **in } @@ -6187,8 +6207,8 @@ func (in *Instance) DeepCopyInto(out *Instance) { *out = new(string) **out = **in } - if in.SriovNetSupport != nil { - in, out := &in.SriovNetSupport, &out.SriovNetSupport + if in.SRIOVNetSupport != nil { + in, out := &in.SRIOVNetSupport, &out.SRIOVNetSupport *out = new(string) **out = **in } @@ -7567,8 +7587,8 @@ func (in *LaunchSpecification) DeepCopyInto(out *LaunchSpecification) { *out = new(string) **out = **in } - if in.RamdiskID != nil { - in, out := &in.RamdiskID, &out.RamdiskID + if in.RAMDiskID != nil { + in, out := &in.RAMDiskID, &out.RAMDiskID *out = new(string) **out = **in } @@ -11991,8 +12011,8 @@ func (in *ResponseLaunchTemplateData) DeepCopyInto(out *ResponseLaunchTemplateDa *out = new(string) **out = **in } - if in.RamDiskID != nil { - in, out := &in.RamDiskID, &out.RamDiskID + if in.RAMDiskID != nil { + in, out := &in.RAMDiskID, &out.RAMDiskID *out = new(string) **out = **in } @@ -13962,8 +13982,8 @@ func (in *SpotFleetLaunchSpecification) DeepCopyInto(out *SpotFleetLaunchSpecifi *out = new(string) **out = **in } - if in.RamdiskID != nil { - in, out := &in.RamdiskID, &out.RamdiskID + if in.RAMDiskID != nil { + in, out := &in.RAMDiskID, &out.RAMDiskID *out = new(string) **out = **in } diff --git a/config/crd/bases/ec2.services.k8s.aws_routetables.yaml b/config/crd/bases/ec2.services.k8s.aws_routetables.yaml index 69a77d97..dde0f3e5 100644 --- a/config/crd/bases/ec2.services.k8s.aws_routetables.yaml +++ b/config/crd/bases/ec2.services.k8s.aws_routetables.yaml @@ -52,18 +52,66 @@ spec: type: string gatewayID: type: string + gatewayRef: + description: Reference field for GatewayID + properties: + from: + description: AWSResourceReference provides all the values + necessary to reference another k8s resource for finding + the identifier(Id/ARN/Name) + properties: + name: + type: string + type: object + type: object instanceID: type: string localGatewayID: type: string natGatewayID: type: string + natGatewayRef: + description: Reference field for NATGatewayID + properties: + from: + description: AWSResourceReference provides all the values + necessary to reference another k8s resource for finding + the identifier(Id/ARN/Name) + properties: + name: + type: string + type: object + type: object networkInterfaceID: type: string transitGatewayID: type: string + transitGatewayRef: + description: Reference field for TransitGatewayID + properties: + from: + description: AWSResourceReference provides all the values + necessary to reference another k8s resource for finding + the identifier(Id/ARN/Name) + properties: + name: + type: string + type: object + type: object vpcEndpointID: type: string + vpcEndpointRef: + description: Reference field for VPCEndpointID + properties: + from: + description: AWSResourceReference provides all the values + necessary to reference another k8s resource for finding + the identifier(Id/ARN/Name) + properties: + name: + type: string + type: object + type: object vpcPeeringConnectionID: type: string type: object diff --git a/generator.yaml b/generator.yaml index 6dde027f..d8c76846 100644 --- a/generator.yaml +++ b/generator.yaml @@ -117,39 +117,6 @@ resources: DHCPConfigurations.Values: set: - from: AttributeValue.Value - RouteTable: - exceptions: - terminal_codes: - - InvalidVpcID.Malformed - - InvalidVpcID.NotFound - - InvalidParameterValue - fields: - # RouteStatuses as Route to ensure - # fields set server-side (active, origin) - # are exposed in Status - RouteStatuses: - from: - operation: DescribeRouteTables - path: RouteTables.Routes - is_read_only: true - # Routes as CreateRouteInput to ensure only - # user-editable fields are exposed in Spec - Routes: - custom_field: - list_of: CreateRouteInput - VpcId: - references: - resource: VPC - path: Status.VPCID - hooks: - sdk_create_post_set_output: - template_path: hooks/route_table/sdk_create_post_set_output.go.tpl - sdk_read_many_post_set_output: - template_path: hooks/route_table/sdk_read_many_post_set_output.go.tpl - sdk_file_end: - template_path: hooks/route_table/sdk_file_end.go.tpl - update_operation: - custom_method_name: customUpdateRouteTable ElasticIPAddress: exceptions: terminal_codes: @@ -224,6 +191,57 @@ resources: - path: Status.State in: - available + RouteTable: + exceptions: + terminal_codes: + - InvalidVpcID.Malformed + - InvalidVpcID.NotFound + - InvalidParameterValue + fields: + # RouteStatuses as Route to ensure + # fields set server-side (active, origin) + # are exposed in Status + RouteStatuses: + from: + operation: DescribeRouteTables + path: RouteTables.Routes + is_read_only: true + # Routes as CreateRouteInput to ensure only + # user-editable fields are exposed in Spec + Routes: + custom_field: + list_of: CreateRouteInput + VpcId: + references: + resource: VPC + path: Status.VPCID + Routes.GatewayId: + references: + resource: InternetGateway + path: Status.InternetGatewayID + Routes.NatGatewayId: + references: + resource: NATGateway + path: Status.NATGatewayID + Routes.TransitGatewayId: + references: + resource: TransitGateway + path: Status.TransitGatewayID + Routes.VpcEndpointId: + references: + resource: VPCEndpoint + path: Status.VPCEndpointID + hooks: + delta_pre_compare: + code: customPreCompare(a, b) + sdk_create_post_set_output: + template_path: hooks/route_table/sdk_create_post_set_output.go.tpl + sdk_read_many_post_set_output: + template_path: hooks/route_table/sdk_read_many_post_set_output.go.tpl + sdk_file_end: + template_path: hooks/route_table/sdk_file_end.go.tpl + update_operation: + custom_method_name: customUpdateRouteTable SecurityGroup: fields: # support EC2-VPC only @@ -281,6 +299,9 @@ resources: update_operation: custom_method_name: customUpdate exceptions: + errors: + 404: + code: InvalidVpcID.NotFound terminal_codes: - InvalidParameterCombination fields: diff --git a/go.local.mod b/go.local.mod index 3d5cb9e0..ccc0b89d 100644 --- a/go.local.mod +++ b/go.local.mod @@ -17,6 +17,7 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.1.2 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect diff --git a/go.local.sum b/go.local.sum index 1c0bee9b..958358d4 100644 --- a/go.local.sum +++ b/go.local.sum @@ -77,6 +77,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo= +github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= diff --git a/helm/crds/ec2.services.k8s.aws_routetables.yaml b/helm/crds/ec2.services.k8s.aws_routetables.yaml index 69a77d97..dde0f3e5 100644 --- a/helm/crds/ec2.services.k8s.aws_routetables.yaml +++ b/helm/crds/ec2.services.k8s.aws_routetables.yaml @@ -52,18 +52,66 @@ spec: type: string gatewayID: type: string + gatewayRef: + description: Reference field for GatewayID + properties: + from: + description: AWSResourceReference provides all the values + necessary to reference another k8s resource for finding + the identifier(Id/ARN/Name) + properties: + name: + type: string + type: object + type: object instanceID: type: string localGatewayID: type: string natGatewayID: type: string + natGatewayRef: + description: Reference field for NATGatewayID + properties: + from: + description: AWSResourceReference provides all the values + necessary to reference another k8s resource for finding + the identifier(Id/ARN/Name) + properties: + name: + type: string + type: object + type: object networkInterfaceID: type: string transitGatewayID: type: string + transitGatewayRef: + description: Reference field for TransitGatewayID + properties: + from: + description: AWSResourceReference provides all the values + necessary to reference another k8s resource for finding + the identifier(Id/ARN/Name) + properties: + name: + type: string + type: object + type: object vpcEndpointID: type: string + vpcEndpointRef: + description: Reference field for VPCEndpointID + properties: + from: + description: AWSResourceReference provides all the values + necessary to reference another k8s resource for finding + the identifier(Id/ARN/Name) + properties: + name: + type: string + type: object + type: object vpcPeeringConnectionID: type: string type: object diff --git a/pkg/resource/route_table/delta.go b/pkg/resource/route_table/delta.go index f35909bc..a3935e76 100644 --- a/pkg/resource/route_table/delta.go +++ b/pkg/resource/route_table/delta.go @@ -40,6 +40,7 @@ func newResourceDelta( delta.Add("", a, b) return delta } + customPreCompare(a, b) if !reflect.DeepEqual(a.ko.Spec.Routes, b.ko.Spec.Routes) { delta.Add("Spec.Routes", a.ko.Spec.Routes, b.ko.Spec.Routes) diff --git a/pkg/resource/route_table/hooks.go b/pkg/resource/route_table/hooks.go index fad8de96..719dd568 100644 --- a/pkg/resource/route_table/hooks.go +++ b/pkg/resource/route_table/hooks.go @@ -46,7 +46,7 @@ func (rm *resourceManager) syncRoutes( toDelete := []*svcapitypes.CreateRouteInput{} for _, desiredRoute := range desired.ko.Spec.Routes { - if *desiredRoute.GatewayID == LocalRouteGateway { + if (*desiredRoute).GatewayID != nil && *desiredRoute.GatewayID == LocalRouteGateway { // no-op for default route continue } @@ -64,6 +64,10 @@ func (rm *resourceManager) syncRoutes( } if latest != nil { for _, latestRoute := range latest.ko.Spec.Routes { + if (*latestRoute).GatewayID != nil && *latestRoute.GatewayID == LocalRouteGateway { + // no-op for default route + continue + } if desiredRoute := getMatchingRoute(latestRoute, desired); desiredRoute == nil { // latest has a route that is not desired; therefore, delete toDelete = append(toDelete, latestRoute) @@ -226,3 +230,31 @@ func (rm *resourceManager) addRoutesToStatus( ko.Status.RouteStatuses = routesInStatus } } + +// customPreCompare ensures that default values of types are initialised and +// server side defaults are excluded from the delta. +func customPreCompare( + a *resource, + b *resource, +) { + a.ko.Spec.Routes = removeLocalRoute(a.ko.Spec.Routes) + b.ko.Spec.Routes = removeLocalRoute(b.ko.Spec.Routes) +} + +// removeLocalRoute will filter out any routes that have a gateway ID that +// matches the local gateway. Every route table contains a local route for +// communication within the VPC, which cannot be deleted or modified, and should +// not be included as part of the spec. +func removeLocalRoute( + routes []*svcapitypes.CreateRouteInput, +) (ret []*svcapitypes.CreateRouteInput) { + ret = make([]*svcapitypes.CreateRouteInput, 0) + + for _, route := range routes { + if route.GatewayID == nil || *route.GatewayID != LocalRouteGateway { + ret = append(ret, route) + } + } + + return ret +} diff --git a/pkg/resource/route_table/references.go b/pkg/resource/route_table/references.go index 30dcb4f5..5e0b3f2b 100644 --- a/pkg/resource/route_table/references.go +++ b/pkg/resource/route_table/references.go @@ -46,6 +46,18 @@ func (rm *resourceManager) ResolveReferences( namespace := res.MetaObject().GetNamespace() ko := rm.concreteResource(res).ko.DeepCopy() err := validateReferenceFields(ko) + if err == nil { + err = resolveReferenceForRoutes_GatewayID(ctx, apiReader, namespace, ko) + } + if err == nil { + err = resolveReferenceForRoutes_NATGatewayID(ctx, apiReader, namespace, ko) + } + if err == nil { + err = resolveReferenceForRoutes_TransitGatewayID(ctx, apiReader, namespace, ko) + } + if err == nil { + err = resolveReferenceForRoutes_VPCEndpointID(ctx, apiReader, namespace, ko) + } if err == nil { err = resolveReferenceForVPCID(ctx, apiReader, namespace, ko) } @@ -59,6 +71,26 @@ func (rm *resourceManager) ResolveReferences( // validateReferenceFields validates the reference field and corresponding // identifier field. func validateReferenceFields(ko *svcapitypes.RouteTable) error { + for _, iter0 := range ko.Spec.Routes { + if iter0.GatewayRef != nil && iter0.GatewayID != nil { + return ackerr.ResourceReferenceAndIDNotSupportedFor("Routes.GatewayID", "Routes.GatewayRef") + } + } + for _, iter0 := range ko.Spec.Routes { + if iter0.NATGatewayRef != nil && iter0.NATGatewayID != nil { + return ackerr.ResourceReferenceAndIDNotSupportedFor("Routes.NATGatewayID", "Routes.NATGatewayRef") + } + } + for _, iter0 := range ko.Spec.Routes { + if iter0.TransitGatewayRef != nil && iter0.TransitGatewayID != nil { + return ackerr.ResourceReferenceAndIDNotSupportedFor("Routes.TransitGatewayID", "Routes.TransitGatewayRef") + } + } + for _, iter0 := range ko.Spec.Routes { + if iter0.VPCEndpointRef != nil && iter0.VPCEndpointID != nil { + return ackerr.ResourceReferenceAndIDNotSupportedFor("Routes.VPCEndpointID", "Routes.VPCEndpointRef") + } + } if ko.Spec.VPCRef != nil && ko.Spec.VPCID != nil { return ackerr.ResourceReferenceAndIDNotSupportedFor("VPCID", "VPCRef") } @@ -71,9 +103,325 @@ func validateReferenceFields(ko *svcapitypes.RouteTable) error { // hasNonNilReferences returns true if resource contains a reference to another // resource func hasNonNilReferences(ko *svcapitypes.RouteTable) bool { + if ko.Spec.Routes != nil { + for _, iter35 := range ko.Spec.Routes { + if iter35.GatewayRef != nil { + return true + } + } + } + if ko.Spec.Routes != nil { + for _, iter38 := range ko.Spec.Routes { + if iter38.NATGatewayRef != nil { + return true + } + } + } + if ko.Spec.Routes != nil { + for _, iter40 := range ko.Spec.Routes { + if iter40.TransitGatewayRef != nil { + return true + } + } + } + if ko.Spec.Routes != nil { + for _, iter41 := range ko.Spec.Routes { + if iter41.VPCEndpointRef != nil { + return true + } + } + } return false || (ko.Spec.VPCRef != nil) } +// resolveReferenceForRoutes_GatewayID reads the resource referenced +// from Routes.GatewayRef field and sets the Routes.GatewayID +// from referenced resource +func resolveReferenceForRoutes_GatewayID( + ctx context.Context, + apiReader client.Reader, + namespace string, + ko *svcapitypes.RouteTable, +) error { + if ko.Spec.Routes == nil { + return nil + } + + if len(ko.Spec.Routes) > 0 { + for _, elem := range ko.Spec.Routes { + arrw := elem.GatewayRef + + if arrw == nil || arrw.From == nil { + continue + } + + arr := arrw.From + if arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + + if arr == nil || arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: *arr.Name, + } + obj := svcapitypes.InternetGateway{} + err := apiReader.Get(ctx, namespacedName, &obj) + if err != nil { + return err + } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + refResourceTerminal = true + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "InternetGateway", + namespace, *arr.Name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "InternetGateway", + namespace, *arr.Name) + } + if obj.Status.InternetGatewayID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "InternetGateway", + namespace, *arr.Name, + "Status.InternetGatewayID") + } + referencedValue := string(*obj.Status.InternetGatewayID) + elem.GatewayID = &referencedValue + } + } + return nil +} + +// resolveReferenceForRoutes_NATGatewayID reads the resource referenced +// from Routes.NATGatewayRef field and sets the Routes.NATGatewayID +// from referenced resource +func resolveReferenceForRoutes_NATGatewayID( + ctx context.Context, + apiReader client.Reader, + namespace string, + ko *svcapitypes.RouteTable, +) error { + if ko.Spec.Routes == nil { + return nil + } + + if len(ko.Spec.Routes) > 0 { + for _, elem := range ko.Spec.Routes { + arrw := elem.NATGatewayRef + + if arrw == nil || arrw.From == nil { + continue + } + + arr := arrw.From + if arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + + if arr == nil || arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: *arr.Name, + } + obj := svcapitypes.NATGateway{} + err := apiReader.Get(ctx, namespacedName, &obj) + if err != nil { + return err + } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + refResourceTerminal = true + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "NATGateway", + namespace, *arr.Name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "NATGateway", + namespace, *arr.Name) + } + if obj.Status.NATGatewayID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "NATGateway", + namespace, *arr.Name, + "Status.NATGatewayID") + } + referencedValue := string(*obj.Status.NATGatewayID) + elem.NATGatewayID = &referencedValue + } + } + return nil +} + +// resolveReferenceForRoutes_TransitGatewayID reads the resource referenced +// from Routes.TransitGatewayRef field and sets the Routes.TransitGatewayID +// from referenced resource +func resolveReferenceForRoutes_TransitGatewayID( + ctx context.Context, + apiReader client.Reader, + namespace string, + ko *svcapitypes.RouteTable, +) error { + if ko.Spec.Routes == nil { + return nil + } + + if len(ko.Spec.Routes) > 0 { + for _, elem := range ko.Spec.Routes { + arrw := elem.TransitGatewayRef + + if arrw == nil || arrw.From == nil { + continue + } + + arr := arrw.From + if arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + + if arr == nil || arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: *arr.Name, + } + obj := svcapitypes.TransitGateway{} + err := apiReader.Get(ctx, namespacedName, &obj) + if err != nil { + return err + } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + refResourceTerminal = true + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "TransitGateway", + namespace, *arr.Name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "TransitGateway", + namespace, *arr.Name) + } + if obj.Status.TransitGatewayID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "TransitGateway", + namespace, *arr.Name, + "Status.TransitGatewayID") + } + referencedValue := string(*obj.Status.TransitGatewayID) + elem.TransitGatewayID = &referencedValue + } + } + return nil +} + +// resolveReferenceForRoutes_VPCEndpointID reads the resource referenced +// from Routes.VPCEndpointRef field and sets the Routes.VPCEndpointID +// from referenced resource +func resolveReferenceForRoutes_VPCEndpointID( + ctx context.Context, + apiReader client.Reader, + namespace string, + ko *svcapitypes.RouteTable, +) error { + if ko.Spec.Routes == nil { + return nil + } + + if len(ko.Spec.Routes) > 0 { + for _, elem := range ko.Spec.Routes { + arrw := elem.VPCEndpointRef + + if arrw == nil || arrw.From == nil { + continue + } + + arr := arrw.From + if arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + + if arr == nil || arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: *arr.Name, + } + obj := svcapitypes.VPCEndpoint{} + err := apiReader.Get(ctx, namespacedName, &obj) + if err != nil { + return err + } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + refResourceTerminal = true + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "VPCEndpoint", + namespace, *arr.Name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "VPCEndpoint", + namespace, *arr.Name) + } + if obj.Status.VPCEndpointID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "VPCEndpoint", + namespace, *arr.Name, + "Status.VPCEndpointID") + } + referencedValue := string(*obj.Status.VPCEndpointID) + elem.VPCEndpointID = &referencedValue + } + } + return nil +} + // resolveReferenceForVPCID reads the resource referenced // from VPCRef field and sets the VPCID // from referenced resource diff --git a/pkg/resource/vpc/sdk.go b/pkg/resource/vpc/sdk.go index b0df9e09..f9169333 100644 --- a/pkg/resource/vpc/sdk.go +++ b/pkg/resource/vpc/sdk.go @@ -70,7 +70,7 @@ func (rm *resourceManager) sdkFind( resp, err = rm.sdkapi.DescribeVpcsWithContext(ctx, input) rm.metrics.RecordAPICall("READ_MANY", "DescribeVpcs", err) if err != nil { - if awsErr, ok := ackerr.AWSError(err); ok && awsErr.Code() == "UNKNOWN" { + if awsErr, ok := ackerr.AWSError(err); ok && awsErr.Code() == "InvalidVpcID.NotFound" { return nil, ackerr.NotFound } return nil, err diff --git a/test/e2e/tests/test_route_table.py b/test/e2e/tests/test_route_table.py index 1982c9ab..defeefca 100644 --- a/test/e2e/tests/test_route_table.py +++ b/test/e2e/tests/test_route_table.py @@ -160,22 +160,14 @@ def test_crud_route(self, ec2_client): ec2_validator.assert_route(resource_id, "local", "CreateRouteTable") ec2_validator.assert_route(resource_id, igw_id, "CreateRoute") - # Update Route - default_cidr = "10.0.0.0/16" + # Update the Route updated_cidr = "192.168.1.0/24" - patch = {"spec": {"routes": [ - { - #Default route cannot be changed - "destinationCIDRBlock": default_cidr, - "gatewayID": "local" - }, + patch = {"spec": {"routes":[ { "destinationCIDRBlock": updated_cidr, "gatewayID": igw_id } - ] - } - } + ]}} _ = k8s.patch_custom_resource(ref, patch) time.sleep(DEFAULT_WAIT_AFTER_SECONDS) @@ -183,37 +175,18 @@ def test_crud_route(self, ec2_client): resource = k8s.get_resource(ref) assert len(resource['status']['routeStatuses']) == 2 - # Delete Route - patch = {"spec": {"routes": [ - { - "destinationCIDRBlock": default_cidr, - "gatewayID": "local" - } - ] - } - } + # Delete the Route + patch = {"spec": {"routes": []}} _ = k8s.patch_custom_resource(ref, patch) time.sleep(DEFAULT_WAIT_AFTER_SECONDS) resource = k8s.get_resource(ref) - assert len(resource['spec']['routes']) == 1 + assert len(resource['spec']['routes']) == 0 # Route should no longer exist in AWS (default will remain) ec2_validator.assert_route(resource_id, "local", "CreateRouteTable") ec2_validator.assert_route(resource_id, igw_id, "CreateRoute", exists=False) - # Should not be able to delete default route - patch = {"spec": {"routes": [ - ] - } - } - _ = k8s.patch_custom_resource(ref, patch) - time.sleep(DEFAULT_WAIT_AFTER_SECONDS) - - expected_msg = "InvalidParameterValue: cannot remove local route" - terminal_condition = k8s.get_resource_condition(ref, "ACK.Terminal") - assert expected_msg in terminal_condition['message'] - # Delete Route Table _, deleted = k8s.delete_custom_resource(ref) assert deleted is True