Skip to content
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

Aws2-lambda: Add event source mapping test #2749 #3186

Merged
merged 1 commit into from
Oct 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions integration-test-groups/aws2/aws2-lambda/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
<artifactId>iam</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sqs</artifactId>
<scope>test</scope>
</dependency>

<!-- The following dependencies guarantee that this module is built after them. You can update them by running `mvn process-resources -Pformat -N` from the source tree root directory -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
import org.jboss.logging.Logger;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.lambda.model.AliasConfiguration;
import software.amazon.awssdk.services.lambda.model.CreateEventSourceMappingResponse;
import software.amazon.awssdk.services.lambda.model.EventSourceMappingConfiguration;
import software.amazon.awssdk.services.lambda.model.FunctionConfiguration;
import software.amazon.awssdk.services.lambda.model.GetAliasRequest;
import software.amazon.awssdk.services.lambda.model.GetAliasResponse;
Expand All @@ -54,6 +56,7 @@
import software.amazon.awssdk.services.lambda.model.LastUpdateStatus;
import software.amazon.awssdk.services.lambda.model.ListAliasesRequest;
import software.amazon.awssdk.services.lambda.model.ListAliasesResponse;
import software.amazon.awssdk.services.lambda.model.ListEventSourceMappingsResponse;
import software.amazon.awssdk.services.lambda.model.ListFunctionsResponse;
import software.amazon.awssdk.services.lambda.model.ListTagsResponse;
import software.amazon.awssdk.services.lambda.model.ListVersionsByFunctionResponse;
Expand All @@ -70,6 +73,9 @@ public class Aws2LambdaResource {
@ConfigProperty(name = "aws-lambda.role-arn")
String roleArn;

@ConfigProperty(name = "aws-lambda.event-source-arn")
String eventSourceArn;

@Inject
ProducerTemplate producerTemplate;

Expand Down Expand Up @@ -333,6 +339,61 @@ public List<String> listVersions(@QueryParam("functionName") String functionName
}
}

@Path("/event-source-mapping/create")
@POST
@Produces(MediaType.TEXT_PLAIN)
public Response createEventSourceMapping(@QueryParam("functionName") String functionName) throws Exception {
try {
CreateEventSourceMappingResponse response = producerTemplate
.requestBodyAndHeader(
componentUri(functionName, Lambda2Operations.createEventSourceMapping),
null,
Lambda2Constants.EVENT_SOURCE_ARN,
eventSourceArn,
CreateEventSourceMappingResponse.class);

return Response.created(new URI("https://camel.apache.org/")).entity(response.uuid()).build();
} catch (Exception e) {
LOG.info("Exception caught in event-source-mapping/create", e);
LOG.info("Exception cause in event-source-mapping/create", e.getCause());
throw e;
}
}

@Path("/event-source-mapping/list")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Map<String, String> listEventSourceMappings(@QueryParam("functionName") String functionName) {
try {
ListEventSourceMappingsResponse response = producerTemplate.requestBody(
componentUri(functionName, Lambda2Operations.listEventSourceMapping),
null,
ListEventSourceMappingsResponse.class);
return response.eventSourceMappings().stream()
.collect(Collectors.toMap(EventSourceMappingConfiguration::uuid, EventSourceMappingConfiguration::state));
} catch (Exception e) {
LOG.info("Exception caught in event-source-mapping/list", e);
LOG.info("Exception cause in event-source-mapping/list", e.getCause());
throw e;
}
}

@Path("/event-source-mapping/delete")
@DELETE
public void deleteEventSourceMapping(@QueryParam("eventSourceMappingUuid") String eventSourceMappingUuid) {
try {
producerTemplate.requestBodyAndHeader(
componentUri(null, Lambda2Operations.deleteEventSourceMapping),
null,
Lambda2Constants.EVENT_SOURCE_UUID,
eventSourceMappingUuid);
} catch (Exception e) {
LOG.info("Exception caught in event-source-mapping/delete", e);
LOG.info("Exception cause in event-source-mapping/delete", e.getCause());
throw e;
}
}

