Skip to content

Commit 070873e

Browse files
authored
Merge pull request #866 from labmonkey/template
Big updates to Uptime Kuma mod
2 parents 2faa77c + 65bb87b commit 070873e

File tree

11 files changed

+627
-290
lines changed

11 files changed

+627
-290
lines changed

.github/workflows/BuildImage.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ env:
77
ENDPOINT: "linuxserver/mods" #don't modify
88
BASEIMAGE: "swag" #replace
99
MODNAME: "auto-uptime-kuma" #replace
10+
MULTI_ARCH: "false" #set to false if not needed
1011

1112
jobs:
1213
set-vars:
@@ -19,6 +20,7 @@ jobs:
1920
echo "ENDPOINT=${{ env.ENDPOINT }}" >> $GITHUB_OUTPUT
2021
echo "BASEIMAGE=${{ env.BASEIMAGE }}" >> $GITHUB_OUTPUT
2122
echo "MODNAME=${{ env.MODNAME }}" >> $GITHUB_OUTPUT
23+
echo "MULTI_ARCH=${{ env.MULTI_ARCH }}" >> $GITHUB_OUTPUT
2224
# **** If the mod needs to be versioned, set the versioning logic below. Otherwise leave as is. ****
2325
MOD_VERSION=""
2426
echo "MOD_VERSION=${MOD_VERSION}" >> $GITHUB_OUTPUT
@@ -27,6 +29,7 @@ jobs:
2729
ENDPOINT: ${{ steps.outputs.outputs.ENDPOINT }}
2830
BASEIMAGE: ${{ steps.outputs.outputs.BASEIMAGE }}
2931
MODNAME: ${{ steps.outputs.outputs.MODNAME }}
32+
MULTI_ARCH: ${{ steps.outputs.outputs.MULTI_ARCH }}
3033
MOD_VERSION: ${{ steps.outputs.outputs.MOD_VERSION }}
3134

