INTEGRITY Dokumentace

Infrastruktura jako kód (IaC)

Zatímco Wrangler usnadňuje nahrávání a správu Workerů, přesto někdy potřebujete programovatější přístup. Ten může spočívat v použití nástrojů Infrastructure as Code (IaC) nebo v přímé práci s Workers API. Mezi příklady patří build a deploy skripty, CI/CD pipeline, vlastní vývojářské nástroje a automatizované testování.

Aby to bylo jednodušší, Cloudflare poskytuje SDK knihovny pro oblíbené jazyky, jako je cloudflare-typescript a cloudflare-python. Pro IaC můžete použít nástroje jako Terraform od HashiCorpu a Cloudflare Terraform Provider ke správě prostředků Workers.

Níže jsou uvedeny příklady nasazení Workeru pomocí různých nástrojů a jazyků, spolu s důležitými informacemi pro správu Workers pomocí IaC.

Všechny tyto příklady vyžadují ID účtu a API token (nikoli Global API key), aby fungoval.

Bundlování ve Workers

Žádný z následujících příkladů nedělá Bundlování ve Workers. Obvykle se to dělá pomocí Wrangleru nebo nástroje, jako je esbuild.

Tento krok sestavení byste obvykle spustili před použitím Terraform plánu nebo před nahráním skriptu přes API:

wrangler deploy --dry-run --outdir build

Pokud pro sestavení používáte Wrangler a pro nahrávání jinou metodu, nezapomeňte zkopírovat celou konfiguraci z wrangler.json do konfigurace Terraformu nebo API požadavku. To je obzvláště důležité u compatibility_date nebo příznaky, na kterých váš skript závisí.

Terraform

V tomto příkladu potřebujete lokální soubor nazvaný my-script.mjs s obsahem skriptu podobným následujícím příkladům. Další informace najdete v Cloudflare Terraform Provider, a odkázat na Příklad zdroje skriptu Workers pro všechna dostupná nastavení prostředků.

variable "account_id" {
  default = "replace_me"
}

resource "cloudflare_worker" "my_worker" {
  account_id = var.account_id
  name = "my-worker"
  observability = {
    enabled = true
  }
}

resource "cloudflare_worker_version" "my_worker_version" {
  account_id = var.account_id
  worker_id = cloudflare_worker.my_worker.id
  compatibility_date = "2025-02-21" # Set this to today's date
  main_module = "my-script.mjs"
  modules = [
    {
      name = "my-script.mjs"
      content_type = "application/javascript+module"
      # Replacement (version creation) is triggered whenever this file changes
      content_file = "my-script.mjs"
    }
  ]
}

resource "cloudflare_workers_deployment" "my_worker_deployment" {
  account_id = var.account_id
  script_name = cloudflare_worker.my_worker.name
  strategy = "percentage"
  versions = [{
    percentage = 100
    version_id = cloudflare_worker_version.my_worker_version.id
  }]
}

Všimněte si, že nemusíte spravovat všechny tyto prostředky v Terraformu. Můžete například použít pouze cloudflare_worker resource a zároveň pro verze i nasazení volně používat Wrangler nebo vlastní nasazovací nástroje.

Bindings v Terraformu

Bindings umožňují vašemu Workeru interagovat se zdroji na Cloudflare Developer Platform. V Terraformu se bindingy konfigurují jinak než ve Wrangleru. Místo samostatných vlastností nejvyšší úrovně pro každý typ bindingu (jako kv_namespaces, r2_buckets, atd.), Terraform používá jediný bindings pole, kde každá vazba (binding) má type vlastnost spolu s vlastnostmi specifickými pro daný typ.

Níže jsou uvedeny příklady jednotlivých typů vazeb a jejich povinných vlastností:

Vazba KV Namespace

Navázat na KV namespace pro úložiště klíč-hodnota:

bindings = [{
  type = "kv_namespace"
  name = "MY_KV"
  namespace_id = "your-kv-namespace-id"
}]

Vlastnosti:

R2 Bucket Binding

Navázat na R2 bucket pro objektové úložiště:

bindings = [{
  type = "r2_bucket"
  name = "MY_BUCKET"
  bucket_name = "my-bucket-name"
}]

Vlastnosti:

D1 Database Binding

Navázat na databáze D1 pro úložiště SQL:

bindings = [{
  type = "d1"
  name = "DB"
  id = "your-database-id"
}]

