Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 2 additions & 2 deletions packages/@aws-cdk/pipelines/lib/docker-credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import { Fn } from '@aws-cdk/core';
export abstract class DockerCredential {
/**
* Creates a DockerCredential for DockerHub.
* Convenience method for `fromCustomRegistry('index.docker.io', opts)`.
* Convenience method for `customRegistry('https://index.docker.io/v1/', opts)`.
*/
public static dockerHub(secret: secretsmanager.ISecret, opts: ExternalDockerCredentialOptions = {}): DockerCredential {
return new ExternalDockerCredential('index.docker.io', secret, opts);
return new ExternalDockerCredential('https://index.docker.io/v1/', secret, opts);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('ExternalDockerCredential', () => {
test('dockerHub defaults registry domain', () => {
const creds = cdkp.DockerCredential.dockerHub(secret);

expect(Object.keys(creds._renderCdkAssetsConfig())).toEqual(['index.docker.io']);
expect(Object.keys(creds._renderCdkAssetsConfig())).toEqual(['https://index.docker.io/v1/']);
});

test('minimal example only renders secret', () => {
Expand Down
10 changes: 2 additions & 8 deletions packages/cdk-assets/bin/docker-credential-cdk-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,8 @@ async function main() {
}

// Read the domain to fetch from stdin
let rawDomain = fs.readFileSync(0, { encoding: 'utf-8' }).trim();
// Paranoid handling to ensure new URL() doesn't throw if the schema is missing.
// Not convinced docker will ever pass in a url like 'index.docker.io/v1', but just in case...
rawDomain = rawDomain.includes('://') ? rawDomain : `https://${rawDomain}`;
const domain = new URL(rawDomain).hostname;

const credentials = await fetchDockerLoginCredentials(new DefaultAwsClient(), config, domain);

let endpoint = fs.readFileSync(0, { encoding: 'utf-8' }).trim();
const credentials = await fetchDockerLoginCredentials(new DefaultAwsClient(), config, endpoint);
// Write the credentials back to stdout
fs.writeFileSync(1, JSON.stringify(credentials));
}
Expand Down
11 changes: 8 additions & 3 deletions packages/cdk-assets/lib/private/docker-credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,17 @@ export function cdkCredentialsConfig(): DockerCredentialsConfig | undefined {
}

/** Fetches login credentials from the configured source (e.g., SecretsManager, ECR) */
export async function fetchDockerLoginCredentials(aws: IAws, config: DockerCredentialsConfig, domain: string) {
if (!Object.keys(config.domainCredentials).includes(domain)) {
export async function fetchDockerLoginCredentials(aws: IAws, config: DockerCredentialsConfig, endpoint: string) {
// Paranoid handling to ensure new URL() doesn't throw if the schema is missing
// For official docker registry, docker will pass https://index.docker.io/v1/
endpoint = endpoint.includes('://') ? endpoint : `https://${endpoint}`;
const domain = new URL(endpoint).hostname;

if (!Object.keys(config.domainCredentials).includes(domain) && !Object.keys(config.domainCredentials).includes(endpoint)) {
throw new Error(`unknown domain ${domain}`);
}

const domainConfig = config.domainCredentials[domain];
let domainConfig = config.domainCredentials[domain] ?? config.domainCredentials[endpoint];

if (domainConfig.secretsManagerSecretId) {
const sm = await aws.secretsManagerClient({ assumeRoleArn: domainConfig.assumeRoleArn });
Expand Down
11 changes: 10 additions & 1 deletion packages/cdk-assets/lib/private/docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,17 @@ export class Docker {
private async execute(args: string[], options: ShellOptions = {}) {
const configArgs = this.configDir ? ['--config', this.configDir] : [];

const pathToCdkAssets = path.resolve(__dirname, '..', '..', 'bin');
try {
await shell(['docker', ...configArgs, ...args], { logger: this.logger, ...options });
await shell(['docker', ...configArgs, ...args], {
logger: this.logger,
...options,
env: {
...process.env,
...options.env,
PATH: `${pathToCdkAssets}${path.delimiter}${options.env?.PATH ?? process.env.PATH}`,
},
});
} catch (e) {
if (e.code === 'ENOENT') {
throw new Error('Unable to execute \'docker\' in order to build a container asset. Please install \'docker\' and try again.');
Expand Down
14 changes: 13 additions & 1 deletion packages/cdk-assets/test/private/docker-credentials.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,27 @@ describe('fetchDockerLoginCredentials', () => {
await expect(fetchDockerLoginCredentials(aws, config, 'misconfigured.example.com')).rejects.toThrow(/unknown credential type/);
});

test('does not throw on correctly configured raw domain', async () => {
expect(fetchDockerLoginCredentials(aws, config, 'https://secret.example.com/v1/')).resolves;
});

describe('SecretsManager', () => {
test('returns the credentials sucessfully if configured correctly', async () => {
test('returns the credentials sucessfully if configured correctly - domain', async () => {
mockSecretWithSecretString({ username: 'secretUser', secret: 'secretPass' });

const creds = await fetchDockerLoginCredentials(aws, config, 'secret.example.com');

expect(creds).toEqual({ Username: 'secretUser', Secret: 'secretPass' });
});

test('returns the credentials successfully if configured correctly - raw domain', async () => {
mockSecretWithSecretString({ username: 'secretUser', secret: 'secretPass' });

const creds = await fetchDockerLoginCredentials(aws, config, 'https://secret.example.com');

expect(creds).toEqual({ Username: 'secretUser', Secret: 'secretPass' });
});

test('throws when SecretsManager returns an error', async () => {
const errMessage = "Secrets Manager can't find the specified secret.";
aws.mockSecretsManager.getSecretValue = mockedApiFailure('ResourceNotFoundException', errMessage);
Expand Down