Skip to content

Commit

Permalink
Merge pull request #10 from mirelap-amazon/master
Browse files Browse the repository at this point in the history
Add support for AgentPermissions in CloudFormation (Schema + CreateHandler)
  • Loading branch information
gimki authored May 26, 2020
2 parents 0ddbc23 + 1713e9d commit ee0cbc5
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 76 deletions.
2 changes: 1 addition & 1 deletion aws-codeguruprofiler-profilinggroup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pre-commit run --all-files && AWS_REGION=us-east-1 mvn clean verify package

5. Create a sample CloudFormation stack that defines a profiling group:
```
aws cloudformation create-stack --region us-east-1 --template-body "file://sample-template.json" --stack-name "sample-profiling-group-resource-creation"
aws cloudformation create-stack --region us-east-1 --template-body "file://sample-template.json" --stack-name "sample-profiling-group-resource-creation" --capabilities CAPABILITY_IAM
```

6. Validate the creation of the profiling group!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
"Arn": {
"type": "string",
"pattern": "^arn:aws(-(cn|gov))?:[a-z-]+:(([a-z]+-)+[0-9]+):([0-9]{12}):[^.]+$"
},
"ArnIam": {
"type": "string",
"pattern": "^arn:aws(-(cn|gov))?:iam::([0-9]{12}):[^.]+$"
}
},
"properties": {
Expand All @@ -16,6 +20,23 @@
"maxLength": 255,
"pattern": "^[\\w-]+$"
},
"AgentPermissions": {
"description": "The agent permissions attached to this profiling group.",
"type": "object",
"additionalProperties": false,
"required": [
"Principals"
],
"properties": {
"Principals": {
"description": "The principals for the agent permissions.",
"type": "array",
"items": {
"$ref": "#/definitions/ArnIam"
}
}
}
},
"Arn": {
"description": "The Amazon Resource Name (ARN) of the specified profiling group.",
"$ref": "#/definitions/Arn",
Expand All @@ -40,7 +61,8 @@
"handlers": {
"create": {
"permissions": [
"codeguru-profiler:CreateProfilingGroup"
"codeguru-profiler:CreateProfilingGroup",
"codeguru-profiler:PutPermission"
]
},
"read": {
Expand Down
1 change: 1 addition & 0 deletions aws-codeguruprofiler-profilinggroup/resource-role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Resources:
- "codeguru-profiler:DeleteProfilingGroup"
- "codeguru-profiler:DescribeProfilingGroup"
- "codeguru-profiler:ListProfilingGroups"
- "codeguru-profiler:PutPermission"
Resource: "*"
Outputs:
ExecutionRoleArn:
Expand Down
37 changes: 37 additions & 0 deletions aws-codeguruprofiler-profilinggroup/sample-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,43 @@
"Properties": {
"ProfilingGroupName": "MySampleProfilingGroup"
}
},
"MyProfilingGroupWithAgentPermissions": {
"Type": "AWS::CodeGuruProfiler::ProfilingGroup",
"Properties": {
"ProfilingGroupName": "MySampleProfilingGroupWithAgentPermissions",
"AgentPermissions": {
"Principals": [
{
"Fn::GetAtt": [
"MyProfilingGroupRole",
"Arn"
]
}
]
}
}
},
"MyProfilingGroupRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"ec2.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
}
}
}
},
"Outputs": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package software.amazon.codeguruprofiler.profilinggroup;

import software.amazon.awssdk.services.codeguruprofiler.CodeGuruProfilerClient;
import software.amazon.awssdk.services.codeguruprofiler.model.CodeGuruProfilerException;
import software.amazon.awssdk.services.codeguruprofiler.model.ConflictException;
import software.amazon.awssdk.services.codeguruprofiler.model.CreateProfilingGroupRequest;
import software.amazon.awssdk.services.codeguruprofiler.model.DeleteProfilingGroupRequest;
import software.amazon.awssdk.services.codeguruprofiler.model.InternalServerException;
import software.amazon.awssdk.services.codeguruprofiler.model.PutPermissionRequest;
import software.amazon.awssdk.services.codeguruprofiler.model.ServiceQuotaExceededException;
import software.amazon.awssdk.services.codeguruprofiler.model.ThrottlingException;
import software.amazon.awssdk.services.codeguruprofiler.model.ValidationException;
Expand All @@ -17,6 +20,12 @@
import software.amazon.cloudformation.proxy.ProgressEvent;
import software.amazon.cloudformation.proxy.ResourceHandlerRequest;

import java.util.List;
import java.util.Optional;

import static java.lang.String.format;
import static software.amazon.awssdk.services.codeguruprofiler.model.ActionGroup.AGENT_PERMISSIONS;

