From ee6967a3d293702a325bbd6d3b9ef348e4b42946 Mon Sep 17 00:00:00 2001 From: Arush Sharma Date: Tue, 15 Jul 2025 10:38:24 -0700 Subject: [PATCH 1/2] add support for domainame --- apis/v1alpha1/ack-generate-metadata.yaml | 12 +- apis/v1alpha1/domain_name.go | 165 ++++ apis/v1alpha1/enums.go | 12 +- apis/v1alpha1/generator.yaml | 31 +- apis/v1alpha1/types.go | 49 +- apis/v1alpha1/zz_generated.deepcopy.go | 327 ++++++- cmd/controller/main.go | 1 + config/controller/kustomization.yaml | 2 +- ...igateway.services.k8s.aws_domainnames.yaml | 298 +++++++ config/crd/kustomization.yaml | 1 + config/rbac/cluster-role-controller.yaml | 2 + config/rbac/role-reader.yaml | 1 + config/rbac/role-writer.yaml | 2 + generator.yaml | 31 +- helm/Chart.yaml | 4 +- ...igateway.services.k8s.aws_domainnames.yaml | 298 +++++++ helm/templates/NOTES.txt | 2 +- helm/templates/_helpers.tpl | 2 + helm/templates/role-reader.yaml | 1 + helm/templates/role-writer.yaml | 2 + helm/values.yaml | 3 +- pkg/resource/domain_name/delta.go | 166 ++++ pkg/resource/domain_name/descriptor.go | 155 ++++ pkg/resource/domain_name/hooks.go | 181 ++++ pkg/resource/domain_name/identifiers.go | 55 ++ pkg/resource/domain_name/manager.go | 412 +++++++++ pkg/resource/domain_name/manager_factory.go | 100 +++ pkg/resource/domain_name/references.go | 57 ++ pkg/resource/domain_name/resource.go | 124 +++ pkg/resource/domain_name/sdk.go | 829 ++++++++++++++++++ pkg/resource/domain_name/tags.go | 108 +++ pkg/resource/integration/sdk.go | 2 +- .../sdk_update_post_build_request.go.tpl | 3 + .../sdk_update_pre_build_request.go.tpl | 8 + test/e2e/resources/domain_name_simple.yaml | 14 + test/e2e/tests/domain_name_test.py | 69 ++ 36 files changed, 3445 insertions(+), 84 deletions(-) create mode 100644 apis/v1alpha1/domain_name.go create mode 100644 config/crd/bases/apigateway.services.k8s.aws_domainnames.yaml create mode 100644 helm/crds/apigateway.services.k8s.aws_domainnames.yaml create mode 100644 pkg/resource/domain_name/delta.go create mode 100644 pkg/resource/domain_name/descriptor.go create mode 100644 pkg/resource/domain_name/hooks.go create mode 100644 pkg/resource/domain_name/identifiers.go create mode 100644 pkg/resource/domain_name/manager.go create mode 100644 pkg/resource/domain_name/manager_factory.go create mode 100644 pkg/resource/domain_name/references.go create mode 100644 pkg/resource/domain_name/resource.go create mode 100644 pkg/resource/domain_name/sdk.go create mode 100644 pkg/resource/domain_name/tags.go create mode 100644 templates/hooks/domain_name/sdk_update_post_build_request.go.tpl create mode 100644 templates/hooks/domain_name/sdk_update_pre_build_request.go.tpl create mode 100644 test/e2e/resources/domain_name_simple.yaml create mode 100644 test/e2e/tests/domain_name_test.py diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index c6cc31d..87ec10e 100644 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,13 +1,13 @@ ack_generate_info: - build_date: "2025-06-10T23:50:47Z" - build_hash: e675923dfc54d8b6e09730098c3e3e1056d3c1e9 - go_version: go1.24.3 - version: v0.48.0 -api_directory_checksum: d82f4d79382f6fe41728ddbdfb5ec025b658575f + build_date: "2025-07-16T00:02:48Z" + build_hash: c6808295bbb03aac999713ecf1f3aa5cd698a17e + go_version: go1.24.1 + version: v0.49.0 +api_directory_checksum: 6c7aa5e131743e63ddf99d1e874b3226626847fa api_version: v1alpha1 aws_sdk_go_version: v1.32.6 generator_config_info: - file_checksum: 94f4e735b9f53f4992c3402f108ea847d3d438f2 + file_checksum: 5d322c740d60158574aed90e95f95920409e987b original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/domain_name.go b/apis/v1alpha1/domain_name.go new file mode 100644 index 0000000..11a685f --- /dev/null +++ b/apis/v1alpha1/domain_name.go @@ -0,0 +1,165 @@ +// 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" +) + +// DomainNameSpec defines the desired state of DomainName. +// +// Represents a custom domain name as a user-friendly host name of an API (RestApi). +type DomainNameSpec struct { + + // The reference to an Amazon Web Services-managed certificate that will be + // used by edge-optimized endpoint or private endpoint for this domain name. + // Certificate Manager is the only supported source. + CertificateARN *string `json:"certificateARN,omitempty"` + // [Deprecated] The body of the server certificate that will be used by edge-optimized + // endpoint or private endpoint for this domain name provided by your certificate + // authority. + CertificateBody *string `json:"certificateBody,omitempty"` + // [Deprecated] The intermediate certificates and optionally the root certificate, + // one after the other without any blank lines, used by an edge-optimized endpoint + // for this domain name. If you include the root certificate, your certificate + // chain must start with intermediate certificates and end with the root certificate. + // Use the intermediate certificates that were provided by your certificate + // authority. Do not include any intermediaries that are not in the chain of + // trust path. + CertificateChain *string `json:"certificateChain,omitempty"` + // The user-friendly name of the certificate that will be used by edge-optimized + // endpoint or private endpoint for this domain name. + CertificateName *string `json:"certificateName,omitempty"` + // [Deprecated] Your edge-optimized endpoint's domain name certificate's private + // key. + CertificatePrivateKey *string `json:"certificatePrivateKey,omitempty"` + // The name of the DomainName resource. + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable once set" + // +kubebuilder:validation:Required + DomainName *string `json:"domainName"` + // The endpoint configuration of this DomainName showing the endpoint types + // of the domain name. + EndpointConfiguration *EndpointConfiguration `json:"endpointConfiguration,omitempty"` + MutualTLSAuthentication *MutualTLSAuthenticationInput `json:"mutualTLSAuthentication,omitempty"` + // The ARN of the public certificate issued by ACM to validate ownership of + // your custom domain. Only required when configuring mutual TLS and using an + // ACM imported or private CA certificate ARN as the regionalCertificateArn. + OwnershipVerificationCertificateARN *string `json:"ownershipVerificationCertificateARN,omitempty"` + // A stringified JSON policy document that applies to the execute-api service + // for this DomainName regardless of the caller and Method configuration. Supported + // only for private custom domain names. + Policy *string `json:"policy,omitempty"` + // The reference to an Amazon Web Services-managed certificate that will be + // used by regional endpoint for this domain name. Certificate Manager is the + // only supported source. + RegionalCertificateARN *string `json:"regionalCertificateARN,omitempty"` + // The user-friendly name of the certificate that will be used by regional endpoint + // for this domain name. + RegionalCertificateName *string `json:"regionalCertificateName,omitempty"` + // The Transport Layer Security (TLS) version + cipher suite for this DomainName. + // The valid values are TLS_1_0 and TLS_1_2. + SecurityPolicy *string `json:"securityPolicy,omitempty"` + // The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. + // The tag key can be up to 128 characters and must not start with aws:. The + // tag value can be up to 256 characters. + Tags map[string]*string `json:"tags,omitempty"` +} + +// DomainNameStatus defines the observed state of DomainName +type DomainNameStatus 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 timestamp when the certificate that was used by edge-optimized endpoint + // or private endpoint for this domain name was uploaded. + // +kubebuilder:validation:Optional + CertificateUploadDate *metav1.Time `json:"certificateUploadDate,omitempty"` + // The domain name of the Amazon CloudFront distribution associated with this + // custom domain name for an edge-optimized endpoint. You set up this association + // when adding a DNS record pointing the custom domain name to this distribution + // name. For more information about CloudFront distributions, see the Amazon + // CloudFront documentation. + // +kubebuilder:validation:Optional + DistributionDomainName *string `json:"distributionDomainName,omitempty"` + // The region-agnostic Amazon Route 53 Hosted Zone ID of the edge-optimized + // endpoint. The valid value is Z2FDTNDATAQYW2 for all the regions. For more + // information, see Set up a Regional Custom Domain Name and AWS Regions and + // Endpoints for API Gateway. + // +kubebuilder:validation:Optional + DistributionHostedZoneID *string `json:"distributionHostedZoneID,omitempty"` + // The identifier for the domain name resource. Supported only for private custom + // domain names. + // +kubebuilder:validation:Optional + DomainNameID *string `json:"domainNameID,omitempty"` + // The status of the DomainName migration. The valid values are AVAILABLE and + // UPDATING. If the status is UPDATING, the domain cannot be modified further + // until the existing operation is complete. If it is AVAILABLE, the domain + // can be updated. + // +kubebuilder:validation:Optional + DomainNameStatus *string `json:"domainNameStatus,omitempty"` + // An optional text message containing detailed information about status of + // the DomainName migration. + // +kubebuilder:validation:Optional + DomainNameStatusMessage *string `json:"domainNameStatusMessage,omitempty"` + // A stringified JSON policy document that applies to the API Gateway Management + // service for this DomainName. This policy document controls access for access + // association sources to create domain name access associations with this DomainName. + // Supported only for private custom domain names. + // +kubebuilder:validation:Optional + ManagementPolicy *string `json:"managementPolicy,omitempty"` + // The domain name associated with the regional endpoint for this custom domain + // name. You set up this association by adding a DNS record that points the + // custom domain name to this regional domain name. The regional domain name + // is returned by API Gateway when you create a regional endpoint. + // +kubebuilder:validation:Optional + RegionalDomainName *string `json:"regionalDomainName,omitempty"` + // The region-specific Amazon Route 53 Hosted Zone ID of the regional endpoint. + // For more information, see Set up a Regional Custom Domain Name and AWS Regions + // and Endpoints for API Gateway. + // +kubebuilder:validation:Optional + RegionalHostedZoneID *string `json:"regionalHostedZoneID,omitempty"` +} + +// DomainName is the Schema for the DomainNames API +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +type DomainName struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec DomainNameSpec `json:"spec,omitempty"` + Status DomainNameStatus `json:"status,omitempty"` +} + +// DomainNameList contains a list of DomainName +// +kubebuilder:object:root=true +type DomainNameList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []DomainName `json:"items"` +} + +func init() { + SchemeBuilder.Register(&DomainName{}, &DomainNameList{}) +} diff --git a/apis/v1alpha1/enums.go b/apis/v1alpha1/enums.go index 716e285..0e7312c 100644 --- a/apis/v1alpha1/enums.go +++ b/apis/v1alpha1/enums.go @@ -96,14 +96,14 @@ const ( DocumentationPartType_RESPONSE_HEADER DocumentationPartType = "RESPONSE_HEADER" ) -type DomainNameStatus string +type DomainNameStatus_SDK string const ( - DomainNameStatus_AVAILABLE DomainNameStatus = "AVAILABLE" - DomainNameStatus_PENDING DomainNameStatus = "PENDING" - DomainNameStatus_PENDING_CERTIFICATE_REIMPORT DomainNameStatus = "PENDING_CERTIFICATE_REIMPORT" - DomainNameStatus_PENDING_OWNERSHIP_VERIFICATION DomainNameStatus = "PENDING_OWNERSHIP_VERIFICATION" - DomainNameStatus_UPDATING DomainNameStatus = "UPDATING" + DomainNameStatus_SDK_AVAILABLE DomainNameStatus_SDK = "AVAILABLE" + DomainNameStatus_SDK_PENDING DomainNameStatus_SDK = "PENDING" + DomainNameStatus_SDK_PENDING_CERTIFICATE_REIMPORT DomainNameStatus_SDK = "PENDING_CERTIFICATE_REIMPORT" + DomainNameStatus_SDK_PENDING_OWNERSHIP_VERIFICATION DomainNameStatus_SDK = "PENDING_OWNERSHIP_VERIFICATION" + DomainNameStatus_SDK_UPDATING DomainNameStatus_SDK = "UPDATING" ) type EndpointType string diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index ed13b03..a873cbd 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -49,7 +49,7 @@ ignore: # - Deployment - DocumentationPart - DocumentationVersion - - DomainName + # - DomainName - DomainNameAccessAssociation - Model - RequestValidator @@ -426,4 +426,31 @@ resources: exceptions: terminal_codes: - BadRequestException - - InvalidParameter \ No newline at end of file + - InvalidParameter + DomainName: + fields: + DomainName: + is_primary_key: true + is_required: true + is_immutable: true + RestAPIID: + references: + resource: RestAPI + path: Status.ID + is_required: true + is_immutable: true + hooks: + sdk_update_pre_build_request: + template_path: hooks/domain_name/sdk_update_pre_build_request.go.tpl + sdk_update_post_build_request: + template_path: hooks/domain_name/sdk_update_post_build_request.go.tpl + exceptions: + terminal_codes: + - BadRequestException + - InvalidParameter + synced: + when: + - path: Status.DomainNameStatus + in: + - AVAILABLE + - NOT_AVAILABLE diff --git a/apis/v1alpha1/types.go b/apis/v1alpha1/types.go index 4697a59..8dc2370 100644 --- a/apis/v1alpha1/types.go +++ b/apis/v1alpha1/types.go @@ -141,8 +141,19 @@ type DocumentationVersion struct { Version *string `json:"version,omitempty"` } +// Represents a domain name access association between an access association +// source and a private custom domain name. With a domain name access association, +// an access association source can invoke a private custom domain name while +// isolated from the public internet. +type DomainNameAccessAssociation struct { + AccessAssociationSource *string `json:"accessAssociationSource,omitempty"` + DomainNameAccessAssociationARN *string `json:"domainNameAccessAssociationARN,omitempty"` + DomainNameARN *string `json:"domainNameARN,omitempty"` + Tags map[string]*string `json:"tags,omitempty"` +} + // Represents a custom domain name as a user-friendly host name of an API (RestApi). -type DomainName struct { +type DomainName_SDK struct { CertificateARN *string `json:"certificateARN,omitempty"` CertificateName *string `json:"certificateName,omitempty"` CertificateUploadDate *metav1.Time `json:"certificateUploadDate,omitempty"` @@ -151,29 +162,25 @@ type DomainName struct { DomainName *string `json:"domainName,omitempty"` DomainNameARN *string `json:"domainNameARN,omitempty"` DomainNameID *string `json:"domainNameID,omitempty"` + DomainNameStatus *string `json:"domainNameStatus,omitempty"` DomainNameStatusMessage *string `json:"domainNameStatusMessage,omitempty"` // The endpoint configuration to indicate the types of endpoints an API (RestApi) // or its custom domain name (DomainName) has. - EndpointConfiguration *EndpointConfiguration `json:"endpointConfiguration,omitempty"` - ManagementPolicy *string `json:"managementPolicy,omitempty"` - OwnershipVerificationCertificateARN *string `json:"ownershipVerificationCertificateARN,omitempty"` - Policy *string `json:"policy,omitempty"` - RegionalCertificateARN *string `json:"regionalCertificateARN,omitempty"` - RegionalCertificateName *string `json:"regionalCertificateName,omitempty"` - RegionalDomainName *string `json:"regionalDomainName,omitempty"` - RegionalHostedZoneID *string `json:"regionalHostedZoneID,omitempty"` - Tags map[string]*string `json:"tags,omitempty"` -} - -// Represents a domain name access association between an access association -// source and a private custom domain name. With a domain name access association, -// an access association source can invoke a private custom domain name while -// isolated from the public internet. -type DomainNameAccessAssociation struct { - AccessAssociationSource *string `json:"accessAssociationSource,omitempty"` - DomainNameAccessAssociationARN *string `json:"domainNameAccessAssociationARN,omitempty"` - DomainNameARN *string `json:"domainNameARN,omitempty"` - Tags map[string]*string `json:"tags,omitempty"` + EndpointConfiguration *EndpointConfiguration `json:"endpointConfiguration,omitempty"` + ManagementPolicy *string `json:"managementPolicy,omitempty"` + // The mutual TLS authentication configuration for a custom domain name. If + // specified, API Gateway performs two-way authentication between the client + // and the server. Clients must present a trusted certificate to access your + // API. + MutualTLSAuthentication *MutualTLSAuthentication `json:"mutualTLSAuthentication,omitempty"` + OwnershipVerificationCertificateARN *string `json:"ownershipVerificationCertificateARN,omitempty"` + Policy *string `json:"policy,omitempty"` + RegionalCertificateARN *string `json:"regionalCertificateARN,omitempty"` + RegionalCertificateName *string `json:"regionalCertificateName,omitempty"` + RegionalDomainName *string `json:"regionalDomainName,omitempty"` + RegionalHostedZoneID *string `json:"regionalHostedZoneID,omitempty"` + SecurityPolicy *string `json:"securityPolicy,omitempty"` + Tags map[string]*string `json:"tags,omitempty"` } // The endpoint configuration to indicate the types of endpoints an API (RestApi) diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index a58a2c9..7059684 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -1431,47 +1431,138 @@ func (in *DocumentationVersion) DeepCopy() *DocumentationVersion { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DomainName) DeepCopyInto(out *DomainName) { *out = *in - if in.CertificateARN != nil { - in, out := &in.CertificateARN, &out.CertificateARN + 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 DomainName. +func (in *DomainName) DeepCopy() *DomainName { + if in == nil { + return nil + } + out := new(DomainName) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DomainName) 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 *DomainNameAccessAssociation) DeepCopyInto(out *DomainNameAccessAssociation) { + *out = *in + if in.AccessAssociationSource != nil { + in, out := &in.AccessAssociationSource, &out.AccessAssociationSource *out = new(string) **out = **in } - if in.CertificateName != nil { - in, out := &in.CertificateName, &out.CertificateName + if in.DomainNameAccessAssociationARN != nil { + in, out := &in.DomainNameAccessAssociationARN, &out.DomainNameAccessAssociationARN *out = new(string) **out = **in } - if in.CertificateUploadDate != nil { - in, out := &in.CertificateUploadDate, &out.CertificateUploadDate - *out = (*in).DeepCopy() + if in.DomainNameARN != nil { + in, out := &in.DomainNameARN, &out.DomainNameARN + *out = new(string) + **out = **in } - if in.DistributionDomainName != nil { - in, out := &in.DistributionDomainName, &out.DistributionDomainName + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make(map[string]*string, len(*in)) + for key, val := range *in { + var outVal *string + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = new(string) + **out = **in + } + (*out)[key] = outVal + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainNameAccessAssociation. +func (in *DomainNameAccessAssociation) DeepCopy() *DomainNameAccessAssociation { + if in == nil { + return nil + } + out := new(DomainNameAccessAssociation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DomainNameList) DeepCopyInto(out *DomainNameList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DomainName, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainNameList. +func (in *DomainNameList) DeepCopy() *DomainNameList { + if in == nil { + return nil + } + out := new(DomainNameList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DomainNameList) 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 *DomainNameSpec) DeepCopyInto(out *DomainNameSpec) { + *out = *in + if in.CertificateARN != nil { + in, out := &in.CertificateARN, &out.CertificateARN *out = new(string) **out = **in } - if in.DistributionHostedZoneID != nil { - in, out := &in.DistributionHostedZoneID, &out.DistributionHostedZoneID + if in.CertificateBody != nil { + in, out := &in.CertificateBody, &out.CertificateBody *out = new(string) **out = **in } - if in.DomainName != nil { - in, out := &in.DomainName, &out.DomainName + if in.CertificateChain != nil { + in, out := &in.CertificateChain, &out.CertificateChain *out = new(string) **out = **in } - if in.DomainNameARN != nil { - in, out := &in.DomainNameARN, &out.DomainNameARN + if in.CertificateName != nil { + in, out := &in.CertificateName, &out.CertificateName *out = new(string) **out = **in } - if in.DomainNameID != nil { - in, out := &in.DomainNameID, &out.DomainNameID + if in.CertificatePrivateKey != nil { + in, out := &in.CertificatePrivateKey, &out.CertificatePrivateKey *out = new(string) **out = **in } - if in.DomainNameStatusMessage != nil { - in, out := &in.DomainNameStatusMessage, &out.DomainNameStatusMessage + if in.DomainName != nil { + in, out := &in.DomainName, &out.DomainName *out = new(string) **out = **in } @@ -1480,10 +1571,10 @@ func (in *DomainName) DeepCopyInto(out *DomainName) { *out = new(EndpointConfiguration) (*in).DeepCopyInto(*out) } - if in.ManagementPolicy != nil { - in, out := &in.ManagementPolicy, &out.ManagementPolicy - *out = new(string) - **out = **in + if in.MutualTLSAuthentication != nil { + in, out := &in.MutualTLSAuthentication, &out.MutualTLSAuthentication + *out = new(MutualTLSAuthenticationInput) + (*in).DeepCopyInto(*out) } if in.OwnershipVerificationCertificateARN != nil { in, out := &in.OwnershipVerificationCertificateARN, &out.OwnershipVerificationCertificateARN @@ -1505,13 +1596,8 @@ func (in *DomainName) DeepCopyInto(out *DomainName) { *out = new(string) **out = **in } - if in.RegionalDomainName != nil { - in, out := &in.RegionalDomainName, &out.RegionalDomainName - *out = new(string) - **out = **in - } - if in.RegionalHostedZoneID != nil { - in, out := &in.RegionalHostedZoneID, &out.RegionalHostedZoneID + if in.SecurityPolicy != nil { + in, out := &in.SecurityPolicy, &out.SecurityPolicy *out = new(string) **out = **in } @@ -1533,26 +1619,120 @@ func (in *DomainName) DeepCopyInto(out *DomainName) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainName. -func (in *DomainName) DeepCopy() *DomainName { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainNameSpec. +func (in *DomainNameSpec) DeepCopy() *DomainNameSpec { if in == nil { return nil } - out := new(DomainName) + out := new(DomainNameSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DomainNameAccessAssociation) DeepCopyInto(out *DomainNameAccessAssociation) { +func (in *DomainNameStatus) DeepCopyInto(out *DomainNameStatus) { *out = *in - if in.AccessAssociationSource != nil { - in, out := &in.AccessAssociationSource, &out.AccessAssociationSource + 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.CertificateUploadDate != nil { + in, out := &in.CertificateUploadDate, &out.CertificateUploadDate + *out = (*in).DeepCopy() + } + if in.DistributionDomainName != nil { + in, out := &in.DistributionDomainName, &out.DistributionDomainName *out = new(string) **out = **in } - if in.DomainNameAccessAssociationARN != nil { - in, out := &in.DomainNameAccessAssociationARN, &out.DomainNameAccessAssociationARN + if in.DistributionHostedZoneID != nil { + in, out := &in.DistributionHostedZoneID, &out.DistributionHostedZoneID + *out = new(string) + **out = **in + } + if in.DomainNameID != nil { + in, out := &in.DomainNameID, &out.DomainNameID + *out = new(string) + **out = **in + } + if in.DomainNameStatus != nil { + in, out := &in.DomainNameStatus, &out.DomainNameStatus + *out = new(string) + **out = **in + } + if in.DomainNameStatusMessage != nil { + in, out := &in.DomainNameStatusMessage, &out.DomainNameStatusMessage + *out = new(string) + **out = **in + } + if in.ManagementPolicy != nil { + in, out := &in.ManagementPolicy, &out.ManagementPolicy + *out = new(string) + **out = **in + } + if in.RegionalDomainName != nil { + in, out := &in.RegionalDomainName, &out.RegionalDomainName + *out = new(string) + **out = **in + } + if in.RegionalHostedZoneID != nil { + in, out := &in.RegionalHostedZoneID, &out.RegionalHostedZoneID + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainNameStatus. +func (in *DomainNameStatus) DeepCopy() *DomainNameStatus { + if in == nil { + return nil + } + out := new(DomainNameStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DomainName_SDK) DeepCopyInto(out *DomainName_SDK) { + *out = *in + if in.CertificateARN != nil { + in, out := &in.CertificateARN, &out.CertificateARN + *out = new(string) + **out = **in + } + if in.CertificateName != nil { + in, out := &in.CertificateName, &out.CertificateName + *out = new(string) + **out = **in + } + if in.CertificateUploadDate != nil { + in, out := &in.CertificateUploadDate, &out.CertificateUploadDate + *out = (*in).DeepCopy() + } + if in.DistributionDomainName != nil { + in, out := &in.DistributionDomainName, &out.DistributionDomainName + *out = new(string) + **out = **in + } + if in.DistributionHostedZoneID != nil { + in, out := &in.DistributionHostedZoneID, &out.DistributionHostedZoneID + *out = new(string) + **out = **in + } + if in.DomainName != nil { + in, out := &in.DomainName, &out.DomainName *out = new(string) **out = **in } @@ -1561,6 +1741,71 @@ func (in *DomainNameAccessAssociation) DeepCopyInto(out *DomainNameAccessAssocia *out = new(string) **out = **in } + if in.DomainNameID != nil { + in, out := &in.DomainNameID, &out.DomainNameID + *out = new(string) + **out = **in + } + if in.DomainNameStatus != nil { + in, out := &in.DomainNameStatus, &out.DomainNameStatus + *out = new(string) + **out = **in + } + if in.DomainNameStatusMessage != nil { + in, out := &in.DomainNameStatusMessage, &out.DomainNameStatusMessage + *out = new(string) + **out = **in + } + if in.EndpointConfiguration != nil { + in, out := &in.EndpointConfiguration, &out.EndpointConfiguration + *out = new(EndpointConfiguration) + (*in).DeepCopyInto(*out) + } + if in.ManagementPolicy != nil { + in, out := &in.ManagementPolicy, &out.ManagementPolicy + *out = new(string) + **out = **in + } + if in.MutualTLSAuthentication != nil { + in, out := &in.MutualTLSAuthentication, &out.MutualTLSAuthentication + *out = new(MutualTLSAuthentication) + (*in).DeepCopyInto(*out) + } + if in.OwnershipVerificationCertificateARN != nil { + in, out := &in.OwnershipVerificationCertificateARN, &out.OwnershipVerificationCertificateARN + *out = new(string) + **out = **in + } + if in.Policy != nil { + in, out := &in.Policy, &out.Policy + *out = new(string) + **out = **in + } + if in.RegionalCertificateARN != nil { + in, out := &in.RegionalCertificateARN, &out.RegionalCertificateARN + *out = new(string) + **out = **in + } + if in.RegionalCertificateName != nil { + in, out := &in.RegionalCertificateName, &out.RegionalCertificateName + *out = new(string) + **out = **in + } + if in.RegionalDomainName != nil { + in, out := &in.RegionalDomainName, &out.RegionalDomainName + *out = new(string) + **out = **in + } + if in.RegionalHostedZoneID != nil { + in, out := &in.RegionalHostedZoneID, &out.RegionalHostedZoneID + *out = new(string) + **out = **in + } + if in.SecurityPolicy != nil { + in, out := &in.SecurityPolicy, &out.SecurityPolicy + *out = new(string) + **out = **in + } if in.Tags != nil { in, out := &in.Tags, &out.Tags *out = make(map[string]*string, len(*in)) @@ -1579,12 +1824,12 @@ func (in *DomainNameAccessAssociation) DeepCopyInto(out *DomainNameAccessAssocia } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainNameAccessAssociation. -func (in *DomainNameAccessAssociation) DeepCopy() *DomainNameAccessAssociation { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainName_SDK. +func (in *DomainName_SDK) DeepCopy() *DomainName_SDK { if in == nil { return nil } - out := new(DomainNameAccessAssociation) + out := new(DomainName_SDK) in.DeepCopyInto(out) return out } diff --git a/cmd/controller/main.go b/cmd/controller/main.go index e729874..e93bf60 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -45,6 +45,7 @@ import ( _ "github.com/aws-controllers-k8s/apigateway-controller/pkg/resource/api_method_response" _ "github.com/aws-controllers-k8s/apigateway-controller/pkg/resource/authorizer" _ "github.com/aws-controllers-k8s/apigateway-controller/pkg/resource/deployment" + _ "github.com/aws-controllers-k8s/apigateway-controller/pkg/resource/domain_name" _ "github.com/aws-controllers-k8s/apigateway-controller/pkg/resource/integration" _ "github.com/aws-controllers-k8s/apigateway-controller/pkg/resource/method" _ "github.com/aws-controllers-k8s/apigateway-controller/pkg/resource/resource" diff --git a/config/controller/kustomization.yaml b/config/controller/kustomization.yaml index 3aa7c5f..b6f7482 100644 --- a/config/controller/kustomization.yaml +++ b/config/controller/kustomization.yaml @@ -6,4 +6,4 @@ kind: Kustomization images: - name: controller newName: public.ecr.aws/aws-controllers-k8s/apigateway-controller - newTag: 1.2.5 + newTag: 1.2.6 diff --git a/config/crd/bases/apigateway.services.k8s.aws_domainnames.yaml b/config/crd/bases/apigateway.services.k8s.aws_domainnames.yaml new file mode 100644 index 0000000..5b400b7 --- /dev/null +++ b/config/crd/bases/apigateway.services.k8s.aws_domainnames.yaml @@ -0,0 +1,298 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.2 + name: domainnames.apigateway.services.k8s.aws +spec: + group: apigateway.services.k8s.aws + names: + kind: DomainName + listKind: DomainNameList + plural: domainnames + singular: domainname + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: DomainName is the Schema for the DomainNames 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: |- + DomainNameSpec defines the desired state of DomainName. + + Represents a custom domain name as a user-friendly host name of an API (RestApi). + properties: + certificateARN: + description: |- + The reference to an Amazon Web Services-managed certificate that will be + used by edge-optimized endpoint or private endpoint for this domain name. + Certificate Manager is the only supported source. + type: string + certificateBody: + description: |- + [Deprecated] The body of the server certificate that will be used by edge-optimized + endpoint or private endpoint for this domain name provided by your certificate + authority. + type: string + certificateChain: + description: |- + [Deprecated] The intermediate certificates and optionally the root certificate, + one after the other without any blank lines, used by an edge-optimized endpoint + for this domain name. If you include the root certificate, your certificate + chain must start with intermediate certificates and end with the root certificate. + Use the intermediate certificates that were provided by your certificate + authority. Do not include any intermediaries that are not in the chain of + trust path. + type: string + certificateName: + description: |- + The user-friendly name of the certificate that will be used by edge-optimized + endpoint or private endpoint for this domain name. + type: string + certificatePrivateKey: + description: |- + [Deprecated] Your edge-optimized endpoint's domain name certificate's private + key. + type: string + domainName: + description: The name of the DomainName resource. + type: string + x-kubernetes-validations: + - message: Value is immutable once set + rule: self == oldSelf + endpointConfiguration: + description: |- + The endpoint configuration of this DomainName showing the endpoint types + of the domain name. + properties: + types: + items: + type: string + type: array + vpcEndpointIDs: + items: + type: string + type: array + vpcEndpointRefs: + description: Reference field for VPCEndpointIDs + items: + description: "AWSResourceReferenceWrapper provides a wrapper + around *AWSResourceReference\ntype to provide more user friendly + syntax for references using 'from' field\nEx:\nAPIIDRef:\n\n\tfrom:\n\t + \ name: my-api" + properties: + from: + description: |- + AWSResourceReference provides all the values necessary to reference another + k8s resource for finding the identifier(Id/ARN/Name) + properties: + name: + type: string + namespace: + type: string + type: object + type: object + type: array + type: object + mutualTLSAuthentication: + description: |- + The mutual TLS authentication configuration for a custom domain name. If + specified, API Gateway performs two-way authentication between the client + and the server. Clients must present a trusted certificate to access your + API. + properties: + truststoreURI: + type: string + truststoreVersion: + type: string + type: object + ownershipVerificationCertificateARN: + description: |- + The ARN of the public certificate issued by ACM to validate ownership of + your custom domain. Only required when configuring mutual TLS and using an + ACM imported or private CA certificate ARN as the regionalCertificateArn. + type: string + policy: + description: |- + A stringified JSON policy document that applies to the execute-api service + for this DomainName regardless of the caller and Method configuration. Supported + only for private custom domain names. + type: string + regionalCertificateARN: + description: |- + The reference to an Amazon Web Services-managed certificate that will be + used by regional endpoint for this domain name. Certificate Manager is the + only supported source. + type: string + regionalCertificateName: + description: |- + The user-friendly name of the certificate that will be used by regional endpoint + for this domain name. + type: string + securityPolicy: + description: |- + The Transport Layer Security (TLS) version + cipher suite for this DomainName. + The valid values are TLS_1_0 and TLS_1_2. + type: string + tags: + additionalProperties: + type: string + description: |- + The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. + The tag key can be up to 128 characters and must not start with aws:. The + tag value can be up to 256 characters. + type: object + required: + - domainName + type: object + status: + description: DomainNameStatus defines the observed state of DomainName + properties: + ackResourceMetadata: + description: |- + All CRs managed by ACK have a common `Status.ACKResourceMetadata` member + that is used to contain resource sync state, account ownership, + constructed ARN for the resource + properties: + arn: + description: |- + ARN is the Amazon Resource Name for the resource. This is a + globally-unique identifier and is set only by the ACK service controller + once the controller has orchestrated the creation of the resource OR + when it has verified that an "adopted" resource (a resource where the + ARN annotation was set by the Kubernetes user on the CR) exists and + matches the supplied CR's Spec field values. + https://github.com/aws/aws-controllers-k8s/issues/270 + type: string + ownerAccountID: + description: |- + OwnerAccountID is the AWS Account ID of the account that owns the + backend AWS service API resource. + type: string + region: + description: Region is the AWS region in which the resource exists + or will exist. + type: string + required: + - ownerAccountID + - region + type: object + certificateUploadDate: + description: |- + The timestamp when the certificate that was used by edge-optimized endpoint + or private endpoint for this domain name was uploaded. + format: date-time + type: string + 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 + distributionDomainName: + description: |- + The domain name of the Amazon CloudFront distribution associated with this + custom domain name for an edge-optimized endpoint. You set up this association + when adding a DNS record pointing the custom domain name to this distribution + name. For more information about CloudFront distributions, see the Amazon + CloudFront documentation. + type: string + distributionHostedZoneID: + description: |- + The region-agnostic Amazon Route 53 Hosted Zone ID of the edge-optimized + endpoint. The valid value is Z2FDTNDATAQYW2 for all the regions. For more + information, see Set up a Regional Custom Domain Name and AWS Regions and + Endpoints for API Gateway. + type: string + domainNameID: + description: |- + The identifier for the domain name resource. Supported only for private custom + domain names. + type: string + domainNameStatus: + description: |- + The status of the DomainName migration. The valid values are AVAILABLE and + UPDATING. If the status is UPDATING, the domain cannot be modified further + until the existing operation is complete. If it is AVAILABLE, the domain + can be updated. + type: string + domainNameStatusMessage: + description: |- + An optional text message containing detailed information about status of + the DomainName migration. + type: string + managementPolicy: + description: |- + A stringified JSON policy document that applies to the API Gateway Management + service for this DomainName. This policy document controls access for access + association sources to create domain name access associations with this DomainName. + Supported only for private custom domain names. + type: string + regionalDomainName: + description: |- + The domain name associated with the regional endpoint for this custom domain + name. You set up this association by adding a DNS record that points the + custom domain name to this regional domain name. The regional domain name + is returned by API Gateway when you create a regional endpoint. + type: string + regionalHostedZoneID: + description: |- + The region-specific Amazon Route 53 Hosted Zone ID of the regional endpoint. + For more information, see Set up a Regional Custom Domain Name and AWS Regions + and Endpoints for API Gateway. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 610b567..11abc6b 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -7,6 +7,7 @@ resources: - bases/apigateway.services.k8s.aws_apimethodresponses.yaml - bases/apigateway.services.k8s.aws_authorizers.yaml - bases/apigateway.services.k8s.aws_deployments.yaml + - bases/apigateway.services.k8s.aws_domainnames.yaml - bases/apigateway.services.k8s.aws_integrations.yaml - bases/apigateway.services.k8s.aws_methods.yaml - bases/apigateway.services.k8s.aws_resources.yaml diff --git a/config/rbac/cluster-role-controller.yaml b/config/rbac/cluster-role-controller.yaml index 415c5fe..50abc5c 100644 --- a/config/rbac/cluster-role-controller.yaml +++ b/config/rbac/cluster-role-controller.yaml @@ -30,6 +30,7 @@ rules: - apimethodresponses - authorizers - deployments + - domainnames - integrations - methods - resources @@ -52,6 +53,7 @@ rules: - apimethodresponses/status - authorizers/status - deployments/status + - domainnames/status - integrations/status - methods/status - resources/status diff --git a/config/rbac/role-reader.yaml b/config/rbac/role-reader.yaml index f84b820..7573d59 100644 --- a/config/rbac/role-reader.yaml +++ b/config/rbac/role-reader.yaml @@ -14,6 +14,7 @@ rules: - apimethodresponses - authorizers - deployments + - domainnames - integrations - methods - resources diff --git a/config/rbac/role-writer.yaml b/config/rbac/role-writer.yaml index 0f40871..7e57cfc 100644 --- a/config/rbac/role-writer.yaml +++ b/config/rbac/role-writer.yaml @@ -14,6 +14,7 @@ rules: - apimethodresponses - authorizers - deployments + - domainnames - integrations - methods - resources @@ -36,6 +37,7 @@ rules: - apimethodresponses - authorizers - deployments + - domainnames - integrations - methods - resources diff --git a/generator.yaml b/generator.yaml index ed13b03..a873cbd 100644 --- a/generator.yaml +++ b/generator.yaml @@ -49,7 +49,7 @@ ignore: # - Deployment - DocumentationPart - DocumentationVersion - - DomainName + # - DomainName - DomainNameAccessAssociation - Model - RequestValidator @@ -426,4 +426,31 @@ resources: exceptions: terminal_codes: - BadRequestException - - InvalidParameter \ No newline at end of file + - InvalidParameter + DomainName: + fields: + DomainName: + is_primary_key: true + is_required: true + is_immutable: true + RestAPIID: + references: + resource: RestAPI + path: Status.ID + is_required: true + is_immutable: true + hooks: + sdk_update_pre_build_request: + template_path: hooks/domain_name/sdk_update_pre_build_request.go.tpl + sdk_update_post_build_request: + template_path: hooks/domain_name/sdk_update_post_build_request.go.tpl + exceptions: + terminal_codes: + - BadRequestException + - InvalidParameter + synced: + when: + - path: Status.DomainNameStatus + in: + - AVAILABLE + - NOT_AVAILABLE diff --git a/helm/Chart.yaml b/helm/Chart.yaml index ff41446..bcfcd2a 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v1 name: apigateway-chart description: A Helm chart for the ACK service controller for Amazon API Gateway (apigateway) -version: 1.2.5 -appVersion: 1.2.5 +version: 1.2.6 +appVersion: 1.2.6 home: https://github.com/aws-controllers-k8s/apigateway-controller icon: https://raw.githubusercontent.com/aws/eks-charts/master/docs/logo/aws.png sources: diff --git a/helm/crds/apigateway.services.k8s.aws_domainnames.yaml b/helm/crds/apigateway.services.k8s.aws_domainnames.yaml new file mode 100644 index 0000000..5b400b7 --- /dev/null +++ b/helm/crds/apigateway.services.k8s.aws_domainnames.yaml @@ -0,0 +1,298 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.2 + name: domainnames.apigateway.services.k8s.aws +spec: + group: apigateway.services.k8s.aws + names: + kind: DomainName + listKind: DomainNameList + plural: domainnames + singular: domainname + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: DomainName is the Schema for the DomainNames 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: |- + DomainNameSpec defines the desired state of DomainName. + + Represents a custom domain name as a user-friendly host name of an API (RestApi). + properties: + certificateARN: + description: |- + The reference to an Amazon Web Services-managed certificate that will be + used by edge-optimized endpoint or private endpoint for this domain name. + Certificate Manager is the only supported source. + type: string + certificateBody: + description: |- + [Deprecated] The body of the server certificate that will be used by edge-optimized + endpoint or private endpoint for this domain name provided by your certificate + authority. + type: string + certificateChain: + description: |- + [Deprecated] The intermediate certificates and optionally the root certificate, + one after the other without any blank lines, used by an edge-optimized endpoint + for this domain name. If you include the root certificate, your certificate + chain must start with intermediate certificates and end with the root certificate. + Use the intermediate certificates that were provided by your certificate + authority. Do not include any intermediaries that are not in the chain of + trust path. + type: string + certificateName: + description: |- + The user-friendly name of the certificate that will be used by edge-optimized + endpoint or private endpoint for this domain name. + type: string + certificatePrivateKey: + description: |- + [Deprecated] Your edge-optimized endpoint's domain name certificate's private + key. + type: string + domainName: + description: The name of the DomainName resource. + type: string + x-kubernetes-validations: + - message: Value is immutable once set + rule: self == oldSelf + endpointConfiguration: + description: |- + The endpoint configuration of this DomainName showing the endpoint types + of the domain name. + properties: + types: + items: + type: string + type: array + vpcEndpointIDs: + items: + type: string + type: array + vpcEndpointRefs: + description: Reference field for VPCEndpointIDs + items: + description: "AWSResourceReferenceWrapper provides a wrapper + around *AWSResourceReference\ntype to provide more user friendly + syntax for references using 'from' field\nEx:\nAPIIDRef:\n\n\tfrom:\n\t + \ name: my-api" + properties: + from: + description: |- + AWSResourceReference provides all the values necessary to reference another + k8s resource for finding the identifier(Id/ARN/Name) + properties: + name: + type: string + namespace: + type: string + type: object + type: object + type: array + type: object + mutualTLSAuthentication: + description: |- + The mutual TLS authentication configuration for a custom domain name. If + specified, API Gateway performs two-way authentication between the client + and the server. Clients must present a trusted certificate to access your + API. + properties: + truststoreURI: + type: string + truststoreVersion: + type: string + type: object + ownershipVerificationCertificateARN: + description: |- + The ARN of the public certificate issued by ACM to validate ownership of + your custom domain. Only required when configuring mutual TLS and using an + ACM imported or private CA certificate ARN as the regionalCertificateArn. + type: string + policy: + description: |- + A stringified JSON policy document that applies to the execute-api service + for this DomainName regardless of the caller and Method configuration. Supported + only for private custom domain names. + type: string + regionalCertificateARN: + description: |- + The reference to an Amazon Web Services-managed certificate that will be + used by regional endpoint for this domain name. Certificate Manager is the + only supported source. + type: string + regionalCertificateName: + description: |- + The user-friendly name of the certificate that will be used by regional endpoint + for this domain name. + type: string + securityPolicy: + description: |- + The Transport Layer Security (TLS) version + cipher suite for this DomainName. + The valid values are TLS_1_0 and TLS_1_2. + type: string + tags: + additionalProperties: + type: string + description: |- + The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. + The tag key can be up to 128 characters and must not start with aws:. The + tag value can be up to 256 characters. + type: object + required: + - domainName + type: object + status: + description: DomainNameStatus defines the observed state of DomainName + properties: + ackResourceMetadata: + description: |- + All CRs managed by ACK have a common `Status.ACKResourceMetadata` member + that is used to contain resource sync state, account ownership, + constructed ARN for the resource + properties: + arn: + description: |- + ARN is the Amazon Resource Name for the resource. This is a + globally-unique identifier and is set only by the ACK service controller + once the controller has orchestrated the creation of the resource OR + when it has verified that an "adopted" resource (a resource where the + ARN annotation was set by the Kubernetes user on the CR) exists and + matches the supplied CR's Spec field values. + https://github.com/aws/aws-controllers-k8s/issues/270 + type: string + ownerAccountID: + description: |- + OwnerAccountID is the AWS Account ID of the account that owns the + backend AWS service API resource. + type: string + region: + description: Region is the AWS region in which the resource exists + or will exist. + type: string + required: + - ownerAccountID + - region + type: object + certificateUploadDate: + description: |- + The timestamp when the certificate that was used by edge-optimized endpoint + or private endpoint for this domain name was uploaded. + format: date-time + type: string + 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 + distributionDomainName: + description: |- + The domain name of the Amazon CloudFront distribution associated with this + custom domain name for an edge-optimized endpoint. You set up this association + when adding a DNS record pointing the custom domain name to this distribution + name. For more information about CloudFront distributions, see the Amazon + CloudFront documentation. + type: string + distributionHostedZoneID: + description: |- + The region-agnostic Amazon Route 53 Hosted Zone ID of the edge-optimized + endpoint. The valid value is Z2FDTNDATAQYW2 for all the regions. For more + information, see Set up a Regional Custom Domain Name and AWS Regions and + Endpoints for API Gateway. + type: string + domainNameID: + description: |- + The identifier for the domain name resource. Supported only for private custom + domain names. + type: string + domainNameStatus: + description: |- + The status of the DomainName migration. The valid values are AVAILABLE and + UPDATING. If the status is UPDATING, the domain cannot be modified further + until the existing operation is complete. If it is AVAILABLE, the domain + can be updated. + type: string + domainNameStatusMessage: + description: |- + An optional text message containing detailed information about status of + the DomainName migration. + type: string + managementPolicy: + description: |- + A stringified JSON policy document that applies to the API Gateway Management + service for this DomainName. This policy document controls access for access + association sources to create domain name access associations with this DomainName. + Supported only for private custom domain names. + type: string + regionalDomainName: + description: |- + The domain name associated with the regional endpoint for this custom domain + name. You set up this association by adding a DNS record that points the + custom domain name to this regional domain name. The regional domain name + is returned by API Gateway when you create a regional endpoint. + type: string + regionalHostedZoneID: + description: |- + The region-specific Amazon Route 53 Hosted Zone ID of the regional endpoint. + For more information, see Set up a Regional Custom Domain Name and AWS Regions + and Endpoints for API Gateway. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm/templates/NOTES.txt b/helm/templates/NOTES.txt index 37f078f..1b6664e 100644 --- a/helm/templates/NOTES.txt +++ b/helm/templates/NOTES.txt @@ -1,5 +1,5 @@ {{ .Chart.Name }} has been installed. -This chart deploys "public.ecr.aws/aws-controllers-k8s/apigateway-controller:1.2.5". +This chart deploys "public.ecr.aws/aws-controllers-k8s/apigateway-controller:1.2.6". Check its status by running: kubectl --namespace {{ .Release.Namespace }} get pods -l "app.kubernetes.io/instance={{ .Release.Name }}" diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl index d43477a..5d4ace6 100644 --- a/helm/templates/_helpers.tpl +++ b/helm/templates/_helpers.tpl @@ -77,6 +77,7 @@ rules: - apimethodresponses - authorizers - deployments + - domainnames - integrations - methods - resources @@ -99,6 +100,7 @@ rules: - apimethodresponses/status - authorizers/status - deployments/status + - domainnames/status - integrations/status - methods/status - resources/status diff --git a/helm/templates/role-reader.yaml b/helm/templates/role-reader.yaml index 7c0f6dd..3eaa268 100644 --- a/helm/templates/role-reader.yaml +++ b/helm/templates/role-reader.yaml @@ -21,6 +21,7 @@ rules: - apimethodresponses - authorizers - deployments + - domainnames - integrations - methods - resources diff --git a/helm/templates/role-writer.yaml b/helm/templates/role-writer.yaml index f216ca4..9b533c0 100644 --- a/helm/templates/role-writer.yaml +++ b/helm/templates/role-writer.yaml @@ -21,6 +21,7 @@ rules: - apimethodresponses - authorizers - deployments + - domainnames - integrations - methods - resources @@ -43,6 +44,7 @@ rules: - apimethodresponses - authorizers - deployments + - domainnames - integrations - methods - resources diff --git a/helm/values.yaml b/helm/values.yaml index 23a3e99..4904a32 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -4,7 +4,7 @@ image: repository: public.ecr.aws/aws-controllers-k8s/apigateway-controller - tag: 1.2.5 + tag: 1.2.6 pullPolicy: IfNotPresent pullSecrets: [] @@ -150,6 +150,7 @@ reconcile: - APIMethodResponse - Authorizer - Deployment + - DomainName - Integration - Method - Resource diff --git a/pkg/resource/domain_name/delta.go b/pkg/resource/domain_name/delta.go new file mode 100644 index 0000000..bfc1886 --- /dev/null +++ b/pkg/resource/domain_name/delta.go @@ -0,0 +1,166 @@ +// 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 domain_name + +import ( + "bytes" + "reflect" + + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" +) + +// Hack to avoid import errors during build... +var ( + _ = &bytes.Buffer{} + _ = &reflect.Method{} + _ = &acktags.Tags{} +) + +// newResourceDelta returns a new `ackcompare.Delta` used to compare two +// resources +func newResourceDelta( + a *resource, + b *resource, +) *ackcompare.Delta { + delta := ackcompare.NewDelta() + if (a == nil && b != nil) || + (a != nil && b == nil) { + delta.Add("", a, b) + return delta + } + + if ackcompare.HasNilDifference(a.ko.Spec.CertificateARN, b.ko.Spec.CertificateARN) { + delta.Add("Spec.CertificateARN", a.ko.Spec.CertificateARN, b.ko.Spec.CertificateARN) + } else if a.ko.Spec.CertificateARN != nil && b.ko.Spec.CertificateARN != nil { + if *a.ko.Spec.CertificateARN != *b.ko.Spec.CertificateARN { + delta.Add("Spec.CertificateARN", a.ko.Spec.CertificateARN, b.ko.Spec.CertificateARN) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.CertificateBody, b.ko.Spec.CertificateBody) { + delta.Add("Spec.CertificateBody", a.ko.Spec.CertificateBody, b.ko.Spec.CertificateBody) + } else if a.ko.Spec.CertificateBody != nil && b.ko.Spec.CertificateBody != nil { + if *a.ko.Spec.CertificateBody != *b.ko.Spec.CertificateBody { + delta.Add("Spec.CertificateBody", a.ko.Spec.CertificateBody, b.ko.Spec.CertificateBody) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.CertificateChain, b.ko.Spec.CertificateChain) { + delta.Add("Spec.CertificateChain", a.ko.Spec.CertificateChain, b.ko.Spec.CertificateChain) + } else if a.ko.Spec.CertificateChain != nil && b.ko.Spec.CertificateChain != nil { + if *a.ko.Spec.CertificateChain != *b.ko.Spec.CertificateChain { + delta.Add("Spec.CertificateChain", a.ko.Spec.CertificateChain, b.ko.Spec.CertificateChain) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.CertificateName, b.ko.Spec.CertificateName) { + delta.Add("Spec.CertificateName", a.ko.Spec.CertificateName, b.ko.Spec.CertificateName) + } else if a.ko.Spec.CertificateName != nil && b.ko.Spec.CertificateName != nil { + if *a.ko.Spec.CertificateName != *b.ko.Spec.CertificateName { + delta.Add("Spec.CertificateName", a.ko.Spec.CertificateName, b.ko.Spec.CertificateName) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.CertificatePrivateKey, b.ko.Spec.CertificatePrivateKey) { + delta.Add("Spec.CertificatePrivateKey", a.ko.Spec.CertificatePrivateKey, b.ko.Spec.CertificatePrivateKey) + } else if a.ko.Spec.CertificatePrivateKey != nil && b.ko.Spec.CertificatePrivateKey != nil { + if *a.ko.Spec.CertificatePrivateKey != *b.ko.Spec.CertificatePrivateKey { + delta.Add("Spec.CertificatePrivateKey", a.ko.Spec.CertificatePrivateKey, b.ko.Spec.CertificatePrivateKey) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.DomainName, b.ko.Spec.DomainName) { + delta.Add("Spec.DomainName", a.ko.Spec.DomainName, b.ko.Spec.DomainName) + } else if a.ko.Spec.DomainName != nil && b.ko.Spec.DomainName != nil { + if *a.ko.Spec.DomainName != *b.ko.Spec.DomainName { + delta.Add("Spec.DomainName", a.ko.Spec.DomainName, b.ko.Spec.DomainName) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.EndpointConfiguration, b.ko.Spec.EndpointConfiguration) { + delta.Add("Spec.EndpointConfiguration", a.ko.Spec.EndpointConfiguration, b.ko.Spec.EndpointConfiguration) + } else if a.ko.Spec.EndpointConfiguration != nil && b.ko.Spec.EndpointConfiguration != nil { + if len(a.ko.Spec.EndpointConfiguration.Types) != len(b.ko.Spec.EndpointConfiguration.Types) { + delta.Add("Spec.EndpointConfiguration.Types", a.ko.Spec.EndpointConfiguration.Types, b.ko.Spec.EndpointConfiguration.Types) + } else if len(a.ko.Spec.EndpointConfiguration.Types) > 0 { + if !ackcompare.SliceStringPEqual(a.ko.Spec.EndpointConfiguration.Types, b.ko.Spec.EndpointConfiguration.Types) { + delta.Add("Spec.EndpointConfiguration.Types", a.ko.Spec.EndpointConfiguration.Types, b.ko.Spec.EndpointConfiguration.Types) + } + } + if len(a.ko.Spec.EndpointConfiguration.VPCEndpointIDs) != len(b.ko.Spec.EndpointConfiguration.VPCEndpointIDs) { + delta.Add("Spec.EndpointConfiguration.VPCEndpointIDs", a.ko.Spec.EndpointConfiguration.VPCEndpointIDs, b.ko.Spec.EndpointConfiguration.VPCEndpointIDs) + } else if len(a.ko.Spec.EndpointConfiguration.VPCEndpointIDs) > 0 { + if !ackcompare.SliceStringPEqual(a.ko.Spec.EndpointConfiguration.VPCEndpointIDs, b.ko.Spec.EndpointConfiguration.VPCEndpointIDs) { + delta.Add("Spec.EndpointConfiguration.VPCEndpointIDs", a.ko.Spec.EndpointConfiguration.VPCEndpointIDs, b.ko.Spec.EndpointConfiguration.VPCEndpointIDs) + } + } + } + if ackcompare.HasNilDifference(a.ko.Spec.MutualTLSAuthentication, b.ko.Spec.MutualTLSAuthentication) { + delta.Add("Spec.MutualTLSAuthentication", a.ko.Spec.MutualTLSAuthentication, b.ko.Spec.MutualTLSAuthentication) + } else if a.ko.Spec.MutualTLSAuthentication != nil && b.ko.Spec.MutualTLSAuthentication != nil { + if ackcompare.HasNilDifference(a.ko.Spec.MutualTLSAuthentication.TruststoreURI, b.ko.Spec.MutualTLSAuthentication.TruststoreURI) { + delta.Add("Spec.MutualTLSAuthentication.TruststoreURI", a.ko.Spec.MutualTLSAuthentication.TruststoreURI, b.ko.Spec.MutualTLSAuthentication.TruststoreURI) + } else if a.ko.Spec.MutualTLSAuthentication.TruststoreURI != nil && b.ko.Spec.MutualTLSAuthentication.TruststoreURI != nil { + if *a.ko.Spec.MutualTLSAuthentication.TruststoreURI != *b.ko.Spec.MutualTLSAuthentication.TruststoreURI { + delta.Add("Spec.MutualTLSAuthentication.TruststoreURI", a.ko.Spec.MutualTLSAuthentication.TruststoreURI, b.ko.Spec.MutualTLSAuthentication.TruststoreURI) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.MutualTLSAuthentication.TruststoreVersion, b.ko.Spec.MutualTLSAuthentication.TruststoreVersion) { + delta.Add("Spec.MutualTLSAuthentication.TruststoreVersion", a.ko.Spec.MutualTLSAuthentication.TruststoreVersion, b.ko.Spec.MutualTLSAuthentication.TruststoreVersion) + } else if a.ko.Spec.MutualTLSAuthentication.TruststoreVersion != nil && b.ko.Spec.MutualTLSAuthentication.TruststoreVersion != nil { + if *a.ko.Spec.MutualTLSAuthentication.TruststoreVersion != *b.ko.Spec.MutualTLSAuthentication.TruststoreVersion { + delta.Add("Spec.MutualTLSAuthentication.TruststoreVersion", a.ko.Spec.MutualTLSAuthentication.TruststoreVersion, b.ko.Spec.MutualTLSAuthentication.TruststoreVersion) + } + } + } + if ackcompare.HasNilDifference(a.ko.Spec.OwnershipVerificationCertificateARN, b.ko.Spec.OwnershipVerificationCertificateARN) { + delta.Add("Spec.OwnershipVerificationCertificateARN", a.ko.Spec.OwnershipVerificationCertificateARN, b.ko.Spec.OwnershipVerificationCertificateARN) + } else if a.ko.Spec.OwnershipVerificationCertificateARN != nil && b.ko.Spec.OwnershipVerificationCertificateARN != nil { + if *a.ko.Spec.OwnershipVerificationCertificateARN != *b.ko.Spec.OwnershipVerificationCertificateARN { + delta.Add("Spec.OwnershipVerificationCertificateARN", a.ko.Spec.OwnershipVerificationCertificateARN, b.ko.Spec.OwnershipVerificationCertificateARN) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.Policy, b.ko.Spec.Policy) { + delta.Add("Spec.Policy", a.ko.Spec.Policy, b.ko.Spec.Policy) + } else if a.ko.Spec.Policy != nil && b.ko.Spec.Policy != nil { + if *a.ko.Spec.Policy != *b.ko.Spec.Policy { + delta.Add("Spec.Policy", a.ko.Spec.Policy, b.ko.Spec.Policy) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.RegionalCertificateARN, b.ko.Spec.RegionalCertificateARN) { + delta.Add("Spec.RegionalCertificateARN", a.ko.Spec.RegionalCertificateARN, b.ko.Spec.RegionalCertificateARN) + } else if a.ko.Spec.RegionalCertificateARN != nil && b.ko.Spec.RegionalCertificateARN != nil { + if *a.ko.Spec.RegionalCertificateARN != *b.ko.Spec.RegionalCertificateARN { + delta.Add("Spec.RegionalCertificateARN", a.ko.Spec.RegionalCertificateARN, b.ko.Spec.RegionalCertificateARN) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.RegionalCertificateName, b.ko.Spec.RegionalCertificateName) { + delta.Add("Spec.RegionalCertificateName", a.ko.Spec.RegionalCertificateName, b.ko.Spec.RegionalCertificateName) + } else if a.ko.Spec.RegionalCertificateName != nil && b.ko.Spec.RegionalCertificateName != nil { + if *a.ko.Spec.RegionalCertificateName != *b.ko.Spec.RegionalCertificateName { + delta.Add("Spec.RegionalCertificateName", a.ko.Spec.RegionalCertificateName, b.ko.Spec.RegionalCertificateName) + } + } + if ackcompare.HasNilDifference(a.ko.Spec.SecurityPolicy, b.ko.Spec.SecurityPolicy) { + delta.Add("Spec.SecurityPolicy", a.ko.Spec.SecurityPolicy, b.ko.Spec.SecurityPolicy) + } else if a.ko.Spec.SecurityPolicy != nil && b.ko.Spec.SecurityPolicy != nil { + if *a.ko.Spec.SecurityPolicy != *b.ko.Spec.SecurityPolicy { + delta.Add("Spec.SecurityPolicy", a.ko.Spec.SecurityPolicy, b.ko.Spec.SecurityPolicy) + } + } + desiredACKTags, _ := convertToOrderedACKTags(a.ko.Spec.Tags) + latestACKTags, _ := convertToOrderedACKTags(b.ko.Spec.Tags) + if !ackcompare.MapStringStringEqual(desiredACKTags, latestACKTags) { + delta.Add("Spec.Tags", a.ko.Spec.Tags, b.ko.Spec.Tags) + } + + return delta +} diff --git a/pkg/resource/domain_name/descriptor.go b/pkg/resource/domain_name/descriptor.go new file mode 100644 index 0000000..f95efa0 --- /dev/null +++ b/pkg/resource/domain_name/descriptor.go @@ -0,0 +1,155 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Code generated by ack-generate. DO NOT EDIT. + +package domain_name + +import ( + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + rtclient "sigs.k8s.io/controller-runtime/pkg/client" + k8sctrlutil "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + svcapitypes "github.com/aws-controllers-k8s/apigateway-controller/apis/v1alpha1" +) + +const ( + FinalizerString = "finalizers.apigateway.services.k8s.aws/DomainName" +) + +var ( + GroupVersionResource = svcapitypes.GroupVersion.WithResource("domainnames") + GroupKind = metav1.GroupKind{ + Group: "apigateway.services.k8s.aws", + Kind: "DomainName", + } +) + +// resourceDescriptor implements the +// `aws-service-operator-k8s/pkg/types.AWSResourceDescriptor` interface +type resourceDescriptor struct { +} + +// GroupVersionKind returns a Kubernetes schema.GroupVersionKind struct that +// describes the API Group, Version and Kind of CRs described by the descriptor +func (d *resourceDescriptor) GroupVersionKind() schema.GroupVersionKind { + return svcapitypes.GroupVersion.WithKind(GroupKind.Kind) +} + +// EmptyRuntimeObject returns an empty object prototype that may be used in +// apimachinery and k8s client operations +func (d *resourceDescriptor) EmptyRuntimeObject() rtclient.Object { + return &svcapitypes.DomainName{} +} + +// 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.DomainName), + } +} + +// 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/domain_name/hooks.go b/pkg/resource/domain_name/hooks.go new file mode 100644 index 0000000..ddd7296 --- /dev/null +++ b/pkg/resource/domain_name/hooks.go @@ -0,0 +1,181 @@ +// 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 domain_name + +import ( + "context" + "fmt" + + "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-v2/service/apigateway" + + svcapitypes "github.com/aws-controllers-k8s/apigateway-controller/apis/v1alpha1" + svcapitags "github.com/aws-controllers-k8s/apigateway-controller/pkg/tags" + "github.com/aws-controllers-k8s/apigateway-controller/pkg/util/patch" +) + +func updateDomainNameInput(desired, latest *resource, input *svcsdk.UpdateDomainNameInput, delta *compare.Delta) error { + latestSpec := latest.ko.Spec + desiredSpec := desired.ko.Spec + + var patchSet patch.Set + + handleEndpointConfigurationChanges(&patchSet, &latestSpec, &desiredSpec, delta) + handleRegionalCertificates(&patchSet, &latestSpec, &desiredSpec, delta) + handleOwnershipVerificationCertificate(&patchSet, &latestSpec, &desiredSpec, delta) + handlePolicyChanges(&patchSet, &desiredSpec, delta) + + input.PatchOperations = patchSet.GetPatchOperations() + return nil +} + +func handleEndpointConfigurationChanges(patchSet *patch.Set, latestSpec, desiredSpec *svcapitypes.DomainNameSpec, delta *compare.Delta) { + handleStandardCertificateChanges(patchSet, desiredSpec, delta) + + if delta.DifferentAt("Spec.EndpointConfiguration") && delta.DifferentAt("Spec.EndpointConfiguration.Types") { + var currTypes []*string + if latestSpec.EndpointConfiguration != nil { + currTypes = latestSpec.EndpointConfiguration.Types + } + + var desiredTypes []*string + if desiredSpec.EndpointConfiguration != nil { + desiredTypes = desiredSpec.EndpointConfiguration.Types + } + + patchSet.ForSlice("/endpointConfiguration/types", currTypes, desiredTypes) + } + + handleVPCEndpointIDs(patchSet, latestSpec, desiredSpec) +} + +func handleStandardCertificateChanges(patchSet *patch.Set, desiredSpec *svcapitypes.DomainNameSpec, delta *compare.Delta) { + if delta.DifferentAt("Spec.CertificateARN") { + patchSet.Replace("/certificateArn", desiredSpec.CertificateARN) + } + if delta.DifferentAt("Spec.CertificateName") { + patchSet.Replace("/certificateName", desiredSpec.CertificateName) + } +} + +func handleVPCEndpointIDs(patchSet *patch.Set, latestSpec, desiredSpec *svcapitypes.DomainNameSpec) { + if desiredSpec.EndpointConfiguration.VPCEndpointIDs == nil { + return + } + + var currEndpointIDs []*string + if latestSpec.EndpointConfiguration != nil { + currEndpointIDs = latestSpec.EndpointConfiguration.VPCEndpointIDs + } + + patchSet.ForSlice("/endpointConfiguration/vpcEndpointIds", + currEndpointIDs, + desiredSpec.EndpointConfiguration.VPCEndpointIDs) +} + +func handleRegionalCertificates(patchSet *patch.Set, latestSpec, desiredSpec *svcapitypes.DomainNameSpec, delta *compare.Delta) { + handleFieldWithAddReplaceRemove(patchSet, + "/regionalCertificateArn", + "Spec.RegionalCertificateARN", + desiredSpec.RegionalCertificateARN, + latestSpec.RegionalCertificateARN, + delta) + + handleFieldWithAddReplaceRemove(patchSet, + "/regionalCertificateName", + "Spec.RegionalCertificateName", + desiredSpec.RegionalCertificateName, + latestSpec.RegionalCertificateName, + delta) +} + +func handleOwnershipVerificationCertificate(patchSet *patch.Set, latestSpec, desiredSpec *svcapitypes.DomainNameSpec, delta *compare.Delta) { + handleFieldWithAddReplaceRemove(patchSet, + "/ownershipVerificationCertificateArn", + "Spec.OwnershipVerificationCertificateARN", + desiredSpec.OwnershipVerificationCertificateARN, + latestSpec.OwnershipVerificationCertificateARN, + delta) +} + +func handlePolicyChanges(patchSet *patch.Set, desiredSpec *svcapitypes.DomainNameSpec, delta *compare.Delta) { + if delta.DifferentAt("Spec.Policy") { + patchSet.Replace("/policy", desiredSpec.Policy) + } + if delta.DifferentAt("Spec.SecurityPolicy") { + patchSet.Replace("/securityPolicy", desiredSpec.SecurityPolicy) + } +} + +func handleFieldWithAddReplaceRemove(patchSet *patch.Set, path, deltaPath string, desiredValue, latestValue *string, delta *compare.Delta) { + if !delta.DifferentAt(deltaPath) { + return + } + switch { + case desiredValue == nil: + patchSet.Remove(path, nil) + case latestValue == nil: + patchSet.Add(path, desiredValue) + default: + patchSet.Replace(path, desiredValue) + } +} + +// getDomainNameARN returns the ARN for a given domain name +func (rm *resourceManager) getDomainNameARN(domainName string) string { + // API Gateway domain name ARN format: + // arn:aws:apigateway:region::/domainnames/domain-name + return fmt.Sprintf( + "arn:%s:apigateway:%s::/domainnames/%s", + rm.awsAccountID, + rm.awsRegion, + domainName, + ) +} + +// syncTags synchronizes the tags for a given domain name +func (rm *resourceManager) syncTags( + ctx context.Context, + latest *resource, + desired *resource, +) (err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.syncTags") + defer func() { exit(err) }() + + // Get the ARN of the domain name + resourceARN := rm.getDomainNameARN(*desired.ko.Spec.DomainName) + + // Get the existing tags + existingTags := map[string]*string{} + if latest != nil && latest.ko.Spec.Tags != nil { + existingTags = latest.ko.Spec.Tags + } + + // Get the desired tags + desiredTags := map[string]*string{} + if desired.ko.Spec.Tags != nil { + desiredTags = desired.ko.Spec.Tags + } + + return svcapitags.SyncTags( + ctx, + rm.sdkapi, + rm.metrics, + resourceARN, + desiredTags, + existingTags, + ) +} diff --git a/pkg/resource/domain_name/identifiers.go b/pkg/resource/domain_name/identifiers.go new file mode 100644 index 0000000..9551d45 --- /dev/null +++ b/pkg/resource/domain_name/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 domain_name + +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/domain_name/manager.go b/pkg/resource/domain_name/manager.go new file mode 100644 index 0000000..060a783 --- /dev/null +++ b/pkg/resource/domain_name/manager.go @@ -0,0 +1,412 @@ +// 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 domain_name + +import ( + "context" + "fmt" + "time" + + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + ackcondition "github.com/aws-controllers-k8s/runtime/pkg/condition" + ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" + ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" + ackmetrics "github.com/aws-controllers-k8s/runtime/pkg/metrics" + ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue" + ackrt "github.com/aws-controllers-k8s/runtime/pkg/runtime" + ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" + acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + ackutil "github.com/aws-controllers-k8s/runtime/pkg/util" + "github.com/aws/aws-sdk-go-v2/aws" + svcsdk "github.com/aws/aws-sdk-go-v2/service/apigateway" + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + + svcapitypes "github.com/aws-controllers-k8s/apigateway-controller/apis/v1alpha1" +) + +var ( + _ = ackutil.InStrings + _ = acktags.NewTags() + _ = ackrt.MissingImageTagValue + _ = svcapitypes.DomainName{} +) + +// +kubebuilder:rbac:groups=apigateway.services.k8s.aws,resources=domainnames,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=apigateway.services.k8s.aws,resources=domainnames/status,verbs=get;update;patch + +var lateInitializeFieldNames = []string{} + +// resourceManager is responsible for providing a consistent way to perform +// CRUD operations in a backend AWS service API for Book custom resources. +type resourceManager struct { + // cfg is a copy of the ackcfg.Config object passed on start of the service + // controller + cfg ackcfg.Config + // clientcfg is a copy of the client configuration passed on start of the + // service controller + clientcfg aws.Config + // log refers to the logr.Logger object handling logging for the service + // controller + log logr.Logger + // metrics contains a collection of Prometheus metric objects that the + // service controller and its reconcilers track + metrics *ackmetrics.Metrics + // rr is the Reconciler which can be used for various utility + // functions such as querying for Secret values given a SecretReference + rr acktypes.Reconciler + // awsAccountID is the AWS account identifier that contains the resources + // managed by this resource manager + awsAccountID ackv1alpha1.AWSAccountID + // The AWS Region that this resource manager targets + awsRegion ackv1alpha1.AWSRegion + // sdk is a pointer to the AWS service API client exposed by the + // aws-sdk-go-v2/services/{alias} package. + sdkapi *svcsdk.Client +} + +// concreteResource returns a pointer to a resource from the supplied +// generic AWSResource interface +func (rm *resourceManager) concreteResource( + res acktypes.AWSResource, +) *resource { + // cast the generic interface into a pointer type specific to the concrete + // implementing resource type managed by this resource manager + return res.(*resource) +} + +// ReadOne returns the currently-observed state of the supplied AWSResource in +// the backend AWS service API. +func (rm *resourceManager) ReadOne( + ctx context.Context, + res acktypes.AWSResource, +) (acktypes.AWSResource, error) { + r := rm.concreteResource(res) + if r.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's ReadOne() method received resource with nil CR object") + } + observed, err := rm.sdkFind(ctx, r) + mirrorAWSTags(r, observed) + if err != nil { + if observed != nil { + return rm.onError(observed, err) + } + return rm.onError(r, err) + } + return rm.onSuccess(observed) +} + +// Create attempts to create the supplied AWSResource in the backend AWS +// service API, returning an AWSResource representing the newly-created +// resource +func (rm *resourceManager) Create( + ctx context.Context, + res acktypes.AWSResource, +) (acktypes.AWSResource, error) { + r := rm.concreteResource(res) + if r.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's Create() method received resource with nil CR object") + } + created, err := rm.sdkCreate(ctx, r) + if err != nil { + if created != nil { + return rm.onError(created, err) + } + return rm.onError(r, err) + } + return rm.onSuccess(created) +} + +// Update attempts to mutate the supplied desired AWSResource in the backend AWS +// service API, returning an AWSResource representing the newly-mutated +// resource. +// Note for specialized logic implementers can check to see how the latest +// observed resource differs from the supplied desired state. The +// higher-level reonciler determines whether or not the desired differs +// from the latest observed and decides whether to call the resource +// manager's Update method +func (rm *resourceManager) Update( + ctx context.Context, + resDesired acktypes.AWSResource, + resLatest acktypes.AWSResource, + delta *ackcompare.Delta, +) (acktypes.AWSResource, error) { + desired := rm.concreteResource(resDesired) + latest := rm.concreteResource(resLatest) + if desired.ko == nil || latest.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's Update() method received resource with nil CR object") + } + updated, err := rm.sdkUpdate(ctx, desired, latest, delta) + if err != nil { + if updated != nil { + return rm.onError(updated, err) + } + return rm.onError(latest, err) + } + return rm.onSuccess(updated) +} + +// Delete attempts to destroy the supplied AWSResource in the backend AWS +// service API, returning an AWSResource representing the +// resource being deleted (if delete is asynchronous and takes time) +func (rm *resourceManager) Delete( + ctx context.Context, + res acktypes.AWSResource, +) (acktypes.AWSResource, error) { + r := rm.concreteResource(res) + if r.ko == nil { + // Should never happen... if it does, it's buggy code. + panic("resource manager's Update() method received resource with nil CR object") + } + observed, err := rm.sdkDelete(ctx, r) + if err != nil { + if observed != nil { + return rm.onError(observed, err) + } + return rm.onError(r, err) + } + + return rm.onSuccess(observed) +} + +// ARNFromName returns an AWS Resource Name from a given string name. This +// is useful for constructing ARNs for APIs that require ARNs in their +// GetAttributes operations but all we have (for new CRs at least) is a +// name for the resource +func (rm *resourceManager) ARNFromName(name string) string { + return fmt.Sprintf( + "arn:aws:apigateway:%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.DomainNameStatus == nil { + return false, nil + } + domainNameStatusCandidates := []string{"AVAILABLE", "NOT_AVAILABLE"} + if !ackutil.InStrings(*r.ko.Status.DomainNameStatus, domainNameStatusCandidates) { + 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 map[string]*string + existingTags = r.ko.Spec.Tags + resourceTags, keyOrder := convertToOrderedACKTags(existingTags) + tags := acktags.Merge(resourceTags, defaultTags) + r.ko.Spec.Tags = fromACKTags(tags, keyOrder) + return nil +} + +// FilterAWSTags ignores tags that have keys that start with "aws:" +// is needed to ensure the controller does not attempt to remove +// tags set by AWS. This function needs to be called after each Read +// operation. +// Eg. resources created with cloudformation have tags that cannot be +// removed by an ACK controller +func (rm *resourceManager) FilterSystemTags(res acktypes.AWSResource) { + r := rm.concreteResource(res) + if r == nil || r.ko == nil { + return + } + var existingTags map[string]*string + existingTags = r.ko.Spec.Tags + resourceTags, tagKeyOrder := convertToOrderedACKTags(existingTags) + ignoreSystemTags(resourceTags) + r.ko.Spec.Tags = fromACKTags(resourceTags, tagKeyOrder) +} + +// mirrorAWSTags ensures that AWS tags are included in the desired resource +// if they are present in the latest resource. This will ensure that the +// aws tags are not present in a diff. The logic of the controller will +// ensure these tags aren't patched to the resource in the cluster, and +// will only be present to make sure we don't try to remove these tags. +// +// Although there are a lot of similarities between this function and +// EnsureTags, they are very much different. +// While EnsureTags tries to make sure the resource contains the controller +// tags, mirrowAWSTags tries to make sure tags injected by AWS are mirrored +// from the latest resoruce to the desired resource. +func mirrorAWSTags(a *resource, b *resource) { + if a == nil || a.ko == nil || b == nil || b.ko == nil { + return + } + var existingLatestTags map[string]*string + var existingDesiredTags map[string]*string + existingDesiredTags = a.ko.Spec.Tags + existingLatestTags = b.ko.Spec.Tags + desiredTags, desiredTagKeyOrder := convertToOrderedACKTags(existingDesiredTags) + latestTags, _ := convertToOrderedACKTags(existingLatestTags) + syncAWSTags(desiredTags, latestTags) + a.ko.Spec.Tags = fromACKTags(desiredTags, desiredTagKeyOrder) +} + +// newResourceManager returns a new struct implementing +// acktypes.AWSResourceManager +// This is for AWS-SDK-GO-V2 - Created newResourceManager With AWS sdk-Go-ClientV2 +func newResourceManager( + cfg ackcfg.Config, + clientcfg aws.Config, + log logr.Logger, + metrics *ackmetrics.Metrics, + rr acktypes.Reconciler, + id ackv1alpha1.AWSAccountID, + region ackv1alpha1.AWSRegion, +) (*resourceManager, error) { + return &resourceManager{ + cfg: cfg, + clientcfg: clientcfg, + log: log, + metrics: metrics, + rr: rr, + awsAccountID: id, + awsRegion: region, + sdkapi: svcsdk.NewFromConfig(clientcfg), + }, nil +} + +// onError updates resource conditions and returns updated resource +// it returns nil if no condition is updated. +func (rm *resourceManager) onError( + r *resource, + err error, +) (acktypes.AWSResource, error) { + if r == nil { + return nil, err + } + r1, updated := rm.updateConditions(r, false, err) + if !updated { + return r, err + } + for _, condition := range r1.Conditions() { + if condition.Type == ackv1alpha1.ConditionTypeTerminal && + condition.Status == corev1.ConditionTrue { + // resource is in Terminal condition + // return Terminal error + return r1, ackerr.Terminal + } + } + return r1, err +} + +// onSuccess updates resource conditions and returns updated resource +// it returns the supplied resource if no condition is updated. +func (rm *resourceManager) onSuccess( + r *resource, +) (acktypes.AWSResource, error) { + if r == nil { + return nil, nil + } + r1, updated := rm.updateConditions(r, true, nil) + if !updated { + return r, nil + } + return r1, nil +} diff --git a/pkg/resource/domain_name/manager_factory.go b/pkg/resource/domain_name/manager_factory.go new file mode 100644 index 0000000..5d843d7 --- /dev/null +++ b/pkg/resource/domain_name/manager_factory.go @@ -0,0 +1,100 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +// Code generated by ack-generate. DO NOT EDIT. + +package domain_name + +import ( + "fmt" + "sync" + + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" + ackmetrics "github.com/aws-controllers-k8s/runtime/pkg/metrics" + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/go-logr/logr" + + svcresource "github.com/aws-controllers-k8s/apigateway-controller/pkg/resource" +) + +// resourceManagerFactory produces resourceManager objects. It implements the +// `types.AWSResourceManagerFactory` interface. +type resourceManagerFactory struct { + sync.RWMutex + // rmCache contains resource managers for a particular AWS account ID + rmCache map[string]*resourceManager +} + +// ResourcePrototype returns an AWSResource that resource managers produced by +// this factory will handle +func (f *resourceManagerFactory) ResourceDescriptor() acktypes.AWSResourceDescriptor { + return &resourceDescriptor{} +} + +// ManagerFor returns a resource manager object that can manage resources for a +// supplied AWS account +func (f *resourceManagerFactory) ManagerFor( + cfg ackcfg.Config, + clientcfg aws.Config, + log logr.Logger, + metrics *ackmetrics.Metrics, + rr acktypes.Reconciler, + id ackv1alpha1.AWSAccountID, + region ackv1alpha1.AWSRegion, + roleARN ackv1alpha1.AWSResourceName, +) (acktypes.AWSResourceManager, error) { + // We use the account ID, region, and role ARN to uniquely identify a + // resource manager. This helps us to avoid creating multiple resource + // managers for the same account/region/roleARN combination. + rmId := fmt.Sprintf("%s/%s/%s", id, region, roleARN) + f.RLock() + rm, found := f.rmCache[rmId] + f.RUnlock() + + if found { + return rm, nil + } + + f.Lock() + defer f.Unlock() + + rm, err := newResourceManager(cfg, clientcfg, log, metrics, rr, id, region) + if err != nil { + return nil, err + } + f.rmCache[rmId] = rm + return rm, nil +} + +// IsAdoptable returns true if the resource is able to be adopted +func (f *resourceManagerFactory) IsAdoptable() bool { + return true +} + +// RequeueOnSuccessSeconds returns true if the resource should be requeued after specified seconds +// Default is false which means resource will not be requeued after success. +func (f *resourceManagerFactory) RequeueOnSuccessSeconds() int { + return 0 +} + +func newResourceManagerFactory() *resourceManagerFactory { + return &resourceManagerFactory{ + rmCache: map[string]*resourceManager{}, + } +} + +func init() { + svcresource.RegisterManagerFactory(newResourceManagerFactory()) +} diff --git a/pkg/resource/domain_name/references.go b/pkg/resource/domain_name/references.go new file mode 100644 index 0000000..7f1c830 --- /dev/null +++ b/pkg/resource/domain_name/references.go @@ -0,0 +1,57 @@ +// 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 domain_name + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/client" + + acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" + + svcapitypes "github.com/aws-controllers-k8s/apigateway-controller/apis/v1alpha1" +) + +// ClearResolvedReferences removes any reference values that were made +// concrete in the spec. It returns a copy of the input AWSResource which +// contains the original *Ref values, but none of their respective concrete +// values. +func (rm *resourceManager) ClearResolvedReferences(res acktypes.AWSResource) acktypes.AWSResource { + ko := rm.concreteResource(res).ko.DeepCopy() + + return &resource{ko} +} + +// ResolveReferences finds if there are any Reference field(s) present +// inside AWSResource passed in the parameter and attempts to resolve those +// reference field(s) into their respective target field(s). It returns a +// copy of the input AWSResource with resolved reference(s), a boolean which +// is set to true if the resource contains any references (regardless of if +// they are resolved successfully) and an error if the passed AWSResource's +// reference field(s) could not be resolved. +func (rm *resourceManager) ResolveReferences( + ctx context.Context, + apiReader client.Reader, + res acktypes.AWSResource, +) (acktypes.AWSResource, bool, error) { + return res, false, nil +} + +// validateReferenceFields validates the reference field and corresponding +// identifier field. +func validateReferenceFields(ko *svcapitypes.DomainName) error { + return nil +} diff --git a/pkg/resource/domain_name/resource.go b/pkg/resource/domain_name/resource.go new file mode 100644 index 0000000..13bb897 --- /dev/null +++ b/pkg/resource/domain_name/resource.go @@ -0,0 +1,124 @@ +// 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 domain_name + +import ( + "fmt" + + 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" + "github.com/aws/aws-sdk-go-v2/aws" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + rtclient "sigs.k8s.io/controller-runtime/pkg/client" + + svcapitypes "github.com/aws-controllers-k8s/apigateway-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.DomainName +} + +// Identifiers returns an AWSResourceIdentifiers object containing various +// identifying information, including the AWS account ID that owns the +// resource, the resource's AWS Resource Name (ARN) +func (r *resource) Identifiers() acktypes.AWSResourceIdentifiers { + return &resourceIdentifiers{r.ko.Status.ACKResourceMetadata} +} + +// IsBeingDeleted returns true if the Kubernetes resource has a non-zero +// deletion timestamp +func (r *resource) IsBeingDeleted() bool { + return !r.ko.DeletionTimestamp.IsZero() +} + +// RuntimeObject returns the Kubernetes apimachinery/runtime representation of +// the AWSResource +func (r *resource) RuntimeObject() rtclient.Object { + return r.ko +} + +// MetaObject returns the Kubernetes apimachinery/apis/meta/v1.Object +// representation of the AWSResource +func (r *resource) MetaObject() metav1.Object { + return r.ko.GetObjectMeta() +} + +// Conditions returns the ACK Conditions collection for the AWSResource +func (r *resource) Conditions() []*ackv1alpha1.Condition { + return r.ko.Status.Conditions +} + +// ReplaceConditions sets the Conditions status field for the resource +func (r *resource) ReplaceConditions(conditions []*ackv1alpha1.Condition) { + r.ko.Status.Conditions = conditions +} + +// SetObjectMeta sets the ObjectMeta field for the resource +func (r *resource) SetObjectMeta(meta metav1.ObjectMeta) { + r.ko.ObjectMeta = meta +} + +// SetStatus will set the Status field for the resource +func (r *resource) SetStatus(desired acktypes.AWSResource) { + r.ko.Status = desired.(*resource).ko.Status +} + +// SetIdentifiers sets the Spec or Status field that is referenced as the unique +// resource identifier +func (r *resource) SetIdentifiers(identifier *ackv1alpha1.AWSIdentifiers) error { + if identifier.NameOrID == "" { + return ackerrors.MissingNameIdentifier + } + r.ko.Spec.DomainName = &identifier.NameOrID + + f1, f1ok := identifier.AdditionalKeys["domainNameID"] + if f1ok { + r.ko.Status.DomainNameID = aws.String(f1) + } + + return nil +} + +// PopulateResourceFromAnnotation populates the fields passed from adoption annotation +func (r *resource) PopulateResourceFromAnnotation(fields map[string]string) error { + tmp, ok := fields["domainName"] + if !ok { + return ackerrors.NewTerminalError(fmt.Errorf("required field missing: domainName")) + } + r.ko.Spec.DomainName = &tmp + + f1, f1ok := fields["domainNameID"] + if f1ok { + r.ko.Status.DomainNameID = aws.String(f1) + } + + 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/domain_name/sdk.go b/pkg/resource/domain_name/sdk.go new file mode 100644 index 0000000..157e7ac --- /dev/null +++ b/pkg/resource/domain_name/sdk.go @@ -0,0 +1,829 @@ +// 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 domain_name + +import ( + "context" + "errors" + "fmt" + "reflect" + "strings" + + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" + ackcondition "github.com/aws-controllers-k8s/runtime/pkg/condition" + ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" + ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue" + ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log" + "github.com/aws/aws-sdk-go-v2/aws" + svcsdk "github.com/aws/aws-sdk-go-v2/service/apigateway" + svcsdktypes "github.com/aws/aws-sdk-go-v2/service/apigateway/types" + smithy "github.com/aws/smithy-go" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + svcapitypes "github.com/aws-controllers-k8s/apigateway-controller/apis/v1alpha1" +) + +// Hack to avoid import errors during build... +var ( + _ = &metav1.Time{} + _ = strings.ToLower("") + _ = &svcsdk.Client{} + _ = &svcapitypes.DomainName{} + _ = ackv1alpha1.AWSAccountID("") + _ = &ackerr.NotFound + _ = &ackcondition.NotManagedMessage + _ = &reflect.Value{} + _ = fmt.Sprintf("") + _ = &ackrequeue.NoRequeue{} + _ = &aws.Config{} +) + +// sdkFind returns SDK-specific information about a supplied resource +func (rm *resourceManager) sdkFind( + ctx context.Context, + r *resource, +) (latest *resource, err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.sdkFind") + defer func() { + exit(err) + }() + // If any required fields in the input shape are missing, AWS resource is + // not created yet. Return NotFound here to indicate to callers that the + // resource isn't yet created. + if rm.requiredFieldsMissingFromReadOneInput(r) { + return nil, ackerr.NotFound + } + + input, err := rm.newDescribeRequestPayload(r) + if err != nil { + return nil, err + } + + var resp *svcsdk.GetDomainNameOutput + resp, err = rm.sdkapi.GetDomainName(ctx, input) + rm.metrics.RecordAPICall("READ_ONE", "GetDomainName", err) + if err != nil { + var awsErr smithy.APIError + if errors.As(err, &awsErr) && awsErr.ErrorCode() == "NotFoundException" { + 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() + + if resp.CertificateArn != nil { + ko.Spec.CertificateARN = resp.CertificateArn + } else { + ko.Spec.CertificateARN = nil + } + if resp.CertificateName != nil { + ko.Spec.CertificateName = resp.CertificateName + } else { + ko.Spec.CertificateName = nil + } + if resp.CertificateUploadDate != nil { + ko.Status.CertificateUploadDate = &metav1.Time{*resp.CertificateUploadDate} + } else { + ko.Status.CertificateUploadDate = nil + } + if resp.DistributionDomainName != nil { + ko.Status.DistributionDomainName = resp.DistributionDomainName + } else { + ko.Status.DistributionDomainName = nil + } + if resp.DistributionHostedZoneId != nil { + ko.Status.DistributionHostedZoneID = resp.DistributionHostedZoneId + } else { + ko.Status.DistributionHostedZoneID = nil + } + if resp.DomainName != nil { + ko.Spec.DomainName = resp.DomainName + } else { + ko.Spec.DomainName = nil + } + if ko.Status.ACKResourceMetadata == nil { + ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{} + } + if resp.DomainNameArn != nil { + arn := ackv1alpha1.AWSResourceName(*resp.DomainNameArn) + ko.Status.ACKResourceMetadata.ARN = &arn + } + if resp.DomainNameId != nil { + ko.Status.DomainNameID = resp.DomainNameId + } else { + ko.Status.DomainNameID = nil + } + if resp.DomainNameStatus != "" { + ko.Status.DomainNameStatus = aws.String(string(resp.DomainNameStatus)) + } else { + ko.Status.DomainNameStatus = nil + } + if resp.DomainNameStatusMessage != nil { + ko.Status.DomainNameStatusMessage = resp.DomainNameStatusMessage + } else { + ko.Status.DomainNameStatusMessage = nil + } + if resp.EndpointConfiguration != nil { + f10 := &svcapitypes.EndpointConfiguration{} + if resp.EndpointConfiguration.Types != nil { + f10f0 := []*string{} + for _, f10f0iter := range resp.EndpointConfiguration.Types { + var f10f0elem *string + f10f0elem = aws.String(string(f10f0iter)) + f10f0 = append(f10f0, f10f0elem) + } + f10.Types = f10f0 + } + if resp.EndpointConfiguration.VpcEndpointIds != nil { + f10.VPCEndpointIDs = aws.StringSlice(resp.EndpointConfiguration.VpcEndpointIds) + } + ko.Spec.EndpointConfiguration = f10 + } else { + ko.Spec.EndpointConfiguration = nil + } + if resp.ManagementPolicy != nil { + ko.Status.ManagementPolicy = resp.ManagementPolicy + } else { + ko.Status.ManagementPolicy = nil + } + if resp.MutualTlsAuthentication != nil { + f12 := &svcapitypes.MutualTLSAuthenticationInput{} + if resp.MutualTlsAuthentication.TruststoreUri != nil { + f12.TruststoreURI = resp.MutualTlsAuthentication.TruststoreUri + } + if resp.MutualTlsAuthentication.TruststoreVersion != nil { + f12.TruststoreVersion = resp.MutualTlsAuthentication.TruststoreVersion + } + ko.Spec.MutualTLSAuthentication = f12 + } else { + ko.Spec.MutualTLSAuthentication = nil + } + if resp.OwnershipVerificationCertificateArn != nil { + ko.Spec.OwnershipVerificationCertificateARN = resp.OwnershipVerificationCertificateArn + } else { + ko.Spec.OwnershipVerificationCertificateARN = nil + } + if resp.Policy != nil { + ko.Spec.Policy = resp.Policy + } else { + ko.Spec.Policy = nil + } + if resp.RegionalCertificateArn != nil { + ko.Spec.RegionalCertificateARN = resp.RegionalCertificateArn + } else { + ko.Spec.RegionalCertificateARN = nil + } + if resp.RegionalCertificateName != nil { + ko.Spec.RegionalCertificateName = resp.RegionalCertificateName + } else { + ko.Spec.RegionalCertificateName = nil + } + if resp.RegionalDomainName != nil { + ko.Status.RegionalDomainName = resp.RegionalDomainName + } else { + ko.Status.RegionalDomainName = nil + } + if resp.RegionalHostedZoneId != nil { + ko.Status.RegionalHostedZoneID = resp.RegionalHostedZoneId + } else { + ko.Status.RegionalHostedZoneID = nil + } + if resp.SecurityPolicy != "" { + ko.Spec.SecurityPolicy = aws.String(string(resp.SecurityPolicy)) + } else { + ko.Spec.SecurityPolicy = nil + } + if resp.Tags != nil { + ko.Spec.Tags = aws.StringMap(resp.Tags) + } else { + ko.Spec.Tags = nil + } + + rm.setStatusDefaults(ko) + return &resource{ko}, nil +} + +// requiredFieldsMissingFromReadOneInput returns true if there are any fields +// for the ReadOne Input shape that are required but not present in the +// resource's Spec or Status +func (rm *resourceManager) requiredFieldsMissingFromReadOneInput( + r *resource, +) bool { + return r.ko.Spec.DomainName == nil + +} + +// newDescribeRequestPayload returns SDK-specific struct for the HTTP request +// payload of the Describe API call for the resource +func (rm *resourceManager) newDescribeRequestPayload( + r *resource, +) (*svcsdk.GetDomainNameInput, error) { + res := &svcsdk.GetDomainNameInput{} + + if r.ko.Spec.DomainName != nil { + res.DomainName = r.ko.Spec.DomainName + } + if r.ko.Status.DomainNameID != nil { + res.DomainNameId = r.ko.Status.DomainNameID + } + + 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.CreateDomainNameOutput + _ = resp + resp, err = rm.sdkapi.CreateDomainName(ctx, input) + rm.metrics.RecordAPICall("CREATE", "CreateDomainName", 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.CertificateArn != nil { + ko.Spec.CertificateARN = resp.CertificateArn + } else { + ko.Spec.CertificateARN = nil + } + if resp.CertificateName != nil { + ko.Spec.CertificateName = resp.CertificateName + } else { + ko.Spec.CertificateName = nil + } + if resp.CertificateUploadDate != nil { + ko.Status.CertificateUploadDate = &metav1.Time{*resp.CertificateUploadDate} + } else { + ko.Status.CertificateUploadDate = nil + } + if resp.DistributionDomainName != nil { + ko.Status.DistributionDomainName = resp.DistributionDomainName + } else { + ko.Status.DistributionDomainName = nil + } + if resp.DistributionHostedZoneId != nil { + ko.Status.DistributionHostedZoneID = resp.DistributionHostedZoneId + } else { + ko.Status.DistributionHostedZoneID = nil + } + if resp.DomainName != nil { + ko.Spec.DomainName = resp.DomainName + } else { + ko.Spec.DomainName = nil + } + if ko.Status.ACKResourceMetadata == nil { + ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{} + } + if resp.DomainNameArn != nil { + arn := ackv1alpha1.AWSResourceName(*resp.DomainNameArn) + ko.Status.ACKResourceMetadata.ARN = &arn + } + if resp.DomainNameId != nil { + ko.Status.DomainNameID = resp.DomainNameId + } else { + ko.Status.DomainNameID = nil + } + if resp.DomainNameStatus != "" { + ko.Status.DomainNameStatus = aws.String(string(resp.DomainNameStatus)) + } else { + ko.Status.DomainNameStatus = nil + } + if resp.DomainNameStatusMessage != nil { + ko.Status.DomainNameStatusMessage = resp.DomainNameStatusMessage + } else { + ko.Status.DomainNameStatusMessage = nil + } + if resp.EndpointConfiguration != nil { + f10 := &svcapitypes.EndpointConfiguration{} + if resp.EndpointConfiguration.Types != nil { + f10f0 := []*string{} + for _, f10f0iter := range resp.EndpointConfiguration.Types { + var f10f0elem *string + f10f0elem = aws.String(string(f10f0iter)) + f10f0 = append(f10f0, f10f0elem) + } + f10.Types = f10f0 + } + if resp.EndpointConfiguration.VpcEndpointIds != nil { + f10.VPCEndpointIDs = aws.StringSlice(resp.EndpointConfiguration.VpcEndpointIds) + } + ko.Spec.EndpointConfiguration = f10 + } else { + ko.Spec.EndpointConfiguration = nil + } + if resp.ManagementPolicy != nil { + ko.Status.ManagementPolicy = resp.ManagementPolicy + } else { + ko.Status.ManagementPolicy = nil + } + if resp.MutualTlsAuthentication != nil { + f12 := &svcapitypes.MutualTLSAuthenticationInput{} + if resp.MutualTlsAuthentication.TruststoreUri != nil { + f12.TruststoreURI = resp.MutualTlsAuthentication.TruststoreUri + } + if resp.MutualTlsAuthentication.TruststoreVersion != nil { + f12.TruststoreVersion = resp.MutualTlsAuthentication.TruststoreVersion + } + ko.Spec.MutualTLSAuthentication = f12 + } else { + ko.Spec.MutualTLSAuthentication = nil + } + if resp.OwnershipVerificationCertificateArn != nil { + ko.Spec.OwnershipVerificationCertificateARN = resp.OwnershipVerificationCertificateArn + } else { + ko.Spec.OwnershipVerificationCertificateARN = nil + } + if resp.Policy != nil { + ko.Spec.Policy = resp.Policy + } else { + ko.Spec.Policy = nil + } + if resp.RegionalCertificateArn != nil { + ko.Spec.RegionalCertificateARN = resp.RegionalCertificateArn + } else { + ko.Spec.RegionalCertificateARN = nil + } + if resp.RegionalCertificateName != nil { + ko.Spec.RegionalCertificateName = resp.RegionalCertificateName + } else { + ko.Spec.RegionalCertificateName = nil + } + if resp.RegionalDomainName != nil { + ko.Status.RegionalDomainName = resp.RegionalDomainName + } else { + ko.Status.RegionalDomainName = nil + } + if resp.RegionalHostedZoneId != nil { + ko.Status.RegionalHostedZoneID = resp.RegionalHostedZoneId + } else { + ko.Status.RegionalHostedZoneID = nil + } + if resp.SecurityPolicy != "" { + ko.Spec.SecurityPolicy = aws.String(string(resp.SecurityPolicy)) + } else { + ko.Spec.SecurityPolicy = nil + } + if resp.Tags != nil { + ko.Spec.Tags = aws.StringMap(resp.Tags) + } 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.CreateDomainNameInput, error) { + res := &svcsdk.CreateDomainNameInput{} + + if r.ko.Spec.CertificateARN != nil { + res.CertificateArn = r.ko.Spec.CertificateARN + } + if r.ko.Spec.CertificateBody != nil { + res.CertificateBody = r.ko.Spec.CertificateBody + } + if r.ko.Spec.CertificateChain != nil { + res.CertificateChain = r.ko.Spec.CertificateChain + } + if r.ko.Spec.CertificateName != nil { + res.CertificateName = r.ko.Spec.CertificateName + } + if r.ko.Spec.CertificatePrivateKey != nil { + res.CertificatePrivateKey = r.ko.Spec.CertificatePrivateKey + } + if r.ko.Spec.DomainName != nil { + res.DomainName = r.ko.Spec.DomainName + } + if r.ko.Spec.EndpointConfiguration != nil { + f6 := &svcsdktypes.EndpointConfiguration{} + if r.ko.Spec.EndpointConfiguration.Types != nil { + f6f0 := []svcsdktypes.EndpointType{} + for _, f6f0iter := range r.ko.Spec.EndpointConfiguration.Types { + var f6f0elem string + f6f0elem = string(*f6f0iter) + f6f0 = append(f6f0, svcsdktypes.EndpointType(f6f0elem)) + } + f6.Types = f6f0 + } + if r.ko.Spec.EndpointConfiguration.VPCEndpointIDs != nil { + f6.VpcEndpointIds = aws.ToStringSlice(r.ko.Spec.EndpointConfiguration.VPCEndpointIDs) + } + res.EndpointConfiguration = f6 + } + if r.ko.Spec.MutualTLSAuthentication != nil { + f7 := &svcsdktypes.MutualTlsAuthenticationInput{} + if r.ko.Spec.MutualTLSAuthentication.TruststoreURI != nil { + f7.TruststoreUri = r.ko.Spec.MutualTLSAuthentication.TruststoreURI + } + if r.ko.Spec.MutualTLSAuthentication.TruststoreVersion != nil { + f7.TruststoreVersion = r.ko.Spec.MutualTLSAuthentication.TruststoreVersion + } + res.MutualTlsAuthentication = f7 + } + if r.ko.Spec.OwnershipVerificationCertificateARN != nil { + res.OwnershipVerificationCertificateArn = r.ko.Spec.OwnershipVerificationCertificateARN + } + if r.ko.Spec.Policy != nil { + res.Policy = r.ko.Spec.Policy + } + if r.ko.Spec.RegionalCertificateARN != nil { + res.RegionalCertificateArn = r.ko.Spec.RegionalCertificateARN + } + if r.ko.Spec.RegionalCertificateName != nil { + res.RegionalCertificateName = r.ko.Spec.RegionalCertificateName + } + if r.ko.Spec.SecurityPolicy != nil { + res.SecurityPolicy = svcsdktypes.SecurityPolicy(*r.ko.Spec.SecurityPolicy) + } + if r.ko.Spec.Tags != nil { + res.Tags = aws.ToStringMap(r.ko.Spec.Tags) + } + + return res, nil +} + +// sdkUpdate patches the supplied resource in the backend AWS service API and +// returns a new resource with updated fields. +func (rm *resourceManager) sdkUpdate( + ctx context.Context, + desired *resource, + latest *resource, + delta *ackcompare.Delta, +) (updated *resource, err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.sdkUpdate") + defer func() { + exit(err) + }() + if delta.DifferentAt("Spec.Tags") { + if err := rm.syncTags(ctx, latest, desired); err != nil { + return nil, err + } + } + if !delta.DifferentExcept("Spec.Tags") { + return desired, nil + } + input, err := rm.newUpdateRequestPayload(ctx, desired, delta) + if err != nil { + return nil, err + } + if err := updateDomainNameInput(desired, latest, input, delta); err != nil { + return nil, err + } + + var resp *svcsdk.UpdateDomainNameOutput + _ = resp + resp, err = rm.sdkapi.UpdateDomainName(ctx, input) + rm.metrics.RecordAPICall("UPDATE", "UpdateDomainName", 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.CertificateArn != nil { + ko.Spec.CertificateARN = resp.CertificateArn + } else { + ko.Spec.CertificateARN = nil + } + if resp.CertificateName != nil { + ko.Spec.CertificateName = resp.CertificateName + } else { + ko.Spec.CertificateName = nil + } + if resp.CertificateUploadDate != nil { + ko.Status.CertificateUploadDate = &metav1.Time{*resp.CertificateUploadDate} + } else { + ko.Status.CertificateUploadDate = nil + } + if resp.DistributionDomainName != nil { + ko.Status.DistributionDomainName = resp.DistributionDomainName + } else { + ko.Status.DistributionDomainName = nil + } + if resp.DistributionHostedZoneId != nil { + ko.Status.DistributionHostedZoneID = resp.DistributionHostedZoneId + } else { + ko.Status.DistributionHostedZoneID = nil + } + if resp.DomainName != nil { + ko.Spec.DomainName = resp.DomainName + } else { + ko.Spec.DomainName = nil + } + if ko.Status.ACKResourceMetadata == nil { + ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{} + } + if resp.DomainNameArn != nil { + arn := ackv1alpha1.AWSResourceName(*resp.DomainNameArn) + ko.Status.ACKResourceMetadata.ARN = &arn + } + if resp.DomainNameId != nil { + ko.Status.DomainNameID = resp.DomainNameId + } else { + ko.Status.DomainNameID = nil + } + if resp.DomainNameStatus != "" { + ko.Status.DomainNameStatus = aws.String(string(resp.DomainNameStatus)) + } else { + ko.Status.DomainNameStatus = nil + } + if resp.DomainNameStatusMessage != nil { + ko.Status.DomainNameStatusMessage = resp.DomainNameStatusMessage + } else { + ko.Status.DomainNameStatusMessage = nil + } + if resp.EndpointConfiguration != nil { + f10 := &svcapitypes.EndpointConfiguration{} + if resp.EndpointConfiguration.Types != nil { + f10f0 := []*string{} + for _, f10f0iter := range resp.EndpointConfiguration.Types { + var f10f0elem *string + f10f0elem = aws.String(string(f10f0iter)) + f10f0 = append(f10f0, f10f0elem) + } + f10.Types = f10f0 + } + if resp.EndpointConfiguration.VpcEndpointIds != nil { + f10.VPCEndpointIDs = aws.StringSlice(resp.EndpointConfiguration.VpcEndpointIds) + } + ko.Spec.EndpointConfiguration = f10 + } else { + ko.Spec.EndpointConfiguration = nil + } + if resp.ManagementPolicy != nil { + ko.Status.ManagementPolicy = resp.ManagementPolicy + } else { + ko.Status.ManagementPolicy = nil + } + if resp.MutualTlsAuthentication != nil { + f12 := &svcapitypes.MutualTLSAuthenticationInput{} + if resp.MutualTlsAuthentication.TruststoreUri != nil { + f12.TruststoreURI = resp.MutualTlsAuthentication.TruststoreUri + } + if resp.MutualTlsAuthentication.TruststoreVersion != nil { + f12.TruststoreVersion = resp.MutualTlsAuthentication.TruststoreVersion + } + ko.Spec.MutualTLSAuthentication = f12 + } else { + ko.Spec.MutualTLSAuthentication = nil + } + if resp.OwnershipVerificationCertificateArn != nil { + ko.Spec.OwnershipVerificationCertificateARN = resp.OwnershipVerificationCertificateArn + } else { + ko.Spec.OwnershipVerificationCertificateARN = nil + } + if resp.Policy != nil { + ko.Spec.Policy = resp.Policy + } else { + ko.Spec.Policy = nil + } + if resp.RegionalCertificateArn != nil { + ko.Spec.RegionalCertificateARN = resp.RegionalCertificateArn + } else { + ko.Spec.RegionalCertificateARN = nil + } + if resp.RegionalCertificateName != nil { + ko.Spec.RegionalCertificateName = resp.RegionalCertificateName + } else { + ko.Spec.RegionalCertificateName = nil + } + if resp.RegionalDomainName != nil { + ko.Status.RegionalDomainName = resp.RegionalDomainName + } else { + ko.Status.RegionalDomainName = nil + } + if resp.RegionalHostedZoneId != nil { + ko.Status.RegionalHostedZoneID = resp.RegionalHostedZoneId + } else { + ko.Status.RegionalHostedZoneID = nil + } + if resp.SecurityPolicy != "" { + ko.Spec.SecurityPolicy = aws.String(string(resp.SecurityPolicy)) + } else { + ko.Spec.SecurityPolicy = nil + } + if resp.Tags != nil { + ko.Spec.Tags = aws.StringMap(resp.Tags) + } else { + ko.Spec.Tags = nil + } + + rm.setStatusDefaults(ko) + return &resource{ko}, nil +} + +// newUpdateRequestPayload returns an SDK-specific struct for the HTTP request +// payload of the Update API call for the resource +func (rm *resourceManager) newUpdateRequestPayload( + ctx context.Context, + r *resource, + delta *ackcompare.Delta, +) (*svcsdk.UpdateDomainNameInput, error) { + res := &svcsdk.UpdateDomainNameInput{} + + if r.ko.Spec.DomainName != nil { + res.DomainName = r.ko.Spec.DomainName + } + if r.ko.Status.DomainNameID != nil { + res.DomainNameId = r.ko.Status.DomainNameID + } + + return res, nil +} + +// sdkDelete deletes the supplied resource in the backend AWS service API +func (rm *resourceManager) sdkDelete( + ctx context.Context, + r *resource, +) (latest *resource, err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.sdkDelete") + defer func() { + exit(err) + }() + input, err := rm.newDeleteRequestPayload(r) + if err != nil { + return nil, err + } + var resp *svcsdk.DeleteDomainNameOutput + _ = resp + resp, err = rm.sdkapi.DeleteDomainName(ctx, input) + rm.metrics.RecordAPICall("DELETE", "DeleteDomainName", 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.DeleteDomainNameInput, error) { + res := &svcsdk.DeleteDomainNameInput{} + + if r.ko.Spec.DomainName != nil { + res.DomainName = r.ko.Spec.DomainName + } + if r.ko.Status.DomainNameID != nil { + res.DomainNameId = r.ko.Status.DomainNameID + } + + return res, nil +} + +// setStatusDefaults sets default properties into supplied custom resource +func (rm *resourceManager) setStatusDefaults( + ko *svcapitypes.DomainName, +) { + 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 { + if err == nil { + return false + } + + var terminalErr smithy.APIError + if !errors.As(err, &terminalErr) { + return false + } + switch terminalErr.ErrorCode() { + case "BadRequestException", + "InvalidParameter": + return true + default: + return false + } +} diff --git a/pkg/resource/domain_name/tags.go b/pkg/resource/domain_name/tags.go new file mode 100644 index 0000000..893cd03 --- /dev/null +++ b/pkg/resource/domain_name/tags.go @@ -0,0 +1,108 @@ +// 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 domain_name + +import ( + "slices" + "strings" + + acktags "github.com/aws-controllers-k8s/runtime/pkg/tags" + + svcapitypes "github.com/aws-controllers-k8s/apigateway-controller/apis/v1alpha1" +) + +var ( + _ = svcapitypes.DomainName{} + _ = acktags.NewTags() + ACKSystemTags = []string{"services.k8s.aws/namespace", "services.k8s.aws/controller-version"} +) + +// convertToOrderedACKTags converts the tags parameter into 'acktags.Tags' shape. +// This method helps in creating the hub(acktags.Tags) for merging +// default controller tags with existing resource tags. It also returns a slice +// of keys maintaining the original key Order when the tags are a list +func convertToOrderedACKTags(tags map[string]*string) (acktags.Tags, []string) { + result := acktags.NewTags() + keyOrder := []string{} + + if len(tags) == 0 { + return result, keyOrder + } + for k, v := range tags { + if v == nil { + result[k] = "" + } else { + result[k] = *v + } + } + + return result, keyOrder +} + +// fromACKTags converts the tags parameter into map[string]*string shape. +// This method helps in setting the tags back inside AWSResource after merging +// default controller tags with existing resource tags. When a list, +// it maintains the order from original +func fromACKTags(tags acktags.Tags, keyOrder []string) map[string]*string { + result := map[string]*string{} + + _ = keyOrder + for k, v := range tags { + result[k] = &v + } + + return result +} + +// ignoreSystemTags ignores tags that have keys that start with "aws:" +// and ACKSystemTags, to avoid patching them to the resourceSpec. +// Eg. resources created with cloudformation have tags that cannot be +// removed by an ACK controller +func ignoreSystemTags(tags acktags.Tags) { + for k := range tags { + if strings.HasPrefix(k, "aws:") || + slices.Contains(ACKSystemTags, k) { + delete(tags, k) + } + } +} + +// syncAWSTags ensures AWS-managed tags (prefixed with "aws:") from the latest resource state +// are preserved in the desired state. This prevents the controller from attempting to +// modify AWS-managed tags, which would result in an error. +// +// AWS-managed tags are automatically added by AWS services (e.g., CloudFormation, Service Catalog) +// and cannot be modified or deleted through normal tag operations. Common examples include: +// - aws:cloudformation:stack-name +// - aws:servicecatalog:productArn +// +// Parameters: +// - a: The target Tags map to be updated (typically desired state) +// - b: The source Tags map containing AWS-managed tags (typically latest state) +// +// Example: +// +// latest := Tags{"aws:cloudformation:stack-name": "my-stack", "environment": "prod"} +// desired := Tags{"environment": "dev"} +// SyncAWSTags(desired, latest) +// desired now contains {"aws:cloudformation:stack-name": "my-stack", "environment": "dev"} +func syncAWSTags(a acktags.Tags, b acktags.Tags) { + for k := range b { + if strings.HasPrefix(k, "aws:") { + a[k] = b[k] + } + } +} diff --git a/pkg/resource/integration/sdk.go b/pkg/resource/integration/sdk.go index 97d86a4..2c44fae 100644 --- a/pkg/resource/integration/sdk.go +++ b/pkg/resource/integration/sdk.go @@ -397,7 +397,7 @@ func (rm *resourceManager) newCreateRequestPayload( if r.ko.Spec.TimeoutInMillis != nil { timeoutInMillisCopy0 := *r.ko.Spec.TimeoutInMillis if timeoutInMillisCopy0 > math.MaxInt32 || timeoutInMillisCopy0 < math.MinInt32 { - return nil, fmt.Errorf("error: field timeoutInMillis is of type int32") + return nil, fmt.Errorf("error: field TimeoutInMillis is of type int32") } timeoutInMillisCopy := int32(timeoutInMillisCopy0) res.TimeoutInMillis = &timeoutInMillisCopy diff --git a/templates/hooks/domain_name/sdk_update_post_build_request.go.tpl b/templates/hooks/domain_name/sdk_update_post_build_request.go.tpl new file mode 100644 index 0000000..fafd43d --- /dev/null +++ b/templates/hooks/domain_name/sdk_update_post_build_request.go.tpl @@ -0,0 +1,3 @@ + if err := updateDomainNameInput(desired, latest, input, delta); err != nil { + return nil, err + } \ No newline at end of file diff --git a/templates/hooks/domain_name/sdk_update_pre_build_request.go.tpl b/templates/hooks/domain_name/sdk_update_pre_build_request.go.tpl new file mode 100644 index 0000000..f6c086e --- /dev/null +++ b/templates/hooks/domain_name/sdk_update_pre_build_request.go.tpl @@ -0,0 +1,8 @@ + if delta.DifferentAt("Spec.Tags") { + if err := rm.syncTags(ctx, latest, desired); err != nil { + return nil, err + } + } + if !delta.DifferentExcept("Spec.Tags") { + return desired, nil + } \ No newline at end of file diff --git a/test/e2e/resources/domain_name_simple.yaml b/test/e2e/resources/domain_name_simple.yaml new file mode 100644 index 0000000..55a7a35 --- /dev/null +++ b/test/e2e/resources/domain_name_simple.yaml @@ -0,0 +1,14 @@ +apiVersion: apigateway.services.k8s.aws/v1alpha1 +kind: DomainName +metadata: + name: $DOMAIN_RES_NAME +spec: + domainName: $DOMAIN_NAME + regionalCertificateARN: $CERT_ARN + endpointConfiguration: + types: + - REGIONAL + securityPolicy: TLS_1_2 + tags: + Environment: test + Team: ack-test \ No newline at end of file diff --git a/test/e2e/tests/domain_name_test.py b/test/e2e/tests/domain_name_test.py new file mode 100644 index 0000000..80a2d50 --- /dev/null +++ b/test/e2e/tests/domain_name_test.py @@ -0,0 +1,69 @@ +# 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 API Gateway DomainName resource +""" + +import logging +import time + +import boto3 +import pytest + +from acktest.k8s import resource as k8s +from acktest.k8s import condition +from acktest.resources import random_suffix_name + +from e2e import service_marker, load_apigateway_resource, CRD_GROUP, CRD_VERSION +from e2e.bootstrap_resources import get_bootstrap_resources +from e2e.replacement_values import REPLACEMENT_VALUES + +CREATE_WAIT_AFTER_SECONDS = 60 + +@service_marker +@pytest.mark.canary +class TestDomainName: + + def test_crud_domain_name(self): + test_data = REPLACEMENT_VALUES.copy() + domain_name = random_suffix_name("test-domain", 24) + test_data["DOMAIN_RES_NAME"] = domain_name + test_data["DOMAIN_NAME"] = f"{domain_name}.example.com" + test_data["CERT_ARN"] = "arn:aws:acm:us-west-2:123456789012:certificate/invalid-cert-id" + + # Create a custom resource reference + domain_name_ref = k8s.CustomResourceReference( + CRD_GROUP, CRD_VERSION, "domainnames", + domain_name, namespace="default", + ) + + # Load the resource data + domain_name_data = load_apigateway_resource( + "domain_name_simple", + additional_replacements=test_data, + ) + + logging.debug(f"domain name ref is {domain_name_ref}, data: {domain_name_data}") + + # Attempting Create expecting BadRequestException + k8s.create_custom_resource(domain_name_ref, domain_name_data) + k8s.wait_resource_consumed_by_controller(domain_name_ref) + condition.assert_type_status(domain_name_ref, condition.CONDITION_TYPE_TERMINAL) + + # Verify terminal condition with expected error message + expected_msg = "BadRequestException" + terminal_condition = k8s.get_resource_condition(domain_name_ref, condition.CONDITION_TYPE_TERMINAL) + assert expected_msg in terminal_condition['message'] + + # Clean up + k8s.delete_custom_resource(domain_name_ref) \ No newline at end of file From 229355403b11025769bba59b477748807b7620ba Mon Sep 17 00:00:00 2001 From: Arush Sharma Date: Wed, 16 Jul 2025 10:09:14 -0700 Subject: [PATCH 2/2] fix tamer --- apis/v1alpha1/ack-generate-metadata.yaml | 4 ++-- apis/v1alpha1/generator.yaml | 6 ------ generator.yaml | 6 ------ pkg/resource/integration/sdk.go | 2 +- 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index 87ec10e..1be077d 100644 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,5 +1,5 @@ ack_generate_info: - build_date: "2025-07-16T00:02:48Z" + build_date: "2025-07-16T17:04:23Z" build_hash: c6808295bbb03aac999713ecf1f3aa5cd698a17e go_version: go1.24.1 version: v0.49.0 @@ -7,7 +7,7 @@ api_directory_checksum: 6c7aa5e131743e63ddf99d1e874b3226626847fa api_version: v1alpha1 aws_sdk_go_version: v1.32.6 generator_config_info: - file_checksum: 5d322c740d60158574aed90e95f95920409e987b + file_checksum: 45e066e9415a1e1b3233151ff955ff73e99f1449 original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index a873cbd..7f52c76 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -433,12 +433,6 @@ resources: is_primary_key: true is_required: true is_immutable: true - RestAPIID: - references: - resource: RestAPI - path: Status.ID - is_required: true - is_immutable: true hooks: sdk_update_pre_build_request: template_path: hooks/domain_name/sdk_update_pre_build_request.go.tpl diff --git a/generator.yaml b/generator.yaml index a873cbd..7f52c76 100644 --- a/generator.yaml +++ b/generator.yaml @@ -433,12 +433,6 @@ resources: is_primary_key: true is_required: true is_immutable: true - RestAPIID: - references: - resource: RestAPI - path: Status.ID - is_required: true - is_immutable: true hooks: sdk_update_pre_build_request: template_path: hooks/domain_name/sdk_update_pre_build_request.go.tpl diff --git a/pkg/resource/integration/sdk.go b/pkg/resource/integration/sdk.go index 2c44fae..97d86a4 100644 --- a/pkg/resource/integration/sdk.go +++ b/pkg/resource/integration/sdk.go @@ -397,7 +397,7 @@ func (rm *resourceManager) newCreateRequestPayload( if r.ko.Spec.TimeoutInMillis != nil { timeoutInMillisCopy0 := *r.ko.Spec.TimeoutInMillis if timeoutInMillisCopy0 > math.MaxInt32 || timeoutInMillisCopy0 < math.MinInt32 { - return nil, fmt.Errorf("error: field TimeoutInMillis is of type int32") + return nil, fmt.Errorf("error: field timeoutInMillis is of type int32") } timeoutInMillisCopy := int32(timeoutInMillisCopy0) res.TimeoutInMillis = &timeoutInMillisCopy