in lib/registry-sync/ecr-mirror.ts [83:198]
constructor(scope: Construct, id: string, props: EcrMirrorProps) {
super(scope, id);
if (!props.schedule && !props.autoStart) {
throw new Error('Either schedule or autoStart must be provided');
}
const ecrRegistry = `${Stack.of(scope).account}.dkr.ecr.${Stack.of(scope).region}.amazonaws.com`;
const commands: string[] = [];
const assets = new Array<s3Assets.Asset>();
const codeBuildSecretValue = (key: string, auth: DockerHubCredentials) => {
return `${props.dockerHubCredentials.secret.secretName}:${key}:${auth.versionStage ?? 'AWSCURRENT'}`;
};
const username = codeBuildSecretValue(props.dockerHubCredentials.usernameKey, props.dockerHubCredentials);
const password = codeBuildSecretValue(props.dockerHubCredentials.passwordKey, props.dockerHubCredentials);
this._project = new codebuild.Project(this, 'EcrPushImages', {
environment: {
privileged: true,
buildImage: codebuild.LinuxBuildImage.fromDockerRegistry('jsii/superchain', {
secretsManagerCredentials: props.dockerHubCredentials.secret,
}),
},
environmentVariables: {
// DockerHub credentials to avoid throttling
DOCKERHUB_USERNAME: { value: username, type: codebuild.BuildEnvironmentVariableType.SECRETS_MANAGER },
DOCKERHUB_PASSWORD: { value: password, type: codebuild.BuildEnvironmentVariableType.SECRETS_MANAGER },
},
buildSpec: codebuild.BuildSpec.fromObject(Lazy.any({
produce: () => {
return {
version: '0.2',
phases: {
build: {
commands: [
// start the docker daemon
'nohup /usr/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 --storage-driver=overlay2&',
'timeout 15 sh -c "until docker info; do echo .; sleep 1; done"',
// login to dockerhub so we won't get throttled
'docker login -u ${DOCKERHUB_USERNAME} -p ${DOCKERHUB_PASSWORD}',
// login to ecr so we can push to it
`aws ecr get-login-password | docker login --username AWS --password-stdin ${ecrRegistry}`,
...commands,
],
},
},
};
},
})),
});
for (const image of props.sources) {
const result = image.bind({
scope: this,
ecrRegistry,
syncJob: this._project,
});
commands.push(...result.commands);
const repoTag = `${result.repositoryName}:${result.tag}`;
if (this._repoTagsSeen.has(repoTag)) {
throw new Error(`Mirror source with repository name [${result.repositoryName}] and tag [${result.tag}] already exists.`);
}
this._repoTagsSeen.add(repoTag);
this.createMirrorRepo(result.repositoryName);
const ecrImageUri = `${ecrRegistry}/${result.repositoryName}:${result.tag}`;
commands.push(`docker push ${ecrImageUri}`);
// clean after each push so that we don't fillup disk space
// possibly failing the next pull.
commands.push('docker image prune --all --force');
}
// CodeBuild needs to read the secret to resolve environment variables
props.dockerHubCredentials.secret.grantRead(this._project);
ecr.AuthorizationToken.grantRead(this._project);
this._repos.forEach((r, _) => r.grantPullPush(this._project));
// this project needs to download the assets so it can build them
assets.forEach(a => a.grantRead(this._project));
if (props.autoStart) {
new cr.AwsCustomResource(this, 'BuildExecution', {
policy: cr.AwsCustomResourcePolicy.fromSdkCalls({ resources: [this._project.projectArn] }),
onUpdate: {
action: 'startBuild',
service: 'CodeBuild',
parameters: {
projectName: this._project.projectName,
// to tigger the build on every update
idempotencyToken: `${Date.now()}`,
},
physicalResourceId: cr.PhysicalResourceId.of('EcrRegistryExecution'),
// need since the default reponse if greater than the 4k limit for custom resources.
outputPath: 'build.id',
},
});
}
if (props.schedule) {
new events.Rule(this, 'ScheduledTrigger', {
schedule: props.schedule,
targets: [new targets.CodeBuildProject(this._project)],
});
}
}