diff --git a/app/deploy.py b/app/deploy.py index 0ebcfe18..bb45520a 100644 --- a/app/deploy.py +++ b/app/deploy.py @@ -13,7 +13,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -# Deploys the system components using docker-compose +# Deploys the system components using a deployer (either docker-compose or k8s) import hashlib import copy @@ -22,10 +22,10 @@ import sys from dataclasses import dataclass from importlib import resources import subprocess -from python_on_whales import DockerClient, DockerException import click from pathlib import 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.deployment_create import create as deployment_create 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): cluster_context = _make_cluster_context(global_context, stack, include, exclude, cluster, env_file) # 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, - compose_env_file=cluster_context.env_file) - return DeployCommandContext(stack, cluster_context, docker) + deployer = Deployer(compose_files=cluster_context.compose_files, compose_project_name=cluster_context.cluster, + compose_env_file=cluster_context.env_file) + return DeployCommandContext(stack, cluster_context, deployer) 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}") for pre_start_command in cluster_context.pre_start_commands: _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: _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): @@ -89,7 +89,7 @@ def down_operation(ctx, delete_volumes, extra_args_list): if extra_args_list: timeout_arg = extra_args_list[0] # 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): @@ -97,7 +97,7 @@ def ps_operation(ctx): if not global_context.dry_run: if global_context.verbose: print("Running compose ps") - container_list = ctx.obj.docker.compose.ps() + container_list = ctx.obj.deployer.compose.ps() if len(container_list) > 0: print("Running containers:") for container in container_list: @@ -128,7 +128,7 @@ def port_operation(ctx, extra_args): exposed_port = extra_args_list[1] if global_context.verbose: 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]}") @@ -145,8 +145,8 @@ def exec_operation(ctx, extra_args): if global_context.verbose: print(f"Running compose exec {service_name} {command_to_exec}") try: - ctx.obj.docker.compose.execute(service_name, command_to_exec, envs=container_exec_env) - except DockerException: + ctx.obj.deployer.compose.execute(service_name, command_to_exec, envs=container_exec_env) + except DeployerException: 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: print("Running compose logs") 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: print(stream_content.decode("utf-8"), end="") @@ -214,11 +214,11 @@ def get_stack_status(ctx, stack): ctx_copy.stack = stack 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 if ctx.verbose: print("Running compose ps") - container_list = docker.compose.ps() + container_list = deployer.compose.ps() if len(container_list) > 0: if ctx.debug: print(f"Container list from compose ps: {container_list}") @@ -359,7 +359,7 @@ def _run_command(ctx, cluster_name, command): 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 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 source_value = None try: - source_value = docker.compose.execute(pd.source_container, + source_value = deployer.compose.execute(pd.source_container, ["sh", "-c", "sh /docker-entrypoint-scripts.d/export-" f"{pd.source_variable}.sh"], tty=False, envs=container_exec_env) - except DockerException as error: + except DeployerException as error: if ctx.debug: print(f"Docker exception reading config source: {error}") # 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 ctx.debug: 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", f"sh /scripts/import-{pd.destination_variable}.sh" f" {source_value}"], diff --git a/app/deploy_docker.py b/app/deploy_docker.py new file mode 100644 index 00000000..8db3fa54 --- /dev/null +++ b/app/deploy_docker.py @@ -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 . + +from python_on_whales import DockerClient, DockerException + diff --git a/app/deploy_k8s.py b/app/deploy_k8s.py new file mode 100644 index 00000000..48562508 --- /dev/null +++ b/app/deploy_k8s.py @@ -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 . + +from kubernetes import client, config + diff --git a/app/deploy_types.py b/app/deploy_types.py index 63f32762..ecc0d5a3 100644 --- a/app/deploy_types.py +++ b/app/deploy_types.py @@ -16,8 +16,8 @@ from typing import List from dataclasses import dataclass from pathlib import Path -from python_on_whales import DockerClient from app.command_types import CommandOptions +from app.deployer import Deployer @dataclass @@ -35,7 +35,7 @@ class ClusterContext: class DeployCommandContext: stack: str cluster_context: ClusterContext - docker: DockerClient + deployer: Deployer @dataclass diff --git a/app/deployer.py b/app/deployer.py new file mode 100644 index 00000000..76416125 --- /dev/null +++ b/app/deployer.py @@ -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 . + +from abc import ABC, abstractmethod + +class Deployer(ABC): + + @abstractmethod + def (self, purchase): + pass + + +class DeployerException(Exception) { + +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 7f60f9dd..bf4845a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ ruamel.yaml>=0.17.32 pydantic==1.10.9 tomli==2.0.1 validators==0.22.0 +kubernetes>=28.1.0