Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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/testcontainers/src/container-runtime/auth/auths.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RegistryAuthLocator } from "./registry-auth-locator";
import { registryMatches } from "./registry-matches";
import { Auth, AuthConfig, ContainerRuntimeConfig } from "./types";
import { Auth, AuthConfig, ContainerRuntimeConfig, UsernamePasswordAuthConfig } from "./types";

export class Auths implements RegistryAuthLocator {
public getName(): string {
Expand All @@ -13,7 +13,7 @@ export class Auths implements RegistryAuthLocator {
return undefined;
}

const authConfig: Partial<AuthConfig> = { registryAddress: registry };
const authConfig: Partial<UsernamePasswordAuthConfig> = { registryAddress: registry };

if (auth.email) {
authConfig.email = auth.email;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ describe.sequential("CredentialProvider", () => {
});
});

it("should return the auth config for a registry using an identity token", async () => {
mockSpawnEmitsData(
0,
JSON.stringify({
ServerURL: "registry",
Username: "<token>",
Secret: "dGVzdAo=",
})
);

const credentials = await credentialProvider.getAuthConfig("registry", containerRuntimeConfig);

expect(credentials).toEqual({
registryAddress: "registry",
identityToken: "dGVzdAo=",
});
});

it("should default to the registry url when the server url is not returned", async () => {
mockSpawnEmitsData(
0,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { spawn } from "child_process";
import { log } from "../../common";
import { RegistryAuthLocator } from "./registry-auth-locator";
import { AuthConfig, ContainerRuntimeConfig } from "./types";
import {
AuthConfig,
ContainerRuntimeConfig,
CredentialProviderGetResponse,
IdentityTokenAuthConfig,
UsernamePasswordAuthConfig,
} from "./types";

export abstract class CredentialProvider implements RegistryAuthLocator {
abstract getName(): string;
Expand Down Expand Up @@ -40,12 +46,14 @@ export abstract class CredentialProvider implements RegistryAuthLocator {

const response = chunks.join("");
try {
const parsedResponse = JSON.parse(response);
return resolve({
username: parsedResponse.Username,
password: parsedResponse.Secret,
registryAddress: parsedResponse.ServerURL ?? registry,
});
const credentialProviderResponse = JSON.parse(response) as CredentialProviderGetResponse;

const authConfig =
credentialProviderResponse.Username === "<token>"
? this.parseIdentityTokenConfig(registry, credentialProviderResponse)
: this.parseUsernamePasswordConfig(registry, credentialProviderResponse);

return resolve(authConfig);
} catch (e) {
log.error(`Unexpected response from Docker credential provider GET command: "${response}"`);
return reject(new Error("Unexpected response from Docker credential provider GET command"));
Expand All @@ -56,4 +64,22 @@ export abstract class CredentialProvider implements RegistryAuthLocator {
sink.stdin.end();
});
}

private parseUsernamePasswordConfig(
registry: string,
config: CredentialProviderGetResponse
): UsernamePasswordAuthConfig {
return {
username: config.Username,
password: config.Secret,
registryAddress: config.ServerURL ?? registry,
};
}

private parseIdentityTokenConfig(registry: string, config: CredentialProviderGetResponse): IdentityTokenAuthConfig {
return {
registryAddress: config.ServerURL ?? registry,
identityToken: config.Secret,
};
}
}
20 changes: 8 additions & 12 deletions packages/testcontainers/src/container-runtime/auth/types.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
export type CredentialProviderGetResponse = {
ServerURL: string;
ServerURL?: string;
Username: string;
Secret: string;
};

export type CredentialProviderListResponse = {
[registry: string]: string;
};

export type Auth = {
auth?: string;
email?: string;
username?: string;
password?: string;
};

export type AuthConfig = {
export type AuthConfig = UsernamePasswordAuthConfig | IdentityTokenAuthConfig;

export type UsernamePasswordAuthConfig = {
registryAddress: string;
username: string;
password: string;
registryAddress: string;
email?: string;
};

export type RegistryConfig = {
[registryAddress: string]: {
username: string;
password: string;
};
export type IdentityTokenAuthConfig = {
registryAddress: string;
identityToken: string;
};

export type ContainerRuntimeConfig = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import type { ImageBuildOptions } from "dockerode";
import path from "path";
import { log, RandomUuid, Uuid } from "../common";
import { getAuthConfig, getContainerRuntimeClient, ImageName } from "../container-runtime";
import { AuthConfig } from "../container-runtime/auth/types";
import { getReaper } from "../reaper/reaper";
import { AuthConfig, BuildArgs, RegistryConfig } from "../types";
import { BuildArgs, RegistryConfig } from "../types";
import { getDockerfileImages } from "../utils/dockerfile-parser";
import { createLabels, LABEL_TESTCONTAINERS_SESSION_ID } from "../utils/labels";
import { ImagePullPolicy, PullPolicy } from "../utils/pull-policy";
Expand Down Expand Up @@ -81,6 +82,7 @@ export class GenericContainerBuilder {
dockerfile: this.dockerfileName,
buildargs: this.buildArgs,
nocache: !this.cache,
// @ts-expect-error Dockerode types don't yet include identityToken
registryconfig: registryConfig,
labels,
target: this.target,
Expand Down Expand Up @@ -115,14 +117,7 @@ export class GenericContainerBuilder {
);

return authConfigs
.map((authConfig) => {
return {
[authConfig.registryAddress]: {
username: authConfig.username,
password: authConfig.password,
},
};
})
.map((authConfig) => ({ [authConfig.registryAddress]: authConfig }))
.reduce((prev, next) => ({ ...prev, ...next }), {} as RegistryConfig);
}
}
13 changes: 2 additions & 11 deletions packages/testcontainers/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Readable } from "stream";
import { AuthConfig } from "./container-runtime/auth/types";
import { ContainerCommitOptions } from "./container-runtime/clients/container/types";

export type InspectResult = {
Expand Down Expand Up @@ -71,18 +72,8 @@ export type Labels = { [key: string]: string };
export type HostPortBindings = Array<{ hostIp: string; hostPort: number }>;
export type Ports = { [containerPortWithProtocol: string]: HostPortBindings };

export type AuthConfig = {
username: string;
password: string;
registryAddress: string;
email?: string;
};

export type RegistryConfig = {
[registryAddress: string]: {
username: string;
password: string;
};
[registryAddress: string]: AuthConfig;
};

export type BuildArgs = { [key in string]: string };
Expand Down
Loading