Intergrating Building

This commit is contained in:
stefan.meyer
2024-10-31 14:04:59 +00:00
parent c00dc05e3f
commit d56ecffae6
5 changed files with 129 additions and 0 deletions

View File

@@ -14,9 +14,17 @@ const getK8sAppsApiClient = () => {
return k8sCoreClient;
}
const getK8sBatchApiClient = () => {
const kc = new k8s.KubeConfig();
kc.loadFromFile('/workspace/kube-config.config'); // todo update --> use security role
const k8sJobClient = kc.makeApiClient(k8s.BatchV1Api);
return k8sJobClient;
}
declare const globalThis: {
k8sCoreGlobal: ReturnType<typeof getK8sCoreApiClient>;
k8sAppsGlobal: ReturnType<typeof getK8sAppsApiClient>;
k8sJobGlobal: ReturnType<typeof getK8sBatchApiClient>;
} & typeof global;
const k8sCoreClient = globalThis.k8sCoreGlobal ?? getK8sCoreApiClient()
@@ -26,9 +34,13 @@ if (process.env.NODE_ENV !== 'production') globalThis.k8sCoreGlobal = k8sCoreCli
const k8sAppsClient = globalThis.k8sAppsGlobal ?? getK8sAppsApiClient()
if (process.env.NODE_ENV !== 'production') globalThis.k8sAppsGlobal = k8sAppsClient
const k8sJobClient = globalThis.k8sJobGlobal ?? getK8sBatchApiClient()
if (process.env.NODE_ENV !== 'production') globalThis.k8sJobGlobal = k8sJobClient
class K3sApiAdapter {
core = k8sCoreClient;
apps = k8sAppsClient;
batch = k8sJobClient;
}
const k3s = new K3sApiAdapter();

View File

@@ -7,6 +7,7 @@ import { AppExtendedModel } from "@/model/app-extended.model";
import { ServiceException } from "@/model/service.exception.model";
import { StringUtils } from "../utils/string.utils";
import deploymentService from "./deployment.service";
import buildService, { buildNamespace } from "./build.service";
class AppService {
@@ -14,6 +15,8 @@ class AppService {
const app = await this.getExtendedById(appId);
if (app.sourceType === 'GIT') {
// first make build
await deploymentService.createNamespaceIfNotExists(buildNamespace)
await buildService.buildApp(app);
} else {
// only deploy
await deploymentService.createDeployment(app);

View File

@@ -0,0 +1,113 @@
import { AppExtendedModel } from "@/model/app-extended.model";
import k3s from "../adapter/kubernetes-api.adapter";
import { V1Deployment, V1Job } from "@kubernetes/client-node";
const kanikoImage = "gcr.io/kaniko-project/executor:latest";
export const registryURL = "registry-svc.registry-and-build.svc.cluster.local"
export const buildNamespace = "registry-and-build";
class BuildService {
async buildApp(app: AppExtendedModel) {
const jobDefinition: V1Job = {
apiVersion: "batch/v1",
kind: "Job",
metadata: {
name: `build-${app.id}`,
namespace: buildNamespace,
},
spec: {
ttlSecondsAfterFinished: 100,
template: {
spec: {
containers: [
{
name: `build-${app.id}`,
image: kanikoImage,
args: [`--dockerfile=${app.dockerfilePath}`,
`--context=${app.gitUrl!.replace("https://","git://")}#refs/heads/${app.gitBranch}`,
`--destination=${registryURL}/${app.id}`]
},
],
restartPolicy: "Never",
},
},
backoffLimit: 0,
},
};
if (app.gitUsername && app.gitToken) {
jobDefinition.spec!.template.spec!.containers[0].env = [
{
name: "GIT_USERNAME",
value: app.gitUsername
},
{
name: "GIT_PASSWORD",
value: app.gitToken
}
];
}
await k3s.batch.createNamespacedJob(buildNamespace, jobDefinition);
await this.waitForJobCompletion(buildNamespace, jobDefinition.metadata!.name!);
}
async waitForJobCompletion(namespace:string, jobName:string) {
const POLL_INTERVAL = 10000; // 10 seconds
return await new Promise<void>((resolve, reject) => {
const intervalId = setInterval(async () => {
try {
const jobStatus = await this.getJobStatus(namespace, jobName);
if (jobStatus === 'UNKNOWN') {
console.log(`Job ${jobName} not found.`);
clearInterval(intervalId);
reject(new Error(`Job ${jobName} not found.`));
return;
}
if (jobStatus === 'SUCCEEDED') {
clearInterval(intervalId);
console.log(`Job ${jobName} completed successfully.`);
resolve();
} else if (jobStatus === 'FAILED') {
clearInterval(intervalId);
console.log(`Job ${jobName} failed.`);
reject(new Error(`Job ${jobName} failed.`));
} else {
console.log(`Job ${jobName} is still running...`);
}
} catch (err) {
clearInterval(intervalId);
reject(err);
}
}, POLL_INTERVAL);
});
}
async getJobStatus(namespace:string, jobName:string): Promise<'UNKNOWN' | 'RUNNING' | 'FAILED' | 'SUCCEEDED'> {
try {
const response = await k3s.batch.readNamespacedJobStatus(jobName, namespace);
const job = response.body;
if (!job.status) {
return 'UNKNOWN';
}
if ((job.status.active ?? 0) > 0) {
return 'RUNNING';
}
if ((job.status.succeeded?? 0) > 0) {
return 'SUCCEEDED';
}
if ((job.status.failed ?? 0) > 0) {
return 'FAILED';
}
} catch (err) {
console.error(err);
}
return 'UNKNOWN';
}
}
const buildService = new BuildService();
export default buildService;