Reputation: 31
Is there a way we can (integration) test a Lambda for AWS using testcontainers & the following LocalStackContainer:
new LocalStackContainer(DockerImageName.parse('localstack/localstack:0.11.6'))
.withServices(LocalStackContainer.Service.LAMBDA)
Specifically, how do I "upload" the Lambda to the locally running service, so that I can then trigger & test it?
Upvotes: 2
Views: 4915
Reputation: 12021
Once the LocalStackContainer
is started, you can execute commands inside the container to set up your infrastructure. LocalStack provides the awslocal
binary to create any AWS resources.
Inside your test, you can then have a @BeforeAll
step where you use your container reference and create everything.
The following example does this for S3 and SQS (reference from this blog post):
@Testcontainers
@SpringBootTest
public class YourLocalStackIT {
@Container
static LocalStackContainer localStack = new LocalStackContainer("0.11.6")
.withServices(S3, SQS)
.withEnv("DEFAULT_REGION", "eu-central-1");
@BeforeAll
static void beforeAll() throws IOException, InterruptedException {
localStack.execInContainer("awslocal", "sqs", "create-queue", "--queue-name", QUEUE_NAME);
localStack.execInContainer("awslocal", "s3", "mb", "s3://" + BUCKET_NAME);
}
private static final String QUEUE_NAME = "order-event-test-queue";
private static final String BUCKET_NAME = "order-event-test-bucket";
// ... actual test
}
You can use this as a template to now prepare your AWS Lambda function.
Regarding the source code upload, I guess it makes sense to mount or copy the .zip
version of your Lambda also to your container to then access it inside.
A NodeJS lambda could be created like the following:
awslocal lambda create-function \
--region eu-central1 \
--function-name your-api \
--runtime nodejs8.10 \
--handler lambda.apiHandler \
--memory-size 128 \
--zip-file fileb://api-handler.zip
Upvotes: 2
Reputation: 31
I've managed to setup Localstack and a MySql testcontainer (in groovy spock) like so:
@Shared
public static Network network = Network.newNetwork()
@Shared
@ClassRule
public static MySQLContainer mySql = new MySQLContainer(DockerImageName.parse('mysql:5.7'))
.withNetwork(network)
.withNetworkAliases(MY_SQL_HOST_NAME) as MySQLContainer
@Shared
@ClassRule
public static LocalStackContainer localStack = new LocalStackContainer(DockerImageName.parse('localstack/localstack:0.11.6'))
.withServices(SQS, LAMBDA, CLOUDWATCH, DYNAMODB, KMS, STS, IAM)
.withNetwork(network)
.withEnv('LAMBDA_DOCKER_NETWORK', network.name)
.withCopyFileToContainer(MountableFile.forHostPath(new File('the jar file').path), '/opt/code/localstack/lambda.jar')
.withCopyFileToContainer(MountableFile.forClasspathResource('seed.yaml'), '/init/seed.yaml')
Once the container is up and running the lambda can be deployed using awslocal and standard CLI commands:
def result = localStack.execInContainer(
'awslocal', 'lambda', 'create-function',
'--function-name', 'lambda-name',
'--runtime', 'java8',
'--handler', 'uk.co.blah.blah.Handler',
'--role', 'arn:aws:iam::123456:role/irrelevant',
'--zip-file', 'fileb://lambda.jar',
'--environment', "Variables={MY_SQL_USERNAME=${mySql.username},MY_SQL_PASSWORD=${encryptedDbPassword},PLATFORM_DB_URL=${"jdbc:mysql://${MY_SQL_HOST_NAME}:3306"}," +
"AWS_REGION=${localStack.region}}"
)
Finally, the lambda can be manually triggered in tests like so:
localStack.execInContainer('awslocal', 'lambda', 'invoke', '--function-name', 'lambda-name', 'output0.json')
Upvotes: 1