Skip to content

Commit 49aa7ae

Browse files
dchakrav-githubdiwakar
authored andcommitted
Update README.md
1 parent c6c8083 commit 49aa7ae

File tree

7 files changed

+167
-57
lines changed

7 files changed

+167
-57
lines changed

.gitignore

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,20 @@
1+
# macOS
2+
.DS_Store
3+
._*
4+
5+
# Maven outputs
6+
.classpath
7+
8+
# IntelliJ
9+
*.iml
10+
.idea
11+
out.java
12+
out/
13+
.settings
14+
.project
15+
16+
# auto-generated files
117
target/
2-
~*
18+
19+
# our logs
20+
rpdk.log

.pre-commit-config.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,3 @@ repos:
1515
- --indent=2
1616
- --no-sort-keys
1717
- id: check-merge-conflict
18-
- id: check-yaml
19-
exclude: codebuild-ci.yaml

README.md

Lines changed: 135 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
11
## AWS CloudFormation Java Plugin Test Framework
22

3-
This provides an easier foundation for testing handlers for CRUD along with integated support for KMS. Developers
4-
can easily write sequence of CRUD lifecycle test with expectations and will be tested. There is also a mock based
3+
This provides an easier foundation for testing handlers for CRUD along with integated support for KMS. Developers
4+
can easily write sequence of CRUD lifecycle test with expectations and will be tested. There is also a mock based
55
test based for local unit testing.
66

7-
The framework leverages support for [Named Profiles](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) that allows
8-
developers to test using roles and credentials, to test the exact way in which they expect to work inside CFN for their handlers. Here is the
9-
sample for now this can be used for injecting credentials using the role based profile specified.
7+
The framework leverages support for [Named Profiles](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) that allows
8+
developers to test using roles and credentials, to test the exact way in which they expect to work inside CloudFormation for their handlers. Here is the
9+
sample for now this can be used for injecting credentials using the role based profile specified.
1010

1111
**Sample AWS Named Profile Setup**
1212

13+
```
1314
~/.aws/credentials
1415
...
15-
**cfn-assume-role**
16+
[cfn-assume-role]
1617
aws_access_key_id=[YOUR_ACCESS_KEY_ID]
1718
aws_secret_access_key=[YOUR_SECRET_ACCESS_KEY]
1819
...
19-
20+
2021
~/.aws/config
21-
[profilei **cfn-integration**]
22+
[profilei cfn-integration]
2223
role_arn = arn:aws:iam::<AWS_ACCOUNT_ID>:role/<ROLE_NAME>
23-
source_profile = *cfn-assume-role*
24+
source_profile = cfn-assume-role
25+
```
2426

2527
**Using the named profile for testing**
2628