Vlastnosti:

Vazba Durable Object

Navázat na Durable Object třída:

bindings = [{
  type = "durable_object_namespace"
  name = "MY_DURABLE_OBJECT"
  class_name = "MyDurableObjectClass"
}]

Vlastnosti:

Service Binding

Navázat na další Worker pro komunikaci mezi Workery:

bindings = [{
  type = "service"
  name = "MY_SERVICE"
  service = "other-worker-name"
}]

Vlastnosti:

Queue Binding

Navázat na Queue pro předávání zpráv:

Pro odesílání zpráv:

bindings = [{
  type = "queue"
  name = "MY_QUEUE"
  queue_name = "my-queue"
}]

Vlastnosti:

Pro odebírání zpráv nakonfigurujte svůj Worker jako consumer přímo v prostředku queue, nikoli prostřednictvím vazeb.

Vazba Vectorize

Navázat na Vectorize index pro vektorové vyhledávání:

bindings = [{
  type = "vectorize"
  name = "VECTORIZE_INDEX"
  index_name = "my-index"
}]

Vlastnosti:

Workers AI Binding

Navázat na Workers AI pro AI inferenci:

bindings = [{
  type = "ai"
  name = "AI"
}]

Vlastnosti:

Vazba Hyperdrive

Navázat na Hyperdrive konfigurace pro sdružování databázových připojení:

bindings = [{
  type = "hyperdrive"
  name = "HYPERDRIVE"
  id = "your-hyperdrive-config-id"
}]

Vlastnosti:

Vazba VPC Service

Navázat na VPC Service pro přístup k prostředkům ve vaší privátní síti:

bindings = [{
  type = "vpc_service"
  name = "PRIVATE_API"
  service_id = "your-vpc-service-id"
}]

Vlastnosti:

VPC Service můžete vytvořit pomocí Terraformu za použití cloudflare_connectivity_directory_service prostředek. Úplný návod najdete v Nakonfigurujte VPC Services pomocí nástroje Terraform.

Analytics Engine Binding

Navázat na Analytics Engine datová sada:

bindings = [{
  type = "analytics_engine"
  name = "ANALYTICS"
  dataset = "my_dataset"
}]

Vlastnosti:

Proměnné prostředí

Pro proměnné prostředí ve formátu prostého textu použijte plain_text typ bindingu:

bindings = [{
  type = "plain_text"
  name = "MY_VARIABLE"
  text = "my-value"
}]

Vlastnosti:

Secret Text Binding

Pro šifrované secrets použijte secret_text typ bindingu:

bindings = [{
  type = "secret_text"
  name = "API_KEY"
  text = var.api_key
}]

Vlastnosti:

Kompletní příklad

Následuje příklad kombinující několik typů vazeb:

resource "cloudflare_worker_version" "my_worker_version" {
  account_id = var.account_id
  worker_id = cloudflare_worker.my_worker.id
  compatibility_date = "2025-08-06"
  main_module = "worker.js"

  modules = [{
    name = "worker.js"
    content_type = "application/javascript+module"
    content_file = "worker.js"
  }]

  bindings = [
    {
      type = "kv_namespace"
      name = "MY_KV"
      namespace_id = var.kv_namespace_id
    },
    {
      type = "r2_bucket"
      name = "MY_BUCKET"
      bucket_name = "my-bucket"
    },
    {
      type = "d1"
      name = "DB"
      id = var.d1_database_id
    },
    {
      type = "service"
      name = "AUTH_SERVICE"
      service = "auth-worker"
    },
    {
      type = "plain_text"
      name = "ENVIRONMENT"
      text = "production"
    },
    {
      type = "secret_text"
      name = "API_KEY"
      text = var.api_key
    },
    {
      type = "vpc_service"
      name = "PRIVATE_API"
      service_id = var.vpc_service_id
    }
  ]
}

Knihovny Cloudflare API

Tento příklad používá cloudflare-typescript SDK, které poskytuje pohodlný přístup k Cloudflare REST API ze serverového JavaScriptu nebo TypeScriptu.

#!/usr/bin/env -S npm run tsn -T

