From adea799ee6ae758e39870f980ca69e2b64cd5bef Mon Sep 17 00:00:00 2001 From: willdavsmith Date: Wed, 13 Sep 2023 09:51:56 -0700 Subject: [PATCH] Adding aws creation timestamp to resources --- .github/scripts/delete-aws-resources.sh | 99 +++++++++++++++++++ .../workflows/purge-aws-test-resources.yaml | 18 +--- .../shared/mechanics/aws_mechanics_test.go | 11 ++- ...thcreateandwritepropertyupdate.step1.bicep | 18 +++- ...thcreateandwritepropertyupdate.step2.bicep | 18 +++- ...s-redeploy-withupdatedresource.step1.bicep | 5 + ...s-redeploy-withupdatedresource.step2.bicep | 5 + .../aws_multi_identifier_resource_test.go | 5 +- .../shared/resources/aws_s3_bucket_test.go | 16 ++- .../shared/resources/extender_test.go | 6 +- ...e_test.go => kubemetadata_cascade_test.go} | 0 .../testdata/aws-multi-identifier.bicep | 11 ++- .../resources/testdata/aws-s3-bucket.bicep | 5 + .../extenders-aws-s3-recipe.bicep | 7 ++ test/functional/testUtil.go | 11 +++ 15 files changed, 200 insertions(+), 35 deletions(-) create mode 100755 .github/scripts/delete-aws-resources.sh rename test/functional/shared/resources/{kubmetadata_cascade_test.go => kubemetadata_cascade_test.go} (100%) diff --git a/.github/scripts/delete-aws-resources.sh b/.github/scripts/delete-aws-resources.sh new file mode 100755 index 0000000000..447125edd2 --- /dev/null +++ b/.github/scripts/delete-aws-resources.sh @@ -0,0 +1,99 @@ +#!/bin/bash + +# ------------------------------------------------------------ +# Copyright 2023 The Radius Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. +# ------------------------------------------------------------ + +# Comma-separated list of AWS resource types +RESOURCE_TYPES=$1 + +# Label on AWS resources +LABEL='RadiusCreationTimestamp' + +# File to store the list of deleted resources +DELETED_RESOURCES_FILE='deleted-resources.txt' + +# Number of retries +MAX_RETRIES=5 + +# Retry delay in seconds +RETRY_DELAY=300 # 5 minutes + +# Maximum age of resources in seconds +MAX_AGE=21600 + +# Current time in seconds +CURRENT_TIME=$(date +%s) + +function delete_old_aws_resources() { + # Empty the file + truncate -s 0 $DELETED_RESOURCES_FILE + + for resource_type in ${RESOURCE_TYPES//,/ } + do + aws cloudcontrol list-resources --type-name "$resource_type" --query "ResourceDescriptions[].Identifier" --output text | tr '\t' '\n' | while read identifier + do + aws cloudcontrol get-resource --type-name "$resource_type" --identifier "$identifier" --query "ResourceDescription.Properties" --output text | while read resource + do + resource_tags=$(jq -c -r .Tags <<< "$resource") + for tag in $(jq -c -r '.[]' <<< "$resource_tags") + do + key=$(jq -r '.Key' <<< "$tag") + value=$(jq -r '.Value' <<< "$tag") + if [[ "$key" == "$LABEL" && $((CURRENT_TIME - value)) -gt $MAX_AGE]] + then + echo "Deleting resource of type: $resource_type with identifier: $identifier" + echo "$identifier\n" >> $DELETED_RESOURCES_FILE + aws cloudcontrol delete-resource --type-name "$resource_type" --identifier "$identifier" + fi + done + done + done + done + + if [ -s $DELETED_RESOURCES_FILE ]; then + return 1 + else + return 0 + fi +} + +RETRY_COUNT=0 +while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do + # Trigger the function to delete the resources + delete_old_aws_resources + + # If the function returned 0, then no resources needed to be deleted + # on this run. This means that all resources have been deleted. + if [ $? -eq 0 ]; then + echo "All resources deleted successfully" + break + fi + + # Still have resources to delete, increase the retry count + RETRY_COUNT=$((RETRY_COUNT + 1)) + + # Check if there are more retries left + if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then + # Retry after delay + echo "Retrying in $RETRY_DELAY seconds..." + sleep $RETRY_DELAY + fi +done + +# Check if the maximum number of retries exceeded +if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then + echo "Maximum number of retries exceeded" +fi diff --git a/.github/workflows/purge-aws-test-resources.yaml b/.github/workflows/purge-aws-test-resources.yaml index 76c6a0ee7c..c7f5e228ce 100644 --- a/.github/workflows/purge-aws-test-resources.yaml +++ b/.github/workflows/purge-aws-test-resources.yaml @@ -23,10 +23,11 @@ on: env: AWS_REGION: us-west-2 - AWS_RESOURCE_TYPES: 'AWS::Kinesis::Stream,AWS::S3::Bucket,AWS::RDS::DBInstance,AWS::RDS::DBSubnetGroup,AWS::MemoryDB::Cluster,AWS::MemoryDB::SubnetGroup' + AWS_RESOURCE_TYPES: 'AWS::RDS::DBSubnetGroup,AWS::RDS::DBInstance,AWS::S3::Bucket,AWS::Logs::MetricFilter,AWS::Logs::LogGroup' + jobs: purge_aws_resources: - name: Delete all AWS resources created by tests + name: Delete old AWS resources created by tests runs-on: ubuntu-latest steps: - name: Configure AWS Credentials @@ -35,15 +36,6 @@ jobs: aws-access-key-id: ${{ secrets.FUNCTEST_AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.FUNCTEST_AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} - - name: Filter and delete resources + - name: Delete old AWS resources run: | - RESOURCE_TYPES=${{env.AWS_RESOURCE_TYPES}} - for resource_type in ${RESOURCE_TYPES//,/ } - do - echo "Deleting resources of type $resource_type" - aws cloudcontrol list-resources --type-name "$resource_type" --query "ResourceDescriptions[].Identifier" --output text | tr '\t' '\n' | while read identifier - do - echo "Deleting resource $identifier of type $resource_type" - aws cloudcontrol delete-resource --type-name "$resource_type" --identifier "$identifier" - done - done + ./.github/scripts/delete_aws_resources.sh ${{ env.AWS_RESOURCE_TYPES }} diff --git a/test/functional/shared/mechanics/aws_mechanics_test.go b/test/functional/shared/mechanics/aws_mechanics_test.go index f5a3535809..e904da117d 100644 --- a/test/functional/shared/mechanics/aws_mechanics_test.go +++ b/test/functional/shared/mechanics/aws_mechanics_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/google/uuid" + "github.com/radius-project/radius/test/functional" "github.com/radius-project/radius/test/functional/shared" "github.com/radius-project/radius/test/step" "github.com/radius-project/radius/test/validation" @@ -29,10 +30,11 @@ import ( func Test_AWSRedeployWithUpdatedResourceUpdatesResource(t *testing.T) { templateFmt := "testdata/aws-mechanics-redeploy-withupdatedresource.step%d.bicep" name := "radiusfunctionaltestbucket-" + uuid.New().String() + creationTimestamp := functional.GetCreationTimestamp() test := shared.NewRPTest(t, name, []shared.TestStep{ { - Executor: step.NewDeployExecutor(fmt.Sprintf(templateFmt, 1), "bucketName="+name), + Executor: step.NewDeployExecutor(fmt.Sprintf(templateFmt, 1), "bucketName="+name, "creationTimestamp="+creationTimestamp), SkipKubernetesOutputResourceValidation: true, SkipObjectValidation: true, SkipResourceDeletion: true, @@ -56,7 +58,7 @@ func Test_AWSRedeployWithUpdatedResourceUpdatesResource(t *testing.T) { }, }, { - Executor: step.NewDeployExecutor(fmt.Sprintf(templateFmt, 2), "bucketName="+name), + Executor: step.NewDeployExecutor(fmt.Sprintf(templateFmt, 2), "bucketName="+name, "creationTimestamp="+creationTimestamp), SkipKubernetesOutputResourceValidation: true, SkipObjectValidation: true, AWSResources: &validation.AWSResourceSet{ @@ -86,10 +88,11 @@ func Test_AWSRedeployWithCreateAndWriteOnlyPropertyUpdate(t *testing.T) { t.Skip("This test will fail because step 2 is updating a create-and-write-only property.") name := "my-db" templateFmt := "testdata/aws-mechanics-redeploy-withcreateandwriteonlypropertyupdate.step%d.bicep" + creationTimestamp := functional.GetCreationTimestamp() test := shared.NewRPTest(t, name, []shared.TestStep{ { - Executor: step.NewDeployExecutor(fmt.Sprintf(templateFmt, 1)), + Executor: step.NewDeployExecutor(fmt.Sprintf(templateFmt, 1), "creationTimestamp="+creationTimestamp), SkipKubernetesOutputResourceValidation: true, SkipObjectValidation: true, SkipResourceDeletion: true, @@ -109,7 +112,7 @@ func Test_AWSRedeployWithCreateAndWriteOnlyPropertyUpdate(t *testing.T) { }, }, { - Executor: step.NewDeployExecutor(fmt.Sprintf(templateFmt, 2)), + Executor: step.NewDeployExecutor(fmt.Sprintf(templateFmt, 2), "creationTimestamp="+creationTimestamp), SkipKubernetesOutputResourceValidation: true, SkipObjectValidation: true, AWSResources: &validation.AWSResourceSet{ diff --git a/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withcreateandwritepropertyupdate.step1.bicep b/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withcreateandwritepropertyupdate.step1.bicep index b44351d1e8..62261fd452 100644 --- a/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withcreateandwritepropertyupdate.step1.bicep +++ b/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withcreateandwritepropertyupdate.step1.bicep @@ -1,15 +1,23 @@ import aws as aws -param dbSubnetGroupName string = 'willsmith-rds-mssql-subnet-group-3' +param creationTimestamp string +param dbSubnetGroupName string +param dbName string + resource subnetGroup 'AWS.RDS/DBSubnetGroup@default' = { alias: dbSubnetGroupName properties: { DBSubnetGroupDescription: dbSubnetGroupName SubnetIds: [''] + Tags: [ + { + Key: 'RadiusCreationTimestamp' + Value: creationTimestamp + } + ] } } -param dbName string = 'willsmith-rds-mssql-3' resource db 'AWS.RDS/DBInstance@default' = { alias: dbName properties: { @@ -30,5 +38,11 @@ resource db 'AWS.RDS/DBInstance@default' = { LicenseModel: 'license-included' Timezone: 'GMT Standard Time' CharacterSetName: 'Latin1_General_CI_AS' + Tags: [ + { + Key: 'RadiusCreationTimestamp' + Value: creationTimestamp + } + ] } } diff --git a/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withcreateandwritepropertyupdate.step2.bicep b/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withcreateandwritepropertyupdate.step2.bicep index abe35bd063..c8bdde1a7a 100644 --- a/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withcreateandwritepropertyupdate.step2.bicep +++ b/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withcreateandwritepropertyupdate.step2.bicep @@ -1,15 +1,23 @@ import aws as aws -param dbSubnetGroupName string = 'willsmith-rds-mssql-subnet-group-3' +param creationTimestamp string +param dbSubnetGroupName string +param dbName string + resource subnetGroup 'AWS.RDS/DBSubnetGroup@default' = { alias: dbSubnetGroupName properties: { DBSubnetGroupDescription: dbSubnetGroupName SubnetIds: [''] + Tags: [ + { + Key: 'RadiusCreationTimestamp' + Value: creationTimestamp + } + ] } } -param dbName string = 'willsmith-rds-mssql-3' resource db 'AWS.RDS/DBInstance@default' = { alias: dbName properties: { @@ -30,5 +38,11 @@ resource db 'AWS.RDS/DBInstance@default' = { LicenseModel: 'license-included' Timezone: 'GMT Standard Time' CharacterSetName: 'Latin1_General_CI_AS' + Tags: [ + { + Key: 'RadiusCreationTimestamp' + Value: creationTimestamp + } + ] } } diff --git a/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withupdatedresource.step1.bicep b/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withupdatedresource.step1.bicep index a37c968f81..52c56f9127 100644 --- a/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withupdatedresource.step1.bicep +++ b/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withupdatedresource.step1.bicep @@ -1,5 +1,6 @@ import aws as aws +param creationTimestamp string param bucketName string resource bucket 'AWS.S3/Bucket@default' = { @@ -11,6 +12,10 @@ resource bucket 'AWS.S3/Bucket@default' = { Key: 'testKey' Value: 'testValue' } + { + Key: 'RadiusCreationTimestamp' + Value: creationTimestamp + } ] } } diff --git a/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withupdatedresource.step2.bicep b/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withupdatedresource.step2.bicep index 525eb06388..109f502a2d 100644 --- a/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withupdatedresource.step2.bicep +++ b/test/functional/shared/mechanics/testdata/aws-mechanics-redeploy-withupdatedresource.step2.bicep @@ -1,5 +1,6 @@ import aws as aws +param creationTimestamp string param bucketName string resource bucket 'AWS.S3/Bucket@default' = { @@ -11,6 +12,10 @@ resource bucket 'AWS.S3/Bucket@default' = { Key: 'testKey' Value: 'testValue2' } + { + Key: 'RadiusCreationTimestamp' + Value: creationTimestamp + } ] } } diff --git a/test/functional/shared/resources/aws_multi_identifier_resource_test.go b/test/functional/shared/resources/aws_multi_identifier_resource_test.go index 6042e26a97..96970ef509 100644 --- a/test/functional/shared/resources/aws_multi_identifier_resource_test.go +++ b/test/functional/shared/resources/aws_multi_identifier_resource_test.go @@ -17,7 +17,9 @@ limitations under the License. package resource_test import ( + "fmt" "testing" + "time" "github.com/google/uuid" "github.com/radius-project/radius/test/functional/shared" @@ -30,10 +32,11 @@ func Test_AWS_MultiIdentifier_Resource(t *testing.T) { filterName := "ms" + uuid.New().String() logGroupName := "ms" + uuid.New().String() testName := "ms" + uuid.New().String() + creationTimestamp := fmt.Sprintf("%d", time.Now().Unix()) test := shared.NewRPTest(t, testName, []shared.TestStep{ { - Executor: step.NewDeployExecutor(template, "filterName="+filterName, "logGroupName="+logGroupName), + Executor: step.NewDeployExecutor(template, "filterName="+filterName, "logGroupName="+logGroupName, "creationTimestamp="+creationTimestamp), SkipKubernetesOutputResourceValidation: true, SkipObjectValidation: true, AWSResources: &validation.AWSResourceSet{ diff --git a/test/functional/shared/resources/aws_s3_bucket_test.go b/test/functional/shared/resources/aws_s3_bucket_test.go index 0bb73f858a..6695f3351c 100644 --- a/test/functional/shared/resources/aws_s3_bucket_test.go +++ b/test/functional/shared/resources/aws_s3_bucket_test.go @@ -19,7 +19,7 @@ package resource_test import ( "testing" - "github.com/google/uuid" + "github.com/radius-project/radius/test/functional" "github.com/radius-project/radius/test/functional/shared" "github.com/radius-project/radius/test/step" "github.com/radius-project/radius/test/validation" @@ -27,11 +27,12 @@ import ( func Test_AWS_S3Bucket(t *testing.T) { template := "testdata/aws-s3-bucket.bicep" - name := generateS3BucketName() + name := functional.GenerateS3BucketName() + creationTimestamp := functional.GetCreationTimestamp() test := shared.NewRPTest(t, name, []shared.TestStep{ { - Executor: step.NewDeployExecutor(template, "bucketName="+name), + Executor: step.NewDeployExecutor(template, "bucketName="+name, "creationTimestamp="+creationTimestamp), SkipKubernetesOutputResourceValidation: true, SkipObjectValidation: true, AWSResources: &validation.AWSResourceSet{ @@ -61,11 +62,12 @@ func Test_AWS_S3Bucket(t *testing.T) { func Test_AWS_S3Bucket_Existing(t *testing.T) { template := "testdata/aws-s3-bucket.bicep" templateExisting := "testdata/aws-s3-bucket-existing.bicep" - name := generateS3BucketName() + name := functional.GenerateS3BucketName() + creationTimestamp := functional.GetCreationTimestamp() test := shared.NewRPTest(t, name, []shared.TestStep{ { - Executor: step.NewDeployExecutor(template, "bucketName="+name), + Executor: step.NewDeployExecutor(template, "bucketName="+name, "creationTimestamp="+creationTimestamp), SkipKubernetesOutputResourceValidation: true, SkipObjectValidation: true, SkipResourceDeletion: true, @@ -117,7 +119,3 @@ func Test_AWS_S3Bucket_Existing(t *testing.T) { test.Test(t) } - -func generateS3BucketName() string { - return "radiusfunctionaltestbucket-" + uuid.New().String() -} diff --git a/test/functional/shared/resources/extender_test.go b/test/functional/shared/resources/extender_test.go index f42fe2c36c..6487b8143d 100644 --- a/test/functional/shared/resources/extender_test.go +++ b/test/functional/shared/resources/extender_test.go @@ -109,14 +109,16 @@ func Test_Extender_RecipeAWS(t *testing.T) { template := "testdata/corerp-resources-extender-aws-s3-recipe.bicep" name := "corerp-resources-extenders-aws-s3-recipe" appName := "corerp-resources-extenders-aws-s3-recipe-app" - bucketName := generateS3BucketName() + bucketName := functional.GenerateS3BucketName() bucketID := fmt.Sprintf("/planes/aws/aws/accounts/%s/regions/%s/providers/AWS.S3/Bucket/%s", awsAccountID, awsRegion, bucketName) + creationTimestamp := functional.GetCreationTimestamp() test := shared.NewRPTest(t, name, []shared.TestStep{ { Executor: step.NewDeployExecutor( template, - fmt.Sprintf("bucketName=%s", bucketName), + "bucketName="+bucketName, + "creationTimestamp="+creationTimestamp, functional.GetAWSAccountId(), functional.GetAWSRegion(), functional.GetBicepRecipeRegistry(), diff --git a/test/functional/shared/resources/kubmetadata_cascade_test.go b/test/functional/shared/resources/kubemetadata_cascade_test.go similarity index 100% rename from test/functional/shared/resources/kubmetadata_cascade_test.go rename to test/functional/shared/resources/kubemetadata_cascade_test.go diff --git a/test/functional/shared/resources/testdata/aws-multi-identifier.bicep b/test/functional/shared/resources/testdata/aws-multi-identifier.bicep index f40b60bf30..0498c8d3b9 100644 --- a/test/functional/shared/resources/testdata/aws-multi-identifier.bicep +++ b/test/functional/shared/resources/testdata/aws-multi-identifier.bicep @@ -1,5 +1,6 @@ import aws as aws +param creationTimestamp string param filterName string param logGroupName string @@ -21,7 +22,13 @@ resource metricsFilter 'AWS.Logs/MetricFilter@default' = { resource logGroup 'AWS.Logs/LogGroup@default' = { alias: logGroupName - properties:{ - LogGroupName:logGroupName + properties: { + LogGroupName: logGroupName + Tags: [ + { + Key: 'RadiusCreationTimestamp' + Value: creationTimestamp + } + ] } } diff --git a/test/functional/shared/resources/testdata/aws-s3-bucket.bicep b/test/functional/shared/resources/testdata/aws-s3-bucket.bicep index a37c968f81..52c56f9127 100644 --- a/test/functional/shared/resources/testdata/aws-s3-bucket.bicep +++ b/test/functional/shared/resources/testdata/aws-s3-bucket.bicep @@ -1,5 +1,6 @@ import aws as aws +param creationTimestamp string param bucketName string resource bucket 'AWS.S3/Bucket@default' = { @@ -11,6 +12,10 @@ resource bucket 'AWS.S3/Bucket@default' = { Key: 'testKey' Value: 'testValue' } + { + Key: 'RadiusCreationTimestamp' + Value: creationTimestamp + } ] } } diff --git a/test/functional/shared/resources/testdata/recipes/test-bicep-recipes/extenders-aws-s3-recipe.bicep b/test/functional/shared/resources/testdata/recipes/test-bicep-recipes/extenders-aws-s3-recipe.bicep index a134a9180a..aa7e9d0b99 100644 --- a/test/functional/shared/resources/testdata/recipes/test-bicep-recipes/extenders-aws-s3-recipe.bicep +++ b/test/functional/shared/resources/testdata/recipes/test-bicep-recipes/extenders-aws-s3-recipe.bicep @@ -1,10 +1,17 @@ import aws as aws +param creationTimestamp string param bucketName string resource s3Bucket 'AWS.S3/Bucket@default' = { alias: bucketName properties: { BucketName: bucketName + Tags: [ + { + Key: 'RadiusCreationTimestamp' + Value: creationTimestamp + } + ] } } diff --git a/test/functional/testUtil.go b/test/functional/testUtil.go index ff2bbdff15..1d9ad3fc50 100644 --- a/test/functional/testUtil.go +++ b/test/functional/testUtil.go @@ -26,6 +26,7 @@ import ( "path/filepath" "strings" "testing" + "time" "github.com/google/uuid" contourv1 "github.com/projectcontour/contour/apis/projectcontour/v1" @@ -317,3 +318,13 @@ func WriteBicepParameterFile(t *testing.T, data map[string]any) string { require.NoError(t, err) return file } + +// GetCreationTimestamp returns the elapsed time since the Unix epoch in seconds. +func GetCreationTimestamp() string { + return fmt.Sprintf("%d", time.Now().Unix()) +} + +// GenerateS3BucketName generates a unique S3 bucket name. +func GenerateS3BucketName() string { + return "radiusfunctionaltestbucket-" + uuid.New().String() +}