From ab824ad0b92fab3e058791183f219816e007285a Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Thu, 10 Feb 2022 12:36:52 +0000 Subject: [PATCH 1/2] feat: add support for publishing workers with r2 bucket bindings This change adds the ability to define bindings in your `wrangler.toml` file for R2 buckets. These buckets will then be available in the environment passed to the worker at runtime. Closes #365 --- .changeset/purple-planes-hug.md | 11 ++++++ .../wrangler/src/__tests__/publish.test.ts | 29 +++++++++++++++ packages/wrangler/src/api/form_data.ts | 9 +++++ packages/wrangler/src/api/worker.ts | 6 +++ packages/wrangler/src/config.ts | 37 ++++++++++++++++++- packages/wrangler/src/publish.ts | 1 + 6 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 .changeset/purple-planes-hug.md diff --git a/.changeset/purple-planes-hug.md b/.changeset/purple-planes-hug.md new file mode 100644 index 000000000000..2daf7e8c4a2a --- /dev/null +++ b/.changeset/purple-planes-hug.md @@ -0,0 +1,11 @@ +--- +"wrangler": patch +--- + +feat: add support for publishing workers with r2 bucket bindings + +This change adds the ability to define bindings in your `wrangler.toml` file +for R2 buckets. These buckets will then be available in the environment +passed to the worker at runtime. + +Closes #365 diff --git a/packages/wrangler/src/__tests__/publish.test.ts b/packages/wrangler/src/__tests__/publish.test.ts index f7917bd1f07d..729af8ebf45a 100644 --- a/packages/wrangler/src/__tests__/publish.test.ts +++ b/packages/wrangler/src/__tests__/publish.test.ts @@ -1288,6 +1288,35 @@ export default{ }); }); + describe("r2 bucket bindings", () => { + it("should support r2 bucket bindings", async () => { + writeWranglerToml({ + r2_buckets: [{ binding: "FOO", bucket_name: "foo-bucket" }], + }); + writeWorkerSource(); + mockSubDomainRequest(); + mockUploadWorkerRequest({ + expectedBindings: [ + { bucket_name: "foo-bucket", name: "FOO", type: "r2_bucket" }, + ], + }); + + await runWrangler("publish index.js"); + expect(std.out).toMatchInlineSnapshot(` + "Uploaded + test-name + (TIMINGS) + Deployed + test-name + (TIMINGS) + + test-name.test-sub-domain.workers.dev" + `); + expect(std.err).toMatchInlineSnapshot(`""`); + expect(std.warn).toMatchInlineSnapshot(`""`); + }); + }); + describe("unsafe bindings", () => { it("should warn if using unsafe bindings", async () => { writeWranglerToml({ diff --git a/packages/wrangler/src/api/form_data.ts b/packages/wrangler/src/api/form_data.ts index a5fd0f2b0632..e3c41333075e 100644 --- a/packages/wrangler/src/api/form_data.ts +++ b/packages/wrangler/src/api/form_data.ts @@ -43,6 +43,7 @@ export interface WorkerMetadata { class_name: string; script_name?: string; } + | { type: "r2_bucket"; name: string; bucket_name: string } )[]; } @@ -82,6 +83,14 @@ export function toFormData(worker: CfWorkerInit): FormData { } ); + bindings.r2_buckets?.forEach(({ binding, bucket_name }) => { + metadataBindings.push({ + name: binding, + type: "r2_bucket", + bucket_name, + }); + }); + Object.entries(bindings.vars || {})?.forEach(([key, value]) => { if (typeof value === "string") { metadataBindings.push({ name: key, type: "plain_text", text: value }); diff --git a/packages/wrangler/src/api/worker.ts b/packages/wrangler/src/api/worker.ts index 9efa037f61c6..43c661954fde 100644 --- a/packages/wrangler/src/api/worker.ts +++ b/packages/wrangler/src/api/worker.ts @@ -99,6 +99,11 @@ interface CfDurableObject { script_name?: string; } +interface CfR2Bucket { + binding: string; + bucket_name: string; +} + interface CfUnsafeBinding { name: string; type: string; @@ -138,6 +143,7 @@ export interface CfWorkerInit { kv_namespaces?: CfKvNamespace[]; wasm_modules?: CfWasmModuleBindings; durable_objects?: { bindings: CfDurableObject[] }; + r2_buckets?: CfR2Bucket[]; unsafe?: CfUnsafeBinding[]; }; migrations: undefined | CfDurableObjectMigrations; diff --git a/packages/wrangler/src/config.ts b/packages/wrangler/src/config.ts index 8bcc579654ba..722ca396c2ab 100644 --- a/packages/wrangler/src/config.ts +++ b/packages/wrangler/src/config.ts @@ -203,6 +203,15 @@ export type Config = { preview_id?: string; }[]; + r2_buckets?: { + /** The binding name used to refer to the R2 bucket in the worker. */ + binding: string; + /** The name of this R2 bucket at the edge. */ + bucket_name: string; + /** The preview name of this R2 bucket at the edge. */ + preview_bucket_name?: string; + }[]; + /** * A list of services that your worker should be bound to. * NB: these are not inherited, and HAVE to be duplicated across all environments. @@ -570,7 +579,7 @@ export function normaliseAndValidateEnvironmentsConfig(config: Config) { } // Warn if there is a "required" field in the top level config that has not been specified specified in the environment. - // These required fields are `vars`, `durable_objects`, and `kv_namespaces`. + // These required fields are `vars`, `durable_objects`, `kv_namespaces`, and "r2_buckets". // Each of them has different characteristics that need to be checked. // `vars` is just an object @@ -645,5 +654,31 @@ export function normaliseAndValidateEnvironmentsConfig(config: Config) { } } } + + // `r2_buckets` contains an array of bucket bindings + if (config.r2_buckets !== undefined) { + if (environment.r2_buckets === undefined) { + console.warn( + `In your configuration, "r2_buckets" exists at the top level, but not on "env.${envKey}".\n` + + `This is not what you probably want, since "r2_buckets" is not inherited by environments.\n` + + `Please add "r2_buckets" to "env.${envKey}".` + ); + } else { + const envBindings = new Set( + environment.r2_buckets.map((r2Bucket) => r2Bucket.binding) + ); + for (const bindingName of config.r2_buckets.map( + (r2Bucket) => r2Bucket.binding + )) { + if (!envBindings.has(bindingName)) { + console.warn( + `In your configuration, there is a r2_buckets with binding "${bindingName}" at the top level, but not on "env.${envKey}".\n` + + `This is not what you probably want, since "r2_buckets" is not inherited by environments.\n` + + `Please add a binding for "${bindingName}" to "env.${envKey}.r2_buckets".` + ); + } + } + } + } } } diff --git a/packages/wrangler/src/publish.ts b/packages/wrangler/src/publish.ts index 75407c8e18c7..005b7628f0c3 100644 --- a/packages/wrangler/src/publish.ts +++ b/packages/wrangler/src/publish.ts @@ -205,6 +205,7 @@ export default async function publish(props: Props): Promise { vars: envRootObj.vars, wasm_modules: config.wasm_modules, durable_objects: envRootObj.durable_objects, + r2_buckets: envRootObj.r2_buckets, unsafe: envRootObj.unsafe?.bindings, }; From 69639d1690306a190d9afe9ec81b0efaa0373885 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 13 Feb 2022 08:30:52 +0000 Subject: [PATCH 2/2] fixup! feat: add support for publishing workers with r2 bucket bindings --- packages/wrangler/src/__tests__/dev.test.tsx | 9 ++++++++- packages/wrangler/src/api/worker.ts | 12 ++++++------ packages/wrangler/src/index.tsx | 8 ++++++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/wrangler/src/__tests__/dev.test.tsx b/packages/wrangler/src/__tests__/dev.test.tsx index 2be088492f12..0534bb91724d 100644 --- a/packages/wrangler/src/__tests__/dev.test.tsx +++ b/packages/wrangler/src/__tests__/dev.test.tsx @@ -39,7 +39,14 @@ function renderDev({ initialMode = "local", jsxFactory, jsxFragment, - bindings = {}, + bindings = { + kv_namespaces: [], + vars: {}, + durable_objects: { bindings: [] }, + r2_buckets: [], + wasm_modules: {}, + unsafe: [], + }, public: publicDir, assetPaths, compatibilityDate, diff --git a/packages/wrangler/src/api/worker.ts b/packages/wrangler/src/api/worker.ts index 43c661954fde..fb546ac2234a 100644 --- a/packages/wrangler/src/api/worker.ts +++ b/packages/wrangler/src/api/worker.ts @@ -139,12 +139,12 @@ export interface CfWorkerInit { * All the bindings */ bindings: { - vars?: CfVars; - kv_namespaces?: CfKvNamespace[]; - wasm_modules?: CfWasmModuleBindings; - durable_objects?: { bindings: CfDurableObject[] }; - r2_buckets?: CfR2Bucket[]; - unsafe?: CfUnsafeBinding[]; + vars: CfVars | undefined; + kv_namespaces: CfKvNamespace[] | undefined; + wasm_modules: CfWasmModuleBindings | undefined; + durable_objects: { bindings: CfDurableObject[] } | undefined; + r2_buckets: CfR2Bucket[] | undefined; + unsafe: CfUnsafeBinding[] | undefined; }; migrations: undefined | CfDurableObjectMigrations; compatibility_date: string | undefined; diff --git a/packages/wrangler/src/index.tsx b/packages/wrangler/src/index.tsx index aef7fbcb87e1..e1531d27f127 100644 --- a/packages/wrangler/src/index.tsx +++ b/packages/wrangler/src/index.tsx @@ -77,8 +77,8 @@ async function readConfig(configPath?: string): Promise { // TODO: remove this error before GA. if ("experimental_services" in config) { throw new Error( - `The "experimental_services" field is no longer supported. Instead, use [[unsafe.bindings]] to enable experimental features. Add this to your wrangler.toml: - + `The "experimental_services" field is no longer supported. Instead, use [[unsafe.bindings]] to enable experimental features. Add this to your wrangler.toml: + ${TOML.stringify({ unsafe: { bindings: (config.experimental_services || []).map((serviceDefinition) => { @@ -811,6 +811,7 @@ export async function main(argv: string[]): Promise { vars: envRootObj.vars, wasm_modules: config.wasm_modules, durable_objects: envRootObj.durable_objects, + r2_buckets: envRootObj.r2_buckets, unsafe: envRootObj.unsafe?.bindings, }} /> @@ -1307,6 +1308,9 @@ export async function main(argv: string[]): Promise { kv_namespaces: [], vars: {}, durable_objects: { bindings: [] }, + r2_buckets: [], + wasm_modules: {}, + unsafe: [], }, modules: [], migrations: undefined,