Skip to content

Commit 9ae6d5e

Browse files
Skip checking registry on credential helper list (#1121)
1 parent 553d238 commit 9ae6d5e

File tree

2 files changed

+7
-109
lines changed

2 files changed

+7
-109
lines changed

packages/testcontainers/src/container-runtime/auth/credential-provider.test.ts

Lines changed: 4 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ describe.sequential("CredentialProvider", () => {
2121
});
2222

2323
it("should return the auth config for a registry", async () => {
24-
mockExecReturns(JSON.stringify({ registry: "username" }));
2524
mockSpawnReturns(
2625
0,
2726
JSON.stringify({
@@ -40,24 +39,7 @@ describe.sequential("CredentialProvider", () => {
4039
});
4140
});
4241

43-
it("should not return auth config for registry which is a partial match", async () => {
44-
mockExecReturns(JSON.stringify({ "https://registry.example.com": "username" }));
45-
mockSpawnReturns(
46-
0,
47-
JSON.stringify({
48-
ServerURL: "https://registry.example.com",
49-
Username: "username",
50-
Secret: "secret",
51-
})
52-
);
53-
54-
expect(
55-
await credentialProvider.getAuthConfig("https://registry.example.co", containerRuntimeConfig)
56-
).toBeUndefined();
57-
});
58-
5942
it("should default to the registry url when the server url is not returned", async () => {
60-
mockExecReturns(JSON.stringify({ "https://registry.example.com": "username" }));
6143
mockSpawnReturns(
6244
0,
6345
JSON.stringify({
@@ -66,55 +48,20 @@ describe.sequential("CredentialProvider", () => {
6648
})
6749
);
6850

69-
expect(await credentialProvider.getAuthConfig("https://registry.example.com", containerRuntimeConfig)).toEqual({
70-
registryAddress: "https://registry.example.com",
51+
expect(await credentialProvider.getAuthConfig("registry.example.com", containerRuntimeConfig)).toEqual({
52+
registryAddress: "registry.example.com",
7153
username: "username",
7254
password: "secret",
7355
});
7456
});
7557

76-
it("should return undefined when no auth config found for registry", async () => {
77-
mockExecReturns(JSON.stringify({ registry2: "username" }));
78-
79-
const credentials = await credentialProvider.getAuthConfig("registry1", containerRuntimeConfig);
80-
81-
expect(credentials).toBeUndefined();
82-
});
83-
8458
it("should return undefined when provider name not provided", async () => {
85-
const credentialProvider = new TestCredentialProvider("name", undefined!);
59+
const credentialProvider = new TestCredentialProvider("name", undefined);
8660

8761
expect(await credentialProvider.getAuthConfig("registry", containerRuntimeConfig)).toBeUndefined();
8862
});
8963

90-
it("should throw when list credentials fails", async () => {
91-
mockExecThrows();
92-
93-
await expect(() => credentialProvider.getAuthConfig("registry", containerRuntimeConfig)).rejects.toThrow(
94-
"An error occurred listing credentials"
95-
);
96-
});
97-
98-
it("should throw when list credentials output cannot be parsed", async () => {
99-
mockExecReturns("CANNOT_PARSE");
100-
101-
await expect(() => credentialProvider.getAuthConfig("registry", containerRuntimeConfig)).rejects.toThrow(
102-
"Unexpected response from Docker credential provider LIST command"
103-
);
104-
});
105-
106-
it("should not throw when list credentials command is not implemented", async () => {
107-
mockExec.mockImplementationOnce((command, callback) => {
108-
return callback(new Error(), null, "list is unimplemented\n");
109-
});
110-
111-
const credentials = await credentialProvider.getAuthConfig("registry", containerRuntimeConfig);
112-
113-
expect(credentials).toBeUndefined();
114-
});
115-
11664
it("should throw when get credentials fails", async () => {
117-
mockExecReturns(JSON.stringify({ registry: "username" }));
11865
mockSpawnReturns(
11966
1,
12067
JSON.stringify({
@@ -130,7 +77,6 @@ describe.sequential("CredentialProvider", () => {
13077
});
13178

13279
it("should throw when get credentials output cannot be parsed", async () => {
133-
mockExecReturns(JSON.stringify({ registry: "username" }));
13480
mockSpawnReturns(0, "CANNOT_PARSE");
13581

13682
await expect(() => credentialProvider.getAuthConfig("registry", containerRuntimeConfig)).rejects.toThrow(
@@ -139,18 +85,6 @@ describe.sequential("CredentialProvider", () => {
13985
});
14086
});
14187

142-
function mockExecReturns(stdout: string) {
143-
mockExec.mockImplementationOnce((command, callback) => {
144-
return callback(null, stdout);
145-
});
146-
}
147-
148-
function mockExecThrows() {
149-
mockExec.mockImplementationOnce((command, callback) => {
150-
return callback("An error occurred");
151-
});
152-
}
153-
15488
function mockSpawnReturns(exitCode: number, stdout: string) {
15589
const sink = new EventEmitter() as ChildProcess;
15690

@@ -173,7 +107,7 @@ function mockSpawnReturns(exitCode: number, stdout: string) {
173107
class TestCredentialProvider extends CredentialProvider {
174108
constructor(
175109
private readonly name: string,
176-
private readonly credentialProviderName: string
110+
private readonly credentialProviderName: string | undefined
177111
) {
178112
super();
179113
}

packages/testcontainers/src/container-runtime/auth/credential-provider.ts

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
1-
import { exec, spawn } from "child_process";
1+
import { spawn } from "child_process";
22
import { log } from "../../common";
33
import { RegistryAuthLocator } from "./registry-auth-locator";
4-
import { registryMatches } from "./registry-matches";
5-
import {
6-
AuthConfig,
7-
ContainerRuntimeConfig,
8-
CredentialProviderGetResponse,
9-
CredentialProviderListResponse,
10-
} from "./types";
4+
import { AuthConfig, ContainerRuntimeConfig, CredentialProviderGetResponse } from "./types";
115

126
export abstract class CredentialProvider implements RegistryAuthLocator {
137
abstract getName(): string;
@@ -23,45 +17,15 @@ export abstract class CredentialProvider implements RegistryAuthLocator {
2317
const programName = `docker-credential-${credentialProviderName}`;
2418
log.debug(`Executing Docker credential provider "${programName}"`);
2519

26-
const credentials = await this.listCredentials(programName);
27-
28-
const credentialForRegistry = Object.keys(credentials).find((aRegistry) => registryMatches(aRegistry, registry));
29-
if (!credentialForRegistry) {
30-
log.debug(`No credential found for registry "${registry}"`);
31-
return undefined;
32-
}
33-
3420
const response = await this.runCredentialProvider(registry, programName);
3521

3622
return {
3723
username: response.Username,
3824
password: response.Secret,
39-
registryAddress: response.ServerURL ?? credentialForRegistry,
25+
registryAddress: response.ServerURL ?? registry,
4026
};
4127
}
4228

43-
private listCredentials(providerName: string): Promise<CredentialProviderListResponse> {
44-
return new Promise((resolve, reject) => {
45-
exec(`${providerName} list`, (err, stdout, stderr) => {
46-
if (err) {
47-
if (stderr === "list is unimplemented\n") {
48-
return resolve({});
49-
}
50-
51-
log.error(`An error occurred listing credentials: ${err}`);
52-
return reject(new Error("An error occurred listing credentials"));
53-
}
54-
try {
55-
const response = JSON.parse(stdout);
56-
return resolve(response);
57-
} catch (e) {
58-
log.error(`Unexpected response from Docker credential provider LIST command: "${stdout}"`);
59-
return reject(new Error("Unexpected response from Docker credential provider LIST command"));
60-
}
61-
});
62-
});
63-
}
64-
6529
private runCredentialProvider(registry: string, providerName: string): Promise<CredentialProviderGetResponse> {
6630
return new Promise((resolve, reject) => {
6731
const sink = spawn(providerName, ["get"]);

0 commit comments

Comments
 (0)