diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index ae0c8170..7ed8ce95 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,13 +1,13 @@ ack_generate_info: - build_date: "2023-09-18T23:03:44Z" - build_hash: 892f29d00a4c4ad21a2fa32919921de18190979d - go_version: go1.20.3 - version: v0.27.1 -api_directory_checksum: 6e2d850d97f2f72db31c9bef522eca4ab95b3fcd + build_date: "2023-12-06T21:00:34Z" + build_hash: 1cc9b5172d3d1676af578a3411e8672698ec29ce + go_version: go1.21.0 + version: 1cc9b51 +api_directory_checksum: d452bf19bfd1496aacdc215bf7cc9ea86c55c122 api_version: v1alpha1 aws_sdk_go_version: v1.44.93 generator_config_info: - file_checksum: c474e6cdec9622afad4b4cef415483a7bd0eda41 + file_checksum: 86b3e3aa1ff4769894d475244d0cc5902bcb258f original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index 245abe3c..a01661f3 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -36,6 +36,8 @@ ignore: - CreateVpcEndpointInput.DryRun - CreateVpcEndpointInput.TagSpecifications - CreateVpcEndpointInput.ClientToken + - CreateVpcPeeringConnectionInput.DryRun + - CreateVpcPeeringConnectionInput.TagSpecifications - DeleteRouteInput.DryRun - DeleteRouteInput.RouteTableId # support EC2-VPC only @@ -122,7 +124,7 @@ ignore: #- VpcEndpoint #- Vpc - VpcCidrBlock - - VpcPeeringConnection + #- VpcPeeringConnection - VpnConnectionRoute - VpnConnection - VpnGateway @@ -184,6 +186,10 @@ operations: operation_type: - Delete resource_name: Instance + ModifyVpcPeeringConnectionOptions: + operation_type: + - Update + resource_name: VpcPeeringConnection resources: DhcpOptions: exceptions: @@ -468,6 +474,10 @@ resources: references: resource: VPCEndpoint path: Status.VPCEndpointID + Routes.VPCPeeringConnectionID: + references: + resource: VPCPeeringConnection + path: Status.VPCPeeringConnectionID hooks: delta_pre_compare: code: customPreCompare(delta, a, b) @@ -779,5 +789,45 @@ resources: sdk_file_end: template_path: hooks/vpc_endpoint/sdk_file_end.go.tpl update_operation: - custom_method_name: customUpdateVPCEndpoint - + custom_method_name: customUpdateVPCEndpoint + VpcPeeringConnection: + fields: + VpcId: + references: + resource: VPC + path: Status.VPCID + peerVpcId: + references: + resource: VPC + path: Status.VPCID + AcceptRequest: + type: bool + AccepterPeeringConnectionOptions: + from: + operation: ModifyVpcPeeringConnectionOptions + path: AccepterPeeringConnectionOptions + RequesterPeeringConnectionOptions: + from: + operation: ModifyVpcPeeringConnectionOptions + path: RequesterPeeringConnectionOptions + Tags: + from: + operation: CreateTags + path: Tags + compare: + is_ignored: True + update_operation: + omit_unchanged_fields: true + hooks: + delta_pre_compare: + code: compareTags(delta, a, b) + sdk_create_post_build_request: + template_path: hooks/vpc_peering_connection/sdk_create_post_build_request.go.tpl + sdk_create_post_set_output: + template_path: hooks/vpc_peering_connection/sdk_create_post_set_output.go.tpl + sdk_update_pre_build_request: + template_path: hooks/vpc_peering_connection/sdk_update_pre_build_request.go.tpl + sdk_read_many_post_set_output: + template_path: hooks/vpc_peering_connection/sdk_read_many_post_set_output.go.tpl + sdk_file_end: + template_path: hooks/vpc_peering_connection/sdk_file_end.go.tpl \ No newline at end of file diff --git a/apis/v1alpha1/types.go b/apis/v1alpha1/types.go index 2b3c929f..d5084d1c 100644 --- a/apis/v1alpha1/types.go +++ b/apis/v1alpha1/types.go @@ -678,6 +678,8 @@ type CreateRouteInput struct { // Reference field for VPCEndpointID VPCEndpointRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"vpcEndpointRef,omitempty"` VPCPeeringConnectionID *string `json:"vpcPeeringConnectionID,omitempty"` + // Reference field for VPCPeeringConnectionID + VPCPeeringConnectionRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"vpcPeeringConnectionRef,omitempty"` } // Describes the options for a VPC attachment. @@ -4674,13 +4676,6 @@ type VPCIPv6CIDRBlockAssociation struct { NetworkBorderGroup *string `json:"networkBorderGroup,omitempty"` } -// Describes a VPC peering connection. -type VPCPeeringConnection struct { - ExpirationTime *metav1.Time `json:"expirationTime,omitempty"` - Tags []*Tag `json:"tags,omitempty"` - VPCPeeringConnectionID *string `json:"vpcPeeringConnectionID,omitempty"` -} - // We are retiring EC2-Classic on August 15, 2022. We recommend that you migrate // from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic // to a VPC (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/vpc-migrate.html) @@ -4695,15 +4690,39 @@ type VPCPeeringConnectionOptionsDescription struct { // Describes the status of a VPC peering connection. type VPCPeeringConnectionStateReason struct { + Code *string `json:"code,omitempty"` Message *string `json:"message,omitempty"` } // Describes a VPC in a VPC peering connection. type VPCPeeringConnectionVPCInfo struct { - CIDRBlock *string `json:"cidrBlock,omitempty"` - OwnerID *string `json:"ownerID,omitempty"` - Region *string `json:"region,omitempty"` - VPCID *string `json:"vpcID,omitempty"` + CIDRBlock *string `json:"cidrBlock,omitempty"` + CIDRBlockSet []*CIDRBlock `json:"cidrBlockSet,omitempty"` + IPv6CIDRBlockSet []*IPv6CIDRBlock `json:"ipv6CIDRBlockSet,omitempty"` + OwnerID *string `json:"ownerID,omitempty"` + // + // We are retiring EC2-Classic on August 15, 2022. We recommend that you migrate + // from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic + // to a VPC (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/vpc-migrate.html) + // in the Amazon Elastic Compute Cloud User Guide. + // + // Describes the VPC peering connection options. + PeeringOptions *VPCPeeringConnectionOptionsDescription `json:"peeringOptions,omitempty"` + Region *string `json:"region,omitempty"` + VPCID *string `json:"vpcID,omitempty"` +} + +// Describes a VPC peering connection. +type VPCPeeringConnection_SDK struct { + // Describes a VPC in a VPC peering connection. + AccepterVPCInfo *VPCPeeringConnectionVPCInfo `json:"accepterVPCInfo,omitempty"` + ExpirationTime *metav1.Time `json:"expirationTime,omitempty"` + // Describes a VPC in a VPC peering connection. + RequesterVPCInfo *VPCPeeringConnectionVPCInfo `json:"requesterVPCInfo,omitempty"` + // Describes the status of a VPC peering connection. + Status *VPCPeeringConnectionStateReason `json:"status,omitempty"` + Tags []*Tag `json:"tags,omitempty"` + VPCPeeringConnectionID *string `json:"vpcPeeringConnectionID,omitempty"` } // Describes a VPC. diff --git a/apis/v1alpha1/vpc_peering_connection.go b/apis/v1alpha1/vpc_peering_connection.go new file mode 100644 index 00000000..5f60302c --- /dev/null +++ b/apis/v1alpha1/vpc_peering_connection.go @@ -0,0 +1,106 @@ +// 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" +) + +// VpcPeeringConnectionSpec defines the desired state of VpcPeeringConnection. +// +// Describes a VPC peering connection. +type VPCPeeringConnectionSpec struct { + AcceptRequest *bool `json:"acceptRequest,omitempty"` + // The VPC peering connection options for the accepter VPC. + AccepterPeeringConnectionOptions *PeeringConnectionOptionsRequest `json:"accepterPeeringConnectionOptions,omitempty"` + // The Amazon Web Services account ID of the owner of the accepter VPC. + // + // Default: Your Amazon Web Services account ID + PeerOwnerID *string `json:"peerOwnerID,omitempty"` + // The Region code for the accepter VPC, if the accepter VPC is located in a + // Region other than the Region in which you make the request. + // + // Default: The Region in which you make the request. + PeerRegion *string `json:"peerRegion,omitempty"` + // The ID of the VPC with which you are creating the VPC peering connection. + // You must specify this parameter in the request. + PeerVPCID *string `json:"peerVPCID,omitempty"` + PeerVPCRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"peerVPCRef,omitempty"` + // The VPC peering connection options for the requester VPC. + RequesterPeeringConnectionOptions *PeeringConnectionOptionsRequest `json:"requesterPeeringConnectionOptions,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 requester VPC. You must specify this parameter in the request. + VPCID *string `json:"vpcID,omitempty"` + VPCRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"vpcRef,omitempty"` +} + +// VPCPeeringConnectionStatus defines the observed state of VPCPeeringConnection +type VPCPeeringConnectionStatus 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"` + // Information about the accepter VPC. CIDR block information is only returned + // when describing an active VPC peering connection. + // +kubebuilder:validation:Optional + AccepterVPCInfo *VPCPeeringConnectionVPCInfo `json:"accepterVPCInfo,omitempty"` + // The time that an unaccepted VPC peering connection will expire. + // +kubebuilder:validation:Optional + ExpirationTime *metav1.Time `json:"expirationTime,omitempty"` + // Information about the requester VPC. CIDR block information is only returned + // when describing an active VPC peering connection. + // +kubebuilder:validation:Optional + RequesterVPCInfo *VPCPeeringConnectionVPCInfo `json:"requesterVPCInfo,omitempty"` + // The status of the VPC peering connection. + // +kubebuilder:validation:Optional + Status *VPCPeeringConnectionStateReason `json:"status,omitempty"` + // The ID of the VPC peering connection. + // +kubebuilder:validation:Optional + VPCPeeringConnectionID *string `json:"vpcPeeringConnectionID,omitempty"` +} + +// VPCPeeringConnection is the Schema for the VPCPeeringConnections API +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +type VPCPeeringConnection struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec VPCPeeringConnectionSpec `json:"spec,omitempty"` + Status VPCPeeringConnectionStatus `json:"status,omitempty"` +} + +// VPCPeeringConnectionList contains a list of VPCPeeringConnection +// +kubebuilder:object:root=true +type VPCPeeringConnectionList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []VPCPeeringConnection `json:"items"` +} + +func init() { + SchemeBuilder.Register(&VPCPeeringConnection{}, &VPCPeeringConnectionList{}) +} diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 610526d4..bfad28d1 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -2650,6 +2650,11 @@ func (in *CreateRouteInput) DeepCopyInto(out *CreateRouteInput) { *out = new(string) **out = **in } + if in.VPCPeeringConnectionRef != nil { + in, out := &in.VPCPeeringConnectionRef, &out.VPCPeeringConnectionRef + *out = new(corev1alpha1.AWSResourceReferenceWrapper) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CreateRouteInput. @@ -22230,38 +22235,62 @@ func (in *VPCList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VPCPeeringConnection) DeepCopyInto(out *VPCPeeringConnection) { *out = *in - if in.ExpirationTime != nil { - in, out := &in.ExpirationTime, &out.ExpirationTime - *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 VPCPeeringConnection. +func (in *VPCPeeringConnection) DeepCopy() *VPCPeeringConnection { + if in == nil { + return nil } - if in.Tags != nil { - in, out := &in.Tags, &out.Tags - *out = make([]*Tag, len(*in)) + out := new(VPCPeeringConnection) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VPCPeeringConnection) 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 *VPCPeeringConnectionList) DeepCopyInto(out *VPCPeeringConnectionList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VPCPeeringConnection, len(*in)) for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(Tag) - (*in).DeepCopyInto(*out) - } + (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.VPCPeeringConnectionID != nil { - in, out := &in.VPCPeeringConnectionID, &out.VPCPeeringConnectionID - *out = new(string) - **out = **in - } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VPCPeeringConnection. -func (in *VPCPeeringConnection) DeepCopy() *VPCPeeringConnection { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VPCPeeringConnectionList. +func (in *VPCPeeringConnectionList) DeepCopy() *VPCPeeringConnectionList { if in == nil { return nil } - out := new(VPCPeeringConnection) + out := new(VPCPeeringConnectionList) in.DeepCopyInto(out) return out } +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VPCPeeringConnectionList) 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 *VPCPeeringConnectionOptionsDescription) DeepCopyInto(out *VPCPeeringConnectionOptionsDescription) { *out = *in @@ -22292,9 +22321,85 @@ func (in *VPCPeeringConnectionOptionsDescription) DeepCopy() *VPCPeeringConnecti return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VPCPeeringConnectionSpec) DeepCopyInto(out *VPCPeeringConnectionSpec) { + *out = *in + if in.AcceptRequest != nil { + in, out := &in.AcceptRequest, &out.AcceptRequest + *out = new(bool) + **out = **in + } + if in.AccepterPeeringConnectionOptions != nil { + in, out := &in.AccepterPeeringConnectionOptions, &out.AccepterPeeringConnectionOptions + *out = new(PeeringConnectionOptionsRequest) + (*in).DeepCopyInto(*out) + } + if in.PeerOwnerID != nil { + in, out := &in.PeerOwnerID, &out.PeerOwnerID + *out = new(string) + **out = **in + } + if in.PeerRegion != nil { + in, out := &in.PeerRegion, &out.PeerRegion + *out = new(string) + **out = **in + } + if in.PeerVPCID != nil { + in, out := &in.PeerVPCID, &out.PeerVPCID + *out = new(string) + **out = **in + } + if in.PeerVPCRef != nil { + in, out := &in.PeerVPCRef, &out.PeerVPCRef + *out = new(corev1alpha1.AWSResourceReferenceWrapper) + (*in).DeepCopyInto(*out) + } + if in.RequesterPeeringConnectionOptions != nil { + in, out := &in.RequesterPeeringConnectionOptions, &out.RequesterPeeringConnectionOptions + *out = new(PeeringConnectionOptionsRequest) + (*in).DeepCopyInto(*out) + } + 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.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 VPCPeeringConnectionSpec. +func (in *VPCPeeringConnectionSpec) DeepCopy() *VPCPeeringConnectionSpec { + if in == nil { + return nil + } + out := new(VPCPeeringConnectionSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VPCPeeringConnectionStateReason) DeepCopyInto(out *VPCPeeringConnectionStateReason) { *out = *in + if in.Code != nil { + in, out := &in.Code, &out.Code + *out = new(string) + **out = **in + } if in.Message != nil { in, out := &in.Message, &out.Message *out = new(string) @@ -22312,6 +22417,61 @@ func (in *VPCPeeringConnectionStateReason) DeepCopy() *VPCPeeringConnectionState return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VPCPeeringConnectionStatus) DeepCopyInto(out *VPCPeeringConnectionStatus) { + *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.AccepterVPCInfo != nil { + in, out := &in.AccepterVPCInfo, &out.AccepterVPCInfo + *out = new(VPCPeeringConnectionVPCInfo) + (*in).DeepCopyInto(*out) + } + if in.ExpirationTime != nil { + in, out := &in.ExpirationTime, &out.ExpirationTime + *out = (*in).DeepCopy() + } + if in.RequesterVPCInfo != nil { + in, out := &in.RequesterVPCInfo, &out.RequesterVPCInfo + *out = new(VPCPeeringConnectionVPCInfo) + (*in).DeepCopyInto(*out) + } + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(VPCPeeringConnectionStateReason) + (*in).DeepCopyInto(*out) + } + if in.VPCPeeringConnectionID != nil { + in, out := &in.VPCPeeringConnectionID, &out.VPCPeeringConnectionID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VPCPeeringConnectionStatus. +func (in *VPCPeeringConnectionStatus) DeepCopy() *VPCPeeringConnectionStatus { + if in == nil { + return nil + } + out := new(VPCPeeringConnectionStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VPCPeeringConnectionVPCInfo) DeepCopyInto(out *VPCPeeringConnectionVPCInfo) { *out = *in @@ -22320,11 +22480,38 @@ func (in *VPCPeeringConnectionVPCInfo) DeepCopyInto(out *VPCPeeringConnectionVPC *out = new(string) **out = **in } + if in.CIDRBlockSet != nil { + in, out := &in.CIDRBlockSet, &out.CIDRBlockSet + *out = make([]*CIDRBlock, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(CIDRBlock) + (*in).DeepCopyInto(*out) + } + } + } + if in.IPv6CIDRBlockSet != nil { + in, out := &in.IPv6CIDRBlockSet, &out.IPv6CIDRBlockSet + *out = make([]*IPv6CIDRBlock, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(IPv6CIDRBlock) + (*in).DeepCopyInto(*out) + } + } + } if in.OwnerID != nil { in, out := &in.OwnerID, &out.OwnerID *out = new(string) **out = **in } + if in.PeeringOptions != nil { + in, out := &in.PeeringOptions, &out.PeeringOptions + *out = new(VPCPeeringConnectionOptionsDescription) + (*in).DeepCopyInto(*out) + } if in.Region != nil { in, out := &in.Region, &out.Region *out = new(string) @@ -22347,6 +22534,56 @@ func (in *VPCPeeringConnectionVPCInfo) DeepCopy() *VPCPeeringConnectionVPCInfo { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VPCPeeringConnection_SDK) DeepCopyInto(out *VPCPeeringConnection_SDK) { + *out = *in + if in.AccepterVPCInfo != nil { + in, out := &in.AccepterVPCInfo, &out.AccepterVPCInfo + *out = new(VPCPeeringConnectionVPCInfo) + (*in).DeepCopyInto(*out) + } + if in.ExpirationTime != nil { + in, out := &in.ExpirationTime, &out.ExpirationTime + *out = (*in).DeepCopy() + } + if in.RequesterVPCInfo != nil { + in, out := &in.RequesterVPCInfo, &out.RequesterVPCInfo + *out = new(VPCPeeringConnectionVPCInfo) + (*in).DeepCopyInto(*out) + } + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(VPCPeeringConnectionStateReason) + (*in).DeepCopyInto(*out) + } + 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.VPCPeeringConnectionID != nil { + in, out := &in.VPCPeeringConnectionID, &out.VPCPeeringConnectionID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VPCPeeringConnection_SDK. +func (in *VPCPeeringConnection_SDK) DeepCopy() *VPCPeeringConnection_SDK { + if in == nil { + return nil + } + out := new(VPCPeeringConnection_SDK) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VPCSpec) DeepCopyInto(out *VPCSpec) { *out = *in diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 4e3eda7a..6381f23e 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -48,6 +48,7 @@ import ( _ "github.com/aws-controllers-k8s/ec2-controller/pkg/resource/transit_gateway" _ "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_peering_connection" "github.com/aws-controllers-k8s/ec2-controller/pkg/version" ) diff --git a/config/crd/bases/ec2.services.k8s.aws_routetables.yaml b/config/crd/bases/ec2.services.k8s.aws_routetables.yaml index cf0270b5..cc938cb3 100644 --- a/config/crd/bases/ec2.services.k8s.aws_routetables.yaml +++ b/config/crd/bases/ec2.services.k8s.aws_routetables.yaml @@ -119,6 +119,18 @@ spec: type: object vpcPeeringConnectionID: type: string + vpcPeeringConnectionRef: + description: Reference field for VPCPeeringConnectionID + properties: + from: + description: AWSResourceReference provides all the values + necessary to reference another k8s resource for finding + the identifier(Id/ARN/Name) + properties: + name: + type: string + type: object + type: object type: object type: array tags: diff --git a/config/crd/bases/ec2.services.k8s.aws_vpcpeeringconnections.yaml b/config/crd/bases/ec2.services.k8s.aws_vpcpeeringconnections.yaml new file mode 100644 index 00000000..e19de7ca --- /dev/null +++ b/config/crd/bases/ec2.services.k8s.aws_vpcpeeringconnections.yaml @@ -0,0 +1,292 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: vpcpeeringconnections.ec2.services.k8s.aws +spec: + group: ec2.services.k8s.aws + names: + kind: VPCPeeringConnection + listKind: VPCPeeringConnectionList + plural: vpcpeeringconnections + singular: vpcpeeringconnection + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: VPCPeeringConnection is the Schema for the VPCPeeringConnections + 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: "VpcPeeringConnectionSpec defines the desired state of VpcPeeringConnection. + \n Describes a VPC peering connection." + properties: + acceptRequest: + type: boolean + accepterPeeringConnectionOptions: + description: The VPC peering connection options for the accepter VPC. + properties: + allowDNSResolutionFromRemoteVPC: + type: boolean + allowEgressFromLocalClassicLinkToRemoteVPC: + type: boolean + allowEgressFromLocalVPCToRemoteClassicLink: + type: boolean + type: object + peerOwnerID: + description: "The Amazon Web Services account ID of the owner of the + accepter VPC. \n Default: Your Amazon Web Services account ID" + type: string + peerRegion: + description: "The Region code for the accepter VPC, if the accepter + VPC is located in a Region other than the Region in which you make + the request. \n Default: The Region in which you make the request." + type: string + peerVPCID: + description: The ID of the VPC with which you are creating the VPC + peering connection. You must specify this parameter in the request. + type: string + peerVPCRef: + description: "AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference type to provide more user friendly syntax + for references using 'from' field Ex: APIIDRef: \n from: 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 + type: object + type: object + requesterPeeringConnectionOptions: + description: The VPC peering connection options for the requester + VPC. + properties: + allowDNSResolutionFromRemoteVPC: + type: boolean + allowEgressFromLocalClassicLinkToRemoteVPC: + type: boolean + allowEgressFromLocalVPCToRemoteClassicLink: + type: boolean + type: object + 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 + vpcID: + description: The ID of the requester VPC. You must specify this parameter + in the request. + type: string + vpcRef: + description: "AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference type to provide more user friendly syntax + for references using 'from' field Ex: APIIDRef: \n from: 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 + type: object + type: object + type: object + status: + description: VPCPeeringConnectionStatus defines the observed state of + VPCPeeringConnection + properties: + accepterVPCInfo: + description: Information about the accepter VPC. CIDR block information + is only returned when describing an active VPC peering connection. + properties: + cidrBlock: + type: string + cidrBlockSet: + items: + description: Describes an IPv4 CIDR block. + properties: + cidrBlock: + type: string + type: object + type: array + ipv6CIDRBlockSet: + items: + description: Describes an IPv6 CIDR block. + properties: + ipv6CIDRBlock: + type: string + type: object + type: array + ownerID: + type: string + peeringOptions: + description: "We are retiring EC2-Classic on August 15, 2022. + We recommend that you migrate from EC2-Classic to a VPC. For + more information, see Migrate from EC2-Classic to a VPC (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/vpc-migrate.html) + in the Amazon Elastic Compute Cloud User Guide. \n Describes + the VPC peering connection options." + properties: + allowDNSResolutionFromRemoteVPC: + type: boolean + allowEgressFromLocalClassicLinkToRemoteVPC: + type: boolean + allowEgressFromLocalVPCToRemoteClassicLink: + type: boolean + type: object + region: + type: string + vpcID: + type: string + type: object + 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. TODO(vijat@): Find a better + strategy for resources that do not have ARN in CreateOutputResponse + 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 + expirationTime: + description: The time that an unaccepted VPC peering connection will + expire. + format: date-time + type: string + requesterVPCInfo: + description: Information about the requester VPC. CIDR block information + is only returned when describing an active VPC peering connection. + properties: + cidrBlock: + type: string + cidrBlockSet: + items: + description: Describes an IPv4 CIDR block. + properties: + cidrBlock: + type: string + type: object + type: array + ipv6CIDRBlockSet: + items: + description: Describes an IPv6 CIDR block. + properties: + ipv6CIDRBlock: + type: string + type: object + type: array + ownerID: + type: string + peeringOptions: + description: "We are retiring EC2-Classic on August 15, 2022. + We recommend that you migrate from EC2-Classic to a VPC. For + more information, see Migrate from EC2-Classic to a VPC (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/vpc-migrate.html) + in the Amazon Elastic Compute Cloud User Guide. \n Describes + the VPC peering connection options." + properties: + allowDNSResolutionFromRemoteVPC: + type: boolean + allowEgressFromLocalClassicLinkToRemoteVPC: + type: boolean + allowEgressFromLocalVPCToRemoteClassicLink: + type: boolean + type: object + region: + type: string + vpcID: + type: string + type: object + status: + description: The status of the VPC peering connection. + properties: + code: + type: string + message: + type: string + type: object + vpcPeeringConnectionID: + description: The ID of the VPC peering connection. + 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 c6982e5f..e209741b 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -15,3 +15,4 @@ resources: - bases/ec2.services.k8s.aws_transitgateways.yaml - bases/ec2.services.k8s.aws_vpcs.yaml - bases/ec2.services.k8s.aws_vpcendpoints.yaml + - bases/ec2.services.k8s.aws_vpcpeeringconnections.yaml diff --git a/config/rbac/cluster-role-controller.yaml b/config/rbac/cluster-role-controller.yaml index 80e6076b..32e14d6f 100644 --- a/config/rbac/cluster-role-controller.yaml +++ b/config/rbac/cluster-role-controller.yaml @@ -271,6 +271,26 @@ rules: - get - patch - update +- apiGroups: + - ec2.services.k8s.aws + resources: + - vpcpeeringconnections + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - ec2.services.k8s.aws + resources: + - vpcpeeringconnections/status + verbs: + - get + - patch + - update - apiGroups: - ec2.services.k8s.aws resources: diff --git a/config/rbac/role-reader.yaml b/config/rbac/role-reader.yaml index 505cf354..29deaf69 100644 --- a/config/rbac/role-reader.yaml +++ b/config/rbac/role-reader.yaml @@ -22,6 +22,7 @@ rules: - transitgateways - vpcs - vpcendpoints + - vpcpeeringconnections verbs: - get - list diff --git a/config/rbac/role-writer.yaml b/config/rbac/role-writer.yaml index b8b76c0e..386677e8 100644 --- a/config/rbac/role-writer.yaml +++ b/config/rbac/role-writer.yaml @@ -22,6 +22,7 @@ rules: - transitgateways - vpcs - vpcendpoints + - vpcpeeringconnections verbs: - create - delete @@ -46,6 +47,7 @@ rules: - transitgateways - vpcs - vpcendpoints + - vpcpeeringconnections verbs: - get - patch diff --git a/generator.yaml b/generator.yaml index 245abe3c..a01661f3 100644 --- a/generator.yaml +++ b/generator.yaml @@ -36,6 +36,8 @@ ignore: - CreateVpcEndpointInput.DryRun - CreateVpcEndpointInput.TagSpecifications - CreateVpcEndpointInput.ClientToken + - CreateVpcPeeringConnectionInput.DryRun + - CreateVpcPeeringConnectionInput.TagSpecifications - DeleteRouteInput.DryRun - DeleteRouteInput.RouteTableId # support EC2-VPC only @@ -122,7 +124,7 @@ ignore: #- VpcEndpoint #- Vpc - VpcCidrBlock - - VpcPeeringConnection + #- VpcPeeringConnection - VpnConnectionRoute - VpnConnection - VpnGateway @@ -184,6 +186,10 @@ operations: operation_type: - Delete resource_name: Instance + ModifyVpcPeeringConnectionOptions: + operation_type: + - Update + resource_name: VpcPeeringConnection resources: DhcpOptions: exceptions: @@ -468,6 +474,10 @@ resources: references: resource: VPCEndpoint path: Status.VPCEndpointID + Routes.VPCPeeringConnectionID: + references: + resource: VPCPeeringConnection + path: Status.VPCPeeringConnectionID hooks: delta_pre_compare: code: customPreCompare(delta, a, b) @@ -779,5 +789,45 @@ resources: sdk_file_end: template_path: hooks/vpc_endpoint/sdk_file_end.go.tpl update_operation: - custom_method_name: customUpdateVPCEndpoint - + custom_method_name: customUpdateVPCEndpoint + VpcPeeringConnection: + fields: + VpcId: + references: + resource: VPC + path: Status.VPCID + peerVpcId: + references: + resource: VPC + path: Status.VPCID + AcceptRequest: + type: bool + AccepterPeeringConnectionOptions: + from: + operation: ModifyVpcPeeringConnectionOptions + path: AccepterPeeringConnectionOptions + RequesterPeeringConnectionOptions: + from: + operation: ModifyVpcPeeringConnectionOptions + path: RequesterPeeringConnectionOptions + Tags: + from: + operation: CreateTags + path: Tags + compare: + is_ignored: True + update_operation: + omit_unchanged_fields: true + hooks: + delta_pre_compare: + code: compareTags(delta, a, b) + sdk_create_post_build_request: + template_path: hooks/vpc_peering_connection/sdk_create_post_build_request.go.tpl + sdk_create_post_set_output: + template_path: hooks/vpc_peering_connection/sdk_create_post_set_output.go.tpl + sdk_update_pre_build_request: + template_path: hooks/vpc_peering_connection/sdk_update_pre_build_request.go.tpl + sdk_read_many_post_set_output: + template_path: hooks/vpc_peering_connection/sdk_read_many_post_set_output.go.tpl + sdk_file_end: + template_path: hooks/vpc_peering_connection/sdk_file_end.go.tpl \ No newline at end of file diff --git a/helm/crds/ec2.services.k8s.aws_routetables.yaml b/helm/crds/ec2.services.k8s.aws_routetables.yaml index cf0270b5..cc938cb3 100644 --- a/helm/crds/ec2.services.k8s.aws_routetables.yaml +++ b/helm/crds/ec2.services.k8s.aws_routetables.yaml @@ -119,6 +119,18 @@ spec: type: object vpcPeeringConnectionID: type: string + vpcPeeringConnectionRef: + description: Reference field for VPCPeeringConnectionID + properties: + from: + description: AWSResourceReference provides all the values + necessary to reference another k8s resource for finding + the identifier(Id/ARN/Name) + properties: + name: + type: string + type: object + type: object type: object type: array tags: diff --git a/helm/crds/ec2.services.k8s.aws_vpcpeeringconnections.yaml b/helm/crds/ec2.services.k8s.aws_vpcpeeringconnections.yaml new file mode 100644 index 00000000..e19de7ca --- /dev/null +++ b/helm/crds/ec2.services.k8s.aws_vpcpeeringconnections.yaml @@ -0,0 +1,292 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: vpcpeeringconnections.ec2.services.k8s.aws +spec: + group: ec2.services.k8s.aws + names: + kind: VPCPeeringConnection + listKind: VPCPeeringConnectionList + plural: vpcpeeringconnections + singular: vpcpeeringconnection + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: VPCPeeringConnection is the Schema for the VPCPeeringConnections + 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: "VpcPeeringConnectionSpec defines the desired state of VpcPeeringConnection. + \n Describes a VPC peering connection." + properties: + acceptRequest: + type: boolean + accepterPeeringConnectionOptions: + description: The VPC peering connection options for the accepter VPC. + properties: + allowDNSResolutionFromRemoteVPC: + type: boolean + allowEgressFromLocalClassicLinkToRemoteVPC: + type: boolean + allowEgressFromLocalVPCToRemoteClassicLink: + type: boolean + type: object + peerOwnerID: + description: "The Amazon Web Services account ID of the owner of the + accepter VPC. \n Default: Your Amazon Web Services account ID" + type: string + peerRegion: + description: "The Region code for the accepter VPC, if the accepter + VPC is located in a Region other than the Region in which you make + the request. \n Default: The Region in which you make the request." + type: string + peerVPCID: + description: The ID of the VPC with which you are creating the VPC + peering connection. You must specify this parameter in the request. + type: string + peerVPCRef: + description: "AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference type to provide more user friendly syntax + for references using 'from' field Ex: APIIDRef: \n from: 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 + type: object + type: object + requesterPeeringConnectionOptions: + description: The VPC peering connection options for the requester + VPC. + properties: + allowDNSResolutionFromRemoteVPC: + type: boolean + allowEgressFromLocalClassicLinkToRemoteVPC: + type: boolean + allowEgressFromLocalVPCToRemoteClassicLink: + type: boolean + type: object + 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 + vpcID: + description: The ID of the requester VPC. You must specify this parameter + in the request. + type: string + vpcRef: + description: "AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference type to provide more user friendly syntax + for references using 'from' field Ex: APIIDRef: \n from: 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 + type: object + type: object + type: object + status: + description: VPCPeeringConnectionStatus defines the observed state of + VPCPeeringConnection + properties: + accepterVPCInfo: + description: Information about the accepter VPC. CIDR block information + is only returned when describing an active VPC peering connection. + properties: + cidrBlock: + type: string + cidrBlockSet: + items: + description: Describes an IPv4 CIDR block. + properties: + cidrBlock: + type: string + type: object + type: array + ipv6CIDRBlockSet: + items: + description: Describes an IPv6 CIDR block. + properties: + ipv6CIDRBlock: + type: string + type: object + type: array + ownerID: + type: string + peeringOptions: + description: "We are retiring EC2-Classic on August 15, 2022. + We recommend that you migrate from EC2-Classic to a VPC. For + more information, see Migrate from EC2-Classic to a VPC (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/vpc-migrate.html) + in the Amazon Elastic Compute Cloud User Guide. \n Describes + the VPC peering connection options." + properties: + allowDNSResolutionFromRemoteVPC: + type: boolean + allowEgressFromLocalClassicLinkToRemoteVPC: + type: boolean + allowEgressFromLocalVPCToRemoteClassicLink: + type: boolean + type: object + region: + type: string + vpcID: + type: string + type: object + 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. TODO(vijat@): Find a better + strategy for resources that do not have ARN in CreateOutputResponse + 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 + expirationTime: + description: The time that an unaccepted VPC peering connection will + expire. + format: date-time + type: string + requesterVPCInfo: + description: Information about the requester VPC. CIDR block information + is only returned when describing an active VPC peering connection. + properties: + cidrBlock: + type: string + cidrBlockSet: + items: + description: Describes an IPv4 CIDR block. + properties: + cidrBlock: + type: string + type: object + type: array + ipv6CIDRBlockSet: + items: + description: Describes an IPv6 CIDR block. + properties: + ipv6CIDRBlock: + type: string + type: object + type: array + ownerID: + type: string + peeringOptions: + description: "We are retiring EC2-Classic on August 15, 2022. + We recommend that you migrate from EC2-Classic to a VPC. For + more information, see Migrate from EC2-Classic to a VPC (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/vpc-migrate.html) + in the Amazon Elastic Compute Cloud User Guide. \n Describes + the VPC peering connection options." + properties: + allowDNSResolutionFromRemoteVPC: + type: boolean + allowEgressFromLocalClassicLinkToRemoteVPC: + type: boolean + allowEgressFromLocalVPCToRemoteClassicLink: + type: boolean + type: object + region: + type: string + vpcID: + type: string + type: object + status: + description: The status of the VPC peering connection. + properties: + code: + type: string + message: + type: string + type: object + vpcPeeringConnectionID: + description: The ID of the VPC peering connection. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm/templates/cluster-role-controller.yaml b/helm/templates/cluster-role-controller.yaml index 69cc444d..71edadb0 100644 --- a/helm/templates/cluster-role-controller.yaml +++ b/helm/templates/cluster-role-controller.yaml @@ -286,6 +286,26 @@ rules: - get - patch - update +- apiGroups: + - ec2.services.k8s.aws + resources: + - vpcpeeringconnections + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - ec2.services.k8s.aws + resources: + - vpcpeeringconnections/status + verbs: + - get + - patch + - update - apiGroups: - ec2.services.k8s.aws resources: diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index f40b5f22..cea38ec9 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -123,7 +123,7 @@ spec: readOnly: true {{- end }} {{- if .Values.deployment.extraVolumeMounts -}} - {{ toYaml .Values.deployment.extraVolumeMounts | nindent 12 }} + {{ toYaml .Values.deployment.extraVolumeMounts | nindent 10 }} {{- end }} securityContext: allowPrivilegeEscalation: false @@ -151,11 +151,11 @@ spec: hostNetwork: {{ .Values.deployment.hostNetwork }} dnsPolicy: {{ .Values.deployment.dnsPolicy }} volumes: - {{- if .Values.aws.credentials.secretName -}} + {{- if .Values.aws.credentials.secretName }} - name: {{ .Values.aws.credentials.secretName }} secret: secretName: {{ .Values.aws.credentials.secretName }} - {{ end -}} + {{- end }} {{- if .Values.deployment.extraVolumes }} {{ toYaml .Values.deployment.extraVolumes | indent 8}} {{- end }} diff --git a/helm/templates/role-reader.yaml b/helm/templates/role-reader.yaml index 8da763f1..342d15c6 100644 --- a/helm/templates/role-reader.yaml +++ b/helm/templates/role-reader.yaml @@ -22,6 +22,7 @@ rules: - transitgateways - vpcs - vpcendpoints + - vpcpeeringconnections verbs: - get - list diff --git a/helm/templates/role-writer.yaml b/helm/templates/role-writer.yaml index a715818c..9ee02922 100644 --- a/helm/templates/role-writer.yaml +++ b/helm/templates/role-writer.yaml @@ -35,6 +35,8 @@ rules: - vpcendpoints + - vpcpeeringconnections + verbs: - create - delete @@ -59,6 +61,7 @@ rules: - transitgateways - vpcs - vpcendpoints + - vpcpeeringconnections verbs: - get - patch diff --git a/pkg/resource/route_table/references.go b/pkg/resource/route_table/references.go index 0cb50912..389b9e52 100644 --- a/pkg/resource/route_table/references.go +++ b/pkg/resource/route_table/references.go @@ -61,6 +61,12 @@ func (rm *resourceManager) ClearResolvedReferences(res acktypes.AWSResource) ack } } + for f0idx, f0iter := range ko.Spec.Routes { + if f0iter.VPCPeeringConnectionRef != nil { + ko.Spec.Routes[f0idx].VPCPeeringConnectionID = nil + } + } + if ko.Spec.VPCRef != nil { ko.Spec.VPCID = nil } @@ -109,6 +115,12 @@ func (rm *resourceManager) ResolveReferences( resourceHasReferences = resourceHasReferences || fieldHasReferences } + if fieldHasReferences, err := rm.resolveReferenceForRoutes_VPCPeeringConnectionID(ctx, apiReader, namespace, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences + } + if fieldHasReferences, err := rm.resolveReferenceForVPCID(ctx, apiReader, namespace, ko); err != nil { return &resource{ko}, (resourceHasReferences || fieldHasReferences), err } else { @@ -146,6 +158,12 @@ func validateReferenceFields(ko *svcapitypes.RouteTable) error { } } + for _, f0iter := range ko.Spec.Routes { + if f0iter.VPCPeeringConnectionRef != nil && f0iter.VPCPeeringConnectionID != nil { + return ackerr.ResourceReferenceAndIDNotSupportedFor("Routes.VPCPeeringConnectionID", "Routes.VPCPeeringConnectionRef") + } + } + if ko.Spec.VPCRef != nil && ko.Spec.VPCID != nil { return ackerr.ResourceReferenceAndIDNotSupportedFor("VPCID", "VPCRef") } @@ -471,6 +489,85 @@ func getReferencedResourceState_VPCEndpoint( return nil } +// resolveReferenceForRoutes_VPCPeeringConnectionID reads the resource referenced +// from Routes.VPCPeeringConnectionRef field and sets the Routes.VPCPeeringConnectionID +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForRoutes_VPCPeeringConnectionID( + ctx context.Context, + apiReader client.Reader, + namespace string, + ko *svcapitypes.RouteTable, +) (hasReferences bool, err error) { + for f0idx, f0iter := range ko.Spec.Routes { + if f0iter.VPCPeeringConnectionRef != nil && f0iter.VPCPeeringConnectionRef.From != nil { + hasReferences = true + arr := f0iter.VPCPeeringConnectionRef.From + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: Routes.VPCPeeringConnectionRef") + } + obj := &svcapitypes.VPCPeeringConnection{} + if err := getReferencedResourceState_VPCPeeringConnection(ctx, apiReader, obj, *arr.Name, namespace); err != nil { + return hasReferences, err + } + ko.Spec.Routes[f0idx].VPCPeeringConnectionID = (*string)(obj.Status.VPCPeeringConnectionID) + } + } + + return hasReferences, nil +} + +// getReferencedResourceState_VPCPeeringConnection 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_VPCPeeringConnection( + ctx context.Context, + apiReader client.Reader, + obj *svcapitypes.VPCPeeringConnection, + 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 refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + return ackerr.ResourceReferenceTerminalFor( + "VPCPeeringConnection", + namespace, name) + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "VPCPeeringConnection", + namespace, name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "VPCPeeringConnection", + namespace, name) + } + if obj.Status.VPCPeeringConnectionID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "VPCPeeringConnection", + namespace, name, + "Status.VPCPeeringConnectionID") + } + return nil +} + // resolveReferenceForVPCID reads the resource referenced // from VPCRef field and sets the VPCID // from referenced resource. Returns a boolean indicating whether a reference diff --git a/pkg/resource/vpc_peering_connection/delta.go b/pkg/resource/vpc_peering_connection/delta.go new file mode 100644 index 00000000..890209e7 --- /dev/null +++ b/pkg/resource/vpc_peering_connection/delta.go @@ -0,0 +1,140 @@ +// 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 vpc_peering_connection + +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 + } + compareTags(delta, a, b) + + if ackcompare.HasNilDifference(a.ko.Spec.AcceptRequest, b.ko.Spec.AcceptRequest) { + delta.Add("Spec.AcceptRequest", a.ko.Spec.AcceptRequest, b.ko.Spec.AcceptRequest) + } else if a.ko.Spec.AcceptRequest != nil && b.ko.Spec.AcceptRequest != nil { + if *a.ko.Spec.AcceptRequest != *b.ko.Spec.AcceptRequest { + delta.Add("Spec.AcceptRequest", a.ko.Spec.AcceptRequest, b.ko.Spec.AcceptRequest) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.AccepterPeeringConnectionOptions, b.ko.Spec.AccepterPeeringConnectionOptions) { + delta.Add("Spec.AccepterPeeringConnectionOptions", a.ko.Spec.AccepterPeeringConnectionOptions, b.ko.Spec.AccepterPeeringConnectionOptions) + } else if a.ko.Spec.AccepterPeeringConnectionOptions != nil && b.ko.Spec.AccepterPeeringConnectionOptions != nil { + if ackcompare.HasNilDifference(a.ko.Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC, b.ko.Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC) { + delta.Add("Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC", a.ko.Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC, b.ko.Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC) + } else if a.ko.Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC != nil && b.ko.Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC != nil { + if *a.ko.Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC != *b.ko.Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC { + delta.Add("Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC", a.ko.Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC, b.ko.Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC, b.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC) { + delta.Add("Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC", a.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC, b.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC) + } else if a.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC != nil && b.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC != nil { + if *a.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC != *b.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC { + delta.Add("Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC", a.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC, b.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink, b.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink) { + delta.Add("Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink", a.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink, b.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink) + } else if a.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink != nil && b.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink != nil { + if *a.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink != *b.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink { + delta.Add("Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink", a.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink, b.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink) + } + } + } + if ackcompare.HasNilDifference(a.ko.Spec.PeerOwnerID, b.ko.Spec.PeerOwnerID) { + delta.Add("Spec.PeerOwnerID", a.ko.Spec.PeerOwnerID, b.ko.Spec.PeerOwnerID) + } else if a.ko.Spec.PeerOwnerID != nil && b.ko.Spec.PeerOwnerID != nil { + if *a.ko.Spec.PeerOwnerID != *b.ko.Spec.PeerOwnerID { + delta.Add("Spec.PeerOwnerID", a.ko.Spec.PeerOwnerID, b.ko.Spec.PeerOwnerID) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.PeerRegion, b.ko.Spec.PeerRegion) { + delta.Add("Spec.PeerRegion", a.ko.Spec.PeerRegion, b.ko.Spec.PeerRegion) + } else if a.ko.Spec.PeerRegion != nil && b.ko.Spec.PeerRegion != nil { + if *a.ko.Spec.PeerRegion != *b.ko.Spec.PeerRegion { + delta.Add("Spec.PeerRegion", a.ko.Spec.PeerRegion, b.ko.Spec.PeerRegion) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.PeerVPCID, b.ko.Spec.PeerVPCID) { + delta.Add("Spec.PeerVPCID", a.ko.Spec.PeerVPCID, b.ko.Spec.PeerVPCID) + } else if a.ko.Spec.PeerVPCID != nil && b.ko.Spec.PeerVPCID != nil { + if *a.ko.Spec.PeerVPCID != *b.ko.Spec.PeerVPCID { + delta.Add("Spec.PeerVPCID", a.ko.Spec.PeerVPCID, b.ko.Spec.PeerVPCID) + } + } + if !reflect.DeepEqual(a.ko.Spec.PeerVPCRef, b.ko.Spec.PeerVPCRef) { + delta.Add("Spec.PeerVPCRef", a.ko.Spec.PeerVPCRef, b.ko.Spec.PeerVPCRef) + } + if ackcompare.HasNilDifference(a.ko.Spec.RequesterPeeringConnectionOptions, b.ko.Spec.RequesterPeeringConnectionOptions) { + delta.Add("Spec.RequesterPeeringConnectionOptions", a.ko.Spec.RequesterPeeringConnectionOptions, b.ko.Spec.RequesterPeeringConnectionOptions) + } else if a.ko.Spec.RequesterPeeringConnectionOptions != nil && b.ko.Spec.RequesterPeeringConnectionOptions != nil { + if ackcompare.HasNilDifference(a.ko.Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC, b.ko.Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC) { + delta.Add("Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC", a.ko.Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC, b.ko.Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC) + } else if a.ko.Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC != nil && b.ko.Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC != nil { + if *a.ko.Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC != *b.ko.Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC { + delta.Add("Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC", a.ko.Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC, b.ko.Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC, b.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC) { + delta.Add("Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC", a.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC, b.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC) + } else if a.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC != nil && b.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC != nil { + if *a.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC != *b.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC { + delta.Add("Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC", a.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC, b.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink, b.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink) { + delta.Add("Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink", a.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink, b.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink) + } else if a.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink != nil && b.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink != nil { + if *a.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink != *b.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink { + delta.Add("Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink", a.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink, b.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink) + } + } + } + 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/vpc_peering_connection/descriptor.go b/pkg/resource/vpc_peering_connection/descriptor.go new file mode 100644 index 00000000..24bce95a --- /dev/null +++ b/pkg/resource/vpc_peering_connection/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 vpc_peering_connection + +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/VPCPeeringConnection" +) + +var ( + GroupVersionResource = svcapitypes.GroupVersion.WithResource("vpcpeeringconnections") + GroupKind = metav1.GroupKind{ + Group: "ec2.services.k8s.aws", + Kind: "VPCPeeringConnection", + } +) + +// 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.VPCPeeringConnection{} +} + +// 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.VPCPeeringConnection), + } +} + +// 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/vpc_peering_connection/hooks.go b/pkg/resource/vpc_peering_connection/hooks.go new file mode 100644 index 00000000..f55c73b6 --- /dev/null +++ b/pkg/resource/vpc_peering_connection/hooks.go @@ -0,0 +1,236 @@ +// 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 vpc_peering_connection + +import ( + "context" + "fmt" + "time" + + svcapitypes "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1" + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue" + ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" + svcsdk "github.com/aws/aws-sdk-go/service/ec2" +) + +var ( + ErrVPCPeeringConnectionCreating = fmt.Errorf( + "VPCPerringConnection in '%v' state, cannot be modified or deleted", + "creating", + ) + ErrVPCPeeringConnectionProvisioning = fmt.Errorf( + "VPCPerringConnection in '%v' state, cannot be modified or deleted", + svcsdk.VpcPeeringConnectionStateReasonCodeProvisioning, + ) + ErrVPCPeeringConnectionDeleting = fmt.Errorf( + "VPCPerringConnection in '%v' state, cannot be modified or deleted", + svcsdk.VpcPeeringConnectionStateReasonCodeDeleting, + ) +) + +var ( + requeueWaitWhileDeleting = ackrequeue.NeededAfter( + ErrVPCPeeringConnectionDeleting, + 5*time.Second, + ) + requeueWaitWhileCreating = ackrequeue.NeededAfter( + ErrVPCPeeringConnectionCreating, + 5*time.Second, + ) + requeueWaitWhileProvisioning = ackrequeue.NeededAfter( + ErrVPCPeeringConnectionProvisioning, + 5*time.Second, + ) +) + +func isVPCPeeringConnectionCreating(r *resource) bool { + if r.ko.Status.Status == nil || r.ko.Status.Status.Code == nil { + return false + } + status := *r.ko.Status.Status.Code + return status == string(svcapitypes.VPCPeeringConnectionStateReasonCode_initiating_request) +} + +func isVPCPeeringConnectionProvisioning(r *resource) bool { + if r.ko.Status.Status == nil || r.ko.Status.Status.Code == nil { + return false + } + status := *r.ko.Status.Status.Code + return status == string(svcapitypes.VPCPeeringConnectionStateReasonCode_provisioning) +} + +func isVPCPeeringConnectionDeleting(r *resource) bool { + if r.ko.Status.Status == nil || r.ko.Status.Status.Code == nil { + return false + } + status := *r.ko.Status.Status.Code + return status == string(svcapitypes.VPCPeeringConnectionStateReasonCode_deleting) +} + +func isVPCPeeringConnectionActive(r *resource) bool { + if r.ko.Status.Status == nil || r.ko.Status.Status.Code == nil { + return false + } + status := *r.ko.Status.Status.Code + return status == string(svcapitypes.VPCPeeringConnectionStateReasonCode_active) +} + +func isVPCPeeringConnectionPendingAcceptance(r *resource) bool { + if r.ko.Status.Status == nil || r.ko.Status.Status.Code == nil { + return false + } + status := *r.ko.Status.Status.Code + return status == string(svcapitypes.VPCPeeringConnectionStateReasonCode_pending_acceptance) +} + +// syncTags used to keep tags in sync by calling Create and Delete API's +func (rm *resourceManager) syncTags( + ctx context.Context, + desired *resource, + latest *resource, +) (err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.syncTags") + defer func(err error) { + exit(err) + }(err) + + resourceId := []*string{latest.ko.Status.VPCPeeringConnectionID} + + toAdd, toDelete := computeTagsDelta( + desired.ko.Spec.Tags, latest.ko.Spec.Tags, + ) + + if len(toDelete) > 0 { + rlog.Debug("removing tags from vpc peering connection resource", "tags", toDelete) + _, err = rm.sdkapi.DeleteTagsWithContext( + ctx, + &svcsdk.DeleteTagsInput{ + Resources: resourceId, + Tags: rm.sdkTags(toDelete), + }, + ) + rm.metrics.RecordAPICall("UPDATE", "DeleteTags", err) + if err != nil { + return err + } + + } + + if len(toAdd) > 0 { + rlog.Debug("adding tags to vpc peering connection resource", "tags", toAdd) + _, err = rm.sdkapi.CreateTagsWithContext( + ctx, + &svcsdk.CreateTagsInput{ + Resources: resourceId, + Tags: rm.sdkTags(toAdd), + }, + ) + rm.metrics.RecordAPICall("UPDATE", "CreateTags", err) + if err != nil { + return err + } + } + + return nil +} + +// sdkTags converts *svcapitypes.Tag array to a *svcsdk.Tag array +func (rm *resourceManager) sdkTags( + tags []*svcapitypes.Tag, +) (sdktags []*svcsdk.Tag) { + + for _, i := range tags { + sdktag := rm.newTag(*i) + sdktags = append(sdktags, sdktag) + } + + return sdktags +} + +// computeTagsDelta returns tags to be added and removed from the resource +func computeTagsDelta( + desired []*svcapitypes.Tag, + latest []*svcapitypes.Tag, +) (toAdd []*svcapitypes.Tag, toDelete []*svcapitypes.Tag) { + + desiredTags := map[string]string{} + for _, tag := range desired { + desiredTags[*tag.Key] = *tag.Value + } + + latestTags := map[string]string{} + for _, tag := range latest { + latestTags[*tag.Key] = *tag.Value + } + + for _, tag := range desired { + val, ok := latestTags[*tag.Key] + if !ok || val != *tag.Value { + toAdd = append(toAdd, tag) + } + } + + for _, tag := range latest { + _, ok := desiredTags[*tag.Key] + if !ok { + toDelete = append(toDelete, tag) + } + } + + return toAdd, toDelete + +} + +// compareTags is a custom comparison function for comparing lists of Tag +// structs where the order of the structs in the list is not important. +func compareTags( + delta *ackcompare.Delta, + a *resource, + b *resource, +) { + if len(a.ko.Spec.Tags) != len(b.ko.Spec.Tags) { + delta.Add("Spec.Tags", a.ko.Spec.Tags, b.ko.Spec.Tags) + } else if len(a.ko.Spec.Tags) > 0 { + addedOrUpdated, removed := computeTagsDelta(a.ko.Spec.Tags, b.ko.Spec.Tags) + if len(addedOrUpdated) != 0 || len(removed) != 0 { + delta.Add("Spec.Tags", a.ko.Spec.Tags, b.ko.Spec.Tags) + } + } +} + +// updateTagSpecificationsInCreateRequest adds +// Tags defined in the Spec to CreateVpcPeeringConnectionInput.TagSpecification +// and ensures the ResourceType is always set to 'vpc-peering-connection' +func updateTagSpecificationsInCreateRequest(r *resource, + input *svcsdk.CreateVpcPeeringConnectionInput) { + input.TagSpecifications = nil + desiredTagSpecs := svcsdk.TagSpecification{} + if r.ko.Spec.Tags != nil { + requestedTags := []*svcsdk.Tag{} + for _, desiredTag := range r.ko.Spec.Tags { + // Add in tags defined in the Spec + tag := &svcsdk.Tag{} + if desiredTag.Key != nil && desiredTag.Value != nil { + tag.SetKey(*desiredTag.Key) + tag.SetValue(*desiredTag.Value) + } + requestedTags = append(requestedTags, tag) + } + desiredTagSpecs.SetResourceType("vpc-peering-connection") + desiredTagSpecs.SetTags(requestedTags) + input.TagSpecifications = []*svcsdk.TagSpecification{&desiredTagSpecs} + } +} diff --git a/pkg/resource/vpc_peering_connection/identifiers.go b/pkg/resource/vpc_peering_connection/identifiers.go new file mode 100644 index 00000000..fbb1369c --- /dev/null +++ b/pkg/resource/vpc_peering_connection/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 vpc_peering_connection + +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/vpc_peering_connection/manager.go b/pkg/resource/vpc_peering_connection/manager.go new file mode 100644 index 00000000..d2867475 --- /dev/null +++ b/pkg/resource/vpc_peering_connection/manager.go @@ -0,0 +1,360 @@ +// 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 vpc_peering_connection + +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/aws/session" + svcsdk "github.com/aws/aws-sdk-go/service/ec2" + svcsdkapi "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "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.VPCPeeringConnection{} +) + +// +kubebuilder:rbac:groups=ec2.services.k8s.aws,resources=vpcpeeringconnections,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=ec2.services.k8s.aws,resources=vpcpeeringconnections/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 + // 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 + // sess is the AWS SDK Session object used to communicate with the backend + // AWS service API + sess *session.Session + // sdk is a pointer to the AWS service API interface exposed by the + // aws-sdk-go/services/{alias}/{alias}iface package. + sdkapi svcsdkapi.EC2API +} + +// 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) + 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 := ToACKTags(existingTags) + tags := acktags.Merge(resourceTags, defaultTags) + r.ko.Spec.Tags = FromACKTags(tags) + return nil +} + +// newResourceManager returns a new struct implementing +// acktypes.AWSResourceManager +func newResourceManager( + cfg ackcfg.Config, + log logr.Logger, + metrics *ackmetrics.Metrics, + rr acktypes.Reconciler, + sess *session.Session, + id ackv1alpha1.AWSAccountID, + region ackv1alpha1.AWSRegion, +) (*resourceManager, error) { + return &resourceManager{ + cfg: cfg, + log: log, + metrics: metrics, + rr: rr, + awsAccountID: id, + awsRegion: region, + sess: sess, + sdkapi: svcsdk.New(sess), + }, 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/vpc_peering_connection/manager_factory.go b/pkg/resource/vpc_peering_connection/manager_factory.go new file mode 100644 index 00000000..a51d36c4 --- /dev/null +++ b/pkg/resource/vpc_peering_connection/manager_factory.go @@ -0,0 +1,96 @@ +// 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 vpc_peering_connection + +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/aws/session" + "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, + log logr.Logger, + metrics *ackmetrics.Metrics, + rr acktypes.Reconciler, + sess *session.Session, + id ackv1alpha1.AWSAccountID, + region ackv1alpha1.AWSRegion, +) (acktypes.AWSResourceManager, error) { + rmId := fmt.Sprintf("%s/%s", id, region) + f.RLock() + rm, found := f.rmCache[rmId] + f.RUnlock() + + if found { + return rm, nil + } + + f.Lock() + defer f.Unlock() + + rm, err := newResourceManager(cfg, log, metrics, rr, sess, 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/vpc_peering_connection/references.go b/pkg/resource/vpc_peering_connection/references.go new file mode 100644 index 00000000..45b98269 --- /dev/null +++ b/pkg/resource/vpc_peering_connection/references.go @@ -0,0 +1,198 @@ +// 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 vpc_peering_connection + +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 ko.Spec.PeerVPCRef != nil { + ko.Spec.PeerVPCID = 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) { + namespace := res.MetaObject().GetNamespace() + ko := rm.concreteResource(res).ko + + resourceHasReferences := false + err := validateReferenceFields(ko) + if fieldHasReferences, err := rm.resolveReferenceForPeerVPCID(ctx, apiReader, namespace, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences + } + + if fieldHasReferences, err := rm.resolveReferenceForVPCID(ctx, apiReader, namespace, 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.VPCPeeringConnection) error { + + if ko.Spec.PeerVPCRef != nil && ko.Spec.PeerVPCID != nil { + return ackerr.ResourceReferenceAndIDNotSupportedFor("PeerVPCID", "PeerVPCRef") + } + + if ko.Spec.VPCRef != nil && ko.Spec.VPCID != nil { + return ackerr.ResourceReferenceAndIDNotSupportedFor("VPCID", "VPCRef") + } + return nil +} + +// resolveReferenceForPeerVPCID reads the resource referenced +// from PeerVPCRef field and sets the PeerVPCID +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForPeerVPCID( + ctx context.Context, + apiReader client.Reader, + namespace string, + ko *svcapitypes.VPCPeeringConnection, +) (hasReferences bool, err error) { + if ko.Spec.PeerVPCRef != nil && ko.Spec.PeerVPCRef.From != nil { + hasReferences = true + arr := ko.Spec.PeerVPCRef.From + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: PeerVPCRef") + } + obj := &svcapitypes.VPC{} + if err := getReferencedResourceState_VPC(ctx, apiReader, obj, *arr.Name, namespace); err != nil { + return hasReferences, err + } + ko.Spec.PeerVPCID = (*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 refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + return ackerr.ResourceReferenceTerminalFor( + "VPC", + namespace, name) + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "VPC", + namespace, name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "VPC", + namespace, name) + } + if obj.Status.VPCID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "VPC", + namespace, name, + "Status.VPCID") + } + 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, + namespace string, + ko *svcapitypes.VPCPeeringConnection, +) (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") + } + 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 +} diff --git a/pkg/resource/vpc_peering_connection/resource.go b/pkg/resource/vpc_peering_connection/resource.go new file mode 100644 index 00000000..dbb2ecfc --- /dev/null +++ b/pkg/resource/vpc_peering_connection/resource.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 vpc_peering_connection + +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.VPCPeeringConnection +} + +// 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.VPCPeeringConnectionID = &identifier.NameOrID + + 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/vpc_peering_connection/sdk.go b/pkg/resource/vpc_peering_connection/sdk.go new file mode 100644 index 00000000..abeb2b85 --- /dev/null +++ b/pkg/resource/vpc_peering_connection/sdk.go @@ -0,0 +1,801 @@ +// 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 vpc_peering_connection + +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/aws" + svcsdk "github.com/aws/aws-sdk-go/service/ec2" + 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("") + _ = &aws.JSONValue{} + _ = &svcsdk.EC2{} + _ = &svcapitypes.VPCPeeringConnection{} + _ = ackv1alpha1.AWSAccountID("") + _ = &ackerr.NotFound + _ = &ackcondition.NotManagedMessage + _ = &reflect.Value{} + _ = fmt.Sprintf("") + _ = &ackrequeue.NoRequeue{} +) + +// 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.DescribeVpcPeeringConnectionsOutput + resp, err = rm.sdkapi.DescribeVpcPeeringConnectionsWithContext(ctx, input) + rm.metrics.RecordAPICall("READ_MANY", "DescribeVpcPeeringConnections", err) + if err != nil { + if awsErr, ok := ackerr.AWSError(err); ok && awsErr.Code() == "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.VpcPeeringConnections { + if elem.AccepterVpcInfo != nil { + f0 := &svcapitypes.VPCPeeringConnectionVPCInfo{} + if elem.AccepterVpcInfo.CidrBlock != nil { + f0.CIDRBlock = elem.AccepterVpcInfo.CidrBlock + } + if elem.AccepterVpcInfo.CidrBlockSet != nil { + f0f1 := []*svcapitypes.CIDRBlock{} + for _, f0f1iter := range elem.AccepterVpcInfo.CidrBlockSet { + f0f1elem := &svcapitypes.CIDRBlock{} + if f0f1iter.CidrBlock != nil { + f0f1elem.CIDRBlock = f0f1iter.CidrBlock + } + f0f1 = append(f0f1, f0f1elem) + } + f0.CIDRBlockSet = f0f1 + } + if elem.AccepterVpcInfo.Ipv6CidrBlockSet != nil { + f0f2 := []*svcapitypes.IPv6CIDRBlock{} + for _, f0f2iter := range elem.AccepterVpcInfo.Ipv6CidrBlockSet { + f0f2elem := &svcapitypes.IPv6CIDRBlock{} + if f0f2iter.Ipv6CidrBlock != nil { + f0f2elem.IPv6CIDRBlock = f0f2iter.Ipv6CidrBlock + } + f0f2 = append(f0f2, f0f2elem) + } + f0.IPv6CIDRBlockSet = f0f2 + } + if elem.AccepterVpcInfo.OwnerId != nil { + f0.OwnerID = elem.AccepterVpcInfo.OwnerId + } + if elem.AccepterVpcInfo.PeeringOptions != nil { + f0f4 := &svcapitypes.VPCPeeringConnectionOptionsDescription{} + if elem.AccepterVpcInfo.PeeringOptions.AllowDnsResolutionFromRemoteVpc != nil { + f0f4.AllowDNSResolutionFromRemoteVPC = elem.AccepterVpcInfo.PeeringOptions.AllowDnsResolutionFromRemoteVpc + } + if elem.AccepterVpcInfo.PeeringOptions.AllowEgressFromLocalClassicLinkToRemoteVpc != nil { + f0f4.AllowEgressFromLocalClassicLinkToRemoteVPC = elem.AccepterVpcInfo.PeeringOptions.AllowEgressFromLocalClassicLinkToRemoteVpc + } + if elem.AccepterVpcInfo.PeeringOptions.AllowEgressFromLocalVpcToRemoteClassicLink != nil { + f0f4.AllowEgressFromLocalVPCToRemoteClassicLink = elem.AccepterVpcInfo.PeeringOptions.AllowEgressFromLocalVpcToRemoteClassicLink + } + f0.PeeringOptions = f0f4 + } + if elem.AccepterVpcInfo.Region != nil { + f0.Region = elem.AccepterVpcInfo.Region + } + if elem.AccepterVpcInfo.VpcId != nil { + f0.VPCID = elem.AccepterVpcInfo.VpcId + } + ko.Status.AccepterVPCInfo = f0 + } else { + ko.Status.AccepterVPCInfo = nil + } + if elem.ExpirationTime != nil { + ko.Status.ExpirationTime = &metav1.Time{*elem.ExpirationTime} + } else { + ko.Status.ExpirationTime = nil + } + if elem.RequesterVpcInfo != nil { + f2 := &svcapitypes.VPCPeeringConnectionVPCInfo{} + if elem.RequesterVpcInfo.CidrBlock != nil { + f2.CIDRBlock = elem.RequesterVpcInfo.CidrBlock + } + if elem.RequesterVpcInfo.CidrBlockSet != nil { + f2f1 := []*svcapitypes.CIDRBlock{} + for _, f2f1iter := range elem.RequesterVpcInfo.CidrBlockSet { + f2f1elem := &svcapitypes.CIDRBlock{} + if f2f1iter.CidrBlock != nil { + f2f1elem.CIDRBlock = f2f1iter.CidrBlock + } + f2f1 = append(f2f1, f2f1elem) + } + f2.CIDRBlockSet = f2f1 + } + if elem.RequesterVpcInfo.Ipv6CidrBlockSet != nil { + f2f2 := []*svcapitypes.IPv6CIDRBlock{} + for _, f2f2iter := range elem.RequesterVpcInfo.Ipv6CidrBlockSet { + f2f2elem := &svcapitypes.IPv6CIDRBlock{} + if f2f2iter.Ipv6CidrBlock != nil { + f2f2elem.IPv6CIDRBlock = f2f2iter.Ipv6CidrBlock + } + f2f2 = append(f2f2, f2f2elem) + } + f2.IPv6CIDRBlockSet = f2f2 + } + if elem.RequesterVpcInfo.OwnerId != nil { + f2.OwnerID = elem.RequesterVpcInfo.OwnerId + } + if elem.RequesterVpcInfo.PeeringOptions != nil { + f2f4 := &svcapitypes.VPCPeeringConnectionOptionsDescription{} + if elem.RequesterVpcInfo.PeeringOptions.AllowDnsResolutionFromRemoteVpc != nil { + f2f4.AllowDNSResolutionFromRemoteVPC = elem.RequesterVpcInfo.PeeringOptions.AllowDnsResolutionFromRemoteVpc + } + if elem.RequesterVpcInfo.PeeringOptions.AllowEgressFromLocalClassicLinkToRemoteVpc != nil { + f2f4.AllowEgressFromLocalClassicLinkToRemoteVPC = elem.RequesterVpcInfo.PeeringOptions.AllowEgressFromLocalClassicLinkToRemoteVpc + } + if elem.RequesterVpcInfo.PeeringOptions.AllowEgressFromLocalVpcToRemoteClassicLink != nil { + f2f4.AllowEgressFromLocalVPCToRemoteClassicLink = elem.RequesterVpcInfo.PeeringOptions.AllowEgressFromLocalVpcToRemoteClassicLink + } + f2.PeeringOptions = f2f4 + } + if elem.RequesterVpcInfo.Region != nil { + f2.Region = elem.RequesterVpcInfo.Region + } + if elem.RequesterVpcInfo.VpcId != nil { + f2.VPCID = elem.RequesterVpcInfo.VpcId + } + ko.Status.RequesterVPCInfo = f2 + } else { + ko.Status.RequesterVPCInfo = nil + } + if elem.Status != nil { + f3 := &svcapitypes.VPCPeeringConnectionStateReason{} + if elem.Status.Code != nil { + f3.Code = elem.Status.Code + } + if elem.Status.Message != nil { + f3.Message = elem.Status.Message + } + ko.Status.Status = f3 + } else { + ko.Status.Status = 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.VpcPeeringConnectionId != nil { + ko.Status.VPCPeeringConnectionID = elem.VpcPeeringConnectionId + } else { + ko.Status.VPCPeeringConnectionID = nil + } + found = true + break + } + if !found { + return nil, ackerr.NotFound + } + + rm.setStatusDefaults(ko) + + // Artificially trigger detection by delta.DifferentAt("Spec.AcceptRequest") + res := &resource{ko} + if isVPCPeeringConnectionPendingAcceptance(res) { + res.ko.Spec.AcceptRequest = aws.Bool(false) + } else if isVPCPeeringConnectionActive(res) || isVPCPeeringConnectionProvisioning(res) { + res.ko.Spec.AcceptRequest = aws.Bool(true) + } else if isVPCPeeringConnectionCreating(res) { + return nil, requeueWaitWhileCreating + } + + 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 r.ko.Status.VPCPeeringConnectionID == nil + +} + +// 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.DescribeVpcPeeringConnectionsInput, error) { + res := &svcsdk.DescribeVpcPeeringConnectionsInput{} + + if r.ko.Status.VPCPeeringConnectionID != nil { + f4 := []*string{} + f4 = append(f4, r.ko.Status.VPCPeeringConnectionID) + res.SetVpcPeeringConnectionIds(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.CreateVpcPeeringConnectionOutput + _ = resp + resp, err = rm.sdkapi.CreateVpcPeeringConnectionWithContext(ctx, input) + rm.metrics.RecordAPICall("CREATE", "CreateVpcPeeringConnection", 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.VpcPeeringConnection.AccepterVpcInfo != nil { + f0 := &svcapitypes.VPCPeeringConnectionVPCInfo{} + if resp.VpcPeeringConnection.AccepterVpcInfo.CidrBlock != nil { + f0.CIDRBlock = resp.VpcPeeringConnection.AccepterVpcInfo.CidrBlock + } + if resp.VpcPeeringConnection.AccepterVpcInfo.CidrBlockSet != nil { + f0f1 := []*svcapitypes.CIDRBlock{} + for _, f0f1iter := range resp.VpcPeeringConnection.AccepterVpcInfo.CidrBlockSet { + f0f1elem := &svcapitypes.CIDRBlock{} + if f0f1iter.CidrBlock != nil { + f0f1elem.CIDRBlock = f0f1iter.CidrBlock + } + f0f1 = append(f0f1, f0f1elem) + } + f0.CIDRBlockSet = f0f1 + } + if resp.VpcPeeringConnection.AccepterVpcInfo.Ipv6CidrBlockSet != nil { + f0f2 := []*svcapitypes.IPv6CIDRBlock{} + for _, f0f2iter := range resp.VpcPeeringConnection.AccepterVpcInfo.Ipv6CidrBlockSet { + f0f2elem := &svcapitypes.IPv6CIDRBlock{} + if f0f2iter.Ipv6CidrBlock != nil { + f0f2elem.IPv6CIDRBlock = f0f2iter.Ipv6CidrBlock + } + f0f2 = append(f0f2, f0f2elem) + } + f0.IPv6CIDRBlockSet = f0f2 + } + if resp.VpcPeeringConnection.AccepterVpcInfo.OwnerId != nil { + f0.OwnerID = resp.VpcPeeringConnection.AccepterVpcInfo.OwnerId + } + if resp.VpcPeeringConnection.AccepterVpcInfo.PeeringOptions != nil { + f0f4 := &svcapitypes.VPCPeeringConnectionOptionsDescription{} + if resp.VpcPeeringConnection.AccepterVpcInfo.PeeringOptions.AllowDnsResolutionFromRemoteVpc != nil { + f0f4.AllowDNSResolutionFromRemoteVPC = resp.VpcPeeringConnection.AccepterVpcInfo.PeeringOptions.AllowDnsResolutionFromRemoteVpc + } + if resp.VpcPeeringConnection.AccepterVpcInfo.PeeringOptions.AllowEgressFromLocalClassicLinkToRemoteVpc != nil { + f0f4.AllowEgressFromLocalClassicLinkToRemoteVPC = resp.VpcPeeringConnection.AccepterVpcInfo.PeeringOptions.AllowEgressFromLocalClassicLinkToRemoteVpc + } + if resp.VpcPeeringConnection.AccepterVpcInfo.PeeringOptions.AllowEgressFromLocalVpcToRemoteClassicLink != nil { + f0f4.AllowEgressFromLocalVPCToRemoteClassicLink = resp.VpcPeeringConnection.AccepterVpcInfo.PeeringOptions.AllowEgressFromLocalVpcToRemoteClassicLink + } + f0.PeeringOptions = f0f4 + } + if resp.VpcPeeringConnection.AccepterVpcInfo.Region != nil { + f0.Region = resp.VpcPeeringConnection.AccepterVpcInfo.Region + } + if resp.VpcPeeringConnection.AccepterVpcInfo.VpcId != nil { + f0.VPCID = resp.VpcPeeringConnection.AccepterVpcInfo.VpcId + } + ko.Status.AccepterVPCInfo = f0 + } else { + ko.Status.AccepterVPCInfo = nil + } + if resp.VpcPeeringConnection.ExpirationTime != nil { + ko.Status.ExpirationTime = &metav1.Time{*resp.VpcPeeringConnection.ExpirationTime} + } else { + ko.Status.ExpirationTime = nil + } + if resp.VpcPeeringConnection.RequesterVpcInfo != nil { + f2 := &svcapitypes.VPCPeeringConnectionVPCInfo{} + if resp.VpcPeeringConnection.RequesterVpcInfo.CidrBlock != nil { + f2.CIDRBlock = resp.VpcPeeringConnection.RequesterVpcInfo.CidrBlock + } + if resp.VpcPeeringConnection.RequesterVpcInfo.CidrBlockSet != nil { + f2f1 := []*svcapitypes.CIDRBlock{} + for _, f2f1iter := range resp.VpcPeeringConnection.RequesterVpcInfo.CidrBlockSet { + f2f1elem := &svcapitypes.CIDRBlock{} + if f2f1iter.CidrBlock != nil { + f2f1elem.CIDRBlock = f2f1iter.CidrBlock + } + f2f1 = append(f2f1, f2f1elem) + } + f2.CIDRBlockSet = f2f1 + } + if resp.VpcPeeringConnection.RequesterVpcInfo.Ipv6CidrBlockSet != nil { + f2f2 := []*svcapitypes.IPv6CIDRBlock{} + for _, f2f2iter := range resp.VpcPeeringConnection.RequesterVpcInfo.Ipv6CidrBlockSet { + f2f2elem := &svcapitypes.IPv6CIDRBlock{} + if f2f2iter.Ipv6CidrBlock != nil { + f2f2elem.IPv6CIDRBlock = f2f2iter.Ipv6CidrBlock + } + f2f2 = append(f2f2, f2f2elem) + } + f2.IPv6CIDRBlockSet = f2f2 + } + if resp.VpcPeeringConnection.RequesterVpcInfo.OwnerId != nil { + f2.OwnerID = resp.VpcPeeringConnection.RequesterVpcInfo.OwnerId + } + if resp.VpcPeeringConnection.RequesterVpcInfo.PeeringOptions != nil { + f2f4 := &svcapitypes.VPCPeeringConnectionOptionsDescription{} + if resp.VpcPeeringConnection.RequesterVpcInfo.PeeringOptions.AllowDnsResolutionFromRemoteVpc != nil { + f2f4.AllowDNSResolutionFromRemoteVPC = resp.VpcPeeringConnection.RequesterVpcInfo.PeeringOptions.AllowDnsResolutionFromRemoteVpc + } + if resp.VpcPeeringConnection.RequesterVpcInfo.PeeringOptions.AllowEgressFromLocalClassicLinkToRemoteVpc != nil { + f2f4.AllowEgressFromLocalClassicLinkToRemoteVPC = resp.VpcPeeringConnection.RequesterVpcInfo.PeeringOptions.AllowEgressFromLocalClassicLinkToRemoteVpc + } + if resp.VpcPeeringConnection.RequesterVpcInfo.PeeringOptions.AllowEgressFromLocalVpcToRemoteClassicLink != nil { + f2f4.AllowEgressFromLocalVPCToRemoteClassicLink = resp.VpcPeeringConnection.RequesterVpcInfo.PeeringOptions.AllowEgressFromLocalVpcToRemoteClassicLink + } + f2.PeeringOptions = f2f4 + } + if resp.VpcPeeringConnection.RequesterVpcInfo.Region != nil { + f2.Region = resp.VpcPeeringConnection.RequesterVpcInfo.Region + } + if resp.VpcPeeringConnection.RequesterVpcInfo.VpcId != nil { + f2.VPCID = resp.VpcPeeringConnection.RequesterVpcInfo.VpcId + } + ko.Status.RequesterVPCInfo = f2 + } else { + ko.Status.RequesterVPCInfo = nil + } + if resp.VpcPeeringConnection.Status != nil { + f3 := &svcapitypes.VPCPeeringConnectionStateReason{} + if resp.VpcPeeringConnection.Status.Code != nil { + f3.Code = resp.VpcPeeringConnection.Status.Code + } + if resp.VpcPeeringConnection.Status.Message != nil { + f3.Message = resp.VpcPeeringConnection.Status.Message + } + ko.Status.Status = f3 + } else { + ko.Status.Status = nil + } + if resp.VpcPeeringConnection.Tags != nil { + f4 := []*svcapitypes.Tag{} + for _, f4iter := range resp.VpcPeeringConnection.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.VpcPeeringConnection.VpcPeeringConnectionId != nil { + ko.Status.VPCPeeringConnectionID = resp.VpcPeeringConnection.VpcPeeringConnectionId + } else { + ko.Status.VPCPeeringConnectionID = nil + } + + rm.setStatusDefaults(ko) + + // This causes a requeue and the rest of the fields will be synced on the next reconciliation loop + ackcondition.SetSynced(&resource{ko}, corev1.ConditionFalse, nil, nil) + + 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.CreateVpcPeeringConnectionInput, error) { + res := &svcsdk.CreateVpcPeeringConnectionInput{} + + if r.ko.Spec.PeerOwnerID != nil { + res.SetPeerOwnerId(*r.ko.Spec.PeerOwnerID) + } + if r.ko.Spec.PeerRegion != nil { + res.SetPeerRegion(*r.ko.Spec.PeerRegion) + } + if r.ko.Spec.PeerVPCID != nil { + res.SetPeerVpcId(*r.ko.Spec.PeerVPCID) + } + if r.ko.Spec.VPCID != nil { + res.SetVpcId(*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 isVPCPeeringConnectionCreating(desired) { + return desired, requeueWaitWhileCreating + } + if isVPCPeeringConnectionProvisioning(desired) { + return desired, requeueWaitWhileProvisioning + } + if isVPCPeeringConnectionDeleting(desired) { + return desired, requeueWaitWhileDeleting + } + + // in case of pending acceptance or accepted state we make the updates. + if delta.DifferentAt("Spec.Tags") { + if err := rm.syncTags(ctx, desired, latest); err != nil { + return nil, err + } + } + + if delta.DifferentAt("Spec.AcceptRequest") { + // Throw a Terminal Error, if the field was set to 'true' and is now set to 'false' + if desired.ko.Spec.AcceptRequest == nil || !*desired.ko.Spec.AcceptRequest { + msg := fmt.Sprintf("You cannot set AcceptRequest to false after setting it to true") + return nil, ackerr.NewTerminalError(fmt.Errorf(msg)) + + // Accept the VPC Peering Connection Request, if the field is set to 'true' and is still at status Pending Acceptance + } else if *latest.ko.Status.Status.Code == "pending-acceptance" { + acceptInput := &svcsdk.AcceptVpcPeeringConnectionInput{ + VpcPeeringConnectionId: latest.ko.Status.VPCPeeringConnectionID, + } + acceptResp, err := rm.sdkapi.AcceptVpcPeeringConnectionWithContext(ctx, acceptInput) + if err != nil { + return nil, err + } + rlog.Debug("VPC Peering Connection accepted", "apiResponse", acceptResp) + readOneLatest, err := rm.ReadOne(ctx, desired) + if err != nil { + return nil, err + } + latest = rm.concreteResource(readOneLatest.DeepCopy()) + desired.ko.Status.Status = latest.ko.Status.Status + // This causes a requeue and the rest of the fields will be synced on the next reconciliation loop + ackcondition.SetSynced(desired, corev1.ConditionFalse, nil, nil) + return desired, nil + } + } + + // Only continue if something other than Tags or certain fields has changed in the Spec + if !delta.DifferentExcept("Spec.Tags", "Spec.AcceptRequest") { + return desired, nil + } + input, err := rm.newUpdateRequestPayload(ctx, desired, delta) + if err != nil { + return nil, err + } + + var resp *svcsdk.ModifyVpcPeeringConnectionOptionsOutput + _ = resp + resp, err = rm.sdkapi.ModifyVpcPeeringConnectionOptionsWithContext(ctx, input) + rm.metrics.RecordAPICall("UPDATE", "ModifyVpcPeeringConnectionOptions", 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.AccepterPeeringConnectionOptions != nil { + f0 := &svcapitypes.PeeringConnectionOptionsRequest{} + if resp.AccepterPeeringConnectionOptions.AllowDnsResolutionFromRemoteVpc != nil { + f0.AllowDNSResolutionFromRemoteVPC = resp.AccepterPeeringConnectionOptions.AllowDnsResolutionFromRemoteVpc + } + if resp.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVpc != nil { + f0.AllowEgressFromLocalClassicLinkToRemoteVPC = resp.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVpc + } + if resp.AccepterPeeringConnectionOptions.AllowEgressFromLocalVpcToRemoteClassicLink != nil { + f0.AllowEgressFromLocalVPCToRemoteClassicLink = resp.AccepterPeeringConnectionOptions.AllowEgressFromLocalVpcToRemoteClassicLink + } + ko.Spec.AccepterPeeringConnectionOptions = f0 + } else { + ko.Spec.AccepterPeeringConnectionOptions = nil + } + if resp.RequesterPeeringConnectionOptions != nil { + f1 := &svcapitypes.PeeringConnectionOptionsRequest{} + if resp.RequesterPeeringConnectionOptions.AllowDnsResolutionFromRemoteVpc != nil { + f1.AllowDNSResolutionFromRemoteVPC = resp.RequesterPeeringConnectionOptions.AllowDnsResolutionFromRemoteVpc + } + if resp.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVpc != nil { + f1.AllowEgressFromLocalClassicLinkToRemoteVPC = resp.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVpc + } + if resp.RequesterPeeringConnectionOptions.AllowEgressFromLocalVpcToRemoteClassicLink != nil { + f1.AllowEgressFromLocalVPCToRemoteClassicLink = resp.RequesterPeeringConnectionOptions.AllowEgressFromLocalVpcToRemoteClassicLink + } + ko.Spec.RequesterPeeringConnectionOptions = f1 + } else { + ko.Spec.RequesterPeeringConnectionOptions = 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.ModifyVpcPeeringConnectionOptionsInput, error) { + res := &svcsdk.ModifyVpcPeeringConnectionOptionsInput{} + + if delta.DifferentAt("Spec.AccepterPeeringConnectionOptions") { + if r.ko.Spec.AccepterPeeringConnectionOptions != nil { + f0 := &svcsdk.PeeringConnectionOptionsRequest{} + if r.ko.Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC != nil { + f0.SetAllowDnsResolutionFromRemoteVpc(*r.ko.Spec.AccepterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC) + } + if r.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC != nil { + f0.SetAllowEgressFromLocalClassicLinkToRemoteVpc(*r.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC) + } + if r.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink != nil { + f0.SetAllowEgressFromLocalVpcToRemoteClassicLink(*r.ko.Spec.AccepterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink) + } + res.SetAccepterPeeringConnectionOptions(f0) + } + } + if delta.DifferentAt("Spec.RequesterPeeringConnectionOptions") { + if r.ko.Spec.RequesterPeeringConnectionOptions != nil { + f2 := &svcsdk.PeeringConnectionOptionsRequest{} + if r.ko.Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC != nil { + f2.SetAllowDnsResolutionFromRemoteVpc(*r.ko.Spec.RequesterPeeringConnectionOptions.AllowDNSResolutionFromRemoteVPC) + } + if r.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC != nil { + f2.SetAllowEgressFromLocalClassicLinkToRemoteVpc(*r.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalClassicLinkToRemoteVPC) + } + if r.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink != nil { + f2.SetAllowEgressFromLocalVpcToRemoteClassicLink(*r.ko.Spec.RequesterPeeringConnectionOptions.AllowEgressFromLocalVPCToRemoteClassicLink) + } + res.SetRequesterPeeringConnectionOptions(f2) + } + } + if delta.DifferentAt("Spec.VPCPeeringConnectionID") { + if r.ko.Status.VPCPeeringConnectionID != nil { + res.SetVpcPeeringConnectionId(*r.ko.Status.VPCPeeringConnectionID) + } + } + + 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.DeleteVpcPeeringConnectionOutput + _ = resp + resp, err = rm.sdkapi.DeleteVpcPeeringConnectionWithContext(ctx, input) + rm.metrics.RecordAPICall("DELETE", "DeleteVpcPeeringConnection", 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.DeleteVpcPeeringConnectionInput, error) { + res := &svcsdk.DeleteVpcPeeringConnectionInput{} + + if r.ko.Status.VPCPeeringConnectionID != nil { + res.SetVpcPeeringConnectionId(*r.ko.Status.VPCPeeringConnectionID) + } + + return res, nil +} + +// setStatusDefaults sets default properties into supplied custom resource +func (rm *resourceManager) setStatusDefaults( + ko *svcapitypes.VPCPeeringConnection, +) { + 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 +} + +func (rm *resourceManager) newTag( + c svcapitypes.Tag, +) *svcsdk.Tag { + res := &svcsdk.Tag{} + if c.Key != nil { + res.SetKey(*c.Key) + } + if c.Value != nil { + res.SetValue(*c.Value) + } + + return res +} diff --git a/pkg/resource/vpc_peering_connection/tags.go b/pkg/resource/vpc_peering_connection/tags.go new file mode 100644 index 00000000..b5632f37 --- /dev/null +++ b/pkg/resource/vpc_peering_connection/tags.go @@ -0,0 +1,63 @@ +// 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 vpc_peering_connection + +import ( + acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" + + svcapitypes "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1" +) + +var ( + _ = svcapitypes.VPCPeeringConnection{} + _ = acktags.NewTags() +) + +// ToACKTags 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. +func ToACKTags(tags []*svcapitypes.Tag) acktags.Tags { + result := acktags.NewTags() + if tags == nil || len(tags) == 0 { + return result + } + + for _, t := range tags { + if t.Key != nil { + if t.Value == nil { + result[*t.Key] = "" + } else { + result[*t.Key] = *t.Value + } + } + } + + return result +} + +// 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. +func FromACKTags(tags acktags.Tags) []*svcapitypes.Tag { + result := []*svcapitypes.Tag{} + for k, v := range tags { + kCopy := k + vCopy := v + tag := svcapitypes.Tag{Key: &kCopy, Value: &vCopy} + result = append(result, &tag) + } + return result +} diff --git a/templates/hooks/vpc_peering_connection/sdk_create_post_build_request.go.tpl b/templates/hooks/vpc_peering_connection/sdk_create_post_build_request.go.tpl new file mode 100644 index 00000000..31eec6c0 --- /dev/null +++ b/templates/hooks/vpc_peering_connection/sdk_create_post_build_request.go.tpl @@ -0,0 +1 @@ + updateTagSpecificationsInCreateRequest(desired, input) \ No newline at end of file diff --git a/templates/hooks/vpc_peering_connection/sdk_create_post_set_output.go.tpl b/templates/hooks/vpc_peering_connection/sdk_create_post_set_output.go.tpl new file mode 100644 index 00000000..4c37dc70 --- /dev/null +++ b/templates/hooks/vpc_peering_connection/sdk_create_post_set_output.go.tpl @@ -0,0 +1,3 @@ + + // This causes a requeue and the rest of the fields will be synced on the next reconciliation loop + ackcondition.SetSynced(&resource{ko}, corev1.ConditionFalse, nil, nil) diff --git a/templates/hooks/vpc_peering_connection/sdk_file_end.go.tpl b/templates/hooks/vpc_peering_connection/sdk_file_end.go.tpl new file mode 100644 index 00000000..1d08f18c --- /dev/null +++ b/templates/hooks/vpc_peering_connection/sdk_file_end.go.tpl @@ -0,0 +1,23 @@ +{{ $CRD := .CRD }} +{{ $SDKAPI := .SDKAPI }} + +{{/* Generate helper methods for VPC Peering Connection */}} +{{- range $specFieldName, $specField := $CRD.Config.Resources.VpcPeeringConnection.Fields }} +{{- if $specField.From }} +{{- $operationName := $specField.From.Operation }} +{{- $operation := (index $SDKAPI.API.Operations $operationName) -}} +{{- range $vpcPeeringConnectionRefName, $vpcPeeringConnectionMemberRefs := $operation.InputRef.Shape.MemberRefs -}} +{{- if eq $vpcPeeringConnectionRefName "Tags" }} +{{- $vpcPeeringConnectionRef := $vpcPeeringConnectionMemberRefs.Shape.MemberRef }} +{{- $vpcPeeringConnectionRefName = "Tag" }} +func (rm *resourceManager) new{{ $vpcPeeringConnectionRefName }}( + c svcapitypes.{{ $vpcPeeringConnectionRefName }}, +) *svcsdk.{{ $vpcPeeringConnectionRefName }} { + res := &svcsdk.{{ $vpcPeeringConnectionRefName }}{} +{{ GoCodeSetSDKForStruct $CRD "" "res" $vpcPeeringConnectionRef "" "c" 1 }} + return res +} +{{- end }} +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/hooks/vpc_peering_connection/sdk_read_many_post_set_output.go.tpl b/templates/hooks/vpc_peering_connection/sdk_read_many_post_set_output.go.tpl new file mode 100644 index 00000000..68c54a4b --- /dev/null +++ b/templates/hooks/vpc_peering_connection/sdk_read_many_post_set_output.go.tpl @@ -0,0 +1,10 @@ + + // Artificially trigger detection by delta.DifferentAt("Spec.AcceptRequest") + res := &resource{ko} + if isVPCPeeringConnectionPendingAcceptance(res) { + res.ko.Spec.AcceptRequest = aws.Bool(false) + } else if isVPCPeeringConnectionActive(res) || isVPCPeeringConnectionProvisioning(res) { + res.ko.Spec.AcceptRequest = aws.Bool(true) + } else if isVPCPeeringConnectionCreating(res) { + return nil, requeueWaitWhileCreating + } diff --git a/templates/hooks/vpc_peering_connection/sdk_update_pre_build_request.go.tpl b/templates/hooks/vpc_peering_connection/sdk_update_pre_build_request.go.tpl new file mode 100644 index 00000000..9fec83cc --- /dev/null +++ b/templates/hooks/vpc_peering_connection/sdk_update_pre_build_request.go.tpl @@ -0,0 +1,51 @@ + + if isVPCPeeringConnectionCreating(desired) { + return desired, requeueWaitWhileCreating + } + if isVPCPeeringConnectionProvisioning(desired) { + return desired, requeueWaitWhileProvisioning + } + if isVPCPeeringConnectionDeleting(desired) { + return desired, requeueWaitWhileDeleting + } + + // in case of pending acceptance or accepted state we make the updates. + if delta.DifferentAt("Spec.Tags") { + if err := rm.syncTags(ctx, desired, latest); err != nil { + return nil, err + } + } + + if delta.DifferentAt("Spec.AcceptRequest") { + // Throw a Terminal Error, if the field was set to 'true' and is now set to 'false' + if desired.ko.Spec.AcceptRequest == nil || !*desired.ko.Spec.AcceptRequest { + msg := fmt.Sprintf("You cannot set AcceptRequest to false after setting it to true") + return nil, ackerr.NewTerminalError(fmt.Errorf(msg)) + + // Accept the VPC Peering Connection Request, if the field is set to 'true' and is still at status Pending Acceptance + } else if *latest.ko.Status.Status.Code == "pending-acceptance" { + acceptInput := &svcsdk.AcceptVpcPeeringConnectionInput{ + VpcPeeringConnectionId: latest.ko.Status.VPCPeeringConnectionID, + } + acceptResp, err := rm.sdkapi.AcceptVpcPeeringConnectionWithContext(ctx, acceptInput) + if err != nil { + return nil, err + } + rlog.Debug("VPC Peering Connection accepted", "apiResponse", acceptResp) + readOneLatest, err := rm.ReadOne(ctx, desired) + if err != nil { + return nil, err + } + latest = rm.concreteResource(readOneLatest.DeepCopy()) + desired.ko.Status.Status = latest.ko.Status.Status + // This causes a requeue and the rest of the fields will be synced on the next reconciliation loop + ackcondition.SetSynced(desired, corev1.ConditionFalse, nil, nil) + return desired, nil + } + } + + + // Only continue if something other than Tags or certain fields has changed in the Spec + if !delta.DifferentExcept("Spec.Tags", "Spec.AcceptRequest") { + return desired, nil + } \ No newline at end of file diff --git a/test/e2e/resources/vpc_peering_connection.yaml b/test/e2e/resources/vpc_peering_connection.yaml new file mode 100644 index 00000000..0459e9d2 --- /dev/null +++ b/test/e2e/resources/vpc_peering_connection.yaml @@ -0,0 +1,19 @@ +apiVersion: ec2.services.k8s.aws/v1alpha1 +kind: VPCPeeringConnection +metadata: + name: $VPC_PEERING_CONNECTION_NAME +spec: + vpcID: $VPC_ID + peerVPCID: $PEER_VPC_ID + acceptRequest: true + requesterPeeringConnectionOptions: + allowDNSResolutionFromRemoteVPC: true + allowEgressFromLocalClassicLinkToRemoteVPC: true + allowEgressFromLocalVPCToRemoteClassicLink: true + accepterPeeringConnectionOptions: + allowDNSResolutionFromRemoteVPC: true + allowEgressFromLocalClassicLinkToRemoteVPC: true + allowEgressFromLocalVPCToRemoteClassicLink: true + tags: + - key: $TAG_KEY + value: $TAG_VALUE \ No newline at end of file diff --git a/test/e2e/resources/vpc_peering_connection_ref.yaml b/test/e2e/resources/vpc_peering_connection_ref.yaml new file mode 100644 index 00000000..438ace32 --- /dev/null +++ b/test/e2e/resources/vpc_peering_connection_ref.yaml @@ -0,0 +1,23 @@ +apiVersion: ec2.services.k8s.aws/v1alpha1 +kind: VPCPeeringConnection +metadata: + name: $VPC_PEERING_CONNECTION_NAME +spec: + vpcRef: + from: + name: $VPC_REF_NAME + peerVPCRef: + from: + name: $PEER_VPC_REF_NAME + acceptRequest: true + requesterPeeringConnectionOptions: + allowDNSResolutionFromRemoteVPC: true + allowEgressFromLocalClassicLinkToRemoteVPC: true + allowEgressFromLocalVPCToRemoteClassicLink: true + accepterPeeringConnectionOptions: + allowDNSResolutionFromRemoteVPC: true + allowEgressFromLocalClassicLinkToRemoteVPC: true + allowEgressFromLocalVPCToRemoteClassicLink: true + tags: + - key: $TAG_KEY + value: $TAG_VALUE diff --git a/test/e2e/tests/helper.py b/test/e2e/tests/helper.py index 099db34b..9f3d7a72 100644 --- a/test/e2e/tests/helper.py +++ b/test/e2e/tests/helper.py @@ -249,3 +249,25 @@ def assert_vpc_endpoint(self, vpc_endpoint_id: str, exists=True): except self.ec2_client.exceptions.ClientError: pass assert res_found is exists + + def get_vpc_peering_connection(self, vpc_peering_connection_id: str) -> Union[None, Dict]: + try: + aws_res = self.ec2_client.describe_vpc_peering_connections( + VpcPeeringConnectionIds=[vpc_peering_connection_id] + ) + if len(aws_res["VpcPeeringConnections"]) > 0: + return aws_res["VpcPeeringConnections"][0] + return None + except self.ec2_client.exceptions.ClientError: + return None + + def assert_vpc_peering_connection(self, vpc_peering_connection_id: str, exists=True): + res_found = False + try: + aws_res = self.ec2_client.describe_vpc_peering_connections(VpcPeeringConnectionIds=[vpc_peering_connection_id]) + res_found = len(aws_res["VpcPeeringConnections"]) > 0 + except self.ec2_client.exceptions.ClientError: + pass + assert (res_found is exists + or + aws_res["VpcPeeringConnections"][0]["Status"]["Code"] == "deleted") \ No newline at end of file diff --git a/test/e2e/tests/test_vpc_peering_connection.py b/test/e2e/tests/test_vpc_peering_connection.py new file mode 100644 index 00000000..780ba503 --- /dev/null +++ b/test/e2e/tests/test_vpc_peering_connection.py @@ -0,0 +1,357 @@ +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.bootstrap_resources import get_bootstrap_resources +from e2e.replacement_values import REPLACEMENT_VALUES +from e2e.tests.helper import EC2Validator + +RESOURCE_PLURAL = "vpcpeeringconnections" +VPC_RESOURCE_PLURAL = "vpcs" + +CREATE_WAIT_AFTER_SECONDS = 10 +DELETE_WAIT_AFTER_SECONDS = 10 +MODIFY_WAIT_AFTER_SECONDS = 5 +DEFAULT_WAIT_AFTER_SECONDS = 5 + +@pytest.fixture +def simple_vpc_peering_connection(request): + resource_name = random_suffix_name("simple-vpc-peering-connection-test", 40) + resources = get_bootstrap_resources() + + # Create an additional VPC to test Peering with the Shared Test VPC + + # Replacements for Test VPC + replacements = REPLACEMENT_VALUES.copy() + replacements["VPC_NAME"] = resource_name + replacements["CIDR_BLOCK"] = "10.1.0.0/16" + replacements["ENABLE_DNS_SUPPORT"] = "False" + replacements["ENABLE_DNS_HOSTNAMES"] = "False" + replacements["TAG_KEY"] = "initialtagkey" + replacements["TAG_VALUE"] = "initialtagvalue" + + 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 VPC CR + vpc_resource_data = load_ec2_resource( + "vpc", + additional_replacements=replacements, + ) + logging.debug(vpc_resource_data) + + # Create k8s resource + vpc_ref = k8s.CustomResourceReference( + CRD_GROUP, CRD_VERSION, VPC_RESOURCE_PLURAL, + resource_name, namespace="default", + ) + k8s.create_custom_resource(vpc_ref, vpc_resource_data) + time.sleep(CREATE_WAIT_AFTER_SECONDS) + + vpc_cr = k8s.wait_resource_consumed_by_controller(vpc_ref) + assert vpc_cr is not None + assert k8s.get_resource_exists(vpc_ref) + + # Create the VPC Peering Connection + + # Replacements for VPC Peering Connection + replacements["VPC_PEERING_CONNECTION_NAME"] = resource_name + replacements["VPC_ID"] = resources.SharedTestVPC.vpc_id + replacements["PEER_VPC_ID"] = vpc_cr["status"]["vpcID"] + + # Load VPCPeeringConnection CR + resource_data = load_ec2_resource( + "vpc_peering_connection", + 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) + wait_for_vpc_peering_connection_status(ref) + # Get the CR again after waiting for the Status to be updated + cr = k8s.wait_resource_consumed_by_controller(ref) + assert cr["status"]["status"]["code"] == "active" + + yield (ref, cr) + + # Delete VPC Peering Connection k8s resource + try: + _, deleted = k8s.delete_custom_resource(ref, 3, 10) + assert deleted + except: + pass + + time.sleep(DELETE_WAIT_AFTER_SECONDS) + + # Delete VPC resource + _, vpc_deleted = k8s.delete_custom_resource(vpc_ref, 3, 10) + assert vpc_deleted is True + +@pytest.fixture +def ref_vpc_peering_connection(request): + resource_name = random_suffix_name("ref-vpc-peering-connection-test", 40) + + # Create 2 VPCs with ACK to test Peering with and refer to them by their k8s resource name + + # Replacements for Test VPC 1 + replacements = REPLACEMENT_VALUES.copy() + replacements["VPC_NAME"] = resource_name + "-1" + replacements["CIDR_BLOCK"] = "10.0.0.0/16" + replacements["ENABLE_DNS_SUPPORT"] = "False" + replacements["ENABLE_DNS_HOSTNAMES"] = "False" + replacements["TAG_KEY"] = "initialtagkey" + replacements["TAG_VALUE"] = "initialtagvalue" + + # Load VPC CR + vpc_1_resource_data = load_ec2_resource( + "vpc", + additional_replacements=replacements, + ) + logging.debug(vpc_1_resource_data) + + # Create k8s resource + vpc_1_ref = k8s.CustomResourceReference( + CRD_GROUP, CRD_VERSION, VPC_RESOURCE_PLURAL, + replacements["VPC_NAME"], namespace="default", + ) + k8s.create_custom_resource(vpc_1_ref, vpc_1_resource_data) + time.sleep(CREATE_WAIT_AFTER_SECONDS) + + vpc_1_cr = k8s.wait_resource_consumed_by_controller(vpc_1_ref) + assert vpc_1_cr is not None + assert k8s.get_resource_exists(vpc_1_ref) + + # Replacements for Test VPC 2 (squashes previous values used by VPC 1) + replacements["VPC_NAME"] = resource_name + "-2" + replacements["CIDR_BLOCK"] = "10.1.0.0/16" + + # Load VPC CR + vpc_2_resource_data = load_ec2_resource( + "vpc", + additional_replacements=replacements, + ) + logging.debug(vpc_2_resource_data) + + # Create k8s resource + vpc_2_ref = k8s.CustomResourceReference( + CRD_GROUP, CRD_VERSION, VPC_RESOURCE_PLURAL, + replacements["VPC_NAME"], namespace="default", + ) + k8s.create_custom_resource(vpc_2_ref, vpc_2_resource_data) + time.sleep(CREATE_WAIT_AFTER_SECONDS) + + vpc_2_cr = k8s.wait_resource_consumed_by_controller(vpc_2_ref) + assert vpc_2_cr is not None + assert k8s.get_resource_exists(vpc_2_ref) + + # Create the VPC Peering Connection + + # Replacements for VPC Peering Connection + replacements["VPC_PEERING_CONNECTION_NAME"] = resource_name + replacements["VPC_REF_NAME"] = resource_name + "-1" + replacements["PEER_VPC_REF_NAME"] = resource_name + "-2" + + # Load VPCPeeringConnection CR + resource_data = load_ec2_resource( + "vpc_peering_connection_ref", + 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) + wait_for_vpc_peering_connection_status(ref) + # Get the CR again after waiting for the Status to be updated + cr = k8s.wait_resource_consumed_by_controller(ref) + assert cr["status"]["status"]["code"] == "active" + + yield (ref, cr) + + # Delete VPC Peering Connection k8s resource + try: + _, deleted = k8s.delete_custom_resource(ref, 3, 10) + assert deleted + except: + pass + + time.sleep(DELETE_WAIT_AFTER_SECONDS) + + # Delete 2 x VPC resources + try: + _, vpc_1_deleted = k8s.delete_custom_resource(vpc_1_ref, 3, 10) + _, vpc_2_deleted = k8s.delete_custom_resource(vpc_2_ref, 3, 10) + assert vpc_1_deleted is True + assert vpc_2_deleted is True + except: + pass + +def wait_for_vpc_peering_connection_status(ref, timeout_seconds=120): + start_time = time.time() + while time.time() - start_time < timeout_seconds: + resource = k8s.wait_resource_consumed_by_controller(ref) + print("CR contents", resource) + if resource["status"]["status"]["code"] == "active": + logging.debug("VPC Peering Connection Status Code is 'active'", resource) + return resource + time.sleep(5) + print("CR contents", resource) + raise TimeoutError(f"Timed out waiting for VPC Peering Connection status to become 'active'", "Current status code", resource["status"]["status"]["code"]) + +@service_marker +@pytest.mark.canary +class TestVPCPeeringConnections: + def test_create_delete_ref(self, ec2_client, ref_vpc_peering_connection): + (ref, cr) = ref_vpc_peering_connection + vpc_peering_connection_id = cr["status"]["vpcPeeringConnectionID"] + + # Check VPC Peering Connection exists + ec2_validator = EC2Validator(ec2_client) + ec2_validator.assert_vpc_peering_connection(vpc_peering_connection_id) + + # Delete k8s resource + _, deleted = k8s.delete_custom_resource(ref, 2, 5) + assert deleted is True + + time.sleep(DELETE_WAIT_AFTER_SECONDS) + + # Check VPC Peering Connection no longer exists in AWS + ec2_validator.assert_vpc_peering_connection(vpc_peering_connection_id, exists=False) + + def test_create_delete(self, ec2_client, simple_vpc_peering_connection): + (ref, cr) = simple_vpc_peering_connection + vpc_peering_connection_id = cr["status"]["vpcPeeringConnectionID"] + + # Check VPC Peering Connection exists + ec2_validator = EC2Validator(ec2_client) + ec2_validator.assert_vpc_peering_connection(vpc_peering_connection_id) + + # Delete k8s resource + _, deleted = k8s.delete_custom_resource(ref, 2, 5) + assert deleted is True + + time.sleep(DELETE_WAIT_AFTER_SECONDS) + + # Check VPC Peering Connection no longer exists in AWS + ec2_validator.assert_vpc_peering_connection(vpc_peering_connection_id, exists=False) + + def test_crud_tags(self, ec2_client, simple_vpc_peering_connection): + (ref, cr) = simple_vpc_peering_connection + + resource = k8s.get_resource(ref) + resource_id = cr["status"]["vpcPeeringConnectionID"] + + time.sleep(CREATE_WAIT_AFTER_SECONDS) + + # Check VPC Peering Connection exists in AWS + ec2_validator = EC2Validator(ec2_client) + ec2_validator.assert_vpc_peering_connection(resource_id) + + # Check system and user tags exist for VPC Peering Connection resource + vpc_peering_connection = ec2_validator.get_vpc_peering_connection(resource_id) + user_tags = { + "initialtagkey": "initialtagvalue" + } + tags.assert_ack_system_tags( + tags=vpc_peering_connection["Tags"], + ) + tags.assert_equal_without_ack_tags( + expected=user_tags, + actual=vpc_peering_connection["Tags"], + ) + + # Update tags + update_tags = [ + { + "key": "updatedtagkey", + "value": "updatedtagvalue", + } + ] + + # Patch the VPCPeeringConnection, updating the tags with a new pair + updates = { + "spec": {"tags": update_tags}, + } + + 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=5) + + # Check for updated user tags; system tags should persist + vpc_peering_connection = ec2_validator.get_vpc_peering_connection(resource_id) + updated_tags = { + "updatedtagkey": "updatedtagvalue" + } + tags.assert_ack_system_tags( + tags=vpc_peering_connection["Tags"], + ) + tags.assert_equal_without_ack_tags( + expected=updated_tags, + actual=vpc_peering_connection["Tags"], + ) + + # Patch the VPCPeeringConnection resource, deleting the tags + updates = { + "spec": {"tags": []}, + } + + 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=5) + + # Check for removed user tags; system tags should persist + vpc_peering_connection = ec2_validator.get_vpc_peering_connection(resource_id) + tags.assert_ack_system_tags( + tags=vpc_peering_connection["Tags"], + ) + tags.assert_equal_without_ack_tags( + expected=[], + actual=vpc_peering_connection["Tags"], + ) + + # Check user tags are removed from Spec + resource = k8s.get_resource(ref) + assert len(resource["spec"]["tags"]) == 0 + + # Delete k8s resource + try: + _, deleted = k8s.delete_custom_resource(ref, 3, 10) + assert deleted + except: + pass + time.sleep(DELETE_WAIT_AFTER_SECONDS) + + # Check VPC Peering Connection no longer exists in AWS + ec2_validator.assert_vpc_peering_connection(resource_id, exists=False) +