mirror of
https://github.com/actions/runner-container-hooks.git
synced 2025-12-20 06:42:34 +00:00
Bump all dependencies (#234)
* Bump all dependencies * build and reformat * lint * format
This commit is contained in:
@@ -5,7 +5,8 @@ import {
|
||||
JobContainerInfo,
|
||||
ContextPorts,
|
||||
PrepareJobArgs,
|
||||
writeToResponseFile
|
||||
writeToResponseFile,
|
||||
ServiceContainerInfo
|
||||
} from 'hooklib'
|
||||
import path from 'path'
|
||||
import {
|
||||
@@ -167,7 +168,9 @@ function generateResponseFile(
|
||||
const ctxPorts: ContextPorts = {}
|
||||
if (c.ports?.length) {
|
||||
for (const port of c.ports) {
|
||||
ctxPorts[port.containerPort] = port.hostPort
|
||||
if (port.containerPort && port.hostPort) {
|
||||
ctxPorts[port.containerPort.toString()] = port.hostPort.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +196,7 @@ async function copyExternalsToRoot(): Promise<void> {
|
||||
}
|
||||
|
||||
export function createContainerSpec(
|
||||
container: JobContainerInfo,
|
||||
container: JobContainerInfo | ServiceContainerInfo,
|
||||
name: string,
|
||||
jobContainer = false,
|
||||
extension?: k8s.V1PodTemplateSpec
|
||||
@@ -208,24 +211,24 @@ export function createContainerSpec(
|
||||
image: container.image,
|
||||
ports: containerPorts(container)
|
||||
} as k8s.V1Container
|
||||
if (container.workingDirectory) {
|
||||
podContainer.workingDir = container.workingDirectory
|
||||
if (container['workingDirectory']) {
|
||||
podContainer.workingDir = container['workingDirectory']
|
||||
}
|
||||
|
||||
if (container.entryPoint) {
|
||||
podContainer.command = [container.entryPoint]
|
||||
}
|
||||
|
||||
if (container.entryPointArgs?.length > 0) {
|
||||
if (container.entryPointArgs && container.entryPointArgs.length > 0) {
|
||||
podContainer.args = fixArgs(container.entryPointArgs)
|
||||
}
|
||||
|
||||
podContainer.env = []
|
||||
for (const [key, value] of Object.entries(
|
||||
container['environmentVariables']
|
||||
container['environmentVariables'] || {}
|
||||
)) {
|
||||
if (value && key !== 'HOME') {
|
||||
podContainer.env.push({ name: key, value: value as string })
|
||||
podContainer.env.push({ name: key, value })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +237,7 @@ export function createContainerSpec(
|
||||
value: 'true'
|
||||
})
|
||||
|
||||
if (!('CI' in container['environmentVariables'])) {
|
||||
if (!('CI' in (container['environmentVariables'] || {}))) {
|
||||
podContainer.env.push({
|
||||
name: 'CI',
|
||||
value: 'true'
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import * as core from '@actions/core'
|
||||
import { Command, getInputFromStdin, prepareJobArgs } from 'hooklib'
|
||||
import {
|
||||
Command,
|
||||
getInputFromStdin,
|
||||
PrepareJobArgs,
|
||||
RunContainerStepArgs,
|
||||
RunScriptStepArgs
|
||||
} from 'hooklib'
|
||||
import {
|
||||
cleanupJob,
|
||||
prepareJob,
|
||||
@@ -27,16 +33,16 @@ async function run(): Promise<void> {
|
||||
let exitCode = 0
|
||||
switch (command) {
|
||||
case Command.PrepareJob:
|
||||
await prepareJob(args as prepareJobArgs, responseFile)
|
||||
await prepareJob(args as PrepareJobArgs, responseFile)
|
||||
return process.exit(0)
|
||||
case Command.CleanupJob:
|
||||
await cleanupJob()
|
||||
return process.exit(0)
|
||||
case Command.RunScriptStep:
|
||||
await runScriptStep(args, state, null)
|
||||
await runScriptStep(args as RunScriptStepArgs, state, null)
|
||||
return process.exit(0)
|
||||
case Command.RunContainerStep:
|
||||
exitCode = await runContainerStep(args)
|
||||
exitCode = await runContainerStep(args as RunContainerStepArgs)
|
||||
return process.exit(exitCode)
|
||||
default:
|
||||
throw new Error(`Command not recognized: ${command}`)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as k8s from '@kubernetes/client-node'
|
||||
import { ContainerInfo, Registry } from 'hooklib'
|
||||
import * as stream from 'stream'
|
||||
import type { ContainerInfo, Registry } from 'hooklib'
|
||||
import {
|
||||
getJobPodName,
|
||||
getRunnerPodName,
|
||||
@@ -127,8 +127,10 @@ export async function createPod(
|
||||
mergePodSpecWithOptions(appPod.spec, extension.spec)
|
||||
}
|
||||
|
||||
const { body } = await k8sApi.createNamespacedPod(namespace(), appPod)
|
||||
return body
|
||||
return await k8sApi.createNamespacedPod({
|
||||
namespace: namespace(),
|
||||
body: appPod
|
||||
})
|
||||
}
|
||||
|
||||
export async function createJob(
|
||||
@@ -183,46 +185,42 @@ export async function createJob(
|
||||
}
|
||||
}
|
||||
|
||||
const { body } = await k8sBatchV1Api.createNamespacedJob(namespace(), job)
|
||||
return body
|
||||
return await k8sBatchV1Api.createNamespacedJob({
|
||||
namespace: namespace(),
|
||||
body: job
|
||||
})
|
||||
}
|
||||
|
||||
export async function getContainerJobPodName(jobName: string): Promise<string> {
|
||||
const selector = `job-name=${jobName}`
|
||||
const backOffManager = new BackOffManager(60)
|
||||
while (true) {
|
||||
const podList = await k8sApi.listNamespacedPod(
|
||||
namespace(),
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
selector,
|
||||
1
|
||||
)
|
||||
const podList = await k8sApi.listNamespacedPod({
|
||||
namespace: namespace(),
|
||||
labelSelector: selector,
|
||||
limit: 1
|
||||
})
|
||||
|
||||
if (!podList.body.items?.length) {
|
||||
if (!podList.items?.length) {
|
||||
await backOffManager.backOff()
|
||||
continue
|
||||
}
|
||||
|
||||
if (!podList.body.items[0].metadata?.name) {
|
||||
if (!podList.items[0].metadata?.name) {
|
||||
throw new Error(
|
||||
`Failed to determine the name of the pod for job ${jobName}`
|
||||
)
|
||||
}
|
||||
return podList.body.items[0].metadata.name
|
||||
return podList.items[0].metadata.name
|
||||
}
|
||||
}
|
||||
|
||||
export async function deletePod(podName: string): Promise<void> {
|
||||
await k8sApi.deleteNamespacedPod(
|
||||
podName,
|
||||
namespace(),
|
||||
undefined,
|
||||
undefined,
|
||||
0
|
||||
)
|
||||
export async function deletePod(name: string): Promise<void> {
|
||||
await k8sApi.deleteNamespacedPod({
|
||||
name,
|
||||
namespace: namespace(),
|
||||
gracePeriodSeconds: 0
|
||||
})
|
||||
}
|
||||
|
||||
export async function execPodStep(
|
||||
@@ -261,7 +259,6 @@ export async function execPodStep(
|
||||
}
|
||||
)
|
||||
// If exec.exec fails, explicitly reject the outer promise
|
||||
// eslint-disable-next-line github/no-then
|
||||
.catch(e => reject(e))
|
||||
})
|
||||
}
|
||||
@@ -274,7 +271,7 @@ export async function waitForJobToComplete(jobName: string): Promise<void> {
|
||||
return
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`job ${jobName} has failed`)
|
||||
throw new Error(`job ${jobName} has failed: ${JSON.stringify(error)}`)
|
||||
}
|
||||
await backOffManager.backOff()
|
||||
}
|
||||
@@ -315,8 +312,10 @@ export async function createDockerSecret(
|
||||
)
|
||||
}
|
||||
|
||||
const { body } = await k8sApi.createNamespacedSecret(namespace(), secret)
|
||||
return body
|
||||
return await k8sApi.createNamespacedSecret({
|
||||
namespace: namespace(),
|
||||
body: secret
|
||||
})
|
||||
}
|
||||
|
||||
export async function createSecretForEnvs(envs: {
|
||||
@@ -340,30 +339,33 @@ export async function createSecretForEnvs(envs: {
|
||||
secret.data[key] = Buffer.from(value).toString('base64')
|
||||
}
|
||||
|
||||
await k8sApi.createNamespacedSecret(namespace(), secret)
|
||||
await k8sApi.createNamespacedSecret({
|
||||
namespace: namespace(),
|
||||
body: secret
|
||||
})
|
||||
return secretName
|
||||
}
|
||||
|
||||
export async function deleteSecret(secretName: string): Promise<void> {
|
||||
await k8sApi.deleteNamespacedSecret(secretName, namespace())
|
||||
export async function deleteSecret(name: string): Promise<void> {
|
||||
await k8sApi.deleteNamespacedSecret({
|
||||
name,
|
||||
namespace: namespace()
|
||||
})
|
||||
}
|
||||
|
||||
export async function pruneSecrets(): Promise<void> {
|
||||
const secretList = await k8sApi.listNamespacedSecret(
|
||||
namespace(),
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
new RunnerInstanceLabel().toString()
|
||||
)
|
||||
if (!secretList.body.items.length) {
|
||||
const secretList = await k8sApi.listNamespacedSecret({
|
||||
namespace: namespace(),
|
||||
labelSelector: new RunnerInstanceLabel().toString()
|
||||
})
|
||||
if (!secretList.items.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
secretList.body.items.map(
|
||||
secret => secret.metadata?.name && deleteSecret(secret.metadata.name)
|
||||
secretList.items.map(
|
||||
async secret =>
|
||||
secret.metadata?.name && (await deleteSecret(secret.metadata.name))
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -391,7 +393,9 @@ export async function waitForPodPhases(
|
||||
await backOffManager.backOff()
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Pod ${podName} is unhealthy with phase status ${phase}`)
|
||||
throw new Error(
|
||||
`Pod ${podName} is unhealthy with phase status ${phase}: ${JSON.stringify(error)}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,7 +418,7 @@ export function getPrepareJobTimeoutSeconds(): number {
|
||||
return timeoutSeconds
|
||||
}
|
||||
|
||||
async function getPodPhase(podName: string): Promise<PodPhase> {
|
||||
async function getPodPhase(name: string): Promise<PodPhase> {
|
||||
const podPhaseLookup = new Set<string>([
|
||||
PodPhase.PENDING,
|
||||
PodPhase.RUNNING,
|
||||
@@ -422,8 +426,10 @@ async function getPodPhase(podName: string): Promise<PodPhase> {
|
||||
PodPhase.FAILED,
|
||||
PodPhase.UNKNOWN
|
||||
])
|
||||
const { body } = await k8sApi.readNamespacedPod(podName, namespace())
|
||||
const pod = body
|
||||
const pod = await k8sApi.readNamespacedPod({
|
||||
name,
|
||||
namespace: namespace()
|
||||
})
|
||||
|
||||
if (!pod.status?.phase || !podPhaseLookup.has(pod.status.phase)) {
|
||||
return PodPhase.UNKNOWN
|
||||
@@ -431,11 +437,13 @@ async function getPodPhase(podName: string): Promise<PodPhase> {
|
||||
return pod.status?.phase as PodPhase
|
||||
}
|
||||
|
||||
async function isJobSucceeded(jobName: string): Promise<boolean> {
|
||||
const { body } = await k8sBatchV1Api.readNamespacedJob(jobName, namespace())
|
||||
const job = body
|
||||
async function isJobSucceeded(name: string): Promise<boolean> {
|
||||
const job = await k8sBatchV1Api.readNamespacedJob({
|
||||
name,
|
||||
namespace: namespace()
|
||||
})
|
||||
if (job.status?.failed) {
|
||||
throw new Error(`job ${jobName} has failed`)
|
||||
throw new Error(`job ${name} has failed`)
|
||||
}
|
||||
return !!job.status?.succeeded
|
||||
}
|
||||
@@ -455,30 +463,26 @@ export async function getPodLogs(
|
||||
process.stderr.write(err.message)
|
||||
})
|
||||
|
||||
const r = await log.log(namespace(), podName, containerName, logStream, {
|
||||
await log.log(namespace(), podName, containerName, logStream, {
|
||||
follow: true,
|
||||
pretty: false,
|
||||
timestamps: false
|
||||
})
|
||||
await new Promise(resolve => r.on('close', () => resolve(null)))
|
||||
await new Promise(resolve => logStream.on('end', () => resolve(null)))
|
||||
}
|
||||
|
||||
export async function prunePods(): Promise<void> {
|
||||
const podList = await k8sApi.listNamespacedPod(
|
||||
namespace(),
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
new RunnerInstanceLabel().toString()
|
||||
)
|
||||
if (!podList.body.items.length) {
|
||||
const podList = await k8sApi.listNamespacedPod({
|
||||
namespace: namespace(),
|
||||
labelSelector: new RunnerInstanceLabel().toString()
|
||||
})
|
||||
if (!podList.items.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
podList.body.items.map(
|
||||
pod => pod.metadata?.name && deletePod(pod.metadata.name)
|
||||
podList.items.map(
|
||||
async pod => pod.metadata?.name && (await deletePod(pod.metadata.name))
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -486,16 +490,16 @@ export async function prunePods(): Promise<void> {
|
||||
export async function getPodStatus(
|
||||
name: string
|
||||
): Promise<k8s.V1PodStatus | undefined> {
|
||||
const { body } = await k8sApi.readNamespacedPod(name, namespace())
|
||||
return body.status
|
||||
const pod = await k8sApi.readNamespacedPod({
|
||||
name,
|
||||
namespace: namespace()
|
||||
})
|
||||
return pod.status
|
||||
}
|
||||
|
||||
export async function isAuthPermissionsOK(): Promise<boolean> {
|
||||
const sar = new k8s.V1SelfSubjectAccessReview()
|
||||
const asyncs: Promise<{
|
||||
response: unknown
|
||||
body: k8s.V1SelfSubjectAccessReview
|
||||
}>[] = []
|
||||
const asyncs: Promise<k8s.V1SelfSubjectAccessReview>[] = []
|
||||
for (const resource of requiredPermissions) {
|
||||
for (const verb of resource.verbs) {
|
||||
sar.spec = new k8s.V1SelfSubjectAccessReviewSpec()
|
||||
@@ -505,11 +509,13 @@ export async function isAuthPermissionsOK(): Promise<boolean> {
|
||||
sar.spec.resourceAttributes.group = resource.group
|
||||
sar.spec.resourceAttributes.resource = resource.resource
|
||||
sar.spec.resourceAttributes.subresource = resource.subresource
|
||||
asyncs.push(k8sAuthorizationV1Api.createSelfSubjectAccessReview(sar))
|
||||
asyncs.push(
|
||||
k8sAuthorizationV1Api.createSelfSubjectAccessReview({ body: sar })
|
||||
)
|
||||
}
|
||||
}
|
||||
const responses = await Promise.all(asyncs)
|
||||
return responses.every(resp => resp.body.status?.allowed)
|
||||
return responses.every(resp => resp.status?.allowed)
|
||||
}
|
||||
|
||||
export async function isPodContainerAlpine(
|
||||
@@ -527,7 +533,7 @@ export async function isPodContainerAlpine(
|
||||
podName,
|
||||
containerName
|
||||
)
|
||||
} catch (err) {
|
||||
} catch {
|
||||
isAlpine = false
|
||||
}
|
||||
|
||||
@@ -535,9 +541,12 @@ export async function isPodContainerAlpine(
|
||||
}
|
||||
|
||||
async function getCurrentNodeName(): Promise<string> {
|
||||
const resp = await k8sApi.readNamespacedPod(getRunnerPodName(), namespace())
|
||||
const resp = await k8sApi.readNamespacedPod({
|
||||
name: getRunnerPodName(),
|
||||
namespace: namespace()
|
||||
})
|
||||
|
||||
const nodeName = resp.body.spec?.nodeName
|
||||
const nodeName = resp.spec?.nodeName
|
||||
if (!nodeName) {
|
||||
throw new Error('Failed to determine node name')
|
||||
}
|
||||
@@ -647,6 +656,8 @@ export function containerPorts(
|
||||
}
|
||||
|
||||
export async function getPodByName(name): Promise<k8s.V1Pod> {
|
||||
const { body } = await k8sApi.readNamespacedPod(name, namespace())
|
||||
return body
|
||||
return await k8sApi.readNamespacedPod({
|
||||
name,
|
||||
namespace: namespace()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ exec ${environmentPrefix} ${entryPoint} ${
|
||||
|
||||
export function generateContainerName(image: string): string {
|
||||
const nameWithTag = image.split('/').pop()
|
||||
const name = nameWithTag?.split(':').at(0)
|
||||
const name = nameWithTag?.split(':')[0]
|
||||
|
||||
if (!name) {
|
||||
throw new Error(`Image definition '${image}' is invalid`)
|
||||
|
||||
Reference in New Issue
Block a user