private static String componentUri(String functionName, Lambda2Operations operation) {
return "aws2-lambda:" + functionName + "?operation=" + operation;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
Expand Down Expand Up @@ -85,6 +86,7 @@ public void performingOperationsOnLambdaFunctionShouldSucceed() {
createGetDeleteAndListAliasShouldSucceed(functionName);
createListDeleteFunctionTagsShouldSucceed(functionName, functionArn);
publishAndListVersionShouldSucceed(functionName);
createListAndDeleteEventSourceMappingShouldSucceed(functionName);

RestAssured.given()
.delete("/aws2-lambda/function/delete/" + functionName)
Expand Down Expand Up @@ -235,6 +237,68 @@ public void publishAndListVersionShouldSucceed(String functionName) {
.body("$", hasItem("1"));
}

public void createListAndDeleteEventSourceMappingShouldSucceed(String functionName) {
String eventSourceMappingUuid = RestAssured.given()
.queryParam("functionName", functionName)
.post("/aws2-lambda/event-source-mapping/create")
.then()
.statusCode(201)
.extract().asString();
assertNotNull(eventSourceMappingUuid);

await().pollDelay(10L, TimeUnit.SECONDS)
.pollInterval(1L, TimeUnit.SECONDS)
.atMost(120, TimeUnit.SECONDS)
.until(() -> {
Map<String, String> eventSourceMappingStatuses = RestAssured.given()
.queryParam("functionName", functionName)
.get("/aws2-lambda/event-source-mapping/list")
.then()
.statusCode(200)
.extract().jsonPath().getMap("$", String.class, String.class);

if (!eventSourceMappingStatuses.containsKey(eventSourceMappingUuid)) {
LOG.infof("Found no event source mapping with id '%s', so retrying", eventSourceMappingUuid);
return false;
}
String status = eventSourceMappingStatuses.get(eventSourceMappingUuid);
if (!"Enabled".equals(status)) {
String format = "The event source mapping with id '%s' has status '%s', so retrying";
LOG.infof(format, eventSourceMappingUuid, status);
return false;
} else {
String format = "The event source mapping with id '%s' has status 'Enabled', so moving to next step";
LOG.infof(format, eventSourceMappingUuid);
return true;
}
});

RestAssured.given()
.queryParam("eventSourceMappingUuid", eventSourceMappingUuid)
.delete("/aws2-lambda/event-source-mapping/delete")
.then()
.statusCode(204);

await().pollDelay(16L, TimeUnit.SECONDS)
.pollInterval(1L, TimeUnit.SECONDS)
.atMost(120, TimeUnit.SECONDS)
.until(() -> {
Map<String, String> eventSourceMappingStatuses = RestAssured.given()
.queryParam("functionName", functionName)
.get("/aws2-lambda/event-source-mapping/list")
.then()
.statusCode(200)
.extract().jsonPath().getMap("$", String.class, String.class);

if (eventSourceMappingStatuses.containsKey(eventSourceMappingUuid)) {
String format = "The event source mapping with id '%s' is still present with status '%s', so retrying";
LOG.infof(format, eventSourceMappingUuid, eventSourceMappingStatuses.get(eventSourceMappingUuid));
return false;
}
return true;
});
}

static byte[] createInitialLambdaFunctionZip() {
return createLambdaFunctionZip(INITIAL_FUNCTION_SOURCE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,24 @@
import org.apache.commons.lang3.RandomStringUtils;
import org.testcontainers.containers.localstack.LocalStackContainer.Service;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.AttachRolePolicyRequest;
import software.amazon.awssdk.services.iam.model.CreateRoleRequest;
import software.amazon.awssdk.services.iam.model.DeleteRoleRequest;
import software.amazon.awssdk.services.iam.model.DetachRolePolicyRequest;
import software.amazon.awssdk.services.iam.model.GetRoleRequest;
import software.amazon.awssdk.services.iam.waiters.IamWaiter;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.CreateQueueRequest;
import software.amazon.awssdk.services.sqs.model.DeleteQueueRequest;
import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest;
import software.amazon.awssdk.services.sqs.model.GetQueueAttributesResponse;
import software.amazon.awssdk.services.sqs.model.QueueAttributeName;

public class Aws2LambdaTestEnvCustomizer implements Aws2TestEnvCustomizer {

@Override
public Service[] localstackServices() {
return new Service[] { Service.LAMBDA, Service.IAM };
return new Service[] { Service.LAMBDA, Service.IAM, Service.SQS };
}

@Override
Expand All @@ -43,6 +51,7 @@ public Service[] exportCredentialsForLocalstackServices() {
@Override
public void customize(Aws2TestEnvContext envContext) {

// Customize a role needed to execute AWS Lambda functions that can moreover be triggered by an SQS queue event source
final String id = RandomStringUtils.randomAlphanumeric(16).toLowerCase(Locale.ROOT);
final String roleName = "cq-lambda-" + id;

Expand All @@ -67,10 +76,35 @@ public void customize(Aws2TestEnvContext envContext) {
.role().arn();
envContext.closeable(() -> iamClient.deleteRole(DeleteRoleRequest.builder().roleName(roleName).build()));

final String policyArn = "arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole";
AttachRolePolicyRequest attachRolePolicyRequest = AttachRolePolicyRequest.builder()
.roleName(roleName)
.policyArn(policyArn)
.build();
iamClient.attachRolePolicy(attachRolePolicyRequest);
envContext.closeable(() -> iamClient
.detachRolePolicy(DetachRolePolicyRequest.builder().roleName(roleName).policyArn(policyArn).build()));

try (IamWaiter w = iamClient.waiter()) {
w.waitUntilRoleExists(GetRoleRequest.builder().roleName(roleName).build());
}

envContext.property("aws-lambda.role-arn", roleArn);

// Create an SQS queue that will serve as event source in order to test event source mappings
final String queueName = "camel-quarkus-" + RandomStringUtils.randomAlphanumeric(49).toLowerCase(Locale.ROOT);
final SqsClient sqsClient = envContext.client(Service.SQS, SqsClient::builder);

final CreateQueueRequest createQueueRequest = CreateQueueRequest.builder().queueName(queueName).build();
final String queueUrl = sqsClient.createQueue(createQueueRequest).queueUrl();
envContext.closeable(() -> sqsClient.deleteQueue(DeleteQueueRequest.builder().queueUrl(queueUrl).build()));

GetQueueAttributesRequest getQueueAttributesRequest = GetQueueAttributesRequest.builder()
.attributeNames(QueueAttributeName.QUEUE_ARN)
.queueUrl(queueUrl)
.build();
GetQueueAttributesResponse getQueueAttributesResponse = sqsClient.getQueueAttributes(getQueueAttributesRequest);
String queueArn = getQueueAttributesResponse.attributes().get(QueueAttributeName.QUEUE_ARN);
envContext.property("aws-lambda.event-source-arn", queueArn);
}
}