Page MenuHomeDevCentral
Paste P320

docker-paas-list-containers.py
ActivePublic

Authored by dereckson on Mar 27 2023, 19:42.
Tags
None
Referenced Files
F2194265: docker-paas-list-containers.py
Mar 27 2023, 19:42
F2194264: docker-paas-list-containers.py
Mar 27 2023, 19:42
Subscribers
None
#!/usr/bin/env python3
# -------------------------------------------------------------
# Docker PaaS - List of containers
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
# Description: Resolve the dependencies between containers
# into a graph, and use topological sorting
# to offer an order of containers.
#
# This can be used to run every container in
# the right order to avoid any link issue.
#
# License: BSD-2-Clause
# -------------------------------------------------------------
from graphlib import TopologicalSorter
import subprocess
import sys
import yaml
CONTAINER_DELIMITERS = [":"]
# -------------------------------------------------------------
# Resolve dependencies
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def solve_containers_order(containers, dependencies):
graph = {}
for service, instances in containers.items():
if service not in dependencies:
graph |= {instance: [] for instance in instances}
continue
rules = dependencies[service]
for instance, args in instances.items():
graph[instance] = solve_dependencies(containers, rules, instance, args)
return list(TopologicalSorter(graph).static_order())
def solve_dependencies(containers, rules, instance, args):
other_instances = []
if "depends_of_containers" in rules:
other_instances += [
get_value(args, container_key)
for container_key in rules["depends_of_containers"]
]
if "depends_of_services" in rules:
if "network" not in args:
print(
f"[WARN] Can't resolve depends_of_services rule for instance {instance} as no network is specified.",
file=sys.stderr,
)
return other_instances
for service in rules["depends_of_services"]:
for other_instance, other_args in containers[service].items():
if "network" not in other_args:
# For example, pixelfed_redis isn't on a network
continue
if args["network"] == other_args["network"]:
other_instances.append(other_instance)
return other_instances
# -------------------------------------------------------------
# Helper methods to get and clean container values
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def clean_value(container):
for delimiter in CONTAINER_DELIMITERS:
pos = container.find(delimiter)
if pos > -1:
container = container[:pos]
return container
def get_value(args, key):
if "." in key:
pos = key.find(".")
return get_value(args[key[:pos]], key[pos + 1 :])
try:
return clean_value(args[key])
except KeyError:
print(f"[FATAL] No {key} in {args}", file=sys.stderr)
sys.exit(1)
# -------------------------------------------------------------
# Helper methods to query Salt pillar
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def query_pillar(key):
cmd = ["sudo", "salt-call", "pillar.get", key, "--out", "yaml"]
process = subprocess.run(cmd, capture_output=True)
if process.returncode != 0:
raise RuntimeError("Can't query Salt: " + process.stderr)
return yaml.safe_load(process.stdout)["local"]
# -------------------------------------------------------------
# Application entry point
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def run():
try:
containers = query_pillar("docker_containers")
dependencies = query_pillar("docker_containers_dependencies")
except RuntimeError as e:
print(e, file=sys.stderr)
sys.exit(2)
for container in solve_containers_order(containers, dependencies):
print(container)
if __name__ == "__main__":
run()