Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apis/v1alpha1/ack-generate-metadata.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
ack_generate_info:
build_date: "2025-09-19T17:14:03Z"
build_date: "2025-10-17T18:47:29Z"
build_hash: 6b4211163dcc34776b01da9a18217bac0f4103fd
go_version: go1.24.6
version: v0.52.0
api_directory_checksum: bcdceff2d7ddf7c98141572260ef2e6cee8bf23f
api_directory_checksum: d2887bf57c4e94a2687e17c41f74c875131c0beb
api_version: v1alpha1
aws_sdk_go_version: v1.32.6
generator_config_info:
file_checksum: cc3489b53a45170d339a4de0d7d7ec0aa788955e
file_checksum: 3c4832feff83bc9c29b40bc73bafc1d7e75ab1cd
original_file_name: generator.yaml
last_modification:
reason: API generation
11 changes: 8 additions & 3 deletions apis/v1alpha1/generator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ ignore:
# Replica of Spec.SSESpecification
- TableDescription.SSEDescription
- TableDescription.TableClassSummary
- CreateTableInput.ResourcePolicy
- CreateTableInput.WarmThroughput
operations:
UpdateGlobalTable:
Expand All @@ -29,7 +28,13 @@ resources:
custom_field:
list_of: CreateReplicationGroupMemberAction
compare:
is_ignored: true
is_ignored: true
ResourcePolicy:
from:
operation: PutResourcePolicy
path: Policy
compare:
is_ignored: true
GlobalSecondaryIndexesDescriptions:
custom_field:
list_of: GlobalSecondaryIndexDescription
Expand Down Expand Up @@ -74,7 +79,7 @@ resources:
from:
operation: UpdateContributorInsights
path: ContributorInsightsAction
compare:
compare:
is_ignored: true
exceptions:
errors:
Expand Down
13 changes: 13 additions & 0 deletions apis/v1alpha1/table.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions apis/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions config/crd/bases/dynamodb.services.k8s.aws_tables.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,21 @@ spec:
format: int64
type: integer
type: object
resourcePolicy:
description: |-
An Amazon Web Services resource-based policy document in JSON format.

* The maximum size supported for a resource-based policy document is 20
KB. DynamoDB counts whitespaces when calculating the size of a policy
against this limit.

* Within a resource-based policy, if the action for a DynamoDB service-linked
role (SLR) to replicate data for a global table is denied, adding or deleting
a replica will fail with an error.

For a full list of all considerations that apply while attaching a resource-based
policy, see Resource-based policy considerations (https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/rbac-considerations.html).
type: string
sseSpecification:
description: Represents the settings used to enable server-side encryption.
properties:
Expand Down
11 changes: 8 additions & 3 deletions generator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ ignore:
# Replica of Spec.SSESpecification
- TableDescription.SSEDescription
- TableDescription.TableClassSummary
- CreateTableInput.ResourcePolicy
- CreateTableInput.WarmThroughput
operations:
UpdateGlobalTable:
Expand All @@ -29,7 +28,13 @@ resources:
custom_field:
list_of: CreateReplicationGroupMemberAction
compare:
is_ignored: true
is_ignored: true
ResourcePolicy:
from:
operation: PutResourcePolicy
path: Policy
compare:
is_ignored: true
GlobalSecondaryIndexesDescriptions:
custom_field:
list_of: GlobalSecondaryIndexDescription
Expand Down Expand Up @@ -74,7 +79,7 @@ resources:
from:
operation: UpdateContributorInsights
path: ContributorInsightsAction
compare:
compare:
is_ignored: true
exceptions:
errors:
Expand Down
15 changes: 15 additions & 0 deletions helm/crds/dynamodb.services.k8s.aws_tables.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,21 @@ spec:
format: int64
type: integer
type: object
resourcePolicy:
description: |-
An Amazon Web Services resource-based policy document in JSON format.

- The maximum size supported for a resource-based policy document is 20
KB. DynamoDB counts whitespaces when calculating the size of a policy
against this limit.

- Within a resource-based policy, if the action for a DynamoDB service-linked
role (SLR) to replicate data for a global table is denied, adding or deleting
a replica will fail with an error.

