Skip to content

Commit

Permalink
Add new versions secret commands and rollback behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
WalshyDev committed Jun 19, 2024
1 parent 6f445cf commit f7ed70a
Show file tree
Hide file tree
Showing 22 changed files with 1,732 additions and 160 deletions.
13 changes: 13 additions & 0 deletions .changeset/little-rice-promise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"wrangler": minor
---

feat: add `wrangler versions secret put`, `wrangler versions secret bulk` and `wrangler versions secret list`

`wrangler versions secret put` allows for you to add/update a secret even if the latest version is not fully deployed. A new version with this secret will be created, the existing secrets and config are copied from the latest version.

`wrangler versions secret bulk` allows you to bulk add/update multiple secrets at once, this behaves the same as `secret put` and will only make one new version.

`wrangler versions secret list` lists the secrets available to the currently deployed versions. `wrangler secret list` will list for the latest version.

Additionally, we will now prompt for extra confirmation if attempting to rollback to a version with different secrets than the currently deployed.
193 changes: 193 additions & 0 deletions packages/wrangler/src/__tests__/rollback.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import { http, HttpResponse } from "msw";
import { describe, expect, test } from "vitest";
import { CANNOT_ROLLBACK_WITH_MODIFIED_SECERT_CODE } from "../versions/api";
import { collectCLIOutput } from "./helpers/collect-cli-output";
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
import { mockConfirm, mockPrompt } from "./helpers/mock-dialogs";
import { createFetchResult, msw } from "./helpers/msw";
import { runWrangler } from "./helpers/run-wrangler";
import type { ApiDeployment } from "../versions/types";

