Skip to content

Commit

Permalink
Experimental worker-to-worker bindings support (#166)
Browse files Browse the repository at this point in the history
* Refactor how we pass bindings from config to the worker upload api.

There are 3 types of 'bindings' that we upload with a worker definition: `vars`, `kv_namespaces`, and `durable_objects`. These are configured in `wrangler.toml`, and get passed through to the api that uploads a worker definition (in `form_data.ts`), when using `dev` and `publish` commands.

We currently serialise it as a big `variables` object, where the key of the objects defines the binding "name", and the value contains information to be bound. (https://github.com/cloudflare/wrangler2/blob/0330ecf1b54c92dfe86cb3f38394f453ed418381/packages/wrangler/src/index.tsx#L507-L530) This code sucks for many reasons: it loses some type information, hard to read/grok, hard to add new types of bindings, and mostly it's unnecessary.

This PR refactors this code to instead mostly preserve the configuration style structure all the way until we actually serialise it when uploading a worker definition. I also added some more types along the way, and cleared some dead code.

I will follow up this PR with 2 PRs soon: one, which introduces service bindings (as a fresh take on #156), and another one that introduces tests for configuration.

* correctly pass the name of a kv namespace when binding

oops.

* Pass a couple of lint warnings, un-export some unused exports

* Experimental worker-to-worker bindings support

Adds support for experimental worker-to-worker bindings, letting you call a worker from another worker. The Wrangler config field looks like follows:

```toml
services = [{ name = "foo", script_name = "foo-service", environment = "production" }]
```
Where:
- `name` is the name of the binding in your worker code
- `script_name` is the script to reference
- `environment` is the environment of the script to reference (e.g. `production`, `staging`, etc)

### What doesn't work?

- The API still doesn't support worker-to-worker bindings for previews, so `wrangler dev` will fail for now

### Differences from #156

This PR is based in #156, with a few changes -

- the configuration field `env` is renamed to `environment` (we want to move away from short forms like `env`)
- the configuration field `environment` is not an optional field (because we want to explicitly bind to environments, or folks could accidentally mess with production data)
- the configuration field services is called `experimental_services` so it's clear on usage that this is not ready for production usage yet, and is only for our internal testing. We also log a warning when we see the field.

* Add a changeset

* s/script_name/service
  • Loading branch information
threepointone authored Dec 24, 2021
1 parent 34ad323 commit ce2d7d1
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/shiny-crews-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": patch
---

Add experimental support for worker-to-worker service bindings. This introduces a new field in configuration `experimental_services`, and serialises it when creating and uploading a worker definition. This is highly experimental, and doesn't work with `wrangler dev` yet.
15 changes: 15 additions & 0 deletions packages/wrangler/src/api/form_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ interface WorkerMetadata {
class_name: string;
script_name?: string;
}
| {
type: "service";
name: string;
service: string;
environment: string;
}
)[];
}

Expand Down Expand Up @@ -88,6 +94,15 @@ export function toFormData(worker: CfWorkerInit): FormData {
metadataBindings.push({ name: key, type: "plain_text", text: value });
});

bindings.services?.forEach(({ name, service, environment }) => {
metadataBindings.push({
name,
type: "service",
service,
environment,
});
});

const metadata: WorkerMetadata = {
...(mainType !== "commonjs" ? { main_module: name } : { body_part: name }),
bindings: metadataBindings,
Expand Down
10 changes: 10 additions & 0 deletions packages/wrangler/src/api/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,15 @@ interface CfDurableObject {
script_name?: string;
}

/**
* A Service.
*/
interface CfService {
name: string;
service: string;
environment: string;
}

export interface CfDurableObjectMigrations {
old_tag?: string;
new_tag: string;
Expand Down Expand Up @@ -119,6 +128,7 @@ export interface CfWorkerInit {
kv_namespaces?: CfKvNamespace[];
durable_objects?: { bindings: CfDurableObject[] };
vars?: CfVars;
services?: CfService[];
};
migrations: void | CfDurableObjectMigrations;
compatibility_date: string | void;
Expand Down
11 changes: 10 additions & 1 deletion packages/wrangler/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ type DurableObject = {
script_name?: string;
};

type Service = {
name: string;
service: string;
environment: string;
};

type Build = {
command?: string;
cwd?: string;
Expand Down Expand Up @@ -86,6 +92,8 @@ type Env = {
vars?: Vars;
durable_objects?: { bindings: DurableObject[] };
kv_namespaces?: KVNamespace[];
experimental_services?: Service[];
migrations?: DurableObjectMigration[];
usage_model?: UsageModel; // inherited
};

Expand All @@ -108,9 +116,10 @@ export type Config = {
jsx_factory?: string; // inherited
jsx_fragment?: string; // inherited
vars?: Vars;
migrations?: DurableObjectMigration[];
durable_objects?: { bindings: DurableObject[] };
kv_namespaces?: KVNamespace[];
experimental_services?: Service[];
migrations?: DurableObjectMigration[];
site?: Site; // inherited
// we should use typescript to parse cron patterns
triggers?: { crons: Cron[] }; // inherited
Expand Down
14 changes: 13 additions & 1 deletion packages/wrangler/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@ async function readConfig(path?: string): Promise<Config> {
});
});

const mirroredFields = ["vars", "kv_namespaces", "durable_objects"];
const mirroredFields = [
"vars",
"kv_namespaces",
"durable_objects",
"experimental_services",
];
Object.keys(config.env || {}).forEach((env) => {
mirroredFields.forEach((field) => {
// if it exists on top level, it should exist on env defns
Expand All @@ -93,6 +98,12 @@ async function readConfig(path?: string): Promise<Config> {
});
});

if ("experimental_services" in config) {
console.warn(
"The experimental_services field is only for cloudflare internal usage right now, and is subject to change. Please do not use this on production projects"
);
}

// todo: validate, add defaults
// let's just do some basics for now

Expand Down Expand Up @@ -528,6 +539,7 @@ export async function main(argv: string[]): Promise<void> {
),
vars: envRootObj.vars,
durable_objects: envRootObj.durable_objects,
services: envRootObj.experimental_services,
}}
/>
);
Expand Down
1 change: 1 addition & 0 deletions packages/wrangler/src/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ export default async function publish(props: Props): Promise<void> {
),
vars: envRootObj.vars,
durable_objects: envRootObj.durable_objects,
services: envRootObj.experimental_services,
};

const worker: CfWorkerInit = {
Expand Down

0 comments on commit ce2d7d1

Please sign in to comment.