From 0c562f81a70d4525de8f23861ee98d2512986f25 Mon Sep 17 00:00:00 2001 From: Aditya Choudhari <48932219+adityachoudhari26@users.noreply.github.com> Date: Tue, 10 Dec 2024 09:53:56 -0800 Subject: [PATCH] fix: Trigger release for all resources when moving deployment (#257) --- .../job-dispatch/src/deployment-update.ts | 100 +++++++----------- 1 file changed, 40 insertions(+), 60 deletions(-) diff --git a/packages/job-dispatch/src/deployment-update.ts b/packages/job-dispatch/src/deployment-update.ts index d1df003c..10eb6331 100644 --- a/packages/job-dispatch/src/deployment-update.ts +++ b/packages/job-dispatch/src/deployment-update.ts @@ -1,7 +1,7 @@ import type { ResourceCondition } from "@ctrlplane/validators/resources"; import { isPresent } from "ts-is-present"; -import { and, eq, inArray, isNotNull, isNull, takeFirst } from "@ctrlplane/db"; +import { and, eq, inArray, isNotNull, takeFirst } from "@ctrlplane/db"; import { db } from "@ctrlplane/db/client"; import * as SCHEMA from "@ctrlplane/db/schema"; import { @@ -9,58 +9,60 @@ import { FilterType, } from "@ctrlplane/validators/conditions"; -import { handleEvent } from "./events/index.js"; +import { getEventsForDeploymentRemoved, handleEvent } from "./events/index.js"; import { dispatchReleaseJobTriggers } from "./job-dispatch.js"; import { isPassingReleaseStringCheckPolicy } from "./policies/release-string-check.js"; import { isPassingAllPolicies } from "./policy-checker.js"; import { createJobApprovals } from "./policy-create.js"; import { createReleaseJobTriggers } from "./release-job-trigger.js"; -const getResourcesOnlyInNewSystem = async ( - newSystemId: string, - oldSystemId: string, +const moveRunbooksLinkedToHooksToNewSystem = async ( + deployment: SCHEMA.Deployment, ) => { + const isDeploymentHook = and( + eq(SCHEMA.hook.scopeType, "deployment"), + eq(SCHEMA.hook.scopeId, deployment.id), + ); + + return db.query.hook + .findMany({ + where: isDeploymentHook, + with: { runhooks: { with: { runbook: true } } }, + }) + .then((hooks) => { + const runbookIds = hooks.flatMap((h) => + h.runhooks.map((rh) => rh.runbook.id), + ); + return db + .update(SCHEMA.runbook) + .set({ systemId: deployment.systemId }) + .where(inArray(SCHEMA.runbook.id, runbookIds)); + }); +}; + +const getResourcesInNewSystem = async (deployment: SCHEMA.Deployment) => { const hasFilter = isNotNull(SCHEMA.environment.resourceFilter); const newSystem = await db.query.system.findFirst({ - where: eq(SCHEMA.system.id, newSystemId), + where: eq(SCHEMA.system.id, deployment.systemId), with: { environments: { where: hasFilter } }, }); - const oldSystem = await db.query.system.findFirst({ - where: eq(SCHEMA.system.id, oldSystemId), - with: { environments: { where: hasFilter } }, - }); + if (newSystem == null) return []; - if (newSystem == null || oldSystem == null) return []; + const filters = newSystem.environments + .map((env) => env.resourceFilter) + .filter(isPresent); - const newSystemFilter: ResourceCondition = { - type: FilterType.Comparison, - operator: ComparisonOperator.Or, - conditions: newSystem.environments - .flatMap((env) => env.resourceFilter) - .filter(isPresent), - }; + if (filters.length === 0) return []; - const notInOldSystemFilter: ResourceCondition = { + const systemFilter: ResourceCondition = { type: FilterType.Comparison, operator: ComparisonOperator.Or, - not: true, - conditions: oldSystem.environments - .flatMap((env) => env.resourceFilter) - .filter(isPresent), - }; - - const filter: ResourceCondition = { - type: FilterType.Comparison, - operator: ComparisonOperator.And, - conditions: [newSystemFilter, notInOldSystemFilter], + conditions: filters, }; return db.query.resource.findMany({ - where: and( - SCHEMA.resourceMatchesMetadata(db, filter), - isNull(SCHEMA.resource.deletedAt), - ), + where: SCHEMA.resourceMatchesMetadata(db, systemFilter), }); }; @@ -69,35 +71,13 @@ export const handleDeploymentSystemChanged = async ( prevSystemId: string, userId?: string, ) => { - const resourcesOnlyInNewSystem = await getResourcesOnlyInNewSystem( - deployment.systemId, - prevSystemId, + await getEventsForDeploymentRemoved(deployment, prevSystemId).then((events) => + Promise.allSettled(events.map(handleEvent)), ); - const events = resourcesOnlyInNewSystem.map((resource) => ({ - action: "deployment.resource.removed" as const, - payload: { deployment, resource }, - })); - await Promise.allSettled(events.map(handleEvent)); + await moveRunbooksLinkedToHooksToNewSystem(deployment); - const isDeploymentHook = and( - eq(SCHEMA.hook.scopeType, "deployment"), - eq(SCHEMA.hook.scopeId, deployment.id), - ); - await db.query.hook - .findMany({ - where: isDeploymentHook, - with: { runhooks: { with: { runbook: true } } }, - }) - .then((hooks) => { - const runbookIds = hooks.flatMap((h) => - h.runhooks.map((rh) => rh.runbook.id), - ); - return db - .update(SCHEMA.runbook) - .set({ systemId: deployment.systemId }) - .where(inArray(SCHEMA.runbook.id, runbookIds)); - }); + const resources = await getResourcesInNewSystem(deployment); const createTriggers = userId != null @@ -105,7 +85,7 @@ export const handleDeploymentSystemChanged = async ( : createReleaseJobTriggers(db, "new_release"); await createTriggers .deployments([deployment.id]) - .resources(resourcesOnlyInNewSystem.map((r) => r.id)) + .resources(resources.map((r) => r.id)) .filter(isPassingReleaseStringCheckPolicy) .then(createJobApprovals) .insert()