Skip to content

Commit 2a02858

Browse files
[wrangler] fix: don't require private credential when reusing an existing Secrets Store secret (#14362)
1 parent 44a4084 commit 2a02858

3 files changed

Lines changed: 209 additions & 54 deletions

File tree

.changeset/free-islands-sink.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
Don't require the private credential when reusing an existing Secrets Store secret in `containers registries configure`
6+
7+
`wrangler containers registries configure` now checks whether the target Secrets Store secret already exists before resolving the private credential. When the secret already exists it is reused by reference, so the private credential no longer needs to be supplied (via stdin in non-interactive mode, or via a prompt interactively). This applies to all external registries.
8+
9+
The new-secret path is unchanged: the credential is still required and stored. The only visible interactive change is that the secret prompt now appears last and only when a new secret is being created.

packages/wrangler/src/__tests__/containers/registries.test.ts

Lines changed: 163 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -236,16 +236,16 @@ describe("containers registries configure", () => {
236236
setIsTTY(true);
237237
const awsEcrDomain = "123456789012.dkr.ecr.us-west-2.amazonaws.com";
238238
const storeId = "test-store-id-123";
239-
mockPrompt({
240-
text: "Enter AWS Secret Access Key:",
241-
options: { isSecret: true },
242-
result: "test-secret-access-key",
243-
});
244239
mockPrompt({
245240
text: "Secret name:",
246241
options: { isSecret: false, defaultValue: "AWS_Secret_Access_Key" },
247242
result: "AWS_Secret_Access_Key",
248243
});
244+
mockPrompt({
245+
text: "Enter AWS Secret Access Key:",
246+
options: { isSecret: true },
247+
result: "test-secret-access-key",
248+
});
249249

250250
mockListSecretStores([
251251
{
@@ -285,11 +285,6 @@ describe("containers registries configure", () => {
285285
const awsEcrDomain = "123456789012.dkr.ecr.us-west-2.amazonaws.com";
286286
const newStoreId = "new-store-id-456";
287287

288-
mockPrompt({
289-
text: "Enter AWS Secret Access Key:",
290-
options: { isSecret: true },
291-
result: "test-secret-access-key",
292-
});
293288
mockConfirm({
294289
text: "No existing Secret Stores found. Create a Secret Store to store your registry credentials?",
295290
result: true,
@@ -299,6 +294,11 @@ describe("containers registries configure", () => {
299294
options: { isSecret: false, defaultValue: "AWS_Secret_Access_Key" },
300295
result: "AWS_Secret_Access_Key",
301296
});
297+
mockPrompt({
298+
text: "Enter AWS Secret Access Key:",
299+
options: { isSecret: true },
300+
result: "test-secret-access-key",
301+
});
302302

303303
mockListSecretStores([]);
304304
mockCreateSecretStore(newStoreId);
@@ -334,16 +334,16 @@ describe("containers registries configure", () => {
334334
const awsEcrDomain = "123456789012.dkr.ecr.us-west-2.amazonaws.com";
335335
const providedStoreId = "provided-store-id-789";
336336

337-
mockPrompt({
338-
text: "Enter AWS Secret Access Key:",
339-
options: { isSecret: true },
340-
result: "test-secret-access-key",
341-
});
342337
mockPrompt({
343338
text: "Secret name:",
344339
options: { isSecret: false, defaultValue: "AWS_Secret_Access_Key" },
345340
result: "AWS_Secret_Access_Key",
346341
});
342+
mockPrompt({
343+
text: "Enter AWS Secret Access Key:",
344+
options: { isSecret: true },
345+
result: "test-secret-access-key",
346+
});
347347

348348
mockListSecrets(providedStoreId, []);
349349
mockCreateSecret(providedStoreId);
@@ -371,13 +371,13 @@ describe("containers registries configure", () => {
371371
372372
│ Configuring AWS ECR registry: 123456789012.dkr.ecr.us-west-2.amazonaws.com
373373
374-
│ Getting AWS Secret Access Key...
375-
376374
377375
│ Setting up integration with Secrets Store...
378376
379377
380378
379+
│ Getting AWS Secret Access Key...
380+
381381
│ Container-scoped secret "AWS_Secret_Access_Key" created in Secrets Store.
382382
383383
╰ Registry configuration completed
@@ -386,6 +386,51 @@ describe("containers registries configure", () => {
386386
`);
387387
});
388388

389+
it("should reuse an existing secret interactively without prompting for the credential", async ({
390+
expect,
391+
}) => {
392+
setIsTTY(true);
393+
const awsEcrDomain = "123456789012.dkr.ecr.us-west-2.amazonaws.com";
394+
const providedStoreId = "provided-store-id-reuse";
395+
const secretName = "existing_secret";
396+
397+
mockConfirm({
398+
text: "Do you want to reuse the existing secret? If not, then you'll be prompted to pick a new name.",
399+
result: true,
400+
});
401+
402+
mockListSecrets(providedStoreId, [
403+
{
404+
id: "existing-secret-id",
405+
store_id: providedStoreId,
406+
name: secretName,
407+
comment: "",
408+
scopes: ["containers"],
409+
created: "2024-01-01T00:00:00Z",
410+
modified: "2024-01-01T00:00:00Z",
411+
status: "active",
412+
},
413+
]);
414+
mockPutRegistry(expect, {
415+
domain: awsEcrDomain,
416+
is_public: false,
417+
auth: {
418+
public_credential: "test-access-key-id",
419+
private_credential: {
420+
store_id: providedStoreId,
421+
secret_name: secretName,
422+
},
423+
},
424+
kind: "ECR",
425+
});
426+
427+
await runWrangler(
428+
`containers registries configure ${awsEcrDomain} --aws-access-key-id=test-access-key-id --secret-store-id=${providedStoreId} --secret-name=${secretName}`
429+
);
430+
431+
expect(cliStd.stdout).not.toContain("created in Secrets Store");
432+
});
433+
389434
describe("non-interactive", () => {
390435
beforeEach(() => {
391436
setIsTTY(false);
@@ -426,7 +471,7 @@ describe("containers registries configure", () => {
426471
);
427472
});
428473

429-
it("should reuse existing secret with --skip-confirmation", async ({
474+
it("should ignore a piped stdin value when reusing an existing secret with --skip-confirmation", async ({
430475
expect,
431476
}) => {
432477
const storeId = "test-store-id-reuse";
@@ -474,6 +519,53 @@ describe("containers registries configure", () => {
474519
// Should not contain "created" message since we reused existing secret
475520
expect(cliStd.stdout).not.toContain("created in Secrets Store");
476521
});
522+
523+
it("should reuse existing secret without requiring a value (no stdin)", async ({
524+
expect,
525+
}) => {
526+
const storeId = "test-store-id-reuse";
527+
const secretName = "existing_secret";
528+
529+
mockListSecretStores([
530+
{
531+
id: storeId,
532+
account_id: "some-account-id",
533+
name: "Default",
534+
created: "2024-01-01T00:00:00Z",
535+
modified: "2024-01-01T00:00:00Z",
536+
},
537+
]);
538+
mockListSecrets(storeId, [
539+
{
540+
id: "existing-secret-id",
541+
store_id: storeId,
542+
name: secretName,
543+
comment: "",
544+
scopes: ["containers"],
545+
created: "2024-01-01T00:00:00Z",
546+
modified: "2024-01-01T00:00:00Z",
547+
status: "active",
548+
},
549+
]);
550+
mockPutRegistry(expect, {
551+
domain: awsEcrDomain,
552+
is_public: false,
553+
auth: {
554+
public_credential: "test-access-key-id",
555+
private_credential: {
556+
store_id: storeId,
557+
secret_name: secretName,
558+
},
559+
},
560+
kind: "ECR",
561+
});
562+
563+
await runWrangler(
564+
`containers registries configure ${awsEcrDomain} --public-credential=test-access-key-id --secret-name=${secretName} --skip-confirmation`
565+
);
566+
567+
expect(cliStd.stdout).not.toContain("created in Secrets Store");
568+
});
477569
});
478570
});
479571

@@ -484,16 +576,16 @@ describe("containers registries configure", () => {
484576
setIsTTY(true);
485577
const dockerHubDomain = "docker.io";
486578
const storeId = "test-store-id-123";
487-
mockPrompt({
488-
text: "Enter DockerHub PAT Token:",
489-
options: { isSecret: true },
490-
result: "test-pat-token",
491-
});
492579
mockPrompt({
493580
text: "Secret name:",
494581
options: { isSecret: false, defaultValue: "DockerHub_PAT_Token" },
495582
result: "DockerHub_PAT_Token",
496583
});
584+
mockPrompt({
585+
text: "Enter DockerHub PAT Token:",
586+
options: { isSecret: true },
587+
result: "test-pat-token",
588+
});
497589

498590
mockListSecretStores([
499591
{
@@ -565,6 +657,54 @@ describe("containers registries configure", () => {
565657
`containers registries configure ${dockerHubDomain} --public-credential=cloudchambertest --secret-name=DockerHub_PAT_Token`
566658
);
567659
});
660+
661+
it("should reuse existing secret without requiring a value (no stdin)", async ({
662+
expect,
663+
}) => {
664+
const storeId = "test-store-id-reuse";
665+
const secretName = "existing_secret";
666+
667+
// No value is piped via stdin; the existing secret is reused by reference.
668+
mockListSecretStores([
669+
{
670+
id: storeId,
671+
account_id: "some-account-id",
672+
name: "Default",
673+
created: "2024-01-01T00:00:00Z",
674+
modified: "2024-01-01T00:00:00Z",
675+
},
676+
]);
677+
mockListSecrets(storeId, [
678+
{
679+
id: "existing-secret-id",
680+
store_id: storeId,
681+
name: secretName,
682+
comment: "",
683+
scopes: ["containers"],
684+
created: "2024-01-01T00:00:00Z",
685+
modified: "2024-01-01T00:00:00Z",
686+
status: "active",
687+
},
688+
]);
689+
mockPutRegistry(expect, {
690+
domain: dockerHubDomain,
691+
is_public: false,
692+
auth: {
693+
public_credential: "cloudchambertest",
694+
private_credential: {
695+
store_id: storeId,
696+
secret_name: secretName,
697+
},
698+
},
699+
kind: "DockerHub",
700+
});
701+
702+
await runWrangler(
703+
`containers registries configure ${dockerHubDomain} --public-credential=cloudchambertest --secret-name=${secretName} --skip-confirmation`
704+
);
705+
706+
expect(cliStd.stdout).not.toContain("created in Secrets Store");
707+
});
568708
});
569709
});
570710
});

0 commit comments

Comments
 (0)