stackup/stackup/__main__.py

111 lines
3.4 KiB
Python

import logging
import socket
import sys
from typing import List
import docker.models
import stackup.__about__
import stackup.config
import stackup.errors
def determine_volumes(
client: docker.DockerClient,
local_container: docker.models.containers.Container,
is_swarm: bool,
) -> List[docker.models.volumes.Volume]:
logger = logging.getLogger(__name__)
if is_swarm:
stack_label = "com.docker.stack.namespace"
else:
stack_label = "com.docker.compose.project"
stack = local_container.labels[stack_label]
logger.debug(
f"Identified local stack as '{stack}' from namespace label '{stack_label}' on local container {local_container.id}"
)
# Primary filter (via docker) is for volumes in the detected stack
# Secondary filter (via the list comp) is for volumes that have the label
# that enables them for stackup processing. The end result is that ``volumes``
# is a list of volumes that are enabled for stackup processing in the current
# stack
volumes = [
item
for item in client.volumes.list(filters={"label": f"{stack_label}={stack}"})
if item.attrs["Labels"].get("stackup.enable")
]
logger.info(
f"Identified {len(volumes)} in stack '{stack}' for backup: {', '.join([item.attrs['Name']] for item in volumes)}"
)
if not volumes:
raise stackup.errors.NoVolumesEnabled
# Determine which (if any) volumes are missing from the current container
local_volumes = {item["Name"]: item for item in local_container.attrs["Mounts"]}
logger.debug(
f"Identified {len(local_volumes)} volumes mounted into local container {local_container.id}: {', '.join(local_volumes.keys())}"
)
missing = [
item.attrs["Name"]
for item in volumes
if item.attrs["Name"] not in local_volumes
]
if missing:
raise stackup.errors.EnabledVolumeNotMountedError(
f"One or more volumes enabled for backup in stack '{stack}' are not mounted in the current container ({local_container.id}): {', '.join(missing)}"
)
return volumes
def main() -> int:
config = stackup.config.StackupConfig.build()
logging.basicConfig(
format="%(levelname)s: %(message)s",
level=config.log_level,
)
logger = logging.getLogger(__name__)
logger.info(
f"Starting {stackup.__about__.__title__} v{stackup.__about__.__version__}"
)
logger.debug(config)
logger.debug("Loading Docker client from local environment")
client = docker.from_env()
logger.debug(f"Connected to Docker daemon at {client.api.base_url}")
# Determine whether we're operating in swarm mode
try:
client.swarm.version
except TypeError:
is_swarm = False
logger.debug(f"Daemon at {client.api.base_url} is not bound to a swarm")
else:
is_swarm = True
logger.debug(
f"Daemon at {client.api.base_url} is bound to swarm {client.swarm.id}"
)
local_container = client.containers.get(socket.gethostname())
logger.debug(f"Identified local container as {local_container.id}")
try:
volumes = determine_volumes(client, local_container, is_swarm)
except stackup.errors.NoVolumesEnabled:
return 0
except stackup.errors.StackupError as err:
logger.error(str(err))
return 1
return 0
if __name__ == "__main__":
sys.exit(main())