Skip to content

Commit

Permalink
feat(k8s): allow setting custom kubeconfig path
Browse files Browse the repository at this point in the history
  • Loading branch information
edvald committed Aug 29, 2019
1 parent 51b8082 commit 8b4a6d5
Show file tree
Hide file tree
Showing 39 changed files with 243 additions and 189 deletions.
2 changes: 1 addition & 1 deletion docs/reference/module-types/kubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
| --------------- | -------- | ------- |
Expand Down
11 changes: 11 additions & 0 deletions docs/reference/providers/kubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -929,6 +939,7 @@ providers:
ingressClass:
ingressHttpPort: 80
ingressHttpsPort: 443
kubeconfig:
namespace:
setupIngressController: false
```
Expand Down
32 changes: 21 additions & 11 deletions garden-service/src/plugins/kubernetes/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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<ApiInfo> {
Expand Down Expand Up @@ -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")) {
Expand Down Expand Up @@ -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}`, {
Expand All @@ -389,12 +395,16 @@ export async function getKubeConfig(log: LogEntry) {
}
}

async function getContextConfig(log: LogEntry, context: string): Promise<KubeConfig> {
if (cachedConfigs[context]) {
return cachedConfigs[context]
async function getContextConfig(log: LogEntry, provider: KubernetesProvider): Promise<KubeConfig> {
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 :/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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] })
Expand Down Expand Up @@ -249,7 +249,7 @@ async function runRegistryGarbageCollection(ctx: KubernetesPluginContext, api: K

await apply({
log,
context: provider.config.context,
provider,
manifests: [writableRegistry],
namespace: systemNamespace,
})
Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand Down
1 change: 1 addition & 0 deletions garden-service/src/plugins/kubernetes/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export interface KubernetesBaseConfig extends ProviderConfig {
ingressHttpPort: number
ingressHttpsPort: number
ingressClass?: string
kubeconfig?: string
namespace?: string
resources: KubernetesResources
storage: KubernetesStorage
Expand Down
6 changes: 3 additions & 3 deletions garden-service/src/plugins/kubernetes/container/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,15 +237,15 @@ export async function execInBuilder({ provider, log, args, timeout, podName }: B

return kubectl.exec({
args: execCmd,
context: provider.config.context,
provider,
log,
namespace: systemNamespace,
timeout,
})
}

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)
Expand All @@ -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,
Expand Down
16 changes: 8 additions & 8 deletions garden-service/src/plugins/kubernetes/container/deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ export async function deployContainerService(params: DeployServiceParams<Contain
const manifests = await createContainerObjects(k8sCtx, log, service, runtimeContext, hotReload)

// TODO: use Helm instead of kubectl apply
const context = k8sCtx.provider.config.context
const provider = k8sCtx.provider
const pruneSelector = "service=" + service.name

await apply({ log, context, manifests, force, namespace, pruneSelector })
await apply({ log, provider, manifests, force, namespace, pruneSelector })

await waitForResources({
ctx: k8sCtx,
Expand All @@ -69,7 +69,7 @@ export async function createContainerObjects(
const version = service.module.version
const provider = k8sCtx.provider
const namespace = await getAppNamespace(k8sCtx, log, provider)
const api = await KubeApi.factory(log, provider.config.context)
const api = await KubeApi.factory(log, provider)
const ingresses = await createIngressResources(api, provider, namespace, service)
const deployment = await createDeployment({ provider, service, runtimeContext, namespace, enableHotReload, log })
const kubeservices = await createServiceResources(service, namespace)
Expand Down Expand Up @@ -412,11 +412,10 @@ export async function deleteService(params: DeleteServiceParams): Promise<Servic
const namespace = await getAppNamespace(k8sCtx, log, k8sCtx.provider)
const provider = k8sCtx.provider

const context = provider.config.context
await deleteContainerDeployment({ namespace, context, serviceName: service.name, log })
await deleteContainerDeployment({ namespace, provider, serviceName: service.name, log })
await deleteObjectsByLabel({
log,
context,
provider,
namespace,
labelKey: "service",
labelValue: service.name,
Expand All @@ -428,11 +427,12 @@ export async function deleteService(params: DeleteServiceParams): Promise<Servic
}

export async function deleteContainerDeployment(
{ namespace, context, serviceName, log }: { namespace: string, context: string, serviceName: string, log: LogEntry },
{ namespace, provider, serviceName, log }:
{ namespace: string, provider: KubernetesProvider, serviceName: string, log: LogEntry },
) {

let found = true
const api = await KubeApi.factory(log, context)
const api = await KubeApi.factory(log, provider)

try {
await api.extensions.deleteNamespacedDeployment(serviceName, namespace, <any>{})
Expand Down
8 changes: 4 additions & 4 deletions garden-service/src/plugins/kubernetes/container/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import { emptyRuntimeContext } from "../../../runtime-context"
export async function getServiceLogs(params: GetServiceLogsParams<ContainerModule>) {
const { ctx, log, service } = params
const k8sCtx = <KubernetesPluginContext>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,
Expand All @@ -30,5 +30,5 @@ export async function getServiceLogs(params: GetServiceLogsParams<ContainerModul
log,
})]

return getAllLogs({ ...params, context, namespace, resources })
return getAllLogs({ ...params, provider, namespace, resources })
}
10 changes: 4 additions & 6 deletions garden-service/src/plugins/kubernetes/container/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export async function execInDeployment(
interactive: boolean,
},
) {
const api = await KubeApi.factory(log, provider.config.context)
const api = await KubeApi.factory(log, provider)
const deployment = await api.apps.readNamespacedDeployment(deploymentName, namespace)
const pods = await getWorkloadPods(api, namespace, deployment)

Expand All @@ -87,7 +87,7 @@ export async function execInDeployment(
const kubecmd = ["exec", ...opts, pod.metadata.name, "--", ...command]
const res = await kubectl.spawnAndWait({
log,
context: api.context,
provider,
namespace,
args: kubecmd,
ignoreError: true,
Expand All @@ -104,7 +104,6 @@ export async function runContainerModule(
}: RunModuleParams<ContainerModule>,
): Promise<RunResult> {
const provider = <KubernetesProvider>ctx.provider
const context = provider.config.context
const namespace = await getAppNamespace(ctx, log, provider)

// Apply overrides
Expand All @@ -123,7 +122,7 @@ export async function runContainerModule(
}

return runPod({
context,
provider,
image,
interactive,
ignoreError,
Expand Down Expand Up @@ -155,7 +154,6 @@ export async function runContainerTask(
{ ctx, log, module, task, taskVersion, interactive, runtimeContext }: RunTaskParams<ContainerModule>,
): Promise<RunTaskResult> {
const provider = <KubernetesProvider>ctx.provider
const context = provider.config.context
const namespace = await getAppNamespace(ctx, log, provider)

// Apply overrides
Expand All @@ -175,7 +173,7 @@ export async function runContainerTask(
}

const res = await runPod({
context,
provider,
image,
interactive,
ignoreError: false,
Expand Down
2 changes: 1 addition & 1 deletion garden-service/src/plugins/kubernetes/container/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion garden-service/src/plugins/kubernetes/container/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 6 additions & 7 deletions garden-service/src/plugins/kubernetes/helm/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,18 @@ export async function buildHelmModule({ ctx, module, log }: BuildModuleParams<He
provider: k8sCtx.provider,
skipCreate: true,
})
const context = ctx.provider.config.context
const baseModule = getBaseModule(module)

if (!baseModule && !(await containsSource(module))) {
log.debug("Fetching chart...")
try {
await fetchChart(namespace, context, log, module)
await fetchChart(k8sCtx, namespace, log, module)
} catch {
// update the local helm repo and retry
log.debug("Updating Helm repo...")
await helm(namespace, context, log, ...["repo", "update"])
await helm({ ctx: k8sCtx, namespace, log, args: [...["repo", "update"]] })
log.debug("Fetching chart (after updating)...")
await fetchChart(namespace, context, log, module)
await fetchChart(k8sCtx, namespace, log, module)
}
}

Expand All @@ -54,10 +53,10 @@ export async function buildHelmModule({ ctx, module, log }: BuildModuleParams<He
return { fresh: true }
}

async function fetchChart(namespace: string, context: string, log: LogEntry, module: HelmModule) {
async function fetchChart(ctx: KubernetesPluginContext, namespace: string, log: LogEntry, module: HelmModule) {
const buildPath = module.buildPath

await helm(namespace, context, log, "init", "--client-only")
await helm({ ctx, namespace, log, args: ["init", "--client-only"] })

const fetchArgs = [
"fetch", module.spec.chart!,
Expand All @@ -70,5 +69,5 @@ async function fetchChart(namespace: string, context: string, log: LogEntry, mod
if (module.spec.repo) {
fetchArgs.push("--repo", module.spec.repo)
}
await helm(namespace, context, log, ...fetchArgs)
await helm({ ctx, namespace, log, args: [...fetchArgs] })
}
Loading

0 comments on commit 8b4a6d5

Please sign in to comment.