@@ -30,7 +32,6 @@ sample for now this can be used for injecting credentials using the role based p
3032
<li><u>Create a Managed Policy for the user</u>
3133
Here the credentials section has an user credentials that is provided with only sts:assumeRole
3234
permission. Here is the policy that is associated with cfn-assume-role user in the account.
33-
3435
<pre>
3536
{
3637
"Version": "2012-10-17",
@@ -48,9 +49,9 @@ sample for now this can be used for injecting credentials using the role based p
4849
<li><u>Create a Managed Policy for the services you are testing with</u>
4950
This is needed to test all integration for CRUD+L needed for logs. You can always narrow it down further.
5051
Recommended approach is to define the above policies as customer managed policies in IAM in the account and
51-
associate with the role and users as appropriate. This is an example policy to test CloudWatch LogGroup
52-
and KMS integration
53-
52+
associate with the role and users as appropriate. This is an example policy to test, replace
53+
[INSERT_YOUR_SERVICE] with the service you are integrating with and need KMS integration. E.g.
54+
use _logs_ as the service name for integrating with CloudWatch Logs service.
5455
<pre>
5556
{
5657
"Version": "2012-10-17",
@@ -68,44 +69,41 @@ sample for now this can be used for injecting credentials using the role based p
6869
}
6970
</pre>
7071
</li>
71-
<li><u>Create a user cfn-assume-role with Managed Policy create in (1)</u>
72+
<li><u>Create a user cfn-assume-role with Managed Policy create in Step (1)</u>
7273
Download the access_key, secret_key for this user and add it to the credentials file under
7374
cfn-assume-role
7475
</li>
75-
<li><u>Create cfn-integration role with the con</u></li>
76+
<li><u>Create cfn-integration role with the reference to the managed policy we created above.</u></li>
7677
<li><u>Update your poml.xml</u>
7778
Here is how to use this for unit testing. First add the dependency to you maven <u>pom.xml</u>
78-
79-
<pre>{@code
80-
<!-- for sts support to assume role setup above -->
81-
<dependency>
82-
<groupId>software.amazon.awssdk</groupId>
83-
<artifactId>sts</artifactId>
84-
<version>2.10.91</version>
85-
<scope>test</scope>
86-
</dependency>
87-
88-
<!-- for kms key handling support -->
89-
<dependency>
90-
<groupId>software.amazon.awssdk</groupId>
91-
<artifactId>kms</artifactId>
92-
<version>2.10.91</version>
93-
</dependency>
94-
95-
<dependency>
96-
<groupId>software.amazon.cloudformation.test</groupId>
97-
<artifactId>cloudformation-cli-java-plugin-testing-support</artifactId>
98-
<version>1.0-SNAPSHOT</version>
99-
<scope>test</scope>
100-
</dependency>
101-
}</pre>
79+
<pre><code>
80+
&lt;!-- for sts support to assume role setup above --&gt;
81+
&lt;dependency&gt;
82+
&lt;groupId&gt;software.amazon.awssdk&lt;/groupId&gt;
83+
&lt;artifactId&gt;sts&lt;/artifactId&gt;
84+
&lt;version&gt;2.10.91&lt;/version&gt;
85+
&lt;scope&gt;test&lt;/scope&gt;
86+
&lt;/dependency&gt;
87+
&lt;!-- for kms key handling support --&gt;
88+
&lt;dependency&gt;
89+
&lt;groupId&gt;software.amazon.awssdk&lt;/groupId&gt;
90+
&lt;artifactId&gt;kms&lt;/artifactId&gt;
91+
&lt;version&gt;2.10.91&lt;/version&gt;
92+
&lt;/dependency&gt;
93+
&lt;dependency&gt;
94+
&lt;groupId&gt;software.amazon.cloudformation.test&lt;/groupId&gt;
95+
&lt;artifactId&gt;cloudformation-cli-java-plugin-testing-support&lt;/artifactId&gt;
96+
&lt;version&gt;1.0.0&lt;/version&gt;
97+
&lt;scope&gt;test&lt;/scope&gt;
98+
&lt;/dependency&gt;
99+
</code></pre>
102100
</li>
103101
</ol>
104102

105103
<b>How to use it?</b>
106104
<p>
107105
Sample code illustrating how to use this setup with KMS. To make scheduling the key for delete in case of abort to
108-
testing the key is aliased using the alias name [KEY_ALIAS](src/software/amazon/cloudformation/test/KMSKeyEnabledServiceIntegrationTestBase.java)
106+
testing the key is aliased using the alias name [KEY_ALIAS](src/main/software/amazon/cloudformation/test/KMSKeyEnabledServiceIntegrationTestBase.java)
109107
The test when it runs to completion will automatically move the KMS key for delete. If test is rerun
110108
the KMS key will be made active again for the duration of he test run and disable and scheduled to be deleted.
111109
Regardless of how many times we run these tests there is only one key with the alias managed in the account.
@@ -114,8 +112,8 @@ To ensure that this test does not run for build environments like Travis etc. we
114112
{@link org.junit.jupiter.api.condition.EnabledIfSystemProperty}. To run the test with maven we would
115113
use
116114

117-
```
118-
mvn -Ddesktop=true test
115+
```sh
116+
mvn -Ddesktop=true test
119117
```
120118

121119
to run the test code shown below
@@ -156,8 +154,8 @@ to run the test code shown below
156154
String kmsKeyArn = getKmsKeyArn();
157155
// Add your service to use KMS key
158156
addServiceAccess("logs", kmsKeyId);
159-
model.setKMSKey(kmsKeyArn);
160-
ProgressEvent&lt;ResourceModel, CallbackContext&gt; event = new UpdateHandler()
157+
model.setKMSKeyArn(kmsKeyArn);
158+
ProgressEvent<ResourceModel, CallbackContext> event = new UpdateHandler()
161159
.handleRequest(getProxy(), createRequest(model, current), null, getLoggerProxy());
162160
assertThat(event.isSuccess()).isTrue();
163161
model = event.getResourceModel();
@@ -167,10 +165,101 @@ to run the test code shown below
167165
}
168166
```
169167

168+
**Example Lifecycle Testing with KMS Support**
169+
170+
```java
171+
@ExtendWith(InjectProfileCredentials.class)
172+
@EnabledIfSystemProperty(named = "desktop", matches = "true")
173+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
174+
public class CRUDLifecycleTest extends CRUDLifecycleTestBase<ResourceModel, CallbackContext> {
175+
176+
private final ReadHandler readHandler = new ReadHandler();
177+
private final DeleteHandler deleteHandler = new DeleteHandler();
178+
private final UpdateHandler updateHandler = new UpdateHandler();
179+
private final CreateHandler createHandler = new CreateHandler();
180+
private final Map<Action, HandlerInvoke<ResourceModel, CallbackContext>> handlers =
181+
ImmutableMap.<Action, HandlerInvoke<ResourceModel, CallbackContext>>builder()
182+
.put(Action.CREATE, createHandler::handleRequest)
183+
.put(Action.READ, readHandler::handleRequest)
184+
.put(Action.UPDATE, updateHandler::handleRequest)
185+
.put(Action.DELETE, deleteHandler::handleRequest)
186+
.build();
187+
188+
private final String logGroupName = "logGroup-TEST-DELETE-" + UUID.randomUUID().toString();
189+
190+
static final Delay override = Constant.of().delay(Duration.ofSeconds(1))
191+
.timeout(Duration.ofSeconds(10)).build();
192+
public CRUDLifecycleTest(@InjectSessionCredentials(profile = "cfn-integ") AwsSessionCredentials sessionCredentials) {
193+
super(sessionCredentials, ((apiCall, provided) -> override));
194+
}
195+
196+
@Override
197+
protected List<ResourceModels> testSeed() {
198+
final String kmsKeyId = getKmsKeyId();
199+
final String kmsKeyArn = getKmsKeyArn();
200+
List<ResourceModels> testGroups = new ArrayList<>(1);
201+
202+
testGroups.add(
203+
newStepBuilder()
204+
.group("simple_normal")
205+
.create(ResourceModel.builder().logGroupName(logGroupName).build())
206+
.update(ResourceModel.builder().logGroupName(logGroupName).retentionInDays(7).build())
207+
.delete(ResourceModel.builder().logGroupName(logGroupName).build()));
208+
209+
testGroups.add(
210+
newStepBuilder()
211+
.group("complex_with_failed_kms")
212+
.create(ResourceModel.builder().logGroupName(logGroupName).build())
213+
.createFail(ResourceModel.builder().logGroupName(logGroupName).build())
214+
.updateFail(ResourceModel.builder().logGroupName(logGroupName).retentionInDays(10).build())
215+
.update(ResourceModel.builder().logGroupName(logGroupName).retentionInDays(7).build())
216+
.updateFail(
217+
ResourceModel.builder().logGroupName(logGroupName).retentionInDays(7)
218+
.kMSKey("kmsKeyDoesNotExist").build())
219+
//
220+
// can not access logs service
221+
//
222+
.updateFail(() -> {
223+
removeServiceAccess("logs", kmsKeyId, Region.US_EAST_2);
224+
return ResourceModel.builder().logGroupName(logGroupName).retentionInDays(7)
225+
.kMSKey(kmsKeyArn).build();
226+
})
227+
.update(() -> {
228+
addServiceAccess("logs", kmsKeyId, Region.US_EAST_2);
229+
return ResourceModel.builder().logGroupName(logGroupName).retentionInDays(7)
230+
.kMSKey(kmsKeyArn).build();
231+
})
232+
.delete(ResourceModel.builder().logGroupName(logGroupName).build()));
233+
return testGroups;
234+
}
235+
236+
@Override
237+
protected Map<Action, HandlerInvoke<ResourceModel, CallbackContext>> handlers() {
238+
return handlers;
239+
}
240+
241+
@Override
242+
protected CallbackContext context() {
243+
return new CallbackContext();
244+
}
245+
}
246+
247+
```
248+
170249
**See Also**
171250

172-
software.amazon.cloudformation.test.KMSKeyEnabledServiceIntegrationTestBase
173-
software.amazon.cloudformation.test.AbstractLifecycleTestBase
251+
*Lifecycle Testing*
252+
253+
These classes use the annotation and named profiles described above to make testing against live AWS services easy.
254+
255+
[software.amazon.cloudformation.test.KMSKeyEnabledServiceIntegrationTestBase](src/main/software/amazon/cloudformation/test/KMSKeyEnabledServiceIntegrationTestBase.java)
256+
[software.amazon.cloudformation.test.AbstractLifecycleTestBase](src/main/software/amazon/cloudformation/test/AbstractLifecycleTestBase.java)
257+
258+
*Local Unit Testing*
259+
260+
This class provides the step plumbing
261+
262+
[software.amazon.cloudformation.test.AbstractMockTestBase](src/main/software/amazon/cloudformation/test/AbstractMockTestBase.java)
174263

175264

176265
## License

buildspec.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,3 @@ phases:
1010
commands:
1111
- pre-commit run --all-files
1212
- mvn clean verify --no-transfer-progress
13-
- mvn clean verify --no-transfer-progress

pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<groupId>software.amazon.cloudformation.test</groupId>
99
<artifactId>cloudformation-cli-java-plugin-testing-support</artifactId>
1010
<name>aws-cloudformation-local-test</name>
11-
<version>1.0</version>
11+
<version>1.0.0</version>
1212
<packaging>jar</packaging>
1313

1414
<properties>
@@ -81,7 +81,7 @@
8181
<dependency>
8282
<groupId>software.amazon.cloudformation</groupId>
8383
<artifactId>aws-cloudformation-rpdk-java-plugin</artifactId>
84-
<version>1.0.3</version>
84+
<version>1.0.4</version>
8585
</dependency>
8686

8787
<dependency>
@@ -145,7 +145,7 @@
145145
<version>${maven-javadoc-plugin.version}</version>
146146
<configuration>
147147
<show>public</show>
148-
<bottom><![CDATA[Copyright &#169; 2019 Amazon Web Services, Inc. All Rights Reserved.]]></bottom>
148+
<bottom><![CDATA[Copyright &#169; Amazon Web Services, Inc. All Rights Reserved.]]></bottom>
149149
</configuration>
150150
<executions>
151151
<execution>

src/main/java/software/amazon/cloudformation/test/CRUDLifecycleTestBase.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import static org.assertj.core.api.Assertions.assertThat;
88

9-
import org.opentest4j.AssertionFailedError;
109
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
1110
import software.amazon.cloudformation.Action;
1211
import software.amazon.cloudformation.exceptions.ResourceNotFoundException;
@@ -27,8 +26,6 @@
2726
import java.util.concurrent.atomic.AtomicReference;
2827
import java.util.function.Supplier;
2928
import java.util.stream.Collectors;
30-
import java.util.stream.Stream;
31-
3229

3330
@NotThreadSafe
3431
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@@ -230,6 +227,15 @@ final List<DynamicTest> CRUD_Tests() {
230227
}
231228
)
232229
);
230+
//
231+
// https://junit.org/junit5/docs/5.0.2/api/org/junit/jupiter/api/DynamicTest.html. This is verifying
232+
// that reading the state after Create(C)/Update(U) operation is consistent with the desired state that we
233+
// sent in. If the C/U is expected to fail, then we check to see that the previous desired state is
234+
// indeed equal. The other thing this forces is it ensures that C/U handlers do delegate to Read(R)
235+
// handlers post application of configuration. This prevents the set of bugs like missing primary
236+
// identifiers etc. that R always provides ensuring all read-only attributes are being communicated
237+
// post a C/U operation.
238+
//
233239
if (step.action != Action.DELETE) {
234240
tests.add(
235241
//

src/main/java/software/amazon/cloudformation/test/package-info.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
* Download the access_key, secret_key for this user and add it to the credentials file under
7575
* cfn-assume-role
7676
* </li>
77-
* <li><u>Create cfn-integration role with the con</u></li>
77+
* <li><u>Create cfn-integration role with the reference to the managed policy we created above.</u></li>
7878
* <li><u>Update your poml.xml</u>
7979
* Here is how to use this for unit testing. First add the dependency to you maven <u>pom.xml</u>
8080
*

0 commit comments

Comments
 (0)