From 8b4a6d5eca00e84472bd334fadd15990078bcd60 Mon Sep 17 00:00:00 2001 From: Jon Edvald Date: Wed, 31 Jul 2019 19:53:06 +0200 Subject: [PATCH] feat(k8s): allow setting custom kubeconfig path --- docs/reference/module-types/kubernetes.md | 2 +- docs/reference/providers/kubernetes.md | 11 +++++ garden-service/src/plugins/kubernetes/api.ts | 32 +++++++++----- .../commands/cleanup-cluster-registry.ts | 10 ++--- .../src/plugins/kubernetes/config.ts | 1 + .../src/plugins/kubernetes/container/build.ts | 6 +-- .../kubernetes/container/deployment.ts | 16 +++---- .../src/plugins/kubernetes/container/logs.ts | 8 ++-- .../src/plugins/kubernetes/container/run.ts | 10 ++--- .../plugins/kubernetes/container/status.ts | 2 +- .../src/plugins/kubernetes/container/test.ts | 2 +- .../src/plugins/kubernetes/helm/build.ts | 13 +++--- .../src/plugins/kubernetes/helm/common.ts | 42 ++++++++++-------- .../src/plugins/kubernetes/helm/deployment.ts | 13 +++--- .../src/plugins/kubernetes/helm/helm-cli.ts | 22 +++++++--- .../src/plugins/kubernetes/helm/logs.ts | 6 +-- .../src/plugins/kubernetes/helm/run.ts | 10 ++--- .../src/plugins/kubernetes/helm/status.ts | 6 +-- .../src/plugins/kubernetes/helm/test.ts | 4 +- .../src/plugins/kubernetes/helm/tiller.ts | 43 +++++++++---------- garden-service/src/plugins/kubernetes/init.ts | 8 ++-- .../src/plugins/kubernetes/kubectl.ts | 25 ++++++----- .../kubernetes/kubernetes-module/handlers.ts | 15 +++---- .../src/plugins/kubernetes/kubernetes.ts | 21 ++++++--- .../src/plugins/kubernetes/local/config.ts | 10 ++++- garden-service/src/plugins/kubernetes/logs.ts | 9 ++-- .../src/plugins/kubernetes/namespace.ts | 7 ++- .../src/plugins/kubernetes/port-forward.ts | 2 +- garden-service/src/plugins/kubernetes/run.ts | 7 +-- .../src/plugins/kubernetes/secrets.ts | 6 +-- .../src/plugins/kubernetes/status/status.ts | 8 ++-- .../src/plugins/kubernetes/system.ts | 2 +- .../src/plugins/kubernetes/task-results.ts | 4 +- .../src/plugins/kubernetes/test-results.ts | 4 +- .../src/plugins/openfaas/openfaas.ts | 13 +++--- garden-service/src/tasks/resolve-provider.ts | 6 ++- .../plugin/provider/configureProvider.ts | 1 + garden-service/src/util/ext-tools.ts | 3 +- .../plugins/kubernetes/container/ingress.ts | 22 +++++----- 39 files changed, 243 insertions(+), 189 deletions(-) diff --git a/docs/reference/module-types/kubernetes.md b/docs/reference/module-types/kubernetes.md index 5c49f4e40e..72179a7d82 100644 --- a/docs/reference/module-types/kubernetes.md +++ b/docs/reference/module-types/kubernetes.md @@ -208,7 +208,7 @@ POSIX-style path or filename to copy the directory or file(s). ### `dependencies` -List of names of services that should be deployed before this chart. +The names of any services that this service depends on at runtime, and the names of any tasks that should be executed before this service is deployed. | Type | Required | Default | | --------------- | -------- | ------- | diff --git a/docs/reference/providers/kubernetes.md b/docs/reference/providers/kubernetes.md index 544ff24d75..22816e1afe 100644 --- a/docs/reference/providers/kubernetes.md +++ b/docs/reference/providers/kubernetes.md @@ -847,6 +847,16 @@ The external HTTPS port of the cluster's ingress controller. | -------- | -------- | ------- | | `number` | No | `443` | +### `providers[].kubeconfig` + +[providers](#providers) > kubeconfig + +Path to kubeconfig file to use instead of the system default. Must be a POSIX-style path. + +| Type | Required | +| -------- | -------- | +| `string` | No | + ### `providers[].namespace` [providers](#providers) > namespace @@ -929,6 +939,7 @@ providers: ingressClass: ingressHttpPort: 80 ingressHttpsPort: 443 + kubeconfig: namespace: setupIngressController: false ``` diff --git a/garden-service/src/plugins/kubernetes/api.ts b/garden-service/src/plugins/kubernetes/api.ts index c6c1a054b5..bd16378365 100644 --- a/garden-service/src/plugins/kubernetes/api.ts +++ b/garden-service/src/plugins/kubernetes/api.ts @@ -29,6 +29,7 @@ import AsyncLock = require("async-lock") import request = require("request-promise") import requestErrors = require("request-promise/errors") import { safeLoad, safeDump } from "js-yaml" +import { readFile } from "fs-extra" import { Omit } from "../../util/util" import { zip, omitBy, isObject, isPlainObject, keyBy } from "lodash" @@ -37,6 +38,7 @@ import { KubernetesResource, KubernetesServerResource, KubernetesServerList } fr import { LogEntry } from "../../logger/log-entry" import { kubectl } from "./kubectl" import { urlJoin } from "../../util/string" +import { KubernetesProvider } from "./config" interface ApiGroupMap { [groupVersion: string]: V1APIGroup @@ -153,9 +155,9 @@ export class KubeApi { } } - static async factory(log: LogEntry, context: string) { - const config = await getContextConfig(log, context) - return new KubeApi(context, config) + static async factory(log: LogEntry, provider: KubernetesProvider) { + const config = await getContextConfig(log, provider) + return new KubeApi(provider.config.context, config) } async getApiInfo(): Promise { @@ -338,7 +340,7 @@ export class KubeApi { return Reflect.get(target, name, receiver) } - return function(...args) { + return function (...args) { const defaultHeaders = target["defaultHeaders"] if (name.startsWith("patch")) { @@ -375,12 +377,16 @@ function getGroupBasePath(groupId: string) { return groupId.includes("/") ? `/apis/${groupId}` : `/api/${groupId}` } -export async function getKubeConfig(log: LogEntry) { +export async function getKubeConfig(log: LogEntry, provider: KubernetesProvider) { let kubeConfigStr: string try { - // We use kubectl for this, to support merging multiple paths in the KUBECONFIG env var - kubeConfigStr = await kubectl.stdout({ log, args: ["config", "view", "--raw"] }) + if (provider.config.kubeconfig) { + kubeConfigStr = (await readFile(provider.config.kubeconfig)).toString() + } else { + // We use kubectl for this, to support merging multiple paths in the KUBECONFIG env var + kubeConfigStr = await kubectl.stdout({ log, provider, args: ["config", "view", "--raw"] }) + } return safeLoad(kubeConfigStr) } catch (error) { throw new RuntimeError(`Unable to load kubeconfig: ${error}`, { @@ -389,12 +395,16 @@ export async function getKubeConfig(log: LogEntry) { } } -async function getContextConfig(log: LogEntry, context: string): Promise { - if (cachedConfigs[context]) { - return cachedConfigs[context] +async function getContextConfig(log: LogEntry, provider: KubernetesProvider): Promise { + const kubeconfigPath = provider.config.kubeconfig + const context = provider.config.context + const cacheKey = kubeconfigPath ? `${kubeconfigPath}:${context}` : context + + if (cachedConfigs[cacheKey]) { + return cachedConfigs[cacheKey] } - const rawConfig = await getKubeConfig(log) + const rawConfig = await getKubeConfig(log, provider) const kc = new KubeConfig() // There doesn't appear to be a method to just load the parsed config :/ diff --git a/garden-service/src/plugins/kubernetes/commands/cleanup-cluster-registry.ts b/garden-service/src/plugins/kubernetes/commands/cleanup-cluster-registry.ts index b42dfff8a1..9d42f73b14 100644 --- a/garden-service/src/plugins/kubernetes/commands/cleanup-cluster-registry.ts +++ b/garden-service/src/plugins/kubernetes/commands/cleanup-cluster-registry.ts @@ -53,7 +53,7 @@ export const cleanupClusterRegistry: PluginCommand = { await cleanupBuildSyncVolume(provider, log) // Scan through all Pods in cluster - const api = await KubeApi.factory(log, provider.config.context) + const api = await KubeApi.factory(log, provider) const imagesInUse = await getImagesInUse(api, provider, log) // Get images in registry @@ -219,7 +219,7 @@ async function runRegistryGarbageCollection(ctx: KubernetesPluginContext, api: K }) delete modifiedDeployment.status - await apply({ log, context: provider.config.context, manifests: [modifiedDeployment], namespace: systemNamespace }) + await apply({ log, provider, manifests: [modifiedDeployment], namespace: systemNamespace }) // -> Wait for registry to be up again await waitForResources({ ctx, provider, log, serviceName: "docker-registry", resources: [modifiedDeployment] }) @@ -249,7 +249,7 @@ async function runRegistryGarbageCollection(ctx: KubernetesPluginContext, api: K await apply({ log, - context: provider.config.context, + provider, manifests: [writableRegistry], namespace: systemNamespace, }) @@ -352,7 +352,7 @@ async function cleanupBuildSyncVolume(provider: KubernetesProvider, log: LogEntr // Returns the name for one of the build-sync pods in the cluster // (doesn't matter which one, they all use the same volume) async function getBuildSyncPodName(provider: KubernetesProvider, log: LogEntry) { - const api = await KubeApi.factory(log, provider.config.context) + const api = await KubeApi.factory(log, provider) const builderStatusRes = await api.apps.readNamespacedDeployment(buildSyncDeploymentName, systemNamespace) const builderPods = await getPods(api, systemNamespace, builderStatusRes.spec.selector.matchLabels) @@ -375,7 +375,7 @@ async function execInBuildSync({ provider, log, args, timeout, podName }: Builde return kubectl.exec({ args: execCmd, - context: provider.config.context, + provider, log, namespace: systemNamespace, timeout, diff --git a/garden-service/src/plugins/kubernetes/config.ts b/garden-service/src/plugins/kubernetes/config.ts index 42ce629e6d..253fcb965f 100644 --- a/garden-service/src/plugins/kubernetes/config.ts +++ b/garden-service/src/plugins/kubernetes/config.ts @@ -67,6 +67,7 @@ export interface KubernetesBaseConfig extends ProviderConfig { ingressHttpPort: number ingressHttpsPort: number ingressClass?: string + kubeconfig?: string namespace?: string resources: KubernetesResources storage: KubernetesStorage diff --git a/garden-service/src/plugins/kubernetes/container/build.ts b/garden-service/src/plugins/kubernetes/container/build.ts index 340162415b..36cae52952 100644 --- a/garden-service/src/plugins/kubernetes/container/build.ts +++ b/garden-service/src/plugins/kubernetes/container/build.ts @@ -237,7 +237,7 @@ export async function execInBuilder({ provider, log, args, timeout, podName }: B return kubectl.exec({ args: execCmd, - context: provider.config.context, + provider, log, namespace: systemNamespace, timeout, @@ -245,7 +245,7 @@ export async function execInBuilder({ provider, log, args, timeout, podName }: B } export async function getBuilderPodName(provider: KubernetesProvider, log: LogEntry) { - const api = await KubeApi.factory(log, provider.config.context) + const api = await KubeApi.factory(log, provider) const builderStatusRes = await api.apps.readNamespacedDeployment(dockerDaemonDeploymentName, systemNamespace) const builderPods = await getPods(api, systemNamespace, builderStatusRes.spec.selector.matchLabels) @@ -266,7 +266,7 @@ async function runKaniko(provider: KubernetesProvider, log: LogEntry, module: Co const registryHostname = getRegistryHostname() return runPod({ - context: provider.config.context, + provider, ignoreError: false, image: kanikoImage, interactive: false, diff --git a/garden-service/src/plugins/kubernetes/container/deployment.ts b/garden-service/src/plugins/kubernetes/container/deployment.ts index 54bfc0c0f4..2c0f42e949 100644 --- a/garden-service/src/plugins/kubernetes/container/deployment.ts +++ b/garden-service/src/plugins/kubernetes/container/deployment.ts @@ -42,10 +42,10 @@ export async function deployContainerService(params: DeployServiceParams{}) diff --git a/garden-service/src/plugins/kubernetes/container/logs.ts b/garden-service/src/plugins/kubernetes/container/logs.ts index 0df427daba..81522915c0 100644 --- a/garden-service/src/plugins/kubernetes/container/logs.ts +++ b/garden-service/src/plugins/kubernetes/container/logs.ts @@ -17,11 +17,11 @@ import { emptyRuntimeContext } from "../../../runtime-context" export async function getServiceLogs(params: GetServiceLogsParams) { const { ctx, log, service } = params const k8sCtx = ctx - const context = k8sCtx.provider.config.context - const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) + const provider = k8sCtx.provider + const namespace = await getAppNamespace(k8sCtx, log, provider) const resources = [await createDeployment({ - provider: k8sCtx.provider, + provider, service, // No need for the proper context here runtimeContext: emptyRuntimeContext, @@ -30,5 +30,5 @@ export async function getServiceLogs(params: GetServiceLogsParams, ): Promise { const provider = ctx.provider - const context = provider.config.context const namespace = await getAppNamespace(ctx, log, provider) // Apply overrides @@ -123,7 +122,7 @@ export async function runContainerModule( } return runPod({ - context, + provider, image, interactive, ignoreError, @@ -155,7 +154,6 @@ export async function runContainerTask( { ctx, log, module, task, taskVersion, interactive, runtimeContext }: RunTaskParams, ): Promise { const provider = ctx.provider - const context = provider.config.context const namespace = await getAppNamespace(ctx, log, provider) // Apply overrides @@ -175,7 +173,7 @@ export async function runContainerTask( } const res = await runPod({ - context, + provider, image, interactive, ignoreError: false, diff --git a/garden-service/src/plugins/kubernetes/container/status.ts b/garden-service/src/plugins/kubernetes/container/status.ts index a0797529a8..295fd81fa0 100644 --- a/garden-service/src/plugins/kubernetes/container/status.ts +++ b/garden-service/src/plugins/kubernetes/container/status.ts @@ -30,7 +30,7 @@ export async function getContainerServiceStatus( // TODO: hash and compare all the configuration files (otherwise internal changes don't get deployed) const version = module.version const provider = k8sCtx.provider - const api = await KubeApi.factory(log, provider.config.context) + const api = await KubeApi.factory(log, provider) const namespace = await getAppNamespace(k8sCtx, log, provider) // FIXME: [objects, matched] and ingresses can be run in parallel diff --git a/garden-service/src/plugins/kubernetes/container/test.ts b/garden-service/src/plugins/kubernetes/container/test.ts index 5abbd27c08..af03b65943 100644 --- a/garden-service/src/plugins/kubernetes/container/test.ts +++ b/garden-service/src/plugins/kubernetes/container/test.ts @@ -45,7 +45,7 @@ export async function testContainerModule( } const result = await runPod({ - context: provider.config.context, + provider, image, interactive, ignoreError: true, // to ensure results get stored when an error occurs diff --git a/garden-service/src/plugins/kubernetes/helm/build.ts b/garden-service/src/plugins/kubernetes/helm/build.ts index 247fe76e2f..012eae7051 100644 --- a/garden-service/src/plugins/kubernetes/helm/build.ts +++ b/garden-service/src/plugins/kubernetes/helm/build.ts @@ -24,19 +24,18 @@ export async function buildHelmModule({ ctx, module, log }: BuildModuleParamsloadTemplate(await helm(namespace, context, log, - "template", - "--name", releaseName, - "--namespace", namespace, - ...await getValueFileArgs(module), - chartPath, - )) + const objects = loadTemplate(await helm({ + ctx: k8sCtx, + log, + namespace, + args: [ + "template", + "--name", releaseName, + "--namespace", namespace, + ...await getValueFileArgs(module), + chartPath, + ], + })) const resources = objects .filter(obj => { @@ -301,19 +305,23 @@ async function renderHelmTemplateString( skipCreate: true, }) const releaseName = getReleaseName(module) - const context = ctx.provider.config.context try { await writeFile(tempFilePath, value) - const objects = loadTemplate(await helm(namespace, context, log, - "template", - "--name", releaseName, - "--namespace", namespace, - ...await getValueFileArgs(module), - "-x", tempFilePath, - chartPath, - )) + const objects = loadTemplate(await helm({ + ctx: k8sCtx, + log, + namespace, + args: [ + "template", + "--name", releaseName, + "--namespace", namespace, + ...await getValueFileArgs(module), + "-x", tempFilePath, + chartPath, + ], + })) return objects[0] diff --git a/garden-service/src/plugins/kubernetes/helm/deployment.ts b/garden-service/src/plugins/kubernetes/helm/deployment.ts index 858c8b3ae1..b2550fa713 100644 --- a/garden-service/src/plugins/kubernetes/helm/deployment.ts +++ b/garden-service/src/plugins/kubernetes/helm/deployment.ts @@ -47,10 +47,9 @@ export async function deployService( const provider = k8sCtx.provider const chartPath = await getChartPath(module) const namespace = await getAppNamespace(k8sCtx, log, provider) - const context = provider.config.context const releaseName = getReleaseName(module) - const releaseStatus = await getReleaseStatus(namespace, provider.config.context, releaseName, log) + const releaseStatus = await getReleaseStatus(k8sCtx, releaseName, log) if (releaseStatus.state === "missing") { log.silly(`Installing Helm release ${releaseName}`) @@ -66,7 +65,7 @@ export async function deployService( if (force) { installArgs.push("--replace") } - await helm(namespace, context, log, ...installArgs) + await helm({ ctx: k8sCtx, namespace, log, args: [...installArgs] }) } else { log.silly(`Upgrading Helm release ${releaseName}`) const upgradeArgs = [ @@ -78,7 +77,7 @@ export async function deployService( if (force) { upgradeArgs.push("--force") } - await helm(namespace, context, log, ...upgradeArgs) + await helm({ ctx: k8sCtx, namespace, log, args: [...upgradeArgs] }) } if (hotReload && hotReloadSpec && hotReloadTarget) { @@ -93,7 +92,7 @@ export async function deployService( containerName: resourceSpec && resourceSpec.containerName, }) - await apply({ log, context: provider.config.context, manifests: [hotReloadTarget], namespace }) + await apply({ log, provider, manifests: [hotReloadTarget], namespace }) } // FIXME: we should get these objects from the cluster, and not from the local `helm template` command, because @@ -113,11 +112,9 @@ export async function deleteService(params: DeleteServiceParams): Promisectx - const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) - const context = k8sCtx.provider.config.context const releaseName = getReleaseName(module) - await helm(namespace, context, log, "delete", "--purge", releaseName) + await helm({ ctx: k8sCtx, log, args: ["delete", "--purge", releaseName] }) log.setSuccess("Service deleted") return { state: "missing" } diff --git a/garden-service/src/plugins/kubernetes/helm/helm-cli.ts b/garden-service/src/plugins/kubernetes/helm/helm-cli.ts index 35aa50feb0..8b2599e2cc 100644 --- a/garden-service/src/plugins/kubernetes/helm/helm-cli.ts +++ b/garden-service/src/plugins/kubernetes/helm/helm-cli.ts @@ -8,6 +8,8 @@ import { BinaryCmd } from "../../../util/ext-tools" import { LogEntry } from "../../../logger/log-entry" +import { KubernetesPluginContext } from "../config" +import { getAppNamespace } from "../namespace"; const helmCmd = new BinaryCmd({ name: "helm", @@ -39,16 +41,26 @@ const helmCmd = new BinaryCmd({ }, }) -export async function helm(namespace: string, context: string, log: LogEntry, ...args: string[]) { - args = [ +export async function helm( + { ctx, namespace, log, args }: + { ctx: KubernetesPluginContext, namespace?: string, log: LogEntry, args: string[]; }, +) { + if (!namespace) { + namespace = await getAppNamespace(ctx, log, ctx.provider) + } + + const opts = [ "--tiller-namespace", namespace, - "--kube-context", context, - ...args, + "--kube-context", ctx.provider.config.context, ] + if (ctx.provider.config.kubeconfig) { + opts.push("--kubeconfig", ctx.provider.config.kubeconfig) + } + return helmCmd.stdout({ log, - args, + args: [...opts, ...args], // Helm itself will time out pretty reliably, so we shouldn't time out early on our side. timeout: 3600, }) diff --git a/garden-service/src/plugins/kubernetes/helm/logs.ts b/garden-service/src/plugins/kubernetes/helm/logs.ts index b7e9580641..4544ecdb3f 100644 --- a/garden-service/src/plugins/kubernetes/helm/logs.ts +++ b/garden-service/src/plugins/kubernetes/helm/logs.ts @@ -16,10 +16,10 @@ import { getChartResources } from "./common" export async function getServiceLogs(params: GetServiceLogsParams) { const { ctx, module, log } = params const k8sCtx = ctx - const context = k8sCtx.provider.config.context - const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) + const provider = k8sCtx.provider + const namespace = await getAppNamespace(k8sCtx, log, provider) const resources = await getChartResources(k8sCtx, module, log) - return getAllLogs({ ...params, context, namespace, resources }) + return getAllLogs({ ...params, provider, namespace, resources }) } diff --git a/garden-service/src/plugins/kubernetes/helm/run.ts b/garden-service/src/plugins/kubernetes/helm/run.ts index 1e9e230449..2b761f7edd 100644 --- a/garden-service/src/plugins/kubernetes/helm/run.ts +++ b/garden-service/src/plugins/kubernetes/helm/run.ts @@ -26,8 +26,8 @@ export async function runHelmModule( }: RunModuleParams, ): Promise { const k8sCtx = ctx - const context = k8sCtx.provider.config.context - const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) + const provider = k8sCtx.provider + const namespace = await getAppNamespace(k8sCtx, log, provider) const resourceSpec = getServiceResourceSpec(module) if (!resourceSpec) { @@ -55,7 +55,7 @@ export async function runHelmModule( } return runPod({ - context, + provider, image: container.image, interactive, ignoreError, @@ -72,7 +72,7 @@ export async function runHelmTask( ): Promise { // TODO: deduplicate this from testHelmModule const k8sCtx = ctx - const context = k8sCtx.provider.config.context + const provider = k8sCtx.provider const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) const { command, args } = task.spec @@ -96,7 +96,7 @@ export async function runHelmTask( } const res = await runPod({ - context, + provider, image: container.image, interactive, ignoreError: false, diff --git a/garden-service/src/plugins/kubernetes/helm/status.ts b/garden-service/src/plugins/kubernetes/helm/status.ts index 38b8a71f45..358d24dab8 100644 --- a/garden-service/src/plugins/kubernetes/helm/status.ts +++ b/garden-service/src/plugins/kubernetes/helm/status.ts @@ -62,7 +62,7 @@ export async function getServiceStatus( } const provider = k8sCtx.provider - const api = await KubeApi.factory(log, provider.config.context) + const api = await KubeApi.factory(log, provider) const namespace = await getAppNamespace(k8sCtx, log, provider) let { state, remoteObjects } = await compareDeployedObjects(k8sCtx, api, namespace, chartResources, log, false) @@ -80,11 +80,11 @@ export async function getServiceStatus( } export async function getReleaseStatus( - namespace: string, context: string, releaseName: string, log: LogEntry, + ctx: KubernetesPluginContext, releaseName: string, log: LogEntry, ): Promise { try { log.silly(`Getting the release status for ${releaseName}`) - const res = JSON.parse(await helm(namespace, context, log, "status", releaseName, "--output", "json")) + const res = JSON.parse(await helm({ ctx, log, args: ["status", releaseName, "--output", "json"] })) const statusCode = res.info.status.code return { state: helmStatusCodeMap[statusCode], diff --git a/garden-service/src/plugins/kubernetes/helm/test.ts b/garden-service/src/plugins/kubernetes/helm/test.ts index 7be47369c1..23753a789a 100644 --- a/garden-service/src/plugins/kubernetes/helm/test.ts +++ b/garden-service/src/plugins/kubernetes/helm/test.ts @@ -24,7 +24,7 @@ export async function testHelmModule( TestModuleParams, ): Promise { const k8sCtx = ctx - const context = k8sCtx.provider.config.context + const provider = k8sCtx.provider const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) // Get the container spec to use for running @@ -54,7 +54,7 @@ export async function testHelmModule( } const result = await runPod({ - context, + provider, image, interactive, ignoreError: true, // to ensure results get stored when an error occurs diff --git a/garden-service/src/plugins/kubernetes/helm/tiller.ts b/garden-service/src/plugins/kubernetes/helm/tiller.ts index 53e7f076f5..52beb010df 100644 --- a/garden-service/src/plugins/kubernetes/helm/tiller.ts +++ b/garden-service/src/plugins/kubernetes/helm/tiller.ts @@ -6,7 +6,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { PluginContext } from "../../../plugin-context" import { LogEntry } from "../../../logger/log-entry" import { KubernetesResource } from "../types" import { helm } from "./helm-cli" @@ -16,18 +15,18 @@ import { getAppNamespace } from "../namespace" import { checkResourceStatuses, waitForResources } from "../status/status" import { combineStates } from "../../../types/service" import { apply } from "../kubectl" -import { KubernetesProvider } from "../config" +import { KubernetesProvider, KubernetesPluginContext } from "../config" import chalk from "chalk" const serviceAccountName = "garden-tiller" -export async function checkTillerStatus(ctx: PluginContext, provider: KubernetesProvider, log: LogEntry) { - const api = await KubeApi.factory(log, provider.config.context) - const namespace = await getAppNamespace(ctx, log, provider) +export async function checkTillerStatus(ctx: KubernetesPluginContext, log: LogEntry) { + const api = await KubeApi.factory(log, ctx.provider) + const namespace = await getAppNamespace(ctx, log, ctx.provider) const resources = [ ...getRoleResources(namespace), - ...await getTillerResources(ctx, provider, log), + ...await getTillerResources(ctx, log), ] const statuses = await checkResourceStatuses(api, namespace, resources, log) @@ -36,19 +35,18 @@ export async function checkTillerStatus(ctx: PluginContext, provider: Kubernetes } interface InstallTillerParams { - ctx: PluginContext + ctx: KubernetesPluginContext provider: KubernetesProvider log: LogEntry force?: boolean } export async function installTiller({ ctx, log, provider, force = false }: InstallTillerParams) { - if (!force && await checkTillerStatus(ctx, provider, log) === "ready") { + if (!force && await checkTillerStatus(ctx, log) === "ready") { return } const namespace = await getAppNamespace(ctx, log, provider) - const context = provider.config.context const entry = log.info({ section: "tiller", @@ -59,30 +57,31 @@ export async function installTiller({ ctx, log, provider, force = false }: Insta // Need to install the RBAC stuff ahead of Tiller const roleResources = getRoleResources(namespace) entry.setState("Applying Tiller RBAC resources...") - await apply({ log, context, manifests: roleResources, namespace }) + await apply({ log, provider, manifests: roleResources, namespace }) await waitForResources({ ctx, provider, serviceName: "tiller", resources: roleResources, log: entry }) - const tillerResources = await getTillerResources(ctx, provider, log) + const tillerResources = await getTillerResources(ctx, log) const pruneSelector = "app=helm,name=tiller" entry.setState("Deploying Tiller...") - await apply({ log, context, manifests: tillerResources, namespace, pruneSelector }) + await apply({ log, provider, manifests: tillerResources, namespace, pruneSelector }) await waitForResources({ ctx, provider, serviceName: "tiller", resources: tillerResources, log: entry }) entry.setSuccess({ msg: chalk.green(`Done (took ${entry.getDuration(1)} sec)`), append: true }) } async function getTillerResources( - ctx: PluginContext, provider: KubernetesProvider, log: LogEntry, + ctx: KubernetesPluginContext, log: LogEntry, ): Promise { - const namespace = await getAppNamespace(ctx, log, provider) - const context = provider.config.context - - const tillerManifests = await helm(namespace, context, log, - "init", - "--service-account", serviceAccountName, - "--dry-run", - "--debug", - ) + const tillerManifests = await helm({ + ctx, + log, + args: [ + "init", + "--service-account", serviceAccountName, + "--dry-run", + "--debug", + ], + }) const resources = safeLoadAll(tillerManifests) diff --git a/garden-service/src/plugins/kubernetes/init.ts b/garden-service/src/plugins/kubernetes/init.ts index fbb6bb9311..e785a4cd62 100644 --- a/garden-service/src/plugins/kubernetes/init.ts +++ b/garden-service/src/plugins/kubernetes/init.ts @@ -44,7 +44,7 @@ export async function getEnvironmentStatus({ ctx, log }: GetEnvironmentStatusPar const namespaces = await prepareNamespaces({ ctx, log }) // Check Tiller status in project namespace - if (await checkTillerStatus(k8sCtx, k8sCtx.provider, log) !== "ready") { + if (await checkTillerStatus(k8sCtx, log) !== "ready") { projectReady = false } @@ -84,14 +84,14 @@ export async function getEnvironmentStatus({ ctx, log }: GetEnvironmentStatusPar const sysCtx = await sysGarden.getPluginContext(sysProvider) // Check Tiller status in system namespace - const tillerStatus = await checkTillerStatus(sysCtx, sysCtx.provider, log) + const tillerStatus = await checkTillerStatus(sysCtx, log) if (tillerStatus !== "ready") { result.ready = false detail.systemTillerReady = false } - const api = await KubeApi.factory(log, provider.config.context) + const api = await KubeApi.factory(log, provider) const contextForLog = `Checking Garden system service status for plugin "${ctx.provider.name}"` const sysNamespaceUpToDate = await systemNamespaceUpToDate(api, log, systemNamespace, contextForLog) @@ -217,7 +217,7 @@ export async function prepareSystem( export async function cleanupEnvironment({ ctx, log }: CleanupEnvironmentParams) { const k8sCtx = ctx - const api = await KubeApi.factory(log, k8sCtx.provider.config.context) + const api = await KubeApi.factory(log, k8sCtx.provider) const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) const entry = log.info({ section: "kubernetes", diff --git a/garden-service/src/plugins/kubernetes/kubectl.ts b/garden-service/src/plugins/kubernetes/kubectl.ts index ed7003e5b8..d4605c3191 100644 --- a/garden-service/src/plugins/kubernetes/kubectl.ts +++ b/garden-service/src/plugins/kubernetes/kubectl.ts @@ -10,10 +10,11 @@ import * as _spawn from "cross-spawn" import { encodeYamlMulti } from "../../util/util" import { BinaryCmd, ExecParams } from "../../util/ext-tools" import { LogEntry } from "../../logger/log-entry" +import { KubernetesProvider } from "./config" export interface ApplyParams { log: LogEntry, - context: string, + provider: KubernetesProvider manifests: object[], dryRun?: boolean, force?: boolean, @@ -24,7 +25,7 @@ export interface ApplyParams { export const KUBECTL_DEFAULT_TIMEOUT = 300 export async function apply( - { log, context, manifests: objects, dryRun = false, force = false, namespace, pruneSelector }: ApplyParams, + { log, provider, manifests: objects, dryRun = false, force = false, namespace, pruneSelector }: ApplyParams, ) { const input = Buffer.from(encodeYamlMulti(objects)) @@ -34,7 +35,7 @@ export async function apply( pruneSelector && args.push("--prune", "--selector", pruneSelector) args.push("--output=json", "-f", "-") - const result = await kubectl.stdout({ log, context, namespace, args, input }) + const result = await kubectl.stdout({ log, provider, namespace, args, input }) try { return JSON.parse(result) @@ -45,7 +46,7 @@ export async function apply( export interface DeleteObjectsParams { log: LogEntry, - context: string, + provider: KubernetesProvider namespace: string, labelKey: string, labelValue: string, @@ -56,7 +57,7 @@ export interface DeleteObjectsParams { export async function deleteObjectsByLabel( { log, - context, + provider, namespace, labelKey, labelValue, @@ -73,7 +74,7 @@ export async function deleteObjectsByLabel( includeUninitialized && args.push("--include-uninitialized") - const result = await kubectl.stdout({ context, namespace, args, log }) + const result = await kubectl.stdout({ provider, namespace, args, log }) try { return JSON.parse(result) @@ -84,7 +85,7 @@ export async function deleteObjectsByLabel( interface KubectlParams extends ExecParams { log: LogEntry - context?: string + provider: KubernetesProvider namespace?: string configPath?: string args: string[] @@ -127,12 +128,14 @@ class Kubectl extends BinaryCmd { } private prepareArgs(params: KubectlParams) { - const { context, namespace, configPath, args } = params + const { provider, namespace, configPath, args } = params - const opts: string[] = [] + const opts: string[] = [ + `--context=${provider.config.context}`, + ] - if (context) { - opts.push(`--context=${context}`) + if (provider.config.kubeconfig) { + opts.push(`--kubeconfig=${provider.config.kubeconfig}`) } if (namespace) { diff --git a/garden-service/src/plugins/kubernetes/kubernetes-module/handlers.ts b/garden-service/src/plugins/kubernetes/kubernetes-module/handlers.ts index 10229e70bb..fe9b46e0c8 100644 --- a/garden-service/src/plugins/kubernetes/kubernetes-module/handlers.ts +++ b/garden-service/src/plugins/kubernetes/kubernetes-module/handlers.ts @@ -57,8 +57,7 @@ async function getServiceStatus( provider: k8sCtx.provider, skipCreate: true, }) - const context = ctx.provider.config.context - const api = await KubeApi.factory(log, context) + const api = await KubeApi.factory(log, k8sCtx.provider) const manifests = await getManifests(module) const { state, remoteObjects } = await compareDeployedObjects(k8sCtx, api, namespace, manifests, log, false) @@ -85,11 +84,10 @@ async function deployService( provider: k8sCtx.provider, skipCreate: true, }) - const context = ctx.provider.config.context const manifests = await getManifests(module) const pruneSelector = getSelector(service) - await apply({ log, context, manifests, force, namespace, pruneSelector }) + await apply({ log, provider: k8sCtx.provider, manifests, force, namespace, pruneSelector }) await waitForResources({ ctx: k8sCtx, @@ -109,10 +107,9 @@ async function deleteService(params: DeleteServiceParams): Promise) { const { ctx, log, module } = params const k8sCtx = ctx - const context = k8sCtx.provider.config.context - const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) + const provider = k8sCtx.provider + const namespace = await getAppNamespace(k8sCtx, log, provider) const manifests = await getManifests(module) - return getAllLogs({ ...params, context, namespace, resources: manifests }) + return getAllLogs({ ...params, provider, namespace, resources: manifests }) } function getSelector(service: KubernetesService) { diff --git a/garden-service/src/plugins/kubernetes/kubernetes.ts b/garden-service/src/plugins/kubernetes/kubernetes.ts index 09bdb7b1e2..799ab997e2 100644 --- a/garden-service/src/plugins/kubernetes/kubernetes.ts +++ b/garden-service/src/plugins/kubernetes/kubernetes.ts @@ -27,10 +27,13 @@ import { clusterInit } from "./commands/cluster-init" import { uninstallGardenServices } from "./commands/uninstall-garden-services" import chalk from "chalk" import { joi, joiIdentifier } from "../../config/common" +import { resolve } from "path" export const name = "kubernetes" -export async function configureProvider({ projectName, config }: ConfigureProviderParams) { +export async function configureProvider( + { projectName, projectRoot, config }: ConfigureProviderParams, +) { config._systemServices = [] if (!config.namespace) { @@ -69,22 +72,26 @@ export async function configureProvider({ projectName, config }: ConfigureProvid ) } + if (config.kubeconfig) { + config.kubeconfig = resolve(projectRoot, config.kubeconfig) + } + return { name: config.name, config } } export async function debugInfo({ ctx, log, includeProject }: GetDebugInfoParams): Promise { - const k8sContext = ctx - const { context } = k8sContext.provider.config + const k8sCtx = ctx + const provider = k8sCtx.provider const entry = log.info({ section: ctx.provider.name, msg: "collecting provider configuration", status: "active" }) const namespacesList = [systemNamespace, systemMetadataNamespace] if (includeProject) { - const appNamespace = await getAppNamespace(k8sContext, log, k8sContext.provider) - const appMetadataNamespace = await getMetadataNamespace(k8sContext, log, k8sContext.provider) + const appNamespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) + const appMetadataNamespace = await getMetadataNamespace(k8sCtx, log, k8sCtx.provider) namespacesList.push(appNamespace, appMetadataNamespace) } const namespaces = await Bluebird.map(namespacesList, async (ns) => { const nsEntry = entry.info({ section: ns, msg: "collecting namespace configuration", status: "active" }) - const out = await kubectl.stdout({ log, context, args: ["get", "all", "--namespace", ns, "--output", "json"] }) + const out = await kubectl.stdout({ log, provider, args: ["get", "all", "--namespace", ns, "--output", "json"] }) nsEntry.setSuccess({ msg: chalk.green(`Done (took ${log.getDuration(1)} sec)`), append: true }) return { namespace: ns, @@ -93,7 +100,7 @@ export async function debugInfo({ ctx, log, includeProject }: GetDebugInfoParams }) entry.setSuccess({ msg: chalk.green(`Done (took ${log.getDuration(1)} sec)`), append: true }) - const version = await kubectl.stdout({ log, context, args: ["version", "--output", "json"] }) + const version = await kubectl.stdout({ log, provider, args: ["version", "--output", "json"] }) return { info: { version: JSON.parse(version), namespaces }, diff --git a/garden-service/src/plugins/kubernetes/local/config.ts b/garden-service/src/plugins/kubernetes/local/config.ts index a2e8859e44..62844f1396 100644 --- a/garden-service/src/plugins/kubernetes/local/config.ts +++ b/garden-service/src/plugins/kubernetes/local/config.ts @@ -54,7 +54,15 @@ export async function configureProvider({ config, log, projectName }: ConfigureP if (!context) { // automatically detect supported kubectl context if not explicitly configured - const kubeConfig = await getKubeConfig(log) + // create dummy provider with just enough info needed for the getKubeConfig function + const provider = { + name: config.name, + dependencies: [], + config, + moduleConfigs: [], + status: { ready: true, outputs: {} }, + } + const kubeConfig = await getKubeConfig(log, provider) const currentContext = kubeConfig["current-context"] if (currentContext && supportedContexts.includes(currentContext)) { diff --git a/garden-service/src/plugins/kubernetes/logs.ts b/garden-service/src/plugins/kubernetes/logs.ts index 5da2fe3307..92cfcb1398 100644 --- a/garden-service/src/plugins/kubernetes/logs.ts +++ b/garden-service/src/plugins/kubernetes/logs.ts @@ -20,11 +20,12 @@ import { Service } from "../../types/service" import Stream from "ts-stream" import { LogEntry } from "../../logger/log-entry" import * as Bluebird from "bluebird" +import { KubernetesProvider } from "./config" interface GetLogsBaseParams { log: LogEntry - context: string namespace: string + provider: KubernetesProvider service: Service stream: Stream follow: boolean @@ -64,12 +65,12 @@ export async function getPodLogs(params: GetPodLogsParams) { * Stream all logs for the given resources and service. */ export async function getAllLogs(params: GetAllLogsParams) { - const api = await KubeApi.factory(params.log, params.context) + const api = await KubeApi.factory(params.log, params.provider) const podNames = await getAllPodNames(api, params.namespace, params.resources) return getPodLogs({ ...params, podNames }) } -async function getLogs({ log, context, namespace, service, stream, tail, follow, podName }: GetLogsParams) { +async function getLogs({ log, provider, namespace, service, stream, tail, follow, podName }: GetLogsParams) { // TODO: do this via API instead of kubectl const kubectlArgs = [ "logs", @@ -84,7 +85,7 @@ async function getLogs({ log, context, namespace, service, stream, tail, follow, kubectlArgs.push(`pod/${podName}`) - const proc = await kubectl.spawn({ log, context, namespace, args: kubectlArgs }) + const proc = await kubectl.spawn({ log, provider, namespace, args: kubectlArgs }) let timestamp: Date proc.stdout! diff --git a/garden-service/src/plugins/kubernetes/namespace.ts b/garden-service/src/plugins/kubernetes/namespace.ts index bf0e91fa23..3b2b0c82ac 100644 --- a/garden-service/src/plugins/kubernetes/namespace.ts +++ b/garden-service/src/plugins/kubernetes/namespace.ts @@ -76,7 +76,7 @@ export async function getNamespace( } if (!skipCreate) { - const api = await KubeApi.factory(log, provider.config.context) + const api = await KubeApi.factory(log, provider) await ensureNamespace(api, namespace) } @@ -111,11 +111,10 @@ export async function getAllNamespaces(api: KubeApi): Promise { */ export async function prepareNamespaces({ ctx, log }: GetEnvironmentStatusParams) { const k8sCtx = ctx - const kubeContext = k8sCtx.provider.config.context try { // TODO: use API instead of kubectl (I just couldn't find which API call to make) - await kubectl.exec({ log, context: kubeContext, args: ["version"] }) + await kubectl.exec({ log, provider: k8sCtx.provider, args: ["version"] }) } catch (err) { let message = err.message if (err.stdout) { @@ -128,7 +127,7 @@ export async function prepareNamespaces({ ctx, log }: GetEnvironmentStatusParams `Unable to connect to Kubernetes cluster. ` + `Please make sure it is running, reachable and that you have the right context configured.`, { - kubeContext, + providerConfig: k8sCtx.provider.config, message, }, ) diff --git a/garden-service/src/plugins/kubernetes/port-forward.ts b/garden-service/src/plugins/kubernetes/port-forward.ts index 018f1a5f13..6271d5dc13 100644 --- a/garden-service/src/plugins/kubernetes/port-forward.ts +++ b/garden-service/src/plugins/kubernetes/port-forward.ts @@ -89,7 +89,7 @@ export async function getPortForward( const portForwardArgs = ["port-forward", targetResource, portMapping] log.silly(`Running 'kubectl ${portForwardArgs.join(" ")}'`) - const proc = await kubectl.spawn({ log, context: k8sCtx.provider.config.context, namespace, args: portForwardArgs }) + const proc = await kubectl.spawn({ log, provider: k8sCtx.provider, namespace, args: portForwardArgs }) return new Promise((resolve) => { proc.on("error", (error) => { diff --git a/garden-service/src/plugins/kubernetes/run.ts b/garden-service/src/plugins/kubernetes/run.ts index 18200374af..123e643a93 100644 --- a/garden-service/src/plugins/kubernetes/run.ts +++ b/garden-service/src/plugins/kubernetes/run.ts @@ -12,9 +12,10 @@ import { Module } from "../../types/module" import { LogEntry } from "../../logger/log-entry" import { V1PodSpec } from "@kubernetes/client-node" import { PluginError } from "../../exceptions" +import { KubernetesProvider } from "./config" interface RunPodParams { - context: string, + provider: KubernetesProvider, image: string, interactive: boolean, ignoreError: boolean, @@ -29,7 +30,7 @@ interface RunPodParams { export async function runPod( { - context, + provider, ignoreError, image, interactive, @@ -93,7 +94,7 @@ export async function runPod( const res = await kubectl.spawnAndWait({ log, - context, + provider, namespace, ignoreError, args: kubecmd, diff --git a/garden-service/src/plugins/kubernetes/secrets.ts b/garden-service/src/plugins/kubernetes/secrets.ts index 137646cb12..e86657b0df 100644 --- a/garden-service/src/plugins/kubernetes/secrets.ts +++ b/garden-service/src/plugins/kubernetes/secrets.ts @@ -20,7 +20,7 @@ import { pick } from "lodash" export async function getSecret({ ctx, log, key }: GetSecretParams) { const k8sCtx = ctx - const api = await KubeApi.factory(log, k8sCtx.provider.config.context) + const api = await KubeApi.factory(log, k8sCtx.provider) const ns = await getMetadataNamespace(k8sCtx, log, k8sCtx.provider) try { @@ -38,7 +38,7 @@ export async function getSecret({ ctx, log, key }: GetSecretParams) { export async function setSecret({ ctx, log, key, value }: SetSecretParams) { // we store configuration in a separate metadata namespace, so that configs aren't cleared when wiping the namespace const k8sCtx = ctx - const api = await KubeApi.factory(log, k8sCtx.provider.config.context) + const api = await KubeApi.factory(log, k8sCtx.provider) const ns = await getMetadataNamespace(k8sCtx, log, k8sCtx.provider) const body = { body: { @@ -70,7 +70,7 @@ export async function setSecret({ ctx, log, key, value }: SetSecretParams) { export async function deleteSecret({ ctx, log, key }: DeleteSecretParams) { const k8sCtx = ctx - const api = await KubeApi.factory(log, k8sCtx.provider.config.context) + const api = await KubeApi.factory(log, k8sCtx.provider) const ns = await getMetadataNamespace(k8sCtx, log, k8sCtx.provider) try { diff --git a/garden-service/src/plugins/kubernetes/status/status.ts b/garden-service/src/plugins/kubernetes/status/status.ts index 556fd1d706..c85efee4c7 100644 --- a/garden-service/src/plugins/kubernetes/status/status.ts +++ b/garden-service/src/plugins/kubernetes/status/status.ts @@ -168,7 +168,7 @@ export async function waitForResources({ ctx, provider, serviceName, resources, msg: `Waiting for resources to be ready...`, }) - const api = await KubeApi.factory(log, provider.config.context) + const api = await KubeApi.factory(log, provider) const namespace = await getAppNamespace(ctx, log, provider) while (true) { @@ -273,10 +273,10 @@ export async function compareDeployedObjects( // with exit code 1 if there is a mismatch, but may also fail with the same exit code for a number of other reasons, // including the cluster not supporting dry-runs, certain CRDs not supporting dry-runs etc. const yamlResources = await encodeYamlMulti(resources) - const context = ctx.provider.config.context + const provider = ctx.provider try { - await kubectl.exec({ log, context, namespace, args: ["diff", "-f", "-"], input: Buffer.from(yamlResources) }) + await kubectl.exec({ log, provider, namespace, args: ["diff", "-f", "-"], input: Buffer.from(yamlResources) }) // If the commands exits succesfully, the check was successful and the diff is empty. log.verbose(`kubectl diff indicates all resources match the deployed resources.`) @@ -384,7 +384,7 @@ export async function compareDeployedObjects( async function getDeployedResource( ctx: PluginContext, provider: KubernetesProvider, resource: KubernetesResource, log: LogEntry, ): Promise { - const api = await KubeApi.factory(log, provider.config.context) + const api = await KubeApi.factory(log, provider) const namespace = resource.metadata.namespace || await getAppNamespace(ctx, log, provider) try { diff --git a/garden-service/src/plugins/kubernetes/system.ts b/garden-service/src/plugins/kubernetes/system.ts index 99e9c5dcc8..bd04c4f0df 100644 --- a/garden-service/src/plugins/kubernetes/system.ts +++ b/garden-service/src/plugins/kubernetes/system.ts @@ -188,7 +188,7 @@ interface PrepareSystemServicesParams extends GetSystemServicesStatusParams { export async function prepareSystemServices( { ctx, sysGarden, log, namespace, serviceNames, force }: PrepareSystemServicesParams, ) { - const api = await KubeApi.factory(log, ctx.provider.config.context) + const api = await KubeApi.factory(log, ctx.provider) const contextForLog = `Preparing environment for plugin "${ctx.provider.name}"` const outdated = !(await systemNamespaceUpToDate(api, log, namespace, contextForLog)) diff --git a/garden-service/src/plugins/kubernetes/task-results.ts b/garden-service/src/plugins/kubernetes/task-results.ts index 536bd77003..86b450159a 100644 --- a/garden-service/src/plugins/kubernetes/task-results.ts +++ b/garden-service/src/plugins/kubernetes/task-results.ts @@ -27,7 +27,7 @@ export async function getTaskResult( { ctx, log, module, task, taskVersion }: GetTaskResultParams, ): Promise { const k8sCtx = ctx - const api = await KubeApi.factory(log, k8sCtx.provider.config.context) + const api = await KubeApi.factory(log, k8sCtx.provider) const ns = await getMetadataNamespace(k8sCtx, log, k8sCtx.provider) const resultKey = getTaskResultKey(ctx, module, task.name, taskVersion) @@ -86,7 +86,7 @@ export async function storeTaskResult( { ctx, log, module, taskName, taskVersion, result }: StoreTaskResultParams, ) { const provider = ctx.provider - const api = await KubeApi.factory(log, provider.config.context) + const api = await KubeApi.factory(log, provider) const namespace = await getMetadataNamespace(ctx, log, provider) result = trimRunOutput(result) diff --git a/garden-service/src/plugins/kubernetes/test-results.ts b/garden-service/src/plugins/kubernetes/test-results.ts index 6aa671eaab..5a6ac99c0e 100644 --- a/garden-service/src/plugins/kubernetes/test-results.ts +++ b/garden-service/src/plugins/kubernetes/test-results.ts @@ -29,7 +29,7 @@ export async function getTestResult( { ctx, log, module, testName, testVersion }: GetTestResultParams, ): Promise { const k8sCtx = ctx - const api = await KubeApi.factory(log, k8sCtx.provider.config.context) + const api = await KubeApi.factory(log, k8sCtx.provider) const resultKey = getTestResultKey(k8sCtx, module, testName, testVersion) try { @@ -75,7 +75,7 @@ export async function storeTestResult( { ctx, log, module, testName, testVersion, result }: StoreTestResultParams, ): Promise { const k8sCtx = ctx - const api = await KubeApi.factory(log, k8sCtx.provider.config.context) + const api = await KubeApi.factory(log, k8sCtx.provider) const testResult: TestResult = { ...trimRunOutput(result), diff --git a/garden-service/src/plugins/openfaas/openfaas.ts b/garden-service/src/plugins/openfaas/openfaas.ts index 6a24791a68..e58522041d 100644 --- a/garden-service/src/plugins/openfaas/openfaas.ts +++ b/garden-service/src/plugins/openfaas/openfaas.ts @@ -186,14 +186,13 @@ async function configureProvider( async function getServiceLogs(params: GetServiceLogsParams) { const { ctx, log, service } = params - const k8sProvider = getK8sProvider(ctx.provider.dependencies) - const context = k8sProvider.config.context - const namespace = await getAppNamespace(ctx, log, k8sProvider) + const provider = getK8sProvider(ctx.provider.dependencies) + const namespace = await getAppNamespace(ctx, log, provider) - const api = await KubeApi.factory(log, k8sProvider.config.context) + const api = await KubeApi.factory(log, provider) const resources = await getResources(api, service, namespace) - return getAllLogs({ ...params, context, namespace, resources }) + return getAllLogs({ ...params, provider, namespace, resources }) } async function deployService(params: DeployServiceParams): Promise { @@ -212,7 +211,7 @@ async function deployService(params: DeployServiceParams): Promi // wait until deployment is ready const namespace = await getAppNamespace(ctx, log, k8sProvider) - const api = await KubeApi.factory(log, k8sProvider.config.context) + const api = await KubeApi.factory(log, k8sProvider) const resources = await getResources(api, service, namespace) await waitForResources({ @@ -281,7 +280,7 @@ async function getServiceStatus({ ctx, module, service, log }: GetServiceStatusP }] const namespace = await getAppNamespace(openFaasCtx, log, k8sProvider) - const api = await KubeApi.factory(log, k8sProvider.config.context) + const api = await KubeApi.factory(log, k8sProvider) let deployment: KubernetesDeployment diff --git a/garden-service/src/tasks/resolve-provider.ts b/garden-service/src/tasks/resolve-provider.ts index 7f8da438bd..6a7d602f40 100644 --- a/garden-service/src/tasks/resolve-provider.ts +++ b/garden-service/src/tasks/resolve-provider.ts @@ -88,7 +88,6 @@ export class ResolveProviderTask extends BaseTask { this.log.silly(`Resolving template strings for plugin ${this.config.name}`) let resolvedConfig = await resolveTemplateStrings(this.config, context) - resolvedConfig.path = this.garden.projectRoot const providerName = resolvedConfig.name this.log.silly(`Validating ${providerName} config`) @@ -96,13 +95,15 @@ export class ResolveProviderTask extends BaseTask { resolvedConfig = validateWithPath({ config: resolvedConfig, schema: this.plugin.configSchema, - path: resolvedConfig.path, + path: this.garden.projectRoot, projectRoot: this.garden.projectRoot, configType: "provider", ErrorClass: ConfigurationError, }) } + resolvedConfig.path = this.garden.projectRoot + const configureHandler = (this.plugin.actions || {}).configureProvider let moduleConfigs: ModuleConfig[] = [] @@ -115,6 +116,7 @@ export class ResolveProviderTask extends BaseTask { config: resolvedConfig, configStore: this.garden.configStore, projectName: this.garden.projectName, + projectRoot: this.garden.projectRoot, dependencies: resolvedProviders, }) diff --git a/garden-service/src/types/plugin/provider/configureProvider.ts b/garden-service/src/types/plugin/provider/configureProvider.ts index 31d13e7600..6789bd8b03 100644 --- a/garden-service/src/types/plugin/provider/configureProvider.ts +++ b/garden-service/src/types/plugin/provider/configureProvider.ts @@ -20,6 +20,7 @@ export interface ConfigureProviderParams { config: T log: LogEntry projectName: string + projectRoot: string dependencies: Provider[] configStore: ConfigStore } diff --git a/garden-service/src/util/ext-tools.ts b/garden-service/src/util/ext-tools.ts index 39ef163d80..7708413c37 100644 --- a/garden-service/src/util/ext-tools.ts +++ b/garden-service/src/util/ext-tools.ts @@ -255,13 +255,14 @@ export class BinaryCmd extends Library { return path } - async exec({ args, cwd, env, log, timeout, input }: ExecParams) { + async exec({ args, cwd, env, log, timeout, input, ignoreError }: ExecParams) { const path = await this.getPath(log) return execa(path, args || [], { cwd: cwd || dirname(path), timeout: this.getTimeout(timeout) * 1000, env, input, + reject: !ignoreError, }) } diff --git a/garden-service/test/unit/src/plugins/kubernetes/container/ingress.ts b/garden-service/test/unit/src/plugins/kubernetes/container/ingress.ts index 1f3c72f155..81a6bf6165 100644 --- a/garden-service/test/unit/src/plugins/kubernetes/container/ingress.ts +++ b/garden-service/test/unit/src/plugins/kubernetes/container/ingress.ts @@ -396,8 +396,8 @@ describe("createIngressResources", () => { } } - async function getKubeApi(context: string) { - const api = await KubeApi.factory(garden.log, context) + async function getKubeApi(provider: KubernetesProvider) { + const api = await KubeApi.factory(garden.log, provider) const core = td.replace(api, "core") td.when(core.readNamespacedSecret("somesecret", "somenamespace")).thenResolve(myDomainCertSecret) @@ -416,7 +416,7 @@ describe("createIngressResources", () => { port: "http", }) - const api = await getKubeApi(basicProvider.config.context) + const api = await getKubeApi(basicProvider) const ingresses = await createIngressResources(api, basicProvider, namespace, service) expect(ingresses).to.eql([{ @@ -458,7 +458,7 @@ describe("createIngressResources", () => { port: "http", }) - const api = await getKubeApi(basicProvider.config.context) + const api = await getKubeApi(basicProvider) const ingresses = await createIngressResources(api, basicProvider, namespace, service) expect(ingresses).to.eql([{ @@ -509,7 +509,7 @@ describe("createIngressResources", () => { }, ) - const api = await getKubeApi(basicProvider.config.context) + const api = await getKubeApi(basicProvider) const ingresses = await createIngressResources(api, basicProvider, namespace, service) expect(ingresses).to.eql([ @@ -581,7 +581,7 @@ describe("createIngressResources", () => { }, ) - const api = await getKubeApi(singleTlsProvider.config.context) + const api = await getKubeApi(singleTlsProvider) const ingresses = await createIngressResources(api, singleTlsProvider, namespace, service) td.verify(api.upsert("Secret", namespace, myDomainCertSecret)) @@ -630,7 +630,7 @@ describe("createIngressResources", () => { }, ) - const api = await getKubeApi(basicConfig.context) + const api = await getKubeApi(basicProvider) const provider = { name: "kubernetes", @@ -676,7 +676,7 @@ describe("createIngressResources", () => { status: { ready: true, outputs: {} }, } - const api = await getKubeApi(basicConfig.context) + const api = await getKubeApi(basicProvider) const err: any = new Error("nope") err.code = 404 @@ -708,7 +708,7 @@ describe("createIngressResources", () => { status: { ready: true, outputs: {} }, } - const api = await getKubeApi(basicConfig.context) + const api = await getKubeApi(basicProvider) const err: any = new Error("nope") err.code = 404 @@ -731,7 +731,7 @@ describe("createIngressResources", () => { }, ) - const api = await getKubeApi(multiTlsProvider.config.context) + const api = await getKubeApi(multiTlsProvider) const ingresses = await createIngressResources(api, multiTlsProvider, namespace, service) td.verify(api.upsert("Secret", namespace, wildcardDomainCertSecret)) @@ -781,7 +781,7 @@ describe("createIngressResources", () => { }, ) - const api = await getKubeApi(basicConfig.context) + const api = await getKubeApi(basicProvider) const provider = { name: "kubernetes",