From 0ff375144c50669a649f976be3a21e6b46b10e58 Mon Sep 17 00:00:00 2001 From: diana esteves Date: Tue, 24 Sep 2024 16:43:59 -0500 Subject: [PATCH 001/110] [Pulumi] Tutorial updates (#16829) * wip * heading formatting * wip * wip * wip * updated worker script code * updated route code * updated code * update tutorial flow * lint check * grammar * grammar * undo settings file change * cf signup link * update code to use esc projects * Update src/content/docs/pulumi/tutorial/manage-secrets.mdx Co-authored-by: Claire W <78226508+crwaters16@users.noreply.github.com> * Update src/content/docs/pulumi/tutorial/manage-secrets.mdx Co-authored-by: Claire W <78226508+crwaters16@users.noreply.github.com> --------- Co-authored-by: diana esteves Co-authored-by: Claire W <78226508+crwaters16@users.noreply.github.com> --- src/content/docs/pulumi/tutorial/add-site.mdx | 594 +++----- .../docs/pulumi/tutorial/hello-world.mdx | 1231 ++++++++++++++--- .../docs/pulumi/tutorial/manage-secrets.mdx | 18 +- 3 files changed, 1245 insertions(+), 598 deletions(-) diff --git a/src/content/docs/pulumi/tutorial/add-site.mdx b/src/content/docs/pulumi/tutorial/add-site.mdx index c3a5d406c46956..a9321ed6bae090 100644 --- a/src/content/docs/pulumi/tutorial/add-site.mdx +++ b/src/content/docs/pulumi/tutorial/add-site.mdx @@ -9,7 +9,8 @@ languages: - Java - .NET - YAML -updated: 2024-08-02 +updated: 2024-09-13 +difficulty: Beginner sidebar: order: 1 head: @@ -19,10 +20,9 @@ head: import { TabItem, Tabs } from "~/components"; -In this tutorial, you will go through step-by-step instructions to bring an existing site to Cloudflare using Pulumi Infrastructure as Code (IaC) so that you can become familiar with the resource management lifecycle. In particular, you will create a Zone and a DNS record to resolve your newly added site. This tutorial adopts the IaC principle to complete the steps listed in the [Add site tutorial](/fundamentals/setup/manage-domains/add-site/). +In this tutorial, you will follow step-by-step instructions to bring an existing site to Cloudflare using Pulumi infrastructure as code (IaC) to familiarize yourself with the resource management lifecycle. In particular, you will create a Zone and a DNS record to resolve your newly added site. This tutorial adopts the IaC principle to complete the steps listed in the [Add site tutorial](/fundamentals/setup/manage-domains/add-site/). :::note - You will provision resources that qualify under free tier offerings for both Pulumi Cloud and Cloudflare. ::: @@ -30,16 +30,18 @@ You will provision resources that qualify under free tier offerings for both Pul Ensure you have: -- A Cloudflare account and API Token with permission to edit the resources in this tutorial. If you need to, sign up for a [Cloudflare account](https://dash.cloudflare.com/sign-up/workers-and-pages) before continuing. Your token must have: +- A Cloudflare account and API Token with permission to edit the resources in this tutorial. If you need to, sign up for a [Cloudflare account](https://www.cloudflare.com/sign-up) before continuing. Your token must have: - `Zone-Zone-Edit` permission - `Zone-DNS-Edit` permission - `include-All zones from an account-` zone resource -- A Pulumi Cloud account. You can sign up for an [always-free, individual tier](https://app.pulumi.com/signup). +- A Pulumi Cloud account. You can sign up for an [always-free individual tier](https://app.pulumi.com/signup). - The [Pulumi CLI](/pulumi/installing/) is installed on your machine. - A [Pulumi-supported programming language](https://github.com/pulumi/pulumi?tab=readme-ov-file#languages) is configured. (TypeScript, JavaScript, Python, Go, .NET, Java, or use YAML) - A domain name. You may use `example.com` to complete the tutorial. -## Initialize Pulumi +## 1. Initialize your project + +A Pulumi project is a collection of files in a dedicated folder that describes the infrastructure you want to create. The Pulumi project folder is identified by the required `Pulumi.yaml` file. You will use the Pulumi CLI to create and configure a new project. ### a. Create a directory @@ -50,7 +52,9 @@ mkdir addsite-cloudflare cd addsite-cloudflare ``` -### b. Login +### b. Login to Pulumi Cloud + +[Pulumi Cloud](https://www.pulumi.com/product/pulumi-cloud/) is a hosted service that provides a secure and scalable platform for managing your infrastructure as code. You will use it to store your Pulumi backend configurations. At the prompt, press Enter to log into your Pulumi Cloud account via the browser. Alternatively, you may provide a [Pulumi Cloud access token](https://www.pulumi.com/docs/pulumi-cloud/access-management/access-tokens/). @@ -58,12 +62,9 @@ At the prompt, press Enter to log into your Pulumi Cloud account via the browser pulumi login ``` -### c. Create a program - -:::note +### c. Create a new program A Pulumi program is code written in a [supported programming language](https://github.com/pulumi/pulumi?tab=readme-ov-file#languages) that defines infrastructure resources. -::: To create a program, select your language of choice and run the `pulumi` command: @@ -117,72 +118,55 @@ pulumi new yaml --name addsite-cloudflare --yes -### d. Save your settings +### d. Create a stack + +A Pulumi [stack](https://www.pulumi.com/docs/concepts/stack/) is an instance of a Pulumi program. Stacks are independently configurable and may represent different environments (development, staging, production) or feature branches. For this tutorial, you'll use the `dev` stack. + +To instantiate your `dev` stack, run: + +```sh +pulumi up --yes +# wait a few seconds for the stack to be instantiated. +``` + +You have not defined any resources at this point, so you'll have an empty stack. -You will need: +### e. Save your settings + +In this step, you will store your settings in a Pulumi [ESC Environment](https://www.pulumi.com/docs/esc/environments/), a YAML file containing configurations and secrets. These can be accessed in several ways, including a Pulumi program. All ESC Environments securely reside in your Pulumi Cloud account and can be fully managed via the Pulumi CLI. For this tutorial, you will store the following values: - Your Cloudflare [account ID](/fundamentals/setup/find-account-and-zone-ids/). - A valid Cloudflare API [token](/fundamentals/api/get-started/create-token/). - A domain. For instance, `example.com`. -:::note - -A Pulumi [ESC Environment](https://www.pulumi.com/docs/esc/environments/) is a YAML file containing configurations and secrets that pertain to your application and infrastructure. These can be accessed in several ways, including a Pulumi program. All ESC Environments reside in your Pulumi Cloud account. -::: - ```sh # Define an ESC Environment name -E=my-dev-env +E=clouflare/my-dev-env # Create a new Pulumi ESC Environment -pulumi config env init --env $E --yes --stack dev +pulumi config env init --env $E --yes +``` -# Replace API_TOKEN with your Cloudflare API Token -pulumi env set $E --secret pulumiConfig.cloudflare:apiToken API_TOKEN +```sh output +Creating environment clouflare/my-dev-env for stack dev... +``` +```sh # Replace abc123 with your Cloudflare Account ID pulumi env set $E --plaintext pulumiConfig.accountId abc123 +# Replace API_TOKEN with your Cloudflare API Token +pulumi env set $E --secret pulumiConfig.cloudflare:apiToken API_TOKEN + # Replace example.com with your registered domain, or leave as is pulumi env set $E --plaintext pulumiConfig.domain example.com - -# Review your ESC Environment -pulumi env open $E -{ - "pulumiConfig": { - "accountId": "111222333", - "cloudflare:apiToken": "abc123abc123", - "domain": "example.com" - } -} -``` - -### e. Create a stack - -:::note - -A Pulumi [stack](https://www.pulumi.com/docs/concepts/stack/) is an instance of a Pulumi program. Stacks are independently configurable and may represent different environments (development, staging, production) or feature branches. For this tutorial, you'll use the `dev` stack. -::: - -To instantiate your `dev` stack, run: - -```sh -pulumi up --yes --stack dev -# wait a few seconds for the stack to be instantiated. ``` -At this point, you have not defined any resources so you'll have an empty stack. - -## Add a Zone +### f. Install the Cloudflare package -:::note - -A domain, or site, is known as a Zone in Cloudflare. -::: +You need to install the Cloudflare package for your language of choice in order to define Cloudflare resources in your Pulumi program. -You will now add the Pulumi Cloudflare package and a Cloudflare Zone resource to your Pulumi program. - -### a. Install dependencies +Install the Cloudflare package by running the following command: @@ -207,15 +191,13 @@ added 1 package ... ```sh -echo "pulumi_cloudflare>=5.35,<6.0.0" >> requirements.txt +echo "pulumi_cloudflare>=5.38,<6.0.0" >> requirements.txt source venv/bin/activate pip install -r requirements.txt ``` ```sh output -... -Collecting pulumi-cloudflare -... +...Collecting pulumi-cloudflare... ``` @@ -239,20 +221,18 @@ Below are Apache Maven instructions. For other Java project managers such as Gra com.pulumi cloudflare - 5.35.1 + 5.38.0 ``` -1. Run: +3. Run: ```sh mvn clean install ``` ```sh output -... -[INFO] BUILD SUCCESS -... +...[INFO] BUILD SUCCESS... ``` @@ -273,12 +253,20 @@ There are no dependencies to download for YAML. Skip ahead. -### b. Modify the program +## 2. Define Cloudflare resources in code + +With the Cloudflare package installed, you can now define any [supported Cloudflare resource](https://www.pulumi.com/registry/packages/cloudflare/) in your Pulumi program. You'll define a Zone, and a DNS Record next. + +### a. Add a Zone + +A domain, or site, is known as a Zone in Cloudflare. In Pulumi, the [Zone resource](https://www.pulumi.com/registry/packages/cloudflare/api-docs/zone/) represents a Cloudflare Zone. Replace the contents of your entrypoint file with the following: +**Filename: `index.js`** + ```js "use strict"; const pulumi = require("@pulumi/pulumi"); @@ -296,12 +284,15 @@ const zone = new cloudflare.Zone("my-zone", { jumpStart: true, }); -// Export the zone ID exports.zoneId = zone.id; +exports.nameservers = zone.nameServers; +exports.status = zone.status; ``` +**Filename: `index.ts`** + ```typescript import * as pulumi from "@pulumi/pulumi"; import * as cloudflare from "@pulumi/cloudflare"; @@ -318,12 +309,15 @@ const zone = new cloudflare.Zone("my-zone", { jumpStart: true, }); -// Export the zone ID export const zoneId = zone.id; +export const nameservers = zone.nameServers; +export const status = zone.status; ``` +**Filename: `__main__.py`** + ```py import pulumi import pulumi_cloudflare as cloudflare @@ -338,12 +332,15 @@ zone = cloudflare.Zone("my-zone", plan="free", jump_start=True) -# Export the zone ID pulumi.export("zoneId", zone.id) +pulumi.export('nameservers', zone.name_servers) +pulumi.export('status', zone.status) ``` +**Filename: `main.go`** + ```go package main @@ -366,8 +363,9 @@ func main() { return err } - // Export the zone ID ctx.Export("zoneId", zone.ID()) + ctx.Export("nameservers", zone.NameServers) + ctx.Export("status", zone.Status) return nil }) } @@ -375,7 +373,7 @@ func main() { -The entrypoint file is under the `src/main/java/myproject` directory. +**Filename: `src/main/java/myproject/App.java`** ```java package myproject; @@ -402,6 +400,8 @@ public class App { .build()); ctx.export("zoneId", zone.id()); + ctx.export("nameservers", zone.nameServers()); + ctx.export("status", zone.status()); }); } } @@ -409,8 +409,11 @@ public class App { +**Filename: `Program.cs`** + ```csharp using System.Threading.Tasks; +using System.Collections.Immutable; using Pulumi; using Pulumi.Cloudflare; @@ -435,20 +438,25 @@ class Program }); this.ZoneId = zone.Id; + this.Nameservers = zone.NameServers; + this.Status = zone.Status; } [Output] public Output ZoneId { get; set; } + public Output> Nameservers { get; set; } + public Output Status { get; set; } } } ``` -```yaml -environment: - - my-dev-env +**Filename: `Pulumi.yaml`** +```yaml +name: addsite-cloudflare +runtime: yaml resources: myZone: type: cloudflare:Zone @@ -460,178 +468,29 @@ resources: outputs: zoneId: ${myZone.id} + nameservers: ${exampleZone.nameServers} + status: ${exampleZone.status} ``` -### c. Apply the changes +Notice that the code also outputs several properties from the Zone resource, such as the `zoneId`, `nameservers`, and `status`, so that they can easily be accessed in subsequent steps. -```sh -pulumi up --yes --stack dev -# wait a few seconds while the changes take effect -``` - -### d. (Optional) Review the zone +### b. Add a DNS Record -Review the value of `zoneId` to confirm the Zone creation. +You will now add a DNS [Record resource](https://www.pulumi.com/registry/packages/cloudflare/api-docs/record/) to test previously configured Zone. -```sh -pulumi stack output zoneId -``` - -```sh output -d8fcb6d731fe1c2d75e2e8d6ad63fad5 -``` - -## Update your nameservers - -Once you have added a domain to Cloudflare, that domain will receive two assigned authoritative nameservers. - -:::note - -This process makes Cloudflare your authoritative DNS provider, allowing your DNS queries and web traffic to be served from and protected by the Cloudflare network. - -[Learn more about pending domains](/dns/zone-setups/reference/domain-status/) -::: - -### a. Update the program - -Towards the end of your entrypoint file, below the `zoneId` variable, add the following: +Add the following code snippet to your entrypoint file **after** the Zone resource definition: -```js -exports.nameservers = zone.nameServers; -exports.status = zone.status; -``` - - - -```typescript -export const nameservers = zone.nameServers; -export const status = zone.status; -``` - - - -```py -pulumi.export('nameservers', zone.name_servers) -pulumi.export('status', zone.status) -``` - - - -```go -ctx.Export("nameservers", zone.NameServers) -ctx.Export("status", zone.Status) -``` - - - -```java -ctx.export("nameservers", zone.nameServers()); -ctx.export("status", zone.status()); -``` - - - -1. Add `using System.Collections.Immutable;` at the top of your `Program.cs` file. -2. Below `this.ZoneId = zone.Id;`, add: - -```csharp -this.Nameservers = zone.NameServers; -this.Status = zone.Status; -``` - -3. Below `public Output ZoneId { get; set; }`, add: - -```csharp -public Output> Nameservers { get; set; } -public Output Status { get; set; } -``` - - - -```yaml -nameservers: ${exampleZone.nameServers} -status: ${exampleZone.status} -``` - - - -### b. Apply the changes - -```sh -pulumi up --yes --stack dev -``` - -### c. Obtain the nameservers - -Review the value of `nameservers` to retrieve the assigned nameservers: - -```sh -pulumi stack output --stack dev -``` - -### d. Update your registrar - -:::note - -If you use `example.com` as your site, skip ahead to [Add a DNS record](#add-a-dns-record). -::: - -Update the nameservers at your registrar to activate Cloudflare services for your domain. Instructions are registrar-specific. You may be able to find guidance under [this consolidated list of common registrars](/dns/zone-setups/full-setup/setup/#update-your-registrar). - -:::caution - -Registrars take up to 24 hours to process nameserver changes. -::: - -### e. Check your domain status - -Once successfully registered, your domain `status` will change to `active`. - -```sh -pulumi stack output -``` - -## Add a DNS record - -You will now add a DNS record to your domain. - -### a. Modify your program - -Below is the final version of how your Pulumi program entrypoint file should look. -Replace the contents of your entrypoint file with the following: - - +**Filename: `index.js`** ```js -"use strict"; -const pulumi = require("@pulumi/pulumi"); -const cloudflare = require("@pulumi/cloudflare"); - -const config = new pulumi.Config(); -const accountId = config.require("accountId"); -const domain = config.require("domain"); - -// Create a Cloudflare resource (Zone) -const zone = new cloudflare.Zone("my-zone", { - zone: domain, - accountId: accountId, - plan: "free", - jumpStart: true, -}); - -// Export the zone ID -exports.zoneId = zone.id; -exports.nameservers = zone.nameServers; -exports.status = zone.status; - const record = new cloudflare.Record("my-record", { zoneId: zone.id, name: domain, - value: "192.0.2.1", + content: "192.0.2.1", type: "A", proxied: true, }); @@ -639,36 +498,13 @@ const record = new cloudflare.Record("my-record", { -```typescript -import * as pulumi from "@pulumi/pulumi"; -import * as cloudflare from "@pulumi/cloudflare"; - -const config = new pulumi.Config(); -const accountId = config.require("accountId"); -const domain = config.require("domain"); - -// Create a Cloudflare resource (Zone) -const zone = new cloudflare.Zone("my-zone", { - zone: domain, - accountId: accountId, - plan: "free", // Choose the desired plan, e.g., "free", "pro", "business", etc. - jumpStart: true, -}); - -// Export the zone ID -export const zoneId = zone.id; - -// Export the Cloudflare-assigned nameservers. -export const nameservers = zone.nameServers; - -// Export the status -export const status = zone.status; +**Filename: `index.ts`** -// Set up a Record for your site +```typescript const record = new cloudflare.Record("my-record", { - zoneId: zoneId, + zoneId: zone.id, name: domain, - value: "192.0.2.1", + content: "192.0.2.1", type: "A", proxied: true, }); @@ -676,29 +512,13 @@ const record = new cloudflare.Record("my-record", { -```py -import pulumi -import pulumi_cloudflare as cloudflare - -account_id = pulumi.Config().require("accountId") -domain = pulumi.Config().require("domain") - -# Create a Cloudflare resource (Zone) -zone = cloudflare.Zone("my-zone", - zone=domain, - account_id=account_id, - plan="free", - jump_start=True) - -# Export the zone ID -pulumi.export("zoneId", zone.id) -pulumi.export('nameservers', zone.name_servers) -pulumi.export('status', zone.status) +**Filename: `__main__.py`** +```py record = cloudflare.Record("my-record", zone_id=zone.id, name=domain, - value="192.0.2.1", + content="192.0.2.1", type="A", proxied=True ) @@ -707,37 +527,14 @@ record = cloudflare.Record("my-record", -```go -package main - -import ( - cloudflare "github.com/pulumi/pulumi-cloudflare/sdk/v3/go/cloudflare" - "github.com/pulumi/pulumi/sdk/v3/go/pulumi" -) - -func main() { - pulumi.Run(func(ctx *pulumi.Context) error { - domain, _ := ctx.GetConfig("domain") - - // Create a Cloudflare resource (Zone) - zone, err := cloudflare.NewZone(ctx, "my-zone", &cloudflare.ZoneArgs{ - Zone: pulumi.String(domain), - Plan: pulumi.String("free"), - JumpStart: pulumi.Bool(true), - }) - if err != nil { - return err - } +**Filename: `main.go`** - // Export the zone ID - ctx.Export("zoneId", zone.ID()) - ctx.Export("nameservers", zone.NameServers) - ctx.Export("status", zone.Status) +```go _, err = cloudflare.NewRecord(ctx, "my-record", &cloudflare.RecordArgs{ ZoneId: zone.ID(), Name: pulumi.String(domain), - Value: pulumi.String("192.0.2.1"), + Content: pulumi.String("192.0.2.1"), Type: pulumi.String("A"), Proxied: pulumi.Bool(true), }) @@ -745,145 +542,114 @@ func main() { return err } - return nil - }) -} - ``` +**Filename: `src/main/java/myproject/App.java`** + ```java -package myproject; -import com.pulumi.Pulumi; -import com.pulumi.Context; -import com.pulumi.cloudflare.ZoneArgs; -import com.pulumi.cloudflare.Zone; +// Add imports import com.pulumi.cloudflare.Record; import com.pulumi.cloudflare.RecordArgs; - -public class App { - public static void main(String[] args) { - Pulumi.run(ctx -> { - var config = ctx.config(); - - String accountId = config.require("accountId"); - String domain = config.require("domain"); - - var zone = new Zone("my-zone", ZoneArgs.builder() - .zone(domain) - .accountId(accountId) - .plan("free") - .jumpStart(true) - .build()); - - ctx.export("zoneId", zone.id()); - ctx.export("nameservers", zone.nameServers()); - ctx.export("status", zone.status()); - - new Record("my-record", RecordArgs.builder() - .zoneId(zone.id()) - .name(domain) - .value("192.0.2.1") - .type("A") - .proxied(true) - .build()); - }); - } -} +// Below the Zone resource, add +new Record("my-record", RecordArgs.builder() +.zoneId(zone.id()) +.name(domain) +.content("192.0.2.1") +.type("A") +.proxied(true) +.build()); ``` -```csharp -using System.Collections.Immutable; -using System.Threading.Tasks; -using Pulumi; -using Pulumi.Cloudflare; +**Filename: `Program.cs`** -class Program +```csharp +new Record("my-record", new RecordArgs { - static Task Main() => Deployment.RunAsync(); - - class MyStack : Stack - { - public MyStack() - { - var config = new Pulumi.Config(); - var accountId = config.Require("accountId"); - var domain = config.Require("domain"); - - var zone = new Zone("my-zone", new ZoneArgs - { - ZoneName = domain, - AccountId = accountId, - Plan = "free", - JumpStart = true - }); - - this.ZoneId = zone.Id; - this.Nameservers = zone.NameServers; - this.Status = zone.Status; - - new Record("my-record", new RecordArgs - { - ZoneId = zone.Id, - Name = domain, - Value = "192.0.2.1", - Type = "A", - Proxied = true - }); - - } - - [Output] - public Output ZoneId { get; set; } - public Output> Nameservers { get; set; } - public Output Status { get; set; } - } -} + ZoneId = zone.Id, + Name = domain, + Content = "192.0.2.1", + Type = "A", + Proxied = true +}); ``` -```yaml -environment: - - my-dev-env - -resources: - myZone: - type: cloudflare:Zone - properties: - zone: ${domain} - accountId: ${accountId} - plan: "free" - jumpStart: true +**Filename: `Pulumi.yaml`** +```yaml myRecord: type: cloudflare:Record properties: zoneId: ${myZone.id} name: ${domain} - value: 192.0.2.1 + content: 192.0.2.1 type: A proxied: true -outputs: - zoneId: ${myZone.id} - nameservers: ${exampleZone.nameServers} - status: ${exampleZone.status} ``` -### b. Apply the changes +## 3. Deploy your changes + +Now that you have defined your resources, you can deploy the changes using the Pulumi CLI so that they are reflected in your Cloudflare account. + +To deploy the changes, run: ```sh -pulumi up --yes --stack dev +pulumi up --yes +``` + +```sh output +wait for the dev stack to become ready ``` -## Verify your setup +## 4. Configure your DNS provider + +:::note + +This process makes Cloudflare your authoritative DNS provider, allowing your DNS queries and web traffic to be served from and protected by the Cloudflare network. + +[Learn more about pending domains](/dns/zone-setups/reference/domain-status/) +::: + +:::note +If your site is `example.com`, skip ahead to [Test your site](#5-test-your-site). +::: + +### a. Obtain your nameservers + +Once you have added a domain to Cloudflare, that domain will receive two assigned authoritative nameservers. + +To retrieve the assigned `nameservers`, run: + +```sh +pulumi stack output +``` + +### b. Update your registrar + +Update the nameservers at your registrar to activate Cloudflare services for your domain. The instructions are registrar-specific. You may be able to find guidance under [this consolidated list of common registrars](/dns/zone-setups/full-setup/setup/#update-your-registrar). + +:::caution +Registrars take up to 24 hours to process nameserver changes. +::: + +### c. Check your domain status + +Once successfully registered, your domain `status` will change to `active`. + +```sh +pulumi stack output +``` + +## 5. Test your site You will run two `nslookup` commands against the Cloudflare-assigned nameservers. @@ -897,11 +663,15 @@ nslookup $DOMAIN $NS1 nslookup $DOMAIN $NS2 ``` -For .NET use `Nameservers` as the Output. +For .NET, use `Nameservers` as the Output. Confirm your response returns the IP address(es) for your site. -## Clean up +:::note +You will not receive a valid response if you use `example.com` as your site. +::: + +## 6. Clean up In this last step, you will remove the resources and stack used throughout the tutorial. @@ -917,8 +687,8 @@ pulumi destroy --yes pulumi stack rm dev ``` -You have incrementally defined Cloudflare resources needed to add a site to Cloudflare. After each new resource, you apply the changes to your `dev` stack via the `pulumi up` command. You declare the resources in your programming language of choice and let Pulumi handle the rest. - ## Next steps -Follow the [Hello World tutorial](/pulumi/tutorial/hello-world/) to deploy a serverless app with Pulumi. +You have incrementally defined Cloudflare resources needed to add a site to Cloudflare. You declare the resources in your programming language of choice and let Pulumi handle the rest. + +To deploy a serverless app with Pulumi, follow the [Deploy a Worker tutorial](/pulumi/tutorial/hello-world/). \ No newline at end of file diff --git a/src/content/docs/pulumi/tutorial/hello-world.mdx b/src/content/docs/pulumi/tutorial/hello-world.mdx index ed931d4c5b9686..7ec579c436dd69 100644 --- a/src/content/docs/pulumi/tutorial/hello-world.mdx +++ b/src/content/docs/pulumi/tutorial/hello-world.mdx @@ -1,13 +1,18 @@ --- -title: Deploy a Worker with Pulumi +title: Deploy a Worker pcx_content_type: tutorial products: - Workers -updated: 2024-01-08 -content_type: 📝 Tutorial +updated: 2024-09-13 difficulty: Beginner languages: + - JavaScript - TypeScript + - Python + - Go + - Java + - .NET + - YAML sidebar: order: 3 head: @@ -15,325 +20,1193 @@ head: content: Deploy a Worker --- -In this tutorial, you will go through step-by-step instructions to deploy a Hello World web application using Cloudflare Workers and Pulumi Infrastructure as Code (IaC) so that you can become familiar with the resource management lifecycle. In particular, you will create a Worker, a Route, and a DNS Record to access the application before cleaning up all the resources. +import { TabItem, Tabs } from "~/components"; -![alt_text](~/assets/images/pulumi/hello-world-tutorial/sn2.png "Running Cloudflare Workers application deployed with Pulumi") +In this tutorial, you will follow step-by-step instructions to deploy a Hello World application using Cloudflare Workers and Pulumi infrastructure as code (IaC) to familiarize yourself with the resource management lifecycle. In particular, you will create a Worker, a Route, and a DNS Record to access the application before cleaning up all the resources. :::note - You will provision resources that qualify under free tier offerings for both Pulumi Cloud and Cloudflare. - ::: ## Before you begin Ensure you have: + +- A Cloudflare account and API Token with permission to edit the resources in this tutorial. If you need to, sign up for a [Cloudflare account](https://www.cloudflare.com/sign-up) before continuing. Your token must have the following: + - `Account-Workers Scripts-Edit` permission + - `Zone-Workers Route-Edit` permission + - `Zone-DNS-Edit` permission +- A Pulumi Cloud account. You can sign up for an [always-free individual tier](https://app.pulumi.com/signup). +- The [Pulumi CLI](/pulumi/installing/) is installed on your machine. +- A [Pulumi-supported programming language](https://github.com/pulumi/pulumi?tab=readme-ov-file#languages) configured. (TypeScript, JavaScript, Python, Go, .NET, Java, or use YAML) +- A Cloudflare-managed domain. Complete the [Add a site tutorial](/pulumi/tutorial/add-site/) to bring your existing domain under Cloudflare. -- A Cloudflare account and API Token with permission to edit the resources in this tutorial. If you need to, sign up for a [Cloudflare account](https://dash.cloudflare.com/sign-up/workers-and-pages) before continuing. -- A Pulumi Cloud account. You can sign up for an [always-free, individual tier](https://app.pulumi.com/signup). -- [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) and the [Pulumi CLI](/pulumi/installing/) installed on your machine. -- A Cloudflare Zone. Complete the [Add a Site tutorial](/pulumi/tutorial/add-site/) to create one. +## 1. Initialize your project -:::note[Link to the full solution] +A Pulumi project is a collection of files in a dedicated folder that describes the infrastructure you want to create. The Pulumi project folder is identified by the required `Pulumi.yaml` file. You will use the Pulumi CLI to create and configure a new project. + +### a. Create a directory -You can find the complete solution of this tutorial under [this Pulumi repo and branch](https://github.com/pulumi/tutorials/tree/cloudflare-typescript-hello-world-end). To deploy the final version, run the following: +Use a new and empty directory for this tutorial. ```sh -mkdir serverless-cloudflare && cd serverless-cloudflare -pulumi new https://github.com/pulumi/tutorials/tree/cloudflare-typescript-hello-world-end -npm install -pulumi up --yes +mkdir serverless-cloudflare +cd serverless-cloudflare ``` -::: +### b. Login to Pulumi Cloud -## Initialize Pulumi +[Pulumi Cloud](https://www.pulumi.com/product/pulumi-cloud/) is a hosted service that provides a secure and scalable platform for managing your infrastructure as code. You will use it to store your Pulumi backend configurations. -### a. Create a directory +At the prompt, press Enter to log into your Pulumi Cloud account via the browser. Alternatively, you may provide a [Pulumi Cloud access token](https://www.pulumi.com/docs/pulumi-cloud/access-management/access-tokens/). + +```sh +pulumi login +``` + +### c. Create a new program + +A Pulumi program is code written in a [supported programming language](https://github.com/pulumi/pulumi?tab=readme-ov-file#languages) that defines infrastructure resources. -You'll use a new and empty directory for this tutorial. +To create a program, select your language of choice and run the `pulumi` command: + + ```sh -mkdir serverless-cloudflare -cd serverless-cloudflare +pulumi new javascript --name serverless-cloudflare --yes +# wait a few seconds while the project is initialized ``` -### b. Login + -At the prompt, press Enter to log into your Pulumi Cloud account via the browser. Alternatively, you may provide a [Pulumi Cloud access token](https://www.pulumi.com/docs/pulumi-cloud/access-management/access-tokens/). +```sh +pulumi new typescript --name serverless-cloudflare --yes +# wait a few seconds while the project is initialized +``` + + ```sh -pulumi login +pulumi new python --name serverless-cloudflare --yes +# wait a few seconds while the project is initialized ``` -### c. Create a program + -:::note +```sh +pulumi new go --name serverless-cloudflare --yes +# wait a few seconds while the project is initialized +``` -A Pulumi program is code written in a [supported programming language](https://www.pulumi.com/docs/languages-sdks/) that defines infrastructure resources. We'll use TypeScript. + -::: +```sh +pulumi new java --name serverless-cloudflare --yes +# wait a few seconds while the project is initialized +``` -To create a program, run: + ```sh -pulumi new https://github.com/pulumi/tutorials/tree/cloudflare-typescript-hello-world-begin +pulumi new csharp --name serverless-cloudflare --yes +# wait a few seconds while the project is initialized ``` -Complete the prompts with defaults where available; otherwise, provide the requested information. You will need: + -- Your Cloudflare [account ID](/fundamentals/setup/find-account-and-zone-ids/). -- Your Cloudflare [Zone ID](/fundamentals/setup/find-account-and-zone-ids/). -- A registered domain. For instance, `example.com` -- A valid Cloudflare API [token](/fundamentals/api/get-started/create-token/). +```sh +pulumi new yaml --name serverless-cloudflare --yes +``` + + ### d. Create a stack -:::note +A Pulumi [stack](https://www.pulumi.com/docs/concepts/stack/) is an instance of a Pulumi program. Stacks are independently configurable and may represent different environments (development, staging, production) or feature branches. For this tutorial, you'll use the `dev` stack. -A Pulumi stack is an instance of a Pulumi program. Stacks are independently configurable and may represent different environments (development, staging, production) or feature branches. +To instantiate your `dev` stack, run: -::: +```sh +pulumi up --yes +# wait a few seconds for the stack to be instantiated. +``` -To create a stack, run: +You have not defined any resources at this point, so you'll have an empty stack. + +### e. Save your application settings + +In this step, you will store your application settings in a Pulumi [ESC Environment](https://www.pulumi.com/docs/esc/environments/), a YAML file containing configurations and secrets. These can be accessed in several ways, including a Pulumi program. All ESC Environments securely reside in your Pulumi Cloud account and can be fully managed via the Pulumi CLI. For this tutorial, you will store the following values: + +- Your Cloudflare [account ID](/fundamentals/setup/find-account-and-zone-ids/). +- A valid Cloudflare API [token](/fundamentals/api/get-started/create-token/). +- A domain. For instance, `example.com`. ```sh -pulumi up --yes +# Give your new ESC Environment a name +E=hello-world/dev-env + +# Initialize the new ESC Environment +pulumi config env init --env $E --yes ``` -After the above command completes, review the value of `myFirstOutput` for correctness. +```sh output +Creating environment hello-world/dev-env for stack dev... +``` -### e. (Optional) Review the stack +```sh +# Replace abc123 with your Cloudflare account ID +pulumi env set $E --plaintext pulumiConfig.accountId abc123 -From the output above, follow **your** _View in Browser_ link to get familiar with the Pulumi stack. +# Replace API_TOKEN with your Cloudflare API token +pulumi env set $E --secret pulumiConfig.cloudflare:apiToken API_TOKEN -:::note +# Replace example.com with your domain +pulumi env set $E --plaintext pulumiConfig.domain example.com +``` -You have not yet created any Cloudflare resources but have defined a variable, `myFirstOutput`, and the Pulumi stack. +### f. Install the Cloudflare package -::: +You need to install the Cloudflare package for your language of choice in order to define Cloudflare resources in your Pulumi program. + +Install the Cloudflare package by running the following command: + + + +```sh +npm install @pulumi/cloudflare +``` + +```sh output +added 1 package ... +``` + + -Example: +```sh +npm install @pulumi/cloudflare +``` -```bash -View in Browser (Ctrl+O): -https://app.pulumi.com/diana-pulumi-corp/serverless-cloudflare/dev/updates/1 +```sh output +added 1 package ... ``` -![alt_text](~/assets/images/pulumi/hello-world-tutorial/sn3.png "Pulumi Cloud stack") + -## Add a Worker +```sh +echo "pulumi_cloudflare>=5.38,<6.0.0" >> requirements.txt +source venv/bin/activate +pip install -r requirements.txt +``` -You will now add a Cloudflare Worker to the Pulumi stack, `dev`. +```sh output +...Collecting pulumi-cloudflare... +``` -### a. Add Cloudflare Worker to index.ts + -Replace the contents of your `index.ts` file with the following: +```sh +go get github.com/pulumi/pulumi-cloudflare/sdk/v3/go/cloudflare +``` -```typescript -import * as pulumi from "@pulumi/pulumi"; -import * as cloudflare from "@pulumi/cloudflare"; -import * as fs from "fs"; +```sh output +go: downloading github.com/pulumi/pulumi-cloudflare ... +``` -const config = new pulumi.Config(); -const accountId = config.require("accountId"); + -// A Worker script to invoke -export const script = new cloudflare.WorkerScript("hello-world-script", { - accountId: accountId, - name: "hello-world", - // Read the content of the worker from a file - content: fs.readFileSync("./app/worker.ts", "utf8"), -}); +Below are Apache Maven instructions. For other Java project managers such as Gradle, see the official [Maven repository](https://central.sonatype.com/artifact/com.pulumi/cloudflare/overview) + +1. Open your `pom.xml` file. +2. Add the Pulumi Cloudflare dependency inside the `` section. + +```xml + + com.pulumi + cloudflare + 5.38.0 + ``` -### b. Install dependencies +3. Run: ```sh -npm install @pulumi/cloudflare +mvn clean install +``` + +```sh output +...[INFO] BUILD SUCCESS... ``` -### c. Apply the changes + ```sh -pulumi up --yes +dotnet add package Pulumi.Cloudflare +``` + +```sh output +... +info : Adding PackageReference for package 'Pulumi.Cloudflare' into project +... ``` -### d. (Optional) View the Cloudflare Dashboard + + +There are no dependencies to download for YAML. Skip ahead. + + + +## 2. Define Cloudflare resources in code -You can view your Cloudflare resource directly in the Cloudflare Dashboard to validate its existence. +With the Cloudflare package installed, you can now define any [supported Cloudflare resource](https://www.pulumi.com/registry/packages/cloudflare/) in your Pulumi program. Next, define a Worker, a Route, and a DNS Record. -1. Log into the [Cloudflare dashboard](https://dash.cloudflare.com/). -2. Select your account. -3. Go to **Workers & Pages**. -4. Open the "hello-world" application. Example: - ![alt_text](~/assets/images/pulumi/hello-world-tutorial/sn4.png) +### a. Add a Workers script -## Add a Worker route +The [Workers Script resource](https://www.pulumi.com/registry/packages/cloudflare/api-docs/workersscript/) represents a Cloudflare Worker that can be deployed to the Cloudflare network. -You will now add a Worker Route to the Pulumi stack, `dev` so the script can have an endpoint. +Replace the contents of your entrypoint file with the following: -### a. Add Worker Route to index.ts + -Replace the contents of your `index.ts` file with the following: +**Filename: `index.js`** + +```javascript +"use strict"; +const pulumi = require("@pulumi/pulumi"); +const cloudflare = require("@pulumi/cloudflare"); + +const config = new pulumi.Config(); +const accountId = config.require("accountId"); +const domain = config.require("domain"); + +const content = `export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response("Hello World!", options); + }, +};` + +const worker = new cloudflare.WorkersScript("hello-world-worker", { + accountId: accountId, + name: "hello-world-worker", + content: content, + module: true, // ES6 module +}); +``` + + + +**Filename: `index.ts`** ```typescript import * as pulumi from "@pulumi/pulumi"; import * as cloudflare from "@pulumi/cloudflare"; -import * as fs from "fs"; const config = new pulumi.Config(); const accountId = config.require("accountId"); -const zoneId = config.require("zoneId"); const domain = config.require("domain"); -// A Worker script to invoke -export const script = new cloudflare.WorkerScript("hello-world-script", { - accountId: accountId, - name: "hello-world", - // Read the content of the worker from a file - content: fs.readFileSync("./app/worker.ts", "utf8"), +const content = `export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response("Hello World!", options); + }, +};` + +const worker = new cloudflare.WorkersScript("hello-world-worker", { + accountId: accountId, + name: "hello-world-worker", + content: content, + module: true, // ES6 module }); +``` + + + +**Filename: `__main__.py`** + +```python +"""Pulumi program """ +import pulumi +import pulumi_cloudflare as cloudflare + +CONFIG = pulumi.Config() +ACCOUNT_ID = CONFIG.get("accountId") +DOMAIN = CONFIG.require("domain") +CONTENT = """ +export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response("Hello World!", options); + }, +}; +""" + +worker = cloudflare.WorkersScript("hello-world-worker", + account_id=ACCOUNT_ID, + name="hello-world-worker", + content=CONTENT, + module=True # ES6 module +) +``` -// A Worker route to serve requests and the Worker script -export const route = new cloudflare.WorkerRoute("hello-world-route", { - zoneId: zoneId, - pattern: "hello-world." + domain, - scriptName: script.name, + + +**Filename: `main.go`** + +```go +package main + +import ( + "github.com/pulumi/pulumi-cloudflare/sdk/v5/go/cloudflare" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config" +) + +func main() { + pulumi.Run(func(ctx *pulumi.Context) error { + conf := config.New(ctx, "") + accountID := conf.Get("accountId") + domain := conf.Get("domain") + content := ` + export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response("Hello World!", options); + }, + }; + ` + worker, err := cloudflare.NewWorkersScript(ctx, "hello-world-worker", &cloudflare.WorkersScriptArgs{ + AccountId: pulumi.String(accountID), + Name: pulumi.String("hello-world-worker"), + Content: pulumi.String(content), + Module: pulumi.Bool(true), // ES6 module + }) + if err != nil { + return err + } + + return nil + }) +} +``` + + + +**Filename: `src/main/java/myproject/App.java`** + +```java +package myproject; + +import com.pulumi.Pulumi; +import com.pulumi.cloudflare.WorkersScript; +import com.pulumi.cloudflare.WorkersScriptArgs; +import com.pulumi.core.Output; + +public class App { + public static void main(String[] args) { + Pulumi.run(ctx -> { + var content = """ + export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response("Hello World!", options); + }, + }; + """; + + var accountId = ctx.config().require("accountId"); + var domain = ctx.config().require("domain"); + var worker = new WorkersScript("hello-world-worker", WorkersScriptArgs.builder() + .accountId(accountId) + .name("hello-world-worker") + .content(content) + .module(true) + .build()); + + return; + }); + } +} +``` + + + +**Filename: `Program.cs`** + +```csharp +using Pulumi; +using Cloudflare = Pulumi.Cloudflare; + +return await Deployment.RunAsync(() => +{ + var config = new Config(); + var accountId = config.Require("accountId"); + var domain = config.Require("domain"); + var content = @" + export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response(""Hello World!"", options); + }, + }; + "; + + var worker = new Cloudflare.WorkersScript("hello-world-worker", new() + { + AccountId = accountId, + Name = "hello-world-worker", + Content = content, + Module = true + }); + return; }); ``` -### b. Apply changes + + +**Filename: `Pulumi.yaml`** + +```yaml +name: serverless-cloudflare +runtime: yaml +resources: + worker: + type: cloudflare:WorkersScript + properties: + accountId: "${accountId}" + name: "hello-world-worker" + content: | + export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response("Hello World!", options); + }, + }; + module: true +``` -```sh -pulumi up --yes + + +### b. Add a Route + +You will now add a [Workers Route resource](https://www.pulumi.com/registry/packages/cloudflare/api-docs/workersroute/) to your Pulumi program so the Workers script can have an endpoint and be active. To properly configure the Route, you will also look up the zone ID for your domain. + +Add the following code snippet to your entrypoint file **after** the Worker script resource: + + + +**Filename: `index.js`** + +```javascript +const zone = cloudflare.getZone({ + accountId: accountId, + name: domain +}); + +const zoneId = zone.then(z => z.zoneId); + +const route = new cloudflare.WorkersRoute("hello-world-route", { + zoneId: zoneId, + pattern: "hello-world." + domain, + scriptName: worker.name, +}); +``` + + + +**Filename: `index.ts`** + +```typescript +const zone = cloudflare.getZone({ + accountId: accountId, + name: domain +}); + +const zoneId = zone.then(z => z.zoneId); + +const route = new cloudflare.WorkersRoute("hello-world-route", { + zoneId: zoneId, + pattern: "hello-world." + domain, + scriptName: worker.name, +}); +``` + + + +**Filename: `__main__.py`** + +```python +zone = cloudflare.get_zone(account_id=ACCOUNT_ID, name=DOMAIN) +zone_id = zone.zone_id +route = cloudflare.WorkersRoute("hello-world-route", + zone_id=zone_id, + pattern="hello-world." + DOMAIN, + script_name=worker.name +) +``` + + + +**Filename: `main.go`** + +```go +zone, err := cloudflare.LookupZone(ctx, &cloudflare.LookupZoneArgs{ + AccountId: &accountID, + Name: &domain, +}, nil) +if err != nil { + return err +} + +route, err := cloudflare.NewWorkersRoute(ctx, "hello-world-route", &cloudflare.WorkersRouteArgs{ + ZoneId: pulumi.String(zone.Id), + Pattern: pulumi.String("hello-world." + domain), + ScriptName: worker.Name, +}) +if err != nil { + return err +} +``` + + + +**Filename: `src/main/java/myproject/App.java`** + +```java +final var zone = CloudflareFunctions.getZone(GetZoneArgs.builder() + .accountId(accountId) + .name(domain) + .build()); +var route = new WorkersRoute("hello-world-route", WorkersRouteArgs.builder() + .zoneId(zone.applyValue(getZoneResult -> getZoneResult.id())) + .pattern("hello-world." + domain) + .scriptName(worker.name()) + .build()); +``` + + + +**Filename: `Program.cs`** + +```csharp +var zone = Output.Create(Cloudflare.GetZone.InvokeAsync(new() +{ + AccountId = accountId, + Name = domain, +})); +var route = new Cloudflare.WorkersRoute("hello-world-route", new() +{ + ZoneId = zone.Apply(z => z.Id), + Pattern = "hello-world." + domain, + ScriptName = worker.Name, +}); ``` -### c. (Optional) View the Cloudflare Worker route in the dashboard + -In the Cloudflare Dashboard, the Worker application now contains the previously defined Worker Route. +**Filename: `Pulumi.yaml`** -1. Log into the [Cloudflare dashboard](https://dash.cloudflare.com/). -2. Select your account. -3. Go to **Workers & Pages**. -4. Select your application. -5. For **Routes**, select **View** to verify the Worker Route details match your definition. - ![alt_text](~/assets/images/pulumi/hello-world-tutorial/sn5.png "Cloudflare Dashboard - Worker Route") +Below the `runtime` key, add the following code: -## Add a DNS record +```yaml +# new top-level section +variables: + zone: + fn::invoke: + function: cloudflare:getZone + arguments: + accountId: ${accountId} + name: ${domain} +``` + +Below the `worker` resource, add the following code: + +```yaml + route: + type: cloudflare:WorkersRoute + properties: + zoneId: ${zone.id} + pattern: "hello-world.${domain}" + scriptName: ${worker.name} +``` -You will now add a DNS record to your domain so the previously configured route can be accessed via a URL. + -### a. Add DNS Record to index.ts +### c. Add a DNS Record -Replace the contents of your `index.ts` file with the following: +You will now add a DNS [Record resource](https://www.pulumi.com/registry/packages/cloudflare/api-docs/record/) to resolve the previously configured Route. In the next step, you'll also output the Route URL so it can be easily accessed. + +Add the following code snippet to your entrypoint file **after** the Route resource: + + + +**Filename: `index.js`** + +```javascript +const record = new cloudflare.Record("hello-world-record", { + name: route.pattern, + type: "A", + content: "192.0.2.1", + zoneId: zoneId, + proxied: true +}); + +exports.url = pulumi.interpolate`https://${record.hostname}` +``` + + + +**Filename: `index.ts`** ```typescript -import * as pulumi from "@pulumi/pulumi"; -import * as cloudflare from "@pulumi/cloudflare"; -import * as fs from "fs"; +const record = new cloudflare.Record("hello-world-record", { + name: route.pattern, + type: "A", + content: "192.0.2.1", + zoneId: zoneId, + proxied: true, +}); + +export const url = pulumi.interpolate`https://${record.hostname}`; +``` + + + +**Filename: `__main__.py`** + +```python +record = cloudflare.Record("hello-world-record", + name=route.pattern, + type="A", + content="192.0.2.1", + zone_id=zone_id, + proxied=True +) + +url = pulumi.Output.concat("https://", record.hostname) +pulumi.export('url', url) +``` + + + +**Filename: `main.go`** + +```go +record, err := cloudflare.NewRecord(ctx, "hello-world-record", &cloudflare.RecordArgs{ + Name: route.Pattern, + Type: pulumi.String("A"), + Content: pulumi.String("192.0.2.1"), + ZoneId: pulumi.String(zone.Id), + Proxied: pulumi.Bool(true), +}) +if err != nil { + return err +} + +ctx.Export("url", pulumi.Sprintf("https://%s", record.Hostname)) +``` + + + +**Filename: `src/main/java/myproject/App.java`** + +```java +var record = new Record("hello-world-record", RecordArgs.builder() + .name(route.pattern()) + .type("A") + .content("192.0.2.1") + .zoneId(zone.applyValue(getZoneResult -> getZoneResult.id())) + .proxied(true) + .build()); + +ctx.export("url", Output.format("https://%s", record.hostname())); +``` + + + +**Filename: `Program.cs`** + +Notice the updated ' return ' statement because you're now exporting a value. Ensure that you also include `using System.Collections.Generic;` in your imports. + +```csharp +var record = new Cloudflare.Record("hello-world-record", new() +{ + Name = route.Pattern, + Type = "A", + Content = "192.0.2.1", + ZoneId = zone.Apply(z => z.Id), + Proxied = true +}); + +return new Dictionary +{ + ["url"] = Output.Format($"https://{record.Hostname}") +}; +``` + + + +Notice the new top-level `outputs` section. + +```yaml + record: + type: cloudflare:Record + properties: + name: ${route.pattern} + type: A + content: "192.0.2.1" + zoneId: ${zone.id} + proxied: true + +outputs: + url: "https://${record.hostname}" +``` + + + +:::note + +You may need to use `http` instead depending on your domain settings. + +::: + +### d. (Optional) Verify your code + +Confirm all your changes match the full solution below: + + + + +**Filename: `index.js`** + +```javascript +"use strict"; +const pulumi = require("@pulumi/pulumi"); +const cloudflare = require("@pulumi/cloudflare"); const config = new pulumi.Config(); const accountId = config.require("accountId"); -const zoneId = config.require("zoneId"); const domain = config.require("domain"); -// A Worker script to invoke -export const script = new cloudflare.WorkerScript("hello-world-script", { - accountId: accountId, - name: "hello-world", - // Read the content of the worker from a file - content: fs.readFileSync("./app/worker.ts", "utf8"), +const content = `export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response("Hello World!", options); + }, +};` + +const worker = new cloudflare.WorkersScript("hello-world-worker", { + accountId: accountId, + name: "hello-world-worker", + content: content, + module: true, // ES6 module +}); + +const zone = cloudflare.getZone({ + accountId: accountId, + name: domain }); -// A Worker route to serve requests and the Worker script -export const route = new cloudflare.WorkerRoute("hello-world-route", { - zoneId: zoneId, - pattern: "hello-world." + domain, - scriptName: script.name, +const zoneId = zone.then(z => z.zoneId); + +const route = new cloudflare.WorkersRoute("hello-world-route", { + zoneId: zoneId, + pattern: "hello-world." + domain, + scriptName: worker.name, }); -// A DNS record to access the route from the domain -export const record = new cloudflare.Record("hello-world-record", { - zoneId: zoneId, - name: script.name, - value: "192.0.2.1", - type: "A", - proxied: true, +const record = new cloudflare.Record("hello-world-record", { + name: route.pattern, + type: "A", + content: "192.0.2.1", + zoneId: zoneId, + proxied: true }); -export const url = route.pattern; +exports.url = pulumi.interpolate`https://${record.hostname}` + ``` -:::note + + -The last line in the code will create an output with the endpoint for the Hello World app. +**Filename: `index.ts`** -::: +```typescript +import * as pulumi from "@pulumi/pulumi"; +import * as cloudflare from "@pulumi/cloudflare"; -### b. Apply the changes +const config = new pulumi.Config(); +const accountId = config.require("accountId"); +const domain = config.require("domain"); + +const content = `export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response("Hello World!", options); + }, +};` + +const worker = new cloudflare.WorkersScript("hello-world-worker", { + accountId: accountId, + name: "hello-world-worker", + content: content, + module: true, // ES6 module +}); + +const zone = cloudflare.getZone({ + accountId: accountId, + name: domain +}); + +const zoneId = zone.then(z => z.zoneId); + +const route = new cloudflare.WorkersRoute("hello-world-route", { + zoneId: zoneId, + pattern: "hello-world." + domain, + scriptName: worker.name, +}); + +const record = new cloudflare.Record("hello-world-record", { + name: route.pattern, + type: "A", + content: "192.0.2.1", + zoneId: zoneId, + proxied: true, +}); + +export const url = pulumi.interpolate`https://${record.hostname}`; -```sh -pulumi up --yes ``` -### c. (Optional) View all the resources in Pulumi Cloud + + + +**Filename: `__main__.py`** + +```python +"""Pulumi program """ +import pulumi +import pulumi_cloudflare as cloudflare + +CONFIG = pulumi.Config() +ACCOUNT_ID = CONFIG.get("accountId") +DOMAIN = CONFIG.require("domain") +CONTENT = """ +export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response("Hello World!", options); + }, +}; +""" + +worker = cloudflare.WorkersScript("hello-world-worker", + account_id=ACCOUNT_ID, + name="hello-world-worker", + content=CONTENT, + module=True # ES6 module +) + +zone = cloudflare.get_zone(account_id=ACCOUNT_ID, name=DOMAIN) +zone_id = zone.zone_id +route = cloudflare.WorkersRoute("hello-world-route", + zone_id=zone_id, + pattern="hello-world." + DOMAIN, + script_name=worker.name +) + +record = cloudflare.Record("hello-world-record", + name=route.pattern, + type="A", + content="192.0.2.1", + zone_id=zone_id, + proxied=True +) + +url = pulumi.Output.concat("https://", record.hostname) +pulumi.export('url', url) -1. In your browser, open your [Pulumi Cloud](https://app.pulumi.com/) -2. Navigate to your stack, `serverless-cloudflare/dev`. -3. Confirm all the defined resources are created and healthy. Example: +``` -![alt_text](~/assets/images/pulumi/hello-world-tutorial/sn6.png "Pulumi Cloud stack") + + + +**Filename: `main.go`** + +```go +package main + +import ( + "github.com/pulumi/pulumi-cloudflare/sdk/v5/go/cloudflare" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config" +) + +func main() { + pulumi.Run(func(ctx *pulumi.Context) error { + conf := config.New(ctx, "") + accountID := conf.Get("accountId") + domain := conf.Get("domain") + content := ` + export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response("Hello World!", options); + }, + }; + ` + worker, err := cloudflare.NewWorkersScript(ctx, "hello-world-worker", &cloudflare.WorkersScriptArgs{ + AccountId: pulumi.String(accountID), + Name: pulumi.String("hello-world-worker"), + Content: pulumi.String(content), + Module: pulumi.Bool(true), // ES6 module + }) + if err != nil { + return err + } + zone, err := cloudflare.LookupZone(ctx, &cloudflare.LookupZoneArgs{ + AccountId: &accountID, + Name: &domain, + }, nil) + if err != nil { + return err + } + + route, err := cloudflare.NewWorkersRoute(ctx, "hello-world-route", &cloudflare.WorkersRouteArgs{ + ZoneId: pulumi.String(zone.Id), + Pattern: pulumi.String("hello-world." + domain), + ScriptName: worker.Name, + }) + if err != nil { + return err + } + + record, err := cloudflare.NewRecord(ctx, "hello-world-record", &cloudflare.RecordArgs{ + Name: route.Pattern, + Type: pulumi.String("A"), + Content: pulumi.String("192.0.2.1"), + ZoneId: pulumi.String(zone.Id), + Proxied: pulumi.Bool(true), + }) + if err != nil { + return err + } + + ctx.Export("url", pulumi.Sprintf("https://%s", record.Hostname)) + + return nil + }) +} -## Test the app +``` -You have incrementally added all the Cloudflare resources needed to run and access your Hello World application. This was done by defining the resources in TypeScript and letting Pulumi handle the rest. + + + +**Filename: `src/main/java/myproject/App.java`** + +```java +package myproject; + +import com.pulumi.Pulumi; +import com.pulumi.core.Output; +import com.pulumi.cloudflare.WorkersScript; +import com.pulumi.cloudflare.WorkersScriptArgs; +import com.pulumi.cloudflare.CloudflareFunctions; +import com.pulumi.cloudflare.inputs.GetZoneArgs; +import com.pulumi.cloudflare.WorkersRoute; +import com.pulumi.cloudflare.WorkersRouteArgs; +import com.pulumi.cloudflare.Record; +import com.pulumi.cloudflare.RecordArgs; + +public class App { + public static void main(String[] args) { + Pulumi.run(ctx -> { + var content = """ + export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response("Hello World!", options); + }, + }; + """; + + var accountId = ctx.config().require("accountId"); + var domain = ctx.config().require("domain"); + + var worker = new WorkersScript("hello-world-worker", WorkersScriptArgs.builder() + .accountId(accountId) + .name("hello-world-worker") + .content(content) + .module(true) + .build()); + final var zone = CloudflareFunctions.getZone(GetZoneArgs.builder() + .accountId(accountId) + .name(domain) + .build()); + var route = new WorkersRoute("hello-world-route", WorkersRouteArgs.builder() + .zoneId(zone.applyValue(getZoneResult -> getZoneResult.id())) + .pattern("hello-world." + domain) + .scriptName(worker.name()) + .build()); + var record = new Record("hello-world-record", RecordArgs.builder() + .name(route.pattern()) + .type("A") + .content("192.0.2.1") + .zoneId(zone.applyValue(getZoneResult -> getZoneResult.id())) + .proxied(true) + .build()); + + ctx.export("url", Output.format("https://%s", record.hostname())); + return; + }); + } +} -You can test your application via the terminal or browser. +``` -- In the terminal + + + +**Filename: `Program.cs`** + +```csharp +using System.Collections.Generic; +using Pulumi; +using Cloudflare = Pulumi.Cloudflare; + +return await Deployment.RunAsync(() => +{ + var config = new Config(); + var accountId = config.Require("accountId"); + var domain = config.Require("domain"); + var content = @" + export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response(""Hello World!"", options); + }, + }; + "; + + var worker = new Cloudflare.WorkersScript("hello-world-worker", new() + { + AccountId = accountId, + Name = "hello-world-worker", + Content = content, + Module = true + }); + var zone = Output.Create(Cloudflare.GetZone.InvokeAsync(new() + { + AccountId = accountId, + Name = domain, + })); + var route = new Cloudflare.WorkersRoute("hello-world-route", new() + { + ZoneId = zone.Apply(z => z.Id), + Pattern = "hello-world." + domain, + ScriptName = worker.Name, + }); + + var record = new Cloudflare.Record("hello-world-record", new() + { + Name = route.Pattern, + Type = "A", + Content = "192.0.2.1", + ZoneId = zone.Apply(z => z.Id), + Proxied = true + }); + + return new Dictionary + { + ["url"] = Output.Format($"https://{record.Hostname}") + }; +}); -```sh -pulumi stack output url ``` -```sh output -hello-world.atxyall.com + + + +**Filename: `Pulumi.yaml`** + +```yaml +name: serverless-cloudflare +runtime: yaml +variables: + zone: + fn::invoke: + function: cloudflare:getZone + arguments: + accountId: ${accountId} + name: ${domain} + +resources: + worker: + type: cloudflare:WorkersScript + properties: + accountId: "${accountId}" + name: "hello-world-worker" + content: | + export default { + async fetch(request) { + const options = { headers: { 'content-type': 'text/plain' } }; + return new Response("Hello World!", options); + }, + }; + module: true + route: + type: cloudflare:WorkersRoute + properties: + zoneId: ${zone.id} + pattern: "hello-world.${domain}" + scriptName: ${worker.name} + record: + type: cloudflare:Record + properties: + name: ${route.pattern} + type: A + content: "192.0.2.1" + zoneId: ${zone.id} + proxied: true + +outputs: + url: "https://${record.hostname}" ``` + + + +## 3. Deploy your application + +Now that you have defined all the Cloudflare resources, you can deploy the Hello World application to your Cloudflare account using the Pulumi CLI. + +To deploy the changes, run: + ```sh -curl "https://$(pulumi stack output url)" +pulumi up --yes ``` ```sh output - - - - Hello World - - -

Serverless with Pulumi

-

The current time is: Thu Oct 05 2023 22:02:17 GMT+0000 (Coordinated Universal Time).

- - - +wait for the dev stack to become ready ``` -:::note - -Depending on your domain settings, you may need to use "http" instead. +## 4. Test the Worker -::: +You incrementally added Cloudflare resources to run and access your Hello World application. You can test your application by curling the `url` output from the Pulumi stack. -- In your browser, open `hello-world.YOUR_DOMAIN.com` - -Example: +```sh +curl $(pulumi stack output url) +``` -![alt_text](~/assets/images/pulumi/hello-world-tutorial/sn2.png "Hello World app browser screenshot") +```sh output +Hello, World! +``` -## Clean up +## 5. Clean up -In this last step, you will run a couple of commands to clean up the resources and stack you used throughout the tutorial. +In this last step, you will clean up the resources and stack used throughout the tutorial. ### a. Delete the Cloudflare resources @@ -346,3 +1219,7 @@ pulumi destroy ```sh pulumi stack rm dev ``` + +## Next steps + +Visit the [Cloudflare package documentation](https://www.pulumi.com/docs/reference/pkg/cloudflare/) to explore other resources you can define with Pulumi and Cloudflare. diff --git a/src/content/docs/pulumi/tutorial/manage-secrets.mdx b/src/content/docs/pulumi/tutorial/manage-secrets.mdx index 13fcb676fbe8ba..b0f4758d35b276 100644 --- a/src/content/docs/pulumi/tutorial/manage-secrets.mdx +++ b/src/content/docs/pulumi/tutorial/manage-secrets.mdx @@ -22,12 +22,12 @@ You will provision resources that qualify under free tier offerings for both Pul Ensure you have: -- A Cloudflare account. [Sign up for a Cloudflare account](https://dash.cloudflare.com/sign-up/workers-and-pages). +- A Cloudflare account. [Sign up for a Cloudflare account](https://www.cloudflare.com/sign-up). - A Pulumi Cloud account. [Sign up for a Pulumi Cloud](https://app.pulumi.com/signup). - The [Pulumi ESC CLI](https://www.pulumi.com/docs/install/esc/) installed. - A Wrangler project. To create one, follow the [Create a New Worker project step](/workers/get-started/guide/#1-create-a-new-worker-project). -## Set up a new Environment +## 1. Set up a new Environment A [Pulumi ESC Environment](https://www.pulumi.com/docs/esc/environments/), or Environment, is a YAML file containing configurations and secrets for your application and infrastructure. These can be accessed in several ways, including shell commands. All ESC Environments reside in your Pulumi Cloud account. @@ -50,7 +50,7 @@ Environment names must be unique within a Pulumi organization and may only conta ::: ```sh -ESC_ENV=my-dev-environment +ESC_ENV=wrangler/my-dev-environment esc env init $ESC_ENV ``` @@ -58,7 +58,7 @@ esc env init $ESC_ENV Environment created. ``` -## Log into Cloudflare +## 2. Log into Cloudflare Now that the Pulumi ESC Environment has been created, it can be consumed in various ways. For instance, to log into your Cloudflare account without needing to predefine credentials in your shell. @@ -80,7 +80,7 @@ esc env set $ESC_ENV environmentVariables.CLOUDFLARE_API_TOKEN 123abc --secret The API token is declared as a `secret`. Once the Environment is saved, Pulumi will encrypt its value and replace it with ciphertext. ::: -### a. Log out +### b. Log out Ensure you're not currently logged in to your Cloudflare account. @@ -92,7 +92,7 @@ npx wrangler logout Not logged in, exiting... ``` -### a. Log in +### c. Log in Pass ESC-stored Cloudflare credentials to Wrangler. @@ -107,7 +107,7 @@ Getting User settings... When you use the `esc run` command, it opens the Environment and sets the specified Environment variables into a temporary environment. After that, it uses those variables in the context of the `wrangler` command. This is especially helpful when running `wrangler` commands in a CI/CD environment but wanting to avoid storing credentials directly in your pipeline. -## Add Worker secrets +## 3. Add Worker secrets Pulumi ESC centralizes secrets, and Wrangler can be used to pass them on to Workers and other Cloudflare resources. You will use the `wrangler secret put` command for this purpose. @@ -125,7 +125,7 @@ esc run -i ${ESC_ENV} -- sh -c 'echo "$TOP_SECRET" | npx wrangler secret put TOP By using an external secrets management solution, commonly used Worker secrets can be stored in a single shared Environment that is accessed by the relevant Workers. You can use shell commands with `esc` to incorporate scripting and integrate them into deployment pipelines or `make` commands. Use `esc [command] --help` for more information about the various commands available in the CLI. -## Load `.dev.vars` +## 4. Load `.dev.vars` In this step, you will configure an Environment to load your `.dev.vars` file programmatically. @@ -138,7 +138,7 @@ With a dedicated ESC Environment to store all the `.dev.vars` secrets, you can u ### a. Create an Environment ```sh -E=my-devvars +E=wrangler/my-devvars esc env init $E ``` From 6966349e2bb257a6f152ecc0dcf76d7f3b34e7da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denise=20Pe=C3=B1a?= <75506267+dcpena@users.noreply.github.com> Date: Tue, 24 Sep 2024 17:36:02 -0500 Subject: [PATCH 002/110] Added in Type & MetaInfo highlighting (#17073) * Added in Type & MetaInfo highlighting * Adding missing components from Import section * Removed unnecessary slash --- .../docs/kv/concepts/kv-namespaces.mdx | 7 +- .../functions/wrangler-configuration.mdx | 32 +- src/content/docs/pages/get-started/c3.mdx | 20 +- .../workers-ai/configuration/bindings.mdx | 6 +- .../multipart-upload-metadata.mdx | 14 +- .../docs/workers/runtime-apis/request.mdx | 24 +- src/content/docs/workers/wrangler/api.mdx | 6 +- .../docs/workers/wrangler/commands.mdx | 616 +++++++++--------- .../docs/workers/wrangler/configuration.mdx | 206 +++--- .../docs/workers/wrangler/custom-builds.mdx | 8 +- .../v1-to-v2/wrangler-legacy/commands.mdx | 10 +- .../wrangler/system-environment-variables.mdx | 20 +- .../workers/wrangler-commands/hyperdrive.mdx | 56 +- .../partials/workers/wrangler-commands/kv.mdx | 122 ++-- 14 files changed, 579 insertions(+), 568 deletions(-) diff --git a/src/content/docs/kv/concepts/kv-namespaces.mdx b/src/content/docs/kv/concepts/kv-namespaces.mdx index 43a2cb3f1708dd..7fae7c5a2848b4 100644 --- a/src/content/docs/kv/concepts/kv-namespaces.mdx +++ b/src/content/docs/kv/concepts/kv-namespaces.mdx @@ -5,6 +5,7 @@ sidebar: order: 7 --- +import { Type, MetaInfo } from "~/components"; A KV namespace is a key-value database replicated to Cloudflare’s global network. @@ -20,15 +21,15 @@ KV namespace IDs are public and bound to your account. To bind KV namespaces to your Worker, assign an array of the below object to the `kv_namespaces` key. -* `binding` string required +* `binding` * The binding name used to refer to the KV namespace. -* `id` string required +* `id` * The ID of the KV namespace. -* `preview_id` string optional +* `preview_id` * The ID of the KV namespace used during `wrangler dev`. diff --git a/src/content/docs/pages/functions/wrangler-configuration.mdx b/src/content/docs/pages/functions/wrangler-configuration.mdx index 85d1ab723c66e3..d64cb386b46a68 100644 --- a/src/content/docs/pages/functions/wrangler-configuration.mdx +++ b/src/content/docs/pages/functions/wrangler-configuration.mdx @@ -5,7 +5,7 @@ sidebar: order: 6 --- -import { Render, TabItem, Tabs } from "~/components"; +import { Render, TabItem, Tabs, Type, MetaInfo } from "~/components"; :::caution @@ -278,15 +278,15 @@ API_KEY = "6567875fvgt" Inheritable keys are configurable at the top-level, and can be inherited (or overridden) by environment-specific configuration. -- `name` string required +- `name` - The name of your Pages project. Alphanumeric and dashes only. -- `pages_build_output_dir` string required +- `pages_build_output_dir` - The path to your project's build output folder. For example: `./dist`. -- `compatibility_date` string required +- `compatibility_date` - A date in the form `yyyy-mm-dd`, which will be used to determine which version of the Workers runtime is used. Refer to [Compatibility dates](/workers/configuration/compatibility-dates/). @@ -294,7 +294,7 @@ Inheritable keys are configurable at the top-level, and can be inherited (or ove - A list of flags that enable features from upcoming features of the Workers runtime, usually used together with `compatibility_date`. Refer to [compatibility dates](/workers/configuration/compatibility-dates/). -- `send_metrics` boolean optional +- `send_metrics` - Whether Wrangler should send usage metrics to Cloudflare for this project. @@ -335,47 +335,47 @@ API_KEY = "8901234bfgd" This will work for local development, but will fail to validate when you try to deploy. -- `vars` object optional +- `vars` - A map of environment variables to set when deploying your Function. Refer to [Environment variables](/pages/functions/bindings/#environment-variables). -- `d1_databases` object optional +- `d1_databases` - A list of D1 databases that your Function should be bound to. Refer to [D1 databases](/pages/functions/bindings/#d1-databases). -- `durable_objects` object optional +- `durable_objects` - A list of Durable Objects that your Function should be bound to. Refer to [Durable Objects](/pages/functions/bindings/#durable-objects). -- `hyperdrive` object optional +- `hyperdrive` - Specifies Hyperdrive configs that your Function should be bound to. Refer to [Hyperdrive](/pages/functions/bindings/#r2-buckets). -- `kv_namespaces` object optional +- `kv_namespaces` - A list of KV namespaces that your Function should be bound to. Refer to [KV namespaces](/pages/functions/bindings/#kv-namespaces). -- `queues.producers` object optional +- `queues.producers` - Specifies Queues Producers that are bound to this Function. Refer to [Queues Producers](/queues/get-started/#4-set-up-your-producer-worker). -- `r2_buckets` object optional +- `r2_buckets` - A list of R2 buckets that your Function should be bound to. Refer to [R2 buckets](/pages/functions/bindings/#r2-buckets). -- `vectorize` object optional +- `vectorize` - A list of Vectorize indexes that your Function should be bound to. Refer to [Vectorize indexes](/vectorize/get-started/intro/#3-bind-your-worker-to-your-index). -- `services` object optional +- `services` - A list of service bindings that your Function should be bound to. Refer to [service bindings](/pages/functions/bindings/#service-bindings). -- `analytics_engine_datasets` object optional +- `analytics_engine_datasets` - Specifies analytics engine datasets that are bound to this Function. Refer to [Workers Analytics Engine](/analytics/analytics-engine/get-started/). -- `ai` object optional +- `ai` - Specifies an AI binding to this Function. Refer to [Workers AI](/pages/functions/bindings/#workers-ai). diff --git a/src/content/docs/pages/get-started/c3.mdx b/src/content/docs/pages/get-started/c3.mdx index b3716abe541449..65085069600c82 100644 --- a/src/content/docs/pages/get-started/c3.mdx +++ b/src/content/docs/pages/get-started/c3.mdx @@ -10,7 +10,7 @@ description: Use C3 (`create-cloudflare` CLI) to set up and deploy new deployment. --- -import { Render, TabItem, Tabs } from "~/components"; +import { Render, TabItem, Tabs, Type, MetaInfo } from "~/components"; Cloudflare provides a CLI command for creating new Workers and Pages projects — `npm create cloudflare`, powered by the [`create-cloudflare` package](https://www.npmjs.com/package/create-cloudflare). @@ -90,7 +90,7 @@ bun create cloudflare@latest [--] [] [OPTIONS] [-- ] -- `DIRECTORY` string optional +- `DIRECTORY` - The directory where the application should be created. The name of the application is taken from the directory name. @@ -98,7 +98,7 @@ bun create cloudflare@latest [--] [] [OPTIONS] [-- ] - CLI arguments to pass to eventual third party CLIs C3 might invoke (in the case of full-stack applications). -- `--category` string optional +- `--category` - The kind of templates that should be created. @@ -109,7 +109,7 @@ bun create cloudflare@latest [--] [] [OPTIONS] [-- ] - `demo`: Application Starter - `remote-template`: Template from a GitHub repo -- `--type` string optional +- `--type` - The type of application that should be created. @@ -123,7 +123,7 @@ bun create cloudflare@latest [--] [] [OPTIONS] [-- ] - `openapi`: A Worker implementing an OpenAPI REST endpoint. - `pre-existing`: Fetch a Worker initialized from the Cloudflare dashboard. -- `--framework` string optional +- `--framework` - The type of framework to use to create a web application (when using this option, `--type` is ignored). @@ -143,7 +143,7 @@ bun create cloudflare@latest [--] [] [OPTIONS] [-- ] - `svelte` - `vue` -- `--template` string optional +- `--template` - Create a new project via an external template hosted in a git repository @@ -194,13 +194,13 @@ bun create cloudflare@latest [--] [] [OPTIONS] [-- ] - Open with your browser the deployed application (this option is ignored if the application is not deployed). -- `--existing-script` string optional +- `--existing-script` - The name of an existing Cloudflare Workers script to clone locally. When using this option, `--type` is coerced to `pre-existing`. - When `--existing-script` is specified, `deploy` will be ignored. -- `-y`, `--accept-defaults` boolean optional +- `-y`, `--accept-defaults` - Use all the default C3 options each can also be overridden by specifying it. @@ -208,11 +208,11 @@ bun create cloudflare@latest [--] [] [OPTIONS] [-- ] - Automatically uses the latest version of C3. -- `-v`, `--version` boolean optional +- `-v`, `--version` - Show version number. -- `-h`, `--help` boolean optional +- `-h`, `--help` - Show a help message. diff --git a/src/content/docs/workers-ai/configuration/bindings.mdx b/src/content/docs/workers-ai/configuration/bindings.mdx index 4024b7115ae7f6..6fe530a57adc75 100644 --- a/src/content/docs/workers-ai/configuration/bindings.mdx +++ b/src/content/docs/workers-ai/configuration/bindings.mdx @@ -6,6 +6,8 @@ sidebar: --- +import { Type, MetaInfo } from "~/components"; + ## Workers [Workers](/workers/) provides a serverless execution environment that allows you to create new applications or augment existing ones. @@ -41,13 +43,13 @@ const answer = await env.AI.run('@cf/meta/llama-3.1-8b-instruct', { -* `model` string required +* `model` * The model to run. **Supported options** - * `stream` boolean optional + * `stream` * Returns a stream of results as they are available. diff --git a/src/content/docs/workers/configuration/multipart-upload-metadata.mdx b/src/content/docs/workers/configuration/multipart-upload-metadata.mdx index 205042e9afb825..374269d940a3eb 100644 --- a/src/content/docs/workers/configuration/multipart-upload-metadata.mdx +++ b/src/content/docs/workers/configuration/multipart-upload-metadata.mdx @@ -4,6 +4,8 @@ title: Multipart upload metadata --- +import { Type, MetaInfo } from "~/components"; + If you're using the [Workers Script Upload API](/api/operations/worker-script-upload-worker-module) or [Version Upload API](/api/operations/worker-versions-upload-version) directly, `multipart/form-data` uploads require you to specify a `metadata` part. This metadata defines the Worker's configuration in JSON format, analogue to the [wrangler.toml file](/workers/wrangler/configuration/). ## Sample `metadata` @@ -33,7 +35,7 @@ At a minimum, the `main_module` key is required to upload a Worker. -* `main_module` string required +* `main_module` * The part name that contains the module entry point of the Worker that will be executed. For example, `main.js`. @@ -41,12 +43,12 @@ At a minimum, the `main_module` key is required to upload a Worker. * [Bindings](#bindings) to expose in the Worker. -* `placement` object optional +* `placement` * [Smart placement](/workers/configuration/smart-placement/) object for the Worker. * `mode` field only supports `smart` for automatic placement. -* `compatibility_date` string optional +* `compatibility_date` * [Compatibility Date](/workers/configuration/compatibility-dates/#setting-compatibility-date) indicating targeted support in the Workers runtime. Backwards incompatible fixes to the runtime following this date will not affect this Worker. Highly recommended to set a `compatibility_date`, otherwise if on upload via the API, it defaults to the oldest compatibility date before any flags took effect (2021-11-02). @@ -54,7 +56,7 @@ At a minimum, the `main_module` key is required to upload a Worker. * [Compatibility Flags](/workers/configuration/compatibility-dates/#setting-compatibility-flags) that enable or disable certain features in the Workers runtime. Used to enable upcoming features or opt in or out of specific changes not included in a `compatibility_date`. -* `usage_model` string optional +* `usage_model` * Usage model to apply to invocations, only allowed value is `standard`. @@ -75,7 +77,7 @@ These attributes are **not available** for version uploads. * [Durable Objects migrations](/durable-objects/reference/durable-objects-migrations/) to apply. -* `logpush` boolean optional +* `logpush` * Whether [Logpush](/cloudflare-for-platforms/cloudflare-for-saas/hostname-analytics/#logpush) is turned on for the Worker. @@ -100,7 +102,7 @@ These attributes are **not available** for immediately deployed uploads. -* `annotations` object optional +* `annotations` * Annotations object specific to the Worker version. * `workers/message` specifies a custom message for the version. diff --git a/src/content/docs/workers/runtime-apis/request.mdx b/src/content/docs/workers/runtime-apis/request.mdx index 6b2769aece9951..fdd95875cf0a49 100644 --- a/src/content/docs/workers/runtime-apis/request.mdx +++ b/src/content/docs/workers/runtime-apis/request.mdx @@ -6,6 +6,8 @@ description: Interface that represents an HTTP request. --- +import { Type, MetaInfo } from "~/components"; + The [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request) interface represents an HTTP request and is part of the [Fetch API](/workers/runtime-apis/fetch/). ## Background @@ -66,7 +68,7 @@ An object containing properties that you want to apply to the request. * Cloudflare-specific properties that can be set on the `Request` that control how Cloudflare’s global network handles the request. -* `method` string optional +* `method` * The HTTP request method. The default is `GET`. In Workers, all [HTTP request methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) are supported, except for [`CONNECT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT). @@ -79,7 +81,7 @@ An object containing properties that you want to apply to the request. * The request body, if any. * Note that a request using the GET or HEAD method cannot have a body. -* `redirect` string optional +* `redirect` * The redirect mode to use: `follow`, `error`, or `manual`. The default for a new `Request` object is `follow`. Note, however, that the incoming `Request` property of a `FetchEvent` will have redirect mode `manual`. @@ -98,16 +100,16 @@ Invalid or incorrectly-named keys in the `cf` object will be silently ignored. C -* `apps` boolean optional +* `apps` * Whether [Cloudflare Apps](https://www.cloudflare.com/apps/) should be enabled for this request. Defaults to `true`. -* `cacheEverything` boolean optional +* `cacheEverything` * Treats all content as static and caches all [file types](/cache/concepts/default-cache-behavior#default-cached-file-extensions) beyond the Cloudflare default cached content. Respects cache headers from the origin web server. This is equivalent to setting the Page Rule [**Cache Level** (to **Cache Everything**)](/rules/page-rules/reference/settings/). Defaults to `false`. This option applies to `GET` and `HEAD` request methods only. -* `cacheKey` string optional +* `cacheKey` * A request’s cache key is what determines if two requests are the same for caching purposes. If a request has the same cache key as some previous request, then Cloudflare can serve the same cached response for both. @@ -115,7 +117,7 @@ Invalid or incorrectly-named keys in the `cf` object will be silently ignored. C * This option appends additional [**Cache-Tag**](/cache/how-to/purge-cache/purge-by-tags/) headers to the response from the origin server. This allows for purges of cached content based on tags provided by the Worker, without modifications to the origin server. This is performed using the [**Purge by Tag**](/cache/how-to/purge-cache/purge-by-tags/#purge-using-cache-tags) feature, which is currently only available to Enterprise zones. If this option is used in a non-Enterprise zone, the additional headers will not be appended. -* `cacheTtl` number optional +* `cacheTtl` * This option forces Cloudflare to cache the response for this request, regardless of what headers are seen on the response. This is equivalent to setting two Page Rules: [**Edge Cache TTL**](/cache/how-to/edge-browser-cache-ttl/) and [**Cache Level** (to **Cache Everything**)](/rules/page-rules/reference/settings/). The value must be zero or a positive number. A value of `0` indicates that the cache asset expires immediately. This option applies to `GET` and `HEAD` request methods only. @@ -127,23 +129,23 @@ Invalid or incorrectly-named keys in the `cf` object will be silently ignored. C * Enables [Image Resizing](/images/transform-images/) for this request. The possible values are described in [Transform images via Workers](/images/transform-images/transform-via-workers/) documentation. -* `mirage` boolean optional +* `mirage` * Whether [Mirage](https://www.cloudflare.com/website-optimization/mirage/) should be enabled for this request, if otherwise configured for this zone. Defaults to `true`. -* `polish` string optional +* `polish` * Sets [Polish](https://blog.cloudflare.com/introducing-polish-automatic-image-optimizati/) mode. The possible values are `lossy`, `lossless` or `off`. -* `resolveOverride` string optional +* `resolveOverride` * Directs the request to an alternate origin server by overriding the DNS lookup. The value of `resolveOverride` specifies an alternate hostname which will be used when determining the origin IP address, instead of using the hostname specified in the URL. The `Host` header of the request will still match what is in the URL. Thus, `resolveOverride` allows a request to be sent to a different server than the URL / `Host` header specifies. However, `resolveOverride` will only take effect if both the URL host and the host specified by `resolveOverride` are within your zone. If either specifies a host from a different zone / domain, then the option will be ignored for security reasons. If you need to direct a request to a host outside your zone (while keeping the `Host` header pointing within your zone), first create a CNAME record within your zone pointing to the outside host, and then set `resolveOverride` to point at the CNAME record. Note that, for security reasons, it is not possible to set the `Host` header to specify a host outside of your zone unless the request is actually being sent to that host. -* `scrapeShield` boolean optional +* `scrapeShield` * Whether [ScrapeShield](https://blog.cloudflare.com/introducing-scrapeshield-discover-defend-dete/) should be enabled for this request, if otherwise configured for this zone. Defaults to `true`. -* `webp` boolean optional +* `webp` * Enables or disables [WebP](https://blog.cloudflare.com/a-very-webp-new-year-from-cloudflare/) image format in [Polish](/images/polish/). diff --git a/src/content/docs/workers/wrangler/api.mdx b/src/content/docs/workers/wrangler/api.mdx index a0bd2126fa2717..52ecf4e9c63bf9 100644 --- a/src/content/docs/workers/wrangler/api.mdx +++ b/src/content/docs/workers/wrangler/api.mdx @@ -9,7 +9,7 @@ description: A set of programmatic APIs that can be integrated with local --- -import { Render, TabItem, Tabs } from "~/components" +import { Render, TabItem, Tabs, Type, MetaInfo } from "~/components" Wrangler offers APIs to programmatically interact with your Cloudflare Workers. @@ -48,7 +48,7 @@ const worker = await unstable_dev(script, options) * A string containing a path to your Worker script, relative to your Worker project's root directory. -* `options` object optional +* `options` * Optional options object containing `wrangler dev` configuration settings. * Include an `experimental` object inside `options` to access experimental features such as `disableExperimentalWarning`. @@ -257,7 +257,7 @@ const platform = await getPlatformProxy(options); -* `options` object optional +* `options` * Optional options object containing preferences for the bindings: diff --git a/src/content/docs/workers/wrangler/commands.mdx b/src/content/docs/workers/wrangler/commands.mdx index d1e65300ade0e0..a64c1adb150719 100644 --- a/src/content/docs/workers/wrangler/commands.mdx +++ b/src/content/docs/workers/wrangler/commands.mdx @@ -7,7 +7,7 @@ head: description: Create, develop, and deploy your Cloudflare Workers with Wrangler commands. --- -import { TabItem, Tabs, Render } from "~/components"; +import { TabItem, Tabs, Render, Type, MetaInfo } from "~/components"; Wrangler offers a number of commands to manage your Cloudflare Workers. @@ -45,13 +45,13 @@ Wrangler offers a number of commands to manage your Cloudflare Workers. The following global flags work on every command, with some exceptions for `pages` commands. -- `--help` boolean +- `--help` - Show help. -- `--version` boolean +- `--version` - Show version number. -- `--config` string (not supported by Pages) +- `--config` (not supported by Pages) - Path to `.toml` configuration file. -- `--experimental-json-config` boolean (not supported by Pages) +- `--experimental-json-config` (not supported by Pages) - ⚠️ This is an experimental command. Read configuration from a `wrangler.json` file, instead of `wrangler.toml`. `wrangler.json` is a [JSONC](https://code.visualstudio.com/docs/languages/json#_json-with-comments) file. ::: @@ -133,7 +133,7 @@ Open the Cloudflare developer documentation in your default browser. wrangler docs [] ``` -- `COMMAND` string optional +- `COMMAND` - The Wrangler command you want to learn more about. This opens your default browser to the section of the documentation that describes the command. ## `init` @@ -144,11 +144,11 @@ Create a new project via the [create-cloudflare-cli (C3) tool](/workers/get-star wrangler init [] [OPTIONS] ``` -- `NAME` string optional (default: name of working directory) +- `NAME` - The name of the Workers project. This is both the directory name and `name` property in the generated `wrangler.toml` [configuration](/workers/wrangler/configuration/) file. -- `--yes` boolean optional +- `--yes` - Answer yes to any prompts for new projects. -- `--from-dash` string optional +- `--from-dash` - Fetch a Worker initialized from the dashboard. This is done by passing the flag and the Worker name. `wrangler init --from-dash `. - The `--from-dash` command will not automatically sync changes made to the dashboard after the command is used. Therefore, it is recommended that you continue using the CLI. @@ -168,9 +168,9 @@ Create a new project using an existing [Workers template](https://github.com/clo wrangler generate [] [TEMPLATE] ``` -- `NAME` string optional (default: name of working directory) +- `NAME` - The name of the Workers project. This is both the directory name and `name` property in the generated `wrangler.toml` [configuration](/workers/wrangler/configuration/) file. -- `TEMPLATE` string optional +- `TEMPLATE` - The URL of a GitHub template, with a default [worker-template](https://github.com/cloudflare/worker-template). Browse a list of available templates on the [cloudflare/workers-sdk](https://github.com/cloudflare/workers-sdk/tree/main/templates#usage) repository. --- @@ -192,9 +192,9 @@ Creates a new D1 database, and provides the binding and UUID that you will put i wrangler d1 create [OPTIONS] ``` -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the new D1 database. -- `--location` string optional +- `--location` - Provide an optional [location hint](/d1/configuration/data-location/) for your database leader. - Available options include `weur` (Western Europe), `eeur` (Eastern Europe), `apac` (Asia Pacific), `oc` (Oceania), `wnam` (Western North America), and `enam` (Eastern North America). @@ -206,9 +206,9 @@ Get information about a D1 database, including the current database size and sta wrangler d1 info [OPTIONS] ``` -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the D1 database to get information about. -- `--json` boolean optional +- `--json` - Return output as JSON rather than a table. ### `list` @@ -219,7 +219,7 @@ List all D1 databases in your account. wrangler d1 list [OPTIONS] ``` -- `--json` boolean optional +- `--json` - Return output as JSON rather than a table. ### `delete` @@ -230,9 +230,9 @@ Delete a D1 database. wrangler d1 delete [OPTIONS] ``` -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the D1 database to delete. -- `-y, --skip-confirmation` boolean optional +- `-y, --skip-confirmation` - Skip deletion confirmation prompt. ### `execute` @@ -249,25 +249,25 @@ You must provide either `--command` or `--file` for this command to run successf ::: -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the D1 database to execute a query on. -- `--command` string optional +- `--command` - The SQL query you wish to execute. -- `--file` string optional +- `--file` - Path to the SQL file you wish to execute. -- `-y, --yes` boolean optional +- `-y, --yes` - Answer `yes` to any prompts. -- `--local` boolean(default: true) optional +- `--local` - Execute commands/files against a local database for use with [wrangler dev](#dev). -- `--remote` boolean (default: false) optional +- `--remote` - Execute commands/files against a remote D1 database for use with [wrangler dev --remote](#dev). -- `--persist-to` string optional +- `--persist-to` - Specify directory to use for local persistence (for use in combination with `--local`). -- `--json` boolean optional +- `--json` - Return output as JSON rather than a table. -- `--preview` boolean optional +- `--preview` - Execute commands/files against a preview D1 database (as defined by `preview_database_id` in [Wrangler.toml](/workers/wrangler/configuration/#d1-databases)). -- `--batch-size` number optional +- `--batch-size` - Number of queries to send in a single batch. ### `export` @@ -285,17 +285,17 @@ Export a D1 database or table's schema and/or content to a `.sql` file. wrangler d1 export [OPTIONS] ``` -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the D1 database to export. -- `--remote` boolean (default: false) optional +- `--remote` - Execute commands/files against a remote D1 database for use with [wrangler dev --remote](#dev). -- `--output` string optional +- `--output` - Path to the SQL file for your export. -- `--table` string optional +- `--table` - The name of the table within a D1 database to export. -- `--no-data` boolean (default: false) optional +- `--no-data` - Controls whether export SQL file contains database data. Note that `--no-data=true` is not recommended due to a known wrangler limitation that intreprets the value as false. -- `--no-schema` boolean (default: false) optional +- `--no-schema` - Controls whether export SQL file contains database schema. Note that `--no-schema=true` is not recommended due to a known wrangler limitation that intreprets the value as false. ### `time-travel restore` @@ -306,13 +306,13 @@ Restore a database to a specific point-in-time using [Time Travel](/d1/reference wrangler d1 time-travel restore [OPTIONS] ``` -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the D1 database to execute a query on. -- `--bookmark` string optional +- `--bookmark` - A D1 bookmark representing the state of a database at a specific point in time. -- `--timestamp` string optional +- `--timestamp` - A UNIX timestamp or JavaScript date-time `string` within the last 30 days. -- `--json` boolean optional +- `--json` - Return output as JSON rather than a table. ### `time-travel info` @@ -323,11 +323,11 @@ Inspect the current state of a database for a specific point-in-time using [Time wrangler d1 time-travel info [OPTIONS] ``` -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the D1 database to execute a query on. -- `--timestamp` string optional +- `--timestamp` - A UNIX timestamp or JavaScript date-time `string` within the last 30 days. -- `--json` boolean optional +- `--json` b - Return output as JSON rather than a table. ### `backup create` @@ -345,7 +345,7 @@ Initiate a D1 backup. wrangler d1 backup create ``` -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the D1 database to backup. ### `backup list` @@ -363,7 +363,7 @@ List all available backups. wrangler d1 backup list ``` -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the D1 database to list the backups of. ### `backup restore` @@ -381,9 +381,9 @@ Restore a backup into a D1 database. wrangler d1 backup restore ``` -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the D1 database to restore the backup into. -- `BACKUP_ID` string required +- `BACKUP_ID` - The ID of the backup you wish to restore. ### `backup download` @@ -401,11 +401,11 @@ Download existing data to your local machine. wrangler d1 backup download ``` -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the D1 database you wish to download the backup of. -- `BACKUP_ID` string required +- `BACKUP_ID` - The ID of the backup you wish to download. -- `--output` string optional +- `--output` - The `.sqlite3` file to write to (defaults to `'..sqlite3'`). ### `migrations create` @@ -422,9 +422,9 @@ The filename will include a version number and the migration name you specify be wrangler d1 migrations create ``` -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the D1 database you wish to create a migration for. -- `MIGRATION_NAME` string required +- `MIGRATION_NAME` - A descriptive name for the migration you wish to create. ### `migrations list` @@ -435,13 +435,13 @@ View a list of unapplied migration files. wrangler d1 migrations list [OPTIONS] ``` -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the D1 database you wish to list unapplied migrations for. -- `--local` boolean optional +- `--local` - Show the list of unapplied migration files on your locally persisted D1 database. -- `--persist-to` string optional +- `--persist-to` - Specify directory to use for local persistence (for use in combination with `--local`). -- `--preview` boolean optional +- `--preview` - Show the list of unapplied migration files on your preview D1 database (as defined by `preview_database_id` in [`wrangler.toml`](/workers/wrangler/configuration/#d1-databases)). ### `migrations apply` @@ -460,17 +460,17 @@ If applying a migration results in an error, this migration will be rolled back, wrangler d1 migrations apply [OPTIONS] ``` -- `DATABASE_NAME` string required +- `DATABASE_NAME` - The name of the D1 database you wish to apply your migrations on. -- `--local` boolean (default: true)optional +- `--local` - Execute any unapplied migrations on your locally persisted D1 database. -- `--remote` boolean (default: false) optional +- `--remote` - Execute any unapplied migrations on your remote D1 database. -- `--persist-to` string optional +- `--persist-to` - Specify directory to use for local persistence (for use in combination with `--local`). -- `--preview` boolean optional +- `--preview` - Execute any unapplied migrations on your preview D1 database (as defined by `preview_database_id` in [`wrangler.toml`](/workers/wrangler/configuration/#d1-databases)). -- `--batch-size` number optional +- `--batch-size` - Number of queries to send in a single batch. --- @@ -500,15 +500,15 @@ Creates a new vector index, and provides the binding and name that you will put npx wrangler vectorize create [--dimensions=] [--metric=] [--description=] ``` -- `INDEX_NAME` string required +- `INDEX_NAME` - The name of the new index to create. Must be unique for an account and cannot be changed after creation or reused after the deletion of an index. -- `--dimensions` number required +- `--dimensions` - The vector dimension width to configure the index for. Cannot be changed after creation. -- `--metric` string required +- `--metric` - The distance metric to use for calculating vector distance. Must be one of `cosine`, `euclidean`, or `dot-product`. -- `--description` string optional +- `--description` - A description for your index. -- `--deprecated-v1` boolean optional +- `--deprecated-v1` - Create a legacy Vectorize index. Please note that legacy Vectorize indexes are on a [deprecation path](/vectorize/reference/transition-vectorize-legacy). ### `list` @@ -519,7 +519,7 @@ List all Vectorize indexes in your account, including the configured dimensions npx wrangler vectorize list ``` -- `--deprecated-v1` boolean optional +- `--deprecated-v1` - List legacy Vectorize indexes. Please note that legacy Vectorize indexes are on a [deprecation path](/vectorize/reference/transition-vectorize-legacy). ### `get` @@ -530,9 +530,9 @@ Get details about an individual index, including its configuration. npx wrangler vectorize get ``` -- `INDEX_NAME` string required +- `INDEX_NAME` - The name of the index to fetch details for. -- `--deprecated-v1` boolean optional +- `--deprecated-v1` - Get a legacy Vectorize index. Please note that legacy Vectorize indexes are on a [deprecation path](/vectorize/reference/transition-vectorize-legacy). ### `info` @@ -543,7 +543,7 @@ Get some additional information about an individual index, including the vector npx wrangler vectorize info ``` -- `INDEX_NAME` string required +- `INDEX_NAME` - The name of the index to fetch details for. ### `delete` @@ -554,11 +554,11 @@ Delete a Vectorize index. npx wrangler vectorize delete [OPTIONS] ``` -- `INDEX_NAME` string required +- `INDEX_NAME` - The name of the Vectorize index to delete. -- `--force` boolean optional +- `--force` - Skip confirmation when deleting the index (Note: This is not a recoverable operation). -- `--deprecated-v1` boolean optional +- `--deprecated-v1` - Delete a legacy Vectorize index. Please note that legacy Vectorize indexes are on a [deprecation path](/vectorize/reference/transition-vectorize-legacy). ### `insert` @@ -569,13 +569,13 @@ Insert vectors into an index. npx wrangler vectorize insert [OPTIONS] ``` -- `INDEX_NAME` string required +- `INDEX_NAME` - The name of the Vectorize index to upsert vectors in. -- `--file` string required +- `--file` - A file containing the vectors to insert in newline-delimited JSON (JSON) format. -- `--batch-size` number optional +- `--batch-size` - The number of vectors to insert at a time (default: `1000`). -- `--deprecated-v1` boolean optional +- `--deprecated-v1` - Insert into a legacy Vectorize index. Please note that legacy Vectorize indexes are on a [deprecation path](/vectorize/reference/transition-vectorize-legacy). ### `upsert` @@ -586,11 +586,11 @@ Upsert vectors into an index. Existing vectors in the index would be overwritten npx wrangler vectorize upsert [OPTIONS] ``` -- `INDEX_NAME` string required +- `INDEX_NAME` - The name of the Vectorize index to upsert vectors in. -- `--file` string required +- `--file` - A file containing the vectors to insert in newline-delimited JSON (JSON) format. -- `--batch-size` number optional +- `--batch-size` - The number of vectors to insert at a time (default: `5000`). ### `query` @@ -601,19 +601,19 @@ Query a Vectorize index for similar vectors. npx wrangler vectorize query [OPTIONS] ``` -- `INDEX_NAME` string required +- `INDEX_NAME` - The name of the Vectorize index to query. -- `--vector` array required +- `--vector` - Vector against which the Vectorize index is queried. -- `--top-k` number optional +- `--top-k` - The number of vectors to query (default: `5`). -- `--return-values` boolean optional +- `--return-values` - Enable to return vector values in the response (default: `false`). -- `--return-metadata` string optional +- `--return-metadata` - Enable to return vector metadata in the response. Must be one of `none`, `indexed`, or `all` (default: `none`). -- `--namespace` string optional +- `--namespace` - Query response to only include vectors from this namespace. -- `--filter` string optional +- `--filter` - Filter vectors based on this metadata filter. Example: `'{ 'p1': 'abc', 'p2': { '$ne': true }, 'p3': 10, 'p4': false, 'nested.p5': 'abcd' }'` ### `get-vectors` @@ -624,9 +624,9 @@ Fetch vectors from a Vectorize index using the provided ids. npx wrangler vectorize get-vectors [OPTIONS] ``` -- `INDEX_NAME` string required +- `INDEX_NAME` - The name of the Vectorize index from which vectors need to be fetched. -- `--ids` array required +- `--ids` - List of ids for which vectors must be fetched. ### `delete-vectors` @@ -637,9 +637,9 @@ Delete vectors in a Vectorize index using the provided ids. npx wrangler vectorize delete-vectors [OPTIONS] ``` -- `INDEX_NAME` string required +- `INDEX_NAME` - The name of the Vectorize index from which vectors need to be deleted. -- `--ids` array required +- `--ids` - List of ids corresponding to the vectors that must be deleted. ### `create-metadata-index` @@ -650,11 +650,11 @@ Enable metadata filtering on the specified property. npx wrangler vectorize create-metadata-index [OPTIONS] ``` -- `INDEX_NAME` string required +- `INDEX_NAME` - The name of the Vectorize index for which metadata index needs to be created. -- `--property-name` string required +- `--property-name` - Metadata property for which metadata filtering should be enabled. -- `--type` string required +- `--type` - Data type of the property. Must be one of `string`, `number`, or `boolean`. ### `list-metadata-index` @@ -665,7 +665,7 @@ List metadata properties on which metadata filtering is enabled. npx wrangler vectorize list-metadata-index [OPTIONS] ``` -- `INDEX_NAME` string required +- `INDEX_NAME` - The name of the Vectorize index for which metadata indexes needs to be fetched. ### `delete-metadata-index` @@ -676,9 +676,9 @@ Disable metadata filtering on the specified property. npx wrangler vectorize delete-metadata-index [OPTIONS] ``` -- `INDEX_NAME` string required +- `INDEX_NAME` - The name of the Vectorize index for which metadata index needs to be disabled. -- `--property-name` string required +- `--property-name` - Metadata property for which metadata filtering should be disabled. --- @@ -703,78 +703,78 @@ As of Wrangler v3.2.0, `wrangler dev` is supported by any Linux distributions pr ::: -- `SCRIPT` string +- `SCRIPT` - The path to an entry point for your Worker. Only required if your `wrangler.toml` does not include a `main` key (for example, `main = "index.js"`). -- `--name` string optional +- `--name` - Name of the Worker. -- `--no-bundle` boolean (default: false) optional +- `--no-bundle` - Skip Wrangler's build steps. Particularly useful when using custom builds. Refer to [Bundling](https://developers.cloudflare.com/workers/wrangler/bundling/) for more information. -- `--env` string optional +- `--env` - Perform on a specific environment. -- `--compatibility-date` string optional +- `--compatibility-date` - A date in the form yyyy-mm-dd, which will be used to determine which version of the Workers runtime is used. -- `--compatibility-flags`, `--compatibility-flag` string\[] optional +- `--compatibility-flags`, `--compatibility-flag` - Flags to use for compatibility checks. -- `--latest` boolean (default: true) optional +- `--latest` - Use the latest version of the Workers runtime. -- `--ip` string optional +- `--ip` - IP address to listen on, defaults to `localhost`. -- `--port` number optional +- `--port` - Port to listen on. -- `--inspector-port` number optional +- `--inspector-port` - Port for devtools to connect to. -- `--routes`, `--route` string\[] optional +- `--routes`, `--route` - Routes to upload. - For example: `--route example.com/*`. -- `--host` string optional +- `--host` - Host to forward requests to, defaults to the zone of project. -- `--local-protocol` "http"|"https" (default: http) optional +- `--local-protocol` - Protocol to listen to requests on. -- `--https-key-path` string optional +- `--https-key-path` - Path to a custom certificate key. -- `--https-cert-path` string optional +- `--https-cert-path` - Path to a custom certificate. -- `--local-upstream` string optional +- `--local-upstream` - Host to act as origin in local mode, defaults to `dev.host` or route. -- `--legacy-assets` string optional experimental +- `--legacy-assets` - Root folder of static assets to be served. - Use in combination with `--name` and `--latest` for basic static file hosting. For example: `wrangler dev --name personal_blog --legacy-assets dist/ --latest`. -- `--assets` string optional experimental +- `--assets` - Root folder of static assets to be served. :::caution This is an experimental feature and its behavior will be changing soon. Use `--legacy-assets` instead to ensure that you get consistent behavior when this option changes. ::: -- `--site` string optional +- `--site` - Root folder of static assets for Workers Sites. -- `--site-include` string\[] optional +- `--site-include` - Array of `.gitignore`-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded. -- `--site-exclude` string\[] optional +- `--site-exclude` - Array of `.gitignore`-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded. -- `--upstream-protocol` "http"|"https" (default: https) optional +- `--upstream-protocol` - Protocol to forward requests to host on. -- `--var` key:value\[] optional +- `--var` - Array of `key:value` pairs to inject as variables into your code. The value will always be passed as a string to your Worker. - For example, `--var git_hash:$(git rev-parse HEAD) test:123` makes the `git_hash` and `test` variables available in your Worker's `env`. - This flag is an alternative to defining [`vars`](/workers/wrangler/configuration/#non-inheritable-keys) in your `wrangler.toml`. If defined in both places, this flag's values will be used. -- `--define` key:value\[] optional +- `--define` - Array of `key:value` pairs to replace global identifiers in your code. - For example, `--define GIT_HASH:$(git rev-parse HEAD)` will replace all uses of `GIT_HASH` with the actual value at build time. - This flag is an alternative to defining [`define`](/workers/wrangler/configuration/#non-inheritable-keys) in your `wrangler.toml`. If defined in both places, this flag's values will be used. -- `--tsconfig` string optional +- `--tsconfig` - Path to a custom `tsconfig.json` file. -- `--minify` boolean optional +- `--minify` - Minify the Worker. -- `--node-compat` boolean optional +- `--node-compat` - Enable Node.js compatibility. -- `--persist-to` string optional +- `--persist-to` - Specify directory to use for local persistence. -- `--remote` boolean (default: false) optional +- `--remote` - Develop against remote resources and data stored on Cloudflare's network. -- `--test-scheduled` boolean (default: false) optional +- `--test-scheduled` - Exposes a `/__scheduled` fetch route which will trigger a scheduled event (Cron Trigger) for testing during development. To simulate different cron patterns, a `cron` query parameter can be passed in: `/__scheduled?cron=*+*+*+*+*`. -- `--log-level` "debug"|"info"|"log"|"warn"|"error"|"none" (default: log) optional +- `--log-level` - Specify Wrangler's logging level. -- `--show-interactive-dev-session` boolean (default: true if the terminal supports interactivity) optional +- `--show-interactive-dev-session` - Show the interactive dev session. - `--alias` `Array` - Specify modules to alias using [module aliasing](/workers/wrangler/configuration/#module-aliasing). @@ -797,61 +797,61 @@ None of the options for this command are required. Also, many can be set in your ::: -- `SCRIPT` string +- `SCRIPT` - The path to an entry point for your Worker. Only required if your `wrangler.toml` does not include a `main` key (for example, `main = "index.js"`). -- `--name` string optional +- `--name` - Name of the Worker. -- `--no-bundle` boolean (default: false) optional +- `--no-bundle` - Skip Wrangler's build steps. Particularly useful when using custom builds. Refer to [Bundling](https://developers.cloudflare.com/workers/wrangler/bundling/) for more information. -- `--env` string optional +- `--env` - Perform on a specific environment. -- `--outdir` string optional +- `--outdir` - Path to directory where Wrangler will write the bundled Worker files. -- `--compatibility-date` string optional +- `--compatibility-date` - A date in the form yyyy-mm-dd, which will be used to determine which version of the Workers runtime is used. -- `--compatibility-flags`, `--compatibility-flag` string\[] optional +- `--compatibility-flags`, `--compatibility-flag` - Flags to use for compatibility checks. -- `--latest` boolean (default: true) optional +- `--latest` - Use the latest version of the Workers runtime. -- `--legacy-assets` string optional experimental +- `--legacy-assets` - Root folder of static assets to be served. - Use in combination with `--name` and `--latest` for basic static file hosting. For example: `wrangler dev --name personal_blog --legacy-assets dist/ --latest`. -- `--assets` string optional experimental +- `--assets` - Root folder of static assets to be served. :::caution This is an experimental feature and its behavior will be changing soon. Use `--legacy-assets` instead to ensure that you get consistent behavior when this option changes. ::: -- `--site` string optional +- `--site` - Root folder of static assets for Workers Sites. -- `--site-include` string\[] optional +- `--site-include` - Array of `.gitignore`-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded. -- `--site-exclude` string\[] optional +- `--site-exclude` - Array of `.gitignore`-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded. -- `--var` key:value\[] optional +- `--var` - Array of `key:value` pairs to inject as variables into your code. The value will always be passed as a string to your Worker. - For example, `--var git_hash:$(git rev-parse HEAD) test:123` makes the `git_hash` and `test` variables available in your Worker's `env`. - This flag is an alternative to defining [`vars`](/workers/wrangler/configuration/#non-inheritable-keys) in your `wrangler.toml`. If defined in both places, this flag's values will be used. -- `--define` key:value\[] optional +- `--define` - Array of `key:value` pairs to replace global identifiers in your code. - For example, `--define GIT_HASH:$(git rev-parse HEAD)` will replace all uses of `GIT_HASH` with the actual value at build time. - This flag is an alternative to defining [`define`](/workers/wrangler/configuration/#non-inheritable-keys) in your `wrangler.toml`. If defined in both places, this flag's values will be used. -- `--triggers`, `--schedule`, `--schedules` string\[] optional +- `--triggers`, `--schedule`, `--schedules` - Cron schedules to attach to the deployed Worker. Refer to [Cron Trigger Examples](/workers/configuration/cron-triggers/#examples). - `--routes`, `--route` string\[] optional - Routes where this Worker will be deployed. - For example: `--route example.com/*`. -- `--tsconfig` string optional +- `--tsconfig` - Path to a custom `tsconfig.json` file. -- `--minify` boolean optional +- `--minify` - Minify the bundled Worker before deploying. -- `--node-compat` boolean optional +- `--node-compat` - Enable node.js compatibility. -- `--dry-run` boolean (default: false) optional +- `--dry-run` - Compile a project without actually deploying to live servers. Combined with `--outdir`, this is also useful for testing the output of `npx wrangler deploy`. It also gives developers a chance to upload our generated sourcemap to a service like Sentry, so that errors from the Worker can be mapped against source code, but before the service goes live. -- `--keep-vars` boolean (default: false) optional +- `--keep-vars` - It is recommended best practice to treat your Wrangler developer environment as a source of truth for your Worker configuration, and avoid making changes via the Cloudflare dashboard. - If you change your environment variables or bindings in the Cloudflare dashboard, Wrangler will override them the next time you deploy. If you want to disable this behaviour set `keep-vars` to `true`. -- `--dispatch-namespace` string optional +- `--dispatch-namespace` - Specify the [Workers for Platforms dispatch namespace](/cloudflare-for-platforms/workers-for-platforms/get-started/configuration/#2-create-a-dispatch-namespace) to upload this Worker to. --- @@ -880,13 +880,13 @@ Delete your Worker and all associated Cloudflare developer platform resources. wrangler delete [ From 53ef9db5b77986397dd5561f7efb4b1a5eceae62 Mon Sep 17 00:00:00 2001 From: Brendan Irvine-Broque Date: Sun, 29 Sep 2024 12:25:25 +0200 Subject: [PATCH 110/110] Add docs for 'cannot perform I/O on behalf of a different request (#16844) * Add docs for 'cannot perform I/O on behalf of a different request refs https://github.com/cloudflare/workerd/pull/2715 * Apply suggestions from code review Co-authored-by: ToriLindsay --------- Co-authored-by: ToriLindsay --- .../docs/workers/observability/errors.mdx | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/content/docs/workers/observability/errors.mdx b/src/content/docs/workers/observability/errors.mdx index dd1125829635d8..82b60139fa5734 100644 --- a/src/content/docs/workers/observability/errors.mdx +++ b/src/content/docs/workers/observability/errors.mdx @@ -132,6 +132,49 @@ export default { }; ``` + +### Cannot perform I/O on behalf of a different request + +``` +Uncaught (in promise) Error: Cannot perform I/O on behalf of a different request. I/O objects (such as streams, request/response bodies, and others) created in the context of one request handler cannot be accessed from a different request's handler. +``` + +This error occurs when you attempt to share input/output (I/O) objects (such as streams, requests, or responses) created by one invocation of your Worker in the context of a different invocation. + +In Cloudflare Workers, each invocation is handled independently and has its own execution context. This design ensures optimal performance and security by isolating requests from one another. When you try to share I/O objects between different invocations, you break this isolation. Since these objects are tied to the specific request they were created in, accessing them from another request's handler is not allowed and leads to the error. + +This error is most commonly caused by attempting to cache an I/O object, like a [Request](/workers/runtime-apis/request/) in global scope, and then access it in a subsequent request. For example, if you create a Worker and run the following code in local development, and make two requests to your Worker in quick succession, you can reproduce this error: + +```js +let cachedResponse = null; + +export default { + async fetch(request, env, ctx) { + if (cachedResponse) { return cachedResponse; } + cachedResponse = new Response('Hello, world!'); + await new Promise(resolve => setTimeout(resolve, 5000)); // Sleep for 5s to demonstrate this particular error case + return cachedResponse; + } +}; +``` + +You can fix this by instead storing only the data in global scope, rather than the I/O object itself: + +```js +let cachedData = null; + +export default { + async fetch(request, env, ctx) { + if (cachedData) { return new Response(cachedData); } + const response = new Response('Hello, world!'); + cachedData = await response.text(); + return new Response(cachedData, response); + } +}; +``` + +If you need to share state across requests, consider using [Durable Objects](/durable-objects/). If you need to cache data across requests, consider using [Workers KV](/kv/). + ## Errors on Worker upload These errors occur when a Worker is uploaded or modified.