Skip to content

Commit

Permalink
feat: created local-kubernetes plugin and added config options
Browse files Browse the repository at this point in the history
This is a step in the direction of properly supporting remote k8s
clusters. Still need to implement pushing built images to the cluster.
  • Loading branch information
edvald committed Apr 22, 2018
1 parent 6c20da5 commit 1fcf88d
Show file tree
Hide file tree
Showing 23 changed files with 330 additions and 179 deletions.
32 changes: 32 additions & 0 deletions docs/remote-kubernetes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## Running against a remote Kubernetes cluster

### Setup

You need to have a running ingress controller on your cluster to route requests to
the deployed services. This can generally be any controller of your choosing, such
as the nginx ingress controller.

You also need a configured [context](https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/)
on your development machine.

Then all you need to do is configure the environment and provider in your project
`garden.yml`. You need to specify your configured context and the hostname of your
ingress controller. Example:

```yaml
project:
environments:
dev:
providers:
kubernetes:
context: my-dev-context
ingressHostname: k8s-dev.mydomain.com
ingressClass: nginx # this is optional, but may be necessary for your ingress controller configuration
defaultEnvironment: dev
```
Note that you need to have permissions to create namespaces and to create deployments,
daemonsets, services and ingresses within the namespaces created. The plugin will
create two namespaces per user and project, one to run services and another to manage
metadata and configuration (this is so that your environment can be reset without
clearing your configuration variables).
2 changes: 1 addition & 1 deletion examples/hello-world/garden.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ project:
environments:
local:
providers:
kubernetes:
local-kubernetes:
context: docker-for-desktop
local-google-cloud-functions: {}
dev:
Expand Down
8 changes: 1 addition & 7 deletions examples/multi-container/garden.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
project:
name: multi-container
environments:
local:
providers:
docker:
type: kubernetes
context: docker-for-desktop
dev:
providers:
docker:
type: kubernetes
kubernetes:
context: my-dev-context
6 changes: 3 additions & 3 deletions src/garden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ export class Garden {

// Load configured plugins
// Validate configuration
for (const [providerName, providerConfig] of Object.entries(config.providers)) {
this.loadPlugin(providerName, providerConfig)
for (const [providerName, provider] of Object.entries(config.providers)) {
this.loadPlugin(providerName, provider)
}
}

Expand Down Expand Up @@ -366,7 +366,7 @@ export class Garden {

// allow plugins to override their own config (that gets passed to action handlers)
if (plugin.config) {
this.config.providers[pluginName] = config
this.config.providers[pluginName] = plugin.config
}

const actions = plugin.actions || {}
Expand Down
53 changes: 34 additions & 19 deletions src/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
ServiceLogEntry,
TestResult,
ModuleActions,
PluginActionParamsBase,
} from "./types/plugin"
import {
Service,
Expand Down Expand Up @@ -112,13 +113,27 @@ export function createPluginContext(garden: Garden): PluginContext {
return f.bind(garden)
}

const config = { ...garden.config }
const projectConfig = { ...garden.config }

// TODO: find a nicer way to do this (like a type-safe wrapper function)
function commonParams(handler): PluginActionParamsBase {
const providerName = handler["pluginName"]
const providerConfig = projectConfig.providers[handler["pluginName"]]

return {
ctx,
provider: {
name: providerName,
config: providerConfig,
},
}
}

const ctx: PluginContext = {
projectName: garden.projectName,
projectRoot: garden.projectRoot,
log: garden.log,
config,
config: projectConfig,
vcs: garden.vcs,

// TODO: maybe we should move some of these here
Expand All @@ -138,44 +153,44 @@ export function createPluginContext(garden: Garden): PluginContext {

parseModule: async <T extends Module>(moduleConfig: T["_ConfigType"]) => {
const handler = garden.getModuleActionHandler("parseModule", moduleConfig.type)
return handler({ ctx, config, moduleConfig })
return handler({ ...commonParams(handler), moduleConfig })
},

getModuleBuildStatus: async <T extends Module>(module: T, logEntry?: LogEntry) => {
const defaultHandler = garden.getModuleActionHandler("getModuleBuildStatus", "generic")
const handler = garden.getModuleActionHandler("getModuleBuildStatus", module.type, defaultHandler)
return handler({ ctx, config, module, logEntry })
return handler({ ...commonParams(handler), module, logEntry })
},

buildModule: async <T extends Module>(module: T, logEntry?: LogEntry) => {
await garden.buildDir.syncDependencyProducts(module)
const defaultHandler = garden.getModuleActionHandler("buildModule", "generic")
const handler = garden.getModuleActionHandler("buildModule", module.type, defaultHandler)
return handler({ ctx, config, module, logEntry })
return handler({ ...commonParams(handler), module, logEntry })
},

pushModule: async <T extends Module>(module: T, logEntry?: LogEntry) => {
const handler = garden.getModuleActionHandler("pushModule", module.type, dummyPushHandler)
return handler({ ctx, config, module, logEntry })
return handler({ ...commonParams(handler), module, logEntry })
},

testModule: async <T extends Module>(module: T, testSpec: TestSpec, logEntry?: LogEntry) => {
const defaultHandler = garden.getModuleActionHandler("testModule", "generic")
const handler = garden.getModuleActionHandler("testModule", module.type, defaultHandler)
const env = garden.getEnvironment()
return handler({ ctx, config, module, testSpec, env, logEntry })
return handler({ ...commonParams(handler), module, testSpec, env, logEntry })
},

getTestResult: async <T extends Module>(module: T, version: TreeVersion, logEntry?: LogEntry) => {
const handler = garden.getModuleActionHandler("getTestResult", module.type, async () => null)
const env = garden.getEnvironment()
return handler({ ctx, config, module, version, env, logEntry })
return handler({ ...commonParams(handler), module, version, env, logEntry })
},

getEnvironmentStatus: async () => {
const handlers = garden.getActionHandlers("getEnvironmentStatus")
const env = garden.getEnvironment()
return Bluebird.props(mapValues(handlers, h => h({ ctx, config, env })))
return Bluebird.props(mapValues(handlers, h => h({ ...commonParams(h), env })))
},

configureEnvironment: async () => {
Expand All @@ -189,7 +204,7 @@ export function createPluginContext(garden: Garden): PluginContext {
msg: "Configuring...",
})

await handler({ ctx, config, env, logEntry })
await handler({ ...commonParams(handler), env, logEntry })

logEntry.setSuccess("Configured")
})
Expand All @@ -199,13 +214,13 @@ export function createPluginContext(garden: Garden): PluginContext {
destroyEnvironment: async () => {
const handlers = garden.getActionHandlers("destroyEnvironment")
const env = garden.getEnvironment()
await Bluebird.each(values(handlers), h => h({ ctx, config, env }))
await Bluebird.each(values(handlers), h => h({ ...commonParams(h), env }))
return ctx.getEnvironmentStatus()
},

getServiceStatus: async <T extends Module>(service: Service<T>) => {
const handler = garden.getModuleActionHandler("getServiceStatus", service.module.type)
return handler({ ctx, config, service, env: garden.getEnvironment() })
return handler({ ...commonParams(handler), service, env: garden.getEnvironment() })
},

deployService: async <T extends Module>(
Expand All @@ -217,7 +232,7 @@ export function createPluginContext(garden: Garden): PluginContext {
serviceContext = { envVars: {}, dependencies: {} }
}

return handler({ ctx, config, service, serviceContext, env: garden.getEnvironment(), logEntry })
return handler({ ...commonParams(handler), service, serviceContext, env: garden.getEnvironment(), logEntry })
},

getServiceOutputs: async <T extends Module>(service: Service<T>) => {
Expand All @@ -228,24 +243,24 @@ export function createPluginContext(garden: Garden): PluginContext {
} catch (err) {
return {}
}
return handler({ ctx, config, service, env: garden.getEnvironment() })
return handler({ ...commonParams(handler), service, env: garden.getEnvironment() })
},

execInService: async <T extends Module>(service: Service<T>, command: string[]) => {
const handler = garden.getModuleActionHandler("execInService", service.module.type)
return handler({ ctx, config, service, command, env: garden.getEnvironment() })
return handler({ ...commonParams(handler), service, command, env: garden.getEnvironment() })
},

getServiceLogs: async <T extends Module>(service: Service<T>, stream: Stream<ServiceLogEntry>, tail?: boolean) => {
const handler = garden.getModuleActionHandler("getServiceLogs", service.module.type, dummyLogStreamer)
return handler({ ctx, config, service, stream, tail, env: garden.getEnvironment() })
return handler({ ...commonParams(handler), service, stream, tail, env: garden.getEnvironment() })
},

getConfig: async (key: string[]) => {
garden.validateConfigKey(key)
// TODO: allow specifying which provider to use for configs
const handler = garden.getActionHandler("getConfig")
const value = await handler({ ctx, config, key, env: garden.getEnvironment() })
const value = await handler({ ...commonParams(handler), key, env: garden.getEnvironment() })

if (value === null) {
throw new NotFoundError(`Could not find config key ${key}`, { key })
Expand All @@ -257,13 +272,13 @@ export function createPluginContext(garden: Garden): PluginContext {
setConfig: async (key: string[], value: string) => {
garden.validateConfigKey(key)
const handler = garden.getActionHandler("setConfig")
return handler({ ctx, config, key, value, env: garden.getEnvironment() })
return handler({ ...commonParams(handler), key, value, env: garden.getEnvironment() })
},

deleteConfig: async (key: string[]) => {
garden.validateConfigKey(key)
const handler = garden.getActionHandler("deleteConfig")
const res = await handler({ ctx, config, key, env: garden.getEnvironment() })
const res = await handler({ ...commonParams(handler), key, env: garden.getEnvironment() })

if (!res.found) {
throw new NotFoundError(`Could not find config key ${key}`, { key })
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/google/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,6 @@ export function gcloud(project?: string, account?: string) {
export function getProject<T extends GoogleCloudModule>(providerName: string, service: Service<T>, env: Environment) {
// TODO: this is very contrived - we should rethink this a bit and pass
// provider configuration when calling the plugin
const providerConfig = env.config.providers[providerName]
return service.config.project || providerConfig["default-project"] || null
const provider = env.config.providers[providerName]
return service.config.project || provider["default-project"] || null
}
4 changes: 2 additions & 2 deletions src/plugins/google/google-cloud-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const gardenPlugin = (): GardenPlugin => ({
},

async deployService(
{ ctx, config, service, env }: DeployServiceParams<GoogleCloudFunctionsModule>,
{ ctx, provider, service, env }: DeployServiceParams<GoogleCloudFunctionsModule>,
) {
// TODO: provide env vars somehow to function
const project = getProject(pluginName, service, env)
Expand All @@ -86,7 +86,7 @@ export const gardenPlugin = (): GardenPlugin => ({
"--trigger-http",
])

return getServiceStatus({ ctx, config, service, env })
return getServiceStatus({ ctx, provider, service, env })
},

async getServiceOutputs({ service, env }: GetServiceOutputsParams<GoogleCloudFunctionsModule>) {
Expand Down
1 change: 1 addition & 0 deletions src/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const builtinPlugins: RegisterPluginParam[] = [
"./google/google-cloud-functions",
"./local/local-google-cloud-functions",
"./kubernetes",
"./kubernetes/local",
"./npm-package",
"./google/google-app-engine",
].map(p => resolve(__dirname, p))
Expand Down
Loading

0 comments on commit 1fcf88d

Please sign in to comment.