├── .DS_Store
├── .github
├── FUNDING.yml
└── workflows
│ ├── publish-gh-pages.yml
│ └── publish-release.yml
├── .gitignore
├── .htmlhintrc
├── .npmignore
├── CHANGELOG.md
├── README.md
├── ROADMAP.md
├── examples
├── .DS_Store
├── DockerWindowsSettings.png
├── action-inspect-flows.json
├── bitcoin.png
├── flow.json
├── flow.png
├── flows-all.json
├── flows.json
├── list-flows.json
└── support_banner.png
├── gulpfile.js
├── package-lock.json
├── package.json
├── src
├── docker-config-actions.html
├── docker-config-actions.ts
├── docker-configuration.html
├── docker-configuration.ts
├── docker-container-actions.html
├── docker-container-actions.ts
├── docker-engine-actions.html
├── docker-engine-actions.ts
├── docker-events.html
├── docker-events.ts
├── docker-image-actions.html
├── docker-image-actions.ts
├── docker-network-actions.html
├── docker-network-actions.ts
├── docker-node-actions.html
├── docker-node-actions.ts
├── docker-plugin-actions.html
├── docker-plugin-actions.ts
├── docker-secret-actions.html
├── docker-secret-actions.ts
├── docker-service-actions.html
├── docker-service-actions.ts
├── docker-swarm-actions.html
├── docker-swarm-actions.ts
├── docker-task-actions.html
├── docker-task-actions.ts
├── docker-volume-actions.html
├── docker-volume-actions.ts
└── icons
│ └── docker.png
├── test
├── docker-container-actions_spec.js
├── docker-containers_spec.js
├── docker-events_spec.js
└── docker-images_spec.js
└── tsconfig.json
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naimo84/node-red-contrib-dockerode/d04142772dfd7e101a86e9dbd02710812169e840/.DS_Store
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: naimo84
2 | ko_fi: naimo84
3 | custom: ['https://paypal.me/NeumannBenjamin']
4 |
--------------------------------------------------------------------------------
/.github/workflows/publish-gh-pages.yml:
--------------------------------------------------------------------------------
1 | name: publish-gh-pages
2 | on:
3 | push:
4 | branches: [ docs ]
5 | jobs:
6 | build-and-deploy:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout
10 | uses: actions/checkout@v2.3.1
11 | - name: Install and Build
12 | run: |
13 | npm install
14 | npm run build
15 | - name: Deploy
16 | uses: JamesIves/github-pages-deploy-action@4.1.4
17 | with:
18 | branch: gh-pages
19 | folder: build
--------------------------------------------------------------------------------
/.github/workflows/publish-release.yml:
--------------------------------------------------------------------------------
1 | name: publish-release
2 | on:
3 | release:
4 | types: [ released ]
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout
10 | uses: actions/checkout@v2
11 | - name: Install Node.js
12 | uses: actions/setup-node@v1
13 | with:
14 | node-version: 16
15 | registry-url: 'https://registry.npmjs.org'
16 | - run: npm install
17 | - run: npm run build
18 | - run: npm publish --access public
19 | env:
20 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
21 | - name: Pack tarball
22 | id: pack_tarball
23 | run: |
24 | PACK_NAME=$(npm pack | tail -n 1)
25 | echo "::set-output name=tar_filename::$PACK_NAME"
26 | - name: Upload
27 | uses: actions/upload-artifact@v2
28 | with:
29 | name: package
30 | path: "*.tgz"
31 | - name: Get release
32 | id: get_release
33 | uses: bruceadams/get-release@v1.2.3
34 | env:
35 | GITHUB_TOKEN: ${{ github.token }}
36 | - name: Upload asset to github release page
37 | id: upload-release-asset
38 | uses: actions/upload-release-asset@v1
39 | env:
40 | GITHUB_TOKEN: ${{ github.token }}
41 | with:
42 | upload_url: ${{ steps.get_release.outputs.upload_url }}
43 | asset_path: ./${{ steps.pack_tarball.outputs.tar_filename }}
44 | asset_name: ${{ steps.pack_tarball.outputs.tar_filename }}
45 | asset_content_type: application/gzip
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .DS_Store
3 | dist
4 | build
--------------------------------------------------------------------------------
/.htmlhintrc:
--------------------------------------------------------------------------------
1 | {
2 | "doctype-first": false
3 | }
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .github
2 | .vscode
3 | test/*
4 | *.js.map
5 | src/*
6 | gulpfile.js
7 | examples/*.png
8 | *.tgz
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # node-red-contrib-dockerode
2 |
3 | This Node RED module connects Docker with Node-RED.
4 |
5 | > Node-RED is a tool for wiring together hardware devices, APIs and online services in new and interesting ways.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ## :sparkling_heart: Support my projects
15 |
16 | I open-source almost everything I can, and I try to reply to everyone needing help using these projects. Obviously,
17 | this takes time. You can integrate and use these projects in your applications _for free_! You can even change the source code and redistribute (even resell it).
18 |
19 | Thank you to all my backers!
20 | ### People
21 |
22 | - [fflorent](https://github.com/fflorent)
23 | - [Speeedy0815](https://github.com/Speeedy0815)
24 | - Ralf S.
25 | - Enno L.
26 | - Jürgen G.
27 | - Mark MC G.
28 | - Kay-Uwe M.
29 | - Craig O.
30 | - Manuel G.
31 |
32 | ### Become a backer
33 |
34 |
35 | However, if you get some profit from this or just want to encourage me to continue creating stuff, there are few ways you can do it:
36 |
37 | - Starring and sharing the projects you like :rocket:
38 | - **Crypto.com** — Use my referral link https://crypto.com/app/f2smbah8fm to sign up for Crypto.com and we both get $25 USD :)
39 |
40 | - [][paypal-donations] — You can make one-time donations via PayPal. I'll probably buy a ~~coffee~~ tea. :tea:
41 | - [](https://ko-fi.com/T6T412CXA) — I'll buy a ~~tea~~ coffee. :coffee: :wink:
42 |
43 | Thanks! :heart:
44 |
45 | ## :cloud: Installation
46 |
47 | First of all install [Node-RED](http://nodered.org/docs/getting-started/installation)
48 |
49 | ## :yum: How to contribute
50 |
51 | * git clone https://github.com/naimo84/node-red-contrib-dockerode.git
52 | * cd node-red-contrib-dockerode
53 | * npm install
54 | * gulp
55 | * cd ~/.node-red
56 | * npm install /path/to/node-red-contrib-dockerode
57 |
58 |
59 |
60 | ## Usage
61 |
62 | ### Configuration:
63 |
64 | #### docker.sock
65 |
66 | - ***Using Node-RED in a Docker-Container***
67 |
68 | The Node-RED container must have access to the docker.sock, so you have to add the docker-group ID to the container with docker run ... --group-add 250
the ID 250 may be different on your system.
69 |
70 | #### Exposing TCP-Daemon port
71 |
72 | - ***hostname*** hostname of docker (e.g. "localhost")
73 | - ***port*** port of docker (e.g. "2375")
74 |
75 | In order to expose the docker-engine TCP daemon, you have to do the following:
76 |
77 | - ***Docker for Windows / Docker Desktop:***
78 |
Under Settings / General check "Expose daemon on tcp://localhost:2375 without TLS"
79 |
80 | 
81 |
82 | - ***Docker-CE***
83 |
84 | See https://success.docker.com/article/how-do-i-enable-the-remote-api-for-dockerd
85 |
86 | or:
87 |
88 | ```
89 | # File: /etc/default/docker
90 | # Use DOCKER_OPTS to modify the daemon startup options.
91 | #DOCKER_OPTS=""
92 | DOCKER_OPTS="-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock"
93 | ```
94 |
95 | or:
96 |
97 | ```
98 | # File: /lib/systemd/system/docker.service
99 | ExecStart=/usr/bin/docker daemon -H fd:// -H tcp://0.0.0.0:2375
100 | ```
101 |
102 | [badge_brave]: ./examples/support_banner.png
103 | [badge_paypal]: https://img.shields.io/badge/Donate-PayPal-blue.svg
104 | [paypal-donations]: https://paypal.me/NeumannBenjamin
105 |
106 |
--------------------------------------------------------------------------------
/ROADMAP.md:
--------------------------------------------------------------------------------
1 | ### System
2 | * POST /auth [API v1.40 - SystemAuth](https://docs.docker.com/engine/api/v1.40/#operation/SystemAuth)
3 | * POST /info [API v1.40 - SystemInfo](https://docs.docker.com/engine/api/v1.40/#operation/SystemInfo)
4 | * POST /version [API v1.40 - SystemVersion](https://docs.docker.com/engine/api/v1.40/#operation/SystemVersion)
5 | * POST /_ping [API v1.40 - SystemPing](https://docs.docker.com/engine/api/v1.40/#operation/SystemPing)
6 | * HEAD /_ping [API v1.40 - SystemPingHead](https://docs.docker.com/engine/api/v1.40/#operation/SystemPingHead)
7 | * GET /events [API v1.40 - SystemEvents](https://docs.docker.com/engine/api/v1.40/#operation/SystemEvents)
8 | * GET /system/df [API v1.40 - SystemDataUsage](https://docs.docker.com/engine/api/v1.40/#operation/SystemDataUsage)
9 | * ## TODO:
10 | * * Test
11 |
12 | ### Distribution
13 | GET /distribution/{name}/json [API v1.40 - DistributionInspect](https://docs.docker.com/engine/api/v1.40/#operation/DistributionInspect)
14 | * ## TODO:
15 | * * Test
16 |
17 | ### Session
18 | * POST /session [API v1.40 - Session](https://docs.docker.com/engine/api/v1.40/#operation/Session)
19 | * ## TODO:
20 | * * Test
21 |
22 | ### Containers
23 | * GET /containers/json [API v1.40 - ContainerList](https://docs.docker.com/engine/api/v1.40/#operation/ContainerList)
24 |
25 | * POST /containers/create [API v1.40 - ContainerCreate](https://docs.docker.com/engine/api/v1.40/#operation/ContainerCreate)
26 | * * Locate or implement
27 | * GET /containers/(id or name)/json [API v1.40 - ContainerInspect](https://docs.docker.com/engine/api/v1.40/#operation/ContainerInspect)
28 | * * ~~Errors~~
29 | * * ~~Tested~~
30 | * GET /containers/(id or name)/top [API v1.40 - ContainerTop](https://docs.docker.com/engine/api/v1.40/#operation/ContainerTop)
31 | * * ~~Errors~~
32 | * * ~~Tested~~
33 | * GET /containers/(id or name)/logs [API v1.40 - ContainerLogs](https://docs.docker.com/engine/api/v1.40/#operation/ContainerLogs)
34 | * * ~~Errors~~
35 | * * Failed
36 | * GET /containers/(id or name)/changes [API v1.40 - ContainerChanges](https://docs.docker.com/engine/api/v1.40/#operation/ContainerChanges)
37 | * * ~~Errors~~
38 | * * ~~Tested~~
39 | * GET /containers/(id or name)/export [API v1.40 - ContainerExport](https://docs.docker.com/engine/api/v1.40/#operation/ContainerExport)
40 | * * ~~Errors~~
41 | * * Failed
42 | * GET /containers/(id or name)/stats [API v1.40 - ContainerStats](https://docs.docker.com/engine/api/v1.40/#operation/ContainerStats)
43 | * * ~~Errors~~
44 | * * Failed
45 | * POST /containers/{id}/resize [API v1.40 - ](https://docs.docker.com/engine/api/v1.40/#operation/ContainerResize)
46 | * * ~~Errors~~
47 | * * Failed
48 | * POST /containers/(id or name)/start [API v1.40 - ContainerResize](https://docs.docker.com/engine/api/v1.40/#operation/ContainerStart)
49 | * * ~~Errors~~
50 | * * Failed Odd messge
51 | * POST /containers/(id or name)/stop [API v1.40 - ContainerStop](https://docs.docker.com/engine/api/v1.40/#operation/ContainerStop)
52 | * * ~~Errors~~
53 | * * Failed Odd messge
54 | * POST /containers/(id or name)/restart [API v1.40 - ](https://docs.docker.com/engine/api/v1.40/#operation/ContainerRestart)
55 | * * ~~Errors~~
56 | * * Failed Odd messge
57 | * POST /containers/(id or name)/kill [API v1.40 - ](https://docs.docker.com/engine/api/v1.40/#operation/ContainerKill)
58 | * * ~~Errors~~
59 | * * Failed Odd messge
60 | * POST /containers/(id or name)/update [API v1.40 - ContainerKill](https://docs.docker.com/engine/api/v1.40/#operation/ContainerUpdate)
61 | * * ~~Errors~~
62 | * * Failed Odd messge
63 | * POST /containers/(id or name)/rename [API v1.40 - ContainerRename](https://docs.docker.com/engine/api/v1.40/#operation/ContainerRename)
64 | * * ~~Errors~~
65 | * * Failed Odd messge
66 | * POST /containers/(id or name)/pause [API v1.40 - ContainerPause](https://docs.docker.com/engine/api/v1.40/#operation/ContainerPause)
67 | * * ~~Errors~~
68 | * * Failed Odd messge
69 | * POST /containers/(id or name)/unpause [API v1.40 - ContainerUnpause](https://docs.docker.com/engine/api/v1.40/#operation/ContainerUnpause)
70 | * * ~~Errors~~
71 | * * Failed Odd messge
72 | * POST /containers/(id or name)/attach [API v1.40 - ](https://docs.docker.com/engine/api/v1.40/#operation/ContainerAttach)
73 | * * ~~Errors~~
74 | * * Failed Odd messge
75 | * GET /containers/{id}/attach/ws [API v1.40 - ContainerAttach](https://docs.docker.com/engine/api/v1.40/#operation/ContainerAttachWebsocket)
76 | * * ~~Errors~~
77 | * * Failed Odd messge
78 | * * Not found in dockerode
79 | * POST /containers/(id or name)/wait [API v1.40 - ContainerWait](https://docs.docker.com/engine/api/v1.40/#operation/ContainerWait)
80 | * * ~~Errors~~
81 | * * Failed Odd messge
82 | * DELETE /containers/(id or name) [API v1.40 - ContainerDelete](https://docs.docker.com/engine/api/v1.40/#operation/ContainerDelete)
83 | * * ~~Errors~~
84 | * * Failed Odd messge
85 | * HEAD /containers/{id}/archive [API v1.40 - ContainerArchiveInfo](https://docs.docker.com/engine/api/v1.40/#operation/ContainerArchiveInfo)
86 | * * ~~Errors~~
87 | * * Failed Odd messge
88 | * PUT /containers/(id or name)/archive [API v1.40 - ContainerArchive](https://docs.docker.com/engine/api/v1.40/#operation/ContainerArchive)
89 | * * ~~Errors~~
90 | * * Fix and test
91 | * POST /containers/prune [API v1.40 - ContainerPrune](https://docs.docker.com/engine/api/v1.40/#operation/ContainerPrune)
92 | * * Not found in dockerode
93 | * ## TODO:
94 | * * Test
95 |
96 | ### Exec
97 | * POST /containers/{id}/exec [API v1.40 - ContainerExec](https://docs.docker.com/engine/api/v1.40/#operation/ContainerExec)
98 | * POST /exec/{id}/start [API v1.40 - ExecStart](https://docs.docker.com/engine/api/v1.40/#operation/ExecStart)
99 | * POST /exec/{id}/resize [API v1.40 - ExecResize](https://docs.docker.com/engine/api/v1.40/#operation/ExecResize)
100 | * GET /exec/{id}/json [API v1.40 - ExecInspect](https://docs.docker.com/engine/api/v1.40/#operation/ExecInspect)
101 | * ## TODO:
102 | * * Test
103 |
104 | ### Volumes
105 | * GET /volumes [API v1.40 - VolumeList](https://docs.docker.com/engine/api/v1.40/#operation/VolumeList)
106 | * POST /volumes/create [API v1.40 - VolumeCreate](https://docs.docker.com/engine/api/v1.40/#operation/VolumeCreate)
107 | * GET /volumes/(name) [API v1.40 - VolumeInspect](https://docs.docker.com/engine/api/v1.40/#operation/VolumeInspect)
108 | * DELETE /volumes/(name) [API v1.40 - VolumeDelete](https://docs.docker.com/engine/api/v1.40/#operation/VolumeDelete)
109 | * POST /volumes/prune [API v1.40 - VolumePrune](https://docs.docker.com/engine/api/v1.40/#operation/VolumePrune)
110 | * ## TODO:
111 | * * Test
112 |
113 | ### Images
114 | * GET /images/json [API v1.40 - ImageList](https://docs.docker.com/engine/api/v1.40/#operation/ImageList)
115 | * POST /build [API v1.40 - ImageBuild](https://docs.docker.com/engine/api/v1.40/#operation/ImageBuild)
116 | * POST /build/prune [API v1.40 - BuildPrune](https://docs.docker.com/engine/api/v1.40/#operation/BuildPrune)
117 | * POST /images/create [API v1.40 - ImageCreate](https://docs.docker.com/engine/api/v1.40/#operation/ImageCreate)
118 | * GET /images/(name)/json [API v1.40 - ImageInspect](https://docs.docker.com/engine/api/v1.40/#operation/ImageInspect)
119 | * GET /images/(name)/history [API v1.40 - ImageHistory](https://docs.docker.com/engine/api/v1.40/#operation/ImageHistory)
120 | * POST /images/(name)/push [API v1.40 - ImagePush](https://docs.docker.com/engine/api/v1.40/#operation/ImagePush)
121 | * POST /images/(name)/tag [API v1.40 - ImageTag](https://docs.docker.com/engine/api/v1.40/#operation/ImageTag)
122 | * DELETE /images/(name) [API v1.40 - ImageDelete](https://docs.docker.com/engine/api/v1.40/#operation/ImageDelete)
123 | * GET /images/search [API v1.40 - ImageSearch](https://docs.docker.com/engine/api/v1.40/#operation/ImageSearch)
124 | * POST /images/prune [API v1.40 - ImagePrune](https://docs.docker.com/engine/api/v1.40/#operation/ImagePrune)
125 | * POST /commit [API v1.40 - ImageCommit](https://docs.docker.com/engine/api/v1.40/#operation/ImageCommit)
126 | * GET /images/(name)/get [API v1.40 - ](https://docs.docker.com/engine/api/v1.40/#operation/ImageGet)
127 | * GET /images/get [API v1.40 - ImageGet](https://docs.docker.com/engine/api/v1.40/#operation/ImageGetAll)
128 | * POST /images/load [API v1.40 - ImageLoad](https://docs.docker.com/engine/api/v1.40/#operation/ImageLoad)
129 | * ## TODO:
130 | * * Test
131 |
132 | ### Networks
133 | * GET /networks [API v1.40 - NetworkList](https://docs.docker.com/engine/api/v1.40/#operation/NetworkList)
134 | * GET /networks/(id or name) [API v1.40 - NetworkInspect](https://docs.docker.com/engine/api/v1.40/#operation/NetworkInspect)
135 | * DELETE /networks/(id or name) [API v1.40 - NetworkDelete](https://docs.docker.com/engine/api/v1.40/#operation/NetworkDelete)
136 | * POST /networks/create [API v1.40 - NetworkCreate](https://docs.docker.com/engine/api/v1.40/#operation/NetworkCreate)
137 | * POST /networks/(id or name)/connect [API v1.40 - NetworkConnect](https://docs.docker.com/engine/api/v1.40/#operation/NetworkConnect)
138 | * POST /networks/(id or name)/disconnect [API v1.40 - NetworkDisconnect](https://docs.docker.com/engine/api/v1.40/#operation/NetworkDisconnect)
139 | * POST /networks/prune [API v1.40 - NetworkPrune](https://docs.docker.com/engine/api/v1.40/#operation/NetworkPrune)
140 | * ## TODO:
141 | * * Test
142 |
143 | ### Plugins
144 | * GET /plugins [API v1.40 - PluginList](https://docs.docker.com/engine/api/v1.40/#operation/PluginList)
145 | * GET /plugins/privileges [API v1.40 - GetPluginPrivileges](https://docs.docker.com/engine/api/v1.40/#operation/GetPluginPrivileges)
146 | * POST /plugins/pull [API v1.40 - PluginPull](https://docs.docker.com/engine/api/v1.40/#operation/PluginPull)
147 | * GET /plugins/{name}/json [API v1.40 - PluginInspect](https://docs.docker.com/engine/api/v1.40/#operation/PluginInspect)
148 | * DELETE /plugins/{name} [API v1.40 - PluginDelete](https://docs.docker.com/engine/api/v1.40/#operation/PluginDelete)
149 | * POST /plugins/{name}/enable [API v1.40 - PluginEnable](https://docs.docker.com/engine/api/v1.40/#operation/PluginEnable)
150 | * POST /plugins/{name}/disable [API v1.40 - PluginDisable](https://docs.docker.com/engine/api/v1.40/#operation/PluginDisable)
151 | * POST /plugins/{name}/upgrade [API v1.40 - PluginUpgrade](https://docs.docker.com/engine/api/v1.40/#operation/PluginUpgrade)
152 | * POST /plugins/create [API v1.40 - PluginCreate](https://docs.docker.com/engine/api/v1.40/#operation/PluginCreate)
153 | * POST /plugins/{name}/push [API v1.40 - PluginPush](https://docs.docker.com/engine/api/v1.40/#operation/PluginPush)
154 | * POST /plugins/{name}/set [API v1.40 - PluginSet](https://docs.docker.com/engine/api/v1.40/#operation/PluginSet)
155 | * ## TODO:
156 | * * Test
157 |
158 | ### Configs
159 | * GET /configs [API v1.40 - ConfigList](https://docs.docker.com/engine/api/v1.40/#operation/ConfigList)
160 | * POST /configs/create [API v1.40 - ConfigCreate](https://docs.docker.com/engine/api/v1.40/#operation/ConfigCreate)
161 | * * ~~Errors~~
162 | * * ~~Tested~~
163 | * GET /configs/{id} [API v1.40 - ConfigInspect](https://docs.docker.com/engine/api/v1.40/#operation/ConfigInspect)
164 | * * ~~Errors~~
165 | * DELETE /configs/{id} [API v1.40 - ConfigDelete](https://docs.docker.com/engine/api/v1.40/#operation/ConfigDelete)
166 | * * ~~Errors~~
167 | * POST /configs/{id}/update [API v1.40 - ConfigUpdate](https://docs.docker.com/engine/api/v1.40/#operation/ConfigUpdate)
168 | * * ~~Errors~~
169 | * ## TODO:
170 | * * Test
171 |
172 | ### Secrets
173 | * GET /secrets [API v1.40 - SecretList](https://docs.docker.com/engine/api/v1.40/#operation/SecretList)
174 | * POST /secrets/create [API v1.40 - SecretCreate](https://docs.docker.com/engine/api/v1.40/#operation/SecretCreate)
175 | * GET /secrets/{id} [API v1.40 - SecretInspect](https://docs.docker.com/engine/api/v1.40/#operation/SecretInspect)
176 | * DELETE /secrets/{id} [API v1.40 - SecretDelete](https://docs.docker.com/engine/api/v1.40/#operation/SecretDelete)
177 | * POST /secrets/{id}/update [API v1.40 - SecretUpdate](https://docs.docker.com/engine/api/v1.40/#operation/SecretUpdate)
178 | * ## TODO:
179 | * * Test
180 |
181 | ### Nodes
182 | * GET /nodes [API v1.40 - NodeList](https://docs.docker.com/engine/api/v1.40/#operation/NodeList)
183 | * GET /nodes/{id} [API v1.40 - NodeInspect](https://docs.docker.com/engine/api/v1.40/#operation/NodeInspect)
184 | * DELETE /nodes/{id} [API v1.40 - NodeDelete](https://docs.docker.com/engine/api/v1.40/#operation/NodeDelete)
185 | * POST /nodes/{id}/update [API v1.40 - NodeUpdate](https://docs.docker.com/engine/api/v1.40/#operation/NodeUpdate)
186 | * ## TODO:
187 | * * Test
188 |
189 | ### Services
190 | * GET /services [API v1.40 - ServiceCreate](https://docs.docker.com/engine/api/v1.40/#operation/c)
191 | * POST /services/create [API v1.40 - ServiceCreate](https://docs.docker.com/engine/api/v1.40/#operation/ServiceCreate)
192 | * GET /services/{id} [API v1.40 - ServiceInspect](https://docs.docker.com/engine/api/v1.40/#operation/ServiceInspect)
193 | * DELETE /services/{id} [API v1.40 - ServiceDelete](https://docs.docker.com/engine/api/v1.40/#operation/ServiceDelete)
194 | * POST /services/{id}/update [API v1.40 - ServiceUpdate](https://docs.docker.com/engine/api/v1.40/#operation/ServiceUpdate)
195 | * ## TODO:
196 | * * Test
197 |
198 | ### Swarm
199 | * GET /swarm [API v1.40 - Swarm](https://docs.docker.com/engine/api/v1.40/#tag/Swarm)
200 | * POST /swarm/init [API v1.40 - SwarmInit](https://docs.docker.com/engine/api/v1.40/#operation/SwarmInit)
201 | * POST /swarm/join [API v1.40 - SwarmJoin](https://docs.docker.com/engine/api/v1.40/#operation/SwarmJoin)
202 | * POST /swarm/leave [API v1.40 - SwarmLeave](https://docs.docker.com/engine/api/v1.40/#operation/SwarmLeave)
203 | * POST /swarm/update [API v1.40 - SwarmUpdate](https://docs.docker.com/engine/api/v1.40/#operation/SwarmUpdate)
204 | * ## TODO:
205 | * * Test
206 |
207 | ### Tasks
208 | * GET /tasks [API v1.40 - TaskList](https://docs.docker.com/engine/api/v1.40/#operation/TaskList)
209 | * GET /tasks/(id) [API v1.40 - TaskInspect](https://docs.docker.com/engine/api/v1.40/#operation/TaskInspect)
210 | * GET /tasks/{id}/logs [API v1.40 - TaskLogs](https://docs.docker.com/engine/api/v1.40/#operation/TaskLogs)
211 | * ## TODO:
212 | * * Test
213 |
--------------------------------------------------------------------------------
/examples/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naimo84/node-red-contrib-dockerode/d04142772dfd7e101a86e9dbd02710812169e840/examples/.DS_Store
--------------------------------------------------------------------------------
/examples/DockerWindowsSettings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naimo84/node-red-contrib-dockerode/d04142772dfd7e101a86e9dbd02710812169e840/examples/DockerWindowsSettings.png
--------------------------------------------------------------------------------
/examples/action-inspect-flows.json:
--------------------------------------------------------------------------------
1 | [{"id":"493fedf0.d14764","type":"docker-plugin-actions","z":"2c4e61f8.7fa14e","name":"","config":"789ca14c.874fe","plugin":"docker.io/vieux/sshfs:latest","action":"","x":530,"y":600,"wires":[["3d0404c.ff9f3fc"]]},{"id":"789ca14c.874fe","type":"docker-configuration","z":"","host":"/var/run/docker.sock","port":""}]
--------------------------------------------------------------------------------
/examples/bitcoin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naimo84/node-red-contrib-dockerode/d04142772dfd7e101a86e9dbd02710812169e840/examples/bitcoin.png
--------------------------------------------------------------------------------
/examples/flow.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "4c3b05bf.206fbc",
4 | "type": "tab",
5 | "label": "Docker",
6 | "disabled": false,
7 | "info": ""
8 | },
9 | {
10 | "id": "cdee0698.8e7b98",
11 | "type": "docker-events",
12 | "z": "4c3b05bf.206fbc",
13 | "name": "",
14 | "config": "579edabe.09fe34",
15 | "x": 370,
16 | "y": 120,
17 | "wires": [
18 | [
19 | "dcf691b9.d092f"
20 | ]
21 | ]
22 | },
23 | {
24 | "id": "dcf691b9.d092f",
25 | "type": "debug",
26 | "z": "4c3b05bf.206fbc",
27 | "name": "",
28 | "active": true,
29 | "tosidebar": true,
30 | "console": false,
31 | "tostatus": false,
32 | "complete": "false",
33 | "x": 630,
34 | "y": 180,
35 | "wires": []
36 | },
37 | {
38 | "id": "2749d98c.8b80b6",
39 | "type": "inject",
40 | "z": "4c3b05bf.206fbc",
41 | "name": "Trigger",
42 | "topic": "",
43 | "payload": "",
44 | "payloadType": "str",
45 | "repeat": "",
46 | "crontab": "",
47 | "once": false,
48 | "onceDelay": 0.1,
49 | "x": 190,
50 | "y": 180,
51 | "wires": [
52 | [
53 | "fc156449.abcde8"
54 | ]
55 | ]
56 | },
57 | {
58 | "id": "2b4da824.b1d358",
59 | "type": "docker-images",
60 | "z": "4c3b05bf.206fbc",
61 | "name": "",
62 | "config": "579edabe.09fe34",
63 | "x": 380,
64 | "y": 240,
65 | "wires": [
66 | [
67 | "dcf691b9.d092f"
68 | ]
69 | ]
70 | },
71 | {
72 | "id": "fc156449.abcde8",
73 | "type": "docker-containers",
74 | "z": "4c3b05bf.206fbc",
75 | "name": "",
76 | "config": "579edabe.09fe34",
77 | "x": 390,
78 | "y": 180,
79 | "wires": [
80 | [
81 | "dcf691b9.d092f"
82 | ]
83 | ]
84 | },
85 | {
86 | "id": "45796a6b.e92334",
87 | "type": "inject",
88 | "z": "4c3b05bf.206fbc",
89 | "name": "Trigger",
90 | "topic": "",
91 | "payload": "",
92 | "payloadType": "str",
93 | "repeat": "",
94 | "crontab": "",
95 | "once": false,
96 | "onceDelay": 0.1,
97 | "x": 190,
98 | "y": 240,
99 | "wires": [
100 | [
101 | "2b4da824.b1d358"
102 | ]
103 | ]
104 | },
105 | {
106 | "id": "84dfb014.8caf",
107 | "type": "docker-container-actions",
108 | "z": "4c3b05bf.206fbc",
109 | "name": "",
110 | "config": "579edabe.09fe34",
111 | "container": "test",
112 | "action": "",
113 | "x": 400,
114 | "y": 300,
115 | "wires": [
116 | [
117 | "dcf691b9.d092f"
118 | ]
119 | ]
120 | },
121 | {
122 | "id": "bdc65a41.31f6a8",
123 | "type": "inject",
124 | "z": "4c3b05bf.206fbc",
125 | "name": "start",
126 | "topic": "",
127 | "payload": "start",
128 | "payloadType": "str",
129 | "repeat": "",
130 | "crontab": "",
131 | "once": false,
132 | "onceDelay": 0.1,
133 | "x": 190,
134 | "y": 300,
135 | "wires": [
136 | [
137 | "84dfb014.8caf"
138 | ]
139 | ]
140 | },
141 | {
142 | "id": "f2535691.050318",
143 | "type": "inject",
144 | "z": "4c3b05bf.206fbc",
145 | "name": "stop",
146 | "topic": "",
147 | "payload": "stop",
148 | "payloadType": "str",
149 | "repeat": "",
150 | "crontab": "",
151 | "once": false,
152 | "onceDelay": 0.1,
153 | "x": 190,
154 | "y": 340,
155 | "wires": [
156 | [
157 | "84dfb014.8caf"
158 | ]
159 | ]
160 | },
161 | {
162 | "id": "579edabe.09fe34",
163 | "type": "docker-configuration",
164 | "z": "",
165 | "host": "localhost",
166 | "port": "2375"
167 | }
168 | ]
--------------------------------------------------------------------------------
/examples/flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naimo84/node-red-contrib-dockerode/d04142772dfd7e101a86e9dbd02710812169e840/examples/flow.png
--------------------------------------------------------------------------------
/examples/flows.json:
--------------------------------------------------------------------------------
1 | [{"id":"30e2a557.11c8fa","type":"tab","label":"List","disabled":false,"info":""},{"id":"2c4e61f8.7fa14e","type":"tab","label":"Action Inspect","disabled":false,"info":""},{"id":"bc635cab.6a09d","type":"tab","label":"Events","disabled":false,"info":""},{"id":"6f250d7d.f92b84","type":"tab","label":"Container Actions","disabled":false,"info":""},{"id":"49886661.38cda8","type":"tab","label":"Image Actions","disabled":false,"info":""},{"id":"636535a8.0b3b9c","type":"tab","label":"Service Actions","disabled":false,"info":""},{"id":"ba1fa15.d0ffa6","type":"tab","label":"Task Actions","disabled":false,"info":""},{"id":"873fccf9.b9f54","type":"tab","label":"Node Actions","disabled":false,"info":""},{"id":"970da91d.a3a7a8","type":"tab","label":"Secret Actions","disabled":false,"info":""},{"id":"f47b225e.85ceb","type":"tab","label":"Network Actions","disabled":false,"info":""},{"id":"b94f799a.9fef68","type":"tab","label":"Volume Actions","disabled":false,"info":""},{"id":"913a0952.76d548","type":"tab","label":"Plugin Action","disabled":false,"info":""},{"id":"789ca14c.874fe","type":"docker-configuration","z":"","host":"/var/run/docker.sock","port":""},{"id":"30d46297.30cefe","type":"debug","z":"30e2a557.11c8fa","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":750,"y":400,"wires":[]},{"id":"84b666c4.8a2fd8","type":"docker-secrets","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":480,"y":520,"wires":[["30d46297.30cefe"]]},{"id":"f0b68932.58f9e8","type":"docker-volumes","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":480,"y":580,"wires":[["30d46297.30cefe"]]},{"id":"d703e731.486808","type":"docker-plugins","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":480,"y":640,"wires":[["30d46297.30cefe"]]},{"id":"2309b83e.56d988","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":640,"wires":[["d703e731.486808"]]},{"id":"163ca0ac.4f1cbf","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":520,"wires":[["84b666c4.8a2fd8"]]},{"id":"a195e180.a8cfa","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":460,"wires":[["7aa92544.b7c2fc"]]},{"id":"e8d6d08d.ab969","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":580,"wires":[["f0b68932.58f9e8"]]},{"id":"529d35eb.7bf70c","type":"docker-nodes","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":470,"y":400,"wires":[["30d46297.30cefe"]]},{"id":"d5d5e1c8.69158","type":"docker-tasks","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":470,"y":340,"wires":[["30d46297.30cefe"]]},{"id":"5fd6bae4.05f754","type":"docker-services","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":480,"y":280,"wires":[["30d46297.30cefe"]]},{"id":"ad8a94b.2fb9268","type":"docker-containers","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":490,"y":220,"wires":[["30d46297.30cefe"]]},{"id":"66341f67.50435","type":"docker-images","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":480,"y":160,"wires":[["30d46297.30cefe"]]},{"id":"b9c3164b.b25508","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":400,"wires":[["529d35eb.7bf70c"]]},{"id":"5830e11c.dff72","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":280,"wires":[["5fd6bae4.05f754"]]},{"id":"345fedac.247432","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":220,"wires":[["ad8a94b.2fb9268"]]},{"id":"1a4945cc.f551fa","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":340,"wires":[["d5d5e1c8.69158"]]},{"id":"d41c87be.bfcee8","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":160,"wires":[["66341f67.50435"]]},{"id":"7aa92544.b7c2fc","type":"docker-networks","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":480,"y":460,"wires":[["30d46297.30cefe"]]},{"id":"f5f23376.19d42","type":"docker-network-actions","z":"2c4e61f8.7fa14e","name":"","config":"789ca14c.874fe","network":"docker-Traefik_private","action":"","x":600,"y":480,"wires":[["fe027d79.b1ae5"]]},{"id":"ee4949ad.63e9c8","type":"docker-volume-actions","z":"2c4e61f8.7fa14e","name":"","config":"789ca14c.874fe","volume":"896bbbffe6e824882e95270f8caad2aba25a67df76ccec42c837355985edfdff","action":"","x":600,"y":540,"wires":[["fe027d79.b1ae5"]]},{"id":"1e0cf6f2.869b79","type":"docker-secret-actions","z":"2c4e61f8.7fa14e","name":"","config":"789ca14c.874fe","secret":"basic-auth-user","action":"inspect","x":600,"y":420,"wires":[["fe027d79.b1ae5"]]},{"id":"27ebd8db.654b68","type":"docker-image-actions","z":"2c4e61f8.7fa14e","name":"","config":"789ca14c.874fe","image":"sha256:ed82e641b0d524c305ddc77a8d458c1f1e9197b895555dc89941ae807f7eed4c","action":"","x":590,"y":120,"wires":[["fe027d79.b1ae5"]]},{"id":"fe027d79.b1ae5","type":"debug","z":"2c4e61f8.7fa14e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":830,"y":120,"wires":[]},{"id":"6c92b5dd.d259ac","type":"change","z":"2c4e61f8.7fa14e","name":"","rules":[{"t":"move","p":"payload.action","pt":"msg","to":"action","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":120,"wires":[["27ebd8db.654b68","6bb67a98.2df094","41c0f68.eead708","ea911dad.637cb","c5926c78.5c357","1e0cf6f2.869b79","f5f23376.19d42","ee4949ad.63e9c8","493fedf0.d14764"]]},{"id":"2cfdabf6.6645f4","type":"inject","z":"2c4e61f8.7fa14e","name":"","topic":"","payload":"{\"action\":\"inspect\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":120,"wires":[["6c92b5dd.d259ac"]]},{"id":"723208fd.3f2a28","type":"docker-events","z":"bc635cab.6a09d","name":"","config":"789ca14c.874fe","x":220,"y":140,"wires":[["10034a13.fde4b6"]]},{"id":"10034a13.fde4b6","type":"debug","z":"bc635cab.6a09d","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":380,"y":140,"wires":[]},{"id":"493fedf0.d14764","type":"docker-plugin-actions","z":"2c4e61f8.7fa14e","name":"","config":"789ca14c.874fe","plugin":"docker.io/vieux/sshfs:latest","action":"","x":590,"y":600,"wires":[["fe027d79.b1ae5"]]},{"id":"c5926c78.5c357","type":"docker-node-actions","z":"2c4e61f8.7fa14e","name":"","config":"789ca14c.874fe","node":"docker-desktop","action":"","x":590,"y":360,"wires":[["fe027d79.b1ae5"]]},{"id":"ea911dad.637cb","type":"docker-task-actions","z":"2c4e61f8.7fa14e","name":"","config":"789ca14c.874fe","task":"r1qhhym0s5gzqf5uwtijmhf73","action":"","x":590,"y":300,"wires":[["fe027d79.b1ae5"]]},{"id":"41c0f68.eead708","type":"docker-service-actions","z":"2c4e61f8.7fa14e","name":"","config":"789ca14c.874fe","service":"docker-Traefik_whoami","action":"","x":600,"y":240,"wires":[["fe027d79.b1ae5"]]},{"id":"f4c18bb.918cc78","type":"docker-image-actions","z":"49886661.38cda8","name":"","config":"789ca14c.874fe","image":"sha256:ed82e641b0d524c305ddc77a8d458c1f1e9197b895555dc89941ae807f7eed4c","action":"","x":610,"y":40,"wires":[["ba46b4d.2c9f048"]]},{"id":"ba46b4d.2c9f048","type":"debug","z":"49886661.38cda8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":40,"wires":[]},{"id":"8c3fd148.02a79","type":"change","z":"49886661.38cda8","name":"","rules":[{"t":"move","p":"payload.action","pt":"msg","to":"action","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":40,"wires":[["f4c18bb.918cc78"]]},{"id":"bffdd600.eef378","type":"inject","z":"49886661.38cda8","name":"","topic":"","payload":"{\"action\":\"history\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":40,"wires":[["8c3fd148.02a79"]]},{"id":"c64020dc.bf9ab","type":"inject","z":"49886661.38cda8","name":"","topic":"","payload":"{\"action\":\"tag\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":100,"wires":[["8c3fd148.02a79"]]},{"id":"da289ce.7769e6","type":"docker-container-actions","z":"6f250d7d.f92b84","name":"","config":"789ca14c.874fe","container":"docker-Traefik_traefik.1.ifkm9pi1004lwrojz075i1ygu","action":"","cmd":"","x":630,"y":80,"wires":[["7f849cff.3d1294"]]},{"id":"538a7ca.0d25a84","type":"inject","z":"6f250d7d.f92b84","name":"","topic":"","payload":"{\"action\":\"inspect\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":80,"wires":[["794ea30a.f7dc5c"]]},{"id":"7f849cff.3d1294","type":"debug","z":"6f250d7d.f92b84","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":850,"y":80,"wires":[]},{"id":"794ea30a.f7dc5c","type":"change","z":"6f250d7d.f92b84","name":"","rules":[{"t":"move","p":"payload.action","pt":"msg","to":"action","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":390,"y":80,"wires":[["da289ce.7769e6"]]},{"id":"16e75790.fbe098","type":"inject","z":"6f250d7d.f92b84","name":"","topic":"","payload":"{\"action\":\"stop\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":120,"y":200,"wires":[["794ea30a.f7dc5c"]]},{"id":"50fccb82.4eb7e4","type":"docker-service-actions","z":"636535a8.0b3b9c","name":"","config":"789ca14c.874fe","service":"docker-Traefik_whoami","action":"","x":580,"y":60,"wires":[["d783ba3b.488fe8"]]},{"id":"d78f9d0b.94362","type":"inject","z":"636535a8.0b3b9c","name":"","topic":"","payload":"{\"action\":\"inspect\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":60,"wires":[["b55c823f.737d1"]]},{"id":"b55c823f.737d1","type":"change","z":"636535a8.0b3b9c","name":"","rules":[{"t":"move","p":"payload.action","pt":"msg","to":"action","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":370,"y":60,"wires":[["50fccb82.4eb7e4"]]},{"id":"7d53ec3f.8430a4","type":"inject","z":"636535a8.0b3b9c","name":"","topic":"","payload":"{\"action\":\"logs\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":120,"y":120,"wires":[["b55c823f.737d1"]]},{"id":"56234ae4.4730e4","type":"inject","z":"636535a8.0b3b9c","name":"","topic":"","payload":"{\"action\":\"remove\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":180,"wires":[["b55c823f.737d1"]]},{"id":"b6bab857.4596a8","type":"inject","z":"6f250d7d.f92b84","name":"","topic":"","payload":"{\"action\":\"start\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":120,"y":140,"wires":[["794ea30a.f7dc5c"]]},{"id":"6bb67a98.2df094","type":"docker-container-actions","z":"2c4e61f8.7fa14e","name":"","config":"789ca14c.874fe","container":"nodered_nodered_1","action":"","cmd":"","x":610,"y":180,"wires":[["fe027d79.b1ae5"]]},{"id":"d783ba3b.488fe8","type":"debug","z":"636535a8.0b3b9c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":900,"y":60,"wires":[]},{"id":"2d204398.19272c","type":"docker-task-actions","z":"ba1fa15.d0ffa6","name":"","config":"789ca14c.874fe","task":"ayczjadojmq9ddnwihkapbyu4","action":"","x":600,"y":80,"wires":[["41973181.46586"]]},{"id":"14a642de.dcb2ad","type":"inject","z":"ba1fa15.d0ffa6","name":"","topic":"","payload":"{\"action\":\"inspect\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":80,"wires":[["b7f3d9ed.313e88"]]},{"id":"b7f3d9ed.313e88","type":"change","z":"ba1fa15.d0ffa6","name":"","rules":[{"t":"move","p":"payload.action","pt":"msg","to":"action","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":390,"y":80,"wires":[["2d204398.19272c"]]},{"id":"41973181.46586","type":"debug","z":"ba1fa15.d0ffa6","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":780,"y":80,"wires":[]},{"id":"d326f1b7.f7c55","type":"inject","z":"873fccf9.b9f54","name":"","topic":"","payload":"{\"action\":\"inspect\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":40,"wires":[["c7409940.432e18"]]},{"id":"c7409940.432e18","type":"change","z":"873fccf9.b9f54","name":"","rules":[{"t":"move","p":"payload.action","pt":"msg","to":"action","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":40,"wires":[["a886ea87.f59a18"]]},{"id":"e1970dec.01472","type":"debug","z":"873fccf9.b9f54","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":770,"y":40,"wires":[]},{"id":"a886ea87.f59a18","type":"docker-node-actions","z":"873fccf9.b9f54","name":"","config":"789ca14c.874fe","node":"docker-desktop","action":"","x":610,"y":40,"wires":[["e1970dec.01472"]]},{"id":"497a0f95.d9d6e","type":"inject","z":"970da91d.a3a7a8","name":"","topic":"","payload":"{\"action\":\"inspect\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":40,"wires":[["6f67a463.46900c"]]},{"id":"6f67a463.46900c","type":"change","z":"970da91d.a3a7a8","name":"","rules":[{"t":"move","p":"payload.action","pt":"msg","to":"action","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":40,"wires":[["4eaf3a7b.b530a4"]]},{"id":"90d7276e.0467e8","type":"debug","z":"970da91d.a3a7a8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":770,"y":40,"wires":[]},{"id":"4eaf3a7b.b530a4","type":"docker-secret-actions","z":"970da91d.a3a7a8","name":"","config":"789ca14c.874fe","secret":"basic-auth-user","action":"","x":600,"y":40,"wires":[["90d7276e.0467e8"]]},{"id":"8623cd71.8cd59","type":"inject","z":"f47b225e.85ceb","name":"","topic":"","payload":"{\"action\":\"inspect\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":40,"wires":[["b4bf5d0b.5ce3e"]]},{"id":"b4bf5d0b.5ce3e","type":"change","z":"f47b225e.85ceb","name":"","rules":[{"t":"move","p":"payload.action","pt":"msg","to":"action","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":390,"y":40,"wires":[["f8e8bf6a.d58c2"]]},{"id":"cac309f0.f43ec8","type":"debug","z":"f47b225e.85ceb","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":770,"y":40,"wires":[]},{"id":"f8e8bf6a.d58c2","type":"docker-network-actions","z":"f47b225e.85ceb","name":"","config":"789ca14c.874fe","network":"docker-Traefik_private","action":"","x":600,"y":40,"wires":[["cac309f0.f43ec8"]]},{"id":"6537538f.3adb8c","type":"inject","z":"b94f799a.9fef68","name":"","topic":"","payload":"{\"action\":\"inspect\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":40,"wires":[["a2ad97e4.c280d8"]]},{"id":"a2ad97e4.c280d8","type":"change","z":"b94f799a.9fef68","name":"","rules":[{"t":"move","p":"payload.action","pt":"msg","to":"action","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":40,"wires":[["294232b5.bb9e0e"]]},{"id":"be34e8f3.9e6a08","type":"debug","z":"b94f799a.9fef68","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":770,"y":40,"wires":[]},{"id":"294232b5.bb9e0e","type":"docker-volume-actions","z":"b94f799a.9fef68","name":"","config":"789ca14c.874fe","volume":"docker_mailconfig","action":"","x":600,"y":40,"wires":[["be34e8f3.9e6a08"]]},{"id":"116d78d0.7651b7","type":"docker-plugin-actions","z":"913a0952.76d548","name":"","config":"789ca14c.874fe","plugin":"docker.io/vieux/sshfs:latest","action":"","x":610,"y":40,"wires":[["9ae2cbd1.faded8"]]},{"id":"cbd4ec33.3a2b5","type":"inject","z":"913a0952.76d548","name":"","topic":"","payload":"{\"action\":\"inspect\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":40,"wires":[["b9fcfe0b.69024"]]},{"id":"b9fcfe0b.69024","type":"change","z":"913a0952.76d548","name":"","rules":[{"t":"move","p":"payload.action","pt":"msg","to":"action","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":40,"wires":[["116d78d0.7651b7"]]},{"id":"9ae2cbd1.faded8","type":"debug","z":"913a0952.76d548","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":790,"y":40,"wires":[]}]
--------------------------------------------------------------------------------
/examples/list-flows.json:
--------------------------------------------------------------------------------
1 | [{"id":"30e2a557.11c8fa","type":"tab","label":"List","disabled":false,"info":""},{"id":"30d46297.30cefe","type":"debug","z":"30e2a557.11c8fa","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":750,"y":400,"wires":[]},{"id":"84b666c4.8a2fd8","type":"docker-secrets","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":480,"y":520,"wires":[["30d46297.30cefe"]]},{"id":"f0b68932.58f9e8","type":"docker-volumes","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":480,"y":580,"wires":[["30d46297.30cefe"]]},{"id":"d703e731.486808","type":"docker-plugins","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":480,"y":640,"wires":[["30d46297.30cefe"]]},{"id":"2309b83e.56d988","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":640,"wires":[["d703e731.486808"]]},{"id":"163ca0ac.4f1cbf","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":520,"wires":[["84b666c4.8a2fd8"]]},{"id":"a195e180.a8cfa","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":460,"wires":[["7aa92544.b7c2fc"]]},{"id":"e8d6d08d.ab969","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":580,"wires":[["f0b68932.58f9e8"]]},{"id":"529d35eb.7bf70c","type":"docker-nodes","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":470,"y":400,"wires":[["30d46297.30cefe"]]},{"id":"d5d5e1c8.69158","type":"docker-tasks","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":470,"y":340,"wires":[["30d46297.30cefe"]]},{"id":"5fd6bae4.05f754","type":"docker-services","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":480,"y":280,"wires":[["30d46297.30cefe"]]},{"id":"ad8a94b.2fb9268","type":"docker-containers","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":490,"y":220,"wires":[["30d46297.30cefe"]]},{"id":"66341f67.50435","type":"docker-images","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":480,"y":160,"wires":[["30d46297.30cefe"]]},{"id":"b9c3164b.b25508","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":400,"wires":[["529d35eb.7bf70c"]]},{"id":"5830e11c.dff72","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":280,"wires":[["5fd6bae4.05f754"]]},{"id":"345fedac.247432","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":220,"wires":[["ad8a94b.2fb9268"]]},{"id":"1a4945cc.f551fa","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":340,"wires":[["d5d5e1c8.69158"]]},{"id":"d41c87be.bfcee8","type":"inject","z":"30e2a557.11c8fa","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":160,"wires":[["66341f67.50435"]]},{"id":"7aa92544.b7c2fc","type":"docker-networks","z":"30e2a557.11c8fa","name":"","config":"789ca14c.874fe","x":480,"y":460,"wires":[["30d46297.30cefe"]]},{"id":"789ca14c.874fe","type":"docker-configuration","z":"","host":"/var/run/docker.sock","port":""}]
--------------------------------------------------------------------------------
/examples/support_banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naimo84/node-red-contrib-dockerode/d04142772dfd7e101a86e9dbd02710812169e840/examples/support_banner.png
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require("gulp");
2 | var ts = require("gulp-typescript");
3 | var tsProject = ts.createProject("tsconfig.json");
4 | var sourcemaps = require('gulp-sourcemaps');
5 | var nodemon = require('gulp-nodemon');
6 | var watch = require('gulp-watch');
7 |
8 | var paths = {
9 | pages: ['src/*.html'],
10 | src: 'src',
11 | dist: 'dist'
12 | };
13 |
14 | function copyHtml() {
15 | gulp.src('src/icons/*.png', { base: paths.src })
16 | .pipe(gulp.dest(paths.dist))
17 | return gulp.src(paths.pages, { base: paths.src })
18 | .pipe(gulp.dest(paths.dist));
19 | }
20 |
21 | gulp.task("copy-html", copyHtml);
22 |
23 | gulp.task('develop', function (done) {
24 | var stream = nodemon({
25 | legacyWatch: true,
26 | exec: 'node --inspect=9229 --preserve-symlinks --experimental-modules --trace-warnings /usr/lib/node_modules/node-red/red.js',
27 | ext: '*.js',
28 | watch: [paths.dist],
29 | ignore: ["*.map"],
30 | done: done,
31 | verbose: true,
32 | delay: 10000,
33 | env: { "NO_UPDATE_NOTIFIER": "1" }
34 | });
35 |
36 | copyHtml();
37 | tsProject.src()
38 | .pipe(sourcemaps.init())
39 | .pipe(tsProject())
40 | .js
41 | .pipe(sourcemaps.write('.'))
42 | .pipe(gulp.dest(paths.dist));
43 |
44 | watch(paths.pages).on('change', () => {
45 | copyHtml();
46 | stream.emit('restart', 10000)
47 | });
48 |
49 | watch('src/*.ts').on('change', () => {
50 | tsProject.src()
51 | .pipe(sourcemaps.init())
52 | .pipe(tsProject())
53 | .js
54 | .pipe(sourcemaps.write('.'))
55 | .pipe(gulp.dest(paths.dist));
56 |
57 | stream.emit('restart', 10000)
58 | });
59 |
60 | stream
61 | .on('restart', function () {
62 | console.log('restarted!')
63 | })
64 | .on('crash', function () {
65 | console.error('Application has crashed!\n')
66 | stream.emit('restart', 10000) // restart the server in 10 seconds
67 | })
68 | })
69 |
70 | gulp.task("default", gulp.series(
71 | gulp.parallel('copy-html'),
72 | () => {
73 | return tsProject.src()
74 | .pipe(sourcemaps.init())
75 | .pipe(tsProject())
76 | .js
77 | .pipe(sourcemaps.write('.'))
78 | .pipe(gulp.dest(paths.dist));
79 | })
80 | );
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-red-contrib-dockerode",
3 | "version": "0.15.0",
4 | "description": "node-red nodes to communicate with docker",
5 | "author": {
6 | "name": "naimo84",
7 | "email": "git@neumann-benjamin.de"
8 | },
9 | "contributors": [
10 | {
11 | "name": "naimo84",
12 | "email": "git@neumann-benjamin.de"
13 | },
14 | {
15 | "name": "ethanbrooks",
16 | "email": "ethan.brooks@gmail.com"
17 | }
18 | ],
19 | "repository": {
20 | "type": "git",
21 | "url": "https://github.com/naimo84/node-red-contrib-dockerode"
22 | },
23 | "keywords": [
24 | "node-red",
25 | "docker"
26 | ],
27 | "license": "MIT",
28 | "licenses": [
29 | {
30 | "type": "MIT",
31 | "url": "https://github.com/naimo84/node-red-contrib-dockerode/blob/master/LICENSE"
32 | }
33 | ],
34 | "scripts": {
35 | "build": "gulp",
36 | "release": "release-it",
37 | "dev": "gulp develop",
38 | "nodered": "node-red"
39 | },
40 | "node-red": {
41 | "nodes": {
42 | "docker-configuration": "dist/docker-configuration.js",
43 | "docker-events": "dist/docker-events.js",
44 | "docker-swarm-actions": "dist/docker-swarm-actions.js",
45 | "docker-engine-actions": "dist/docker-engine-actions.js",
46 | "docker-config-actions": "dist/docker-config-actions.js",
47 | "docker-image-actions": "dist/docker-image-actions.js",
48 | "docker-container-actions": "dist/docker-container-actions.js",
49 | "docker-service-actions": "dist/docker-service-actions.js",
50 | "docker-task-actions": "dist/docker-task-actions.js",
51 | "docker-node-actions": "dist/docker-node-actions.js",
52 | "docker-secret-actions": "dist/docker-secret-actions.js",
53 | "docker-network-actions": "dist/docker-network-actions.js",
54 | "docker-volume-actions": "dist/docker-volume-actions.js",
55 | "docker-plugin-actions": "dist/docker-plugin-actions.js"
56 | }
57 | },
58 | "dependencies": {
59 | "dockerode": "^3.3.1",
60 | "stream": "0.0.2",
61 | "debug": "^4.3.2"
62 | },
63 | "devDependencies": {
64 | "@types/dockerode": "^2.5.20",
65 | "@types/node": "^12.7.2",
66 | "@types/node-red": "^0.20.0",
67 | "commitlint": "^8.1.0",
68 | "dockerode-mock": "^0.3.2",
69 | "gulp": "^4.0.2",
70 | "gulp-nodemon": "^2.4.2",
71 | "gulp-sourcemaps": "^2.6.5",
72 | "gulp-typescript": "^6.0.0-alpha.1",
73 | "gulp-watch": "^5.0.1",
74 | "husky": "^3.0.5",
75 | "mocha": "6.2.0",
76 | "node-red": "3.1.9",
77 | "node-red-contrib-mock-node": "^0.4.0",
78 | "node-red-node-test-helper": "^0.2.3",
79 | "should": "13.2.3",
80 | "release-it": "^14.11.8",
81 | "typescript": "^3.5.3"
82 | },
83 | "release-it": {
84 | "git": {
85 | "commitMessage": "chore: release v${version}",
86 | "changelog": "npx auto-changelog --stdout --commit-limit false --unreleased --template https://raw.githubusercontent.com/release-it/release-it/master/templates/changelog-compact.hbs"
87 | },
88 | "github": {
89 | "release": true
90 | },
91 | "npm": {
92 | "publish": false
93 | },
94 | "hooks": {
95 | "after:bump": "npx auto-changelog -p"
96 | }
97 | },
98 | "commitlint": {
99 | "format": {
100 | "helpUrl": "test"
101 | },
102 | "rules": {
103 | "body-leading-blank": [
104 | 1,
105 | "always"
106 | ],
107 | "footer-leading-blank": [
108 | 1,
109 | "always"
110 | ],
111 | "header-max-length": [
112 | 2,
113 | "always",
114 | 72
115 | ],
116 | "scope-case": [
117 | 2,
118 | "always",
119 | "lower-case"
120 | ],
121 | "subject-case": [
122 | 2,
123 | "never",
124 | [
125 | "sentence-case",
126 | "start-case",
127 | "pascal-case",
128 | "upper-case"
129 | ]
130 | ],
131 | "subject-empty": [
132 | 2,
133 | "never"
134 | ],
135 | "subject-full-stop": [
136 | 2,
137 | "never",
138 | "."
139 | ],
140 | "type-case": [
141 | 2,
142 | "always",
143 | "lower-case"
144 | ],
145 | "type-empty": [
146 | 2,
147 | "never"
148 | ],
149 | "type-enum": [
150 | 2,
151 | "always",
152 | [
153 | "build",
154 | "chore",
155 | "ci",
156 | "docs",
157 | "feat",
158 | "fix",
159 | "perf",
160 | "refactor",
161 | "revert",
162 | "style",
163 | "test"
164 | ]
165 | ]
166 | }
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/src/docker-config-actions.html:
--------------------------------------------------------------------------------
1 |
2 |
62 |
63 |
90 |
91 |
--------------------------------------------------------------------------------
/src/docker-config-actions.ts:
--------------------------------------------------------------------------------
1 | import { Red, Node } from 'node-red';
2 | import { DockerConfiguration } from './docker-configuration';
3 | import * as Dockerode from 'dockerode';
4 |
5 | module.exports = function (RED: Red) {
6 |
7 | function DockerConfigAction(n: any) {
8 | RED.nodes.createNode(this, n);
9 | let config = RED.nodes.getNode(n.config) as unknown as DockerConfiguration;
10 | let client = config.getClient();
11 | this.on('input', (msg) => {
12 | RED.log.debug(msg);
13 | let configId: string = n.config || msg.payload.configId || msg.configId || undefined;
14 | let action = n.action || msg.action || msg.payload.action || undefined;
15 | let options = n.options || msg.options || msg.payload.options || undefined;
16 | if (configId === undefined && !['list', 'prune', 'create'].includes(action)) {
17 | this.error("Config id/name must be provided via configuration or via `msg.config`");
18 | return;
19 | }
20 | this.status({});
21 | executeAction(configId, options, client, action, this,msg);
22 | });
23 |
24 | function executeAction(configId: string, options: any, client: Dockerode, action: string, node: Node,msg) {
25 |
26 | let config = client.getConfig(configId);
27 |
28 | switch (action) {
29 |
30 | case 'list':
31 | // https://docs.docker.com/engine/api/v1.40/#operation/ConfigList
32 | client.listConfigs({ all: true })
33 | .then(res => {
34 | node.status({ fill: 'green', shape: 'dot', text: configId + ' started' });
35 | node.send(Object.assign(msg,{ payload: res }));
36 | }).catch(err => {
37 | if (err.statusCode === 400) {
38 | node.error(`Bad parameter: ${err.reason}`, msg);
39 | node.send({ payload: err });
40 | } else if (err.statusCode === 500) {
41 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
42 | node.send({ payload: err });
43 | } else {
44 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
45 | return;
46 | }
47 | });
48 | break;
49 |
50 | case 'create':
51 | // https://docs.docker.com/engine/api/v1.40/#operation/ConfigCreate
52 | client.createConfig(options)
53 | .then(res => {
54 | node.status({ fill: 'green', shape: 'dot', text: configId + ' remove' });
55 | node.send(Object.assign(msg,{ payload: res }));
56 | }).catch(err => {
57 | if (err.statusCode === 500) {
58 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
59 | node.send({ payload: err });
60 | } else if (err.statusCode === 409) {
61 | node.error(`Name conflicts with an existing objectd: [${configId}]`, msg);
62 | node.send({ payload: err });
63 | } else {
64 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
65 | return;
66 | }
67 | });
68 | break;
69 |
70 | case 'inspect':
71 | // https://docs.docker.com/engine/api/v1.40/#operation/ConfigInspect
72 | config.inspect()
73 | .then(res => {
74 | node.status({ fill: 'green', shape: 'dot', text: configId + ' started' });
75 | node.send(Object.assign(msg,{ payload: res }));
76 | }).catch(err => {
77 | if (err.statusCode === 503) {
78 | node.error(`Node is not part of a swarm: [${configId}]`, msg);
79 | node.send({ payload: err });
80 | } else if (err.statusCode === 404) {
81 | node.error(`Config not found: [${configId}]`, msg);
82 | node.send({ payload: err });
83 | } else if (err.statusCode === 500) {
84 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
85 | node.send({ payload: err });
86 | } else {
87 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
88 | return;
89 | }
90 | });
91 | break;
92 |
93 | case 'remove':
94 | // https://docs.docker.com/engine/api/v1.40/#operation/ConfigDelete
95 | config.remove()
96 | .then(res => {
97 | node.status({ fill: 'green', shape: 'dot', text: configId + ' remove' });
98 | node.send(Object.assign(msg,{ payload: res }));
99 | }).catch(err => {
100 | if (err.statusCode === 503) {
101 | node.error(`Node is not part of a swarm: [${configId}]`, msg);
102 | node.send({ payload: err });
103 | } else if (err.statusCode === 404) {
104 | node.error(`Config not found: [${configId}]`, msg);
105 | node.send({ payload: err });
106 | } else if(err.statusCode === 500) {
107 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
108 | node.send({ payload: err });
109 | } else {
110 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
111 | return;
112 | }
113 | });
114 | break;
115 |
116 | case 'update':
117 | // https://docs.docker.com/engine/api/v1.40/#operation/ConfigUpdate
118 | config.update()
119 | .then(res => {
120 | node.status({ fill: 'green', shape: 'dot', text: configId + ' remove' });
121 | node.send(Object.assign(msg,{ payload: res }));
122 | }).catch(err => {
123 | if (err.statusCode === 503) {
124 | node.error(`Node is not part of a swarm: [${configId}]`, msg);
125 | node.send({ payload: err });
126 | } else if (err.statusCode === 400) {
127 | node.error(`Bad parameter: [${configId}]`, msg);
128 | node.send({ payload: err });
129 | } else if (err.statusCode === 404) {
130 | node.error(`Config not found: [${configId}]`, msg);
131 | node.send({ payload: err });
132 | } else if(err.statusCode === 500) {
133 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
134 | node.send({ payload: err });
135 | } else {
136 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
137 | return;
138 | }
139 | });
140 | break;
141 |
142 | default:
143 | node.error(`Called with an unknown action: ${action}`, msg);
144 | return;
145 | }
146 | }
147 | }
148 |
149 |
150 | RED.httpAdmin.post("/configSearch", RED.auth.needsPermission('flows.write'), function (req, res) {
151 | RED.log.debug("POST /configSearch");
152 |
153 | const nodeId = req.body.id;
154 | let config = RED.nodes.getNode(nodeId);
155 |
156 | discoverSonos(config, (configs) => {
157 | RED.log.debug("GET /configSearch: " + configs.length + " found");
158 | res.json(configs);
159 | });
160 | });
161 |
162 | function discoverSonos(config, discoveryCallback) {
163 | let client = config.getClient();
164 | client.listConfigs({ all: true })
165 | .then(configs => discoveryCallback(configs))
166 | .catch(err => this.error(err));
167 | }
168 |
169 | RED.nodes.registerType('docker-config-actions', DockerConfigAction);
170 | }
171 |
172 |
--------------------------------------------------------------------------------
/src/docker-configuration.html:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
--------------------------------------------------------------------------------
/src/docker-configuration.ts:
--------------------------------------------------------------------------------
1 | import { Red } from 'node-red';
2 | import { readFileSync } from 'fs';
3 | import * as Dockerode from 'dockerode';
4 |
5 |
6 | export interface DockerConfiguration {
7 | host: string
8 | ca: string
9 | cert: string
10 | key: string
11 | port: number
12 | action: string
13 | container: string
14 | options: any
15 | getClient(): Dockerode
16 | }
17 |
18 | module.exports = function (RED: Red) {
19 |
20 |
21 | function DockerConfiguration(n) {
22 | RED.nodes.createNode(this, n);
23 |
24 | let node: DockerConfiguration = this;
25 | node.host = n.host;
26 | node.port = n.port || "2375";
27 | node.options = n.options;
28 | node.ca = n.ca;
29 | node.key = n.key;
30 | node.cert = n.cert;
31 |
32 | node.getClient = (): Dockerode => {
33 | let dockeropt = {};
34 |
35 | if (node.host.includes("docker.sock")) {
36 | dockeropt = {
37 | socketPath: node.host
38 | }
39 | } else {
40 | dockeropt = {
41 | host: node.host,
42 | port: node.port
43 | }
44 | }
45 |
46 | if (node.ca) {
47 | dockeropt = Object.assign(dockeropt, {
48 | ca: readFileSync(node.ca)
49 | })
50 | }
51 | if (node.key) {
52 | dockeropt = Object.assign(dockeropt, {
53 | key: readFileSync(node.key)
54 | })
55 | }
56 | if (node.cert) {
57 | dockeropt = Object.assign(dockeropt, {
58 | cert: readFileSync(node.cert)
59 | })
60 | }
61 | return new Dockerode(dockeropt);
62 | };
63 | }
64 | RED.nodes.registerType("docker-configuration", DockerConfiguration);
65 |
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/src/docker-engine-actions.html:
--------------------------------------------------------------------------------
1 |
2 |
68 |
69 |
96 |
97 |
--------------------------------------------------------------------------------
/src/docker-engine-actions.ts:
--------------------------------------------------------------------------------
1 | import { Red, Node } from 'node-red';
2 | import { DockerConfiguration } from './docker-configuration';
3 | import * as Dockerode from 'dockerode';
4 |
5 | module.exports = function (RED: Red) {
6 |
7 | function DockerEngineAction(n: any) {
8 | RED.nodes.createNode(this, n);
9 | let config = RED.nodes.getNode(n.config) as unknown as DockerConfiguration;
10 | let client = config.getClient();
11 | this.on('input', (msg) => {
12 |
13 | let action = n.action || msg.action || msg.payload.action || undefined;
14 | let options = n.options || msg.options|| msg.options || msg.payload.options || undefined;
15 | let file = n.options || msg.options|| msg.options || undefined;
16 | let containerId: string = n.containerId || msg.payload.containerId || msg.containerId || n.containerName || msg.payload.containerName || msg.containerName || undefined;
17 | if (containerId === undefined && !['list', 'prune', 'create'].includes(action)) {
18 | this.error("Container id/name must be provided via configuration or via `msg.containerId`");
19 | return;
20 | }
21 | this.status({});
22 | executeAction(containerId, file, client, action, options, this,msg);
23 | });
24 |
25 | function executeAction(containerId: string, file: File, client: Dockerode, action: string, options: any, node: Node,msg) {
26 |
27 | let engine = client;
28 |
29 | switch (action) {
30 | case 'auth':
31 | engine.checkAuth(options)
32 | .then(res => {
33 | node.status({ fill: 'green', shape: 'dot', text: ' remove' });
34 | node.send(Object.assign(msg,{ payload: res }));
35 | }).catch(err => {
36 | if (err.statusCode === 500) {
37 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
38 | node.send({ payload: err });
39 | } else {
40 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
41 | return;
42 | }
43 | });
44 | break;
45 | case 'info':
46 | engine.info()
47 | .then(res => {
48 | node.status({ fill: 'green', shape: 'dot', text: ' remove' });
49 | node.send(Object.assign(msg,{ payload: res }));
50 | }).catch(err => {
51 | if (err.statusCode === 500) {
52 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
53 | node.send({ payload: err });
54 | } else {
55 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
56 | return;
57 | }
58 | });
59 | break;
60 |
61 | case 'version':
62 | engine.version()
63 | .then(res => {
64 | node.status({ fill: 'green', shape: 'dot', text: 'Engine started' });
65 | node.send(Object.assign(msg,{ payload: res }));
66 | }).catch(err => {
67 | if (err.statusCode === 500) {
68 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
69 | node.send({ payload: err });
70 | } else {
71 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
72 | return;
73 | }
74 | });
75 | break;
76 | case 'ping':
77 | engine.ping()
78 | .then(res => {
79 | node.status({ fill: 'green', shape: 'dot', text: ' remove' });
80 | node.send(Object.assign(msg,{ payload: res }));
81 | }).catch(err => {
82 | if (err.statusCode === 500) {
83 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
84 | node.send({ payload: err });
85 | } else {
86 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
87 | return;
88 | }
89 | });
90 | break;
91 | case 'df':
92 | engine.df()
93 | .then(res => {
94 | node.status({ fill: 'green', shape: 'dot', text: ' remove' });
95 | node.send(Object.assign(msg,{ payload: res }));
96 | }).catch(err => {
97 | if (err.statusCode === 500) {
98 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
99 | node.send({ payload: err });
100 | } else {
101 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
102 | return;
103 | }
104 | });
105 | break;
106 | case 'import-image':
107 | engine.importImage(options, file)
108 | .then(res => {
109 | node.status({ fill: 'green', shape: 'dot', text: ' remove' });
110 | node.send(Object.assign(msg,{ payload: res }));
111 | }).catch(err => {
112 | if (err.statusCode === 500) {
113 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
114 | node.send({ payload: err });
115 | } else {
116 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
117 | return;
118 | }
119 | });
120 | break;
121 | /* case 'run':
122 | engine.run()
123 | .then(res => {
124 | node.status({ fill: 'green', shape: 'dot', text: ' remove' });
125 | node.send(Object.assign(msg,{ payload: res }));
126 | }).catch(err => {
127 | if (err.statusCode === 500) {
128 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
129 | node.send({ payload: err });
130 | } else {
131 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
132 | return;
133 | }
134 | });
135 | break;
136 | */
137 | case 'build':
138 | engine.buildImage(options)
139 | .then(res => {
140 | node.status({ fill: 'green', shape: 'dot', text: ' stopped' });
141 | node.send(Object.assign(msg,{ payload: res }));
142 | }).catch(err => {
143 | if (err.statusCode === 500) {
144 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
145 | node.send({ payload: err });
146 | } else {
147 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
148 | return;
149 | }
150 | });
151 | break;
152 |
153 |
154 | case 'exec-start':
155 | engine.getExec(containerId).start(options)
156 | .then(res => {
157 | node.status({ fill: 'green', shape: 'dot', text: ' remove' });
158 | node.send(Object.assign(msg,{ payload: res }));
159 | }).catch(err => {
160 | if (err.statusCode === 500) {
161 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
162 | node.send({ payload: err });
163 | } else {
164 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
165 | return;
166 | }
167 | });
168 | break;
169 | case 'exec-resize':
170 | engine.getExec(containerId).start(options)
171 | .then(res => {
172 | node.status({ fill: 'green', shape: 'dot', text: containerId + ' remove' });
173 | node.send(Object.assign(msg,{ payload: res }));
174 | }).catch(err => {
175 | if (err.statusCode === 500) {
176 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
177 | node.send({ payload: err });
178 | } else {
179 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
180 | return;
181 | }
182 | });
183 | break;
184 | case 'exec-json':
185 | engine.getExec(containerId).inspect()
186 | .then(res => {
187 | node.status({ fill: 'green', shape: 'dot', text: containerId + ' remove' });
188 | node.send(Object.assign(msg,{ payload: res }));
189 | }).catch(err => {
190 | if (err.statusCode === 500) {
191 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
192 | node.send({ payload: err });
193 | } else {
194 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
195 | return;
196 | }
197 | });
198 | break;
199 | default:
200 | node.error(`Called with an unknown action: ${action}`, msg);
201 | return;
202 | }
203 | }
204 | }
205 |
206 | RED.nodes.registerType('docker-engine-actions', DockerEngineAction);
207 | }
208 |
209 |
--------------------------------------------------------------------------------
/src/docker-events.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
23 |
24 |
40 |
41 |
44 |
--------------------------------------------------------------------------------
/src/docker-events.ts:
--------------------------------------------------------------------------------
1 | import { Red } from 'node-red';
2 | import { DockerConfiguration } from './docker-configuration';
3 |
4 | export interface DockerEvent {
5 | Type?: string,
6 | Action?: string,
7 | time?: string,
8 | timeNano?: string
9 | }
10 |
11 | module.exports = function (RED: Red) {
12 |
13 | function DockerEvents(n: any) {
14 | let node: any = this as any;
15 | RED.nodes.createNode(node, n);
16 |
17 | let config = (RED.nodes.getNode(n.config) as unknown as DockerConfiguration);
18 | let client = config.getClient();
19 | let globalEvents = null;
20 |
21 | node.on('input', async (msg, send, done) => {
22 | try {
23 | if(globalEvents !== null){
24 | globalEvents.removeAllListeners();
25 | globalEvents.destroy();
26 | }
27 |
28 | const events: NodeJS.ReadableStream = await client.getEvents()
29 | globalEvents = events;
30 | node.status({ fill: 'green', shape: 'dot', text: 'node-red:common.status.connected' });
31 |
32 | events.on('data', (data) => {
33 | let event: DockerEvent = {};
34 | try {
35 | event = JSON.parse(data.toString());
36 | } catch (e) {
37 | node.error('Error parsing JSON', msg);
38 | return
39 | }
40 |
41 | send({
42 | _msgid: RED.util.generateId(),
43 | type: event.Type,
44 | action: event.Action,
45 | time: event.time,
46 | timeNano: event.timeNano,
47 | payload: event
48 | });
49 | done();
50 | });
51 |
52 | events.on('close', () => {
53 | node.status({ fill: 'red', shape: 'ring', text: 'node-red:common.status.disconnected' });
54 | node.warn('Docker event stream closed.');
55 | send([null, {
56 | _msgid: RED.util.generateId(),
57 | type: 'dockerode',
58 | payload: {
59 | connected: false
60 | }
61 | }])
62 | done();
63 | });
64 | events.on('error', (err) => {
65 | node.status({ fill: 'red', shape: 'ring', text: 'node-red:common.status.disconnected' });
66 | send([null, {
67 | _msgid: RED.util.generateId(),
68 | type: 'dockerode',
69 | payload: {
70 | connected: false
71 | }
72 | }])
73 | done('Error:', err);
74 | });
75 | events.on('end', () => {
76 | node.status({ fill: 'yellow', shape: 'ring', text: 'stream ended' });
77 | node.warn('Docker event stream ended.');
78 | done();
79 | });
80 | }
81 | catch {
82 | console.error('Failed to attach docker event listener.');
83 | send([null, {
84 | _msgid: RED.util.generateId(),
85 | type: 'dockerode',
86 | payload: {
87 | connected: false
88 | }
89 | }])
90 | done();
91 | }
92 | })
93 | node.emit("input", {});
94 | }
95 |
96 | RED.nodes.registerType('docker-events', DockerEvents);
97 | }
98 |
--------------------------------------------------------------------------------
/src/docker-image-actions.html:
--------------------------------------------------------------------------------
1 |
2 |
71 |
72 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/src/docker-image-actions.ts:
--------------------------------------------------------------------------------
1 | import { Red, Node } from 'node-red';
2 | import { DockerConfiguration } from './docker-configuration';
3 | import * as Dockerode from 'dockerode';
4 |
5 | module.exports = function (RED: Red) {
6 |
7 |
8 | function DockerImageAction(n: any) {
9 | RED.nodes.createNode(this, n);
10 | let config = RED.nodes.getNode(n.config) as unknown as DockerConfiguration;
11 | let client = config.getClient();
12 | this.on('input', (msg) => {
13 |
14 | let imageId: string = n.image || msg.payload.imageId || msg.imageId || undefined;
15 | let action = n.action || msg.action || msg.payload.action || undefined;
16 | let options = {};
17 |
18 |
19 | if (imageId === undefined && !['list', 'prune', 'create'].includes(action)) {
20 | this.error("Image id/name must be provided via configuration or via `msg.image`");
21 | return;
22 | }
23 | this.status({});
24 | executeAction(imageId, options, client, action, this,msg);
25 | });
26 |
27 | function executeAction(imageId: string, options: any, client: Dockerode, action: string, node: Node,msg) {
28 |
29 | let image = client.getImage(imageId);
30 |
31 | switch (action) {
32 |
33 | case 'list':
34 | // https://docs.docker.com/engine/api/v1.40/#operation/ImagesList
35 | client.listImages({ all: true })
36 | .then(res => {
37 | node.status({ fill: 'green', shape: 'dot', text: imageId + ' started' });
38 | node.send(Object.assign(msg,{ payload: res }));
39 | }).catch(err => {
40 | if (err.statusCode === 400) {
41 | node.error(`Bad parameter: ${err.reason}`, msg);
42 | node.send({ payload: err });
43 | } else if (err.statusCode === 500) {
44 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
45 | node.send({ payload: err });
46 | } else {
47 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
48 | return;
49 | }
50 | });
51 | break;
52 |
53 | case 'inspect':
54 | // https://docs.docker.com/engine/api/v1.40/#operation/ImageInspect
55 | image.inspect()
56 | .then(res => {
57 | node.status({ fill: 'green', shape: 'dot', text: imageId + ' started' });
58 | node.send(Object.assign(msg,{ payload: res }));
59 | }).catch(err => {
60 | if (err.statusCode === 500) {
61 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
62 | node.send({ payload: err });
63 | } else {
64 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
65 | return;
66 | }
67 | });
68 | break;
69 |
70 | case 'create':
71 | // https://docs.docker.com/engine/api/v1.40/#operation/ImageCreate
72 | client.createImage(options)
73 | .then(res => {
74 | node.status({ fill: 'green', shape: 'dot', text: imageId + ' remove' });
75 | node.send(Object.assign(msg,{ payload: res }));
76 | }).catch(err => {
77 | if (err.statusCode === 500) {
78 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
79 | node.send({ payload: err });
80 | } else {
81 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
82 | return;
83 | }
84 | });
85 | break;
86 |
87 | case 'remove':
88 | // https://docs.docker.com/engine/api/v1.40/#operation/ImageRemove
89 | image.remove()
90 | .then(res => {
91 | node.status({ fill: 'green', shape: 'dot', text: imageId + ' remove' });
92 | node.send(Object.assign(msg,{ payload: res }));
93 | }).catch(err => {
94 | if (err.statusCode === 500) {
95 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
96 | node.send({ payload: err });
97 | } else {
98 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
99 | return;
100 | }
101 | });
102 | break;
103 | case 'history':
104 | // https://docs.docker.com/engine/api/v1.40/#operation/ImageHistory
105 | image.history()
106 | .then(res => {
107 | node.status({ fill: 'green', shape: 'dot', text: imageId + ' remove' });
108 | node.send(Object.assign(msg,{ payload: res }));
109 | }).catch(err => {
110 | if (err.statusCode === 500) {
111 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
112 | node.send({ payload: err });
113 | } else {
114 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
115 | return;
116 | }
117 | });
118 | break;
119 | case 'tag':
120 | // https://docs.docker.com/engine/api/v1.40/#operation/ImageTag
121 | let repo = 'bla/bla';
122 | let tag = 'Hello';
123 | image.tag({"repo": repo, "tag": tag})
124 | .then(res => {
125 | node.status({ fill: 'green', shape: 'dot', text: imageId + ' remove' });
126 | node.send(Object.assign(msg,{ payload: res }));
127 | }).catch(err => {
128 | if (err.statusCode === 500) {
129 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
130 | node.send({ payload: err });
131 | } else {
132 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
133 | return;
134 | }
135 | });
136 | break;
137 | case 'push':
138 | // https://docs.docker.com/engine/api/v1.40/#operation/ImagePush
139 | image.push()
140 | .then(res => {
141 | node.status({ fill: 'green', shape: 'dot', text: imageId + ' remove' });
142 | node.send(Object.assign(msg,{ payload: res }));
143 | }).catch(err => {
144 | if (err.statusCode === 500) {
145 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
146 | node.send({ payload: err });
147 | } else {
148 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
149 | return;
150 | }
151 | });
152 | break;
153 | default:
154 | node.error(`Called with an unknown action: ${action}`, msg);
155 | return;
156 | }
157 | }
158 | }
159 |
160 | RED.httpAdmin.post("/imageSearch", RED.auth.needsPermission('flows.write'), function (req, res) {
161 | RED.log.debug("POST /imageSearch");
162 |
163 | const nodeId = req.body.id;
164 | let config = RED.nodes.getNode(nodeId);
165 |
166 | discoverSonos(config, (images) => {
167 | RED.log.debug("GET /imageSearch: " + images.length + " found");
168 | res.json(images);
169 | });
170 | });
171 |
172 | function discoverSonos(config, discoveryCallback) {
173 | let client = config.getClient();
174 | client.listImages({ all: true })
175 | .then(images => discoveryCallback(images))
176 | .catch(err => this.error(err));
177 | }
178 |
179 | RED.nodes.registerType('docker-image-actions', DockerImageAction);
180 | }
181 |
182 |
--------------------------------------------------------------------------------
/src/docker-network-actions.html:
--------------------------------------------------------------------------------
1 |
2 |
74 |
75 |
116 |
117 |
--------------------------------------------------------------------------------
/src/docker-network-actions.ts:
--------------------------------------------------------------------------------
1 | import { Red, Node } from 'node-red';
2 | import { DockerConfiguration } from './docker-configuration';
3 | import * as Dockerode from 'dockerode';
4 |
5 | module.exports = function (RED: Red) {
6 |
7 | function DockerNetworkAction(n: any) {
8 | RED.nodes.createNode(this, n);
9 | let config = RED.nodes.getNode(n.config) as unknown as DockerConfiguration;
10 | let client = config.getClient();
11 | this.on('input', (msg) => {
12 |
13 | let networkId: string = RED.util.evaluateNodeProperty(n.network !== '' ? n.network : '{}', n.networktype, n, msg) || msg.payload.networkId || msg.networkId || undefined;
14 | let action = n.action || msg.action || msg.payload.action || undefined;
15 | let options = RED.util.evaluateNodeProperty(n.options !== '' ? n.options : '{}', n.optionstype, n, msg) || msg.options || msg.payload.options || undefined;
16 |
17 | if (networkId === undefined && !['list', 'prune', 'create'].includes(action)) {
18 | this.error("Network id/name must be provided via configuration or via `msg.network`");
19 | return;
20 | }
21 | this.status({});
22 | executeAction(networkId, options, client, action, this, msg);
23 | });
24 |
25 | async function executeAction(networkId: string, options: any, client: Dockerode, action: string, node: Node, msg) {
26 |
27 | let network = client.getNetwork(networkId);
28 |
29 | switch (action) {
30 |
31 | case 'list':
32 | // https://docs.docker.com/engine/api/v1.40/#operation/NetworkList
33 | client.listNetworks({ all: true })
34 | .then(res => {
35 | node.status({ fill: 'green', shape: 'dot', text: networkId + ' started' });
36 | node.send(Object.assign(msg, { payload: res }));
37 | }).catch(err => {
38 | if (err.statusCode === 400) {
39 | node.error(`Bad parameter: ${err.reason}`, msg);
40 | node.send({ payload: err });
41 | } else if (err.statusCode === 500) {
42 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
43 | node.send({ payload: err });
44 | } else {
45 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
46 | return;
47 | }
48 | });
49 | break;
50 |
51 | case 'inspect':
52 | // https://docs.docker.com/engine/api/v1.40/#operation/NetworkInspect
53 | network.inspect()
54 | .then(res => {
55 | node.status({ fill: 'green', shape: 'dot', text: networkId + ' started' });
56 | node.send(Object.assign(msg, { payload: res }));
57 | }).catch(err => {
58 | if (err.statusCode === 500) {
59 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
60 | node.send({ payload: err });
61 | } else {
62 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
63 | return;
64 | }
65 | });
66 | break;
67 |
68 | case 'remove':
69 | // https://docs.docker.com/engine/api/v1.40/#operation/NetworkRemove
70 | network.remove()
71 | .then(res => {
72 | node.status({ fill: 'green', shape: 'dot', text: networkId + ' remove' });
73 | node.send(Object.assign(msg, { payload: res }));
74 | }).catch(err => {
75 | if (err.statusCode === 500) {
76 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
77 | node.send({ payload: err });
78 | } else {
79 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
80 | return;
81 | }
82 | });
83 | break;
84 |
85 | case 'connect':
86 | // https://docs.docker.com/engine/api/v1.40/#operation/NetworkConnect
87 | network.connect()
88 | .then(res => {
89 | node.status({ fill: 'green', shape: 'dot', text: networkId + ' remove' });
90 | node.send(Object.assign(msg, { payload: res }));
91 | }).catch(err => {
92 | if (err.statusCode === 500) {
93 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
94 | node.send({ payload: err });
95 | } else {
96 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
97 | return;
98 | }
99 | });
100 | break;
101 |
102 | case 'disconnect':
103 | // https://docs.docker.com/engine/api/v1.40/#operation/NetworkDisconnect
104 | network.disconnect()
105 | .then(res => {
106 | node.status({ fill: 'green', shape: 'dot', text: networkId + ' remove' });
107 | node.send(Object.assign(msg, { payload: res }));
108 | }).catch(err => {
109 | if (err.statusCode === 500) {
110 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
111 | node.send({ payload: err });
112 | } else {
113 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
114 | return;
115 | }
116 | });
117 | break;
118 |
119 | case 'prune':
120 | // https://docs.docker.com/engine/api/v1.40/#operation/NetworkPrune
121 | client.pruneNetworks()
122 | .then(res => {
123 | node.status({ fill: 'green', shape: 'dot', text: networkId + ' remove' });
124 | node.send(Object.assign(msg, { payload: res }));
125 | }).catch(err => {
126 | if (err.statusCode === 500) {
127 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
128 | node.send({ payload: err });
129 | } else {
130 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
131 | return;
132 | }
133 | });
134 | break;
135 | case 'create':
136 | let create = false;
137 | try {
138 | await network.inspect();
139 | } catch (err) {
140 | create = true
141 | }
142 | if (create) {
143 | // https://docs.docker.com/engine/api/v1.40/#operation/NetworkCreate
144 | client.createNetwork(Object.assign(options, { 'name': networkId }))
145 | .then(res => {
146 | node.status({ fill: 'green', shape: 'dot', text: networkId + ' created' });
147 | node.send(Object.assign(msg, { payload: res }));
148 | }).catch(err => {
149 | if (err.statusCode === 500) {
150 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
151 | node.send({ payload: err });
152 | } else {
153 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
154 | return;
155 | }
156 | });
157 | }
158 | else {
159 | node.status({ fill: 'green', shape: 'dot', text: networkId + ' already exists' });
160 | node.send(Object.assign(msg, { payload: {} }));
161 | }
162 | break;
163 |
164 | default:
165 | node.error(`Called with an unknown action: ${action}`, msg);
166 | return;
167 | }
168 | }
169 | }
170 |
171 | RED.httpAdmin.post("/networkSearch", RED.auth.needsPermission('flows.write'), function (req, res) {
172 | RED.log.debug("POST /networkSearch");
173 |
174 | const nodeId = req.body.id;
175 | let config = RED.nodes.getNode(nodeId);
176 |
177 | discoverSonos(config, (networks) => {
178 | RED.log.debug("GET /networkSearch: " + networks.length + " found");
179 | res.json(networks);
180 | });
181 | });
182 |
183 | function discoverSonos(config, discoveryCallback) {
184 | let client = config.getClient();
185 | client.listNetworks({ all: true })
186 | .then(networks => discoveryCallback(networks))
187 | .catch(err => this.error(err));
188 | }
189 |
190 | RED.nodes.registerType('docker-network-actions', DockerNetworkAction);
191 | }
192 |
193 |
--------------------------------------------------------------------------------
/src/docker-node-actions.html:
--------------------------------------------------------------------------------
1 |
2 |
73 |
74 |
111 |
112 |
--------------------------------------------------------------------------------
/src/docker-node-actions.ts:
--------------------------------------------------------------------------------
1 | import { Red, Node } from 'node-red';
2 | import { DockerConfiguration } from './docker-configuration';
3 | import * as Dockerode from 'dockerode';
4 |
5 | module.exports = function (RED: Red) {
6 |
7 | function DockerNodeAction(n: any) {
8 | RED.nodes.createNode(this, n);
9 | let config = RED.nodes.getNode(n.config) as unknown as DockerConfiguration;
10 | let client = config.getClient();
11 | this.on('input', (msg) => {
12 |
13 | let nodeId: string = n.node || msg.payload.nodeId || msg.nodeId || undefined;
14 | let action = n.action || msg.action || msg.payload.action || undefined;
15 |
16 | if (nodeId === undefined && !['list', 'prune', 'create'].includes(action)) {
17 | this.error("Node id/name must be provided via configuration or via `msg.node`");
18 | return;
19 | }
20 | this.status({});
21 | executeAction(nodeId, client, action, this, msg, {
22 | options: RED.util.evaluateNodeProperty(n.options !== '' ? n.options : '{}', n.optionstype, n, msg) || {},
23 | });
24 | });
25 |
26 | function executeAction(nodeId: string, client: Dockerode, action: string, node: Node, msg, config: { options: {} }) {
27 |
28 | let nodeClient = client.getNode(nodeId);
29 |
30 | switch (action) {
31 |
32 | case 'list':
33 | // https://docs.docker.com/engine/api/v1.40/#operation/NodeList
34 | client.listNodes({ all: true })
35 | .then(res => {
36 | node.status({ fill: 'green', shape: 'dot', text: nodeId + ' started' });
37 | node.send(Object.assign(msg, { payload: res }));
38 | }).catch(err => {
39 | if (err.statusCode === 400) {
40 | node.error(`Bad parameter: ${err.reason}`, msg);
41 | node.send({ payload: err });
42 | } else if (err.statusCode === 500) {
43 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
44 | node.send({ payload: err });
45 | } else {
46 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
47 | return;
48 | }
49 | });
50 | break;
51 | case 'inspect':
52 | // https://docs.docker.com/engine/api/v1.40/#operation/NodeInspect
53 | nodeClient.inspect()
54 | .then(res => {
55 | node.status({ fill: 'green', shape: 'dot', text: nodeId + ' started' });
56 | node.send(Object.assign(msg, { payload: res }));
57 | }).catch(err => {
58 | if (err.statusCode === 500) {
59 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
60 | node.send({ payload: err });
61 | } else {
62 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
63 | return;
64 | }
65 | });
66 | break;
67 | case 'remove':
68 | // https://docs.docker.com/engine/api/v1.40/#operation/NodeDelete
69 | nodeClient.remove()
70 | .then(res => {
71 | node.status({ fill: 'green', shape: 'dot', text: nodeId + ' stopped' });
72 | node.send(Object.assign(msg, { payload: res }));
73 | }).catch(err => {
74 | if (err.statusCode === 500) {
75 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
76 | node.send({ payload: err });
77 | } else {
78 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
79 | return;
80 | }
81 | });
82 | break;
83 | case 'update':
84 | // https://docs.docker.com/engine/api/v1.40/#operation/NodeUpdate
85 | nodeClient.update(config.options)
86 | .then(res => {
87 | node.status({ fill: 'green', shape: 'dot', text: nodeId + ' restarted' });
88 | node.send(Object.assign(msg, { payload: res }));
89 | }).catch(err => {
90 | if (err.statusCode === 500) {
91 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
92 | node.send({ payload: err });
93 | } else {
94 | node.error(`System Error: [${err.statusCode}] ${err}`, msg);
95 | return;
96 | }
97 | });
98 | break;
99 |
100 |
101 | default:
102 | node.error(`Called with an unknown action: ${action}`, msg);
103 | return;
104 | }
105 | }
106 | }
107 |
108 |
109 | RED.httpAdmin.post("/nodeSearch", RED.auth.needsPermission('flows.write'), function (req, res) {
110 | RED.log.debug("POST /nodeSearch");
111 |
112 | const nodeId = req.body.id;
113 | let config = RED.nodes.getNode(nodeId);
114 |
115 | discoverSonos(config, (nodes) => {
116 | RED.log.debug("GET /nodeSearch: " + nodes.length + " found");
117 | res.json(nodes);
118 | });
119 | });
120 |
121 | function discoverSonos(config, discoveryCallback) {
122 | let client = config.getClient();
123 | client.listNodes({ all: true })
124 | .then(nodes => discoveryCallback(nodes))
125 | .catch(err => this.error(err));
126 | }
127 |
128 |
129 | RED.nodes.registerType('docker-node-actions', DockerNodeAction);
130 | }
131 |
132 |
--------------------------------------------------------------------------------
/src/docker-plugin-actions.html:
--------------------------------------------------------------------------------
1 |
2 |
71 |
72 |
105 |
106 |
--------------------------------------------------------------------------------
/src/docker-plugin-actions.ts:
--------------------------------------------------------------------------------
1 | import { Red, Node } from 'node-red';
2 | import { DockerConfiguration } from './docker-configuration';
3 | import * as Dockerode from 'dockerode';
4 |
5 | module.exports = function (RED: Red) {
6 |
7 | function DockerPluginAction(n: any) {
8 | RED.nodes.createNode(this, n);
9 | let config = RED.nodes.getNode(n.config) as unknown as DockerConfiguration;
10 | let client = config.getClient();
11 | this.on('input', (msg) => {
12 |
13 | let pluginId: string = n.plugin || msg.payload.pluginId || msg.pluginId || undefined;
14 | let action = n.action || msg.action || msg.payload.action || undefined;
15 | let options = n.options || msg.options || msg.payload.options || undefined;
16 |
17 | if (pluginId === undefined && !['list', 'prune', 'create'].includes(action)) {
18 | this.error("Plugin id/name must be provided via configuration or via `msg.plugin`");
19 | return;
20 | }
21 | this.status({});
22 | executeAction(pluginId, options, client, action, this,msg);
23 | });
24 |
25 | function executeAction(pluginId: string, options: any, client: Dockerode, action: string, node: Node ,msg) {
26 |
27 | let remote ={};
28 | let plugin = client.getPlugin(pluginId, remote);
29 |
30 | switch (action) {
31 |
32 | case 'list':
33 | // https://docs.docker.com/engine/api/v1.40/#operation/PluginList
34 | client.listPlugins({ all: true })
35 | .then(res => {
36 | node.status({ fill: 'green', shape: 'dot', text: pluginId + ' started' });
37 | node.send(Object.assign(msg,{ payload: res }));
38 | }).catch(err => {
39 | if (err.statusCode === 400) {
40 | node.error(`Bad parameter: ${err.reason}`, msg);
41 | node.send({ payload: err });
42 | } else if (err.statusCode === 500) {
43 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
44 | node.send({ payload: err });
45 | } else {
46 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
47 | return;
48 | }
49 | });
50 | break;
51 |
52 |
53 | case 'inspect':
54 | // https://docs.docker.com/engine/api/v1.40/#operation/PluginInspect
55 | plugin.inspect()
56 | .then(res => {
57 | node.status({ fill: 'green', shape: 'dot', text: pluginId + ' started' });
58 | node.send(Object.assign(msg,{ payload: res }));
59 | }).catch(err => {
60 | if (err.statusCode === 500) {
61 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
62 | node.send({ payload: err });
63 | } else {
64 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
65 | return;
66 | }
67 | });
68 | break;
69 | case 'remove':
70 | // https://docs.docker.com/engine/api/v1.40/#operation/PluginDelete
71 | plugin.remove()
72 | .then(res => {
73 | node.status({ fill: 'green', shape: 'dot', text: pluginId + ' remove' });
74 | node.send(Object.assign(msg,{ payload: res }));
75 | }).catch(err => {
76 | if (err.statusCode === 500) {
77 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
78 | node.send({ payload: err });
79 | } else {
80 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
81 | return;
82 | }
83 | });
84 | break;
85 |
86 | case 'enable':
87 | // https://docs.docker.com/engine/api/v1.40/#operation/PluginEndable
88 | plugin.enable()
89 | .then(res => {
90 | node.status({ fill: 'green', shape: 'dot', text: pluginId + ' remove' });
91 | node.send(Object.assign(msg,{ payload: res }));
92 | }).catch(err => {
93 | if (err.statusCode === 500) {
94 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
95 | node.send({ payload: err });
96 | } else {
97 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
98 | return;
99 | }
100 | });
101 | break;
102 |
103 | case 'disable':
104 | // https://docs.docker.com/engine/api/v1.40/#operation/PluginDisable
105 | plugin.disable()
106 | .then(res => {
107 | node.status({ fill: 'green', shape: 'dot', text: pluginId + ' remove' });
108 | node.send(Object.assign(msg,{ payload: res }));
109 | }).catch(err => {
110 | if (err.statusCode === 500) {
111 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
112 | node.send({ payload: err });
113 | } else {
114 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
115 | return;
116 | }
117 | });
118 | break;
119 |
120 | case 'configure':
121 | // https://docs.docker.com/engine/api/v1.40/#operation/PluginConfigue
122 | plugin.configure()
123 | .then(res => {
124 | node.status({ fill: 'green', shape: 'dot', text: pluginId + ' remove' });
125 | node.send(Object.assign(msg,{ payload: res }));
126 | }).catch(err => {
127 | if (err.statusCode === 500) {
128 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
129 | node.send({ payload: err });
130 | } else {
131 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
132 | return;
133 | }
134 | });
135 | break;
136 |
137 |
138 | case 'privileges':
139 | // https://docs.docker.com/engine/api/v1.40/#operation/PluginPrivledges
140 | plugin.privileges()
141 | .then(res => {
142 | node.status({ fill: 'green', shape: 'dot', text: pluginId + ' remove' });
143 | node.send(Object.assign(msg,{ payload: res }));
144 | }).catch(err => {
145 | if (err.statusCode === 500) {
146 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
147 | node.send({ payload: err });
148 | } else {
149 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
150 | return;
151 | }
152 | });
153 | break;
154 | case 'push':
155 | // https://docs.docker.com/engine/api/v1.40/#operation/PluginPush
156 | plugin.push()
157 | .then(res => {
158 | node.status({ fill: 'green', shape: 'dot', text: pluginId + ' remove' });
159 | node.send(Object.assign(msg,{ payload: res }));
160 | }).catch(err => {
161 | if (err.statusCode === 500) {
162 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
163 | node.send({ payload: err });
164 | } else {
165 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
166 | return;
167 | }
168 | });
169 | break;
170 |
171 | case 'pull':
172 | // https://docs.docker.com/engine/api/v1.40/#operation/PluginPull
173 | plugin.pull(options)
174 | .then(res => {
175 | node.status({ fill: 'green', shape: 'dot', text: pluginId + ' remove' });
176 | node.send(Object.assign(msg,{ payload: res }));
177 | }).catch(err => {
178 | if (err.statusCode === 500) {
179 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
180 | node.send({ payload: err });
181 | } else {
182 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
183 | return;
184 | }
185 | });
186 | break;
187 |
188 | case 'upgrade':
189 | // https://docs.docker.com/engine/api/v1.40/#operation/PluginUpgrade
190 | plugin.upgrade(options)
191 | .then(res => {
192 | node.status({ fill: 'green', shape: 'dot', text: pluginId + ' remove' });
193 | node.send(Object.assign(msg,{ payload: res }));
194 | }).catch(err => {
195 | if (err.statusCode === 500) {
196 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
197 | node.send({ payload: err });
198 | } else {
199 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
200 | return;
201 | }
202 | });
203 | break;
204 |
205 | case 'create':
206 | // https://docs.docker.com/engine/api/v1.40/#operation/PluginCreate
207 | client.createPlugin(options)
208 | .then(res => {
209 | node.status({ fill: 'green', shape: 'dot', text: pluginId + ' remove' });
210 | node.send(Object.assign(msg,{ payload: res }));
211 | }).catch(err => {
212 | if (err.statusCode === 500) {
213 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
214 | node.send({ payload: err });
215 | } else {
216 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
217 | return;
218 | }
219 | });
220 | break;
221 |
222 | default:
223 | node.error(`Called with an unknown action: ${action}`, msg);
224 | return;
225 | }
226 | }
227 | }
228 |
229 | RED.httpAdmin.post("/pluginSearch", RED.auth.needsPermission('flows.write'), function (req, res) {
230 | RED.log.debug("POST /pluginSearch");
231 |
232 | const nodeId = req.body.id;
233 | let config = RED.nodes.getNode(nodeId);
234 |
235 | discoverSonos(config, (plugins) => {
236 | RED.log.debug("GET /pluginSearch: " + plugins.length + " found");
237 | res.json(plugins);
238 | });
239 | });
240 |
241 | function discoverSonos(config, discoveryCallback) {
242 | let client = config.getClient();
243 | client.listPlugins({ all: true })
244 | .then(plugins => discoveryCallback(plugins))
245 | .catch(err => this.error(err));
246 | }
247 |
248 | RED.nodes.registerType('docker-plugin-actions', DockerPluginAction);
249 | }
250 |
251 |
--------------------------------------------------------------------------------
/src/docker-secret-actions.html:
--------------------------------------------------------------------------------
1 |
2 |
58 |
59 |
86 |
87 |
--------------------------------------------------------------------------------
/src/docker-secret-actions.ts:
--------------------------------------------------------------------------------
1 | import { Red, Node } from 'node-red';
2 | import { DockerConfiguration } from './docker-configuration';
3 | import * as Dockerode from 'dockerode';
4 |
5 | module.exports = function (RED: Red) {
6 |
7 | function DockerSecretAction(n: any) {
8 | RED.nodes.createNode(this, n);
9 | let config = RED.nodes.getNode(n.config) as unknown as DockerConfiguration;
10 | let client = config.getClient();
11 | this.on('input', (msg) => {
12 |
13 | let secretId: string = n.secret || msg.payload.secretId || msg.secretId || undefined;
14 | //TODO: make this disabled by default
15 | let action = n.action || msg.action || msg.payload.action || undefined;
16 | let options = n.options || msg.options || msg.payload.options || undefined;
17 | if (secretId === undefined && !['list', 'prune', 'create'].includes(action)) {
18 | this.error("Secret id/name must be provided via configuration or via `msg.secret`");
19 | return;
20 | }
21 | this.status({});
22 | executeAction(secretId, options, client, action, this,msg);
23 | });
24 |
25 | function executeAction(secretId: string, options: any, client: Dockerode, action: string, node: Node,msg) {
26 |
27 | let secret = client.getSecret(secretId);
28 |
29 | switch (action) {
30 |
31 | case 'list':
32 | // https://docs.docker.com/engine/api/v1.40/#operation/SecretList
33 | client.listSecrets({ all: true })
34 | .then(res => {
35 | node.status({ fill: 'green', shape: 'dot', text: secretId + ' started' });
36 | node.send(Object.assign(msg,{ payload: res }));
37 | }).catch(err => {
38 | if (err.statusCode === 400) {
39 | node.error(`Bad parameter: ${err.reason}`, msg);
40 | node.send({ payload: err });
41 | } else if (err.statusCode === 500) {
42 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
43 | node.send({ payload: err });
44 | } else {
45 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
46 | return;
47 | }
48 | });
49 | break;
50 |
51 | case 'inspect':
52 | // https://docs.docker.com/engine/api/v1.40/#operation/SecretInspect
53 | secret.inspect()
54 | .then(res => {
55 | node.status({ fill: 'green', shape: 'dot', text: secretId + ' started' });
56 | node.send(Object.assign(msg,{ payload: res }));
57 | }).catch(err => {
58 | if (err.statusCode === 500) {
59 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
60 | node.send({ payload: err });
61 | } else {
62 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
63 | return;
64 | }
65 | });
66 | break;
67 |
68 | case 'remove':
69 | // https://docs.docker.com/engine/api/v1.40/#operation/SecretDelete
70 | secret.remove()
71 | .then(res => {
72 | node.status({ fill: 'green', shape: 'dot', text: secretId + ' stopped' });
73 | node.send(Object.assign(msg,{ payload: res }));
74 | }).catch(err => {
75 | if (err.statusCode === 500) {
76 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
77 | node.send({ payload: err });
78 | } else {
79 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
80 | return;
81 | }
82 | });
83 | break;
84 |
85 | case 'update':
86 | // https://docs.docker.com/engine/api/v1.40/#operation/SecretUpdate
87 | secret.update()
88 | .then(res => {
89 | node.status({ fill: 'green', shape: 'dot', text: secretId + ' restarted' });
90 | node.send(Object.assign(msg,{ payload: res }));
91 | }).catch(err => {
92 | if (err.statusCode === 500) {
93 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
94 | node.send({ payload: err });
95 | } else {
96 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
97 | return;
98 | }
99 | });
100 | break;
101 |
102 | case 'create':
103 | // https://docs.docker.com/engine/api/v1.40/#operation/SecretCreate
104 | client.createSecret(options)
105 | .then(res => {
106 | node.status({ fill: 'green', shape: 'dot', text: secretId + ' restarted' });
107 | node.send(Object.assign(msg,{ payload: res }));
108 | }).catch(err => {
109 | if (err.statusCode === 500) {
110 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
111 | node.send({ payload: err });
112 | } else {
113 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
114 | return;
115 | }
116 | });
117 | break;
118 | default:
119 | node.error(`Called with an unknown action: ${action}`, msg);
120 | return;
121 | }
122 | }
123 | }
124 |
125 | RED.httpAdmin.post("/secretSearch", RED.auth.needsPermission('flows.write'), function (req, res) {
126 | RED.log.debug("POST /secretSearch");
127 |
128 | const nodeId = req.body.id;
129 | let config = RED.nodes.getNode(nodeId);
130 |
131 | discoverSonos(config, (secrets) => {
132 | RED.log.debug("GET /secretSearch: " + secrets.length + " found");
133 | res.json(secrets);
134 | });
135 | });
136 |
137 | function discoverSonos(config, discoveryCallback) {
138 | let client = config.getClient();
139 | client.listSecrets({ all: true })
140 | .then(secrets => discoveryCallback(secrets))
141 | .catch(err => this.error(err));
142 | }
143 |
144 | RED.nodes.registerType('docker-secret-actions', DockerSecretAction);
145 | }
146 |
147 |
--------------------------------------------------------------------------------
/src/docker-service-actions.html:
--------------------------------------------------------------------------------
1 |
2 |
68 |
69 |
102 |
103 |
--------------------------------------------------------------------------------
/src/docker-service-actions.ts:
--------------------------------------------------------------------------------
1 | import { Red, Node } from 'node-red';
2 | import { DockerConfiguration } from './docker-configuration';
3 | import * as Dockerode from 'dockerode';
4 |
5 | module.exports = function (RED: Red) {
6 |
7 | function DockerServiceAction(n: any) {
8 | RED.nodes.createNode(this, n);
9 | let config = RED.nodes.getNode(n.config) as unknown as DockerConfiguration;
10 | let client = config.getClient();
11 | this.on('input', (msg) => {
12 |
13 | let serviceId: string = n.service || msg.payload?.serviceId || msg.serviceId || undefined;
14 | if(serviceId === undefined){
15 | serviceId = n.serviceName || msg.payload?.serviceName || msg.serviceName || undefined;
16 | }
17 | let action = n.action || msg.action || msg.payload?.action || undefined;
18 | if (serviceId === undefined && !['list', 'prune', 'create'].includes(action)) {
19 | this.error("Service id/name must be provided via configuration or via `msg.service`");
20 | return;
21 | }
22 | let options = RED.util.evaluateNodeProperty(n.options, n.optionstype, n, msg) || msg.options || msg.options || msg.payload?.options || undefined;
23 |
24 | this.status({});
25 | executeAction(serviceId, options, client, action, this,msg);
26 | });
27 |
28 | function executeAction(serviceId: string, options: any ,client: Dockerode, action: string, node: Node,msg) {
29 |
30 | let service = client.getService(serviceId);
31 |
32 | switch (action) {
33 |
34 | case 'list':
35 | // https://docs.docker.com/engine/api/v1.40/#operation/ServiceList
36 | client.listServices({ all: true })
37 | .then(res => {
38 | node.status({ fill: 'green', shape: 'dot', text: serviceId + ' started' });
39 | node.send(Object.assign(msg,{ payload: res }));
40 | }).catch(err => {
41 | if (err.statusCode === 400) {
42 | node.error(`Bad parameter: ${err.reason}`, msg);
43 | node.send({ payload: err });
44 | } else if (err.statusCode === 500) {
45 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
46 | node.send({ payload: err });
47 | } else {
48 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
49 | return;
50 | }
51 | });
52 | break;
53 |
54 | case 'create':
55 | // https://docs.docker.com/engine/api/v1.40/#operation/ServiceCreate
56 | client.createService(options)
57 | .then(res => {
58 | node.status({ fill: 'green', shape: 'dot', text: serviceId + ' started' });
59 | node.send(Object.assign(msg,{ payload: res }));
60 | }).catch(err => {
61 | if (err.statusCode === 400) {
62 | node.error(`Bad parameter: ${err.reason}`, msg);
63 | node.send({ payload: err });
64 | } else if (err.statusCode === 500) {
65 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
66 | node.send({ payload: err });
67 | } else {
68 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
69 | return;
70 | }
71 | });
72 | break;
73 |
74 | case 'inspect':
75 | // https://docs.docker.com/engine/api/v1.40/#operation/ServiceInspect
76 | service.inspect()
77 | .then(res => {
78 | node.status({ fill: 'green', shape: 'dot', text: 'Inspected: ' + serviceId });
79 | node.send(Object.assign(msg,{ payload: res }));
80 | }).catch(err => {
81 | if (err.statusCode === 500) {
82 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
83 | node.send({ payload: err });
84 | } else {
85 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
86 | return;
87 | }
88 | });
89 | break;
90 |
91 | case 'update':
92 | // https://docs.docker.com/engine/api/v1.40/#operation/ServiceUpdate
93 | service.update(options)
94 | .then(res => {
95 | node.status({ fill: 'green', shape: 'dot', text: 'Updated: ' + serviceId });
96 | node.send(Object.assign(msg,{ payload: res }));
97 | }).catch(err => {
98 | if (err.statusCode === 500) {
99 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
100 | node.send({ payload: err });
101 | } else {
102 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
103 | return;
104 | }
105 | });
106 | break;
107 |
108 | case 'remove':
109 | // https://docs.docker.com/engine/api/v1.40/#operation/ServiceDelete
110 | service.remove()
111 | .then(res => {
112 | node.status({ fill: 'green', shape: 'dot', text: 'Update: ' + serviceId });
113 | node.send(Object.assign(msg,{ payload: res }));
114 | }).catch(err => {
115 | if (err.statusCode === 500) {
116 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
117 | node.send({ payload: err });
118 | } else {
119 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
120 | return;
121 | }
122 | });
123 | break;
124 | //Todo: tail
125 | case 'logs':
126 | // https://docs.docker.com/engine/api/v1.40/#operation/ServiceLogs
127 | service.logs()
128 | .then(res => {
129 | node.status({ fill: 'green', shape: 'dot', text: 'Logging: ' + serviceId });
130 | node.send(Object.assign(msg,{ payload: res }));
131 | }).catch(err => {
132 | if (err.statusCode === 500) {
133 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
134 | node.send({ payload: err });
135 | } else {
136 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
137 | return;
138 | }
139 | });
140 | break;
141 |
142 | default:
143 | node.error(`Called with an unknown action: ${action}`, msg);
144 | return;
145 | }
146 | }
147 | }
148 |
149 | RED.httpAdmin.post("/serviceSearch", RED.auth.needsPermission('flows.write'), function (req, res) {
150 | RED.log.debug("POST /serviceSearch");
151 |
152 | const nodeId = req.body.id;
153 | let config = RED.nodes.getNode(nodeId);
154 |
155 | discoverSonos(config, (services) => {
156 | RED.log.debug("GET /serviceSearch: " + services.length + " found");
157 | res.json(services);
158 | });
159 | });
160 |
161 | function discoverSonos(config, discoveryCallback) {
162 | let client = config.getClient();
163 | client.listServices({ all: true })
164 | .then(services => discoveryCallback(services))
165 | .catch(err => this.error(err));
166 | }
167 |
168 | RED.nodes.registerType('docker-service-actions', DockerServiceAction);
169 | }
170 |
171 |
--------------------------------------------------------------------------------
/src/docker-swarm-actions.html:
--------------------------------------------------------------------------------
1 |
2 |
42 |
43 |
63 |
64 |
--------------------------------------------------------------------------------
/src/docker-swarm-actions.ts:
--------------------------------------------------------------------------------
1 | import { Red, Node } from 'node-red';
2 | import { DockerConfiguration } from './docker-configuration';
3 | import * as Dockerode from 'dockerode';
4 |
5 | module.exports = function (RED: Red) {
6 |
7 | function DockerSwarmAction(n: any) {
8 | RED.nodes.createNode(this, n);
9 | let config = RED.nodes.getNode(n.config) as unknown as DockerConfiguration;
10 | let client = config.getClient();
11 | this.on('input', (msg) => {
12 |
13 |
14 | let action = n.action || msg.action || msg.payload.action || undefined;
15 | let options = n.options || msg.options|| msg.options || msg.payload.options || undefined;
16 |
17 | this.status({});
18 | executeAction( options, client, action, this,msg);
19 | });
20 |
21 | function executeAction(options: any, client: Dockerode, action: string, node: Node,msg) {
22 |
23 | let swarm = client;
24 |
25 | switch (action) {
26 | case 'inspect':
27 | // https://docs.docker.com/engine/api/v1.40/#operation/SwarmInspect
28 | swarm.swarmInspect()
29 | .then(res => {
30 | node.status({ fill: 'green', shape: 'dot', text: ' started' });
31 | node.send(Object.assign(msg,{ payload: res }));
32 | }).catch(err => {
33 | if (err.statusCode === 500) {
34 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
35 | node.send({ payload: err });
36 | } else {
37 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
38 | return;
39 | }
40 | });
41 | break;
42 |
43 | case 'update':
44 | // https://docs.docker.com/engine/api/v1.40/#operation/SwarmUpdate
45 | swarm.swarmUpdate(options)
46 | .then(res => {
47 | node.status({ fill: 'green', shape: 'dot', text: ' stopped' });
48 | node.send(Object.assign(msg,{ payload: res }));
49 | }).catch(err => {
50 | if (err.statusCode === 500) {
51 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
52 | node.send({ payload: err });
53 | } else {
54 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
55 | return;
56 | }
57 | });
58 | break;
59 |
60 | case 'join':
61 | // https://docs.docker.com/engine/api/v1.40/#operation/SwarmJoin
62 | swarm.swarmJoin(options)
63 | .then(res => {
64 | node.status({ fill: 'green', shape: 'dot', text: ' remove' });
65 | node.send(Object.assign(msg,{ payload: res }));
66 | }).catch(err => {
67 | if (err.statusCode === 500) {
68 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
69 | node.send({ payload: err });
70 | } else {
71 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
72 | return;
73 | }
74 | });
75 | break;
76 | case 'leave':
77 | // https://docs.docker.com/engine/api/v1.40/#operation/SwarmLeave
78 | swarm.swarmLeave(options)
79 | .then(res => {
80 | node.status({ fill: 'green', shape: 'dot', text: ' remove' });
81 | node.send(Object.assign(msg,{ payload: res }));
82 | }).catch(err => {
83 | if (err.statusCode === 500) {
84 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
85 | node.send({ payload: err });
86 | } else {
87 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
88 | return;
89 | }
90 | });
91 | break;
92 |
93 | case 'init':
94 | // https://docs.docker.com/engine/api/v1.40/#operation/SwarmInit
95 | swarm.swarmInit(options)
96 | .then(res => {
97 | node.status({ fill: 'green', shape: 'dot', text: ' remove' });
98 | node.send(Object.assign(msg,{ payload: res }));
99 | }).catch(err => {
100 | if (err.statusCode === 500) {
101 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
102 | node.send({ payload: err });
103 | }else if (err.statusCode === 400) {
104 | node.warn(`Unable to init swarm. Bad payload.`);
105 | node.send({ payload: err });
106 | } else {
107 | node.error(`Error init swarm: [${err.statusCode}] ${err.reason}`, msg);
108 | return;
109 | }
110 | });
111 | break;
112 | default:
113 | node.error(`Called with an unknown action: ${action}`, msg);
114 | return;
115 | }
116 | }
117 | }
118 |
119 | RED.nodes.registerType('docker-swarm-actions', DockerSwarmAction);
120 | }
121 |
122 |
--------------------------------------------------------------------------------
/src/docker-task-actions.html:
--------------------------------------------------------------------------------
1 |
2 |
56 |
57 |
82 |
83 |
--------------------------------------------------------------------------------
/src/docker-task-actions.ts:
--------------------------------------------------------------------------------
1 | import { Red, Node } from 'node-red';
2 | import { DockerConfiguration } from './docker-configuration';
3 | import * as Dockerode from 'dockerode';
4 |
5 | module.exports = function (RED: Red) {
6 |
7 | function DockerTaskAction(n: any) {
8 | RED.nodes.createNode(this, n);
9 | let config = RED.nodes.getNode(n.config) as unknown as DockerConfiguration;
10 | let client = config.getClient();
11 | this.on('input', (msg) => {
12 |
13 | let taskId: string = n.task || msg.payload.taskId || msg.taskId || undefined;
14 | let action = n.action || msg.action || msg.payload.action || undefined;
15 |
16 |
17 | if (taskId === undefined && !['list', 'prune', 'create'].includes(action)) {
18 | this.error("Task id/name must be provided via configuration or via `msg.task`");
19 | return;
20 | }
21 | this.status({});
22 | executeAction(taskId, client, action, this,msg);
23 | });
24 |
25 | function executeAction(taskId: string, client: Dockerode, action: string, node: Node,msg) {
26 |
27 | let task = client.getTask(taskId);
28 |
29 | switch (action) {
30 |
31 |
32 |
33 | case 'list':
34 | // https://docs.docker.com/engine/api/v1.40/#operation/TaskList
35 | client.listTasks({ all: true })
36 | .then(res => {
37 | node.status({ fill: 'green', shape: 'dot', text: taskId + ' started' });
38 | node.send(Object.assign(msg,{ payload: res }));
39 | }).catch(err => {
40 | if (err.statusCode === 400) {
41 | node.error(`Bad parameter: ${err.reason}`, msg);
42 | node.send({ payload: err });
43 | } else if (err.statusCode === 500) {
44 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
45 | node.send({ payload: err });
46 | } else {
47 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
48 | return;
49 | }
50 | });
51 | break;
52 |
53 | case 'inspect':
54 | // https://docs.docker.com/engine/api/v1.40/#operation/TaskInspect
55 | task.inspect()
56 | .then(res => {
57 | node.status({ fill: 'green', shape: 'dot', text: taskId + ' started' });
58 | node.send(Object.assign(msg,{ payload: res }));
59 | }).catch(err => {
60 | if (err.statusCode === 500) {
61 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
62 | node.send({ payload: err });
63 | } else {
64 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
65 | return;
66 | }
67 | });
68 | break;
69 | /*
70 | case 'log':
71 | // https://docs.docker.com/engine/api/v1.40/#operation/Session
72 | task.logs(options)
73 | .then(res => {
74 | node.status({ fill: 'green', shape: 'dot', text: taskId + ' started' });
75 | node.send(Object.assign(msg,{ payload: res }));
76 | }).catch(err => {
77 | if (err.statusCode === 500) {
78 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
79 | node.send({ payload: err });
80 | } else {
81 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
82 | return;
83 | }
84 | });
85 | break;
86 | */
87 | default:
88 | node.error(`Called with an unknown action: ${action}`, msg);
89 | return;
90 | }
91 | }
92 | }
93 |
94 | RED.httpAdmin.post("/taskSearch", RED.auth.needsPermission('flows.write'), function (req, res) {
95 | RED.log.debug("POST /taskSearch");
96 |
97 | const nodeId = req.body.id;
98 | let config = RED.nodes.getNode(nodeId);
99 |
100 | discoverSonos(config, (tasks) => {
101 | RED.log.debug("GET /taskSearch: " + tasks.length + " found");
102 | res.json(tasks);
103 | });
104 | });
105 |
106 | function discoverSonos(config, discoveryCallback) {
107 | let client = config.getClient();
108 | client.listTasks({ all: true })
109 | // .then(tasks => console.log(tasks))
110 | .then(tasks => discoveryCallback(tasks))
111 | .catch(err => this.error(err));
112 | }
113 |
114 | RED.nodes.registerType('docker-task-actions', DockerTaskAction);
115 | }
116 |
117 |
--------------------------------------------------------------------------------
/src/docker-volume-actions.html:
--------------------------------------------------------------------------------
1 |
2 |
72 |
73 |
113 |
114 |
--------------------------------------------------------------------------------
/src/docker-volume-actions.ts:
--------------------------------------------------------------------------------
1 | import { Red, Node } from 'node-red';
2 | import { DockerConfiguration } from './docker-configuration';
3 | import * as Dockerode from 'dockerode';
4 |
5 |
6 | module.exports = function (RED: Red) {
7 |
8 | function DockerVolumeAction(n: any) {
9 | RED.nodes.createNode(this, n);
10 | let config = RED.nodes.getNode(n.config) as unknown as DockerConfiguration;
11 | let client = config.getClient();
12 | this.on('input', (msg, send, done) => {
13 |
14 | let volumeId: string = RED.util.evaluateNodeProperty(n.volume, n.volumetype, n, msg) || msg.payload.volumeId || msg.volumeId || undefined;
15 | let action = n.action || msg.action || msg.payload.action || undefined;
16 | let options = RED.util.evaluateNodeProperty(n.options, n.optionstype, n, msg) || msg.options || msg.payload.options || undefined;
17 | if (volumeId === undefined && !['list', 'prune', 'create'].includes(action)) {
18 | this.error("Volume id/name must be provided via configuration or via `msg.volume`");
19 | return;
20 | }
21 | this.status({});
22 | executeAction(volumeId, options, client, action, this, msg, send, done);
23 | });
24 |
25 | function executeAction(volumeId: string, options: any, client: Dockerode, action: string, node: Node, msg, send, done) {
26 |
27 | let volume = client.getVolume(volumeId);
28 |
29 | switch (action) {
30 |
31 | case 'list':
32 | // https://docs.docker.com/engine/api/v1.40/#operation/VolumeList
33 | client.listVolumes({ all: true })
34 | .then(res => {
35 | node.status({ fill: 'green', shape: 'dot', text: volumeId + ' started' });
36 | node.send(Object.assign(msg, { payload: res }));
37 | }).catch(err => {
38 | if (err.statusCode === 400) {
39 | node.error(`Bad parameter: ${err.reason}`, msg);
40 | node.send({ payload: err });
41 | } else if (err.statusCode === 500) {
42 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
43 | node.send({ payload: err });
44 | } else {
45 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
46 | return;
47 | }
48 | });
49 | break;
50 |
51 | case 'inspect':
52 | // https://docs.docker.com/engine/api/v1.40/#operation/VolumeInspect
53 | volume.inspect()
54 | .then(res => {
55 | node.status({ fill: 'green', shape: 'dot', text: volumeId + ' started' });
56 | node.send(Object.assign(msg, { payload: res }));
57 | }).catch(err => {
58 | // 404 No such volume
59 | if (err.statusCode === 500) {
60 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
61 | node.send({ payload: err });
62 | } else {
63 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
64 | return;
65 | }
66 | });
67 | break;
68 | case 'remove':
69 | // https://docs.docker.com/engine/api/v1.40/#operation/VolumeDelete
70 | volume.remove()
71 | .then(res => {
72 | node.status({ fill: 'green', shape: 'dot', text: volumeId + ' stopped' });
73 | node.send(Object.assign(msg, { payload: res }));
74 | }).catch(err => {
75 | if (err.statusCode === 404) {
76 | node.error(`No such volume or volume driver`, msg);
77 | node.send({ payload: err });
78 | } else if (err.statusCode === 409) {
79 | node.error(`Volume is in use and cannot be removed`, msg);
80 | node.send({ payload: err });
81 | } else if (err.statusCode === 500) {
82 | node.error(`Server error: [${err.statusCode}] ${err.reason}`, msg);
83 | node.send({ payload: err });
84 | } else {
85 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
86 | return;
87 | }
88 | });
89 | break;
90 | case 'create':
91 | // https://docs.docker.com/engine/api/v1.40/#operation/VolumeCreate
92 | options.name = volumeId;
93 | client.createVolume(options).then(res => {
94 | node.status({ fill: 'green', shape: 'dot', text: volumeId + ' created' });
95 | send(Object.assign(msg, { payload: res }));
96 | done();
97 | }).catch(err => {
98 | if (err.statusCode === 500) {
99 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
100 | node.send({ payload: err });
101 | } else {
102 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
103 | return;
104 | }
105 | });
106 | break;
107 |
108 | case 'prune':
109 | // https://docs.docker.com/engine/api/v1.40/#operation/VolumePrune
110 | client.pruneVolumes()
111 | .then(res => {
112 | node.status({ fill: 'green', shape: 'dot', text: volumeId + ' stopped' });
113 | node.send(Object.assign(msg, { payload: res }));
114 | }).catch(err => {
115 | if (err.statusCode === 500) {
116 | node.error(`Server Error: [${err.statusCode}] ${err.reason}`, msg);
117 | node.send({ payload: err });
118 | } else {
119 | node.error(`System Error: [${err.statusCode}] ${err.reason}`, msg);
120 | return;
121 | }
122 | });
123 | break;
124 |
125 | default:
126 | node.error(`Called with an unknown action: ${action}`, msg);
127 | return;
128 | }
129 | }
130 | }
131 |
132 | RED.httpAdmin.post("/volumeSearch", RED.auth.needsPermission('flows.write'), function (req, res) {
133 | RED.log.debug("POST /volumeSearch");
134 |
135 | const nodeId = req.body.id;
136 | let config = RED.nodes.getNode(nodeId);
137 |
138 | discoverSonos(config, (volumes) => {
139 | RED.log.debug("GET /volumeSearch: " + volumes.length + " found");
140 | res.json(volumes);
141 | });
142 | });
143 |
144 | function discoverSonos(config, discoveryCallback) {
145 | let client = config.getClient();
146 | client.listVolumes({ all: true })
147 | // .then(volumes => console.log(volumes))
148 | .then(volumes => discoveryCallback(volumes))
149 | .catch(err => this.error(err));
150 | }
151 |
152 | RED.nodes.registerType('docker-volume-actions', DockerVolumeAction);
153 | }
154 |
155 |
--------------------------------------------------------------------------------
/src/icons/docker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naimo84/node-red-contrib-dockerode/d04142772dfd7e101a86e9dbd02710812169e840/src/icons/docker.png
--------------------------------------------------------------------------------
/test/docker-container-actions_spec.js:
--------------------------------------------------------------------------------
1 | var should = require("should");
2 | var helper = require("node-red-node-test-helper");
3 |
4 | helper.init(require.resolve('node-red'));
5 |
6 | describe('Container Actions Node', function () {
7 |
8 | beforeEach(function (done) {
9 | helper.startServer(done);
10 | });
11 |
12 | afterEach(function (done) {
13 | helper.unload().then(function () {
14 | helper.stopServer(done);
15 | });
16 | });
17 |
18 | it('should be loaded', function (done) {
19 | var flow = [
20 | { id: "c1", type: "docker-configuration" },
21 | { id: "n1", type: "docker-container-actions", config: "c1" }
22 | ];
23 | var dockerContainersNode = require("../dist/docker-container-actions.js");
24 | var dockerConfigNode = require("../dist/docker-configuration.js");
25 |
26 |
27 |
28 | helper.load([dockerConfigNode, dockerContainersNode], flow, function () {
29 | var n1 = helper.getNode("n1");
30 | n1.should.have.property('type', 'docker-container-actions');
31 | done();
32 | });
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/test/docker-containers_spec.js:
--------------------------------------------------------------------------------
1 | var should = require("should");
2 | var helper = require("node-red-node-test-helper");
3 | var docker = require('dockerode-mock')
4 |
5 |
6 | helper.init(require.resolve('node-red'));
7 |
8 |
9 |
10 | describe('Containers Node', function () {
11 |
12 | beforeEach(function (done) {
13 | helper.startServer(done);
14 | });
15 |
16 | afterEach(function (done) {
17 | helper.unload().then(function () {
18 | helper.stopServer(done);
19 | });
20 | });
21 |
22 | it('should be loaded', function (done) {
23 | var flow = [
24 | { id: "c1", type: "docker-configuration" },
25 | { id: "n1", type: "docker-containers", config: "c1" }
26 | ];
27 | var dockerContainersNode = require("../dist/docker-containers.js");
28 | var dockerConfigNode = require("../dist/docker-configuration.js");
29 |
30 |
31 |
32 | helper.load([dockerConfigNode, dockerContainersNode], flow, function () {
33 | var n1 = helper.getNode("n1");
34 | n1.should.have.property('type', 'docker-containers');
35 | done();
36 | });
37 | });
38 |
39 | // it('should return docker container', function (done) {
40 | // docker.overrides = {
41 | // 'GET /_ping': { error: 'connection error' },
42 | // 'GET /containers/json?': {},
43 | // }
44 |
45 | // var flow = [
46 | // {
47 | // "id": "n2",
48 | // "type": "helper"
49 | // },
50 | // {
51 | // "id": "dc1",
52 | // "type": "docker-containers",
53 | // "config": "c1",
54 | // "wires": [
55 | // [
56 | // "n2"
57 | // ]
58 | // ]
59 | // },
60 | // {
61 | // "id": "c1",
62 | // "type": "docker-configuration",
63 | // "host": "localhost",
64 | // "port": "2375"
65 | // }
66 | // ];
67 |
68 | // var dockerContainersNode = require("../dist/docker-containers.js");
69 | // var dockerConfigNode = require("../dist/configuration.js");
70 |
71 | // helper.load([dockerConfigNode, dockerContainersNode], flow, function () {
72 | // try {
73 | // var n2 = helper.getNode("n2");
74 | // var dc1 = helper.getNode("dc1");
75 | // dc1.receive({ payload: "" });
76 | // n2.on("input", function (msg) {
77 | // msg.should.not.be.empty();
78 | // done();
79 | // });
80 |
81 | // } catch (err) {
82 | // console.log(err);
83 | // }
84 | // });
85 | // });
86 | });
87 |
--------------------------------------------------------------------------------
/test/docker-events_spec.js:
--------------------------------------------------------------------------------
1 | var should = require("should");
2 | var helper = require("node-red-node-test-helper");
3 | var docker = require('dockerode-mock')
4 |
5 |
6 | helper.init(require.resolve('node-red'));
7 |
8 |
9 |
10 | describe('Events Node', function () {
11 |
12 | beforeEach(function (done) {
13 | helper.startServer(done);
14 | });
15 |
16 | afterEach(function (done) {
17 | helper.unload().then(function () {
18 | helper.stopServer(done);
19 | });
20 | });
21 |
22 | it('should be loaded', function (done) {
23 | var flow = [
24 | { id: "c1", type: "docker-configuration" },
25 | { id: "n1", type: "docker-events", config: "c1" }
26 | ];
27 | var dockerContainersNode = require("../dist/docker-events.js");
28 | var dockerConfigNode = require("../dist/docker-configuration.js");
29 |
30 |
31 |
32 | helper.load([dockerConfigNode, dockerContainersNode], flow, function () {
33 | var n1 = helper.getNode("n1");
34 | n1.should.have.property('type', 'docker-events');
35 | done();
36 | });
37 | });
38 |
39 | });
40 |
--------------------------------------------------------------------------------
/test/docker-images_spec.js:
--------------------------------------------------------------------------------
1 | var should = require("should");
2 | var helper = require("node-red-node-test-helper");
3 | var docker = require('dockerode-mock')
4 |
5 |
6 | helper.init(require.resolve('node-red'));
7 |
8 |
9 |
10 | describe('Iamges Node', function () {
11 |
12 | beforeEach(function (done) {
13 | helper.startServer(done);
14 | });
15 |
16 | afterEach(function (done) {
17 | helper.unload().then(function () {
18 | helper.stopServer(done);
19 | });
20 | });
21 |
22 | it('should be loaded', function (done) {
23 | var flow = [
24 | { id: "c1", type: "docker-configuration" },
25 | { id: "n1", type: "docker-images", config: "c1" }
26 | ];
27 | var dockerContainersNode = require("../dist/docker-images.js");
28 | var dockerConfigNode = require("../dist/docker-configuration.js");
29 |
30 |
31 |
32 | helper.load([dockerConfigNode, dockerContainersNode], flow, function () {
33 | var n1 = helper.getNode("n1");
34 | n1.should.have.property('type', 'docker-images');
35 | done();
36 | });
37 | });
38 |
39 |
40 | });
41 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "*": [
6 | "types/*",
7 | "src/*"
8 | ]
9 | },
10 | "outDir": "./dist",
11 | "target": "es5",
12 | "moduleResolution": "node",
13 | "strict": false,
14 | "sourceMap": true,
15 | "noUnusedLocals": true,
16 | "noUnusedParameters": true,
17 | },
18 | "include": [
19 | "./src/**/*"
20 | ],
21 | "exclude": [
22 | "node_modules"
23 | ]
24 | }
--------------------------------------------------------------------------------