From 9cc6130d782ab0575fe87fd578beac49e9498908 Mon Sep 17 00:00:00 2001 From: Jon Edvald Date: Wed, 17 Apr 2019 17:18:44 +0200 Subject: [PATCH] fix(k8s): allow multiple paths in KUBECONFIG env var We now use kubectl to merge them, to keep things nice and consistent. Closes #711 --- garden-service/src/plugins/kubernetes/api.ts | 93 ++++++++++--------- .../kubernetes/container/deployment.ts | 13 +-- .../src/plugins/kubernetes/container/logs.ts | 4 +- .../src/plugins/kubernetes/container/run.ts | 9 +- .../plugins/kubernetes/container/status.ts | 6 +- .../src/plugins/kubernetes/container/test.ts | 2 +- .../src/plugins/kubernetes/helm/build.ts | 2 +- .../src/plugins/kubernetes/helm/common.ts | 4 +- .../src/plugins/kubernetes/helm/deployment.ts | 4 +- .../src/plugins/kubernetes/helm/logs.ts | 2 +- .../src/plugins/kubernetes/helm/run.ts | 5 +- .../src/plugins/kubernetes/helm/status.ts | 4 +- .../src/plugins/kubernetes/helm/test.ts | 4 +- .../src/plugins/kubernetes/helm/tiller.ts | 8 +- .../src/plugins/kubernetes/hot-reload.ts | 2 +- garden-service/src/plugins/kubernetes/init.ts | 18 ++-- .../src/plugins/kubernetes/kubectl.ts | 8 +- .../kubernetes/kubernetes-module/handlers.ts | 12 +-- .../src/plugins/kubernetes/local/config.ts | 23 ++--- garden-service/src/plugins/kubernetes/logs.ts | 2 +- .../src/plugins/kubernetes/namespace.ts | 15 +-- .../src/plugins/kubernetes/secrets.ts | 18 ++-- .../src/plugins/kubernetes/status.ts | 8 +- .../src/plugins/kubernetes/task-results.ts | 22 +++-- garden-service/src/plugins/kubernetes/test.ts | 19 +++- .../src/plugins/openfaas/openfaas.ts | 45 ++++----- garden-service/test/integ-helpers.ts | 6 +- garden-service/test/integ/src/pre-release.ts | 4 +- .../plugins/kubernetes/container/ingress.ts | 22 ++--- 29 files changed, 206 insertions(+), 178 deletions(-) diff --git a/garden-service/src/plugins/kubernetes/api.ts b/garden-service/src/plugins/kubernetes/api.ts index 7438c911e9..3d0b80e71a 100644 --- a/garden-service/src/plugins/kubernetes/api.ts +++ b/garden-service/src/plugins/kubernetes/api.ts @@ -16,22 +16,16 @@ import { V1Secret, Policy_v1beta1Api, } from "@kubernetes/client-node" -import { join } from "path" import request = require("request-promise") -import { readFileSync, pathExistsSync } from "fs-extra" -import { safeLoad } from "js-yaml" +import { safeLoad, safeDump } from "js-yaml" import { zip, omitBy, isObject } from "lodash" -import { GardenBaseError } from "../../exceptions" -import { homedir } from "os" +import { GardenBaseError, RuntimeError, ConfigurationError } from "../../exceptions" import { KubernetesResource } from "./types" -import * as dedent from "dedent" import { LogEntry } from "../../logger/log-entry" import { splitLast, findByName } from "../../util/util" +import { kubectl } from "./kubectl" -let kubeConfigStr: string -let kubeConfig: any - -const configs: { [context: string]: KubeConfig } = {} +const cachedConfigs: { [context: string]: KubeConfig } = {} // NOTE: be warned, the API of the client library is very likely to change @@ -73,8 +67,6 @@ export class KubernetesError extends GardenBaseError { } export class KubeApi { - private config: KubeConfig - public apiExtensions: Apiextensions_v1beta1Api public apps: Apps_v1Api public core: Core_v1Api @@ -82,15 +74,27 @@ export class KubeApi { public policy: Policy_v1beta1Api public rbac: RbacAuthorization_v1Api - constructor(public context: string) { - this.config = getConfig(this.context) + constructor(public context: string, private config: KubeConfig) { + const cluster = this.config.getCurrentCluster() + + if (!cluster) { + throw new ConfigurationError(`Could not read cluster from kubeconfig for context ${context}`, { + context, + config, + }) + } for (const [name, cls] of Object.entries(apiTypes)) { - const api = new cls(this.config.getCurrentCluster()!.server) + const api = new cls(cluster.server) this[name] = this.proxyApi(api, this.config) } } + static async factory(log: LogEntry, context: string) { + const config = await getContextConfig(log, context) + return new KubeApi(context, config) + } + async readBySpec(namespace: string, spec: KubernetesResource, log: LogEntry) { // this is just awful, sorry. any better ideas? - JE const name = spec.metadata.name @@ -245,7 +249,7 @@ export class KubeApi { /** * Wrapping the API objects to deal with bugs. */ - private proxyApi(api: T, config): T { + private proxyApi(api: T, config: KubeConfig): T { api.setDefaultAuthentication(config) return new Proxy(api, { @@ -254,7 +258,7 @@ export class KubeApi { return Reflect.get(target, name, receiver) } - return function(...args) { + return function (...args) { const defaultHeaders = target["defaultHeaders"] if (name.startsWith("patch")) { @@ -277,41 +281,42 @@ export class KubeApi { } } -function getConfig(context: string): KubeConfig { - const kubeConfigPath = process.env.KUBECONFIG || join(homedir(), ".kube", "config") +export async function getKubeConfig(log: LogEntry) { + let kubeConfigStr: string - if (pathExistsSync(kubeConfigPath)) { - kubeConfigStr = readFileSync(kubeConfigPath).toString() - } else { - // Fall back to a blank kubeconfig if none is found - kubeConfigStr = dedent` - apiVersion: v1 - kind: Config - clusters: [] - contexts: [] - preferences: {} - users: [] - ` + 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"] }) + return safeLoad(kubeConfigStr) + } catch (error) { + throw new RuntimeError(`Unable to load kubeconfig: ${error}`, { + error, + }) } - kubeConfig = safeLoad(kubeConfigStr) +} - if (!configs[context]) { - const kc = new KubeConfig() +async function getContextConfig(log: LogEntry, context: string): Promise { + if (cachedConfigs[context]) { + return cachedConfigs[context] + } - kc.loadFromString(kubeConfigStr) - kc.setCurrentContext(context) + const rawConfig = await getKubeConfig(log) + const kc = new KubeConfig() - // FIXME: need to patch a bug in the library here (https://github.com/kubernetes-client/javascript/pull/54) - for (const [a, b] of zip(kubeConfig["clusters"] || [], kc.clusters)) { - if (a && a["cluster"]["insecure-skip-tls-verify"] === true) { - (b).skipTLSVerify = true - } - } + // There doesn't appear to be a method to just load the parsed config :/ + kc.loadFromString(safeDump(rawConfig)) + kc.setCurrentContext(context) - configs[context] = kc + // FIXME: need to patch a bug in the library here (https://github.com/kubernetes-client/javascript/pull/54) + for (const [a, b] of zip(rawConfig["clusters"] || [], kc.clusters)) { + if (a && a["cluster"]["insecure-skip-tls-verify"] === true) { + (b).skipTLSVerify = true + } } - return configs[context] + cachedConfigs[context] = kc + + return kc } function wrapError(err) { diff --git a/garden-service/src/plugins/kubernetes/container/deployment.ts b/garden-service/src/plugins/kubernetes/container/deployment.ts index 2a70265e2b..955f7f8636 100644 --- a/garden-service/src/plugins/kubernetes/container/deployment.ts +++ b/garden-service/src/plugins/kubernetes/container/deployment.ts @@ -35,8 +35,8 @@ export async function deployContainerService(params: DeployServiceParamsctx - const namespace = await getAppNamespace(k8sCtx, k8sCtx.provider) - const manifests = await createContainerObjects(k8sCtx, service, runtimeContext, hotReload) + const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) + const manifests = await createContainerObjects(k8sCtx, log, service, runtimeContext, hotReload) // TODO: use Helm instead of kubectl apply const context = k8sCtx.provider.config.context @@ -57,6 +57,7 @@ export async function deployContainerService(params: DeployServiceParamsctx const version = service.module.version const provider = k8sCtx.provider - const namespace = await getAppNamespace(k8sCtx, provider) - const api = new KubeApi(provider.config.context) + const namespace = await getAppNamespace(k8sCtx, log, provider) + const api = await KubeApi.factory(log, provider.config.context) const ingresses = await createIngressResources(api, provider, namespace, service) const deployment = await createDeployment(provider, service, runtimeContext, namespace, enableHotReload) const kubeservices = await createServiceResources(service, namespace) @@ -381,7 +382,7 @@ export function rsyncTargetPath(path: string) { export async function deleteService(params: DeleteServiceParams): Promise { const { ctx, log, service } = params const k8sCtx = ctx - const namespace = await getAppNamespace(k8sCtx, k8sCtx.provider) + const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) const provider = k8sCtx.provider const context = provider.config.context @@ -404,7 +405,7 @@ export async function deleteContainerDeployment( ) { let found = true - const api = new KubeApi(context) + const api = await KubeApi.factory(log, context) try { await api.extensions.deleteNamespacedDeployment(serviceName, namespace, {}) diff --git a/garden-service/src/plugins/kubernetes/container/logs.ts b/garden-service/src/plugins/kubernetes/container/logs.ts index 57f21751af..b898f891f5 100644 --- a/garden-service/src/plugins/kubernetes/container/logs.ts +++ b/garden-service/src/plugins/kubernetes/container/logs.ts @@ -14,10 +14,10 @@ import { KubernetesPluginContext } from "../kubernetes" import { createDeployment } from "./deployment" export async function getServiceLogs(params: GetServiceLogsParams) { - const { ctx, service } = params + const { ctx, log, service } = params const k8sCtx = ctx const context = k8sCtx.provider.config.context - const namespace = await getAppNamespace(k8sCtx, k8sCtx.provider) + const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) const resources = [await createDeployment(k8sCtx.provider, service, params.runtimeContext, namespace, false)] diff --git a/garden-service/src/plugins/kubernetes/container/run.ts b/garden-service/src/plugins/kubernetes/container/run.ts index a39b2c64cf..a1babfb569 100644 --- a/garden-service/src/plugins/kubernetes/container/run.ts +++ b/garden-service/src/plugins/kubernetes/container/run.ts @@ -29,9 +29,9 @@ export async function execInService(params: ExecInServiceParams const { ctx, log, service, command, interactive } = params const k8sCtx = ctx const provider = k8sCtx.provider - const api = new KubeApi(provider.config.context) + const api = await KubeApi.factory(log, provider.config.context) const status = await getContainerServiceStatus({ ...params, hotReload: false }) - const namespace = await getAppNamespace(k8sCtx, k8sCtx.provider) + const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) // TODO: this check should probably live outside of the plugin if (!includes(["ready", "outdated"], status.state)) { @@ -88,7 +88,7 @@ export async function runContainerModule( ): Promise { const provider = ctx.provider const context = provider.config.context - const namespace = await getAppNamespace(ctx, provider) + const namespace = await getAppNamespace(ctx, log, provider) const image = await containerHelpers.getDeploymentImageId(module, provider.config.deploymentRegistry) return runPod({ @@ -126,7 +126,7 @@ export async function runContainerTask( const provider = ctx.provider const context = provider.config.context - const namespace = await getAppNamespace(ctx, provider) + const namespace = await getAppNamespace(ctx, log, provider) const image = await containerHelpers.getDeploymentImageId(module, provider.config.deploymentRegistry) const res = await runPod({ @@ -148,6 +148,7 @@ export async function runContainerTask( await storeTaskResult({ ctx, + log, result, taskVersion, taskName: task.name, diff --git a/garden-service/src/plugins/kubernetes/container/status.ts b/garden-service/src/plugins/kubernetes/container/status.ts index a4d3148881..87a6fa0261 100644 --- a/garden-service/src/plugins/kubernetes/container/status.ts +++ b/garden-service/src/plugins/kubernetes/container/status.ts @@ -29,11 +29,11 @@ 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 = new KubeApi(provider.config.context) - const namespace = await getAppNamespace(k8sCtx, provider) + const api = await KubeApi.factory(log, provider.config.context) + const namespace = await getAppNamespace(k8sCtx, log, provider) // FIXME: [objects, matched] and ingresses can be run in parallel - const objects = await createContainerObjects(k8sCtx, service, runtimeContext, hotReload) + const objects = await createContainerObjects(k8sCtx, log, service, runtimeContext, hotReload) const { state, remoteObjects } = await compareDeployedObjects(k8sCtx, api, namespace, objects, log, true) const ingresses = await getIngresses(service, api, provider) diff --git a/garden-service/src/plugins/kubernetes/container/test.ts b/garden-service/src/plugins/kubernetes/container/test.ts index b7fed88b01..5f02ae219a 100644 --- a/garden-service/src/plugins/kubernetes/container/test.ts +++ b/garden-service/src/plugins/kubernetes/container/test.ts @@ -32,5 +32,5 @@ export async function testContainerModule( log, }) - return storeTestResult({ ctx, module, testName, testVersion, result }) + return storeTestResult({ ctx, log, module, testName, testVersion, result }) } diff --git a/garden-service/src/plugins/kubernetes/helm/build.ts b/garden-service/src/plugins/kubernetes/helm/build.ts index 2826dd2253..bc8609a600 100644 --- a/garden-service/src/plugins/kubernetes/helm/build.ts +++ b/garden-service/src/plugins/kubernetes/helm/build.ts @@ -20,7 +20,7 @@ import { KubernetesPluginContext } from "../kubernetes" export async function buildHelmModule({ ctx, module, log }: BuildModuleParams): Promise { const k8sCtx = ctx - const namespace = await getNamespace({ ctx: k8sCtx, provider: k8sCtx.provider, skipCreate: true }) + const namespace = await getNamespace({ ctx: k8sCtx, log, provider: k8sCtx.provider, skipCreate: true }) const context = ctx.provider.config.context const baseModule = getBaseModule(module) diff --git a/garden-service/src/plugins/kubernetes/helm/common.ts b/garden-service/src/plugins/kubernetes/helm/common.ts index 5dc8a43efc..e5d262b735 100644 --- a/garden-service/src/plugins/kubernetes/helm/common.ts +++ b/garden-service/src/plugins/kubernetes/helm/common.ts @@ -42,7 +42,7 @@ export async function getChartResources(ctx: PluginContext, module: Module, log: const k8sCtx = ctx const chartPath = await getChartPath(module) const valuesPath = getValuesPath(chartPath) - const namespace = await getNamespace({ ctx: k8sCtx, provider: k8sCtx.provider, skipCreate: true }) + const namespace = await getNamespace({ ctx: k8sCtx, log, provider: k8sCtx.provider, skipCreate: true }) const context = ctx.provider.config.context const releaseName = getReleaseName(module) @@ -269,7 +269,7 @@ async function renderHelmTemplateString( const tempFilePath = join(chartPath, "templates", cryptoRandomString(16)) const valuesPath = getValuesPath(chartPath) const k8sCtx = ctx - const namespace = await getNamespace({ ctx: k8sCtx, provider: k8sCtx.provider, skipCreate: true }) + const namespace = await getNamespace({ ctx: k8sCtx, log, provider: k8sCtx.provider, skipCreate: true }) const releaseName = getReleaseName(module) const context = ctx.provider.config.context diff --git a/garden-service/src/plugins/kubernetes/helm/deployment.ts b/garden-service/src/plugins/kubernetes/helm/deployment.ts index e07ac30428..e47adfb132 100644 --- a/garden-service/src/plugins/kubernetes/helm/deployment.ts +++ b/garden-service/src/plugins/kubernetes/helm/deployment.ts @@ -45,7 +45,7 @@ export async function deployService( const provider = k8sCtx.provider const chartPath = await getChartPath(module) const valuesPath = getValuesPath(chartPath) - const namespace = await getAppNamespace(k8sCtx, provider) + const namespace = await getAppNamespace(k8sCtx, log, provider) const context = provider.config.context const releaseName = getReleaseName(module) @@ -103,7 +103,7 @@ export async function deleteService(params: DeleteServiceParams): Promisectx - const namespace = await getAppNamespace(k8sCtx, k8sCtx.provider) + const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) const context = k8sCtx.provider.config.context const releaseName = getReleaseName(module) diff --git a/garden-service/src/plugins/kubernetes/helm/logs.ts b/garden-service/src/plugins/kubernetes/helm/logs.ts index 54eb2cd26d..ce593ad21c 100644 --- a/garden-service/src/plugins/kubernetes/helm/logs.ts +++ b/garden-service/src/plugins/kubernetes/helm/logs.ts @@ -17,7 +17,7 @@ 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, k8sCtx.provider) + const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) const resources = await getChartResources(k8sCtx, module, log) diff --git a/garden-service/src/plugins/kubernetes/helm/run.ts b/garden-service/src/plugins/kubernetes/helm/run.ts index 194359aaf2..22794c08ab 100644 --- a/garden-service/src/plugins/kubernetes/helm/run.ts +++ b/garden-service/src/plugins/kubernetes/helm/run.ts @@ -25,7 +25,7 @@ export async function runHelmModule( ): Promise { const k8sCtx = ctx const context = k8sCtx.provider.config.context - const namespace = await getAppNamespace(k8sCtx, k8sCtx.provider) + const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) const serviceResourceSpec = getServiceResourceSpec(module) if (!serviceResourceSpec) { @@ -57,7 +57,7 @@ export async function runHelmTask( ): Promise { const k8sCtx = ctx const context = k8sCtx.provider.config.context - const namespace = await getAppNamespace(k8sCtx, k8sCtx.provider) + const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) const args = task.spec.args const image = await getImage(k8sCtx, module, log, task.spec.resource || getServiceResourceSpec(module)) @@ -79,6 +79,7 @@ export async function runHelmTask( await storeTaskResult({ ctx, + log, result, taskVersion, taskName: task.name, diff --git a/garden-service/src/plugins/kubernetes/helm/status.ts b/garden-service/src/plugins/kubernetes/helm/status.ts index fccff4b94f..1921997846 100644 --- a/garden-service/src/plugins/kubernetes/helm/status.ts +++ b/garden-service/src/plugins/kubernetes/helm/status.ts @@ -61,8 +61,8 @@ export async function getServiceStatus( } const provider = k8sCtx.provider - const api = new KubeApi(provider.config.context) - const namespace = await getAppNamespace(k8sCtx, provider) + const api = await KubeApi.factory(log, provider.config.context) + const namespace = await getAppNamespace(k8sCtx, log, provider) let { state, remoteObjects } = await compareDeployedObjects(k8sCtx, api, namespace, chartResources, log, false) const detail = { remoteObjects } diff --git a/garden-service/src/plugins/kubernetes/helm/test.ts b/garden-service/src/plugins/kubernetes/helm/test.ts index e4e3b76d76..bcf5328cfc 100644 --- a/garden-service/src/plugins/kubernetes/helm/test.ts +++ b/garden-service/src/plugins/kubernetes/helm/test.ts @@ -27,7 +27,7 @@ export async function testHelmModule( const k8sCtx = ctx const context = k8sCtx.provider.config.context - const namespace = await getAppNamespace(k8sCtx, k8sCtx.provider) + const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) const chartResources = await getChartResources(k8sCtx, module, log) const resourceSpec = testConfig.spec.resource || getServiceResourceSpec(module) @@ -48,5 +48,5 @@ export async function testHelmModule( log, }) - return storeTestResult({ ctx: k8sCtx, module, testName, testVersion, result }) + return storeTestResult({ ctx: k8sCtx, log, module, testName, testVersion, result }) } diff --git a/garden-service/src/plugins/kubernetes/helm/tiller.ts b/garden-service/src/plugins/kubernetes/helm/tiller.ts index 83d2081334..1e0ccd23b6 100644 --- a/garden-service/src/plugins/kubernetes/helm/tiller.ts +++ b/garden-service/src/plugins/kubernetes/helm/tiller.ts @@ -22,8 +22,8 @@ import chalk from "chalk" const serviceAccountName = "garden-tiller" export async function checkTillerStatus(ctx: PluginContext, provider: KubernetesProvider, log: LogEntry) { - const api = new KubeApi(provider.config.context) - const namespace = await getAppNamespace(ctx, provider) + const api = await KubeApi.factory(log, provider.config.context) + const namespace = await getAppNamespace(ctx, log, provider) const resources = [ ...getRoleResources(namespace), @@ -36,7 +36,7 @@ export async function checkTillerStatus(ctx: PluginContext, provider: Kubernetes } export async function installTiller(ctx: PluginContext, provider: KubernetesProvider, log: LogEntry) { - const namespace = await getAppNamespace(ctx, provider) + const namespace = await getAppNamespace(ctx, log, provider) const context = provider.config.context const entry = log.info({ @@ -61,7 +61,7 @@ export async function installTiller(ctx: PluginContext, provider: KubernetesProv async function getTillerResources( ctx: PluginContext, provider: KubernetesProvider, log: LogEntry, ): Promise { - const namespace = await getAppNamespace(ctx, provider) + const namespace = await getAppNamespace(ctx, log, provider) const context = provider.config.context const tillerManifests = await helm(namespace, context, log, diff --git a/garden-service/src/plugins/kubernetes/hot-reload.ts b/garden-service/src/plugins/kubernetes/hot-reload.ts index 0e88c630a3..8af3b2c524 100644 --- a/garden-service/src/plugins/kubernetes/hot-reload.ts +++ b/garden-service/src/plugins/kubernetes/hot-reload.ts @@ -285,7 +285,7 @@ async function getLocalRsyncPort(ctx: PluginContext, log: LogEntry, targetDeploy } const k8sCtx = ctx - const namespace = await getAppNamespace(k8sCtx, k8sCtx.provider) + const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) // Forward random free local port to the remote rsync container. rsyncLocalPort = await getPort() diff --git a/garden-service/src/plugins/kubernetes/init.ts b/garden-service/src/plugins/kubernetes/init.ts index 49e8e6e50b..bb04dd05d0 100644 --- a/garden-service/src/plugins/kubernetes/init.ts +++ b/garden-service/src/plugins/kubernetes/init.ts @@ -63,8 +63,8 @@ async function prepareNamespaces({ ctx, log }: GetEnvironmentStatusParams) { } await Bluebird.all([ - getMetadataNamespace(k8sCtx, k8sCtx.provider), - getAppNamespace(k8sCtx, k8sCtx.provider), + getMetadataNamespace(k8sCtx, log, k8sCtx.provider), + getAppNamespace(k8sCtx, log, k8sCtx.provider), ]) } @@ -85,7 +85,7 @@ export async function getRemoteEnvironmentStatus({ ctx, log }: GetEnvironmentSta // Note: We don't need the system namespaces for remote k8s for now - // const api = new KubeApi(k8sCtx.provider.config.context) + // const api = await KubeApi.factory(log, k8sCtx.provider.config.context) // const contextForLog = `Checking environment status for plugin "kubernetes"` // const sysNamespaceUpToDate = await systemNamespaceUpToDate(api, log, contextForLog) // if (!sysNamespaceUpToDate) { @@ -118,7 +118,7 @@ export async function getLocalEnvironmentStatus({ ctx, log }: GetEnvironmentStat const serviceStatuses = pick(sysStatus.services, getSystemServices(provider)) - const api = new KubeApi(provider.config.context) + const api = await KubeApi.factory(log, provider.config.context) const servicesReady = every(values(serviceStatuses).map(s => s.state === "ready")) const contextForLog = `Checking environment status for plugin "local-kubernetes"` @@ -141,7 +141,7 @@ export async function getLocalEnvironmentStatus({ ctx, log }: GetEnvironmentStat } // Add the Kubernetes dashboard to the Garden dashboard - const namespace = await getAppNamespace(k8sCtx, provider) + const namespace = await getAppNamespace(k8sCtx, log, provider) const defaultHostname = provider.config.defaultHostname const dashboardStatus = sysStatus.services["kubernetes-dashboard"] @@ -182,7 +182,7 @@ export async function prepareRemoteEnvironment({ ctx, log }: PrepareEnvironmentP // Note: We don't need the system namespaces for remote k8s for now - // const api = new KubeApi(provider.config.context) + // const api = await KubeApi.factory(log, provider.config.context) // const contextForLog = `Preparing environment for plugin "kubernetes"` // if (!await systemNamespaceUpToDate(api, log, contextForLog)) { // await recreateSystemNamespaces(api, log) @@ -197,7 +197,7 @@ export async function prepareLocalEnvironment({ ctx, log }: PrepareEnvironmentPa // make sure system services are deployed const k8sCtx = ctx if (!isSystemGarden(k8sCtx.provider)) { - const api = new KubeApi(k8sCtx.provider.config.context) + const api = await KubeApi.factory(log, k8sCtx.provider.config.context) const contextForLog = `Preparing environment for plugin "local-kubernetes"` const outdated = !(await systemNamespaceUpToDate(api, log, contextForLog)) if (outdated) { @@ -256,8 +256,8 @@ export async function recreateSystemNamespaces(api: KubeApi, log: LogEntry) { export async function cleanupEnvironment({ ctx, log }: CleanupEnvironmentParams) { const k8sCtx = ctx - const api = new KubeApi(k8sCtx.provider.config.context) - const namespace = await getAppNamespace(k8sCtx, k8sCtx.provider) + const api = await KubeApi.factory(log, k8sCtx.provider.config.context) + const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) const entry = log.info({ section: "kubernetes", msg: `Deleting namespace ${namespace} (this may take a while)`, diff --git a/garden-service/src/plugins/kubernetes/kubectl.ts b/garden-service/src/plugins/kubernetes/kubectl.ts index 90002db7b4..ed7003e5b8 100644 --- a/garden-service/src/plugins/kubernetes/kubectl.ts +++ b/garden-service/src/plugins/kubernetes/kubectl.ts @@ -84,7 +84,7 @@ export async function deleteObjectsByLabel( interface KubectlParams extends ExecParams { log: LogEntry - context: string + context?: string namespace?: string configPath?: string args: string[] @@ -129,7 +129,11 @@ class Kubectl extends BinaryCmd { private prepareArgs(params: KubectlParams) { const { context, namespace, configPath, args } = params - const opts: string[] = [`--context=${context}`] + const opts: string[] = [] + + if (context) { + opts.push(`--context=${context}`) + } if (namespace) { opts.push(`--namespace=${namespace}`) diff --git a/garden-service/src/plugins/kubernetes/kubernetes-module/handlers.ts b/garden-service/src/plugins/kubernetes/kubernetes-module/handlers.ts index 43589722d1..2d1c004bb3 100644 --- a/garden-service/src/plugins/kubernetes/kubernetes-module/handlers.ts +++ b/garden-service/src/plugins/kubernetes/kubernetes-module/handlers.ts @@ -52,9 +52,9 @@ async function getServiceStatus( { ctx, module, log }: GetServiceStatusParams, ): Promise { const k8sCtx = ctx - const namespace = await getNamespace({ ctx: k8sCtx, provider: k8sCtx.provider, skipCreate: true }) + const namespace = await getNamespace({ ctx: k8sCtx, log, provider: k8sCtx.provider, skipCreate: true }) const context = ctx.provider.config.context - const api = new KubeApi(context) + const api = await KubeApi.factory(log, context) const manifests = await getManifests(module) const { state, remoteObjects } = await compareDeployedObjects(k8sCtx, api, namespace, manifests, log, false) @@ -72,7 +72,7 @@ async function deployService( const { ctx, force, module, service, log } = params const k8sCtx = ctx - const namespace = await getNamespace({ ctx: k8sCtx, provider: k8sCtx.provider, skipCreate: true }) + const namespace = await getNamespace({ ctx: k8sCtx, log, provider: k8sCtx.provider, skipCreate: true }) const context = ctx.provider.config.context const manifests = await getManifests(module) @@ -93,7 +93,7 @@ async function deployService( async function deleteService(params: DeleteServiceParams): Promise { const { ctx, log, service, module } = params const k8sCtx = ctx - const namespace = await getAppNamespace(k8sCtx, k8sCtx.provider) + const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) const provider = k8sCtx.provider const manifests = await getManifests(module) @@ -112,10 +112,10 @@ async function deleteService(params: DeleteServiceParams): Promise) { - const { ctx, module } = params + const { ctx, log, module } = params const k8sCtx = ctx const context = k8sCtx.provider.config.context - const namespace = await getAppNamespace(k8sCtx, k8sCtx.provider) + const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider) const manifests = await getManifests(module) return getAllLogs({ ...params, context, namespace, resources: manifests }) diff --git a/garden-service/src/plugins/kubernetes/local/config.ts b/garden-service/src/plugins/kubernetes/local/config.ts index b2baaa615a..384edb619b 100644 --- a/garden-service/src/plugins/kubernetes/local/config.ts +++ b/garden-service/src/plugins/kubernetes/local/config.ts @@ -7,29 +7,17 @@ */ import * as execa from "execa" -import { safeLoad } from "js-yaml" import * as Joi from "joi" -import { join } from "path" -import { readFile } from "fs-extra" -import { homedir } from "os" import { KubernetesBaseConfig, kubernetesConfigBase } from "../kubernetes" import { ConfigureProviderParams } from "../../../types/plugin/params" import { joiProviderName } from "../../../config/common" +import { getKubeConfig } from "../api" // TODO: split this into separate plugins to handle Docker for Mac and Minikube // note: this is in order of preference, in case neither is set as the current kubectl context // and none is explicitly configured in the garden.yml const supportedContexts = ["docker-for-desktop", "minikube"] -const kubeConfigPath = join(homedir(), ".kube", "config") - -async function getKubeConfig(): Promise { - try { - return safeLoad((await readFile(kubeConfigPath)).toString()) - } catch { - return {} - } -} /** * Automatically set docker environment variables for minikube @@ -74,7 +62,7 @@ export async function configureProvider({ config, log, projectName }: ConfigureP if (!context) { // automatically detect supported kubectl context if not explicitly configured - const kubeConfig = await getKubeConfig() + const kubeConfig = await getKubeConfig(log) const currentContext = kubeConfig["current-context"] if (currentContext && supportedContexts.includes(currentContext)) { @@ -92,11 +80,16 @@ export async function configureProvider({ config, log, projectName }: ConfigureP } } } + + if (!context && kubeConfig.contexts.length > 0) { + context = kubeConfig.contexts[0].name + log.debug({ section: config.name, msg: `No kubectl context auto-detected, using first available: ${context}` }) + } } if (!context) { context = supportedContexts[0] - log.debug({ section: config.name, msg: `No kubectl context auto-detected, using default: ${context}` }) + log.debug({ section: config.name, msg: `No kubectl context configured, using default: ${context}` }) } if (context === "minikube") { diff --git a/garden-service/src/plugins/kubernetes/logs.ts b/garden-service/src/plugins/kubernetes/logs.ts index 736d19c404..49e3fef43f 100644 --- a/garden-service/src/plugins/kubernetes/logs.ts +++ b/garden-service/src/plugins/kubernetes/logs.ts @@ -64,7 +64,7 @@ export async function getPodLogs(params: GetPodLogsParams) { * Stream all logs for the given resources and service. */ export async function getAllLogs(params: GetAllLogsParams) { - const api = new KubeApi(params.context) + const api = await KubeApi.factory(params.log, params.context) const podNames = await getAllPodNames(api, params.namespace, params.resources) return getPodLogs({ ...params, podNames }) } diff --git a/garden-service/src/plugins/kubernetes/namespace.ts b/garden-service/src/plugins/kubernetes/namespace.ts index 53fbeb9e7d..d7d600fab3 100644 --- a/garden-service/src/plugins/kubernetes/namespace.ts +++ b/garden-service/src/plugins/kubernetes/namespace.ts @@ -12,6 +12,7 @@ import { KubernetesProvider } from "./kubernetes" import { name as providerName } from "./kubernetes" import { AuthenticationError } from "../../exceptions" import { getPackageVersion } from "../../util/util" +import { LogEntry } from "../../logger/log-entry" const GARDEN_VERSION = getPackageVersion() const created: { [name: string]: boolean } = {} @@ -51,8 +52,8 @@ export async function createNamespace(api: KubeApi, namespace: string) { } export async function getNamespace( - { ctx, provider, suffix, skipCreate }: - { ctx: PluginContext, provider: KubernetesProvider, suffix?: string, skipCreate?: boolean }, + { ctx, log, provider, suffix, skipCreate }: + { ctx: PluginContext, log: LogEntry, provider: KubernetesProvider, suffix?: string, skipCreate?: boolean }, ): Promise { let namespace @@ -85,19 +86,19 @@ export async function getNamespace( } if (!skipCreate) { - const api = new KubeApi(provider.config.context) + const api = await KubeApi.factory(log, provider.config.context) await ensureNamespace(api, namespace) } return namespace } -export async function getAppNamespace(ctx: PluginContext, provider: KubernetesProvider) { - return getNamespace({ ctx, provider }) +export async function getAppNamespace(ctx: PluginContext, log: LogEntry, provider: KubernetesProvider) { + return getNamespace({ ctx, log, provider }) } -export function getMetadataNamespace(ctx: PluginContext, provider: KubernetesProvider) { - return getNamespace({ ctx, provider, suffix: "metadata" }) +export function getMetadataNamespace(ctx: PluginContext, log: LogEntry, provider: KubernetesProvider) { + return getNamespace({ ctx, log, provider, suffix: "metadata" }) } export async function getAllNamespaces(api: KubeApi): Promise { diff --git a/garden-service/src/plugins/kubernetes/secrets.ts b/garden-service/src/plugins/kubernetes/secrets.ts index a2a51fb95b..95d91a9a3f 100644 --- a/garden-service/src/plugins/kubernetes/secrets.ts +++ b/garden-service/src/plugins/kubernetes/secrets.ts @@ -14,10 +14,10 @@ import { ConfigurationError } from "../../exceptions" import { GetSecretParams, SetSecretParams, DeleteSecretParams } from "../../types/plugin/params" import { getMetadataNamespace } from "./namespace" -export async function getSecret({ ctx, key }: GetSecretParams) { +export async function getSecret({ ctx, log, key }: GetSecretParams) { const k8sCtx = ctx - const api = new KubeApi(k8sCtx.provider.config.context) - const ns = await getMetadataNamespace(k8sCtx, k8sCtx.provider) + const api = await KubeApi.factory(log, k8sCtx.provider.config.context) + const ns = await getMetadataNamespace(k8sCtx, log, k8sCtx.provider) try { const res = await api.core.readNamespacedSecret(key, ns) @@ -31,11 +31,11 @@ export async function getSecret({ ctx, key }: GetSecretParams) { } } -export async function setSecret({ ctx, key, value }: SetSecretParams) { +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 = new KubeApi(k8sCtx.provider.config.context) - const ns = await getMetadataNamespace(k8sCtx, k8sCtx.provider) + const api = await KubeApi.factory(log, k8sCtx.provider.config.context) + const ns = await getMetadataNamespace(k8sCtx, log, k8sCtx.provider) const body = { body: { apiVersion: "v1", @@ -64,10 +64,10 @@ export async function setSecret({ ctx, key, value }: SetSecretParams) { return {} } -export async function deleteSecret({ ctx, key }: DeleteSecretParams) { +export async function deleteSecret({ ctx, log, key }: DeleteSecretParams) { const k8sCtx = ctx - const api = new KubeApi(k8sCtx.provider.config.context) - const ns = await getMetadataNamespace(k8sCtx, k8sCtx.provider) + const api = await KubeApi.factory(log, k8sCtx.provider.config.context) + const ns = await getMetadataNamespace(k8sCtx, log, k8sCtx.provider) try { await api.core.deleteNamespacedSecret(key, ns, {}) diff --git a/garden-service/src/plugins/kubernetes/status.ts b/garden-service/src/plugins/kubernetes/status.ts index 3db9fbf709..5058bf59d5 100644 --- a/garden-service/src/plugins/kubernetes/status.ts +++ b/garden-service/src/plugins/kubernetes/status.ts @@ -354,8 +354,8 @@ export async function waitForResources({ ctx, provider, serviceName, resources: msg: `Waiting for service to be ready...`, }) - const api = new KubeApi(provider.config.context) - const namespace = await getAppNamespace(ctx, provider) + const api = await KubeApi.factory(log, provider.config.context) + const namespace = await getAppNamespace(ctx, log, provider) let prevStatuses: WorkloadStatus[] = objects.map((obj) => ({ state: "unknown", obj, @@ -568,8 +568,8 @@ export async function compareDeployedObjects( async function getDeployedObject( ctx: PluginContext, provider: KubernetesProvider, obj: KubernetesResource, log: LogEntry, ): Promise { - const api = new KubeApi(provider.config.context) - const namespace = obj.metadata.namespace || await getAppNamespace(ctx, provider) + const api = await KubeApi.factory(log, provider.config.context) + const namespace = obj.metadata.namespace || await getAppNamespace(ctx, log, provider) try { const res = await api.readBySpec(namespace, obj, log) diff --git a/garden-service/src/plugins/kubernetes/task-results.ts b/garden-service/src/plugins/kubernetes/task-results.ts index 00b7380e58..d10e032b10 100644 --- a/garden-service/src/plugins/kubernetes/task-results.ts +++ b/garden-service/src/plugins/kubernetes/task-results.ts @@ -16,13 +16,14 @@ import { getMetadataNamespace } from "./namespace" import { RunTaskResult } from "../../types/plugin/outputs" import { deserializeValues, serializeValues } from "../../util/util" import { PluginContext } from "../../plugin-context" +import { LogEntry } from "../../logger/log-entry" export async function getTaskResult( - { ctx, task, taskVersion }: GetTaskResultParams, + { ctx, log, task, taskVersion }: GetTaskResultParams, ): Promise { const k8sCtx = ctx - const api = new KubeApi(k8sCtx.provider.config.context) - const ns = await getMetadataNamespace(k8sCtx, k8sCtx.provider) + const api = await KubeApi.factory(log, k8sCtx.provider.config.context) + const ns = await getMetadataNamespace(k8sCtx, log, k8sCtx.provider) const resultKey = getTaskResultKey(task.name, taskVersion) try { @@ -41,18 +42,25 @@ export function getTaskResultKey(taskName: string, version: ModuleVersion) { return `task-result--${taskName}--${version.versionString}` } +interface StoreTaskResultParams { + ctx: PluginContext, + log: LogEntry, + taskName: string, + taskVersion: ModuleVersion, + result: RunTaskResult, +} + /** * Store a task run result as a ConfigMap in the cluster. * * TODO: Implement a CRD for this. */ export async function storeTaskResult( - { ctx, taskName, taskVersion, result }: - { ctx: PluginContext, taskName: string, taskVersion: ModuleVersion, result: RunTaskResult }, + { ctx, log, taskName, taskVersion, result }: StoreTaskResultParams, ): Promise { const provider = ctx.provider - const api = new KubeApi(provider.config.context) - const ns = await getMetadataNamespace(ctx, provider) + const api = await KubeApi.factory(log, provider.config.context) + const ns = await getMetadataNamespace(ctx, log, provider) const resultKey = getTaskResultKey(taskName, taskVersion) const body = { diff --git a/garden-service/src/plugins/kubernetes/test.ts b/garden-service/src/plugins/kubernetes/test.ts index 990add4448..422f41ddac 100644 --- a/garden-service/src/plugins/kubernetes/test.ts +++ b/garden-service/src/plugins/kubernetes/test.ts @@ -17,14 +17,15 @@ import { HelmModule } from "./helm/config" import { PluginContext } from "../../plugin-context" import { KubernetesPluginContext } from "./kubernetes" import { systemMetadataNamespace } from "./system" +import { LogEntry } from "../../logger/log-entry" const testResultNamespace = systemMetadataNamespace export async function getTestResult( - { ctx, module, testName, testVersion }: GetTestResultParams, + { ctx, log, module, testName, testVersion }: GetTestResultParams, ): Promise { const k8sCtx = ctx - const api = new KubeApi(k8sCtx.provider.config.context) + const api = await KubeApi.factory(log, k8sCtx.provider.config.context) const resultKey = getTestResultKey(k8sCtx, module, testName, testVersion) try { @@ -43,17 +44,25 @@ export function getTestResultKey(ctx: PluginContext, module: Module, testName: s return `test-result--${ctx.projectName}--${module.name}--${testName}--${version.versionString}` } +interface StoreTestResultParams { + ctx: PluginContext, + log: LogEntry, + module: Module, + testName: string, + testVersion: ModuleVersion, + result: RunResult, +} + /** * Store a test run result as a ConfigMap in the cluster. * * TODO: Implement a CRD for this. */ export async function storeTestResult( - { ctx, module, testName, testVersion, result }: - { ctx: PluginContext, module: Module, testName: string, testVersion: ModuleVersion, result: RunResult }, + { ctx, log, module, testName, testVersion, result }: StoreTestResultParams, ): Promise { const k8sCtx = ctx - const api = new KubeApi(k8sCtx.provider.config.context) + const api = await KubeApi.factory(log, k8sCtx.provider.config.context) const testResult: TestResult = { ...result, diff --git a/garden-service/src/plugins/openfaas/openfaas.ts b/garden-service/src/plugins/openfaas/openfaas.ts index 695ab63763..0fd8976928 100644 --- a/garden-service/src/plugins/openfaas/openfaas.ts +++ b/garden-service/src/plugins/openfaas/openfaas.ts @@ -55,6 +55,7 @@ import { CleanupEnvironmentParams } from "../../types/plugin/params" import dedent = require("dedent") import { getAllLogs } from "../kubernetes/logs" import { installTiller, checkTillerStatus } from "../kubernetes/helm/tiller" +import { LogEntry } from "../../logger/log-entry" const systemProjectPath = join(STATIC_DIR, "openfaas", "system") export const stackFilename = "stack.yml" @@ -125,7 +126,7 @@ export function gardenPlugin(): GardenPlugin { actions: { async getEnvironmentStatus({ ctx, log }: GetEnvironmentStatusParams) { const openFaasCtx = ctx - const ofGarden = await getOpenFaasGarden(openFaasCtx) + const ofGarden = await getOpenFaasGarden(openFaasCtx, log) const status = await ofGarden.actions.getStatus({ log }) const envReady = every(values(status.providers).map(s => s.ready)) const servicesReady = every(values(status.services).map(s => s.state === "ready")) @@ -145,7 +146,7 @@ export function gardenPlugin(): GardenPlugin { async prepareEnvironment({ ctx, force, log }: PrepareEnvironmentParams) { const openFaasCtx = ctx // TODO: refactor to dedupe similar code in local-kubernetes - const ofGarden = await getOpenFaasGarden(openFaasCtx) + const ofGarden = await getOpenFaasGarden(openFaasCtx, log) await ofGarden.actions.prepareEnvironment({ force, log }) @@ -168,7 +169,7 @@ export function gardenPlugin(): GardenPlugin { }, async cleanupEnvironment({ ctx, log }: CleanupEnvironmentParams) { - const ofGarden = await getOpenFaasGarden(ctx) + const ofGarden = await getOpenFaasGarden(ctx, log) await ofGarden.actions.cleanupEnvironment({ log }) return {} }, @@ -177,7 +178,9 @@ export function gardenPlugin(): GardenPlugin { openfaas: { describeType, - async configure({ ctx, moduleConfig }: ConfigureModuleParams): Promise { + async configure( + { ctx, log, moduleConfig }: ConfigureModuleParams, + ): Promise { moduleConfig.build.dependencies.push({ name: "templates", plugin: "openfaas", @@ -206,7 +209,7 @@ export function gardenPlugin(): GardenPlugin { })) moduleConfig.outputs = { - endpoint: await getInternalServiceUrl(ctx, moduleConfig), + endpoint: await getInternalServiceUrl(ctx, log, moduleConfig), } return moduleConfig @@ -232,12 +235,12 @@ export function gardenPlugin(): GardenPlugin { getServiceStatus, async getServiceLogs(params: GetServiceLogsParams) { - const { ctx, service } = params + const { ctx, log, service } = params const k8sProvider = getK8sProvider(ctx) const context = k8sProvider.config.context - const namespace = await getAppNamespace(ctx, k8sProvider) + const namespace = await getAppNamespace(ctx, log, k8sProvider) - const api = new KubeApi(k8sProvider.config.context) + const api = await KubeApi.factory(log, k8sProvider.config.context) const resources = await getResources(api, service, namespace) return getAllLogs({ ...params, context, namespace, resources }) @@ -260,8 +263,8 @@ export function gardenPlugin(): GardenPlugin { // wait until deployment is ready const k8sProvider = getK8sProvider(openFaasCtx) - const namespace = await getAppNamespace(openFaasCtx, k8sProvider) - const api = new KubeApi(k8sProvider.config.context) + const namespace = await getAppNamespace(openFaasCtx, log, k8sProvider) + const api = await KubeApi.factory(log, k8sProvider.config.context) const resources = await getResources(api, service, namespace) await waitForResources({ @@ -354,8 +357,8 @@ async function getServiceStatus({ ctx, module, service, log }: GetServiceStatusP protocol: "http", }] - const namespace = await getAppNamespace(openFaasCtx, k8sProvider) - const api = new KubeApi(k8sProvider.config.context) + const namespace = await getAppNamespace(openFaasCtx, log, k8sProvider) + const api = await KubeApi.factory(log, k8sProvider.config.context) let deployment @@ -435,9 +438,9 @@ function getServicePath(config: OpenFaasModuleConfig) { return join("/", "function", config.name) } -async function getInternalGatewayUrl(ctx: PluginContext) { +async function getInternalGatewayUrl(ctx: PluginContext, log: LogEntry) { const k8sProvider = getK8sProvider(ctx) - const namespace = await getOpenfaasNamespace(ctx, k8sProvider, true) + const namespace = await getOpenfaasNamespace(ctx, log, k8sProvider, true) return `http://gateway.${namespace}.svc.cluster.local:8080` } @@ -463,22 +466,22 @@ function getExternalGatewayUrl(ctx: PluginContext) { return `http://${hostname}:${ingressPort}` } -async function getInternalServiceUrl(ctx: PluginContext, config: OpenFaasModuleConfig) { - return urlResolve(await getInternalGatewayUrl(ctx), getServicePath(config)) +async function getInternalServiceUrl(ctx: PluginContext, log: LogEntry, config: OpenFaasModuleConfig) { + return urlResolve(await getInternalGatewayUrl(ctx, log), getServicePath(config)) } async function getOpenfaasNamespace( - ctx: PluginContext, k8sProvider: KubernetesProvider, skipCreate?: boolean, + ctx: PluginContext, log: LogEntry, k8sProvider: KubernetesProvider, skipCreate?: boolean, ) { - return getNamespace({ ctx, provider: k8sProvider, skipCreate, suffix: "openfaas" }) + return getNamespace({ ctx, log, provider: k8sProvider, skipCreate, suffix: "openfaas" }) } -export async function getOpenFaasGarden(ctx: PluginContext): Promise { +export async function getOpenFaasGarden(ctx: PluginContext, log: LogEntry): Promise { // TODO: figure out good way to retrieve namespace from kubernetes plugin through an exposed interface // (maybe allow plugins to expose arbitrary data on the Provider object?) const k8sProvider = getK8sProvider(ctx) - const namespace = await getOpenfaasNamespace(ctx, k8sProvider, true) - const functionNamespace = await getAppNamespace(ctx, k8sProvider) + const namespace = await getOpenfaasNamespace(ctx, log, k8sProvider, true) + const functionNamespace = await getAppNamespace(ctx, log, k8sProvider) const hostname = getExternalGatewayHostname(ctx.provider, k8sProvider) diff --git a/garden-service/test/integ-helpers.ts b/garden-service/test/integ-helpers.ts index 034108b422..c22ad0c9d9 100644 --- a/garden-service/test/integ-helpers.ts +++ b/garden-service/test/integ-helpers.ts @@ -6,7 +6,7 @@ import { resolve } from "path" import { GARDEN_DIR_NAME } from "../src/constants" import { KubeApi } from "../src/plugins/kubernetes/api" import { deleteNamespaces } from "../src/plugins/kubernetes/init" -import { TaskLogStatus } from "../src/logger/log-entry" +import { TaskLogStatus, LogEntry } from "../src/logger/log-entry" import { JsonLogEntry } from "../src/logger/writers/json-terminal-writer" import { getAllNamespaces } from "../src/plugins/kubernetes/namespace" import { getExampleProjects } from "./helpers" @@ -19,7 +19,7 @@ export async function removeExampleDotGardenDirs() { }) } -export async function deleteExampleNamespaces(includeSystemNamespaces = false) { +export async function deleteExampleNamespaces(log: LogEntry, includeSystemNamespaces = false) { const namespacesToDelete: string[] = [] const exampleProjectNames = Object.keys(getExampleProjects()) @@ -33,7 +33,7 @@ export async function deleteExampleNamespaces(includeSystemNamespaces = false) { } // TODO: Accept context parameter in integ script. - const api = new KubeApi("docker-for-desktop") + const api = await KubeApi.factory(log, "docker-for-desktop") const existingNamespaces = await getAllNamespaces(api) await deleteNamespaces(intersection(existingNamespaces, namespacesToDelete), api) diff --git a/garden-service/test/integ/src/pre-release.ts b/garden-service/test/integ/src/pre-release.ts index 250fcbf017..7dad7c37c4 100644 --- a/garden-service/test/integ/src/pre-release.ts +++ b/garden-service/test/integ/src/pre-release.ts @@ -13,16 +13,18 @@ import { taskCompletedStep, } from "../../run-garden" import { deleteExampleNamespaces, searchLog, removeExampleDotGardenDirs } from "../../integ-helpers" +import { getLogger } from "../../../src/logger/logger" // TODO: Add test for verifying that CLI returns with an error when called with an unknown command describe("PreReleaseTests", () => { const simpleProjectPath = resolve(examplesDir, "simple-project") + const log = getLogger().placeholder() before(async () => { mlog.log("deleting example project namespaces and .garden folders") - await deleteExampleNamespaces(false) + await deleteExampleNamespaces(log, false) await removeExampleDotGardenDirs() }) 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 08f9f439fa..7d21c009cc 100644 --- a/garden-service/test/unit/src/plugins/kubernetes/container/ingress.ts +++ b/garden-service/test/unit/src/plugins/kubernetes/container/ingress.ts @@ -375,8 +375,8 @@ describe("createIngressResources", () => { } } - function getKubeApi(context: string) { - const api = new KubeApi(context) + async function getKubeApi(context: string) { + const api = await KubeApi.factory(garden.log, context) const core = td.replace(api, "core") td.when(core.readNamespacedSecret("somesecret", "somenamespace")).thenResolve({ @@ -401,7 +401,7 @@ describe("createIngressResources", () => { port: "http", }) - const api = getKubeApi(basicProvider.config.context) + const api = await getKubeApi(basicProvider.config.context) const ingresses = await createIngressResources(api, basicProvider, namespace, service) expect(ingresses).to.eql([{ @@ -443,7 +443,7 @@ describe("createIngressResources", () => { port: "http", }) - const api = getKubeApi(basicProvider.config.context) + const api = await getKubeApi(basicProvider.config.context) const ingresses = await createIngressResources(api, basicProvider, namespace, service) expect(ingresses).to.eql([{ @@ -494,7 +494,7 @@ describe("createIngressResources", () => { }, ) - const api = getKubeApi(basicProvider.config.context) + const api = await getKubeApi(basicProvider.config.context) const ingresses = await createIngressResources(api, basicProvider, namespace, service) expect(ingresses).to.eql([ @@ -566,7 +566,7 @@ describe("createIngressResources", () => { }, ) - const api = getKubeApi(singleTlsProvider.config.context) + const api = await getKubeApi(singleTlsProvider.config.context) const ingresses = await createIngressResources(api, singleTlsProvider, namespace, service) td.verify(api.upsert("Secret", namespace, myDomainCertSecret)) @@ -615,7 +615,7 @@ describe("createIngressResources", () => { }, ) - const api = getKubeApi(basicConfig.context) + const api = await getKubeApi(basicConfig.context) const provider = { name: "kubernetes", @@ -655,7 +655,7 @@ describe("createIngressResources", () => { }, } - const api = getKubeApi(basicConfig.context) + const api = await getKubeApi(basicConfig.context) const err: any = new Error("nope") err.code = 404 @@ -688,7 +688,7 @@ describe("createIngressResources", () => { }, } - const api = getKubeApi(basicConfig.context) + const api = await getKubeApi(basicConfig.context) const err: any = new Error("nope") err.code = 404 @@ -713,7 +713,7 @@ describe("createIngressResources", () => { }, ) - const api = getKubeApi(multiTlsProvider.config.context) + const api = await getKubeApi(multiTlsProvider.config.context) const ingresses = await createIngressResources(api, multiTlsProvider, namespace, service) td.verify(api.upsert("Secret", namespace, wildcardDomainCertSecret)) @@ -763,7 +763,7 @@ describe("createIngressResources", () => { }, ) - const api = getKubeApi(basicConfig.context) + const api = await getKubeApi(basicConfig.context) const provider = { name: "kubernetes",