Skip to content

Commit

Permalink
fix(local-k8s): remove hardcoded ingress class
Browse files Browse the repository at this point in the history
  • Loading branch information
edvald committed Feb 22, 2019
1 parent 291f368 commit 9eb6052
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 165 deletions.
2 changes: 1 addition & 1 deletion garden-service/src/docs/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { execModuleSpecSchema } from "../plugins/exec"
import { projectSchema } from "../config/project"
import { baseModuleSpecSchema } from "../config/module"
import handlebars = require("handlebars")
import { configSchema as localK8sConfigSchema } from "../plugins/kubernetes/local/local"
import { configSchema as localK8sConfigSchema } from "../plugins/kubernetes/local/config"
import { configSchema as k8sConfigSchema } from "../plugins/kubernetes/kubernetes"
import { configSchema as openfaasConfigSchema } from "../plugins/openfaas/openfaas"
import { openfaasModuleSpecSchema } from "../plugins/openfaas/openfaas"
Expand Down
12 changes: 10 additions & 2 deletions garden-service/src/garden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ export class Garden {
}

private async loadPlugin(pluginName: string, config: ProviderConfig) {
this.log.silly(`Loading plugin ${pluginName}`)
const factory = this.registeredPlugins[pluginName]

if (!factory) {
Expand Down Expand Up @@ -450,7 +451,12 @@ export class Garden {
}

if (configureHandler) {
const configureOutput = await configureHandler({ config: providerConfig })
this.log.silly(`Calling configureProvider on ${pluginName}`)
const configureOutput = await configureHandler({
config: providerConfig,
projectName: this.projectName,
log: this.log,
})
providerConfig = configureOutput.config
}

Expand All @@ -459,6 +465,8 @@ export class Garden {
} else {
this.environment.providers[providerIndex].config = providerConfig
}

this.log.silly(`Done loading plugin ${pluginName}`)
}

getPlugin(pluginName: string) {
Expand Down Expand Up @@ -509,7 +517,7 @@ export class Garden {
})
const ctx = this.getPluginContext(configureHandler["pluginName"])

config = await configureHandler({ ctx, moduleConfig: config })
config = await configureHandler({ ctx, moduleConfig: config, log: this.log })

// FIXME: We should be able to avoid this
config.name = getModuleKey(config.name, config.plugin)
Expand Down
146 changes: 146 additions & 0 deletions garden-service/src/plugins/kubernetes/local/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Copyright (C) 2018 Garden Technologies, Inc. <info@garden.io>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

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"

// 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<any> {
try {
return safeLoad((await readFile(kubeConfigPath)).toString())
} catch {
return {}
}
}

/**
* Automatically set docker environment variables for minikube
* TODO: it would be better to explicitly provide those to docker instead of using process.env
*/
async function setMinikubeDockerEnv() {
const minikubeEnv = await execa.stdout("minikube", ["docker-env", "--shell=bash"])
for (const line of minikubeEnv.split("\n")) {
const matched = line.match(/^export (\w+)="(.+)"$/)
if (matched) {
process.env[matched[1]] = matched[2]
}
}
}

export interface LocalKubernetesConfig extends KubernetesBaseConfig {
_system?: Symbol
setupIngressController: string | null
}

export const configSchema = kubernetesConfigBase
.keys({
namespace: Joi.string()
.default(undefined, "<project name>")
.description(
"Specify which namespace to deploy services to (defaults to the project name). " +
"Note that the framework generates other namespaces as well with this name as a prefix.",
),
setupIngressController: Joi.string()
.allow("nginx", false, null)
.default("nginx")
.description("Set this to null or false to skip installing/enabling the `nginx` ingress controller."),
_system: Joi.any().meta({ internal: true }),
})
.description("The provider configuration for the local-kubernetes plugin.")

export async function configureProvider({ config, log, projectName }: ConfigureProviderParams<LocalKubernetesConfig>) {
let context = config.context
let defaultHostname = config.defaultHostname
let setupIngressController = config.setupIngressController

if (!context) {
// automatically detect supported kubectl context if not explicitly configured
const kubeConfig = await getKubeConfig()
const currentContext = kubeConfig["current-context"]

if (currentContext && supportedContexts.includes(currentContext)) {
// prefer current context if set and supported
context = currentContext
log.debug({ section: config.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
log.debug({ section: config.name, msg: `Using detected context: ${context}` })
break
}
}
}
}

if (!context) {
context = supportedContexts[0]
log.debug({ section: config.name, msg: `No kubectl context auto-detected, using default: ${context}` })
}

if (context === "minikube") {
await execa("minikube", ["config", "set", "WantUpdateNotification", "false"])

if (!defaultHostname) {
// use the nip.io service to give a hostname to the instance, if none is explicitly configured
const minikubeIp = await execa.stdout("minikube", ["ip"])
defaultHostname = `${projectName}.${minikubeIp}.nip.io`
}

if (config.setupIngressController === "nginx") {
log.silly("Using minikube's ingress addon")
await execa("minikube", ["addons", "enable", "ingress"])
// make sure the prepare handler doesn't also set up the ingress controller
setupIngressController = null
}

await setMinikubeDockerEnv()

} else {
if (!defaultHostname) {
defaultHostname = `${projectName}.local.app.garden`
}
}

const ingressClass = config.ingressClass || config.setupIngressController || undefined

config = {
name: config.name,
context,
defaultHostname,
deploymentRegistry: {
hostname: "foo.garden", // this is not used by this plugin, but required by the base plugin
namespace: "_",
},
forceSsl: false,
imagePullSecrets: config.imagePullSecrets,
ingressHttpPort: 80,
ingressHttpsPort: 443,
ingressClass,
namespace: config.namespace || projectName,
setupIngressController,
tlsCertificates: config.tlsCertificates,
_system: config._system,
}

return { name: config.name, config }
}
146 changes: 5 additions & 141 deletions garden-service/src/plugins/kubernetes/local/local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,155 +6,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import * as execa from "execa"
import { safeLoad } from "js-yaml"
import * as Joi from "joi"
import { join } from "path"
import { GardenPlugin, PluginFactoryParams } from "../../../types/plugin/plugin"
import {
gardenPlugin as k8sPlugin,
KubernetesBaseConfig,
kubernetesConfigBase,
} from "../kubernetes"
import { readFile } from "fs-extra"
import { homedir } from "os"
import { GardenPlugin } from "../../../types/plugin/plugin"
import { gardenPlugin as k8sPlugin } from "../kubernetes"
import { getLocalEnvironmentStatus, prepareLocalEnvironment } from "../init"
import { ConfigureProviderParams } from "../../../types/plugin/params"

