Initial refactor

This commit is contained in:
David Boreham 2023-10-19 15:22:12 -06:00
parent 69b071ee7a
commit f55ca7ff5b
6 changed files with 83 additions and 21 deletions

View File

@ -13,7 +13,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http:#www.gnu.org/licenses/>. # along with this program. If not, see <http:#www.gnu.org/licenses/>.
# Deploys the system components using docker-compose # Deploys the system components using a deployer (either docker-compose or k8s)
import hashlib import hashlib
import copy import copy
@ -22,10 +22,10 @@ import sys
from dataclasses import dataclass from dataclasses import dataclass
from importlib import resources from importlib import resources
import subprocess import subprocess
from python_on_whales import DockerClient, DockerException
import click import click
from pathlib import Path from pathlib import Path
from app.util import include_exclude_check, get_parsed_stack_config, global_options2, get_dev_root_path from app.util import include_exclude_check, get_parsed_stack_config, global_options2, get_dev_root_path
from app.deployer import Deployer, DeployerException
from app.deploy_types import ClusterContext, DeployCommandContext from app.deploy_types import ClusterContext, DeployCommandContext
from app.deployment_create import create as deployment_create from app.deployment_create import create as deployment_create
from app.deployment_create import init as deployment_init from app.deployment_create import init as deployment_init
@ -57,9 +57,9 @@ def command(ctx, include, exclude, env_file, cluster):
def create_deploy_context(global_context, stack, include, exclude, cluster, env_file): def create_deploy_context(global_context, stack, include, exclude, cluster, env_file):
cluster_context = _make_cluster_context(global_context, stack, include, exclude, cluster, env_file) cluster_context = _make_cluster_context(global_context, stack, include, exclude, cluster, env_file)
# See: https://gabrieldemarmiesse.github.io/python-on-whales/sub-commands/compose/ # See: https://gabrieldemarmiesse.github.io/python-on-whales/sub-commands/compose/
docker = DockerClient(compose_files=cluster_context.compose_files, compose_project_name=cluster_context.cluster, deployer = Deployer(compose_files=cluster_context.compose_files, compose_project_name=cluster_context.cluster,
compose_env_file=cluster_context.env_file) compose_env_file=cluster_context.env_file)
return DeployCommandContext(stack, cluster_context, docker) return DeployCommandContext(stack, cluster_context, deployer)
def up_operation(ctx, services_list, stay_attached=False): def up_operation(ctx, services_list, stay_attached=False):
@ -74,10 +74,10 @@ def up_operation(ctx, services_list, stay_attached=False):
print(f"Running compose up with container_exec_env: {container_exec_env}, extra_args: {services_list}") print(f"Running compose up with container_exec_env: {container_exec_env}, extra_args: {services_list}")
for pre_start_command in cluster_context.pre_start_commands: for pre_start_command in cluster_context.pre_start_commands:
_run_command(global_context, cluster_context.cluster, pre_start_command) _run_command(global_context, cluster_context.cluster, pre_start_command)
deploy_context.docker.compose.up(detach=not stay_attached, services=services_list) deploy_context.deployer.compose.up(detach=not stay_attached, services=services_list)
for post_start_command in cluster_context.post_start_commands: for post_start_command in cluster_context.post_start_commands:
_run_command(global_context, cluster_context.cluster, post_start_command) _run_command(global_context, cluster_context.cluster, post_start_command)
_orchestrate_cluster_config(global_context, cluster_context.config, deploy_context.docker, container_exec_env) _orchestrate_cluster_config(global_context, cluster_context.config, deploy_context.deployer, container_exec_env)
def down_operation(ctx, delete_volumes, extra_args_list): def down_operation(ctx, delete_volumes, extra_args_list):
@ -89,7 +89,7 @@ def down_operation(ctx, delete_volumes, extra_args_list):
if extra_args_list: if extra_args_list:
timeout_arg = extra_args_list[0] timeout_arg = extra_args_list[0]
# Specify shutdown timeout (default 10s) to give services enough time to shutdown gracefully # Specify shutdown timeout (default 10s) to give services enough time to shutdown gracefully
ctx.obj.docker.compose.down(timeout=timeout_arg, volumes=delete_volumes) ctx.obj.deployer.compose.down(timeout=timeout_arg, volumes=delete_volumes)
def ps_operation(ctx): def ps_operation(ctx):
@ -97,7 +97,7 @@ def ps_operation(ctx):
if not global_context.dry_run: if not global_context.dry_run:
if global_context.verbose: if global_context.verbose:
print("Running compose ps") print("Running compose ps")
container_list = ctx.obj.docker.compose.ps() container_list = ctx.obj.deployer.compose.ps()
if len(container_list) > 0: if len(container_list) > 0:
print("Running containers:") print("Running containers:")
for container in container_list: for container in container_list:
@ -128,7 +128,7 @@ def port_operation(ctx, extra_args):
exposed_port = extra_args_list[1] exposed_port = extra_args_list[1]
if global_context.verbose: if global_context.verbose:
print(f"Running compose port {service_name} {exposed_port}") print(f"Running compose port {service_name} {exposed_port}")
mapped_port_data = ctx.obj.docker.compose.port(service_name, exposed_port) mapped_port_data = ctx.obj.deployer.compose.port(service_name, exposed_port)
print(f"{mapped_port_data[0]}:{mapped_port_data[1]}") print(f"{mapped_port_data[0]}:{mapped_port_data[1]}")
@ -145,8 +145,8 @@ def exec_operation(ctx, extra_args):
if global_context.verbose: if global_context.verbose:
print(f"Running compose exec {service_name} {command_to_exec}") print(f"Running compose exec {service_name} {command_to_exec}")
try: try:
ctx.obj.docker.compose.execute(service_name, command_to_exec, envs=container_exec_env) ctx.obj.deployer.compose.execute(service_name, command_to_exec, envs=container_exec_env)
except DockerException: except DeployerException:
print("container command returned error exit status") print("container command returned error exit status")
@ -157,7 +157,7 @@ def logs_operation(ctx, tail: int, follow: bool, extra_args: str):
if global_context.verbose: if global_context.verbose:
print("Running compose logs") print("Running compose logs")
services_list = extra_args_list if extra_args_list is not None else [] services_list = extra_args_list if extra_args_list is not None else []
logs_stream = ctx.obj.docker.compose.logs(services=services_list, tail=tail, follow=follow, stream=True) logs_stream = ctx.obj.deployer.compose.logs(services=services_list, tail=tail, follow=follow, stream=True)
for stream_type, stream_content in logs_stream: for stream_type, stream_content in logs_stream:
print(stream_content.decode("utf-8"), end="") print(stream_content.decode("utf-8"), end="")
@ -214,11 +214,11 @@ def get_stack_status(ctx, stack):
ctx_copy.stack = stack ctx_copy.stack = stack
cluster_context = _make_cluster_context(ctx_copy, stack, None, None, None, None) cluster_context = _make_cluster_context(ctx_copy, stack, None, None, None, None)
docker = DockerClient(compose_files=cluster_context.compose_files, compose_project_name=cluster_context.cluster) deployer = Deployer(compose_files=cluster_context.compose_files, compose_project_name=cluster_context.cluster)
# TODO: refactor to avoid duplicating this code above # TODO: refactor to avoid duplicating this code above
if ctx.verbose: if ctx.verbose:
print("Running compose ps") print("Running compose ps")
container_list = docker.compose.ps() container_list = deployer.compose.ps()
if len(container_list) > 0: if len(container_list) > 0:
if ctx.debug: if ctx.debug:
print(f"Container list from compose ps: {container_list}") print(f"Container list from compose ps: {container_list}")
@ -359,7 +359,7 @@ def _run_command(ctx, cluster_name, command):
sys.exit(1) sys.exit(1)
def _orchestrate_cluster_config(ctx, cluster_config, docker, container_exec_env): def _orchestrate_cluster_config(ctx, cluster_config, deployer, container_exec_env):
@dataclass @dataclass
class ConfigDirective: class ConfigDirective:
@ -390,13 +390,13 @@ def _orchestrate_cluster_config(ctx, cluster_config, docker, container_exec_env)
# TODO: fix the script paths so they're consistent between containers # TODO: fix the script paths so they're consistent between containers
source_value = None source_value = None
try: try:
source_value = docker.compose.execute(pd.source_container, source_value = deployer.compose.execute(pd.source_container,
["sh", "-c", ["sh", "-c",
"sh /docker-entrypoint-scripts.d/export-" "sh /docker-entrypoint-scripts.d/export-"
f"{pd.source_variable}.sh"], f"{pd.source_variable}.sh"],
tty=False, tty=False,
envs=container_exec_env) envs=container_exec_env)
except DockerException as error: except DeployerException as error:
if ctx.debug: if ctx.debug:
print(f"Docker exception reading config source: {error}") print(f"Docker exception reading config source: {error}")
# If the script executed failed for some reason, we get: # If the script executed failed for some reason, we get:
@ -411,7 +411,7 @@ def _orchestrate_cluster_config(ctx, cluster_config, docker, container_exec_env)
if source_value: if source_value:
if ctx.debug: if ctx.debug:
print(f"fetched source value: {source_value}") print(f"fetched source value: {source_value}")
destination_output = docker.compose.execute(pd.destination_container, destination_output = deployer.compose.execute(pd.destination_container,
["sh", "-c", ["sh", "-c",
f"sh /scripts/import-{pd.destination_variable}.sh" f"sh /scripts/import-{pd.destination_variable}.sh"
f" {source_value}"], f" {source_value}"],

17
app/deploy_docker.py Normal file
View File

@ -0,0 +1,17 @@
# Copyright © 2022, 2023 Vulcanize
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
from python_on_whales import DockerClient, DockerException

17
app/deploy_k8s.py Normal file
View File

@ -0,0 +1,17 @@
# Copyright © 2023 Vulcanize
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
from kubernetes import client, config

View File

@ -16,8 +16,8 @@
from typing import List from typing import List
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from python_on_whales import DockerClient
from app.command_types import CommandOptions from app.command_types import CommandOptions
from app.deployer import Deployer
@dataclass @dataclass
@ -35,7 +35,7 @@ class ClusterContext:
class DeployCommandContext: class DeployCommandContext:
stack: str stack: str
cluster_context: ClusterContext cluster_context: ClusterContext
docker: DockerClient deployer: Deployer
@dataclass @dataclass

27
app/deployer.py Normal file
View File

@ -0,0 +1,27 @@
# Copyright © 2023 Vulcanize
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
from abc import ABC, abstractmethod
class Deployer(ABC):
@abstractmethod
def (self, purchase):
pass
class DeployerException(Exception) {
}

View File

@ -8,3 +8,4 @@ ruamel.yaml>=0.17.32
pydantic==1.10.9 pydantic==1.10.9
tomli==2.0.1 tomli==2.0.1
validators==0.22.0 validators==0.22.0
kubernetes>=28.1.0