diff --git a/src/lxc/creation_utils.py b/src/lxc/creation_utils.py index 28442b1..6f7a657 100644 --- a/src/lxc/creation_utils.py +++ b/src/lxc/creation_utils.py @@ -6,6 +6,7 @@ from typing import TYPE_CHECKING from . import lxc_utils from ..utils.resources_utils import get_path +from ..utils.utils import optional if TYPE_CHECKING: from .lxc import LXC @@ -58,11 +59,6 @@ def are_all_conditions_met(lxc: LXC): for condition_type in step["conditions"]: result = check_conditions(c_type=condition_type, parent=step["conditions"], lxc=lxc, result=result) - if all(result): - logging.info(f"All creations conditions met for LXC {lxc.lxc_id}, running deploy steps...") - else: - logging.info(f"Not all creations conditions met for LXC {lxc.lxc_id}, running creation steps...") - return all(result) @@ -121,35 +117,13 @@ def check_conditions_for_step(step: dict, lxc: LXC): result = [] if "conditions" in step: for condition in step['conditions']: - result.append(check_conditions(condition, parent=step['conditions'], lxc=lxc, result=result)) + result = check_conditions(condition, parent=step['conditions'], lxc=lxc, result=result) else: - return True + return False return all(result) -def optional(var: any, placeholder: any): - """Return a placeholder if the given variable is None, otherwise return the variable - - Parameters - ---------- - var: any - Variable to check if it is None - placeholder: any - Placeholder to return if the variable is None - - Returns - ------- - any[any] - The variable if it is not None, otherwise the placeholder - """ - - if var is None: - return placeholder - else: - return var - - def run_steps(lxc: LXC): """Run the creation steps for the given LXC @@ -164,43 +138,44 @@ def run_steps(lxc: LXC): for index, step in enumerate(creation_steps): - logging.info(f"Running step {index + 1}/{len(creation_steps)} for LXC {lxc.get_id()}...") if check_conditions_for_step(step, lxc): logging.info(f"Conditions already met for step {index + 1}/{len(creation_steps)} for LXC {lxc.get_id()}, " f"skipping...") - break + continue + + logging.info(f"Running step {index + 1}/{len(creation_steps)} for LXC {lxc.get_id()}...") match step["type"]: # Support for scripts case "script": - lxc_utils.run_script_parser(lxc, step) + lxc_utils.run_script_step_parser(lxc, step) case "file_create": lxc.create_file(step["path"], optional(step["permission"], 644)) case "file_copy": - commands_utils.copy_local_file_to_lxc(lxc, get_path(lxc, step["path"]), step["destination"]) + lxc.pve.copy_file_to_lxc(lxc, get_path(lxc, step["path"]), step["destination"]) case "folder": - commands_utils.create_folder_in_lxc(lxc, step["path"], optional(step["permission"], 755)) + lxc.create_directory(step["path"], optional(step["permission"], 755)) case "folder_copy": - commands_utils.copy_local_folder_to_lxc(lxc, get_path(lxc, step["path"]), step["destination"]) + lxc.pve.copy_folder_to_lxc(lxc, get_path(lxc, step["path"]), step["destination"]) case "command": lxc.run_command(command=step["command"], working_directory=optional(step["working_directory"], None), return_status_code=True) case "docker": - commands_utils.run_docker_command(lxc, step["container"], step["command"]) + lxc.run_docker_command(step["container"], step["command"]) case "docker_compose": - commands_utils.run_docker_compose_command(lxc, step["command"], - optional(step["working_directory"], None)) + lxc.run_docker_compose_command(command=step["command"], + working_directory=optional(step["working_directory"], None)) case "git": lxc.run_command(command=f"git clone {step['url']} {step['destination']}", return_status_code=True) case "download": - commands_utils.download_file(lxc, step["url"], step["destination"]) + lxc.download_file(step["url"], step["destination"]) case "unzip": - commands_utils.unzip_file(lxc, step["path"], optional(step["destination"], None)) + lxc.unzip_file(step["path"], optional(step["destination"], None)) case "install-package": - commands_utils.install_package(lxc, step["package"]) + lxc.install_package(step["package"]) case "remove-package": - commands_utils.remove_package(lxc, step["package"]) + lxc.remove_package(step["package"]) case "start": lxc.start() case "stop": @@ -208,4 +183,4 @@ def run_steps(lxc: LXC): case "reboot": lxc.reboot() case "replace-in-files": - commands_utils.replace_in_files(lxc, step["files"], step["search"], step["replace"]) + lxc.replace_in_files(step["files"], step["search"], step["replace"]) diff --git a/src/utils/proxmox.py b/src/utils/proxmox.py index 99f0d41..e139c32 100644 --- a/src/utils/proxmox.py +++ b/src/utils/proxmox.py @@ -2,7 +2,7 @@ from __future__ import annotations import fnmatch import subprocess -from pathlib import Path, PosixPath +from pathlib import Path from typing import TYPE_CHECKING from fabric import Connection @@ -134,7 +134,7 @@ class ProxmoxHost(LinuxMachine): """Get all the LXCs on the Proxmox host.""" pct_list_output = self.run_command("pct list", exception_on_exit=False) pct_list_output = pct_list_output.split("\n")[1:] - ids = [line.split()[0] for line in pct_list_output] + ids = [int(line.split()[0]) for line in pct_list_output] if not ids: return [] @@ -212,10 +212,11 @@ class ProxmoxHost(LinuxMachine): """Copy the given file to the given LXC.""" if isinstance(source, Path): source = str(source.as_posix()) - if isinstance(destination, Path): - destination = str(destination.as_posix()) + if isinstance(destination, str): + destination = Path(destination) - self.run_command(f"pct push {lxc.get_id()} {source} {destination}") + lxc.create_directory(destination.parent.as_posix()) + self.run_command(f"pct push {lxc.get_id()} {source} {destination.as_posix()}") def copy_folder_to_lxc(self, lxc: LXC, source: str or Path, destination: str or Path): """Copy the given folder to the given LXC.""" @@ -224,7 +225,7 @@ class ProxmoxHost(LinuxMachine): if isinstance(destination, Path): destination = str(destination.as_posix()) - self.run_command(f"pct push {lxc.get_id()} {source} {destination} -r") + self.run_command(f"scp -B -r {source} {lxc.get_ssh_string()}:{destination}") def list_dir(self, directory: str or Path, glob_filter: str = '*'): """List the given directory.""" diff --git a/src/utils/resources_utils.py b/src/utils/resources_utils.py index c38c2c2..a16aed7 100644 --- a/src/utils/resources_utils.py +++ b/src/utils/resources_utils.py @@ -1,10 +1,9 @@ from __future__ import annotations +import os import pathlib from typing import TYPE_CHECKING -from .. import project_path - if TYPE_CHECKING: from ..lxc.lxc import LXC @@ -35,14 +34,15 @@ def get_path(lxc: LXC, path: str): Complete path to the resource file """ - parent = pathlib.Path(project_path).joinpath("resources") + parent = pathlib.Path(lxc.pve.repo_path) if path.startswith("global/"): # Use global folder return parent.joinpath(path[len("global/"):]) elif path.startswith("protected/"): # Use protected folder - return parent.parent.joinpath("protected_resources", path[len("protected/"):]) + app_path = pathlib.Path(os.path.dirname(__file__)).parent.parent + return app_path.joinpath("protected_resources", path[len("protected/"):]) else: # Use VM/LXC folder lxc_id = lxc.get_id() diff --git a/src/utils/utils.py b/src/utils/utils.py index e41c96e..55525f1 100644 --- a/src/utils/utils.py +++ b/src/utils/utils.py @@ -1,3 +1,8 @@ +from pathlib import Path + +from src.utils.proxmox import ProxmoxHost + + def get_install_package_command(distribution: str): """Retrieve the correct command to install a package based on the distribution specified It supports all the distribution supported by Proxmox VE (from the pct command documentation). @@ -88,3 +93,48 @@ def get_remove_package_command(distribution: str): return "zypper remove -y" else: raise Exception(f"Unsupported distribution: {distribution}") + + +def optional(var: any, placeholder: any): + """Return a placeholder if the given variable is None, otherwise return the variable + + Parameters + ---------- + var: any + Variable to check if it is None + placeholder: any + Placeholder to return if the variable is None + + Returns + ------- + any[any] + The variable if it is not None, otherwise the placeholder + """ + + if var is None: + return placeholder + else: + return var + + +def copy_local_file_to_pve(pve: ProxmoxHost, local_source: str or Path, destination: str or Path): + """Copy a local file (in the compiled app for users) to a Proxmox VE host + + Parameters + ---------- + pve: ProxmoxHost + Proxmox VE host to copy the file to + local_source: str or Path + Path to the local file to copy + destination: str or Path + Destination path on the Proxmox VE host + """ + + if isinstance(local_source, Path): + local_source = str(local_source.as_posix()) + + if isinstance(destination, str): + destination = Path(destination) + + pve.run_command(f"mkdir -p {destination.parent.as_posix()}") + pve.get_connection().put(local_source, destination.as_posix())