/**
 * Create and deploy a Worker
 *
 * Docs:
 * - https://developers.cloudflare.com/workers/configuration/versions-and-deployments/
 * - https://developers.cloudflare.com/workers/platform/infrastructure-as-code/
 *
 * Prerequisites:
 * 1. Generate an API token: https://developers.cloudflare.com/fundamentals/api/get-started/create-token/
 * 2. Find your account ID: https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/
 * 3. Find your workers.dev subdomain: https://developers.cloudflare.com/workers/configuration/routing/workers-dev/
 *
 * Environment variables:
 *   - CLOUDFLARE_API_TOKEN (required)
 *   - CLOUDFLARE_ACCOUNT_ID (required)
 *   - CLOUDFLARE_SUBDOMAIN (optional)
 *
 * Usage:
 *   Run this script to deploy a simple "Hello World" Worker.
 *   Access it at: my-hello-world-worker.$subdomain.workers.dev
 */

import { exit } from "node:process";

import Cloudflare from "cloudflare";

const WORKER_NAME = "my-hello-world-worker";
const SCRIPT_FILENAME = `${WORKER_NAME}.mjs`;

function loadConfig() {
	const apiToken = process.env["CLOUDFLARE_API_TOKEN"];
	if (!apiToken) {
		throw new Error(
			"Missing required environment variable: CLOUDFLARE_API_TOKEN",
		);
	}

	const accountId = process.env["CLOUDFLARE_ACCOUNT_ID"];
	if (!accountId) {
		throw new Error(
			"Missing required environment variable: CLOUDFLARE_ACCOUNT_ID",
		);
	}

	const subdomain = process.env["CLOUDFLARE_SUBDOMAIN"];

	return {
		apiToken,
		accountId,
		subdomain: subdomain || undefined,
		workerName: WORKER_NAME,
	};
}

const config = loadConfig();
const client = new Cloudflare({
	apiToken: config.apiToken,
});

async function main() {
	try {
		console.log("🚀 Starting Worker creation and deployment...");

		const scriptContent = `
      export default {
        async fetch(request, env, ctx) {
          return new Response(env.MESSAGE, { status: 200 });
        },
      }`.trim();

		let worker;
		try {
			worker = await client.workers.beta.workers.get(config.workerName, {
				account_id: config.accountId,
			});
			console.log(`♻️  Worker ${config.workerName} already exists. Using it.`);
		} catch (error) {
			if (!(error instanceof Cloudflare.NotFoundError)) {
				throw error;
			}
			console.log(`✏️  Creating Worker ${config.workerName}...`);
			worker = await client.workers.beta.workers.create({
				account_id: config.accountId,
				name: config.workerName,
				subdomain: {
					enabled: config.subdomain !== undefined,
				},
				observability: {
					enabled: true,
				},
			});
		}

		console.log(`⚙️  Worker id: ${worker.id}`);
		console.log("✏️  Creating Worker version...");

		// Create the first version of the Worker
		const version = await client.workers.beta.workers.versions.create(
			worker.id,
			{
				account_id: config.accountId,
				main_module: SCRIPT_FILENAME,
				compatibility_date: new Date().toISOString().split("T")[0],
				bindings: [
					{
						type: "plain_text",
						name: "MESSAGE",
						text: "Hello World!",
					},
				],
				modules: [
					{
						name: SCRIPT_FILENAME,
						content_type: "application/javascript+module",
						content_base64: Buffer.from(scriptContent).toString("base64"),
					},
				],
			},
		);

		console.log(`⚙️  Version id: ${version.id}`);
		console.log("🚚 Creating Worker deployment...");

		// Create a deployment and point all traffic to the version we created
		await client.workers.scripts.deployments.create(config.workerName, {
			account_id: config.accountId,
			strategy: "percentage",
			versions: [
				{
					percentage: 100,
					version_id: version.id,
				},
			],
		});

		console.log("✅ Deployment successful!");

		if (config.subdomain) {
			console.log(`
🌍 Your Worker is live!
📍 URL: https://${config.workerName}.${config.subdomain}.workers.dev/
`);
		} else {
			console.log(`
⚠️  Set up a route, custom domain, or workers.dev subdomain to access your Worker.
Add CLOUDFLARE_SUBDOMAIN to your environment variables to set one up automatically.
`);
		}
	} catch (error) {
		console.error("❌ Deployment failed:", error);
		exit(1);
	}
}

main();
#!/usr/bin/env -S npm run tsn -T