For a full list of all considerations that apply while attaching a resource-based
policy, see Resource-based policy considerations (https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/rbac-considerations.html).
type: string
sseSpecification:
description: Represents the settings used to enable server-side encryption.
properties:
Expand Down
56 changes: 44 additions & 12 deletions pkg/resource/table/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,17 +166,6 @@ func (rm *resourceManager) customUpdateTable(
setSyncedCondition(desired, corev1.ConditionFalse, &msg, nil)
return desired, requeueWaitWhileCreating
}
if isTableUpdating(latest) {
msg := "table is currently being updated"
setSyncedCondition(desired, corev1.ConditionFalse, &msg, nil)
return desired, requeueWaitWhileUpdating
}
if tableHasTerminalStatus(latest) {
msg := "table is in '" + *latest.ko.Status.TableStatus + "' status"
setTerminalCondition(desired, corev1.ConditionTrue, &msg, nil)
setSyncedCondition(desired, corev1.ConditionTrue, nil, nil)
return desired, nil
}

// Merge in the information we read from the API call above to the copy of
// the original Kubernetes object we passed to the function
Expand All @@ -188,10 +177,36 @@ func (rm *resourceManager) customUpdateTable(
return nil, err
}
}
if !delta.DifferentExcept("Spec.Tags") {

// ResourcePolicy can be updated independently of table state
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

if delta.DifferentAt("Spec.ResourcePolicy") {
if latest.ko.Status.ACKResourceMetadata == nil || latest.ko.Status.ACKResourceMetadata.ARN == nil {
rlog.Debug("skipping ResourcePolicy sync - table ARN not available yet")
return &resource{ko}, requeueWaitWhileCreating
}

err = rm.syncResourcePolicy(ctx, desired, latest)
if err != nil {
return nil, fmt.Errorf("cannot update table resource policy %v", err)
}
}

if !delta.DifferentExcept("Spec.Tags", "Spec.ResourcePolicy") {
return &resource{ko}, nil
}

if isTableUpdating(latest) {
msg := "table is currently being updated"
setSyncedCondition(desired, corev1.ConditionFalse, &msg, nil)
return desired, requeueWaitWhileUpdating
}
if tableHasTerminalStatus(latest) {
msg := "table is in '" + *latest.ko.Status.TableStatus + "' status"
setTerminalCondition(desired, corev1.ConditionTrue, &msg, nil)
setSyncedCondition(desired, corev1.ConditionTrue, nil, nil)
return desired, nil
}

if delta.DifferentAt("Spec.TimeToLive") {
if err := rm.syncTTL(ctx, desired, latest); err != nil {
// Ignore "already disabled errors"
Expand Down Expand Up @@ -522,6 +537,15 @@ func (rm *resourceManager) setResourceAdditionalFields(
} else {
ko.Spec.ContinuousBackups = pitrSpec
}

if ko.Status.ACKResourceMetadata != nil && ko.Status.ACKResourceMetadata.ARN != nil {
policy, err := rm.getResourcePolicyWithContext(ctx, (*string)(ko.Status.ACKResourceMetadata.ARN))
if err != nil {
return err
}
ko.Spec.ResourcePolicy = policy
}

return nil
}

Expand Down Expand Up @@ -671,6 +695,14 @@ func customPreCompare(
}
}

if ackcompare.HasNilDifference(a.ko.Spec.ResourcePolicy, b.ko.Spec.ResourcePolicy) {
delta.Add("Spec.ResourcePolicy", a.ko.Spec.ResourcePolicy, b.ko.Spec.ResourcePolicy)
} else if a.ko.Spec.ResourcePolicy != nil && b.ko.Spec.ResourcePolicy != nil {
if *a.ko.Spec.ResourcePolicy != *b.ko.Spec.ResourcePolicy {
delta.Add("Spec.ResourcePolicy", a.ko.Spec.ResourcePolicy, b.ko.Spec.ResourcePolicy)
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this block the same the original generated code when we set compare.is_ignore: false ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually we do have a way to parse IAM policies that gets formatted/reformatted by the AWS APIs, checkout https://github.com/aws-controllers-k8s/iam-controller/blob/main/pkg/resource/role/hooks.go#L398-L432

}

// equalAttributeDefinitions return whether two AttributeDefinition arrays are equal or not.
Expand Down
136 changes: 136 additions & 0 deletions pkg/resource/table/hooks_resource_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// 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 table

import (
"context"
"errors"

ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors"
ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
svcsdk "github.com/aws/aws-sdk-go-v2/service/dynamodb"
svcsdktypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

// syncResourcePolicy updates a DynamoDB table's resource-based policy.
func (rm *resourceManager) syncResourcePolicy(
ctx context.Context,
desired *resource,
latest *resource,
) (err error) {
rlog := ackrtlog.FromContext(ctx)
exit := rlog.Trace("rm.syncResourcePolicy")
defer func(err error) { exit(err) }(err)

if desired.ko.Spec.ResourcePolicy == nil {
return rm.deleteResourcePolicy(ctx, latest)
}

return rm.putResourcePolicy(ctx, desired)
}

// putResourcePolicy attaches or updates a resource-based policy to a DynamoDB table.
func (rm *resourceManager) putResourcePolicy(
ctx context.Context,
r *resource,
) (err error) {
rlog := ackrtlog.FromContext(ctx)
exit := rlog.Trace("rm.putResourcePolicy")
defer func(err error) { exit(err) }(err)

if r.ko.Spec.ResourcePolicy == nil {
return nil
}

tableARN := (*string)(r.ko.Status.ACKResourceMetadata.ARN)
if tableARN == nil || *tableARN == "" {
return errors.New("table ARN is required to put resource policy")
}

_, err = rm.sdkapi.PutResourcePolicy(
ctx,
&svcsdk.PutResourcePolicyInput{
ResourceArn: tableARN,
Policy: r.ko.Spec.ResourcePolicy,
},
)
rm.metrics.RecordAPICall("UPDATE", "PutResourcePolicy", err)
return err
}

// deleteResourcePolicy removes a resource-based policy from a DynamoDB table.
func (rm *resourceManager) deleteResourcePolicy(
ctx context.Context,
r *resource,
) (err error) {
rlog := ackrtlog.FromContext(ctx)
exit := rlog.Trace("rm.deleteResourcePolicy")
defer func(err error) { exit(err) }(err)

tableARN := (*string)(r.ko.Status.ACKResourceMetadata.ARN)
if tableARN == nil || *tableARN == "" {
return errors.New("table ARN is required to delete resource policy")
}

_, err = rm.sdkapi.DeleteResourcePolicy(
ctx,
&svcsdk.DeleteResourcePolicyInput{
ResourceArn: tableARN,
},
)

if err != nil {
var policyNotFoundErr *svcsdktypes.PolicyNotFoundException
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p: It would be good to add a unit test validating this behavior.

if errors.As(err, &policyNotFoundErr) {
// Policy already doesn't exist, this is a success case
return nil
}
}

rm.metrics.RecordAPICall("DELETE", "DeleteResourcePolicy", err)
return err
}

// getResourcePolicyWithContext retrieves the resource-based policy of a DynamoDB table.
func (rm *resourceManager) getResourcePolicyWithContext(
ctx context.Context,
tableARN *string,
) (*string, error) {
var err error
rlog := ackrtlog.FromContext(ctx)
exit := rlog.Trace("rm.getResourcePolicyWithContext")
defer func(err error) { exit(err) }(err)

if tableARN == nil || *tableARN == "" {
return nil, errors.New("table ARN is required to get resource policy")
}

res, err := rm.sdkapi.GetResourcePolicy(
ctx,
&svcsdk.GetResourcePolicyInput{
ResourceArn: tableARN,
},
)

if err != nil {
if awsErr, ok := ackerr.AWSError(err); ok && awsErr.ErrorCode() == "PolicyNotFoundException" {
return nil, nil
}
rm.metrics.RecordAPICall("GET", "GetResourcePolicy", err)
return nil, err
}

rm.metrics.RecordAPICall("GET", "GetResourcePolicy", nil)
return res.Policy, nil
}
Loading