diff --git a/README.md b/README.md index d95ce14..b92d7f2 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,7 @@ broken in the latest version (please report an issue in this repository). Use th ansible-galaxy collection install dynatrace.oneagent:==1.0.0 ``` -See [using Ansible collections](https://docs.ansible.com/ansible/devel/user_guide/collections_using.html) for more -details. +See [using Ansible collections](https://docs.ansible.com/ansible/devel/user_guide/collections_using.html) for more details. ## Use Cases @@ -53,6 +52,8 @@ See [OneAgent role README](roles/oneagent/README.md) for more details. ## Testing +The collection was tested against Ansible sanity tests and component tests. The latter runs regular deployment with +the installer and checks veriety of installation scenarios. See [OneAgent role tests README](roles/oneagent/tests/README.md) for more details. ## Support diff --git a/galaxy.yml b/galaxy.yml index d476328..56e4c5c 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -6,15 +6,20 @@ readme: README.md authors: - Dynatrace LLC description: Module to install and configure Dynatrace OneAgent deployment -license: - - MIT +license_file: LICENSE tags: - dynatrace - oneagent - - monitoring + - agent - deployment + - monitoring + - infrastructure + - linux + - windows repository: https://github.com/Dynatrace/Dynatrace-OneAgent-Ansible documentation: https://docs.dynatrace.com/docs/setup-and-configuration/dynatrace-oneagent/deployment-orchestration/ansible build_ignore: - - roles/oneagent/tests - - roles/oneagent/examples \ No newline at end of file + - .github + - .pre-commit-config.yaml + - .gitignore + - venv diff --git a/roles/oneagent/examples/advanced_config/advanced_config.yml b/roles/oneagent/examples/advanced_config/advanced_config.yml index 230d37d..e01f5da 100644 --- a/roles/oneagent/examples/advanced_config/advanced_config.yml +++ b/roles/oneagent/examples/advanced_config/advanced_config.yml @@ -3,8 +3,6 @@ Download OneAgent installer in specific version to a custom directory with additional OneAgent install parameters. Both linux_other and linux_arm have different user specified by platform args parameter. hosts: linux_other,linux_arm - collections: - - dynatrace.oneagent # credentials.yml file contains oneagent_environment_url and # oneagent_paas_token variables that needs to be stored securely vars_files: @@ -22,4 +20,4 @@ tasks: - name: Import Dynatrace OneAgent role ansible.builtin.import_role: - name: oneagent + name: dynatrace.oneagent.oneagent diff --git a/roles/oneagent/examples/local_installer/local_installer.yml b/roles/oneagent/examples/local_installer/local_installer.yml index fdc1237..7bf2bdc 100644 --- a/roles/oneagent/examples/local_installer/local_installer.yml +++ b/roles/oneagent/examples/local_installer/local_installer.yml @@ -3,9 +3,7 @@ Basic OneAgent installation using a local installer. Hosts placed in unix hosts groups have its local installer paths defined in inventory file. Main node communicates with Windows hosts over SSH. hosts: windows,unix - collections: - - dynatrace.oneagent tasks: - name: Import Dynatrace OneAgent role ansible.builtin.import_role: - name: oneagent + name: dynatrace.oneagent.oneagent diff --git a/roles/oneagent/examples/oneagentctl_config/oneagentctl_config.yml b/roles/oneagent/examples/oneagentctl_config/oneagentctl_config.yml index 343aed1..9308383 100644 --- a/roles/oneagent/examples/oneagentctl_config/oneagentctl_config.yml +++ b/roles/oneagent/examples/oneagentctl_config/oneagentctl_config.yml @@ -1,8 +1,6 @@ --- - name: Apply host level configuration with oneagentctl hosts: linux_other - collections: - - dynatrace.oneagent vars: oneagent_install_args: - --set-host-name=new_host_name @@ -12,4 +10,4 @@ tasks: - name: Import Dynatrace OneAgent role ansible.builtin.import_role: - name: oneagent + name: dynatrace.oneagent.oneagent diff --git a/roles/oneagent/tests/README.md b/roles/oneagent/tests/README.md index 7e49db4..6e4d3ac 100644 --- a/roles/oneagent/tests/README.md +++ b/roles/oneagent/tests/README.md @@ -1,10 +1,12 @@ # Component tests + The tests support two types of deployment: - local - the tests are run on the same Unix machine as main node; - remote - the tests are run on a remote Windows (Unix is not supported at the moment) machine; Currently, there is no option to mix these two types of deployment and the tests must be run for one platform at a time. ## Remote deployment + For remote deployment, regular OneAgent installers are used, which are downloaded from the Dynatrace environment during the tests. To use this type of deployment, the following parameters must be provided: - `--user` - username for the remote machine; @@ -15,12 +17,14 @@ the tests. To use this type of deployment, the following parameters must be prov Failing to provide any of these parameters will result in failure. ## Local deployment + For local deployment, the tests are using mocked version of the OneAgent installer, which simulates its basic behavior - returning version, deploying `uninstall.sh` script and creating `oneagentctl`, used for configuring installation. To use this type of deployment, the only required parameter is `--linux_x86=localhost`. In case, multiple platforms for local deployment are specified or any other platforms is used along with local one, only the first local platform is used. ## Requirements + - Python 3.10+ - pip 21.0+ - venv 20.0+ @@ -36,8 +40,7 @@ Upon downloading the collection $ apt-get install -y python3-venv python3-pip sshpass # Create virtual environment -$ python -m venv venv -$ source venv/bin/activate +$ python -m venv venv && source venv/bin/activate # Install requirements $ pip install -r roles/oneagent/tests/requirements.txt diff --git a/roles/oneagent/tests/ansible/config.py b/roles/oneagent/tests/ansible/config.py index 52386ea..180863c 100644 --- a/roles/oneagent/tests/ansible/config.py +++ b/roles/oneagent/tests/ansible/config.py @@ -6,21 +6,17 @@ ANSIBLE_PASS_KEY, ANSIBLE_RESOURCE_DIR, ANSIBLE_USER_KEY, - COLLECTION_NAME, - COLLECTION_NAMESPACE, CREDENTIALS_FILE_NAME, HOSTS_TEMPLATE_FILE_NAME, INSTALLED_COLLECTIONS_DIR, INVENTORY_FILE, PLAYBOOK_FILE, PLAYBOOK_TEMPLATE_FILE_NAME, - ROLE_NAME, TEST_COLLECTIONS_DIR, - TEST_SIGNATURE_FILE, ) from util.common_utils import read_yaml_file, write_yaml_file -from util.constants.common_constants import TEST_DIRECTORY, INSTALLERS_DIRECTORY, INSTALLER_CERTIFICATE_FILE_NAME +from util.constants.common_constants import TEST_DIRECTORY from util.test_data_types import DeploymentPlatform, PlatformCollection @@ -30,17 +26,18 @@ def _prepare_collection() -> None: def _prepare_playbook_file() -> None: - shutil.copy( - str(ANSIBLE_RESOURCE_DIR / PLAYBOOK_TEMPLATE_FILE_NAME), str(TEST_DIRECTORY / PLAYBOOK_TEMPLATE_FILE_NAME) - ) + shutil.copy(str(ANSIBLE_RESOURCE_DIR / PLAYBOOK_TEMPLATE_FILE_NAME), + str(TEST_DIRECTORY / PLAYBOOK_TEMPLATE_FILE_NAME)) def _prepare_inventory_file(user: str, platforms: PlatformCollection) -> None: host_file = TEST_DIRECTORY / HOSTS_TEMPLATE_FILE_NAME - shutil.copy(str(ANSIBLE_RESOURCE_DIR / HOSTS_TEMPLATE_FILE_NAME), str(host_file)) + shutil.copy(str(ANSIBLE_RESOURCE_DIR / + HOSTS_TEMPLATE_FILE_NAME), str(host_file)) data = read_yaml_file(INVENTORY_FILE) for platform, hosts in platforms.items(): - group_data = data["all"]["children"][platform.family()]["children"][platform.value] + group_data = data["all"]["children"][platform.family() + ]["children"][platform.value] group_data["hosts"] = {k: None for k in hosts} group_data["vars"][ANSIBLE_USER_KEY] = user # TODO: Add condition to fail test if localhost is used with multiple platforms @@ -53,7 +50,8 @@ def _prepare_inventory_file(user: str, platforms: PlatformCollection) -> None: def _prepare_credentials_file(user: str, password: str) -> None: credentials_file = TEST_DIRECTORY / CREDENTIALS_FILE_NAME - shutil.copy(str(ANSIBLE_RESOURCE_DIR / CREDENTIALS_FILE_NAME), str(credentials_file)) + shutil.copy(str(ANSIBLE_RESOURCE_DIR / CREDENTIALS_FILE_NAME), + str(credentials_file)) data = read_yaml_file(credentials_file) data[ANSIBLE_USER_KEY] = user data[ANSIBLE_PASS_KEY] = password @@ -86,7 +84,8 @@ class AnsibleConfig: INSTALLER_ARCH_KEY = "oneagent_installer_arch" INSTALLER_PLATFORM_ARGS_KEY = "oneagent_platform_install_args" - def __init__(self, user: str, password: str, platforms: PlatformCollection): + def __init__(self, user: str, password: str, + platforms: PlatformCollection): self.user = user self.password = password self.platforms = platforms @@ -102,9 +101,14 @@ def set_common_parameter(self, key: str, value: Any) -> None: data[0][AnsibleConfig.PARAM_SECTION_KEY][key] = value write_yaml_file(PLAYBOOK_FILE, data) - def set_platform_parameter(self, platform: DeploymentPlatform, key: str, value: Any) -> None: + def set_platform_parameter( + self, + platform: DeploymentPlatform, + key: str, + value: Any) -> None: data = read_yaml_file(INVENTORY_FILE) - group_data = data["all"]["children"][platform.family()]["children"][platform.value] + group_data = data["all"]["children"][platform.family() + ]["children"][platform.value] group_data[self.PARAM_SECTION_KEY][key] = value write_yaml_file(INVENTORY_FILE, data) @@ -119,6 +123,7 @@ def clear_parameters_section(self) -> None: write_yaml_file(PLAYBOOK_FILE, data) data = read_yaml_file(INVENTORY_FILE) for platform in DeploymentPlatform: - group_data = data["all"]["children"][platform.family()]["children"][platform.value] + group_data = data["all"]["children"][platform.family( + )]["children"][platform.value] group_data[self.PARAM_SECTION_KEY] = {} write_yaml_file(INVENTORY_FILE, data) diff --git a/roles/oneagent/tests/ansible/constants.py b/roles/oneagent/tests/ansible/constants.py index bdfdbb7..875c204 100644 --- a/roles/oneagent/tests/ansible/constants.py +++ b/roles/oneagent/tests/ansible/constants.py @@ -28,14 +28,15 @@ # we need to get the script's user home dir to access installed collection. # Parents[-3] for __file__ will return "/home/" INSTALLED_COLLECTIONS_DIR = Path.home() / ".ansible" / "collections" -NAMESPACE_DIR = INSTALLED_COLLECTIONS_DIR / "ansible_collections" / COLLECTION_NAMESPACE +NAMESPACE_DIR = INSTALLED_COLLECTIONS_DIR / \ + "ansible_collections" / COLLECTION_NAMESPACE ROLE_DIR = NAMESPACE_DIR / COLLECTION_NAME / "roles" / ROLE_NAME PLAYBOOK_FILE = TEST_DIRECTORY / PLAYBOOK_TEMPLATE_FILE_NAME INVENTORY_FILE = TEST_DIRECTORY / HOSTS_TEMPLATE_FILE_NAME TEST_COLLECTIONS_DIR = TEST_DIRECTORY / "collections" -TEST_SIGNATURE_FILE = TEST_COLLECTIONS_DIR / "ansible_collections" / COLLECTION_NAMESPACE /\ - COLLECTION_NAME / "roles" / ROLE_NAME / "files" / INSTALLER_CERTIFICATE_FILE_NAME +TEST_SIGNATURE_FILE = TEST_COLLECTIONS_DIR / "ansible_collections" / COLLECTION_NAMESPACE / \ + COLLECTION_NAME / "roles" / ROLE_NAME / "files" / INSTALLER_CERTIFICATE_FILE_NAME # Public LOCAL_INSTALLERS_LOCATION = INSTALLERS_DIRECTORY diff --git a/roles/oneagent/tests/ansible/runner.py b/roles/oneagent/tests/ansible/runner.py index f8e8217..b5fd57b 100644 --- a/roles/oneagent/tests/ansible/runner.py +++ b/roles/oneagent/tests/ansible/runner.py @@ -1,7 +1,8 @@ import subprocess import logging -from ansible.constants import HOSTS_TEMPLATE_FILE_NAME, PLAYBOOK_TEMPLATE_FILE_NAME, CREDENTIALS_FILE_NAME, TEST_DIRECTORY +from ansible.constants import (HOSTS_TEMPLATE_FILE_NAME, PLAYBOOK_TEMPLATE_FILE_NAME, CREDENTIALS_FILE_NAME, + TEST_DIRECTORY) from util.test_data_types import CommandResult, DeploymentResult @@ -12,19 +13,24 @@ def __init__(self, user: str, password: str): def run_deployment(self) -> DeploymentResult: with open(TEST_DIRECTORY / PLAYBOOK_TEMPLATE_FILE_NAME, 'r') as f: - logging.debug(f"Running playbook ({PLAYBOOK_TEMPLATE_FILE_NAME}):\n{f.read()}") + logging.debug( + "Running playbook (%s):\n%s", PLAYBOOK_TEMPLATE_FILE_NAME, f.read()) with open(TEST_DIRECTORY / HOSTS_TEMPLATE_FILE_NAME, 'r') as f: - logging.debug(f"Inventory file ({HOSTS_TEMPLATE_FILE_NAME}):\n{f.read()}") + logging.debug( + "Inventory file (%s):\n%s", HOSTS_TEMPLATE_FILE_NAME, f.read()) with open(TEST_DIRECTORY / CREDENTIALS_FILE_NAME, 'r') as f: - logging.debug(f"Credentials file ({CREDENTIALS_FILE_NAME}):\n{f.read()}") + logging.debug( + "Credentials file (%s):\n%s", CREDENTIALS_FILE_NAME, f.read()) - res = subprocess.run( - ["ansible-playbook", "-i", TEST_DIRECTORY / HOSTS_TEMPLATE_FILE_NAME, TEST_DIRECTORY / PLAYBOOK_TEMPLATE_FILE_NAME], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - check=False, - ) + res = subprocess.run(["ansible-playbook", + "-i", + TEST_DIRECTORY / HOSTS_TEMPLATE_FILE_NAME, + TEST_DIRECTORY / PLAYBOOK_TEMPLATE_FILE_NAME], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + check=False, + ) return [CommandResult(res.returncode, res.stdout, res.stderr)] diff --git a/roles/oneagent/tests/command/command_wrapper.py b/roles/oneagent/tests/command/command_wrapper.py index 9ca4229..7c2ff5d 100644 --- a/roles/oneagent/tests/command/command_wrapper.py +++ b/roles/oneagent/tests/command/command_wrapper.py @@ -22,5 +22,9 @@ def create_directory(self, address: str, directory: Path) -> CommandResult: raise NotImplementedError @abstractmethod - def run_command(self, address: str, command: str, *args: str) -> CommandResult: + def run_command( + self, + address: str, + command: str, + *args: str) -> CommandResult: raise NotImplementedError diff --git a/roles/oneagent/tests/command/platform_command_wrapper.py b/roles/oneagent/tests/command/platform_command_wrapper.py index 20be60d..7bcf05e 100644 --- a/roles/oneagent/tests/command/platform_command_wrapper.py +++ b/roles/oneagent/tests/command/platform_command_wrapper.py @@ -11,22 +11,49 @@ def __init__(self, user: str, password: str): self.unix_command_wrapper = UnixCommandWrapper(user, password) self.windows_command_wrapper = WindowsCommandWrapper(user, password) - def _get_command_wrapper(self, platform: DeploymentPlatform) -> CommandWrapper: + def _get_command_wrapper( + self, + platform: DeploymentPlatform) -> CommandWrapper: if platform == DeploymentPlatform.WINDOWS_X86: return self.windows_command_wrapper return self.unix_command_wrapper - def get_file_content(self, platform: DeploymentPlatform, address: str, file: Path) -> CommandResult: - return self._get_command_wrapper(platform).get_file_content(address, file) - - def file_exists(self, platform: DeploymentPlatform, address: str, file: Path) -> CommandResult: + def get_file_content( + self, + platform: DeploymentPlatform, + address: str, + file: Path) -> CommandResult: + return self._get_command_wrapper( + platform).get_file_content(address, file) + + def file_exists( + self, + platform: DeploymentPlatform, + address: str, + file: Path) -> CommandResult: return self._get_command_wrapper(platform).file_exists(address, file) - def directory_exists(self, platform: DeploymentPlatform, address: str, directory: Path) -> CommandResult: - return self._get_command_wrapper(platform).directory_exists(address, directory) - - def create_directory(self, platform: DeploymentPlatform, address: str, directory: Path) -> CommandResult: - return self._get_command_wrapper(platform).create_directory(address, directory) - - def run_command(self, platform: DeploymentPlatform, address: str, command: str, *args: str) -> CommandResult: - return self._get_command_wrapper(platform).run_command(address, command, *args) + def directory_exists( + self, + platform: DeploymentPlatform, + address: str, + directory: Path) -> CommandResult: + return self._get_command_wrapper( + platform).directory_exists(address, directory) + + def create_directory( + self, + platform: DeploymentPlatform, + address: str, + directory: Path) -> CommandResult: + return self._get_command_wrapper( + platform).create_directory(address, directory) + + def run_command( + self, + platform: DeploymentPlatform, + address: str, + command: str, + *args: str) -> CommandResult: + return self._get_command_wrapper( + platform).run_command(address, command, *args) diff --git a/roles/oneagent/tests/command/unix/unix_command_wrapper.py b/roles/oneagent/tests/command/unix/unix_command_wrapper.py index 4050c3b..6cc8e9c 100644 --- a/roles/oneagent/tests/command/unix/unix_command_wrapper.py +++ b/roles/oneagent/tests/command/unix/unix_command_wrapper.py @@ -10,10 +10,18 @@ class UnixCommandWrapper(CommandWrapper): def __init__(self, user: str, password: str): self.password = password - def _execute(self, address: str, command: str, *args: str) -> CommandResult: - out = subprocess.run( - " ".join([command, *args]), stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, check=False, shell=True - ) + def _execute( + self, + address: str, + command: str, + *args: str) -> CommandResult: + out = subprocess.run(" ".join([command, + *args]), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + check=False, + shell=True) return CommandResult(out.returncode, out.stdout, out.stderr) def get_file_content(self, address: str, file: Path) -> CommandResult: @@ -28,5 +36,10 @@ def directory_exists(self, address: str, directory: Path) -> CommandResult: def create_directory(self, address: str, directory: Path) -> CommandResult: return self._execute(address, "mkdir", "-p", str(directory)) - def run_command(self, address: str, command: str, *args: str) -> CommandResult: - return self._execute(address, f"echo {self.password} | sudo -S {command}", *args) + def run_command( + self, + address: str, + command: str, + *args: str) -> CommandResult: + return self._execute( + address, f"echo {self.password} | sudo -S {command}", *args) diff --git a/roles/oneagent/tests/command/windows/windows_command_executor.py b/roles/oneagent/tests/command/windows/windows_command_executor.py index 4c62c01..0abddf8 100644 --- a/roles/oneagent/tests/command/windows/windows_command_executor.py +++ b/roles/oneagent/tests/command/windows/windows_command_executor.py @@ -11,4 +11,7 @@ def __init__(self, user: str, password: str): def execute(self, address: str, command: str, *args: str) -> CommandResult: session = winrm.Session(address, auth=(self.user, self.password)) out = session.run_cmd(command, args) - return CommandResult(out.status_code, out.std_out.decode("utf-8"), out.std_err.decode("utf-8")) + return CommandResult( + out.status_code, + out.std_out.decode("utf-8"), + out.std_err.decode("utf-8")) diff --git a/roles/oneagent/tests/command/windows/windows_command_wrapper.py b/roles/oneagent/tests/command/windows/windows_command_wrapper.py index ad153b5..1eac646 100644 --- a/roles/oneagent/tests/command/windows/windows_command_wrapper.py +++ b/roles/oneagent/tests/command/windows/windows_command_wrapper.py @@ -13,14 +13,17 @@ def get_file_content(self, address: str, file: Path) -> CommandResult: return self.executor.execute(address, "type", str(file)) def file_exists(self, address: str, file: Path) -> CommandResult: - # Windows needs double quoting for passing paths containing + # Windows needs double quoting for passing paths # containing spaces, single quotes don't work - return self.executor.execute(address, f'if exist "{file}" (exit 0) else (exit 1)') + return self.executor.execute( + address, f'if exist "{file}" (exit 0) else (exit 1)') def directory_exists(self, address: str, directory: Path) -> CommandResult: - return self.executor.execute(address, f'if exist "{directory}\\*" (exit 0) else (exit 1)') + return self.executor.execute( + address, f'if exist "{directory}\\*" (exit 0) else (exit 1)') - def _run_directory_creation_command(self, address: str, directory: Path) -> CommandResult: + def _run_directory_creation_command( + self, address: str, directory: Path) -> CommandResult: result = CommandResult(0, "", "") if self.directory_exists(address, directory).returncode == 1: result = self.executor.execute(address, "md", str(directory)) @@ -33,5 +36,9 @@ def create_directory(self, address: str, directory: Path) -> CommandResult: return result return self._run_directory_creation_command(address, directory) - def run_command(self, address: str, command: str, *args: str) -> CommandResult: + def run_command( + self, + address: str, + command: str, + *args: str) -> CommandResult: return self.executor.execute(address, f'"{command}"', *args) diff --git a/roles/oneagent/tests/conftest.py b/roles/oneagent/tests/conftest.py index a60b3dc..07e0ab7 100644 --- a/roles/oneagent/tests/conftest.py +++ b/roles/oneagent/tests/conftest.py @@ -16,8 +16,12 @@ from util.common_utils import prepare_test_dirs from util.test_data_types import DeploymentPlatform, PlatformCollection, DeploymentResult from util.test_helpers import check_agent_state, perform_operation_on_platforms -from util.constants.common_constants import (TEST_DIRECTORY, INSTALLERS_DIRECTORY, COMPONENT_TEST_BASE, - SERVER_DIRECTORY, LOG_DIRECTORY) +from util.constants.common_constants import ( + TEST_DIRECTORY, + INSTALLERS_DIRECTORY, + COMPONENT_TEST_BASE, + SERVER_DIRECTORY, + LOG_DIRECTORY) # Command line options @@ -41,14 +45,16 @@ def is_local_deployment(platforms: PlatformCollection) -> bool: return any("localhost" in hosts for _, hosts in platforms.items()) -def parse_platforms_from_options(options: dict[str, Any]) -> PlatformCollection: +def parse_platforms_from_options( + options: dict[str, Any]) -> PlatformCollection: platforms: PlatformCollection = {} deployment_platforms = [e.value for e in DeploymentPlatform] for key, hosts in options.items(): if key in deployment_platforms and hosts: if "localhost" in hosts: - logging.info(f"Local deployment detected for {key}, only this host will be used") + logging.info( + f"Local deployment detected for {key}, only this host will be used") return {DeploymentPlatform.from_str(key): hosts} platforms[DeploymentPlatform.from_str(key)] = hosts return platforms @@ -57,7 +63,8 @@ def parse_platforms_from_options(options: dict[str, Any]) -> PlatformCollection: @pytest.fixture(scope="session", autouse=True) def create_test_directories(request) -> None: if request.config.getoption(PRESERVE_INSTALLERS_KEY): - logging.info("Installers will be preserved, no installers will be generated") + logging.info( + "Installers will be preserved, no installers will be generated") shutil.rmtree(SERVER_DIRECTORY, ignore_errors=True) shutil.rmtree(LOG_DIRECTORY, ignore_errors=True) shutil.rmtree(TEST_DIRECTORY, ignore_errors=True) @@ -89,21 +96,29 @@ def prepare_installers(request) -> None: pytest.exit("Generating installers failed") elif tenant and tenant_token: logging.info("Downloading installers and signature...") - if not download_signature(cert_url) or not download_installers(tenant, tenant_token, platforms): + if not download_signature(cert_url) or not download_installers( + tenant, tenant_token, platforms): pytest.exit("Downloading installers and signature failed") else: - pytest.exit("No tenant or tenant token provided, cannot download installers") + pytest.exit( + "No tenant or tenant token provided, cannot download installers") @pytest.fixture(scope="session", autouse=True) def installer_server_url(request) -> None: port = 8021 - ip_address = socket.gethostbyname(socket.gethostname()) - url = f"https://{ip_address}:{port}" - - logging.info(f"Running server on {url}...") - - proc = subprocess.Popen([sys.executable, "-m", "server", "--port", f"{port}", "--ip-address", ip_address], + ipaddress = socket.gethostbyname(socket.gethostname()) + url = f"https://{ipaddress}:{port}" + + logging.info("Running server on %s...", url) + + proc = subprocess.Popen([sys.executable, + "-m", + "server", + "--port", + f"{port}", + "--ip-address", + ipaddress], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding="utf-8", @@ -130,12 +145,15 @@ def handle_test_environment(runner, configurator, platforms, wrapper) -> None: configurator.set_common_parameter(configurator.PACKAGE_STATE_KEY, "absent") logging.info("Check if agent is uninstalled") - perform_operation_on_platforms(platforms, check_agent_state, wrapper, False) + perform_operation_on_platforms( + platforms, check_agent_state, wrapper, False) results: DeploymentResult = runner.run_deployment() for result in results: if result.returncode != 0: - logging.error("Failed to clean up environment, output: %s", result.stdout) + logging.error( + "Failed to clean up environment, output: %s", + result.stdout) shutil.rmtree("/var/lib/dynatrace", ignore_errors=True) @@ -143,18 +161,43 @@ def handle_test_environment(runner, configurator, platforms, wrapper) -> None: def pytest_addoption(parser) -> None: - parser.addini(CA_CERT_URL_KEY, "Url to CA certificate for downloading installers") - - parser.addoption(f"--{USER_KEY}", type=str, help="Name of the user", required=False) - parser.addoption(f"--{PASS_KEY}", type=str, help="Password of the user", required=False) - parser.addoption(f"--{TENANT_KEY}", type=str, help="Tenant URL for downloading installer", required=False) - parser.addoption(f"--{TENANT_TOKEN_KEY}", type=str, help="API key for downloading installer", required=False) - parser.addoption(f"--{PRESERVE_INSTALLERS_KEY}", type=bool, default=False, help="Preserve installers after test run", required=False) + parser.addini( + CA_CERT_URL_KEY, + "Url to CA certificate for downloading installers") + + parser.addoption( + f"--{USER_KEY}", + type=str, + help="Name of the user", + required=False) + parser.addoption( + f"--{PASS_KEY}", + type=str, + help="Password of the user", + required=False) + parser.addoption( + f"--{TENANT_KEY}", + type=str, + help="Tenant URL for downloading installer", + required=False) + parser.addoption( + f"--{TENANT_TOKEN_KEY}", + type=str, + help="API key for downloading installer", + required=False) + parser.addoption( + f"--{PRESERVE_INSTALLERS_KEY}", + type=bool, + default=False, + help="Preserve installers after test run", + required=False) for platform in DeploymentPlatform: - parser.addoption( - f"--{platform.value}", type=str, nargs="+", default=[], help="List of IPs for specified platform" - ) + parser.addoption(f"--{platform.value}", + type=str, + nargs="+", + default=[], + help="List of IPs for specified platform") def pytest_configure() -> None: @@ -189,4 +232,4 @@ def pytest_generate_tests(metafunc) -> None: metafunc.parametrize(PLATFORMS_KEY, [platforms]) if WRAPPER_KEY in metafunc.fixturenames: - metafunc.parametrize(WRAPPER_KEY, [wrapper]) \ No newline at end of file + metafunc.parametrize(WRAPPER_KEY, [wrapper]) diff --git a/roles/oneagent/tests/resources/ansible/oneagent.yml b/roles/oneagent/tests/resources/ansible/oneagent.yml index b966231..f8dfda5 100644 --- a/roles/oneagent/tests/resources/ansible/oneagent.yml +++ b/roles/oneagent/tests/resources/ansible/oneagent.yml @@ -5,6 +5,6 @@ - credentials.yml vars: {} tasks: - - name: Import OneAgent role + - name: Import Dynatrace OneAgent role ansible.builtin.import_role: name: dynatrace.oneagent.oneagent diff --git a/roles/oneagent/tests/resources/installers/Dynatrace-OneAgent-Linux.sh b/roles/oneagent/tests/resources/installers/Dynatrace-OneAgent-Linux.sh index 796e4a6..1808374 100644 --- a/roles/oneagent/tests/resources/installers/Dynatrace-OneAgent-Linux.sh +++ b/roles/oneagent/tests/resources/installers/Dynatrace-OneAgent-Linux.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash -eu # This script acts as a self contained installer of the procuct @@ -9,16 +9,18 @@ readonly INSTALLER_VERSION="##VERSION##" readonly DEPLOYMENT_CONF_PATH="/var/lib/dynatrace/oneagent/agent/config" readonly UNINSTALL_SCRIPT="uninstall.sh" -readonly UNINSTALL_CODE="$(cat <<-ENDUNINSTALL +UNINSTALL_CODE="$(cat <<-ENDUNINSTALL ##UNINSTALL_CODE## ENDUNINSTALL )" +readonly UNINSTALL_CODE readonly ONEAGENTCTL_BIN="oneagentctl" -readonly ONEAGENTCTL_CODE="$(cat <<-ENDCTL +ONEAGENTCTL_CODE="$(cat <<-ENDCTL ##ONEAGENTCTL_CODE## ENDCTL )" +readonly ONEAGENTCTL_CODE CTL_PARAMS= INSTALL_DIR="${DEFAULT_INSTALL_DIR}" @@ -61,6 +63,7 @@ deployUninstallScript() { } applyConfig() { + # shellcheck disable=SC2086 "${INSTALL_DIR}/agent/tools/${ONEAGENTCTL_BIN}" ${CTL_PARAMS} } diff --git a/roles/oneagent/tests/resources/installers/oneagentctl.sh b/roles/oneagent/tests/resources/installers/oneagentctl.sh index 08da32a..a17ea08 100644 --- a/roles/oneagent/tests/resources/installers/oneagentctl.sh +++ b/roles/oneagent/tests/resources/installers/oneagentctl.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -eu # This file simulates deployment functionalities of oneagentctl binary, used to configure installation. @@ -20,11 +20,14 @@ saveToConfig() { # setter: --set-host-property local setter="$(cutVariable "${1}" "=" 1)" # setterType: property - local setterType="$(cutVariable "${setter}" "-" "5-")" + local setterType + setterType="$(cutVariable "${setter}" "-" "5-")" # value: TENANT=tenant1 - local value="$(cutVariable "${1}" "=" "2-")" + local value + value="$(cutVariable "${1}" "=" "2-")" # property: TENANT - local property="$(cutVariable "${value}" "=" "1")" + local property + property="$(cutVariable "${value}" "=" "1")" local setterFile="${DEPLOYMENT_CONF_PATH}/${setterType}" if grep -q "${property}" "${setterFile}"; then sed -i "s/${property}.*/${value}/" "${setterFile}" @@ -36,7 +39,8 @@ saveToConfig() { } readFromConfig() { - local getterType="$(cutVariable "${1}" "-" "5-")" + local getterType + getterType="$(cutVariable "${1}" "-" "5-")" if [ "${getterType}" = "properties" ]; then getterType="property" @@ -49,7 +53,7 @@ main() { if [ "${1}" = '--version' ]; then printf '%s\n' "${INSTALLER_VERSION}" elif printf "%s" "${1}" | grep -q "^--get"; then - readFromConfig ${1} + readFromConfig "${1}" elif printf "%s" "${1}" | grep -q "^--set"; then saveToConfig "$@" fi diff --git a/roles/oneagent/tests/resources/installers/uninstall.sh b/roles/oneagent/tests/resources/installers/uninstall.sh index 9bb2649..69305fd 100644 --- a/roles/oneagent/tests/resources/installers/uninstall.sh +++ b/roles/oneagent/tests/resources/installers/uninstall.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash -eu # This file simulates the basic behavior of the uninstall.sh script diff --git a/roles/oneagent/tests/server/server.py b/roles/oneagent/tests/server/server.py index efe0f12..fa823af 100644 --- a/roles/oneagent/tests/server/server.py +++ b/roles/oneagent/tests/server/server.py @@ -6,8 +6,12 @@ from flask import Blueprint, Flask, request, send_file from util.common_utils import get_installers -from util.constants.common_constants import (INSTALLERS_DIRECTORY, SERVER_DIRECTORY, INSTALLER_CERTIFICATE_FILE_NAME, - SERVER_CERTIFICATE_FILE_NAME, SERVER_PRIVATE_KEY_FILE_NAME) +from util.constants.common_constants import ( + INSTALLERS_DIRECTORY, + SERVER_DIRECTORY, + INSTALLER_CERTIFICATE_FILE_NAME, + SERVER_CERTIFICATE_FILE_NAME, + SERVER_PRIVATE_KEY_FILE_NAME) from util.ssl_certificate_generator import SSLCertificateGenerator @@ -19,7 +23,8 @@ def get_installer(system: str, arch: str, version: str) -> TransferResult: - logging.info(f"Getting installer for system {system}_{arch} in {version} version") + logging.info( + "Getting installer for system %s_%s in %s version", system, arch, version) installers = get_installers(system, arch, version, True) @@ -36,7 +41,7 @@ def get_installer(system: str, arch: str, version: str) -> TransferResult: def get_ca_certificate() -> TransferResult: cert_file = INSTALLERS_DIRECTORY / INSTALLER_CERTIFICATE_FILE_NAME if not cert_file.exists(): - logging.warning(f"{cert_file} not found") + logging.warning("%s not found", cert_file) return f"{cert_file} not found", HTTPStatus.NOT_FOUND return send_file(cert_file) @@ -53,11 +58,16 @@ def get_agent_in_version(system, version) -> TransferResult: def main() -> None: logging.basicConfig( - format="%(asctime)s [server] %(levelname)s: %(message)s", datefmt="%H:%M:%S", level=logging.INFO - ) + format="%(asctime)s [server] %(levelname)s: %(message)s", + datefmt="%H:%M:%S", + level=logging.INFO) parser = argparse.ArgumentParser(description='Run Flask server.') - parser.add_argument("--ip-address", type=str, dest='ip_address', help="IP address of the host to run the server on") + parser.add_argument( + "--ip-address", + type=str, + dest='ip_address', + help="IP address of the host to run the server on") parser.add_argument("--port", type=int, help="Port to run the server on") args = parser.parse_args() @@ -68,10 +78,13 @@ def main() -> None: organization_name="Dynatrace", common_name=args.ip_address ) - generator.generate_and_save(f"{SERVER_DIRECTORY / SERVER_PRIVATE_KEY_FILE_NAME}", + generator.generate_and_save(f"{SERVER_DIRECTORY /SERVER_PRIVATE_KEY_FILE_NAME}", f"{SERVER_DIRECTORY / SERVER_CERTIFICATE_FILE_NAME}") - context = (f"{SERVER_DIRECTORY / SERVER_CERTIFICATE_FILE_NAME}", f"{SERVER_DIRECTORY / SERVER_PRIVATE_KEY_FILE_NAME}") - app.register_blueprint(installer_bp, url_prefix="/api/v1/deployment/installer/agent") + context = (f"{SERVER_DIRECTORY / SERVER_CERTIFICATE_FILE_NAME}", + f"{SERVER_DIRECTORY / SERVER_PRIVATE_KEY_FILE_NAME}") + app.register_blueprint( + installer_bp, + url_prefix="/api/v1/deployment/installer/agent") app.register_blueprint(certificate_bp) app.run(host="0.0.0.0", debug=True, ssl_context=context, port=args.port) diff --git a/roles/oneagent/tests/test_installAndConfig.py b/roles/oneagent/tests/test_installAndConfig.py index 662e962..d90c1bb 100644 --- a/roles/oneagent/tests/test_installAndConfig.py +++ b/roles/oneagent/tests/test_installAndConfig.py @@ -42,11 +42,18 @@ def _assert_oneagentctl_getter( def _check_install_args( - platform: DeploymentPlatform, address: str, wrapper: PlatformCommandWrapper, ansible: str) -> None: + platform: DeploymentPlatform, + address: str, + wrapper: PlatformCommandWrapper, + ansible: str) -> None: logging.debug("Platform: %s, IP: %s", platform, address) oneagentctl = f"{get_oneagentctl_path(platform)}" - metadata = wrapper.run_command(platform, address, oneagentctl, "--get-deployment-metadata") + metadata = wrapper.run_command( + platform, + address, + oneagentctl, + "--get-deployment-metadata") assert metadata.returncode == 0 params = dict(kv.split("=") for kv in metadata.stdout.strip().splitlines()) @@ -56,21 +63,42 @@ def _check_install_args( assert params[TECH_NAME_KEY] is not None and params[TECH_NAME_KEY] == ansible -def _check_config_args(platform: DeploymentPlatform, address: str, wrapper: PlatformCommandWrapper, expected_tags: set[str], - expected_properties: set[str]): +def _check_config_args( + platform: DeploymentPlatform, + address: str, + wrapper: PlatformCommandWrapper, + expected_tags: set[str], + expected_properties: set[str]): logging.debug("Platform: %s, IP: %s", platform, address) - _assert_oneagentctl_getter(platform, address, wrapper, CTL_OPTION_GET_HOST_TAGS, expected_tags) - _assert_oneagentctl_getter(platform, address, wrapper, CTL_OPTION_GET_HOST_PROPERTIES, expected_properties) - - -def _check_output_for_secrets(result: DeploymentResult, installer_server_url) -> None: + _assert_oneagentctl_getter( + platform, + address, + wrapper, + CTL_OPTION_GET_HOST_TAGS, + expected_tags) + _assert_oneagentctl_getter( + platform, + address, + wrapper, + CTL_OPTION_GET_HOST_PROPERTIES, + expected_properties) + + +def _check_output_for_secrets( + result: DeploymentResult, + installer_server_url) -> None: for out in result: assert INSTALLER_SERVER_TOKEN not in out.stdout assert installer_server_url not in out.stderr -def test_basic_installation(runner, configurator, platforms, wrapper, installer_server_url): +def test_basic_installation( + runner, + configurator, + platforms, + wrapper, + installer_server_url): logging.info("Running basic installation test") dummy_common_tag = "dummy_common_tag" @@ -79,18 +107,26 @@ def test_basic_installation(runner, configurator, platforms, wrapper, installer_ dummy_platform_property = "dummy_platform_key=dummy_platform_value" set_installer_download_params(configurator, installer_server_url) - configurator.set_common_parameter(configurator.VALIDATE_DOWNLOAD_CERTS_KEY, False) - configurator.set_common_parameter(configurator.PRESERVE_INSTALLER_KEY, True) - configurator.set_common_parameter(configurator.INSTALLER_ARGS_KEY, - [f"{CTL_OPTION_SET_HOST_TAG}={dummy_common_tag}", - f"{CTL_OPTION_SET_HOST_PROPERTY}={dummy_common_property}"]) - - for platform, _ in platforms.items(): - download_dir: Path = get_platform_argument(platform, UNIX_DOWNLOAD_PATH, WINDOWS_DOWNLOAD_PATH) - configurator.set_platform_parameter(platform, configurator.DOWNLOAD_DIR_KEY, str(download_dir)) - configurator.set_common_parameter(configurator.INSTALLER_PLATFORM_ARGS_KEY, - [f"{CTL_OPTION_SET_HOST_TAG}={dummy_platform_tag}", - f"{CTL_OPTION_SET_HOST_PROPERTY}={dummy_platform_property}"]) + configurator.set_common_parameter( + configurator.VALIDATE_DOWNLOAD_CERTS_KEY, False) + configurator.set_common_parameter( + configurator.PRESERVE_INSTALLER_KEY, True) + configurator.set_common_parameter( + configurator.INSTALLER_ARGS_KEY, + [ + f"{CTL_OPTION_SET_HOST_TAG}={dummy_common_tag}", + f"{CTL_OPTION_SET_HOST_PROPERTY}={dummy_common_property}"]) + + for platform, hosts in platforms.items(): + download_dir: Path = get_platform_argument( + platform, UNIX_DOWNLOAD_PATH, WINDOWS_DOWNLOAD_PATH) + configurator.set_platform_parameter( + platform, configurator.DOWNLOAD_DIR_KEY, str(download_dir)) + configurator.set_common_parameter( + configurator.INSTALLER_PLATFORM_ARGS_KEY, + [ + f"{CTL_OPTION_SET_HOST_TAG}={dummy_platform_tag}", + f"{CTL_OPTION_SET_HOST_PROPERTY}={dummy_platform_property}"]) result = run_deployment(runner) @@ -100,17 +136,22 @@ def test_basic_installation(runner, configurator, platforms, wrapper, installer_ logging.info("Check if agent is installed") perform_operation_on_platforms(platforms, check_agent_state, wrapper, True) - logging.info("Check if installer was downloaded to correct place and preserved") + logging.info( + "Check if installer was downloaded to correct place and preserved") perform_operation_on_platforms( - platforms, check_download_directory, wrapper, True, UNIX_DOWNLOAD_PATH, WINDOWS_DOWNLOAD_PATH - ) + platforms, + check_download_directory, + wrapper, + True, + UNIX_DOWNLOAD_PATH, + WINDOWS_DOWNLOAD_PATH) logging.info("Check if config args were applied correctly") - perform_operation_on_platforms(platforms, - _check_config_args, - wrapper, - {dummy_common_tag, dummy_platform_tag}, - {dummy_common_property, dummy_platform_property}) + perform_operation_on_platforms( + platforms, _check_config_args, wrapper, { + dummy_common_tag, dummy_platform_tag}, { + dummy_common_property, dummy_platform_property}) logging.info("Check if installer args were passed correctly") - perform_operation_on_platforms(platforms, _check_install_args, wrapper, TECH_NAME) + perform_operation_on_platforms( + platforms, _check_install_args, wrapper, TECH_NAME) diff --git a/roles/oneagent/tests/test_localInstaller.py b/roles/oneagent/tests/test_localInstaller.py index 82ab7a9..edebbb9 100644 --- a/roles/oneagent/tests/test_localInstaller.py +++ b/roles/oneagent/tests/test_localInstaller.py @@ -13,17 +13,24 @@ ) -def test_local_installer(runner, configurator, platforms, wrapper, installer_server_url): +def test_local_installer( + runner, + configurator, + platforms, + wrapper, + installer_server_url): logging.info("Running local installer test") set_ca_cert_download_params(configurator, installer_server_url) - for platform, _ in platforms.items(): + for platform, hosts in platforms.items(): installers_location = LOCAL_INSTALLERS_LOCATION - latest_installer_name = get_installers(platform.system(), platform.arch(), "latest")[-1] + latest_installer_name = get_installers( + platform.system(), platform.arch(), "latest")[-1] configurator.set_platform_parameter( - platform, configurator.LOCAL_INSTALLER_KEY, f"{installers_location}/{latest_installer_name}" - ) + platform, + configurator.LOCAL_INSTALLER_KEY, + f"{installers_location}/{latest_installer_name}") run_deployment(runner) @@ -32,5 +39,9 @@ def test_local_installer(runner, configurator, platforms, wrapper, installer_ser logging.info("Check if installer was removed") perform_operation_on_platforms( - platforms, check_download_directory, wrapper, False, UNIX_DEFAULT_DOWNLOAD_PATH, WINDOWS_DEFAULT_DOWNLOAD_PATH - ) + platforms, + check_download_directory, + wrapper, + False, + UNIX_DEFAULT_DOWNLOAD_PATH, + WINDOWS_DEFAULT_DOWNLOAD_PATH) diff --git a/roles/oneagent/tests/test_resilience.py b/roles/oneagent/tests/test_resilience.py index 2893ef6..3c6d968 100644 --- a/roles/oneagent/tests/test_resilience.py +++ b/roles/oneagent/tests/test_resilience.py @@ -40,7 +40,10 @@ def _error_messages() -> dict[str, str]: return _prepare_test_data(_parse_error_messages_file()) -def _check_deployment_failure(results: DeploymentResult, expected_message: str, expected_code: int) -> None: +def _check_deployment_failure( + results: DeploymentResult, + expected_message: str, + expected_code: int) -> None: logging.info("Check if installation failed") for result in results: assert result.returncode == expected_code @@ -50,7 +53,11 @@ def _check_deployment_failure(results: DeploymentResult, expected_message: str, assert re.search(expected_message, result.stdout + result.stderr) -def test_invalid_required_parameters(_error_messages, runner, configurator, installer_server_url): +def test_invalid_required_parameters( + _error_messages, + runner, + configurator, + installer_server_url): logging.info("Running missing required parameters test") logging.debug("Removing required parameter - direct download scenario") @@ -64,21 +71,31 @@ def test_invalid_required_parameters(_error_messages, runner, configurator, inst ) -def test_invalid_architecture(_error_messages, runner, configurator, installer_server_url): +def test_invalid_architecture( + _error_messages, + runner, + configurator, + installer_server_url): logging.info("Running invalid architecture test") set_installer_download_params(configurator, installer_server_url) - configurator.set_common_parameter(configurator.INSTALLER_ARCH_KEY, "unknown_arch") + configurator.set_common_parameter( + configurator.INSTALLER_ARCH_KEY, "unknown_arch") _check_deployment_failure( - run_deployment(runner, True), _error_messages[UNKNOWN_ARCHITECTURE_KEY], FAILED_DEPLOYMENT_EXIT_CODE - ) + run_deployment( + runner, + True), + _error_messages[UNKNOWN_ARCHITECTURE_KEY], + FAILED_DEPLOYMENT_EXIT_CODE) def test_missing_local_installer(_error_messages, runner, configurator): logging.info("Running missing local installer test") - configurator.set_common_parameter(configurator.LOCAL_INSTALLER_KEY, "non_existing_installer") + configurator.set_common_parameter( + configurator.LOCAL_INSTALLER_KEY, + "non_existing_installer") _check_deployment_failure( run_deployment(runner, True), @@ -88,13 +105,19 @@ def test_missing_local_installer(_error_messages, runner, configurator): @enable_for_system_family(family="unix") -def test_directories_contain_spaces(_error_messages, runner, configurator, platforms, installer_server_url): +def test_directories_contain_spaces( + _error_messages, + runner, + configurator, + platforms, + installer_server_url): logging.info("Running directories contain spaces test") logging.debug("Space in directory path - INSTALL_PATH scenario") set_installer_download_params(configurator, installer_server_url) installer_args = ["INSTALL_PATH=/path with spaces"] - configurator.set_common_parameter(configurator.INSTALLER_ARGS_KEY, installer_args) + configurator.set_common_parameter( + configurator.INSTALLER_ARGS_KEY, installer_args) _check_deployment_failure( run_deployment(runner, True), @@ -105,7 +128,9 @@ def test_directories_contain_spaces(_error_messages, runner, configurator, platf logging.debug("Space in directory path - download dir scenario") configurator.clear_parameters_section() set_installer_download_params(configurator, installer_server_url) - configurator.set_common_parameter(configurator.DOWNLOAD_DIR_KEY, "/path with spaces") + configurator.set_common_parameter( + configurator.DOWNLOAD_DIR_KEY, + "/path with spaces") _check_deployment_failure( run_deployment(runner, True), @@ -114,11 +139,16 @@ def test_directories_contain_spaces(_error_messages, runner, configurator, platf ) -def test_version_parameter_too_low(_error_messages, runner, configurator, installer_server_url): +def test_version_parameter_too_low( + _error_messages, + runner, + configurator, + installer_server_url): logging.info("Running version parameter too low test") set_installer_download_params(configurator, installer_server_url) - configurator.set_common_parameter(configurator.INSTALLER_VERSION_KEY, "0.0.0") + configurator.set_common_parameter( + configurator.INSTALLER_VERSION_KEY, "0.0.0") _check_deployment_failure( run_deployment(runner, True), @@ -127,40 +157,71 @@ def test_version_parameter_too_low(_error_messages, runner, configurator, instal ) -def test_multiple_install_path_arguments(_error_messages, runner, configurator, installer_server_url): +def test_multiple_install_path_arguments( + _error_messages, + runner, + configurator, + installer_server_url): logging.info("Running multiple install path arguments test") set_installer_download_params(configurator, installer_server_url) - configurator.set_common_parameter(configurator.INSTALLER_ARGS_KEY, ["INSTALL_PATH=/path1"]) - configurator.set_common_parameter(configurator.INSTALLER_PLATFORM_ARGS_KEY, ["INSTALL_PATH=/path2"]) + configurator.set_common_parameter( + configurator.INSTALLER_ARGS_KEY, + ["INSTALL_PATH=/path1"]) + configurator.set_common_parameter( + configurator.INSTALLER_PLATFORM_ARGS_KEY, + ["INSTALL_PATH=/path2"]) _check_deployment_failure( - run_deployment(runner, True), _error_messages[MULTIPLE_INSTALL_PATH_KEY], FAILED_DEPLOYMENT_EXIT_CODE - ) - - -def test_failed_download(_error_messages, runner, configurator, installer_server_url): + run_deployment( + runner, + True), + _error_messages[MULTIPLE_INSTALL_PATH_KEY], + FAILED_DEPLOYMENT_EXIT_CODE) + + +def test_failed_download( + _error_messages, + runner, + configurator, + installer_server_url): logging.info("Running failed download test") set_installer_download_params(configurator, installer_server_url) - configurator.set_common_parameter(configurator.ENVIRONMENT_URL_KEY, "0.0.0.0") + configurator.set_common_parameter( + configurator.ENVIRONMENT_URL_KEY, "0.0.0.0") _check_deployment_failure( - run_deployment(runner, True), _error_messages[DOWNLOAD_FAILED_KEY], FAILED_DEPLOYMENT_EXIT_CODE - ) + run_deployment( + runner, + True), + _error_messages[DOWNLOAD_FAILED_KEY], + FAILED_DEPLOYMENT_EXIT_CODE) # noinspection PyUnusedLocal @enable_for_system_family(family="unix") -def test_failed_signature_verification(_error_messages, runner, configurator, platforms, installer_server_url): +def test_failed_signature_verification( + _error_messages, + runner, + configurator, + platforms, + installer_server_url): logging.info("Running failed signature verification test") set_installer_download_params(configurator, installer_server_url) - configurator.set_common_parameter(configurator.FORCE_CERT_DOWNLOAD_KEY, False) - configurator.set_common_parameter(configurator.INSTALLER_VERSION_KEY, "latest") + configurator.set_common_parameter( + configurator.FORCE_CERT_DOWNLOAD_KEY, False) + configurator.set_common_parameter( + configurator.INSTALLER_VERSION_KEY, "latest") with TEST_SIGNATURE_FILE.open("w") as signature: signature.write("break signature by writing some text") universal_message = _error_messages.get(SIGNATURE_VERIFICATION_FAILED_KEY) - _check_deployment_failure(run_deployment(runner, True), universal_message, FAILED_DEPLOYMENT_EXIT_CODE) + _check_deployment_failure( + run_deployment( + runner, + True), + universal_message, + FAILED_DEPLOYMENT_EXIT_CODE) diff --git a/roles/oneagent/tests/test_upgrade.py b/roles/oneagent/tests/test_upgrade.py index 25eaac3..fe3b665 100644 --- a/roles/oneagent/tests/test_upgrade.py +++ b/roles/oneagent/tests/test_upgrade.py @@ -1,5 +1,6 @@ import logging import re +from typing import Dict from command.platform_command_wrapper import PlatformCommandWrapper from util.common_utils import get_oneagentctl_path, get_installers @@ -11,30 +12,44 @@ run_deployment, ) -def _get_versions_for_platforms(platforms: PlatformCollection, latest: bool) -> dict[DeploymentPlatform, str]: + +def _get_versions_for_platforms( + platforms: PlatformCollection, latest: bool) -> dict[DeploymentPlatform, str]: versions: Dict[DeploymentPlatform, str] = {} - for platform, _ in platforms.items(): + for platform, hosts in platforms.items(): installers = get_installers(platform.system(), platform.arch()) versioned_installer = installers[-1 if latest else 0] - versions[platform] = re.search(r"\d.\d+.\d+.\d+-\d+", str(versioned_installer)).group() + versions[platform] = re.search( + r"\d.\d+.\d+.\d+-\d+", + str(versioned_installer)).group() return versions -def _check_agent_version( - platform: DeploymentPlatform, address: str, wrapper: PlatformCommandWrapper, versions: dict[DeploymentPlatform, str] -) -> None: - installed_version = wrapper.run_command(platform, address, f"{get_oneagentctl_path(platform)}", "--version") + +def _check_agent_version(platform: DeploymentPlatform, + address: str, + wrapper: PlatformCommandWrapper, + versions: dict[DeploymentPlatform, str]) -> None: + installed_version = wrapper.run_command( + platform, address, f"{get_oneagentctl_path(platform)}", "--version") assert installed_version.stdout.strip() == versions[platform] -def test_upgrade(runner, configurator, platforms, wrapper, installer_server_url): +def test_upgrade( + runner, + configurator, + platforms, + wrapper, + installer_server_url): logging.info("Running upgrade test") set_installer_download_params(configurator, installer_server_url) - configurator.set_common_parameter(configurator.VALIDATE_DOWNLOAD_CERTS_KEY, False) + configurator.set_common_parameter( + configurator.VALIDATE_DOWNLOAD_CERTS_KEY, False) old_versions = _get_versions_for_platforms(platforms, False) for platform, version in old_versions.items(): - configurator.set_platform_parameter(platform, configurator.INSTALLER_VERSION_KEY, version) + configurator.set_platform_parameter( + platform, configurator.INSTALLER_VERSION_KEY, version) run_deployment(runner) @@ -42,9 +57,14 @@ def test_upgrade(runner, configurator, platforms, wrapper, installer_server_url) perform_operation_on_platforms(platforms, check_agent_state, wrapper, True) logging.info("Check if agent has proper version") - perform_operation_on_platforms(platforms, _check_agent_version, wrapper, old_versions) + perform_operation_on_platforms( + platforms, + _check_agent_version, + wrapper, + old_versions) - configurator.set_common_parameter(configurator.INSTALLER_VERSION_KEY, "latest") + configurator.set_common_parameter( + configurator.INSTALLER_VERSION_KEY, "latest") run_deployment(runner) @@ -53,4 +73,8 @@ def test_upgrade(runner, configurator, platforms, wrapper, installer_server_url) logging.info("Check if agent has proper version") new_versions = _get_versions_for_platforms(platforms, True) - perform_operation_on_platforms(platforms, _check_agent_version, wrapper, new_versions) \ No newline at end of file + perform_operation_on_platforms( + platforms, + _check_agent_version, + wrapper, + new_versions) diff --git a/roles/oneagent/tests/util/common_utils.py b/roles/oneagent/tests/util/common_utils.py index c3fc940..18f6194 100644 --- a/roles/oneagent/tests/util/common_utils.py +++ b/roles/oneagent/tests/util/common_utils.py @@ -2,7 +2,7 @@ import os import shutil from pathlib import Path -from typing import Any +from typing import Any, Dict import yaml @@ -28,7 +28,10 @@ def prepare_test_dirs() -> None: def remove_if_exists(path: Path) -> None: if path.exists(): try: - shutil.rmtree(str(path)) if os.path.isdir(str(path)) else os.remove(str(path)) + shutil.rmtree( + str(path)) if os.path.isdir( + str(path)) else os.remove( + str(path)) except OSError as os_error: logging.error("Failed to remove %s: %s", path, os_error) @@ -45,10 +48,16 @@ def write_yaml_file(file: Path, data: ParsedYaml) -> None: def get_oneagentctl_path(platform: DeploymentPlatform) -> Path: - return get_platform_argument(platform, UNIX_ONEAGENTCTL_PATH, WINDOWS_ONEAGENTCTL_PATH) + return get_platform_argument( + platform, + UNIX_ONEAGENTCTL_PATH, + WINDOWS_ONEAGENTCTL_PATH) -def get_platform_argument(platform: DeploymentPlatform, unix_arg: Any, windows_arg: Any) -> Any: +def get_platform_argument( + platform: DeploymentPlatform, + unix_arg: Any, + windows_arg: Any) -> Any: return windows_arg if platform == DeploymentPlatform.WINDOWS_X86 else unix_arg @@ -58,32 +67,42 @@ def _get_platform_by_installer(installer: Path) -> DeploymentPlatform: if platform.arch() in name and platform.system() in name: return platform - # Special handling for Linux_x86 and Windows as the installer does not contain architecture in its name + # Special handling for Linux_x86 and Windows as the installer does not + # contain architecture in its name if DeploymentPlatform.WINDOWS_X86.system() in name: return DeploymentPlatform.WINDOWS_X86 return DeploymentPlatform.LINUX_X86 -def _get_available_installers() -> dict[DeploymentPlatform, list[Path]]: - installers: dict[DeploymentPlatform, list[Path]] = {k: [] for k in DeploymentPlatform} - for installer in sorted(INSTALLERS_DIRECTORY.glob(f"{INSTALLER_PARTIAL_NAME}*")): +def _get_available_installers() -> Dict[DeploymentPlatform, list[Path]]: + installers: dict[DeploymentPlatform, list[Path]] = { + k: [] for k in DeploymentPlatform} + for installer in sorted( + INSTALLERS_DIRECTORY.glob( + f"{INSTALLER_PARTIAL_NAME}*")): platform = _get_platform_by_installer(installer) installers[platform].append(installer) return installers -def get_installers(system: str, arch: str, version: str = "", include_paths: bool = False) -> list[Path | str]: +def get_installers(system: str, arch: str, version: str = "", + include_paths: bool = False) -> list[Path | str]: try: - # Special handling for mocking server behavior as URL for Linux installers contains "unix" instead of linux + # Special handling for mocking server behavior as URL for Linux + # installers contains "unix" instead of linux system = INSTALLER_SYSTEM_NAME_TYPE_MAP[system] - platform_installers = _get_available_installers()[DeploymentPlatform.from_system_and_arch(system, arch)] - installers = platform_installers if include_paths else [ins.name for ins in platform_installers] + platform_installers = _get_available_installers( + )[DeploymentPlatform.from_system_and_arch(system, arch)] + installers = platform_installers if include_paths else [ + ins.name for ins in platform_installers] if not version: return installers if version == "latest": return [installers[-1]] - return [installer for installer in installers if version in str(installer)] + return [ + installer for installer in installers if version in str(installer)] except Exception as ex: - logging.error(f"Failed to get installer for {system}_{arch} in {version} version: {ex}") + logging.error( + "Failed to get installer for %s_%s in %s version: %s", system, arch, version, ex) return [] diff --git a/roles/oneagent/tests/util/constants/common_constants.py b/roles/oneagent/tests/util/constants/common_constants.py index 3f9d0d4..ab1e3fb 100644 --- a/roles/oneagent/tests/util/constants/common_constants.py +++ b/roles/oneagent/tests/util/constants/common_constants.py @@ -4,7 +4,8 @@ # TODO: is cwd() correct? COMPONENT_TEST_BASE = Path().cwd() / "test_dir" TEST_DIRECTORY = COMPONENT_TEST_BASE / "working_dir" -RESOURCES_DIRECTORY = Path(__file__).resolve().parent.parent.parent / "resources" +RESOURCES_DIRECTORY = Path(__file__).resolve( +).parent.parent.parent / "resources" INSTALLERS_DIRECTORY = COMPONENT_TEST_BASE / "installers" SERVER_DIRECTORY = COMPONENT_TEST_BASE / "server" INSTALLERS_RESOURCE_DIR = RESOURCES_DIRECTORY / "installers" @@ -16,10 +17,15 @@ SERVER_PRIVATE_KEY_FILE_NAME = "server.key" INSTALLER_PARTIAL_NAME = "Dynatrace-OneAgent" -INSTALLER_SYSTEM_NAME_TYPE_MAP = {"linux": "linux", "unix": "linux", "aix": "aix", "windows": "windows"} +INSTALLER_SYSTEM_NAME_TYPE_MAP = { + "linux": "linux", + "unix": "linux", + "aix": "aix", + "windows": "windows"} INSTALLER_SERVER_TOKEN = "abcdefghijk1234567890" + class InstallerVersion(Enum): OLD = "1.199.0.20241008-150308" LATEST = "1.300.0.20241008-150308" diff --git a/roles/oneagent/tests/util/constants/unix_constants.py b/roles/oneagent/tests/util/constants/unix_constants.py index 5079c25..dd294a9 100644 --- a/roles/oneagent/tests/util/constants/unix_constants.py +++ b/roles/oneagent/tests/util/constants/unix_constants.py @@ -3,4 +3,5 @@ UNIX_ONEAGENTCTL_BIN_NAME = "oneagentctl" UNIX_DEFAULT_INSTALL_PATH = Path("/opt") / "dynatrace" / "oneagent" UNIX_DEFAULT_DOWNLOAD_PATH = Path("/tmp") -UNIX_ONEAGENTCTL_PATH = UNIX_DEFAULT_INSTALL_PATH / "agent" / "tools" / UNIX_ONEAGENTCTL_BIN_NAME +UNIX_ONEAGENTCTL_PATH = UNIX_DEFAULT_INSTALL_PATH / \ + "agent" / "tools" / UNIX_ONEAGENTCTL_BIN_NAME diff --git a/roles/oneagent/tests/util/constants/windows_constants.py b/roles/oneagent/tests/util/constants/windows_constants.py index be3f8a2..60ed6e3 100644 --- a/roles/oneagent/tests/util/constants/windows_constants.py +++ b/roles/oneagent/tests/util/constants/windows_constants.py @@ -1,6 +1,8 @@ from pathlib import Path WINDOWS_ONEAGENTCTL_BIN_NAME = "oneagentctl.exe" -WINDOWS_DEFAULT_INSTALL_PATH = Path("C:\\Program Files") / "dynatrace" / "oneagent" +WINDOWS_DEFAULT_INSTALL_PATH = Path( + "C:\\Program Files") / "dynatrace" / "oneagent" WINDOWS_DEFAULT_DOWNLOAD_PATH = Path("%TEMP%") -WINDOWS_ONEAGENTCTL_PATH = WINDOWS_DEFAULT_INSTALL_PATH / "agent" / "tools" / WINDOWS_ONEAGENTCTL_BIN_NAME +WINDOWS_ONEAGENTCTL_PATH = WINDOWS_DEFAULT_INSTALL_PATH / \ + "agent" / "tools" / WINDOWS_ONEAGENTCTL_BIN_NAME diff --git a/roles/oneagent/tests/util/installer_provider.py b/roles/oneagent/tests/util/installer_provider.py index 98d1074..79e6da6 100644 --- a/roles/oneagent/tests/util/installer_provider.py +++ b/roles/oneagent/tests/util/installer_provider.py @@ -1,130 +1,173 @@ -import logging -import subprocess - -import requests - -from pathlib import Path - -from util.test_data_types import DeploymentPlatform, PlatformCollection -from util.ssl_certificate_generator import SSLCertificateGenerator -from util.constants.common_constants import (INSTALLERS_RESOURCE_DIR, INSTALLERS_DIRECTORY, - INSTALLER_PRIVATE_KEY_FILE_NAME, INSTALLER_CERTIFICATE_FILE_NAME, - InstallerVersion) - - -def get_file_content(path: Path) -> list[str]: - with path.open("r") as f: - return f.readlines() - - -def replace_tag(source: list[str], old: str, new: str) -> list[str]: - return [line.replace(old, new) for line in source] - - -def sign_installer(installer: list[str]) -> list[str]: - cmd = ["openssl", "cms", "-sign", - "-signer", f"{INSTALLERS_DIRECTORY / INSTALLER_CERTIFICATE_FILE_NAME}", - "-inkey", f"{INSTALLERS_DIRECTORY / INSTALLER_PRIVATE_KEY_FILE_NAME}"] - - proc = subprocess.run(cmd, input=f"{''.join(installer)}", encoding="utf-8", stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - if proc.returncode != 0: - logging.error(f"Failed to sign installer: {proc.stdout}") - return [] - - signed_installer = proc.stdout.splitlines() - delimiter = next(l for l in signed_installer if l.startswith("----")) - index = signed_installer.index(delimiter) - signed_installer = signed_installer[index + 1:] - - custom_delimiter = "----SIGNED-INSTALLER" - return [f"{l}\n" if not l.startswith(delimiter) else f"{l.replace(delimiter, custom_delimiter)}\n" for l in signed_installer] - - -def generate_installers() -> bool: - uninstall_template = get_file_content(INSTALLERS_RESOURCE_DIR / "uninstall.sh") - uninstall_code = replace_tag(uninstall_template, "$", r"\$") - - oneagentctl_template = get_file_content(INSTALLERS_RESOURCE_DIR / "oneagentctl.sh") - oneagentctl_code = replace_tag(oneagentctl_template, "$", r"\$") - - installer_partial_name = "Dynatrace-OneAgent-Linux" - installer_template = get_file_content(INSTALLERS_RESOURCE_DIR / f"{installer_partial_name}.sh") - installer_template = replace_tag(installer_template, "##UNINSTALL_CODE##", "".join(uninstall_code)) - installer_template = replace_tag(installer_template, "##ONEAGENTCTL_CODE##", "".join(oneagentctl_code)) - - generator = SSLCertificateGenerator( - country_name="US", - state_name="California", - locality_name="San Francisco", - organization_name="Dynatrace", - common_name="127.0.0.1" - ) - generator.generate_and_save(f"{INSTALLERS_DIRECTORY / INSTALLER_PRIVATE_KEY_FILE_NAME}", - f"{INSTALLERS_DIRECTORY / INSTALLER_CERTIFICATE_FILE_NAME}") - - # REMOVE INSTALLER VERSION - for version in InstallerVersion: - installer_code = replace_tag(installer_template, "##VERSION##", version.value) - installer_code = sign_installer(installer_code) - if not installer_code: - return False - with open(INSTALLERS_DIRECTORY / f"{installer_partial_name}-{version.value}.sh", "w") as f: - f.writelines(installer_code) - - return True - - -def get_installers_versions_from_tenant(tenant: str, tenant_token: str, system_family: str) -> list[str]: - url = f"{tenant}/api/v1/deployment/installer/agent/versions/{system_family}/default" - headers = {"accept": "application/json", "Authorization": f"Api-Token {tenant_token}"} - - resp = requests.get(url, headers=headers) - - try: - versions = resp.json()["availableVersions"] - if len(versions) < 2: - logging.error(f"List of available installers is too short: {versions}") - else: - return [versions[0], versions[-1]] - except KeyError: - logging.error(f"Failed to get list of installer versions: {resp.content}") - return [] - - -def download_and_save(path: Path, url: str, headers: dict[str, str]) -> bool: - resp = requests.get(url, headers=headers) - - if not resp.ok: - logging.error(f"Failed to download file {path}: {resp.text}") - return False - - with path.open("wb") as f: - f.write(resp.content) - return True - - -def download_signature(url: str) -> bool: - path = INSTALLERS_DIRECTORY / INSTALLER_CERTIFICATE_FILE_NAME - return download_and_save(path, url, {}) - - -def download_installer(tenant, tenant_token, version: str, platform: DeploymentPlatform) -> bool: - family = platform.family() - url = f"{tenant}/api/v1/deployment/installer/agent/{family}/default/version/{version}" - headers = {"accept": "application/octet-stream", "Authorization": f"Api-Token {tenant_token}"} - - ext = "exe" if family == "windows" else "sh" - path = INSTALLERS_DIRECTORY / f"Dynatrace-OneAgent-{family}_{platform.arch()}-{version}.{ext}" - - return download_and_save(path, url, headers) - - -def download_installers(tenant, tenant_token, platforms: PlatformCollection) -> bool: - for platform, _ in platforms.items(): - versions = get_installers_versions_from_tenant(tenant, tenant_token, platform.family()) - if not versions: - return False - for version in versions: - if not download_installer(tenant, tenant_token, version, platform): - return False - return True +import logging +import subprocess + +import requests + +from pathlib import Path + +from util.test_data_types import DeploymentPlatform, PlatformCollection +from util.ssl_certificate_generator import SSLCertificateGenerator +from util.constants.common_constants import ( + INSTALLERS_RESOURCE_DIR, + INSTALLERS_DIRECTORY, + INSTALLER_PRIVATE_KEY_FILE_NAME, + INSTALLER_CERTIFICATE_FILE_NAME, + InstallerVersion) + + +def get_file_content(path: Path) -> list[str]: + with path.open("r") as f: + return f.readlines() + + +def replace_tag(source: list[str], old: str, new: str) -> list[str]: + return [line.replace(old, new) for line in source] + + +def sign_installer(installer: list[str]) -> list[str]: + cmd = ["openssl", + "cms", + "-sign", + "-signer", + f"{INSTALLERS_DIRECTORY / INSTALLER_CERTIFICATE_FILE_NAME}", + "-inkey", + f"{INSTALLERS_DIRECTORY / INSTALLER_PRIVATE_KEY_FILE_NAME}"] + + proc = subprocess.run( + cmd, + input=f"{''.join(installer)}", + encoding="utf-8", + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=False) + if proc.returncode != 0: + logging.error("Failed to sign installer: %s") + return [] + + signed_installer = proc.stdout.splitlines() + delimiter = next(l for l in signed_installer if l.startswith("----")) + index = signed_installer.index(delimiter) + signed_installer = signed_installer[index + 1:] + + custom_delimiter = "----SIGNED-INSTALLER" + + return [f"{l}\n" if not l.startswith(delimiter) + else f"{l.replace(delimiter, custom_delimiter)}\n" + for l in signed_installer] + + +def generate_installers() -> bool: + uninstall_template = get_file_content( + INSTALLERS_RESOURCE_DIR / "uninstall.sh") + uninstall_code = replace_tag(uninstall_template, "$", r"\$") + + oneagentctl_template = get_file_content( + INSTALLERS_RESOURCE_DIR / "oneagentctl.sh") + oneagentctl_code = replace_tag(oneagentctl_template, "$", r"\$") + + installer_partial_name = "Dynatrace-OneAgent-Linux" + installer_template = get_file_content( + INSTALLERS_RESOURCE_DIR / + f"{installer_partial_name}.sh") + installer_template = replace_tag( + installer_template, + "##UNINSTALL_CODE##", + "".join(uninstall_code)) + installer_template = replace_tag( + installer_template, + "##ONEAGENTCTL_CODE##", + "".join(oneagentctl_code)) + + generator = SSLCertificateGenerator( + country_name="US", + state_name="California", + locality_name="San Francisco", + organization_name="Dynatrace", + common_name="127.0.0.1" + ) + generator.generate_and_save(f"{INSTALLERS_DIRECTORY /INSTALLER_PRIVATE_KEY_FILE_NAME}", + f"{INSTALLERS_DIRECTORY /INSTALLER_CERTIFICATE_FILE_NAME}") + + # REMOVE INSTALLER VERSION + for version in InstallerVersion: + installer_code = replace_tag( + installer_template, "##VERSION##", version.value) + installer_code = sign_installer(installer_code) + if not installer_code: + return False + with open(INSTALLERS_DIRECTORY / f"{installer_partial_name}-{version.value}.sh", "w") as f: + f.writelines(installer_code) + + return True + + +def get_installers_versions_from_tenant( + tenant: str, + tenant_token: str, + system_family: str) -> list[str]: + url = f"{tenant}/api/v1/deployment/installer/agent/versions/{system_family}/default" + headers = {"accept": "application/json", + "Authorization": f"Api-Token {tenant_token}"} + + resp = requests.get(url, headers=headers) + + try: + versions = resp.json()["availableVersions"] + if len(versions) < 2: + logging.error( + "List of available installers is too short: %s", versions) + else: + return [versions[0], versions[-1]] + except KeyError: + logging.error( + "Failed to get list of installer versions: %s", resp.content) + return [] + + +def download_and_save(path: Path, url: str, headers: dict[str, str]) -> bool: + resp = requests.get(url, headers=headers) + + if not resp.ok: + logging.error("Failed to download file %s: %s", path, resp.text) + return False + + with path.open("wb") as f: + f.write(resp.content) + return True + + +def download_signature(url: str) -> bool: + path = INSTALLERS_DIRECTORY / INSTALLER_CERTIFICATE_FILE_NAME + return download_and_save(path, url, {}) + + +def download_installer( + tenant, + tenant_token, + version: str, + platform: DeploymentPlatform) -> bool: + family = platform.family() + url = f"{tenant}/api/v1/deployment/installer/agent/{family}/default/version/{version}" + headers = {"accept": "application/octet-stream", + "Authorization": f"Api-Token {tenant_token}"} + + ext = "exe" if family == "windows" else "sh" + path = INSTALLERS_DIRECTORY / \ + f"Dynatrace-OneAgent-{family}_{platform.arch()}-{version}.{ext}" + + return download_and_save(path, url, headers) + + +def download_installers( + tenant, + tenant_token, + platforms: PlatformCollection) -> bool: + for platform, hosts in platforms.items(): + versions = get_installers_versions_from_tenant( + tenant, tenant_token, platform.family()) + if not versions: + return False + for version in versions: + if not download_installer(tenant, tenant_token, version, platform): + return False + return True diff --git a/roles/oneagent/tests/util/ssl_certificate_generator.py b/roles/oneagent/tests/util/ssl_certificate_generator.py index 941b209..dd0dedb 100644 --- a/roles/oneagent/tests/util/ssl_certificate_generator.py +++ b/roles/oneagent/tests/util/ssl_certificate_generator.py @@ -11,7 +11,14 @@ class SSLCertificateGenerator: - def __init__(self, country_name, state_name, locality_name, organization_name, common_name, validity_days=365): + def __init__( + self, + country_name, + state_name, + locality_name, + organization_name, + common_name, + validity_days=365): self.country_name = country_name self.state_name = state_name self.locality_name = locality_name @@ -26,7 +33,10 @@ def _generate_key_pair(self) -> (rsa.RSAPrivateKey, rsa.RSAPublicKey): ) return private_key, private_key.public_key() - def _generate_certificate(self, private_key, public_key) -> x509.Certificate: + def _generate_certificate( + self, + private_key, + public_key) -> x509.Certificate: builder = x509.CertificateBuilder() builder = builder.subject_name(x509.Name([ x509.NameAttribute(NameOID.COUNTRY_NAME, self.country_name), @@ -43,14 +53,15 @@ def _generate_certificate(self, private_key, public_key) -> x509.Certificate: x509.NameAttribute(NameOID.COMMON_NAME, self.common_name), ])) builder = builder.not_valid_before(datetime.utcnow()) - builder = builder.not_valid_after(datetime.utcnow() + timedelta(days=self.validity_days)) + builder = builder.not_valid_after( + datetime.utcnow() + + timedelta( + days=self.validity_days)) builder = builder.serial_number(int(uuid.uuid4())) builder = builder.public_key(public_key) - builder = builder.add_extension( - x509.SubjectAlternativeName([x509.IPAddress(ipaddress.ip_address(self.common_name))]), - critical=False, - ) + builder = builder.add_extension(x509.SubjectAlternativeName( + [x509.IPAddress(ipaddress.ip_address(self.common_name))]), critical=False, ) return builder.sign( private_key=private_key, algorithm=hashes.SHA256(), @@ -71,5 +82,10 @@ def _save_certificate(self, filename, certificate) -> None: def generate_and_save(self, private_key_path, certificate_path) -> None: private_key, public_key = self._generate_key_pair() self._save_private_key(private_key_path, private_key) - self._save_certificate(certificate_path, self._generate_certificate(private_key, public_key)) - logging.info("Self-signed certificate generated and saved successfully") + self._save_certificate( + certificate_path, + self._generate_certificate( + private_key, + public_key)) + logging.info( + "Self-signed certificate generated and saved successfully") diff --git a/roles/oneagent/tests/util/test_data_types.py b/roles/oneagent/tests/util/test_data_types.py index 348d3ba..25e66b7 100644 --- a/roles/oneagent/tests/util/test_data_types.py +++ b/roles/oneagent/tests/util/test_data_types.py @@ -27,7 +27,7 @@ def arch(self) -> str: return str(self.value).split("_")[1] def system(self) -> str: - return str(self.value).split("_")[0] + return str(self.value).split("_", maxsplit=1)[0] @staticmethod def from_str(param: str) -> "DeploymentPlatform": diff --git a/roles/oneagent/tests/util/test_helpers.py b/roles/oneagent/tests/util/test_helpers.py index a2195d5..98b1921 100644 --- a/roles/oneagent/tests/util/test_helpers.py +++ b/roles/oneagent/tests/util/test_helpers.py @@ -8,8 +8,12 @@ from ansible.config import AnsibleConfig from ansible.runner import AnsibleRunner from util.common_utils import get_oneagentctl_path, get_platform_argument -from util.constants.common_constants import (INSTALLER_PARTIAL_NAME, INSTALLER_SERVER_TOKEN, INSTALLER_CERTIFICATE_FILE_NAME, - SERVER_DIRECTORY, SERVER_CERTIFICATE_FILE_NAME) +from util.constants.common_constants import ( + INSTALLER_PARTIAL_NAME, + INSTALLER_SERVER_TOKEN, + INSTALLER_CERTIFICATE_FILE_NAME, + SERVER_DIRECTORY, + SERVER_CERTIFICATE_FILE_NAME) from util.test_data_types import DeploymentPlatform, DeploymentResult, PlatformCollection CallableOperation = Callable[[DeploymentPlatform, str, Any], None] @@ -24,46 +28,65 @@ def enable_for_system_family(family: str) -> Callable: def func_wrapper(func): @functools.wraps(func) def params_wrapper(*args, **kwargs): - config: AnsibleConfig = _get_param_by_name("configurator", **kwargs) - platforms: PlatformCollection = _get_param_by_name("platforms", **kwargs) + config: AnsibleConfig = _get_param_by_name( + "configurator", **kwargs) + platforms: PlatformCollection = _get_param_by_name( + "platforms", **kwargs) if any(p.family() == family for p in platforms.keys()): config.set_deployment_hosts(family) func(*args, **kwargs) else: - logging.info(f"Skipping test for specified platform") + logging.info("Skipping test for specified platform") return params_wrapper return func_wrapper -def perform_operation_on_platforms(platforms: PlatformCollection, operation: CallableOperation, *args) -> None: +def perform_operation_on_platforms( + platforms: PlatformCollection, + operation: CallableOperation, + *args) -> None: for platform, hosts in platforms.items(): for address in hosts: operation(platform, address, *args) -def set_ca_cert_download_params(config: AnsibleConfig, installer_server_url: str) -> None: - config.set_common_parameter(config.CA_CERT_DOWNLOAD_URL_KEY, f"{installer_server_url}/{INSTALLER_CERTIFICATE_FILE_NAME}") - config.set_common_parameter(config.CA_CERT_DOWNLOAD_CERT_KEY, f"{SERVER_DIRECTORY / SERVER_CERTIFICATE_FILE_NAME}") +def set_ca_cert_download_params( + config: AnsibleConfig, + installer_server_url: str) -> None: + config.set_common_parameter( + config.CA_CERT_DOWNLOAD_URL_KEY, + f"{installer_server_url}/{INSTALLER_CERTIFICATE_FILE_NAME}") + config.set_common_parameter( + config.CA_CERT_DOWNLOAD_CERT_KEY, f"{SERVER_DIRECTORY / SERVER_CERTIFICATE_FILE_NAME}") config.set_common_parameter(config.FORCE_CERT_DOWNLOAD_KEY, True) -def set_installer_download_params(config: AnsibleConfig, installer_server_url: str) -> None: - config.set_common_parameter(config.ENVIRONMENT_URL_KEY, installer_server_url) +def set_installer_download_params( + config: AnsibleConfig, + installer_server_url: str) -> None: + config.set_common_parameter( + config.ENVIRONMENT_URL_KEY, + installer_server_url) config.set_common_parameter(config.PAAS_TOKEN_KEY, INSTALLER_SERVER_TOKEN) - config.set_common_parameter(config.INSTALLER_DOWNLOAD_CERT_KEY, f"{SERVER_DIRECTORY / SERVER_CERTIFICATE_FILE_NAME}") + config.set_common_parameter( + config.INSTALLER_DOWNLOAD_CERT_KEY, f"{SERVER_DIRECTORY / SERVER_CERTIFICATE_FILE_NAME}") for platform in DeploymentPlatform: - config.set_platform_parameter(platform, config.INSTALLER_ARCH_KEY, platform.arch()) + config.set_platform_parameter( + platform, config.INSTALLER_ARCH_KEY, platform.arch()) set_ca_cert_download_params(config, installer_server_url) -def run_deployment(runner: AnsibleRunner, ignore_errors: bool = False) -> DeploymentResult: +def run_deployment( + runner: AnsibleRunner, + ignore_errors: bool = False) -> DeploymentResult: results = runner.run_deployment() logging.info("Deployment finished") for result in results: - logging.debug(f"Exit code: {result.returncode}\nOutput: {result.stdout}, Error: {result.stderr}") + logging.debug( + "Exit code: %s\nOutput: %s, Error: %s", result.returncode, result.stdout, result.stderr) if not ignore_errors: logging.info("Check exit codes") @@ -73,10 +96,13 @@ def run_deployment(runner: AnsibleRunner, ignore_errors: bool = False) -> Deploy def check_agent_state( - platform: DeploymentPlatform, address: str, wrapper: PlatformCommandWrapper, installed: bool -) -> None: + platform: DeploymentPlatform, + address: str, + wrapper: PlatformCommandWrapper, + installed: bool) -> None: logging.debug("Platform: %s, IP: %s", platform, address) - result = wrapper.file_exists(platform, address, get_oneagentctl_path(platform)) + result = wrapper.file_exists( + platform, address, get_oneagentctl_path(platform)) assert result.returncode == 0 if installed else 1 @@ -89,7 +115,12 @@ def check_download_directory( windows_path: Path, ) -> None: logging.debug("Platform: %s, IP: %s", platform, address) - download_path: Path = get_platform_argument(platform, unix_path, windows_path) + download_path: Path = get_platform_argument( + platform, unix_path, windows_path) installer_path = download_path / f"{INSTALLER_PARTIAL_NAME}*" - assert wrapper.directory_exists(platform, address, download_path).returncode == 0 - assert wrapper.file_exists(platform, address, installer_path).returncode == 0 if exists else 1 + assert wrapper.directory_exists( + platform, address, download_path).returncode == 0 + assert wrapper.file_exists( + platform, + address, + installer_path).returncode == 0 if exists else 1