Skip to content

Commit

Permalink
refactor: standardise error message from execa and spawn
Browse files Browse the repository at this point in the history
  • Loading branch information
eysi09 committed Nov 1, 2019
1 parent 8b9bbfe commit 035599d
Show file tree
Hide file tree
Showing 17 changed files with 334 additions and 99 deletions.
3 changes: 2 additions & 1 deletion examples/demo-project/garden.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ environments:
providers:
- name: kubernetes
# Replace these values as appropriate
context: gke_garden-dev-200012_europe-west1-b_garden-dev-1
# context: gke_garden-dev-200012_europe-west1-b_garden-dev-1
context: foo
namespace: ${local.env.USER || local.username}-demo-project
defaultHostname: ${local.env.USER || local.username}-demo-project.dev-1.sys.garden
buildMode: cluster-docker
Expand Down
4 changes: 2 additions & 2 deletions garden-service/src/build-dir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import {
import { emptyDir, ensureDir } from "fs-extra"
import { ConfigurationError } from "./exceptions"
import { FileCopySpec, Module, getModuleKey } from "./types/module"
import execa from "execa"
import { normalizeLocalRsyncPath } from "./util/fs"
import { LogEntry } from "./logger/log-entry"
import { ModuleConfig } from "./config/module"
import { ConfigGraph } from "./config-graph"
import { exec } from "./util/util"

// FIXME: We don't want to keep special casing this module type so we need to think
// of a better way around this.
Expand Down Expand Up @@ -179,7 +179,7 @@ export class BuildDir {
log.silly(`File list: ${JSON.stringify(files)}`)
}

await execa("rsync", [...syncOpts, sourcePath, destinationPath], { input })
await exec("rsync", [...syncOpts, sourcePath, destinationPath], { input })
}
}

