Skip to content

Commit 9eb6052

Browse files
committed
fix(local-k8s): remove hardcoded ingress class
1 parent 291f368 commit 9eb6052

File tree

8 files changed

+187
-165
lines changed

8 files changed

+187
-165
lines changed

garden-service/src/docs/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { execModuleSpecSchema } from "../plugins/exec"
2424
import { projectSchema } from "../config/project"
2525
import { baseModuleSpecSchema } from "../config/module"
2626
import handlebars = require("handlebars")
27-
import { configSchema as localK8sConfigSchema } from "../plugins/kubernetes/local/local"
27+
import { configSchema as localK8sConfigSchema } from "../plugins/kubernetes/local/config"
2828
import { configSchema as k8sConfigSchema } from "../plugins/kubernetes/kubernetes"
2929
import { configSchema as openfaasConfigSchema } from "../plugins/openfaas/openfaas"
3030
import { openfaasModuleSpecSchema } from "../plugins/openfaas/openfaas"

garden-service/src/garden.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ export class Garden {
375375
}
376376

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

380381
if (!factory) {
@@ -450,7 +451,12 @@ export class Garden {
450451
}
451452

452453
if (configureHandler) {
453-
const configureOutput = await configureHandler({ config: providerConfig })
454+
this.log.silly(`Calling configureProvider on ${pluginName}`)
455+
const configureOutput = await configureHandler({
456+
config: providerConfig,
457+
projectName: this.projectName,
458+
log: this.log,
459+
})
454460
providerConfig = configureOutput.config
455461
}
456462

@@ -459,6 +465,8 @@ export class Garden {
459465
} else {
460466
this.environment.providers[providerIndex].config = providerConfig
461467
}
468+
469+
this.log.silly(`Done loading plugin ${pluginName}`)
462470
}
463471

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

512-
config = await configureHandler({ ctx, moduleConfig: config })
520+
config = await configureHandler({ ctx, moduleConfig: config, log: this.log })
513521

