diff --git a/README.md b/README.md --- a/README.md +++ b/README.md @@ -63,6 +63,43 @@ You can also create a check calling `check_http_200` without argument, and it will test every site. +### check_container_present + +#### Run the check + +With argument, check if the specified Docker container is running: +`check_container_present foo` + +Without argument, compare the list of containers present with +the expected one. + +The configuration is only required if you use it without argument. + +#### Available check types + +* check_docker_containers: a list of expected containers + +#### Configuration example + +```yaml +checks: + check_docker_containers: + - foo + - bar +``` + +#### Requirements + +A Docker engine with CLI restructured, ie Docker 1.13+, is needed: + * To list the containers it uses `docker container ls`. + * To get more info on a container down, + it uses `docker container inspect`. + +#### Not features + +This check isn't intended to detect containers +run with other engines like `systemd-nspawn`. + ## Return values of checks The checks use the standard Nagios/NRPE exit codes: diff --git a/bin/check_container_present b/bin/check_container_present new file mode 100755 --- /dev/null +++ b/bin/check_container_present @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 + +# ------------------------------------------------------------- +# Platform checks - Containers +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Project: Nasqueron +# Description: Check if a container is present +# License: BSD-2-Clause +# ------------------------------------------------------------- + + +import sys + +from platformchecks import exitcode +from platformchecks.checks import DockerContainerCheck +from platformchecks.config import parse_config + + +# ------------------------------------------------------------- +# Configuration +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +def get_containers(): + return parse_config().get("checks", {}).get("check_docker_containers", []) + + +# ------------------------------------------------------------- +# Application entry point +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +def initialize_check_or_die(): + check = DockerContainerCheck() + if not check.initialize(): + print("UNKNOWN", check.error, file=sys.stderr) + sys.exit(exitcode.UNKNOWN) + + return check + + +def run_all(check, containers): + success = True + messages = [] + + for container in containers: + container_success, message = check.perform(container) + + success &= container_success + messages.append(message) + + print("\n".join(messages)) + return exitcode.ok_or_critical(success) + + +def run(check, container): + success, message = check.perform(container) + + print(message) + return exitcode.ok_or_critical(success) + + +if __name__ == "__main__": + argc = len(sys.argv) + + container_check = initialize_check_or_die() + + if argc < 2: + exitCode = run_all(container_check, get_containers()) + else: + exitCode = run(container_check, sys.argv[1]) + + sys.exit(exitCode) diff --git a/setup.cfg b/setup.cfg --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = platform-checks -version = 0.1.1 +version = 0.1.2 author = Sébastien Santoro author_email = dereckson@espace-win.org description = Platform checks NRPE / Nagios diff --git a/src/platformchecks/checks/__init__.py b/src/platformchecks/checks/__init__.py --- a/src/platformchecks/checks/__init__.py +++ b/src/platformchecks/checks/__init__.py @@ -1 +1,2 @@ from .http import HttpCheck +from .docker import DockerContainerCheck diff --git a/src/platformchecks/checks/docker.py b/src/platformchecks/checks/docker.py new file mode 100644 --- /dev/null +++ b/src/platformchecks/checks/docker.py @@ -0,0 +1,71 @@ +# ------------------------------------------------------------- +# Platform checks +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Project: Nasqueron +# Description: Check if a container is up +# License: BSD-2-Clause +# ------------------------------------------------------------- + + +import json +import subprocess + + +def inspect_container(container): + p = subprocess.run( + ["docker", "container", "inspect", container], capture_output=True + ) + + if p.returncode > 0: + raise ValueError(p.stderr.decode().strip()) + + return json.loads(p.stdout)[0] + + +def describe_state(state): + return f"{state['Status']} ({state['ExitCode']}) {state['FinishedAt']}" + + +class DockerContainerCheck: + def __init__(self): + self.containers = [] + + self.error = "" + + def initialize(self): + p = subprocess.run( + "docker container ls | awk '(NR>1) {print $NF}'", + shell=True, + capture_output=True, + ) + + if p.stderr: + self.error = p.stderr.decode().strip() + return False + + self.containers = p.stdout.decode().strip().split("\n") + return True + + def perform(self, container): + return self.has(container), self.build_message(container) + + def has(self, container): + return container in self.containers + + def build_message(self, container): + if self.has(container): + return f"{container} UP" + + message = f"{container} DOWN" + + try: + # Returns container state lik + state = inspect_container(container)["State"] + message += " " + describe_state(state) + except ValueError as ex: + # Detect cases when the container doesn't exist + message += " " + str(ex) + except subprocess.CalledProcessError: + pass + + return message diff --git a/src/platformchecks/exitcode.py b/src/platformchecks/exitcode.py --- a/src/platformchecks/exitcode.py +++ b/src/platformchecks/exitcode.py @@ -11,3 +11,11 @@ WARNING = 1 CRITICAL = 2 UNKNOWN = 3 + + +def ok_or_critical(is_successful): + return OK if is_successful else CRITICAL + + +def ok_or_warning(is_successful): + return OK if is_successful else WARNING