Expand Down
5 changes: 2 additions & 3 deletions garden-service/src/commands/get/get-debug-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ import {
} from "../base"
import { findProjectConfig } from "../../config/base"
import { ensureDir, copy, remove, pathExists, writeFile } from "fs-extra"
import { getPackageVersion } from "../../util/util"
import { getPackageVersion, exec } from "../../util/util"
import { platform, release } from "os"
import { join, relative, basename, dirname } from "path"
import execa = require("execa")
import { LogEntry } from "../../logger/log-entry"
import { deline } from "../../util/string"
import { findConfigPathsInPath, getConfigFilePath, defaultDotIgnoreFiles } from "../../util/fs"
Expand Down Expand Up @@ -115,7 +114,7 @@ export async function collectSystemDiagnostic(gardenDirPath: string, log: LogEnt
const dockerLog = log.info({ section: "Docker", msg: "collecting info", status: "active" })
let dockerVersion = ""
try {
dockerVersion = (await execa("docker", ["--version"])).stdout
dockerVersion = (await exec("docker", ["--version"])).stdout
dockerLog.setSuccess({ msg: chalk.green(`Done (took ${log.getDuration(1)} sec)`), append: true })
} catch (error) {
log.error("Error encountered while executing docker")
Expand Down
4 changes: 2 additions & 2 deletions garden-service/src/commands/get/get-eysi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import dedent = require("dedent")
import { readFile } from "fs-extra"
import { STATIC_DIR } from "../../constants"
import { join } from "path"
import execa = require("execa")
import { exec } from "../../util/util"

export class GetEysiCommand extends Command {
name = "eysi"
Expand All @@ -32,7 +32,7 @@ export class GetEysiCommand extends Command {

try {
// Close enough.
await execa("say", ["Hello", ",", "I", "am", "Aysey"])
await exec("say", ["Hello", ",", "I", "am", "Aysey"])
} catch (_) { }

return { result: { eysi } }
Expand Down
5 changes: 2 additions & 3 deletions garden-service/src/garden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -496,10 +496,9 @@ export class Garden {

if (failed.length) {
const messages = failed.map(r => `- ${r!.name}: ${r!.error!.message}`)
const names = failed.map(r => r!.name)
throw new PluginError(
`Failed resolving one or more providers:\n${messages.join(
"\n",
)}`,
`Failed resolving one or more providers:\n- ${names.join("\n- ")}`,
{ rawConfigs, taskResults, messages },
)
}
Expand Down
22 changes: 7 additions & 15 deletions garden-service/src/plugins/exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ import { BuildModuleParams, BuildResult } from "../types/plugin/module/build"
import { TestModuleParams } from "../types/plugin/module/testModule"
import { TestResult } from "../types/plugin/module/getTestResult"
import { RunTaskParams, RunTaskResult } from "../types/plugin/task/runTask"
import { createOutputStream } from "../util/util"
import { createOutputStream, exec } from "../util/util"
import { LogLevel } from "../logger/log-node"
import { ConfigurationError } from "../exceptions"
import execa = require("execa")
import { LogEntry } from "../logger/log-entry"

export const name = "exec"

Expand All @@ -37,15 +35,6 @@ const execPathDoc = dedent`
If the top level \`local\` directive is set to \`true\`, the command runs in the module source directory instead.
`

function execWithStream(cmd: string, log: LogEntry, opts: execa.Options) {
const proc = execa(cmd, opts)

const outputStream = createOutputStream(log.placeholder(LogLevel.debug))
proc.stdout!.pipe(outputStream)
proc.stderr!.pipe(outputStream)
return proc
}

export interface ExecTestSpec extends BaseTestSpec {
command: string[],
env: { [key: string]: string },
Expand Down Expand Up @@ -198,12 +187,13 @@ export async function buildExecModule({ module, log }: BuildModuleParams<ExecMod
const { command } = module.spec.build

if (command.length) {
const result = await execWithStream(command.join(" "), log, {
const result = await exec(command.join(" "), [], {
cwd: module.buildPath,
env: {
...process.env,
...mapValues(module.spec.env, v => v.toString()),
},
outputStream: createOutputStream(log.placeholder(LogLevel.debug)),
shell: true,
})

Expand All @@ -222,7 +212,7 @@ export async function testExecModule({ module, log, testConfig }: TestModulePara
const startedAt = new Date()
const { command } = testConfig.spec

const result = await execWithStream(command.join(" "), log, {
const result = await exec(command.join(" "), [], {
cwd: module.buildPath,
env: {
...process.env,
Expand All @@ -231,6 +221,7 @@ export async function testExecModule({ module, log, testConfig }: TestModulePara
...mapValues(testConfig.spec.env, v => v + ""),
},
reject: false,
outputStream: createOutputStream(log.placeholder(LogLevel.debug)),
shell: true,
})

Expand All @@ -252,13 +243,14 @@ export async function runExecTask(params: RunTaskParams): Promise<RunTaskResult>
const command = task.spec.command
const startedAt = new Date()

const result = await execWithStream(command.join(" "), log, {
const result = await exec(command.join(" "), [], {
cwd: module.buildPath,
env: {
...process.env,
...mapValues(module.spec.env, v => v.toString()),
...mapValues(task.spec.env, v => v.toString()),
},
outputStream: createOutputStream(log.placeholder(LogLevel.debug)),
shell: true,
})

Expand Down
4 changes: 2 additions & 2 deletions garden-service/src/plugins/kubernetes/container/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { BuildModuleParams, BuildResult } from "../../../types/plugin/module/bui
import { getPods, millicpuToString, megabytesToString } from "../util"
import { systemNamespace } from "../system"
import { RSYNC_PORT } from "../constants"
import execa = require("execa")
import { posix, resolve } from "path"
import { KubeApi } from "../api"
import { kubectl } from "../kubectl"
Expand All @@ -31,6 +30,7 @@ import { getPortForward } from "../port-forward"
import chalk from "chalk"
import { Writable } from "stream"
import { LogLevel } from "../../../logger/log-node"
import { exec } from "../../../util/util"

const dockerDaemonDeploymentName = "garden-docker-daemon"
const dockerDaemonContainerName = "docker-daemon"
Expand Down Expand Up @@ -149,7 +149,7 @@ const remoteBuild: BuildHandler = async (params) => {

// We retry a couple of times, because we may get intermittent connection issues or concurrency issues
await pRetry(
() => execa("rsync", ["-vrpztgo", "--relative", "--delete", src, destination]),
() => exec("rsync", ["-vrpztgo", "--relative", "--delete", src, destination]),
{ retries: 3, minTimeout: 500 },
)

Expand Down
4 changes: 2 additions & 2 deletions garden-service/src/plugins/kubernetes/hot-reload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/

import Bluebird from "bluebird"
import execa from "execa"
import normalizePath = require("normalize-path")
import { V1Deployment, V1DaemonSet, V1StatefulSet } from "@kubernetes/client-node"
import { ContainerModule, ContainerHotReloadSpec } from "../container/config"
Expand All @@ -29,6 +28,7 @@ import { normalizeLocalRsyncPath } from "../../util/fs"
import { createWorkloadResource } from "./container/deployment"
import { kubectl } from "./kubectl"
import { labelSelectorToString } from "./util"
import { exec } from "../../util/util"

export const RSYNC_PORT_NAME = "garden-rsync"

Expand Down Expand Up @@ -291,7 +291,7 @@ export async function syncToService(

log.debug(`Hot-reloading from ${src} to ${destination}`)

return execa("rsync", ["-vrpztgo", src, destination])
return exec("rsync", ["-vrpztgo", src, destination])
})

const postSyncCommand = hotReloadSpec.postSyncCommand
Expand Down
8 changes: 4 additions & 4 deletions garden-service/src/plugins/kubernetes/local/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
*/

import Bluebird from "bluebird"
import execa from "execa"
import { KubernetesBaseConfig, kubernetesConfigBase, k8sContextSchema } from "../config"
import { ConfigureProviderParams } from "../../../types/plugin/provider/configureProvider"
import { joiProviderName, joi } from "../../../config/common"
import { getKubeConfig } from "../api"
import { configureMicrok8sAddons } from "./microk8s"
import { setMinikubeDockerEnv } from "./minikube"
import { ContainerRegistryConfig } from "../../container/config"
import { exec } from "../../../util/util"

// TODO: split this into separate plugins to handle Docker for Mac and Minikube

Expand Down Expand Up @@ -99,17 +99,17 @@ export async function configureProvider({ config, log, projectName }: ConfigureP
["config", "set", "WantUpdateNotification", "false"],
["addons", "enable", "dashboard"],
]
await Bluebird.map(initCmds, async (cmd) => execa("minikube", cmd))
await Bluebird.map(initCmds, async (cmd) => exec("minikube", cmd))

if (!defaultHostname) {
// use the nip.io service to give a hostname to the instance, if none is explicitly configured
const { stdout } = await execa("minikube", ["ip"])
const { stdout } = await exec("minikube", ["ip"])
defaultHostname = `${projectName}.${stdout}.nip.io`
}

if (config.setupIngressController === "nginx") {
log.debug("Using minikube's ingress addon")
await execa("minikube", ["addons", "enable", "ingress"])
await exec("minikube", ["addons", "enable", "ingress"])
}

await setMinikubeDockerEnv()
Expand Down
6 changes: 3 additions & 3 deletions garden-service/src/plugins/kubernetes/local/microk8s.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import execa from "execa"
import { RuntimeError } from "../../../exceptions"
import { LogEntry } from "../../../logger/log-entry"
import { exec } from "../../../util/util"

export async function configureMicrok8sAddons(log: LogEntry, addons: string[]) {
let status = ""

try {
status = (await execa("microk8s.status")).stdout
status = (await exec("microk8s.status")).stdout
} catch {
// This is caught below.
}
Expand All @@ -29,6 +29,6 @@ export async function configureMicrok8sAddons(log: LogEntry, addons: string[]) {

if (missingAddons.length > 0) {
log.info({ section: "microk8s", msg: `enabling required addons (${missingAddons.join(", ")})` })
await execa("microk8s.enable", missingAddons)
await exec("microk8s.enable", missingAddons)
}
}
3 changes: 2 additions & 1 deletion garden-service/src/plugins/kubernetes/local/minikube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import execa from "execa"
import { exec } from "../../../util/util"

/**
* Automatically set docker environment variables for minikube
Expand All @@ -16,7 +17,7 @@ export async function setMinikubeDockerEnv() {
let minikubeEnv: string

try {
minikubeEnv = (await execa("minikube", ["docker-env", "--shell=bash"])).stdout
minikubeEnv = (await exec("minikube", ["docker-env", "--shell=bash"])).stdout
} catch (err) {
if ((<execa.ExecaError>err).stderr.includes("driver does not support")) {
return
Expand Down
24 changes: 9 additions & 15 deletions garden-service/src/plugins/kubernetes/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { GetEnvironmentStatusParams } from "../../types/plugin/provider/getEnvir
import { kubectl, KUBECTL_DEFAULT_TIMEOUT } from "./kubectl"
import { LogEntry } from "../../logger/log-entry"
import { gardenAnnotationKey } from "../../util/string"
import dedent from "dedent"

const GARDEN_VERSION = getPackageVersion()
type CreateNamespaceStatus = "pending" | "created"
Expand Down Expand Up @@ -116,21 +117,14 @@ export async function prepareNamespaces({ ctx, log }: GetEnvironmentStatusParams
// TODO: use API instead of kubectl (I just couldn't find which API call to make)
await kubectl.exec({ log, provider: k8sCtx.provider, args: ["version"] })
} catch (err) {
let message = err.message
if (err.stdout) {
message += err.stdout
}
if (err.stderr) {
message += err.stderr
}
throw new DeploymentError(
`Unable to connect to Kubernetes cluster. ` +
`Please make sure it is running, reachable and that you have the right context configured.`,
{
providerConfig: k8sCtx.provider.config,
message,
},
)
log.setError("Error")

throw new DeploymentError(dedent`
Unable to connect to Kubernetes cluster. Got error:
${err.message}
`,
{ providerConfig: k8sCtx.provider.config })
}

return Bluebird.props({
Expand Down
12 changes: 3 additions & 9 deletions garden-service/src/util/ext-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import { platform } from "os"
import { pathExists, createWriteStream, ensureDir, chmod, remove, move } from "fs-extra"
import { ConfigurationError, ParameterError, GardenBaseError } from "../exceptions"
import { join, dirname, basename, sep } from "path"
import { hashString } from "./util"
import { hashString, exec } from "./util"
import Axios from "axios"
import execa from "execa"
import tar from "tar"
import { SupportedPlatform, GARDEN_GLOBAL_PATH } from "../constants"
import { LogEntry } from "../logger/log-entry"
Expand Down Expand Up @@ -269,20 +268,15 @@ export class BinaryCmd extends Library {

log.debug(`Execing '${path} ${args.join(" ")}' in ${cwd}`)

const proc = execa(path, args, {
return exec(path, args, {
cwd,
timeout: this.getTimeout(timeout) * 1000,
env,
input,
outputStream,
reject: !ignoreError,
})

if (outputStream) {
proc.stdout && proc.stdout.pipe(outputStream)
proc.stderr && proc.stderr.pipe(outputStream)
}

return proc
}

async stdout(params: ExecParams) {
Expand Down
Loading

0 comments on commit 035599d

Please sign in to comment.