From b2c632c349ef422794062dba869c757e99204769 Mon Sep 17 00:00:00 2001 From: Jon Edvald Date: Wed, 23 May 2018 00:12:24 +0200 Subject: [PATCH] feat: support and documentation for Minikube --- .circleci/config.yml | 4 ++ docs/minikube.md | 42 ++++++++++++++++++++ examples/hello-world/garden.yml | 1 - garden.yml | 2 + src/garden.ts | 2 +- src/plugins/kubernetes/local.ts | 70 +++++++++++++++++++++++++++++++-- src/types/plugin.ts | 2 +- src/types/project.ts | 1 - 8 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 docs/minikube.md diff --git a/.circleci/config.yml b/.circleci/config.yml index e13461ab14..de2530e610 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -29,6 +29,10 @@ jobs: # fallback to using the latest cache if no exact match is found - v2-dependencies- + - run: + command: | + curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/v1.10.0/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/ + - run: sudo apt install rsync ruby ruby-dev cmake libicu-dev pkg-config - run: sudo gem install --no-document copyright-header - run: npm install diff --git a/docs/minikube.md b/docs/minikube.md new file mode 100644 index 0000000000..6c5bb9e5d5 --- /dev/null +++ b/docs/minikube.md @@ -0,0 +1,42 @@ +## Using Garden with Minikube + +Garden can be used with [Minikube](https://github.com/kubernetes/minikube) on supported platforms. + +### Installation + +For installation instructions, please see the [official guide](https://github.com/kubernetes/minikube#installation). +You'll likely also need to install a driver to run the Minikube VM, please follow the +[instructions here](https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#hyperkit-driver) +and note the name of the driver. + +Once Minikube and the appropriate driver for your OS is installed, you can start it by running: + + minikube start --vm-driver= # e.g. hyperkit on macOS + +You'll also need to have Docker (for macOS, we recommend [Docker for Mac](https://docs.docker.com/engine/installation/)) +and [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) installed. + +_NOTE: Garden is not yet officially supported on Windows, but we have every intention to support it. +Please file any issues and we will try and respond promptly._ + +### Usage + +The `local-kubernetes` plugin attempts to automatically detect if it is installed and set the appropriate context +for connecting to the local Kubernetes instance. In most cases you should not have to update your `garden.yml` +since it uses the `local-kubernetes` plugin by default, but you can configure it explicitly in your project +`garden.yml` like so: + +```yaml +project: + environments: + - name: local + providers: + - name: local-kubernetes + context: minikube +``` + +_Note: If you happen to have installed both Minikube and the Docker for Mac version with Kubernetes enabled, +`garden` will choose whichever one is configured as the current context in your `kubectl` configuration, and if neither +is set as the current context, Docker for Mac is preferred by default._ + +Once configured, the `local-kubernetes` plugin will automatically configure everything Garden needs to work. diff --git a/examples/hello-world/garden.yml b/examples/hello-world/garden.yml index 1c1fc85628..3a14de0bd1 100644 --- a/examples/hello-world/garden.yml +++ b/examples/hello-world/garden.yml @@ -10,7 +10,6 @@ project: - name: local providers: - name: local-kubernetes - context: docker-for-desktop - name: local-google-cloud-functions - name: dev providers: diff --git a/garden.yml b/garden.yml index 24b4eb7a36..1850cd3e62 100644 --- a/garden.yml +++ b/garden.yml @@ -5,3 +5,5 @@ project: providers: - name: container - name: local-kubernetes + # note: this context is not actually used for anything + context: docker-for-desktop diff --git a/src/garden.ts b/src/garden.ts index db33534532..29a90c3e0d 100644 --- a/src/garden.ts +++ b/src/garden.ts @@ -380,8 +380,8 @@ export class Garden { try { plugin = factory({ - garden: this, config, + logEntry: this.log, }) } catch (error) { throw new PluginError(`Unexpected error when loading plugin "${pluginName}": ${error}`, { diff --git a/src/plugins/kubernetes/local.ts b/src/plugins/kubernetes/local.ts index f195c9a916..95e4878a97 100644 --- a/src/plugins/kubernetes/local.ts +++ b/src/plugins/kubernetes/local.ts @@ -6,11 +6,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { execSync } from "child_process" +import { readFileSync } from "fs" +import { safeLoad } from "js-yaml" import { every, values, } from "lodash" import * as Joi from "joi" +import { join } from "path" import { validate } from "../../types/common" import { ConfigureEnvironmentParams, @@ -32,6 +36,11 @@ import { isSystemGarden, } from "./system" +// 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(process.env.HOME || "~", ".kube", "config") + // extend the environment configuration to also set up an ingress controller and dashboard export async function getLocalEnvironmentStatus( { ctx, provider, env, logEntry }: GetEnvironmentStatusParams, @@ -78,19 +87,72 @@ async function configureLocalEnvironment( } } -export const name = "local-kubernetes" +function getKubeConfig(): any { + try { + return safeLoad(readFileSync(kubeConfigPath).toString()) + } catch { + return {} + } +} + +function setMinikubeDockerEnv() { + const minikubeEnv = execSync("minikube docker-env --shell=bash").toString() + for (const line of minikubeEnv.split("\n")) { + const matched = line.match(/^export (\w+)="(.+)"$/) + if (matched) { + process.env[matched[1]] = matched[2] + } + } +} const configSchema = providerConfigBase.keys({ - context: Joi.string().default("docker-for-desktop"), + context: Joi.string(), _system: Joi.any(), }) -export function gardenPlugin({ config }): GardenPlugin { +export const name = "local-kubernetes" + +export function gardenPlugin({ config, logEntry }): GardenPlugin { config = validate(config, configSchema, { context: "kubernetes provider config" }) + let context = config.context + + if (!context) { + // automatically detect supported kubectl context if not explicitly configured + const kubeConfig = getKubeConfig() + const currentContext = kubeConfig["current-context"] + + if (currentContext && supportedContexts.includes(currentContext)) { + // prefer current context if set and supported + context = currentContext + logEntry.debug({ section: name, msg: `Using current context: ${context}` }) + } else if (kubeConfig.contexts) { + const availableContexts = kubeConfig.contexts.map(c => c.name) + + for (const supportedContext of supportedContexts) { + if (availableContexts.includes(supportedContext)) { + context = supportedContext + logEntry.debug({ section: name, msg: `Using detected context: ${context}` }) + break + } + } + } + } + + if (!context) { + context = supportedContexts[0] + logEntry.debug({ section: name, msg: `No kubectl context auto-deteced, using default: ${context}` }) + } + + if (context === "minikube") { + // automatically set docker environment variables for minikube + // TODO: it would be better to explicitly provide those to docker instead of using process.env + setMinikubeDockerEnv() + } + const k8sConfig: KubernetesConfig = { name: config.name, - context: config.context, + context, ingressHostname: "local.app.garden", ingressClass: "nginx", // TODO: support SSL on local deployments diff --git a/src/types/plugin.ts b/src/types/plugin.ts index e2d9a1dab1..53d676f3b0 100644 --- a/src/types/plugin.ts +++ b/src/types/plugin.ts @@ -318,7 +318,7 @@ export interface GardenPlugin { } export interface PluginFactory { - ({ garden: Garden, config: object }): GardenPlugin + ({ config: object, logEntry: LogEntry }): GardenPlugin pluginName?: string } export type RegisterPluginParam = string | PluginFactory diff --git a/src/types/project.ts b/src/types/project.ts index 1b99c597b1..0679fdd353 100644 --- a/src/types/project.ts +++ b/src/types/project.ts @@ -46,7 +46,6 @@ export const defaultEnvironments: EnvironmentConfig[] = [ providers: [ { name: "local-kubernetes", - context: "docker-for-desktop", }, ], variables: {},