/**
 * Create and deploy a Worker
 *
 * Docs:
 * - https://developers.cloudflare.com/workers/configuration/versions-and-deployments/
 * - https://developers.cloudflare.com/workers/platform/infrastructure-as-code/
 *
 * Prerequisites:
 * 1. Generate an API token: https://developers.cloudflare.com/fundamentals/api/get-started/create-token/
 * 2. Find your account ID: https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/
 * 3. Find your workers.dev subdomain: https://developers.cloudflare.com/workers/configuration/routing/workers-dev/
 *
 * Environment variables:
 *   - CLOUDFLARE_API_TOKEN (required)
 *   - CLOUDFLARE_ACCOUNT_ID (required)
 *   - CLOUDFLARE_SUBDOMAIN (optional)
 *
 * Usage:
 *   Run this script to deploy a simple "Hello World" Worker.
 *   Access it at: my-hello-world-worker.$subdomain.workers.dev
 */

import { exit } from 'node:process';

import Cloudflare from 'cloudflare';

interface Config {
  apiToken: string;
  accountId: string;
  subdomain: string | undefined;
  workerName: string;
}

const WORKER_NAME = 'my-hello-world-worker';
const SCRIPT_FILENAME = `${WORKER_NAME}.mjs`;

function loadConfig(): Config {
  const apiToken = process.env['CLOUDFLARE_API_TOKEN'];
  if (!apiToken) {
    throw new Error('Missing required environment variable: CLOUDFLARE_API_TOKEN');
  }

  const accountId = process.env['CLOUDFLARE_ACCOUNT_ID'];
  if (!accountId) {
    throw new Error('Missing required environment variable: CLOUDFLARE_ACCOUNT_ID');
  }

  const subdomain = process.env['CLOUDFLARE_SUBDOMAIN'];

  return {
    apiToken,
    accountId,
    subdomain: subdomain || undefined,
    workerName: WORKER_NAME,
  };
}

const config = loadConfig();
const client = new Cloudflare({
  apiToken: config.apiToken,
});

async function main(): Promise<void> {
  try {
    console.log('🚀 Starting Worker creation and deployment...');

    const scriptContent = `
      export default {
        async fetch(request, env, ctx) {
          return new Response(env.MESSAGE, { status: 200 });
        },
      }`.trim();

    let worker;
    try {
      worker = await client.workers.beta.workers.get(config.workerName, {
        account_id: config.accountId,
      });
      console.log(`♻️  Worker ${config.workerName} already exists. Using it.`);
    } catch (error) {
      if (!(error instanceof Cloudflare.NotFoundError)) { throw error; }
      console.log(`✏️  Creating Worker ${config.workerName}...`);
      worker = await client.workers.beta.workers.create({
        account_id: config.accountId,
        name: config.workerName,
        subdomain: {
          enabled: config.subdomain !== undefined,
        },
        observability: {
          enabled: true,
        },
      });
    }

    console.log(`⚙️  Worker id: ${worker.id}`);
    console.log('✏️  Creating Worker version...');

    // Create the first version of the Worker
    const version = await client.workers.beta.workers.versions.create(worker.id, {
      account_id: config.accountId,
      main_module: SCRIPT_FILENAME,
      compatibility_date: new Date().toISOString().split('T')[0]!,
      bindings: [
        {
          type: 'plain_text',
          name: 'MESSAGE',
          text: 'Hello World!',
        },
      ],
      modules: [
        {
          name: SCRIPT_FILENAME,
          content_type: 'application/javascript+module',
          content_base64: Buffer.from(scriptContent).toString('base64'),
        },
      ],
    });

    console.log(`⚙️  Version id: ${version.id}`);
    console.log('🚚 Creating Worker deployment...');

    // Create a deployment and point all traffic to the version we created
    await client.workers.scripts.deployments.create(config.workerName, {
      account_id: config.accountId,
      strategy: 'percentage',
      versions: [
        {
            percentage: 100,
            version_id: version.id,
          },
        ],
    });

    console.log('✅ Deployment successful!');

    if (config.subdomain) {
      console.log(`
🌍 Your Worker is live!
📍 URL: https://${config.workerName}.${config.subdomain}.workers.dev/
`);
    } else {
      console.log(`
⚠️  Set up a route, custom domain, or workers.dev subdomain to access your Worker.
Add CLOUDFLARE_SUBDOMAIN to your environment variables to set one up automatically.
`);
    }
  } catch (error) {
    console.error('❌ Deployment failed:', error);
    exit(1);
  }
}

main();

Cloudflare REST API

