add docs
This commit is contained in:
@@ -16,8 +16,13 @@ logger.setLevel(logging.DEBUG) #TODO: add something to change log levles
|
|||||||
|
|
||||||
class Setup:
|
class Setup:
|
||||||
|
|
||||||
|
"""Setup class ensuring appropriate files and symlinks are created for a Composer instance"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_symlink():
|
def create_symlink():
|
||||||
|
|
||||||
|
"""Creates a symlink between the path stored in HOST_DATA_PATH and store/data"""
|
||||||
|
|
||||||
host_data_env = getenv("HOST_DATA_PATH", None)
|
host_data_env = getenv("HOST_DATA_PATH", None)
|
||||||
if host_data_env is None:
|
if host_data_env is None:
|
||||||
logger.info(f"HOST_DATA_PATH is not set, symlink not created")
|
logger.info(f"HOST_DATA_PATH is not set, symlink not created")
|
||||||
@@ -35,25 +40,42 @@ class Setup:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def populate_store():
|
def populate_store():
|
||||||
|
|
||||||
|
"""Creates empty store/stack/.env file if it doesn't exist"""
|
||||||
|
|
||||||
Path("store/stack").mkdir(parents=True, exist_ok=True)
|
Path("store/stack").mkdir(parents=True, exist_ok=True)
|
||||||
Path("store/stack/.env").touch(exist_ok=True)
|
Path("store/stack/.env").touch(exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
class Composer:
|
class Composer:
|
||||||
|
|
||||||
|
"""Composer instance to manage a single docker compose stack
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
docker: DockerClient instance used to interact with the compose stack
|
||||||
|
services: list of Service objects defined by the stack
|
||||||
|
"""
|
||||||
|
|
||||||
services = []
|
services = []
|
||||||
|
docker = DockerClient(
|
||||||
|
compose_files=["store/stack/compose.yml"],
|
||||||
|
compose_env_files=["store/stack/.env"]
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, remote_url):
|
def __init__(self, remote_url):
|
||||||
|
|
||||||
|
"""Initializes the Composer based on a remote_url
|
||||||
|
|
||||||
|
Args:
|
||||||
|
remote_url: string containing the https location of the repo
|
||||||
|
"""
|
||||||
|
|
||||||
if not Path("store/stack/.git").exists():
|
if not Path("store/stack/.git").exists():
|
||||||
self.repo = Repo.init("store/stack")
|
self.repo = Repo.init("store/stack")
|
||||||
self._add_remote(remote_url)
|
self.repo.create_remote('origin', url)
|
||||||
else:
|
else:
|
||||||
self.repo = Repo("store/stack")
|
self.repo = Repo("store/stack")
|
||||||
self.repo.remotes.origin.pull('master')
|
self.repo.remotes.origin.pull('master')
|
||||||
self.docker = DockerClient(
|
|
||||||
compose_files=["store/stack/compose.yml"],
|
|
||||||
compose_env_files=["store/stack/.env"]
|
|
||||||
)
|
|
||||||
if self.docker.compose.config(return_json=True)["name"] != "stack":
|
if self.docker.compose.config(return_json=True)["name"] != "stack":
|
||||||
logger.warn(f"Composer stack name is wrong, either make sure it is set manually or keep it store/stack")
|
logger.warn(f"Composer stack name is wrong, either make sure it is set manually or keep it store/stack")
|
||||||
self.start_update_job()
|
self.start_update_job()
|
||||||
@@ -61,16 +83,22 @@ class Composer:
|
|||||||
self.set_status("up")
|
self.set_status("up")
|
||||||
logger.info("Composer started and services created")
|
logger.info("Composer started and services created")
|
||||||
|
|
||||||
def _add_remote(self, url):
|
|
||||||
self.repo.create_remote('origin', url)
|
|
||||||
|
|
||||||
def _create_services(self):
|
def _create_services(self):
|
||||||
|
|
||||||
|
"""Recreates service list after the Composer stack is updated"""
|
||||||
|
|
||||||
for service in self.services:
|
for service in self.services:
|
||||||
self.services[service].close()
|
self.services[service].close()
|
||||||
services_dict = self.docker.compose.config().services
|
services_dict = self.docker.compose.config().services
|
||||||
self.services = {service: Service(service, services_dict[service].labels, self) for service in services_dict}
|
self.services = {service: Service(service, services_dict[service].labels, self) for service in services_dict}
|
||||||
|
|
||||||
def start_update_job(self):
|
def start_update_job(self):
|
||||||
|
|
||||||
|
"""Schedules a recurring update based on the UPDATE environment variable
|
||||||
|
|
||||||
|
Each update occurs after an interval of UPDATE minutes. If UPDATE does not contain a valid integer in minutes, defaults to 1 minute.
|
||||||
|
"""
|
||||||
|
|
||||||
update_time = getenv("UPDATE", 1)
|
update_time = getenv("UPDATE", 1)
|
||||||
try:
|
try:
|
||||||
self.update_time = int(update_time)
|
self.update_time = int(update_time)
|
||||||
@@ -81,6 +109,13 @@ class Composer:
|
|||||||
schedule.every(self.update_time).minutes.do(self.update)
|
schedule.every(self.update_time).minutes.do(self.update)
|
||||||
|
|
||||||
def set_status(self, status):
|
def set_status(self, status):
|
||||||
|
|
||||||
|
"""Sets the running status of the compose stack
|
||||||
|
|
||||||
|
Args:
|
||||||
|
status: string containing "up", "down", or "restart" corresponding to the respective running state of the composer
|
||||||
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if status == "up":
|
if status == "up":
|
||||||
self.docker.compose.up(
|
self.docker.compose.up(
|
||||||
@@ -103,6 +138,9 @@ class Composer:
|
|||||||
logger.critical(f"Setting status of composer failed: {error}")
|
logger.critical(f"Setting status of composer failed: {error}")
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
|
||||||
|
"""Updates the Composer stack based on the remote repo"""
|
||||||
|
|
||||||
current = self.repo.head.commit
|
current = self.repo.head.commit
|
||||||
self.repo.remotes.origin.pull('master')
|
self.repo.remotes.origin.pull('master')
|
||||||
if current != self.repo.head.commit:
|
if current != self.repo.head.commit:
|
||||||
@@ -112,23 +150,54 @@ class Composer:
|
|||||||
|
|
||||||
class Service:
|
class Service:
|
||||||
|
|
||||||
update_task = None
|
"""Service instance meant to represent a container in a Composer stack
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
labels: labels given to the service
|
||||||
|
name: name given to the service
|
||||||
|
parent: Composer instance containing the service
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, labels, parent):
|
def __init__(self, name, labels, parent):
|
||||||
|
|
||||||
|
"""Initializes the Service instance
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: name given to the service
|
||||||
|
labels: labels given to the service
|
||||||
|
parent: Composer instance containing the service
|
||||||
|
"""
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.labels = labels
|
self.labels = labels
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.update_task = self.start_update_job()
|
self._update_task = self.start_update_job()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
|
||||||
|
"""Safely removes the service instance"""
|
||||||
|
|
||||||
if self.update_time is None:
|
if self.update_time is None:
|
||||||
return None
|
return None
|
||||||
schedule.cancel_job(self.update_task)
|
schedule.cancel_job(self._update_task)
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
|
|
||||||
|
"""Gets the running status of the service
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
"up" or "down" depending on the running status of the service
|
||||||
|
"""
|
||||||
|
|
||||||
return "up" if bool(self.parent.docker.compose.ps(services=[self.name])) else "down"
|
return "up" if bool(self.parent.docker.compose.ps(services=[self.name])) else "down"
|
||||||
|
|
||||||
def start_update_job(self):
|
def start_update_job(self):
|
||||||
|
|
||||||
|
"""Schedules a recurring update based on the composer.update label
|
||||||
|
|
||||||
|
Each update occurs after an interval of composer.update minutes. If composer.update does not contain a valid integer in minutes, defaults to the parent Composer's update_time.
|
||||||
|
"""
|
||||||
|
|
||||||
self.update_time = self.parent.update_time
|
self.update_time = self.parent.update_time
|
||||||
if 'composer.update' in self.labels:
|
if 'composer.update' in self.labels:
|
||||||
try:
|
try:
|
||||||
@@ -142,6 +211,13 @@ class Service:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def set_status(self, status):
|
def set_status(self, status):
|
||||||
|
|
||||||
|
"""Sets the running status of the service
|
||||||
|
|
||||||
|
Args:
|
||||||
|
status: string containing "up", "down", or "restart" corresponding to the respective running state of the service
|
||||||
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if status == "up":
|
if status == "up":
|
||||||
self.parent.docker.compose.up(
|
self.parent.docker.compose.up(
|
||||||
@@ -166,14 +242,9 @@ class Service:
|
|||||||
logger.critical(error)
|
logger.critical(error)
|
||||||
|
|
||||||
def update(self, restart=True):
|
def update(self, restart=True):
|
||||||
try:
|
|
||||||
self.parent.docker.compose.build(
|
"""Updates the Composer stack based on container images"""
|
||||||
quiet=True,
|
|
||||||
services=[self.name]
|
|
||||||
)
|
|
||||||
except DockerException:
|
|
||||||
logger.warn(f"Failed to build docker image for service {self.name} - cancelling container update")
|
|
||||||
return None
|
|
||||||
self.parent.docker.compose.pull(
|
self.parent.docker.compose.pull(
|
||||||
ignore_pull_failures=True,
|
ignore_pull_failures=True,
|
||||||
quiet=True,
|
quiet=True,
|
||||||
|
|||||||
Reference in New Issue
Block a user