Skip to content

Commit

Permalink
refactor: add configureProvider plugin action
Browse files Browse the repository at this point in the history
  • Loading branch information
edvald committed Jan 25, 2019
1 parent 7b02fdd commit bdf6994
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 116 deletions.
2 changes: 1 addition & 1 deletion garden-service/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ export class ActionHelper implements TypeGuard {
}
}

private async callActionHandler<T extends keyof PluginActions>(
private async callActionHandler<T extends keyof Omit<PluginActions, "configureProvider">>(
{ params, actionType, pluginName, defaultHandler }:
{
params: ActionHelperParams<PluginActionParams[T]>,
Expand Down
46 changes: 30 additions & 16 deletions garden-service/src/garden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
sortBy,
difference,
find,
findIndex,
} from "lodash"
const AsyncLock = require("async-lock")

Expand All @@ -44,7 +45,7 @@ import {
pluginModuleSchema,
pluginSchema,
} from "./types/plugin/plugin"
import { Environment, SourceConfig, defaultProvider, Provider } from "./config/project"
import { Environment, SourceConfig, defaultProvider, ProviderConfig, Provider } from "./config/project"
import {
findByName,
getIgnorer,
Expand Down Expand Up @@ -418,7 +419,7 @@ export class Garden {
this.registeredPlugins[name] = factory
}

private async loadPlugin(pluginName: string, config: object) {
private async loadPlugin(pluginName: string, config: ProviderConfig) {
const factory = this.registeredPlugins[pluginName]

if (!factory) {
Expand All @@ -428,12 +429,11 @@ export class Garden {
})
}

let plugin
let plugin: GardenPlugin

try {
plugin = await factory({
projectName: this.projectName,
config,
log: this.log,
})
} catch (error) {
Expand All @@ -447,18 +447,6 @@ export class Garden {

this.loadedPlugins[pluginName] = plugin

// allow plugins to extend their own config (that gets passed to action handlers)
const providerConfig = findByName(this.environment.providers, pluginName)
if (providerConfig) {
extend(providerConfig, plugin.config, config)
} else {
const provider: Provider = {
name: pluginName,
config: extend({ name: pluginName }, plugin.config, config),
}
this.environment.providers.push(provider)
}

for (const modulePath of plugin.modules || []) {
let moduleConfig = await this.resolveModule(modulePath)
if (!moduleConfig) {
Expand Down Expand Up @@ -486,6 +474,32 @@ export class Garden {
handler && this.addModuleActionHandler(pluginName, actionType, moduleType, handler)
}
}

// allow plugins to be configured more than once
// (to support extending config for fixed plugins and environment defaults)
let providerIndex = findIndex(this.environment.providers, ["name", pluginName])
let providerConfig: ProviderConfig = providerIndex === -1
? config
: this.environment.providers[providerIndex].config

extend(providerConfig, config)

// call configureProvider action if provided
const configureHandler = actions.configureProvider
if (configureHandler) {
const configureOutput = await configureHandler({ config: providerConfig })
providerConfig = configureOutput.config
}

if (plugin.configSchema) {
providerConfig = validate(providerConfig, plugin.configSchema, { context: `${pluginName} configuration` })
}

if (providerIndex === -1) {
this.environment.providers.push({ name: pluginName, config: providerConfig })
} else {
this.environment.providers[providerIndex].config = providerConfig
}
}

private getPlugin(pluginName: string) {
Expand Down
8 changes: 3 additions & 5 deletions garden-service/src/plugins/kubernetes/kubernetes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import * as Joi from "joi"
import dedent = require("dedent")

import { joiArray, joiIdentifier, validate } from "../../config/common"
import { joiArray, joiIdentifier } from "../../config/common"
import { GardenPlugin } from "../../types/plugin/plugin"
import { Provider, providerConfigBaseSchema, ProviderConfig } from "../../config/project"
import { helmHandlers } from "./helm/handlers"
Expand Down Expand Up @@ -139,11 +139,9 @@ const configSchema = kubernetesConfigBase
_system: Joi.any().meta({ internal: true }),
})

export function gardenPlugin({ config }: { config: KubernetesConfig }): GardenPlugin {
config = validate(config, configSchema, { context: "kubernetes provider config" })

export function gardenPlugin(): GardenPlugin {
return {
config,
configSchema,
actions: {
getEnvironmentStatus: getRemoteEnvironmentStatus,
prepareEnvironment: prepareRemoteEnvironment,
Expand Down
131 changes: 67 additions & 64 deletions garden-service/src/plugins/kubernetes/local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,16 @@ import * as execa from "execa"
import { safeLoad } from "js-yaml"
import * as Joi from "joi"
import { join } from "path"
import { GardenPlugin } from "../../types/plugin/plugin"
import { validate } from "../../config/common"
import { GardenPlugin, PluginFactoryParams } from "../../types/plugin/plugin"
import {
gardenPlugin as k8sPlugin,
KubernetesBaseConfig,
kubernetesConfigBase,
KubernetesConfig,
} from "./kubernetes"
import { readFile } from "fs-extra"
import { homedir } from "os"
import { getLocalEnvironmentStatus, prepareLocalEnvironment } from "./init"
import { ConfigureProviderParams } from "../../types/plugin/params"

// TODO: split this into separate plugins to handle Docker for Mac and Minikube

Expand Down Expand Up @@ -73,79 +72,83 @@ const configSchema = kubernetesConfigBase

export const name = "local-kubernetes"

export async function gardenPlugin({ projectName, config, log }): Promise<GardenPlugin> {
config = validate(config, configSchema, { context: "local-kubernetes provider config" })

let context = config.context
let defaultHostname = config.defaultHostname

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
export function gardenPlugin({ projectName, log }: PluginFactoryParams): GardenPlugin {
const plugin = k8sPlugin()

plugin.configSchema = configSchema

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

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) {
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 (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 (!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`
await Promise.all([
// TODO: wait for ingress addon to be ready, if it was previously disabled
execa("minikube", ["addons", "enable", "ingress"]),
setMinikubeDockerEnv(),
])
} else {
if (!defaultHostname) {
defaultHostname = `${projectName}.local.app.garden`
}
}

await Promise.all([
// TODO: wait for ingress addon to be ready, if it was previously disabled
execa("minikube", ["addons", "enable", "ingress"]),
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,
tlsCertificates: config.tlsCertificates,
_system: config._system,
}
}

const k8sConfig: KubernetesConfig = {
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: config.ingressClass,
namespace: config.namespace || projectName,
tlsCertificates: config.tlsCertificates,
_system: config._system,
return { name: config.name, config }
}

const plugin = k8sPlugin({ config: k8sConfig })

// override the environment configuration steps
plugin.actions!.getEnvironmentStatus = getLocalEnvironmentStatus
plugin.actions!.prepareEnvironment = prepareLocalEnvironment
Expand Down
5 changes: 2 additions & 3 deletions garden-service/src/plugins/openfaas/openfaas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,9 @@ const configSchema = providerConfigBaseSchema

type OpenFaasProvider = Provider<OpenFaasConfig>

export function gardenPlugin({ config }: { config: OpenFaasConfig }): GardenPlugin {
config = validate(config, configSchema, { context: "OpenFaaS provider config" })

export function gardenPlugin(): GardenPlugin {
return {
configSchema,
modules: [join(STATIC_DIR, "openfaas", "templates")],
actions: {
async getEnvironmentStatus({ ctx, log }: GetEnvironmentStatusParams) {
Expand Down
9 changes: 9 additions & 0 deletions garden-service/src/types/plugin/outputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ import { Module } from "../module"
import { ServiceStatus } from "../service"
import { moduleConfigSchema, ModuleConfig } from "../../config/module"
import { DashboardPage, dashboardPagesSchema } from "../../config/dashboard"
import { ProviderConfig, providerConfigBaseSchema, Provider } from "../../config/project"

export interface ConfigureProviderResult<T extends ProviderConfig = ProviderConfig> extends Provider<T> { }
export const configureProviderResultSchema = Joi.object()
.keys({
config: providerConfigBaseSchema,
})

export interface EnvironmentStatus {
ready: boolean
Expand Down Expand Up @@ -308,6 +315,8 @@ export const taskStatusSchema = Joi.object()
})

export interface PluginActionOutputs {
configureProvider: Promise<ConfigureProviderResult>

getEnvironmentStatus: Promise<EnvironmentStatus>
prepareEnvironment: Promise<PrepareEnvironmentResult>
cleanupEnvironment: Promise<CleanupEnvironmentResult>
Expand Down
33 changes: 24 additions & 9 deletions garden-service/src/types/plugin/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { EnvironmentStatus, ServiceLogEntry, environmentStatusSchema } from "./o
import { moduleConfigSchema } from "../../config/module"
import { testConfigSchema } from "../../config/test"
import { taskSchema } from "../../config/task"
import { ProviderConfig } from "../../config/project"

export interface PluginActionContextParams {
ctx: PluginContext
Expand Down Expand Up @@ -70,16 +71,10 @@ const taskActionParamsSchema = moduleActionParamsSchema
/**
* Plugin actions
*/
export interface DescribeModuleTypeParams { }
export const describeModuleTypeParamsSchema = Joi.object()
.keys({})

export interface ConfigureModuleParams<T extends Module = Module> {
ctx: PluginContext
log?: LogEntry
moduleConfig: T["_ConfigType"]
export interface ConfigureProviderParams<T extends ProviderConfig = any> {
config: T
}
export const configureModuleParamsSchema = Joi.object()
export const configureProviderParamsSchema = Joi.object()
.keys({
ctx: pluginContextSchema
.required(),
Expand Down Expand Up @@ -131,6 +126,8 @@ export interface DeleteSecretParams extends PluginActionParamsBase {
export const deleteSecretParamsSchema = getSecretParamsSchema

export interface PluginActionParams {
configureProvider: ConfigureProviderParams

getEnvironmentStatus: GetEnvironmentStatusParams
prepareEnvironment: PrepareEnvironmentParams
cleanupEnvironment: CleanupEnvironmentParams
Expand All @@ -143,6 +140,24 @@ export interface PluginActionParams {
/**
* Module actions
*/
export interface DescribeModuleTypeParams { }
export const describeModuleTypeParamsSchema = Joi.object()
.keys({})

export interface ConfigureModuleParams<T extends Module = Module> {
ctx: PluginContext
logEntry?: LogEntry
moduleConfig: T["_ConfigType"]
}
export const configureModuleParamsSchema = Joi.object()
.keys({
ctx: pluginContextSchema
.required(),
logEntry: logEntrySchema,
moduleConfig: moduleConfigSchema
.required(),
})

export interface GetBuildStatusParams<T extends Module = Module> extends PluginModuleActionParamsBase<T> { }
export const getBuildStatusParamsSchema = moduleActionParamsSchema

Expand Down
Loading

0 comments on commit bdf6994

Please sign in to comment.