diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index a5df9075..61174650 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -29,6 +29,9 @@ ignore: - CreateVpcEndpointInput.DryRun - CreateVpcEndpointInput.TagSpecifications - CreateVpcEndpointInput.ClientToken + - CreateVpcEndpointServiceConfigurationInput.ClientToken + - CreateVpcEndpointServiceConfigurationInput.DryRun + - CreateVpcEndpointServiceConfigurationInput.TagSpecifications - DeleteRouteInput.DryRun - DeleteRouteInput.RouteTableId # support EC2-VPC only @@ -111,7 +114,7 @@ ignore: #- TransitGateway - Volume - VpcEndpointConnectionNotification - - VpcEndpointServiceConfiguration + #- VpcEndpointServiceConfiguration #- VpcEndpoint #- Vpc - VpcCidrBlock @@ -141,6 +144,12 @@ operations: operation_type: - Delete resource_name: VpcEndpoint + CreateVpcEndpointServiceConfiguration: + output_wrapper_field_path: ServiceConfiguration + DeleteVpcEndpointServiceConfigurations: + operation_type: + - Delete + resource_name: VpcEndpointServiceConfiguration RunInstances: #ouput shape: Reservation output_wrapper_field_path: Instances @@ -378,22 +387,22 @@ resources: references: resource: VPC path: Status.VPCID - Routes.GatewayId: - references: - resource: InternetGateway - path: Status.InternetGatewayID - Routes.NatGatewayId: - references: - resource: NATGateway - path: Status.NATGatewayID - Routes.TransitGatewayId: - references: - resource: TransitGateway - path: Status.TransitGatewayID - Routes.VpcEndpointId: - references: - resource: VPCEndpoint - path: Status.VPCEndpointID +# Routes.GatewayId: +# references: +# resource: InternetGateway +# path: Status.InternetGatewayID +# Routes.NatGatewayId: +# references: +# resource: NATGateway +# path: Status.NATGatewayID +# Routes.TransitGatewayId: +# references: +# resource: TransitGateway +# path: Status.TransitGatewayID +# Routes.VpcEndpointId: +# references: +# resource: VPCEndpoint +# path: Status.VPCEndpointID hooks: delta_pre_compare: code: customPreCompare(delta, a, b) @@ -465,6 +474,37 @@ resources: template_path: hooks/security_group/sdk_read_many_post_set_output.go.tpl update_operation: custom_method_name: customUpdateSecurityGroup + VpcEndpointServiceConfiguration: + fields: + ServiceID: + is_primary_key: true + is_read_only: true + print: + path: Status.serviceID + name: ServiceID + ServiceState: + is_read_only: true + print: + path: Status.serviceState + name: ServiceState + Tags: + from: + operation: CreateTags + path: Tags + compare: + is_ignored: true + synced: + when: + - path: Status.ServiceState + in: + - available + hooks: + delta_pre_compare: + code: compareTags(delta, a, b) + sdk_delete_post_build_request: + template_path: hooks/vpc_endpoint_service_configuration/sdk_delete_post_build_request.go.tpl + sdk_file_end: + template_path: hooks/vpc_endpoint_service_configuration/sdk_file_end.go.tpl Subnet: fields: # code-generator infers fields into diff --git a/apis/v1alpha1/types.go b/apis/v1alpha1/types.go index 0f9bb7b7..82f7e8f4 100644 --- a/apis/v1alpha1/types.go +++ b/apis/v1alpha1/types.go @@ -659,21 +659,13 @@ type CreateRouteInput struct { DestinationPrefixListID *string `json:"destinationPrefixListID,omitempty"` EgressOnlyInternetGatewayID *string `json:"egressOnlyInternetGatewayID,omitempty"` GatewayID *string `json:"gatewayID,omitempty"` - // Reference field for GatewayID - GatewayRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"gatewayRef,omitempty"` - InstanceID *string `json:"instanceID,omitempty"` - LocalGatewayID *string `json:"localGatewayID,omitempty"` - NATGatewayID *string `json:"natGatewayID,omitempty"` - // Reference field for NATGatewayID - NATGatewayRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"natGatewayRef,omitempty"` - NetworkInterfaceID *string `json:"networkInterfaceID,omitempty"` - TransitGatewayID *string `json:"transitGatewayID,omitempty"` - // Reference field for TransitGatewayID - TransitGatewayRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"transitGatewayRef,omitempty"` - VPCEndpointID *string `json:"vpcEndpointID,omitempty"` - // Reference field for VPCEndpointID - VPCEndpointRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"vpcEndpointRef,omitempty"` - VPCPeeringConnectionID *string `json:"vpcPeeringConnectionID,omitempty"` + InstanceID *string `json:"instanceID,omitempty"` + LocalGatewayID *string `json:"localGatewayID,omitempty"` + NATGatewayID *string `json:"natGatewayID,omitempty"` + NetworkInterfaceID *string `json:"networkInterfaceID,omitempty"` + TransitGatewayID *string `json:"transitGatewayID,omitempty"` + VPCEndpointID *string `json:"vpcEndpointID,omitempty"` + VPCPeeringConnectionID *string `json:"vpcPeeringConnectionID,omitempty"` } // Describes the options for a VPC attachment. @@ -3044,6 +3036,7 @@ type PrivateDNSDetails struct { // Information about the private DNS name for the service endpoint. type PrivateDNSNameConfiguration struct { Name *string `json:"name,omitempty"` + State *string `json:"state,omitempty"` Type *string `json:"type_,omitempty"` Value *string `json:"value,omitempty"` } @@ -3655,24 +3648,39 @@ type ServiceConfiguration struct { GatewayLoadBalancerARNs []*string `json:"gatewayLoadBalancerARNs,omitempty"` ManagesVPCEndpoints *bool `json:"managesVPCEndpoints,omitempty"` NetworkLoadBalancerARNs []*string `json:"networkLoadBalancerARNs,omitempty"` + PayerResponsibility *string `json:"payerResponsibility,omitempty"` PrivateDNSName *string `json:"privateDNSName,omitempty"` - ServiceID *string `json:"serviceID,omitempty"` - ServiceName *string `json:"serviceName,omitempty"` - Tags []*Tag `json:"tags,omitempty"` + // Information about the private DNS name for the service endpoint. + PrivateDNSNameConfiguration *PrivateDNSNameConfiguration `json:"privateDNSNameConfiguration,omitempty"` + ServiceID *string `json:"serviceID,omitempty"` + ServiceName *string `json:"serviceName,omitempty"` + ServiceState *string `json:"serviceState,omitempty"` + ServiceType []*ServiceTypeDetail `json:"serviceType,omitempty"` + SupportedIPAddressTypes []*string `json:"supportedIPAddressTypes,omitempty"` + Tags []*Tag `json:"tags,omitempty"` } // Describes a VPC endpoint service. type ServiceDetail struct { - AcceptanceRequired *bool `json:"acceptanceRequired,omitempty"` - AvailabilityZones []*string `json:"availabilityZones,omitempty"` - BaseEndpointDNSNames []*string `json:"baseEndpointDNSNames,omitempty"` - ManagesVPCEndpoints *bool `json:"managesVPCEndpoints,omitempty"` - Owner *string `json:"owner,omitempty"` - PrivateDNSName *string `json:"privateDNSName,omitempty"` - ServiceID *string `json:"serviceID,omitempty"` - ServiceName *string `json:"serviceName,omitempty"` - Tags []*Tag `json:"tags,omitempty"` - VPCEndpointPolicySupported *bool `json:"vpcEndpointPolicySupported,omitempty"` + AcceptanceRequired *bool `json:"acceptanceRequired,omitempty"` + AvailabilityZones []*string `json:"availabilityZones,omitempty"` + BaseEndpointDNSNames []*string `json:"baseEndpointDNSNames,omitempty"` + ManagesVPCEndpoints *bool `json:"managesVPCEndpoints,omitempty"` + Owner *string `json:"owner,omitempty"` + PayerResponsibility *string `json:"payerResponsibility,omitempty"` + PrivateDNSName *string `json:"privateDNSName,omitempty"` + PrivateDNSNameVerificationState *string `json:"privateDNSNameVerificationState,omitempty"` + ServiceID *string `json:"serviceID,omitempty"` + ServiceName *string `json:"serviceName,omitempty"` + ServiceType []*ServiceTypeDetail `json:"serviceType,omitempty"` + SupportedIPAddressTypes []*string `json:"supportedIPAddressTypes,omitempty"` + Tags []*Tag `json:"tags,omitempty"` + VPCEndpointPolicySupported *bool `json:"vpcEndpointPolicySupported,omitempty"` +} + +// Describes the type of service for a VPC endpoint. +type ServiceTypeDetail struct { + ServiceType *string `json:"serviceType,omitempty"` } // Describes the time period for a Scheduled Instance to start its first schedule. diff --git a/apis/v1alpha1/vpc_endpoint_service_configuration.go b/apis/v1alpha1/vpc_endpoint_service_configuration.go new file mode 100644 index 00000000..618d84be --- /dev/null +++ b/apis/v1alpha1/vpc_endpoint_service_configuration.go @@ -0,0 +1,110 @@ +// 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" +) + +// VpcEndpointServiceConfigurationSpec defines the desired state of VpcEndpointServiceConfiguration. +type VPCEndpointServiceConfigurationSpec struct { + + // Indicates whether requests from service consumers to create an endpoint to + // your service must be accepted manually. + AcceptanceRequired *bool `json:"acceptanceRequired,omitempty"` + // The Amazon Resource Names (ARNs) of one or more Gateway Load Balancers. + GatewayLoadBalancerARNs []*string `json:"gatewayLoadBalancerARNs,omitempty"` + // The Amazon Resource Names (ARNs) of one or more Network Load Balancers for + // your service. + NetworkLoadBalancerARNs []*string `json:"networkLoadBalancerARNs,omitempty"` + // (Interface endpoint configuration) The private DNS name to assign to the + // VPC endpoint service. + PrivateDNSName *string `json:"privateDNSName,omitempty"` + // The supported IP address types. The possible values are ipv4 and ipv6. + SupportedIPAddressTypes []*string `json:"supportedIPAddressTypes,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"` +} + +// VPCEndpointServiceConfigurationStatus defines the observed state of VPCEndpointServiceConfiguration +type VPCEndpointServiceConfigurationStatus struct { + // All CRs managed by ACK have a common `Status.ACKResourceMetadata` member + // that is used to contain resource sync state, account ownership, + // constructed ARN for the resource + // +kubebuilder:validation:Optional + ACKResourceMetadata *ackv1alpha1.ResourceMetadata `json:"ackResourceMetadata"` + // All CRS managed by ACK have a common `Status.Conditions` member that + // contains a collection of `ackv1alpha1.Condition` objects that describe + // the various terminal states of the CR and its backend AWS service API + // resource + // +kubebuilder:validation:Optional + Conditions []*ackv1alpha1.Condition `json:"conditions"` + // The Availability Zones in which the service is available. + // +kubebuilder:validation:Optional + AvailabilityZones []*string `json:"availabilityZones,omitempty"` + // The DNS names for the service. + // +kubebuilder:validation:Optional + BaseEndpointDNSNames []*string `json:"baseEndpointDNSNames,omitempty"` + // Indicates whether the service manages its VPC endpoints. Management of the + // service VPC endpoints using the VPC endpoint API is restricted. + // +kubebuilder:validation:Optional + ManagesVPCEndpoints *bool `json:"managesVPCEndpoints,omitempty"` + // The payer responsibility. + // +kubebuilder:validation:Optional + PayerResponsibility *string `json:"payerResponsibility,omitempty"` + // Information about the endpoint service private DNS name configuration. + // +kubebuilder:validation:Optional + PrivateDNSNameConfiguration *PrivateDNSNameConfiguration `json:"privateDNSNameConfiguration,omitempty"` + // The ID of the service. + // +kubebuilder:validation:Optional + ServiceID *string `json:"serviceID,omitempty"` + // The name of the service. + // +kubebuilder:validation:Optional + ServiceName *string `json:"serviceName,omitempty"` + // The service state. + // +kubebuilder:validation:Optional + ServiceState *string `json:"serviceState,omitempty"` + // The type of service. + // +kubebuilder:validation:Optional + ServiceType []*ServiceTypeDetail `json:"serviceType,omitempty"` +} + +// VPCEndpointServiceConfiguration is the Schema for the VPCEndpointServiceConfigurations API +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="ServiceID",type=string,priority=0,JSONPath=`.status.serviceID` +// +kubebuilder:printcolumn:name="ServiceState",type=string,priority=0,JSONPath=`.status.serviceState` +type VPCEndpointServiceConfiguration struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec VPCEndpointServiceConfigurationSpec `json:"spec,omitempty"` + Status VPCEndpointServiceConfigurationStatus `json:"status,omitempty"` +} + +// VPCEndpointServiceConfigurationList contains a list of VPCEndpointServiceConfiguration +// +kubebuilder:object:root=true +type VPCEndpointServiceConfigurationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []VPCEndpointServiceConfiguration `json:"items"` +} + +func init() { + SchemeBuilder.Register(&VPCEndpointServiceConfiguration{}, &VPCEndpointServiceConfigurationList{}) +} diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index b76e40b0..38ed1c57 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -2585,11 +2585,6 @@ func (in *CreateRouteInput) DeepCopyInto(out *CreateRouteInput) { *out = new(string) **out = **in } - if in.GatewayRef != nil { - in, out := &in.GatewayRef, &out.GatewayRef - *out = new(corev1alpha1.AWSResourceReferenceWrapper) - (*in).DeepCopyInto(*out) - } if in.InstanceID != nil { in, out := &in.InstanceID, &out.InstanceID *out = new(string) @@ -2605,11 +2600,6 @@ func (in *CreateRouteInput) DeepCopyInto(out *CreateRouteInput) { *out = new(string) **out = **in } - if in.NATGatewayRef != nil { - in, out := &in.NATGatewayRef, &out.NATGatewayRef - *out = new(corev1alpha1.AWSResourceReferenceWrapper) - (*in).DeepCopyInto(*out) - } if in.NetworkInterfaceID != nil { in, out := &in.NetworkInterfaceID, &out.NetworkInterfaceID *out = new(string) @@ -2620,21 +2610,11 @@ func (in *CreateRouteInput) DeepCopyInto(out *CreateRouteInput) { *out = new(string) **out = **in } - if in.TransitGatewayRef != nil { - in, out := &in.TransitGatewayRef, &out.TransitGatewayRef - *out = new(corev1alpha1.AWSResourceReferenceWrapper) - (*in).DeepCopyInto(*out) - } if in.VPCEndpointID != nil { in, out := &in.VPCEndpointID, &out.VPCEndpointID *out = new(string) **out = **in } - if in.VPCEndpointRef != nil { - in, out := &in.VPCEndpointRef, &out.VPCEndpointRef - *out = new(corev1alpha1.AWSResourceReferenceWrapper) - (*in).DeepCopyInto(*out) - } if in.VPCPeeringConnectionID != nil { in, out := &in.VPCPeeringConnectionID, &out.VPCPeeringConnectionID *out = new(string) @@ -13817,6 +13797,11 @@ func (in *PrivateDNSNameConfiguration) DeepCopyInto(out *PrivateDNSNameConfigura *out = new(string) **out = **in } + if in.State != nil { + in, out := &in.State, &out.State + *out = new(string) + **out = **in + } if in.Type != nil { in, out := &in.Type, &out.Type *out = new(string) @@ -16723,11 +16708,21 @@ func (in *ServiceConfiguration) DeepCopyInto(out *ServiceConfiguration) { } } } + if in.PayerResponsibility != nil { + in, out := &in.PayerResponsibility, &out.PayerResponsibility + *out = new(string) + **out = **in + } if in.PrivateDNSName != nil { in, out := &in.PrivateDNSName, &out.PrivateDNSName *out = new(string) **out = **in } + if in.PrivateDNSNameConfiguration != nil { + in, out := &in.PrivateDNSNameConfiguration, &out.PrivateDNSNameConfiguration + *out = new(PrivateDNSNameConfiguration) + (*in).DeepCopyInto(*out) + } if in.ServiceID != nil { in, out := &in.ServiceID, &out.ServiceID *out = new(string) @@ -16738,6 +16733,33 @@ func (in *ServiceConfiguration) DeepCopyInto(out *ServiceConfiguration) { *out = new(string) **out = **in } + if in.ServiceState != nil { + in, out := &in.ServiceState, &out.ServiceState + *out = new(string) + **out = **in + } + if in.ServiceType != nil { + in, out := &in.ServiceType, &out.ServiceType + *out = make([]*ServiceTypeDetail, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(ServiceTypeDetail) + (*in).DeepCopyInto(*out) + } + } + } + if in.SupportedIPAddressTypes != nil { + in, out := &in.SupportedIPAddressTypes, &out.SupportedIPAddressTypes + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } if in.Tags != nil { in, out := &in.Tags, &out.Tags *out = make([]*Tag, len(*in)) @@ -16801,11 +16823,21 @@ func (in *ServiceDetail) DeepCopyInto(out *ServiceDetail) { *out = new(string) **out = **in } + if in.PayerResponsibility != nil { + in, out := &in.PayerResponsibility, &out.PayerResponsibility + *out = new(string) + **out = **in + } if in.PrivateDNSName != nil { in, out := &in.PrivateDNSName, &out.PrivateDNSName *out = new(string) **out = **in } + if in.PrivateDNSNameVerificationState != nil { + in, out := &in.PrivateDNSNameVerificationState, &out.PrivateDNSNameVerificationState + *out = new(string) + **out = **in + } if in.ServiceID != nil { in, out := &in.ServiceID, &out.ServiceID *out = new(string) @@ -16816,6 +16848,28 @@ func (in *ServiceDetail) DeepCopyInto(out *ServiceDetail) { *out = new(string) **out = **in } + if in.ServiceType != nil { + in, out := &in.ServiceType, &out.ServiceType + *out = make([]*ServiceTypeDetail, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(ServiceTypeDetail) + (*in).DeepCopyInto(*out) + } + } + } + if in.SupportedIPAddressTypes != nil { + in, out := &in.SupportedIPAddressTypes, &out.SupportedIPAddressTypes + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } if in.Tags != nil { in, out := &in.Tags, &out.Tags *out = make([]*Tag, len(*in)) @@ -16844,6 +16898,26 @@ func (in *ServiceDetail) DeepCopy() *ServiceDetail { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceTypeDetail) DeepCopyInto(out *ServiceTypeDetail) { + *out = *in + if in.ServiceType != nil { + in, out := &in.ServiceType, &out.ServiceType + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceTypeDetail. +func (in *ServiceTypeDetail) DeepCopy() *ServiceTypeDetail { + if in == nil { + return nil + } + out := new(ServiceTypeDetail) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SlotDateTimeRangeRequest) DeepCopyInto(out *SlotDateTimeRangeRequest) { *out = *in @@ -21314,6 +21388,228 @@ func (in *VPCEndpointList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VPCEndpointServiceConfiguration) DeepCopyInto(out *VPCEndpointServiceConfiguration) { + *out = *in + 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 VPCEndpointServiceConfiguration. +func (in *VPCEndpointServiceConfiguration) DeepCopy() *VPCEndpointServiceConfiguration { + if in == nil { + return nil + } + out := new(VPCEndpointServiceConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VPCEndpointServiceConfiguration) 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 *VPCEndpointServiceConfigurationList) DeepCopyInto(out *VPCEndpointServiceConfigurationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VPCEndpointServiceConfiguration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VPCEndpointServiceConfigurationList. +func (in *VPCEndpointServiceConfigurationList) DeepCopy() *VPCEndpointServiceConfigurationList { + if in == nil { + return nil + } + out := new(VPCEndpointServiceConfigurationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VPCEndpointServiceConfigurationList) 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 *VPCEndpointServiceConfigurationSpec) DeepCopyInto(out *VPCEndpointServiceConfigurationSpec) { + *out = *in + if in.AcceptanceRequired != nil { + in, out := &in.AcceptanceRequired, &out.AcceptanceRequired + *out = new(bool) + **out = **in + } + if in.GatewayLoadBalancerARNs != nil { + in, out := &in.GatewayLoadBalancerARNs, &out.GatewayLoadBalancerARNs + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } + if in.NetworkLoadBalancerARNs != nil { + in, out := &in.NetworkLoadBalancerARNs, &out.NetworkLoadBalancerARNs + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } + if in.PrivateDNSName != nil { + in, out := &in.PrivateDNSName, &out.PrivateDNSName + *out = new(string) + **out = **in + } + if in.SupportedIPAddressTypes != nil { + in, out := &in.SupportedIPAddressTypes, &out.SupportedIPAddressTypes + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]*Tag, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Tag) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VPCEndpointServiceConfigurationSpec. +func (in *VPCEndpointServiceConfigurationSpec) DeepCopy() *VPCEndpointServiceConfigurationSpec { + if in == nil { + return nil + } + out := new(VPCEndpointServiceConfigurationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VPCEndpointServiceConfigurationStatus) DeepCopyInto(out *VPCEndpointServiceConfigurationStatus) { + *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.AvailabilityZones != nil { + in, out := &in.AvailabilityZones, &out.AvailabilityZones + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } + if in.BaseEndpointDNSNames != nil { + in, out := &in.BaseEndpointDNSNames, &out.BaseEndpointDNSNames + *out = make([]*string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(string) + **out = **in + } + } + } + if in.ManagesVPCEndpoints != nil { + in, out := &in.ManagesVPCEndpoints, &out.ManagesVPCEndpoints + *out = new(bool) + **out = **in + } + if in.PayerResponsibility != nil { + in, out := &in.PayerResponsibility, &out.PayerResponsibility + *out = new(string) + **out = **in + } + if in.PrivateDNSNameConfiguration != nil { + in, out := &in.PrivateDNSNameConfiguration, &out.PrivateDNSNameConfiguration + *out = new(PrivateDNSNameConfiguration) + (*in).DeepCopyInto(*out) + } + if in.ServiceID != nil { + in, out := &in.ServiceID, &out.ServiceID + *out = new(string) + **out = **in + } + if in.ServiceName != nil { + in, out := &in.ServiceName, &out.ServiceName + *out = new(string) + **out = **in + } + if in.ServiceState != nil { + in, out := &in.ServiceState, &out.ServiceState + *out = new(string) + **out = **in + } + if in.ServiceType != nil { + in, out := &in.ServiceType, &out.ServiceType + *out = make([]*ServiceTypeDetail, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(ServiceTypeDetail) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VPCEndpointServiceConfigurationStatus. +func (in *VPCEndpointServiceConfigurationStatus) DeepCopy() *VPCEndpointServiceConfigurationStatus { + if in == nil { + return nil + } + out := new(VPCEndpointServiceConfigurationStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VPCEndpointSpec) DeepCopyInto(out *VPCEndpointSpec) { *out = *in diff --git a/cmd/controller/main.go b/cmd/controller/main.go index e3e4fef9..1de5362f 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -45,6 +45,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_endpoint_service_configuration" "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..82fbb92b 100644 --- a/config/crd/bases/ec2.services.k8s.aws_routetables.yaml +++ b/config/crd/bases/ec2.services.k8s.aws_routetables.yaml @@ -57,66 +57,18 @@ spec: type: string gatewayID: type: string - gatewayRef: - description: Reference field for GatewayID - properties: - from: - description: AWSResourceReference provides all the values - necessary to reference another k8s resource for finding - the identifier(Id/ARN/Name) - properties: - name: - type: string - type: object - type: object instanceID: type: string localGatewayID: type: string natGatewayID: type: string - natGatewayRef: - description: Reference field for NATGatewayID - properties: - from: - description: AWSResourceReference provides all the values - necessary to reference another k8s resource for finding - the identifier(Id/ARN/Name) - properties: - name: - type: string - type: object - type: object networkInterfaceID: type: string transitGatewayID: type: string - transitGatewayRef: - description: Reference field for TransitGatewayID - properties: - from: - description: AWSResourceReference provides all the values - necessary to reference another k8s resource for finding - the identifier(Id/ARN/Name) - properties: - name: - type: string - type: object - type: object vpcEndpointID: type: string - vpcEndpointRef: - description: Reference field for VPCEndpointID - properties: - from: - description: AWSResourceReference provides all the values - necessary to reference another k8s resource for finding - the identifier(Id/ARN/Name) - properties: - name: - type: string - type: object - type: object vpcPeeringConnectionID: type: string type: object diff --git a/config/crd/bases/ec2.services.k8s.aws_vpcendpointserviceconfigurations.yaml b/config/crd/bases/ec2.services.k8s.aws_vpcendpointserviceconfigurations.yaml new file mode 100644 index 00000000..ca544996 --- /dev/null +++ b/config/crd/bases/ec2.services.k8s.aws_vpcendpointserviceconfigurations.yaml @@ -0,0 +1,206 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: vpcendpointserviceconfigurations.ec2.services.k8s.aws +spec: + group: ec2.services.k8s.aws + names: + kind: VPCEndpointServiceConfiguration + listKind: VPCEndpointServiceConfigurationList + plural: vpcendpointserviceconfigurations + singular: vpcendpointserviceconfiguration + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.serviceID + name: ServiceID + type: string + - jsonPath: .status.serviceState + name: ServiceState + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: VPCEndpointServiceConfiguration is the Schema for the VPCEndpointServiceConfigurations + 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: VpcEndpointServiceConfigurationSpec defines the desired state + of VpcEndpointServiceConfiguration. + properties: + acceptanceRequired: + description: Indicates whether requests from service consumers to + create an endpoint to your service must be accepted manually. + type: boolean + gatewayLoadBalancerARNs: + description: The Amazon Resource Names (ARNs) of one or more Gateway + Load Balancers. + items: + type: string + type: array + networkLoadBalancerARNs: + description: The Amazon Resource Names (ARNs) of one or more Network + Load Balancers for your service. + items: + type: string + type: array + privateDNSName: + description: (Interface endpoint configuration) The private DNS name + to assign to the VPC endpoint service. + type: string + supportedIPAddressTypes: + description: The supported IP address types. The possible values are + ipv4 and ipv6. + items: + type: string + type: array + tags: + description: The tags. The value parameter is required, but if you + don't want the tag to have a value, specify the parameter with no + value, and we set the value to an empty string. + items: + description: Describes a tag. + properties: + key: + type: string + value: + type: string + type: object + type: array + type: object + status: + description: VPCEndpointServiceConfigurationStatus defines the observed + state of VPCEndpointServiceConfiguration + properties: + ackResourceMetadata: + description: All CRs managed by ACK have a common `Status.ACKResourceMetadata` + member that is used to contain resource sync state, account ownership, + constructed ARN for the resource + properties: + arn: + description: 'ARN is the Amazon Resource Name for the resource. + This is a globally-unique identifier and is set only by the + ACK service controller once the controller has orchestrated + the creation of the resource OR when it has verified that an + "adopted" resource (a resource where the ARN annotation was + set by the Kubernetes user on the CR) exists and matches the + supplied CR''s Spec field values. 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 + availabilityZones: + description: The Availability Zones in which the service is available. + items: + type: string + type: array + baseEndpointDNSNames: + description: The DNS names for the service. + items: + type: string + type: array + 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 + managesVPCEndpoints: + description: Indicates whether the service manages its VPC endpoints. + Management of the service VPC endpoints using the VPC endpoint API + is restricted. + type: boolean + payerResponsibility: + description: The payer responsibility. + type: string + privateDNSNameConfiguration: + description: Information about the endpoint service private DNS name + configuration. + properties: + name: + type: string + state: + type: string + type_: + type: string + value: + type: string + type: object + serviceID: + description: The ID of the service. + type: string + serviceName: + description: The name of the service. + type: string + serviceState: + description: The service state. + type: string + serviceType: + description: The type of service. + items: + description: Describes the type of service for a VPC endpoint. + properties: + serviceType: + type: string + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index d324d847..9a24ae52 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -1,8 +1,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -bases: - - common resources: + - common - bases/ec2.services.k8s.aws_dhcpoptions.yaml - bases/ec2.services.k8s.aws_elasticipaddresses.yaml - bases/ec2.services.k8s.aws_instances.yaml @@ -14,3 +13,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_vpcendpointserviceconfigurations.yaml diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index b4521337..c89f8ed4 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -12,7 +12,7 @@ #commonLabels: # someName: someValue -bases: +resources: - ../crd - ../rbac - ../controller diff --git a/config/rbac/cluster-role-controller.yaml b/config/rbac/cluster-role-controller.yaml index 1a34cee5..6ca3d9a5 100644 --- a/config/rbac/cluster-role-controller.yaml +++ b/config/rbac/cluster-role-controller.yaml @@ -231,6 +231,26 @@ rules: - get - patch - update +- apiGroups: + - ec2.services.k8s.aws + resources: + - vpcendpointserviceconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - ec2.services.k8s.aws + resources: + - vpcendpointserviceconfigurations/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 e7793f78..b569f660 100644 --- a/config/rbac/role-reader.yaml +++ b/config/rbac/role-reader.yaml @@ -20,6 +20,7 @@ rules: - transitgateways - vpcs - vpcendpoints + - vpcendpointserviceconfigurations verbs: - get - list diff --git a/config/rbac/role-writer.yaml b/config/rbac/role-writer.yaml index 0d62a00d..f2d97745 100644 --- a/config/rbac/role-writer.yaml +++ b/config/rbac/role-writer.yaml @@ -20,6 +20,7 @@ rules: - transitgateways - vpcs - vpcendpoints + - vpcendpointserviceconfigurations verbs: - create - delete @@ -42,6 +43,7 @@ rules: - transitgateways - vpcs - vpcendpoints + - vpcendpointserviceconfigurations verbs: - get - patch diff --git a/generator.yaml b/generator.yaml index a5df9075..61174650 100644 --- a/generator.yaml +++ b/generator.yaml @@ -29,6 +29,9 @@ ignore: - CreateVpcEndpointInput.DryRun - CreateVpcEndpointInput.TagSpecifications - CreateVpcEndpointInput.ClientToken + - CreateVpcEndpointServiceConfigurationInput.ClientToken + - CreateVpcEndpointServiceConfigurationInput.DryRun + - CreateVpcEndpointServiceConfigurationInput.TagSpecifications - DeleteRouteInput.DryRun - DeleteRouteInput.RouteTableId # support EC2-VPC only @@ -111,7 +114,7 @@ ignore: #- TransitGateway - Volume - VpcEndpointConnectionNotification - - VpcEndpointServiceConfiguration + #- VpcEndpointServiceConfiguration #- VpcEndpoint #- Vpc - VpcCidrBlock @@ -141,6 +144,12 @@ operations: operation_type: - Delete resource_name: VpcEndpoint + CreateVpcEndpointServiceConfiguration: + output_wrapper_field_path: ServiceConfiguration + DeleteVpcEndpointServiceConfigurations: + operation_type: + - Delete + resource_name: VpcEndpointServiceConfiguration RunInstances: #ouput shape: Reservation output_wrapper_field_path: Instances @@ -378,22 +387,22 @@ resources: references: resource: VPC path: Status.VPCID - Routes.GatewayId: - references: - resource: InternetGateway - path: Status.InternetGatewayID - Routes.NatGatewayId: - references: - resource: NATGateway - path: Status.NATGatewayID - Routes.TransitGatewayId: - references: - resource: TransitGateway - path: Status.TransitGatewayID - Routes.VpcEndpointId: - references: - resource: VPCEndpoint - path: Status.VPCEndpointID +# Routes.GatewayId: +# references: +# resource: InternetGateway +# path: Status.InternetGatewayID +# Routes.NatGatewayId: +# references: +# resource: NATGateway +# path: Status.NATGatewayID +# Routes.TransitGatewayId: +# references: +# resource: TransitGateway +# path: Status.TransitGatewayID +# Routes.VpcEndpointId: +# references: +# resource: VPCEndpoint +# path: Status.VPCEndpointID hooks: delta_pre_compare: code: customPreCompare(delta, a, b) @@ -465,6 +474,37 @@ resources: template_path: hooks/security_group/sdk_read_many_post_set_output.go.tpl update_operation: custom_method_name: customUpdateSecurityGroup + VpcEndpointServiceConfiguration: + fields: + ServiceID: + is_primary_key: true + is_read_only: true + print: + path: Status.serviceID + name: ServiceID + ServiceState: + is_read_only: true + print: + path: Status.serviceState + name: ServiceState + Tags: + from: + operation: CreateTags + path: Tags + compare: + is_ignored: true + synced: + when: + - path: Status.ServiceState + in: + - available + hooks: + delta_pre_compare: + code: compareTags(delta, a, b) + sdk_delete_post_build_request: + template_path: hooks/vpc_endpoint_service_configuration/sdk_delete_post_build_request.go.tpl + sdk_file_end: + template_path: hooks/vpc_endpoint_service_configuration/sdk_file_end.go.tpl Subnet: fields: # code-generator infers fields into diff --git a/helm/crds/ec2.services.k8s.aws_routetables.yaml b/helm/crds/ec2.services.k8s.aws_routetables.yaml index cf0270b5..82fbb92b 100644 --- a/helm/crds/ec2.services.k8s.aws_routetables.yaml +++ b/helm/crds/ec2.services.k8s.aws_routetables.yaml @@ -57,66 +57,18 @@ spec: type: string gatewayID: type: string - gatewayRef: - description: Reference field for GatewayID - properties: - from: - description: AWSResourceReference provides all the values - necessary to reference another k8s resource for finding - the identifier(Id/ARN/Name) - properties: - name: - type: string - type: object - type: object instanceID: type: string localGatewayID: type: string natGatewayID: type: string - natGatewayRef: - description: Reference field for NATGatewayID - properties: - from: - description: AWSResourceReference provides all the values - necessary to reference another k8s resource for finding - the identifier(Id/ARN/Name) - properties: - name: - type: string - type: object - type: object networkInterfaceID: type: string transitGatewayID: type: string - transitGatewayRef: - description: Reference field for TransitGatewayID - properties: - from: - description: AWSResourceReference provides all the values - necessary to reference another k8s resource for finding - the identifier(Id/ARN/Name) - properties: - name: - type: string - type: object - type: object vpcEndpointID: type: string - vpcEndpointRef: - description: Reference field for VPCEndpointID - properties: - from: - description: AWSResourceReference provides all the values - necessary to reference another k8s resource for finding - the identifier(Id/ARN/Name) - properties: - name: - type: string - type: object - type: object vpcPeeringConnectionID: type: string type: object diff --git a/helm/crds/ec2.services.k8s.aws_vpcendpointserviceconfigurations.yaml b/helm/crds/ec2.services.k8s.aws_vpcendpointserviceconfigurations.yaml new file mode 100644 index 00000000..ca544996 --- /dev/null +++ b/helm/crds/ec2.services.k8s.aws_vpcendpointserviceconfigurations.yaml @@ -0,0 +1,206 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: vpcendpointserviceconfigurations.ec2.services.k8s.aws +spec: + group: ec2.services.k8s.aws + names: + kind: VPCEndpointServiceConfiguration + listKind: VPCEndpointServiceConfigurationList + plural: vpcendpointserviceconfigurations + singular: vpcendpointserviceconfiguration + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.serviceID + name: ServiceID + type: string + - jsonPath: .status.serviceState + name: ServiceState + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: VPCEndpointServiceConfiguration is the Schema for the VPCEndpointServiceConfigurations + 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: VpcEndpointServiceConfigurationSpec defines the desired state + of VpcEndpointServiceConfiguration. + properties: + acceptanceRequired: + description: Indicates whether requests from service consumers to + create an endpoint to your service must be accepted manually. + type: boolean + gatewayLoadBalancerARNs: + description: The Amazon Resource Names (ARNs) of one or more Gateway + Load Balancers. + items: + type: string + type: array + networkLoadBalancerARNs: + description: The Amazon Resource Names (ARNs) of one or more Network + Load Balancers for your service. + items: + type: string + type: array + privateDNSName: + description: (Interface endpoint configuration) The private DNS name + to assign to the VPC endpoint service. + type: string + supportedIPAddressTypes: + description: The supported IP address types. The possible values are + ipv4 and ipv6. + items: + type: string + type: array + tags: + description: The tags. The value parameter is required, but if you + don't want the tag to have a value, specify the parameter with no + value, and we set the value to an empty string. + items: + description: Describes a tag. + properties: + key: + type: string + value: + type: string + type: object + type: array + type: object + status: + description: VPCEndpointServiceConfigurationStatus defines the observed + state of VPCEndpointServiceConfiguration + properties: + ackResourceMetadata: + description: All CRs managed by ACK have a common `Status.ACKResourceMetadata` + member that is used to contain resource sync state, account ownership, + constructed ARN for the resource + properties: + arn: + description: 'ARN is the Amazon Resource Name for the resource. + This is a globally-unique identifier and is set only by the + ACK service controller once the controller has orchestrated + the creation of the resource OR when it has verified that an + "adopted" resource (a resource where the ARN annotation was + set by the Kubernetes user on the CR) exists and matches the + supplied CR''s Spec field values. 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 + availabilityZones: + description: The Availability Zones in which the service is available. + items: + type: string + type: array + baseEndpointDNSNames: + description: The DNS names for the service. + items: + type: string + type: array + 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 + managesVPCEndpoints: + description: Indicates whether the service manages its VPC endpoints. + Management of the service VPC endpoints using the VPC endpoint API + is restricted. + type: boolean + payerResponsibility: + description: The payer responsibility. + type: string + privateDNSNameConfiguration: + description: Information about the endpoint service private DNS name + configuration. + properties: + name: + type: string + state: + type: string + type_: + type: string + value: + type: string + type: object + serviceID: + description: The ID of the service. + type: string + serviceName: + description: The name of the service. + type: string + serviceState: + description: The service state. + type: string + serviceType: + description: The type of service. + items: + description: Describes the type of service for a VPC endpoint. + properties: + serviceType: + type: string + type: object + type: array + 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 15543962..7d4e8ae0 100644 --- a/helm/templates/cluster-role-controller.yaml +++ b/helm/templates/cluster-role-controller.yaml @@ -246,6 +246,26 @@ rules: - get - patch - update +- apiGroups: + - ec2.services.k8s.aws + resources: + - vpcendpointserviceconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - ec2.services.k8s.aws + resources: + - vpcendpointserviceconfigurations/status + verbs: + - get + - patch + - update - apiGroups: - ec2.services.k8s.aws resources: diff --git a/helm/templates/role-reader.yaml b/helm/templates/role-reader.yaml index 08760026..300c928e 100644 --- a/helm/templates/role-reader.yaml +++ b/helm/templates/role-reader.yaml @@ -20,6 +20,7 @@ rules: - transitgateways - vpcs - vpcendpoints + - vpcendpointserviceconfigurations verbs: - get - list diff --git a/helm/templates/role-writer.yaml b/helm/templates/role-writer.yaml index 86939537..b36e67ec 100644 --- a/helm/templates/role-writer.yaml +++ b/helm/templates/role-writer.yaml @@ -31,6 +31,8 @@ rules: - vpcendpoints + - vpcendpointserviceconfigurations + verbs: - create - delete @@ -53,6 +55,7 @@ rules: - transitgateways - vpcs - vpcendpoints + - vpcendpointserviceconfigurations verbs: - get - patch diff --git a/pkg/resource/internet_gateway/references.go b/pkg/resource/internet_gateway/references.go index 26849b13..299b9b67 100644 --- a/pkg/resource/internet_gateway/references.go +++ b/pkg/resource/internet_gateway/references.go @@ -85,50 +85,68 @@ func resolveReferenceForVPC( namespace string, ko *svcapitypes.InternetGateway, ) error { - if ko.Spec.VPCRef != nil && - ko.Spec.VPCRef.From != nil { + if ko.Spec.VPCRef != nil && ko.Spec.VPCRef.From != nil { arr := ko.Spec.VPCRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: VPCRef") } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.VPC{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { + obj := &svcapitypes.VPC{} + if err := getReferencedResourceState_VPC(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } + ko.Spec.VPC = (*string)(obj.Status.VPCID) + } + + return 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 refResourceTerminal { + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { return ackerr.ResourceReferenceTerminalFor( "VPC", - namespace, *arr.Name) + namespace, name) } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "VPC", - namespace, *arr.Name) - } - if obj.Status.VPCID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "VPC", - namespace, *arr.Name, - "Status.VPCID") - } - referencedValue := string(*obj.Status.VPCID) - ko.Spec.VPC = &referencedValue + } + 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 } diff --git a/pkg/resource/nat_gateway/references.go b/pkg/resource/nat_gateway/references.go index 51d7a2a7..90becabc 100644 --- a/pkg/resource/nat_gateway/references.go +++ b/pkg/resource/nat_gateway/references.go @@ -94,50 +94,68 @@ func resolveReferenceForAllocationID( namespace string, ko *svcapitypes.NATGateway, ) error { - if ko.Spec.AllocationRef != nil && - ko.Spec.AllocationRef.From != nil { + if ko.Spec.AllocationRef != nil && ko.Spec.AllocationRef.From != nil { arr := ko.Spec.AllocationRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: AllocationRef") } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.ElasticIPAddress{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { + obj := &svcapitypes.ElasticIPAddress{} + if err := getReferencedResourceState_ElasticIPAddress(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } + ko.Spec.AllocationID = (*string)(obj.Status.AllocationID) + } + + return nil +} + +// getReferencedResourceState_ElasticIPAddress 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_ElasticIPAddress( + ctx context.Context, + apiReader client.Reader, + obj *svcapitypes.ElasticIPAddress, + 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 refResourceTerminal { + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { return ackerr.ResourceReferenceTerminalFor( "ElasticIPAddress", - namespace, *arr.Name) + namespace, name) } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "ElasticIPAddress", - namespace, *arr.Name) - } - if obj.Status.AllocationID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "ElasticIPAddress", - namespace, *arr.Name, - "Status.AllocationID") - } - referencedValue := string(*obj.Status.AllocationID) - ko.Spec.AllocationID = &referencedValue + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "ElasticIPAddress", + namespace, name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "ElasticIPAddress", + namespace, name) + } + if obj.Status.AllocationID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "ElasticIPAddress", + namespace, name, + "Status.AllocationID") } return nil } @@ -151,50 +169,68 @@ func resolveReferenceForSubnetID( namespace string, ko *svcapitypes.NATGateway, ) error { - if ko.Spec.SubnetRef != nil && - ko.Spec.SubnetRef.From != nil { + if ko.Spec.SubnetRef != nil && ko.Spec.SubnetRef.From != nil { arr := ko.Spec.SubnetRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: SubnetRef") } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.Subnet{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { + obj := &svcapitypes.Subnet{} + if err := getReferencedResourceState_Subnet(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } + ko.Spec.SubnetID = (*string)(obj.Status.SubnetID) + } + + return nil +} + +// getReferencedResourceState_Subnet looks up whether a referenced resource +// exists and is in a ACK.ResourceSynced=True state. If the referenced resource does exist and is +// in a Synced state, returns nil, otherwise returns `ackerr.ResourceReferenceTerminalFor` or +// `ResourceReferenceNotSyncedFor` depending on if the resource is in a Terminal state. +func getReferencedResourceState_Subnet( + ctx context.Context, + apiReader client.Reader, + obj *svcapitypes.Subnet, + name string, // the Kubernetes name of the referenced resource + namespace string, // the Kubernetes namespace of the referenced resource +) error { + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + err := apiReader.Get(ctx, namespacedName, obj) + if err != nil { + return err + } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true } - if refResourceTerminal { + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { return ackerr.ResourceReferenceTerminalFor( "Subnet", - namespace, *arr.Name) + namespace, name) } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "Subnet", - namespace, *arr.Name) - } - if obj.Status.SubnetID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "Subnet", - namespace, *arr.Name, - "Status.SubnetID") - } - referencedValue := string(*obj.Status.SubnetID) - ko.Spec.SubnetID = &referencedValue + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "Subnet", + namespace, name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "Subnet", + namespace, name) + } + if obj.Status.SubnetID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "Subnet", + namespace, name, + "Status.SubnetID") } return nil } diff --git a/pkg/resource/route_table/references.go b/pkg/resource/route_table/references.go index e78cb66c..6303c40c 100644 --- a/pkg/resource/route_table/references.go +++ b/pkg/resource/route_table/references.go @@ -46,18 +46,6 @@ func (rm *resourceManager) ResolveReferences( namespace := res.MetaObject().GetNamespace() ko := rm.concreteResource(res).ko.DeepCopy() err := validateReferenceFields(ko) - if err == nil { - err = resolveReferenceForRoutes_GatewayID(ctx, apiReader, namespace, ko) - } - if err == nil { - err = resolveReferenceForRoutes_NATGatewayID(ctx, apiReader, namespace, ko) - } - if err == nil { - err = resolveReferenceForRoutes_TransitGatewayID(ctx, apiReader, namespace, ko) - } - if err == nil { - err = resolveReferenceForRoutes_VPCEndpointID(ctx, apiReader, namespace, ko) - } if err == nil { err = resolveReferenceForVPCID(ctx, apiReader, namespace, ko) } @@ -76,26 +64,6 @@ func (rm *resourceManager) ResolveReferences( // validateReferenceFields validates the reference field and corresponding // identifier field. func validateReferenceFields(ko *svcapitypes.RouteTable) error { - for _, iter0 := range ko.Spec.Routes { - if iter0.GatewayRef != nil && iter0.GatewayID != nil { - return ackerr.ResourceReferenceAndIDNotSupportedFor("Routes.GatewayID", "Routes.GatewayRef") - } - } - for _, iter0 := range ko.Spec.Routes { - if iter0.NATGatewayRef != nil && iter0.NATGatewayID != nil { - return ackerr.ResourceReferenceAndIDNotSupportedFor("Routes.NATGatewayID", "Routes.NATGatewayRef") - } - } - for _, iter0 := range ko.Spec.Routes { - if iter0.TransitGatewayRef != nil && iter0.TransitGatewayID != nil { - return ackerr.ResourceReferenceAndIDNotSupportedFor("Routes.TransitGatewayID", "Routes.TransitGatewayRef") - } - } - for _, iter0 := range ko.Spec.Routes { - if iter0.VPCEndpointRef != nil && iter0.VPCEndpointID != nil { - return ackerr.ResourceReferenceAndIDNotSupportedFor("Routes.VPCEndpointID", "Routes.VPCEndpointRef") - } - } if ko.Spec.VPCRef != nil && ko.Spec.VPCID != nil { return ackerr.ResourceReferenceAndIDNotSupportedFor("VPCID", "VPCRef") } @@ -108,378 +76,80 @@ func validateReferenceFields(ko *svcapitypes.RouteTable) error { // hasNonNilReferences returns true if resource contains a reference to another // resource func hasNonNilReferences(ko *svcapitypes.RouteTable) bool { - if ko.Spec.Routes != nil { - for _, iter37 := range ko.Spec.Routes { - if iter37.GatewayRef != nil { - return true - } - } - } - if ko.Spec.Routes != nil { - for _, iter40 := range ko.Spec.Routes { - if iter40.NATGatewayRef != nil { - return true - } - } - } - if ko.Spec.Routes != nil { - for _, iter42 := range ko.Spec.Routes { - if iter42.TransitGatewayRef != nil { - return true - } - } - } - if ko.Spec.Routes != nil { - for _, iter43 := range ko.Spec.Routes { - if iter43.VPCEndpointRef != nil { - return true - } - } - } return false || (ko.Spec.VPCRef != nil) } -// resolveReferenceForRoutes_GatewayID reads the resource referenced -// from Routes.GatewayRef field and sets the Routes.GatewayID +// resolveReferenceForVPCID reads the resource referenced +// from VPCRef field and sets the VPCID // from referenced resource -func resolveReferenceForRoutes_GatewayID( +func resolveReferenceForVPCID( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.RouteTable, ) error { - if ko.Spec.Routes == nil { - return nil - } - - if len(ko.Spec.Routes) > 0 { - for _, elem := range ko.Spec.Routes { - arrw := elem.GatewayRef - - if arrw == nil || arrw.From == nil { - continue - } - - arr := arrw.From - if arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") - } - - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") - } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.InternetGateway{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { - return err - } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } - } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "InternetGateway", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "InternetGateway", - namespace, *arr.Name) - } - if obj.Status.InternetGatewayID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "InternetGateway", - namespace, *arr.Name, - "Status.InternetGatewayID") - } - referencedValue := string(*obj.Status.InternetGatewayID) - elem.GatewayID = &referencedValue + if ko.Spec.VPCRef != nil && ko.Spec.VPCRef.From != nil { + arr := ko.Spec.VPCRef.From + if arr == nil || arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty: VPCRef") } - } - return nil -} - -// resolveReferenceForRoutes_NATGatewayID reads the resource referenced -// from Routes.NATGatewayRef field and sets the Routes.NATGatewayID -// from referenced resource -func resolveReferenceForRoutes_NATGatewayID( - ctx context.Context, - apiReader client.Reader, - namespace string, - ko *svcapitypes.RouteTable, -) error { - if ko.Spec.Routes == nil { - return nil - } - - if len(ko.Spec.Routes) > 0 { - for _, elem := range ko.Spec.Routes { - arrw := elem.NATGatewayRef - - if arrw == nil || arrw.From == nil { - continue - } - - arr := arrw.From - if arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") - } - - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") - } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.NATGateway{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { - return err - } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } - } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "NATGateway", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "NATGateway", - namespace, *arr.Name) - } - if obj.Status.NATGatewayID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "NATGateway", - namespace, *arr.Name, - "Status.NATGatewayID") - } - referencedValue := string(*obj.Status.NATGatewayID) - elem.NATGatewayID = &referencedValue + obj := &svcapitypes.VPC{} + if err := getReferencedResourceState_VPC(ctx, apiReader, obj, *arr.Name, namespace); err != nil { + return err } + ko.Spec.VPCID = (*string)(obj.Status.VPCID) } - return nil -} - -// resolveReferenceForRoutes_TransitGatewayID reads the resource referenced -// from Routes.TransitGatewayRef field and sets the Routes.TransitGatewayID -// from referenced resource -func resolveReferenceForRoutes_TransitGatewayID( - ctx context.Context, - apiReader client.Reader, - namespace string, - ko *svcapitypes.RouteTable, -) error { - if ko.Spec.Routes == nil { - return nil - } - - if len(ko.Spec.Routes) > 0 { - for _, elem := range ko.Spec.Routes { - arrw := elem.TransitGatewayRef - - if arrw == nil || arrw.From == nil { - continue - } - - arr := arrw.From - if arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") - } - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") - } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.TransitGateway{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { - return err - } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } - } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "TransitGateway", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "TransitGateway", - namespace, *arr.Name) - } - if obj.Status.TransitGatewayID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "TransitGateway", - namespace, *arr.Name, - "Status.TransitGatewayID") - } - referencedValue := string(*obj.Status.TransitGatewayID) - elem.TransitGatewayID = &referencedValue - } - } return nil } -// resolveReferenceForRoutes_VPCEndpointID reads the resource referenced -// from Routes.VPCEndpointRef field and sets the Routes.VPCEndpointID -// from referenced resource -func resolveReferenceForRoutes_VPCEndpointID( +// 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, - namespace string, - ko *svcapitypes.RouteTable, + obj *svcapitypes.VPC, + name string, // the Kubernetes name of the referenced resource + namespace string, // the Kubernetes namespace of the referenced resource ) error { - if ko.Spec.Routes == nil { - return nil + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, } - - if len(ko.Spec.Routes) > 0 { - for _, elem := range ko.Spec.Routes { - arrw := elem.VPCEndpointRef - - if arrw == nil || arrw.From == nil { - continue - } - - arr := arrw.From - if arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") - } - - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") - } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.VPCEndpoint{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { - return err - } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } - } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "VPCEndpoint", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "VPCEndpoint", - namespace, *arr.Name) - } - if obj.Status.VPCEndpointID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "VPCEndpoint", - namespace, *arr.Name, - "Status.VPCEndpointID") - } - referencedValue := string(*obj.Status.VPCEndpointID) - elem.VPCEndpointID = &referencedValue - } + err := apiReader.Get(ctx, namespacedName, obj) + if err != nil { + return err } - return nil -} - -// resolveReferenceForVPCID reads the resource referenced -// from VPCRef field and sets the VPCID -// from referenced resource -func resolveReferenceForVPCID( - ctx context.Context, - apiReader client.Reader, - namespace string, - ko *svcapitypes.RouteTable, -) error { - if ko.Spec.VPCRef != nil && - ko.Spec.VPCRef.From != nil { - arr := ko.Spec.VPCRef.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") - } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.VPC{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { - return err - } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true } - if refResourceTerminal { + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { return ackerr.ResourceReferenceTerminalFor( "VPC", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "VPC", - namespace, *arr.Name) - } - if obj.Status.VPCID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "VPC", - namespace, *arr.Name, - "Status.VPCID") + namespace, name) } - referencedValue := string(*obj.Status.VPCID) - ko.Spec.VPCID = &referencedValue + } + 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 } diff --git a/pkg/resource/security_group/references.go b/pkg/resource/security_group/references.go index 2057062e..fc59c72b 100644 --- a/pkg/resource/security_group/references.go +++ b/pkg/resource/security_group/references.go @@ -88,50 +88,68 @@ func resolveReferenceForVPCID( namespace string, ko *svcapitypes.SecurityGroup, ) error { - if ko.Spec.VPCRef != nil && - ko.Spec.VPCRef.From != nil { + if ko.Spec.VPCRef != nil && ko.Spec.VPCRef.From != nil { arr := ko.Spec.VPCRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: VPCRef") } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.VPC{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { + obj := &svcapitypes.VPC{} + if err := getReferencedResourceState_VPC(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } + ko.Spec.VPCID = (*string)(obj.Status.VPCID) + } + + return 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 refResourceTerminal { + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { return ackerr.ResourceReferenceTerminalFor( "VPC", - namespace, *arr.Name) + namespace, name) } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "VPC", - namespace, *arr.Name) - } - if obj.Status.VPCID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "VPC", - namespace, *arr.Name, - "Status.VPCID") - } - referencedValue := string(*obj.Status.VPCID) - ko.Spec.VPCID = &referencedValue + } + 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 } diff --git a/pkg/resource/subnet/references.go b/pkg/resource/subnet/references.go index a58166fe..5ca08f9e 100644 --- a/pkg/resource/subnet/references.go +++ b/pkg/resource/subnet/references.go @@ -94,54 +94,72 @@ func resolveReferenceForRouteTables( namespace string, ko *svcapitypes.Subnet, ) error { - if ko.Spec.RouteTableRefs != nil && - len(ko.Spec.RouteTableRefs) > 0 { - resolvedReferences := []*string{} - for _, arrw := range ko.Spec.RouteTableRefs { - arr := arrw.From + if len(ko.Spec.RouteTableRefs) > 0 { + resolved0 := []*string{} + for _, iter0 := range ko.Spec.RouteTableRefs { + arr := iter0.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: RouteTableRefs") } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.RouteTable{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { + obj := &svcapitypes.RouteTable{} + if err := getReferencedResourceState_RouteTable(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } - } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "RouteTable", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "RouteTable", - namespace, *arr.Name) - } - if obj.Status.RouteTableID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "RouteTable", - namespace, *arr.Name, - "Status.RouteTableID") - } - referencedValue := string(*obj.Status.RouteTableID) - resolvedReferences = append(resolvedReferences, &referencedValue) + resolved0 = append(resolved0, (*string)(obj.Status.RouteTableID)) + } + ko.Spec.RouteTables = resolved0 + } + + return nil +} + +// getReferencedResourceState_RouteTable 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_RouteTable( + ctx context.Context, + apiReader client.Reader, + obj *svcapitypes.RouteTable, + 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 } - ko.Spec.RouteTables = resolvedReferences + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + return ackerr.ResourceReferenceTerminalFor( + "RouteTable", + namespace, name) + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "RouteTable", + namespace, name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "RouteTable", + namespace, name) + } + if obj.Status.RouteTableID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "RouteTable", + namespace, name, + "Status.RouteTableID") } return nil } @@ -155,50 +173,68 @@ func resolveReferenceForVPCID( namespace string, ko *svcapitypes.Subnet, ) error { - if ko.Spec.VPCRef != nil && - ko.Spec.VPCRef.From != nil { + if ko.Spec.VPCRef != nil && ko.Spec.VPCRef.From != nil { arr := ko.Spec.VPCRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: VPCRef") } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.VPC{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { + obj := &svcapitypes.VPC{} + if err := getReferencedResourceState_VPC(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } + ko.Spec.VPCID = (*string)(obj.Status.VPCID) + } + + return 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 refResourceTerminal { + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { return ackerr.ResourceReferenceTerminalFor( "VPC", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "VPC", - namespace, *arr.Name) + namespace, name) } - if obj.Status.VPCID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "VPC", - namespace, *arr.Name, - "Status.VPCID") - } - referencedValue := string(*obj.Status.VPCID) - ko.Spec.VPCID = &referencedValue + } + 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 } diff --git a/pkg/resource/vpc_endpoint/references.go b/pkg/resource/vpc_endpoint/references.go index 002907f6..dda4ff76 100644 --- a/pkg/resource/vpc_endpoint/references.go +++ b/pkg/resource/vpc_endpoint/references.go @@ -106,54 +106,72 @@ func resolveReferenceForRouteTableIDs( namespace string, ko *svcapitypes.VPCEndpoint, ) error { - if ko.Spec.RouteTableRefs != nil && - len(ko.Spec.RouteTableRefs) > 0 { - resolvedReferences := []*string{} - for _, arrw := range ko.Spec.RouteTableRefs { - arr := arrw.From + if len(ko.Spec.RouteTableRefs) > 0 { + resolved0 := []*string{} + for _, iter0 := range ko.Spec.RouteTableRefs { + arr := iter0.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: RouteTableRefs") } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.RouteTable{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { + obj := &svcapitypes.RouteTable{} + if err := getReferencedResourceState_RouteTable(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } - } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "RouteTable", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "RouteTable", - namespace, *arr.Name) - } - if obj.Status.RouteTableID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "RouteTable", - namespace, *arr.Name, - "Status.RouteTableID") - } - referencedValue := string(*obj.Status.RouteTableID) - resolvedReferences = append(resolvedReferences, &referencedValue) + resolved0 = append(resolved0, (*string)(obj.Status.RouteTableID)) + } + ko.Spec.RouteTableIDs = resolved0 + } + + return nil +} + +// getReferencedResourceState_RouteTable 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_RouteTable( + ctx context.Context, + apiReader client.Reader, + obj *svcapitypes.RouteTable, + 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 } - ko.Spec.RouteTableIDs = resolvedReferences + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + return ackerr.ResourceReferenceTerminalFor( + "RouteTable", + namespace, name) + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "RouteTable", + namespace, name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "RouteTable", + namespace, name) + } + if obj.Status.RouteTableID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "RouteTable", + namespace, name, + "Status.RouteTableID") } return nil } @@ -167,54 +185,72 @@ func resolveReferenceForSecurityGroupIDs( namespace string, ko *svcapitypes.VPCEndpoint, ) error { - if ko.Spec.SecurityGroupRefs != nil && - len(ko.Spec.SecurityGroupRefs) > 0 { - resolvedReferences := []*string{} - for _, arrw := range ko.Spec.SecurityGroupRefs { - arr := arrw.From + if len(ko.Spec.SecurityGroupRefs) > 0 { + resolved0 := []*string{} + for _, iter0 := range ko.Spec.SecurityGroupRefs { + arr := iter0.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: SecurityGroupRefs") } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.SecurityGroup{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { + obj := &svcapitypes.SecurityGroup{} + if err := getReferencedResourceState_SecurityGroup(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } - } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "SecurityGroup", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "SecurityGroup", - namespace, *arr.Name) - } - if obj.Status.ID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "SecurityGroup", - namespace, *arr.Name, - "Status.ID") - } - referencedValue := string(*obj.Status.ID) - resolvedReferences = append(resolvedReferences, &referencedValue) + resolved0 = append(resolved0, (*string)(obj.Status.ID)) + } + ko.Spec.SecurityGroupIDs = resolved0 + } + + return nil +} + +// getReferencedResourceState_SecurityGroup 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_SecurityGroup( + ctx context.Context, + apiReader client.Reader, + obj *svcapitypes.SecurityGroup, + 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 } - ko.Spec.SecurityGroupIDs = resolvedReferences + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + return ackerr.ResourceReferenceTerminalFor( + "SecurityGroup", + namespace, name) + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "SecurityGroup", + namespace, name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "SecurityGroup", + namespace, name) + } + if obj.Status.ID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "SecurityGroup", + namespace, name, + "Status.ID") } return nil } @@ -228,54 +264,72 @@ func resolveReferenceForSubnetIDs( namespace string, ko *svcapitypes.VPCEndpoint, ) error { - if ko.Spec.SubnetRefs != nil && - len(ko.Spec.SubnetRefs) > 0 { - resolvedReferences := []*string{} - for _, arrw := range ko.Spec.SubnetRefs { - arr := arrw.From + if len(ko.Spec.SubnetRefs) > 0 { + resolved0 := []*string{} + for _, iter0 := range ko.Spec.SubnetRefs { + arr := iter0.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: SubnetRefs") } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.Subnet{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { + obj := &svcapitypes.Subnet{} + if err := getReferencedResourceState_Subnet(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } - } - if refResourceTerminal { - return ackerr.ResourceReferenceTerminalFor( - "Subnet", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "Subnet", - namespace, *arr.Name) - } - if obj.Status.SubnetID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "Subnet", - namespace, *arr.Name, - "Status.SubnetID") - } - referencedValue := string(*obj.Status.SubnetID) - resolvedReferences = append(resolvedReferences, &referencedValue) + resolved0 = append(resolved0, (*string)(obj.Status.SubnetID)) + } + ko.Spec.SubnetIDs = resolved0 + } + + return nil +} + +// getReferencedResourceState_Subnet looks up whether a referenced resource +// exists and is in a ACK.ResourceSynced=True state. If the referenced resource does exist and is +// in a Synced state, returns nil, otherwise returns `ackerr.ResourceReferenceTerminalFor` or +// `ResourceReferenceNotSyncedFor` depending on if the resource is in a Terminal state. +func getReferencedResourceState_Subnet( + ctx context.Context, + apiReader client.Reader, + obj *svcapitypes.Subnet, + name string, // the Kubernetes name of the referenced resource + namespace string, // the Kubernetes namespace of the referenced resource +) error { + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + err := apiReader.Get(ctx, namespacedName, obj) + if err != nil { + return err + } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true } - ko.Spec.SubnetIDs = resolvedReferences + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + return ackerr.ResourceReferenceTerminalFor( + "Subnet", + namespace, name) + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "Subnet", + namespace, name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "Subnet", + namespace, name) + } + if obj.Status.SubnetID == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "Subnet", + namespace, name, + "Status.SubnetID") } return nil } @@ -289,50 +343,68 @@ func resolveReferenceForVPCID( namespace string, ko *svcapitypes.VPCEndpoint, ) error { - if ko.Spec.VPCRef != nil && - ko.Spec.VPCRef.From != nil { + if ko.Spec.VPCRef != nil && ko.Spec.VPCRef.From != nil { arr := ko.Spec.VPCRef.From if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty") + return fmt.Errorf("provided resource reference is nil or empty: VPCRef") } - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: *arr.Name, - } - obj := svcapitypes.VPC{} - err := apiReader.Get(ctx, namespacedName, &obj) - if err != nil { + obj := &svcapitypes.VPC{} + if err := getReferencedResourceState_VPC(ctx, apiReader, obj, *arr.Name, namespace); err != nil { return err } - var refResourceSynced, refResourceTerminal bool - for _, cond := range obj.Status.Conditions { - if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && - cond.Status == corev1.ConditionTrue { - refResourceSynced = true - } - if cond.Type == ackv1alpha1.ConditionTypeTerminal && - cond.Status == corev1.ConditionTrue { - refResourceTerminal = true - } + ko.Spec.VPCID = (*string)(obj.Status.VPCID) + } + + return 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 refResourceTerminal { + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { return ackerr.ResourceReferenceTerminalFor( "VPC", - namespace, *arr.Name) - } - if !refResourceSynced { - return ackerr.ResourceReferenceNotSyncedFor( - "VPC", - namespace, *arr.Name) + namespace, name) } - if obj.Status.VPCID == nil { - return ackerr.ResourceReferenceMissingTargetFieldFor( - "VPC", - namespace, *arr.Name, - "Status.VPCID") - } - referencedValue := string(*obj.Status.VPCID) - ko.Spec.VPCID = &referencedValue + } + 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 } diff --git a/pkg/resource/vpc_endpoint_service_configuration/delta.go b/pkg/resource/vpc_endpoint_service_configuration/delta.go new file mode 100644 index 00000000..6b875ca6 --- /dev/null +++ b/pkg/resource/vpc_endpoint_service_configuration/delta.go @@ -0,0 +1,72 @@ +// 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_endpoint_service_configuration + +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.AcceptanceRequired, b.ko.Spec.AcceptanceRequired) { + delta.Add("Spec.AcceptanceRequired", a.ko.Spec.AcceptanceRequired, b.ko.Spec.AcceptanceRequired) + } else if a.ko.Spec.AcceptanceRequired != nil && b.ko.Spec.AcceptanceRequired != nil { + if *a.ko.Spec.AcceptanceRequired != *b.ko.Spec.AcceptanceRequired { + delta.Add("Spec.AcceptanceRequired", a.ko.Spec.AcceptanceRequired, b.ko.Spec.AcceptanceRequired) + } + } + if !ackcompare.SliceStringPEqual(a.ko.Spec.GatewayLoadBalancerARNs, b.ko.Spec.GatewayLoadBalancerARNs) { + delta.Add("Spec.GatewayLoadBalancerARNs", a.ko.Spec.GatewayLoadBalancerARNs, b.ko.Spec.GatewayLoadBalancerARNs) + } + if !ackcompare.SliceStringPEqual(a.ko.Spec.NetworkLoadBalancerARNs, b.ko.Spec.NetworkLoadBalancerARNs) { + delta.Add("Spec.NetworkLoadBalancerARNs", a.ko.Spec.NetworkLoadBalancerARNs, b.ko.Spec.NetworkLoadBalancerARNs) + } + if ackcompare.HasNilDifference(a.ko.Spec.PrivateDNSName, b.ko.Spec.PrivateDNSName) { + delta.Add("Spec.PrivateDNSName", a.ko.Spec.PrivateDNSName, b.ko.Spec.PrivateDNSName) + } else if a.ko.Spec.PrivateDNSName != nil && b.ko.Spec.PrivateDNSName != nil { + if *a.ko.Spec.PrivateDNSName != *b.ko.Spec.PrivateDNSName { + delta.Add("Spec.PrivateDNSName", a.ko.Spec.PrivateDNSName, b.ko.Spec.PrivateDNSName) + } + } + if !ackcompare.SliceStringPEqual(a.ko.Spec.SupportedIPAddressTypes, b.ko.Spec.SupportedIPAddressTypes) { + delta.Add("Spec.SupportedIPAddressTypes", a.ko.Spec.SupportedIPAddressTypes, b.ko.Spec.SupportedIPAddressTypes) + } + + return delta +} diff --git a/pkg/resource/vpc_endpoint_service_configuration/descriptor.go b/pkg/resource/vpc_endpoint_service_configuration/descriptor.go new file mode 100644 index 00000000..e45aedf0 --- /dev/null +++ b/pkg/resource/vpc_endpoint_service_configuration/descriptor.go @@ -0,0 +1,154 @@ +// 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_endpoint_service_configuration + +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" + 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/VPCEndpointServiceConfiguration" +) + +var ( + GroupVersionResource = svcapitypes.GroupVersion.WithResource("vpcendpointserviceconfigurations") + GroupKind = metav1.GroupKind{ + Group: "ec2.services.k8s.aws", + Kind: "VPCEndpointServiceConfiguration", + } +) + +// resourceDescriptor implements the +// `aws-service-operator-k8s/pkg/types.AWSResourceDescriptor` interface +type resourceDescriptor struct { +} + +// GroupKind returns a Kubernetes metav1.GroupKind struct that describes the +// API Group and Kind of CRs described by the descriptor +func (d *resourceDescriptor) GroupKind() *metav1.GroupKind { + return &GroupKind +} + +// EmptyRuntimeObject returns an empty object prototype that may be used in +// apimachinery and k8s client operations +func (d *resourceDescriptor) EmptyRuntimeObject() rtclient.Object { + return &svcapitypes.VPCEndpointServiceConfiguration{} +} + +// 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.VPCEndpointServiceConfiguration), + } +} + +// 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_endpoint_service_configuration/hooks.go b/pkg/resource/vpc_endpoint_service_configuration/hooks.go new file mode 100644 index 00000000..019f7adf --- /dev/null +++ b/pkg/resource/vpc_endpoint_service_configuration/hooks.go @@ -0,0 +1,152 @@ +// 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_endpoint_service_configuration + +import ( + "context" + "errors" + + svcapitypes "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1" + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" + + svcsdk "github.com/aws/aws-sdk-go/service/ec2" +) + +// addIDToDeleteRequest adds resource's Vpc Endpoint Service Configuration ID to DeleteRequest. +// Return error to indicate to callers that the resource is not yet created. +func addIDToDeleteRequest(r *resource, + input *svcsdk.DeleteVpcEndpointServiceConfigurationsInput) error { + if r.ko.Status.ServiceID == nil { + return errors.New("unable to extract ServiceID from resource") + } + input.ServiceIds = []*string{r.ko.Status.ServiceID} + return nil +} + +// 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.ServiceID} + + toAdd, toDelete := computeTagsDelta( + desired.ko.Spec.Tags, latest.ko.Spec.Tags, + ) + + if len(toDelete) > 0 { + rlog.Debug("removing tags from VPCEndpoint 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 VPCEndpoint 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) + } + } +} diff --git a/pkg/resource/vpc_endpoint_service_configuration/identifiers.go b/pkg/resource/vpc_endpoint_service_configuration/identifiers.go new file mode 100644 index 00000000..701c3df0 --- /dev/null +++ b/pkg/resource/vpc_endpoint_service_configuration/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_endpoint_service_configuration + +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_endpoint_service_configuration/manager.go b/pkg/resource/vpc_endpoint_service_configuration/manager.go new file mode 100644 index 00000000..2abbd004 --- /dev/null +++ b/pkg/resource/vpc_endpoint_service_configuration/manager.go @@ -0,0 +1,368 @@ +// 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_endpoint_service_configuration + +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.VPCEndpointServiceConfiguration{} +) + +// +kubebuilder:rbac:groups=ec2.services.k8s.aws,resources=vpcendpointserviceconfigurations,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=ec2.services.k8s.aws,resources=vpcendpointserviceconfigurations/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") + } + + if r.ko.Status.ServiceState == nil { + return false, nil + } + serviceStateCandidates := []string{"available"} + if !ackutil.InStrings(*r.ko.Status.ServiceState, serviceStateCandidates) { + return false, nil + } + + 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_endpoint_service_configuration/manager_factory.go b/pkg/resource/vpc_endpoint_service_configuration/manager_factory.go new file mode 100644 index 00000000..e6f40a30 --- /dev/null +++ b/pkg/resource/vpc_endpoint_service_configuration/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_endpoint_service_configuration + +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_endpoint_service_configuration/references.go b/pkg/resource/vpc_endpoint_service_configuration/references.go new file mode 100644 index 00000000..83f65224 --- /dev/null +++ b/pkg/resource/vpc_endpoint_service_configuration/references.go @@ -0,0 +1,52 @@ +// 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_endpoint_service_configuration + +import ( + "context" + "sigs.k8s.io/controller-runtime/pkg/client" + + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + + svcapitypes "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1" +) + +// 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 target field(s). +// It returns an AWSResource with resolved reference(s), and an error if the +// passed AWSResource's reference field(s) cannot be resolved. +// This method also adds/updates the ConditionTypeReferencesResolved for the +// AWSResource. +func (rm *resourceManager) ResolveReferences( + ctx context.Context, + apiReader client.Reader, + res acktypes.AWSResource, +) (acktypes.AWSResource, error) { + return res, nil +} + +// validateReferenceFields validates the reference field and corresponding +// identifier field. +func validateReferenceFields(ko *svcapitypes.VPCEndpointServiceConfiguration) error { + return nil +} + +// hasNonNilReferences returns true if resource contains a reference to another +// resource +func hasNonNilReferences(ko *svcapitypes.VPCEndpointServiceConfiguration) bool { + return false +} diff --git a/pkg/resource/vpc_endpoint_service_configuration/resource.go b/pkg/resource/vpc_endpoint_service_configuration/resource.go new file mode 100644 index 00000000..f7750437 --- /dev/null +++ b/pkg/resource/vpc_endpoint_service_configuration/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_endpoint_service_configuration + +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.VPCEndpointServiceConfiguration +} + +// 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 timestemp +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.ServiceID = &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_endpoint_service_configuration/sdk.go b/pkg/resource/vpc_endpoint_service_configuration/sdk.go new file mode 100644 index 00000000..699f7dee --- /dev/null +++ b/pkg/resource/vpc_endpoint_service_configuration/sdk.go @@ -0,0 +1,677 @@ +// 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_endpoint_service_configuration + +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.VPCEndpointServiceConfiguration{} + _ = 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.DescribeVpcEndpointServiceConfigurationsOutput + resp, err = rm.sdkapi.DescribeVpcEndpointServiceConfigurationsWithContext(ctx, input) + rm.metrics.RecordAPICall("READ_MANY", "DescribeVpcEndpointServiceConfigurations", 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.ServiceConfigurations { + if elem.AcceptanceRequired != nil { + ko.Spec.AcceptanceRequired = elem.AcceptanceRequired + } else { + ko.Spec.AcceptanceRequired = nil + } + if elem.AvailabilityZones != nil { + f1 := []*string{} + for _, f1iter := range elem.AvailabilityZones { + var f1elem string + f1elem = *f1iter + f1 = append(f1, &f1elem) + } + ko.Status.AvailabilityZones = f1 + } else { + ko.Status.AvailabilityZones = nil + } + if elem.BaseEndpointDnsNames != nil { + f2 := []*string{} + for _, f2iter := range elem.BaseEndpointDnsNames { + var f2elem string + f2elem = *f2iter + f2 = append(f2, &f2elem) + } + ko.Status.BaseEndpointDNSNames = f2 + } else { + ko.Status.BaseEndpointDNSNames = nil + } + if elem.GatewayLoadBalancerArns != nil { + f3 := []*string{} + for _, f3iter := range elem.GatewayLoadBalancerArns { + var f3elem string + f3elem = *f3iter + f3 = append(f3, &f3elem) + } + ko.Spec.GatewayLoadBalancerARNs = f3 + } else { + ko.Spec.GatewayLoadBalancerARNs = nil + } + if elem.ManagesVpcEndpoints != nil { + ko.Status.ManagesVPCEndpoints = elem.ManagesVpcEndpoints + } else { + ko.Status.ManagesVPCEndpoints = nil + } + if elem.NetworkLoadBalancerArns != nil { + f5 := []*string{} + for _, f5iter := range elem.NetworkLoadBalancerArns { + var f5elem string + f5elem = *f5iter + f5 = append(f5, &f5elem) + } + ko.Spec.NetworkLoadBalancerARNs = f5 + } else { + ko.Spec.NetworkLoadBalancerARNs = nil + } + if elem.PayerResponsibility != nil { + ko.Status.PayerResponsibility = elem.PayerResponsibility + } else { + ko.Status.PayerResponsibility = nil + } + if elem.PrivateDnsName != nil { + ko.Spec.PrivateDNSName = elem.PrivateDnsName + } else { + ko.Spec.PrivateDNSName = nil + } + if elem.PrivateDnsNameConfiguration != nil { + f8 := &svcapitypes.PrivateDNSNameConfiguration{} + if elem.PrivateDnsNameConfiguration.Name != nil { + f8.Name = elem.PrivateDnsNameConfiguration.Name + } + if elem.PrivateDnsNameConfiguration.State != nil { + f8.State = elem.PrivateDnsNameConfiguration.State + } + if elem.PrivateDnsNameConfiguration.Type != nil { + f8.Type = elem.PrivateDnsNameConfiguration.Type + } + if elem.PrivateDnsNameConfiguration.Value != nil { + f8.Value = elem.PrivateDnsNameConfiguration.Value + } + ko.Status.PrivateDNSNameConfiguration = f8 + } else { + ko.Status.PrivateDNSNameConfiguration = nil + } + if elem.ServiceId != nil { + ko.Status.ServiceID = elem.ServiceId + } else { + ko.Status.ServiceID = nil + } + if elem.ServiceName != nil { + ko.Status.ServiceName = elem.ServiceName + } else { + ko.Status.ServiceName = nil + } + if elem.ServiceState != nil { + ko.Status.ServiceState = elem.ServiceState + } else { + ko.Status.ServiceState = nil + } + if elem.ServiceType != nil { + f12 := []*svcapitypes.ServiceTypeDetail{} + for _, f12iter := range elem.ServiceType { + f12elem := &svcapitypes.ServiceTypeDetail{} + if f12iter.ServiceType != nil { + f12elem.ServiceType = f12iter.ServiceType + } + f12 = append(f12, f12elem) + } + ko.Status.ServiceType = f12 + } else { + ko.Status.ServiceType = nil + } + if elem.SupportedIpAddressTypes != nil { + f13 := []*string{} + for _, f13iter := range elem.SupportedIpAddressTypes { + var f13elem string + f13elem = *f13iter + f13 = append(f13, &f13elem) + } + ko.Spec.SupportedIPAddressTypes = f13 + } else { + ko.Spec.SupportedIPAddressTypes = nil + } + if elem.Tags != nil { + f14 := []*svcapitypes.Tag{} + for _, f14iter := range elem.Tags { + f14elem := &svcapitypes.Tag{} + if f14iter.Key != nil { + f14elem.Key = f14iter.Key + } + if f14iter.Value != nil { + f14elem.Value = f14iter.Value + } + f14 = append(f14, f14elem) + } + ko.Spec.Tags = f14 + } else { + ko.Spec.Tags = nil + } + found = true + break + } + if !found { + return nil, ackerr.NotFound + } + + rm.setStatusDefaults(ko) + return &resource{ko}, nil +} + +// requiredFieldsMissingFromReadManyInput returns true if there are any fields +// for the ReadMany Input shape that are required but not present in the +// resource's Spec or Status +func (rm *resourceManager) requiredFieldsMissingFromReadManyInput( + r *resource, +) bool { + return false +} + +// 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.DescribeVpcEndpointServiceConfigurationsInput, error) { + res := &svcsdk.DescribeVpcEndpointServiceConfigurationsInput{} + + 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 + } + + var resp *svcsdk.CreateVpcEndpointServiceConfigurationOutput + _ = resp + resp, err = rm.sdkapi.CreateVpcEndpointServiceConfigurationWithContext(ctx, input) + rm.metrics.RecordAPICall("CREATE", "CreateVpcEndpointServiceConfiguration", 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.ServiceConfiguration.AcceptanceRequired != nil { + ko.Spec.AcceptanceRequired = resp.ServiceConfiguration.AcceptanceRequired + } else { + ko.Spec.AcceptanceRequired = nil + } + if resp.ServiceConfiguration.AvailabilityZones != nil { + f1 := []*string{} + for _, f1iter := range resp.ServiceConfiguration.AvailabilityZones { + var f1elem string + f1elem = *f1iter + f1 = append(f1, &f1elem) + } + ko.Status.AvailabilityZones = f1 + } else { + ko.Status.AvailabilityZones = nil + } + if resp.ServiceConfiguration.BaseEndpointDnsNames != nil { + f2 := []*string{} + for _, f2iter := range resp.ServiceConfiguration.BaseEndpointDnsNames { + var f2elem string + f2elem = *f2iter + f2 = append(f2, &f2elem) + } + ko.Status.BaseEndpointDNSNames = f2 + } else { + ko.Status.BaseEndpointDNSNames = nil + } + if resp.ServiceConfiguration.GatewayLoadBalancerArns != nil { + f3 := []*string{} + for _, f3iter := range resp.ServiceConfiguration.GatewayLoadBalancerArns { + var f3elem string + f3elem = *f3iter + f3 = append(f3, &f3elem) + } + ko.Spec.GatewayLoadBalancerARNs = f3 + } else { + ko.Spec.GatewayLoadBalancerARNs = nil + } + if resp.ServiceConfiguration.ManagesVpcEndpoints != nil { + ko.Status.ManagesVPCEndpoints = resp.ServiceConfiguration.ManagesVpcEndpoints + } else { + ko.Status.ManagesVPCEndpoints = nil + } + if resp.ServiceConfiguration.NetworkLoadBalancerArns != nil { + f5 := []*string{} + for _, f5iter := range resp.ServiceConfiguration.NetworkLoadBalancerArns { + var f5elem string + f5elem = *f5iter + f5 = append(f5, &f5elem) + } + ko.Spec.NetworkLoadBalancerARNs = f5 + } else { + ko.Spec.NetworkLoadBalancerARNs = nil + } + if resp.ServiceConfiguration.PayerResponsibility != nil { + ko.Status.PayerResponsibility = resp.ServiceConfiguration.PayerResponsibility + } else { + ko.Status.PayerResponsibility = nil + } + if resp.ServiceConfiguration.PrivateDnsName != nil { + ko.Spec.PrivateDNSName = resp.ServiceConfiguration.PrivateDnsName + } else { + ko.Spec.PrivateDNSName = nil + } + if resp.ServiceConfiguration.PrivateDnsNameConfiguration != nil { + f8 := &svcapitypes.PrivateDNSNameConfiguration{} + if resp.ServiceConfiguration.PrivateDnsNameConfiguration.Name != nil { + f8.Name = resp.ServiceConfiguration.PrivateDnsNameConfiguration.Name + } + if resp.ServiceConfiguration.PrivateDnsNameConfiguration.State != nil { + f8.State = resp.ServiceConfiguration.PrivateDnsNameConfiguration.State + } + if resp.ServiceConfiguration.PrivateDnsNameConfiguration.Type != nil { + f8.Type = resp.ServiceConfiguration.PrivateDnsNameConfiguration.Type + } + if resp.ServiceConfiguration.PrivateDnsNameConfiguration.Value != nil { + f8.Value = resp.ServiceConfiguration.PrivateDnsNameConfiguration.Value + } + ko.Status.PrivateDNSNameConfiguration = f8 + } else { + ko.Status.PrivateDNSNameConfiguration = nil + } + if resp.ServiceConfiguration.ServiceId != nil { + ko.Status.ServiceID = resp.ServiceConfiguration.ServiceId + } else { + ko.Status.ServiceID = nil + } + if resp.ServiceConfiguration.ServiceName != nil { + ko.Status.ServiceName = resp.ServiceConfiguration.ServiceName + } else { + ko.Status.ServiceName = nil + } + if resp.ServiceConfiguration.ServiceState != nil { + ko.Status.ServiceState = resp.ServiceConfiguration.ServiceState + } else { + ko.Status.ServiceState = nil + } + if resp.ServiceConfiguration.ServiceType != nil { + f12 := []*svcapitypes.ServiceTypeDetail{} + for _, f12iter := range resp.ServiceConfiguration.ServiceType { + f12elem := &svcapitypes.ServiceTypeDetail{} + if f12iter.ServiceType != nil { + f12elem.ServiceType = f12iter.ServiceType + } + f12 = append(f12, f12elem) + } + ko.Status.ServiceType = f12 + } else { + ko.Status.ServiceType = nil + } + if resp.ServiceConfiguration.SupportedIpAddressTypes != nil { + f13 := []*string{} + for _, f13iter := range resp.ServiceConfiguration.SupportedIpAddressTypes { + var f13elem string + f13elem = *f13iter + f13 = append(f13, &f13elem) + } + ko.Spec.SupportedIPAddressTypes = f13 + } else { + ko.Spec.SupportedIPAddressTypes = nil + } + if resp.ServiceConfiguration.Tags != nil { + f14 := []*svcapitypes.Tag{} + for _, f14iter := range resp.ServiceConfiguration.Tags { + f14elem := &svcapitypes.Tag{} + if f14iter.Key != nil { + f14elem.Key = f14iter.Key + } + if f14iter.Value != nil { + f14elem.Value = f14iter.Value + } + f14 = append(f14, f14elem) + } + ko.Spec.Tags = f14 + } else { + ko.Spec.Tags = nil + } + + rm.setStatusDefaults(ko) + return &resource{ko}, nil +} + +// newCreateRequestPayload returns an SDK-specific struct for the HTTP request +// payload of the Create API call for the resource +func (rm *resourceManager) newCreateRequestPayload( + ctx context.Context, + r *resource, +) (*svcsdk.CreateVpcEndpointServiceConfigurationInput, error) { + res := &svcsdk.CreateVpcEndpointServiceConfigurationInput{} + + if r.ko.Spec.AcceptanceRequired != nil { + res.SetAcceptanceRequired(*r.ko.Spec.AcceptanceRequired) + } + if r.ko.Spec.GatewayLoadBalancerARNs != nil { + f1 := []*string{} + for _, f1iter := range r.ko.Spec.GatewayLoadBalancerARNs { + var f1elem string + f1elem = *f1iter + f1 = append(f1, &f1elem) + } + res.SetGatewayLoadBalancerArns(f1) + } + if r.ko.Spec.NetworkLoadBalancerARNs != nil { + f2 := []*string{} + for _, f2iter := range r.ko.Spec.NetworkLoadBalancerARNs { + var f2elem string + f2elem = *f2iter + f2 = append(f2, &f2elem) + } + res.SetNetworkLoadBalancerArns(f2) + } + if r.ko.Spec.PrivateDNSName != nil { + res.SetPrivateDnsName(*r.ko.Spec.PrivateDNSName) + } + if r.ko.Spec.SupportedIPAddressTypes != nil { + f4 := []*string{} + for _, f4iter := range r.ko.Spec.SupportedIPAddressTypes { + var f4elem string + f4elem = *f4iter + f4 = append(f4, &f4elem) + } + res.SetSupportedIpAddressTypes(f4) + } + + 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) + }() + input, err := rm.newUpdateRequestPayload(ctx, desired, delta) + if err != nil { + return nil, err + } + + var resp *svcsdk.ModifyVpcEndpointServiceConfigurationOutput + _ = resp + resp, err = rm.sdkapi.ModifyVpcEndpointServiceConfigurationWithContext(ctx, input) + rm.metrics.RecordAPICall("UPDATE", "ModifyVpcEndpointServiceConfiguration", 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() + + 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.ModifyVpcEndpointServiceConfigurationInput, error) { + res := &svcsdk.ModifyVpcEndpointServiceConfigurationInput{} + + if r.ko.Spec.AcceptanceRequired != nil { + res.SetAcceptanceRequired(*r.ko.Spec.AcceptanceRequired) + } + if r.ko.Spec.PrivateDNSName != nil { + res.SetPrivateDnsName(*r.ko.Spec.PrivateDNSName) + } + if r.ko.Status.ServiceID != nil { + res.SetServiceId(*r.ko.Status.ServiceID) + } + + 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 + } + if err = addIDToDeleteRequest(r, input); err != nil { + return nil, ackerr.NotFound + } + var resp *svcsdk.DeleteVpcEndpointServiceConfigurationsOutput + _ = resp + resp, err = rm.sdkapi.DeleteVpcEndpointServiceConfigurationsWithContext(ctx, input) + rm.metrics.RecordAPICall("DELETE", "DeleteVpcEndpointServiceConfigurations", 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.DeleteVpcEndpointServiceConfigurationsInput, error) { + res := &svcsdk.DeleteVpcEndpointServiceConfigurationsInput{} + + return res, nil +} + +// setStatusDefaults sets default properties into supplied custom resource +func (rm *resourceManager) setStatusDefaults( + ko *svcapitypes.VPCEndpointServiceConfiguration, +) { + 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_endpoint_service_configuration/tags.go b/pkg/resource/vpc_endpoint_service_configuration/tags.go new file mode 100644 index 00000000..6c1c4607 --- /dev/null +++ b/pkg/resource/vpc_endpoint_service_configuration/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_endpoint_service_configuration + +import ( + acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" + + svcapitypes "github.com/aws-controllers-k8s/ec2-controller/apis/v1alpha1" +) + +var ( + _ = svcapitypes.VPCEndpointServiceConfiguration{} + _ = 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_endpoint_service_configuration/sdk_delete_post_build_request.go.tpl b/templates/hooks/vpc_endpoint_service_configuration/sdk_delete_post_build_request.go.tpl new file mode 100644 index 00000000..57b70b49 --- /dev/null +++ b/templates/hooks/vpc_endpoint_service_configuration/sdk_delete_post_build_request.go.tpl @@ -0,0 +1,3 @@ + if err = addIDToDeleteRequest(r, input); err != nil { + return nil, ackerr.NotFound + } \ No newline at end of file diff --git a/templates/hooks/vpc_endpoint_service_configuration/sdk_file_end.go.tpl b/templates/hooks/vpc_endpoint_service_configuration/sdk_file_end.go.tpl new file mode 100644 index 00000000..882da970 --- /dev/null +++ b/templates/hooks/vpc_endpoint_service_configuration/sdk_file_end.go.tpl @@ -0,0 +1,23 @@ +{{ $CRD := .CRD }} +{{ $SDKAPI := .SDKAPI }} + +{{/* Generate helper methods for VpcEndpointServiceConfiguration */}} +{{- range $specFieldName, $specField := $CRD.Config.Resources.VpcEndpointServiceConfiguration.Fields }} +{{- if $specField.From }} +{{- $operationName := $specField.From.Operation }} +{{- $operation := (index $SDKAPI.API.Operations $operationName) -}} +{{- range $vpcEndpointServiceConfigurationRefName, $vpcEndpointServiceConfigurationMemberRefs := $operation.InputRef.Shape.MemberRefs -}} +{{- if eq $vpcEndpointServiceConfigurationRefName "Tags" }} +{{- $vpcEndpointServiceConfigurationRef := $vpcEndpointServiceConfigurationMemberRefs.Shape.MemberRef }} +{{- $vpcEndpointServiceConfigurationRefName = "Tag" }} +func (rm *resourceManager) new{{ $vpcEndpointServiceConfigurationRefName }}( + c svcapitypes.{{ $vpcEndpointServiceConfigurationRefName }}, +) *svcsdk.{{ $vpcEndpointServiceConfigurationRefName }} { + res := &svcsdk.{{ $vpcEndpointServiceConfigurationRefName }}{} +{{ GoCodeSetSDKForStruct $CRD "" "res" $vpcEndpointServiceConfigurationRef "" "c" 1 }} + return res +} +{{- end }} +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/test/e2e/resources/vpc_endpoint_service_configuration.yaml b/test/e2e/resources/vpc_endpoint_service_configuration.yaml new file mode 100644 index 00000000..d51d3702 --- /dev/null +++ b/test/e2e/resources/vpc_endpoint_service_configuration.yaml @@ -0,0 +1,15 @@ +apiVersion: ec2.services.k8s.aws/v1alpha1 +kind: VPCEndpointServiceConfiguration +metadata: + name: $VPC_ENDPOINT_SERVICE_NAME +spec: + acceptanceRequired: $ACCEPTANCE_REQUIRED + # gatewayLoadBalancerARNs: $GATEWAY_LOAD_BALANCER_ARN_SET + networkLoadBalancerARNs: + - $NETWORK_LOAD_BALANCER_ARN_SET + supportedIPAddressTypes: + - $SUPPORTED_IP_ADDRESS_TYPE_SET + privateDNSName: $PRIVATE_DNS_NAME + tags: + - key: $TAG_KEY + value: $TAG_VALUE \ No newline at end of file diff --git a/test/e2e/tests/helper.py b/test/e2e/tests/helper.py index 788d1d59..1fab6fe6 100644 --- a/test/e2e/tests/helper.py +++ b/test/e2e/tests/helper.py @@ -189,4 +189,38 @@ def assert_vpc_endpoint(self, vpc_endpoint_id: str, exists=True): res_found = len(aws_res["VpcEndpoints"]) > 0 except self.ec2_client.exceptions.ClientError: pass + assert res_found is exists + def get_vpc_endpoint_service_configuration(self, vpc_endpoint_service_configuration_id: str) -> Union[None, Dict]: + try: + aws_res = self.ec2_client.describe_vpc_endpoint_service_configurations(ServiceIds=[vpc_endpoint_service_configuration_id]) + if len(aws_res["ServiceConfigurations"]) > 0: + return aws_res["ServiceConfigurations"][0] + return None + except self.ec2_client.exceptions.ClientError: + return None + + def assert_vpc_endpoint_service_configuration(self, vpc_endpoint_service_configuration_id: str, exists=True): + res_found = False + try: + aws_res = self.ec2_client.describe_vpc_endpoint_service_configurations(ServiceIds=[vpc_endpoint_service_configuration_id]) + res_found = len(aws_res["ServiceConfigurations"]) > 0 + except self.ec2_client.exceptions.ClientError: + pass + assert res_found is exists + def get_vpc_endpoint_service_configuration(self, vpc_endpoint_service_configuration_id: str) -> Union[None, Dict]: + try: + aws_res = self.ec2_client.describe_vpc_endpoint_service_configurations(ServiceIds=[vpc_endpoint_service_configuration_id]) + if len(aws_res["ServiceConfigurations"]) > 0: + return aws_res["ServiceConfigurations"][0] + return None + except self.ec2_client.exceptions.ClientError: + return None + + def assert_vpc_endpoint_service_configuration(self, vpc_endpoint_service_configuration_id: str, exists=True): + res_found = False + try: + aws_res = self.ec2_client.describe_vpc_endpoint_service_configurations(ServiceIds=[vpc_endpoint_service_configuration_id]) + res_found = len(aws_res["ServiceConfigurations"]) > 0 + except self.ec2_client.exceptions.ClientError: + pass assert res_found is exists \ No newline at end of file diff --git a/test/e2e/tests/test_vpc_endpoint_service_configuration.py b/test/e2e/tests/test_vpc_endpoint_service_configuration.py new file mode 100644 index 00000000..36b92bfc --- /dev/null +++ b/test/e2e/tests/test_vpc_endpoint_service_configuration.py @@ -0,0 +1,117 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You may +# not use this file except in compliance with the License. A copy of the +# License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Integration tests for the Vpc Endpoint Service Configuraion API. +""" +from os import environ +import pytest +import time +import logging + + +from acktest.resources import random_suffix_name +from acktest.k8s import resource as k8s +from acktest.bootstrapping.elbv2 import NetworkLoadBalancer +from e2e import service_marker, CRD_GROUP, CRD_VERSION, load_ec2_resource +from e2e.replacement_values import REPLACEMENT_VALUES +from e2e.tests.helper import EC2Validator + +# Default to us-west-2 since that's where prow is deployed +REGION = "us-west-2" if environ.get('AWS_DEFAULT_REGION') is None else environ.get('AWS_DEFAULT_REGION') +RESOURCE_PLURAL = "vpcendpointserviceconfigurations" + +CREATE_WAIT_AFTER_SECONDS = 10 +DELETE_WAIT_AFTER_SECONDS = 10 +MODIFY_WAIT_AFTER_SECONDS = 5 + +@pytest.fixture +def simple_vpc_endpoint_service_configuration(request): + test_resource_values = REPLACEMENT_VALUES.copy() + + nlb = NetworkLoadBalancer("vpc-ep-service-test") + nlb.bootstrap() + + supported_ip_address_types = "ipv4" + + resource_name = random_suffix_name("vpc-ep-service", 24) + + test_resource_values["VPC_ENDPOINT_SERVICE_NAME"] = resource_name + test_resource_values["ACCEPTANCE_REQUIRED"] = "False" + test_resource_values["PRIVATE_DNS_NAME"] = "" + test_resource_values["NETWORK_LOAD_BALANCER_ARN_SET"] = nlb.arn + test_resource_values["SUPPORTED_IP_ADDRESS_TYPE_SET"] = supported_ip_address_types + + + marker = request.node.get_closest_marker("resource_data") + if marker is not None: + data = marker.args[0] + if 'tag_key' in data: + test_resource_values["TAG_KEY"] = data["tag_key"] + if 'tag_value' in data: + test_resource_values["TAG_VALUE"] = data["tag_value"] + + # Load VPC Endpoint Service Configuration CR + resource_data = load_ec2_resource( + "vpc_endpoint_service_configuration", + additional_replacements=test_resource_values, + ) + logging.debug(resource_data) + + # Create k8s resource + ref = k8s.CustomResourceReference( + CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, + resource_name, namespace="default", + ) + k8s.create_custom_resource(ref, resource_data) + time.sleep(CREATE_WAIT_AFTER_SECONDS) + + cr = k8s.wait_resource_consumed_by_controller(ref) + assert cr is not None + assert k8s.get_resource_exists(ref) + + yield (ref, cr) + + # Try to delete, if doesn't already exist + try: + _, deleted = k8s.delete_custom_resource(ref, 3, 10) + assert deleted + except: + pass + + finally: + nlb.cleanup() + + +@service_marker +@pytest.mark.canary +class TestVpcEndpointServiceConfiguration: + def test_vpc_endpoint_service_configuration_create_delete(self, ec2_client, simple_vpc_endpoint_service_configuration): + (ref, cr) = simple_vpc_endpoint_service_configuration + + resource_id = cr["status"]["serviceID"] + + time.sleep(CREATE_WAIT_AFTER_SECONDS) + + # Check VPC Endpoint Service exists in AWS + ec2_validator = EC2Validator(ec2_client) + ec2_validator.assert_vpc_endpoint_service_configuration(resource_id) + + # Delete k8s resource + _, deleted = k8s.delete_custom_resource(ref) + assert deleted is True + + time.sleep(DELETE_WAIT_AFTER_SECONDS) + + # Check VPC Endpoint Service no longer exists in AWS + ec2_validator.assert_vpc_endpoint_service_configuration(resource_id, exists=False) +