Skip to content

Commit c0e97af

Browse files
authored
Merge pull request #533 from getwud/feature/#210_manual_update
⭐ [TRIGGER] - Add support for running triggers manually(API/UI)
2 parents 42b78ec + 1f38b98 commit c0e97af

31 files changed

+234
-87
lines changed

Dockerfile

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ ENV WORKDIR=/home/node/app
1010
ENV WUD_LOG_FORMAT=text
1111
ENV WUD_VERSION=$WUD_VERSION
1212

13+
HEALTHCHECK --interval=30s --timeout=5s CMD curl --fail http://localhost:${WUD_SERVER_PORT:-3000}/health || exit 1
14+
1315
WORKDIR /home/node/app
1416

1517
RUN mkdir /store
1618

1719
# Add TZDATA to allow easy local time configuration
1820
RUN apt update \
19-
&& apt install tzdata openssl \
21+
&& apt install -y tzdata openssl curl \
2022
&& rm -rf /var/cache/apt/*
2123

2224
# Dependencies stage

app/api/container.js

+40
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const registry = require('../registry');
55
const { getServerConfiguration } = require('../configuration');
66
const { mapComponentsToList } = require('./component');
77
const Trigger = require('../triggers/providers/Trigger');
8+
const log = require('../log').child({ component: 'container' });
89

910
const router = express.Router();
1011

@@ -156,6 +157,44 @@ async function getContainerTriggers(req, res) {
156157
}
157158
}
158159

160+
/**
161+
* Run trigger.
162+
* @param {*} req
163+
* @param {*} res
164+
*/
165+
async function runTrigger(req, res) {
166+
const { id, triggerType, triggerName } = req.params;
167+
168+
const containerToTrigger = storeContainer.getContainer(id);
169+
if (containerToTrigger) {
170+
const triggerToRun = getTriggers()[`${triggerType}.${triggerName}`];
171+
if (triggerToRun) {
172+
try {
173+
await triggerToRun.trigger(containerToTrigger);
174+
log.info(
175+
`Trigger executed with success (type=${triggerType}, name=${triggerName}, container=${JSON.stringify(containerToTrigger)})`,
176+
);
177+
res.status(200).json({});
178+
} catch (e) {
179+
log.warn(
180+
`Trigger cannot be executed without container (type=${triggerType}, name=${triggerName})`,
181+
);
182+
res.status(500).json({
183+
error: `Error when running trigger ${triggerType}.${triggerName} (${e.message})`,
184+
});
185+
}
186+
} else {
187+
res.status(404).json({
188+
error: 'Trigger not found',
189+
});
190+
}
191+
} else {
192+
res.status(404).json({
193+
error: 'Container not found',
194+
});
195+
}
196+
}
197+
159198
/**
160199
* Watch an image.
161200
* @param req
@@ -211,6 +250,7 @@ function init() {
211250
router.get('/:id', getContainer);
212251
router.delete('/:id', deleteContainer);
213252
router.get('/:id/triggers', getContainerTriggers);
253+
router.post('/:id/triggers/:triggerType/:triggerName', runTrigger);
214254
router.post('/:id/watch', watchContainer);
215255
return router;
216256
}

app/nodemon.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"WUD_AUTH_BASIC_JOHN_HASH": "$apr1$aefKbZEa$ZSA5Y3zv9vDQOxr283NGx/",
99
"WUD_TRIGGER_NTFY_ONE_TOPIC": "235ef38e-f1db-414a-964f-ce3f2cc8094d",
1010
"WUD_TRIGGER_NTFY_TWO_TOPIC": "6abd7f9d-f140-4543-af3e-749252660d89",
11-
"WUD_TRIGGER_NTFY_THREE_TOPIC": "9f5c9961-adb2-4ab7-a078-0671856948c4"
11+
"WUD_TRIGGER_NTFY_THREE_TOPIC": "9f5c9961-adb2-4ab7-a078-0671856948c4",
12+
"WUD_TRIGGER_NTFY_THREE_AUTO": "false"
1213
}
1314
}

app/triggers/providers/Trigger.js

+16-17
Original file line numberDiff line numberDiff line change
@@ -257,17 +257,20 @@ class Trigger extends Component {
257257
*/
258258
async init() {
259259
await this.initTrigger();
260-
if (this.configuration.mode.toLowerCase() === 'simple') {
261-
this.log.debug('Configure "simple" mode');
262-
event.registerContainerReport(async (containerReport) =>
263-
this.handleContainerReport(containerReport),
264-
);
265-
}
266-
if (this.configuration.mode.toLowerCase() === 'batch') {
267-
this.log.debug('Configure "batch" mode');
268-
event.registerContainerReports(async (containersReports) =>
269-
this.handleContainerReports(containersReports),
270-
);
260+
if (this.configuration.auto) {
261+
this.log.info(`Registering for auto execution`);
262+
if (this.configuration.mode.toLowerCase() === 'simple') {
263+
event.registerContainerReport(async (containerReport) =>
264+
this.handleContainerReport(containerReport),
265+
);
266+
}
267+
if (this.configuration.mode.toLowerCase() === 'batch') {
268+
event.registerContainerReports(async (containersReports) =>
269+
this.handleContainerReports(containersReports),
270+
);
271+
}
272+
} else {
273+
this.log.info(`Registering for manual execution`);
271274
}
272275
}
273276

@@ -279,6 +282,7 @@ class Trigger extends Component {
279282
validateConfiguration(configuration) {
280283
const schema = this.getConfigurationSchema();
281284
const schemaWithDefaultOptions = schema.append({
285+
auto: this.joi.bool().default(true),
282286
threshold: this.joi
283287
.string()
284288
.insensitive()
@@ -292,18 +296,13 @@ class Trigger extends Component {
292296
once: this.joi.boolean().default(true),
293297
simpletitle: this.joi
294298
.string()
295-
296299
.default('New ${kind} found for container ${name}'),
297300
simplebody: this.joi
298301
.string()
299-
300302
.default(
301303
'Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote}\n${link}',
302304
),
303-
batchtitle: this.joi
304-
.string()
305-
306-
.default('${count} updates available'),
305+
batchtitle: this.joi.string().default('${count} updates available'),
307306
});
308307
const schemaValidated =
309308
schemaWithDefaultOptions.validate(configuration);

app/triggers/providers/Trigger.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const configurationValid = {
1717
threshold: 'all',
1818
once: true,
1919
mode: 'simple',
20-
20+
auto: true,
2121
simpletitle: 'New ${kind} found for container ${name}',
2222

2323
simplebody:

app/triggers/providers/apprise/Apprise.test.js

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const configurationValid = {
1010
url: 'http://xxx.com',
1111
urls: 'maito://user:pass@gmail.com',
1212
threshold: 'all',
13+
auto: true,
1314
once: true,
1415
mode: 'simple',
1516

app/triggers/providers/discord/Discord.test.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ const configurationValid = {
1111
botusername: 'Bot Name',
1212
cardcolor: 65280,
1313
cardlabel: 'Container',
14+
auto: true,
1415
threshold: 'all',
1516
mode: 'simple',
1617
once: true,
17-
18+
auto: true,
1819
simpletitle: 'New ${kind} found for container ${name}',
1920

2021
simplebody:
@@ -50,7 +51,7 @@ test('maskConfiguration should mask sensitive data', () => {
5051
cardcolor: 65280,
5152
cardlabel: 'Container',
5253
once: true,
53-
54+
auto: true,
5455
simplebody:
5556
'Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote}\n${link}',
5657

app/triggers/providers/docker/Docker.test.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@ const configurationValid = {
88
threshold: 'all',
99
mode: 'simple',
1010
once: true,
11-
11+
auto: true,
1212
simpletitle: 'New ${kind} found for container ${name}',
13-
1413
simplebody:
1514
'Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote}\n${link}',
16-
1715
batchtitle: '${count} updates available',
1816
};
1917

app/triggers/providers/gotify/Gotify.test.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ const configurationValid = {
1111
mode: 'simple',
1212
threshold: 'all',
1313
once: true,
14-
14+
auto: true,
1515
simpletitle: 'New ${kind} found for container ${name}',
16-
1716
simplebody:
1817
'Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote}\n${link}',
1918

app/triggers/providers/http/Http.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const configurationValid = {
1212
threshold: 'all',
1313
mode: 'simple',
1414
once: true,
15-
15+
auto: true,
1616
simpletitle: 'New ${kind} found for container ${name}',
1717

1818
simplebody:

app/triggers/providers/ifttt/Ifttt.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const configurationValid = {
1313
threshold: 'all',
1414
mode: 'simple',
1515
once: true,
16-
16+
auto: true,
1717
simpletitle: 'New ${kind} found for container ${name}',
1818

1919
simplebody:

app/triggers/providers/kafka/Kafka.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const configurationValid = {
1515
threshold: 'all',
1616
mode: 'simple',
1717
once: true,
18-
18+
auto: true,
1919
simpletitle: 'New ${kind} found for container ${name}',
2020

2121
simplebody:

app/triggers/providers/mqtt/Mqtt.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const configurationValid = {
2626
threshold: 'all',
2727
mode: 'simple',
2828
once: true,
29-
29+
auto: true,
3030
simpletitle: 'New ${kind} found for container ${name}',
3131

3232
simplebody:

app/triggers/providers/ntfy/Ntfy.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const configurationValid = {
1313
mode: 'simple',
1414
threshold: 'all',
1515
once: true,
16-
16+
auto: true,
1717
simpletitle: 'New ${kind} found for container ${name}',
1818

1919
simplebody:

app/triggers/providers/pushover/Pushover.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const configurationValid = {
2323
threshold: 'all',
2424
mode: 'simple',
2525
once: true,
26-
26+
auto: true,
2727
simpletitle: 'New ${kind} found for container ${name}',
2828

2929
simplebody:
@@ -102,7 +102,7 @@ test('maskConfiguration should mask sensitive data', () => {
102102
mode: 'simple',
103103
priority: 0,
104104
device: undefined,
105-
105+
auto: true,
106106
simplebody:
107107
'Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote}\n${link}',
108108

app/triggers/providers/slack/Slack.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const configurationValid = {
1212
threshold: 'all',
1313
mode: 'simple',
1414
once: true,
15-
15+
auto: true,
1616
simpletitle: 'New ${kind} found for container ${name}',
1717

1818
simplebody:

app/triggers/providers/smtp/Smtp.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const configurationValid = {
1414
threshold: 'all',
1515
mode: 'simple',
1616
once: true,
17-
17+
auto: true,
1818
simpletitle: 'New ${kind} found for container ${name}',
1919

2020
simplebody:

app/triggers/providers/telegram/Telegram.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const configurationValid = {
99
threshold: 'all',
1010
mode: 'simple',
1111
once: true,
12-
12+
auto: true,
1313
simpletitle: 'New ${kind} found for container ${name}',
1414

1515
simplebody:
@@ -43,7 +43,7 @@ test('maskConfiguration should mask sensitive data', () => {
4343
chatid: '1*******9',
4444
mode: 'simple',
4545
once: true,
46-
46+
auto: true,
4747
simplebody:
4848
'Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote}\n${link}',
4949

docs/api/container.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ curl http://wud:3000/api/containers/31a61a8305ef1fc9a71fa4f20a68d7ec88b28e32303b
121121

122122
[
123123
{
124-
"id": "trigger.ntfy.one",
124+
"id": "ntfy.one",
125125
"type": "ntfy",
126126
"name": "one",
127127
"configuration": {
@@ -174,6 +174,14 @@ curl -X POST http://wud:3000/api/containers/ca0edc3fb0b4647963629bdfccbb3ccfa352
174174
}
175175
```
176176

177+
## Run a trigger on the container
178+
179+
This operation lets you manually run a trigger on the container.
180+
181+
```bash
182+
curl -X POST http://wud:3000/api/containers/31a61a8305ef1fc9a71fa4f20a68d7ec88b28e32303bbc4a5f192e851165b816/triggers/ntfy.one
183+
```
184+
177185
## Delete a Container
178186
This operation lets you delete a container by id.
179187

docs/changelog/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# Changelog
22

33
# 8.0.0 (wip)
4+
- :star: [TRIGGER] - Add support for automatic or manual triggers
45
- :star: [REGISTRY] - Add support for multiple registries of the same type
6+
- :star: [DOCKER] - Add default healthcheck to the `wud` docker image
57
- :fire: [TRIGGER] - Fix specific triggers to specific containers association issue
68
- :lock: Upgrade to node.js 23
79
- :lock: Add prettier

docs/configuration/triggers/README.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ You just need to give them different names.
1616
### Common trigger configuration
1717
All implemented triggers, in addition to their specific configuration, also support the following common configuration variables.
1818

19-
| Env var | Required | Description | Supported values | Default value when missing |
20-
|------------------------------------------------------------|:--------------:|----------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
21-
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_MODE` | :white_circle: | Trigger for each container update or trigger once with all available updates as a list | `simple`, `batch` | `simple` |
22-
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_ONCE` | :white_circle: | Run trigger once (do not repeat previous results) | `true`, `false` | `true` |
23-
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_THRESHOLD` | :white_circle: | The threshold to reach to run the trigger | `all`, `major`, `minor`, `patch` | `all` |
24-
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_SIMPLETITLE` | :white_circle: | The template to use to render the title of the notification (simple mode) | String template with placeholders `${id}` `${name}` `${watcher}` `${kind}` `${semver}` `${local}` `${remote}` `${link}` | `New ${kind} found for container ${name}` |
25-
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_BATCHTITLE` | :white_circle: | The template to use to render the title of the notification (batch mode) | String template with placeholders `${count}` | `${count} updates available` |
26-
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_SIMPLEBODY` | :white_circle: | The template to use to render the body of the notification | String template with placeholders `${id}` `${name}` `${watcher}` `${kind}` `${semver}` `${local}` `${remote}` `${link}` | `Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote} \n ${link}` |
19+
| Env var | Required | Description | Supported values | Default value when missing |
20+
|---------------------------------------------------------|:--------------:|-----------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
21+
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_AUTO` | :white_circle: | `true` to automatically execute the trigger. `false` to manually execute it (from UI, API...) | `true`, `false` | `true` |
22+
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_BATCHTITLE` | :white_circle: | The template to use to render the title of the notification (batch mode) | String template with placeholders `${count}` | `${count} updates available` |
23+
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_MODE` | :white_circle: | Trigger for each container update or trigger once with all available updates as a list | `simple`, `batch` | `simple` |
24+
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_ONCE` | :white_circle: | Run trigger once (do not repeat previous results) | `true`, `false` | `true` |
25+
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_SIMPLEBODY` | :white_circle: | The template to use to render the body of the notification | String template with placeholders `${id}` `${name}` `${watcher}` `${kind}` `${semver}` `${local}` `${remote}` `${link}` | `Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote} \n ${link}` |
26+
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_SIMPLETITLE` | :white_circle: | The template to use to render the title of the notification (simple mode) | String template with placeholders `${id}` `${name}` `${watcher}` `${kind}` `${semver}` `${local}` `${remote}` `${link}` | `New ${kind} found for container ${name}` |
27+
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_THRESHOLD` | :white_circle: | The threshold to reach to run the trigger | `all`, `major`, `minor`, `patch` | `all` |
2728

2829
?> Threshold `all` means that the trigger will run regardless of the nature of the change
2930

@@ -35,7 +36,6 @@ All implemented triggers, in addition to their specific configuration, also supp
3536

3637
?> `WUD_TRIGGER_{trigger_type}_{trigger_name}_ONCE=false` can be useful when `WUD_TRIGGER_{trigger_type}_{trigger_name}_MODE=batch` to get a report with all pending updates.
3738

38-
3939
### Examples
4040

4141
<!-- tabs:start -->

docs/introduction/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
![Maintainability](https://img.shields.io/codeclimate/maintainability/getwud/wud)
77
![Coverage](https://img.shields.io/codeclimate/coverage/getwud/wud)
88

9-
## What's up Docker? <small>(aka **WUD**)</small>
9+
## WUD <small>(aka **What's up Docker?**)</small>
1010
Gets you notified when new versions of your Docker containers are available and lets you react the way you want.
1111

1212
#### WUD is built on 3 concepts:

docs/monitoring/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ services:
2525
image: getwud/wud:7.2.0
2626
...
2727
healthcheck:
28-
test: node --eval "fetch('http://localhost:3000').catch(() => process.exit(1))"
28+
test: curl --fail http://localhost:${WUD_SERVER_PORT:-3000}/health || exit 1
2929
interval: 10s
3030
timeout: 10s
3131
retries: 3

0 commit comments

Comments
 (0)