Skip to content

Commit

Permalink
refactor: major hardening of internal plugin APIs
Browse files Browse the repository at this point in the history
This is a hefty refactor that makes internal plugin APIs more
consistent and better isolated. Plugins can no longer directly
manipulate Module or Service objects except within their own scope
of execution. Plugin handler outputs are also validated now, and
type-safety has been improved across the board.

One of the more important implications is that plugins can no longer
add or modify methods on the Module or Service class globally, and
several modifications were needed to accommodate that change.
  • Loading branch information
edvald committed May 23, 2018
1 parent 51e2f33 commit 242d0aa
Show file tree
Hide file tree
Showing 88 changed files with 2,472 additions and 1,596 deletions.
2 changes: 1 addition & 1 deletion examples/hello-world/services/hello-container/garden.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module:
build:
dependencies:
- hello-npm-package
test:
tests:
- name: unit
command: [npm, test]
- name: integ
Expand Down
4 changes: 2 additions & 2 deletions examples/hello-world/services/hello-function/garden.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ module:
description: Hello world serverless function
name: hello-function
type: google-cloud-function
services:
functions:
- name: hello-function
entrypoint: helloFunction
test:
tests:
- name: unit
command: [npm, test]
build:
Expand Down
19 changes: 10 additions & 9 deletions src/build-dir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ import {
import * as Rsync from "rsync"
import { GARDEN_DIR_NAME } from "./constants"
import { ConfigurationError } from "./exceptions"
import { PluginContext } from "./plugin-context"
import { execRsyncCmd } from "./util"
import {
BuildCopySpec,
Module,
} from "./types/module"
import { zip } from "lodash"

// Lazily construct a directory of modules inside which all build steps are performed.

Expand All @@ -44,27 +44,28 @@ export class BuildDir {
ensureDirSync(this.buildDirPath)
}

async syncFromSrc<T extends Module>(module: T) {
async syncFromSrc(module: Module) {
await this.sync(
resolve(this.projectRoot, module.path, "*"),
await this.buildPath(module),
)
}

async syncDependencyProducts<T extends Module>(ctx: PluginContext, module: T) {
async syncDependencyProducts(module: Module) {
await this.syncFromSrc(module)
const buildPath = await this.buildPath(module)
const buildDependencies = await module.getBuildDependencies()
const dependencyConfigs = module.config.build.dependencies || []

await bluebirdMap(module.config.build.dependencies || [], async (depConfig) => {
if (!depConfig.copy) {
return []
await bluebirdMap(zip(buildDependencies, dependencyConfigs), async ([sourceModule, depConfig]) => {
if (!sourceModule || !depConfig || !depConfig.copy) {
return
}

const sourceModule = await ctx.getModule(depConfig.name)
const sourceBuildPath = await this.buildPath(sourceModule)

// Sync to the module's top-level dir by default.
return bluebirdMap(depConfig.copy, (copy: BuildCopySpec) => {
await bluebirdMap(depConfig.copy, (copy: BuildCopySpec) => {
if (isAbsolute(copy.source)) {
throw new ConfigurationError(`Source path in build dependency copy spec must be a relative path`, {
copySpec: copy,
Expand All @@ -88,7 +89,7 @@ export class BuildDir {
await emptyDir(this.buildDirPath)
}

async buildPath<T extends Module>(module: T): Promise<string> {
async buildPath(module: Module): Promise<string> {
const path = resolve(this.buildDirPath, module.name)
await ensureDir(path)
return path
Expand Down
2 changes: 1 addition & 1 deletion src/commands/call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class CallCommand extends Command<typeof callArgs> {

// TODO: better error when service doesn't exist
const service = await ctx.getService(serviceName)
const status = await ctx.getServiceStatus(service)
const status = await ctx.getServiceStatus({ serviceName })

if (status.state !== "ready") {
throw new RuntimeError(`Service ${service.name} is not running`, {
Expand Down
5 changes: 3 additions & 2 deletions src/commands/config/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ export class ConfigDeleteCommand extends Command<typeof configDeleteArgs> {
arguments = configDeleteArgs

async action(ctx: PluginContext, args: DeleteArgs) {
const res = await ctx.deleteConfig(args.key.split("."))
const key = args.key.split(".")
const res = await ctx.deleteConfig({ key })

if (res.found) {
ctx.log.info(`Deleted config key ${args.key}`)
} else {
throw new NotFoundError(`Could not find config key ${args.key}`, { key: args.key })
throw new NotFoundError(`Could not find config key ${args.key}`, { key })
}

return { ok: true }
Expand Down
12 changes: 9 additions & 3 deletions src/commands/config/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { NotFoundError } from "../../exceptions"
import { PluginContext } from "../../plugin-context"
import { Command, ParameterValues, StringParameter } from "../base"

Expand All @@ -27,10 +28,15 @@ export class ConfigGetCommand extends Command<typeof configGetArgs> {
arguments = configGetArgs

async action(ctx: PluginContext, args: GetArgs) {
const res = await ctx.getConfig(args.key.split("."))
const key = args.key.split(".")
const { value } = await ctx.getConfig({ key })

ctx.log.info(res)
if (value === null || value === undefined) {
throw new NotFoundError(`Could not find config key ${args.key}`, { key })
}

return { [args.key]: res }
ctx.log.info(value)

return { [args.key]: value }
}
}
3 changes: 2 additions & 1 deletion src/commands/config/set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ export class ConfigSetCommand extends Command<typeof configSetArgs> {
arguments = configSetArgs

async action(ctx: PluginContext, args: SetArgs) {
await ctx.setConfig(args.key.split("."), args.value)
const key = args.key.split(".")
await ctx.setConfig({ key, value: args.value })
ctx.log.info(`Set config key ${args.key}`)
return { ok: true }
}
Expand Down
2 changes: 1 addition & 1 deletion src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export class DeployCommand extends Command<typeof deployArgs, typeof deployOpts>
ctx.log.header({ emoji: "rocket", command: "Deploy" })

// TODO: make this a task
await ctx.configureEnvironment()
await ctx.configureEnvironment({})

const watch = opts.watch
const force = opts.force
Expand Down
2 changes: 1 addition & 1 deletion src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class DevCommand extends Command {

ctx.log.info(chalk.gray.italic(`\nGood ${getGreetingTime()}! Let's get your environment wired up...\n`))

await ctx.configureEnvironment()
await ctx.configureEnvironment({})

const modules = await ctx.getModules()

Expand Down
4 changes: 2 additions & 2 deletions src/commands/environment/configure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import { PluginContext } from "../../plugin-context"
import { EnvironmentStatusMap } from "../../types/plugin"
import { EnvironmentStatusMap } from "../../types/plugin/outputs"
import { Command } from "../base"

export class EnvironmentConfigureCommand extends Command {
Expand All @@ -19,7 +19,7 @@ export class EnvironmentConfigureCommand extends Command {
const { name } = ctx.getEnvironment()
ctx.log.header({ emoji: "gear", command: `Configuring ${name} environment` })

const result = await ctx.configureEnvironment()
const result = await ctx.configureEnvironment({})

ctx.log.info("")
ctx.log.header({ emoji: "heavy_check_mark", command: `Done!` })
Expand Down
4 changes: 2 additions & 2 deletions src/commands/environment/destroy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { PluginContext } from "../../plugin-context"

import { Command } from "../base"
import { EnvironmentStatusMap } from "../../types/plugin"
import { EnvironmentStatusMap } from "../../types/plugin/outputs"

export class EnvironmentDestroyCommand extends Command {
name = "destroy"
Expand All @@ -20,7 +20,7 @@ export class EnvironmentDestroyCommand extends Command {
const { name } = ctx.getEnvironment()
ctx.log.header({ emoji: "skull_and_crossbones", command: `Destroying ${name} environment` })

const result = await ctx.destroyEnvironment()
const result = await ctx.destroyEnvironment({})

ctx.log.finish()

Expand Down
4 changes: 2 additions & 2 deletions src/commands/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { Command } from "./base"
import { EntryStyle } from "../logger/types"
import { PluginContext } from "../plugin-context"
import { LoginStatusMap } from "../types/plugin"
import { LoginStatusMap } from "../types/plugin/outputs"

export class LoginCommand extends Command {
name = "login"
Expand All @@ -19,7 +19,7 @@ export class LoginCommand extends Command {
ctx.log.header({ emoji: "unlock", command: "Login" })
ctx.log.info({ msg: "Logging in...", entryStyle: EntryStyle.activity })

const result = await ctx.login()
const result = await ctx.login({})

ctx.log.info("\nLogin success!")

Expand Down
4 changes: 2 additions & 2 deletions src/commands/logout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { Command } from "./base"
import { EntryStyle } from "../logger/types"
import { PluginContext } from "../plugin-context"
import { LoginStatusMap } from "../types/plugin"
import { LoginStatusMap } from "../types/plugin/outputs"

export class LogoutCommand extends Command {
name = "logout"
Expand All @@ -21,7 +21,7 @@ export class LogoutCommand extends Command {

const entry = ctx.log.info({ msg: "Logging out...", entryStyle: EntryStyle.activity })

const result = await ctx.logout()
const result = await ctx.logout({})

entry.setSuccess("Logged out successfully")

Expand Down
5 changes: 3 additions & 2 deletions src/commands/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { PluginContext } from "../plugin-context"
import { BooleanParameter, Command, ParameterValues, StringParameter } from "./base"
import chalk from "chalk"
import { ServiceLogEntry } from "../types/plugin"
import { ServiceLogEntry } from "../types/plugin/outputs"
import Bluebird = require("bluebird")
import { Service } from "../types/service"
import Stream from "ts-stream"
Expand Down Expand Up @@ -39,6 +39,7 @@ export class LogsCommand extends Command<typeof logsArgs, typeof logsOpts> {

async action(ctx: PluginContext, args: Args, opts: Opts) {
const names = args.service ? args.service.split(",") : undefined
const tail = opts.tail
const services = await ctx.getServices(names)

const result: ServiceLogEntry[] = []
Expand All @@ -53,7 +54,7 @@ export class LogsCommand extends Command<typeof logsArgs, typeof logsOpts> {
// NOTE: This will work differently when we have Elasticsearch set up for logging, but is
// quite servicable for now.
await Bluebird.map(services, async (service: Service<any>) => {
await ctx.getServiceLogs(service, stream, opts.tail)
await ctx.getServiceLogs({ serviceName: service.name, stream, tail })
})

return result
Expand Down
14 changes: 7 additions & 7 deletions src/commands/run/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import chalk from "chalk"
import { PluginContext } from "../../plugin-context"
import { BuildTask } from "../../tasks/build"
import { RunResult } from "../../types/plugin"
import { RunResult } from "../../types/plugin/outputs"
import { BooleanParameter, Command, ParameterValues, StringParameter } from "../base"
import {
uniq,
Expand Down Expand Up @@ -50,19 +50,19 @@ export class RunModuleCommand extends Command<typeof runArgs, typeof runOpts> {
options = runOpts

async action(ctx: PluginContext, args: Args, opts: Opts): Promise<RunResult> {
const name = args.module
const module = await ctx.getModule(name)
const moduleName = args.module
const module = await ctx.getModule(moduleName)

const msg = args.command
? `Running command ${chalk.white(args.command)} in module ${chalk.white(name)}`
: `Running module ${chalk.white(name)}`
? `Running command ${chalk.white(args.command)} in module ${chalk.white(moduleName)}`
: `Running module ${chalk.white(moduleName)}`

ctx.log.header({
emoji: "runner",
command: msg,
})

await ctx.configureEnvironment()
await ctx.configureEnvironment({})

const buildTask = await BuildTask.factory({ ctx, module, force: opts["force-build"] })
await ctx.addTask(buildTask)
Expand All @@ -79,6 +79,6 @@ export class RunModuleCommand extends Command<typeof runArgs, typeof runOpts> {

printRuntimeContext(ctx, runtimeContext)

return ctx.runModule({ module, command, runtimeContext, silent: false, interactive: opts.interactive })
return ctx.runModule({ moduleName, command, runtimeContext, silent: false, interactive: opts.interactive })
}
}
12 changes: 6 additions & 6 deletions src/commands/run/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import chalk from "chalk"
import { PluginContext } from "../../plugin-context"
import { BuildTask } from "../../tasks/build"
import { RunResult } from "../../types/plugin"
import { RunResult } from "../../types/plugin/outputs"
import { BooleanParameter, Command, ParameterValues, StringParameter } from "../base"
import { printRuntimeContext } from "./index"

Expand Down Expand Up @@ -40,16 +40,16 @@ export class RunServiceCommand extends Command<typeof runArgs, typeof runOpts> {
options = runOpts

async action(ctx: PluginContext, args: Args, opts: Opts): Promise<RunResult> {
const name = args.service
const service = await ctx.getService(name)
const serviceName = args.service
const service = await ctx.getService(serviceName)
const module = service.module

ctx.log.header({
emoji: "runner",
command: `Running service ${chalk.cyan(name)} in module ${chalk.cyan(module.name)}`,
command: `Running service ${chalk.cyan(serviceName)} in module ${chalk.cyan(module.name)}`,
})

await ctx.configureEnvironment()
await ctx.configureEnvironment({})

const buildTask = await BuildTask.factory({ ctx, module, force: opts["force-build"] })
await ctx.addTask(buildTask)
Expand All @@ -60,6 +60,6 @@ export class RunServiceCommand extends Command<typeof runArgs, typeof runOpts> {

printRuntimeContext(ctx, runtimeContext)

return ctx.runService({ service, runtimeContext, silent: false, interactive: opts.interactive })
return ctx.runService({ serviceName, runtimeContext, silent: false, interactive: opts.interactive })
}
}
15 changes: 7 additions & 8 deletions src/commands/run/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import chalk from "chalk"
import { ParameterError } from "../../exceptions"
import { PluginContext } from "../../plugin-context"
import { BuildTask } from "../../tasks/build"
import { RunResult } from "../../types/plugin"
import { RunResult } from "../../types/plugin/outputs"
import {
findByName,
getNames,
Expand Down Expand Up @@ -52,15 +52,14 @@ export class RunTestCommand extends Command<typeof runArgs, typeof runOpts> {
const moduleName = args.module
const testName = args.test
const module = await ctx.getModule(moduleName)
const config = module.config

const testSpec = findByName(config.test, testName)
const testConfig = findByName(module.tests, testName)

if (!testSpec) {
if (!testConfig) {
throw new ParameterError(`Could not find test "${testName}" in module ${moduleName}`, {
moduleName,
testName,
availableTests: getNames(config.test),
availableTests: getNames(module.tests),
})
}

Expand All @@ -69,18 +68,18 @@ export class RunTestCommand extends Command<typeof runArgs, typeof runOpts> {
command: `Running test ${chalk.cyan(testName)} in module ${chalk.cyan(moduleName)}`,
})

await ctx.configureEnvironment()
await ctx.configureEnvironment({})

const buildTask = await BuildTask.factory({ ctx, module, force: opts["force-build"] })
await ctx.addTask(buildTask)
await ctx.processTasks()

const interactive = opts.interactive
const deps = await ctx.getServices(testSpec.dependencies)
const deps = await ctx.getServices(testConfig.dependencies)
const runtimeContext = await module.prepareRuntimeContext(deps)

printRuntimeContext(ctx, runtimeContext)

return ctx.testModule({ module, interactive, runtimeContext, silent: false, testSpec })
return ctx.testModule({ moduleName, interactive, runtimeContext, silent: false, testConfig })
}
}
2 changes: 1 addition & 1 deletion src/commands/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class ScanCommand extends Command {
const modules = await ctx.getModules()

const output = await Bluebird.map(modules, async (m) => {
const config = await m.config
const config = m.config
return {
name: m.name,
type: m.type,
Expand Down
Loading

0 comments on commit 242d0aa

Please sign in to comment.