diff --git a/README.md b/README.md
index 390da9c..eadf0f6 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
[![Gitlab Project](https://img.shields.io/badge/GitLab-Project-554488.svg)](https://gitlab.com/ix.ai/cioban/)
-A docker swarm service for automatically updating your services to the latest image tag push. You can enable telegram notifications, so you get a message after every successful update.
+A docker swarm service for automatically updating your services to the latest image tag push. You can enable telegram or gotify notifications, so you get a message after every successful update.
## Usage Examples
@@ -52,6 +52,8 @@ Cioban will try to update your services every 5 minutes by default. The followin
| `FILTER_SERVICES` | - | Anything accepted by the filtering flag in `docker service ls`. Example: `label=ai.ix.auto-update=true` |
| `TELEGRAM_TOKEN` | - | See the [Telegram documentation](https://core.telegram.org/bots#creating-a-new-bot) how to get a new token |
| `TELEGRAM_CHAT_ID` | - | See this question on [stackoverflow](https://stackoverflow.com/questions/32423837/telegram-bot-how-to-get-a-group-chat-id) |
+| `GOTIFY_URL` | - | The URL of the [Gotify](https://gotify.net/) server |
+| `GOTIFY_TOKEN` | - | The APP token for Gotify |
| `NOTIFY_INCLUDE_NEW_IMAGE` | - | Set this variable to anything to include the new image (including digest) in the update notification |
| `NOTIFY_INCLUDE_OLD_IMAGE` | - | Set this variable to anything to include the old image (including digest) in the update notification |
| `LOGLEVEL` | `INFO` | [Logging Level](https://docs.python.org/3/library/logging.html#levels) |
diff --git a/cioban/__main__.py b/cioban/__main__.py
index 878b82e..c9157b9 100644
--- a/cioban/__main__.py
+++ b/cioban/__main__.py
@@ -23,58 +23,57 @@
filters = os.environ.get('FILTER_SERVICES')
filters = filters.split('=', 1)
options['filters'] = {filters[0]: filters[1]}
- log.info('FILTER_SERVICES: "{}"'.format(options['filters']))
- else:
- log.info('FILTER_SERVICES not set')
+ log.info(f"FILTER_SERVICES: '{options['filters']}'")
if os.environ.get('BLACKLIST_SERVICES'):
blacklist = os.environ.get('BLACKLIST_SERVICES')
options['blacklist'] = blacklist.split(' ')
- log.info('BLACKLIST_SERVICES: "{}"'.format(options['blacklist']))
- else:
- log.info('BLACKLIST_SERVICES not set')
+ log.info(f"BLACKLIST_SERVICES: '{options['blacklist']}'")
if os.environ.get('TELEGRAM_TOKEN'):
options['telegram_token'] = os.environ.get('TELEGRAM_TOKEN')
log.info('TELEGRAM_TOKEN is set')
- else:
- log.info('TELEGRAM_TOKEN not set')
if os.environ.get('TELEGRAM_CHAT_ID'):
options['telegram_chat_id'] = os.environ.get('TELEGRAM_CHAT_ID')
log.info('TELEGRAM_CHAT_ID is set')
- else:
- log.info('TELEGRAM_CHAT_ID not set')
+
+ if os.environ.get('GOTIFY_URL'):
+ options['gotify_url'] = os.environ.get('GOTIFY_URL')
+ log.info(f"GOTIFY_URL: {options['gotify_url']}")
+
+ if os.environ.get('GOTIFY_TOKEN'):
+ options['gotify_token'] = os.environ.get('GOTIFY_TOKEN')
+ log.info(f"GOTIFY_TOKEN is set")
if os.environ.get('NOTIFY_INCLUDE_NEW_IMAGE'):
options['notify_include_new_image'] = True
log.info('NOTIFY_INCLUDE_NEW_IMAGE is set')
- else:
- log.info('NOTIFY_INCLUDE_NEW_IMAGE not set')
if os.environ.get('NOTIFY_INCLUDE_OLD_IMAGE'):
options['notify_include_old_image'] = True
log.info('NOTIFY_INCLUDE_OLD_IMAGE is set')
- else:
- log.info('NOTIFY_INCLUDE_OLD_IMAGE not set')
if options.get('telegram_token') and options.get('telegram_chat_id'):
options['notifiers'].append('telegram')
+ if options.get('gotify_url') and options.get('gotify_token'):
+ options['notifiers'].append('gotify')
+
options['sleep_time'] = os.environ.get('SLEEP_TIME', '5m')
- log.info('SLEEP_TIME: {}'.format(options['sleep_time']))
+ log.info(f"SLEEP_TIME: {options['sleep_time']}")
options['prometheus_port'] = int(os.environ.get('PORT', 9308))
- log.info('PORT: {}'.format(options['prometheus_port']))
-
- log.warning(
- "Starting {} {}-{} with prometheus metrics on port {}".format(
- __package__,
- constants.VERSION,
- constants.BUILD,
- options['prometheus_port'],
- )
+ log.info(f"PORT: {options['prometheus_port']}")
+
+ startup_message = "Starting {} {}-{} with prometheus metrics on port {}".format(
+ __package__,
+ constants.VERSION,
+ constants.BUILD,
+ options['prometheus_port'],
)
+ log.warning(startup_message)
c = cioban.Cioban(**options)
+ c.notify(title="CIOBAN Startup", message=startup_message)
c.run()
diff --git a/cioban/cioban.py b/cioban/cioban.py
index 6acf23b..6c770a2 100644
--- a/cioban/cioban.py
+++ b/cioban/cioban.py
@@ -3,6 +3,7 @@
""" A docker swarm service for automatically updating your services to the latest image tag push. """
import logging
+import requests
import pause
import docker
from prometheus_client import start_http_server
@@ -21,8 +22,6 @@ class Cioban():
'sleep_time': '5m',
'prometheus_port': 9308,
'notifiers': [],
- 'telegram_chat_id': None,
- 'telegram_token': None,
'notify_include_new_image': False,
'notify_include_old_image': False,
}
@@ -34,7 +33,7 @@ def __init__(self, **kwargs):
if k in self.settings:
self.settings[k] = v
else:
- log.debug(f'{k} not found in settings')
+ log.debug(f'{k} not found in settings. Ignoring.')
prometheus.PROM_INFO.info({'version': f'{constants.VERSION}'})
@@ -66,7 +65,6 @@ def __init__(self, **kwargs):
if notifier.lower() in k.lower():
notifier_options.update({k.lower(): v})
self.notifiers.register(notifier, **notifier_options)
- log.debug('Registered {}'.format(notifier))
log.debug('Cioban initialized')
@@ -88,7 +86,7 @@ def __get_updated_image(self, image, image_sha):
updated_image = None
try:
registry_data = self.docker.images.get_registry_data(image)
- except docker.errors.APIError as error:
+ except (docker.errors.APIError, requests.exceptions.ReadTimeout) as error:
log.error(f'Failed to retrieve the registry data for {image}. The error: {error}')
if registry_data:
@@ -191,3 +189,7 @@ def get_services(self):
log.debug(f'Blacklisted {blacklist_service}')
services.remove(service)
return services
+
+ def notify(self, **kwargs):
+ """ Sends a notification through the registered notifiers """
+ self.notifiers.notify(**kwargs)
diff --git a/cioban/notifiers/core.py b/cioban/notifiers/core.py
index 1ac4349..808e5bd 100644
--- a/cioban/notifiers/core.py
+++ b/cioban/notifiers/core.py
@@ -27,6 +27,7 @@ class CoreNotifiers():
notifiers = [
'telegram',
+ 'gotify',
]
registered = []
@@ -37,11 +38,12 @@ def register(self, notifier, **kwargs):
for n in self.notifiers:
if n == notifier:
instance = importlib.import_module(f'cioban.notifiers.{notifier}_notifier')
- self.registered.append(instance.Notifier(**kwargs))
+ self.registered.append({notifier: instance.Notifier(**kwargs)})
log.debug(f'Registered {notifier}')
def notify(self, **kwargs):
""" dispatches a notification to the registered notifiers """
- log.debug('Sending notification')
- for notifier in self.registered:
- notifier.notify(**kwargs)
+ for i in self.registered:
+ for notifier in i:
+ log.debug(f'Sending notification to {notifier}')
+ i[notifier].notify(**kwargs)
diff --git a/cioban/notifiers/gotify_notifier.py b/cioban/notifiers/gotify_notifier.py
new file mode 100644
index 0000000..7e1d422
--- /dev/null
+++ b/cioban/notifiers/gotify_notifier.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+""" Gotify """
+
+import logging
+from urllib.parse import urljoin
+import requests
+from . import core
+
+log = logging.getLogger('cioban')
+
+
+class Notifier():
+ """ The notify class """
+
+ def __init__(self, **kwargs):
+ self.token = kwargs['gotify_token']
+ self.url = urljoin(kwargs['gotify_url'], f'/message?token={self.token}')
+ log.debug(f"Initialized with {kwargs['gotify_url']}")
+
+ def __post_message(self, title, message):
+ """ sends the notification to telegram """
+ try:
+ resp = requests.post(self.url, json={
+ 'title': title,
+ 'message': message,
+ })
+ except requests.exceptions.RequestException as e:
+ # Print exception if reqeuest fails
+ log.error(f'Could not connect to Gotify server. The error: {e}')
+
+ # Print request result if server returns http error code
+ if resp.status_code is not requests.codes.ok: # pylint: disable=no-member
+ log.error(f'{bytes.decode(resp.content)}')
+ else:
+ log.info("Sent message to gotify")
+ log.debug(f"Message: {message}")
+
+ def notify(self, title="CIOBAN: Service Updated", **kwargs):
+ """ parses the arguments, formats the message and dispatches it """
+ log.debug('Sending message to gotify')
+ message = ""
+ if kwargs.get('message'):
+ message = kwargs['message']
+ else:
+ for k, v in kwargs.items():
+ message += f'{core.key_to_title(k)}: {v}\n'
+ self.__post_message(title, message)
+
+ def noop(self):
+ """ Does nothing """
diff --git a/cioban/notifiers/telegram_notifier.py b/cioban/notifiers/telegram_notifier.py
index bfb76bf..69f47d6 100644
--- a/cioban/notifiers/telegram_notifier.py
+++ b/cioban/notifiers/telegram_notifier.py
@@ -58,9 +58,12 @@ def __post_message(self, message):
def notify(self, title="CIOBAN: Service Updated", **kwargs):
""" parses the arguments, formats the message and dispatches it """
log.debug('Sending notification to telegram')
- message = '{0} {1} {0}\n'.format(u'\U00002611', title)
- for k, v in kwargs.items():
- message += '{}: {}\n'.format(core.key_to_title(k), v)
+ message = f'{title}\n'
+ if kwargs.get('message'):
+ message += kwargs['message']
+ else:
+ for k, v in kwargs.items():
+ message += f'{core.key_to_title(k)}: {v}\n'
self.__post_message(message)
def noop(self):