Skip to content

Commit 4f2cbfe

Browse files
committed
Try rolling updates
1 parent dbc9659 commit 4f2cbfe

5 files changed

Lines changed: 113 additions & 43 deletions

File tree

‎web/actions/projects.ts‎

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -698,11 +698,26 @@ export async function deployService(serviceId: string) {
698698
currentStage: "deploying",
699699
});
700700

701-
for (const dep of existingDeployments) {
702-
await db
703-
.delete(deploymentPorts)
704-
.where(eq(deploymentPorts.deploymentId, dep.id));
705-
await db.delete(deployments).where(eq(deployments.id, dep.id));
701+
const runningDeployments = existingDeployments.filter(
702+
(d) => d.status === "running" || d.status === "healthy",
703+
);
704+
705+
const useRollingUpdate = !service.stateful && runningDeployments.length > 0;
706+
707+
if (useRollingUpdate) {
708+
for (const dep of runningDeployments) {
709+
await db
710+
.update(deployments)
711+
.set({ status: "draining" })
712+
.where(eq(deployments.id, dep.id));
713+
}
714+
} else {
715+
for (const dep of existingDeployments) {
716+
await db
717+
.delete(deploymentPorts)
718+
.where(eq(deploymentPorts.deploymentId, dep.id));
719+
await db.delete(deployments).where(eq(deployments.id, dep.id));
720+
}
706721
}
707722

708723
const servicePortsList = await db

‎web/app/api/v1/agent/expected-state/route.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const EXPECTED_STATUSES = [
2020
"starting",
2121
"healthy",
2222
"running",
23+
"draining",
2324
"unknown",
2425
] as const;
2526

‎web/app/api/v1/agent/status/route.ts‎

Lines changed: 89 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { NextRequest, NextResponse } from "next/server";
22
import { db } from "@/db";
3-
import { deployments, servers, services, rollouts } from "@/db/schema";
3+
import {
4+
deployments,
5+
deploymentPorts,
6+
servers,
7+
services,
8+
rollouts,
9+
} from "@/db/schema";
410
import { eq, and, inArray, isNotNull, isNull } from "drizzle-orm";
511
import { verifyAgentRequest } from "@/lib/agent-auth";
612

@@ -24,6 +30,14 @@ type StatusReport = {
2430
};
2531

2632
async function checkRolloutProgress(rolloutId: string): Promise<void> {
33+
const rollout = await db
34+
.select()
35+
.from(rollouts)
36+
.where(eq(rollouts.id, rolloutId))
37+
.then((r) => r[0]);
38+
39+
if (!rollout || rollout.status !== "in_progress") return;
40+
2741
const rolloutDeployments = await db
2842
.select()
2943
.from(deployments)
@@ -41,60 +55,99 @@ async function checkRolloutProgress(rolloutId: string): Promise<void> {
4155
const allHealthy = newDeployments.every((d) => d.status === "healthy");
4256

4357
if (allHealthy) {
44-
console.log(
45-
`[rollout:${rolloutId}] all healthy → running, completing rollout`,
46-
);
58+
const serviceId = newDeployments[0].serviceId;
4759

48-
await db
49-
.update(deployments)
50-
.set({ status: "running" })
51-
.where(
52-
and(
53-
eq(deployments.rolloutId, rolloutId),
54-
eq(deployments.status, "healthy"),
55-
),
56-
);
57-
58-
await db
60+
const updated = await db
5961
.update(rollouts)
6062
.set({
6163
status: "completed",
6264
currentStage: "completed",
6365
completedAt: new Date(),
6466
})
65-
.where(eq(rollouts.id, rolloutId));
67+
.where(
68+
and(eq(rollouts.id, rolloutId), eq(rollouts.status, "in_progress")),
69+
)
70+
.returning();
71+
72+
if (updated.length > 0) {
73+
console.log(
74+
`[rollout:${rolloutId}] all healthy → running, completing rollout`,
75+
);
76+
77+
await db
78+
.update(deployments)
79+
.set({ status: "stopping" })
80+
.where(
81+
and(
82+
eq(deployments.serviceId, serviceId),
83+
eq(deployments.status, "draining"),
84+
),
85+
);
86+
87+
await db
88+
.update(deployments)
89+
.set({ status: "running" })
90+
.where(
91+
and(
92+
eq(deployments.rolloutId, rolloutId),
93+
eq(deployments.status, "healthy"),
94+
),
95+
);
96+
}
6697
}
6798
}
6899

69100
async function handleRolloutFailure(
70101
rolloutId: string,
71102
failedStage: string,
72103
): Promise<void> {
73-
await db
74-
.update(rollouts)
75-
.set({ status: "failed", currentStage: failedStage })
76-
.where(eq(rollouts.id, rolloutId));
77-
78104
const rolloutDeployments = await db
79105
.select()
80106
.from(deployments)
81107
.where(eq(deployments.rolloutId, rolloutId));
82108

83-
const newDeployments = rolloutDeployments.filter(
84-
(d) => d.status !== "running" && d.status !== "stopped",
85-
);
109+
if (rolloutDeployments.length === 0) return;
110+
111+
const serviceId = rolloutDeployments[0].serviceId;
86112

87-
for (const dep of newDeployments) {
113+
const updated = await db
114+
.update(rollouts)
115+
.set({ status: "failed", currentStage: failedStage })
116+
.where(and(eq(rollouts.id, rolloutId), eq(rollouts.status, "in_progress")))
117+
.returning();
118+
119+
if (updated.length > 0) {
88120
await db
89121
.update(deployments)
90-
.set({ status: "rolled_back", failedStage: failedStage })
91-
.where(eq(deployments.id, dep.id));
92-
}
122+
.set({ status: "running" })
123+
.where(
124+
and(
125+
eq(deployments.serviceId, serviceId),
126+
eq(deployments.status, "draining"),
127+
),
128+
);
93129

94-
await db
95-
.update(rollouts)
96-
.set({ status: "rolled_back", completedAt: new Date() })
97-
.where(eq(rollouts.id, rolloutId));
130+
await db
131+
.update(deployments)
132+
.set({ status: "rolled_back", failedStage })
133+
.where(
134+
and(
135+
eq(deployments.rolloutId, rolloutId),
136+
inArray(deployments.status, [
137+
"pending",
138+
"pulling",
139+
"starting",
140+
"healthy",
141+
"failed",
142+
]),
143+
),
144+
);
145+
146+
await db
147+
.update(rollouts)
148+
.set({ status: "rolled_back", completedAt: new Date() })
149+
.where(eq(rollouts.id, rolloutId));
150+
}
98151
}
99152

100153
export async function POST(request: NextRequest) {
@@ -184,12 +237,12 @@ export async function POST(request: NextRequest) {
184237
if (!reportedDeploymentIds.includes(dep.id)) {
185238
if (dep.status === "stopping") {
186239
console.log(
187-
`[status:${serverId.slice(0, 8)}] deployment ${dep.id.slice(0, 8)} was stopping and container gone, marking STOPPED`,
240+
`[status:${serverId.slice(0, 8)}] deployment ${dep.id.slice(0, 8)} was stopping and container gone, deleting`,
188241
);
189242
await db
190-
.update(deployments)
191-
.set({ status: "stopped", healthStatus: null, containerId: null })
192-
.where(eq(deployments.id, dep.id));
243+
.delete(deploymentPorts)
244+
.where(eq(deploymentPorts.deploymentId, dep.id));
245+
await db.delete(deployments).where(eq(deployments.id, dep.id));
193246
} else {
194247
console.log(
195248
`[status:${serverId.slice(0, 8)}] deployment ${dep.id.slice(0, 8)} NOT reported, marking UNKNOWN`,

‎web/components/ui/button.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ const buttonVariants = cva(
99
variants: {
1010
variant: {
1111
default:
12-
"bg-primary text-primary-foreground dark:bg-primary/85 dark:text-white dark:border-white/15 dark:shadow-[0_0_0_1px_rgba(255,255,255,0.12)] [a]:hover:bg-primary/80 dark:[a]:hover:bg-primary/75",
12+
"bg-primary text-primary-foreground dark:bg-primary/85 dark:text-white dark:border-white/15 [a]:hover:bg-primary/80 dark:[a]:hover:bg-primary/75",
1313
positive:
14-
"bg-emerald-500 text-white hover:bg-emerald-500/90 dark:bg-emerald-500/85 dark:hover:bg-emerald-500/80 dark:border-white/15 dark:shadow-[0_0_0_1px_rgba(255,255,255,0.12)]",
14+
"bg-emerald-500 text-white hover:bg-emerald-500/90 dark:bg-emerald-500/85 dark:hover:bg-emerald-500/80 dark:border-white/15",
1515
outline:
1616
"border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground",
1717
secondary:

‎web/db/schema.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ export const deployments = pgTable(
266266
"starting",
267267
"healthy",
268268
"running",
269+
"draining",
269270
"stopping",
270271
"stopped",
271272
"failed",

0 commit comments

Comments
 (0)