| 
									
										
										
										
											2021-05-23 13:31:02 +00:00
										 |  |  | import * as k8s from '@kubernetes/client-node'; | 
					
						
							| 
									
										
										
										
											2021-09-22 20:05:21 +00:00
										 |  |  | import { BuildParameters } from '../..'; | 
					
						
							| 
									
										
										
										
											2021-05-28 17:54:54 +00:00
										 |  |  | import * as core from '@actions/core'; | 
					
						
							| 
									
										
										
										
											2021-10-04 23:23:49 +00:00
										 |  |  | import { CloudRunnerProviderInterface } from '../services/cloud-runner-provider-interface'; | 
					
						
							|  |  |  | import CloudRunnerSecret from '../services/cloud-runner-secret'; | 
					
						
							| 
									
										
										
										
											2021-06-18 19:52:07 +00:00
										 |  |  | import KubernetesStorage from './kubernetes-storage'; | 
					
						
							| 
									
										
										
										
											2021-10-04 23:23:49 +00:00
										 |  |  | import CloudRunnerEnvironmentVariable from '../services/cloud-runner-environment-variable'; | 
					
						
							| 
									
										
										
										
											2021-12-29 17:25:38 +00:00
										 |  |  | import KubernetesTaskRunner from './kubernetes-task-runner'; | 
					
						
							| 
									
										
										
										
											2021-06-19 23:06:44 +00:00
										 |  |  | import KubernetesSecret from './kubernetes-secret'; | 
					
						
							| 
									
										
										
										
											2021-06-26 04:01:43 +00:00
										 |  |  | import waitUntil from 'async-wait-until'; | 
					
						
							| 
									
										
										
										
											2021-06-26 01:50:03 +00:00
										 |  |  | import KubernetesJobSpecFactory from './kubernetes-job-spec-factory'; | 
					
						
							| 
									
										
										
										
											2021-07-13 00:28:16 +00:00
										 |  |  | import KubernetesServiceAccount from './kubernetes-service-account'; | 
					
						
							| 
									
										
										
										
											2021-10-04 23:23:49 +00:00
										 |  |  | import CloudRunnerLogger from '../services/cloud-runner-logger'; | 
					
						
							| 
									
										
										
										
											2021-12-29 17:25:38 +00:00
										 |  |  | import { CoreV1Api } from '@kubernetes/client-node'; | 
					
						
							| 
									
										
										
										
											2021-06-18 19:27:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-17 20:09:42 +00:00
										 |  |  | class Kubernetes implements CloudRunnerProviderInterface { | 
					
						
							| 
									
										
										
										
											2021-06-19 23:06:44 +00:00
										 |  |  |   private kubeConfig: k8s.KubeConfig; | 
					
						
							| 
									
										
										
										
											2021-06-06 19:39:06 +00:00
										 |  |  |   private kubeClient: k8s.CoreV1Api; | 
					
						
							|  |  |  |   private kubeClientBatch: k8s.BatchV1Api; | 
					
						
							| 
									
										
										
										
											2021-08-15 21:59:58 +00:00
										 |  |  |   private buildGuid: string = ''; | 
					
						
							| 
									
										
										
										
											2021-06-06 19:39:06 +00:00
										 |  |  |   private buildParameters: BuildParameters; | 
					
						
							| 
									
										
										
										
											2021-06-06 21:22:22 +00:00
										 |  |  |   private pvcName: string = ''; | 
					
						
							|  |  |  |   private secretName: string = ''; | 
					
						
							|  |  |  |   private jobName: string = ''; | 
					
						
							| 
									
										
										
										
											2021-06-06 19:39:06 +00:00
										 |  |  |   private namespace: string; | 
					
						
							| 
									
										
										
										
											2021-06-06 20:14:12 +00:00
										 |  |  |   private podName: string = ''; | 
					
						
							|  |  |  |   private containerName: string = ''; | 
					
						
							| 
									
										
										
										
											2021-06-26 01:50:03 +00:00
										 |  |  |   private cleanupCronJobName: string = ''; | 
					
						
							| 
									
										
										
										
											2021-07-13 00:28:16 +00:00
										 |  |  |   private serviceAccountName: string = ''; | 
					
						
							| 
									
										
										
										
											2021-03-13 23:44:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-19 20:35:22 +00:00
										 |  |  |   constructor(buildParameters: BuildParameters) { | 
					
						
							| 
									
										
										
										
											2021-06-26 01:50:03 +00:00
										 |  |  |     this.kubeConfig = new k8s.KubeConfig(); | 
					
						
							|  |  |  |     this.kubeConfig.loadFromDefault(); | 
					
						
							|  |  |  |     this.kubeClient = this.kubeConfig.makeApiClient(k8s.CoreV1Api); | 
					
						
							|  |  |  |     this.kubeClientBatch = this.kubeConfig.makeApiClient(k8s.BatchV1Api); | 
					
						
							| 
									
										
										
										
											2021-09-21 18:27:04 +00:00
										 |  |  |     CloudRunnerLogger.log('Loaded default Kubernetes configuration for this environment'); | 
					
						
							| 
									
										
										
										
											2021-05-23 13:31:02 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-06 21:22:22 +00:00
										 |  |  |     this.namespace = 'default'; | 
					
						
							|  |  |  |     this.buildParameters = buildParameters; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-12-25 20:10:12 +00:00
										 |  |  |   public async setupSharedResources( | 
					
						
							| 
									
										
										
										
											2021-08-15 21:59:58 +00:00
										 |  |  |     buildGuid: string, | 
					
						
							| 
									
										
										
										
											2021-06-19 22:15:44 +00:00
										 |  |  |     buildParameters: BuildParameters, | 
					
						
							|  |  |  |     // eslint-disable-next-line no-unused-vars
 | 
					
						
							|  |  |  |     branchName: string, | 
					
						
							|  |  |  |     // eslint-disable-next-line no-unused-vars
 | 
					
						
							|  |  |  |     defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], | 
					
						
							|  |  |  |   ) { | 
					
						
							| 
									
										
										
										
											2021-06-26 01:54:37 +00:00
										 |  |  |     try { | 
					
						
							| 
									
										
										
										
											2021-09-08 20:21:15 +00:00
										 |  |  |       this.pvcName = `unity-builder-pvc-${buildGuid}`; | 
					
						
							| 
									
										
										
										
											2021-08-15 21:59:58 +00:00
										 |  |  |       this.cleanupCronJobName = `unity-builder-cronjob-${buildGuid}`; | 
					
						
							|  |  |  |       this.serviceAccountName = `service-account-${buildGuid}`; | 
					
						
							| 
									
										
										
										
											2021-06-26 01:54:37 +00:00
										 |  |  |       await KubernetesStorage.createPersistentVolumeClaim( | 
					
						
							|  |  |  |         buildParameters, | 
					
						
							|  |  |  |         this.pvcName, | 
					
						
							|  |  |  |         this.kubeClient, | 
					
						
							|  |  |  |         this.namespace, | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2021-07-13 00:28:16 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       await KubernetesServiceAccount.createServiceAccount(this.serviceAccountName, this.namespace, this.kubeClient); | 
					
						
							| 
									
										
										
										
											2021-06-26 01:54:37 +00:00
										 |  |  |     } catch (error) { | 
					
						
							|  |  |  |       throw error; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-19 22:15:44 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-25 20:10:12 +00:00
										 |  |  |   async runTask( | 
					
						
							| 
									
										
										
										
											2021-08-15 21:59:58 +00:00
										 |  |  |     buildGuid: string, | 
					
						
							| 
									
										
										
										
											2021-06-18 20:36:45 +00:00
										 |  |  |     image: string, | 
					
						
							| 
									
										
										
										
											2021-12-29 16:28:42 +00:00
										 |  |  |     commands: string, | 
					
						
							| 
									
										
										
										
											2021-06-18 20:36:45 +00:00
										 |  |  |     mountdir: string, | 
					
						
							|  |  |  |     workingdir: string, | 
					
						
							| 
									
										
										
										
											2021-08-17 22:13:46 +00:00
										 |  |  |     environment: CloudRunnerEnvironmentVariable[], | 
					
						
							|  |  |  |     secrets: CloudRunnerSecret[], | 
					
						
							| 
									
										
										
										
											2021-12-30 20:25:28 +00:00
										 |  |  |   ): Promise<string> { | 
					
						
							| 
									
										
										
										
											2021-06-18 20:36:45 +00:00
										 |  |  |     try { | 
					
						
							|  |  |  |       // setup
 | 
					
						
							| 
									
										
										
										
											2021-08-15 21:59:58 +00:00
										 |  |  |       this.buildGuid = buildGuid; | 
					
						
							|  |  |  |       this.secretName = `build-credentials-${buildGuid}`; | 
					
						
							|  |  |  |       this.jobName = `unity-builder-job-${buildGuid}`; | 
					
						
							| 
									
										
										
										
											2021-09-15 03:35:57 +00:00
										 |  |  |       this.containerName = `main`; | 
					
						
							| 
									
										
										
										
											2021-06-19 23:06:44 +00:00
										 |  |  |       await KubernetesSecret.createSecret(secrets, this.secretName, this.namespace, this.kubeClient); | 
					
						
							| 
									
										
										
										
											2021-06-26 01:50:03 +00:00
										 |  |  |       const jobSpec = KubernetesJobSpecFactory.getJobSpec( | 
					
						
							|  |  |  |         commands, | 
					
						
							|  |  |  |         image, | 
					
						
							|  |  |  |         mountdir, | 
					
						
							|  |  |  |         workingdir, | 
					
						
							|  |  |  |         environment, | 
					
						
							| 
									
										
										
										
											2021-12-29 20:49:13 +00:00
										 |  |  |         secrets, | 
					
						
							| 
									
										
										
										
											2021-08-15 21:59:58 +00:00
										 |  |  |         this.buildGuid, | 
					
						
							| 
									
										
										
										
											2021-06-26 01:50:03 +00:00
										 |  |  |         this.buildParameters, | 
					
						
							|  |  |  |         this.secretName, | 
					
						
							|  |  |  |         this.pvcName, | 
					
						
							|  |  |  |         this.jobName, | 
					
						
							|  |  |  |         k8s, | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2021-06-18 20:36:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-19 04:27:24 +00:00
										 |  |  |       //run
 | 
					
						
							| 
									
										
										
										
											2022-01-30 17:21:38 +00:00
										 |  |  |       const jobResult = await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec); | 
					
						
							| 
									
										
										
										
											2022-01-30 18:55:49 +00:00
										 |  |  |       CloudRunnerLogger.log(`Creating build job ${JSON.stringify(jobResult.body.metadata, undefined, 4)}`); | 
					
						
							| 
									
										
										
										
											2022-01-30 16:00:03 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-29 22:39:09 +00:00
										 |  |  |       await new Promise((promise) => setTimeout(promise, 5000)); | 
					
						
							| 
									
										
										
										
											2021-09-21 18:27:04 +00:00
										 |  |  |       CloudRunnerLogger.log('Job created'); | 
					
						
							| 
									
										
										
										
											2021-12-29 17:25:38 +00:00
										 |  |  |       this.setPodNameAndContainerName(await Kubernetes.findPodFromJob(this.kubeClient, this.jobName, this.namespace)); | 
					
						
							| 
									
										
										
										
											2021-09-21 18:27:04 +00:00
										 |  |  |       CloudRunnerLogger.log('Watching pod until running'); | 
					
						
							| 
									
										
										
										
											2021-12-30 21:00:38 +00:00
										 |  |  |       let output = ''; | 
					
						
							|  |  |  |       // eslint-disable-next-line no-constant-condition
 | 
					
						
							|  |  |  |       while (true) { | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |           await KubernetesTaskRunner.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace); | 
					
						
							|  |  |  |           CloudRunnerLogger.log('Pod running, streaming logs'); | 
					
						
							|  |  |  |           output = await KubernetesTaskRunner.runTask( | 
					
						
							|  |  |  |             this.kubeConfig, | 
					
						
							|  |  |  |             this.kubeClient, | 
					
						
							|  |  |  |             this.jobName, | 
					
						
							|  |  |  |             this.podName, | 
					
						
							|  |  |  |             'main', | 
					
						
							|  |  |  |             this.namespace, | 
					
						
							|  |  |  |             CloudRunnerLogger.log, | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         } catch (error: any) { | 
					
						
							|  |  |  |           if (error.message.includes(`HTTP`)) { | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             throw error; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-06-19 23:06:44 +00:00
										 |  |  |       await this.cleanupTaskResources(); | 
					
						
							| 
									
										
										
										
											2021-12-30 20:25:28 +00:00
										 |  |  |       return output; | 
					
						
							| 
									
										
										
										
											2021-06-18 20:36:45 +00:00
										 |  |  |     } catch (error) { | 
					
						
							| 
									
										
										
										
											2021-09-21 18:27:04 +00:00
										 |  |  |       CloudRunnerLogger.log('Running job failed'); | 
					
						
							| 
									
										
										
										
											2021-06-19 20:35:22 +00:00
										 |  |  |       core.error(JSON.stringify(error, undefined, 4)); | 
					
						
							| 
									
										
										
										
											2021-06-19 23:06:44 +00:00
										 |  |  |       await this.cleanupTaskResources(); | 
					
						
							| 
									
										
										
										
											2021-06-06 20:10:01 +00:00
										 |  |  |       throw error; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-05-23 14:26:57 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-06 21:22:22 +00:00
										 |  |  |   setPodNameAndContainerName(pod: k8s.V1Pod) { | 
					
						
							|  |  |  |     this.podName = pod.metadata?.name || ''; | 
					
						
							|  |  |  |     this.containerName = pod.status?.containerStatuses?.[0].name || ''; | 
					
						
							| 
									
										
										
										
											2021-05-28 17:37:30 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-19 23:06:44 +00:00
										 |  |  |   async cleanupTaskResources() { | 
					
						
							| 
									
										
										
										
											2021-09-21 18:27:04 +00:00
										 |  |  |     CloudRunnerLogger.log('cleaning up'); | 
					
						
							| 
									
										
										
										
											2022-01-30 17:21:38 +00:00
										 |  |  |     try { | 
					
						
							|  |  |  |       await this.kubeClientBatch.deleteNamespacedJob(this.jobName, this.namespace); | 
					
						
							|  |  |  |       await this.kubeClient.deleteNamespacedPod(this.podName, this.namespace); | 
					
						
							|  |  |  |       await this.kubeClient.deleteNamespacedSecret(this.secretName, this.namespace); | 
					
						
							| 
									
										
										
										
											2022-01-30 21:11:13 +00:00
										 |  |  |       await new Promise((promise) => setTimeout(promise, 5000)); | 
					
						
							| 
									
										
										
										
											2022-01-30 17:21:38 +00:00
										 |  |  |     } catch (error) { | 
					
						
							|  |  |  |       CloudRunnerLogger.log('Failed to cleanup, error:'); | 
					
						
							|  |  |  |       core.error(JSON.stringify(error, undefined, 4)); | 
					
						
							|  |  |  |       CloudRunnerLogger.log('Abandoning cleanup, build error:'); | 
					
						
							|  |  |  |       throw error; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-26 04:01:43 +00:00
										 |  |  |     try { | 
					
						
							|  |  |  |       await waitUntil( | 
					
						
							| 
									
										
										
										
											2022-01-30 15:41:45 +00:00
										 |  |  |         async () => { | 
					
						
							|  |  |  |           const jobBody = (await this.kubeClientBatch.readNamespacedJob(this.jobName, this.namespace)).body; | 
					
						
							| 
									
										
										
										
											2022-01-30 18:47:54 +00:00
										 |  |  |           const podBody = (await this.kubeClient.readNamespacedPod(this.podName, this.namespace)).body; | 
					
						
							|  |  |  |           return (jobBody === null || jobBody.status?.active === 0) && podBody === null; | 
					
						
							| 
									
										
										
										
											2022-01-30 15:41:45 +00:00
										 |  |  |         }, | 
					
						
							| 
									
										
										
										
											2021-06-26 04:01:43 +00:00
										 |  |  |         { | 
					
						
							|  |  |  |           timeout: 500000, | 
					
						
							|  |  |  |           intervalBetweenAttempts: 15000, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2022-01-12 23:15:24 +00:00
										 |  |  |       // eslint-disable-next-line no-empty
 | 
					
						
							|  |  |  |     } catch {} | 
					
						
							| 
									
										
										
										
											2021-05-23 15:08:32 +00:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-06-19 23:06:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-25 20:10:12 +00:00
										 |  |  |   async cleanupSharedResources( | 
					
						
							| 
									
										
										
										
											2021-06-19 23:06:44 +00:00
										 |  |  |     // eslint-disable-next-line no-unused-vars
 | 
					
						
							| 
									
										
										
										
											2021-08-15 21:59:58 +00:00
										 |  |  |     buildGuid: string, | 
					
						
							| 
									
										
										
										
											2021-06-19 23:06:44 +00:00
										 |  |  |     // eslint-disable-next-line no-unused-vars
 | 
					
						
							|  |  |  |     buildParameters: BuildParameters, | 
					
						
							|  |  |  |     // eslint-disable-next-line no-unused-vars
 | 
					
						
							|  |  |  |     branchName: string, | 
					
						
							|  |  |  |     // eslint-disable-next-line no-unused-vars
 | 
					
						
							|  |  |  |     defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], | 
					
						
							|  |  |  |   ) { | 
					
						
							| 
									
										
										
										
											2022-01-30 18:47:54 +00:00
										 |  |  |     CloudRunnerLogger.log(`deleting PVC`); | 
					
						
							| 
									
										
										
										
											2022-01-30 20:46:29 +00:00
										 |  |  |     this.kubeClient.deleteNamespacedPersistentVolumeClaim(this.pvcName, this.namespace); | 
					
						
							| 
									
										
										
										
											2022-01-30 21:09:58 +00:00
										 |  |  |     CloudRunnerLogger.log(`deleted PVC`); | 
					
						
							| 
									
										
										
										
											2021-06-19 23:06:44 +00:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-12-29 17:25:38 +00:00
										 |  |  |   static async findPodFromJob(kubeClient: CoreV1Api, jobName: string, namespace: string) { | 
					
						
							|  |  |  |     const namespacedPods = await kubeClient.listNamespacedPod(namespace); | 
					
						
							|  |  |  |     const pod = namespacedPods.body.items.find((x) => x.metadata?.labels?.['job-name'] === jobName); | 
					
						
							|  |  |  |     if (pod === undefined) { | 
					
						
							|  |  |  |       throw new Error("pod with job-name label doesn't exist"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return pod; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-08-09 19:27:47 +00:00
										 |  |  | } | 
					
						
							|  |  |  | export default Kubernetes; |