From 5f602da413f0208a9f0583abd4aa5298d7934eb3 Mon Sep 17 00:00:00 2001 From: michaelhtm <98621731+michaelhtm@users.noreply.github.com> Date: Wed, 10 Dec 2025 12:19:23 -0800 Subject: [PATCH] late_initialize DBCluster and DBInstance fields that get defaulted by AWS Some spec fields get default values from AWS after they are created. These defaults cause the controller to keep requeuing, trying to set them to null. With these changes, we late initialize these fields, ensuring they are defaulted if the user does not define them --- apis/v1alpha1/ack-generate-metadata.yaml | 8 +-- apis/v1alpha1/generator.yaml | 52 +++++++++++++++++-- .../services.k8s.aws_iamroleselectors.yaml | 10 ++++ config/crd/common/kustomization.yaml | 2 +- generator.yaml | 52 +++++++++++++++++-- .../services.k8s.aws_iamroleselectors.yaml | 10 ++++ helm/templates/deployment.yaml | 9 ++++ helm/values.schema.json | 9 +++- helm/values.yaml | 2 + pkg/resource/db_cluster/manager.go | 9 +++- pkg/resource/db_instance/manager.go | 48 +++++++++++++---- 11 files changed, 184 insertions(+), 27 deletions(-) diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index 00c8af7..6dda952 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,13 +1,13 @@ ack_generate_info: - build_date: "2025-11-29T03:44:25Z" - build_hash: 23c7074fa310ad1ccb38946775397c203b49f024 + build_date: "2025-12-12T18:06:45Z" + build_hash: 5c8b9050006ef6c7d3a97c279e7b1bc163f20a0a go_version: go1.25.4 - version: v0.56.0 + version: v0.56.0-3-g5c8b905 api_directory_checksum: 90b0d1adcc91f4a1b1f1b436e3ac0c30d9271678 api_version: v1alpha1 aws_sdk_go_version: v1.32.6 generator_config_info: - file_checksum: 23c3c400e5913ebaa0047af70fda453b065ce321 + file_checksum: fbab0d4bb9fa74cc5e6e8d59465de73495fd73d7 original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index 3f5af19..6370105 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -225,6 +225,9 @@ resources: compare: # We have a custom comparison function... is_ignored: true + DatabaseInsightsMode: + late_initialize: + skip_incomplete_check: {} renames: operations: CreateDBCluster: @@ -319,7 +322,8 @@ resources: - InvalidParameterCombination fields: AvailabilityZone: - late_initialize: {} + late_initialize: + skip_incomplete_check: {} is_immutable: true DBInstanceIdentifier: is_primary_key: true @@ -328,7 +332,9 @@ resources: name: "STATUS" MasterUserPassword: is_secret: true - KMSKeyId: + KMSKeyID: + late_initialize: + skip_incomplete_check: {} references: resource: Key service_name: kms @@ -352,9 +358,41 @@ resources: service_name: ec2 path: Status.ID BackupTarget: - late_initialize: {} + late_initialize: + skip_incomplete_check: {} NetworkType: - late_initialize: {} + late_initialize: + skip_incomplete_check: {} + AutoMinorVersionUpgrade: + late_initialize: + skip_incomplete_check: {} + CACertificateIdentifier: + late_initialize: + skip_incomplete_check: {} + DatabaseInsightsMode: + late_initialize: + skip_incomplete_check: {} + LicenseModel: + late_initialize: + skip_incomplete_check: {} + MultiAZ: + late_initialize: + skip_incomplete_check: {} + PreferredBackupWindow: + late_initialize: + skip_incomplete_check: {} + PreferredMaintenanceWindow: + late_initialize: + skip_incomplete_check: {} + StorageEncrypted: + late_initialize: + skip_incomplete_check: {} + StorageThroughput: + late_initialize: + skip_incomplete_check: {} + StorageType: + late_initialize: + skip_incomplete_check: {} # Used by restore db instance from db snapshot DBSnapshotIdentifier: from: @@ -397,6 +435,12 @@ resources: PerformanceInsightsRetentionPeriod: late_initialize: skip_incomplete_check: {} + DatabaseInsightsMode: + late_initialize: + skip_incomplete_check: {} + IOPS: + late_initialize: + skip_incomplete_check: {} Tags: compare: # We have a custom comparison function... diff --git a/config/crd/common/bases/services.k8s.aws_iamroleselectors.yaml b/config/crd/common/bases/services.k8s.aws_iamroleselectors.yaml index 9477c90..803a75c 100644 --- a/config/crd/common/bases/services.k8s.aws_iamroleselectors.yaml +++ b/config/crd/common/bases/services.k8s.aws_iamroleselectors.yaml @@ -63,6 +63,16 @@ spec: required: - names type: object + resourceLabelSelector: + description: LabelSelector is a label query over a set of resources. + properties: + matchLabels: + additionalProperties: + type: string + type: object + required: + - matchLabels + type: object resourceTypeSelector: items: properties: diff --git a/config/crd/common/kustomization.yaml b/config/crd/common/kustomization.yaml index 8165534..65cb01b 100644 --- a/config/crd/common/kustomization.yaml +++ b/config/crd/common/kustomization.yaml @@ -3,5 +3,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - - bases/services.k8s.aws_iamroleselectors.yaml - bases/services.k8s.aws_fieldexports.yaml + - bases/services.k8s.aws_iamroleselectors.yaml diff --git a/generator.yaml b/generator.yaml index 3f5af19..6370105 100644 --- a/generator.yaml +++ b/generator.yaml @@ -225,6 +225,9 @@ resources: compare: # We have a custom comparison function... is_ignored: true + DatabaseInsightsMode: + late_initialize: + skip_incomplete_check: {} renames: operations: CreateDBCluster: @@ -319,7 +322,8 @@ resources: - InvalidParameterCombination fields: AvailabilityZone: - late_initialize: {} + late_initialize: + skip_incomplete_check: {} is_immutable: true DBInstanceIdentifier: is_primary_key: true @@ -328,7 +332,9 @@ resources: name: "STATUS" MasterUserPassword: is_secret: true - KMSKeyId: + KMSKeyID: + late_initialize: + skip_incomplete_check: {} references: resource: Key service_name: kms @@ -352,9 +358,41 @@ resources: service_name: ec2 path: Status.ID BackupTarget: - late_initialize: {} + late_initialize: + skip_incomplete_check: {} NetworkType: - late_initialize: {} + late_initialize: + skip_incomplete_check: {} + AutoMinorVersionUpgrade: + late_initialize: + skip_incomplete_check: {} + CACertificateIdentifier: + late_initialize: + skip_incomplete_check: {} + DatabaseInsightsMode: + late_initialize: + skip_incomplete_check: {} + LicenseModel: + late_initialize: + skip_incomplete_check: {} + MultiAZ: + late_initialize: + skip_incomplete_check: {} + PreferredBackupWindow: + late_initialize: + skip_incomplete_check: {} + PreferredMaintenanceWindow: + late_initialize: + skip_incomplete_check: {} + StorageEncrypted: + late_initialize: + skip_incomplete_check: {} + StorageThroughput: + late_initialize: + skip_incomplete_check: {} + StorageType: + late_initialize: + skip_incomplete_check: {} # Used by restore db instance from db snapshot DBSnapshotIdentifier: from: @@ -397,6 +435,12 @@ resources: PerformanceInsightsRetentionPeriod: late_initialize: skip_incomplete_check: {} + DatabaseInsightsMode: + late_initialize: + skip_incomplete_check: {} + IOPS: + late_initialize: + skip_incomplete_check: {} Tags: compare: # We have a custom comparison function... diff --git a/helm/crds/services.k8s.aws_iamroleselectors.yaml b/helm/crds/services.k8s.aws_iamroleselectors.yaml index 9477c90..803a75c 100644 --- a/helm/crds/services.k8s.aws_iamroleselectors.yaml +++ b/helm/crds/services.k8s.aws_iamroleselectors.yaml @@ -63,6 +63,16 @@ spec: required: - names type: object + resourceLabelSelector: + description: LabelSelector is a label query over a set of resources. + properties: + matchLabels: + additionalProperties: + type: string + type: object + required: + - matchLabels + type: object resourceTypeSelector: items: properties: diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index 7f692af..afb1a4b 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -51,6 +51,13 @@ spec: - "$(AWS_REGION)" - --aws-endpoint-url - "$(AWS_ENDPOINT_URL)" +{{- if .Values.aws.identity_endpoint_url }} + - --aws-identity-endpoint-url + - "$(AWS_IDENTITY_ENDPOINT_URL)" +{{- end }} +{{- if .Values.aws.allow_unsafe_aws_endpoint_urls }} + - --allow-unsafe-aws-endpoint-urls +{{- end }} {{- if .Values.log.enable_development_logging }} - --enable-development-logging {{- end }} @@ -109,6 +116,8 @@ spec: value: {{ .Values.aws.region }} - name: AWS_ENDPOINT_URL value: {{ .Values.aws.endpoint_url | quote }} + - name: AWS_IDENTITY_ENDPOINT_URL + value: {{ .Values.aws.identity_endpoint_url | quote }} - name: ACK_WATCH_NAMESPACE value: {{ include "ack-rds-controller.watch-namespace" . }} - name: ACK_WATCH_SELECTORS diff --git a/helm/values.schema.json b/helm/values.schema.json index c3f56a0..619cfe3 100644 --- a/helm/values.schema.json +++ b/helm/values.schema.json @@ -171,9 +171,16 @@ "region": { "type": "string" }, - "endpoint": { + "endpoint_url": { "type": "string" }, + "identity_endpoint_url": { + "type": "string" + }, + "allow_unsafe_aws_endpoint_urls": { + "type": "boolean", + "default": false + }, "credentials": { "description": "AWS credentials information", "properties": { diff --git a/helm/values.yaml b/helm/values.yaml index 8f2aea3..930b4ca 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -90,6 +90,8 @@ aws: # If specified, use the AWS region for AWS API calls region: "" endpoint_url: "" + identity_endpoint_url: "" + allow_unsafe_aws_endpoint_urls: false credentials: # If specified, Secret with shared credentials file to use. secretName: "" diff --git a/pkg/resource/db_cluster/manager.go b/pkg/resource/db_cluster/manager.go index 60c2508..1015954 100644 --- a/pkg/resource/db_cluster/manager.go +++ b/pkg/resource/db_cluster/manager.go @@ -50,7 +50,7 @@ var ( // +kubebuilder:rbac:groups=rds.services.k8s.aws,resources=dbclusters,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=rds.services.k8s.aws,resources=dbclusters/status,verbs=get;update;patch -var lateInitializeFieldNames = []string{} +var lateInitializeFieldNames = []string{"DatabaseInsightsMode"} // resourceManager is responsible for providing a consistent way to perform // CRUD operations in a backend AWS service API for Book custom resources. @@ -257,7 +257,12 @@ func (rm *resourceManager) lateInitializeFromReadOneOutput( observed acktypes.AWSResource, latest acktypes.AWSResource, ) acktypes.AWSResource { - return latest + observedKo := rm.concreteResource(observed).ko.DeepCopy() + latestKo := rm.concreteResource(latest).ko.DeepCopy() + if observedKo.Spec.DatabaseInsightsMode != nil && latestKo.Spec.DatabaseInsightsMode == nil { + latestKo.Spec.DatabaseInsightsMode = observedKo.Spec.DatabaseInsightsMode + } + return &resource{latestKo} } // IsSynced returns true if the resource is synced. diff --git a/pkg/resource/db_instance/manager.go b/pkg/resource/db_instance/manager.go index d22cc6d..0894f42 100644 --- a/pkg/resource/db_instance/manager.go +++ b/pkg/resource/db_instance/manager.go @@ -50,7 +50,7 @@ var ( // +kubebuilder:rbac:groups=rds.services.k8s.aws,resources=dbinstances,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=rds.services.k8s.aws,resources=dbinstances/status,verbs=get;update;patch -var lateInitializeFieldNames = []string{"AvailabilityZone", "BackupTarget", "NetworkType", "PerformanceInsightsKMSKeyID", "PerformanceInsightsRetentionPeriod"} +var lateInitializeFieldNames = []string{"AutoMinorVersionUpgrade", "AvailabilityZone", "BackupTarget", "CACertificateIdentifier", "DatabaseInsightsMode", "IOPS", "KMSKeyID", "LicenseModel", "MultiAZ", "NetworkType", "PerformanceInsightsKMSKeyID", "PerformanceInsightsRetentionPeriod", "PreferredBackupWindow", "PreferredMaintenanceWindow", "StorageEncrypted", "StorageThroughput", "StorageType"} // resourceManager is responsible for providing a consistent way to perform // CRUD operations in a backend AWS service API for Book custom resources. @@ -248,16 +248,6 @@ func (rm *resourceManager) LateInitialize( func (rm *resourceManager) incompleteLateInitialization( res acktypes.AWSResource, ) bool { - ko := rm.concreteResource(res).ko.DeepCopy() - if ko.Spec.AvailabilityZone == nil { - return true - } - if ko.Spec.BackupTarget == nil { - return true - } - if ko.Spec.NetworkType == nil { - return true - } return false } @@ -269,12 +259,33 @@ func (rm *resourceManager) lateInitializeFromReadOneOutput( ) acktypes.AWSResource { observedKo := rm.concreteResource(observed).ko.DeepCopy() latestKo := rm.concreteResource(latest).ko.DeepCopy() + if observedKo.Spec.AutoMinorVersionUpgrade != nil && latestKo.Spec.AutoMinorVersionUpgrade == nil { + latestKo.Spec.AutoMinorVersionUpgrade = observedKo.Spec.AutoMinorVersionUpgrade + } if observedKo.Spec.AvailabilityZone != nil && latestKo.Spec.AvailabilityZone == nil { latestKo.Spec.AvailabilityZone = observedKo.Spec.AvailabilityZone } if observedKo.Spec.BackupTarget != nil && latestKo.Spec.BackupTarget == nil { latestKo.Spec.BackupTarget = observedKo.Spec.BackupTarget } + if observedKo.Spec.CACertificateIdentifier != nil && latestKo.Spec.CACertificateIdentifier == nil { + latestKo.Spec.CACertificateIdentifier = observedKo.Spec.CACertificateIdentifier + } + if observedKo.Spec.DatabaseInsightsMode != nil && latestKo.Spec.DatabaseInsightsMode == nil { + latestKo.Spec.DatabaseInsightsMode = observedKo.Spec.DatabaseInsightsMode + } + if observedKo.Spec.IOPS != nil && latestKo.Spec.IOPS == nil { + latestKo.Spec.IOPS = observedKo.Spec.IOPS + } + if observedKo.Spec.KMSKeyID != nil && latestKo.Spec.KMSKeyID == nil { + latestKo.Spec.KMSKeyID = observedKo.Spec.KMSKeyID + } + if observedKo.Spec.LicenseModel != nil && latestKo.Spec.LicenseModel == nil { + latestKo.Spec.LicenseModel = observedKo.Spec.LicenseModel + } + if observedKo.Spec.MultiAZ != nil && latestKo.Spec.MultiAZ == nil { + latestKo.Spec.MultiAZ = observedKo.Spec.MultiAZ + } if observedKo.Spec.NetworkType != nil && latestKo.Spec.NetworkType == nil { latestKo.Spec.NetworkType = observedKo.Spec.NetworkType } @@ -284,6 +295,21 @@ func (rm *resourceManager) lateInitializeFromReadOneOutput( if observedKo.Spec.PerformanceInsightsRetentionPeriod != nil && latestKo.Spec.PerformanceInsightsRetentionPeriod == nil { latestKo.Spec.PerformanceInsightsRetentionPeriod = observedKo.Spec.PerformanceInsightsRetentionPeriod } + if observedKo.Spec.PreferredBackupWindow != nil && latestKo.Spec.PreferredBackupWindow == nil { + latestKo.Spec.PreferredBackupWindow = observedKo.Spec.PreferredBackupWindow + } + if observedKo.Spec.PreferredMaintenanceWindow != nil && latestKo.Spec.PreferredMaintenanceWindow == nil { + latestKo.Spec.PreferredMaintenanceWindow = observedKo.Spec.PreferredMaintenanceWindow + } + if observedKo.Spec.StorageEncrypted != nil && latestKo.Spec.StorageEncrypted == nil { + latestKo.Spec.StorageEncrypted = observedKo.Spec.StorageEncrypted + } + if observedKo.Spec.StorageThroughput != nil && latestKo.Spec.StorageThroughput == nil { + latestKo.Spec.StorageThroughput = observedKo.Spec.StorageThroughput + } + if observedKo.Spec.StorageType != nil && latestKo.Spec.StorageType == nil { + latestKo.Spec.StorageType = observedKo.Spec.StorageType + } return &resource{latestKo} }