Skip to content

Commit

Permalink
feat(container): added hotReload.postSyncCommand
Browse files Browse the repository at this point in the history
Adds a new config option to hot reload specs: `postSyncCommand`.

This is a command that is run inside the container after a hot reload
sync takes place.
  • Loading branch information
thsig committed Sep 23, 2019
1 parent 40c716a commit eb94288
Show file tree
Hide file tree
Showing 17 changed files with 611 additions and 9 deletions.
20 changes: 20 additions & 0 deletions docs/reference/module-types/container.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,25 @@ hotReload:
- target: "/app/src"
```

### `hotReload.postSyncCommand[]`

[hotReload](#hotreload) > postSyncCommand

An optional command to run inside the container after syncing.

| Type | Required |
| --------------- | -------- |
| `array[string]` | No |

Example:

```yaml
hotReload:
...
postSyncCommand:
- rebuild-static-assets.sh
```

### `dockerfile`

POSIX-style name of Dockerfile, relative to module root.
Expand Down Expand Up @@ -1015,6 +1034,7 @@ hotReload:
sync:
- source: .
target:
postSyncCommand:
dockerfile:
services:
- name:
Expand Down
20 changes: 20 additions & 0 deletions docs/reference/module-types/maven-container.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,25 @@ hotReload:
- target: "/app/src"
```

### `hotReload.postSyncCommand[]`

