Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvise provisioning modularisation #7519

Open
penalosa opened this issue Dec 11, 2024 · 0 comments
Open

Improvise provisioning modularisation #7519

penalosa opened this issue Dec 11, 2024 · 0 comments
Assignees

Comments

@penalosa
Copy link
Contributor

          We could consider providing a simpler path towards modularisation by injecting the various handlers (kv, r2, d1, etc.) so that provisionBindings and runProvisioningFlow stay agnostic?

I was thinking something like this:

interface ResourceHandler {
	type: string; // The resource type (e.g., 'kv_namespace', 'r2_bucket', etc.)
	provision(
		accountId: string,
		bindings: CfWorkerInit["bindings"],
		settings: Settings | undefined
	): Promise<PendingResource[]>;
	listExisting(accountId: string): Promise<NormalisedResourceInfo[]>;
}

interface PendingResource {
	binding: string;
	create: (name: string) => Promise<string>;
	updateId: (id: string) => void;
	type: string;
}

class Provisioner {
	#pendingResources: { [key: string]: PendingResource[] };

	constructor(private handlers: ResourceHandler[]) {}

	async provision(
		accountId: string,
		bindings: CfWorkerInit["bindings"],
		scriptName: string
	): Promise<void> {
		let settings: Settings | undefined;

		try {
			settings = await getSettings(accountId, scriptName);
		} catch (error) {
			logger.debug("No settings found");
		}

		for (const handler of this.handlers) {
			pendingResources[handler.type] = await handler.provision(
				accountId,
				bindings,
				settings
			);
		}

		for (const pendingResource in pendingResources) {
			await this.#runProvisioningFlow(pendingResource);
		}
	}

  async #runProvisioningFlow(pendingResource: PendingResource) {
		// ...  
		const handler = this.handlers.find((h) => h.type === pendingResource.type);
		
		if (handler && this.#pendingResources.length > 0) {
			const preExistingResources = await handler.listExisting("accountId");

			...

			for (const item of this.#pendingResources) {
				// ...
			}
		}
	}
}

You could then define the kvHandler in the kv folder, e.g. /src/kv/provision.ts:

const kvHandler: ResourceHandler = {
	type: "kv_namespace",

	async provision(
		accountId: string,
		bindings: CfWorkerInit["bindings"],
		settings: Settings | undefined
	): Promise<PendingResources[]> {
		const pendingResources: PendingResource[] = [];
		for (const binding of bindings.kv_namespaces || []) {
			if (
				!settings?.bindings.find(
					(b) => b.name === binding.binding && b.type === "kv_namespace"
				)
			) {
				pendingResources.push({
					binding: binding.binding,
					create: async (name: string) => {
						const id = await createKVNamespace(accountId, name);
						return id;
					},
					updateId: (id: string) => {
						binding.id = id;
					},
					type: "kv_namespace",
				});
			}
		}
		return pendingResources;
	},

	async listExisting(accountId: string): Promise<NormalisedResourceInfo[]> {
		const namespaces = await listKVNamespaces(accountId);
		return namespaces.map((ns) => ({ name: ns.title, id: ns.id }));
	},
};

And finally, create the provisioner with all the required handlers:

const provisioner = new Provisioner([kvHandler, r2BucketHandler, d1DatabaseHandler]);

// await provisioner.provision(accountId, bindings, scriptName);

Originally posted by @andyjessop in #7427 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Backlog
Development

No branches or pull requests

2 participants