3235
build:
@@ -42,4 +45,5 @@ jobs:
4245
ENDPOINT: ${{ needs.set-vars.outputs.ENDPOINT }}
4346
BASEIMAGE: ${{ needs.set-vars.outputs.BASEIMAGE }}
4447
MODNAME: ${{ needs.set-vars.outputs.MODNAME }}
48+
MULTI_ARCH: ${{ needs.set-vars.outputs.MULTI_ARCH }}
4549
MOD_VERSION: ${{ needs.set-vars.outputs.MOD_VERSION }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,5 @@ $RECYCLE.BIN/
4141
Network Trash Folder
4242
Temporary Items
4343
.apdisk
44+
45+
__pycache__

README.md

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ This mod gives SWAG the ability to automatically add Uptime Kuma "Monitors" for
44

55
## Requirements
66

7-
Running [Uptime Kuma](https://github.com/louislam/uptime-kuma) instance with `username` and `password` configured. The container should be in the same [user defined bridge network](https://docs.linuxserver.io/general/swag#docker-networking) as SWAG.
7+
- This mod needs the [universal-docker mod](https://github.com/linuxserver/docker-mods/tree/universal-docker) installed and set up with either mapping docker.sock or setting the environment variable `DOCKER_HOST=remoteaddress`.
8+
- Other containers to be auto-detected and reverse proxied should be in the same [user defined bridge network](https://docs.linuxserver.io/general/swag#docker-networking) as SWAG.
9+
- A running [Uptime Kuma](https://github.com/louislam/uptime-kuma) instance (at least version `1.21.3`) with `username` and `password` configured. Also in the same network as mentioned above.
810

911
## Installation
1012

11-
In SWAG docker arguments, set an environment variable `DOCKER_MODS=linuxserver/mods:swag-auto-uptime-kuma`.
13+
In SWAG docker arguments, set an environment variable `DOCKER_MODS=linuxserver/mods:universal-docker|linuxserver/mods:swag-auto-uptime-kuma`.
1214

1315
Add additional environment variables to the SWAG docker image:
1416

@@ -20,6 +22,8 @@ Add additional environment variables to the SWAG docker image:
2022

2123
Unfortunately Uptime Kuma does not provide API keys for it's Socket.io API at the moment and Username/Password have to be used.
2224

25+
This mod additionaly reads the `URL` environment variable which is part of the SWAG configuration itself.
26+
2327
Finally, add `swag.uptime-kuma.enabled=true` label at minimum to each of your containers that you wish to monitor. More types of labels are listed in next section.
2428

2529
## Labels
@@ -33,11 +37,59 @@ This mod is utilizing the wonderful [Uptime Kuma API](https://github.com/lucashe
3337
| `swag.uptime-kuma.monitor.url` | `https://{containerName}.{domainName}` | `https://radarr.domain.com/` <br> `https://pihole.domain.com/admin/` | By default the URL of each container if build based of the actual container name (`{containerName}`) defined in docker and the value of `URL` environment variable (`{domainName}`) defined in SWAG (as required by SWAG itself). |
3438
| `swag.uptime-kuma.monitor.type` | http | http | While technically possible to override the monitor type the purpose of this mod is to monitor HTTP endpoints. |
3539
| `swag.uptime-kuma.monitor. description` | Automatically generated by SWAG auto-uptime-kuma | My own description | The description is only for informational purposes and can be freely changed |
40+
| `swag.uptime-kuma.monitor. parent` | | `"Media Servers"`, `"Tools"`, `"2137"` | A "special" label that can be used to create Monitor Groups. The value can be a name of the group which then will by dynamically created if it does not exist. A group name has to be unique (different than any of your container names). Alternatively an ID of the group can be used (can be found in the URL when editing the group in Uptime Kuma). Please note that in this mod only a name of the group can be defined. In case you want to edit additional parameters of the group then its best to create it manually and use an ID as value here. |
3641
| `swag.uptime-kuma.monitor.*` | | `swag.uptime-kuma.monitor. maxretries=5` <br> `swag.uptime-kuma.monitor. accepted_statuscodes= 200-299,404,501` | There are many more properties to configure. The fact that aything can be changed does not mean that it should. Some properties or combinations could not work and should be changed only if you know what you are doing. Please check the [Uptime Kuma API](https://uptime-kuma-api.readthedocs.io/en/latest/api.html#uptime_kuma_api.UptimeKumaApi.add_monitor) for more examples. Properties that are expected to be lists should be separated by comma `,` |
3742

43+
### Setting default values for all containers
44+
45+
This mod does not have an ability to set global default values for your Monitors. In case you would like to set some label value to be same for all of the monitored containers you have few options:
46+
47+
- In case you are using docker-compose then there are many ways of setting defaults such as [Extensions](https://docs.docker.com/compose/multiple-compose-files/extends/), [Fragments](https://docs.docker.com/compose/compose-file/10-fragments/) or [Extends](https://docs.docker.com/compose/multiple-compose-files/extends/).
48+
49+
Here is how I am using `extends` myself:
50+
51+
`docker-compose.template.yml`
52+
```
53+
services:
54+
monitored:
55+
labels:
56+
swag.uptime-kuma.enabled: true
57+
swag.uptime-kuma.monitor.interval: 69
58+
swag.uptime-kuma.monitor.retryInterval: 300
59+
swag.uptime-kuma.monitor.maxretries: 10
60+
```
61+
`docker-compose.yml`
62+
```
63+
services:
64+
bitwarden:
65+
extends:
66+
file: docker-compose.template.yml
67+
service: monitored
68+
# ... some other stuff
69+
labels:
70+
swag: enable
71+
whatever.else: hello
72+
swag.uptime-kuma.monitor.interval: 123 # label specific to this container
73+
```
74+
If you define it as above then the labels will be merged and/or overriden and result with:
75+
```
76+
...
77+
labels:
78+
swag: enable
79+
whatever.else: hello
80+
swag.uptime-kuma.enabled: true
81+
swag.uptime-kuma.monitor.interval: 123 # overriden
82+
swag.uptime-kuma.monitor.retryInterval: 300
83+
swag.uptime-kuma.monitor.maxretries: 10
84+
```
85+
86+
- In case you are using docker cli you could either define your labels with a common variable or use a common label file for the monitored containers [more info here](https://docs.docker.com/reference/cli/docker/container/run/#label)
87+
88+
- In case you are using any other way to deploy your containers then please look into documentation of your tool for any templating features.
89+
3890
## Notifications
3991

40-
While ultimately this mod makes it easier to setup notifications for your docker containers it does not configure more than Uptime Kuma Monitors. In order to receive Notifications you should configure them manually and then either enable one type to be default for all your Monitors or specify the Notifications by using the `swag.uptime-kuma.monitor.notificationIDList` label.
92+
While ultimately this mod makes it easier to setup notifications for your docker containers it does not configure more than Uptime Kuma Monitors. In order to receive Notifications you should configure them manually and then either enable one type to be default for all your Monitors or specify the Notifications by using the `swag.uptime-kuma.monitor.notificationIDList` label. Please note that if you define one or more notifications in Uptime Kuma to be default (enabled by default for new monitors) then even if you specify custom `notificationIDList` via labels then the default notifications will be always appended to the list.
4193

4294
## Known Limitations
4395

@@ -47,6 +99,8 @@ While ultimately this mod makes it easier to setup notifications for your docker
4799

48100
- Due to limitations of the Uptime Kuma API whenever you make changes to your container or labels that already have a Monior setup then the **Update** action will be performed by running **Delete** followed by **Add**. What it means that all changes will result in a new Monitor for the same container that will lose history of the heartbeats, all manual changes and get a new 'id' number.
49101

50-
## Purge data
102+
## Command Line mode
51103

52104
For the purpose of development or simply if you feel that you want to purge all the Monitors and files created by this mod you can run following command via `ssh`: `docker exec swag python3 /app/auto-uptime-kuma.py -purge` (where `swag` is your container name of the SWAG instance).
105+
106+
It is also possible to fetch and print the raw API data of a Monitor from Uptime Kuma API `ssh`: `docker exec swag python3 /app/auto-uptime-kuma.py -monitor container_name` (where `container_name` is the name of the container that Monitor belongs to).

root/app/auto-uptime-kuma.py

Lines changed: 131 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,135 @@
1-
from swagDocker import SwagDocker
2-
from swagUptimeKuma import SwagUptimeKuma
3-
import sys
4-
import argparse
5-
import os
6-
7-
8-
def parseCommandLine():
9-
"""
10-
Different application behavior if executed from CLI
11-
"""
12-
parser = argparse.ArgumentParser()
13-
parser.add_argument('-purge', action='store_true')
14-
args = parser.parse_args()
15-
16-
if (args.purge == True):
17-
swagUptimeKuma.purgeData()
18-
swagUptimeKuma.disconnect()
19-
sys.exit(0)
20-
21-
22-
def addOrUpdateMonitors(domainName, swagContainers):
23-
for swagContainer in swagContainers:
24-
containerConfig = swagDocker.parseContainerLabels(
25-
swagContainer.labels, ".monitor.")
26-
containerName = swagContainer.name
27-
monitorData = swagUptimeKuma.parseMonitorData(
28-
containerName, domainName, containerConfig)
29-
30-
if (not swagUptimeKuma.monitorExists(containerName)):
31-
swagUptimeKuma.addMonitor(containerName, domainName, monitorData)
1+
from auto_uptime_kuma.config_service import ConfigService
2+
from auto_uptime_kuma.uptime_kuma_service import UptimeKumaService
3+
from auto_uptime_kuma.docker_service import DockerService
4+
from auto_uptime_kuma.log import Log
5+
import sys, os
6+
7+
8+
def add_or_update_monitors(
9+
docker_service: DockerService,
10+
config_service: ConfigService,
11+
uptime_kuma_service: UptimeKumaService,
12+
):
13+
for container in docker_service.get_swag_containers():
14+
container_config = docker_service.parse_container_labels(
15+
container.labels, ".monitor."
16+
)
17+
container_name = container.name
18+
monitor_data = uptime_kuma_service.build_monitor_data(
19+
container_name, container_config
20+
)
21+
22+
if not uptime_kuma_service.monitor_exists(container_name):
23+
uptime_kuma_service.create_monitor(container_name, container_config)
3224
else:
33-
swagUptimeKuma.updateMonitor(
34-
containerName, domainName, monitorData)
35-
36-
37-
def getMonitorsToBeRemoved(swagContainers, apiMonitors):
38-
# Monitors to be removed are those that no longer have an existing container
39-
# Monitor <-> Container link is done by comparing the container name with the monitor swag tag value
40-
existingMonitorNames = [swagUptimeKuma.getMonitorSwagTagValue(
41-
monitor) for monitor in apiMonitors]
42-
existingContainerNames = [container.name for container in swagContainers]
43-
44-
monitorsToBeRemoved = [
45-
containerName for containerName in existingMonitorNames if containerName not in existingContainerNames]
46-
return monitorsToBeRemoved
25+
if not config_service.config_exists(container_name):
26+
Log.info(
27+
f"Monitor '{monitor_data['name']}' for container '{container_name}'"
28+
" exists but no preset config found, generating from scratch"
29+
)
30+
config_service.create_config(container_name, monitor_data)
31+
uptime_kuma_service.edit_monitor(container_name, monitor_data)
32+
33+
34+
def delete_removed_monitors(
35+
docker_service: DockerService, uptime_kuma_service: UptimeKumaService
36+
):
37+
Log.info("Searching for Monitors that should be deleted")
38+
# Monitors to be deleted are those that no longer have an existing container
39+
# Monitor <-> Container link is done by comparing the container name
40+
# with the monitor swag tag value
41+
existing_monitor_names = [
42+
uptime_kuma_service.get_monitor_swag_tag_value(monitor)
43+
for monitor in uptime_kuma_service.monitors
44+
]
45+
existing_container_names = [
46+
container.name for container in docker_service.get_swag_containers()
47+
]
48+
49+
monitors_to_be_deleted = [
50+
containerName
51+
for containerName in existing_monitor_names
52+
if containerName not in existing_container_names
53+
]
54+
55+
monitors_to_be_deleted = list(filter(None, monitors_to_be_deleted))
56+
57+
uptime_kuma_service.delete_monitors(monitors_to_be_deleted)
58+
59+
60+
def delete_removed_groups(uptime_kuma_service: UptimeKumaService):
61+
Log.info("Searching for Groups that should be deleted")
62+
# Groups to be deleted are those that no longer have any child Monitors
63+
existing_monitor_group_ids = [
64+
monitor["parent"] for monitor in uptime_kuma_service.monitors
65+
]
66+
67+
# remove empty values
68+
existing_monitor_group_ids = list(filter(None, existing_monitor_group_ids))
69+
# get unique values
70+
existing_monitor_group_ids = list(set(existing_monitor_group_ids))
71+
72+
groups_to_be_deleted = []
73+
74+
for group in uptime_kuma_service.groups:
75+
if group["id"] not in existing_monitor_group_ids:
76+
groups_to_be_deleted.append(group["name"])
77+
78+
uptime_kuma_service.delete_groups(groups_to_be_deleted)
79+
80+
81+
def execute_cli_mode(
82+
config_service: ConfigService, uptime_kuma_service: UptimeKumaService
83+
):
84+
Log.info("Mod was executed from CLI. Running manual tasks.")
85+
args = config_service.get_cli_args()
86+
if args.purge:
87+
uptime_kuma_service.purge_data()
88+
89+
config_service.purge_data()
90+
if args.monitor:
91+
Log.info(f"Requesting data for Monitor '{args.monitor}'")
92+
print(uptime_kuma_service.get_monitor(args.monitor))
93+
94+
uptime_kuma_service.disconnect()
4795

4896

4997
if __name__ == "__main__":
50-
url = os.environ['UPTIME_KUMA_URL']
51-
username = os.environ['UPTIME_KUMA_USERNAME']
52-
password = os.environ['UPTIME_KUMA_PASSWORD']
53-
domainName = os.environ['URL']
54-
55-
swagDocker = SwagDocker("swag.uptime-kuma")
56-
swagUptimeKuma = SwagUptimeKuma(url, username, password)
57-
58-
parseCommandLine()
59-
60-
swagContainers = swagDocker.getSwagContainers()
61-
62-
addOrUpdateMonitors(domainName, swagContainers)
63-
64-
monitorsToBeRemoved = getMonitorsToBeRemoved(
65-
swagContainers, swagUptimeKuma.apiMonitors)
66-
swagUptimeKuma.deleteMonitors(monitorsToBeRemoved)
67-
68-
swagUptimeKuma.disconnect()
98+
Log.init("mod-auto-uptime-kuma")
99+
100+
url = os.environ["UPTIME_KUMA_URL"]
101+
username = os.environ["UPTIME_KUMA_USERNAME"]
102+
password = os.environ["UPTIME_KUMA_PASSWORD"]
103+
domainName = os.environ["URL"]
104+
105+
configService = ConfigService(domainName)
106+
uptimeKumaService = UptimeKumaService(configService)
107+
dockerService = DockerService("swag.uptime-kuma")
108+
is_connected = uptimeKumaService.connect(url, username, password)
109+
110+
if not is_connected:
111+
sys.exit()
112+
113+
uptimeKumaService.load_data()
114+
if uptimeKumaService.default_notifications:
115+
notification_names = [
116+
f"{notification['id']}:{notification['name']}"
117+
for notification in uptimeKumaService.default_notifications
118+
]
119+
Log.info(
120+
f"The following notifications are enabled by default: {notification_names}"
121+
)
122+
123+
if configService.is_cli_mode():
124+
execute_cli_mode(configService, uptimeKumaService)
125+
sys.exit()
126+
127+
add_or_update_monitors(dockerService, configService, uptimeKumaService)
128+
129+
# reload data after the sync above
130+
uptimeKumaService.load_data()
131+
# cleanup
132+
delete_removed_monitors(dockerService, uptimeKumaService)
133+
delete_removed_groups(uptimeKumaService)
134+
135+
uptimeKumaService.disconnect()

root/app/auto_uptime_kuma/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)