[hotReload](#hotreload) > postSyncCommand

An optional command to run inside the container after syncing.

| Type | Required |
| --------------- | -------- |
| `array[string]` | No |

Example:

```yaml
hotReload:
...
postSyncCommand:
- rebuild-static-assets.sh
```

### `dockerfile`

POSIX-style name of Dockerfile, relative to module root.
Expand Down Expand Up @@ -1050,6 +1069,7 @@ hotReload:
sync:
- source: .
target:
postSyncCommand:
dockerfile:
services:
- name:
Expand Down
31 changes: 27 additions & 4 deletions docs/using-garden/hot-reload.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Hot Reload

When the `local-kubernetes` or `kubernetes` provider is used, `container` modules can be configured to hot-reload their running services when the module's sources change (i.e. without redeploying). In essence, hot-reloading copies source files into the appropriate running containers (local or remote) when code is changed by the user.
When the `local-kubernetes` or `kubernetes` provider is used, `container` modules can be configured to hot-reload their running services when the module's sources change (i.e. without redeploying). In essence, hot-reloading copies syncs files into the appropriate running containers (local or remote) when code is changed by the user, and optionally runs a post-sync command inside the container.

For example, services that can be run with a file system watcher that automatically update the running application process when sources change (e.g. nodemon, Django, Ruby on Rails, and many other web app frameworks) are a natural fit for this feature.
For example, services that can be run with a file system watcher that automatically updates the running application process when sources change (e.g. nodemon, Django, Ruby on Rails, and many other web app frameworks) are a natural fit for this feature.

## Usage

Expand All @@ -14,9 +14,9 @@ Subsequently deploying a service belonging to a module configured for hot reload

Since hot reloading is triggered via Garden's file system watcher, hot reloading only occurs while a watch-mode Garden command is running.

## Quick example
## Basic example

Following is a simple example of a module configured for hot reloading:
Following is an example of a module configured for hot reloading:

```yaml
kind: Module
Expand All @@ -41,6 +41,7 @@ If a `source` is specified along with `target`, that subpath in the module's dir
You can configure several such `source`/`target` pairs, but note that the `source` paths must be disjoint, i.e. a `source` path may not be a subdirectory of another `source` path within the same module. Here's an example:

```yaml
hotReload:
sync:
- source: /foo
target: /app/foo
Expand All @@ -49,3 +50,25 @@ You can configure several such `source`/`target` pairs, but note that the `sourc
```

Lastly, `hotReloadArgs` specifies the arguments to use to run the container (when deployed with hot reloading enabled). If no `hotReloadArgs` are specified, `args` is also used to run the container when the service is deployed with hot reloading enabled

## Adding a `postSyncCommand`

A `postSyncCommand` can also be added to a module's hot reload configuration. This command is executed inside the running container during each hot reload, after syncing is completed (as the name suggests).

Following is a snippet from the `hot-reload-post-sync-command` example project. Here, a `postSyncCommand` is used to `touch` a file, updating its modification time. This way, `nodemon` only has to watch one file to keep the running application up to date. See the `hot-reload-post-sync-command` example for more details and a fuller discussion.

```yaml
kind: Module
description: Node greeting service
name: node-service
type: container
hotReload:
sync:
- target: /app/
postSyncCommand: [touch, /app/hotreloadfile]
services:
- name: node-service
args: [npm, start]
hotReloadArgs: [npm, run, dev] # Runs modemon main.js --watch hotreloadfile
...
```
55 changes: 55 additions & 0 deletions examples/hot-reload-post-sync-command/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Hot-reload post-sync command example project

This example is a variation on the `hot-reload` example. Here, we demonstrate the `hotReload.postSyncCommand` option by providing a motivating example of its use.

Since this project builds on the `hot-reload` example, you may want to check out that project's README if you haven't already.

## Adding a `postSyncCommand`

Like the `hot-reload` example project, This project contains a single service called `node-service`. When running, the service waits for requests on `/hello` and responds with a message.

Here, however, we modify the `dev` npm script in `node-service/package.json` to have `nodemon` only watch a single file (`/app/hotreloadfile` in the container's directory structure) for changes:

```json
{
...
"scripts": {
"start": "node main.js",
"dev": "nodemon main.js --watch hotreloadfile",
"test": "echo OK"
},
...
}
```

We also add a `postSyncCommand` to `node-service`'s `garden.yml`:

```yaml
kind: Module
description: Node greeting service
name: node-service
type: container
hotReload:
sync:
- target: /app/
postSyncCommand: [touch, /app/hotreloadfile]
services:
- name: node-service
args: [npm, start]
hotReloadArgs: [npm, run, dev]
...
```

When one is specified, the `postSyncCommand` is executed inside inside the running container during each hot reload, after any changed files have been synced.

In this example, the idea is to "notify" the `nodemon` process that a reload is needed by `touch`-ing `hotreloadfile` during each hot reload. `nodemon` will then pick up the updated modification time, triggering a reload. `hotreloadfile` doesn't exist when the image is built (and doesn't need to for our purposes here).

> Note: There's nothing special about the name `hotreloadfile` here. Any file name would do.
Since `nodemon` only has to watch a single path, this approach should significantly reduce the resource footprint `nodemon`'s FS watching incurs when compared to e.g. watching all of the module's source paths.

When this general approach is applicable for several modules in the system (which depends on the particular languages, frameworks and libraries being used), the lighter total FS watching footprint may facilitate more services being deployed with hot reloading enabled during development.

## Usage

Identical to the `hot-reload` example project. The only difference is that here, the updated message returned by `garden call` is a result of `nodemon` performing a reload after picking up the updated modification time of `hotreloadfile` (instead of noticing/watching for changes to `node-service/app.js` directly).
13 changes: 13 additions & 0 deletions examples/hot-reload-post-sync-command/garden.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
kind: Project
name: hot-reload-post-sync-command
environments:
- name: local
providers:
- name: local-kubernetes
- name: testing
providers:
- name: kubernetes
context: gke_garden-dev-200012_europe-west1-b_garden-dev-1
namespace: hot-reload-callback-testing-${local.env.CIRCLE_BUILD_NUM || local.username}
defaultHostname: hot-reload-callback-testing.dev-1.sys.garden
buildMode: cluster-docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
Dockerfile
garden.yml
app.yaml
13 changes: 13 additions & 0 deletions examples/hot-reload-post-sync-command/node-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM node:9-alpine

ENV PORT=8080
EXPOSE ${PORT}

RUN npm install -g nodemon

ADD . /app
WORKDIR /app

RUN npm install

CMD ["npm", "start"]
14 changes: 14 additions & 0 deletions examples/hot-reload-post-sync-command/node-service/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const express = require("express")

const app = express()

app.get("/hello", (req, res) => {
res.json({message: "Hello from Node!"})
})

// This is the path GAE uses for health checks
app.get("/_ah/health", (req, res) => {
res.sendStatus(200)
})

module.exports = { app }
22 changes: 22 additions & 0 deletions examples/hot-reload-post-sync-command/node-service/garden.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
kind: Module
description: Node greeting service
name: node-service
type: container
hotReload:
sync:
- target: /app/
postSyncCommand: [touch, /app/hotreloadfile]
services:
- name: node-service
args: [npm, start]
hotReloadArgs: [npm, run, dev]
ports:
- name: http
containerPort: 8080
ingresses:
- path: /hello
port: http
healthCheck:
httpGet:
path: /_ah/health
port: http
3 changes: 3 additions & 0 deletions examples/hot-reload-post-sync-command/node-service/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const { app } = require("./app")

app.listen(process.env.PORT, "0.0.0.0", () => console.log("App started"))
Loading

0 comments on commit eb94288

Please sign in to comment.