Otevřete terminál nebo vytvořte shellový skript pro nahrání Workeru a správu verzí a nasazení pomocí curl. Skripty Workers jsou soubory JavaScript ES Modules, ale podporujeme také Python Workers (open beta) a Workers v Rustu.

account_id="replace_me"
api_token="replace_me"
worker_name="my-hello-world-worker"

worker_script_base64=$(echo '
export default {
  async fetch(request, env, ctx) {
    return new Response(env.MESSAGE, { status: 200 });
  }
};
' | base64)

# Note the below will fail if the worker already exists!
# Here's how to delete the Worker
#
# worker_id="replace-me"
# curl "https://api.cloudflare.com/client/v4/accounts/$account_id/workers/workers/$worker_id" \
#   -X DELETE \
#   -H "Authorization: Bearer $api_token"

# Create the Worker
worker_id=$(curl "https://api.cloudflare.com/client/v4/accounts/$account_id/workers/workers" \
  -X POST \
  -H "Authorization: Bearer $api_token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "'$worker_name'"
  }' \
  | jq -r '.result.id')

echo "\nWorker ID: $worker_id\n"

# Upload the Worker's first version
version_id=$(curl "https://api.cloudflare.com/client/v4/accounts/$account_id/workers/workers/$worker_id/versions" \
  -X POST \
  -H "Authorization: Bearer $api_token" \
  -H "Content-Type: application/json" \
  -d '{
    "compatibility_date": "2025-08-06",
    "main_module": "'$worker_name'.mjs",
    "modules": [
      {
        "name": "'$worker_name'.mjs",
        "content_type": "application/javascript+module",
        "content_base64": "'$worker_script_base64'"
      }
    ],
    "bindings": [
      {
        "type": "plain_text",
        "name": "MESSAGE",
        "text": "Hello World!"
      }
    ]
  }' \
  | jq -r '.result.id')

echo "\nVersion ID: $version_id\n"

# Create a deployment for the Worker
deployment_id=$(curl "https://api.cloudflare.com/client/v4/accounts/$account_id/workers/scripts/$worker_name/deployments" \
  -X POST \
  -H "Authorization: Bearer $api_token" \
  -H "Content-Type: application/json" \
  -d '{
    "strategy": "percentage",
    "versions": [
      {
        "percentage": 100,
        "version_id": "'$version_id'"
      }
    ]
  }' \
  | jq -r '.result.id')

echo "\nDeployment ID: $deployment_id\n"

Python Workers mají svou vlastní speciální text/x-python typ obsahu a python_workers příznak kompatibility.

account_id="replace_me"
api_token="replace_me"
worker_name="my-hello-world-worker"

worker_script_base64=$(echo '
from workers import WorkerEntrypoint, Response

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        return Response(self.env.MESSAGE)
' | base64)

# Note the below will fail if the worker already exists!
# Here's how to delete the Worker
#
# worker_id="replace-me"
# curl "https://api.cloudflare.com/client/v4/accounts/$account_id/workers/workers/$worker_id" \
#   -X DELETE \
#   -H "Authorization: Bearer $api_token"

# Create the Worker
worker_id=$(curl "https://api.cloudflare.com/client/v4/accounts/$account_id/workers/workers" \
  -X POST \
  -H "Authorization: Bearer $api_token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "'$worker_name'"
  }' \
  | jq -r '.result.id')

echo "\nWorker ID: $worker_id\n"

# Upload the Worker's first version
version_id=$(curl "https://api.cloudflare.com/client/v4/accounts/$account_id/workers/workers/$worker_id/versions" \
  -X POST \
  -H "Authorization: Bearer $api_token" \
  -H "Content-Type: application/json" \
  -d '{
    "compatibility_date": "2025-08-06",
    "compatibility_flags": [
      "python_workers"
    ],
    "main_module": "'$worker_name'.py",
    "modules": [
      {
        "name": "'$worker_name'.py",
        "content_type": "text/x-python",
        "content_base64": "'$worker_script_base64'"
      }
    ],
    "bindings": [
      {
        "type": "plain_text",
        "name": "MESSAGE",
        "text": "Hello World!"
      }
    ]
  }' \
  | jq -r '.result.id')

echo "\nVersion ID: $version_id\n"

# Create a deployment for the Worker
deployment_id=$(curl "https://api.cloudflare.com/client/v4/accounts/$account_id/workers/scripts/$worker_name/deployments" \
  -X POST \
  -H "Authorization: Bearer $api_token" \
  -H "Content-Type: application/json" \
  -d '{
    "strategy": "percentage",
    "versions": [
      {
        "percentage": 100,
        "version_id": "'$version_id'"
      }
    ]
  }' \
  | jq -r '.result.id')

echo "\nDeployment ID: $deployment_id\n"

rozhraní API pro nahrávání multipart/form-data

Toto API používá multipart/form-data k nahrání Workeru a implicitně vytvoří verzi a nasazení. Výše uvedené API se doporučuje pro přímou správu verzí a nasazení.

account_id="replace_me"
api_token="replace_me"
worker_name="my-hello-world-script"

script_content='export default {
  async fetch(request, env, ctx) {
    return new Response(env.MESSAGE, { status: 200 });
  }
};'

# Upload the Worker
curl "https://api.cloudflare.com/client/v4/accounts/$account_id/workers/scripts/$worker_name" \
  -X PUT \
  -H "Authorization: Bearer $api_token" \
  -F "metadata={
    'main_module': '"$worker_name".mjs',
    'bindings': [
      {
        'type': 'plain_text',
        'name': 'MESSAGE',
        'text': 'Hello World!'
      }
    ],
    'compatibility_date': '$today'
  };type=application/json" \
  -F "$worker_name.mjs=@-;filename=$worker_name.mjs;type=application/javascript+module" <<EOF
