feat: prepare 2.6.1-rc5 shared databases and build SSE
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import type { KubernetesObject, V1Deployment } from "@kubernetes/client-node";
|
||||
import { DatabaseReconciliationError } from "../../lib/database";
|
||||
import {
|
||||
createManagementService,
|
||||
type ManagementDependencies,
|
||||
@@ -67,6 +68,27 @@ function dependencies(
|
||||
}
|
||||
|
||||
describe("server management service", () => {
|
||||
test.each([
|
||||
["namespace precheck", { readNamespace: async () => { throw new Error("token=private"); } }],
|
||||
["database dependency", { reconcileDatabases: async () => { throw new Error("token=private"); } }],
|
||||
["database resource listing", { listDatabaseResources: async () => { throw new Error("token=private"); } }],
|
||||
["database resource ownership", { applyResource: async () => { throw new Error("token=private"); }, listDatabaseResources: async () => [object("Database", "DB_PASSWORD_private123", "db-uid")] }],
|
||||
] as const)("labels database %s failures without leaking details", async (phase, overrides) => {
|
||||
const service = createManagementService(dependencies(overrides));
|
||||
let failure: unknown;
|
||||
try { await service.reconcileDatabases(workspace, { services: {} }); }
|
||||
catch (error) { failure = error; }
|
||||
expect(failure).toBeInstanceOf(DatabaseReconciliationError);
|
||||
expect((failure as DatabaseReconciliationError).phase).toBe(phase);
|
||||
expect((failure as Error).message).not.toContain("private");
|
||||
});
|
||||
|
||||
test("preserves an existing database substep rather than masking it", async () => {
|
||||
const service = createManagementService(dependencies({
|
||||
reconcileDatabases: async () => { throw new DatabaseReconciliationError("database apply", new Error("private")); },
|
||||
}));
|
||||
await expect(service.reconcileDatabases(workspace, { services: {} })).rejects.toMatchObject({ phase: "database apply" });
|
||||
});
|
||||
test("waits on deployments together and cancels siblings after a crash", async () => {
|
||||
const started: string[] = [];
|
||||
let siblingCancelled = false;
|
||||
@@ -344,14 +366,18 @@ describe("server management service", () => {
|
||||
namespace: "database",
|
||||
},
|
||||
};
|
||||
const service = createManagementService(
|
||||
dependencies({ listDatabaseResources: async () => [database] }),
|
||||
);
|
||||
const deleted: ResourceIdentity[] = [];
|
||||
const service = createManagementService(dependencies({
|
||||
listDatabaseResources: async () => [database],
|
||||
deleteResource: async (identity) => { deleted.push(identity); },
|
||||
}));
|
||||
const plan = await service.planDown(workspace, true);
|
||||
expect(plan.delete.map(({ kind, uid }) => [kind, uid])).toEqual([
|
||||
["Database", "database-uid"],
|
||||
["Namespace", "namespace-uid"],
|
||||
]);
|
||||
expect(plan.retained.map(({ kind, uid }) => [kind, uid])).toEqual([["Database", "database-uid"]]);
|
||||
await service.down(workspace, true);
|
||||
expect(deleted.map(({ kind }) => kind)).toEqual(["Namespace"]);
|
||||
|
||||
const missingUid = createManagementService(
|
||||
dependencies({
|
||||
@@ -368,6 +394,87 @@ describe("server management service", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("preserves foreign and owned database CRs on full down while deleting other resources", async () => {
|
||||
const shared = { ...object("Database", "sastify-store", "shared-uid", {
|
||||
"app.kubernetes.io/managed-by": "kuber", [WORKSPACE_PROJECT_LABEL]: workspace.project,
|
||||
[WORKSPACE_UID_LABEL]: "other-workspace",
|
||||
}), metadata: { ...object("Database", "sastify-store", "shared-uid").metadata,
|
||||
namespace: "database", labels: {
|
||||
"app.kubernetes.io/managed-by": "kuber", [WORKSPACE_PROJECT_LABEL]: workspace.project,
|
||||
[WORKSPACE_UID_LABEL]: "other-workspace",
|
||||
} } };
|
||||
const owned = { ...object("Database", "local", "local-uid"), metadata: {
|
||||
...object("Database", "local", "local-uid").metadata, namespace: "database",
|
||||
} };
|
||||
const deleted: string[] = [];
|
||||
const service = createManagementService(dependencies({
|
||||
listProjectResources: async () => [object("Deployment", "api", "deployment-uid")],
|
||||
listDatabaseResources: async () => [shared, owned],
|
||||
deleteResource: async (identity) => { deleted.push(identity.kind); },
|
||||
}));
|
||||
const plan = await service.down(workspace, true);
|
||||
expect(plan.delete.map(({ kind }) => kind)).toEqual(["Deployment", "Namespace"]);
|
||||
expect(plan.retained.map(({ name }) => name)).toEqual(["local"]);
|
||||
expect(deleted).toEqual(["Deployment", "Namespace"]);
|
||||
});
|
||||
|
||||
test("reuses declared foreign database without taking its workspace UID", async () => {
|
||||
const db = { ...object("Database", "sastify-store", "shared-uid", {
|
||||
"app.kubernetes.io/managed-by": "kuber", [WORKSPACE_PROJECT_LABEL]: workspace.project,
|
||||
[WORKSPACE_UID_LABEL]: "other-workspace",
|
||||
}), metadata: {
|
||||
...object("Database", "sastify-store", "shared-uid").metadata,
|
||||
namespace: "database",
|
||||
labels: { "app.kubernetes.io/managed-by": "kuber",
|
||||
[WORKSPACE_PROJECT_LABEL]: workspace.project, [WORKSPACE_UID_LABEL]: "other-workspace" },
|
||||
}, spec: { owner: "sastify", cluster: { name: "postgres" } } };
|
||||
const before = structuredClone(db);
|
||||
const applied: KubernetesObject[] = [];
|
||||
const service = createManagementService(dependencies({
|
||||
readNamespace: async (project) => ({ uid: "namespace-uid", labels: {
|
||||
"app.kubernetes.io/managed-by": "kuber",
|
||||
[WORKSPACE_UID_LABEL]: project === workspace.project ? workspace.uid : "second-uid",
|
||||
} }),
|
||||
listDatabaseResources: async () => [db],
|
||||
applyResource: async (resource) => { applied.push(resource); return resource; },
|
||||
}));
|
||||
const compose = { services: { web: { volumes: ["postgresql:sastify/sastify-store"] } } } as never;
|
||||
for (const project of [workspace, { project: "second-project", uid: "second-uid" }]) {
|
||||
await service.reconcileDatabases(project, compose);
|
||||
}
|
||||
expect(applied).toEqual([]);
|
||||
expect(db).toEqual(before);
|
||||
await expect(service.reconcileDatabases(workspace, { services: {} })).rejects.toMatchObject({ phase: "database resource ownership" });
|
||||
expect(applied).toEqual([]);
|
||||
});
|
||||
|
||||
test("adopts unowned and same-workspace databases and rejects foreign conflicting specs", async () => {
|
||||
const resources: KubernetesObject[] = [
|
||||
{ ...object("Database", "new-db", "new-uid"), metadata: {
|
||||
...object("Database", "new-db", "new-uid").metadata,
|
||||
namespace: "database", labels: { "app.kubernetes.io/managed-by": "kuber" },
|
||||
} },
|
||||
{ ...object("Database", "owned-db", "owned-uid"), metadata: {
|
||||
...object("Database", "owned-db", "owned-uid").metadata, namespace: "database",
|
||||
} },
|
||||
];
|
||||
const applied: KubernetesObject[] = [];
|
||||
const service = createManagementService(dependencies({
|
||||
listDatabaseResources: async () => resources,
|
||||
applyResource: async (resource) => { applied.push(resource); return resource; },
|
||||
}));
|
||||
await service.reconcileDatabases(workspace, { services: {} });
|
||||
expect(applied.map((resource) => resource.metadata?.labels?.[WORKSPACE_UID_LABEL])).toEqual([workspace.uid, workspace.uid]);
|
||||
resources[0] = { ...resources[0]!, metadata: { ...resources[0]!.metadata,
|
||||
labels: { [WORKSPACE_UID_LABEL]: "other-workspace" } }, spec: {
|
||||
owner: "wrong", cluster: { name: "postgres" },
|
||||
} } as KubernetesObject;
|
||||
await expect(service.reconcileDatabases(workspace, { services: { web: {
|
||||
volumes: ["postgresql:sastify/new-db"],
|
||||
} } } as never)).rejects.toMatchObject({ phase: "database resource ownership" });
|
||||
expect(applied).toHaveLength(2);
|
||||
});
|
||||
|
||||
test("stop deletes matching HPAs before scaling selected deployments", async () => {
|
||||
const calls: string[] = [];
|
||||
const service = createManagementService(
|
||||
|
||||
Reference in New Issue
Block a user