It feels like a chicken and egg problem. I'm trying to build a Docker image, add it to my ECR repository, and use it in a Lambda function using CDK V2.
Here is my code:
const repository = new ecr.Repository(this, `WorkerHelloRepository`, {
repositoryName: `worker-hello`,
imageTagMutability: ecr.TagMutability.IMMUTABLE,
imageScanOnPush: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
lifecycleRules: [
{
description: 'Keeps a maximum number of images to minimize storage',
maxImageCount: 10,
},
],
});
const asset = new DockerImageAsset(this, `WorkerHelloDockerImageAsset`, {
directory: path.join(__dirname, `../../../workers/hello`),
buildArgs: {},
});
const destinationImageName = `${repository.repositoryUri}:${asset.assetHash}`;
new ecrdeploy.ECRDeployment(this, 'EcrDeployment', {
src: new ecrdeploy.DockerImageName(asset.imageUri),
dest: new ecrdeploy.DockerImageName(destinationImageName),
});
const lambdaFunction = new lambda.DockerImageFunction(this, `WorkerHelloFunction`, {
code: lambda.DockerImageCode.fromEcr(repository, {
tagOrDigest: asset.assetHash,
cmd: ['index.newEntry'],
}),
});
repository.grantPull(lambdaFunction);
lambdaFunction.addToRolePolicy(
new cdk.aws_iam.PolicyStatement({
actions: ['ecr:BatchGetImage', 'ecr:GetDownloadUrlForLayer'],
resources: [repository.repositoryArn],
effect: cdk.aws_iam.Effect.ALLOW,
}),
);
The error returned by CloudFormation says that the image does not exist:
4:05:35 AM | CREATE_FAILED | AWS::Lambda::Function | workers-stack/work...orkerHelloFunction
Resource handler returned message: "Source image ***********.dkr.ecr.us-west-2.amazonaws.com/worker-hello:55106229314a459e2cf7fbfa14c2c03b728ffd20a70dad
3cf25ff51d7664abce does not exist. Provide a valid source image. (Service: Lambda, Status Code: 400, Request ID: 6ef7e839-ddb3-43c1-9170-c50865f6af70)" (Reques
tToken: be9bf05b-47e6-5239-da32-46cbe422853d, HandlerErrorCode: InvalidRequest)
But when I check in the ECR repository (that CloudFormation fails to delete since it's not empty), I can see the image and its uri is correct.
I tried manually adding a dependency from the Lambda function to the created assets by doing:
lambdaFunction.node.addDependency(asset);
I'm pretty sure CDK is smart enough to actually detect this dependency anyway, and it didn't solve the problem.
I really want to avoid having a separate stack just for ECR, but I guess that would solve the problem. What I didn't try is to move the ECR stuff and Lambda resources in two different nested stacks and add a dependency from one to the other, but I don't see why it would work better.
Any idea? Or even any guidance on how to solve this chicken and egg problem?
Your dependency should be on the ECR deployment, not the original asset (since the lambda is not using the asset direcly).
You got most of the way there, this should work:
const deployment = new ecrdeploy.ECRDeployment(this, 'EcrDeployment', {
src: new ecrdeploy.DockerImageName(asset.imageUri),
dest: new ecrdeploy.DockerImageName(destinationImageName),
});
const lambdaFunction = new lambda.DockerImageFunction(this, `WorkerHelloFunction`, {
code: lambda.DockerImageCode.fromEcr(repository, {
tagOrDigest: asset.assetHash,
cmd: ['index.newEntry'],
}),
});
// Dependency on the deployment, not the asset
lambdaFunction.node.addDependency(deployment);