diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index e6c3128c..da2fac81 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,13 +1,13 @@ ack_generate_info: - build_date: "2025-03-25T22:26:20Z" + build_date: "2025-03-28T15:21:59Z" build_hash: 3722729cebe6d3c03c7e442655ef0846f91566a2 go_version: go1.24.0 version: v0.43.2-7-g3722729 -api_directory_checksum: b31faecf6092fab9677498f3624e624fee4cbaed +api_directory_checksum: bb3f7a9b3924fdbe63a4082a9134b3f1f653979b api_version: v1alpha1 aws_sdk_go_version: v1.32.6 generator_config_info: - file_checksum: 33b2c8e790a68bd28b5efe9faa3b8e5fb980d1f4 + file_checksum: 34c308f1552184ca33451041fd787bfd9d32dc5e original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index 6013e90f..f52582b3 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -46,6 +46,8 @@ ignore: - CreateSubnetInput.Ipv6NetmaskLength - CreateSubnetInput.TagSpecifications - CreateSubnetOutput.Subnet.BlockPublicAccessStates + - CreateTransitGatewayVpcAttachmentInput.DryRun + - CreateTransitGatewayVpcAttachmentInput.TagSpecifications - CreateTransitGatewayInput.DryRun - CreateTransitGatewayInput.Options.SecurityGroupReferencingSupport - CreateTransitGatewayInput.TagSpecifications @@ -181,7 +183,7 @@ ignore: - TransitGatewayRouteTable - TransitGatewayRoute - TransitGatewayRouteTableAnnouncement - - TransitGatewayVpcAttachment + # - TransitGatewayVpcAttachment #- TransitGateway - VerifiedAccessEndpoint - VerifiedAccessGroup @@ -279,6 +281,8 @@ operations: operation_type: - Update resource_name: VpcPeeringConnection + DescribeTransitGatewayVpcAttachments: + custom_check_required_fields_missing_method: checkForMissingRequiredFields resources: CapacityReservation: fields: @@ -799,6 +803,52 @@ resources: template_path: hooks/transit_gateway/sdk_file_end.go.tpl update_operation: custom_method_name: customUpdateTransitGateway + TransitGatewayVpcAttachment: + renames: + operations: + CreateTransitGatewayVpcAttachment: + output_fields: + TransitGatewayAttachmentId: ID + DescribeTransitGatewayVpcAttachments: + input_fields: + TransitGatewayAttachmentIds: ID + output_fields: + TransitGatewayAttachmentId: ID + ModifyTransitGatewayVpcAttachment: + input_fields: + TransitGatewayAttachmentId: ID + DeleteTransitGatewayVpcAttachment: + input_fields: + TransitGatewayAttachmentId: ID + fields: + Tags: + from: + operation: CreateTags + path: Tags + SubnetIds: + references: + resource: Subnet + path: Status.SubnetID + VpcId: + references: + resource: VPC + path: Status.VPCID + TransitGatewayId: + references: + resource: TransitGateway + path: Status.TransitGatewayID + ID: + is_primary_key: true + print: + path: Status.ID + name: ID + hooks: + sdk_create_post_build_request: + template_path: hooks/transit_gateway_vpc_attachment/sdk_create_post_build_request.go.tpl + sdk_update_pre_build_request: + template_path: hooks/transit_gateway_vpc_attachment/sdk_update_pre_build_request.go.tpl + sdk_update_post_build_request: + template_path: hooks/transit_gateway_vpc_attachment/sdk_update_post_build_request.go.tpl Vpc: update_operation: custom_method_name: customUpdateVPC diff --git a/apis/v1alpha1/transit_gateway_vpc_attachment.go b/apis/v1alpha1/transit_gateway_vpc_attachment.go new file mode 100644 index 00000000..79251051 --- /dev/null +++ b/apis/v1alpha1/transit_gateway_vpc_attachment.go @@ -0,0 +1,97 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Code generated by ack-generate. DO NOT EDIT. + +package v1alpha1 + +import ( + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// TransitGatewayVpcAttachmentSpec defines the desired state of TransitGatewayVpcAttachment. +// +// Describes a VPC attachment. +type TransitGatewayVPCAttachmentSpec struct { + + // The VPC attachment options. + Options *CreateTransitGatewayVPCAttachmentRequestOptions `json:"options,omitempty"` + // The IDs of one or more subnets. You can specify only one subnet per Availability + // Zone. You must specify at least one subnet, but we recommend that you specify + // two subnets for better availability. The transit gateway uses one IP address + // from each specified subnet. + SubnetIDs []*string `json:"subnetIDs,omitempty"` + SubnetRefs []*ackv1alpha1.AWSResourceReferenceWrapper `json:"subnetRefs,omitempty"` + // The tags. The value parameter is required, but if you don't want the tag + // to have a value, specify the parameter with no value, and we set the value + // to an empty string. + Tags []*Tag `json:"tags,omitempty"` + // The ID of the transit gateway. + TransitGatewayID *string `json:"transitGatewayID,omitempty"` + TransitGatewayRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"transitGatewayRef,omitempty"` + // The ID of the VPC. + VPCID *string `json:"vpcID,omitempty"` + VPCRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"vpcRef,omitempty"` +} + +// TransitGatewayVPCAttachmentStatus defines the observed state of TransitGatewayVPCAttachment +type TransitGatewayVPCAttachmentStatus struct { + // All CRs managed by ACK have a common `Status.ACKResourceMetadata` member + // that is used to contain resource sync state, account ownership, + // constructed ARN for the resource + // +kubebuilder:validation:Optional + ACKResourceMetadata *ackv1alpha1.ResourceMetadata `json:"ackResourceMetadata"` + // All CRs managed by ACK have a common `Status.Conditions` member that + // contains a collection of `ackv1alpha1.Condition` objects that describe + // the various terminal states of the CR and its backend AWS service API + // resource + // +kubebuilder:validation:Optional + Conditions []*ackv1alpha1.Condition `json:"conditions"` + // The creation time. + // +kubebuilder:validation:Optional + CreationTime *metav1.Time `json:"creationTime,omitempty"` + // The ID of the attachment. + // +kubebuilder:validation:Optional + ID *string `json:"id,omitempty"` + // The state of the VPC attachment. Note that the initiating state has been + // deprecated. + // +kubebuilder:validation:Optional + State *string `json:"state,omitempty"` + // The ID of the Amazon Web Services account that owns the VPC. + // +kubebuilder:validation:Optional + VPCOwnerID *string `json:"vpcOwnerID,omitempty"` +} + +// TransitGatewayVPCAttachment is the Schema for the TransitGatewayVPCAttachments API +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="ID",type=string,priority=0,JSONPath=`.status.id` +type TransitGatewayVPCAttachment struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec TransitGatewayVPCAttachmentSpec `json:"spec,omitempty"` + Status TransitGatewayVPCAttachmentStatus `json:"status,omitempty"` +} + +// TransitGatewayVPCAttachmentList contains a list of TransitGatewayVPCAttachment +// +kubebuilder:object:root=true +type TransitGatewayVPCAttachmentList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []TransitGatewayVPCAttachment `json:"items"` +} + +func init() { + SchemeBuilder.Register(&TransitGatewayVPCAttachment{}, &TransitGatewayVPCAttachmentList{}) +} diff --git a/apis/v1alpha1/types.go b/apis/v1alpha1/types.go index 95d500c3..40b5cb7e 100644 --- a/apis/v1alpha1/types.go +++ b/apis/v1alpha1/types.go @@ -904,7 +904,9 @@ type CreateRouteInput struct { // Describes the options for a VPC attachment. type CreateTransitGatewayVPCAttachmentRequestOptions struct { + ApplianceModeSupport *string `json:"applianceModeSupport,omitempty"` DNSSupport *string `json:"dnsSupport,omitempty"` + IPv6Support *string `json:"ipv6Support,omitempty"` SecurityGroupReferencingSupport *string `json:"securityGroupReferencingSupport,omitempty"` } @@ -3320,7 +3322,9 @@ type ModifyTransitGatewayOptions struct { // Describes the options for a VPC attachment. type ModifyTransitGatewayVPCAttachmentRequestOptions struct { + ApplianceModeSupport *string `json:"applianceModeSupport,omitempty"` DNSSupport *string `json:"dnsSupport,omitempty"` + IPv6Support *string `json:"ipv6Support,omitempty"` SecurityGroupReferencingSupport *string `json:"securityGroupReferencingSupport,omitempty"` } @@ -5077,7 +5081,8 @@ type TrafficMirrorTarget struct { // Describes an association between a resource attachment and a transit gateway // route table. type TransitGatewayAssociation struct { - ResourceID *string `json:"resourceID,omitempty"` + ResourceID *string `json:"resourceID,omitempty"` + TransitGatewayAttachmentID *string `json:"transitGatewayAttachmentID,omitempty"` } // Describes an attachment between a resource and a transit gateway. @@ -5085,6 +5090,7 @@ type TransitGatewayAttachment struct { CreationTime *metav1.Time `json:"creationTime,omitempty"` ResourceID *string `json:"resourceID,omitempty"` ResourceOwnerID *string `json:"resourceOwnerID,omitempty"` + State *string `json:"state,omitempty"` Tags []*Tag `json:"tags,omitempty"` TransitGatewayAttachmentID *string `json:"transitGatewayAttachmentID,omitempty"` TransitGatewayID *string `json:"transitGatewayID,omitempty"` @@ -5111,15 +5117,19 @@ type TransitGatewayAttachmentPropagation struct { // Describes a transit gateway Connect attachment. type TransitGatewayConnect struct { - CreationTime *metav1.Time `json:"creationTime,omitempty"` - Tags []*Tag `json:"tags,omitempty"` - TransitGatewayID *string `json:"transitGatewayID,omitempty"` + CreationTime *metav1.Time `json:"creationTime,omitempty"` + State *string `json:"state,omitempty"` + Tags []*Tag `json:"tags,omitempty"` + TransitGatewayAttachmentID *string `json:"transitGatewayAttachmentID,omitempty"` + TransitGatewayID *string `json:"transitGatewayID,omitempty"` + TransportTransitGatewayAttachmentID *string `json:"transportTransitGatewayAttachmentID,omitempty"` } // Describes a transit gateway Connect peer. type TransitGatewayConnectPeer struct { - CreationTime *metav1.Time `json:"creationTime,omitempty"` - Tags []*Tag `json:"tags,omitempty"` + CreationTime *metav1.Time `json:"creationTime,omitempty"` + Tags []*Tag `json:"tags,omitempty"` + TransitGatewayAttachmentID *string `json:"transitGatewayAttachmentID,omitempty"` } // Describes the Connect peer details. @@ -5217,6 +5227,7 @@ type TransitGatewayOptions struct { type TransitGatewayPeeringAttachment struct { AccepterTransitGatewayAttachmentID *string `json:"accepterTransitGatewayAttachmentID,omitempty"` CreationTime *metav1.Time `json:"creationTime,omitempty"` + State *string `json:"state,omitempty"` Tags []*Tag `json:"tags,omitempty"` TransitGatewayAttachmentID *string `json:"transitGatewayAttachmentID,omitempty"` } @@ -5245,7 +5256,8 @@ type TransitGatewayPolicyTable struct { // Describes a transit gateway policy table association. type TransitGatewayPolicyTableAssociation struct { - ResourceID *string `json:"resourceID,omitempty"` + ResourceID *string `json:"resourceID,omitempty"` + TransitGatewayAttachmentID *string `json:"transitGatewayAttachmentID,omitempty"` } // Describes a transit gateway policy table entry @@ -5255,7 +5267,8 @@ type TransitGatewayPolicyTableEntry struct { // Describes a transit gateway prefix list attachment. type TransitGatewayPrefixListAttachment struct { - ResourceID *string `json:"resourceID,omitempty"` + ResourceID *string `json:"resourceID,omitempty"` + TransitGatewayAttachmentID *string `json:"transitGatewayAttachmentID,omitempty"` } // Describes a prefix list reference. @@ -5268,6 +5281,7 @@ type TransitGatewayPrefixListReference struct { // Describes route propagation. type TransitGatewayPropagation struct { ResourceID *string `json:"resourceID,omitempty"` + TransitGatewayAttachmentID *string `json:"transitGatewayAttachmentID,omitempty"` TransitGatewayRouteTableID *string `json:"transitGatewayRouteTableID,omitempty"` } @@ -5311,6 +5325,7 @@ type TransitGatewayRouteTableAnnouncement struct { CreationTime *metav1.Time `json:"creationTime,omitempty"` PeerCoreNetworkID *string `json:"peerCoreNetworkID,omitempty"` PeerTransitGatewayID *string `json:"peerTransitGatewayID,omitempty"` + PeeringAttachmentID *string `json:"peeringAttachmentID,omitempty"` Tags []*Tag `json:"tags,omitempty"` TransitGatewayID *string `json:"transitGatewayID,omitempty"` } @@ -5338,23 +5353,28 @@ type TransitGatewayRouteTableRoute struct { State *string `json:"state,omitempty"` } -// Describes a VPC attachment. -type TransitGatewayVPCAttachment struct { - CreationTime *metav1.Time `json:"creationTime,omitempty"` - SubnetIDs []*string `json:"subnetIDs,omitempty"` - Tags []*Tag `json:"tags,omitempty"` - TransitGatewayAttachmentID *string `json:"transitGatewayAttachmentID,omitempty"` - TransitGatewayID *string `json:"transitGatewayID,omitempty"` - VPCID *string `json:"vpcID,omitempty"` - VPCOwnerID *string `json:"vpcOwnerID,omitempty"` -} - // Describes the VPC attachment options. type TransitGatewayVPCAttachmentOptions struct { + ApplianceModeSupport *string `json:"applianceModeSupport,omitempty"` DNSSupport *string `json:"dnsSupport,omitempty"` + IPv6Support *string `json:"ipv6Support,omitempty"` SecurityGroupReferencingSupport *string `json:"securityGroupReferencingSupport,omitempty"` } +// Describes a VPC attachment. +type TransitGatewayVPCAttachment_SDK struct { + CreationTime *metav1.Time `json:"creationTime,omitempty"` + // Describes the VPC attachment options. + Options *TransitGatewayVPCAttachmentOptions `json:"options,omitempty"` + State *string `json:"state,omitempty"` + SubnetIDs []*string `json:"subnetIDs,omitempty"` + Tags []*Tag `json:"tags,omitempty"` + TransitGatewayAttachmentID *string `json:"transitGatewayAttachmentID,omitempty"` + TransitGatewayID *string `json:"transitGatewayID,omitempty"` + VPCID *string `json:"vpcID,omitempty"` + VPCOwnerID *string `json:"vpcOwnerID,omitempty"` +} + // Describes a transit gateway. type TransitGateway_SDK struct { CreationTime *metav1.Time `json:"creationTime,omitempty"` @@ -5687,13 +5707,14 @@ type VPNConnectionOptions struct { // Describes VPN connection options. type VPNConnectionOptionsSpecification struct { - EnableAcceleration *bool `json:"enableAcceleration,omitempty"` - LocalIPv4NetworkCIDR *string `json:"localIPv4NetworkCIDR,omitempty"` - LocalIPv6NetworkCIDR *string `json:"localIPv6NetworkCIDR,omitempty"` - OutsideIPAddressType *string `json:"outsideIPAddressType,omitempty"` - RemoteIPv4NetworkCIDR *string `json:"remoteIPv4NetworkCIDR,omitempty"` - RemoteIPv6NetworkCIDR *string `json:"remoteIPv6NetworkCIDR,omitempty"` - StaticRoutesOnly *bool `json:"staticRoutesOnly,omitempty"` + EnableAcceleration *bool `json:"enableAcceleration,omitempty"` + LocalIPv4NetworkCIDR *string `json:"localIPv4NetworkCIDR,omitempty"` + LocalIPv6NetworkCIDR *string `json:"localIPv6NetworkCIDR,omitempty"` + OutsideIPAddressType *string `json:"outsideIPAddressType,omitempty"` + RemoteIPv4NetworkCIDR *string `json:"remoteIPv4NetworkCIDR,omitempty"` + RemoteIPv6NetworkCIDR *string `json:"remoteIPv6NetworkCIDR,omitempty"` + StaticRoutesOnly *bool `json:"staticRoutesOnly,omitempty"` + TransportTransitGatewayAttachmentID *string `json:"transportTransitGatewayAttachmentID,omitempty"` } // Describes a virtual private gateway. diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index a0949756..f64c646b 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -3723,11 +3723,21 @@ func (in *CreateRouteInput) DeepCopy() *CreateRouteInput { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CreateTransitGatewayVPCAttachmentRequestOptions) DeepCopyInto(out *CreateTransitGatewayVPCAttachmentRequestOptions) { *out = *in + if in.ApplianceModeSupport != nil { + in, out := &in.ApplianceModeSupport, &out.ApplianceModeSupport + *out = new(string) + **out = **in + } if in.DNSSupport != nil { in, out := &in.DNSSupport, &out.DNSSupport *out = new(string) **out = **in } + if in.IPv6Support != nil { + in, out := &in.IPv6Support, &out.IPv6Support + *out = new(string) + **out = **in + } if in.SecurityGroupReferencingSupport != nil { in, out := &in.SecurityGroupReferencingSupport, &out.SecurityGroupReferencingSupport *out = new(string) @@ -14518,11 +14528,21 @@ func (in *ModifyTransitGatewayOptions) DeepCopy() *ModifyTransitGatewayOptions { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ModifyTransitGatewayVPCAttachmentRequestOptions) DeepCopyInto(out *ModifyTransitGatewayVPCAttachmentRequestOptions) { *out = *in + if in.ApplianceModeSupport != nil { + in, out := &in.ApplianceModeSupport, &out.ApplianceModeSupport + *out = new(string) + **out = **in + } if in.DNSSupport != nil { in, out := &in.DNSSupport, &out.DNSSupport *out = new(string) **out = **in } + if in.IPv6Support != nil { + in, out := &in.IPv6Support, &out.IPv6Support + *out = new(string) + **out = **in + } if in.SecurityGroupReferencingSupport != nil { in, out := &in.SecurityGroupReferencingSupport, &out.SecurityGroupReferencingSupport *out = new(string) @@ -23059,6 +23079,11 @@ func (in *TransitGatewayAssociation) DeepCopyInto(out *TransitGatewayAssociation *out = new(string) **out = **in } + if in.TransitGatewayAttachmentID != nil { + in, out := &in.TransitGatewayAttachmentID, &out.TransitGatewayAttachmentID + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransitGatewayAssociation. @@ -23088,6 +23113,11 @@ func (in *TransitGatewayAttachment) DeepCopyInto(out *TransitGatewayAttachment) *out = new(string) **out = **in } + if in.State != nil { + in, out := &in.State, &out.State + *out = new(string) + **out = **in + } if in.Tags != nil { in, out := &in.Tags, &out.Tags *out = make([]*Tag, len(*in)) @@ -23208,6 +23238,11 @@ func (in *TransitGatewayConnect) DeepCopyInto(out *TransitGatewayConnect) { in, out := &in.CreationTime, &out.CreationTime *out = (*in).DeepCopy() } + if in.State != nil { + in, out := &in.State, &out.State + *out = new(string) + **out = **in + } if in.Tags != nil { in, out := &in.Tags, &out.Tags *out = make([]*Tag, len(*in)) @@ -23219,11 +23254,21 @@ func (in *TransitGatewayConnect) DeepCopyInto(out *TransitGatewayConnect) { } } } + if in.TransitGatewayAttachmentID != nil { + in, out := &in.TransitGatewayAttachmentID, &out.TransitGatewayAttachmentID + *out = new(string) + **out = **in + } if in.TransitGatewayID != nil { in, out := &in.TransitGatewayID, &out.TransitGatewayID *out = new(string) **out = **in } + if in.TransportTransitGatewayAttachmentID != nil { + in, out := &in.TransportTransitGatewayAttachmentID, &out.TransportTransitGatewayAttachmentID + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransitGatewayConnect. @@ -23254,6 +23299,11 @@ func (in *TransitGatewayConnectPeer) DeepCopyInto(out *TransitGatewayConnectPeer } } } + if in.TransitGatewayAttachmentID != nil { + in, out := &in.TransitGatewayAttachmentID, &out.TransitGatewayAttachmentID + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransitGatewayConnectPeer. @@ -23745,6 +23795,11 @@ func (in *TransitGatewayPeeringAttachment) DeepCopyInto(out *TransitGatewayPeeri in, out := &in.CreationTime, &out.CreationTime *out = (*in).DeepCopy() } + if in.State != nil { + in, out := &in.State, &out.State + *out = new(string) + **out = **in + } if in.Tags != nil { in, out := &in.Tags, &out.Tags *out = make([]*Tag, len(*in)) @@ -23881,6 +23936,11 @@ func (in *TransitGatewayPolicyTableAssociation) DeepCopyInto(out *TransitGateway *out = new(string) **out = **in } + if in.TransitGatewayAttachmentID != nil { + in, out := &in.TransitGatewayAttachmentID, &out.TransitGatewayAttachmentID + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransitGatewayPolicyTableAssociation. @@ -23921,6 +23981,11 @@ func (in *TransitGatewayPrefixListAttachment) DeepCopyInto(out *TransitGatewayPr *out = new(string) **out = **in } + if in.TransitGatewayAttachmentID != nil { + in, out := &in.TransitGatewayAttachmentID, &out.TransitGatewayAttachmentID + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransitGatewayPrefixListAttachment. @@ -23971,6 +24036,11 @@ func (in *TransitGatewayPropagation) DeepCopyInto(out *TransitGatewayPropagation *out = new(string) **out = **in } + if in.TransitGatewayAttachmentID != nil { + in, out := &in.TransitGatewayAttachmentID, &out.TransitGatewayAttachmentID + *out = new(string) + **out = **in + } if in.TransitGatewayRouteTableID != nil { in, out := &in.TransitGatewayRouteTableID, &out.TransitGatewayRouteTableID *out = new(string) @@ -24171,6 +24241,11 @@ func (in *TransitGatewayRouteTableAnnouncement) DeepCopyInto(out *TransitGateway *out = new(string) **out = **in } + if in.PeeringAttachmentID != nil { + in, out := &in.PeeringAttachmentID, &out.PeeringAttachmentID + *out = new(string) + **out = **in + } if in.Tags != nil { in, out := &in.Tags, &out.Tags *out = make([]*Tag, len(*in)) @@ -24388,9 +24463,104 @@ func (in *TransitGatewayStatus) DeepCopy() *TransitGatewayStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TransitGatewayVPCAttachment) DeepCopyInto(out *TransitGatewayVPCAttachment) { *out = *in - if in.CreationTime != nil { - in, out := &in.CreationTime, &out.CreationTime - *out = (*in).DeepCopy() + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransitGatewayVPCAttachment. +func (in *TransitGatewayVPCAttachment) DeepCopy() *TransitGatewayVPCAttachment { + if in == nil { + return nil + } + out := new(TransitGatewayVPCAttachment) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TransitGatewayVPCAttachment) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TransitGatewayVPCAttachmentList) DeepCopyInto(out *TransitGatewayVPCAttachmentList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]TransitGatewayVPCAttachment, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransitGatewayVPCAttachmentList. +func (in *TransitGatewayVPCAttachmentList) DeepCopy() *TransitGatewayVPCAttachmentList { + if in == nil { + return nil + } + out := new(TransitGatewayVPCAttachmentList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TransitGatewayVPCAttachmentList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TransitGatewayVPCAttachmentOptions) DeepCopyInto(out *TransitGatewayVPCAttachmentOptions) { + *out = *in + if in.ApplianceModeSupport != nil { + in, out := &in.ApplianceModeSupport, &out.ApplianceModeSupport + *out = new(string) + **out = **in + } + if in.DNSSupport != nil { + in, out := &in.DNSSupport, &out.DNSSupport + *out = new(string) + **out = **in + } + if in.IPv6Support != nil { + in, out := &in.IPv6Support, &out.IPv6Support + *out = new(string) + **out = **in + } + if in.SecurityGroupReferencingSupport != nil { + in, out := &in.SecurityGroupReferencingSupport, &out.SecurityGroupReferencingSupport + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransitGatewayVPCAttachmentOptions. +func (in *TransitGatewayVPCAttachmentOptions) DeepCopy() *TransitGatewayVPCAttachmentOptions { + if in == nil { + return nil + } + out := new(TransitGatewayVPCAttachmentOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TransitGatewayVPCAttachmentSpec) DeepCopyInto(out *TransitGatewayVPCAttachmentSpec) { + *out = *in + if in.Options != nil { + in, out := &in.Options, &out.Options + *out = new(CreateTransitGatewayVPCAttachmentRequestOptions) + (*in).DeepCopyInto(*out) } if in.SubnetIDs != nil { in, out := &in.SubnetIDs, &out.SubnetIDs @@ -24403,6 +24573,17 @@ func (in *TransitGatewayVPCAttachment) DeepCopyInto(out *TransitGatewayVPCAttach } } } + if in.SubnetRefs != nil { + in, out := &in.SubnetRefs, &out.SubnetRefs + *out = make([]*corev1alpha1.AWSResourceReferenceWrapper, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(corev1alpha1.AWSResourceReferenceWrapper) + (*in).DeepCopyInto(*out) + } + } + } if in.Tags != nil { in, out := &in.Tags, &out.Tags *out = make([]*Tag, len(*in)) @@ -24414,21 +24595,71 @@ func (in *TransitGatewayVPCAttachment) DeepCopyInto(out *TransitGatewayVPCAttach } } } - if in.TransitGatewayAttachmentID != nil { - in, out := &in.TransitGatewayAttachmentID, &out.TransitGatewayAttachmentID - *out = new(string) - **out = **in - } if in.TransitGatewayID != nil { in, out := &in.TransitGatewayID, &out.TransitGatewayID *out = new(string) **out = **in } + if in.TransitGatewayRef != nil { + in, out := &in.TransitGatewayRef, &out.TransitGatewayRef + *out = new(corev1alpha1.AWSResourceReferenceWrapper) + (*in).DeepCopyInto(*out) + } if in.VPCID != nil { in, out := &in.VPCID, &out.VPCID *out = new(string) **out = **in } + if in.VPCRef != nil { + in, out := &in.VPCRef, &out.VPCRef + *out = new(corev1alpha1.AWSResourceReferenceWrapper) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransitGatewayVPCAttachmentSpec. +func (in *TransitGatewayVPCAttachmentSpec) DeepCopy() *TransitGatewayVPCAttachmentSpec { + if in == nil { + return nil + } + out := new(TransitGatewayVPCAttachmentSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TransitGatewayVPCAttachmentStatus) DeepCopyInto(out *TransitGatewayVPCAttachmentStatus) { + *out = *in + if in.ACKResourceMetadata != nil { + in, out := &in.ACKResourceMetadata, &out.ACKResourceMetadata + *out = new(corev1alpha1.ResourceMetadata) + (*in).DeepCopyInto(*out) + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]*corev1alpha1.Condition, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(corev1alpha1.Condition) + (*in).DeepCopyInto(*out) + } + } + } + if in.CreationTime != nil { + in, out := &in.CreationTime, &out.CreationTime + *out = (*in).DeepCopy() + } + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(string) + **out = **in + } + if in.State != nil { + in, out := &in.State, &out.State + *out = new(string) + **out = **in + } if in.VPCOwnerID != nil { in, out := &in.VPCOwnerID, &out.VPCOwnerID *out = new(string) @@ -24436,37 +24667,83 @@ func (in *TransitGatewayVPCAttachment) DeepCopyInto(out *TransitGatewayVPCAttach } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransitGatewayVPCAttachment. -func (in *TransitGatewayVPCAttachment) DeepCopy() *TransitGatewayVPCAttachment { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransitGatewayVPCAttachmentStatus. +func (in *TransitGatewayVPCAttachmentStatus) DeepCopy() *TransitGatewayVPCAttachmentStatus { if in == nil { return nil } - out := new(TransitGatewayVPCAttachment) + out := new(TransitGatewayVPCAttachmentStatus) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TransitGatewayVPCAttachmentOptions) DeepCopyInto(out *TransitGatewayVPCAttachmentOptions) { +func (in *TransitGatewayVPCAttachment_SDK) DeepCopyInto(out *TransitGatewayVPCAttachment_SDK) { *out = *in - if in.DNSSupport != nil { - in, out := &in.DNSSupport, &out.DNSSupport + if in.CreationTime != nil { + in, out := &in.CreationTime, &out.CreationTime + *out = (*in).DeepCopy() + } + if in.Options != nil { + in, out := &in.Options, &out.Options + *out = new(TransitGatewayVPCAttachmentOptions) + (*in).DeepCopyInto(*out) + } + if in.State != nil { + in, out := &in.State, &out.State *out = new(string) **out = **in } - if in.SecurityGroupReferencingSupport != nil { - in, out := &in.SecurityGroupReferencingSupport, &out.SecurityGroupReferencingSupport + if in.SubnetIDs != nil { + in, out := &in.SubnetIDs, &out.SubnetIDs + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]*Tag, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Tag) + (*in).DeepCopyInto(*out) + } + } + } + if in.TransitGatewayAttachmentID != nil { + in, out := &in.TransitGatewayAttachmentID, &out.TransitGatewayAttachmentID + *out = new(string) + **out = **in + } + if in.TransitGatewayID != nil { + in, out := &in.TransitGatewayID, &out.TransitGatewayID + *out = new(string) + **out = **in + } + if in.VPCID != nil { + in, out := &in.VPCID, &out.VPCID + *out = new(string) + **out = **in + } + if in.VPCOwnerID != nil { + in, out := &in.VPCOwnerID, &out.VPCOwnerID *out = new(string) **out = **in } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransitGatewayVPCAttachmentOptions. -func (in *TransitGatewayVPCAttachmentOptions) DeepCopy() *TransitGatewayVPCAttachmentOptions { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransitGatewayVPCAttachment_SDK. +func (in *TransitGatewayVPCAttachment_SDK) DeepCopy() *TransitGatewayVPCAttachment_SDK { if in == nil { return nil } - out := new(TransitGatewayVPCAttachmentOptions) + out := new(TransitGatewayVPCAttachment_SDK) in.DeepCopyInto(out) return out } @@ -26888,6 +27165,11 @@ func (in *VPNConnectionOptionsSpecification) DeepCopyInto(out *VPNConnectionOpti *out = new(bool) **out = **in } + if in.TransportTransitGatewayAttachmentID != nil { + in, out := &in.TransportTransitGatewayAttachmentID, &out.TransportTransitGatewayAttachmentID + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VPNConnectionOptionsSpecification. diff --git a/cmd/controller/main.go b/cmd/controller/main.go index da00dec2..23d59d2c 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -51,6 +51,7 @@ import ( _ "github.com/aws-controllers-k8s/ec2-controller/pkg/resource/security_group" _ "github.com/aws-controllers-k8s/ec2-controller/pkg/resource/subnet" _ "github.com/aws-controllers-k8s/ec2-controller/pkg/resource/transit_gateway" + _ "github.com/aws-controllers-k8s/ec2-controller/pkg/resource/transit_gateway_vpc_attachment" _ "github.com/aws-controllers-k8s/ec2-controller/pkg/resource/vpc" _ "github.com/aws-controllers-k8s/ec2-controller/pkg/resource/vpc_endpoint" _ "github.com/aws-controllers-k8s/ec2-controller/pkg/resource/vpc_endpoint_service_configuration" diff --git a/config/crd/bases/ec2.services.k8s.aws_transitgatewayvpcattachments.yaml b/config/crd/bases/ec2.services.k8s.aws_transitgatewayvpcattachments.yaml new file mode 100644 index 00000000..9d7c4119 --- /dev/null +++ b/config/crd/bases/ec2.services.k8s.aws_transitgatewayvpcattachments.yaml @@ -0,0 +1,234 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.2 + name: transitgatewayvpcattachments.ec2.services.k8s.aws +spec: + group: ec2.services.k8s.aws + names: + kind: TransitGatewayVPCAttachment + listKind: TransitGatewayVPCAttachmentList + plural: transitgatewayvpcattachments + singular: transitgatewayvpcattachment + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.id + name: ID + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: TransitGatewayVPCAttachment is the Schema for the TransitGatewayVPCAttachments + API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + TransitGatewayVpcAttachmentSpec defines the desired state of TransitGatewayVpcAttachment. + + Describes a VPC attachment. + properties: + options: + description: The VPC attachment options. + properties: + applianceModeSupport: + type: string + dnsSupport: + type: string + ipv6Support: + type: string + securityGroupReferencingSupport: + type: string + type: object + subnetIDs: + description: |- + The IDs of one or more subnets. You can specify only one subnet per Availability + Zone. You must specify at least one subnet, but we recommend that you specify + two subnets for better availability. The transit gateway uses one IP address + from each specified subnet. + items: + type: string + type: array + subnetRefs: + items: + description: "AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference\ntype to provide more user friendly syntax + for references using 'from' field\nEx:\nAPIIDRef:\n\n\tfrom:\n\t + \ name: my-api" + 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 + namespace: + type: string + type: object + type: object + type: array + tags: + description: |- + The tags. The value parameter is required, but if you don't want the tag + to have a value, specify the parameter with no value, and we set the value + to an empty string. + items: + description: Describes a tag. + properties: + key: + type: string + value: + type: string + type: object + type: array + transitGatewayID: + description: The ID of the transit gateway. + type: string + transitGatewayRef: + description: "AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference\ntype to provide more user friendly syntax + for references using 'from' field\nEx:\nAPIIDRef:\n\n\tfrom:\n\t + \ name: my-api" + 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 + namespace: + type: string + type: object + type: object + vpcID: + description: The ID of the VPC. + type: string + vpcRef: + description: "AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference\ntype to provide more user friendly syntax + for references using 'from' field\nEx:\nAPIIDRef:\n\n\tfrom:\n\t + \ name: my-api" + 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 + namespace: + type: string + type: object + type: object + type: object + status: + description: TransitGatewayVPCAttachmentStatus defines the observed state + of TransitGatewayVPCAttachment + properties: + ackResourceMetadata: + description: |- + All CRs managed by ACK have a common `Status.ACKResourceMetadata` member + that is used to contain resource sync state, account ownership, + constructed ARN for the resource + properties: + arn: + description: |- + ARN is the Amazon Resource Name for the resource. This is a + globally-unique identifier and is set only by the ACK service controller + once the controller has orchestrated the creation of the resource OR + when it has verified that an "adopted" resource (a resource where the + ARN annotation was set by the Kubernetes user on the CR) exists and + matches the supplied CR's Spec field values. + https://github.com/aws/aws-controllers-k8s/issues/270 + type: string + ownerAccountID: + description: |- + OwnerAccountID is the AWS Account ID of the account that owns the + backend AWS service API resource. + type: string + region: + description: Region is the AWS region in which the resource exists + or will exist. + type: string + required: + - ownerAccountID + - region + type: object + conditions: + description: |- + All CRs managed by ACK have a common `Status.Conditions` member that + contains a collection of `ackv1alpha1.Condition` objects that describe + the various terminal states of the CR and its backend AWS service API + resource + items: + description: |- + Condition is the common struct used by all CRDs managed by ACK service + controllers to indicate terminal states of the CR and its backend AWS + service API resource + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type is the type of the Condition + type: string + required: + - status + - type + type: object + type: array + creationTime: + description: The creation time. + format: date-time + type: string + id: + description: The ID of the attachment. + type: string + state: + description: |- + The state of the VPC attachment. Note that the initiating state has been + deprecated. + type: string + vpcOwnerID: + description: The ID of the Amazon Web Services account that owns the + VPC. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index d671a172..ac3df0e0 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -14,6 +14,7 @@ resources: - bases/ec2.services.k8s.aws_securitygroups.yaml - bases/ec2.services.k8s.aws_subnets.yaml - bases/ec2.services.k8s.aws_transitgateways.yaml + - bases/ec2.services.k8s.aws_transitgatewayvpcattachments.yaml - bases/ec2.services.k8s.aws_vpcs.yaml - bases/ec2.services.k8s.aws_vpcendpoints.yaml - bases/ec2.services.k8s.aws_vpcendpointserviceconfigurations.yaml diff --git a/config/rbac/cluster-role-controller.yaml b/config/rbac/cluster-role-controller.yaml index fe7ee008..9f52726d 100644 --- a/config/rbac/cluster-role-controller.yaml +++ b/config/rbac/cluster-role-controller.yaml @@ -37,6 +37,7 @@ rules: - securitygroups - subnets - transitgateways + - transitgatewayvpcattachments - vpcendpoints - vpcendpointserviceconfigurations - vpcpeeringconnections @@ -64,6 +65,7 @@ rules: - securitygroups/status - subnets/status - transitgateways/status + - transitgatewayvpcattachments/status - vpcendpoints/status - vpcendpointserviceconfigurations/status - vpcpeeringconnections/status diff --git a/config/rbac/role-reader.yaml b/config/rbac/role-reader.yaml index c98f612b..4a2f83e4 100644 --- a/config/rbac/role-reader.yaml +++ b/config/rbac/role-reader.yaml @@ -21,6 +21,7 @@ rules: - securitygroups - subnets - transitgateways + - transitgatewayvpcattachments - vpcs - vpcendpoints - vpcendpointserviceconfigurations diff --git a/config/rbac/role-writer.yaml b/config/rbac/role-writer.yaml index edc3394c..d333272a 100644 --- a/config/rbac/role-writer.yaml +++ b/config/rbac/role-writer.yaml @@ -21,6 +21,7 @@ rules: - securitygroups - subnets - transitgateways + - transitgatewayvpcattachments - vpcs - vpcendpoints - vpcendpointserviceconfigurations @@ -48,6 +49,7 @@ rules: - securitygroups - subnets - transitgateways + - transitgatewayvpcattachments - vpcs - vpcendpoints - vpcendpointserviceconfigurations diff --git a/generator.yaml b/generator.yaml index 6013e90f..f52582b3 100644 --- a/generator.yaml +++ b/generator.yaml @@ -46,6 +46,8 @@ ignore: - CreateSubnetInput.Ipv6NetmaskLength - CreateSubnetInput.TagSpecifications - CreateSubnetOutput.Subnet.BlockPublicAccessStates + - CreateTransitGatewayVpcAttachmentInput.DryRun + - CreateTransitGatewayVpcAttachmentInput.TagSpecifications - CreateTransitGatewayInput.DryRun - CreateTransitGatewayInput.Options.SecurityGroupReferencingSupport - CreateTransitGatewayInput.TagSpecifications @@ -181,7 +183,7 @@ ignore: - TransitGatewayRouteTable - TransitGatewayRoute - TransitGatewayRouteTableAnnouncement - - TransitGatewayVpcAttachment + # - TransitGatewayVpcAttachment #- TransitGateway - VerifiedAccessEndpoint - VerifiedAccessGroup @@ -279,6 +281,8 @@ operations: operation_type: - Update resource_name: VpcPeeringConnection + DescribeTransitGatewayVpcAttachments: + custom_check_required_fields_missing_method: checkForMissingRequiredFields resources: CapacityReservation: fields: @@ -799,6 +803,52 @@ resources: template_path: hooks/transit_gateway/sdk_file_end.go.tpl update_operation: custom_method_name: customUpdateTransitGateway + TransitGatewayVpcAttachment: + renames: + operations: + CreateTransitGatewayVpcAttachment: + output_fields: + TransitGatewayAttachmentId: ID + DescribeTransitGatewayVpcAttachments: + input_fields: + TransitGatewayAttachmentIds: ID + output_fields: + TransitGatewayAttachmentId: ID + ModifyTransitGatewayVpcAttachment: + input_fields: + TransitGatewayAttachmentId: ID + DeleteTransitGatewayVpcAttachment: + input_fields: + TransitGatewayAttachmentId: ID + fields: + Tags: + from: + operation: CreateTags + path: Tags + SubnetIds: + references: + resource: Subnet + path: Status.SubnetID + VpcId: + references: + resource: VPC + path: Status.VPCID + TransitGatewayId: + references: + resource: TransitGateway + path: Status.TransitGatewayID + ID: + is_primary_key: true + print: + path: Status.ID + name: ID + hooks: + sdk_create_post_build_request: + template_path: hooks/transit_gateway_vpc_attachment/sdk_create_post_build_request.go.tpl + sdk_update_pre_build_request: + template_path: hooks/transit_gateway_vpc_attachment/sdk_update_pre_build_request.go.tpl + sdk_update_post_build_request: + template_path: hooks/transit_gateway_vpc_attachment/sdk_update_post_build_request.go.tpl Vpc: update_operation: custom_method_name: customUpdateVPC diff --git a/helm/crds/ec2.services.k8s.aws_transitgatewayvpcattachments.yaml b/helm/crds/ec2.services.k8s.aws_transitgatewayvpcattachments.yaml new file mode 100644 index 00000000..9d7c4119 --- /dev/null +++ b/helm/crds/ec2.services.k8s.aws_transitgatewayvpcattachments.yaml @@ -0,0 +1,234 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.2 + name: transitgatewayvpcattachments.ec2.services.k8s.aws +spec: + group: ec2.services.k8s.aws + names: + kind: TransitGatewayVPCAttachment + listKind: TransitGatewayVPCAttachmentList + plural: transitgatewayvpcattachments + singular: transitgatewayvpcattachment + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.id + name: ID + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: TransitGatewayVPCAttachment is the Schema for the TransitGatewayVPCAttachments + API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + TransitGatewayVpcAttachmentSpec defines the desired state of TransitGatewayVpcAttachment. + + Describes a VPC attachment. + properties: + options: + description: The VPC attachment options. + properties: + applianceModeSupport: + type: string + dnsSupport: + type: string + ipv6Support: + type: string + securityGroupReferencingSupport: + type: string + type: object + subnetIDs: + description: |- + The IDs of one or more subnets. You can specify only one subnet per Availability + Zone. You must specify at least one subnet, but we recommend that you specify + two subnets for better availability. The transit gateway uses one IP address + from each specified subnet. + items: + type: string + type: array + subnetRefs: + items: + description: "AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference\ntype to provide more user friendly syntax + for references using 'from' field\nEx:\nAPIIDRef:\n\n\tfrom:\n\t + \ name: my-api" + 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 + namespace: + type: string + type: object + type: object + type: array + tags: + description: |- + The tags. The value parameter is required, but if you don't want the tag + to have a value, specify the parameter with no value, and we set the value + to an empty string. + items: + description: Describes a tag. + properties: + key: + type: string + value: + type: string + type: object + type: array + transitGatewayID: + description: The ID of the transit gateway. + type: string + transitGatewayRef: + description: "AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference\ntype to provide more user friendly syntax + for references using 'from' field\nEx:\nAPIIDRef:\n\n\tfrom:\n\t + \ name: my-api" + 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 + namespace: + type: string + type: object + type: object + vpcID: + description: The ID of the VPC. + type: string + vpcRef: + description: "AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference\ntype to provide more user friendly syntax + for references using 'from' field\nEx:\nAPIIDRef:\n\n\tfrom:\n\t + \ name: my-api" + 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 + namespace: + type: string + type: object + type: object + type: object + status: + description: TransitGatewayVPCAttachmentStatus defines the observed state + of TransitGatewayVPCAttachment + properties: + ackResourceMetadata: + description: |- + All CRs managed by ACK have a common `Status.ACKResourceMetadata` member + that is used to contain resource sync state, account ownership, + constructed ARN for the resource + properties: + arn: + description: |- + ARN is the Amazon Resource Name for the resource. This is a + globally-unique identifier and is set only by the ACK service controller + once the controller has orchestrated the creation of the resource OR + when it has verified that an "adopted" resource (a resource where the + ARN annotation was set by the Kubernetes user on the CR) exists and + matches the supplied CR's Spec field values. + https://github.com/aws/aws-controllers-k8s/issues/270 + type: string + ownerAccountID: + description: |- + OwnerAccountID is the AWS Account ID of the account that owns the + backend AWS service API resource. + type: string + region: + description: Region is the AWS region in which the resource exists + or will exist. + type: string + required: + - ownerAccountID + - region + type: object + conditions: + description: |- + All CRs managed by ACK have a common `Status.Conditions` member that + contains a collection of `ackv1alpha1.Condition` objects that describe + the various terminal states of the CR and its backend AWS service API + resource + items: + description: |- + Condition is the common struct used by all CRDs managed by ACK service + controllers to indicate terminal states of the CR and its backend AWS + service API resource + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type is the type of the Condition + type: string + required: + - status + - type + type: object + type: array + creationTime: + description: The creation time. + format: date-time + type: string + id: + description: The ID of the attachment. + type: string + state: + description: |- + The state of the VPC attachment. Note that the initiating state has been + deprecated. + type: string + vpcOwnerID: + description: The ID of the Amazon Web Services account that owns the + VPC. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl index f0e38abb..20e2a7c1 100644 --- a/helm/templates/_helpers.tpl +++ b/helm/templates/_helpers.tpl @@ -84,6 +84,7 @@ rules: - securitygroups - subnets - transitgateways + - transitgatewayvpcattachments - vpcendpoints - vpcendpointserviceconfigurations - vpcpeeringconnections @@ -111,6 +112,7 @@ rules: - securitygroups/status - subnets/status - transitgateways/status + - transitgatewayvpcattachments/status - vpcendpoints/status - vpcendpointserviceconfigurations/status - vpcpeeringconnections/status diff --git a/helm/templates/role-reader.yaml b/helm/templates/role-reader.yaml index c1f0e595..3763752b 100644 --- a/helm/templates/role-reader.yaml +++ b/helm/templates/role-reader.yaml @@ -21,6 +21,7 @@ rules: - securitygroups - subnets - transitgateways + - transitgatewayvpcattachments - vpcs - vpcendpoints - vpcendpointserviceconfigurations diff --git a/helm/templates/role-writer.yaml b/helm/templates/role-writer.yaml index 7fa983db..004cd28a 100644 --- a/helm/templates/role-writer.yaml +++ b/helm/templates/role-writer.yaml @@ -21,6 +21,7 @@ rules: - securitygroups - subnets - transitgateways + - transitgatewayvpcattachments - vpcs - vpcendpoints - vpcendpointserviceconfigurations @@ -48,6 +49,7 @@ rules: - securitygroups - subnets - transitgateways + - transitgatewayvpcattachments - vpcs - vpcendpoints - vpcendpointserviceconfigurations diff --git a/pkg/resource/transit_gateway_vpc_attachment/delta.go b/pkg/resource/transit_gateway_vpc_attachment/delta.go new file mode 100644 index 00000000..38005f67 --- /dev/null +++ b/pkg/resource/transit_gateway_vpc_attachment/delta.go @@ -0,0 +1,115 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Code generated by ack-generate. DO NOT EDIT. + +package transit_gateway_vpc_attachment + +import ( + "bytes" + "reflect" + + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" +) + +// Hack to avoid import errors during build... +var ( + _ = &bytes.Buffer{} + _ = &reflect.Method{} + _ = &acktags.Tags{} +) + +// newResourceDelta returns a new `ackcompare.Delta` used to compare two +// resources +func newResourceDelta( + a *resource, + b *resource, +) *ackcompare.Delta { + delta := ackcompare.NewDelta() + if (a == nil && b != nil) || + (a != nil && b == nil) { + delta.Add("", a, b) + return delta + } + + if ackcompare.HasNilDifference(a.ko.Spec.Options, b.ko.Spec.Options) { + delta.Add("Spec.Options", a.ko.Spec.Options, b.ko.Spec.Options) + } else if a.ko.Spec.Options != nil && b.ko.Spec.Options != nil { + if ackcompare.HasNilDifference(a.ko.Spec.Options.ApplianceModeSupport, b.ko.Spec.Options.ApplianceModeSupport) { + delta.Add("Spec.Options.ApplianceModeSupport", a.ko.Spec.Options.ApplianceModeSupport, b.ko.Spec.Options.ApplianceModeSupport) + } else if a.ko.Spec.Options.ApplianceModeSupport != nil && b.ko.Spec.Options.ApplianceModeSupport != nil { + if *a.ko.Spec.Options.ApplianceModeSupport != *b.ko.Spec.Options.ApplianceModeSupport { + delta.Add("Spec.Options.ApplianceModeSupport", a.ko.Spec.Options.ApplianceModeSupport, b.ko.Spec.Options.ApplianceModeSupport) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.Options.DNSSupport, b.ko.Spec.Options.DNSSupport) { + delta.Add("Spec.Options.DNSSupport", a.ko.Spec.Options.DNSSupport, b.ko.Spec.Options.DNSSupport) + } else if a.ko.Spec.Options.DNSSupport != nil && b.ko.Spec.Options.DNSSupport != nil { + if *a.ko.Spec.Options.DNSSupport != *b.ko.Spec.Options.DNSSupport { + delta.Add("Spec.Options.DNSSupport", a.ko.Spec.Options.DNSSupport, b.ko.Spec.Options.DNSSupport) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.Options.IPv6Support, b.ko.Spec.Options.IPv6Support) { + delta.Add("Spec.Options.IPv6Support", a.ko.Spec.Options.IPv6Support, b.ko.Spec.Options.IPv6Support) + } else if a.ko.Spec.Options.IPv6Support != nil && b.ko.Spec.Options.IPv6Support != nil { + if *a.ko.Spec.Options.IPv6Support != *b.ko.Spec.Options.IPv6Support { + delta.Add("Spec.Options.IPv6Support", a.ko.Spec.Options.IPv6Support, b.ko.Spec.Options.IPv6Support) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.Options.SecurityGroupReferencingSupport, b.ko.Spec.Options.SecurityGroupReferencingSupport) { + delta.Add("Spec.Options.SecurityGroupReferencingSupport", a.ko.Spec.Options.SecurityGroupReferencingSupport, b.ko.Spec.Options.SecurityGroupReferencingSupport) + } else if a.ko.Spec.Options.SecurityGroupReferencingSupport != nil && b.ko.Spec.Options.SecurityGroupReferencingSupport != nil { + if *a.ko.Spec.Options.SecurityGroupReferencingSupport != *b.ko.Spec.Options.SecurityGroupReferencingSupport { + delta.Add("Spec.Options.SecurityGroupReferencingSupport", a.ko.Spec.Options.SecurityGroupReferencingSupport, b.ko.Spec.Options.SecurityGroupReferencingSupport) + } + } + } + if len(a.ko.Spec.SubnetIDs) != len(b.ko.Spec.SubnetIDs) { + delta.Add("Spec.SubnetIDs", a.ko.Spec.SubnetIDs, b.ko.Spec.SubnetIDs) + } else if len(a.ko.Spec.SubnetIDs) > 0 { + if !ackcompare.SliceStringPEqual(a.ko.Spec.SubnetIDs, b.ko.Spec.SubnetIDs) { + delta.Add("Spec.SubnetIDs", a.ko.Spec.SubnetIDs, b.ko.Spec.SubnetIDs) + } + } + if !reflect.DeepEqual(a.ko.Spec.SubnetRefs, b.ko.Spec.SubnetRefs) { + delta.Add("Spec.SubnetRefs", a.ko.Spec.SubnetRefs, b.ko.Spec.SubnetRefs) + } + desiredACKTags, _ := convertToOrderedACKTags(a.ko.Spec.Tags) + latestACKTags, _ := convertToOrderedACKTags(b.ko.Spec.Tags) + if !ackcompare.MapStringStringEqual(desiredACKTags, latestACKTags) { + delta.Add("Spec.Tags", a.ko.Spec.Tags, b.ko.Spec.Tags) + } + if ackcompare.HasNilDifference(a.ko.Spec.TransitGatewayID, b.ko.Spec.TransitGatewayID) { + delta.Add("Spec.TransitGatewayID", a.ko.Spec.TransitGatewayID, b.ko.Spec.TransitGatewayID) + } else if a.ko.Spec.TransitGatewayID != nil && b.ko.Spec.TransitGatewayID != nil { + if *a.ko.Spec.TransitGatewayID != *b.ko.Spec.TransitGatewayID { + delta.Add("Spec.TransitGatewayID", a.ko.Spec.TransitGatewayID, b.ko.Spec.TransitGatewayID) + } + } + if !reflect.DeepEqual(a.ko.Spec.TransitGatewayRef, b.ko.Spec.TransitGatewayRef) { + delta.Add("Spec.TransitGatewayRef", a.ko.Spec.TransitGatewayRef, b.ko.Spec.TransitGatewayRef) + } + if ackcompare.HasNilDifference(a.ko.Spec.VPCID, b.ko.Spec.VPCID) { + delta.Add("Spec.VPCID", a.ko.Spec.VPCID, b.ko.Spec.VPCID) + } else if a.ko.Spec.VPCID != nil && b.ko.Spec.VPCID != nil { + if *a.ko.Spec.VPCID != *b.ko.Spec.VPCID { + delta.Add("Spec.VPCID", a.ko.Spec.VPCID, b.ko.Spec.VPCID) + } + } + if !reflect.DeepEqual(a.ko.Spec.VPCRef, b.ko.Spec.VPCRef) { + delta.Add("Spec.VPCRef", a.ko.Spec.VPCRef, b.ko.Spec.VPCRef) + } + + return delta +} diff --git a/pkg/resource/transit_gateway_vpc_attachment/descriptor.go b/pkg/resource/transit_gateway_vpc_attachment/descriptor.go new file mode 100644 index 00000000..3e84744e --- /dev/null +++ b/pkg/resource/transit_gateway_vpc_attachment/descriptor.go @@ -0,0 +1,155 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Code generated by ack-generate. DO NOT EDIT. + +package transit_gateway_vpc_attachment + +import ( + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + rtclient "sigs.k8s.io/controller-runtime/pkg/client" + k8sctrlutil "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + svcapitypes "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1" +) + +const ( + FinalizerString = "finalizers.ec2.services.k8s.aws/TransitGatewayVPCAttachment" +) + +var ( + GroupVersionResource = svcapitypes.GroupVersion.WithResource("transitgatewayvpcattachments") + GroupKind = metav1.GroupKind{ + Group: "ec2.services.k8s.aws", + Kind: "TransitGatewayVPCAttachment", + } +) + +// resourceDescriptor implements the +// `aws-service-operator-k8s/pkg/types.AWSResourceDescriptor` interface +type resourceDescriptor struct { +} + +// GroupVersionKind returns a Kubernetes schema.GroupVersionKind struct that +// describes the API Group, Version and Kind of CRs described by the descriptor +func (d *resourceDescriptor) GroupVersionKind() schema.GroupVersionKind { + return svcapitypes.GroupVersion.WithKind(GroupKind.Kind) +} + +// EmptyRuntimeObject returns an empty object prototype that may be used in +// apimachinery and k8s client operations +func (d *resourceDescriptor) EmptyRuntimeObject() rtclient.Object { + return &svcapitypes.TransitGatewayVPCAttachment{} +} + +// ResourceFromRuntimeObject returns an AWSResource that has been initialized +// with the supplied runtime.Object +func (d *resourceDescriptor) ResourceFromRuntimeObject( + obj rtclient.Object, +) acktypes.AWSResource { + return &resource{ + ko: obj.(*svcapitypes.TransitGatewayVPCAttachment), + } +} + +// Delta returns an `ackcompare.Delta` object containing the difference between +// one `AWSResource` and another. +func (d *resourceDescriptor) Delta(a, b acktypes.AWSResource) *ackcompare.Delta { + return newResourceDelta(a.(*resource), b.(*resource)) +} + +// IsManaged returns true if the supplied AWSResource is under the management +// of an ACK service controller. What this means in practice is that the +// underlying custom resource (CR) in the AWSResource has had a +// resource-specific finalizer associated with it. +func (d *resourceDescriptor) IsManaged( + res acktypes.AWSResource, +) bool { + obj := res.RuntimeObject() + if obj == nil { + // Should not happen. If it does, there is a bug in the code + panic("nil RuntimeMetaObject in AWSResource") + } + // Remove use of custom code once + // https://github.com/kubernetes-sigs/controller-runtime/issues/994 is + // fixed. This should be able to be: + // + // return k8sctrlutil.ContainsFinalizer(obj, FinalizerString) + return containsFinalizer(obj, FinalizerString) +} + +// Remove once https://github.com/kubernetes-sigs/controller-runtime/issues/994 +// is fixed. +func containsFinalizer(obj rtclient.Object, finalizer string) bool { + f := obj.GetFinalizers() + for _, e := range f { + if e == finalizer { + return true + } + } + return false +} + +// MarkManaged places the supplied resource under the management of ACK. What +// this typically means is that the resource manager will decorate the +// underlying custom resource (CR) with a finalizer that indicates ACK is +// managing the resource and the underlying CR may not be deleted until ACK is +// finished cleaning up any backend AWS service resources associated with the +// CR. +func (d *resourceDescriptor) MarkManaged( + res acktypes.AWSResource, +) { + obj := res.RuntimeObject() + if obj == nil { + // Should not happen. If it does, there is a bug in the code + panic("nil RuntimeMetaObject in AWSResource") + } + k8sctrlutil.AddFinalizer(obj, FinalizerString) +} + +// MarkUnmanaged removes the supplied resource from management by ACK. What +// this typically means is that the resource manager will remove a finalizer +// underlying custom resource (CR) that indicates ACK is managing the resource. +// This will allow the Kubernetes API server to delete the underlying CR. +func (d *resourceDescriptor) MarkUnmanaged( + res acktypes.AWSResource, +) { + obj := res.RuntimeObject() + if obj == nil { + // Should not happen. If it does, there is a bug in the code + panic("nil RuntimeMetaObject in AWSResource") + } + k8sctrlutil.RemoveFinalizer(obj, FinalizerString) +} + +// MarkAdopted places descriptors on the custom resource that indicate the +// resource was not created from within ACK. +func (d *resourceDescriptor) MarkAdopted( + res acktypes.AWSResource, +) { + obj := res.RuntimeObject() + if obj == nil { + // Should not happen. If it does, there is a bug in the code + panic("nil RuntimeObject in AWSResource") + } + curr := obj.GetAnnotations() + if curr == nil { + curr = make(map[string]string) + } + curr[ackv1alpha1.AnnotationAdopted] = "true" + obj.SetAnnotations(curr) +} diff --git a/pkg/resource/transit_gateway_vpc_attachment/hooks.go b/pkg/resource/transit_gateway_vpc_attachment/hooks.go new file mode 100644 index 00000000..96e01777 --- /dev/null +++ b/pkg/resource/transit_gateway_vpc_attachment/hooks.go @@ -0,0 +1,104 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +package transit_gateway_vpc_attachment + +import ( + "fmt" + + ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue" + "github.com/aws/aws-sdk-go-v2/aws" + svcsdk "github.com/aws/aws-sdk-go-v2/service/ec2" + svcsdktypes "github.com/aws/aws-sdk-go-v2/service/ec2/types" + + "github.com/aws-controllers-k8s/ec2-controller/pkg/tags" +) + +var StatusAvailable = svcsdktypes.TransitGatewayAttachmentStateAvailable + +// requeueWaitUntilCanModify returns a `ackrequeue.RequeueNeededAfter` struct +// explaining the cluster cannot be modified until it reaches an active status. +func requeueWaitUntilCanModify(r *resource) *ackrequeue.RequeueNeeded { + if r.ko.Status.State == nil { + return nil + } + status := *r.ko.Status.State + return ackrequeue.Needed( + fmt.Errorf("transitGatewayAttachment is in '%s' and state, cannot be modified until '%s'", + status, StatusAvailable), + ) +} + +// updateTagSpecificationsInCreateRequest adds +// Tags defined in the Spec to CreateDhcpOptionsInput.TagSpecification +// and ensures the ResourceType is always set to 'transit-gateway-vpc-attachment' +func updateTagSpecificationsInCreateRequest(r *resource, + input *svcsdk.CreateTransitGatewayVpcAttachmentInput) { + input.TagSpecifications = nil + desiredTagSpecs := svcsdktypes.TagSpecification{} + if r.ko.Spec.Tags != nil { + requestedTags := []svcsdktypes.Tag{} + for _, desiredTag := range r.ko.Spec.Tags { + // Add in tags defined in the Spec + tag := svcsdktypes.Tag{} + if desiredTag.Key != nil && desiredTag.Value != nil { + tag.Key = desiredTag.Key + tag.Value = desiredTag.Value + } + requestedTags = append(requestedTags, tag) + } + desiredTagSpecs.ResourceType = svcsdktypes.ResourceTypeTransitGatewayAttachment + desiredTagSpecs.Tags = requestedTags + input.TagSpecifications = []svcsdktypes.TagSpecification{desiredTagSpecs} + } +} + +func compareSubnetIDs(desiredSubnetIDs, latestSubnetIDs []*string) ([]string, []string) { + + toAdd, toRemove := []string{}, []string{} + desired, latest := aws.ToStringSlice(desiredSubnetIDs), aws.ToStringSlice(latestSubnetIDs) + + for _, d := range desired { + found := false + for _, l := range latest { + if d == l { + found = true + break + } + } + if !found { + toAdd = append(toAdd, d) + } + } + + for _, l := range latest { + found := false + for _, d := range desired { + if l == d { + found = true + break + } + } + if !found { + toRemove = append(toAdd, l) + } + } + + return toAdd, toRemove +} + +func (rm *resourceManager) checkForMissingRequiredFields(r *resource) bool { + return r.ko.Status.ID == nil +} + +var syncTags = tags.Sync diff --git a/pkg/resource/transit_gateway_vpc_attachment/identifiers.go b/pkg/resource/transit_gateway_vpc_attachment/identifiers.go new file mode 100644 index 00000000..403e4de5 --- /dev/null +++ b/pkg/resource/transit_gateway_vpc_attachment/identifiers.go @@ -0,0 +1,55 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Code generated by ack-generate. DO NOT EDIT. + +package transit_gateway_vpc_attachment + +import ( + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" +) + +// resourceIdentifiers implements the +// `aws-service-operator-k8s/pkg/types.AWSResourceIdentifiers` interface +type resourceIdentifiers struct { + meta *ackv1alpha1.ResourceMetadata +} + +// ARN returns the AWS Resource Name for the backend AWS resource. If nil, +// this means the resource has not yet been created in the backend AWS +// service. +func (ri *resourceIdentifiers) ARN() *ackv1alpha1.AWSResourceName { + if ri.meta != nil { + return ri.meta.ARN + } + return nil +} + +// OwnerAccountID returns the AWS account identifier in which the +// backend AWS resource resides, or nil if this information is not known +// for the resource +func (ri *resourceIdentifiers) OwnerAccountID() *ackv1alpha1.AWSAccountID { + if ri.meta != nil { + return ri.meta.OwnerAccountID + } + return nil +} + +// Region returns the AWS region in which the resource exists, or +// nil if this information is not known. +func (ri *resourceIdentifiers) Region() *ackv1alpha1.AWSRegion { + if ri.meta != nil { + return ri.meta.Region + } + return nil +} diff --git a/pkg/resource/transit_gateway_vpc_attachment/manager.go b/pkg/resource/transit_gateway_vpc_attachment/manager.go new file mode 100644 index 00000000..43079ffe --- /dev/null +++ b/pkg/resource/transit_gateway_vpc_attachment/manager.go @@ -0,0 +1,404 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Code generated by ack-generate. DO NOT EDIT. + +package transit_gateway_vpc_attachment + +import ( + "context" + "fmt" + "time" + + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + ackcondition "github.com/aws-controllers-k8s/runtime/pkg/condition" + ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" + ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" + ackmetrics "github.com/aws-controllers-k8s/runtime/pkg/metrics" + ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue" + ackrt "github.com/aws-controllers-k8s/runtime/pkg/runtime" + ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" + acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + ackutil "github.com/aws-controllers-k8s/runtime/pkg/util" + "github.com/aws/aws-sdk-go-v2/aws" + svcsdk "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + + svcapitypes "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1" +) + +var ( + _ = ackutil.InStrings + _ = acktags.NewTags() + _ = ackrt.MissingImageTagValue + _ = svcapitypes.TransitGatewayVPCAttachment{} +) + +// +kubebuilder:rbac:groups=ec2.services.k8s.aws,resources=transitgatewayvpcattachments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=ec2.services.k8s.aws,resources=transitgatewayvpcattachments/status,verbs=get;update;patch + +var lateInitializeFieldNames = []string{} + +// resourceManager is responsible for providing a consistent way to perform +// CRUD operations in a backend AWS service API for Book custom resources. +type resourceManager struct { + // cfg is a copy of the ackcfg.Config object passed on start of the service + // controller + cfg ackcfg.Config + // clientcfg is a copy of the client configuration passed on start of the + // service controller + clientcfg aws.Config + // log refers to the logr.Logger object handling logging for the service + // controller + log logr.Logger + // metrics contains a collection of Prometheus metric objects that the + // service controller and its reconcilers track + metrics *ackmetrics.Metrics + // rr is the Reconciler which can be used for various utility + // functions such as querying for Secret values given a SecretReference + rr acktypes.Reconciler + // awsAccountID is the AWS account identifier that contains the resources + // managed by this resource manager + awsAccountID ackv1alpha1.AWSAccountID + // The AWS Region that this resource manager targets + awsRegion ackv1alpha1.AWSRegion + // sdk is a pointer to the AWS service API client exposed by the + // aws-sdk-go-v2/services/{alias} package. + sdkapi *svcsdk.Client +} + +// concreteResource returns a pointer to a resource from the supplied +// generic AWSResource interface +func (rm *resourceManager) concreteResource( + res acktypes.AWSResource, +) *resource { + // cast the generic interface into a pointer type specific to the concrete + // implementing resource type managed by this resource manager + return res.(*resource) +} + +// ReadOne returns the currently-observed state of the supplied AWSResource in +// the backend AWS service API. +func (rm *resourceManager) ReadOne( + ctx context.Context, + res acktypes.AWSResource, +) (acktypes.AWSResource, error) { + r := rm.concreteResource(res) + if r.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's ReadOne() method received resource with nil CR object") + } + observed, err := rm.sdkFind(ctx, r) + mirrorAWSTags(r, observed) + if err != nil { + if observed != nil { + return rm.onError(observed, err) + } + return rm.onError(r, err) + } + return rm.onSuccess(observed) +} + +// Create attempts to create the supplied AWSResource in the backend AWS +// service API, returning an AWSResource representing the newly-created +// resource +func (rm *resourceManager) Create( + ctx context.Context, + res acktypes.AWSResource, +) (acktypes.AWSResource, error) { + r := rm.concreteResource(res) + if r.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's Create() method received resource with nil CR object") + } + created, err := rm.sdkCreate(ctx, r) + if err != nil { + if created != nil { + return rm.onError(created, err) + } + return rm.onError(r, err) + } + return rm.onSuccess(created) +} + +// Update attempts to mutate the supplied desired AWSResource in the backend AWS +// service API, returning an AWSResource representing the newly-mutated +// resource. +// Note for specialized logic implementers can check to see how the latest +// observed resource differs from the supplied desired state. The +// higher-level reonciler determines whether or not the desired differs +// from the latest observed and decides whether to call the resource +// manager's Update method +func (rm *resourceManager) Update( + ctx context.Context, + resDesired acktypes.AWSResource, + resLatest acktypes.AWSResource, + delta *ackcompare.Delta, +) (acktypes.AWSResource, error) { + desired := rm.concreteResource(resDesired) + latest := rm.concreteResource(resLatest) + if desired.ko == nil || latest.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's Update() method received resource with nil CR object") + } + updated, err := rm.sdkUpdate(ctx, desired, latest, delta) + if err != nil { + if updated != nil { + return rm.onError(updated, err) + } + return rm.onError(latest, err) + } + return rm.onSuccess(updated) +} + +// Delete attempts to destroy the supplied AWSResource in the backend AWS +// service API, returning an AWSResource representing the +// resource being deleted (if delete is asynchronous and takes time) +func (rm *resourceManager) Delete( + ctx context.Context, + res acktypes.AWSResource, +) (acktypes.AWSResource, error) { + r := rm.concreteResource(res) + if r.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's Update() method received resource with nil CR object") + } + observed, err := rm.sdkDelete(ctx, r) + if err != nil { + if observed != nil { + return rm.onError(observed, err) + } + return rm.onError(r, err) + } + + return rm.onSuccess(observed) +} + +// ARNFromName returns an AWS Resource Name from a given string name. This +// is useful for constructing ARNs for APIs that require ARNs in their +// GetAttributes operations but all we have (for new CRs at least) is a +// name for the resource +func (rm *resourceManager) ARNFromName(name string) string { + return fmt.Sprintf( + "arn:aws:ec2:%s:%s:%s", + rm.awsRegion, + rm.awsAccountID, + name, + ) +} + +// LateInitialize returns an acktypes.AWSResource after setting the late initialized +// fields from the readOne call. This method will initialize the optional fields +// which were not provided by the k8s user but were defaulted by the AWS service. +// If there are no such fields to be initialized, the returned object is similar to +// object passed in the parameter. +func (rm *resourceManager) LateInitialize( + ctx context.Context, + latest acktypes.AWSResource, +) (acktypes.AWSResource, error) { + rlog := ackrtlog.FromContext(ctx) + // If there are no fields to late initialize, do nothing + if len(lateInitializeFieldNames) == 0 { + rlog.Debug("no late initialization required.") + return latest, nil + } + latestCopy := latest.DeepCopy() + lateInitConditionReason := "" + lateInitConditionMessage := "" + observed, err := rm.ReadOne(ctx, latestCopy) + if err != nil { + lateInitConditionMessage = "Unable to complete Read operation required for late initialization" + lateInitConditionReason = "Late Initialization Failure" + ackcondition.SetLateInitialized(latestCopy, corev1.ConditionFalse, &lateInitConditionMessage, &lateInitConditionReason) + ackcondition.SetSynced(latestCopy, corev1.ConditionFalse, nil, nil) + return latestCopy, err + } + lateInitializedRes := rm.lateInitializeFromReadOneOutput(observed, latestCopy) + incompleteInitialization := rm.incompleteLateInitialization(lateInitializedRes) + if incompleteInitialization { + // Add the condition with LateInitialized=False + lateInitConditionMessage = "Late initialization did not complete, requeuing with delay of 5 seconds" + lateInitConditionReason = "Delayed Late Initialization" + ackcondition.SetLateInitialized(lateInitializedRes, corev1.ConditionFalse, &lateInitConditionMessage, &lateInitConditionReason) + ackcondition.SetSynced(lateInitializedRes, corev1.ConditionFalse, nil, nil) + return lateInitializedRes, ackrequeue.NeededAfter(nil, time.Duration(5)*time.Second) + } + // Set LateInitialized condition to True + lateInitConditionMessage = "Late initialization successful" + lateInitConditionReason = "Late initialization successful" + ackcondition.SetLateInitialized(lateInitializedRes, corev1.ConditionTrue, &lateInitConditionMessage, &lateInitConditionReason) + return lateInitializedRes, nil +} + +// incompleteLateInitialization return true if there are fields which were supposed to be +// late initialized but are not. If all the fields are late initialized, false is returned +func (rm *resourceManager) incompleteLateInitialization( + res acktypes.AWSResource, +) bool { + return false +} + +// lateInitializeFromReadOneOutput late initializes the 'latest' resource from the 'observed' +// resource and returns 'latest' resource +func (rm *resourceManager) lateInitializeFromReadOneOutput( + observed acktypes.AWSResource, + latest acktypes.AWSResource, +) acktypes.AWSResource { + return latest +} + +// IsSynced returns true if the resource is synced. +func (rm *resourceManager) IsSynced(ctx context.Context, res acktypes.AWSResource) (bool, error) { + r := rm.concreteResource(res) + if r.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's IsSynced() method received resource with nil CR object") + } + + return true, nil +} + +// EnsureTags ensures that tags are present inside the AWSResource. +// If the AWSResource does not have any existing resource tags, the 'tags' +// field is initialized and the controller tags are added. +// If the AWSResource has existing resource tags, then controller tags are +// added to the existing resource tags without overriding them. +// If the AWSResource does not support tags, only then the controller tags +// will not be added to the AWSResource. +func (rm *resourceManager) EnsureTags( + ctx context.Context, + res acktypes.AWSResource, + md acktypes.ServiceControllerMetadata, +) error { + r := rm.concreteResource(res) + if r.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's EnsureTags method received resource with nil CR object") + } + defaultTags := ackrt.GetDefaultTags(&rm.cfg, r.ko, md) + var existingTags []*svcapitypes.Tag + existingTags = r.ko.Spec.Tags + resourceTags, keyOrder := convertToOrderedACKTags(existingTags) + tags := acktags.Merge(resourceTags, defaultTags) + r.ko.Spec.Tags = fromACKTags(tags, keyOrder) + return nil +} + +// FilterAWSTags ignores tags that have keys that start with "aws:" +// is needed to ensure the controller does not attempt to remove +// tags set by AWS. This function needs to be called after each Read +// operation. +// Eg. resources created with cloudformation have tags that cannot be +// removed by an ACK controller +func (rm *resourceManager) FilterSystemTags(res acktypes.AWSResource) { + r := rm.concreteResource(res) + if r == nil || r.ko == nil { + return + } + var existingTags []*svcapitypes.Tag + existingTags = r.ko.Spec.Tags + resourceTags, tagKeyOrder := convertToOrderedACKTags(existingTags) + ignoreSystemTags(resourceTags) + r.ko.Spec.Tags = fromACKTags(resourceTags, tagKeyOrder) +} + +// mirrorAWSTags ensures that AWS tags are included in the desired resource +// if they are present in the latest resource. This will ensure that the +// aws tags are not present in a diff. The logic of the controller will +// ensure these tags aren't patched to the resource in the cluster, and +// will only be present to make sure we don't try to remove these tags. +// +// Although there are a lot of similarities between this function and +// EnsureTags, they are very much different. +// While EnsureTags tries to make sure the resource contains the controller +// tags, mirrowAWSTags tries to make sure tags injected by AWS are mirrored +// from the latest resoruce to the desired resource. +func mirrorAWSTags(a *resource, b *resource) { + if a == nil || a.ko == nil || b == nil || b.ko == nil { + return + } + var existingLatestTags []*svcapitypes.Tag + var existingDesiredTags []*svcapitypes.Tag + existingDesiredTags = a.ko.Spec.Tags + existingLatestTags = b.ko.Spec.Tags + desiredTags, desiredTagKeyOrder := convertToOrderedACKTags(existingDesiredTags) + latestTags, _ := convertToOrderedACKTags(existingLatestTags) + syncAWSTags(desiredTags, latestTags) + a.ko.Spec.Tags = fromACKTags(desiredTags, desiredTagKeyOrder) +} + +// newResourceManager returns a new struct implementing +// acktypes.AWSResourceManager +// This is for AWS-SDK-GO-V2 - Created newResourceManager With AWS sdk-Go-ClientV2 +func newResourceManager( + cfg ackcfg.Config, + clientcfg aws.Config, + log logr.Logger, + metrics *ackmetrics.Metrics, + rr acktypes.Reconciler, + id ackv1alpha1.AWSAccountID, + region ackv1alpha1.AWSRegion, +) (*resourceManager, error) { + return &resourceManager{ + cfg: cfg, + clientcfg: clientcfg, + log: log, + metrics: metrics, + rr: rr, + awsAccountID: id, + awsRegion: region, + sdkapi: svcsdk.NewFromConfig(clientcfg), + }, nil +} + +// onError updates resource conditions and returns updated resource +// it returns nil if no condition is updated. +func (rm *resourceManager) onError( + r *resource, + err error, +) (acktypes.AWSResource, error) { + if r == nil { + return nil, err + } + r1, updated := rm.updateConditions(r, false, err) + if !updated { + return r, err + } + for _, condition := range r1.Conditions() { + if condition.Type == ackv1alpha1.ConditionTypeTerminal && + condition.Status == corev1.ConditionTrue { + // resource is in Terminal condition + // return Terminal error + return r1, ackerr.Terminal + } + } + return r1, err +} + +// onSuccess updates resource conditions and returns updated resource +// it returns the supplied resource if no condition is updated. +func (rm *resourceManager) onSuccess( + r *resource, +) (acktypes.AWSResource, error) { + if r == nil { + return nil, nil + } + r1, updated := rm.updateConditions(r, true, nil) + if !updated { + return r, nil + } + return r1, nil +} diff --git a/pkg/resource/transit_gateway_vpc_attachment/manager_factory.go b/pkg/resource/transit_gateway_vpc_attachment/manager_factory.go new file mode 100644 index 00000000..6ca653ab --- /dev/null +++ b/pkg/resource/transit_gateway_vpc_attachment/manager_factory.go @@ -0,0 +1,100 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Code generated by ack-generate. DO NOT EDIT. + +package transit_gateway_vpc_attachment + +import ( + "fmt" + "sync" + + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" + ackmetrics "github.com/aws-controllers-k8s/runtime/pkg/metrics" + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/go-logr/logr" + + svcresource "github.com/aws-controllers-k8s/ec2-controller/pkg/resource" +) + +// resourceManagerFactory produces resourceManager objects. It implements the +// `types.AWSResourceManagerFactory` interface. +type resourceManagerFactory struct { + sync.RWMutex + // rmCache contains resource managers for a particular AWS account ID + rmCache map[string]*resourceManager +} + +// ResourcePrototype returns an AWSResource that resource managers produced by +// this factory will handle +func (f *resourceManagerFactory) ResourceDescriptor() acktypes.AWSResourceDescriptor { + return &resourceDescriptor{} +} + +// ManagerFor returns a resource manager object that can manage resources for a +// supplied AWS account +func (f *resourceManagerFactory) ManagerFor( + cfg ackcfg.Config, + clientcfg aws.Config, + log logr.Logger, + metrics *ackmetrics.Metrics, + rr acktypes.Reconciler, + id ackv1alpha1.AWSAccountID, + region ackv1alpha1.AWSRegion, + roleARN ackv1alpha1.AWSResourceName, +) (acktypes.AWSResourceManager, error) { + // We use the account ID, region, and role ARN to uniquely identify a + // resource manager. This helps us to avoid creating multiple resource + // managers for the same account/region/roleARN combination. + rmId := fmt.Sprintf("%s/%s/%s", id, region, roleARN) + f.RLock() + rm, found := f.rmCache[rmId] + f.RUnlock() + + if found { + return rm, nil + } + + f.Lock() + defer f.Unlock() + + rm, err := newResourceManager(cfg, clientcfg, log, metrics, rr, id, region) + if err != nil { + return nil, err + } + f.rmCache[rmId] = rm + return rm, nil +} + +// IsAdoptable returns true if the resource is able to be adopted +func (f *resourceManagerFactory) IsAdoptable() bool { + return true +} + +// RequeueOnSuccessSeconds returns true if the resource should be requeued after specified seconds +// Default is false which means resource will not be requeued after success. +func (f *resourceManagerFactory) RequeueOnSuccessSeconds() int { + return 0 +} + +func newResourceManagerFactory() *resourceManagerFactory { + return &resourceManagerFactory{ + rmCache: map[string]*resourceManager{}, + } +} + +func init() { + svcresource.RegisterManagerFactory(newResourceManagerFactory()) +} diff --git a/pkg/resource/transit_gateway_vpc_attachment/references.go b/pkg/resource/transit_gateway_vpc_attachment/references.go new file mode 100644 index 00000000..dc39e430 --- /dev/null +++ b/pkg/resource/transit_gateway_vpc_attachment/references.go @@ -0,0 +1,371 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Code generated by ack-generate. DO NOT EDIT. + +package transit_gateway_vpc_attachment + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + + svcapitypes "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1" +) + +// ClearResolvedReferences removes any reference values that were made +// concrete in the spec. It returns a copy of the input AWSResource which +// contains the original *Ref values, but none of their respective concrete +// values. +func (rm *resourceManager) ClearResolvedReferences(res acktypes.AWSResource) acktypes.AWSResource { + ko := rm.concreteResource(res).ko.DeepCopy() + + if len(ko.Spec.SubnetRefs) > 0 { + ko.Spec.SubnetIDs = nil + } + + if ko.Spec.TransitGatewayRef != nil { + ko.Spec.TransitGatewayID = nil + } + + if ko.Spec.VPCRef != nil { + ko.Spec.VPCID = nil + } + + return &resource{ko} +} + +// ResolveReferences finds if there are any Reference field(s) present +// inside AWSResource passed in the parameter and attempts to resolve those +// reference field(s) into their respective target field(s). It returns a +// copy of the input AWSResource with resolved reference(s), a boolean which +// is set to true if the resource contains any references (regardless of if +// they are resolved successfully) and an error if the passed AWSResource's +// reference field(s) could not be resolved. +func (rm *resourceManager) ResolveReferences( + ctx context.Context, + apiReader client.Reader, + res acktypes.AWSResource, +) (acktypes.AWSResource, bool, error) { + ko := rm.concreteResource(res).ko + + resourceHasReferences := false + err := validateReferenceFields(ko) + if fieldHasReferences, err := rm.resolveReferenceForSubnetIDs(ctx, apiReader, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences + } + + if fieldHasReferences, err := rm.resolveReferenceForTransitGatewayID(ctx, apiReader, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences + } + + if fieldHasReferences, err := rm.resolveReferenceForVPCID(ctx, apiReader, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences + } + + return &resource{ko}, resourceHasReferences, err +} + +// validateReferenceFields validates the reference field and corresponding +// identifier field. +func validateReferenceFields(ko *svcapitypes.TransitGatewayVPCAttachment) error { + + if len(ko.Spec.SubnetRefs) > 0 && len(ko.Spec.SubnetIDs) > 0 { + return ackerr.ResourceReferenceAndIDNotSupportedFor("SubnetIDs", "SubnetRefs") + } + if len(ko.Spec.SubnetRefs) == 0 && len(ko.Spec.SubnetIDs) == 0 { + return ackerr.ResourceReferenceOrIDRequiredFor("SubnetIDs", "SubnetRefs") + } + + if ko.Spec.TransitGatewayRef != nil && ko.Spec.TransitGatewayID != nil { + return ackerr.ResourceReferenceAndIDNotSupportedFor("TransitGatewayID", "TransitGatewayRef") + } + if ko.Spec.TransitGatewayRef == nil && ko.Spec.TransitGatewayID == nil { + return ackerr.ResourceReferenceOrIDRequiredFor("TransitGatewayID", "TransitGatewayRef") + } + + if ko.Spec.VPCRef != nil && ko.Spec.VPCID != nil { + return ackerr.ResourceReferenceAndIDNotSupportedFor("VPCID", "VPCRef") + } + if ko.Spec.VPCRef == nil && ko.Spec.VPCID == nil { + return ackerr.ResourceReferenceOrIDRequiredFor("VPCID", "VPCRef") + } + return nil +} + +// resolveReferenceForSubnetIDs reads the resource referenced +// from SubnetRefs field and sets the SubnetIDs +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForSubnetIDs( + ctx context.Context, + apiReader client.Reader, + ko *svcapitypes.TransitGatewayVPCAttachment, +) (hasReferences bool, err error) { + for _, f0iter := range ko.Spec.SubnetRefs { + if f0iter != nil && f0iter.From != nil { + hasReferences = true + arr := f0iter.From + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: SubnetRefs") + } + namespace := ko.ObjectMeta.GetNamespace() + if arr.Namespace != nil && *arr.Namespace != "" { + namespace = *arr.Namespace + } + obj := &svcapitypes.Subnet{} + if err := getReferencedResourceState_Subnet(ctx, apiReader, obj, *arr.Name, namespace); err != nil { + return hasReferences, err + } + if ko.Spec.SubnetIDs == nil { + ko.Spec.SubnetIDs = make([]*string, 0, 1) + } + ko.Spec.SubnetIDs = append(ko.Spec.SubnetIDs, (*string)(obj.Status.SubnetID)) + } + } + + return hasReferences, nil +} + +// getReferencedResourceState_Subnet looks up whether a referenced resource +// exists and is in a ACK.ResourceSynced=True state. If the referenced resource does exist and is +// in a Synced state, returns nil, otherwise returns `ackerr.ResourceReferenceTerminalFor` or +// `ResourceReferenceNotSyncedFor` depending on if the resource is in a Terminal state. +func getReferencedResourceState_Subnet( + ctx context.Context, + apiReader client.Reader, + obj *svcapitypes.Subnet, + name string, // the Kubernetes name of the referenced resource + namespace string, // the Kubernetes namespace of the referenced resource +) error { + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + err := apiReader.Get(ctx, namespacedName, obj) + if err != nil { + return err + } + var refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + return ackerr.ResourceReferenceTerminalFor( + "Subnet", + namespace, name) + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "Subnet", + namespace, name) + } + var refResourceSynced bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "Subnet", + namespace, name) + } + if obj.Status.SubnetID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "Subnet", + namespace, name, + "Status.SubnetID") + } + return nil +} + +// resolveReferenceForTransitGatewayID reads the resource referenced +// from TransitGatewayRef field and sets the TransitGatewayID +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForTransitGatewayID( + ctx context.Context, + apiReader client.Reader, + ko *svcapitypes.TransitGatewayVPCAttachment, +) (hasReferences bool, err error) { + if ko.Spec.TransitGatewayRef != nil && ko.Spec.TransitGatewayRef.From != nil { + hasReferences = true + arr := ko.Spec.TransitGatewayRef.From + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: TransitGatewayRef") + } + namespace := ko.ObjectMeta.GetNamespace() + if arr.Namespace != nil && *arr.Namespace != "" { + namespace = *arr.Namespace + } + obj := &svcapitypes.TransitGateway{} + if err := getReferencedResourceState_TransitGateway(ctx, apiReader, obj, *arr.Name, namespace); err != nil { + return hasReferences, err + } + ko.Spec.TransitGatewayID = (*string)(obj.Status.TransitGatewayID) + } + + return hasReferences, nil +} + +// getReferencedResourceState_TransitGateway looks up whether a referenced resource +// exists and is in a ACK.ResourceSynced=True state. If the referenced resource does exist and is +// in a Synced state, returns nil, otherwise returns `ackerr.ResourceReferenceTerminalFor` or +// `ResourceReferenceNotSyncedFor` depending on if the resource is in a Terminal state. +func getReferencedResourceState_TransitGateway( + ctx context.Context, + apiReader client.Reader, + obj *svcapitypes.TransitGateway, + name string, // the Kubernetes name of the referenced resource + namespace string, // the Kubernetes namespace of the referenced resource +) error { + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + err := apiReader.Get(ctx, namespacedName, obj) + if err != nil { + return err + } + var refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + return ackerr.ResourceReferenceTerminalFor( + "TransitGateway", + namespace, name) + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "TransitGateway", + namespace, name) + } + var refResourceSynced bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "TransitGateway", + namespace, name) + } + if obj.Status.TransitGatewayID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "TransitGateway", + namespace, name, + "Status.TransitGatewayID") + } + return nil +} + +// resolveReferenceForVPCID reads the resource referenced +// from VPCRef field and sets the VPCID +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForVPCID( + ctx context.Context, + apiReader client.Reader, + ko *svcapitypes.TransitGatewayVPCAttachment, +) (hasReferences bool, err error) { + if ko.Spec.VPCRef != nil && ko.Spec.VPCRef.From != nil { + hasReferences = true + arr := ko.Spec.VPCRef.From + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: VPCRef") + } + namespace := ko.ObjectMeta.GetNamespace() + if arr.Namespace != nil && *arr.Namespace != "" { + namespace = *arr.Namespace + } + obj := &svcapitypes.VPC{} + if err := getReferencedResourceState_VPC(ctx, apiReader, obj, *arr.Name, namespace); err != nil { + return hasReferences, err + } + ko.Spec.VPCID = (*string)(obj.Status.VPCID) + } + + return hasReferences, nil +} + +// getReferencedResourceState_VPC looks up whether a referenced resource +// exists and is in a ACK.ResourceSynced=True state. If the referenced resource does exist and is +// in a Synced state, returns nil, otherwise returns `ackerr.ResourceReferenceTerminalFor` or +// `ResourceReferenceNotSyncedFor` depending on if the resource is in a Terminal state. +func getReferencedResourceState_VPC( + ctx context.Context, + apiReader client.Reader, + obj *svcapitypes.VPC, + name string, // the Kubernetes name of the referenced resource + namespace string, // the Kubernetes namespace of the referenced resource +) error { + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + err := apiReader.Get(ctx, namespacedName, obj) + if err != nil { + return err + } + var refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + return ackerr.ResourceReferenceTerminalFor( + "VPC", + namespace, name) + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "VPC", + namespace, name) + } + var refResourceSynced bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "VPC", + namespace, name) + } + if obj.Status.VPCID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "VPC", + namespace, name, + "Status.VPCID") + } + return nil +} diff --git a/pkg/resource/transit_gateway_vpc_attachment/resource.go b/pkg/resource/transit_gateway_vpc_attachment/resource.go new file mode 100644 index 00000000..a572c731 --- /dev/null +++ b/pkg/resource/transit_gateway_vpc_attachment/resource.go @@ -0,0 +1,111 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Code generated by ack-generate. DO NOT EDIT. + +package transit_gateway_vpc_attachment + +import ( + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackerrors "github.com/aws-controllers-k8s/runtime/pkg/errors" + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + rtclient "sigs.k8s.io/controller-runtime/pkg/client" + + svcapitypes "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1" +) + +// Hack to avoid import errors during build... +var ( + _ = &ackerrors.MissingNameIdentifier +) + +// resource implements the `aws-controller-k8s/runtime/pkg/types.AWSResource` +// interface +type resource struct { + // The Kubernetes-native CR representing the resource + ko *svcapitypes.TransitGatewayVPCAttachment +} + +// Identifiers returns an AWSResourceIdentifiers object containing various +// identifying information, including the AWS account ID that owns the +// resource, the resource's AWS Resource Name (ARN) +func (r *resource) Identifiers() acktypes.AWSResourceIdentifiers { + return &resourceIdentifiers{r.ko.Status.ACKResourceMetadata} +} + +// IsBeingDeleted returns true if the Kubernetes resource has a non-zero +// deletion timestamp +func (r *resource) IsBeingDeleted() bool { + return !r.ko.DeletionTimestamp.IsZero() +} + +// RuntimeObject returns the Kubernetes apimachinery/runtime representation of +// the AWSResource +func (r *resource) RuntimeObject() rtclient.Object { + return r.ko +} + +// MetaObject returns the Kubernetes apimachinery/apis/meta/v1.Object +// representation of the AWSResource +func (r *resource) MetaObject() metav1.Object { + return r.ko.GetObjectMeta() +} + +// Conditions returns the ACK Conditions collection for the AWSResource +func (r *resource) Conditions() []*ackv1alpha1.Condition { + return r.ko.Status.Conditions +} + +// ReplaceConditions sets the Conditions status field for the resource +func (r *resource) ReplaceConditions(conditions []*ackv1alpha1.Condition) { + r.ko.Status.Conditions = conditions +} + +// SetObjectMeta sets the ObjectMeta field for the resource +func (r *resource) SetObjectMeta(meta metav1.ObjectMeta) { + r.ko.ObjectMeta = meta +} + +// SetStatus will set the Status field for the resource +func (r *resource) SetStatus(desired acktypes.AWSResource) { + r.ko.Status = desired.(*resource).ko.Status +} + +// SetIdentifiers sets the Spec or Status field that is referenced as the unique +// resource identifier +func (r *resource) SetIdentifiers(identifier *ackv1alpha1.AWSIdentifiers) error { + if identifier.NameOrID == "" { + return ackerrors.MissingNameIdentifier + } + r.ko.Status.ID = &identifier.NameOrID + + return nil +} + +// PopulateResourceFromAnnotation populates the fields passed from adoption annotation +func (r *resource) PopulateResourceFromAnnotation(fields map[string]string) error { + tmp, ok := fields["id"] + if !ok { + return ackerrors.MissingNameIdentifier + } + r.ko.Status.ID = &tmp + + return nil +} + +// DeepCopy will return a copy of the resource +func (r *resource) DeepCopy() acktypes.AWSResource { + koCopy := r.ko.DeepCopy() + return &resource{koCopy} +} diff --git a/pkg/resource/transit_gateway_vpc_attachment/sdk.go b/pkg/resource/transit_gateway_vpc_attachment/sdk.go new file mode 100644 index 00000000..5dbd57c1 --- /dev/null +++ b/pkg/resource/transit_gateway_vpc_attachment/sdk.go @@ -0,0 +1,634 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Code generated by ack-generate. DO NOT EDIT. + +package transit_gateway_vpc_attachment + +import ( + "context" + "errors" + "fmt" + "reflect" + "strings" + + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + ackcondition "github.com/aws-controllers-k8s/runtime/pkg/condition" + ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" + ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue" + ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" + "github.com/aws/aws-sdk-go-v2/aws" + svcsdk "github.com/aws/aws-sdk-go-v2/service/ec2" + svcsdktypes "github.com/aws/aws-sdk-go-v2/service/ec2/types" + smithy "github.com/aws/smithy-go" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + svcapitypes "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1" +) + +// Hack to avoid import errors during build... +var ( + _ = &metav1.Time{} + _ = strings.ToLower("") + _ = &svcsdk.Client{} + _ = &svcapitypes.TransitGatewayVPCAttachment{} + _ = ackv1alpha1.AWSAccountID("") + _ = &ackerr.NotFound + _ = &ackcondition.NotManagedMessage + _ = &reflect.Value{} + _ = fmt.Sprintf("") + _ = &ackrequeue.NoRequeue{} + _ = &aws.Config{} +) + +// sdkFind returns SDK-specific information about a supplied resource +func (rm *resourceManager) sdkFind( + ctx context.Context, + r *resource, +) (latest *resource, err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.sdkFind") + defer func() { + exit(err) + }() + // If any required fields in the input shape are missing, AWS resource is + // not created yet. Return NotFound here to indicate to callers that the + // resource isn't yet created. + if rm.requiredFieldsMissingFromReadManyInput(r) { + return nil, ackerr.NotFound + } + + input, err := rm.newListRequestPayload(r) + if err != nil { + return nil, err + } + var resp *svcsdk.DescribeTransitGatewayVpcAttachmentsOutput + resp, err = rm.sdkapi.DescribeTransitGatewayVpcAttachments(ctx, input) + rm.metrics.RecordAPICall("READ_MANY", "DescribeTransitGatewayVpcAttachments", err) + if err != nil { + var awsErr smithy.APIError + if errors.As(err, &awsErr) && awsErr.ErrorCode() == "UNKNOWN" { + return nil, ackerr.NotFound + } + return nil, err + } + + // Merge in the information we read from the API call above to the copy of + // the original Kubernetes object we passed to the function + ko := r.ko.DeepCopy() + + found := false + for _, elem := range resp.TransitGatewayVpcAttachments { + if elem.CreationTime != nil { + ko.Status.CreationTime = &metav1.Time{*elem.CreationTime} + } else { + ko.Status.CreationTime = nil + } + if elem.Options != nil { + f1 := &svcapitypes.CreateTransitGatewayVPCAttachmentRequestOptions{} + if elem.Options.ApplianceModeSupport != "" { + f1.ApplianceModeSupport = aws.String(string(elem.Options.ApplianceModeSupport)) + } + if elem.Options.DnsSupport != "" { + f1.DNSSupport = aws.String(string(elem.Options.DnsSupport)) + } + if elem.Options.Ipv6Support != "" { + f1.IPv6Support = aws.String(string(elem.Options.Ipv6Support)) + } + if elem.Options.SecurityGroupReferencingSupport != "" { + f1.SecurityGroupReferencingSupport = aws.String(string(elem.Options.SecurityGroupReferencingSupport)) + } + ko.Spec.Options = f1 + } else { + ko.Spec.Options = nil + } + if elem.State != "" { + ko.Status.State = aws.String(string(elem.State)) + } else { + ko.Status.State = nil + } + if elem.SubnetIds != nil { + ko.Spec.SubnetIDs = aws.StringSlice(elem.SubnetIds) + } else { + ko.Spec.SubnetIDs = nil + } + if elem.Tags != nil { + f4 := []*svcapitypes.Tag{} + for _, f4iter := range elem.Tags { + f4elem := &svcapitypes.Tag{} + if f4iter.Key != nil { + f4elem.Key = f4iter.Key + } + if f4iter.Value != nil { + f4elem.Value = f4iter.Value + } + f4 = append(f4, f4elem) + } + ko.Spec.Tags = f4 + } else { + ko.Spec.Tags = nil + } + if elem.TransitGatewayAttachmentId != nil { + ko.Status.ID = elem.TransitGatewayAttachmentId + } else { + ko.Status.ID = nil + } + if elem.TransitGatewayId != nil { + ko.Spec.TransitGatewayID = elem.TransitGatewayId + } else { + ko.Spec.TransitGatewayID = nil + } + if elem.VpcId != nil { + ko.Spec.VPCID = elem.VpcId + } else { + ko.Spec.VPCID = nil + } + if elem.VpcOwnerId != nil { + ko.Status.VPCOwnerID = elem.VpcOwnerId + } else { + ko.Status.VPCOwnerID = nil + } + found = true + break + } + if !found { + return nil, ackerr.NotFound + } + + rm.setStatusDefaults(ko) + return &resource{ko}, nil +} + +// requiredFieldsMissingFromReadManyInput returns true if there are any fields +// for the ReadMany Input shape that are required but not present in the +// resource's Spec or Status +func (rm *resourceManager) requiredFieldsMissingFromReadManyInput( + r *resource, +) bool { + return rm.checkForMissingRequiredFields(r) +} + +// newListRequestPayload returns SDK-specific struct for the HTTP request +// payload of the List API call for the resource +func (rm *resourceManager) newListRequestPayload( + r *resource, +) (*svcsdk.DescribeTransitGatewayVpcAttachmentsInput, error) { + res := &svcsdk.DescribeTransitGatewayVpcAttachmentsInput{} + + if r.ko.Status.ID != nil { + f4 := []string{} + f4 = append(f4, *r.ko.Status.ID) + res.TransitGatewayAttachmentIds = f4 + } + + return res, nil +} + +// sdkCreate creates the supplied resource in the backend AWS service API and +// returns a copy of the resource with resource fields (in both Spec and +// Status) filled in with values from the CREATE API operation's Output shape. +func (rm *resourceManager) sdkCreate( + ctx context.Context, + desired *resource, +) (created *resource, err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.sdkCreate") + defer func() { + exit(err) + }() + input, err := rm.newCreateRequestPayload(ctx, desired) + if err != nil { + return nil, err + } + updateTagSpecificationsInCreateRequest(desired, input) + + var resp *svcsdk.CreateTransitGatewayVpcAttachmentOutput + _ = resp + resp, err = rm.sdkapi.CreateTransitGatewayVpcAttachment(ctx, input) + rm.metrics.RecordAPICall("CREATE", "CreateTransitGatewayVpcAttachment", err) + if err != nil { + return nil, err + } + // Merge in the information we read from the API call above to the copy of + // the original Kubernetes object we passed to the function + ko := desired.ko.DeepCopy() + + if resp.TransitGatewayVpcAttachment.CreationTime != nil { + ko.Status.CreationTime = &metav1.Time{*resp.TransitGatewayVpcAttachment.CreationTime} + } else { + ko.Status.CreationTime = nil + } + if resp.TransitGatewayVpcAttachment.Options != nil { + f1 := &svcapitypes.CreateTransitGatewayVPCAttachmentRequestOptions{} + if resp.TransitGatewayVpcAttachment.Options.ApplianceModeSupport != "" { + f1.ApplianceModeSupport = aws.String(string(resp.TransitGatewayVpcAttachment.Options.ApplianceModeSupport)) + } + if resp.TransitGatewayVpcAttachment.Options.DnsSupport != "" { + f1.DNSSupport = aws.String(string(resp.TransitGatewayVpcAttachment.Options.DnsSupport)) + } + if resp.TransitGatewayVpcAttachment.Options.Ipv6Support != "" { + f1.IPv6Support = aws.String(string(resp.TransitGatewayVpcAttachment.Options.Ipv6Support)) + } + if resp.TransitGatewayVpcAttachment.Options.SecurityGroupReferencingSupport != "" { + f1.SecurityGroupReferencingSupport = aws.String(string(resp.TransitGatewayVpcAttachment.Options.SecurityGroupReferencingSupport)) + } + ko.Spec.Options = f1 + } else { + ko.Spec.Options = nil + } + if resp.TransitGatewayVpcAttachment.State != "" { + ko.Status.State = aws.String(string(resp.TransitGatewayVpcAttachment.State)) + } else { + ko.Status.State = nil + } + if resp.TransitGatewayVpcAttachment.SubnetIds != nil { + ko.Spec.SubnetIDs = aws.StringSlice(resp.TransitGatewayVpcAttachment.SubnetIds) + } else { + ko.Spec.SubnetIDs = nil + } + if resp.TransitGatewayVpcAttachment.Tags != nil { + f4 := []*svcapitypes.Tag{} + for _, f4iter := range resp.TransitGatewayVpcAttachment.Tags { + f4elem := &svcapitypes.Tag{} + if f4iter.Key != nil { + f4elem.Key = f4iter.Key + } + if f4iter.Value != nil { + f4elem.Value = f4iter.Value + } + f4 = append(f4, f4elem) + } + ko.Spec.Tags = f4 + } else { + ko.Spec.Tags = nil + } + if resp.TransitGatewayVpcAttachment.TransitGatewayAttachmentId != nil { + ko.Status.ID = resp.TransitGatewayVpcAttachment.TransitGatewayAttachmentId + } else { + ko.Status.ID = nil + } + if resp.TransitGatewayVpcAttachment.TransitGatewayId != nil { + ko.Spec.TransitGatewayID = resp.TransitGatewayVpcAttachment.TransitGatewayId + } else { + ko.Spec.TransitGatewayID = nil + } + if resp.TransitGatewayVpcAttachment.VpcId != nil { + ko.Spec.VPCID = resp.TransitGatewayVpcAttachment.VpcId + } else { + ko.Spec.VPCID = nil + } + if resp.TransitGatewayVpcAttachment.VpcOwnerId != nil { + ko.Status.VPCOwnerID = resp.TransitGatewayVpcAttachment.VpcOwnerId + } else { + ko.Status.VPCOwnerID = nil + } + + rm.setStatusDefaults(ko) + return &resource{ko}, nil +} + +// newCreateRequestPayload returns an SDK-specific struct for the HTTP request +// payload of the Create API call for the resource +func (rm *resourceManager) newCreateRequestPayload( + ctx context.Context, + r *resource, +) (*svcsdk.CreateTransitGatewayVpcAttachmentInput, error) { + res := &svcsdk.CreateTransitGatewayVpcAttachmentInput{} + + if r.ko.Spec.Options != nil { + f0 := &svcsdktypes.CreateTransitGatewayVpcAttachmentRequestOptions{} + if r.ko.Spec.Options.ApplianceModeSupport != nil { + f0.ApplianceModeSupport = svcsdktypes.ApplianceModeSupportValue(*r.ko.Spec.Options.ApplianceModeSupport) + } + if r.ko.Spec.Options.DNSSupport != nil { + f0.DnsSupport = svcsdktypes.DnsSupportValue(*r.ko.Spec.Options.DNSSupport) + } + if r.ko.Spec.Options.IPv6Support != nil { + f0.Ipv6Support = svcsdktypes.Ipv6SupportValue(*r.ko.Spec.Options.IPv6Support) + } + if r.ko.Spec.Options.SecurityGroupReferencingSupport != nil { + f0.SecurityGroupReferencingSupport = svcsdktypes.SecurityGroupReferencingSupportValue(*r.ko.Spec.Options.SecurityGroupReferencingSupport) + } + res.Options = f0 + } + if r.ko.Spec.SubnetIDs != nil { + res.SubnetIds = aws.ToStringSlice(r.ko.Spec.SubnetIDs) + } + if r.ko.Spec.TransitGatewayID != nil { + res.TransitGatewayId = r.ko.Spec.TransitGatewayID + } + if r.ko.Spec.VPCID != nil { + res.VpcId = r.ko.Spec.VPCID + } + + return res, nil +} + +// sdkUpdate patches the supplied resource in the backend AWS service API and +// returns a new resource with updated fields. +func (rm *resourceManager) sdkUpdate( + ctx context.Context, + desired *resource, + latest *resource, + delta *ackcompare.Delta, +) (updated *resource, err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.sdkUpdate") + defer func() { + exit(err) + }() + + if delta.DifferentAt("Spec.Tags") { + if err := syncTags( + ctx, rm.sdkapi, rm.metrics, *latest.ko.Status.ID, + desired.ko.Spec.Tags, latest.ko.Spec.Tags, + ); err != nil { + return nil, err + } + } + + // Only continue if something other than Tags has changed in the Spec + if !delta.DifferentExcept("Spec.Tags") { + return desired, nil + } + + if *latest.ko.Status.State != string(svcsdktypes.TransitGatewayAttachmentStateAvailable) { + return desired, requeueWaitUntilCanModify(desired) + } + + input, err := rm.newUpdateRequestPayload(ctx, desired, delta) + if err != nil { + return nil, err + } + + input.AddSubnetIds, input.RemoveSubnetIds = compareSubnetIDs( + desired.ko.Spec.SubnetIDs, + latest.ko.Spec.SubnetIDs, + ) + + var resp *svcsdk.ModifyTransitGatewayVpcAttachmentOutput + _ = resp + resp, err = rm.sdkapi.ModifyTransitGatewayVpcAttachment(ctx, input) + rm.metrics.RecordAPICall("UPDATE", "ModifyTransitGatewayVpcAttachment", err) + if err != nil { + return nil, err + } + // Merge in the information we read from the API call above to the copy of + // the original Kubernetes object we passed to the function + ko := desired.ko.DeepCopy() + + if resp.TransitGatewayVpcAttachment.CreationTime != nil { + ko.Status.CreationTime = &metav1.Time{*resp.TransitGatewayVpcAttachment.CreationTime} + } else { + ko.Status.CreationTime = nil + } + if resp.TransitGatewayVpcAttachment.Options != nil { + f1 := &svcapitypes.CreateTransitGatewayVPCAttachmentRequestOptions{} + if resp.TransitGatewayVpcAttachment.Options.ApplianceModeSupport != "" { + f1.ApplianceModeSupport = aws.String(string(resp.TransitGatewayVpcAttachment.Options.ApplianceModeSupport)) + } + if resp.TransitGatewayVpcAttachment.Options.DnsSupport != "" { + f1.DNSSupport = aws.String(string(resp.TransitGatewayVpcAttachment.Options.DnsSupport)) + } + if resp.TransitGatewayVpcAttachment.Options.Ipv6Support != "" { + f1.IPv6Support = aws.String(string(resp.TransitGatewayVpcAttachment.Options.Ipv6Support)) + } + if resp.TransitGatewayVpcAttachment.Options.SecurityGroupReferencingSupport != "" { + f1.SecurityGroupReferencingSupport = aws.String(string(resp.TransitGatewayVpcAttachment.Options.SecurityGroupReferencingSupport)) + } + ko.Spec.Options = f1 + } else { + ko.Spec.Options = nil + } + if resp.TransitGatewayVpcAttachment.State != "" { + ko.Status.State = aws.String(string(resp.TransitGatewayVpcAttachment.State)) + } else { + ko.Status.State = nil + } + if resp.TransitGatewayVpcAttachment.SubnetIds != nil { + ko.Spec.SubnetIDs = aws.StringSlice(resp.TransitGatewayVpcAttachment.SubnetIds) + } else { + ko.Spec.SubnetIDs = nil + } + if resp.TransitGatewayVpcAttachment.Tags != nil { + f4 := []*svcapitypes.Tag{} + for _, f4iter := range resp.TransitGatewayVpcAttachment.Tags { + f4elem := &svcapitypes.Tag{} + if f4iter.Key != nil { + f4elem.Key = f4iter.Key + } + if f4iter.Value != nil { + f4elem.Value = f4iter.Value + } + f4 = append(f4, f4elem) + } + ko.Spec.Tags = f4 + } else { + ko.Spec.Tags = nil + } + if resp.TransitGatewayVpcAttachment.TransitGatewayAttachmentId != nil { + ko.Status.ID = resp.TransitGatewayVpcAttachment.TransitGatewayAttachmentId + } else { + ko.Status.ID = nil + } + if resp.TransitGatewayVpcAttachment.TransitGatewayId != nil { + ko.Spec.TransitGatewayID = resp.TransitGatewayVpcAttachment.TransitGatewayId + } else { + ko.Spec.TransitGatewayID = nil + } + if resp.TransitGatewayVpcAttachment.VpcId != nil { + ko.Spec.VPCID = resp.TransitGatewayVpcAttachment.VpcId + } else { + ko.Spec.VPCID = nil + } + if resp.TransitGatewayVpcAttachment.VpcOwnerId != nil { + ko.Status.VPCOwnerID = resp.TransitGatewayVpcAttachment.VpcOwnerId + } else { + ko.Status.VPCOwnerID = nil + } + + rm.setStatusDefaults(ko) + return &resource{ko}, nil +} + +// newUpdateRequestPayload returns an SDK-specific struct for the HTTP request +// payload of the Update API call for the resource +func (rm *resourceManager) newUpdateRequestPayload( + ctx context.Context, + r *resource, + delta *ackcompare.Delta, +) (*svcsdk.ModifyTransitGatewayVpcAttachmentInput, error) { + res := &svcsdk.ModifyTransitGatewayVpcAttachmentInput{} + + if r.ko.Spec.Options != nil { + f2 := &svcsdktypes.ModifyTransitGatewayVpcAttachmentRequestOptions{} + if r.ko.Spec.Options.ApplianceModeSupport != nil { + f2.ApplianceModeSupport = svcsdktypes.ApplianceModeSupportValue(*r.ko.Spec.Options.ApplianceModeSupport) + } + if r.ko.Spec.Options.DNSSupport != nil { + f2.DnsSupport = svcsdktypes.DnsSupportValue(*r.ko.Spec.Options.DNSSupport) + } + if r.ko.Spec.Options.IPv6Support != nil { + f2.Ipv6Support = svcsdktypes.Ipv6SupportValue(*r.ko.Spec.Options.IPv6Support) + } + if r.ko.Spec.Options.SecurityGroupReferencingSupport != nil { + f2.SecurityGroupReferencingSupport = svcsdktypes.SecurityGroupReferencingSupportValue(*r.ko.Spec.Options.SecurityGroupReferencingSupport) + } + res.Options = f2 + } + if r.ko.Status.ID != nil { + res.TransitGatewayAttachmentId = r.ko.Status.ID + } + + return res, nil +} + +// sdkDelete deletes the supplied resource in the backend AWS service API +func (rm *resourceManager) sdkDelete( + ctx context.Context, + r *resource, +) (latest *resource, err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.sdkDelete") + defer func() { + exit(err) + }() + input, err := rm.newDeleteRequestPayload(r) + if err != nil { + return nil, err + } + var resp *svcsdk.DeleteTransitGatewayVpcAttachmentOutput + _ = resp + resp, err = rm.sdkapi.DeleteTransitGatewayVpcAttachment(ctx, input) + rm.metrics.RecordAPICall("DELETE", "DeleteTransitGatewayVpcAttachment", err) + return nil, err +} + +// newDeleteRequestPayload returns an SDK-specific struct for the HTTP request +// payload of the Delete API call for the resource +func (rm *resourceManager) newDeleteRequestPayload( + r *resource, +) (*svcsdk.DeleteTransitGatewayVpcAttachmentInput, error) { + res := &svcsdk.DeleteTransitGatewayVpcAttachmentInput{} + + if r.ko.Status.ID != nil { + res.TransitGatewayAttachmentId = r.ko.Status.ID + } + + return res, nil +} + +// setStatusDefaults sets default properties into supplied custom resource +func (rm *resourceManager) setStatusDefaults( + ko *svcapitypes.TransitGatewayVPCAttachment, +) { + if ko.Status.ACKResourceMetadata == nil { + ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{} + } + if ko.Status.ACKResourceMetadata.Region == nil { + ko.Status.ACKResourceMetadata.Region = &rm.awsRegion + } + if ko.Status.ACKResourceMetadata.OwnerAccountID == nil { + ko.Status.ACKResourceMetadata.OwnerAccountID = &rm.awsAccountID + } + if ko.Status.Conditions == nil { + ko.Status.Conditions = []*ackv1alpha1.Condition{} + } +} + +// updateConditions returns updated resource, true; if conditions were updated +// else it returns nil, false +func (rm *resourceManager) updateConditions( + r *resource, + onSuccess bool, + err error, +) (*resource, bool) { + ko := r.ko.DeepCopy() + rm.setStatusDefaults(ko) + + // Terminal condition + var terminalCondition *ackv1alpha1.Condition = nil + var recoverableCondition *ackv1alpha1.Condition = nil + var syncCondition *ackv1alpha1.Condition = nil + for _, condition := range ko.Status.Conditions { + if condition.Type == ackv1alpha1.ConditionTypeTerminal { + terminalCondition = condition + } + if condition.Type == ackv1alpha1.ConditionTypeRecoverable { + recoverableCondition = condition + } + if condition.Type == ackv1alpha1.ConditionTypeResourceSynced { + syncCondition = condition + } + } + var termError *ackerr.TerminalError + if rm.terminalAWSError(err) || err == ackerr.SecretTypeNotSupported || err == ackerr.SecretNotFound || errors.As(err, &termError) { + if terminalCondition == nil { + terminalCondition = &ackv1alpha1.Condition{ + Type: ackv1alpha1.ConditionTypeTerminal, + } + ko.Status.Conditions = append(ko.Status.Conditions, terminalCondition) + } + var errorMessage = "" + if err == ackerr.SecretTypeNotSupported || err == ackerr.SecretNotFound || errors.As(err, &termError) { + errorMessage = err.Error() + } else { + awsErr, _ := ackerr.AWSError(err) + errorMessage = awsErr.Error() + } + terminalCondition.Status = corev1.ConditionTrue + terminalCondition.Message = &errorMessage + } else { + // Clear the terminal condition if no longer present + if terminalCondition != nil { + terminalCondition.Status = corev1.ConditionFalse + terminalCondition.Message = nil + } + // Handling Recoverable Conditions + if err != nil { + if recoverableCondition == nil { + // Add a new Condition containing a non-terminal error + recoverableCondition = &ackv1alpha1.Condition{ + Type: ackv1alpha1.ConditionTypeRecoverable, + } + ko.Status.Conditions = append(ko.Status.Conditions, recoverableCondition) + } + recoverableCondition.Status = corev1.ConditionTrue + awsErr, _ := ackerr.AWSError(err) + errorMessage := err.Error() + if awsErr != nil { + errorMessage = awsErr.Error() + } + recoverableCondition.Message = &errorMessage + } else if recoverableCondition != nil { + recoverableCondition.Status = corev1.ConditionFalse + recoverableCondition.Message = nil + } + } + // Required to avoid the "declared but not used" error in the default case + _ = syncCondition + if terminalCondition != nil || recoverableCondition != nil || syncCondition != nil { + return &resource{ko}, true // updated + } + return nil, false // not updated +} + +// terminalAWSError returns awserr, true; if the supplied error is an aws Error type +// and if the exception indicates that it is a Terminal exception +// 'Terminal' exception are specified in generator configuration +func (rm *resourceManager) terminalAWSError(err error) bool { + // No terminal_errors specified for this resource in generator config + return false +} diff --git a/pkg/resource/transit_gateway_vpc_attachment/tags.go b/pkg/resource/transit_gateway_vpc_attachment/tags.go new file mode 100644 index 00000000..48b12c17 --- /dev/null +++ b/pkg/resource/transit_gateway_vpc_attachment/tags.go @@ -0,0 +1,119 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Code generated by ack-generate. DO NOT EDIT. + +package transit_gateway_vpc_attachment + +import ( + "slices" + "strings" + + acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" + + svcapitypes "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1" +) + +var ( + _ = svcapitypes.TransitGatewayVPCAttachment{} + _ = acktags.NewTags() + ACKSystemTags = []string{"services.k8s.aws/namespace", "services.k8s.aws/controller-version"} +) + +// convertToOrderedACKTags converts the tags parameter into 'acktags.Tags' shape. +// This method helps in creating the hub(acktags.Tags) for merging +// default controller tags with existing resource tags. It also returns a slice +// of keys maintaining the original key Order when the tags are a list +func convertToOrderedACKTags(tags []*svcapitypes.Tag) (acktags.Tags, []string) { + result := acktags.NewTags() + keyOrder := []string{} + + if len(tags) == 0 { + return result, keyOrder + } + for _, t := range tags { + if t.Key != nil { + keyOrder = append(keyOrder, *t.Key) + if t.Value != nil { + result[*t.Key] = *t.Value + } else { + result[*t.Key] = "" + } + } + } + + return result, keyOrder +} + +// fromACKTags converts the tags parameter into []*svcapitypes.Tag shape. +// This method helps in setting the tags back inside AWSResource after merging +// default controller tags with existing resource tags. When a list, +// it maintains the order from original +func fromACKTags(tags acktags.Tags, keyOrder []string) []*svcapitypes.Tag { + result := []*svcapitypes.Tag{} + + for _, k := range keyOrder { + v, ok := tags[k] + if ok { + tag := svcapitypes.Tag{Key: &k, Value: &v} + result = append(result, &tag) + delete(tags, k) + } + } + for k, v := range tags { + tag := svcapitypes.Tag{Key: &k, Value: &v} + result = append(result, &tag) + } + + return result +} + +// ignoreSystemTags ignores tags that have keys that start with "aws:" +// and ACKSystemTags, to avoid patching them to the resourceSpec. +// Eg. resources created with cloudformation have tags that cannot be +// removed by an ACK controller +func ignoreSystemTags(tags acktags.Tags) { + for k := range tags { + if strings.HasPrefix(k, "aws:") || + slices.Contains(ACKSystemTags, k) { + delete(tags, k) + } + } +} + +// syncAWSTags ensures AWS-managed tags (prefixed with "aws:") from the latest resource state +// are preserved in the desired state. This prevents the controller from attempting to +// modify AWS-managed tags, which would result in an error. +// +// AWS-managed tags are automatically added by AWS services (e.g., CloudFormation, Service Catalog) +// and cannot be modified or deleted through normal tag operations. Common examples include: +// - aws:cloudformation:stack-name +// - aws:servicecatalog:productArn +// +// Parameters: +// - a: The target Tags map to be updated (typically desired state) +// - b: The source Tags map containing AWS-managed tags (typically latest state) +// +// Example: +// +// latest := Tags{"aws:cloudformation:stack-name": "my-stack", "environment": "prod"} +// desired := Tags{"environment": "dev"} +// SyncAWSTags(desired, latest) +// desired now contains {"aws:cloudformation:stack-name": "my-stack", "environment": "dev"} +func syncAWSTags(a acktags.Tags, b acktags.Tags) { + for k := range b { + if strings.HasPrefix(k, "aws:") { + a[k] = b[k] + } + } +} diff --git a/templates/hooks/transit_gateway_vpc_attachment/sdk_create_post_build_request.go.tpl b/templates/hooks/transit_gateway_vpc_attachment/sdk_create_post_build_request.go.tpl new file mode 100644 index 00000000..31eec6c0 --- /dev/null +++ b/templates/hooks/transit_gateway_vpc_attachment/sdk_create_post_build_request.go.tpl @@ -0,0 +1 @@ + updateTagSpecificationsInCreateRequest(desired, input) \ No newline at end of file diff --git a/templates/hooks/transit_gateway_vpc_attachment/sdk_update_post_build_request.go.tpl b/templates/hooks/transit_gateway_vpc_attachment/sdk_update_post_build_request.go.tpl new file mode 100644 index 00000000..42a41306 --- /dev/null +++ b/templates/hooks/transit_gateway_vpc_attachment/sdk_update_post_build_request.go.tpl @@ -0,0 +1,5 @@ + + input.AddSubnetIds, input.RemoveSubnetIds = compareSubnetIDs( + desired.ko.Spec.SubnetIDs, + latest.ko.Spec.SubnetIDs, + ) \ No newline at end of file diff --git a/templates/hooks/transit_gateway_vpc_attachment/sdk_update_pre_build_request.go.tpl b/templates/hooks/transit_gateway_vpc_attachment/sdk_update_pre_build_request.go.tpl new file mode 100644 index 00000000..97043cf8 --- /dev/null +++ b/templates/hooks/transit_gateway_vpc_attachment/sdk_update_pre_build_request.go.tpl @@ -0,0 +1,18 @@ + + if delta.DifferentAt("Spec.Tags") { + if err := syncTags( + ctx, rm.sdkapi, rm.metrics, *latest.ko.Status.ID, + desired.ko.Spec.Tags, latest.ko.Spec.Tags, + ); err != nil { + return nil, err + } + } + + // Only continue if something other than Tags has changed in the Spec + if !delta.DifferentExcept("Spec.Tags") { + return desired, nil + } + + if *latest.ko.Status.State != string(svcsdktypes.TransitGatewayAttachmentStateAvailable) { + return desired, requeueWaitUntilCanModify(desired) + } diff --git a/test/e2e/bootstrap_resources.py b/test/e2e/bootstrap_resources.py index a0da0ebe..c49bcd5e 100644 --- a/test/e2e/bootstrap_resources.py +++ b/test/e2e/bootstrap_resources.py @@ -18,6 +18,7 @@ from acktest.bootstrapping.elbv2 import NetworkLoadBalancer from acktest.bootstrapping.s3 import Bucket from acktest.bootstrapping.vpc import VPC +from acktest.bootstrapping.vpc import TransitGateway from e2e import bootstrap_directory @dataclass @@ -26,6 +27,7 @@ class BootstrapResources(Resources): SharedTestVPC: VPC AdoptedVPC: VPC NetworkLoadBalancer: NetworkLoadBalancer + TestTransitGateway: TransitGateway _bootstrap_resources = None diff --git a/test/e2e/requirements.txt b/test/e2e/requirements.txt index 87d59369..fa2eddd3 100644 --- a/test/e2e/requirements.txt +++ b/test/e2e/requirements.txt @@ -1 +1 @@ -acktest @ git+https://github.com/aws-controllers-k8s/test-infra.git@c65dbf86469e2baa226c9b5d390b2870d63f5e1f \ No newline at end of file +acktest @ git+https://github.com/aws-controllers-k8s/test-infra.git@a91e19e2100a997fd5f16e6291c6bfdb85dd51cd \ No newline at end of file diff --git a/test/e2e/resources/transitgateway_vpc_attachment.yaml b/test/e2e/resources/transitgateway_vpc_attachment.yaml new file mode 100644 index 00000000..032a5b84 --- /dev/null +++ b/test/e2e/resources/transitgateway_vpc_attachment.yaml @@ -0,0 +1,12 @@ +apiVersion: ec2.services.k8s.aws/v1alpha1 +kind: TransitGatewayVPCAttachment +metadata: + name: $TGWVA_NAME +spec: + vpcID: $VPC_ID + subnetIDs: + - $SUBNET_ID + transitGatewayID: $TGW_ID + tags: + - key: $TAG_KEY + value: $TAG_VALUE diff --git a/test/e2e/service_bootstrap.py b/test/e2e/service_bootstrap.py index a9ab397b..9231a954 100644 --- a/test/e2e/service_bootstrap.py +++ b/test/e2e/service_bootstrap.py @@ -18,6 +18,7 @@ from acktest.bootstrapping import Resources, BootstrapFailureException from acktest.bootstrapping.elbv2 import NetworkLoadBalancer from acktest.bootstrapping.vpc import VPC +from acktest.bootstrapping.vpc import TransitGateway from acktest.bootstrapping.s3 import Bucket from e2e import bootstrap_directory from e2e.bootstrap_resources import BootstrapResources @@ -35,7 +36,8 @@ def service_bootstrap() -> Resources: "ack-ec2-controller-flow-log-tests", ), NetworkLoadBalancer=NetworkLoadBalancer("e2e-vpc-ep-service-test"), - AdoptedVPC=VPC(name_prefix="e2e-adopted-vpc", num_public_subnet=1, num_private_subnet=0) + AdoptedVPC=VPC(name_prefix="e2e-adopted-vpc", num_public_subnet=1, num_private_subnet=0), + TestTransitGateway=TransitGateway() ) try: diff --git a/test/e2e/tests/helper.py b/test/e2e/tests/helper.py index 14d001c1..9ecdbee2 100644 --- a/test/e2e/tests/helper.py +++ b/test/e2e/tests/helper.py @@ -15,6 +15,7 @@ """ from typing import Union, Dict +import time class EC2Validator: @@ -214,6 +215,42 @@ def assert_transit_gateway(self, tgw_id: str, exists=True): pass assert res_found is exists + def wait_transit_gateway_state(self, tgw_id: str, state: str): + is_state = False + max_tries = 5 + try: + for tries in range(max_tries): + transit_gateway = self.ec2_client.describe_transit_gateways(TransitGatewayIds=[tgw_id]) + if transit_gateway['TransitGateways'][0]['State'] == state: + is_state=True + break + else: + time.sleep(30) + except: + pass + return is_state + + def get_transit_gateway_vpc_attachment(self, attachment_id: str) -> Union[None, Dict]: + try: + aws_res = self.ec2_client.describe_transit_gateway_vpc_attachments(TransitGatewayAttachmentIds=[attachment_id]) + if len(aws_res["TransitGatewayVpcAttachments"]) > 0: + return aws_res["TransitGatewayVpcAttachments"][0] + return None + except self.ec2_client.exceptions.ClientError: + return None + + def assert_transit_gateway_vpc_attachment(self, tgw_id: str, exists=True): + res_found = False + try: + aws_res = self.ec2_client.describe_transit_gateway_vpc_attachments(TransitGatewayAttachmentIds=[tgw_id]) + tgw = aws_res["TransitGateways"][0] + # TransitGateway may take awhile to be removed server-side, so + # treat 'deleting' and 'deleted' states as resource no longer existing + res_found = tgw is not None and tgw['State'] != "deleting" and tgw['State'] != "deleted" + except self.ec2_client.exceptions.ClientError: + pass + assert res_found is exists + def get_vpc(self, vpc_id: str) -> Union[None, Dict]: try: aws_res = self.ec2_client.describe_vpcs(VpcIds=[vpc_id]) diff --git a/test/e2e/tests/test_transitgateway_vpc_attachment.py b/test/e2e/tests/test_transitgateway_vpc_attachment.py new file mode 100644 index 00000000..00bf8dae --- /dev/null +++ b/test/e2e/tests/test_transitgateway_vpc_attachment.py @@ -0,0 +1,191 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You may +# not use this file except in compliance with the License. A copy of the +# License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Integration tests for the Vpc Endpoint API. +""" + +from os import environ +import pytest +import time +import logging + +from acktest import tags +from acktest.resources import random_suffix_name +from acktest.k8s import resource as k8s +from e2e import service_marker, CRD_GROUP, CRD_VERSION, load_ec2_resource +from e2e.replacement_values import REPLACEMENT_VALUES +from e2e.bootstrap_resources import get_bootstrap_resources +from e2e.tests.helper import EC2Validator + +RESOURCE_PLURAL = "transitgatewayvpcattachments" + +CREATE_WAIT_AFTER_SECONDS = 30 +MODIFY_WAIT_AFTER_SECONDS = 30 +WAIT_PERIOD = 30 + +@pytest.fixture +def simple_tgw_attachment(request, ec2_client): + resource_name = random_suffix_name("tgw-attachment-test", 24) + + test_vpc = get_bootstrap_resources().SharedTestVPC + test_tgw = get_bootstrap_resources().TestTransitGateway + + tgw_id = test_tgw.transit_gateway_id + + ec2_validator = EC2Validator(ec2_client) + is_available = ec2_validator.wait_transit_gateway_state(tgw_id=tgw_id, state='available') + assert is_available + + replacements = REPLACEMENT_VALUES.copy() + + replacements["TGWVA_NAME"] = resource_name + replacements["VPC_ID"] = test_vpc.vpc_id + replacements["TGW_ID"] = test_tgw.transit_gateway_id + replacements["SUBNET_ID"] = test_vpc.public_subnets.subnet_ids[0] + + marker = request.node.get_closest_marker("resource_data") + if marker is not None: + data = marker.args[0] + if 'tag_key' in data: + replacements["TAG_KEY"] = data['tag_key'] + if 'tag_value' in data: + replacements["TAG_VALUE"] = data['tag_value'] + + # Load TGW Attachment CR + resource_data = load_ec2_resource( + "transitgateway_vpc_attachment", + additional_replacements=replacements, + ) + logging.debug(resource_data) + + # Create k8s resource + ref = k8s.CustomResourceReference( + CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, + resource_name, namespace="default", + ) + k8s.create_custom_resource(ref, resource_data) + time.sleep(CREATE_WAIT_AFTER_SECONDS) + + cr = k8s.wait_resource_consumed_by_controller(ref) + assert cr is not None + assert k8s.get_resource_exists(ref) + + yield (ref, cr) + + # Try to delete, if doesn't already exist + try: + _, deleted = k8s.delete_custom_resource(ref, 3, 10) + assert deleted + except: + pass + +@service_marker +@pytest.mark.canary +class TestTransitGatewayVPCAttachment: + + @pytest.mark.resource_data({'tag_key': 'initialtagkey', 'tag_value': 'initialtagvalue'}) + def test_crud(self, ec2_client, simple_tgw_attachment): + (ref, cr) = simple_tgw_attachment + + assert k8s.wait_on_condition( + ref, + "ACK.ResourceSynced", + "True", + wait_periods=WAIT_PERIOD, + ) + + time.sleep(CREATE_WAIT_AFTER_SECONDS) + attachment_id = cr["status"]["id"] + + # Check TGW Attachment exists and verify initial tags + ec2_validator = EC2Validator(ec2_client) + attachment = ec2_validator.get_transit_gateway_vpc_attachment(attachment_id) + + assert attachment is not None + + initial_tags = { + "initialtagkey": "initialtagvalue" + } + tags.assert_ack_system_tags( + tags=attachment["Tags"], + ) + tags.assert_equal_without_ack_tags( + expected=initial_tags, + actual=attachment["Tags"], + ) + + # Update tags + updated_tags = [ + { + "key": "updatedtagkey", + "value": "updatedtagvalue", + } + ] + + k8s.patch_custom_resource(ref, {"spec": {"tags": updated_tags}}) + time.sleep(MODIFY_WAIT_AFTER_SECONDS) + + # Verify updated tags + attachment = ec2_validator.get_transit_gateway_vpc_attachment(attachment_id) + expected_tags = { + "updatedtagkey": "updatedtagvalue" + } + tags.assert_ack_system_tags( + tags=attachment["Tags"], + ) + tags.assert_equal_without_ack_tags( + expected=expected_tags, + actual=attachment["Tags"], + ) + + # Update options + # dns support is enabled by default + updates = { + "spec": { + "options": { + "dnsSupport": "disable", + } + } + } + + k8s.patch_custom_resource(ref, updates) + time.sleep(MODIFY_WAIT_AFTER_SECONDS) + + # Check resource synced successfully + assert k8s.wait_on_condition(ref, "ACK.ResourceSynced", "True", wait_periods=WAIT_PERIOD) + + # Verify the update in AWS + ec2_validator = EC2Validator(ec2_client) + attachment = ec2_validator.get_transit_gateway_vpc_attachment(attachment_id) + + assert attachment["Options"]["DnsSupport"] == "disable" + + # Update subnet ids + test_vpc = get_bootstrap_resources().SharedTestVPC + updates = { + "spec": { + "subnetIDs": test_vpc.public_subnets.subnet_ids + } + } + + k8s.patch_custom_resource(ref, updates) + time.sleep(MODIFY_WAIT_AFTER_SECONDS) + + # Check resource synced successfully + assert k8s.wait_on_condition(ref, "ACK.ResourceSynced", "True", wait_periods=WAIT_PERIOD) + + # Verify the update in AWS + ec2_validator = EC2Validator(ec2_client) + attachment = ec2_validator.get_transit_gateway_vpc_attachment(attachment_id) + + assert set(attachment["SubnetIds"]) == set(test_vpc.public_subnets.subnet_ids)