157 lines
4.0 KiB
TypeScript
157 lines
4.0 KiB
TypeScript
import type { V1Deployment, V1Pod } from "@kubernetes/client-node";
|
|
import { LABELS } from "../const";
|
|
import { ctx } from "./context";
|
|
import { apps, core, objectApi } from "./k8s";
|
|
import { waitForRollout } from "./rollout";
|
|
|
|
const ManagedBySelector = `app.kubernetes.io/managed-by=${LABELS["app.kubernetes.io/managed-by"]}`;
|
|
|
|
export function getProject() {
|
|
return ctx().project;
|
|
}
|
|
|
|
export async function listDeployments(labelSelector?: string) {
|
|
const { items } = await apps.listNamespacedDeployment({
|
|
namespace: getProject(),
|
|
labelSelector,
|
|
});
|
|
return items;
|
|
}
|
|
|
|
export function listManagedDeployments() {
|
|
return listDeployments(ManagedBySelector);
|
|
}
|
|
|
|
export async function getDeployment(name: string): Promise<V1Deployment> {
|
|
const deployment = await apps.readNamespacedDeployment({
|
|
namespace: getProject(),
|
|
name,
|
|
});
|
|
return deployment;
|
|
}
|
|
|
|
export async function listPodsForDeployment(name: string): Promise<V1Pod[]> {
|
|
const { items } = await core.listNamespacedPod({
|
|
namespace: getProject(),
|
|
labelSelector: `app=${name}`,
|
|
});
|
|
return items;
|
|
}
|
|
|
|
export async function getPodForDeployment(name: string): Promise<V1Pod> {
|
|
const pods = await listPodsForDeployment(name);
|
|
const runningPod = pods.find((pod) => pod.status?.phase === "Running");
|
|
if (runningPod) return runningPod;
|
|
const pod = pods[0];
|
|
if (!pod) throw new Error(`No pod found for deployment ${name}`);
|
|
return pod;
|
|
}
|
|
|
|
export function getPodContainerName(pod: V1Pod): string {
|
|
const container = pod.spec?.containers[0]?.name;
|
|
if (!container)
|
|
throw new Error(`No container found in pod ${pod.metadata?.name}`);
|
|
return container;
|
|
}
|
|
|
|
export async function scaleDeployment(name: string, replicas: number) {
|
|
return objectApi.patch({
|
|
apiVersion: "apps/v1",
|
|
kind: "Deployment",
|
|
metadata: {
|
|
name,
|
|
namespace: getProject(),
|
|
},
|
|
spec: {
|
|
replicas,
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function restartDeployment(name: string) {
|
|
return objectApi.patch({
|
|
apiVersion: "apps/v1",
|
|
kind: "Deployment",
|
|
metadata: {
|
|
name,
|
|
namespace: getProject(),
|
|
},
|
|
spec: {
|
|
template: {
|
|
metadata: {
|
|
annotations: {
|
|
"kubectl.kubernetes.io/restartedAt": new Date().toISOString(),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
function delay(ms: number) {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|
|
|
|
export async function waitForDeploymentRollout(
|
|
name: string,
|
|
timeoutMs = 300000,
|
|
) {
|
|
return waitForRollout(
|
|
name,
|
|
Date.now() + timeoutMs,
|
|
() => getDeployment(name),
|
|
async () =>
|
|
(await apps.listNamespacedReplicaSet({ namespace: getProject() })).items,
|
|
async () =>
|
|
(await core.listNamespacedPod({ namespace: getProject() })).items,
|
|
);
|
|
}
|
|
|
|
export function throwWhen<T, Narrowed extends T>(
|
|
condition: (arg: T) => arg is Narrowed,
|
|
message?: string,
|
|
): (arg: T) => Exclude<T, Narrowed>;
|
|
export function throwWhen<T>(
|
|
condition: (arg: T) => unknown,
|
|
message?: string,
|
|
): (arg: T) => T;
|
|
export function throwWhen<T>(condition: (arg: T) => unknown, message?: string) {
|
|
return (arg: T) => {
|
|
if (condition(arg)) throw new Error(message ?? "Condition matched");
|
|
return arg;
|
|
};
|
|
}
|
|
|
|
export function deepMerge(target: any, source: any): any {
|
|
if (
|
|
typeof target !== "object" ||
|
|
target === null ||
|
|
typeof source !== "object" ||
|
|
source === null
|
|
) {
|
|
return source;
|
|
}
|
|
|
|
const result = Array.isArray(target) ? [...target] : { ...target };
|
|
|
|
for (const key of Object.keys(source)) {
|
|
const targetValue = result[key];
|
|
const sourceValue = source[key];
|
|
|
|
if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
|
|
result[key] = [...targetValue, ...sourceValue]; // or use a custom strategy
|
|
} else if (
|
|
targetValue &&
|
|
typeof targetValue === "object" &&
|
|
sourceValue &&
|
|
typeof sourceValue === "object"
|
|
) {
|
|
result[key] = deepMerge(targetValue, sourceValue);
|
|
} else {
|
|
result[key] = sourceValue;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|