diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index 00c8af7..dd0399f 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,8 +1,8 @@ ack_generate_info: - build_date: "2025-11-29T03:44:25Z" - build_hash: 23c7074fa310ad1ccb38946775397c203b49f024 - go_version: go1.25.4 - version: v0.56.0 + build_date: "2025-12-12T23:10:28Z" + build_hash: 5c8b9050006ef6c7d3a97c279e7b1bc163f20a0a + go_version: go1.25.1 + version: 5c8b905-dirty api_directory_checksum: 90b0d1adcc91f4a1b1f1b436e3ac0c30d9271678 api_version: v1alpha1 aws_sdk_go_version: v1.32.6 diff --git a/config/crd/common/bases/services.k8s.aws_adoptedresources.yaml b/config/crd/common/bases/services.k8s.aws_adoptedresources.yaml new file mode 100644 index 0000000..b7be322 --- /dev/null +++ b/config/crd/common/bases/services.k8s.aws_adoptedresources.yaml @@ -0,0 +1,249 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.2 + name: adoptedresources.services.k8s.aws +spec: + group: services.k8s.aws + names: + kind: AdoptedResource + listKind: AdoptedResourceList + plural: adoptedresources + singular: adoptedresource + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: AdoptedResource is the schema for the AdoptedResource 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: AdoptedResourceSpec defines the desired state of the AdoptedResource. + properties: + aws: + description: AWSIdentifiers provide all unique ways to reference an + AWS resource. + properties: + additionalKeys: + additionalProperties: + type: string + description: |- + AdditionalKeys represents any additional arbitrary identifiers used when + describing the target resource. + type: object + arn: + description: |- + ARN is the AWS Resource Name for the resource. It is a globally + unique identifier. + type: string + nameOrID: + description: |- + NameOrId is a user-supplied string identifier for the resource. It may + or may not be globally unique, depending on the type of resource. + type: string + type: object + kubernetes: + description: |- + ResourceWithMetadata provides the values necessary to create a + Kubernetes resource and override any of its metadata values. + properties: + group: + type: string + kind: + type: string + metadata: + description: |- + ObjectMeta is metadata that all persisted resources must have, which includes all objects + users must create. + It is not possible to use `metav1.ObjectMeta` inside spec, as the controller-gen + automatically converts this to an arbitrary string-string map. + https://github.com/kubernetes-sigs/controller-tools/issues/385 + + Active discussion about inclusion of this field in the spec is happening in this PR: + https://github.com/kubernetes-sigs/controller-tools/pull/395 + + Until this is allowed, or if it never is, we will produce a subset of the object meta + that contains only the fields which the user is allowed to modify in the metadata. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: http://kubernetes.io/docs/user-guide/annotations + type: object + generateName: + description: |- + GenerateName is an optional prefix, used by the server, to generate a unique + name ONLY IF the Name field has not been provided. + If this field is used, the name returned to the client will be different + than the name passed. This value will also be combined with a unique suffix. + The provided value has the same validation rules as the Name field, + and may be truncated by the length of the suffix required to make the value + unique on the server. + + If this field is specified and the generated name exists, the server will + NOT return a 409 - instead, it will either return 201 Created or 500 with Reason + ServerTimeout indicating a unique name could not be found in the time allotted, and the client + should retry (optionally after the time indicated in the Retry-After header). + + Applied only if Name is not specified. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency + type: string + labels: + additionalProperties: + type: string + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. May match selectors of replication controllers + and services. + More info: http://kubernetes.io/docs/user-guide/labels + type: object + name: + description: |- + Name must be unique within a namespace. Is required when creating resources, although + some resources may allow a client to request the generation of an appropriate name + automatically. Name is primarily intended for creation idempotence and configuration + definition. + Cannot be updated. + More info: http://kubernetes.io/docs/user-guide/identifiers#names + type: string + namespace: + description: |- + Namespace defines the space within each name must be unique. An empty namespace is + equivalent to the "default" namespace, but "default" is the canonical representation. + Not all objects are required to be scoped to a namespace - the value of this field for + those objects will be empty. + + Must be a DNS_LABEL. + Cannot be updated. + More info: http://kubernetes.io/docs/user-guide/namespaces + type: string + ownerReferences: + description: |- + List of objects depended by this object. If ALL objects in the list have + been deleted, this object will be garbage collected. If this object is managed by a controller, + then an entry in this list will point to this controller, with the controller field set to true. + There cannot be more than one managing controller. + items: + description: |- + OwnerReference contains enough information to let you identify an owning + object. An owning object must be in the same namespace as the dependent, or + be cluster-scoped, so there is no namespace field. + properties: + apiVersion: + description: API version of the referent. + type: string + blockOwnerDeletion: + description: |- + If true, AND if the owner has the "foregroundDeletion" finalizer, then + the owner cannot be deleted from the key-value store until this + reference is removed. + See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion + for how the garbage collector interacts with this field and enforces the foreground deletion. + Defaults to false. + To set this field, a user needs "delete" permission of the owner, + otherwise 422 (Unprocessable Entity) will be returned. + type: boolean + controller: + description: If true, this reference points to the managing + controller. + type: boolean + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids + type: string + required: + - apiVersion + - kind + - name + - uid + type: object + x-kubernetes-map-type: atomic + type: array + type: object + required: + - group + - kind + type: object + required: + - aws + - kubernetes + type: object + status: + description: AdoptedResourceStatus defines the observed status of the + AdoptedResource. + properties: + conditions: + description: |- + A collection of `ackv1alpha1.Condition` objects that describe the various + terminal states of the adopted resource CR and its target custom 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 + required: + - conditions + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/common/bases/services.k8s.aws_fieldexports.yaml b/config/crd/common/bases/services.k8s.aws_fieldexports.yaml index 6e2c61e..49b4f38 100644 --- a/config/crd/common/bases/services.k8s.aws_fieldexports.yaml +++ b/config/crd/common/bases/services.k8s.aws_fieldexports.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.16.2 name: fieldexports.services.k8s.aws spec: group: services.k8s.aws diff --git a/config/crd/common/kustomization.yaml b/config/crd/common/kustomization.yaml index 8165534..96349f6 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_adoptedresources.yaml - bases/services.k8s.aws_fieldexports.yaml diff --git a/helm/crds/services.k8s.aws_adoptedresources.yaml b/helm/crds/services.k8s.aws_adoptedresources.yaml new file mode 100644 index 0000000..d6cdd10 --- /dev/null +++ b/helm/crds/services.k8s.aws_adoptedresources.yaml @@ -0,0 +1,249 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.19.0 + name: adoptedresources.services.k8s.aws +spec: + group: services.k8s.aws + names: + kind: AdoptedResource + listKind: AdoptedResourceList + plural: adoptedresources + singular: adoptedresource + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: AdoptedResource is the schema for the AdoptedResource 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: AdoptedResourceSpec defines the desired state of the AdoptedResource. + properties: + aws: + description: AWSIdentifiers provide all unique ways to reference an + AWS resource. + properties: + additionalKeys: + additionalProperties: + type: string + description: |- + AdditionalKeys represents any additional arbitrary identifiers used when + describing the target resource. + type: object + arn: + description: |- + ARN is the AWS Resource Name for the resource. It is a globally + unique identifier. + type: string + nameOrID: + description: |- + NameOrId is a user-supplied string identifier for the resource. It may + or may not be globally unique, depending on the type of resource. + type: string + type: object + kubernetes: + description: |- + ResourceWithMetadata provides the values necessary to create a + Kubernetes resource and override any of its metadata values. + properties: + group: + type: string + kind: + type: string + metadata: + description: |- + ObjectMeta is metadata that all persisted resources must have, which includes all objects + users must create. + It is not possible to use `metav1.ObjectMeta` inside spec, as the controller-gen + automatically converts this to an arbitrary string-string map. + https://github.com/kubernetes-sigs/controller-tools/issues/385 + + Active discussion about inclusion of this field in the spec is happening in this PR: + https://github.com/kubernetes-sigs/controller-tools/pull/395 + + Until this is allowed, or if it never is, we will produce a subset of the object meta + that contains only the fields which the user is allowed to modify in the metadata. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: http://kubernetes.io/docs/user-guide/annotations + type: object + generateName: + description: |- + GenerateName is an optional prefix, used by the server, to generate a unique + name ONLY IF the Name field has not been provided. + If this field is used, the name returned to the client will be different + than the name passed. This value will also be combined with a unique suffix. + The provided value has the same validation rules as the Name field, + and may be truncated by the length of the suffix required to make the value + unique on the server. + + If this field is specified and the generated name exists, the server will + NOT return a 409 - instead, it will either return 201 Created or 500 with Reason + ServerTimeout indicating a unique name could not be found in the time allotted, and the client + should retry (optionally after the time indicated in the Retry-After header). + + Applied only if Name is not specified. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency + type: string + labels: + additionalProperties: + type: string + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. May match selectors of replication controllers + and services. + More info: http://kubernetes.io/docs/user-guide/labels + type: object + name: + description: |- + Name must be unique within a namespace. Is required when creating resources, although + some resources may allow a client to request the generation of an appropriate name + automatically. Name is primarily intended for creation idempotence and configuration + definition. + Cannot be updated. + More info: http://kubernetes.io/docs/user-guide/identifiers#names + type: string + namespace: + description: |- + Namespace defines the space within each name must be unique. An empty namespace is + equivalent to the "default" namespace, but "default" is the canonical representation. + Not all objects are required to be scoped to a namespace - the value of this field for + those objects will be empty. + + Must be a DNS_LABEL. + Cannot be updated. + More info: http://kubernetes.io/docs/user-guide/namespaces + type: string + ownerReferences: + description: |- + List of objects depended by this object. If ALL objects in the list have + been deleted, this object will be garbage collected. If this object is managed by a controller, + then an entry in this list will point to this controller, with the controller field set to true. + There cannot be more than one managing controller. + items: + description: |- + OwnerReference contains enough information to let you identify an owning + object. An owning object must be in the same namespace as the dependent, or + be cluster-scoped, so there is no namespace field. + properties: + apiVersion: + description: API version of the referent. + type: string + blockOwnerDeletion: + description: |- + If true, AND if the owner has the "foregroundDeletion" finalizer, then + the owner cannot be deleted from the key-value store until this + reference is removed. + See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion + for how the garbage collector interacts with this field and enforces the foreground deletion. + Defaults to false. + To set this field, a user needs "delete" permission of the owner, + otherwise 422 (Unprocessable Entity) will be returned. + type: boolean + controller: + description: If true, this reference points to the managing + controller. + type: boolean + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids + type: string + required: + - apiVersion + - kind + - name + - uid + type: object + x-kubernetes-map-type: atomic + type: array + type: object + required: + - group + - kind + type: object + required: + - aws + - kubernetes + type: object + status: + description: AdoptedResourceStatus defines the observed status of the + AdoptedResource. + properties: + conditions: + description: |- + A collection of `ackv1alpha1.Condition` objects that describe the various + terminal states of the adopted resource CR and its target custom 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 + required: + - conditions + type: object + type: object + served: true + storage: true + subresources: + status: {} 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_instance/hooks.go b/pkg/resource/db_instance/hooks.go index a504cca..bcbfd51 100644 --- a/pkg/resource/db_instance/hooks.go +++ b/pkg/resource/db_instance/hooks.go @@ -17,6 +17,7 @@ import ( "context" "errors" "fmt" + "slices" "strings" svcapitypes "github.com/aws-controllers-k8s/rds-controller/apis/v1alpha1" @@ -585,3 +586,33 @@ func needStorageUpdate( return strings.Contains(*r.ko.Status.DBInstanceStatus, "storage-full") && delta.DifferentAt("Spec.AllocatedStorage") } + +func StringValueSlice(vs []*string) []string { + out := make([]string, 0, len(vs)) + for _, v := range vs { + if v == nil { + continue + } + out = append(out, *v) + } + return out +} + +func getCloudwatchLogExportsConfigDifferences(cloudwatchLogExportsConfigDesired []*string, cloudwatchLogExportsConfigLatest []*string) ([]*string, []*string) { + logsTypesToEnable := []*string{} + logsTypesToDisable := []*string{} + desired := StringValueSlice(cloudwatchLogExportsConfigDesired) + latest := StringValueSlice(cloudwatchLogExportsConfigLatest) + + for _, config := range cloudwatchLogExportsConfigDesired { + if !slices.Contains(latest, *config) { + logsTypesToEnable = append(logsTypesToEnable, config) + } + } + for _, config := range cloudwatchLogExportsConfigLatest { + if !slices.Contains(desired, *config) { + logsTypesToDisable = append(logsTypesToDisable, config) + } + } + return logsTypesToEnable, logsTypesToDisable +} diff --git a/pkg/resource/db_instance/sdk.go b/pkg/resource/db_instance/sdk.go index 3c3cee2..3381851 100644 --- a/pkg/resource/db_instance/sdk.go +++ b/pkg/resource/db_instance/sdk.go @@ -895,6 +895,8 @@ func (rm *resourceManager) sdkFind( } } + ko.Spec.EnableCloudwatchLogsExports = ko.Status.EnabledCloudwatchLogsExports + return &resource{ko}, nil } @@ -1992,6 +1994,17 @@ func (rm *resourceManager) sdkUpdate( input.PreferredBackupWindow = nil input.DeletionProtection = nil } + if delta.DifferentAt("Spec.EnableCloudwatchLogsExports") { + cloudwatchLogExportsConfigDesired := desired.ko.Spec.EnableCloudwatchLogsExports + //Latest log types config + cloudwatchLogExportsConfigLatest := latest.ko.Spec.EnableCloudwatchLogsExports + logsTypesToEnable, logsTypesToDisable := getCloudwatchLogExportsConfigDifferences(cloudwatchLogExportsConfigDesired, cloudwatchLogExportsConfigLatest) + f24 := &svcsdktypes.CloudwatchLogsExportConfiguration{ + EnableLogTypes: aws.ToStringSlice(logsTypesToEnable), + DisableLogTypes: aws.ToStringSlice(logsTypesToDisable), + } + input.CloudwatchLogsExportConfiguration = f24 + } var resp *svcsdk.ModifyDBInstanceOutput _ = resp diff --git a/templates/hooks/db_instance/sdk_read_many_post_set_output.go.tpl b/templates/hooks/db_instance/sdk_read_many_post_set_output.go.tpl index f2701b9..c154f8f 100644 --- a/templates/hooks/db_instance/sdk_read_many_post_set_output.go.tpl +++ b/templates/hooks/db_instance/sdk_read_many_post_set_output.go.tpl @@ -114,3 +114,5 @@ ko.Spec.DBParameterGroupName = ko.Status.DBParameterGroups[0].DBParameterGroupName } } + + ko.Spec.EnableCloudwatchLogsExports = ko.Status.EnabledCloudwatchLogsExports diff --git a/templates/hooks/db_instance/sdk_update_post_build_request.go.tpl b/templates/hooks/db_instance/sdk_update_post_build_request.go.tpl index 0042b61..aea8c67 100644 --- a/templates/hooks/db_instance/sdk_update_post_build_request.go.tpl +++ b/templates/hooks/db_instance/sdk_update_post_build_request.go.tpl @@ -36,3 +36,14 @@ input.PreferredBackupWindow = nil input.DeletionProtection = nil } + if delta.DifferentAt("Spec.EnableCloudwatchLogsExports") { + cloudwatchLogExportsConfigDesired := desired.ko.Spec.EnableCloudwatchLogsExports + //Latest log types config + cloudwatchLogExportsConfigLatest := latest.ko.Spec.EnableCloudwatchLogsExports + logsTypesToEnable, logsTypesToDisable := getCloudwatchLogExportsConfigDifferences(cloudwatchLogExportsConfigDesired, cloudwatchLogExportsConfigLatest) + f24 := &svcsdktypes.CloudwatchLogsExportConfiguration{ + EnableLogTypes: aws.ToStringSlice(logsTypesToEnable), + DisableLogTypes: aws.ToStringSlice(logsTypesToDisable), + } + input.CloudwatchLogsExportConfiguration = f24 + } diff --git a/test/e2e/tests/test_db_instance.py b/test/e2e/tests/test_db_instance.py index ec27478..e0a8e08 100644 --- a/test/e2e/tests/test_db_instance.py +++ b/test/e2e/tests/test_db_instance.py @@ -203,7 +203,45 @@ def test_crud_postgres14_t3_micro( } ] assert latest_tags == after_update_expected_tags + + def test_crud_cloudwatch_logs14_t3_micro( + self, + postgres14_t3_micro_instance, + ): + (ref, cr, _) = postgres14_t3_micro_instance + db_instance_id = cr["spec"]["dbInstanceIdentifier"] + db_instance.wait_until(db_instance_id, db_instance.status_matches("available")) + + current = db_instance.get(db_instance_id) + assert current is not None + enabledCloudwatchLogsExports = current.get("EnabledCloudwatchLogsExports",None) + assert enabledCloudwatchLogsExports is None + k8s.patch_custom_resource( + ref, + {"spec": {"enableCloudwatchLogsExports": ["postgresql"]}}, + ) + time.sleep(MODIFY_WAIT_AFTER_SECONDS) + + # wait for the resource to get synced after the patch + assert k8s.wait_on_condition(ref, "ACK.ResourceSynced", "True", wait_periods=MAX_WAIT_FOR_SYNCED_MINUTES) + + latest = db_instance.get(db_instance_id) + assert latest is not None + assert latest["EnabledCloudwatchLogsExports"] == ["postgresql"] + k8s.patch_custom_resource( + ref, + {"spec": {"enableCloudwatchLogsExports": None}}, + ) + time.sleep(MODIFY_WAIT_AFTER_SECONDS) + # wait for the resource to get synced after the patch + assert k8s.wait_on_condition(ref, "ACK.ResourceSynced", "True", wait_periods=MAX_WAIT_FOR_SYNCED_MINUTES) + + latest = db_instance.get(db_instance_id) + assert latest is not None + enabledCloudwatchLogsExportsLatest = latest.get("EnabledCloudwatchLogsExports",None) + assert enabledCloudwatchLogsExportsLatest is None + def test_crud_postgres14_update_password( self, postgres14_t3_micro_instance,