public class CreateHandler extends BaseHandler<CallbackContext> {

private final CodeGuruProfilerClient profilerClient = CodeGuruProfilerClientBuilder.create();
Expand All @@ -28,21 +37,67 @@ public ProgressEvent<ResourceModel, CallbackContext> handleRequest(
final CallbackContext callbackContext,
final Logger logger) {

final ResourceModel model = request.getDesiredResourceState();
final String awsAccountId = request.getAwsAccountId();
final String idempotencyToken = request.getClientRequestToken();

try {
CreateProfilingGroupRequest createProfilingGroupRequest = CreateProfilingGroupRequest.builder()
.profilingGroupName(model.getProfilingGroupName())
.clientToken(idempotencyToken)
.build();
final ResourceModel model = request.getDesiredResourceState();
final String pgName = model.getProfilingGroupName();

CreateProfilingGroupRequest createProfilingGroupRequest = CreateProfilingGroupRequest.builder()
.profilingGroupName(pgName)
.clientToken(request.getClientRequestToken())
.build();
safelyInvokeApi(() -> {
proxy.injectCredentialsAndInvokeV2(createProfilingGroupRequest, profilerClient::createProfilingGroup);
});
logger.log(format("%s [%s] for accountId [%s] has been successfully created!", ResourceModel.TYPE_NAME, pgName, awsAccountId));

logger.log(String.format("%s [%s] for accountId [%s] has been successfully created!", ResourceModel.TYPE_NAME, model.getProfilingGroupName(), awsAccountId));
Optional<List<String>> principals = principalsForAgentPermissionsFrom(model);
if (principals.isPresent()) {
putAgentPermissions(proxy, logger, pgName, principals.get(), awsAccountId);
logger.log(format("%s [%s] for accountId [%s] has been successfully updated with agent permissions!",
ResourceModel.TYPE_NAME, pgName, awsAccountId));
}

return ProgressEvent.defaultSuccessHandler(model);
}

return ProgressEvent.defaultSuccessHandler(model);
private void putAgentPermissions(final AmazonWebServicesClientProxy proxy, final Logger logger,
final String pgName, final List<String> principals, final String awsAccountId) {
PutPermissionRequest putPermissionRequest = PutPermissionRequest.builder()
.profilingGroupName(pgName)
.actionGroup(AGENT_PERMISSIONS)
.principals(principals)
.build();

safelyInvokeApi(() -> {
try {
proxy.injectCredentialsAndInvokeV2(putPermissionRequest, profilerClient::putPermission);
} catch (CodeGuruProfilerException putPermissionException) {
logger.log(format("%s [%s] for accountId [%s] has failed when updating the agent permissions, trying to delete the profiling group!",
ResourceModel.TYPE_NAME, pgName, awsAccountId));
deleteProfilingGroup(proxy, logger, pgName, awsAccountId, putPermissionException);
throw putPermissionException;
}
});
}

private void deleteProfilingGroup(AmazonWebServicesClientProxy proxy, Logger logger,
String pgName, String awsAccountId, CodeGuruProfilerException putPermissionException) {
DeleteProfilingGroupRequest deletePgRequest = DeleteProfilingGroupRequest.builder().profilingGroupName(pgName).build();
try {
proxy.injectCredentialsAndInvokeV2(deletePgRequest, profilerClient::deleteProfilingGroup);
logger.log(format("%s [%s] for accountId [%s] has succeeded when deleting the profiling group!",
ResourceModel.TYPE_NAME, pgName, awsAccountId));
} catch (CodeGuruProfilerException deleteException) {
logger.log(format("%s [%s] for accountId [%s] has failed when deleting the profiling group!",
ResourceModel.TYPE_NAME, pgName, awsAccountId));
putPermissionException.addSuppressed(deleteException);
throw putPermissionException;
}
}

private static void safelyInvokeApi(final Runnable lambda) {
try {
lambda.run();
} catch (ConflictException e) {
throw new CfnAlreadyExistsException(e);
} catch (InternalServerException e) {
Expand All @@ -55,4 +110,14 @@ public ProgressEvent<ResourceModel, CallbackContext> handleRequest(
throw new CfnInvalidRequestException(ResourceModel.TYPE_NAME + e.getMessage(), e);
}
}

private static Optional<List<String>> principalsForAgentPermissionsFrom(final ResourceModel model) {
if (model.getAgentPermissions() == null) {
return Optional.empty();
}
if (model.getAgentPermissions().getPrincipals() == null) {
return Optional.empty();
}
return Optional.of(model.getAgentPermissions().getPrincipals());
}
}
Loading

0 comments on commit ee0cbc5

Please sign in to comment.