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
17 changes: 17 additions & 0 deletions .changeset/thirty-walls-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"wrangler": minor
---

Enable automatic resource provisioning by default in Wrangler. This is still an experimental feature, but we're turning on the flag by default to make it easier for people to test it and try it out. You can disable the feature using the `--no-x-provision` flag. It currently works for R2, D1, and KV bindings.

To use this feature, add a binding to your config file _without_ a resource ID:

```jsonc
{
"kv_namespaces": [{ "binding": "MY_KV" }],
"d1_databases": [{ "binding": "MY_DB" }],
"r2_buckets": [{ "binding": "MY_R2" }]
}
```

`wrangler dev` will automatically create these resources for you locally, and when you next run `wrangler deploy` Wrangler will call the Cloudflare API to create the requested resources and link them to your Worker. They'll stay linked across deploys, and you don't need to add the resource IDs to the config file for future deploys to work. This is especially good for shared templates, which now no longer need to include account-specific resource ID when adding a binding.
56 changes: 26 additions & 30 deletions packages/wrangler/e2e/provision.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe.skipIf(!CLOUDFLARE_ACCOUNT_ID)(
const helper = new WranglerE2ETestHelper();

it("can run dev without resource ids", async () => {
const worker = helper.runLongLived("wrangler dev --x-provision");
const worker = helper.runLongLived("wrangler dev");

const { url } = await worker.waitForReady();
await fetch(url);
Expand Down Expand Up @@ -76,12 +76,12 @@ describe.skipIf(!CLOUDFLARE_ACCOUNT_ID)(
});

it("can provision resources and deploy worker", async () => {
const worker = helper.runLongLived(`wrangler deploy --x-provision`);
const worker = helper.runLongLived(`wrangler deploy`);
await worker.exitCode;
const output = await worker.output;
expect(normalize(output)).toMatchInlineSnapshot(`
"Total Upload: xx KiB / gzip: xx KiB
The following bindings need to be provisioned:
Experimental: The following bindings need to be provisioned:
Binding Resource
env.KV KV Namespace
env.D1 D1 Database
Expand Down Expand Up @@ -131,7 +131,7 @@ describe.skipIf(!CLOUDFLARE_ACCOUNT_ID)(
});

it("can inherit bindings on re-deploy and won't re-provision", async () => {
const worker = helper.runLongLived(`wrangler deploy --x-provision`);
const worker = helper.runLongLived(`wrangler deploy`);
await worker.exitCode;
const output = await worker.output;
expect(normalize(output)).toMatchInlineSnapshot(`
Expand All @@ -155,16 +155,14 @@ describe.skipIf(!CLOUDFLARE_ACCOUNT_ID)(
await expect(response.text()).resolves.toEqual("Hello World!");
});
it("can inspect current bindings", async () => {
const versionsRaw = await helper.run(
`wrangler versions list --json --x-provision`
);
const versionsRaw = await helper.run(`wrangler versions list --json`);

const versions = JSON.parse(versionsRaw.stdout) as unknown[];

const latest = versions.at(-1) as { id: string };

const versionView = await helper.run(
`wrangler versions view ${latest.id} --x-provision`
`wrangler versions view ${latest.id}`
);

expect(normalizeOutput(versionView.output)).toMatchInlineSnapshot(`
Expand Down Expand Up @@ -198,31 +196,29 @@ describe.skipIf(!CLOUDFLARE_ACCOUNT_ID)(
binding = "KV2"
`,
});
const worker = helper.runLongLived(
`wrangler versions upload --x-provision`
);
const worker = helper.runLongLived(`wrangler versions upload`);
await worker.exitCode;
const output = await worker.output;
expect(normalize(output)).toMatchInlineSnapshot(`
"Total Upload: xx KiB / gzip: xx KiB
The following bindings need to be provisioned:
Binding Resource
env.KV2 KV Namespace
Provisioning KV2 (KV Namespace)...
🌀 Creating new KV Namespace "tmp-e2e-worker-00000000-0000-0000-0000-000000000000-kv2"...
✨ KV2 provisioned 🎉
🎉 All resources provisioned, continuing with deployment...
Worker Startup Time: (TIMINGS)
Your Worker has access to the following bindings:
Binding Resource
env.KV2 (00000000000000000000000000000000) KV Namespace
env.R2 (inherited) R2 Bucket
Uploaded tmp-e2e-worker-00000000-0000-0000-0000-000000000000 (TIMINGS)
Worker Version ID: 00000000-0000-0000-0000-000000000000
To deploy this version to production traffic use the command wrangler versions deploy
Changes to non-versioned settings (config properties 'logpush' or 'tail_consumers') take effect after your next deployment using the command wrangler versions deploy
Changes to triggers (routes, custom domains, cron schedules, etc) must be applied with the command wrangler triggers deploy"
`);
"Total Upload: xx KiB / gzip: xx KiB
Experimental: The following bindings need to be provisioned:
Binding Resource
env.KV2 KV Namespace
Provisioning KV2 (KV Namespace)...
🌀 Creating new KV Namespace "tmp-e2e-worker-00000000-0000-0000-0000-000000000000-kv2"...
✨ KV2 provisioned 🎉
🎉 All resources provisioned, continuing with deployment...
Worker Startup Time: (TIMINGS)
Your Worker has access to the following bindings:
Binding Resource
env.KV2 (00000000000000000000000000000000) KV Namespace
env.R2 (inherited) R2 Bucket
Uploaded tmp-e2e-worker-00000000-0000-0000-0000-000000000000 (TIMINGS)
Worker Version ID: 00000000-0000-0000-0000-000000000000
To deploy this version to production traffic use the command wrangler versions deploy
Changes to non-versioned settings (config properties 'logpush' or 'tail_consumers') take effect after your next deployment using the command wrangler versions deploy
Changes to triggers (routes, custom domains, cron schedules, etc) must be applied with the command wrangler triggers deploy"
`);
const kvMatch = output.match(/env.KV2 \((?<kv>[0-9a-f]{32})/);
assert(kvMatch?.groups);
kvId2 = kvMatch.groups.kv;
Expand Down
12 changes: 12 additions & 0 deletions packages/wrangler/src/__tests__/deploy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
mockOAuthFlow,
} from "./helpers/mock-oauth-flow";
import { mockUploadWorkerRequest } from "./helpers/mock-upload-worker";
import { mockGetSettings } from "./helpers/mock-worker-settings";
import {
mockGetWorkerSubdomain,
mockSubDomainRequest,
Expand Down Expand Up @@ -105,7 +106,16 @@ describe("deploy", () => {
mockLastDeploymentRequest();
mockDeploymentsListRequest();
mockPatchScriptSettings();
mockGetSettings();
msw.use(...mswListNewDeploymentsLatestFull);
// Pretend all R2 buckets exist for the purposes of deployment testing.
// Otherwise, wrangler deploy would try to provision them. The provisioning
// behaviour is tested in provision.test.ts
msw.use(
http.get("*/accounts/:accountId/r2/buckets/:bucketName", async () => {
return HttpResponse.json(createFetchResult({}));
})
);
});

afterEach(() => {
Expand Down Expand Up @@ -1104,6 +1114,7 @@ describe("deploy", () => {
});

await runWrangler("deploy index.js --env some-env --legacy-env false");

expect(std.out).toMatchInlineSnapshot(`
"
⛅️ wrangler x.x.x
Expand Down Expand Up @@ -14362,6 +14373,7 @@ export default{
msw.use(...mswListNewDeploymentsLatestFull);

mockSubDomainRequest();
mockGetSettings();
writeWorkerSource();
setIsTTY(false);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -729,10 +729,13 @@ describe("dev with remote bindings", { sequential: true }, () => {
),
"index.js": `export default { fetch() { return new Response("hello") } }`,
});
const wranglerStopped = runWrangler("dev --port=0 --inspector-port=0");
const wranglerStopped = runWrangler(
"dev --x-provision=false --port=0 --inspector-port=0"
);
await vi.waitFor(() => expect(std.out).toMatch(/Ready/), {
timeout: 2_000,
});

expect(sessionOptions).toEqual({
auth: {
accountId: "mock-account-id",
Expand All @@ -743,7 +746,9 @@ describe("dev with remote bindings", { sequential: true }, () => {
complianceRegion: undefined,
workerName: "worker",
});

await stopWrangler();

await wranglerStopped;
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe("experimental_getWranglerCommands", () => {
"alias": Array [
"x-provision",
],
"default": true,
"describe": "Experimental: Enable automatic resource provisioning",
"hidden": true,
"type": "boolean",
Expand Down
37 changes: 37 additions & 0 deletions packages/wrangler/src/__tests__/helpers/mock-worker-settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { http, HttpResponse } from "msw";
import { msw } from "./msw";
import type { Settings } from "../../deployment-bundle/bindings";

export function mockGetSettings(
options: {
result?: Settings;
assertAccountId?: string;
assertScriptName?: string;
} = {}
) {
msw.use(
http.get(
"*/accounts/:accountId/workers/scripts/:scriptName/settings",
async ({ params }) => {
if (options.assertAccountId) {
expect(params.accountId).toEqual(options.assertAccountId);
}

if (options.assertScriptName) {
expect(params.scriptName).toEqual(options.assertScriptName);
}

if (!options.result) {
return new Response(null, { status: 404 });
}

return HttpResponse.json({
success: true,
errors: [],
messages: [],
result: options.result,
});
}
)
);
}
1 change: 1 addition & 0 deletions packages/wrangler/src/__tests__/metrics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ describe("metrics", () => {
command: "wrangler docs",
args: {
xRemoteBindings: true,
xProvision: true,
xAutoCreate: true,
search: ["<REDACTED>"],
},
Expand Down
Loading
Loading