Package software.amazon.cloudformation.test

The purpose of this package is to make local testing as painless as possible. It also provides a each model to completely test the lifecycle CRUDL for a resource in unit testing. This makes debugging integration issues easy inside your IDE of choice. For making actual service calls we use AWS cli's named profiles model using roles. This mimics the type of credentials you handlers will receive during actual calls. For named profiles setup please read Named Profiles. The recommended approach to is as shown below

Sample example of AWS CLI setup

     ~/.aws/credentials
     ...
     [cfn-assume-role]
     aws_access_key_id=[YOUR_ACCESS_KEY_ID]
     aws_secret_access_key=[YOUR_SECRET_ACCESS_KEY]
     ...

     ~/.aws/config
     [profile cfn-integration]
     role_arn = arn:aws:iam::0123456789012:role/cfn-integration
     source_profile = cfn-assume-role
How to setup IAM managed policies, user and role credentials for above setup
  1. Create a Managed Policy for the user Here the credentials section has an user credentials that is provided with only sts:assumeRole permission. Here is the policy that is associated with cfn-assume-role user in the account.
             {
                 "Version": "2012-10-17",
                 "Statement": [
                     {
                         "Sid": "VisualEditor0",
                         "Effect": "Allow",
                         "Action": "sts:AssumeRole",
                         "Resource": "*"
                     }
                 ]
             }
             
  2. Create a Managed Policy for the services you are testing with This is needed to test all integration for CRUD+L needed for logs. You can always narrow it down further. Recommended approach is to define the above policies as customer managed policies in IAM in the account and associate with the role and users as appropriate. This is an example policy to test CloudWatch LogGroup and KMS integration
                 {
                     "Version": "2012-10-17",
                     "Statement": [
                         {
                             "Sid": "VisualEditor0",
                             "Effect": "Allow",
                             "Action": [
                                 "kms:*",
                                 "[INSERT_YOUR_SERVICE]:*"
                             ],
                             "Resource": "*"
                         }
                     ]
                 }
              
  3. Create a user cfn-assume-role with Managed Policy create in (1) Download the access_key, secret_key for this user and add it to the credentials file under cfn-assume-role
  4. Create cfn-integration role with the reference to the managed policy we created above.
  5. Update your poml.xml Here is how to use this for unit testing. First add the dependency to you maven pom.xml
    
                     <!-- for sts support to assume role setup above -->
                     <dependency>
                         <groupId>software.amazon.awssdk</groupId>
                         <artifactId>sts</artifactId>
                         <version>2.10.91</version>
                         <scope>test</scope>
                     </dependency>
    
                    <!-- for kms key handling support -->
                    <dependency>
                         <groupId>software.amazon.awssdk</groupId>
                         <artifactId>kms</artifactId>
                         <version>2.10.91</version>
                    </dependency>
    
                     <dependency>
                         <groupId>software.amazon.cloudformation.test</groupId>
                         <artifactId>cloudformation-cli-java-plugin-testing-support</artifactId>
                         <version>1.0-SNAPSHOT</version>
                         <scope>test</scope>
                     </dependency>
                
How to use it?

Sample code illustrating how to use this setup with KMS. To make scheduling the key for delete in case of abort to testing the key is aliased using the alias name KMSKeyEnabledServiceIntegrationTestBase.KEY_ALIAS The test when it runs to completion will automatically move the KMS key for delete. If test is rerun the KMS key will be made active again for the duration of he test run and disable and scheduled to be deleted. Regardless of how many times we run these tests there is only one key with the alias managed in the account. To ensure that this test does not run for build environments we Enable is using system properties using EnabledIfSystemProperty. To run the same shown below with maven we would use

     mvn -Ddesktop=true test
 
 
     package software.amazon.logs.loggroup;

     import org.junit.jupiter.api.*;
     import static org.assertj.core.api.Assertions.assertThat;

     import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
     import software.amazon.cloudformation.proxy.*;
     import software.amazon.cloudformation.test.*;

     @TestMethodOrder(MethodOrderer.OrderAnnotation.class) // we order the tests to follows C to U to D
     @ExtendWith(InjectProfileCredentials.class) // extend with profile based credentials
     @EnabledIfSystemProperty(named = "desktop", matches = "true")
     @TestInstance(TestInstance.Lifecycle.PER_CLASS) // IMP PER_CLASS
     public class LifecycleTest extends KMSKeyEnabledServiceIntegrationTestBase {

         //
         // At the annotation software.amazon.cloudformation.test.annotations.InjectSessionCredentials to the
         // constructor. This will inject the role's credentials
         //
         public LifecycleTest(@InjectSessionCredentials(profile = "cfn-integration") AwsSessionCredentials awsCredentials) {
              super(awsCredentials, ((apiCall, provided) -> override));
         }
         ...
         ...
         @Order(300)
         @Test
         void addValidKMS() {
             final ResourceModel current = ResourceModel.builder().arn(model.getArn())
                 .logGroupName(model.getLogGroupName()).retentionInDays(model.getRetentionInDays()).build();
             // Access a KMS key. The test ensures to only create one key and recycles despite any number of runs
             String kmsKeyId = getKmsKeyId();
             String kmsKeyArn = getKmsKeyArn();
             // Add your service to use KMS key
             addServiceAccess("logs", kmsKeyId);
             model.setKMSKey(kmsKeyArn);
             ProgressEvent<ResourceModel, CallbackContext> event = new UpdateHandler()
                 .handleRequest(getProxy(), createRequest(model, current), null, getLoggerProxy());
             assertThat(event.isSuccess()).isTrue();
             model = event.getResourceModel();
         }
         ...
         ...
     }
 
 
See Also:
KMSKeyEnabledServiceIntegrationTestBase, AbstractLifecycleTestBase
  • Interface Summary 
    Interface Description
    HandlerInvoke<ModelT,​CallbackT extends software.amazon.cloudformation.proxy.StdCallbackContext>  
  • Class Summary 
    Class Description
    AbstractLifecycleTestBase
    Abstract base class that does some of the mocking and proxy setup that you need for testing each of your handlers consistently in order for CRUD + L
    AbstractMockTestBase<CLIENT extends software.amazon.awssdk.core.SdkClient>  
    CRUDLifecycleTestBase<ResourceModelT,​CallbackT extends software.amazon.cloudformation.proxy.StdCallbackContext>  
    InjectProfileCredentials  
    KMSKeyEnabledServiceIntegrationTestBase
    Abstract base class we can derive from that adds support for a KMS key and has helpers methods to update Key policy for accessing the key for service principal