When using CDK Pipelines the autogenerated Assets action will build Docker images, and publish to the cdk-provided ECR. However if included Dockerfiles build on images in a non-public repository (e.g. an ECR in a different account), those builds will fail since the Assets action has no way of specifying sources to docker login in to.
Prior to using Pipelines we've used a shared ECR in a dedicated account to both store our internal base images, as well as images built on top of those. A single docker login would cover both pull and push from that repo.
However when switching to Pipelines, the destination repo changes. Pipelines transparently handles login to to that, but provides no configuration option for docker registries that need to be logged in to prior to asset building.
The error message from the Assets/DockerAsset1 CodeBuild project was:
Step 1/13 : FROM <account>.dkr.ecr.eu-west-1.amazonaws.com/...
| Get https://<account>.dkr.ecr.eu-west-1.amazonaws.com/...: no basic auth credentials
This is a :rocket: Feature Request
How would you expect this to work on the desktop? I.e. if you do cdk deploy?
(What I'm saying is this might have implications for the asset manifest and CLI as well)
TBH I had no expectations of a particular behaviour when deploying locally. I only started using docker image assets after having set up a Pipeline. That said my understanding of cdk synth from having used cdk previously was that it represented the "compile CF templates" step.
The requirement of having done docker login was also never explicit when developing locally, since I was already logged in. Only when moving the docker build to CodeBuild did this become apparent.
Here's the hack I added to my pipeline stack (after the pipeline was created) to get past this:
private static addECRLogin (pipeline: CdkPipeline, sourceECRs: string[]) {
for (const action of pipeline.stage('Assets')?.actions) {
const actionProperties = action.actionProperties;
if (actionProperties.actionName.startsWith('Docker')) {
// workaround for https://github.com/aws/aws-cdk/issues/10999
const publishAction = action as PublishAssetsAction;
const commands: string[] = (publishAction as any).commands;
for (const sourceECR of sourceECRs) {
// NOTE: this makes the simplifying assumption that the sourceECR is in the same region as the pipeline
const command = `aws ecr get-login-password --region ${Stack.of(pipeline).region} | docker login --username AWS --password-stdin ${sourceECR}`;
if (!commands.includes(command)) {
// login needs to happen before the asset publication (that's where docker images are built)
commands.unshift(command);
}
}
new Policy(pipeline, 'AllowECRLoginAndPull', {
statements: [
new PolicyStatement({
actions: [
'ecr:GetAuthorizationToken',
'ecr:GetDownloadUrlForLayer',
'ecr:BatchGetImage',
],
resources: ['*'],
sid: 'AllowECRLoginAndPull',
}),
],
}).attachToRole(actionProperties.role!);
}
}
}
@tobli What do you pass in as sourceECRs for this?
@nsquires413 It's the full ECR repo name, e.g. 123456789012.dkr.ecr.eu-west-1.amazonaws.com
@tobli @pgarbe One more question if you don't mind. I'm trying to translate this method into C#.
Looking at the 'PublishAssetsAction' source code it looks like the it has a private member 'commands'.
It seems like you were still able to access this field by casting the action as 'any'? Is that right? Does anyone know if this is possible in C#?
It seems like you were still able to access this field by casting the action as 'any'? Is that right? Does anyone know if this is possible in C#?
Correct, it's accessing private field via the cast. That's of course risky, but Typescript lets you do that. If C# does I have no clue I'm afraid.
Most helpful comment
@nsquires413 It's the full ECR repo name, e.g.
123456789012.dkr.ecr.eu-west-1.amazonaws.com