Skip to content

Commit

Permalink
refactor: break up runAndCopy
Browse files Browse the repository at this point in the history
This function was getting too long to easily read.
  • Loading branch information
thsig authored and edvald committed Feb 3, 2021
1 parent 4b8a5bb commit 4ac311d
Showing 1 changed file with 171 additions and 57 deletions.
228 changes: 171 additions & 57 deletions core/src/plugins/kubernetes/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ import { cloneDeep } from "lodash"
// Default timeout for individual run/exec operations
const defaultTimeout = 600

// TODO: break this function up
export async function runAndCopy({
ctx,
log,
Expand Down Expand Up @@ -90,7 +89,7 @@ export async function runAndCopy({
const getArtifacts = !interactive && artifacts && artifacts.length > 0 && artifactsPath
const mainContainerName = "main"

const spec: V1PodSpec = {
const podSpec: V1PodSpec = {
containers: [
{
...(container || {}),
Expand All @@ -106,7 +105,7 @@ export async function runAndCopy({
}

if (volumes) {
configureVolumes(module, spec, volumes)
configureVolumes(module, podSpec, volumes)
}

if (!description) {
Expand All @@ -115,33 +114,67 @@ export async function runAndCopy({

const errorMetadata: any = { moduleName: module.name, description, args, artifacts }

if (getArtifacts) {
if (!command) {
throw new ConfigurationError(
deline`
${description} specifies artifacts to export, but doesn't
explicitly set a \`command\`. The kubernetes provider currently requires an explicit command to be set for
tests and tasks that export artifacts, because the image's entrypoint cannot be inferred in that execution
mode. Please set the \`command\` field and try again.
`,
errorMetadata
)
}
if (!podName) {
podName = makePodName("run", module.name)
}

const runParams = {
ctx,
api,
provider,
log,
module,
args,
command,
interactive,
runtimeContext,
timeout,
podSpec,
podName,
namespace,
}

// We start the container with a named pipe and tail that, to get the logs from the actual command
// we plan on running. Then we sleep, so that we can copy files out of the container.
spec.containers[0].command = ["sh", "-c", "mkfifo /tmp/output && cat /tmp/output && sleep 86400"]
if (getArtifacts) {
return runWithArtifacts({
...runParams,
mainContainerName,
artifacts,
artifactsPath: artifactsPath!,
description,
errorMetadata,
stdout,
stderr,
})
} else {
if (args) {
spec.containers[0].args = args
}
if (command) {
spec.containers[0].command = command
}
return runWithoutArtifacts(runParams)
}
}

if (!podName) {
podName = makePodName("run", module.name)
async function runWithoutArtifacts({
ctx,
api,
provider,
log,
module,
args,
command,
timeout,
podSpec,
podName,
namespace,
interactive,
}: RunModuleParams<GardenModule> & {
api: KubeApi
provider: KubernetesProvider
podSpec: V1PodSpec
podName: string
namespace: string
}): Promise<RunResult> {
if (args) {
podSpec.containers[0].args = args
}
if (command) {
podSpec.containers[0].command = command
}

const pod: KubernetesResource<V1Pod> = {
Expand All @@ -151,7 +184,7 @@ export async function runAndCopy({
name: podName,
namespace,
},
spec,
spec: podSpec,
}

const runner = new PodRunner({
Expand All @@ -165,7 +198,7 @@ export async function runAndCopy({
let result: RunResult
const startedAt = new Date()

const timedOutResult = async (logs: string) => {
const timedOutResult = (logs: string) => {
return {
command: runner.getFullCommand(),
completedAt: new Date(),
Expand All @@ -177,39 +210,120 @@ export async function runAndCopy({
}
}

if (!getArtifacts) {
try {
const res = await runner.runAndWait({
log,
remove: true,
timeoutSec: timeout || defaultTimeout,
tty: !!interactive,
})
try {
const res = await runner.runAndWait({
log,
remove: true,
timeoutSec: timeout || defaultTimeout,
tty: !!interactive,
})
result = {
...res,
moduleName: module.name,
version: module.version.versionString,
}
} catch (err) {
if (err.type === "timeout") {
result = timedOutResult(err.detail.logs)
} else if (err.type === "pod-runner") {
// Command exited with non-zero code
result = {
...res,
log: err.detail.logs || err.message,
moduleName: module.name,
version: module.version.versionString,
success: false,
startedAt,
completedAt: new Date(),
command: [...(command || []), ...(args || [])],
}
} catch (err) {
if (err.type === "timeout") {
result = await timedOutResult(err.detail.logs)
} else if (err.type === "pod-runner") {
// Command exited with non-zero code
result = {
log: err.detail.logs || err.message,
moduleName: module.name,
version: module.version.versionString,
success: false,
startedAt,
completedAt: new Date(),
command: [...(command || []), ...(args || [])],
}
} else {
throw err
}
} else {
throw err
}
}

return result
}

async function runWithArtifacts({
ctx,
api,
provider,
log,
module,
args,
command,
timeout,
podSpec,
podName,
mainContainerName,
artifacts,
artifactsPath,
description,
errorMetadata,
stdout,
stderr,
namespace,
}: RunModuleParams<GardenModule> & {
podSpec: V1PodSpec
podName: string
mainContainerName: string
api: KubeApi
provider: KubernetesProvider
artifacts: ArtifactSpec[]
artifactsPath: string
description?: string
errorMetadata: any
stdout?: Writable
stderr?: Writable
namespace: string
}): Promise<RunResult> {
if (!command) {
throw new ConfigurationError(
deline`
${description} specifies artifacts to export, but doesn't
explicitly set a \`command\`. The kubernetes provider currently requires an explicit command to be set for
tests and tasks that export artifacts, because the image's entrypoint cannot be inferred in that execution
mode. Please set the \`command\` field and try again.
`,
errorMetadata
)
}

return result
// We start the container with a named pipe and tail that, to get the logs from the actual command
// we plan on running. Then we sleep, so that we can copy files out of the container.
podSpec.containers[0].command = ["sh", "-c", "mkfifo /tmp/output && cat /tmp/output && sleep 86400"]

const pod: KubernetesResource<V1Pod> = {
apiVersion: "v1",
kind: "Pod",
metadata: {
name: podName,
namespace,
},
spec: podSpec,
}

const runner = new PodRunner({
ctx,
api,
pod,
provider,
namespace,
})

let result: RunResult
const startedAt = new Date()

const timedOutResult = (logs: string) => {
return {
command: runner.getFullCommand(),
completedAt: new Date(),
log: "Command timed out." + (logs ? ` Here are the logs until the timeout occurred:\n\n${logs}` : ""),
moduleName: module.name,
startedAt,
success: false,
version: module.version.versionString,
}
}

const timeoutSec = timeout || defaultTimeout
Expand Down Expand Up @@ -308,7 +422,7 @@ export async function runAndCopy({

if (err.type === "timeout") {
// Command timed out
result = await timedOutResult((await runner.getMainContainerLogs()).trim())
result = timedOutResult((await runner.getMainContainerLogs()).trim())
} else if (err.type === "pod-runner" && res && res.exitCode) {
// Command exited with non-zero code
result = {
Expand All @@ -331,7 +445,7 @@ export async function runAndCopy({
const tmpDir = await tmp.dir({ unsafeCleanup: true })
// Remove leading slash (which is required in the schema)
const sourcePath = artifact.source.slice(1)
const targetPath = resolve(artifactsPath!, artifact.target || ".")
const targetPath = resolve(artifactsPath, artifact.target || ".")

const tarCmd = [
"tar",
Expand Down

0 comments on commit 4ac311d

Please sign in to comment.