2023-03-27 11:14:23 +00:00
|
|
|
import { CoreV1Api, KubeConfig } from '@kubernetes/client-node';
|
|
|
|
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
2022-02-01 02:31:20 +00:00
|
|
|
import waitUntil from 'async-wait-until';
|
2023-03-27 11:14:23 +00:00
|
|
|
import { CloudRunnerSystem } from '../../services/core/cloud-runner-system';
|
|
|
|
import CloudRunner from '../../cloud-runner';
|
|
|
|
import KubernetesPods from './kubernetes-pods';
|
|
|
|
import { FollowLogStreamService } from '../../services/core/follow-log-stream-service';
|
2023-09-18 18:21:44 +00:00
|
|
|
import { RemoteClientLogger } from '../../remote-client/remote-client-logger';
|
2022-02-01 02:31:20 +00:00
|
|
|
|
|
|
|
class KubernetesTaskRunner {
|
2023-03-27 11:14:23 +00:00
|
|
|
static readonly maxRetry: number = 3;
|
2022-02-01 02:31:20 +00:00
|
|
|
static async runTask(
|
|
|
|
kubeConfig: KubeConfig,
|
|
|
|
kubeClient: CoreV1Api,
|
|
|
|
jobName: string,
|
|
|
|
podName: string,
|
|
|
|
containerName: string,
|
|
|
|
namespace: string,
|
|
|
|
) {
|
|
|
|
let output = '';
|
2022-05-04 23:25:17 +00:00
|
|
|
let shouldReadLogs = true;
|
|
|
|
let shouldCleanup = true;
|
2023-03-27 11:14:23 +00:00
|
|
|
let retriesAfterFinish = 0;
|
|
|
|
// eslint-disable-next-line no-constant-condition
|
|
|
|
while (true) {
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
|
|
CloudRunnerLogger.log(
|
2023-09-18 18:21:44 +00:00
|
|
|
`Streaming logs from pod: ${podName} container: ${containerName} namespace: ${namespace} ${CloudRunner.buildParameters.kubeVolumeSize}/${CloudRunner.buildParameters.containerCpu}/${CloudRunner.buildParameters.containerMemory}`,
|
2023-02-05 00:22:32 +00:00
|
|
|
);
|
2023-03-27 11:14:23 +00:00
|
|
|
let extraFlags = ``;
|
|
|
|
extraFlags += (await KubernetesPods.IsPodRunning(podName, namespace, kubeClient))
|
|
|
|
? ` -f -c ${containerName}`
|
|
|
|
: ` --previous`;
|
|
|
|
|
2023-08-14 21:35:49 +00:00
|
|
|
const callback = (outputChunk: string) => {
|
|
|
|
output += outputChunk;
|
2023-03-27 11:14:23 +00:00
|
|
|
|
2023-09-20 20:11:36 +00:00
|
|
|
// split output chunk and handle per line
|
|
|
|
for (const chunk of outputChunk.split(`\n`)) {
|
|
|
|
// check if log start included in logs if so log a message
|
|
|
|
if (chunk.includes(`Collected Logs`)) {
|
|
|
|
CloudRunnerLogger.log(`Log Start found in logs`);
|
|
|
|
}
|
|
|
|
if (chunk.includes(`LOGHASH:`)) {
|
2023-09-25 17:26:32 +00:00
|
|
|
RemoteClientLogger.HandleLogHash(chunk);
|
2023-09-20 20:11:36 +00:00
|
|
|
CloudRunnerLogger.log(`Loghash found`);
|
|
|
|
}
|
|
|
|
if (chunk.includes(`LOGS:`)) {
|
2023-10-02 22:58:34 +00:00
|
|
|
CloudRunnerLogger.log(`LOGS: found`);
|
|
|
|
|
2023-09-24 13:57:44 +00:00
|
|
|
// remove "LOGS: " and decode base64 remaining
|
|
|
|
const unpacked = Buffer.from(chunk.split(`LOGS: `)[1], 'base64').toString('ascii');
|
2023-09-25 17:26:32 +00:00
|
|
|
const result = RemoteClientLogger.HandleLogFull(unpacked);
|
2023-09-25 17:04:33 +00:00
|
|
|
CloudRunnerLogger.log(`Logs found HandleLogChunkLineResult:${result}`);
|
2023-10-02 22:58:34 +00:00
|
|
|
if (result) {
|
|
|
|
FollowLogStreamService.DidReceiveEndOfTransmission = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
2023-09-20 20:11:36 +00:00
|
|
|
}
|
2023-10-02 22:58:34 +00:00
|
|
|
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration(
|
|
|
|
chunk,
|
|
|
|
shouldReadLogs,
|
|
|
|
shouldCleanup,
|
|
|
|
output,
|
|
|
|
));
|
2023-09-20 19:33:43 +00:00
|
|
|
}
|
2023-08-14 21:35:49 +00:00
|
|
|
};
|
2023-03-27 11:14:23 +00:00
|
|
|
try {
|
2023-10-02 22:58:34 +00:00
|
|
|
await CloudRunnerSystem.Run(`kubectl logs ${podName}${extraFlags}`, false, true, callback);
|
2023-03-27 11:14:23 +00:00
|
|
|
} catch (error: any) {
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
|
|
const continueStreaming = await KubernetesPods.IsPodRunning(podName, namespace, kubeClient);
|
|
|
|
CloudRunnerLogger.log(`K8s logging error ${error} ${continueStreaming}`);
|
|
|
|
if (continueStreaming) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (retriesAfterFinish < KubernetesTaskRunner.maxRetry) {
|
|
|
|
retriesAfterFinish++;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
if (FollowLogStreamService.DidReceiveEndOfTransmission) {
|
|
|
|
CloudRunnerLogger.log('end of log stream');
|
|
|
|
break;
|
2022-02-01 02:31:20 +00:00
|
|
|
}
|
|
|
|
}
|
2022-04-11 22:43:41 +00:00
|
|
|
|
2022-02-01 02:31:20 +00:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
static async watchUntilPodRunning(kubeClient: CoreV1Api, podName: string, namespace: string) {
|
2023-06-05 21:48:19 +00:00
|
|
|
let waitComplete: boolean = false;
|
2023-03-27 11:14:23 +00:00
|
|
|
let message = ``;
|
2022-02-01 02:31:20 +00:00
|
|
|
CloudRunnerLogger.log(`Watching ${podName} ${namespace}`);
|
|
|
|
await waitUntil(
|
|
|
|
async () => {
|
|
|
|
const status = await kubeClient.readNamespacedPodStatus(podName, namespace);
|
|
|
|
const phase = status?.body.status?.phase;
|
2023-06-05 22:15:49 +00:00
|
|
|
waitComplete = phase !== 'Pending';
|
2023-03-27 11:14:23 +00:00
|
|
|
message = `Phase:${status.body.status?.phase} \n Reason:${
|
|
|
|
status.body.status?.conditions?.[0].reason || ''
|
|
|
|
} \n Message:${status.body.status?.conditions?.[0].message || ''}`;
|
|
|
|
|
|
|
|
// CloudRunnerLogger.log(
|
|
|
|
// JSON.stringify(
|
|
|
|
// (await kubeClient.listNamespacedEvent(namespace)).body.items
|
|
|
|
// .map((x) => {
|
|
|
|
// return {
|
|
|
|
// message: x.message || ``,
|
|
|
|
// name: x.metadata.name || ``,
|
|
|
|
// reason: x.reason || ``,
|
|
|
|
// };
|
|
|
|
// })
|
|
|
|
// .filter((x) => x.name.includes(podName)),
|
|
|
|
// undefined,
|
|
|
|
// 4,
|
|
|
|
// ),
|
|
|
|
// );
|
2023-06-05 21:48:19 +00:00
|
|
|
if (waitComplete || phase !== 'Pending') return true;
|
2022-04-11 22:43:41 +00:00
|
|
|
|
2022-02-01 02:31:20 +00:00
|
|
|
return false;
|
|
|
|
},
|
|
|
|
{
|
|
|
|
timeout: 2000000,
|
|
|
|
intervalBetweenAttempts: 15000,
|
|
|
|
},
|
|
|
|
);
|
2023-06-05 21:48:19 +00:00
|
|
|
if (!waitComplete) {
|
2023-03-27 11:14:23 +00:00
|
|
|
CloudRunnerLogger.log(message);
|
|
|
|
}
|
2022-04-11 22:43:41 +00:00
|
|
|
|
2023-06-05 21:48:19 +00:00
|
|
|
return waitComplete;
|
2022-02-01 02:31:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default KubernetesTaskRunner;
|