Skip to content

chore(go): add plain text migration examples #1966

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 53 commits into
base: main
Choose a base branch
from

Conversation

rishav-karanjit
Copy link
Member

@rishav-karanjit rishav-karanjit commented Aug 1, 2025

Issue #, if available:

Description of changes:

  • Add examples to migrate from plain text table to DB-ESDK encrypted table.
  • These tests is tested as a unit test but not by round tripping in main because we need to run various combination of stages of migration.
  • Add utility functions required by examples in common utils file so that all examples can re-use same utils.
  • Add README.md file
  • Some nits in existing Java example.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@rishav-karanjit rishav-karanjit marked this pull request as ready for review August 4, 2025 22:38
@rishav-karanjit rishav-karanjit requested a review from a team as a code owner August 4, 2025 22:38
@rishav-karanjit rishav-karanjit changed the title chore(go): add plain text migration chore(go): add plain text migration examples Aug 4, 2025
MigrationStep1(kmsKeyID, tableName, partitionKey, sortKeys[1])
// When: Execute Step 2 with sortReadValue=1, Then: Success (i.e. can read encrypted values)
MigrationStep2(kmsKeyID, tableName, partitionKey, sortKeys[1])

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we also add testcase to write with step 3 and read with step 2? also for step 1_test.go

see similar test in Python.

# Given: Step 3 has succeeded
migration_step_3.migration_step_3_with_client(
kms_key_id=TEST_KMS_KEY_ID, ddb_table_name=TEST_DDB_TABLE_NAME, sort_read_value=3
)
# When: Execute Step 2 with sort_read_value=3
# Then: Success (i.e. can read values in encrypted format)
migration_step_2.migration_step_2_with_client(
kms_key_id=TEST_KMS_KEY_ID, ddb_table_name=TEST_DDB_TABLE_NAME, sort_read_value=3
)

Copy link
Member Author

@rishav-karanjit rishav-karanjit Aug 5, 2025

Choose a reason for hiding this comment

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

Thanks. It was a very good finding.

I did it in this commit: 4eb1d8a. I also figured that a change in local to handle error was not committed. So, this Commit include that too.

@@ -140,8 +140,9 @@ jobs:
run: |
make test_go
- name: Test Examples
- name: Run and Test Examples
Copy link
Contributor

Choose a reason for hiding this comment

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

Why run and test both? Since it's an example, I don't think we need to assert all cases via unit tests, but if it's already in then no worries.

Copy link
Member Author

Choose a reason for hiding this comment

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

For migrations, I needed to run a lot of combination of steps that made sense of run as a unit test. For all other test, just running a round trip was enough but migration is different so it was not enough

Item: item,
}

_, err = ddb.PutItem(context.TODO(), &putInput)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we use a plain vanilla client to demonstrate that we actually aren't doing any encryption and can read unencrypted items using our client?

Copy link
Member Author

@rishav-karanjit rishav-karanjit Aug 5, 2025

Choose a reason for hiding this comment

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

We do this in Test.

This is one combination of the test: In step 1 test, we write with vanilla DDB client and read with intercepted DDB client. In step 0 test, we write with intercepted DDB client and read with the vanilla one.

Generator: &kmsKeyID,
}
kmsKeyring, err := matProv.CreateAwsKmsMrkMultiKeyring(context.Background(), keyringInput)
utils.HandleError(err)
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice touch.


// We return this error because we run test against the error.
// When used in production code, you can decide how you want to handle errors.
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not use utils here as well?

Copy link
Member Author

Choose a reason for hiding this comment

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

Before writing migration example, I thought panic on error everywhere is good enough but with all the test combinations in migration test, it was not good enough. I think many in the team will still have a thought that we panic on every example. So, to make it visually distinct immediately visible in the code, I did not use utils.

if partitionKeyValue != result.Item["partition_key"].(*types.AttributeValueMemberS).Value {
panic("Decrypted item does not match original item")
}
if encryptedAndSignedValue != result.Item["attribute1"].(*types.AttributeValueMemberS).Value {
Copy link
Contributor

Choose a reason for hiding this comment

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

What about other attributes? Maybe add comment if we don't want full assertion.

Copy link
Member Author

Choose a reason for hiding this comment

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

Will this change look good to you? https://github.com/aws/aws-database-encryption-sdk-dynamodb/pull/1966/files#r2255583436

I will apply this change to other places as well.

sortKeys := []string{"0", "1", "2", "3"}

// Successfully executes Step 1
err := MigrationStep1(kmsKeyID, tableName, partitionKey, sortKeys[1])
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we aren't mocking anything here, what's the difference between this and run?

Copy link
Member Author

Choose a reason for hiding this comment

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

We run this only once with the test.

@@ -124,6 +133,33 @@ func HandleError(err error) {
}
}

func AssertServiceError(err error, service string, operation string, errorMessage string) {
if err == nil {
panic("Expected error but got no error")
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't we want to have this function as no-op in case of no errors? In that way, we can always call this instead of figuring out when to call this.

Copy link
Member Author

@rishav-karanjit rishav-karanjit Aug 5, 2025

Choose a reason for hiding this comment

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

Not really. Every time we call this we need to have an Error. If we don't get an error then that means something is wrong.

HandleError(err)

// Create DynamoDB client
client := dynamodb.NewFromConfig(cfg)
Copy link
Contributor

Choose a reason for hiding this comment

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

Or you could just use the already created client :)

Copy link
Member Author

Choose a reason for hiding this comment

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

This functions is used where we don't have access of the ddb client. So, I am re-creating it. We also re-create ddb client in every example so that examples are easier to follow.

return err
}

// Verify we got the expected item back
Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
// Verify we got the expected item back
// Verify we got the expected item back.
// Since we are mostly concerned with Plaintext Override,
// our focus is to assert the encrypted and signed value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants