Skip to content

Commit

Permalink
Merge pull request #533 from getwud/feature/#210_manual_update
Browse files Browse the repository at this point in the history
⭐ [TRIGGER] - Add support for running triggers manually(API/UI)
  • Loading branch information
fmartinou authored Dec 28, 2024
2 parents 42b78ec + 1f38b98 commit c0e97af
Show file tree
Hide file tree
Showing 31 changed files with 234 additions and 87 deletions.
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ ENV WORKDIR=/home/node/app
ENV WUD_LOG_FORMAT=text
ENV WUD_VERSION=$WUD_VERSION

HEALTHCHECK --interval=30s --timeout=5s CMD curl --fail http://localhost:${WUD_SERVER_PORT:-3000}/health || exit 1

WORKDIR /home/node/app

RUN mkdir /store

# Add TZDATA to allow easy local time configuration
RUN apt update \
&& apt install tzdata openssl \
&& apt install -y tzdata openssl curl \
&& rm -rf /var/cache/apt/*

# Dependencies stage
Expand Down
40 changes: 40 additions & 0 deletions app/api/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const registry = require('../registry');
const { getServerConfiguration } = require('../configuration');
const { mapComponentsToList } = require('./component');
const Trigger = require('../triggers/providers/Trigger');
const log = require('../log').child({ component: 'container' });

const router = express.Router();

Expand Down Expand Up @@ -156,6 +157,44 @@ async function getContainerTriggers(req, res) {
}
}

/**
* Run trigger.
* @param {*} req
* @param {*} res
*/
async function runTrigger(req, res) {
const { id, triggerType, triggerName } = req.params;

const containerToTrigger = storeContainer.getContainer(id);
if (containerToTrigger) {
const triggerToRun = getTriggers()[`${triggerType}.${triggerName}`];
if (triggerToRun) {
try {
await triggerToRun.trigger(containerToTrigger);
log.info(
`Trigger executed with success (type=${triggerType}, name=${triggerName}, container=${JSON.stringify(containerToTrigger)})`,
);
res.status(200).json({});
} catch (e) {
log.warn(
`Trigger cannot be executed without container (type=${triggerType}, name=${triggerName})`,
);
res.status(500).json({
error: `Error when running trigger ${triggerType}.${triggerName} (${e.message})`,
});
}
} else {
res.status(404).json({
error: 'Trigger not found',
});
}
} else {
res.status(404).json({
error: 'Container not found',
});
}
}

/**
* Watch an image.
* @param req
Expand Down Expand Up @@ -211,6 +250,7 @@ function init() {
router.get('/:id', getContainer);
router.delete('/:id', deleteContainer);
router.get('/:id/triggers', getContainerTriggers);
router.post('/:id/triggers/:triggerType/:triggerName', runTrigger);
router.post('/:id/watch', watchContainer);
return router;
}
Expand Down
3 changes: 2 additions & 1 deletion app/nodemon.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"WUD_AUTH_BASIC_JOHN_HASH": "$apr1$aefKbZEa$ZSA5Y3zv9vDQOxr283NGx/",
"WUD_TRIGGER_NTFY_ONE_TOPIC": "235ef38e-f1db-414a-964f-ce3f2cc8094d",
"WUD_TRIGGER_NTFY_TWO_TOPIC": "6abd7f9d-f140-4543-af3e-749252660d89",
"WUD_TRIGGER_NTFY_THREE_TOPIC": "9f5c9961-adb2-4ab7-a078-0671856948c4"
"WUD_TRIGGER_NTFY_THREE_TOPIC": "9f5c9961-adb2-4ab7-a078-0671856948c4",
"WUD_TRIGGER_NTFY_THREE_AUTO": "false"
}
}
33 changes: 16 additions & 17 deletions app/triggers/providers/Trigger.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,17 +257,20 @@ class Trigger extends Component {
*/
async init() {
await this.initTrigger();
if (this.configuration.mode.toLowerCase() === 'simple') {
this.log.debug('Configure "simple" mode');
event.registerContainerReport(async (containerReport) =>
this.handleContainerReport(containerReport),
);
}
if (this.configuration.mode.toLowerCase() === 'batch') {
this.log.debug('Configure "batch" mode');
event.registerContainerReports(async (containersReports) =>
this.handleContainerReports(containersReports),
);
if (this.configuration.auto) {
this.log.info(`Registering for auto execution`);
if (this.configuration.mode.toLowerCase() === 'simple') {
event.registerContainerReport(async (containerReport) =>
this.handleContainerReport(containerReport),
);
}
if (this.configuration.mode.toLowerCase() === 'batch') {
event.registerContainerReports(async (containersReports) =>
this.handleContainerReports(containersReports),
);
}
} else {
this.log.info(`Registering for manual execution`);
}
}

Expand All @@ -279,6 +282,7 @@ class Trigger extends Component {
validateConfiguration(configuration) {
const schema = this.getConfigurationSchema();
const schemaWithDefaultOptions = schema.append({
auto: this.joi.bool().default(true),
threshold: this.joi
.string()
.insensitive()
Expand All @@ -292,18 +296,13 @@ class Trigger extends Component {
once: this.joi.boolean().default(true),
simpletitle: this.joi
.string()

.default('New ${kind} found for container ${name}'),
simplebody: this.joi
.string()

.default(
'Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote}\n${link}',
),
batchtitle: this.joi
.string()

.default('${count} updates available'),
batchtitle: this.joi.string().default('${count} updates available'),
});
const schemaValidated =
schemaWithDefaultOptions.validate(configuration);
Expand Down
2 changes: 1 addition & 1 deletion app/triggers/providers/Trigger.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const configurationValid = {
threshold: 'all',
once: true,
mode: 'simple',

auto: true,
simpletitle: 'New ${kind} found for container ${name}',

simplebody:
Expand Down
1 change: 1 addition & 0 deletions app/triggers/providers/apprise/Apprise.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const configurationValid = {
url: 'http://xxx.com',
urls: 'maito://user:[email protected]',
threshold: 'all',
auto: true,
once: true,
mode: 'simple',

Expand Down
5 changes: 3 additions & 2 deletions app/triggers/providers/discord/Discord.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ const configurationValid = {
botusername: 'Bot Name',
cardcolor: 65280,
cardlabel: 'Container',
auto: true,
threshold: 'all',
mode: 'simple',
once: true,

auto: true,
simpletitle: 'New ${kind} found for container ${name}',

simplebody:
Expand Down Expand Up @@ -50,7 +51,7 @@ test('maskConfiguration should mask sensitive data', () => {
cardcolor: 65280,
cardlabel: 'Container',
once: true,

auto: true,
simplebody:
'Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote}\n${link}',

Expand Down
4 changes: 1 addition & 3 deletions app/triggers/providers/docker/Docker.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ const configurationValid = {
threshold: 'all',
mode: 'simple',
once: true,

auto: true,
simpletitle: 'New ${kind} found for container ${name}',

simplebody:
'Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote}\n${link}',

batchtitle: '${count} updates available',
};

Expand Down
3 changes: 1 addition & 2 deletions app/triggers/providers/gotify/Gotify.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ const configurationValid = {
mode: 'simple',
threshold: 'all',
once: true,

auto: true,
simpletitle: 'New ${kind} found for container ${name}',

simplebody:
'Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote}\n${link}',

Expand Down
2 changes: 1 addition & 1 deletion app/triggers/providers/http/Http.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const configurationValid = {
threshold: 'all',
mode: 'simple',
once: true,

auto: true,
simpletitle: 'New ${kind} found for container ${name}',

simplebody:
Expand Down
2 changes: 1 addition & 1 deletion app/triggers/providers/ifttt/Ifttt.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const configurationValid = {
threshold: 'all',
mode: 'simple',
once: true,

auto: true,
simpletitle: 'New ${kind} found for container ${name}',

simplebody:
Expand Down
2 changes: 1 addition & 1 deletion app/triggers/providers/kafka/Kafka.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const configurationValid = {
threshold: 'all',
mode: 'simple',
once: true,

auto: true,
simpletitle: 'New ${kind} found for container ${name}',

simplebody:
Expand Down
2 changes: 1 addition & 1 deletion app/triggers/providers/mqtt/Mqtt.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const configurationValid = {
threshold: 'all',
mode: 'simple',
once: true,

auto: true,
simpletitle: 'New ${kind} found for container ${name}',

simplebody:
Expand Down
2 changes: 1 addition & 1 deletion app/triggers/providers/ntfy/Ntfy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const configurationValid = {
mode: 'simple',
threshold: 'all',
once: true,

auto: true,
simpletitle: 'New ${kind} found for container ${name}',

simplebody:
Expand Down
4 changes: 2 additions & 2 deletions app/triggers/providers/pushover/Pushover.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const configurationValid = {
threshold: 'all',
mode: 'simple',
once: true,

auto: true,
simpletitle: 'New ${kind} found for container ${name}',

simplebody:
Expand Down Expand Up @@ -102,7 +102,7 @@ test('maskConfiguration should mask sensitive data', () => {
mode: 'simple',
priority: 0,
device: undefined,

auto: true,
simplebody:
'Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote}\n${link}',

Expand Down
2 changes: 1 addition & 1 deletion app/triggers/providers/slack/Slack.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const configurationValid = {
threshold: 'all',
mode: 'simple',
once: true,

auto: true,
simpletitle: 'New ${kind} found for container ${name}',

simplebody:
Expand Down
2 changes: 1 addition & 1 deletion app/triggers/providers/smtp/Smtp.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const configurationValid = {
threshold: 'all',
mode: 'simple',
once: true,

auto: true,
simpletitle: 'New ${kind} found for container ${name}',

simplebody:
Expand Down
4 changes: 2 additions & 2 deletions app/triggers/providers/telegram/Telegram.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const configurationValid = {
threshold: 'all',
mode: 'simple',
once: true,

auto: true,
simpletitle: 'New ${kind} found for container ${name}',

simplebody:
Expand Down Expand Up @@ -43,7 +43,7 @@ test('maskConfiguration should mask sensitive data', () => {
chatid: '1*******9',
mode: 'simple',
once: true,

auto: true,
simplebody:
'Container ${name} running with ${kind} ${local} can be updated to ${kind} ${remote}\n${link}',

Expand Down
10 changes: 9 additions & 1 deletion docs/api/container.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ curl http://wud:3000/api/containers/31a61a8305ef1fc9a71fa4f20a68d7ec88b28e32303b

[
{
"id": "trigger.ntfy.one",
"id": "ntfy.one",
"type": "ntfy",
"name": "one",
"configuration": {
Expand Down Expand Up @@ -174,6 +174,14 @@ curl -X POST http://wud:3000/api/containers/ca0edc3fb0b4647963629bdfccbb3ccfa352
}
```

## Run a trigger on the container

This operation lets you manually run a trigger on the container.

```bash
curl -X POST http://wud:3000/api/containers/31a61a8305ef1fc9a71fa4f20a68d7ec88b28e32303bbc4a5f192e851165b816/triggers/ntfy.one
```

## Delete a Container
This operation lets you delete a container by id.

Expand Down
2 changes: 2 additions & 0 deletions docs/changelog/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Changelog

# 8.0.0 (wip)
- :star: [TRIGGER] - Add support for automatic or manual triggers
- :star: [REGISTRY] - Add support for multiple registries of the same type
- :star: [DOCKER] - Add default healthcheck to the `wud` docker image
- :fire: [TRIGGER] - Fix specific triggers to specific containers association issue
- :lock: Upgrade to node.js 23
- :lock: Add prettier
Expand Down
18 changes: 9 additions & 9 deletions docs/configuration/triggers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ You just need to give them different names.
### Common trigger configuration
All implemented triggers, in addition to their specific configuration, also support the following common configuration variables.

| Env var | Required | Description | Supported values | Default value when missing |
|------------------------------------------------------------|:--------------:|----------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
| `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` |
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_ONCE` | :white_circle: | Run trigger once (do not repeat previous results) | `true`, `false` | `true` |
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_THRESHOLD` | :white_circle: | The threshold to reach to run the trigger | `all`, `major`, `minor`, `patch` | `all` |
| `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}` |
| `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` |
| `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}` |
| Env var | Required | Description | Supported values | Default value when missing |
|---------------------------------------------------------|:--------------:|-----------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
| `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` |
| `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` |
| `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` |
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_ONCE` | :white_circle: | Run trigger once (do not repeat previous results) | `true`, `false` | `true` |
| `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}` |
| `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}` |
| `WUD_TRIGGER_{trigger_type}_{trigger_name}_THRESHOLD` | :white_circle: | The threshold to reach to run the trigger | `all`, `major`, `minor`, `patch` | `all` |

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

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

?> `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.


### Examples

<!-- tabs:start -->
Expand Down
2 changes: 1 addition & 1 deletion docs/introduction/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
![Maintainability](https://img.shields.io/codeclimate/maintainability/getwud/wud)
![Coverage](https://img.shields.io/codeclimate/coverage/getwud/wud)

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

#### WUD is built on 3 concepts:
Expand Down
2 changes: 1 addition & 1 deletion docs/monitoring/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ services:
image: getwud/wud:7.2.0
...
healthcheck:
test: node --eval "fetch('http://localhost:3000').catch(() => process.exit(1))"
test: curl --fail http://localhost:${WUD_SERVER_PORT:-3000}/health || exit 1
interval: 10s
timeout: 10s
retries: 3
Expand Down
Loading

0 comments on commit c0e97af

Please sign in to comment.