$script_content
EOF

Pro Workers for Platforms, můžete nahrát Uživatelský Worker na dispatch namespace. Všimněte si Koncový bod API je na /workers/dispatch/namespaces/$DISPATCH_NAMESPACE/scripts/$SCRIPT_NAME.

account_id="replace_me"
api_token="replace_me"
dispatch_namespace="replace_me"
worker_name="my-hello-world-script"

script_content='export default {
  async fetch(request, env, ctx) {
    return new Response(env.MESSAGE, { status: 200 });
  }
};'

# Create a dispatch namespace
curl https://api.cloudflare.com/client/v4/accounts/$account_id/workers/dispatch/namespaces \
  -X POST \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $api_token" \
  -d '{
    "name": "'$dispatch_namespace'"
  }'

# Upload the Worker
curl "https://api.cloudflare.com/client/v4/accounts/$account_id/workers/dispatch/namespaces/$dispatch_namespace/scripts/$worker_name" \
  -X PUT \
  -H "Authorization: Bearer $api_token" \
  -F "metadata={
    'main_module': '"$worker_name".mjs',
    'bindings': [
      {
        'type': 'plain_text',
        'name': 'MESSAGE',
        'text': 'Hello World!'
      }
    ],
    'compatibility_date': '$today'
  };type=application/json" \
  -F "$worker_name.mjs=@-;filename=$worker_name.mjs;type=application/javascript+module" <<EOF
$script_content
EOF

Python Workers

Python Workers (open beta) mají vlastní speciální text/x-python typ obsahu a python_workers příznak kompatibility pro nahrávání pomocí multipart/form-data API.

curl https://api.cloudflare.com/client/v4/accounts/<account_id>/workers/scripts/my-hello-world-script \
  -X PUT \
  -H 'Authorization: Bearer <api_token>' \
  -F 'metadata={
        "main_module": "my-hello-world-script.py",
        "bindings": [
          {
            "type": "plain_text",
            "name": "MESSAGE",
            "text": "Hello World!"
          }
        ],
        "compatibility_date": "$today",
        "compatibility_flags": [
          "python_workers"
        ]
      };type=application/json' \
  -F 'my-hello-world-script.py=@-;filename=my-hello-world-script.py;type=text/x-python' <<EOF
from workers import WorkerEntrypoint, Response

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        return Response(self.env.MESSAGE)
EOF

Co je třeba zvážit u Durable Objects

Durable Object migrace se aplikují společně s nasazeními. To znamená, že se nelze navázat na Durable Object ve verzi, pokud dané nasazení neexistuje, tedy pokud migrace nebyly aplikovány. Pokud například spustíte toto v Terraformu, při prvním použití plánu selže:

resource "cloudflare_worker" "my_worker" {
  account_id = var.account_id
  name = "my-worker"
}