describe("rollback", () => {
const std = collectCLIOutput();
mockAccountId();
mockApiToken();

function mockGetDeployments(multiVersion = false) {
const versions = multiVersion
? [
{ version_id: "version-id-1", percentage: 50 },
{ version_id: "version-id-2", percentage: 50 },
]
: [{ version_id: "version-id-1", percentage: 100 }];

msw.use(
http.get(
`*/accounts/:accountId/workers/scripts/:scriptName/deployments`,
async ({ params }) => {
expect(params.accountId).toEqual("some-account-id");
expect(params.scriptName).toEqual("script-name");

return HttpResponse.json(
createFetchResult({
deployments: [
{
id: "deployment-id",
source: "api",
strategy: "percentage",
versions,
},
] as ApiDeployment[],
})
);
},
{ once: true }
)
);
}

function mockGetVersion(versionId: string) {
msw.use(
http.get(
`*/accounts/:accountId/workers/scripts/:scriptName/versions/${versionId}`,
async ({ params }) => {
expect(params.accountId).toEqual("some-account-id");
expect(params.scriptName).toEqual("script-name");

return HttpResponse.json(
createFetchResult({
id: versionId,
metadata: {},
number: 2,
resources: {
bindings: [
{
type: "secret_text",
name: "SECRET_1",
text: "First secret",
},
{
type: "secret_text",
name: "SECRET_2",
text: "Second secret",
},
{
type: "secret_text",
name: "SECRET_3",
text: "Third secret",
},
],
script: {
etag: "etag",
handlers: ["fetch"],
last_deployed_from: "api",
},
script_runtime: {
usage_model: "standard",
limits: {},
},
},
})
);
},
{ once: true }
)
);
}

function mockPostDeployment(forced = false) {
msw.use(
http.post(
`*/accounts/:accountId/workers/scripts/:scriptName/deployments${forced ? "?force=true" : ""}`,
async ({ params }) => {
expect(params.accountId).toEqual("some-account-id");
expect(params.scriptName).toEqual("script-name");

return HttpResponse.json(createFetchResult({}));
},
{ once: true }
)
);
}

test("can rollback to an earlier version", async () => {
mockGetDeployments();
mockGetVersion("version-id-1");
mockGetVersion("rollback-version");
mockPostDeployment();

mockPrompt({
text: "Please provide a message for this rollback (120 characters max, optional)?",
result: "Test rollback",
});

mockConfirm({
text: "Are you sure you want to deploy this Worker Version to 100% of traffic?",
result: true,
});

await runWrangler(
"rollback --name script-name --version-id rollback-version --x-versions"
);

// Unable to test stdout as the output has weird whitespace. Causing lint to fail with "no-irregular-whitespace"
expect(std.err).toMatchInlineSnapshot(`""`);
});

test("rolling back with changed secrets prompts confirmation", async () => {
mockGetDeployments();
mockGetVersion("version-id-1");
mockGetVersion("rollback-version");

// Deployment will fail due to changed secret
msw.use(
http.post(
`*/accounts/:accountId/workers/scripts/:scriptName/deployments`,
async ({ params }) => {
expect(params.accountId).toEqual("some-account-id");
expect(params.scriptName).toEqual("script-name");

return HttpResponse.json(
createFetchResult(null, false, [
{
code: CANNOT_ROLLBACK_WITH_MODIFIED_SECERT_CODE,
message:
"A secret has changed since this version was active. If you are sure this is ok, add a ?force=true query parameter to allow the rollback. The following secrets have changed: SECRET, SECRET_TWO",
},
]),
{ status: 400 }
);
},
{ once: true }
)
);

// Now deploy again with force and succeed
mockPostDeployment(true);

mockPrompt({
text: "Please provide a message for this rollback (120 characters max, optional)?",
result: "Test rollback",
});

mockConfirm({
text: "Are you sure you want to deploy this Worker Version to 100% of traffic?",
result: true,
});

// We will have an additional confirmation
mockConfirm({
text:
"The following secrets have changed since the target version was deployed. Please confirm you wish to continue with the rollback" +
"\n * SECRET\n * SECRET_TWO",
result: true,
});

await runWrangler(
"rollback --name script-name --version-id rollback-version --x-versions"
);

// Unable to test stdout as the output has weird whitespace. Causing lint to fail with "no-irregular-whitespace"
expect(std.err).toMatchInlineSnapshot(`""`);
});
});
42 changes: 21 additions & 21 deletions packages/wrangler/src/__tests__/secret.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,13 +503,13 @@ describe("wrangler secret", () => {
mockListRequest({ scriptName: "script-name" });
await runWrangler("secret list --name script-name");
expect(std.out).toMatchInlineSnapshot(`
"[
{
\\"name\\": \\"the-secret-name\\",
\\"type\\": \\"secret_text\\"
}
]"
`);
"[
{
\\"name\\": \\"the-secret-name\\",
\\"type\\": \\"secret_text\\"
}
]"
`);
expect(std.err).toMatchInlineSnapshot(`""`);
});

Expand All @@ -519,13 +519,13 @@ describe("wrangler secret", () => {
"secret list --name script-name --env some-env --legacy-env"
);
expect(std.out).toMatchInlineSnapshot(`
"[
{
\\"name\\": \\"the-secret-name\\",
\\"type\\": \\"secret_text\\"
}
]"
`);
"[
{
\\"name\\": \\"the-secret-name\\",
\\"type\\": \\"secret_text\\"
}
]"
`);
expect(std.err).toMatchInlineSnapshot(`""`);
});

Expand All @@ -535,13 +535,13 @@ describe("wrangler secret", () => {
"secret list --name script-name --env some-env --legacy-env false"
);
expect(std.out).toMatchInlineSnapshot(`
"[
{
\\"name\\": \\"the-secret-name\\",
\\"type\\": \\"secret_text\\"
}
]"
`);
"[
{
\\"name\\": \\"the-secret-name\\",
\\"type\\": \\"secret_text\\"
}
]"
`);
expect(std.err).toMatchInlineSnapshot(`""`);
});

Expand Down
Loading

0 comments on commit f7ed70a

Please sign in to comment.