feat: add commands for managing deployments and resources

- Implemented `down` command to delete managed resources, with an option to delete ingress and PVCs.
- Added `exec` command to execute commands inside deployment pods.
- Created `logs` command to show deployment logs with an option to follow the output.
- Introduced `start` command to start deployments without rebuilding images.
- Developed `stop` command to scale managed deployments to zero.

refactor: enhance resource management and Kubernetes interactions

- Refactored resource handling in `lib/apply.ts` to support sorting and applying resources.
- Improved logging functionality with a new logger in `lib/logger.ts`.
- Enhanced build process in `lib/build.ts` to support remote builds and syncing with Git repositories.
- Added functions to manage deployments and pods in `lib/shared.ts`.

chore: update dependencies and TypeScript configuration

- Updated package.json to include `listr2` for task management.
- Modified tsconfig.json to exclude build artifacts and fixtures.
This commit is contained in:
2026-08-09 00:18:14 +07:00 Unverified
parent c08fb36d2e
commit 8f331d4cc7
17 changed files with 1299 additions and 50 deletions
+119
View File
@@ -0,0 +1,119 @@
import { PatchStrategy } from "@kubernetes/client-node";
import type { KubernetesObject } from "@kubernetes/client-node";
import { LABELS } from "../const";
import { objectApi } from "./k8s";
const FIELD_MANAGER = "kuber";
const ResourceOrder = {
Namespace: 0,
PersistentVolumeClaim: 1,
Secret: 2,
Service: 3,
Deployment: 4,
Ingress: 5,
} as const;
const ManagedResources = [
{ apiVersion: "v1", kind: "PersistentVolumeClaim" },
{ apiVersion: "v1", kind: "Secret" },
{ apiVersion: "v1", kind: "Service" },
{ apiVersion: "apps/v1", kind: "Deployment" },
{ apiVersion: "networking.k8s.io/v1", kind: "Ingress" },
] as const;
const ManagedBySelector = `app.kubernetes.io/managed-by=${LABELS["app.kubernetes.io/managed-by"]}`;
function getResourceOrder(resource: KubernetesObject): number {
return ResourceOrder[resource.kind as keyof typeof ResourceOrder] ?? 999;
}
export function sortResources<T extends KubernetesObject>(resources: T[]): T[] {
return [...resources].sort(
(left, right) => getResourceOrder(left) - getResourceOrder(right),
);
}
export function getResourceKey(resource: KubernetesObject): string {
return `${resource.kind}:${resource.metadata?.namespace ?? ""}:${resource.metadata?.name ?? ""}`;
}
export async function applyResource<T extends KubernetesObject>(
resource: T,
): Promise<T> {
return objectApi.patch(
resource,
undefined,
undefined,
FIELD_MANAGER,
true,
PatchStrategy.ServerSideApply,
);
}
export async function applyResources<T extends KubernetesObject>(
resources: T[],
): Promise<T[]> {
const applied: T[] = [];
for (const resource of sortResources(resources)) {
applied.push(await applyResource(resource));
}
return applied;
}
export async function deleteResource(resource: KubernetesObject): Promise<void> {
await objectApi.delete(resource);
}
export async function deleteResources(resources: KubernetesObject[]): Promise<void> {
for (const resource of sortResources(resources).reverse()) {
await deleteResource(resource);
}
}
export async function listManagedResources(
namespace: string,
): Promise<KubernetesObject[]> {
const resources = await Promise.all(
ManagedResources.map(async ({ apiVersion, kind }) => {
const list = await objectApi.list(
apiVersion,
kind,
namespace,
undefined,
undefined,
undefined,
undefined,
ManagedBySelector,
);
return list.items.map((item) => ({
...item,
apiVersion: item.apiVersion ?? apiVersion,
kind: item.kind ?? kind,
metadata: {
...item.metadata,
namespace: item.metadata?.namespace ?? namespace,
},
}));
}),
);
return resources.flat();
}
export async function getStaleResources(
namespace: string,
desiredResources: KubernetesObject[],
): Promise<KubernetesObject[]> {
const desired = new Set(
desiredResources
.filter((resource) => resource.kind !== "Namespace")
.map(getResourceKey),
);
const existing = await listManagedResources(namespace);
return existing.filter((resource) => !desired.has(getResourceKey(resource)));
}