podname logs for log service

This commit is contained in:
Frostebite 2023-08-14 18:59:05 +01:00
parent 14819a6338
commit 2019e730cb
7 changed files with 10 additions and 250 deletions

BIN
dist/index.js generated vendored

Binary file not shown.

BIN
dist/index.js.map generated vendored

Binary file not shown.

View File

@ -16,7 +16,6 @@ import { ProviderResource } from '../provider-resource';
import { ProviderWorkflow } from '../provider-workflow'; import { ProviderWorkflow } from '../provider-workflow';
import { RemoteClientLogger } from '../../remote-client/remote-client-logger'; import { RemoteClientLogger } from '../../remote-client/remote-client-logger';
import { KubernetesRole } from './kubernetes-role'; import { KubernetesRole } from './kubernetes-role';
import KubernetesLogService from './kubernetes-log-service';
import { CloudRunnerSystem } from '../../services/core/cloud-runner-system'; import { CloudRunnerSystem } from '../../services/core/cloud-runner-system';
class Kubernetes implements ProviderInterface { class Kubernetes implements ProviderInterface {
@ -159,8 +158,6 @@ class Kubernetes implements ProviderInterface {
await KubernetesSecret.createSecret(secrets, this.secretName, this.namespace, this.kubeClient); await KubernetesSecret.createSecret(secrets, this.secretName, this.namespace, this.kubeClient);
let output = ''; let output = '';
try { try {
this.ip =
(await KubernetesLogService.createLogDeployment(this.namespace, this.kubeClientApps, this.kubeClient)) || ``;
CloudRunnerLogger.log('Job does not exist'); CloudRunnerLogger.log('Job does not exist');
await this.createJob(commands, image, mountdir, workingdir, environment, secrets); await this.createJob(commands, image, mountdir, workingdir, environment, secrets);
CloudRunnerLogger.log('Watching pod until running'); CloudRunnerLogger.log('Watching pod until running');
@ -271,7 +268,6 @@ class Kubernetes implements ProviderInterface {
CloudRunnerLogger.log(`Build job created`); CloudRunnerLogger.log(`Build job created`);
await new Promise((promise) => setTimeout(promise, 5000)); await new Promise((promise) => setTimeout(promise, 5000));
CloudRunnerLogger.log('Job created'); CloudRunnerLogger.log('Job created');
await KubernetesLogService.cleanupLogDeployment(this.namespace, this.kubeClientApps, this.kubeClient);
return result.body.metadata?.name; return result.body.metadata?.name;
} catch (error) { } catch (error) {
@ -292,7 +288,6 @@ class Kubernetes implements ProviderInterface {
await this.kubeClientBatch.deleteNamespacedJob(this.jobName, this.namespace); await this.kubeClientBatch.deleteNamespacedJob(this.jobName, this.namespace);
await this.kubeClient.deleteNamespacedPod(this.podName, this.namespace); await this.kubeClient.deleteNamespacedPod(this.podName, this.namespace);
await KubernetesRole.deleteRole(this.serviceAccountName, this.namespace, this.rbacAuthorizationV1Api); await KubernetesRole.deleteRole(this.serviceAccountName, this.namespace, this.rbacAuthorizationV1Api);
await KubernetesLogService.cleanupLogDeployment(this.namespace, this.kubeClientApps, this.kubeClient);
} catch (error: any) { } catch (error: any) {
CloudRunnerLogger.log(`Failed to cleanup`); CloudRunnerLogger.log(`Failed to cleanup`);
if (error.response.body.reason !== `NotFound`) { if (error.response.body.reason !== `NotFound`) {

View File

@ -1,158 +0,0 @@
import { CoreV1Api } from '@kubernetes/client-node';
import * as k8s from '@kubernetes/client-node';
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import { CloudRunnerSystem } from '../../services/core/cloud-runner-system';
class KubernetesLogService {
static async cleanupLogDeployment(namespace: string, kubeClientApps: k8s.AppsV1Api, kubeClient: CoreV1Api) {
if (!process.env.LOG_SERVICE_IP) {
return Promise.resolve();
}
await kubeClient.deleteNamespacedService('http-fileserver', namespace);
await kubeClientApps.deleteNamespacedDeployment('http-fileserver', namespace);
}
static async createLogService(serviceAccountName: string, namespace: string, kubeClient: CoreV1Api) {
const serviceAccount = new k8s.V1ServiceAccount();
serviceAccount.apiVersion = 'v1';
serviceAccount.kind = 'ServiceAccount';
serviceAccount.metadata = {
name: serviceAccountName,
};
serviceAccount.automountServiceAccountToken = true;
return kubeClient.createNamespacedServiceAccount(namespace, serviceAccount);
}
static async createLogDeployment(namespace: string, kubeClient: k8s.AppsV1Api, kubeClientCore: CoreV1Api) {
if (!process.env.LOG_SERVICE_IP) {
return `0.0.0.0`;
}
// create a deployment with above json
const deployment = new k8s.V1Deployment();
deployment.apiVersion = 'apps/v1';
deployment.kind = 'Deployment';
deployment.metadata = {
name: 'http-fileserver',
labels: {
service: 'http-fileserver',
},
};
deployment.spec = {
selector: {
matchLabels: {
service: 'http-fileserver',
},
},
replicas: 1,
strategy: {},
template: {
metadata: {
labels: {
service: 'http-fileserver',
},
},
spec: {
containers: [
{
image: 'node:18',
imagePullPolicy: 'Always',
name: 'http-fileserver',
ports: [{ containerPort: 80 }],
command: [
'bash',
'-c',
'while true; do sleep 30; npm i files-upload-server -g; files-upload-server "downloads"; done;',
],
resources: {
requests: {
memory: '750M',
cpu: '0.25',
},
},
},
],
restartPolicy: 'Always',
},
},
};
await kubeClient.createNamespacedDeployment(namespace, deployment);
await this.createLogServiceExpose(namespace, kubeClientCore);
// wait in loop until serivce ip address is exposed
for (let index = 0; index < 10; index++) {
// wait for service to share ip address
await new Promise((resolve) => setTimeout(resolve, 10000));
// get cluster ip address of service
const service = await kubeClientCore.readNamespacedService('http-fileserver', namespace);
// get podname of deployment
const podname = await CloudRunnerSystem.Run(
`kubectl get pods -n ${namespace} -l service=http-fileserver -o jsonpath='{.items[0].metadata.name}'`,
false,
true,
);
// if status of pod is not running, then continue
const podStatus = await CloudRunnerSystem.Run(
`kubectl get pods -n ${namespace} ${podname} -o jsonpath='{.status.phase}'`,
false,
true,
);
if (podStatus !== 'Running') {
CloudRunnerLogger.log(`Pod status: ${podStatus}`);
continue;
}
const logs = await CloudRunnerSystem.Run(`kubectl logs ${podname} -f --timestamps -p`, false, true);
CloudRunnerLogger.log(`Logs: ${logs}`);
// get cluster ip
const ip = service.body?.spec?.clusterIP;
if (ip && ip.length > 0) {
// log service json
CloudRunnerLogger.log(`Service: ${JSON.stringify(service.body, undefined, 4)}`);
CloudRunnerLogger.log(`Service IP: ${ip}`);
return ip;
}
}
}
// create kubernetes service to expose deployment
static async createLogServiceExpose(namespace: string, kubeClient: CoreV1Api) {
if (!process.env.LOG_SERVICE_IP) {
return;
}
// create a service with above json
const service = new k8s.V1Service();
service.apiVersion = 'v1';
service.kind = 'Service';
service.metadata = {
name: 'http-fileserver',
labels: {
service: 'http-fileserver',
},
};
service.spec = {
ports: [
{
name: '80-80',
port: 80,
protocol: 'TCP',
targetPort: 80,
},
],
selector: {
service: 'http-fileserver',
},
type: 'LoadBalancer',
};
await kubeClient.createNamespacedService(namespace, service);
}
}
export default KubernetesLogService;

View File

@ -83,7 +83,7 @@ export class RemoteClient {
await RemoteClient.runCustomHookFiles(`after-build`); await RemoteClient.runCustomHookFiles(`after-build`);
// WIP - need to give the pod permissions to create config map // WIP - need to give the pod permissions to create config map
await RemoteClientLogger.printCollectedLogs(); await RemoteClientLogger.handleLogManagementPostJob();
return new Promise((result) => result(``)); return new Promise((result) => result(``));
} }

View File

@ -3,7 +3,6 @@ import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import CloudRunner from '../cloud-runner'; import CloudRunner from '../cloud-runner';
import CloudRunnerOptions from '../options/cloud-runner-options'; import CloudRunnerOptions from '../options/cloud-runner-options';
import Kubernetes from '../providers/k8s';
export class RemoteClientLogger { export class RemoteClientLogger {
private static get LogFilePath() { private static get LogFilePath() {
@ -34,14 +33,20 @@ export class RemoteClientLogger {
} }
} }
public static async printCollectedLogs() { public static async handleLogManagementPostJob() {
if (CloudRunnerOptions.providerStrategy !== 'k8s') { if (CloudRunnerOptions.providerStrategy !== 'k8s') {
return; return;
} }
CloudRunnerLogger.log(`Collected Logs`); CloudRunnerLogger.log(`Collected Logs`);
CloudRunnerLogger.log(process.env[`LOG_SERVICE_IP`] || ``); const hashedLogs = fs.readFileSync(RemoteClientLogger.LogFilePath).toString();
CloudRunnerLogger.log(hashedLogs);
const logs = fs.readFileSync(RemoteClientLogger.LogFilePath).toString(); const logs = fs.readFileSync(RemoteClientLogger.LogFilePath).toString();
CloudRunnerLogger.log(logs); CloudRunnerLogger.log(logs);
await Kubernetes.Instance.PushLogUpdate(logs);
// loop for 5 mins logging the logs every minute
for (let index = 0; index < 5; index++) {
await new Promise((resolve) => setTimeout(resolve, 60000));
CloudRunnerLogger.log(logs);
}
} }
} }

View File

@ -1,82 +0,0 @@
import BuildParameters from '../../build-parameters';
import { Cli } from '../../cli/cli';
import UnityVersioning from '../../unity-versioning';
import CloudRunner from '../cloud-runner';
import CloudRunnerOptions from '../options/cloud-runner-options';
import KubernetesLogService from '../providers/k8s/kubernetes-log-service';
import CloudRunnerLogger from '../services/core/cloud-runner-logger';
import setups from './cloud-runner-suite.test';
import { v4 as uuidv4 } from 'uuid';
import * as k8s from '@kubernetes/client-node';
import ImageTag from '../../image-tag';
async function CreateParameters(overrides: any) {
if (overrides) {
Cli.options = overrides;
}
return await BuildParameters.create();
}
describe('Cloud Runner Kubernetes', () => {
it('Responds', () => {});
setups();
if (CloudRunnerOptions.cloudRunnerDebug) {
it('Build create log service', async () => {
const overrides = {
versioning: 'None',
projectPath: 'test-project',
unityVersion: UnityVersioning.determineUnityVersion('test-project', UnityVersioning.read('test-project')),
targetPlatform: 'StandaloneLinux64',
cacheKey: `test-case-${uuidv4()}`,
customJob: `
- name: 'step 1'
image: 'ubuntu'
commands: 'curl http://$LOG_SERVICE_IP:80''`,
};
if (CloudRunnerOptions.providerStrategy !== `k8s`) {
return;
}
const buildParameter = await CreateParameters(overrides);
expect(buildParameter.projectPath).toEqual(overrides.projectPath);
await CloudRunner.setup(buildParameter);
const kubeConfig = new k8s.KubeConfig();
kubeConfig.loadFromDefault();
const kubeClient = kubeConfig.makeApiClient(k8s.CoreV1Api);
const kubeClientApps = kubeConfig.makeApiClient(k8s.AppsV1Api);
await KubernetesLogService.createLogDeployment('test', kubeClientApps, kubeClient);
CloudRunnerLogger.log(`run 1 succeeded`);
}, 1_000_000_000);
it('curl log service', async () => {
const overrides = {
versioning: 'None',
projectPath: 'test-project',
unityVersion: UnityVersioning.read('test-project'),
targetPlatform: 'StandaloneLinux64',
cacheKey: `test-case-${uuidv4()}`,
customJob: `
- name: 'step 1'
image: 'ubuntu'
commands: |
apt-get update
apt-get install curl -y
curl http://$LOG_SERVICE_IP:80`,
};
if (CloudRunnerOptions.providerStrategy !== `k8s`) {
return;
}
const buildParameter = await CreateParameters(overrides);
expect(buildParameter.projectPath).toEqual(overrides.projectPath);
const baseImage = new ImageTag(buildParameter);
const results = await CloudRunner.run(buildParameter, baseImage.toString());
const buildSucceededString = 'Build succeeded';
expect(results).toContain(buildSucceededString);
CloudRunnerLogger.log(`run 1 succeeded`);
}, 1_000_000_000);
}
});