514522
// FIXME: We should be able to avoid this
515523
config.name = getModuleKey(config.name, config.plugin)
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Copyright (C) 2018 Garden Technologies, Inc. <info@garden.io>
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
9+
import * as execa from "execa"
10+
import { safeLoad } from "js-yaml"
11+
import * as Joi from "joi"
12+
import { join } from "path"
13+
import { readFile } from "fs-extra"
14+
import { homedir } from "os"
15+
import { KubernetesBaseConfig, kubernetesConfigBase } from "../kubernetes"
16+
import { ConfigureProviderParams } from "../../../types/plugin/params"
17+
18+
// TODO: split this into separate plugins to handle Docker for Mac and Minikube
19+
20+
// note: this is in order of preference, in case neither is set as the current kubectl context
21+
// and none is explicitly configured in the garden.yml
22+
const supportedContexts = ["docker-for-desktop", "minikube"]
23+
const kubeConfigPath = join(homedir(), ".kube", "config")
24+
25+
async function getKubeConfig(): Promise<any> {
26+
try {
27+
return safeLoad((await readFile(kubeConfigPath)).toString())
28+
} catch {
29+
return {}
30+
}
31+
}
32+
33+
/**
34+
* Automatically set docker environment variables for minikube
35+
* TODO: it would be better to explicitly provide those to docker instead of using process.env
36+
*/
37+
async function setMinikubeDockerEnv() {
38+
const minikubeEnv = await execa.stdout("minikube", ["docker-env", "--shell=bash"])
39+
for (const line of minikubeEnv.split("\n")) {
40+
const matched = line.match(/^export (\w+)="(.+)"$/)
41+
if (matched) {
42+
process.env[matched[1]] = matched[2]
43+
}
44+
}
45+
}
46+
47+
export interface LocalKubernetesConfig extends KubernetesBaseConfig {
48+
_system?: Symbol
49+
setupIngressController: string | null
50+
}
51+
52+
export const configSchema = kubernetesConfigBase
53+
.keys({
54+
namespace: Joi.string()
55+
.default(undefined, "<project name>")
56+
.description(
57+
"Specify which namespace to deploy services to (defaults to the project name). " +
58+
"Note that the framework generates other namespaces as well with this name as a prefix.",
59+
),
60+
setupIngressController: Joi.string()
61+
.allow("nginx", false, null)
62+
.default("nginx")
63+
.description("Set this to null or false to skip installing/enabling the `nginx` ingress controller."),
64+
_system: Joi.any().meta({ internal: true }),
65+
})
66+
.description("The provider configuration for the local-kubernetes plugin.")
67+
68+
export async function configureProvider({ config, log, projectName }: ConfigureProviderParams<LocalKubernetesConfig>) {
69+
let context = config.context
70+
let defaultHostname = config.defaultHostname
71+
let setupIngressController = config.setupIngressController
72+
73+
if (!context) {
74+
// automatically detect supported kubectl context if not explicitly configured
75+
const kubeConfig = await getKubeConfig()
76+
const currentContext = kubeConfig["current-context"]
77+
78+
if (currentContext && supportedContexts.includes(currentContext)) {
79+
// prefer current context if set and supported
80+
context = currentContext
81+
log.debug({ section: config.name, msg: `Using current context: ${context}` })
82+
} else if (kubeConfig.contexts) {
83+
const availableContexts = kubeConfig.contexts.map(c => c.name)
84+
85+
for (const supportedContext of supportedContexts) {
86+
if (availableContexts.includes(supportedContext)) {
87+
context = supportedContext
88+
log.debug({ section: config.name, msg: `Using detected context: ${context}` })
89+
break
90+
}
91+
}
92+
}
93+
}
94+
95+
if (!context) {
96+
context = supportedContexts[0]
97+
log.debug({ section: config.name, msg: `No kubectl context auto-detected, using default: ${context}` })
98+
}
99+
100+
if (context === "minikube") {
101+
await execa("minikube", ["config", "set", "WantUpdateNotification", "false"])
102+
103+
if (!defaultHostname) {
104+
// use the nip.io service to give a hostname to the instance, if none is explicitly configured
105+
const minikubeIp = await execa.stdout("minikube", ["ip"])
106+
defaultHostname = `${projectName}.${minikubeIp}.nip.io`
107+
}
108+
109+
if (config.setupIngressController === "nginx") {
110+
log.silly("Using minikube's ingress addon")
111+
await execa("minikube", ["addons", "enable", "ingress"])
112+
// make sure the prepare handler doesn't also set up the ingress controller
113+
setupIngressController = null
114+
}
115+
116+
await setMinikubeDockerEnv()
117+
118+
} else {
119+
if (!defaultHostname) {
120+
defaultHostname = `${projectName}.local.app.garden`
121+
}
122+
}
123+
124+
const ingressClass = config.ingressClass || config.setupIngressController || undefined
125+
126+
config = {
127+
name: config.name,
128+
context,
129+
defaultHostname,
130+
deploymentRegistry: {
131+
hostname: "foo.garden", // this is not used by this plugin, but required by the base plugin
132+
namespace: "_",
133+
},
134+
forceSsl: false,
135+
imagePullSecrets: config.imagePullSecrets,
136+
ingressHttpPort: 80,
137+
ingressHttpsPort: 443,
138+
ingressClass,
139+
namespace: config.namespace || projectName,
140+
setupIngressController,
141+
tlsCertificates: config.tlsCertificates,
142+
_system: config._system,
143+
}
144+
145+
return { name: config.name, config }
146+
}

garden-service/src/plugins/kubernetes/local/local.ts

Lines changed: 5 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -6,155 +6,19 @@
66
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
77
*/
88

9-
import * as execa from "execa"
10-
import { safeLoad } from "js-yaml"
11-
import * as Joi from "joi"
12-
import { join } from "path"
13-
import { GardenPlugin, PluginFactoryParams } from "../../../types/plugin/plugin"
14-
import {
15-
gardenPlugin as k8sPlugin,
16-
KubernetesBaseConfig,
17-
kubernetesConfigBase,
18-
} from "../kubernetes"
19-
import { readFile } from "fs-extra"
20-
import { homedir } from "os"
9+
import { GardenPlugin } from "../../../types/plugin/plugin"
10+
import { gardenPlugin as k8sPlugin } from "../kubernetes"
2111
import { getLocalEnvironmentStatus, prepareLocalEnvironment } from "../init"
22-
import { ConfigureProviderParams } from "../../../types/plugin/params"
23-
24-
// TODO: split this into separate plugins to handle Docker for Mac and Minikube
25-
26-
// note: this is in order of preference, in case neither is set as the current kubectl context
27-
// and none is explicitly configured in the garden.yml
28-
const supportedContexts = ["docker-for-desktop", "minikube"]
29-
const kubeConfigPath = join(homedir(), ".kube", "config")
30-
31-
async function getKubeConfig(): Promise<any> {
32-
try {
33-
return safeLoad((await readFile(kubeConfigPath)).toString())
34-
} catch {
35-
return {}
36-
}
37-
}
38-
39-
/**
40-
* Automatically set docker environment variables for minikube
41-
* TODO: it would be better to explicitly provide those to docker instead of using process.env
42-
*/
43-
async function setMinikubeDockerEnv() {
44-
const minikubeEnv = await execa.stdout("minikube", ["docker-env", "--shell=bash"])
45-
for (const line of minikubeEnv.split("\n")) {
46-
const matched = line.match(/^export (\w+)="(.+)"$/)
47-
if (matched) {
48-
process.env[matched[1]] = matched[2]
49-
}
50-
}
51-
}
52-
53-
export interface LocalKubernetesConfig extends KubernetesBaseConfig {
54-
_system?: Symbol
55-
setupIngressController: string | boolean | null
56-
}
57-
58-
export const configSchema = kubernetesConfigBase
59-
.keys({
60-
namespace: Joi.string()
61-
.default(undefined, "<project name>")
62-
.description(
63-
"Specify which namespace to deploy services to (defaults to the project name). " +
64-
"Note that the framework generates other namespaces as well with this name as a prefix.",
65-
),
66-
setupIngressController: Joi.string()
67-
.allow("nginx", false, null)
68-
.default("nginx")
69-
.description("Set this to null or false to skip installing/enabling the `nginx` ingress controller."),
70-
_system: Joi.any().meta({ internal: true }),
71-
})
72-
.description("The provider configuration for the local-kubernetes plugin.")
12+
import { configureProvider, configSchema } from "./config"
7313