// 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<any> {
try {
return safeLoad((await readFile(kubeConfigPath)).toString())
} catch {
return {}
}
}

/**
* Automatically set docker environment variables for minikube
* TODO: it would be better to explicitly provide those to docker instead of using process.env
*/
async function setMinikubeDockerEnv() {
const minikubeEnv = await execa.stdout("minikube", ["docker-env", "--shell=bash"])
for (const line of minikubeEnv.split("\n")) {
const matched = line.match(/^export (\w+)="(.+)"$/)
if (matched) {
process.env[matched[1]] = matched[2]
}
}
}

export interface LocalKubernetesConfig extends KubernetesBaseConfig {
_system?: Symbol
setupIngressController: string | boolean | null
}

export const configSchema = kubernetesConfigBase
.keys({
namespace: Joi.string()
.default(undefined, "<project name>")
.description(
"Specify which namespace to deploy services to (defaults to the project name). " +
"Note that the framework generates other namespaces as well with this name as a prefix.",
),
setupIngressController: Joi.string()
.allow("nginx", false, null)
.default("nginx")
.description("Set this to null or false to skip installing/enabling the `nginx` ingress controller."),
_system: Joi.any().meta({ internal: true }),
})
.description("The provider configuration for the local-kubernetes plugin.")
import { configureProvider, configSchema } from "./config"

export const name = "local-kubernetes"

export function gardenPlugin({ projectName, log }: PluginFactoryParams): GardenPlugin {
export function gardenPlugin(): GardenPlugin {
const plugin = k8sPlugin()

plugin.configSchema = configSchema

plugin.actions!.configureProvider = async ({ config }: ConfigureProviderParams<LocalKubernetesConfig>) => {
let context = config.context
let defaultHostname = config.defaultHostname
let setupIngressController = config.setupIngressController

if (!context) {
// automatically detect supported kubectl context if not explicitly configured
const kubeConfig = await getKubeConfig()
const currentContext = kubeConfig["current-context"]

if (currentContext && supportedContexts.includes(currentContext)) {
// prefer current context if set and supported
context = currentContext
log.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
log.debug({ section: name, msg: `Using detected context: ${context}` })
break
}
}
}
}

if (!context) {
context = supportedContexts[0]
log.debug({ section: name, msg: `No kubectl context auto-detected, using default: ${context}` })
}

if (context === "minikube") {
await execa("minikube", ["config", "set", "WantUpdateNotification", "false"])

if (!defaultHostname) {
// use the nip.io service to give a hostname to the instance, if none is explicitly configured
const minikubeIp = await execa.stdout("minikube", ["ip"])
defaultHostname = `${projectName}.${minikubeIp}.nip.io`
}

if (config.setupIngressController === "nginx") {
log.silly("Using minikube's ingress addon")
await execa("minikube", ["addons", "enable", "ingress"])
// make sure the prepare handler doesn't also set up the ingress controller
setupIngressController = false
}

await setMinikubeDockerEnv()

} else {
if (!defaultHostname) {
defaultHostname = `${projectName}.local.app.garden`
}
}

config = {
name: config.name,
context,
defaultHostname,
deploymentRegistry: {
hostname: "foo.garden", // this is not used by this plugin, but required by the base plugin
namespace: "_",
},
forceSsl: false,
imagePullSecrets: config.imagePullSecrets,
ingressHttpPort: 80,
ingressHttpsPort: 443,
ingressClass: "nginx",
namespace: config.namespace || projectName,
setupIngressController,
tlsCertificates: config.tlsCertificates,
_system: config._system,
}

return { name: config.name, config }
}
plugin.actions!.configureProvider = configureProvider

// override the environment configuration steps
plugin.actions!.getEnvironmentStatus = getLocalEnvironmentStatus
Expand Down
Loading

0 comments on commit 9eb6052

Please sign in to comment.