@flowfuse/driver-docker 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,8 +11,8 @@ jobs:
11
11
  - uses: actions/checkout@v4
12
12
  - uses: actions/setup-node@v4
13
13
  with:
14
- node-version: 16
14
+ node-version: 18
15
15
  - run: npm ci --omit dev
16
- - uses: JS-DevTools/npm-publish@v2
16
+ - uses: JS-DevTools/npm-publish@v3
17
17
  with:
18
18
  token: ${{ secrets.NPM_PUBLISH_TOKEN }}
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ #### 2.7.0: Release
2
+
3
+ - Persistent storage - Docker (#103) @hardillb
4
+ - Fix LOG_PASSTHROUGH (#104) @hardillb
5
+ - Fix network selection if more than one network labeled 'flowforge' (#102) @hardillb
6
+ - Update release-publish.yml to use NodeJS v18 (#101) @hardillb
7
+ - Bump JS-DevTools/npm-publish from 2 to 3 (#96) @app/dependabot
8
+
1
9
  #### 2.6.0: Release
2
10
 
3
11
 
package/README.md CHANGED
@@ -15,12 +15,17 @@ driver:
15
15
  registry: containers.flowforge.com
16
16
  privateCA: /full/path/to/chain.pem
17
17
  logPassthrough: true
18
+ storage:
19
+ enabled: true
20
+ path: /opt/flowfuse/storage
18
21
  ```
19
22
 
20
23
  - `registry` is the Docker Registry to load Stack Containers from (default: Docker Hub)
21
24
  - `socket` is the path to the docker unix domain socket (default: /var/run/docker.sock)
22
25
  - `privateCA`: is the fully qualified path to a pem file containing trusted CA cert chain (default: not set)
23
26
  - `logPassthrough` Have Node-RED logs printed in JSON format to container stdout (default: false)
27
+ - `storage.enabled` enables mounting a directory into each Node-RED instance as persistence storage (default: false)
28
+ - `storage.path` is the fully qualified path to the root directory for the storage on the host (default: not set)
24
29
 
25
30
  ### Configuration via environment variables
26
31
 
package/docker.js CHANGED
@@ -1,8 +1,9 @@
1
1
  const got = require('got')
2
2
  const Docker = require('dockerode')
3
+ const path = require('path')
4
+ const { chownSync, mkdirSync, rmSync } = require('fs')
3
5
 
4
6
  const createContainer = async (project, domain) => {
5
- const networks = await this._docker.listNetworks({ filters: { label: ['com.docker.compose.network=flowforge'] } })
6
7
  const stack = project.ProjectStack.properties
7
8
  const contOptions = {
8
9
  Image: stack.container,
@@ -15,7 +16,7 @@ const createContainer = async (project, domain) => {
15
16
  AttachStdout: false,
16
17
  AttachStderr: false,
17
18
  HostConfig: {
18
- NetworkMode: networks[0].Name
19
+ NetworkMode: this._network
19
20
  }
20
21
  }
21
22
 
@@ -85,7 +86,7 @@ const createContainer = async (project, domain) => {
85
86
  contOptions.Env.push(`FORGE_NR_SECRET=${credentialSecret}`)
86
87
  }
87
88
 
88
- if (this._options?.logPassthrough) {
89
+ if (this._app.driver.options?.logPassthrough) {
89
90
  contOptions.Env.push('FORGE_LOG_PASSTHROUGH=true')
90
91
  }
91
92
 
@@ -100,6 +101,25 @@ const createContainer = async (project, domain) => {
100
101
  contOptions.Env.push('NODE_EXTRA_CA_CERTS=/usr/local/ssl-certs/chain.pem')
101
102
  }
102
103
 
104
+ if (this._app.config.driver.options?.storage?.enabled || this._app.config.driver.options?.storage?.path) {
105
+ try {
106
+ const localPath = path.join('/opt/persistent-storage', project.id)
107
+ console.log(`Creating dir in container ${localPath}`)
108
+ mkdirSync(localPath)
109
+ chownSync(localPath, 1000, 1000)
110
+ } catch (err) {
111
+ this._app.log.info(`[docker] problem creating persistent storage for ${project.id}`)
112
+ }
113
+ const projectPath = path.join(this._app.config.driver.options?.storage?.path, project.id)
114
+ if (Array.isArray(contOptions.HostConfig?.Binds)) {
115
+ contOptions.HostConfig.Binds.push(`${projectPath}:/data/storage`)
116
+ } else {
117
+ contOptions.HostConfig.Binds = [
118
+ `${projectPath}:/data/storage`
119
+ ]
120
+ }
121
+ }
122
+
103
123
  const containerList = await this._docker.listImages()
104
124
  let containerFound = false
105
125
  let stackName = stack.container
@@ -180,6 +200,35 @@ module.exports = {
180
200
  options.registry = app.config.driver.options?.registry || '' // use docker hub
181
201
  }
182
202
 
203
+ const networks = await this._docker.listNetworks({ filters: { label: ['com.docker.compose.network=flowforge'] } })
204
+ if (networks.length > 1) {
205
+ const filteredNetworks = []
206
+ for (let j = 0; j < networks.length; j++) {
207
+ const details = await this._docker.getNetwork(networks[j].Id).inspect()
208
+ const containers = Object.keys(details.Containers)
209
+ for (let i = 0; i < containers.length; i++) {
210
+ // console.log(containers[i])
211
+ if (containers[i].startsWith(process.env.HOSTNAME)) {
212
+ filteredNetworks.push(networks[j])
213
+ }
214
+ }
215
+ }
216
+ // console.log(JSON.stringify(filteredNetworks, null, 2))
217
+ if (filteredNetworks[0]) {
218
+ this._app.log.info(`[docker] using network ${filteredNetworks[0].Name}`)
219
+ this._network = filteredNetworks[0].Name
220
+ } else {
221
+ this._app.log.info('[docker] unable to find network')
222
+ process.exit(-9)
223
+ }
224
+ } else if (networks.length === 1) {
225
+ this._app.log.info(`[docker] using network ${networks[0].Name}`)
226
+ this._network = networks[0].Name
227
+ } else {
228
+ this._app.log.info('[docker] unable to find network')
229
+ process.exit(-9)
230
+ }
231
+
183
232
  // Get a list of all projects - with the absolute minimum of fields returned
184
233
  const projects = await app.db.models.Project.findAll({
185
234
  attributes: [
@@ -314,6 +363,16 @@ module.exports = {
314
363
  await container.remove()
315
364
  } catch (err) {}
316
365
  }
366
+ if (this._app.config.driver.options?.storage?.enabled) {
367
+ // need to be sure we have permission to delete the dir and it's contents?
368
+ try {
369
+ // This is better and assumes that directory is mounted on `/opt/storage`
370
+ const projectPersistentPath = path.join('/opt/persistent-storage', project.id)
371
+ rmSync(projectPersistentPath, { recursive: true, force: true })
372
+ } catch (err) {
373
+ this._app.log.error(`[docker] Project ${project.id} - error deleting persistent storage: ${err.stack}`)
374
+ }
375
+ }
317
376
  delete this._projects[project.id]
318
377
  },
319
378
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowfuse/driver-docker",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "Docker driver for FlowFuse",
5
5
  "main": "docker.js",
6
6
  "scripts": {