lot more stuff

This commit is contained in:
Morten Olsen
2025-08-04 23:44:14 +02:00
parent daf0ea21bb
commit 757b2fcfac
185 changed files with 115899 additions and 1874 deletions

View File

@@ -1,183 +0,0 @@
import { ApiException, PatchStrategy, V1MicroTime } from '@kubernetes/client-node';
import type { Services } from '../../utils/service.ts';
import { K8sService } from '../k8s.ts';
import { GROUP } from '../../utils/consts.ts';
import { CustomResourceRegistry } from '../../custom-resource/custom-resource.registry.ts';
type ManifestOptions = {
manifest: ExpectedAny;
services: Services;
};
type ManifestMetadata = Record<string, string> & {
name: string;
namespace?: string;
labels?: Record<string, string>;
annotations?: Record<string, string>;
uid: string;
resourceVersion: string;
creationTimestamp: string;
generation: number;
};
type EventOptions = {
reason: string;
message: string;
action: string;
type: 'Normal' | 'Warning' | 'Error';
};
class Manifest<TSpec> {
#options: ManifestOptions;
constructor(options: ManifestOptions) {
this.#options = {
...options,
manifest: options.manifest,
};
}
public get objectRef() {
return {
apiVersion: this.apiVersion,
kind: this.kind,
name: this.metadata.name,
uid: this.metadata.uid,
namespace: this.metadata.namespace,
};
}
public get services(): Services {
return this.#options.services;
}
public get manifest() {
return this.#options.manifest;
}
protected set manifest(obj: ExpectedAny) {
this.#options.manifest = obj;
}
public get dependencyId() {
return `${this.metadata.uid}-${this.metadata.generation}`;
}
public get kind(): string {
return this.#options.manifest.kind;
}
public get apiVersion(): string {
return this.#options.manifest.apiVersion;
}
public get spec(): TSpec {
return this.#options.manifest.spec;
}
public get metadata(): ManifestMetadata {
return this.#options.manifest.metadata;
}
public isOwnerOf = (manifest: ExpectedAny) => {
const ownerRef = manifest?.metadata?.ownerReferences || [];
return ownerRef.some(
(ref: ExpectedAny) =>
ref.apiVersion === this.apiVersion &&
ref.kind === this.kind &&
ref.name === this.metadata.name &&
ref.uid === this.metadata.uid,
);
};
public addEvent = async (event: EventOptions) => {
const { manifest, services } = this.#options;
const k8sService = services.get(K8sService);
await k8sService.eventsApi.createNamespacedEvent({
namespace: manifest.metadata.namespace,
body: {
kind: 'Event',
metadata: {
name: `${manifest.metadata.name}-${Date.now()}-${Buffer.from(crypto.getRandomValues(new Uint8Array(8))).toString('hex')}`,
namespace: manifest.metadata.namespace,
},
eventTime: new V1MicroTime(),
note: event.message,
action: event.action,
reason: event.reason,
type: event.type,
reportingController: GROUP,
reportingInstance: manifest.metadata.name,
regarding: {
apiVersion: manifest.apiVersion,
resourceVersion: manifest.metadata.resourceVersion,
kind: manifest.kind,
name: manifest.metadata.name,
namespace: manifest.metadata.namespace,
uid: manifest.metadata.uid,
},
},
});
};
public patch = async (manifest: ExpectedAny) => {
const { services } = this.#options;
const k8sService = services.get(K8sService);
this.manifest = await k8sService.objectsApi.patch(
{
apiVersion: this.apiVersion,
kind: this.kind,
metadata: {
name: this.metadata.name,
namespace: this.metadata.namespace,
ownerReferences: this.metadata.ownerReferences,
...manifest.metadata,
labels: {
...this.metadata.labels,
...(manifest.metadata?.label || {}),
},
annotations: {
...this.metadata.annotations,
...(manifest.metadata?.annotations || {}),
},
},
spec: manifest.spec || this.spec,
},
undefined,
undefined,
undefined,
undefined,
PatchStrategy.MergePatch,
);
};
public update = async () => {
const { manifest, services } = this.#options;
const k8sService = services.get(K8sService);
const registry = services.get(CustomResourceRegistry);
const crd = registry.getByKind(manifest.kind);
if (!crd) {
throw new Error(`Custom resource ${manifest.kind} not found`);
}
try {
const resource = await k8sService.objectsApi.read({
apiVersion: this.apiVersion,
kind: this.kind,
metadata: {
name: this.metadata.name,
namespace: this.metadata.namespace,
},
});
this.#options.manifest = resource;
} catch (error) {
if (error instanceof ApiException && error.code === 404) {
return undefined;
}
throw error;
}
};
}
export { Manifest };

61
src/services/k8s/k8s.ts Normal file
View File

@@ -0,0 +1,61 @@
import {
KubeConfig,
CoreV1Api,
ApiextensionsV1Api,
CustomObjectsApi,
EventsV1Api,
KubernetesObjectApi,
ApiException,
AppsV1Api,
} from '@kubernetes/client-node';
class K8sService {
#kc: KubeConfig;
#k8sApi: CoreV1Api;
#k8sExtensionsApi: ApiextensionsV1Api;
#k8sCustomObjectsApi: CustomObjectsApi;
#k8sEventsApi: EventsV1Api;
#k8sObjectsApi: KubernetesObjectApi;
#k8sAppsApi: AppsV1Api;
constructor() {
this.#kc = new KubeConfig();
this.#kc.loadFromDefault();
this.#k8sApi = this.#kc.makeApiClient(CoreV1Api);
this.#k8sExtensionsApi = this.#kc.makeApiClient(ApiextensionsV1Api);
this.#k8sCustomObjectsApi = this.#kc.makeApiClient(CustomObjectsApi);
this.#k8sEventsApi = this.#kc.makeApiClient(EventsV1Api);
this.#k8sObjectsApi = this.#kc.makeApiClient(KubernetesObjectApi);
this.#k8sAppsApi = this.#kc.makeApiClient(AppsV1Api);
}
public get config() {
return this.#kc;
}
public get api() {
return this.#k8sApi;
}
public get extensionsApi() {
return this.#k8sExtensionsApi;
}
public get customObjectsApi() {
return this.#k8sCustomObjectsApi;
}
public get eventsApi() {
return this.#k8sEventsApi;
}
public get objectsApi() {
return this.#k8sObjectsApi;
}
public get apps() {
return this.#k8sAppsApi;
}
}
export { K8sService };