Fix working directory and write state for appPod to be used in run-script-step (#8)

* added initial entrypoint script

* change workingg directory working with addition to fix prepare-job state output

* added prepend path

* added run-script-step file generation, removed prepend path from container-step and prepare job

* latest changes with testing run script step

* fix the mounts real fast

* cleanup

* fix tests

* add kind test

* add kind yaml to ignore and run it during ci

* fix kind option

* remove gitignore

* lowercase pwd

* checkout first!

* ignore test file in build.yaml

* fixed wrong working directory and added test to run script step testing for the env

* handle env's/escaping better

* added single quote escape to env escapes

* surounded env value with single quote

* added spacing around run-container-step, changed examples to actually echo hello world

* refactored tests

* make sure to escape properly

* set addition mounts for container steps

* fixup container action mounts

Co-authored-by: Thomas Boop <thboop@github.com>
Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>
This commit is contained in:
Nikola Jokic
2022-06-15 03:41:49 +02:00
committed by GitHub
parent 643bf36fd8
commit 8ea57170d8
23 changed files with 391 additions and 230 deletions

View File

@@ -100,8 +100,13 @@ function generateResponseFile(
appPod: k8s.V1Pod,
isAlpine
): void {
if (!appPod.metadata?.name) {
throw new Error('app pod must have metadata.name specified')
}
const response = {
state: {},
state: {
jobPod: appPod.metadata.name
},
context: {},
isAlpine
}
@@ -163,13 +168,11 @@ function createPodSpec(
name: string,
jobContainer = false
): k8s.V1Container {
if (!container.entryPointArgs) {
container.entryPointArgs = DEFAULT_CONTAINER_ENTRY_POINT_ARGS
}
container.entryPointArgs = DEFAULT_CONTAINER_ENTRY_POINT_ARGS
if (!container.entryPoint) {
container.entryPoint = DEFAULT_CONTAINER_ENTRY_POINT
container.entryPointArgs = DEFAULT_CONTAINER_ENTRY_POINT_ARGS
}
const podContainer = {
name,
image: container.image,

View File

@@ -1,5 +1,5 @@
import * as k8s from '@kubernetes/client-node'
import * as core from '@actions/core'
import * as k8s from '@kubernetes/client-node'
import { RunContainerStepArgs } from 'hooklib'
import {
createJob,
@@ -10,8 +10,14 @@ import {
waitForJobToComplete,
waitForPodPhases
} from '../k8s'
import {
containerVolumes,
DEFAULT_CONTAINER_ENTRY_POINT,
DEFAULT_CONTAINER_ENTRY_POINT_ARGS,
PodPhase,
writeEntryPointScript
} from '../k8s/utils'
import { JOB_CONTAINER_NAME } from './constants'
import { containerVolumes, PodPhase } from '../k8s/utils'
export async function runContainerStep(
stepContainer: RunContainerStepArgs
@@ -19,13 +25,16 @@ export async function runContainerStep(
if (stepContainer.dockerfile) {
throw new Error('Building container actions is not currently supported')
}
let secretName: string | undefined = undefined
core.debug('')
if (stepContainer.environmentVariables) {
secretName = await createSecretForEnvs(stepContainer.environmentVariables)
}
core.debug(`Created secret ${secretName} for container job envs`)
const container = createPodSpec(stepContainer, secretName)
const job = await createJob(container)
if (!job.metadata?.name) {
throw new Error(
@@ -35,6 +44,7 @@ export async function runContainerStep(
)
}
core.debug(`Job created, waiting for pod to start: ${job.metadata?.name}`)
const podName = await getContainerJobPodName(job.metadata.name)
await waitForPodPhases(
podName,
@@ -42,11 +52,16 @@ export async function runContainerStep(
new Set([PodPhase.PENDING, PodPhase.UNKNOWN])
)
core.debug('Container step is running or complete, pulling logs')
await getPodLogs(podName, JOB_CONTAINER_NAME)
core.debug('Waiting for container job to complete')
await waitForJobToComplete(job.metadata.name)
// pod has failed so pull the status code from the container
const status = await getPodStatus(podName)
if (status?.phase === 'Succeeded') {
return 0
}
if (!status?.containerStatuses?.length) {
core.error(
`Can't determine container status from response: ${JSON.stringify(
@@ -68,9 +83,18 @@ function createPodSpec(
const podContainer = new k8s.V1Container()
podContainer.name = JOB_CONTAINER_NAME
podContainer.image = container.image
if (container.entryPoint) {
podContainer.command = [container.entryPoint, ...container.entryPointArgs]
}
const { entryPoint, entryPointArgs } = container
container.entryPoint = 'sh'
const { containerPath } = writeEntryPointScript(
container.workingDirectory,
entryPoint || DEFAULT_CONTAINER_ENTRY_POINT,
entryPoint ? entryPointArgs || [] : DEFAULT_CONTAINER_ENTRY_POINT_ARGS
)
container.entryPointArgs = ['-e', containerPath]
podContainer.command = [container.entryPoint, ...container.entryPointArgs]
if (secretName) {
podContainer.envFrom = [
{
@@ -81,7 +105,7 @@ function createPodSpec(
}
]
}
podContainer.volumeMounts = containerVolumes()
podContainer.volumeMounts = containerVolumes(undefined, false, true)
return podContainer
}

View File

@@ -1,38 +1,35 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import * as fs from 'fs'
import { RunScriptStepArgs } from 'hooklib'
import { execPodStep } from '../k8s'
import { getJobPodName, JOB_CONTAINER_NAME } from './constants'
import { writeEntryPointScript } from '../k8s/utils'
import { JOB_CONTAINER_NAME } from './constants'
export async function runScriptStep(
args: RunScriptStepArgs,
state,
responseFile
): Promise<void> {
const cb = new CommandsBuilder(
args.entryPoint,
args.entryPointArgs,
args.environmentVariables
const { entryPoint, entryPointArgs, environmentVariables } = args
const { containerPath, runnerPath } = writeEntryPointScript(
args.workingDirectory,
entryPoint,
entryPointArgs,
args.prependPath,
environmentVariables
)
await execPodStep(cb.command, getJobPodName(), JOB_CONTAINER_NAME)
}
class CommandsBuilder {
constructor(
private entryPoint: string,
private entryPointArgs: string[],
private environmentVariables: { [key: string]: string }
) {}
get command(): string[] {
const envCommands: string[] = []
if (
this.environmentVariables &&
Object.entries(this.environmentVariables).length
) {
for (const [key, value] of Object.entries(this.environmentVariables)) {
envCommands.push(`${key}=${value}`)
}
}
return ['env', ...envCommands, this.entryPoint, ...this.entryPointArgs]
args.entryPoint = 'sh'
args.entryPointArgs = ['-e', containerPath]
try {
await execPodStep(
[args.entryPoint, ...args.entryPointArgs],
state.jobPod,
JOB_CONTAINER_NAME
)
} catch (err) {
throw new Error(`failed to run script step: ${JSON.stringify(err)}`)
} finally {
fs.rmSync(runnerPath)
}
}