7414
export const name = "local-kubernetes"
7515

76-
export function gardenPlugin({ projectName, log }: PluginFactoryParams): GardenPlugin {
16+
export function gardenPlugin(): GardenPlugin {
7717
const plugin = k8sPlugin()
7818

7919
plugin.configSchema = configSchema
8020

81-
plugin.actions!.configureProvider = async ({ config }: ConfigureProviderParams<LocalKubernetesConfig>) => {
82-
let context = config.context
83-
let defaultHostname = config.defaultHostname
84-
let setupIngressController = config.setupIngressController
85-
86-
if (!context) {
87-
// automatically detect supported kubectl context if not explicitly configured
88-
const kubeConfig = await getKubeConfig()
89-
const currentContext = kubeConfig["current-context"]
90-
91-
if (currentContext && supportedContexts.includes(currentContext)) {
92-
// prefer current context if set and supported
93-
context = currentContext
94-
log.debug({ section: name, msg: `Using current context: ${context}` })
95-
} else if (kubeConfig.contexts) {
96-
const availableContexts = kubeConfig.contexts.map(c => c.name)
97-
98-
for (const supportedContext of supportedContexts) {
99-
if (availableContexts.includes(supportedContext)) {
100-
context = supportedContext
101-
log.debug({ section: name, msg: `Using detected context: ${context}` })
102-
break
103-
}
104-
}
105-
}
106-
}
107-
108-
if (!context) {
109-
context = supportedContexts[0]
110-
log.debug({ section: name, msg: `No kubectl context auto-detected, using default: ${context}` })
111-
}
112-
113-
if (context === "minikube") {
114-
await execa("minikube", ["config", "set", "WantUpdateNotification", "false"])
115-
116-
if (!defaultHostname) {
117-
// use the nip.io service to give a hostname to the instance, if none is explicitly configured
118-
const minikubeIp = await execa.stdout("minikube", ["ip"])
119-
defaultHostname = `${projectName}.${minikubeIp}.nip.io`
120-
}
121-
122-
if (config.setupIngressController === "nginx") {
123-
log.silly("Using minikube's ingress addon")
124-
await execa("minikube", ["addons", "enable", "ingress"])
125-
// make sure the prepare handler doesn't also set up the ingress controller
126-
setupIngressController = false
127-
}
128-
129-
await setMinikubeDockerEnv()
130-
131-
} else {
132-
if (!defaultHostname) {
133-
defaultHostname = `${projectName}.local.app.garden`
134-
}
135-
}
136-
137-
config = {
138-
name: config.name,
139-
context,
140-
defaultHostname,
141-
deploymentRegistry: {
142-
hostname: "foo.garden", // this is not used by this plugin, but required by the base plugin
143-
namespace: "_",
144-
},
145-
forceSsl: false,
146-
imagePullSecrets: config.imagePullSecrets,
147-
ingressHttpPort: 80,
148-
ingressHttpsPort: 443,
149-
ingressClass: "nginx",
150-
namespace: config.namespace || projectName,
151-
setupIngressController,
152-
tlsCertificates: config.tlsCertificates,
153-
_system: config._system,
154-
}
155-
156-
return { name: config.name, config }
157-
}
21+
plugin.actions!.configureProvider = configureProvider
15822

15923
// override the environment configuration steps
16024
plugin.actions!.getEnvironmentStatus = getLocalEnvironmentStatus

0 commit comments

Comments
 (0)