import type { PhysicalDatabaseCapacityPolicy } from './capacityPolicy.js'; /** * Agent tag key for the customer-managed deployment name that identifies a * worker's physical database pool, `(organizationId, agentName, engine)`. * * Published on its own rather than read out of * `databaseLifecycle:capacityPolicies`, so a customer who wants no proactive * scaling still gets a resolvable pool. One value, e.g. `customer-opa`. */ export const DATABASE_LIFECYCLE_TAG_AGENT_NAME = 'databaseLifecycle:agentName'; export class PhysicalPoolIdentityError extends Error { readonly code = 'physical_pool_identity_invalid'; constructor(message: string) { super(message); this.name = 'PhysicalPoolIdentityError'; } } /** * The single physical pool a worker's registration names, or undefined when it * published none. * * Throws {@link PhysicalPoolIdentityError} when the registration names more * than one pool. Guessing between them would provision infrastructure under * one deployment's Terraform state and hand it to another, so callers must * either refuse the registration or withhold the identity. * * Falls back to the capacity policy's name when the tag is absent, for a * worker predating {@link DATABASE_LIFECYCLE_TAG_AGENT_NAME} that is still * rolling: the policy names the same pool, so identity stays unambiguous. */ export function parsePhysicalPoolAgentNameTag( published: string[] | undefined, capacityPolicies: readonly PhysicalDatabaseCapacityPolicy[] = [] ): string | undefined { if (published !== undefined && !Array.isArray(published)) { throw new PhysicalPoolIdentityError(`${DATABASE_LIFECYCLE_TAG_AGENT_NAME} must be an array holding one deployment name.`); } const values = published ?? []; if (values.some((value) => typeof value !== 'string' || value.trim() === '')) { throw new PhysicalPoolIdentityError(`${DATABASE_LIFECYCLE_TAG_AGENT_NAME} must be a non-empty string when provided.`); } const declared = new Set(values.map((value) => value.trim())); if (declared.size > 1) { throw new PhysicalPoolIdentityError( `${DATABASE_LIFECYCLE_TAG_AGENT_NAME} names ${declared.size} pools (${[...declared].sort().join(', ')}); it must name exactly one.` ); } const policyNames = new Set(capacityPolicies.map((policy) => policy.agentName)); const [declaredName] = declared; if (declaredName !== undefined) { // Every policy has to be for the pool this deployment owns. A policy for // another pool would have proactive evaluation scaling something the // deployment cannot provision into. if ([...policyNames].some((policyName) => policyName !== declaredName)) { throw new PhysicalPoolIdentityError( `${DATABASE_LIFECYCLE_TAG_AGENT_NAME} is ${declaredName} but databaseLifecycle:capacityPolicies names ${[...policyNames].sort().join(', ')}; they must name the same pool.` ); } return declaredName; } if (policyNames.size > 1) { throw new PhysicalPoolIdentityError( `databaseLifecycle:capacityPolicies names ${policyNames.size} pools; publish ${DATABASE_LIFECYCLE_TAG_AGENT_NAME} to say which one this deployment owns.` ); } const [policyName] = policyNames; return policyName; }