Skip to content

Commit

Permalink
feat(cli): allow --hot=* in dev/deploy commands
Browse files Browse the repository at this point in the history
Passing an asterisk (*) to the --hot/--hot-reload option deploys all
compatible services with hot reloading enabled (i.e. all services
belonging to a module specifying the hotReload field).

Also throw an error before initializing the environment or proceeding
with deployment if one or more service names passed to the
--hot/--hotReload option aren't configured for hot reloading.
  • Loading branch information
thsig committed Apr 18, 2019
1 parent d692401 commit 15db6ed
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 22 deletions.
20 changes: 11 additions & 9 deletions docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,14 @@ Optionally stays running and automatically re-builds and re-deploys services if

Examples:

garden deploy # deploy all modules in the project
garden deploy my-service # only deploy my-service
garden deploy service-a,service-b # only deploy service-a and service-b
garden deploy --force # force re-deploy of modules, even if they're already deployed
garden deploy --watch # watch for changes to code
garden deploy --watch --hot-reload=my-service # deploys all services, with hot reloading enabled for my-service
garden deploy --env stage # deploy your services to an environment called stage
garden deploy # deploy all modules in the project
garden deploy my-service # only deploy my-service
garden deploy service-a,service-b # only deploy service-a and service-b
garden deploy --force # force re-deploy of modules, even if they're already deployed
garden deploy --watch # watch for changes to code
garden deploy --hot=my-service # deploys all services, with hot reloading enabled for my-service
garden deploy --hot=* # deploys all compatible services with hot reloading enabled
garden deploy --env stage # deploy your services to an environment called stage

##### Usage

Expand All @@ -171,7 +172,7 @@ Examples:
| `--force` | | boolean | Force redeploy of service(s).
| `--force-build` | | boolean | Force rebuild of module(s).
| `--watch` | `-w` | boolean | Watch for changes in module(s) and auto-deploy.
| `--hot-reload` | `-hot` | array:string | The name(s) of the service(s) to deploy with hot reloading enabled. Use comma as a separator to specify multiple services. When this option is used, the command is run in watch mode (i.e. implicitly assumes the --watch/-w flag).
| `--hot-reload` | `-hot` | array:string | The name(s) of the service(s) to deploy with hot reloading enabled. Use comma as a separator to specify multiple services. Use * to deploy all services with hot reloading enabled (ignores services belonging to modules that don't support or haven't configured hot reloading). When this option is used, the command is run in watch mode (i.e. implicitly assumes the --watch/-w flag).

### garden dev

Expand All @@ -186,6 +187,7 @@ Examples:
garden dev
garden dev --hot-reload=foo-service # enable hot reloading for foo-service
garden dev --hot=foo-service,bar-service # enable hot reloading for foo-service and bar-service
garden dev --hot=* # enable hot reloading for all compatible services

##### Usage

Expand All @@ -195,7 +197,7 @@ Examples:

| Argument | Alias | Type | Description |
| -------- | ----- | ---- | ----------- |
| `--hot-reload` | `-hot` | array:string | The name(s) of the service(s) to deploy with hot reloading enabled. Use comma as a separator to specify multiple services.
| `--hot-reload` | `-hot` | array:string | The name(s) of the service(s) to deploy with hot reloading enabled. Use comma as a separator to specify multiple services. Use * to deploy all services with hot reloading enabled (ignores services belonging to modules that don't support or haven't configured hot reloading).

### garden exec

Expand Down
2 changes: 2 additions & 0 deletions docs/using-garden/hot-reload.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ For example, services that can be run with a file system watcher that automatica

Currently, services are only deployed with hot reloading enabled when their names are passed to the `--hot` option via `garden deploy` or `garden dev` commands (e.g. `garden dev --hot=foo-service,bar-service`). If these services don't belong to a module defining a `hotReload` configuration (see below for an example), an error will be thrown if their names are passed to the `--hot` option.

You can also pass `*` (e.g. `--hot=*`/`--hot-reload=*`) to deploy all compatible services with hot reloading enabled (i.e. all services belonging to a module that defines a `hotReload` configuration).

Subsequently deploying a service belonging to a module configured for hot reloading via `garden deploy` (without the watch flag) results in the service being redeployed in standard configuration.

Since hot reloading is triggered via Garden's file system watcher, hot reloading only occurs while a watch-mode Garden command is running.
Expand Down
28 changes: 18 additions & 10 deletions garden-service/src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { processServices } from "../process"
import { logHeader } from "../logger/util"
import { HotReloadTask } from "../tasks/hot-reload"
import { BaseTask } from "../tasks/base"
import { getHotReloadServiceNames, validateHotReloadServiceNames } from "./helpers"

