Page MenuHomeDevCentral
Paste P303

roles/paas-docker/docker/files/resolve-containers-from-compose.py
ActivePublic

Authored by dereckson on Apr 11 2022, 17:13.
#!/usr/bin/env python3
# -------------------------------------------------------------
# Resolve docker-compose containers names
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
# Description: Guess by naive heuristics the containers
# run from a docker-compose YAML configuration.
#
# As Docker doesn't record any link between
# a docker compose or stack information and the
# running containers, the approach consist to
# compare the containers names in a config file,
# with the running containers on the machine.
# License: BSD-2-Clause
# -------------------------------------------------------------
import subprocess
import sys
import yaml
# -------------------------------------------------------------
# Parse docker-compose.yml
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def parse_compose_config(config_path):
with open(config_path) as fd:
return yaml.safe_load(fd)
def resolve_container_prefix(service, image):
image_short = image.split("/")[1]
return image_short + "_" + service + "_"
def resolve_containers_prefix(compose_config):
return [
resolve_container_prefix(name, args["image"])
for name, args in compose_config["services"].items()
]
# -------------------------------------------------------------
# Interact with Docker CLI
#
# This avoid to have to deal with version conflicts with docker
# Python libraries.
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def get_running_container_names():
process = subprocess.run(["docker", "ps"], capture_output=True)
if process.returncode != 0:
raise RuntimeError("Can't get containers: " + process.stderr.decode().strip())
# The output of docker ps starts by an header line to skip.
# Then the last column contains the container name.
docker_ps_lines = process.stdout.decode().strip().split("\n")[1:]
return [line.split()[-1] for line in docker_ps_lines]
# -------------------------------------------------------------
# Cross config file and running Docker information
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def is_container_name_match(config_container, running_container):
# The service web for the image acme/foo is foo_web_1:
# <image name>_<service name>_<number>
n = len(config_container)
return (
running_container.startswith(config_container)
and running_container[n:].isnumeric()
)
def resolve_docker_compose_containers_name(config_path):
config = parse_compose_config(config_path)
config_containers = resolve_containers_prefix(config)
running_containers = get_running_container_names()
return [
running_container
for config_container in config_containers
for running_container in running_containers
if is_container_name_match(config_container, running_container)
]
# -------------------------------------------------------------
# Application entry point
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def run(config_path):
containers = resolve_docker_compose_containers_name(config_path)
print(" ".join(containers))
if __name__ == "__main__":
argc = len(sys.argv)
if argc < 2:
print(f"Usage: {sys.argv[0]} <path to docker-compose.yml>", file=sys.stderr)
sys.exit(1)
run(sys.argv[1])