resource "cloudflare_worker_version" "my_worker_version" {
  account_id = var.account_id
  worker_id = cloudflare_worker.my_worker.id
  bindings = [
    {
      type = "durable_object_namespace"
      name = "my_durable_object"
      class_name = "MyDurableObjectClass"
    }
  ]
  migrations = {
    new_sqlite_classes = [
      "MyDurableObjectClass"
    ]
  }
  # ...version props omitted for brevity
}

resource "cloudflare_workers_deployment" "my_worker_deployment" {
  # ...deployment props omitted for brevity
}

Aby se to podařilo, musíte nejprve zakomentovat durable_object binding blok, aplikujte plán, odkomentujte ho a zakomentujte migrations blok a poté plán znovu aplikujte. Tentokrát se plán úspěšně provede. Totéž platí i pro API nebo SDK. Toto je příklad situace, kdy dává smysl spravovat pouze cloudflare_worker a/nebo cloudflare_workers_deployment prostředky při použití nástroje Wrangler pro sestavení a správu verzí.

Co je třeba zvážit u verzí Workerů

Neměnnost zdroje

Verze Workeru jsou na úrovni API neměnné, což znamená, že je po vytvoření nelze upravit, pouze znovu vytvořit s požadovanými změnami. To znamená, že smysluplné změny cloudflare_worker_version Terraform prostředek vždy vyvolá nahrazení. Když se cloudflare_worker_version resource nahradí, vytvoří se nová verze s požadovanými změnami, ale předchozí verze se nesmaže. Díky tomu má Worker při správě přes Terraform kompletní historii verzí. Jinými slovy, verze jsou neměnné a lze k nim pouze přidávat. Když se nadřazený cloudflare_worker resource smaže, smažou se i všechny existující verze přiřazené k danému Workeru.

Obsah modulu

Moduly verze Workeru podporují dva vzájemně se vylučující způsoby poskytování obsahu:

V obou případech se změny v podkladovém obsahu sledují pomocí vypočítaného content_sha256 atribut. Zadání obsahu pomocí content_file atribut se doporučuje téměř ve všech případech, protože se tak obsah modulu neukládá přímo do stavu. Obsah modulu může být poměrně velký (až desítky megabajtů) a jeho uložení do stavu by zvětšilo soubor stavu a negativně ovlivnilo výkon operací Terraformu. Hlavní případ použití pro content_base64 atribut importuje cloudflare_worker_version Terraform prostředek z API, popsaný níže.

Chování importu

Během importu Terraform vždy vyplní content_base64 atribut ve stavu, bez ohledu na atribut použitý ve vaší konfiguraci.

terraform import cloudflare_worker_version.my_worker_version <account_id>/<worker_id>/<version_id>

Pokud vaše konfigurace používá content_file, dojde po importu k neshodě (stav používá content_base64, konfigurace používá content_file). To je očekávané chování.

Za předpokladu, že obsah lokálního souboru, na který odkazuje content_file odpovídá importovanému obsahu a jejich content_sha256 hodnoty jsou stejné, dojde k aktualizaci na místě (in-place) u cloudflare_worker_version Terraform prostředek. Mělo by jít o aktualizaci na místě, nikoli o nahrazení, protože se podkladový obsah nemění (jen content_sha256 atribut je v obou případech stejný) a prostředek není potřeba aktualizovat na úrovni API. Jediné, co je potřeba aktualizovat, je stav Terraformu, který přejde z používání content_base64 na content_file po aktualizaci.

Pokud chce Terraform prostředek místo toho nahradit kvůli rozdílu ve vypočítané content_sha256 hodnoty, pak se použije obsah lokálního souboru, na který odkazuje content_file neodpovídá importovanému obsahu a prostředek nelze čistě importovat bez aktualizace lokálního souboru tak, aby odpovídal očekávané hodnotě API.

Příklady

Použití content_file:

resource "cloudflare_worker_version" "content_file_example" {
  account_id  = var.account_id
  worker_id   = cloudflare_worker.example.id
  main_module = "worker.js"
  modules = [{
    name         = "worker.js"
    content_type = "application/javascript+module"
    content_file = "build/worker.js"
  }]
}

Použití content_base64:

resource "cloudflare_worker_version" "content_base64_example" {
  account_id  = var.account_id
  worker_id   = cloudflare_worker.example.id
  main_module = "worker.js"
  modules = [{
    name           = "worker.js"
    content_type   = "application/javascript+module"
    content_base64 = base64encode("export default { async fetch() { return new Response('Hello world!') } }")
  }]
}