const deployArgs = {
services: new StringsParameter({
Expand All @@ -41,7 +42,9 @@ const deployOpts = {
}),
"hot-reload": new StringsParameter({
help: deline`The name(s) of the service(s) to deploy with hot reloading enabled.
Use comma as a separator to specify multiple services. When this option is used,
Use comma as a separator to specify multiple services. Use * to deploy all
services with hot reloading enabled (ignores services belonging to modules that
don't support or haven't configured hot reloading). When this option is used,
the command is run in watch mode (i.e. implicitly assumes the --watch/-w flag).
`,
alias: "hot",
Expand All @@ -64,13 +67,14 @@ export class DeployCommand extends Command<Args, Opts> {
Examples:
garden deploy # deploy all modules in the project
garden deploy my-service # only deploy my-service
garden deploy service-a,service-b # only deploy service-a and service-b
garden deploy --force # force re-deploy of modules, even if they're already deployed
garden deploy --watch # watch for changes to code
garden deploy --watch --hot-reload=my-service # deploys all services, with hot reloading enabled for my-service
garden deploy --env stage # deploy your services to an environment called stage
garden deploy # deploy all modules in the project
garden deploy my-service # only deploy my-service
garden deploy service-a,service-b # only deploy service-a and service-b
garden deploy --force # force re-deploy of modules, even if they're already deployed
garden deploy --watch # watch for changes to code
garden deploy --hot=my-service # deploys all services, with hot reloading enabled for my-service
garden deploy --hot=* # deploys all compatible services with hot reloading enabled
garden deploy --env stage # deploy your services to an environment called stage
`

arguments = deployArgs
Expand All @@ -89,11 +93,15 @@ export class DeployCommand extends Command<Args, Opts> {
return { result: {} }
}

const hotReloadServiceNames = opts["hot-reload"] || []

const hotReloadServiceNames = await getHotReloadServiceNames(opts["hot-reload"], initGraph)
let watch
if (hotReloadServiceNames.length > 0) {
await initGraph.getServices(hotReloadServiceNames) // validate the existence of these services
const errMsg = await validateHotReloadServiceNames(hotReloadServiceNames, initGraph)
if (errMsg) {
log.error({ msg: errMsg })
return { result: {} }
}
watch = true
} else {
watch = opts.watch
Expand Down
14 changes: 11 additions & 3 deletions garden-service/src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { Module } from "../types/module"
import { getTestTasks } from "../tasks/test"
import { HotReloadTask } from "../tasks/hot-reload"
import { ConfigGraph } from "../config-graph"
import { getHotReloadServiceNames, validateHotReloadServiceNames } from "./helpers"

const ansiBannerPath = join(STATIC_DIR, "garden-banner-2.txt")

Expand All @@ -38,7 +39,9 @@ const devArgs = {}
const devOpts = {
"hot-reload": new StringsParameter({
help: deline`The name(s) of the service(s) to deploy with hot reloading enabled.
Use comma as a separator to specify multiple services.
Use comma as a separator to specify multiple services. Use * to deploy all
services with hot reloading enabled (ignores services belonging to modules that
don't support or haven't configured hot reloading).
`,
alias: "hot",
}),
Expand All @@ -65,6 +68,7 @@ export class DevCommand extends Command<Args, Opts> {
garden dev
garden dev --hot-reload=foo-service # enable hot reloading for foo-service
garden dev --hot=foo-service,bar-service # enable hot reloading for foo-service and bar-service
garden dev --hot=* # enable hot reloading for all compatible services
`

options = devOpts
Expand All @@ -88,9 +92,13 @@ export class DevCommand extends Command<Args, Opts> {
return {}
}

const hotReloadServiceNames = opts["hot-reload"] || []
const hotReloadServiceNames = await getHotReloadServiceNames(opts["hot-reload"], graph)
if (hotReloadServiceNames.length > 0) {
await graph.getServices(hotReloadServiceNames) // validate the existence of these services
const errMsg = await validateHotReloadServiceNames(hotReloadServiceNames, graph)
if (errMsg) {
log.error({ msg: errMsg })
return { result: {} }
}
}

await garden.actions.prepareEnvironment({ log })
Expand Down
47 changes: 47 additions & 0 deletions garden-service/src/commands/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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 { ConfigGraph } from "../config-graph"
import { Service } from "../types/service"

export async function getHotReloadServiceNames(
namesFromOpt: string[] | undefined, configGraph: ConfigGraph,
) {
const names = namesFromOpt || []
if (names[0] === "*") {
return (await configGraph.getServices())
.filter(s => supportsHotReloading(s))
.map(s => s.name)
} else {
return names
}
}

/**
* Returns an error message string if one or more serviceNames refers to a service that's not configured for
* hot reloading, or if one or more of serviceNames referes to a non-existent service. Returns null otherwise.
*/
export async function validateHotReloadServiceNames(
serviceNames: string[], configGraph: ConfigGraph,
): Promise<string | null> {
const services = await configGraph.getServices(serviceNames)
const invalidNames = services
.filter(s => !supportsHotReloading(s))
.map(s => s.name)
if (invalidNames.length > 0) {
return `The following requested services are not configured for hot reloading: ${invalidNames.join(", ")}`
} else {
return null
}
}

// TODO: Add hotReload to baseModuleSpecSchema. It's bad form to dig into module type-specific fields
// outside the scope of its plugins.
function supportsHotReloading(service: Service) {
return !!service.module.spec.hotReload
}

0 comments on commit 15db6ed

Please sign in to comment.