diff --git a/docs/minikube.md b/docs/minikube.md index 6c5bb9e5d5..cf6176cd64 100644 --- a/docs/minikube.md +++ b/docs/minikube.md @@ -4,7 +4,8 @@ Garden can be used with [Minikube](https://github.com/kubernetes/minikube) on su ### Installation -For installation instructions, please see the [official guide](https://github.com/kubernetes/minikube#installation). +For Minikube 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. @@ -39,4 +40,13 @@ _Note: If you happen to have installed both Minikube and the Docker for Mac vers `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. +### Hostname + +Garden needs the Kubernetes instance to have a hostname. By default Garden will use `.nip.io`. If you'd +like to use a custom hostname, you can specify it via the `ingressHostname` in the `local-kubernetes` provider config +(see above). + +### Anything else? + +Once the above is set up, the `local-kubernetes` plugin will automatically configure everything else Garden needs to +work. The built-in nginx ingress controller will be automatically enabled and used to route requests to services. diff --git a/src/plugins/kubernetes/index.ts b/src/plugins/kubernetes/index.ts index 361e8adfae..49f31fc9b9 100644 --- a/src/plugins/kubernetes/index.ts +++ b/src/plugins/kubernetes/index.ts @@ -43,9 +43,9 @@ export const name = "kubernetes" export interface KubernetesConfig extends ProviderConfig { context: string ingressHostname: string + ingressPort: number ingressClass: string forceSsl: boolean - _system?: Symbol } export interface KubernetesProvider extends Provider { } @@ -53,6 +53,7 @@ export interface KubernetesProvider extends Provider { } const configSchema = providerConfigBase.keys({ context: Joi.string().required(), ingressHostname: Joi.string().hostname().required(), + ingressPort: Joi.number().default(80), ingressClass: Joi.string(), forceSsl: Joi.boolean().default(true), _system: Joi.any(), diff --git a/src/plugins/kubernetes/local.ts b/src/plugins/kubernetes/local.ts index ba7883f745..91db033fb3 100644 --- a/src/plugins/kubernetes/local.ts +++ b/src/plugins/kubernetes/local.ts @@ -15,6 +15,8 @@ import { } from "lodash" import * as Joi from "joi" import { join } from "path" +import { PluginError } from "../../exceptions" +import { DeployTask } from "../../tasks/deploy" import { validate } from "../../types/common" import { GardenPlugin, @@ -38,6 +40,8 @@ import { isSystemGarden, } from "./system" +// 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"] @@ -63,29 +67,50 @@ export async function getLocalEnvironmentStatus( } async function configureLocalEnvironment( - { ctx, provider, env, status, logEntry }: ConfigureEnvironmentParams, + { ctx, provider, env, force, status, logEntry }: ConfigureEnvironmentParams, ) { - await configureEnvironment({ ctx, provider, env, status, logEntry }) + await configureEnvironment({ ctx, provider, env, force, status, logEntry }) if (!isSystemGarden(provider)) { const sysGarden = await getSystemGarden(provider) + const sysCtx = sysGarden.pluginContext const sysProvider = { name: provider.name, config: findByName(sysGarden.config.providers, provider.name)!, } + const sysStatus = await getEnvironmentStatus({ - ctx: sysGarden.pluginContext, + ctx: sysCtx, provider: sysProvider, env, }) + await configureEnvironment({ - ctx: sysGarden.pluginContext, + ctx: sysCtx, env: sysGarden.getEnvironment(), provider: sysProvider, + force, status: sysStatus, logEntry, }) - await sysGarden.pluginContext.deployServices({ logEntry }) + + const services = await sysCtx.getServices(provider.config._systemServices) + + const results = await sysCtx.processServices({ + services, + watch: false, + process: async (service) => { + return [await DeployTask.factory({ ctx: sysCtx, service, force, forceBuild: false })] + }, + }) + + const failed = values(results).filter(r => !!r.error).length + + if (failed) { + throw new PluginError(`local-kubernetes: ${failed} errors occurred when configuring environments`, { + results, + }) + } } return {} @@ -109,9 +134,16 @@ function setMinikubeDockerEnv() { } } +export interface LocalKubernetesConfig extends KubernetesConfig { + _system?: Symbol + _systemServices?: string[] +} + const configSchema = providerConfigBase.keys({ context: Joi.string(), + ingressHostname: Joi.string(), _system: Joi.any(), + _systemServices: Joi.array().items(Joi.string()), }) export const name = "local-kubernetes" @@ -120,6 +152,9 @@ export function gardenPlugin({ config, logEntry }): GardenPlugin { config = validate(config, configSchema, { context: "kubernetes provider config" }) let context = config.context + let systemServices + let ingressHostname + let ingressPort if (!context) { // automatically detect supported kubectl context if not explicitly configured @@ -149,19 +184,37 @@ export function gardenPlugin({ config, logEntry }): GardenPlugin { } if (context === "minikube") { + execSync("minikube addons enable ingress") + + ingressHostname = config.ingressHostname + + if (!ingressHostname) { + // use the nip.io service to give a hostname to the instance, if none is explicitly configured + const minikubeIp = execSync("minikube ip").toString().trim() + ingressHostname = minikubeIp + ".nip.io" + } + + ingressPort = 80 + systemServices = [] + // automatically set docker environment variables for minikube // TODO: it would be better to explicitly provide those to docker instead of using process.env setMinikubeDockerEnv() + } else { + ingressHostname = config.ingressHostname || "local.app.garden" + ingressPort = 32000 } - const k8sConfig: KubernetesConfig = { + const k8sConfig: LocalKubernetesConfig = { name: config.name, context, - ingressHostname: "local.app.garden", + ingressHostname, + ingressPort, ingressClass: "nginx", // TODO: support SSL on local deployments forceSsl: false, _system: config._system, + _systemServices: systemServices, } const plugin = k8sPlugin({ config: k8sConfig }) diff --git a/src/plugins/kubernetes/status.ts b/src/plugins/kubernetes/status.ts index b45be58eb5..cddc23796b 100644 --- a/src/plugins/kubernetes/status.ts +++ b/src/plugins/kubernetes/status.ts @@ -28,7 +28,6 @@ import { import { getServiceHostname } from "./ingress" import { KUBECTL_DEFAULT_TIMEOUT } from "./kubectl" import { getAppNamespace } from "./namespace" -import { localIngressPort } from "./system" export async function checkDeploymentStatus( { ctx, provider, service, resourceVersion }: @@ -42,6 +41,7 @@ export async function checkDeploymentStatus( const endpoints = service.spec.endpoints.map((e: ServiceEndpointSpec) => { // TODO: this should be HTTPS, once we've set up TLS termination at the ingress controller level const protocol: ServiceProtocol = "http" + const localIngressPort = provider.config.ingressPort return { protocol, diff --git a/src/plugins/kubernetes/system.ts b/src/plugins/kubernetes/system.ts index 16eeaf7e34..4e1108132a 100644 --- a/src/plugins/kubernetes/system.ts +++ b/src/plugins/kubernetes/system.ts @@ -12,7 +12,6 @@ import { Garden } from "../../garden" import { KubernetesProvider } from "./index" export const GARDEN_SYSTEM_NAMESPACE = "garden-system" -export const localIngressPort = 32000 const systemProjectPath = join(STATIC_DIR, "kubernetes", "system") const systemSymbol = Symbol()