From 247870ec5e501ae8d4190fe8b5e11d4140b28ff2 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Tue, 9 Apr 2024 00:19:43 -0300 Subject: [PATCH 001/195] Realease DTT1 --- deployability/Jenkinsfiles/Launcher.groovy | 30 + deployability/Jenkinsfiles/Provision.groovy | 24 + .../examples/dtt1-agents-2.yaml => README.MD} | 172 ++++- deployability/__init__.py | 0 deployability/deps/remote_requirements.txt | 5 +- deployability/deps/requirements.txt | 4 + deployability/modules/__init__.py | 6 + deployability/modules/allocation/__init__.py | 5 + .../modules/allocation/allocation.py | 174 +++++ .../modules/allocation/aws/__init__.py | 7 + .../modules/allocation/aws/credentials.py | 186 +++++ .../allocation/aws/helpers/userData.sh | 173 +++++ .../aws/helpers/windowsUserData.ps1 | 23 + .../modules/allocation/aws/instance.py | 98 +++ .../modules/allocation/aws/models.py | 20 + .../modules/allocation/aws/provider.py | 354 ++++++++++ .../modules/allocation/generic/__init__.py | 7 + .../modules/allocation/generic/credentials.py | 60 ++ .../modules/allocation/generic/instance.py | 101 +++ .../modules/allocation/generic/models.py | 132 ++++ .../modules/allocation/generic/provider.py | 204 ++++++ .../modules/allocation/generic/utils.py | 7 + deployability/modules/allocation/main.py | 38 + .../modules/allocation/static/specs/misc.yml | 17 + .../modules/allocation/static/specs/os.yml | 390 +++++++++++ .../modules/allocation/static/specs/size.yml | 34 + .../allocation/static/templates/vagrant.j2 | 31 + .../static/templates/vagrant_black_mini.j2 | 18 + .../static/templates/vagrant_macStadium.j2 | 18 + .../modules/allocation/vagrant/__init__.py | 8 + .../modules/allocation/vagrant/credentials.py | 131 ++++ .../modules/allocation/vagrant/instance.py | 247 +++++++ .../modules/allocation/vagrant/models.py | 32 + .../modules/allocation/vagrant/provider.py | 392 +++++++++++ .../modules/allocation/vagrant/utils.py | 108 +++ deployability/modules/generic/__init__.py | 4 + deployability/modules/generic/ansible.py | 127 ++++ .../modules/generic/logger/__init__.py | 4 + .../modules/generic/logger/config.yaml | 30 + .../modules/generic/logger/filters.py | 22 + .../modules/generic/logger/logger.py | 45 ++ deployability/modules/generic/models.py | 11 + deployability/modules/generic/parser.py | 19 + deployability/modules/generic/utils.py | 62 ++ deployability/modules/provision/README.MD | 256 +++++++ deployability/modules/provision/__init__.py | 4 + deployability/modules/provision/actions.py | 88 +++ deployability/modules/provision/handler.py | 110 +++ deployability/modules/provision/main.py | 31 + deployability/modules/provision/models.py | 73 ++ .../playbooks/package/install/install.j2 | 27 + .../playbooks/package/uninstall/uninstall.j2 | 14 + .../playbooks/source/install/python.j2 | 76 ++ .../wazuh/assistant/install/download.j2 | 15 + .../wazuh/assistant/install/install.j2 | 2 + .../wazuh/assistant/uninstall/download.j2 | 15 + .../wazuh/assistant/uninstall/uninstall.j2 | 2 + .../wazuh/package/install/install.j2 | 14 + .../wazuh/package/install/register.j2 | 7 + .../wazuh/package/install/service.j2 | 5 + .../wazuh/package/install/set_repo.j2 | 37 + .../wazuh/package/uninstall/uninstall.j2 | 15 + deployability/modules/provision/provision.py | 151 ++++ deployability/modules/provision/utils.py | 6 + deployability/modules/setup.py | 30 +- deployability/modules/testing/README.MD | 294 ++++++++ deployability/modules/testing/__init__.py | 6 + deployability/modules/testing/main.py | 30 + deployability/modules/testing/models.py | 32 + .../modules/testing/playbooks/cleanup.yml | 11 + .../modules/testing/playbooks/setup.yml | 12 + .../modules/testing/playbooks/test.yml | 12 + deployability/modules/testing/testing.py | 131 ++++ .../modules/testing/tests/__init__.py | 6 + .../modules/testing/tests/conftest.py | 55 ++ .../modules/testing/tests/helpers/__init__.py | 7 + .../modules/testing/tests/helpers/agent.py | 416 +++++++++++ .../testing/tests/helpers/constants.py | 75 ++ .../modules/testing/tests/helpers/executor.py | 87 +++ .../modules/testing/tests/helpers/generic.py | 658 ++++++++++++++++++ .../testing/tests/helpers/logger/__init__.py | 3 + .../testing/tests/helpers/logger/config.yaml | 22 + .../testing/tests/helpers/logger/filter.py | 23 + .../testing/tests/helpers/logger/logger.py | 24 + .../modules/testing/tests/helpers/manager.py | 358 ++++++++++ .../modules/testing/tests/helpers/utils.py | 69 ++ .../testing/tests/test_agent/__init__.py | 7 + .../testing/tests/test_agent/test_install.py | 105 +++ .../tests/test_agent/test_registration.py | 93 +++ .../testing/tests/test_agent/test_restart.py | 86 +++ .../testing/tests/test_agent/test_stop.py | 78 +++ .../tests/test_agent/test_uninstall.py | 91 +++ .../testing/tests/test_manager/__init__.py | 6 + .../tests/test_manager/test_install.py | 109 +++ .../tests/test_manager/test_restart.py | 53 ++ .../testing/tests/test_manager/test_stop.py | 55 ++ .../tests/test_manager/test_uninstall.py | 59 ++ deployability/modules/testing/utils.py | 7 + .../workflow_engine/{README.md => README.MD} | 22 +- .../modules/workflow_engine/__init__.py | 3 + .../modules/workflow_engine/__main__.py | 3 +- .../examples/dtt1-agents-aws.yaml | 142 ++++ .../examples/dtt1-agents-poc-aws.yaml | 106 +++ .../examples/dtt1-agents-poc2-vagrant.yaml | 105 +++ .../examples/dtt1-agents-vagrant.yaml | 138 ++++ .../examples/dtt1-managers-poc-aws.yaml | 74 ++ .../examples/dtt1-managers-poc-vagrant.yaml | 82 +++ .../workflow_engine/examples/test.yaml | 137 ++++ .../test/aws/test-agent-complete.yaml | 114 +++ .../test/aws/test-agent-restart-ins-prov.yaml | 143 ++++ .../test/aws/test-agent-stop-ins-prov.yaml | 143 ++++ .../aws/test-agent-uninstall-ins-prov.yaml | 143 ++++ .../test/vagrant/test-agent-complete.yaml | 114 +++ .../vagrant/test-agent-stop-ins-prov-2.yaml | 132 ++++ .../vagrant/test-agent-stop-ins-prov.yaml | 133 ++++ .../test-agent-uninstall-ins-prov.yaml | 143 ++++ .../workflow_engine/logger/__init__.py | 3 + .../workflow_engine/logger/config.yaml | 5 +- .../modules/workflow_engine/logger/filter.py | 23 + .../modules/workflow_engine/logger/logger.py | 12 +- .../modules/workflow_engine/models.py | 1 - .../workflow_engine/schema_validator.py | 1 - deployability/modules/workflow_engine/task.py | 1 - .../workflow_engine/workflow_processor.py | 2 +- deployability/plugins/__init__.py | 0 .../plugins/influxdb_reporter/.gitignore | 13 + .../plugins/influxdb_reporter/README.md | 62 ++ .../pytest_influxdb/__init__.py | 0 .../pytest_influxdb/plugin.py | 56 ++ .../pytest_influxdb/reporter.py | 222 ++++++ .../plugins/influxdb_reporter/setup.cfg | 9 + .../plugins/influxdb_reporter/setup.py | 38 + 132 files changed, 9908 insertions(+), 55 deletions(-) create mode 100755 deployability/Jenkinsfiles/Launcher.groovy create mode 100755 deployability/Jenkinsfiles/Provision.groovy rename deployability/{modules/workflow_engine/examples/dtt1-agents-2.yaml => README.MD} (53%) mode change 100755 => 100644 create mode 100755 deployability/__init__.py create mode 100755 deployability/modules/__init__.py create mode 100755 deployability/modules/allocation/__init__.py create mode 100755 deployability/modules/allocation/allocation.py create mode 100644 deployability/modules/allocation/aws/__init__.py create mode 100644 deployability/modules/allocation/aws/credentials.py create mode 100644 deployability/modules/allocation/aws/helpers/userData.sh create mode 100644 deployability/modules/allocation/aws/helpers/windowsUserData.ps1 create mode 100644 deployability/modules/allocation/aws/instance.py create mode 100644 deployability/modules/allocation/aws/models.py create mode 100644 deployability/modules/allocation/aws/provider.py create mode 100644 deployability/modules/allocation/generic/__init__.py create mode 100644 deployability/modules/allocation/generic/credentials.py create mode 100644 deployability/modules/allocation/generic/instance.py create mode 100644 deployability/modules/allocation/generic/models.py create mode 100644 deployability/modules/allocation/generic/provider.py create mode 100644 deployability/modules/allocation/generic/utils.py create mode 100755 deployability/modules/allocation/main.py create mode 100755 deployability/modules/allocation/static/specs/misc.yml create mode 100644 deployability/modules/allocation/static/specs/os.yml create mode 100755 deployability/modules/allocation/static/specs/size.yml create mode 100755 deployability/modules/allocation/static/templates/vagrant.j2 create mode 100644 deployability/modules/allocation/static/templates/vagrant_black_mini.j2 create mode 100755 deployability/modules/allocation/static/templates/vagrant_macStadium.j2 create mode 100644 deployability/modules/allocation/vagrant/__init__.py create mode 100755 deployability/modules/allocation/vagrant/credentials.py create mode 100755 deployability/modules/allocation/vagrant/instance.py create mode 100644 deployability/modules/allocation/vagrant/models.py create mode 100644 deployability/modules/allocation/vagrant/provider.py create mode 100644 deployability/modules/allocation/vagrant/utils.py create mode 100755 deployability/modules/generic/__init__.py create mode 100755 deployability/modules/generic/ansible.py create mode 100644 deployability/modules/generic/logger/__init__.py create mode 100644 deployability/modules/generic/logger/config.yaml create mode 100644 deployability/modules/generic/logger/filters.py create mode 100644 deployability/modules/generic/logger/logger.py create mode 100644 deployability/modules/generic/models.py create mode 100755 deployability/modules/generic/parser.py create mode 100644 deployability/modules/generic/utils.py create mode 100644 deployability/modules/provision/README.MD create mode 100755 deployability/modules/provision/__init__.py create mode 100644 deployability/modules/provision/actions.py create mode 100644 deployability/modules/provision/handler.py create mode 100755 deployability/modules/provision/main.py create mode 100755 deployability/modules/provision/models.py create mode 100644 deployability/modules/provision/playbooks/package/install/install.j2 create mode 100644 deployability/modules/provision/playbooks/package/uninstall/uninstall.j2 create mode 100644 deployability/modules/provision/playbooks/source/install/python.j2 create mode 100755 deployability/modules/provision/playbooks/wazuh/assistant/install/download.j2 create mode 100755 deployability/modules/provision/playbooks/wazuh/assistant/install/install.j2 create mode 100755 deployability/modules/provision/playbooks/wazuh/assistant/uninstall/download.j2 create mode 100755 deployability/modules/provision/playbooks/wazuh/assistant/uninstall/uninstall.j2 create mode 100755 deployability/modules/provision/playbooks/wazuh/package/install/install.j2 create mode 100755 deployability/modules/provision/playbooks/wazuh/package/install/register.j2 create mode 100755 deployability/modules/provision/playbooks/wazuh/package/install/service.j2 create mode 100755 deployability/modules/provision/playbooks/wazuh/package/install/set_repo.j2 create mode 100755 deployability/modules/provision/playbooks/wazuh/package/uninstall/uninstall.j2 create mode 100755 deployability/modules/provision/provision.py create mode 100644 deployability/modules/provision/utils.py create mode 100644 deployability/modules/testing/README.MD create mode 100755 deployability/modules/testing/__init__.py create mode 100755 deployability/modules/testing/main.py create mode 100644 deployability/modules/testing/models.py create mode 100644 deployability/modules/testing/playbooks/cleanup.yml create mode 100644 deployability/modules/testing/playbooks/setup.yml create mode 100644 deployability/modules/testing/playbooks/test.yml create mode 100644 deployability/modules/testing/testing.py create mode 100755 deployability/modules/testing/tests/__init__.py create mode 100755 deployability/modules/testing/tests/conftest.py create mode 100755 deployability/modules/testing/tests/helpers/__init__.py create mode 100644 deployability/modules/testing/tests/helpers/agent.py create mode 100755 deployability/modules/testing/tests/helpers/constants.py create mode 100644 deployability/modules/testing/tests/helpers/executor.py create mode 100644 deployability/modules/testing/tests/helpers/generic.py create mode 100644 deployability/modules/testing/tests/helpers/logger/__init__.py create mode 100644 deployability/modules/testing/tests/helpers/logger/config.yaml create mode 100644 deployability/modules/testing/tests/helpers/logger/filter.py create mode 100644 deployability/modules/testing/tests/helpers/logger/logger.py create mode 100644 deployability/modules/testing/tests/helpers/manager.py create mode 100644 deployability/modules/testing/tests/helpers/utils.py create mode 100644 deployability/modules/testing/tests/test_agent/__init__.py create mode 100644 deployability/modules/testing/tests/test_agent/test_install.py create mode 100644 deployability/modules/testing/tests/test_agent/test_registration.py create mode 100644 deployability/modules/testing/tests/test_agent/test_restart.py create mode 100644 deployability/modules/testing/tests/test_agent/test_stop.py create mode 100644 deployability/modules/testing/tests/test_agent/test_uninstall.py create mode 100644 deployability/modules/testing/tests/test_manager/__init__.py create mode 100644 deployability/modules/testing/tests/test_manager/test_install.py create mode 100644 deployability/modules/testing/tests/test_manager/test_restart.py create mode 100644 deployability/modules/testing/tests/test_manager/test_stop.py create mode 100644 deployability/modules/testing/tests/test_manager/test_uninstall.py create mode 100644 deployability/modules/testing/utils.py rename deployability/modules/workflow_engine/{README.md => README.MD} (97%) mode change 100755 => 100644 create mode 100755 deployability/modules/workflow_engine/examples/dtt1-agents-aws.yaml create mode 100644 deployability/modules/workflow_engine/examples/dtt1-agents-poc-aws.yaml create mode 100644 deployability/modules/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml create mode 100755 deployability/modules/workflow_engine/examples/dtt1-agents-vagrant.yaml create mode 100644 deployability/modules/workflow_engine/examples/dtt1-managers-poc-aws.yaml create mode 100644 deployability/modules/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml create mode 100644 deployability/modules/workflow_engine/examples/test.yaml create mode 100755 deployability/modules/workflow_engine/examples/test/aws/test-agent-complete.yaml create mode 100755 deployability/modules/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml create mode 100755 deployability/modules/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml create mode 100755 deployability/modules/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml create mode 100755 deployability/modules/workflow_engine/examples/test/vagrant/test-agent-complete.yaml create mode 100755 deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml create mode 100755 deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml create mode 100755 deployability/modules/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml create mode 100644 deployability/modules/workflow_engine/logger/filter.py create mode 100755 deployability/plugins/__init__.py create mode 100755 deployability/plugins/influxdb_reporter/.gitignore create mode 100755 deployability/plugins/influxdb_reporter/README.md create mode 100755 deployability/plugins/influxdb_reporter/pytest_influxdb/__init__.py create mode 100755 deployability/plugins/influxdb_reporter/pytest_influxdb/plugin.py create mode 100755 deployability/plugins/influxdb_reporter/pytest_influxdb/reporter.py create mode 100755 deployability/plugins/influxdb_reporter/setup.cfg create mode 100755 deployability/plugins/influxdb_reporter/setup.py diff --git a/deployability/Jenkinsfiles/Launcher.groovy b/deployability/Jenkinsfiles/Launcher.groovy new file mode 100755 index 0000000000..75e88108e8 --- /dev/null +++ b/deployability/Jenkinsfiles/Launcher.groovy @@ -0,0 +1,30 @@ +String jenkins_reference = params.getOrDefault('JENKINS_REFERENCE', 'enhancement/4751-dtt1-iteration-2-poc') +String launcher_path = "modules/provision" +String task_flow_launcher = "main.py" +String workflow = "modules/workflow_engine/examples/dtt1-managers.yaml" +String schema = "modules/workflow_engine/schema.json" + +// Jenkinsfile + +node { + + try { + stage('Clone Repo') { + print("Clone repository") + git branch: "${JENKINS_REFERENCE}", url: 'https://github.com/wazuh/wazuh-qa.git' + } + + stage('Launch Task Flow') { + print("Launch Task Flow dry run") + sh "cd ${env.WORKSPACE}/deployability && python3 ${launcher_path}/${task_flow_launcher} ${workflow} --dry-run" + + print("Launch Task Flow") + sh "cd ${env.WORKSPACE}/deployability && python3 ${launcher_path}/${task_flow_launcher} ${workflow} ${schema}" + } + } + finally{ + stage('Remove venv') { + sh "rm -rf ${env.WORKSPACE}/deployability/venv" + } + } +} \ No newline at end of file diff --git a/deployability/Jenkinsfiles/Provision.groovy b/deployability/Jenkinsfiles/Provision.groovy new file mode 100755 index 0000000000..eb50ddc6dc --- /dev/null +++ b/deployability/Jenkinsfiles/Provision.groovy @@ -0,0 +1,24 @@ +String provision_path = "${WORKSPACE}/modules/provision" +String provision_script = "main.py" +String inventory = "inventory.yaml" +String jenkins_reference = params.getOrDefault('JENKINS_REFERENCE', 'enhancement/4665-dtt1-poc') + +// Jenkinsfile + +node { + + stage('Clone Repo') { + print("Clone repository") + git branch: "${JENKINS_REFERENCE}", url: 'https://github.com/wazuh/wazuh-qa.git' + } + + stage('Provision') { + print("Launch provision") + sh "python3 ${provision_path}/${provision_script} -i ${inventory}" + } + + stage('Remove venv') { + sh "rm -rf ${env.WORKSPACE}/poc-tests/venv" + } + +} \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/dtt1-agents-2.yaml b/deployability/README.MD old mode 100755 new mode 100644 similarity index 53% rename from deployability/modules/workflow_engine/examples/dtt1-agents-2.yaml rename to deployability/README.MD index fa979a6f81..f20c22dd01 --- a/deployability/modules/workflow_engine/examples/dtt1-agents-2.yaml +++ b/deployability/README.MD @@ -1,14 +1,67 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +## Deployability general documentation + +### User documentation + +To perform Deployability type tests, it is necessary to: + +- Install Python libraries + +For this purpose, it is recommended to use virtual environments. Follow the technical [documentation](https://docs.python.org/es/3/library/venv.html): + +1. Activate the environment: + +``` +source {venv directory}/bin/activate +``` + +2. Clone the wazuh-qa repository: + +Navigate to the project directory and switch to the project branch: + +``` +git clone https://github.com/wazuh/wazuh-qa.git +``` + +Navigate to the project directory and switch to the project branch: + +``` +cd wazuh-qa +git checkout {project-branch} +``` + +3. Install requirements: + +``` +pip3 install -r deployability/deps/requirements.txt +``` + +4. Install the Workflow engine library and its launcher: + +While in wazuh-qa: + +``` +cd modules +pip3 uninstall -y workflow_engine && pip3 install . +``` + +5. Test Fixture to Execute + +It will be necessary to create a fixture (yaml file) where the infrastructure, provisioning, and tests to be executed will be declared. + +> Note: It is possible to find some fixture examples in deployability/modules/workflow_engine/examples/ + + +Example: + +``` version: 0.1 -description: This workflow is used to test agents deployment with a single manager. +description: This workflow is used to test agents deployment por DDT1 PoC variables: agents-os: - linux-ubuntu-22.04-amd64 manager-os: linux-ubuntu-22.04-amd64 infra-provider: vagrant - working-dir: /tmp/dtt1 + working-dir: /tmp/dtt1-poc tasks: # Generic agent test task @@ -70,8 +123,7 @@ tasks: - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - install: - component: wazuh-manager - type: aio - version: "4.7.0" + type: package depends-on: - "allocate-manager" @@ -112,9 +164,8 @@ tasks: - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - install: - component: wazuh-agent - type: aio - version: "4.8.0" - live: False + type: package + - component: curl depends-on: - "allocate-{agent}" - "provision-manager" @@ -167,4 +218,103 @@ tasks: - track-output: "{working-dir}/agent-{agent}/track.yaml" foreach: - variable: agents-os - as: agent \ No newline at end of file + as: agent +``` + +Following the schema of the example: + +Configure the following parameters depending on your test case: + +``` +variables/agent-os +variables/manager-os +infra-provider +working-dir +tasks +``` + +Pay attention to the tasks: + +``` +args +depends-on +``` + +> Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill depends-on, consider the steps of your test (allocation, provision, and test) + +6. Execution of Command (local): + +Execute the command by referencing the parameters required by the library (launcher). + +``` +python3 -m workflow_engine {.yaml fixture path} +``` + +Example + +``` +python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml +``` + +> Note: The command execution can also be mediated through Jenkins. + +--- + +### Technical documentation + +- Modules + +The framework has 4 modules (`allocation`, `provision`, `testing`, `observability`) that must act consecutively, and a 5th module that orchestrates the previous modules (`Workflow`). + +|Module|Description +|--|--| +|Workflow (Orchestrator)|Receives a YAML containing all the instructions to execute for the test development. It contains instructions for allocation, provision, and testing. +|Allocation| Receives instructions for the desired architecture and creates the structures, generating IPs, ports either in AWS or locally with Vagrant. +|Provision| Installs applications on the structures created in allocation. +|Testing| Executes tests on the previously defined structures and trigger actions depending the test. +|Observability| Allows the ordered and indexed visualization of data obtained in the 3 previous modules. + +- Directory Structure + +``` +wazuh-qa/ +└── deployability + ├── deps + ├── Jenkinsfiles + ├── modules + │ ├── allocation + │ ├── generic + │ ├── provision + │ ├── testing + │ └── workflow_engine + └── plugins +``` +Deployability contains the following directories: + +|Directory|Description| +|---|---| +|deps|Contains information about the dependencies used for installing the necessary libraries for running the framework.| +|Jenkinsfiles|Contains instructions for the pipelines for test execution.| +|modules|Contains files, the launcher (main.py), and playbooks.| +|plugins|Contains modules for the observability plugin.| + +#### Overview + +![image](https://github.com/wazuh/wazuh-qa/assets/125690423/d09dfa61-67cb-410d-8677-bd904d5c4d51) + +#### Allocator, provision & test process + +![image](https://github.com/wazuh/wazuh-qa/assets/125690423/b1e24a43-13d7-4035-a3e5-5ace4667ec10) + + + +---- + + +[draw-plot.zip](https://github.com/wazuh/wazuh-qa/files/14330528/plot.zip) + + + +### License + +WAZUH Copyright (C) 2015 Wazuh Inc. (License GPLv2) diff --git a/deployability/__init__.py b/deployability/__init__.py new file mode 100755 index 0000000000..e69de29bb2 diff --git a/deployability/deps/remote_requirements.txt b/deployability/deps/remote_requirements.txt index d9312a37b0..effc77f8c4 100755 --- a/deployability/deps/remote_requirements.txt +++ b/deployability/deps/remote_requirements.txt @@ -1,4 +1,7 @@ +# python3-pip pytest>=7.4.2,<8.0.0 chardet==5.2.0 -chardet==5.2.0 pytest-tinybird==0.2.0 +requests>=2.31.0 +psutil>=5.8.0 +colored>=1.4.0,<2.0.0 \ No newline at end of file diff --git a/deployability/deps/requirements.txt b/deployability/deps/requirements.txt index a93a63576e..1e4478b9ec 100755 --- a/deployability/deps/requirements.txt +++ b/deployability/deps/requirements.txt @@ -8,3 +8,7 @@ graphlib==0.9.5 jsonschema==3.2.0 PyYAML==6.0.1 colorlog==6.8.0 +pytest==7.4.4 +paramiko==3.4.0 +requests==2.31.0 +chardet==5.2.0 diff --git a/deployability/modules/__init__.py b/deployability/modules/__init__.py new file mode 100755 index 0000000000..a9b660ce80 --- /dev/null +++ b/deployability/modules/__init__.py @@ -0,0 +1,6 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from .provision import Provision +from .generic import Ansible +from .allocation import Allocator diff --git a/deployability/modules/allocation/__init__.py b/deployability/modules/allocation/__init__.py new file mode 100755 index 0000000000..08249190d1 --- /dev/null +++ b/deployability/modules/allocation/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from .allocation import Allocator diff --git a/deployability/modules/allocation/allocation.py b/deployability/modules/allocation/allocation.py new file mode 100755 index 0000000000..46158d6896 --- /dev/null +++ b/deployability/modules/allocation/allocation.py @@ -0,0 +1,174 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import yaml + +from pathlib import Path + +from .aws.provider import AWSProvider, AWSConfig +from .generic import Instance, Provider, models +from .generic.utils import logger +from .vagrant.provider import VagrantProvider, VagrantConfig + + +PROVIDERS = {'vagrant': VagrantProvider, 'aws': AWSProvider} +CONFIGS = {'vagrant': VagrantConfig, 'aws': AWSConfig} + + +class Allocator: + """ + Allocator class to manage instances based on the payload action. + """ + @classmethod + def run(cls, payload: models.InputPayload) -> None: + """ + Executes the appropriate method based on the payload action. + + Args: + payload (InputPayload): The payload containing the action parameters. + """ + payload = models.InputPayload(**dict(payload)) + # Detect the action and call the appropriate method. + if payload.action == 'create': + logger.info(f"Creating instance at {payload.working_dir}") + return cls.__create(payload) + elif payload.action == 'delete': + logger.info(f"Deleting instance from trackfile {payload.track_output}") + return cls.__delete(payload) + + # Internal methods + + @classmethod + def __create(cls, payload: models.CreationPayload): + """ + Creates an instance and generates the inventory and track files. + + Args: + payload (CreationPayload): The payload containing the parameters + for instance creation. + """ + instance_params = models.CreationPayload(**dict(payload)) + provider: Provider = PROVIDERS[payload.provider]() + config = cls.___get_custom_config(payload) + instance = provider.create_instance( + payload.working_dir, instance_params, config, payload.ssh_key) + logger.info(f"Instance {instance.identifier} created.") + # Start the instance. + instance.start() + logger.info(f"Instance {instance.identifier} started.") + # Generate the inventory and track files. + cls.__generate_inventory(instance, payload.inventory_output) + cls.__generate_track_file(instance, payload.provider, payload.track_output) + + @classmethod + def __delete(cls, payload: models.InstancePayload) -> None: + """ + Deletes an instance based on the data from the track file. + + Args: + payload (InstancePayload): The payload containing the parameters + for instance deletion. + """ + payload = models.TrackPayload(**dict(payload)) + # Read the data from the track file. + with open(payload.track_output, 'r') as f: + track = models.TrackOutput(**yaml.safe_load(f)) + provider = PROVIDERS[track.provider]() + provider.destroy_instance(models.InstancePayload(**dict(track))) + logger.info(f"Instance {track.identifier} deleted.") + + @staticmethod + def ___get_custom_config(payload: models.CreationPayload) -> models.ProviderConfig | None: + """ + Gets the custom configuration from a file. + + Args: + payload (CreationPayload): The payload containing the parameters + for instance creation. + + Returns: + ProviderConfig: The configuration object. + """ + config = payload.custom_provider_config + if not config: + return None + # Read the custom config file and validate it. + config_model: models.ProviderConfig = CONFIGS[payload.provider] + with open(config, 'r') as f: + logger.info(f"Using custom provider config from {config}") + config = config_model(**yaml.safe_load(f)) + return config + + @staticmethod + def __generate_inventory(instance: Instance, inventory_path: Path) -> None: + """ + Generates an inventory file. + + Args: + instance (Instance): The instance for which the inventory file is generated. + inventory_path (Path): The path where the inventory file will be generated. + """ + if inventory_path is None: + inventory_path = Path(instance.path, 'inventory.yml') + if not str(inventory_path).endswith('.yml') and not str(inventory_path).endswith('.yaml'): + inventory_path = Path(inventory_path, 'inventory.yml') + if not inventory_path.parent.exists(): + inventory_path.parent.mkdir(parents=True, exist_ok=True) + ssh_config = instance.ssh_connection_info() + if instance.platform == 'windows': + inventory = models.InventoryOutput(ansible_host=ssh_config.hostname, + ansible_user=ssh_config.user, + ansible_port=ssh_config.port, + ansible_password=ssh_config.password, + ansible_connection='winrm', + ansible_winrm_server_cert_validation='ignore') + elif not ssh_config.private_key: + inventory = models.InventoryOutput(ansible_host=ssh_config.hostname, + ansible_user=ssh_config.user, + ansible_port=ssh_config.port, + ansible_connection='ssh', + ansible_password=ssh_config.password) + else: + inventory = models.InventoryOutput(ansible_host=ssh_config.hostname, + ansible_user=ssh_config.user, + ansible_port=ssh_config.port, + ansible_connection='ssh', + ansible_ssh_private_key_file=str(ssh_config.private_key)) + with open(inventory_path, 'w') as f: + yaml.dump(inventory.model_dump(exclude_none=True), f) + logger.info(f"Inventory file generated at {inventory_path}") + + @staticmethod + def __generate_track_file(instance: Instance, provider_name: str, track_path: Path) -> None: + """ + Generates a track file. + + Args: + instance (Instance): The instance for which the track file is to be generated. + provider_name (str): The name of the provider. + track_path (Path): The path where the track file will be generated. + """ + if track_path is None: + track_path = Path(instance.path, 'track.yml') + if not str(track_path).endswith('.yml') and not str(track_path).endswith('.yaml'): + track_path = Path(track_path, 'track.yml') + if not track_path.parent.exists(): + track_path.parent.mkdir(parents=True, exist_ok=True) + ssh_config = instance.ssh_connection_info() + track = models.TrackOutput(identifier=instance.identifier, + provider=provider_name, + instance_dir=str(instance.path), + key_path=str(instance.credentials.key_path), + host_identifier=str(instance.host_identifier), + host_instance_dir=str(instance.host_instance_dir), + ssh_port=ssh_config.port, + platform=instance.platform, + arch=instance.arch) + with open(track_path, 'w') as f: + yaml.dump(track.model_dump(), f) + if Path(str(instance.path) + "/port.txt").exists(): + Path(str(instance.path) + "/port.txt").unlink() + if Path(str(instance.path) + "/ppc-key").exists(): + Path(str(instance.path) + "/ppc-key").unlink() + logger.info(f"Track file generated at {track_path}") diff --git a/deployability/modules/allocation/aws/__init__.py b/deployability/modules/allocation/aws/__init__.py new file mode 100644 index 0000000000..e2d5bef173 --- /dev/null +++ b/deployability/modules/allocation/aws/__init__.py @@ -0,0 +1,7 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from .credentials import AWSCredentials +from .instance import AWSInstance +from .provider import AWSProvider diff --git a/deployability/modules/allocation/aws/credentials.py b/deployability/modules/allocation/aws/credentials.py new file mode 100644 index 0000000000..7ecc25c919 --- /dev/null +++ b/deployability/modules/allocation/aws/credentials.py @@ -0,0 +1,186 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import os +import boto3 +import random +import string + +from botocore.exceptions import ClientError +from pathlib import Path + +from modules.allocation.generic import Credentials +from modules.allocation.generic.utils import logger + + +class AWSCredentials(Credentials): + """ + A class for generating and deleting EC2 credentials. + + Attributes: + path (Path): The path to store the credentials. + name (str): The name of the credentials. + key_pair (CredentialsKeyPair): The key pair. + + Raises: + CredentialsError: An error occurred while creating the key. + """ + + def __init__(self) -> None: + """ + Initializes the AWSCredentials object. + """ + super().__init__() + self._resource = boto3.resource('ec2') + + def generate(self, base_dir: str | Path, name: str) -> Path: + """ + Generates a new key pair and returns it. + + Args: + base_dir (str | Path): The base directory to store the key pair. + name (str): The name of the key pair. + overwrite (bool): Whether to overwrite if the key pair already exists. + + Returns: + Path: The path of the private key. + + Raises: + CredentialsError: An error occurred during key pair creation. + """ + base_dir = Path(base_dir) + + # Validate base directory + if not base_dir.exists(): + logger.debug(f"Creating base directory: {base_dir}") + base_dir.mkdir(parents=True, exist_ok=True) + elif base_dir.is_file(): + raise self.CredentialsError(f"Invalid base directory: {base_dir}") + + try: + # Check if the key pair already exists + key_pair = self._resource.KeyPair(name) + if key_pair.key_pair_id: + raise self.CredentialsError(f"Key pair {name} already exists.") + except ClientError: + pass + + try: + private_key_path = base_dir / name + # Create the new key pair + key_pair = self._resource.create_key_pair(KeyName=name) + key_material = key_pair.key_material + + # Save the private key + with open(private_key_path, 'w') as key_file: + key_file.write(key_material) + os.chmod(private_key_path, 0o600) + + # Save instance attributes + self.name = name + self.key_path = private_key_path + self.key_id = key_pair.key_pair_id + + return self.key_path + except Exception as e: + raise self.CredentialsError(f"Failed to create key pair: {str(e)}") + + def load(self, name: str) -> str: + """ + Loads an existing key pair and returns its ID. + + Args: + name (str): The name of the key pair. + + Returns: + str: The ID of the key pair. + + Raises: + CredentialsError: An error occurred during key pair loading. + """ + try: + # Load the key pair from AWS + key_pair = self._resource.KeyPair(name) + + if not key_pair.key_pair_id: + raise self.CredentialsError(f"Invalid key name {name}") + + # Save instance attributes + self.name = name + self.key_id = key_pair.key_pair_id + + return self.key_id + except Exception as e: + raise self.CredentialsError(f"Failed to load key pair: {str(e)}") + + def delete(self) -> None: + """Deletes the key pair.""" + if not self.name: + logger.warning(f"Key pair doesn't exist. Skipping deletion.") + return + + try: + # Delete the key pair from AWS + self._resource.KeyPair(self.name).delete() + except Exception as e: + raise self.CredentialsError(f"Failed to delete key pair: {str(e)}") + + # Remove the local private key file + if self.key_path: + logger.debug(f"Deleting private key: {self.key_path}") + Path(self.key_path).unlink() + + # Clear instance attributes + self.name = None + self.key_id = None + self.key_path = None + + def ssh_key_interpreter(self, ssh_key_path: str | Path) -> str: + """ + Gets the id of the SSH Key stored in AWS from the provisioned public or private key + + Args: + public_key_path (str): The public or private key path or aws key id. + + Returns: + str: The ID of the key pair. + + Raises: + CredentialsError: An error occurred during key pair loading. + """ + ssh_key_name = os.path.basename(ssh_key_path) + if ssh_key_name.endswith('.pub'): + key_id = os.path.splitext(ssh_key_name)[0] + else: + key_id = ssh_key_name + return key_id + + def create_password(self) -> str: + """ + Creates a password for the instance. + + Returns: + str: The password for the instance. + """ + # Define character sets + uppercase_letters = string.ascii_uppercase + lowercase_letters = string.ascii_lowercase + numbers = string.digits + + # Combine all character sets + all_characters = uppercase_letters + lowercase_letters + numbers + + # Ensure each set contributes at least one character + password = [random.choice(uppercase_letters), + random.choice(lowercase_letters), + random.choice(numbers)] + + # Fill up the rest of the password length + password.extend(random.choice(all_characters) for _ in range(12 - 4)) + + # Shuffle the password to ensure randomness + random.shuffle(password) + + # Convert list to string + self.name = ''.join(password) diff --git a/deployability/modules/allocation/aws/helpers/userData.sh b/deployability/modules/allocation/aws/helpers/userData.sh new file mode 100644 index 0000000000..062e50b179 --- /dev/null +++ b/deployability/modules/allocation/aws/helpers/userData.sh @@ -0,0 +1,173 @@ +#!/bin/sh + +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +# Default values +DIST_NAME="Linux" +DIST_VER="0" +DIST_SUBVER="0" +SSH_PORT="2200" + + +if [ -r "/etc/os-release" ]; then + . /etc/os-release + DIST_NAME=$ID + DIST_VER=$(echo $VERSION_ID | sed -rn 's/[^0-9]*([0-9]+).*/\1/p') + if [ "X$DIST_VER" = "X" ]; then + DIST_VER="0" + fi + if [ "$DIST_NAME" = "amzn" ] && [ "$DIST_VER" == "2018" ]; then + DIST_VER="1" + fi + DIST_SUBVER=$(echo $VERSION_ID | sed -rn 's/[^0-9]*[0-9]+\.([0-9]+).*/\1/p') + if [ "X$DIST_SUBVER" = "X" ]; then + DIST_SUBVER="0" + fi +fi + +if [ ! -r "/etc/os-release" ] || [ "$DIST_NAME" = "centos" ]; then + # CentOS + if [ -r "/etc/centos-release" ]; then + DIST_NAME="centos" + DIST_VER=`sed -rn 's/.* ([0-9]{1,2})\.*[0-9]{0,2}.*/\1/p' /etc/centos-release` + DIST_SUBVER=`sed -rn 's/.* [0-9]{1,2}\.*([0-9]{0,2}).*/\1/p' /etc/centos-release` + + # Fedora + elif [ -r "/etc/fedora-release" ]; then + DIST_NAME="fedora" + DIST_VER=`sed -rn 's/.* ([0-9]{1,2}) .*/\1/p' /etc/fedora-release` + + # RedHat + elif [ -r "/etc/redhat-release" ]; then + if grep -q "CentOS" /etc/redhat-release; then + DIST_NAME="centos" + else + DIST_NAME="rhel" + fi + DIST_VER=`sed -rn 's/.* ([0-9]{1,2})\.*[0-9]{0,2}.*/\1/p' /etc/redhat-release` + DIST_SUBVER=`sed -rn 's/.* [0-9]{1,2}\.*([0-9]{0,2}).*/\1/p' /etc/redhat-release` + + # Ubuntu + elif [ -r "/etc/lsb-release" ]; then + . /etc/lsb-release + DIST_NAME="ubuntu" + DIST_VER=$(echo $DISTRIB_RELEASE | sed -rn 's/.*([0-9][0-9])\.[0-9][0-9].*/\1/p') + DIST_SUBVER=$(echo $DISTRIB_RELEASE | sed -rn 's/.*[0-9][0-9]\.([0-9][0-9]).*/\1/p') + + # Gentoo + elif [ -r "/etc/gentoo-release" ]; then + DIST_NAME="gentoo" + DIST_VER=`sed -rn 's/.* ([0-9]{1,2})\.[0-9]{1,2}.*/\1/p' /etc/gentoo-release` + DIST_SUBVER=`sed -rn 's/.* [0-9]{1,2}\.([0-9]{1,2}).*/\1/p' /etc/gentoo-release` + + # SuSE + elif [ -r "/etc/SuSE-release" ]; then + DIST_NAME="suse" + DIST_VER=`sed -rn 's/.*VERSION = ([0-9]{1,2}).*/\1/p' /etc/SuSE-release` + DIST_SUBVER=`sed -rn 's/.*PATCHLEVEL = ([0-9]{1,2}).*/\1/p' /etc/SuSE-release` + if [ "$DIST_SUBVER" = "" ]; then #openSuse + DIST_SUBVER=`sed -rn 's/.*VERSION = ([0-9]{1,2})\.([0-9]{1,2}).*/\1/p' /etc/SuSE-release` + fi + + # Arch + elif [ -r "/etc/arch-release" ]; then + DIST_NAME="arch" + DIST_VER=$(uname -r | sed -rn 's/[^0-9]*([0-9]+).*/\1/p') + DIST_SUBVER=$(uname -r | sed -rn 's/[^0-9]*[0-9]+\.([0-9]+).*/\1/p') + + # Debian + elif [ -r "/etc/debian_version" ]; then + DIST_NAME="debian" + DIST_VER=`sed -rn 's/[^0-9]*([0-9]+).*/\1/p' /etc/debian_version` + DIST_SUBVER=`sed -rn 's/[^0-9]*[0-9]+\.([0-9]+).*/\1/p' /etc/debian_version` + + # Slackware + elif [ -r "/etc/slackware-version" ]; then + DIST_NAME="slackware" + DIST_VER=`sed -rn 's/.* ([0-9]{1,2})\.[0-9].*/\1/p' /etc/slackware-version` + DIST_SUBVER=`sed -rn 's/.* [0-9]{1,2}\.([0-9]).*/\1/p' /etc/slackware-version` + + # Darwin + elif [ "$(uname)" = "Darwin" ]; then + DIST_NAME="darwin" + DIST_VER=$(uname -r | sed -En 's/[^0-9]*([0-9]+).*/\1/p') + DIST_SUBVER=$(uname -r | sed -En 's/[^0-9]*[0-9]+\.([0-9]+).*/\1/p') + + # Solaris / SunOS + elif [ "$(uname)" = "SunOS" ]; then + DIST_NAME="sunos" + DIST_VER=$(uname -r | cut -d\. -f1) + DIST_SUBVER=$(uname -r | cut -d\. -f2) + + # HP-UX + elif [ "$(uname)" = "HP-UX" ]; then + DIST_NAME="HP-UX" + DIST_VER=$(uname -r | cut -d\. -f2) + DIST_SUBVER=$(uname -r | cut -d\. -f3) + + # AIX + elif [ "$(uname)" = "AIX" ]; then + DIST_NAME="AIX" + DIST_VER=$(oslevel | cut -d\. -f1) + DIST_SUBVER=$(oslevel | cut -d\. -f2) + + # BSD + elif [ "X$(uname)" = "XOpenBSD" -o "X$(uname)" = "XNetBSD" -o "X$(uname)" = "XFreeBSD" -o "X$(uname)" = "XDragonFly" ]; then + DIST_NAME="bsd" + DIST_VER=$(uname -r | sed -rn 's/[^0-9]*([0-9]+).*/\1/p') + DIST_SUBVER=$(uname -r | sed -rn 's/[^0-9]*[0-9]+\.([0-9]+).*/\1/p') + + elif [ "X$(uname)" = "XLinux" ]; then + DIST_NAME="Linux" + + fi + if [ "X$DIST_SUBVER" = "X" ]; then + DIST_SUBVER="0" + fi +fi + +if [ "$DIST_NAME" = "amzn" ] || [ "$DIST_NAME" = "sles" ]; then + sudo sed -i "s/#Port\s22/Port ${SSH_PORT}/" /etc/ssh/sshd_config + sudo systemctl restart sshd.service +fi + +if [ "$DIST_NAME" = "rhel" ] || [ "$DIST_NAME" = "centos" ] || [ "$DIST_NAME" = "rocky" ] || [ "$DIST_NAME" = "fedora" ]; then + sudo sed -i "s/#Port\s22/Port ${SSH_PORT}/" /etc/ssh/sshd_config + if [ "$DIST_NAME" = "centos" ] && [ "$DIST_VER" != "7" ]; then + sudo yum -y install policycoreutils-python-utils + else + sudo yum -y install policycoreutils-python-utils + fi + sudo semanage port -a -t ssh_port_t -p tcp ${SSH_PORT} + sudo systemctl restart sshd.service + if sudo firewall-cmd --state 2>/dev/null | grep -q -w "running"; then + sudo firewall-cmd --permanent --zone=public --add-port=${SSH_PORT}/tcp + sudo firewall-cmd --reload + fi +fi + +if [ "$DIST_NAME" = "ol" ]; then + yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm + systemctl status amazon-ssm-agent + sed -i "s/#Port\s22/Port ${SSH_PORT}/" /etc/ssh/sshd_config + yum -y install policycoreutils-python-utils + semanage port -a -t ssh_port_t -p tcp ${SSH_PORT} + sleep 60 + systemctl restart sshd.service + firewall-offline-cmd --add-port=${SSH_PORT}/tcp + firewall-cmd --reload +fi + +if [ "$DIST_NAME" = "ubuntu" ] || [ "$DIST_NAME" = "debian" ]; then + perl -pi -e "s/^#?Port 22$/Port ${SSH_PORT}/" /etc/ssh/sshd_config + service sshd restart || service ssh restart +fi + +if [ "$DIST_NAME" = "darwin" ]; then + sed -i '' "s/ 22\/tcp/ ${SSH_PORT}\/tcp/" /etc/services + sed -i '' "s/ 22\/udp/ ${SSH_PORT}\/tcp/" /etc/services + launchctl unload /System/Library/LaunchDaemons/ssh.plist + launchctl load -w /System/Library/LaunchDaemons/ssh.plist +fi diff --git a/deployability/modules/allocation/aws/helpers/windowsUserData.ps1 b/deployability/modules/allocation/aws/helpers/windowsUserData.ps1 new file mode 100644 index 0000000000..18d18a20a0 --- /dev/null +++ b/deployability/modules/allocation/aws/helpers/windowsUserData.ps1 @@ -0,0 +1,23 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + + +try { + $url = "https://raw.githubusercontent.com/ansible/ansible/6e325d9e4dbdc020eb520a81148866d988a5dbc5/examples/scripts/ConfigureRemotingForAnsible.ps1" + $file = "$env:temp\ConfigureRemotingForAnsible.ps1" + (New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file) + powershell.exe -ExecutionPolicy ByPass -File $file +} catch { + $_.Exception.Message + "Error enabling WinRM on HTTPS." +} +New-LocalUser "Administrator" -Password ChangeMe -FullName "Administrator" -Description "Administrator user for remote desktop" +Add-LocalGroupMember -Group "Remote Desktop Users" -Member "Administrator" +# Set Administrator user to administrator group +Add-LocalGroupMember -Group "Administrators" -Member "Administrator" +# Set the password for the Administrator account +$admin = [ADSI]"WinNT://./Administrator, user" +$admin.SetPassword("ChangeMe") +$admin.SetInfo() + diff --git a/deployability/modules/allocation/aws/instance.py b/deployability/modules/allocation/aws/instance.py new file mode 100644 index 0000000000..e4978d1048 --- /dev/null +++ b/deployability/modules/allocation/aws/instance.py @@ -0,0 +1,98 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import boto3 + +from modules.allocation.generic import Instance +from modules.allocation.generic.models import ConnectionInfo, InstancePayload +from modules.allocation.generic.utils import logger +from .credentials import AWSCredentials + + +class AWSInstance(Instance): + """ + AWSInstance class for managing an individual AWS EC2 instance. + It inherits from the generic Instance class. + + Attributes: + path (str or Path): Directory where instance data is stored. + identifier (str): Identifier of the instance. + credentials (AWSCredentials): AWS credentials object. + user (str): User associated with the instance. + """ + + def __init__(self, instance_parameters: InstancePayload, credentials: AWSCredentials = None) -> None: + """ + Initialize an AWSInstance object. + + Args: + instance_parameters (InstancePayload): The parameters of the instance. + credentials (AWSCredentials): AWS credentials object. + """ + super().__init__(instance_parameters, credentials) + self._client = boto3.resource('ec2') + self._instance = self._client.Instance(instance_parameters.identifier) + self.platform = instance_parameters.platform + if not self.credentials: + logger.debug(f"No credentials found. Loading from instance directory.") + self.credentials = self.__get_credentials() + self.host_identifier = instance_parameters.host_identifier + self.host_instance_dir = instance_parameters.host_instance_dir + self.remote_host_parameters = instance_parameters.remote_host_parameters + self.arch = instance_parameters.arch + self.ssh_port = instance_parameters.ssh_port + self._user = instance_parameters.user + + def start(self) -> None: + """Start the AWS EC2 instance.""" + self._instance.start() + self._instance.wait_until_running() + + def reload(self) -> None: + """Reboot the AWS EC2 instance.""" + self._instance.reboot() + + def stop(self) -> None: + """Stop the AWS EC2 instance.""" + self._instance.stop() + self._instance.wait_until_stopped() + + def delete(self) -> None: + """Terminate and delete the AWS EC2 instance.""" + self._instance.terminate() + self._instance.wait_until_terminated() + + def status(self) -> str: + """Get the status of the AWS EC2 instance.""" + return self._instance.state + + def ssh_connection_info(self) -> ConnectionInfo: + """ + Get connection information for SSH. + + Returns: + ConnectionInfo: SSH connection information. + """ + if self.platform == 'windows': + return ConnectionInfo(hostname=self._instance.public_dns_name, + user=self._user, + port=3389, + password=str(self.credentials.name)) + else: + return ConnectionInfo(hostname=self._instance.public_dns_name, + user=self._user, + port=2200, + private_key=str(self.credentials.key_path)) + + def __get_credentials(self) -> AWSCredentials: + """ + Get AWS credentials associated with the instance. + + Returns: + AWSCredentials: Loaded AWS credentials. + """ + key_name = self._instance.key_name + credentials = AWSCredentials() + credentials.load(key_name) + return credentials diff --git a/deployability/modules/allocation/aws/models.py b/deployability/modules/allocation/aws/models.py new file mode 100644 index 0000000000..f862f0e1e7 --- /dev/null +++ b/deployability/modules/allocation/aws/models.py @@ -0,0 +1,20 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from modules.allocation.generic.models import ProviderConfig + + +class AWSConfig(ProviderConfig): + ami: str + zone: str + user: str + key_name: str + type: str + security_groups: list[str] + termination_date: str + issue: str | None = None + team: str + name: str + host_identifier: str | None = None + platform: str diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py new file mode 100644 index 0000000000..287a4c17fd --- /dev/null +++ b/deployability/modules/allocation/aws/provider.py @@ -0,0 +1,354 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import boto3 +import fnmatch +import os +import re +import random +from pathlib import Path +from datetime import datetime, timedelta + +from modules.allocation.generic import Provider +from modules.allocation.generic.models import CreationPayload, InstancePayload, InstancePayload +from modules.allocation.generic.utils import logger +from .credentials import AWSCredentials +from .instance import AWSInstance +from .models import AWSConfig + + +class AWSProvider(Provider): + """ + AWSProvider class for managing AWS EC2 instances. + It inherits from the generic Provider class. + + Attributes: + provider_name (str): Name of the provider ('aws'). + """ + + provider_name = 'aws' + + @classmethod + def _create_instance(cls, base_dir: Path, params: CreationPayload, config: AWSConfig = None, ssh_key: str = None) -> AWSInstance: + """ + Create an AWS EC2 instance. + + Args: + base_dir (Path): Base directory for storing instance data. + params (CreationPayload): Payload containing creation parameters. + config (AWSConfig, optional): Configuration for the instance. Defaults to None. + ssh_key (str, optional): Public or private key for the instance. For example, we assume that if the public key is provided, the private key is located in the same directory and has the same name as the public key. Defaults to None. + + Returns: + AWSInstance: Created AWSInstance object. + """ + temp_id = cls._generate_instance_id(cls.provider_name) + temp_dir = base_dir / temp_id + credentials = AWSCredentials() + teams = ['qa', 'core', 'framework', 'devops', 'frontend', 'operations', 'cloud', 'threat-intel', 'marketing', 'documentation'] + platform = str(params.composite_name.split("-")[0]) + arch = str(params.composite_name.split("-")[3]) + if not config: + logger.debug(f"No config provided. Generating from payload") + # Labels + issue = params.label_issue + label_team = params.label_team + termination_date = params.label_termination_date + host_identifier = None + date_regex = r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}" + url_regex = "(https:\/\/|http:\/\/)?[github]{2,}(\.[com]{2,})?\/wazuh\/[a-zA-Z0-9_-]+(?:-[a-zA-Z0-9_-]+)?\/issues\/[0-9]{2,}" + if not termination_date: + raise ValueError(f"The termination_date label was not provided.") + elif re.match(r'^\d+d$', termination_date): + new_date = datetime.now() + timedelta(days=int(termination_date.split("d")[0])) + termination_date = new_date.strftime("%Y-%m-%d %H:%M:%S") + elif not re.match(date_regex, termination_date): + raise ValueError(f"The termination_date label was not provided or is of incorrect format, example: 2021-12-31 23:59:59 or 2d") + if label_team: + not_match = 0 + for team in teams: + if label_team == team: + label_team = team + break + else: + not_match += 1 + if not_match == len(teams): + raise ValueError(f"The team label provided does not match any of the available teams. Available teams: {teams}") + else: + raise ValueError(f"The team label was not provided. Availables teams: {teams}.") + if params.instance_name: + name = params.instance_name + elif issue: + if not re.match(url_regex, issue): + raise ValueError(f"The issue label was not provided or is of incorrect format, example: https://github.com/wazuh//issues/") + issue_name= re.search(r'github\.com\/wazuh\/([^\/]+)\/issues', issue) + repository = cls.generate_repository_name(str(issue_name.group(1))) + name = repository + "-" + str(re.search(r'(\d+)$', issue).group(1)) + "-" + str(params.composite_name.split("-")[1]) + "-" + str(params.composite_name.split("-")[2]) + else: + name = str(params.composite_name.split("-")[1]) + "-" + str(params.composite_name.split("-")[2]) + "-" + str(params.composite_name.split("-")[3]) + + # Keys. + if platform == "windows": + credentials.create_password() + elif not ssh_key: + logger.debug(f"Generating new key pair") + credentials.generate(temp_dir, name + "-key-" + str(random.randint(1000, 9999))) + else: + logger.debug(f"Using provided key pair") + key_id = credentials.ssh_key_interpreter(ssh_key) + credentials.load(key_id) + # Parse the config if it is not provided. + config = cls.__parse_config(params, credentials, issue, label_team, termination_date, name) + #Generate dedicated host for macOS instances + if platform == 'macos': + host_identifier = cls._generate_dedicated_host(config, str(params.composite_name.split("-")[3])) + config = cls.__parse_config(params, credentials, issue, label_team, termination_date, name, host_identifier) + else: + logger.debug(f"Using provided config") + # Load the existing credentials. + credentials.load(config.key_name) + # Create the temp directory. + # TODO: Review this on the credentials refactor. + if not temp_dir.exists(): + logger.debug(f"Creating temp directory: {temp_dir}") + temp_dir.mkdir(parents=True, exist_ok=True) + # Generate the instance. + instance_id = cls.__create_ec2_instance(config) + # Rename the temp directory to its real name. + instance_dir = Path(base_dir, instance_id) + logger.debug(f"Renaming temp {temp_dir} directory to {instance_dir}") + os.rename(temp_dir, instance_dir) + if not ssh_key: + credentials.key_path = (instance_dir / credentials.name) + else: + credentials.key_path = (os.path.splitext(ssh_key)[0]) + + instance_params = {} + instance_params['instance_dir'] = instance_dir + instance_params['identifier'] = instance_id + instance_params['platform'] = platform + instance_params['host_identifier'] = host_identifier + instance_params['arch'] = arch + instance_params['user'] = config.user + return AWSInstance(InstancePayload(**instance_params), credentials) + + @staticmethod + def _load_instance(instance_dir: Path, instance_id: str) -> AWSInstance: + """ + Load an existing AWS EC2 instance. + + Args: + instance_dir (Path): Directory where instance data is stored. + instance_id (str): Identifier of the instance. + + Returns: + AWSInstance: Loaded AWSInstance object. + """ + instance_params = {} + instance_params['instance_dir'] = instance_dir + instance_params['identifier'] = instance_id + return AWSInstance(InstancePayload(**instance_params)) + + @classmethod + def _destroy_instance(cls, destroy_parameters: InstancePayload) -> None: + """ + Destroy an AWS EC2 instance. + + Args: + destroy_parameters (InstancePayload): The parameters for destroying the instance. + """ + credentials = AWSCredentials() + key_id = os.path.basename(destroy_parameters.key_path) + credentials.load(key_id) + instance_params = {} + instance_params['instance_dir'] = destroy_parameters.instance_dir + instance_params['identifier'] = destroy_parameters.identifier + instance_params['platform'] = destroy_parameters.platform + instance_params['host_identifier'] = destroy_parameters.host_identifier + + instance = AWSInstance(InstancePayload(**instance_params), credentials) + if os.path.dirname(destroy_parameters.key_path) == str(destroy_parameters.instance_dir): + logger.debug(f"Deleting credentials: {instance.credentials.name}") + instance.credentials.delete() + instance.delete() + if destroy_parameters.host_identifier != "None" and destroy_parameters.host_identifier is not None: + cls._release_dedicated_host(destroy_parameters.host_identifier) + + @staticmethod + def __create_ec2_instance(config: AWSConfig) -> str: + """ + Create an AWS EC2 instance. + + Args: + config (AWSConfig): Configuration for the instance. + + Returns: + str: Identifier of the created instance. + """ + client = boto3.resource('ec2') + + userData_file = Path(__file__).parent.parent / 'aws' / 'helpers' / 'userData.sh' + windosUserData_file = Path(__file__).parent.parent / 'aws' / 'helpers' / 'windowsUserData.ps1' + + if config.platform == 'windows': + with open(windosUserData_file, 'r') as file: + userData = file.read() + userData = userData.replace('ChangeMe', config.key_name) + else: + with open(userData_file, 'r') as file: + userData = file.read() + params = { + 'ImageId': config.ami, + 'InstanceType': config.type, + 'SecurityGroupIds': config.security_groups, + 'MinCount': 1, + 'MaxCount': 1, + 'UserData': userData, + 'TagSpecifications': [{ + 'ResourceType': 'instance', + 'Tags': [ + {'Key': 'Name', 'Value': config.name}, + {'Key': 'termination_date', 'Value': config.termination_date}, + {'Key': 'team', 'Value': config.team} + ] + }] + } + if config.platform != 'windows': + params['KeyName'] = config.key_name + + + if config.host_identifier: + params['Placement'] = {'AvailabilityZone': config.zone, 'HostId': config.host_identifier} + + if config.issue: + params['TagSpecifications'][0]['Tags'].append({'Key': 'issue', 'Value': config.issue}) + + instance = client.create_instances(**params)[0] + # Wait until the instance is running. + instance.wait_until_running() + return instance.instance_id + + @classmethod + def __parse_config(cls, params: CreationPayload, credentials: AWSCredentials, issue: str, team: str, termination_date: str, name: str, host_identifier: str = None) -> AWSConfig: + """ + Parse configuration parameters for creating an AWS EC2 instance. + + Args: + params (CreationPayload): Payload containing creation parameters. + credentials (AWSCredentials): AWS credentials object. + issue (str): Issue URL. + team (str): Team label. + termination_date (str): Termination date label. + name (str): Name of the instance. + host_identifier (str): Identifier of the dedicated host. + + Returns: + AWSConfig: Parsed AWSConfig object. + """ + config = {} + + # Get the specs from the yamls. + size_specs = cls._get_size_specs(params.size) + os_specs = cls._get_os_specs(params.composite_name) + mics_specs = cls._get_misc_specs() + arch = params.composite_name.split('-')[-1] + platform = str(params.composite_name.split("-")[0]) + + # Parse the configuration. + if platform == 'macos': + os_specs['zone'] = os_specs['zone'] + 'c' + if arch == 'arm64': + config['type'] = 'mac2.metal' + if arch == 'amd64': + config['type'] = 'mac1.metal' + else: + for spec in size_specs: + if fnmatch.fnmatch(arch, spec): + config['type'] = size_specs[spec]['type'] + break + + config['ami'] = os_specs['ami'] + config['zone'] = os_specs['zone'] + config['user'] = os_specs['user'] + config['key_name'] = credentials.name + config['security_groups'] = mics_specs['security-group'] + config['termination_date'] = termination_date + config['issue'] = issue + config['team'] = team + config['name'] = name + if host_identifier: + config['host_identifier'] = host_identifier + config['platform'] = platform + + return AWSConfig(**config) + + @staticmethod + def _generate_dedicated_host(config: AWSConfig, arch: str) -> str: + """ + Generate a dedicated host for macOS instances. + + Args: + config (AWSConfig): Configuration for the instance. + + Returns: + str: Identifier of the created dedicated host. + """ + client = boto3.client('ec2') + dedicated_host_name = str(config.name) + '-Host-' + arch + logger.info(f"Creating dedicated host: {dedicated_host_name}") + host = client.allocate_hosts(InstanceType=config.type, + AutoPlacement='on', + AvailabilityZone=config.zone, + Quantity=1, + TagSpecifications=[{ + 'ResourceType': 'dedicated-host', + 'Tags': [ + {'Key': 'Name', 'Value': dedicated_host_name}, + {'Key': 'termination_date', 'Value': config.termination_date}, + {'Key': 'issue', 'Value': config.issue}, + {'Key': 'team', 'Value': config.team} + ] + }]) + logger.info(f"Dedicated host created: {host['HostIds'][0]}") + return host['HostIds'][0] + + @staticmethod + def _release_dedicated_host(host_identifier: str) -> str: + """ + Release a dedicated host. + + Args: + host_identifier (str): Identifier of the dedicated host. + + Returns: + str: Identifier of the released dedicated host. + """ + client = boto3.client('ec2') + logger.info(f"Releasing dedicated host: {host_identifier}") + host = client.release_hosts(HostIds=[host_identifier]) + if host['Unsuccessful']: + unsuccessful_messages = [item['Error']['Message'] for item in host['Unsuccessful']] + for message in unsuccessful_messages: + logger.info(f"{message}") + else: + logger.info(f"Dedicated host released: {host_identifier}") + + @staticmethod + def generate_repository_name(repository: str) -> str: + """ + Generate a repository name for the instance. + + Args: + repository (str): Repository name. + + Returns: + str: Repository name for the instance. + """ + matches = re.findall(r'(\w+)', repository) + if len(matches) == 3: + return ''.join([c[0] for c in matches]) + elif len(matches) == 2: + return matches[1] + else: + return repository diff --git a/deployability/modules/allocation/generic/__init__.py b/deployability/modules/allocation/generic/__init__.py new file mode 100644 index 0000000000..c4235dc043 --- /dev/null +++ b/deployability/modules/allocation/generic/__init__.py @@ -0,0 +1,7 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from .credentials import Credentials +from .instance import Instance +from .provider import Provider diff --git a/deployability/modules/allocation/generic/credentials.py b/deployability/modules/allocation/generic/credentials.py new file mode 100644 index 0000000000..44c5bef8ac --- /dev/null +++ b/deployability/modules/allocation/generic/credentials.py @@ -0,0 +1,60 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from abc import ABC, abstractmethod +from pathlib import Path + + +class Credentials(ABC): + """ + An abstract base class for credentials. + + This class provides an interface for generating, loading, and deleting credentials key pairs. + + Attributes: + name (str): The name of the credentials. + key_path (Path): The path of the key. + key_id (str): The id of the key. + """ + + class CredentialsError(Exception): + """ + Exception raised for errors in the key creation process. + """ + pass + + def __init__(self) -> None: + """ + Initializes a Credentials object. + """ + self.name: str = None + self.key_path: Path = None + self.key_id: str = None + + @abstractmethod + def generate(self, **kwargs) -> Path: + """ + Abstract method that generates a credentials key pair. + + Returns: + Path: The path of the generated key pair. + """ + pass + + @abstractmethod + def load(self, **kwargs) -> Path: + """ + Abstract method that loads a credentials key pair. + + Returns: + Path: The path of the loaded key pair. + """ + pass + + @abstractmethod + def delete(self, **kwargs) -> None: + """ + Abstract method that deletes a credentials key pair. + """ + pass diff --git a/deployability/modules/allocation/generic/instance.py b/deployability/modules/allocation/generic/instance.py new file mode 100644 index 0000000000..72a6713e7a --- /dev/null +++ b/deployability/modules/allocation/generic/instance.py @@ -0,0 +1,101 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from abc import ABC, abstractmethod +from pathlib import Path +from .utils import logger + +from .credentials import Credentials +from .models import ConnectionInfo, InstancePayload + + +class Instance(ABC): + """ + An abstract base class for instances. + + This class provides an interface for starting, reloading, stopping, deleting, and getting the status of instances. + It also provides a method to get SSH connection information for the instance. + + Attributes: + path (Path): The path of the instance. + identifier (str): The identifier of the instance. + credentials (Credentials): The credentials of the instance. + """ + + def __init__(self, instance_parameters: InstancePayload, credentials: Credentials = None) -> None: + """ + Initializes an Instance object. + + Args: + instance_parameters (InstancePayload): The parameters of the instance. + credentials (Credentials, optional): The credentials of the instance. Defaults to None. + + Raises: + ValueError: If the path does not exist or is not a directory. + ValueError: If the credentials are not a subclass of Credentials. + """ + path = Path(instance_parameters.instance_dir) + if not path.exists() or not path.is_dir(): + raise ValueError(f"Invalid instance path: {path}") + if credentials and not issubclass(type(credentials), Credentials): + raise ValueError(f"Invalid credentials: {credentials}") + + self.path: Path = path + self.identifier: str = str(instance_parameters.identifier) + self.credentials: Credentials = credentials + self.host_identifier: str = instance_parameters.host_identifier + self.host_instance_dir: Path = instance_parameters.host_instance_dir + self.ssh_port: str = instance_parameters.ssh_port + self.remote_host_parameters: dict = instance_parameters.remote_host_parameters + self.platform: str = instance_parameters.platform + self.arch: str = instance_parameters.arch + self.user: str = instance_parameters.user + + @abstractmethod + def start(self) -> None: + """ + Abstract method that starts the instance. + """ + pass + + @abstractmethod + def reload(self) -> None: + """ + Abstract method that reloads the instance. + """ + pass + + @abstractmethod + def stop(self) -> None: + """ + Abstract method that stops the instance. + """ + pass + + @abstractmethod + def delete(self) -> None: + """ + Abstract method that deletes the instance. + """ + pass + + @abstractmethod + def status(self) -> str: + """ + Abstract method that returns the status of the instance. + + Returns: + str: The status of the instance. + """ + pass + + @abstractmethod + def ssh_connection_info(self) -> ConnectionInfo: + """ + Abstract method that returns the SSH connection information for the instance. + + Returns: + ConnectionInfo: The SSH connection information for the instance. + """ + pass diff --git a/deployability/modules/allocation/generic/models.py b/deployability/modules/allocation/generic/models.py new file mode 100644 index 0000000000..807ef6e576 --- /dev/null +++ b/deployability/modules/allocation/generic/models.py @@ -0,0 +1,132 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from pathlib import Path +from pydantic import BaseModel, IPvAnyAddress, field_validator, model_validator +from typing_extensions import Literal + +class ConnectionInfo(BaseModel): + hostname: str + user: str + port: int + private_key: str | None = None + password: str | None = None + + @field_validator('port', mode='before') + @classmethod + def sanitize_port(cls, v: str | int) -> int: + return int(v) + + +class ProviderConfig(BaseModel): + pass + + +class InventoryOutput(BaseModel): + ansible_host: str | IPvAnyAddress + ansible_user: str + ansible_port: int + ansible_ssh_private_key_file: str | None = None + ansible_password: str | None = None + ansible_connection: Literal['ssh', 'winrm'] | None = None + ansible_winrm_server_cert_validation: Literal['ignore'] | None = None + + +class TrackOutput(BaseModel): + identifier: str + provider: str + instance_dir: str + key_path: str + host_identifier: str = None + host_instance_dir: str | Path = None + ssh_port: int | None = None + platform: str + arch: str + + +class InputPayload(BaseModel): + action: Literal['create', 'delete', 'status'] = 'create' + provider: str | None = None + size: Literal['micro', 'small', 'medium', 'large', None] = None + composite_name: str | None = None + working_dir: Path | None = Path('/tmp/wazuh-qa') + track_output: Path | None = working_dir / 'track.yml' + inventory_output: Path | None = working_dir / 'inventory.yml' + ssh_key: str | None = None + custom_provider_config: Path | None = None + label_issue: str | None = None + label_team: str | None = None + label_termination_date: str | None = None + instance_name: str | None = None + +class CreationPayload(InputPayload): + provider: str + size: Literal['micro', 'small', 'medium', 'large'] | None = None + composite_name: str | None = None + track_output: Path | None = None + inventory_output: Path | None = None + working_dir: Path + ssh_key: str | None = None + custom_provider_config: Path | None = None + label_issue: str | None = None + label_team: str | None = None + label_termination_date: str | None = None + + @model_validator(mode='before') + def validate_dependency(cls, values) -> dict: + """Validate required fields.""" + required_if_not_config = ['composite_name', 'size'] + if values.get('custom_provider_config'): + return values + for attr in required_if_not_config: + if not values.get(attr): + raise ValueError(f"{attr} is required if custom_provider_config is not provided.") + return values + + @field_validator('custom_provider_config') + @classmethod + def check_config(cls, v: Path | None) -> Path | None: + if not v: + return None + if not v.exists(): + raise ValueError(f"Custom provider config file does not exist: {v}") + elif not v.is_file(): + raise ValueError(f"Custom provider config file is not a file: {v}") + elif not v.suffix in ['.yml', '.yaml']: + raise ValueError(f"Custom provider config file must be yaml: {v}") + return v + + @field_validator('working_dir', mode='before') + @classmethod + def check_working_dir(cls, v: str | Path) -> Path: + path = Path(v) + if not path.exists(): + path.mkdir(parents=True, exist_ok=True) + elif not path.is_dir(): + raise ValueError(f"Invalid working directory: {path}") + return path + + +class TrackPayload(BaseModel): + track_output: Path + +class InstancePayload(BaseModel): + identifier: str + instance_dir: str | Path + key_path: Path | None = None + host_identifier: str | None = None + host_instance_dir: str | Path | None = None + remote_host_parameters: dict | None = None + ssh_port: str | None = None + platform: str + arch: str | None = None + user: str | None = None + docker_image: str | None = None + + @field_validator('ssh_port', mode='before') + def validate_port(cls, value) -> str | None: + if not value: + return + + return str(value) diff --git a/deployability/modules/allocation/generic/provider.py b/deployability/modules/allocation/generic/provider.py new file mode 100644 index 0000000000..651e61494a --- /dev/null +++ b/deployability/modules/allocation/generic/provider.py @@ -0,0 +1,204 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import shutil +import uuid +import yaml + +from abc import ABC, abstractmethod +from pathlib import Path + +from .instance import Instance +from .models import CreationPayload, ProviderConfig, InstancePayload +from .utils import logger + + +class Provider(ABC): + """ + An abstract base class for providers. + + This class provides an interface for creating, loading, and destroying instances. + It also provides methods to get OS, size, and miscellaneous specifications for the provider. + + Attributes: + provider_name (str): The name of the provider. + """ + + # Paths to the templates and specs directories. + ROOT_DIR = Path(__file__).parent.parent / 'static' + TEMPLATES_DIR = ROOT_DIR / 'templates' + SPECS_DIR = ROOT_DIR / 'specs' + OS_PATH = SPECS_DIR / 'os.yml' + SIZE_PATH = SPECS_DIR / 'size.yml' + MISC_PATH = SPECS_DIR / 'misc.yml' + + + class ProvisioningError(Exception): + """ + Exception raised for errors in the provisioning process. + """ + pass + + @property + @abstractmethod + def provider_name(self) -> str: + """ + Abstract property that should return the name of the provider. + + Returns: + str: The name of the provider. + """ + pass + + @classmethod + def create_instance(cls, base_dir: str | Path, params: CreationPayload, config: ProviderConfig = None, ssh_key: str = None) -> Instance: + """ + Creates a new instance. + + Args: + base_dir (str | Path): The base directory for the instance. + params (CreationPayload): The parameters for creating the instance. + config (ProviderConfig, optional): The configuration for the instance. Defaults to None. + ssh_key (str, optional): Public or private key for the instance. For example, we assume that if the public key is provided, the private key is located in the same directory and has the same name as the public key. Defaults to None. + + Returns: + Instance: The created instance. + """ + params = CreationPayload(**dict(params)) + base_dir = Path(base_dir) + return cls._create_instance(base_dir, params, config, ssh_key) + + @classmethod + def load_instance(cls, instance_dir: str | Path, instance_id: str) -> Instance: + """ + Loads an existing instance. + + Args: + instance_dir (str | Path): The directory of the instance. + instance_id (str): The id of the instance. + + Returns: + Instance: The loaded instance. + + Raises: + ValueError: If the instance directory does not exist. + """ + instance_dir = Path(instance_dir) + if not instance_dir.exists(): + raise ValueError(f"Instance path {instance_dir} does not exist") + return cls._load_instance(instance_dir, instance_id) + + @classmethod + def destroy_instance(cls, destroy_parameters: InstancePayload) -> None: + """ + Destroys an existing instance and removes its directory. + + Args: + destroy_parameters (InstancePayload): The parameters for destroying the instance. + """ + destroy_parameters.instance_dir = Path(destroy_parameters.instance_dir) + cls._destroy_instance(destroy_parameters) + shutil.rmtree(destroy_parameters.instance_dir, ignore_errors=True) + + @classmethod + @abstractmethod + def _create_instance(cls, base_dir: Path, params: CreationPayload, config: ProviderConfig = None, ssh_key: str = None) -> Instance: + """ + Abstract method that creates a new instance. + + Args: + base_dir (Path): The base directory for the instance. + params (CreationPayload): The parameters for creating the instance. + config (ProviderConfig, optional): The configuration for the instance. Defaults to None. + ssh_key (str, optional): Public or private key for the instance. Defaults to None. + + Returns: + Instance: The created instance. + """ + pass + + @classmethod + @abstractmethod + def _load_instance(cls, instance_dir: Path, identifier: str) -> Instance: + """ + Abstract method that loads an existing instance. + + Args: + instance_dir (Path): The directory of the instance. + identifier (str): The identifier of the instance. + + Returns: + Instance: The loaded instance. + """ + pass + + @classmethod + @abstractmethod + def _destroy_instance(cls, destroy_parameters: InstancePayload) -> None: + """ + Abstract method that destroys an existing instance. + + Args: + destroy_parameters (InstancePayload): The parameters for destroying the instance. + """ + pass + + @staticmethod + def _generate_instance_id(prefix: str) -> str: + """ + Generates a random instance id with the given prefix. + + Args: + prefix (str): The prefix for the instance id. + + Returns: + str: The instance id. + """ + return f"{prefix}-{uuid.uuid4()}".upper() + + @classmethod + def _get_os_specs(cls, composite_name: str) -> dict: + """ + Gets the OS specifications for the provider. + composite_name (str): The name of the composite OS. + + Returns: + dict: A dictionary containing the OS specifications for the provider. + """ + with open(cls.OS_PATH, "r") as f: + version_available = [] + os_list = yaml.safe_load(f).get(cls.provider_name) + for os in os_list: + if os.split("-")[1] == composite_name.split("-")[1]: + version_available.append(os) + if str(os) == composite_name: + return os_list[os] + + raise ValueError(f"OS {composite_name} not available for provider {cls.provider_name}. Available versions are {version_available}") + + @classmethod + def _get_size_specs(cls, size: str) -> dict: + """ + Gets the size specifications for the provider. + size (str): The name of the size. + + Returns: + dict: A dictionary containing the size specifications for the provider. + """ + with open(cls.SIZE_PATH, "r") as f: + size_list = yaml.safe_load(f).get(cls.provider_name) + for s in size_list: + if size == s: + return size_list[s] + + @classmethod + def _get_misc_specs(cls) -> dict: + """ + Gets the miscellaneous specifications for the provider. + + Returns: + dict: A dictionary containing the miscellaneous specifications for the provider. + """ + with open(cls.MISC_PATH, "r") as f: + return yaml.safe_load(f).get(cls.provider_name) diff --git a/deployability/modules/allocation/generic/utils.py b/deployability/modules/allocation/generic/utils.py new file mode 100644 index 0000000000..846adf4292 --- /dev/null +++ b/deployability/modules/allocation/generic/utils.py @@ -0,0 +1,7 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from modules.generic.logger import Logger + +logger = Logger("allocator").get_logger() diff --git a/deployability/modules/allocation/main.py b/deployability/modules/allocation/main.py new file mode 100755 index 0000000000..ddb4dfb8e8 --- /dev/null +++ b/deployability/modules/allocation/main.py @@ -0,0 +1,38 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import argparse +import os +import sys + +project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')) +sys.path.append(project_root) + +from modules.allocation import Allocator +from modules.allocation.generic.models import InputPayload + +def parse_arguments(): + parser = argparse.ArgumentParser(description="Infrastructure providing tool") + parser.add_argument("--provider", choices=['vagrant', 'aws', None], required=False, default=None) + parser.add_argument("--size", choices=['micro', 'small', 'medium', 'large', None], required=False, default=None) + parser.add_argument("--composite-name", required=False, default=None) + parser.add_argument("--action", choices=['create', 'delete'], required=False, default='create') + parser.add_argument("--ssh-key", required=False, default=None) + parser.add_argument("--custom-provider-config", required=False, default=None) + parser.add_argument("--track-output", required=False, default=None) + parser.add_argument("--inventory-output", required=False, default=None) + parser.add_argument("--working-dir", required=False, default='/tmp/wazuh-qa') + parser.add_argument("--label-issue", required=False, default=None) + parser.add_argument("--label-team", required=False, default=None) + parser.add_argument("--label-termination-date", required=False, default=None) + parser.add_argument("--instance-name", required=False, default=None) + return parser.parse_args() + + +def main(): + Allocator.run(InputPayload(**vars(parse_arguments()))) + + +if __name__ == "__main__": + main() diff --git a/deployability/modules/allocation/static/specs/misc.yml b/deployability/modules/allocation/static/specs/misc.yml new file mode 100755 index 0000000000..e112becfb4 --- /dev/null +++ b/deployability/modules/allocation/static/specs/misc.yml @@ -0,0 +1,17 @@ +vagrant: + key: + type: dynamic + name: ${uuid} + instance-name: + format: ${uuid} + tags: + type: qa + +aws: + security-group: [sg-023fd37c4e5b679de] + key: + type: dynamic + name: ${uuid} + tags: + type: qa + name: ddt1-{name} diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml new file mode 100644 index 0000000000..c493f3beb2 --- /dev/null +++ b/deployability/modules/allocation/static/specs/os.yml @@ -0,0 +1,390 @@ +vagrant: + # Ubuntu + linux-ubuntu-16.04-amd64: + box: generic/ubuntu1604 + box_version: 4.3.8 + linux-ubuntu-18.04-amd64: + box: ubuntu/bionic64 + box_version: 20230607.0.0 + linux-ubuntu-20.04-amd64: + box: ubuntu/focal64 + box_version: 20240130.0.0 + linux-ubuntu-22.04-amd64: + box: ubuntu/jammy64 + box_version: 20240131.0.0 + # Debian + linux-debian-9-amd64: + box: generic/debian9 + box_version: 4.3.8 + linux-debian-9-ppc64: + box: testing-debian-image + box_version: latest + linux-debian-10-amd64: + box: generic/debian10 + box_version: 4.3.8 + linux-debian-11-amd64: + box: debian/bullseye64 + box_version: 11.20231211.1 + linux-debian-12-amd64: + box: debian/bookworm64 + box_version: 12.20231211.1 + # Oracle Linux + linux-oracle-7-amd64: + box: generic/oracle7 + box_version: 4.3.12 + linux-oracle-8-amd64: + box: generic/oracle8 + box_version: 4.3.12 + linux-oracle-9-amd64: + box: generic/oracle9 + box_version: 4.3.12 + # openSUSE Linux + linux-opensuse-15-amd64: + box: generic/opensuse15 + box_version: 4.3.8 + linux-opensuse-tumbleweed-amd64: + box: opensuse/Tumbleweed.x86_64 + box_version: 1.0.20240207 + # Centos + linux-centos-7-amd64: + box: centos/7 + box_version: 2004.01 + linux-centos-7-ppc64: + box: testing-centos-image + box_version: latest + linux-centos-8-amd64: + box: generic/centos8 + box_version: 4.3.8 + linux-centos-9-amd64: + box: generic/centos9s + box_version: 4.3.12 + # Redhat + linux-redhat-7-amd64: + box: generic/rhel7 + box_version: 3.6.8 + linux-redhat-8-amd64: + box: generic/rhel8 + box_version: 3.6.8 + linux-redhat-9-amd64: + box: generic/rhel9 + box_version: 4.3.12 + # Amazon + linux-amazon-2-amd64: + box: bento/amazonlinux-2 + box_version: 202305.26.0 + # Fedora + linux-fedora-28-amd64: + box: generic/fedora28 + box_version: 4.3.8 + linux-fedora-29-amd64: + box: generic/fedora29 + box_version: 4.3.8 + linux-fedora-31-amd64: + box: generic/fedora31 + box_version: 4.3.8 + linux-fedora-32-amd64: + box: generic/fedora32 + box_version: 4.3.8 + linux-fedora-37-amd64: + box: generic/fedora37 + box_version: 4.3.8 + linux-fedora-38-amd64: + box: alvistack/fedora-38 + box_version: 20240206.1.1 + # Rocky Linux + linux-rocky-8-amd64: + box: generic/rocky8 + box_version: 4.3.12 + linux-rocky-9-amd64: + box: generic/rocky9 + box_version: 4.3.12 + # Macos + macos-bigsur-11.0-amd64: + box: development/macos-big-sur + box_version: 0.0.0 + macos-catalina-10.15.1-amd64: + box: development/macos-catalina + box_version: 0.0.0 + macos-highsierra-10.13.6-amd64: + box: development/macos-high-sierra + box_version: 0.0.0 + macos-mojave-10.14.3-amd64: + box: development/macos-mojave + box_version: 0.0.0 + macos-sierra-10.12.6-amd64: + box: development/macos-sierra + box_version: 0.0.0 + macos-sierracmake-10.12.6-amd64: + box: development/macos-sierra_cmake + box_version: 0.0.0 + macos-sierragcc9-10.12.6-amd64: + box: development/macos-sierra_gcc9 + box_version: 0.0.0 + macos-monterey-12.6-arm64: + box: macos-12 + box_version: 0.0.0 + macos-monterey-12.0.1-amd64: + box: development/macos-monterey + box_version: 0.0.0 + macos-ventura-13.4.1-arm64: + box: macos-13 + box_version: 0.0.0 + macos-sonoma-14.0-arm64: + box: macos-14 + box_version: 0.0.0 + # Windows + windows-desktop-10-amd64: + box: gusztavvargadr/windows-10 + box_version: 2202.0.2312 + windows-desktop-11-amd64: + box: gusztavvargadr/windows-11 + box_version: 2302.0.2312 + windows-server-2012-amd64: + box: jborean93/WindowsServer2012 + box_version: 1.2.0 + windows-server-2012r2-amd64: + box: addle/windows-server-2012-r2 + box_version: 1.20170205.1 + windows-server-2016-amd64: + box: mwrock/Windows2016 + box_version: 0.3.0 + windows-server-2019-amd64: + box: gusztavvargadr/windows-server-2019-standard + box_version: 1809.0.2310 + windows-server-2022-amd64: + box: gusztavvargadr/windows-server-2022-standard + box_version: 2102.0.2312 + +aws: + # Ubuntu + linux-ubuntu-22.04-amd64: + ami: ami-053b0d53c279acc90 + zone: us-east-1 + user: ubuntu + linux-ubuntu-22.04-arm64: + ami: ami-016485166ec7fa705 + zone: us-east-1 + user: ubuntu + linux-ubuntu-18.04-amd64: + ami: ami-0d23ec1228995e83b + zone: us-east-1 + user: ubuntu + linux-ubuntu-18.04-arm64: + ami: ami-0c7625066eaff908f + zone: us-east-1 + user: ubuntu + linux-ubuntu-20.04-amd64: + ami: ami-0261755bbcb8c4a84 + zone: us-east-1 + user: ubuntu + linux-ubuntu-20.04-arm64: + ami: ami-05f45f2e962205865 + zone: us-east-1 + user: ubuntu + # Debian + linux-debian-10-amd64: + ami: ami-0aef3e5090bf17640 + zone: us-east-1 + user: admin + linux-debian-10-arm64: + ami: ami-0638210d31ea53567 + zone: us-east-1 + user: admin + linux-debian-11-amd64: + ami: ami-0f63aa666117f7cc1 + zone: us-east-1 + user: admin + linux-debian-11-arm64: + ami: ami-02a23764e4c26b541 + zone: us-east-1 + user: admin + linux-debian-12-amd64: + ami: ami-058bd2d568351da34 + zone: us-east-1 + user: admin + linux-debian-12-arm64: + ami: ami-0f58aa386a2280f35 + zone: us-east-1 + user: admin + # Oracle Linux + linux-oracle-7-amd64: + ami: ami-0fb08d5eb039a9ebd + zone: us-east-1 + user: ec2-user + linux-oracle-8-amd64: + ami: ami-04921b5223c6ab7f0 + zone: us-east-1 + user: ec2-user + linux-oracle-9-amd64: + ami: ami-01453ca80e53609e3 + zone: us-east-1 + user: ec2-user + # openSUSE Linux + linux-opensuse-15-amd64: + ami: ami-06b6eb8f8fb7f2916 + zone: us-east-1 + user: ec2-user + # SUSE Linux + linux-suse-15-amd64: + ami: ami-08f3662e2d5b3989a + zone: us-east-1 + user: ec2-user + linux-suse-15-arm64: + ami: ami-0b9eb3aa813f0e4d7 + zone: us-east-1 + user: ec2-user + # Centos + linux-centos-7-amd64: + ami: ami-0aedf6b1cb669b4c7 + zone: us-east-1 + user: centos + linux-centos-7-arm64: + ami: ami-08dbbdbfc4fa2f393 + zone: us-east-1 + user: centos + linux-centos-8-amd64: + ami: ami-05f2b469e504202f7 + zone: us-east-1 + user: cloud-user + linux-centos-8-arm64: + ami: ami-012947942cdc60db6 + zone: us-east-1 + user: centos + linux-centos-9-amd64: + ami: ami-0259c451b16b72e24 + zone: us-east-1 + user: ec2-user + # Redhat + linux-redhat-7-amd64: + ami: ami-0bd83a2aad0515b35 + zone: us-east-1 + user: ec2-user + linux-redhat-7-arm64: + ami: ami-0e97b0e0c6dce1d4e + zone: us-east-1 + user: ec2-user + linux-redhat-8-amd64: + ami: ami-054333d53aa32850c + zone: us-east-1 + user: ec2-user + linux-redhat-8-arm64: + ami: ami-05bcb9a3c5cfe07c9 + zone: us-east-1 + user: ec2-user + linux-redhat-9-amd64: + ami: ami-026ebd4cfe2c043b2 + zone: us-east-1 + user: ec2-user + linux-redhat-9-arm64: + ami: ami-03d6a5256a46c9feb + zone: us-east-1 + user: ec2-user + # Amazon + linux-amazon-2-amd64: + ami: ami-0bb4c991fa89d4b9b + zone: us-east-1 + user: ec2-user + linux-amazon-2-arm64: + ami: ami-03c5a38d438cf3d3e + zone: us-east-1 + user: ec2-user + linux-amazon-2023-amd64: + ami: ami-067d1e60475437da2 + zone: us-east-1 + user: ec2-user + linux-amazon-2023-arm64: + ami: ami-08b46fd32a1a5be7f + zone: us-east-1 + user: ec2-user + # Fedora + linux-fedora-34-amd64: + ami: ami-00c998fee16a6efd1 + zone: us-east-1 + user: fedora + linux-fedora-35-amd64: + ami: ami-0009c66e36c13502d + zone: us-east-1 + user: fedora + linux-fedora-36-amd64: + ami: ami-07299ccc184de5105 + zone: us-east-1 + user: fedora + linux-fedora-37-amd64: + ami: ami-0aab397f94a2c8536 + zone: us-east-1 + user: fedora + linux-fedora-38-amd64: + ami: ami-00eda974dc333e48f + zone: us-east-1 + user: fedora + linux-fedora-39-amd64: + ami: ami-05e56180581b70076 + zone: us-east-1 + user: fedora + # Rocky Linux + linux-rocky-8-amd64: + ami: ami-011ef2017d41cb239 + zone: us-east-1 + user: rocky + linux-rocky-8-arm64: + ami: ami-0ad512af1b9f6ef91 + zone: us-east-1 + user: rocky + linux-rocky-9-amd64: + ami: ami-09c77dc92e45bc3ea + zone: us-east-1 + user: rocky + linux-rocky-9-arm64: + ami: ami-001f90eb122403b4c + zone: us-east-1 + user: rocky + # Macos + macos-ventura-13.6.4-amd64: + ami: ami-0e94a57427e4e0611 + zone: us-east-1 + user: ec2-user + macos-ventura-13.6.4-arm64: + ami: ami-0b288e8fb0e803b12 + zone: us-east-1 + user: ec2-user + macos-sonoma-14.3-amd64: + ami: ami-0873bd33f11079049 + zone: us-east-1 + user: ec2-user + macos-sonoma-14.3-arm64: + ami: ami-0dd07a50e5e3d3ccb + zone: us-east-1 + user: ec2-user + macos-monterey-12.7.3-arm64: + ami: ami-0225fc57a58689798 + zone: us-east-1 + user: ec2-user + macos-monterey-12.7.3-amd64: + ami: ami-0a410356d12928dbc + zone: us-east-1 + user: ec2-user + # Windows + windows-desktop-10-amd64: + ami: ami-0a747df120215911a + zone: us-east-1 + user: Administrator + windows-desktop-11-amd64: # update with the correct AMI for userData + ami: ami-09d8cef159442d5b0 + zone: us-east-1 + user: Jenkins + windows-server-2012r2-amd64: + ami: ami-09a3ef2bc0c6a252f + zone: us-east-1 + user: Administrator + windows-server-2016-amd64: + ami: ami-04d7825822fe66af3 + zone: us-east-1 + user: Administrator + windows-server-2019-amd64: + ami: ami-06cc514f1012a7431 + zone: us-east-1 + user: Administrator + windows-server-2022-amd64: + ami: ami-0f9c44e98edf38a2b + zone: us-east-1 + user: Administrator diff --git a/deployability/modules/allocation/static/specs/size.yml b/deployability/modules/allocation/static/specs/size.yml new file mode 100755 index 0000000000..947ba65730 --- /dev/null +++ b/deployability/modules/allocation/static/specs/size.yml @@ -0,0 +1,34 @@ +vagrant: + micro: + cpu: 1 + memory: 1024 + small: + cpu: 1 + memory: 2048 + medium: + cpu: 2 + memory: 4096 + large: + cpu: 4 + memory: 8192 +aws: + micro: + amd64: + type: t2.small + arm64: + type: a1.medium + small: + amd64: + type: t3.small + arm64: + type: a1.large + medium: + amd64: + type: t3a.medium + arm64: + type: a1.xlarge + large: + amd64: + type: c5ad.xlarge + arm64: + type: c6g.xlarge diff --git a/deployability/modules/allocation/static/templates/vagrant.j2 b/deployability/modules/allocation/static/templates/vagrant.j2 new file mode 100755 index 0000000000..f151640214 --- /dev/null +++ b/deployability/modules/allocation/static/templates/vagrant.j2 @@ -0,0 +1,31 @@ +Vagrant.configure("2") do |config| + # Box image settings + config.vm.box = "{{ config.box }}" + config.vm.box_version = "{{ config.box_version }}" + + # Copy the public key to the VM + config.vm.provision "file", source: "{{ config.public_key }}", destination: ".ssh/authorized_keys" + + # VirtualBox specific settings + config.vm.provider "virtualbox" do |v| + v.memory = "{{ config.memory }}" + v.cpus = "{{ config.cpu }}" + v.name = "{{ config.name }}" + end + + # Network settings + config.vm.network "private_network", ip:"{{ config.ip }}" + config.ssh.forward_agent = true + # Create a file to indicate that the VM has been initialized + # This is required to correctly get the ssh-config of the VM + # TODO: find a better solution + init_indicator = "./init" + if not ::File.exists?(init_indicator) + File.write(init_indicator, "initialized") + else + # Use the IP address of the VM to connect via SSH + config.ssh.host = "{{ config.ip }}" + config.ssh.port = 22 + config.ssh.private_key_path = "{{ config.private_key }}" + end +end diff --git a/deployability/modules/allocation/static/templates/vagrant_black_mini.j2 b/deployability/modules/allocation/static/templates/vagrant_black_mini.j2 new file mode 100644 index 0000000000..870e3ea9ea --- /dev/null +++ b/deployability/modules/allocation/static/templates/vagrant_black_mini.j2 @@ -0,0 +1,18 @@ +Vagrant.configure("2") do |config| + config.vm.box = "{{ config.box }}" + config.vm.network :private_network, type: "dhcp" + config.vm.network "forwarded_port", guest: 22, host: "{{ config.port }}" + + config.vm.provision "file", source: "~/.ssh/vagrant_rsa.pub", destination: "~/.ssh/vagrant_rsa.pub" + config.vm.provision "file", source: "~/.ssh/authorized_keys", destination: "~/.ssh/authorized_keys" + + + config.vm.synced_folder ".", "/vagrant", disabled: true + + config.vm.provider "virtualbox" do |vb| + vb.name = "{{ config.name }}" + vb.customize ["setextradata", :id, "VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled", 1] + vb.memory = "4096" + vb.cpus = "2" + end +end diff --git a/deployability/modules/allocation/static/templates/vagrant_macStadium.j2 b/deployability/modules/allocation/static/templates/vagrant_macStadium.j2 new file mode 100755 index 0000000000..909de87894 --- /dev/null +++ b/deployability/modules/allocation/static/templates/vagrant_macStadium.j2 @@ -0,0 +1,18 @@ +Vagrant.configure("2") do |config| + # Box image settings + config.vm.box = "{{ config.box }}" + + # VirtualBox specific settings + config.vm.provider "parallels" do |v| + v.memory = "4096" + v.cpus = "2" + v.name = "{{ config.name }}" + v.linked_clone = false + end + + # Network settings + config.vm.network :private_network, type: "dhcp" + config.ssh.forward_agent = true + config.vm.network "public_network", bridge: "en0" + config.vm.synced_folder ".", "/vagrant", disabled: true +end diff --git a/deployability/modules/allocation/vagrant/__init__.py b/deployability/modules/allocation/vagrant/__init__.py new file mode 100644 index 0000000000..a5edbb14cb --- /dev/null +++ b/deployability/modules/allocation/vagrant/__init__.py @@ -0,0 +1,8 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from .credentials import VagrantCredentials +from .instance import VagrantInstance +from .provider import VagrantProvider +from .utils import VagrantUtils diff --git a/deployability/modules/allocation/vagrant/credentials.py b/deployability/modules/allocation/vagrant/credentials.py new file mode 100755 index 0000000000..c5a0024601 --- /dev/null +++ b/deployability/modules/allocation/vagrant/credentials.py @@ -0,0 +1,131 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import os +import subprocess + +from pathlib import Path + +from modules.allocation.generic import Credentials +from modules.allocation.generic.utils import logger + + +class VagrantCredentials(Credentials): + """ + A class for generating and deleting Vagrant credentials. + + Attributes: + path (Path): The path to store the credentials. + name (str): The name of the credentials. + public_key (Path): The key path. + + Raises: + CredentialsError: An error occurred while creating the key. + + """ + + def generate(self, base_dir: str | Path, name: str) -> Path: + """ + Generates a new SSH key pair and returns the path to the private key. + + Args: + base_dir (str | Path): The directory where the key pair will be stored. + name (str): The filename of the key pair. + overwrite (bool, optional): If True, an existing key pair with the same name will be overwritten. Defaults to False. + + Returns: + Path: The path to the private key of the generated key pair. + + Raises: + CredentialsError: This exception is raised if there's an error during the key creation process. + """ + if self.key_path and self.key_id: + logger.warning(f"Key pair already exists: {self.key_path}") + return self.key_path + + base_dir = Path(base_dir) + if not base_dir.exists(): + logger.debug(f"Creating base directory: {base_dir}") + base_dir.mkdir(parents=True, exist_ok=True) + elif Path(base_dir).is_file(): + raise self.CredentialsError(f"Invalid base directory: {base_dir}") + + private_key_path = Path(base_dir / name) + public_key_path = private_key_path.with_suffix(".pub") + # Delete the existing key pair if it exists. + if private_key_path.exists(): + logger.warning(f"Key pair already exists: {private_key_path}") + return self.load(base_dir, name) + elif private_key_path.exists(): + private_key_path.unlink() + if public_key_path.exists(): + public_key_path.unlink() + # Generate the key pair. + command = ["ssh-keygen", + "-f", str(private_key_path), + "-m", "PEM", + "-t", "rsa", + "-N", "", + "-q"] + output = subprocess.run(command, check=True, + capture_output=True, text=True) + os.chmod(private_key_path, 0o600) + if output.returncode != 0: + raise self.CredentialsError(f"Error creating key pair: {output.stderr}") + + # Save instance attributes. + self.name = name + self.key_id = name + self.key_path = private_key_path + return self.key_path + + def load(self, path: str | Path) -> None: + """ + Loads an existing key pair from the specified directory. + + Args: + path (str | Path): The path to the key pair. + + Raises: + CredentialsError: This exception is raised if the key pair doesn't exist or the specified directory is invalid. + """ + if path.endswith('.pub'): + key_path = Path(os.path.splitext(path)[0]) + else: + key_path = Path(path) + if not key_path.exists() or not key_path.is_file(): + raise self.CredentialsError(f"Invalid key path {key_path}.") + self.key_path = key_path + self.name = key_path.name + self.key_id = key_path.name + + def delete(self) -> None: + """ + Deletes the key pair from the file system. + """ + if not self.key_path.exists(): + logger.warning(f"Key pair doesn't exist: {self.key_path}.\ + Skipping deletion.") + return + Path(self.key_path).unlink() + Path(self.key_path.with_suffix(".pub")).unlink() + self.key_id = None + self.key_path = None + + def ssh_key_interpreter(self, ssh_key_path: str | Path) -> str: + """ + Gets the path of the public SSH Key from the provisioned public or private key + + Args: + public_key_path (str): The public or private key path or aws key id. + + Returns: + str: The path of the public key. + + Raises: + CredentialsError: An error occurred during key pair loading. + """ + if not ssh_key_path.endswith('.pub'): + ssh_key_path = ssh_key_path + ".pub" + return ssh_key_path diff --git a/deployability/modules/allocation/vagrant/instance.py b/deployability/modules/allocation/vagrant/instance.py new file mode 100755 index 0000000000..d0a9deb4ad --- /dev/null +++ b/deployability/modules/allocation/vagrant/instance.py @@ -0,0 +1,247 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import re +import subprocess + +from pathlib import Path + +from modules.allocation.generic import Instance +from modules.allocation.generic.models import ConnectionInfo, InstancePayload +from modules.allocation.generic.utils import logger +from .credentials import VagrantCredentials +from .utils import VagrantUtils + + +class VagrantInstance(Instance): + """ + The VagrantInstance class represents a Vagrant virtual machine instance. + It inherits from the generic Instance class. + + Attributes: + path (str or Path): Directory where instance data is stored. + identifier (str): Identifier of the instance. + credentials (VagrantCredentials): Vagrant credentials object. + host_identifier (str, optional): The host for the instance. Defaults to None. + host_instance_dir (str | Path, optional): The remote directory of the instance. Defaults to None. + ssh_port (str): SSH port of the instance. + remote_host_parameters (dict): Parameters of the remote host. + """ + def __init__(self, instance_parameters: InstancePayload, credentials: VagrantCredentials = None) -> None: + """ + Initializes a VagrantInstance. + + Args: + instance_parameters (InstancePayload): The parameters of the instance. + credentials (VagrantCredentials, optional): The credentials of the instance. Defaults to None. + """ + super().__init__(instance_parameters, credentials) + self.path: Path = Path(instance_parameters.instance_dir) + self.identifier: str = instance_parameters.identifier + self.credentials: VagrantCredentials = credentials + self.host_identifier: str = instance_parameters.host_identifier + self.host_instance_dir: str | Path = instance_parameters.host_instance_dir + self.ssh_port: str = instance_parameters.ssh_port + self.remote_host_parameters: dict = instance_parameters.remote_host_parameters + self.platform: str = instance_parameters.platform + self.arch: str = instance_parameters.arch + self.docker_image: str = instance_parameters.docker_image + + def start(self) -> None: + """ + Starts the Vagrant virtual machine. + + Returns: + None + """ + if self.arch == 'ppc64': + cmd = f"sudo docker run -itd --name={self.identifier} -p {self.ssh_port}:22 {self.docker_image}" + output = VagrantUtils.remote_command(cmd, self.remote_host_parameters) + container_id = output.split("\n")[0] + public_key = subprocess.run(["cat", str(self.credentials.key_path) + ".pub"], + stdout=subprocess.PIPE).stdout.decode("utf-8") + public_key = public_key.strip("\n") + cmd = f"sudo docker exec -i {container_id} /bin/bash -c 'echo \"{public_key}\" >> /root/.ssh/authorized_keys'" + output = VagrantUtils.remote_command(cmd, self.remote_host_parameters) + return output + else: + self.__run_vagrant_command('up') + + def reload(self) -> None: + """ + Reloads the Vagrant virtual machine. + + Returns: + None + """ + self.__run_vagrant_command('reload') + + def stop(self) -> None: + """ + Stops the Vagrant virtual machine. + + Returns: + None + """ + self.__run_vagrant_command('halt') + + def delete(self) -> None: + """ + Deletes the Vagrant virtual machine. + + Returns: + None + """ + if str(self.arch) == 'ppc64': + cmd = f"sudo docker rm -f {self.identifier}" + VagrantUtils.remote_command(cmd, self.remote_host_parameters) + return + if "not created" in self.status(): + logger.warning(f"Instance {self.identifier} is not created.\ + Skipping deletion.") + return + self.__run_vagrant_command(['destroy', '-f']) + if str(self.host_identifier) == "macstadium": + logger.debug(f"Deleting remote directory {self.host_instance_dir}") + VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) + logger.debug(f"Killing remote process on port {self.ssh_port}") + proccess = VagrantUtils.remote_command(f"sudo lsof -Pi :{self.ssh_port} -sTCP:LISTEN -t", self.remote_host_parameters) + VagrantUtils.remote_command(f"sudo kill -9 {proccess}", self.remote_host_parameters) + if str(self.host_identifier) == "black_mini": + logger.debug(f"Deleting remote directory {self.host_instance_dir}") + VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) + + + def status(self) -> str: + """ + Checks the status of the Vagrant virtual machine. + + Returns: + str: The status of the instance. + """ + output = self.__run_vagrant_command('status') + return self.__parse_vagrant_status(output) + + def ssh_connection_info(self) -> ConnectionInfo: + """ + Returns the SSH configuration of the Vagrant virtual machine. + + Returns: + ConnectionInfo: The SSH configuration of the VM. + """ + # Parse the ssh-config. + ssh_config = {} + if self.arch == 'ppc64': + ssh_config['hostname'] = self.remote_host_parameters['server_ip'] + tmp_port_file = str(self.path) + "/port.txt" + with open(tmp_port_file, 'r') as f: + port = f.read() + ssh_config['port'] = port + ssh_config['user'] = 'root' + if self.credentials: + ssh_config['private_key'] = str(self.credentials.key_path) + else: + if not 'running' in self.status(): + logger.debug(f"Instance {self.identifier} is not running.\ + Starting it.") + self.start() + output = self.__run_vagrant_command('ssh-config') + patterns = {'hostname': r'HostName (.*)', + 'user': r'User (.*)', + 'port': r'Port (.*)', + 'private_key': r'IdentityFile (.*)'} + if self.platform == 'macos': + for key, pattern in patterns.items(): + match = re.search(pattern, output) + if match and key == 'hostname': + ip = match.group(1).strip() + server_ip = self.remote_host_parameters['server_ip'] + tmp_port_file = str(self.path) + "/port.txt" + if str(self.host_identifier) == "macstadium": + if not Path(tmp_port_file).exists(): + port = VagrantUtils.get_port(self.remote_host_parameters) + cmd = f"sudo /usr/bin/ssh -i /Users/jenkins/.ssh/localhost -L {server_ip}:{port}:{ip}:22 -N 127.0.0.1 -f" + VagrantUtils.remote_command(cmd, self.remote_host_parameters) + with open(tmp_port_file, 'w') as f: + f.write(port) + else: + with open(tmp_port_file, 'r') as f: + port = f.read() + ssh_config['port'] = port + else: + with open(tmp_port_file, 'r') as f: + port = f.read() + ssh_config['port'] = port + ssh_config['hostname'] = server_ip + ssh_config['user'] = 'vagrant' + ssh_config['password'] = 'vagrant' + elif self.platform == 'windows': + for key, pattern in patterns.items(): + match = re.search(pattern, output) + if match and key == 'hostname': + ip = match.group(1).strip() + ssh_config['hostname'] = ip + ssh_config['port'] = 3389 + ssh_config['user'] = 'vagrant' + ssh_config['password'] = 'vagrant' + else: + for key, pattern in patterns.items(): + match = re.search(pattern, output) + if not match: + logger.error(f"Couldn't find {key} in ssh-config") + return None + ssh_config[key] = str(match.group(1)).strip("\r") + if self.credentials: + ssh_config['private_key'] = str(self.credentials.key_path) + return ConnectionInfo(**ssh_config) + + def __run_vagrant_command(self, command: str | list) -> str: + """ + Runs a Vagrant command and returns its output. + + Args: + command (str | list): The Vagrant command to run. + + Returns: + str: The output of the command. + """ + if isinstance(command, str): + command = [command] + if self.platform == 'macos': + cmd = f"sudo VAGRANT_CWD={self.host_instance_dir} /usr/local/bin/vagrant " + ' '.join(command) + output = VagrantUtils.remote_command(cmd, self.remote_host_parameters) + return output + else: + try: + output = subprocess.run(["vagrant", *command], + cwd=self.path, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + if stderr := output.stderr.decode("utf-8"): + logger.error(f"Command failed: {stderr}") + return None + return output.stdout.decode("utf-8") + except subprocess.CalledProcessError as e: + logger.error(f"Command failed: {e.stderr}") + return None + + def __parse_vagrant_status(self, message: str) -> str: + """ + Parses the status of the Vagrant virtual machine. + + Args: + message (str): The message to parse. + + Returns: + str: The parsed status. + """ + lines = message.split('\n') + for line in lines: + if 'Current machine states:' in line: + status_line = lines[lines.index(line) + 2] + status = ' '.join(status_line.split()[1:]) + status = status.split('(')[0].strip() + return status diff --git a/deployability/modules/allocation/vagrant/models.py b/deployability/modules/allocation/vagrant/models.py new file mode 100644 index 0000000000..54f5a50ee8 --- /dev/null +++ b/deployability/modules/allocation/vagrant/models.py @@ -0,0 +1,32 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from pathlib import Path +from pydantic import field_validator + +from modules.allocation.generic.models import ProviderConfig + + +class VagrantConfig(ProviderConfig): + ip: str + cpu: int + memory: int + box: str + box_version: str + private_key: str + public_key: str + name: str + port: int = None + platform: str + arch: str + + @field_validator('public_key', mode='before') + @classmethod + def check_public_key(cls, v: str) -> str: + if not v: + return v + path = Path(v) + if not path.exists() or not path.is_file(): + raise ValueError(f"Invalid public key path: {path}") + return v diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py new file mode 100644 index 0000000000..2484809204 --- /dev/null +++ b/deployability/modules/allocation/vagrant/provider.py @@ -0,0 +1,392 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import os +import platform, json +import subprocess +import boto3 + +from jinja2 import Environment, FileSystemLoader +from pathlib import Path +from telnetlib import Telnet + +from modules.allocation.generic import Provider +from modules.allocation.generic.models import CreationPayload, InstancePayload, InstancePayload +from modules.allocation.generic.utils import logger +from .credentials import VagrantCredentials +from .instance import VagrantInstance +from .models import VagrantConfig +from .utils import VagrantUtils + + +class VagrantProvider(Provider): + """ + The VagrantProvider class is a provider for managing Vagrant instances. + It inherits from the generic Provider class. + + Attributes: + provider_name (str): Name of the provider ('vagrant'). + """ + provider_name = 'vagrant' + + @classmethod + def _create_instance(cls, base_dir: Path, params: CreationPayload, config: VagrantConfig = None, ssh_key: str = None) -> VagrantInstance: + """ + Creates a Vagrant instance. + + Args: + base_dir (Path): The base directory for the instance. + params (CreationPayload): The parameters for instance creation. + config (VagrantConfig, optional): The configuration for the instance. Defaults to None. + ssh_key (str, optional): Public or private key for the instance. For example, we assume that if the public key is provided, the private key is located in the same directory and has the same name as the public key. Defaults to None. + + Returns: + VagrantInstance: The created Vagrant instance. + """ + if params.instance_name: + instance_id = params.instance_name + else: + instance_id = cls._generate_instance_id(cls.provider_name) + # Create the instance directory. + instance_dir = base_dir / instance_id + instance_dir.mkdir(parents=True, exist_ok=True) + platform = str(params.composite_name.split("-")[0]) + host_instance_dir = None + host_identifier = None + remote_host_parameters = None + arch = str(params.composite_name.split("-")[3]) + # Used for macOS deployments + if platform == 'macos': + host_instance_dir = "/Users/jenkins/testing/" + instance_id + logger.debug(f"Creating instance directory on remote host") + cmd = f"mkdir {host_instance_dir}" + remote_host_parameters = cls.__remote_host(arch, 'create') + host_identifier = remote_host_parameters['host_provider'] + VagrantUtils.remote_command(cmd, remote_host_parameters) + credentials = VagrantCredentials() + if arch == 'ppc64': + remote_host_parameters = cls.__remote_host(arch, 'create', params.composite_name.split("-")[1], instance_dir) + host_identifier = remote_host_parameters['host_provider'] + if not config: + logger.debug(f"No config provided. Generating from payload") + # Keys. + if not ssh_key: + logger.debug(f"Generating new key pair") + credentials.generate(instance_dir, 'instance_key') + else: + logger.debug(f"Using provided key pair") + public_key = credentials.ssh_key_interpreter(ssh_key) + credentials.load(public_key) + # Parse the config if it is not provided. + config = cls.__parse_config(params, credentials, instance_id, instance_dir, remote_host_parameters) + else: + logger.debug(f"Using provided config") + credentials.load(config.public_key) + + if arch != 'ppc64': + # Create the Vagrantfile. + cls.__create_vagrantfile(instance_dir, config) + logger.debug(f"Vagrantfile created. Creating instance.") + if platform == 'macos': + vagrant_file = str(instance_dir) + '/Vagrantfile' + VagrantUtils.remote_copy(vagrant_file, host_instance_dir, remote_host_parameters) + + instance_params = {} + instance_params['instance_dir'] = instance_dir + instance_params['identifier'] = instance_id + instance_params['platform'] = platform + instance_params['host_identifier'] = host_identifier + instance_params['host_instance_dir'] = host_instance_dir + instance_params['remote_host_parameters'] = remote_host_parameters + instance_params['arch'] = arch + instance_params['docker_image'] = config.box + instance_params['ssh_port'] = config.port + return VagrantInstance(InstancePayload(**instance_params), credentials) + + @staticmethod + def _load_instance(instance_dir: Path, identifier: str) -> VagrantInstance: + """ + Loads a Vagrant instance. + + Args: + instance_dir (Path): The directory of the instance. + identifier (str): The identifier of the instance. + + Returns: + VagrantInstance: The loaded Vagrant instance. + """ + instance_params = InstancePayload(**dict(instance_dir, identifier)) + return VagrantInstance(instance_params) + + @classmethod + def _destroy_instance(cls, destroy_parameters: InstancePayload) -> None: + """ + Destroys a Vagrant instance. + + Args: + destroy_parameters (InstancePayload): The parameters for instance deletion. + Returns: + None + """ + if destroy_parameters.host_identifier == "None" or destroy_parameters.host_identifier is None: + instance_params = {} + instance_params['instance_dir'] = destroy_parameters.instance_dir + instance_params['identifier'] = destroy_parameters.identifier + instance_params['platform'] = destroy_parameters.platform + instance = VagrantInstance(InstancePayload(**instance_params)) + if os.path.dirname(destroy_parameters.key_path) != str(destroy_parameters.instance_dir): + logger.debug(f"The key {destroy_parameters.key_path} will not be deleted. It is the user's responsibility to delete it.") + else: + instance_params = {} + instance_params['instance_dir'] = destroy_parameters.instance_dir + instance_params['identifier'] = destroy_parameters.identifier + instance_params['platform'] = destroy_parameters.platform + instance_params['host_identifier'] = destroy_parameters.host_identifier + instance_params['host_instance_dir'] = destroy_parameters.host_instance_dir + remote_host_parameters = cls.__remote_host(str(destroy_parameters.arch), 'delete', destroy_parameters.host_identifier, destroy_parameters.instance_dir) + instance_params['remote_host_parameters'] = remote_host_parameters + instance_params['arch'] = destroy_parameters.arch + instance_params['ssh_port'] = destroy_parameters.ssh_port + instance = VagrantInstance(InstancePayload(**instance_params)) + logger.debug(f"Destroying instance {destroy_parameters.identifier}") + instance.delete() + + @classmethod + def __create_vagrantfile(cls, instance_dir: Path, config: VagrantConfig) -> None: + """ + Creates a Vagrantfile in the instance directory. + + Args: + instance_dir (Path): The directory to create the Vagrantfile in. + config (VagrantConfig): The configuration for the Vagrantfile. + + Returns: + None + """ + if 'win' in platform.system().lower(): + # Add dobule backslashes for windows. + config.public_key = config.public_key.replace('\\', '\\\\') + content = cls.__render_vagrantfile(config) + with open(instance_dir / 'Vagrantfile', 'w') as f: + f.write(content) + + @classmethod + def __render_vagrantfile(cls, config: VagrantConfig) -> str: + """ + Renders a Vagrantfile template. + + Args: + config (VagrantConfig): The configuration for the Vagrantfile. + + Returns: + str: The rendered Vagrantfile. + """ + environment = Environment(loader=FileSystemLoader(cls.TEMPLATES_DIR)) + if config.platform == 'macos': + if config.arch == 'arm64': + template = environment.get_template("vagrant_macStadium.j2") + else: + template = environment.get_template("vagrant_black_mini.j2") + else: + template = environment.get_template("vagrant.j2") + return template.render(config=config) + + @classmethod + def __parse_config(cls, params: CreationPayload, credentials: VagrantCredentials, instance_id: str, instance_dir: Path, remote_host_parameters: dict = None) -> VagrantConfig: + """ + Parses the configuration for a Vagrant instance. + + Args: + params (CreationPayload): The parameters for instance creation. + credentials (VagrantCredentials): The credentials for the instance. + + Returns: + VagrantConfig: The parsed configuration for the Vagrant instance. + """ + config = {} + # Get the specs from the yamls. + size_specs = cls._get_size_specs(params.size) + os_specs = cls._get_os_specs(params.composite_name) + # Parse the configuration. + config['ip'] = cls.__get_available_ip() + config['box'] = str(os_specs['box']) + config['box_version'] = str(os_specs['box_version']) + config['private_key'] = str(credentials.key_path) + config['public_key'] = str(credentials.key_path.with_suffix('.pub')) + config['cpu'] = size_specs['cpu'] + config['memory'] = size_specs['memory'] + config['name'] = instance_id + config['platform'] = params.composite_name.split("-")[0] + config['arch'] = params.composite_name.split("-")[3] + + if params.composite_name.startswith("macos") and params.composite_name.endswith("amd64") or params.composite_name.split("-")[3] == 'ppc64': + tmp_port_file = str(instance_dir) + "/port.txt" + config['port'] = VagrantUtils.get_port(remote_host_parameters, config['arch']) + with open(tmp_port_file, 'w') as f: + f.write(config['port']) + + return VagrantConfig(**config) + + @classmethod + def __get_available_ip(cls): + """ + Gets an available IP address. + + Returns: + str: An available IP address. + + Raises: + Exception: If no available IP address is found. + """ + def check_ip(ip): + response = os.system("ping -c 1 -w3 " + ip + " > /dev/null 2>&1") + if response != 0: + return ip + + for i in range(2, 254): + ip = f"192.168.57.{i}" + if check_ip(ip): + return ip + + # If no available IP address is found, raise an exception. + raise cls.ProvisioningError("No available IP address found.") + + @staticmethod + def __remote_host(arch: str, action: str, os: str = None, instance_dir: Path = None) -> str: + """ + Returns the host parameters for macOS instances. + + Args: + arch (str): The architecture of the instance. + action (str): The action to perform. + os (str, optional): The operating system of the instance. Defaults to None. + instance_dir (Path, optional): The directory of the instance. Defaults to None. + + Returns: + dict: The host parameters for the remote instance. + """ + client = boto3.client('secretsmanager') + server_port = 22 + timeout = 5 + conn_ok = False + remote_host_parameters = {} + + if arch == 'arm64': + try: + server_ip = client.get_secret_value(SecretId='devops_macstadium_m1_jenkins_ip')['SecretString'] + ssh_password = client.get_secret_value(SecretId='devops_macstadium_m1_jenkins_password')['SecretString'] + ssh_user = client.get_secret_value(SecretId='devops_macstadium_m1_jenkins_user')['SecretString'] + except Exception as e: + raise ValueError('Could not get macOS macStadium server IP: ' + str(e) + '.') + + try: + tn = Telnet(server_ip, server_port, timeout) + conn_ok = True + tn.close() + except Exception as e: + raise ValueError('Could not connect to macOS macStadium server: ' + str(e) + '.') + + remote_host_parameters['server_ip'] = server_ip + remote_host_parameters['ssh_password'] = ssh_password + remote_host_parameters['ssh_user'] = ssh_user + remote_host_parameters['host_provider'] = 'macstadium' + + if conn_ok: + if action == 'create': + cmd = "sudo /usr/local/bin/prlctl list -j" + prlctl_output = subprocess.Popen(f"sshpass -p {ssh_password} ssh {ssh_user}@{server_ip} {cmd}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') + data_list = json.loads(prlctl_output) + uuid_count = 0 + for item in data_list: + if 'uuid' in item: + uuid_count += 1 + if uuid_count < 2: + logger.info(f"macStadium server has less than 2 VMs running, deploying in this host.") + return remote_host_parameters + else: + raise ValueError(f"macStadium server is full capacity, use AWS provider.") + else: + return remote_host_parameters + if arch == 'amd64': + try: + server_ip = client.get_secret_value(SecretId='devops_black_mini_jenkins_ip')['SecretString'] + ssh_password = client.get_secret_value(SecretId='devops_black_mini_jenkins_password')['SecretString'] + ssh_user = client.get_secret_value(SecretId='devops_black_mini_jenkins_user')['SecretString'] + except Exception as e: + raise ValueError('Could not get macOS Black mini server IP: ' + str(e) + '.') + + try: + tn = Telnet(server_ip, server_port, timeout) + conn_ok = True + tn.close() + except Exception as e: + raise ValueError('Could not connect to macOS Black mini server: ' + str(e) + '.') + + remote_host_parameters['server_ip'] = server_ip + remote_host_parameters['ssh_password'] = ssh_password + remote_host_parameters['ssh_user'] = ssh_user + remote_host_parameters['host_provider'] = 'black_mini' + + if conn_ok: + if action == 'create': + loadav_command = "\'python3 -c \"import psutil; print(psutil.getloadavg()[0])\"\'" + cpu_command = "\'python3 -c \"import psutil; print(psutil.getloadavg()[0]/ psutil.cpu_count() * 100)\"\'" + memory_command = "\'python3 -c \"import psutil; print(psutil.virtual_memory().percent)\"\'" + load_average = subprocess.Popen(f"sshpass -p {ssh_password} ssh {ssh_user}@{server_ip} {loadav_command}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') + cpu_usage = subprocess.Popen(f"sshpass -p {ssh_password} ssh {ssh_user}@{server_ip} {cpu_command}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') + memory_usage = subprocess.Popen(f"sshpass -p {ssh_password} ssh {ssh_user}@{server_ip} {memory_command}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') + + if float(load_average) <= 10.0 and float(cpu_usage) <= 70.0 and float(memory_usage) <= 75.0: + logger.info(f"Using the black mini server to deploy.") + return remote_host_parameters + else: + raise ValueError(f"Black mini server is under heavy load, use AWS provider.") + else: + return remote_host_parameters + + if arch == 'ppc64': + if os == 'debian': + try: + server_ip = client.get_secret_value(SecretId='devops_ppc64_debian_jenkins_ip')['SecretString'] + ssh_key = client.get_secret_value(SecretId='devops_ppc64_jenkins_key')['SecretString'] + ssh_user = client.get_secret_value(SecretId='devops_ppc64_debian_jenkins_user')['SecretString'] + remote_host_parameters['host_provider'] = 'debian' + except Exception as e: + raise ValueError('Could not get Debian ppc64 server IP: ' + str(e) + '.') + if os == 'centos': + try: + server_ip = client.get_secret_value(SecretId='devops_ppc64_centos_jenkins_ip')['SecretString'] + ssh_key = client.get_secret_value(SecretId='devops_ppc64_jenkins_key')['SecretString'] + ssh_user = client.get_secret_value(SecretId='devops_ppc64_centos_jenkins_user')['SecretString'] + remote_host_parameters['host_provider'] = 'centos' + except Exception as e: + raise ValueError('Could not get Centos ppc64 server IP: ' + str(e) + '.') + + try: + tn = Telnet(server_ip, server_port, timeout) + conn_ok = True + tn.close() + except Exception as e: + raise ValueError('Could not connect to ppc64 server: ' + str(e) + '.') + + key_path = instance_dir / 'ppc-key' + with open(key_path, 'w') as f: + f.write(ssh_key) + ssh_key = key_path + subprocess.call(['chmod', '0400', key_path]) + + remote_host_parameters['server_ip'] = server_ip + remote_host_parameters['ssh_key'] = ssh_key + remote_host_parameters['ssh_user'] = ssh_user + + if conn_ok: + if action == 'create': + cmd = "sudo docker ps -a" + output = subprocess.Popen(f"ssh -i {ssh_key} {ssh_user}@{server_ip} {cmd}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') + if '2222' in output and '8080' in output: + raise ValueError(f"ppc64 server has full capacity, cannot host a new container") + else: + return remote_host_parameters + else: + return remote_host_parameters diff --git a/deployability/modules/allocation/vagrant/utils.py b/deployability/modules/allocation/vagrant/utils.py new file mode 100644 index 0000000000..d27b2dab80 --- /dev/null +++ b/deployability/modules/allocation/vagrant/utils.py @@ -0,0 +1,108 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import subprocess +from pathlib import Path +from modules.allocation.generic.utils import logger + + +class VagrantUtils: + @classmethod + def remote_command(cls, command: str | list, remote_host_parameters: dict) -> str: + """ + Runs a command on the remote host. + Args: + command (str | list): The command to run. + remote_host_parameters (dict): The parameters of the remote host. + Returns: + str: The output of the command. + """ + ssh_command = None + server_ip = remote_host_parameters['server_ip'] + ssh_user = remote_host_parameters['ssh_user'] + if remote_host_parameters.get('ssh_password'): + ssh_password = remote_host_parameters['ssh_password'] + ssh_command = f"sshpass -p {ssh_password} ssh {ssh_user}@{server_ip} {command}" + if remote_host_parameters.get('ssh_key'): + ssh_key = remote_host_parameters['ssh_key'] + ssh_command = f"ssh -i {ssh_key} {ssh_user}@{server_ip} \"{command}\"" + + try: + output = subprocess.Popen(f"{ssh_command}", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout_data, stderr_data = output.communicate() # Capture stdout and stderr data + stdout_text = stdout_data.decode('utf-8') if stdout_data else "" # Decode stdout bytes to string + stderr_text = stderr_data.decode('utf-8') if stderr_data else "" # Decode stderr bytes to string + + if stderr_text: + logger.error(f"Command failed: {stderr_text}") + return None + + return stdout_text + except subprocess.CalledProcessError as e: + logger.error(f"Command failed: {e.stderr.decode('utf-8')}") + return None + + @classmethod + def remote_copy(cls, instance_dir: Path, host_instance_dir: Path, remote_host_parameters: dict) -> str: + """ + Copies the instance directory to the remote host. + + Args: + instance_dir (Path): The instance directory. + host_instance_dir (Path): The remote directory. + remote_host_parameters (dict): The parameters of the remote host. + + Returns: + str: The output of the command. + """ + server_ip = remote_host_parameters['server_ip'] + ssh_password = remote_host_parameters['ssh_password'] + ssh_user = remote_host_parameters['ssh_user'] + + try: + output = subprocess.Popen(f"sshpass -p {ssh_password} scp -r {instance_dir} {ssh_user}@{server_ip}:{host_instance_dir}", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + _, stderr = output.communicate() + if stderr: + raise ValueError(f"Command failed: {stderr.decode('utf-8')}") + return output.stdout + except subprocess.CalledProcessError as e: + raise ValueError(f"Command failed: {e.stderr.decode('utf-8')}") + + @classmethod + def get_port(cls, remote_host_parameters: dict, arch: str = None) -> int: + """ + Returns the port for the remote host. + + Args: + remote_host_parameters (dict): The parameters of the remote host. + arch (str): The architecture of the remote host. + + Returns: + int: The port. + """ + + if arch == 'ppc64': + cmd = "sudo lsof -i:8080" + output = cls.remote_command(cmd, remote_host_parameters) + if not output: + return str(8080) + cmd = "sudo lsof -i:2222" + output = cls.remote_command(cmd, remote_host_parameters) + if not output: + return str(2222) + + raise ValueError(f"ppc64 server has no available SSH ports.") + else: + for i in range(20, 40): + port = f"432{i}" + cmd = f"sudo lsof -i:{port}" + output = cls.remote_command(cmd, remote_host_parameters) + if not output: + return port diff --git a/deployability/modules/generic/__init__.py b/deployability/modules/generic/__init__.py new file mode 100755 index 0000000000..4270eb1f8f --- /dev/null +++ b/deployability/modules/generic/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from .ansible import Ansible, Inventory diff --git a/deployability/modules/generic/ansible.py b/deployability/modules/generic/ansible.py new file mode 100755 index 0000000000..6eaf083418 --- /dev/null +++ b/deployability/modules/generic/ansible.py @@ -0,0 +1,127 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +import ansible_runner +import jinja2 +import yaml + +from pathlib import Path +from pydantic import BaseModel, IPvAnyAddress + +from modules.generic.utils import Utils +from modules.generic.logger import Logger + + +class Inventory(BaseModel): + ansible_host: str | IPvAnyAddress + ansible_user: str + ansible_port: int + ansible_ssh_private_key_file: str + + +class Ansible: + def __init__(self, ansible_data: dict | Inventory, playbooks_path: str | Path = None): + self.playbooks_path = playbooks_path + self.ansible_data = Inventory(**dict(ansible_data)) + self.inventory = self.generate_inventory() + self.logger = Logger(Path(__file__).stem).get_logger() + + def render_playbooks(self, rendering_variables: dict) -> list[str]: + """ + Render the playbooks with Jinja. + + Args: + rendering_variables (dict): Extra variables to render the playbooks. + """ + tasks = [] + path_to_render_playbooks = rendering_variables.get("templates_path") + template_loader = jinja2.FileSystemLoader(searchpath=path_to_render_playbooks) + template_env = jinja2.Environment(loader=template_loader) + + list_template_tasks = Utils.get_template_list( + path_to_render_playbooks, rendering_variables.get("templates_order")) + self.logger.debug(f"Templates found: {list_template_tasks}") + if list_template_tasks: + for template in list_template_tasks: + loaded_template = template_env.get_template(template) + self.logger.debug(f"Rendering template {template}") + rendered = yaml.safe_load(loaded_template.render(host=self.ansible_data, **rendering_variables)) + + if not rendered: + self.logger.warn(f"Template {template} not rendered") + continue + + tasks += rendered + else: + self.logger.error( + f"No templates found in {path_to_render_playbooks}") + self.logger.debug(tasks) + return tasks + + def render_playbook(self, playbook: str | Path, rendering_variables: dict = {}) -> str | None: + """ + Render one playbook with Jinja. + + Args: + playbook (str, Path): The playbook to render. + rendering_variables (dict): Extra variables to render the playbooks. + """ + playbook = Path(playbook) + if not playbook.exists(): + self.logger.error(f"Error: Playbook {playbook} not found") + return None + _env = jinja2.Environment(loader=jinja2.FileSystemLoader(playbook.parent)) + template = _env.get_template(playbook.name) + self.logger.debug(f"Rendering template {playbook}") + rendered = template.render(host=self.ansible_data, **rendering_variables) + + return yaml.safe_load(rendered) + + def run_playbook(self, playbook: str | Path, extravars: dict = None, verbosity: int = 1, env_vars: dict = {}) -> ansible_runner.Runner: + """ + Run the playbook with ansible_runner. + + Args: + playbook (str, Path): The playbook to run. + extravars (dict): Extra variables to pass to the playbook. + verbosity (int): Verbosity level for the playbook. + env_vars (dict): Environment variables to pass to the playbook. + """ + # Set the callback to yaml to env_vars + env_vars['ANSIBLE_STDOUT_CALLBACK'] = 'community.general.yaml' + + if self.playbooks_path and (isinstance(playbook, str) or isinstance(playbook, Path)): + playbook = Path(self.playbooks_path) / playbook + + self.logger.debug(f"Using inventory: {self.inventory}") + self.logger.debug(f"Running playbook: {playbook}") + result = ansible_runner.run( + inventory=self.inventory, + playbook=playbook, + verbosity=verbosity, + extravars=extravars, + envvars=env_vars, + ) + self.logger.debug(f"Playbook {playbook} finished with status {result.stats}") + return result + + def generate_inventory(self) -> dict: + """ + Generate the inventory for ansible. + + Returns: + dict: Inventory for ansible. + """ + inventory_data = { + 'all': { + 'hosts': { + self.ansible_data.ansible_host: { + 'ansible_port': self.ansible_data.ansible_port, + 'ansible_user': self.ansible_data.ansible_user, + 'ansible_ssh_private_key_file': self.ansible_data.ansible_ssh_private_key_file + } + } + } + } + + return inventory_data diff --git a/deployability/modules/generic/logger/__init__.py b/deployability/modules/generic/logger/__init__.py new file mode 100644 index 0000000000..842ae40bc7 --- /dev/null +++ b/deployability/modules/generic/logger/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from .logger import Logger diff --git a/deployability/modules/generic/logger/config.yaml b/deployability/modules/generic/logger/config.yaml new file mode 100644 index 0000000000..1f86bf764a --- /dev/null +++ b/deployability/modules/generic/logger/config.yaml @@ -0,0 +1,30 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 1 +formatters: + simple: + format: '[%(asctime)s] [%(levelname)s] %(name)s: %(message)s' + colored: + (): colorlog.ColoredFormatter + format: '%(log_color)s[%(asctime)s] [%(levelname)s] %(name)s: %(message)s' + datefmt: '%Y-%m-%d %H:%M:%S' +filters: + uppercase: + (): modules.generic.logger.filters.UppercaseNameFilter +handlers: + console: + class: colorlog.StreamHandler + level: DEBUG + formatter: colored + stream: ext://sys.stdout + filters: [uppercase] + file: + class: logging.FileHandler + level: DEBUG + formatter: simple + filename: /tmp/workflow.log + filters: [uppercase] +root: + level: DEBUG + handlers: [console, file] diff --git a/deployability/modules/generic/logger/filters.py b/deployability/modules/generic/logger/filters.py new file mode 100644 index 0000000000..bcaa06dfb3 --- /dev/null +++ b/deployability/modules/generic/logger/filters.py @@ -0,0 +1,22 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +import logging + + +class UppercaseNameFilter(logging.Filter): + """ + A filter that uppercases the name of the log record. + """ + def filter(self, record: str) -> bool: + """ + Filters the log record to uppercase the name. + + Args: + record (LogRecord): The log record to filter. + + Returns: + bool: True if the record should be logged, False otherwise. + """ + record.name = record.name.upper() + return True diff --git a/deployability/modules/generic/logger/logger.py b/deployability/modules/generic/logger/logger.py new file mode 100644 index 0000000000..373df019b5 --- /dev/null +++ b/deployability/modules/generic/logger/logger.py @@ -0,0 +1,45 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +import logging +import logging.config +import yaml + +from pathlib import Path + + +class Logger: + """ + A Logger class that configures and provides a logger using a configuration file. + + Attributes: + logger (logging.Logger): The configured logger. + """ + + def __init__(self, name: str) -> None: + """ + Initializes the Logger object. + + Args: + name (str): The name of the logger. + """ + self._load_config() + self.logger = logging.getLogger(name) + + def _load_config(self) -> None: + """ + Loads the logging configuration from 'config.yaml' file. + """ + config_path = Path(__file__).parent / 'config.yaml' + with open(config_path, 'r') as f: + config = yaml.safe_load(f.read()) + logging.config.dictConfig(config) + + def get_logger(self) -> logging.Logger: + """ + Returns the configured logger. + + Returns: + logging.Logger: The configured logger. + """ + return self.logger diff --git a/deployability/modules/generic/models.py b/deployability/modules/generic/models.py new file mode 100644 index 0000000000..754087e11a --- /dev/null +++ b/deployability/modules/generic/models.py @@ -0,0 +1,11 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from pydantic import BaseModel, IPvAnyAddress + + +class AnsibleInventory(BaseModel): + ansible_host: str | IPvAnyAddress + ansible_user: str + ansible_port: int + ansible_ssh_private_key_file: str diff --git a/deployability/modules/generic/parser.py b/deployability/modules/generic/parser.py new file mode 100755 index 0000000000..1fa779df73 --- /dev/null +++ b/deployability/modules/generic/parser.py @@ -0,0 +1,19 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from argparse import ArgumentParser +from pydantic import BaseModel + + +def pydantic_argument_parser(parser: ArgumentParser, model: BaseModel): + "Add Pydantic model to an ArgumentParser" + fields = model.model_fields + for name, field in fields.items(): + parser.add_argument( + f"--{name}", + dest=name, + type=field.type_, + default=field.default, + help=field.field_info.description, + ) + return parser.parse_args() diff --git a/deployability/modules/generic/utils.py b/deployability/modules/generic/utils.py new file mode 100644 index 0000000000..80d5c04a82 --- /dev/null +++ b/deployability/modules/generic/utils.py @@ -0,0 +1,62 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +import os +import yaml + +from pathlib import Path + + +class Utils: + @staticmethod + def get_template_list(path, custom_order=None) -> list[Path]: + """ + Get the list of templates in the path. + + Args: + path: Path to the templates. + custom_order: Custom order to sort the templates. + """ + list_tasks = [] + with os.scandir(path) as entries: + for entry in entries: + if entry.is_file(): + list_tasks.append(entry.name) + + if custom_order: + sorted_list = sorted(list_tasks, key=lambda x: custom_order.index( + x) if x in custom_order else float('inf')) + else: + sorted_list = list_tasks + + return sorted_list + + @staticmethod + def load_from_yaml(file_path: str | Path, map_keys: dict = None, specific_key: dict = None) -> dict: + """ + Load data from a yaml file. + + Args: + file_path: Path to the yaml file. + map_keys: Map of keys to change. + specific_key: Specific key to return. + + Returns: + dict: Data from the yaml file. + + Raises: + FileNotFoundError: If the file is not found. + """ + file_path = Path(file_path) + if not file_path.exists(): + raise FileNotFoundError(f'File "{file_path}" not found.') + + data = yaml.safe_load(open(file_path)) + + if map_keys: + data = {map_keys[k]: v for k, v in data.items() if k in map_keys} + + if specific_key: + return data.get(specific_key) + + return data diff --git a/deployability/modules/provision/README.MD b/deployability/modules/provision/README.MD new file mode 100644 index 0000000000..2e453cf2cd --- /dev/null +++ b/deployability/modules/provision/README.MD @@ -0,0 +1,256 @@ +## Provision module + +### User documentation + +The Provision module installs libraries, dependencies and applications on the allocated infrastructure (From Allocation module) + +This module can be executed as follows: + A. Installing and using Workflow engine + B. Direct execution + +#### A. Installing and using Workflow engine + +The execution of the workflow is done through the installation of its library. + +Initially, Python libraries must be installed. we recommended the use of virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. + +1. Create the python environment: + + ```bash + python3 -m venv {environment_name} + ``` + +2. Activate the environment: + + ```bash + source {venv directory}/bin/activate + ``` + +3. Clone the `wazuh-qa` repository: + + Navigate to the project directory and switch to the project branch: + + ```bash + git clone https://github.com/wazuh/wazuh-qa.git + cd wazuh-qa + git checkout {project-branch} + ``` + +4. Install requirements: + + ```bash + pip3 install -r deployability/deps/requirements.txt + ``` + +5. Install the Workflow engine library and its launcher: + + While in wazuh-qa: + + ```bash + cd modules + pip3 uninstall -y workflow_engine && pip3 install . + ``` + + Run the module by doing the following steps: + +6. Test fixture to execute: + + It is required to create a fixture (YAML file) where the infrastructure, provisioning, and tests to be executed is declared. + + >Note: You can find some fixture examples in '[deployability/modules/workflow_engine/examples/](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/workflow_engine/examples)' + + Example: + + ```bash + version: 0.1 + description: This workflow is used to provision agents hosts por DDT1 PoC + variables: + agents-os: + - linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + + tasks: + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent + + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - install: + - component: curl + - component: python + type: source + version: "{python-version}" + - component: virtualenv + type: pip + - component: deps/requirements.txt + type: pip + - component: pyyaml + type: pip + version: 6.0.1 + depends-on: + - "allocate-{agent}" + foreach: + - variable: agents-os + as: agent + ``` + + Following the schema of the example: + + Configure the following parameters depending on your allocation: + + ```yaml + variables/agent-os + variables/manager-os + infra-provider + working-dir + tasks + ``` + + Pay attention to the tasks: + + ```yaml + args + depends-on + ``` + + >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your provision (allocation and provision) + +7. Command execution (local): + + Execute the command by referencing the parameters required by the library (launcher). + + ```bash + python3 -m workflow_engine {.yaml fixture path} + ``` + + Example + + ```bash + python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml + ``` + +#### B. Direct execution + +To execute the Allocation module without installing the Workflow engine, you can use the launcher ('[module/provision/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/main.py)'): + +1. Execution + + While in wazuh-qa/deployability + + ```bash + python3 module/provision/main.py --inventory-manager { inventory .yaml} --install "{'component': '{component}', 'type': '{type}', 'version': '{version}'}" + ``` + + Example: + ```bash + python3 module/provision/main.py --inventory-manager /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml --install "{'component': 'pyyaml', 'type': 'pip', 'version' : '6.0.1'}" + ``` + +--- + +### Technical documentation + +The `provision module` allows for the installation and uninstallation of libraries, dependencies, and applications in the infrastructure. + +It can receive instructions either through the command line or be mediated through the Workflow Engine. + +In both cases, the following information is required: + +```bash + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - install: + - component: curl + - component: python + type: source + version: "{python-version}" + - component: virtualenv + type: pip + - component: deps/requirements.txt + type: pip + - component: pyyaml + type: pip + version: 6.0.1 + depends-on: + - "allocate-{agent}" + foreach: + - variable: agents-os + as: agent +``` + +The snippet obtained from a Workflow fixture highlights the parameters necessary for its operation. These include the inventory path, the dependency/library to install, and the installation type. + +It requires an infrastructure on which to perform its actions. + +The module is composed of: + +- Launcher ('[/wazuh-qa/deployability/modules/provision/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/main.py)') + Entry point for the workflow or the user who wishes to execute a provision. + +- Parameter validator ('[/wazuh-qa/deployability/modules/provision/models.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/models.py)') + Validator for parameters entered by the Workflow engine or the user. + +- Module functions ('[/wazuh-qa/deployability/modules/provision/provision.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/provision.py)') + Module-specific functions responsible for triggering the provision. + +- Component type categorizer ('[/wazuh-qa/deployability/modules/provision/component_type.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/component_type.py)') + Allows obtaining and expanding data based on initially received parameters. + +- Executor ('[/wazuh-qa/deployability/modules/provision/actions.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/actions.py)') + Executes the provision using Actions in the generic module. + +- Utils ('[/wazuh-qa/deployability/modules/provision/utils.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/utils.py)') + Enables reading and changing the format of YAML files to executable formats. + + +![image](https://github.com/wazuh/wazuh-qa/assets/125690423/e06fb59c-6497-4396-b20c-a21a68a5e883) + + +[Provision.drawio.zip](https://github.com/wazuh/wazuh-qa/files/14241269/Provision.drawio.zip) + + +### License + +WAZUH Copyright (C) 2015 Wazuh Inc. (License GPLv2) diff --git a/deployability/modules/provision/__init__.py b/deployability/modules/provision/__init__.py new file mode 100755 index 0000000000..586e4257bf --- /dev/null +++ b/deployability/modules/provision/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from .provision import Provision diff --git a/deployability/modules/provision/actions.py b/deployability/modules/provision/actions.py new file mode 100644 index 0000000000..f6032c5c56 --- /dev/null +++ b/deployability/modules/provision/actions.py @@ -0,0 +1,88 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from modules.generic import Ansible + +from modules.provision.handler import ProvisionHandler +from modules.provision.models import ComponentInfo +from modules.provision.utils import logger + + +class Action: + """ + Class to define the action. + + Attributes: + handler (ProvisionHandler): The provision handler. + ansible (Ansible): The Ansible instance. + """ + + def __init__(self, action: str, component_info: ComponentInfo, ansible_data: dict) -> None: + """ + Initialize the action. + + Args: + action (str): The action to execute. + component_info (ComponentInfo): The component information. + ansible_data (dict): The Ansible data. + """ + component_info = ComponentInfo(**dict(component_info)) + action_type = component_info.type + self.handler = ProvisionHandler(component_info, action, action_type) + self.ansible = Ansible(ansible_data, playbooks_path=self.handler.templates_path) + + def execute(self) -> dict: + """ + Execute the action for the component. + + Returns: + dict: The status of the executed action. + """ + # Get the OS family variable. + self.handler.variables_dict['ansible_os_family'] = self._get_os_family() + # Render the playbook with the variables. + logger.debug(f"Render playbook with vars: {self.handler.variables_dict}.") + tasks = self.ansible.render_playbooks(self.handler.variables_dict) + # Get and execute the playbook. + logger.debug(f"Tasks to execute: {tasks}.") + playbook = self._get_playbook(tasks) + logger.info(f"Execute {self.handler.action} for {self.handler.component_info.component}.") + status = self.ansible.run_playbook(playbook) + + return status + + def _get_os_family(self) -> str: + """ + Get the OS family. + + Returns: + str: The OS family. + """ + ansible_task = [{ + 'name': 'Capture ansible_os_family', + 'set_fact': { + 'ansible_os_family': "{{ ansible_facts['distribution_file_variety'] }}", + 'cacheable': 'yes' + } + }] + logger.debug(f"Get OS family for {self.ansible.ansible_data.ansible_host}.") + playbook = self._get_playbook(ansible_task) + status = self.ansible.run_playbook(playbook) + fact_cache = status.get_fact_cache(host=self.ansible.ansible_data.ansible_host) + logger.debug(f"OS family: {fact_cache.get('ansible_os_family')}.") + return fact_cache.get('ansible_os_family') or '' + + def _get_playbook(self, tasks: list[dict]) -> dict: + """ + Get the playbook to execute. + + Returns: + dict: The playbook to execute. + """ + playbook = { + 'hosts': self.ansible.ansible_data.ansible_host, + 'become': True, + 'gather_facts': True, + 'tasks': tasks, + } + return playbook diff --git a/deployability/modules/provision/handler.py b/deployability/modules/provision/handler.py new file mode 100644 index 0000000000..e125d2284a --- /dev/null +++ b/deployability/modules/provision/handler.py @@ -0,0 +1,110 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from pathlib import Path + +from modules.provision.models import ComponentInfo +from modules.provision.utils import logger + + +class ProvisionHandler: + """ + Class to define how the component provisioning should be handled. + + Attributes: + action (str): The action to be executed. + method (str): The provision method to be executed. + component_info (ComponentInfo): The component information. + templates_path (str): The path to the templates. + templates_order (list[str]): The order of the templates to be executed. + variables_dict (dict): The variables to be used to render the templates. + """ + _base_templates_path = Path(__file__).parent / 'playbooks' + _actions = ['install', 'uninstall'] + _methods = ['package', 'assistant', 'source'] + + def __init__(self, component_info: ComponentInfo, action: str, method: str) -> None: + """ + Initialize the component type. + + Args: + component_info (ComponentInfo): The component information. + action (str): The action to be executed. + method (str): The provision method to be executed. + """ + if not action in self._actions: + raise ValueError(f"Unsupported action: {action}") + if not method in self._methods: + raise ValueError(f"Unsupported method: {method}") + if not "wazuh" in component_info.component and method.lower() == 'assistant': + raise ValueError(f"Assistant actions is only supported for Wazuh components.") + + # We cant uninstall from source. + if action == "uninstall" and method.lower() == "source": + logger.warning(f"Uninstall from source not supported. Using package.") + method = "package" + # Agent can not be installed from assistant. + if 'wazuh-agent' in component_info.component and method.lower() == "assistant": + logger.warning(f"Agent can not be installed from assistant. Using package.") + method = "package" + + self.action = action.lower() + self.method = method.lower() + self.component_info = ComponentInfo(**dict(component_info)) + self.templates_path = self._get_templates_path() + self.templates_order = self._get_templates_order() + self.variables_dict = self._generate_dict() + + def _get_templates_path(self) -> str: + """ + Get the path to the templates. + + Returns: + str: The path to the templates. + """ + # If the component is wazuh, we need to change the templates path. + if "wazuh" in self.component_info.component or self.method == "assistant": + self._base_templates_path = f'{self._base_templates_path}/wazuh' + + return f"{self._base_templates_path}/{self.method}/{self.action}" + + def _get_templates_order(self) -> list[str]: + """ + Get the order of the templates to be executed. + + Returns: + list[str]: List of templates to be executed. + """ + match self.method: + case 'package' if self.action == "install": + return ["set_repo.j2", "install.j2", "register.j2", "service.j2"] + case 'aio': + return ["download.j2", f"{self.action}.j2"] + case 'source': + # This will be kept as it could be used in the wazuh installation from sources. + component_file = f"{self.component_info.component}.j2" + if not Path(f"{self.templates_path}/{component_file}").exists(): + # The source installation is always component specific. + raise ValueError(f"Component source file {component_file} not found.") + return [component_file] + case _: + return [] + + def _generate_dict(self) -> dict: + """ + Generate the dictionary with the variables to be used to render the templates. + + Returns: + dict: The variables to be used to render the templates. + """ + variables = { + 'component': self.component_info.component, + 'version': self.component_info.version, + 'live': self.component_info.live, + 'type': self.component_info.type, + 'dependencies': self.component_info.dependencies or None, + 'templates_path': self.templates_path, + 'templates_order': self.templates_order or None + } + + return variables diff --git a/deployability/modules/provision/main.py b/deployability/modules/provision/main.py new file mode 100755 index 0000000000..9d9e43419a --- /dev/null +++ b/deployability/modules/provision/main.py @@ -0,0 +1,31 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +import argparse +import os +import sys + +project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')) +sys.path.append(project_root) + +from modules.provision import Provision, models + +# ---------------- Methods --------------------- + + +def parse_arguments(): + parser = argparse.ArgumentParser( + description="Provision infraestructure tool") + parser.add_argument("--inventory", default=None, help="Inventory with agent host information") + parser.add_argument("--dependencies", action='append',required=False, help="List of dictionaries with the dependencies inventories.") + parser.add_argument('--install', action='append', default=[], help='List of dictionaries for installation.') + parser.add_argument('--uninstall', action='append', default=[], help='List of dictionaries for uninstall.') + return parser.parse_args() + + +if __name__ == "__main__": + try: + provision = Provision(models.InputPayload(**vars(parse_arguments()))) + provision.run() + except Exception as e: + sys.exit(f"Error while provisioning: {e}") diff --git a/deployability/modules/provision/models.py b/deployability/modules/provision/models.py new file mode 100755 index 0000000000..e80e9b4271 --- /dev/null +++ b/deployability/modules/provision/models.py @@ -0,0 +1,73 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from pathlib import Path +from typing import List, Union +from pydantic import BaseModel, validator, model_validator + + +class ComponentInfo(BaseModel): + component: str + type: str = "package" + version: str = "" + dependencies: dict | None = None + live : bool = False + + +class InputPayload(BaseModel): + inventory: Path | None + install: List[ComponentInfo] | None + uninstall: List[ComponentInfo] | None + dependencies: dict | None = None + + @model_validator(mode="before") + def validations(cls, values): + """ + Validate the inventory. + + Args: + values: InputPayload model. + """ + + values.update(cls.validate_action(values)) + + return values + + @validator('dependencies', pre=True) + def validate_inventory(cls, v) -> dict | None: + """ + Validate inventory recived. + It expects a list or dict of dictionaries with the dependencies. + + Example: + list: [{'manager': 'path/to/inventory.yaml'}, {'agent': 'path/to/inventory.yaml'}] + dict: {'manager': 'path/to/inventory.yaml', 'agent': 'path/to/inventory.yaml'} + """ + if v is None: + return + if isinstance(v, list): + return {k: v for dep in v for k, v in eval(dep).items()} + if isinstance(v, str): + return {k: v for dep in eval(v) for k, v in dep.items()} + return v + + @classmethod + def validate_action(cls, values): + """ + Validate action. + """ + if not values.get('install') and not values.get('uninstall'): + raise ValueError( + 'Invalid action: "install" or "uninstall" must be provided.') + return values + + @validator('install', 'uninstall', pre=True) + def validate_install_uninstall(cls, components) -> Union[None, List[str]]: + if not components: + return + component_list = [] + for item in components: + component_info = ComponentInfo(**eval(item)) + component_list.append(component_info) + + return component_list diff --git a/deployability/modules/provision/playbooks/package/install/install.j2 b/deployability/modules/provision/playbooks/package/install/install.j2 new file mode 100644 index 0000000000..c383dfaf4e --- /dev/null +++ b/deployability/modules/provision/playbooks/package/install/install.j2 @@ -0,0 +1,27 @@ +{% if ansible_os_family.lower() in ['debian'] %} +- name: Update apt cache + shell: apt-get update + +- name: Install {{ component }} + shell: + apt-get install -y {{ component }} +{% endif %} + +{% if ansible_os_family.lower() in ['redhat'] %} +- name: Install EPEL repository + shell: | + yum update -y + yum install -y epel-release +{% endif %} + +{% if ansible_os_family.lower() in ['redhat', 'oraclelinux', 'amazon', 'centos'] %} +- name: Install {{ component }} + shell: yum install -y {{ component }} +{% endif %} + +{% if ansible_os_family.lower() in ['suse'] %} +- name: Update zypper cache + command: "zypper -n ref" +- name: Install the {{ component }} + command: "zypper -n install {{ component }}" +{% endif %} diff --git a/deployability/modules/provision/playbooks/package/uninstall/uninstall.j2 b/deployability/modules/provision/playbooks/package/uninstall/uninstall.j2 new file mode 100644 index 0000000000..aaa2934059 --- /dev/null +++ b/deployability/modules/provision/playbooks/package/uninstall/uninstall.j2 @@ -0,0 +1,14 @@ +{% if ansible_os_family.lower() in ['debian'] %} +- name: Uninstall {{ component }} + command: "apt-get remove -y {{ component }}" +{% endif %} + +{% if ansible_os_family.lower() in ['redhat', 'oraclelinux', 'amazon', 'centos'] %} +- name: Uninstall {{ component }} + command: "yum remove -y {{ component }}" +{% endif %} + +{% if ansible_os_family.lower() in ['suse'] %} +- name: Uninstall {{ component }} + command: "zypper remove -y {{ component }}" +{% endif %} diff --git a/deployability/modules/provision/playbooks/source/install/python.j2 b/deployability/modules/provision/playbooks/source/install/python.j2 new file mode 100644 index 0000000000..a6b7c5ab04 --- /dev/null +++ b/deployability/modules/provision/playbooks/source/install/python.j2 @@ -0,0 +1,76 @@ +- name: Update repositories + {% if ansible_os_family == 'debian' %} + shell: sudo apt-get update -y + {% endif %} + {% if ansible_os_family.lower() in ['redhat', 'centos', 'oraclelinux'] %} + shell: sudo yum update -y + {% endif %} + +- name: Upgrade packages + {% if ansible_os_family == 'debian' %} + shell: sudo apt-get upgrade -y + {% endif %} + {% if ansible_os_family.lower() in ['redhat', 'centos', 'oraclelinux'] %} + shell: sudo yum upgrade -y + {% endif %} + +- name: Install dependencies + {% if ansible_os_family == 'debian' %} + shell: | + sudo apt-get install build-essential -y + sudo apt-get install libreadline-dev -y + sudo apt-get install libncursesw5-dev -y + sudo apt-get install libssl-dev -y + sudo apt-get install libsqlite3-dev -y + sudo apt-get install libgdbm-dev -y + sudo apt-get install libc6-dev -y + sudo apt-get install libbz2-dev -y + sudo apt-get install libffi-dev -y + sudo apt-get install zlib1g-dev -y + {% endif %} + {% if ansible_os_family.lower() in ['redhat', 'centos', 'oraclelinux'] %} + shell: | + sudo yum install zlib-devel -y + sudo yum install gcc -y + sudo yum install openssl-devel -y + sudo yum install bzip2-devel -y + sudo yum install libffi-devel -y + + {% endif %} + +- name: Download and extract Python {{ version }} + get_url: + url: "https://www.python.org/ftp/python/{{ version }}/Python-{{ version }}.tgz" + dest: "/tmp/Python-{{ version }}.tgz" + mode: 0755 + register: download_python + +- name: Extract Python {{ version }} + unarchive: + src: "/tmp/Python-{{ version }}.tgz" + dest: "/tmp" + remote_src: yes + when: download_python.changed + +- name: Build Python {{ version }} + shell: | + cd /tmp/Python-{{ version }} + ./configure --enable-optimizations + args: + chdir: "/tmp/Python-{{ version }}" + when: download_python.changed + register: build_python + +- name: Install Python {{ version }} + shell: | + cd /tmp/Python-{{ version }} + sudo make + sudo make install + when: build_python.changed + register: install_python + +- name: Set default python to {{ version }} + shell: | + sudo update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python{{ ".".join(version.split(".")[:-1]) }} 1 + sudo update-alternatives --install /usr/bin/pip3 pip3 /usr/local/bin/pip{{ ".".join(version.split(".")[:-1]) }} 1 + when: install_python.changed diff --git a/deployability/modules/provision/playbooks/wazuh/assistant/install/download.j2 b/deployability/modules/provision/playbooks/wazuh/assistant/install/download.j2 new file mode 100755 index 0000000000..cb8615ca99 --- /dev/null +++ b/deployability/modules/provision/playbooks/wazuh/assistant/install/download.j2 @@ -0,0 +1,15 @@ + +- name: Install the required packages + shell: | + {% if ansible_os_family.lower() in ['debian'] %} + sudo apt-get update && apt-get -y install curl + {% endif %} + {% if ansible_os_family.lower() in ['redhat', 'oraclelinux', 'amazon', 'centos'] %} + yum -y install curl + {% endif %} + {% if ansible_os_family.lower() in ['suse'] %} + zypper -n install curl + {% endif %} + +- name: Download the Wazuh installation assistant + shell: "curl -sO https://{{ 'packages.wazuh.com' if live else 'packages-dev.wazuh.com' }}/{{ version.split('.')[0] }}.{{ version.split('.')[1] }}/wazuh-install.sh" \ No newline at end of file diff --git a/deployability/modules/provision/playbooks/wazuh/assistant/install/install.j2 b/deployability/modules/provision/playbooks/wazuh/assistant/install/install.j2 new file mode 100755 index 0000000000..fd6ee248db --- /dev/null +++ b/deployability/modules/provision/playbooks/wazuh/assistant/install/install.j2 @@ -0,0 +1,2 @@ +- name: Install {{ component }} with asistant + shell: "bash ./wazuh-install.sh -a -i" diff --git a/deployability/modules/provision/playbooks/wazuh/assistant/uninstall/download.j2 b/deployability/modules/provision/playbooks/wazuh/assistant/uninstall/download.j2 new file mode 100755 index 0000000000..cb8615ca99 --- /dev/null +++ b/deployability/modules/provision/playbooks/wazuh/assistant/uninstall/download.j2 @@ -0,0 +1,15 @@ + +- name: Install the required packages + shell: | + {% if ansible_os_family.lower() in ['debian'] %} + sudo apt-get update && apt-get -y install curl + {% endif %} + {% if ansible_os_family.lower() in ['redhat', 'oraclelinux', 'amazon', 'centos'] %} + yum -y install curl + {% endif %} + {% if ansible_os_family.lower() in ['suse'] %} + zypper -n install curl + {% endif %} + +- name: Download the Wazuh installation assistant + shell: "curl -sO https://{{ 'packages.wazuh.com' if live else 'packages-dev.wazuh.com' }}/{{ version.split('.')[0] }}.{{ version.split('.')[1] }}/wazuh-install.sh" \ No newline at end of file diff --git a/deployability/modules/provision/playbooks/wazuh/assistant/uninstall/uninstall.j2 b/deployability/modules/provision/playbooks/wazuh/assistant/uninstall/uninstall.j2 new file mode 100755 index 0000000000..12180fed90 --- /dev/null +++ b/deployability/modules/provision/playbooks/wazuh/assistant/uninstall/uninstall.j2 @@ -0,0 +1,2 @@ +- name: Uninstall {{ component }} with asistant + shell: "bash ./wazuh-install.sh --uninstall" diff --git a/deployability/modules/provision/playbooks/wazuh/package/install/install.j2 b/deployability/modules/provision/playbooks/wazuh/package/install/install.j2 new file mode 100755 index 0000000000..8ec70d411c --- /dev/null +++ b/deployability/modules/provision/playbooks/wazuh/package/install/install.j2 @@ -0,0 +1,14 @@ +{% if ansible_os_family.lower() in ['debian'] %} +- name: Install the {{ component }} + command: "apt-get -y install {{ component }}" +{% endif %} + +{% if ansible_os_family.lower() in ['redhat', 'oraclelinux', 'amazon', 'centos'] %} +- name: Install the {{ component }} + command: "yum -y install {{ component }}" +{% endif %} + +{% if ansible_os_family.lower() in ['suse'] %} +- name: Install the {{ component }} + command: "zypper -n install {{ component }}" +{% endif %} diff --git a/deployability/modules/provision/playbooks/wazuh/package/install/register.j2 b/deployability/modules/provision/playbooks/wazuh/package/install/register.j2 new file mode 100755 index 0000000000..4fb1fe729f --- /dev/null +++ b/deployability/modules/provision/playbooks/wazuh/package/install/register.j2 @@ -0,0 +1,7 @@ +{% if component == 'wazuh-agent' %} +- name: Modify Wazuh manager IP in Wazuh agent + replace: + path: "/var/ossec/etc/ossec.conf" + regexp: "MANAGER_IP" + replace: {{ dependencies['manager'] }} +{% endif %} diff --git a/deployability/modules/provision/playbooks/wazuh/package/install/service.j2 b/deployability/modules/provision/playbooks/wazuh/package/install/service.j2 new file mode 100755 index 0000000000..3c233c8358 --- /dev/null +++ b/deployability/modules/provision/playbooks/wazuh/package/install/service.j2 @@ -0,0 +1,5 @@ +- name: Enable {{ component }} service + shell: "systemctl daemon-reload && systemctl enable {{ component }}" + +- name: Start {{ component }} service + shell: "systemctl start {{ component }}" diff --git a/deployability/modules/provision/playbooks/wazuh/package/install/set_repo.j2 b/deployability/modules/provision/playbooks/wazuh/package/install/set_repo.j2 new file mode 100755 index 0000000000..3f9e6de437 --- /dev/null +++ b/deployability/modules/provision/playbooks/wazuh/package/install/set_repo.j2 @@ -0,0 +1,37 @@ + +{% if ansible_os_family.lower() in ['debian'] %} + +- name: Update packages cache + command: apt-get update -y + +- name: Install packages + command: apt-get -y install curl gnupg apt-transport-https + +- name: Install GPG key + shell: "curl -s https://{{ 'packages.wazuh.com' if live else 'packages-dev.wazuh.com' }}/key/GPG-KEY-WAZUH | gpg --no-default-keyring --keyring gnupg-ring:/usr/share/keyrings/wazuh.gpg --import && chmod 644 /usr/share/keyrings/wazuh.gpg" + +- name: Add Wazuh repository + shell: "echo \"deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://{{ 'packages.wazuh.com' if live else 'packages-dev.wazuh.com' }}/{{ '4.x' if live else 'pre-release' }}/apt/ {{ 'stable' if live else 'unstable' }} main\" | tee -a /etc/apt/sources.list.d/wazuh.list" + +- name: Update package information + command: "apt-get update" +{% endif %} + +{% if ansible_os_family.lower() in ['redhat', 'oraclelinux', 'amazon', 'centos'] %} +- name: Update packages cache + command: yum makecache + +- name: Import the GPG key + command: "rpm --import https://{{ 'packages.wazuh.com' if live else 'packages-dev.wazuh.com' }}/key/GPG-KEY-WAZUH" + +- name: Add Wazuh repository + shell: "echo -e '[wazuh]\ngpgcheck=1\ngpgkey=https://{{ 'packages.wazuh.com' if live else 'packages-dev.wazuh.com' }}/key/GPG-KEY-WAZUH\nenabled=1\nname=EL-$releasever - Wazuh\nbaseurl=https://{{ 'packages.wazuh.com' if live else 'packages-dev.wazuh.com' }}/{{ '4.x' if live else 'pre-release' }}/yum/\nprotect=1' | tee /etc/yum.repos.d/wazuh.repo" +{% endif %} + +{% if ansible_os_family.lower() in ['suse']%} +- name: Import the GPG key + command: "rpm --import https://{{ 'packages.wazuh.com' if live else 'packages-dev.wazuh.com' }}/key/GPG-KEY-WAZUH" +- name: Add Wazuh repository + shell: "echo -e '[wazuh]\ngpgcheck=1\ngpgkey=https://{{ 'packages.wazuh.com' if live else 'packages-dev.wazuh.com' }}/key/GPG-KEY-WAZUH\nenabled=1\nname=EL-$releasever - Wazuh\nbaseurl=https://{{ 'packages.wazuh.com' if live else 'packages-dev.wazuh.com' }}/{{ '4.x' if live else 'pre-release' }}/yum/\nprotect=1' | tee /etc/zypp/repos.d/wazuh.repo" + +{% endif %} diff --git a/deployability/modules/provision/playbooks/wazuh/package/uninstall/uninstall.j2 b/deployability/modules/provision/playbooks/wazuh/package/uninstall/uninstall.j2 new file mode 100755 index 0000000000..7d2f2466be --- /dev/null +++ b/deployability/modules/provision/playbooks/wazuh/package/uninstall/uninstall.j2 @@ -0,0 +1,15 @@ +{% if ansible_os_family.lower() in ['debian'] %} +- name: Uninstall {{ component }} + command: "apt-get remove --purge {{ component }} -y" +{% endif %} + +{% if ansible_os_family.lower() in ['redhat', 'oraclelinux', 'amazon', 'centos'] %} +- name: Uninstall {{ component }} + command: "yum -y remove {{ component }}" +{% endif %} + +{% if ansible_os_family.lower() in ['suse'] %} +- name: Uninstall {{ component }} + command: "zypper -y remove {{ component }}" +{% endif %} + diff --git a/deployability/modules/provision/provision.py b/deployability/modules/provision/provision.py new file mode 100755 index 0000000000..8019a3c454 --- /dev/null +++ b/deployability/modules/provision/provision.py @@ -0,0 +1,151 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from pathlib import Path + +from modules.generic.utils import Utils +from modules.provision.actions import Action +from modules.provision.utils import logger +from modules.provision.models import InputPayload, ComponentInfo + + +PATH_BASE_DIR = Path(__file__).parents[2] + + +class Provision: + """ + Provision class to install and uninstall components. + + Attributes: + components (list[ComponentInfo]): List of components to install or uninstall. + ansible_data (dict): Ansible data to render the playbooks. + summary (dict): Summary of the provision. + """ + + def __init__(self, payload: InputPayload): + """ + Initialize the provision. + + Args: + payload (InputPayload): Payload with the provision information. + """ + self.summary = {} + + self.action = 'install' if payload.install else 'uninstall' + self.components = self.get_components(payload) + self.ansible_data = self.__load_ansible_data(payload.inventory) + + def run(self) -> None: + """ + Run the provision. + """ + logger.info(f'Initiating provisionment.') + + logger.debug(f'Running action {self.action} for components: {self.components}') + for component in self.components: + try: + logger.info(f'Provisioning "{component.component}"...') + self.__provision(component) + logger.info(f'Provision of "{component.component}" complete successfully.') + except Exception as e: + logger.error(f'Error while provisioning "{component.component}": {e}') + raise + logger.info('All components provisioned successfully.') + logger.debug(f'Provision summary: {self.summary}') + + def get_components(self, payload: InputPayload) -> list[ComponentInfo]: + """ + Validate the component and adds its dependency IP if required. + + Args: + payload (InputPayload): Payload with the provision information. + + Returns: + list[ComponentInfo]: List of components with the dependency IP. + """ + components = payload.install or payload.uninstall + dependencies = self.__get_deps_ips(payload.dependencies) + # Check each component and add the dependency IP if required + for component in components: + component.dependencies = dependencies + self.__validate_component_deps(component) + return components + + def update_status(self, status: dict) -> None: + """ + Update the status of the provision. + + Args: + status (dict): The status of the executed action. + """ + self.summary.update(status.stats) + + def __provision(self, component: ComponentInfo) -> None: + """ + Provision the components. + + Args: + component (ComponentInfo): Component to provision. + """ + action = Action(self.action, component, self.ansible_data) + status = action.execute() + self.update_status(status) + + def __load_ansible_data(self, inventory: str | Path) -> dict: + """ + Load the ansible data from the inventory file. + + Args: + inventory (str | Path): Path to the inventory file. + + Returns: + dict: Ansible data to render the playbooks. + """ + try: + return Utils.load_from_yaml(inventory) + except FileNotFoundError: + logger.error(f'Inventory file "{inventory}" not found.') + raise + except Exception as e: + logger.error(f'Error loading inventory file "{inventory}": {e}') + raise + + def __get_deps_ips(self, dependencies: dict) -> dict | None: + """ + Get the list of dependencies IPs from reading each dependency`s + inventory file and returning its ansible_host as IP. + + Args: + dependencies (list[dict]): List of dependencies. + + Returns: + dict: Dictionary with the dependencies IPs. + """ + if not dependencies: + return + dependencies_ips = {} + for key, value in dependencies.items(): + try: + inventory = Path(value) + if not inventory.exists(): + raise FileNotFoundError(f'Inventory file "{inventory}" not found.') + dep_ip = Utils.load_from_yaml(inventory, specific_key='ansible_host') + dependencies_ips[key] = dep_ip + except Exception as e: + logger.error(f'Error getting dependency IP: {e}') + raise + return dependencies_ips + + def __validate_component_deps(self, component: ComponentInfo) -> None: + """ + Validate the component dependencies. + + Args: + component (ComponentInfo): Component to validate. + """ + name = component.component + dependencies = component.dependencies or {} + # Dependencies validations. + if name == 'wazuh-agent' and not dependencies.get('manager'): + raise ValueError('Dependency IP is required to install Wazuh Agent.') + logger.debug(f"Setting dependencies: {dependencies} for {name} component.") diff --git a/deployability/modules/provision/utils.py b/deployability/modules/provision/utils.py new file mode 100644 index 0000000000..29102ca98e --- /dev/null +++ b/deployability/modules/provision/utils.py @@ -0,0 +1,6 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from modules.generic.logger import Logger + +logger = Logger("provisioner").get_logger() diff --git a/deployability/modules/setup.py b/deployability/modules/setup.py index a3bcf42aaf..ea439929af 100644 --- a/deployability/modules/setup.py +++ b/deployability/modules/setup.py @@ -1,31 +1,29 @@ - # Copyright (C) 2015, Wazuh Inc. # Created by Wazuh, Inc. . -# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 import json -from setuptools import setup, find_packages import os -from pathlib import Path + +from setuptools import setup + def get_files_from_directory(directory): paths = [] - base_path = Path(__file__) for (path, directories, filenames) in os.walk(directory): for filename in filenames: - if filename.endswith(('.yaml', '.json', '.md', '.py')): - paths.append(os.path.join(base_path, path, filename)) + paths.append(os.path.join('..', path, filename)) return paths -def get_version(): - abs_path = Path(__file__).parent.parent / "version.json" - if not os.path.exists(abs_path): - raise FileNotFoundError(f'File "{abs_path}" not found.') +def get_version(): + script_path = os.path.dirname(__file__) + rel_path = "../version.json" + abs_file_path = os.path.join(script_path, rel_path) + f = open(abs_file_path) + data = json.load(f) + version = data['version'] + return version - with open(abs_path, 'r') as abs_file: - data = json.load(abs_file) - version = data['version'] - return version or None package_data_list = get_files_from_directory("workflow_engine") scripts_list = ['engine=workflow_engine.__main__:main'] @@ -44,4 +42,4 @@ def get_version(): entry_points={'console_scripts': scripts_list}, include_package_data=True, zip_safe=False -) \ No newline at end of file +) diff --git a/deployability/modules/testing/README.MD b/deployability/modules/testing/README.MD new file mode 100644 index 0000000000..6821ad98e5 --- /dev/null +++ b/deployability/modules/testing/README.MD @@ -0,0 +1,294 @@ +## Testing Module + +### User documentation + +The test module runs tests on the different components of Wazuh. +It is designed so that you can perform installations, actions on components and uninstallations, performing validations at each step. +This module must receive allocated and provisioned infrastructure. (From Allocation and Provision modules) + +This module can be executed as follows: + A. Installing and using Workflow engine + B. Direct execution + + +#### A. Installing and using Workflow engine + +The execution of the workflow is done through the installation of its library. + +Initially, Python libraries must be installed. we recommended the use of virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. + +1. Create the python environment: + + ```bash + python3 -m venv {environment_name} + ``` + + +2. Activate the environment: + + ```bash + source {venv directory}/bin/activate + ``` + +3. Clone the `wazuh-qa` repository: + + Navigate to the project directory and switch to the project branch: + + ```bash + git clone https://github.com/wazuh/wazuh-qa.git + cd wazuh-qa + git checkout {project-branch} + ``` + +4. Install requirements: + + ```bash + pip3 install -r deployability/deps/requirements.txt + ``` + +5. Install the Workflow engine library and its launcher: + + While in wazuh-qa: + + ```bash + cd modules + pip3 uninstall -y workflow_engine && pip3 install . + ``` + + Run the module by doing the following steps: + +6. Test fixture to execute: + + It will be necessary to create a fixture (YAML file) where the infrastructure, provisioning, and tests to be executed will be declared. + + >Note: It is possible to find some fixture examples in '[deployability/modules/workflow_engine/examples/](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/workflow_engine/examples)' + + Example: + + ```bash + version: 0.1 + description: This workflow is used to test agents deployment por DDT1 PoC + variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: + - linux-redhat-8-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + + tasks: + # Generic agent test task + - task: "run-agent-tests" + description: "Run tests install for the agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-ubuntu-22.04-amd64/inventory.yaml" + - agent-1: "{working-dir}/agent-linux-redhat-8-amd64/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + - live: "True" + depends-on: + - "allocate-{agent}" + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent + ``` + + Following the schema of the example: + + Configure the following parameters depending on your test case: + + ```yaml + variables/agent-os + variables/manager-os + infra-provider + working-dir + tasks + ``` + + Pay attention to the tasks: + + ```yaml + args + depends-on + ``` + + >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your test (allocation, provision, and test) + +7. Command execution (local): + + Execute the command by referencing the parameters required by the library (launcher). + + ```bash + python3 -m workflow_engine {.yaml fixture path} + ``` + + Example + + ```bash + python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml + ``` + +#### B. Direct execution + +To execute the testing module without installing the Workflow engine, it can be done by using the launcher ('[module/testing/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/main.py)'): + +1. Execution + + While in 'wazuh-qa/deployability' + + ```bash + python3 modules/testing/main.py --wazuh-revision '{{ wazuh_revision }}' --wazuh-version '{{ wazuh_version }}' --component {{ component }} --tests 'install,restart,stop,uninstall' --targets '{"wazuh-1":"{{ inventory }}"}' --targets '{"wazuh-2":"{{ inventory }}"}' --live 'True' + ``` + + Examples: + ```bash + python3 modules/testing/main.py --wazuh-revision '40714' --wazuh-version '4.7.3' --component 'manager' --tests 'install,restart,stop,uninstall' --targets '{"wazuh-1":"/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yml"}' --targets '{"wazuh-2":"/tmp/dtt1-poc/manager-linux-redhat-8-amd64/inventory.yml"}' --live 'True' + ``` + + ```bash + python3 modules/testing/main.py --wazuh-revision '40714' --wazuh-version '4.7.3' --component 'agent' --tests 'install,registration,restart,stop,uninstall' --targets '{"wazuh-1":"/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yml"}' --targets '{"agent-1":"/tmp/dtt1-poc/agent-linux-redhat-8-amd64/inventory.yml"}' --targets '{"agent-2":"/tmp/dtt1-poc/agent-linux-redhat-9-amd64/inventory.yml"}' --targets '{"agent-3":"/tmp/dtt1-poc/agent-linux-centos-7-amd64/inventory.yml"}' --live 'True' + ``` + + #### To be considered: + wazuh-1: This is the master node + wazuh-{number}: They are the workers + agent-{number}: They are the agents + + >If the manager component is tested, 'wazuh-' components must be entered in target. + If the agent component is tested, there must be a master and the rest must be 'agent-'s + +--- + +### Technical documentation + +The testing module allows the execution of tests on agents and central components. + +Instructions can be received from the fixture and executed through the Workflow Engine or run through commands on an already provisioned infrastructure. + +In both cases, the following information will be required: + +```bash + - task: "run-agent-tests" + description: "Run tests install for the agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-ubuntu-22.04-amd64/inventory.yaml" + - agent-1: "{working-dir}/agent-linux-redhat-8-amd64/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + depends-on: + - "allocate-{agent}" + - "allocate-manager" +``` +In the exposed fixture fragment, the execution of the testing module launcher (`testing/main.py`), it is necessary to provide the inventory, dependencies, component, tests to execute, Wazuh version, Wazuh revision, and whether the repository is live or not (if not, it will look for information in packages-dev pre-release). + +For manual execution, an example command would be: + + ```bash + python3 modules/testing/main.py --wazuh-revision '40714' --wazuh-version '4.7.3' --component 'agent' --tests 'install,registration,restart,stop,uninstall' --targets '{"wazuh-1":"/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yml"}' --targets '{"agent-1":"/tmp/dtt1-poc/agent-linux-redhat-8-amd64/inventory.yml"}' --live 'True' + ``` + +The module is composed of: + +- **Launcher** ('[/wazuh-qa/deployability/modules/testing/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/main.py)'): Entry point for the workflow or the user who wishes to execute a test. + +- **Parameter validator** ('[/wazuh-qa/deployability/modules/testing/models.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/models.py)'): Validator for parameters entered by the Workflow or the user. + +- **Module functions** ('[/wazuh-qa/deployability/modules/testing/testing.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/testing.py)'): Module-specific functions responsible for triggering the test. + +- **Executor** ('[/wazuh-qa/deployability/modules/provision/actions.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/actions.py)'): Executes the provision using Actions in the generic module. + +- **Playbooks** ('[/wazuh-qa/deployability/modules/tests/playbooks](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/tests/playbooks)'): Contains setup, execution, and post-test cleanup playbooks. + +- **Tests** ('[/wazuh-qa/deployability/modules/tests](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/tests/)'): Contains the test_agent and test_manager directories where the tests to be executed are located. + +- **Helpers** ('[/wazuh-qa/deployability/modules/tests/helpers](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/tests/helpers)'): Contains files that enable the execution of the test. + +- **Conftest** ('[/wazuh-qa/deployability/modules/tests/conftest.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/tests/conftest.py)'): Contains information that will be transferred from the parameter validator to the tests. + + +![image](https://github.com/wazuh/wazuh-qa/assets/125690423/58332378-489a-49da-a73a-4d87d6bed3bc) + + +The testing module receives the infrastructure generated and provisioned by the allocation and provision modules. The module has the ability to execute actions on the hosts as well as perform the necessary validation. + +[Testing.drawio.zip](https://github.com/wazuh/wazuh-qa/files/14372508/Testing.drawio.zip) + +The test module must recieve the infrastructure generated and provisioned by the allocation and provision modules. The module can execute actions on the hosts as well as perform the necessary validation. + +Testing of the manager component includes: +`install`, `restart`, `stop` and `uninstall` +Install should come at the beginning and uninstall at the end, other tests can change their order + +Testing of the agent component includes: +`install`, `registration`, `restart`, `stop` and `uninstall` +Install must come at the beginning followed by registration Uninstall must come at the end and the other tests can change their order + +### License + +WAZUH Copyright (C) 2015 Wazuh Inc. (License GPLv2) diff --git a/deployability/modules/testing/__init__.py b/deployability/modules/testing/__init__.py new file mode 100755 index 0000000000..8a799aac32 --- /dev/null +++ b/deployability/modules/testing/__init__.py @@ -0,0 +1,6 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from .models import InputPayload, ExtraVars +from .testing import Tester diff --git a/deployability/modules/testing/main.py b/deployability/modules/testing/main.py new file mode 100755 index 0000000000..3cf78f564f --- /dev/null +++ b/deployability/modules/testing/main.py @@ -0,0 +1,30 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import argparse +import os +import sys + +project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')) +sys.path.append(project_root) + +from modules.testing import Tester, InputPayload + + +def parse_arguments(): + parser = argparse.ArgumentParser(description="Wazuh testing tool") + parser.add_argument("--targets", action='append', default=[], required=True) + parser.add_argument("--tests", required=True) + parser.add_argument("--component", choices=['manager', 'agent'], required=True) + parser.add_argument("--dependencies", action='append', default=[], required=False) + parser.add_argument("--cleanup", required=False, default=True) + parser.add_argument("--wazuh-version", required=True) + parser.add_argument("--wazuh-revision", required=True) + parser.add_argument("--wazuh-branch", required=False) + parser.add_argument("--live", required=False) + + return parser.parse_args() + +if __name__ == "__main__": + Tester.run(InputPayload(**vars(parse_arguments()))) diff --git a/deployability/modules/testing/models.py b/deployability/modules/testing/models.py new file mode 100644 index 0000000000..f4e168efa8 --- /dev/null +++ b/deployability/modules/testing/models.py @@ -0,0 +1,32 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from typing import Literal + +from pydantic import BaseModel, field_validator + +class ExtraVars(BaseModel): + """Extra vars for testing module.""" + component: Literal['manager', 'agent'] + wazuh_version: str + wazuh_revision: str + wazuh_branch: str | None = None + working_dir: str = '/tmp/tests' + live: bool = True + +class InputPayload(ExtraVars): + """Input payload for testing module.""" + tests: list[str] + targets: list[str] + dependencies: list[str] | None = None + cleanup: bool = True + live: bool = True + + + @field_validator('tests', mode='before') + def validate_tests(cls, value) -> list[str]: + """Validate tests names.""" + if type(value) is str: + value = value.split(',') + + return value diff --git a/deployability/modules/testing/playbooks/cleanup.yml b/deployability/modules/testing/playbooks/cleanup.yml new file mode 100644 index 0000000000..d2a062097a --- /dev/null +++ b/deployability/modules/testing/playbooks/cleanup.yml @@ -0,0 +1,11 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +- hosts: all + become: true + tasks: + - name: Clean test directory + file: + path: "{{ working_dir }}" + state: absent diff --git a/deployability/modules/testing/playbooks/setup.yml b/deployability/modules/testing/playbooks/setup.yml new file mode 100644 index 0000000000..cd55ffb5f4 --- /dev/null +++ b/deployability/modules/testing/playbooks/setup.yml @@ -0,0 +1,12 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +- hosts: localhost + become: true + become_user: "{{ current_user }}" + tasks: + - name: Cleaning old key ssh-keygen registries + ansible.builtin.command: + cmd: "ssh-keygen -f /home/{{ current_user }}/.ssh/known_hosts -R '{{ item }}'" + loop: {{ hosts_ip }} diff --git a/deployability/modules/testing/playbooks/test.yml b/deployability/modules/testing/playbooks/test.yml new file mode 100644 index 0000000000..260e87bad5 --- /dev/null +++ b/deployability/modules/testing/playbooks/test.yml @@ -0,0 +1,12 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +- hosts: localhost + become: true + become_user: "{{ current_user }}" + tasks: + - name: Test {{ test }} for {{ component }} + command: "python3 -m pytest modules/testing/tests/test_{{component}}/test_{{ test }}.py -v --wazuh_version={{ wazuh_version }} --wazuh_revision={{ wazuh_revision }} --component={{ component }} --dependencies='{{ dependencies }}' --targets='{{ targets }}' --live={{ live }} -s" + args: + chdir: "{{ local_host_path }}" diff --git a/deployability/modules/testing/testing.py b/deployability/modules/testing/testing.py new file mode 100644 index 0000000000..4a7cfbe648 --- /dev/null +++ b/deployability/modules/testing/testing.py @@ -0,0 +1,131 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import json +import os + +from modules.generic import Ansible, Inventory +from modules.generic.utils import Utils +from pathlib import Path +from .models import InputPayload, ExtraVars +from .utils import logger + + +class Tester: + _playbooks_dir = Path(__file__).parent / 'playbooks' + _setup_playbook = _playbooks_dir / 'setup.yml' + _cleanup_playbook = _playbooks_dir / 'cleanup.yml' + _test_template = _playbooks_dir / 'test.yml' + + @classmethod + def run(cls, payload: InputPayload) -> None: + """ + Run the tests based on the payload. + + Args: + payload (InputPayload): The payload containing the test parameters. + """ + payload = InputPayload(**dict(payload)) + extra_vars = cls._get_extra_vars(payload).model_dump() + + targets = {} + dependencies = {} + extra_vars['hosts_ip'] = [] + + # Process targets and dependencies + for path_type, paths_list in [("targets", payload.targets), ("dependencies", payload.dependencies)]: + for path in paths_list: + dictionary = eval(path) + inventory = Inventory(**Utils.load_from_yaml(', '.join(dictionary.values()))) + extra_vars['hosts_ip'].extend([inventory.ansible_host] if path_type == "targets" else []) + logger.info(f"Running tests for {(inventory.ansible_host)}") if path_type == "targets" else logger.info(f"Dependencies {inventory.ansible_host}") + if path_type == "targets": + targets.update(dictionary) + else: + dependencies.update(dictionary) + + extra_vars['targets'] = json.dumps(targets).replace('"', "") + extra_vars['dependencies'] = json.dumps(dependencies).replace('"', "") + + # Set extra vars + extra_vars['local_host_path'] = str(Path(__file__).parent.parent.parent) + extra_vars['current_user'] = os.getlogin() + + logger.debug(f"Using extra vars: {extra_vars}") + + # Setup and run tests + target_inventory = Inventory(**Utils.load_from_yaml(str(list(eval(payload.targets[0]).values())[0]))) + ansible = Ansible(ansible_data=target_inventory.model_dump()) + cls._setup(ansible, extra_vars) + cls._run_tests(payload.tests, ansible, extra_vars) + + # Clean up if required + if payload.cleanup: + for target_path in payload.targets: + target_value = eval(target_path).values() + target_inventory = Inventory(**Utils.load_from_yaml(str(list(target_value)[0]))) + logger.info("Cleaning up") + cls._cleanup(ansible, extra_vars['working_dir']) + + @classmethod + def _get_extra_vars(cls, payload: InputPayload) -> ExtraVars: + """ + Get the extra vars for the tests. + + Args: + payload (InputPayload): The payload containing the test parameters. + + Returns: + ExtraVars: The extra vars for the tests. + """ + + return ExtraVars(**payload.model_dump()) + + @classmethod + def _run_tests(cls, test_list: list[str], ansible: Ansible, extra_vars: ExtraVars) -> None: + """ + Execute the playbooks that runs the tests. + + Args: + test_list (list[str]): The list of tests to run. + ansible (Ansible): The Ansible object to run the tests. + extra_vars (ExtraVars): The extra vars for the tests. + """ + for test in test_list: + rendering_var = {**extra_vars, 'test': test} + template = str(cls._test_template) + playbook = ansible.render_playbook(template, rendering_var) + if not playbook: + logger.warning(f"Test {test} not found. Skipped.") + continue + ansible.run_playbook(playbook, extra_vars) + + + @classmethod + def _setup(cls, ansible: Ansible, extra_vars: ExtraVars) -> None: + """ + Setup the environment for the tests. + + Args: + ansible (Ansible): The Ansible object to run the setup. + extra_vars (str): The extra vars for the setup. + """ + rendering_var = {**extra_vars} + template = str(cls._setup_playbook) + playbook = ansible.render_playbook(template, rendering_var) + ansible.run_playbook(playbook, extra_vars) + + + @classmethod + def _cleanup(cls, ansible: Ansible, remote_working_dir: str = '/tmp') -> None: + """ + Cleanup the environment after the tests. + + Args: + ansible (Ansible): The Ansible object to run the cleanup. + remote_working_dir (str): The remote working directory. + """ + extra_vars = {'working_dir': remote_working_dir} + playbook = str(cls._cleanup_playbook) + ansible.run_playbook(playbook, extra_vars) diff --git a/deployability/modules/testing/tests/__init__.py b/deployability/modules/testing/tests/__init__.py new file mode 100755 index 0000000000..6e8bbe1a92 --- /dev/null +++ b/deployability/modules/testing/tests/__init__.py @@ -0,0 +1,6 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from ..testing import Tester +from ..models import InputPayload, ExtraVars diff --git a/deployability/modules/testing/tests/conftest.py b/deployability/modules/testing/tests/conftest.py new file mode 100755 index 0000000000..b6f73bfc6a --- /dev/null +++ b/deployability/modules/testing/tests/conftest.py @@ -0,0 +1,55 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest + +def pytest_addoption(parser): + parser.addoption('--wazuh_version', required=False, help='Wazuh version to test files.') + parser.addoption('--wazuh_revision', required=False, help='Wazuh revision to test.') + parser.addoption('--system', required=False, help='OS version where wazuh was installed.') + parser.addoption('--component', required=False, help='Component to be tested.') + parser.addoption('--dependencies', required=False, help='Dependency to be tested.') + parser.addoption('--targets', required=False, help='Targets to be tested.') + parser.addoption('--live', required=True, help='Packages repository.') + +@pytest.fixture(scope='session') +def wazuh_version(request): + + return request.config.getoption('wazuh_version') + + +@pytest.fixture(scope='session') +def wazuh_revision(request): + + return request.config.getoption('wazuh_revision') + + +@pytest.fixture(scope='session') +def system(request): + + return request.config.getoption('system') + + +@pytest.fixture(scope='session') +def component(request): + + return request.config.getoption('component') + + +@pytest.fixture(scope='session') +def dependencies(request) -> dict | None: + + return request.config.getoption('dependencies') + +@pytest.fixture(scope='session') +def targets(request) -> dict | None: + + return request.config.getoption('targets') + + +@pytest.fixture(scope='session') +def live(request) -> bool | None: + live_value = request.config.getoption('live') + + return live_value.lower() == 'true' if live_value else None diff --git a/deployability/modules/testing/tests/helpers/__init__.py b/deployability/modules/testing/tests/helpers/__init__.py new file mode 100755 index 0000000000..6249db074a --- /dev/null +++ b/deployability/modules/testing/tests/helpers/__init__.py @@ -0,0 +1,7 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from .agent import WazuhAgent +from .generic import HostConfiguration, HostInformation, HostMonitor, CheckFiles +from .manager import WazuhManager diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py new file mode 100644 index 0000000000..7e60c5fbca --- /dev/null +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -0,0 +1,416 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +import requests +import yaml + +from typing import List, Optional +from .constants import WAZUH_CONF, WAZUH_ROOT +from .executor import Executor, WazuhAPI +from .generic import HostInformation, CheckFiles +from .logger.logger import logger + +class WazuhAgent: + + @staticmethod + def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, live) -> None: + + if live == True: + s3_url = 'packages' + release = wazuh_version[0:3] + else: + s3_url = 'packages-dev' + release = 'pre-release' + + os_type = HostInformation.get_os_type(inventory_path) + commands = [] + if 'linux' in os_type: + distribution = HostInformation.get_linux_distribution(inventory_path) + architecture = HostInformation.get_architecture(inventory_path) + + if distribution == 'rpm' and 'x86_64' in architecture: + commands.extend([ + f"curl -o wazuh-agent-{wazuh_version}-1.x86_64.rpm https://{s3_url}.wazuh.com/{release}/yum/wazuh-agent-{wazuh_version}-1.x86_64.rpm && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' rpm -ihv wazuh-agent-{wazuh_version}-1.x86_64.rpm" + ]) + elif distribution == 'rpm' and 'aarch64' in architecture: + commands.extend([ + f"curl -o wazuh-agent-{wazuh_version}-1aarch64.rpm https://{s3_url}.wazuh.com/{release}/yum/wazuh-agent-{wazuh_version}-1.aarch64.rpm && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' rpm -ihv wazuh-agent-{wazuh_version}-1.aarch64.rpm" + ]) + elif distribution == 'deb' and 'x86_64' in architecture: + commands.extend([ + f"wget https://{s3_url}.wazuh.com/{release}/apt/pool/main/w/wazuh-agent/wazuh-agent_{wazuh_version}-1_amd64.deb && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' dpkg -i ./wazuh-agent_{wazuh_version}-1_amd64.deb" + ]) + elif distribution == 'deb' and 'aarch64' in architecture: + commands.extend([ + f"wget https://{s3_url}.wazuh.com/{release}/apt/pool/main/w/wazuh-agent/wazuh-agent_{wazuh_version}-1_arm64.deb && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' dpkg -i ./wazuh-agent_{wazuh_version}-1arm64.deb" + ]) + system_commands = [ + "systemctl daemon-reload", + "systemctl enable wazuh-agent", + "systemctl start wazuh-agent", + "systemctl status wazuh-agent" + ] + + commands.extend(system_commands) + elif 'windows' in os_type : + commands.extend([ + f"Invoke-WebRequest -Uri https://packages.wazuh.com/{release}/windows/wazuh-agent-{wazuh_version}-1.msi" + "-OutFile ${env.tmp}\wazuh-agent;" + "msiexec.exe /i ${env.tmp}\wazuh-agent /q" + f"WAZUH_MANAGER='MANAGER_IP'" + f"WAZUH_AGENT_NAME='{agent_name}'" + f"WAZUH_REGISTRATION_SERVER='MANAGER_IP'", + "NET START WazuhSvc", + "NET STATUS WazuhSvc" + ]) + elif 'macos' in os_type: + if 'intel' in architecture: + commands.extend([ + f'curl -so wazuh-agent.pkg https://{s3_url}.wazuh.com/{release}/macos/wazuh-agent-{wazuh_version}-1.intel64.pkg && echo "WAZUH_MANAGER=\'MANAGER_IP\' && WAZUH_AGENT_NAME=\'{agent_name}\'" > /tmp/wazuh_envs && sudo installer -pkg ./wazuh-agent.pkg -target /' + ]) + elif 'apple' in architecture: + commands.extend([ + f'curl -so wazuh-agent.pkg https://{s3_url}.wazuh.com/{release}/macos/wazuh-agent-{wazuh_version}-1.arm64.pkg && echo "WAZUH_MANAGER=\'MANAGER_IP\' && WAZUH_AGENT_NAME=\'{agent_name}\'" > /tmp/wazuh_envs && sudo installer -pkg ./wazuh-agent.pkg -target /' + ]) + system_commands = [ + '/Library/Ossec/bin/wazuh-control start', + '/Library/Ossec/bin/wazuh-control status' + ] + + commands.extend(system_commands) + logger.info(f'Installing Agent in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + Executor.execute_commands(inventory_path, commands) + + + @staticmethod + def install_agents(inventories_paths=[], wazuh_versions=[], wazuh_revisions=[], agent_names=[], live=[]) -> None: + for index, inventory_path in enumerate(inventories_paths): + WazuhAgent.install_agent(inventory_path, wazuh_versions[index], wazuh_revisions[index], agent_names[index], live[index]) + + + @staticmethod + def register_agent(inventory_path, manager_path): + + with open(manager_path, 'r') as yaml_file: + manager_path = yaml.safe_load(yaml_file) + host = manager_path.get('ansible_host') + + internal_ip = HostInformation.get_internal_ip_from_aws_dns(host) if 'amazonaws' in host else host + + commands = [ + f"sed -i 's/
MANAGER_IP<\/address>/
{internal_ip}<\/address>/g' {WAZUH_CONF}", + "systemctl restart wazuh-agent" + ] + + Executor.execute_commands(inventory_path, commands) + assert internal_ip in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the Manager IP ({internal_ip})in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + + + @staticmethod + def uninstall_agent(inventory_path, wazuh_version=None, wazuh_revision=None) -> None: + os_type = HostInformation.get_os_type(inventory_path) + commands = [] + if 'linux' in os_type: + distribution = HostInformation.get_linux_distribution(inventory_path) + os_name = HostInformation.get_os_name_from_inventory(inventory_path) + if os_name == 'opensuse' or os_name == 'suse': + commands.extend([ + "zypper remove --no-confirm wazuh-agent", + "rm -r /var/ossec" + ]) + else: + if distribution == 'deb': + commands.extend([ + "apt-get remove --purge wazuh-agent -y" + + ]) + elif distribution == 'rpm': + commands.extend([ + "yum remove wazuh-agent -y", + f"rm -rf {WAZUH_ROOT}" + ]) + + + system_commands = [ + "systemctl disable wazuh-agent", + "systemctl daemon-reload" + ] + + commands.extend(system_commands) + elif 'windows' in os_type: + commands.extend([ + f"msiexec.exe /x wazuh-agent-{wazuh_version}-1.msi /qn" + ]) + elif 'macos' in os_type: + commands.extend([ + "/Library/Ossec/bin/wazuh-control stop", + "/bin/rm -r /Library/Ossec", + "/bin/launchctl unload /Library/LaunchDaemons/com.wazuh.agent.plist", + "/bin/rm -f /Library/LaunchDaemons/com.wazuh.agent.plist", + "/bin/rm -rf /Library/StartupItems/WAZUH", + "/usr/bin/dscl . -delete '/Users/wazuh'", + "/usr/bin/dscl . -delete '/Groups/wazuh'", + "/usr/sbin/pkgutil --forget com.wazuh.pkg.wazuh-agent" + ]) + + logger.info(f'Uninstalling Agent in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + Executor.execute_commands(inventory_path, commands) + + + @staticmethod + def uninstall_agents( inventories_paths=[], wazuh_version: Optional[List[str]]=None, wazuh_revision: Optional[List[str]]=None) -> None: + for index, inventory_path in enumerate(inventories_paths): + WazuhAgent.uninstall_agent(inventory_path, wazuh_version[index], wazuh_revision[index]) + + + @staticmethod + def _install_agent_callback(wazuh_params, agent_name, agent_params): + WazuhAgent.install_agent(agent_params, agent_name, wazuh_params['wazuh_version'], wazuh_params['wazuh_revision'], wazuh_params['live']) + + + @staticmethod + def _uninstall_agent_callback(wazuh_params, agent_params): + WazuhAgent.uninstall_agent(agent_params, wazuh_params['wazuh_version'], wazuh_params['wazuh_revision']) + + + @staticmethod + def perform_action_and_scan(agent_params, action_callback) -> dict: + """ + Takes scans using filters, the callback action and compares the result + + Args: + agent_params (str): agent parameters + callbak (cb): callback (action) + + Returns: + result (dict): comparison brief + + """ + result = CheckFiles.perform_action_and_scan(agent_params, action_callback) + os_name = HostInformation.get_os_name_from_inventory(agent_params) + logger.info(f'Applying filters in checkfiles in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') + + if 'debian' in os_name: + filter_data = { + '/boot': {'added': [], 'removed': [], 'modified': ['grubenv']}, + '/usr/bin': { + 'added': [ + 'unattended-upgrade', 'gapplication', 'add-apt-repository', 'gpg-wks-server', 'pkexec', 'gpgsplit', + 'watchgnupg', 'pinentry-curses', 'gpg-zip', 'gsettings', 'gpg-agent', 'gresource', 'gdbus', + 'gpg-connect-agent', 'gpgconf', 'gpgparsemail', 'lspgpot', 'pkaction', 'pkttyagent', 'pkmon', + 'dirmngr', 'kbxutil', 'migrate-pubring-from-classic-gpg', 'gpgcompose', 'pkcheck', 'gpgsm', 'gio', + 'pkcon', 'gpgtar', 'dirmngr-client', 'gpg', 'filebeat', 'gawk', 'curl', 'update-mime-database', + 'dh_installxmlcatalogs', 'appstreamcli', 'lspgpot', 'symcryptrun' + ], + 'removed': [], + 'modified': [] + }, + '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, + '/usr/sbin': { + 'added': [ + 'update-catalog', 'applygnupgdefaults', 'addgnupghome', 'install-sgmlcatalog', 'update-xmlcatalog' + ], + 'removed': [], + 'modified': [] + } + } + else: + filter_data = { + '/boot': { + 'added': ['grub2', 'loader', 'vmlinuz', 'System.map', 'config-', 'initramfs'], + 'removed': [], + 'modified': ['grubenv'] + }, + '/usr/bin': {'added': ['filebeat'], 'removed': [], 'modified': []}, + '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, + '/usr/sbin': {'added': [], 'removed': [], 'modified': []} + } + + # Use of filters + for directory, changes in result.items(): + if directory in filter_data: + for change, files in changes.items(): + if change in filter_data[directory]: + result[directory][change] = [file for file in files if file.split('/')[-1] not in filter_data[directory][change]] + + return result + + @staticmethod + def perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) -> None: + """ + Coordinates the action of install the agent and compares the checkfiles + + Args: + agent_params (str): agent parameters + wazuh_params (str): wazuh parameters + + """ + action_callback = lambda: WazuhAgent._install_agent_callback(wazuh_params, agent_name, agent_params) + result = WazuhAgent.perform_action_and_scan(agent_params, action_callback) + logger.info(f'Pre and post install checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}: {result}') + WazuhAgent.assert_results(result) + + + @staticmethod + def perform_uninstall_and_scan_for_agent(agent_params, wazuh_params) -> None: + """ + Coordinates the action of uninstall the agent and compares the checkfiles + + Args: + agent_params (str): agent parameters + wazuh_params (str): wazuh parameters + + """ + action_callback = lambda: WazuhAgent._uninstall_agent_callback(wazuh_params, agent_params) + result = WazuhAgent.perform_action_and_scan(agent_params, action_callback) + logger.info(f'Pre and post uninstall checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}: {result}') + WazuhAgent.assert_results(result) + + + @staticmethod + def assert_results(result) -> None: + """ + Gets the status of an agent given its name. + + Args: + result (dict): result of comparison between pre and post action scan + + """ + categories = ['/root', '/usr/bin', '/usr/sbin', '/boot'] + actions = ['added', 'modified', 'removed'] + # Testing the results + for category in categories: + for action in actions: + assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category}{action}') + + + def get_agents_information(wazuh_api: WazuhAPI) -> list: + """ + Get information about agents. + + Returns: + List: Information about agents. + """ + response = requests.get(f"{wazuh_api.api_url}/agents", headers=wazuh_api.headers, verify=False) + + try: + return eval(response.text)['data']['affected_items'] + except Exception as e: + logger.error(f"Unexpected error: {e}") + return f"Unexpected error: {e}" + + + def get_agent_status(wazuh_api: WazuhAPI, agent_name) -> str: + """ + Function to get the status of an agent given its name. + + Args: + - agents_data (list): List of dictionaries containing agents' data. + - agent_name (str): Name of the agent whose status is to be obtained. + + Returns: + - str: Status of the agent if found in the data, otherwise returns None. + """ + response = requests.get(f"{wazuh_api.api_url}/agents", headers=wazuh_api.headers, verify=False) + for agent in eval(response.text)['data']['affected_items']: + if agent.get('name') == agent_name: + return agent.get('status') + + return None + + + def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): + """ + Get IP status and name by ID. + + Args: + identifier (str): Agent ID. + + Returns: + List: IP, name, and status of the agent. + """ + try: + agents_information = wazuh_api.get_agents_information() + for element in agents_information: + if element['id'] == identifier: + return [element['ip'], element['name'], element['status']] + except Exception as e: + logger.error(f"Unexpected error: {e}") + return [None, None, None] + + + def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): + """ + Get IP status and name by ID. + + Args: + identifier (str): Agent ID. + + Returns: + List: IP, name, and status of the agent. + """ + try: + agents_information = wazuh_api.get_agents_information() + for element in agents_information: + if element['id'] == identifier: + return [element['ip'], element['name'], element['status']] + except Exception as e: + logger.error(f"Unexpected error: {e}") + return [None, None, None] + + +def add_agent_to_manager(wazuh_api: WazuhAPI, name, ip) -> str: + """ + Add an agent to the manager. + + Args: + wazuh_api (WazuhAPI): Instance of WazuhAPI class. + name (str): Name of the agent. + ip (str): IP address of the agent. + + Returns: + str: Response text. + """ + try: + response = requests.post(f"{wazuh_api.api_url}/agents", json={"name": name, "ip": ip}, headers=wazuh_api.headers, verify=False) + return response.text + except Exception as e: + logger.error(f"Unexpected error: {e}") + return f"Unexpected error: {e}" + + +def restart_agents(wazuh_api: WazuhAPI) -> str: + """ + Restart agents. + + Args: + wazuh_api (WazuhAPI): Instance of WazuhAPI class. + + Returns: + str: Response text. + """ + try: + response = requests.put(f"{wazuh_api.api_url}/agents/restart", headers=wazuh_api.headers, verify=False) + return response.text + except Exception as e: + logger.error(f"Unexpected error: {e}") + return f"Unexpected error: {e}" + + +def agent_status_report(wazuh_api: WazuhAPI) -> dict: + """ + Get agent status report. + + Args: + wazuh_api (WazuhAPI): Instance of WazuhAPI class. + + Returns: + Dict: Agent status report. + """ + try: + response = requests.get(f"{wazuh_api.api_url}/agents/summary/status", headers=wazuh_api.headers, verify=False) + return eval(response.text)['data'] + except Exception as e: + logger.error(f"Unexpected error: {e}") + return {} + diff --git a/deployability/modules/testing/tests/helpers/constants.py b/deployability/modules/testing/tests/helpers/constants.py new file mode 100755 index 0000000000..ea0c6a8098 --- /dev/null +++ b/deployability/modules/testing/tests/helpers/constants.py @@ -0,0 +1,75 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from pathlib import Path + + +# --- Paths --- +WAZUH_ROOT = Path("/var", "ossec") +# Configuration paths +CONFIGURATIONS_DIR = Path(WAZUH_ROOT, "etc") +WAZUH_CONF = Path(CONFIGURATIONS_DIR, "ossec.conf") +CLIENT_KEYS = Path(CONFIGURATIONS_DIR, "client.keys") +# Binaries paths +BINARIES_DIR = Path(WAZUH_ROOT, "bin") +WAZUH_CONTROL = Path(BINARIES_DIR, "wazuh-control") +AGENT_CONTROL = Path(BINARIES_DIR, "agent_control") +CLUSTER_CONTROL = Path(BINARIES_DIR, "cluster_control") +# Logs paths +LOGS_DIR = Path(WAZUH_ROOT, "logs") +WAZUH_LOG = Path(LOGS_DIR, "ossec.log") +ALERTS_DIR = Path(LOGS_DIR, "alerts") +ALERTS_JSON = Path(ALERTS_DIR, "alerts.json") +# Daemons running paths +DAEMONS_DIR = Path(WAZUH_ROOT, "var", "run") +AGENTD_STATE = Path(DAEMONS_DIR, "wazuh-agentd.state") + +# --- Users & Groups --- +WAZUH_USER = "wazuh" +WAZUH_GROUP = "wazuh" + +# --- Daemons --- +AGENTD = 'wazuh-agentd' +AGENTLESSD = 'wazuh-agentlessd' +ANALYSISDD = 'wazuh-analysisd' +APID = 'wazuh-apid' +CLUSTERD = 'wazuh-clusterd' +CSYSLOGD = 'wazuh-csyslogd' +EXECD = 'wazuh-execd' +INTEGRATORD = 'wazuh-integratord' +MAILD = 'wazuh-maild' +MODULESD = 'wazuh-modulesd' +MONITORD = 'wazuh-monitord' +LOGCOLLECTORD = 'wazuh-logcollector' +REMOTED = 'wazuh-remoted' +SYSCHECKD = 'wazuh-syscheckd' +WAZUH_DBD = 'wazuh-db' +# Daemons lists +AGENT_DAEMONS = [AGENTD, + EXECD, + MODULESD, + LOGCOLLECTORD, + SYSCHECKD] +MANAGER_DAEMONS = [AGENTLESSD, + ANALYSISDD, + APID, + CLUSTERD, + CSYSLOGD, + EXECD, + INTEGRATORD, + LOGCOLLECTORD, + MAILD, + MODULESD, + MONITORD, + REMOTED, + SYSCHECKD, + WAZUH_DBD] + +# --- Log messages --- +CONNECTION_SERVER = "New wazuh agent connected" +CONNECTION_AGENT = "Connected to the server" +KEY_REQ_AGENT = "Requesting a key from server" +KEY_REQ_SERVER = "Received request for a new agent" +RELEASING_RESOURCES = "Shutdown received. Releasing resources" +DELETING_RESPONSES = "Shutdown received. Deleting responses" +STARTED = 'INFO: Started' diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py new file mode 100644 index 0000000000..6d204d6ba8 --- /dev/null +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -0,0 +1,87 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import json +import requests +import subprocess +import urllib3 +import yaml + +from base64 import b64encode + + +class Executor: + + @staticmethod + def execute_command(inventory_path, command) -> str: + + with open(inventory_path, 'r') as yaml_file: + inventory_data = yaml.safe_load(yaml_file) + + host = inventory_data.get('ansible_host') + port = inventory_data.get('ansible_port') + private_key_path = inventory_data.get('ansible_ssh_private_key_file') + username = inventory_data.get('ansible_user') + + ssh_command = [ + "ssh", + "-i", private_key_path, + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-p", str(port), + f"{username}@{host}", + "sudo", + command + ] + result = subprocess.run(ssh_command, stdout=subprocess.PIPE, text=True) + + return result.stdout + + + @staticmethod + def execute_commands(inventory_path, commands=[]) -> dict: + + results = {} + for command in commands: + results[command] = Executor.execute_command(inventory_path, command) + + return results + + +class WazuhAPI: + def __init__(self, inventory_path): + self.inventory_path = inventory_path + self.api_url = None + self.headers = None + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + self._authenticate() + + def _authenticate(self): + with open(self.inventory_path, 'r') as yaml_file: + inventory_data = yaml.safe_load(yaml_file) + + user = 'wazuh' + + #----Patch issue https://github.com/wazuh/wazuh-packages/issues/2883------------- + file_path = Executor.execute_command(self.inventory_path, 'pwd').replace("\n","") + '/wazuh-install-files/wazuh-passwords.txt' + if not 'true' in Executor.execute_command(self.inventory_path, f'test -f {file_path} && echo "true" || echo "false"'): + Executor.execute_command(self.inventory_path, 'tar -xvf wazuh-install-files.tar') + password = Executor.execute_command(self.inventory_path, "grep api_password wazuh-install-files/wazuh-passwords.txt | head -n 1 | awk '{print $NF}'").replace("'","").replace("\n","") + #-------------------------------------------------------------------------------- + + login_endpoint = 'security/user/authenticate' + host = inventory_data.get('ansible_host') + port = '55000' + login_url = f"https://{host}:{port}/{login_endpoint}" + basic_auth = f"{user}:{password}".encode() + login_headers = {'Content-Type': 'application/json', + 'Authorization': f'Basic {b64encode(basic_auth).decode()}'} + + token = json.loads(requests.post(login_url, headers=login_headers, verify=False).content.decode())['data']['token'] + + self.api_url = f'https://{host}:{port}' + self.headers = { + 'Content-Type': 'application/json', + 'Authorization': f'Bearer {token}' + } diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py new file mode 100644 index 0000000000..25308fd2cc --- /dev/null +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -0,0 +1,658 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import boto3 +import chardet +import os +import re +import socket +import subprocess +import time +import yaml + +from pathlib import Path +from .constants import WAZUH_CONTROL, CLIENT_KEYS +from .executor import Executor +from ..helpers.logger.logger import logger +from .utils import Utils + + +class HostInformation: + + @staticmethod + def dir_exists(inventory_path, dir_path) -> str: + """ + It returns the True of False depending if the directory exists + + Args: + inventory_path: host's inventory path + dir_path: path of the directory to be checked + + Returns: + bool: True or False + """ + return 'true' in Executor.execute_command(inventory_path, f'test -d {dir_path} && echo "true" || echo "false"') + + + @staticmethod + def file_exists(inventory_path, file_path) -> bool: + """ + It returns the True of False depending if the file exists + + Args: + inventory_path: host's inventory path + file_path: path of the file to be checked + + Returns: + bool: True or False + """ + return 'true' in Executor.execute_command(inventory_path, f'test -f {file_path} && echo "true" || echo "false"') + + + @staticmethod + def get_os_type(inventory_path) -> str: + """ + It returns the os_type of host + + Args: + inventory_path: host's inventory path + + Returns: + str: type of host (windows, linux, macos) + """ + system = Executor.execute_command(inventory_path, 'uname') + return system.lower() + + + @staticmethod + def get_architecture(inventory_path) -> str: + """ + It returns the arch of host + + Args: + inventory_path: host's inventory path + + Returns: + str: architecture (aarch64, x86_64, intel, apple) + """ + return Executor.execute_command(inventory_path, 'uname -m') + + + @staticmethod + def get_linux_distribution(inventory_path) -> str: + """ + It returns the linux distribution of host + + Args: + inventory_path: host's inventory path + + Returns: + str: linux distribution (deb, rpm) + """ + if 'manager' in inventory_path: + os_name = re.search(r'/manager-linux-([^-]+)-', inventory_path).group(1) + elif 'agent' in inventory_path: + os_name = re.search(r'/agent-linux-([^-]+)-', inventory_path).group(1) + + if os_name == 'ubuntu' or os_name == 'debian': + linux_distribution = 'deb' + else: + linux_distribution = 'rpm' + + return linux_distribution + + + @staticmethod + def get_os_name_from_inventory(inventory_path) -> str: + """ + It returns the linux os_name host inventory + + Args: + inventory_path: host's inventory path + + Returns: + str: linux os name (debian, ubuntu, opensuse, amazon, centos, redhat) + """ + if 'manager' in inventory_path: + os_name = re.search(r'/manager-linux-([^-]+)-', inventory_path).group(1) + elif 'agent' in inventory_path: + os_name = re.search(r'/agent-linux-([^-]+)-', inventory_path).group(1) + + return os_name + + @staticmethod + def get_os_name_and_version_from_inventory(inventory_path) -> tuple: + """ + It returns the linux os_name and version host inventory + + Args: + inventory_path: host's inventory path + + Returns: + tuple: linux os name and version (e.g., ('ubuntu', '22.04')) + """ + if 'manager' in inventory_path: + match = re.search(r'/manager-linux-([^-]+)-([^-]+)-', inventory_path) + elif 'agent' in inventory_path: + match = re.search(r'/agent-linux-([^-]+)-([^-]+)-', inventory_path) + if match: + os_name = match.group(1) + version = match.group(2) + return os_name+'-'+version + else: + return None + + @staticmethod + def get_current_dir(inventory_path) -> str: + """ + It returns the current directory + + Args: + inventory_path: host's inventory path + + Returns: + str: current directory + """ + + return Executor.execute_command(inventory_path, 'pwd').replace("\n","") + + @staticmethod + def get_internal_ip_from_aws_dns(dns_name): + """ + It returns the private AWS IP from dns_name + + Args: + dns_name (str): host's dns public dns name + + Returns: + str: private ip + """ + ec2 = boto3.client('ec2') + response = ec2.describe_instances(Filters=[{'Name': 'dns-name', 'Values': [dns_name]}]) + if response['Reservations']: + instance = response['Reservations'][0]['Instances'][0] + return instance['PrivateIpAddress'] + else: + return None + + @staticmethod + def get_client_keys(inventory_path) -> list[dict]: + """ + Get the client keys from the client.keys file in the host. + + Args: + inventory_path (str): host's inventory path + + Returns: + list: List of dictionaries with the client keys. + """ + clients = [] + client_key = Executor.execute_command(inventory_path, f'cat {CLIENT_KEYS}') + lines = client_key.split('\n')[:-1] + for line in lines: + _id, name, address, password = line.strip().split() + client_info = { + "id": _id, + "name": name, + "address": address, + "password": password + } + clients.append(client_info) + return clients + + +class HostConfiguration: + + @staticmethod + def sshd_config(inventory_path) -> None: + """ + Configures sshd_config file to connect using password + + Args: + inventory_path: host's inventory path + + """ + + commands = ["sudo sed -i '/^PasswordAuthentication/s/^/#/' /etc/ssh/sshd_config", "sudo sed -i '/^PermitRootLogin no/s/^/#/' /etc/ssh/sshd_config", 'echo -e "PasswordAuthentication yes\nPermitRootLogin yes" | sudo tee -a /etc/ssh/sshd_config', 'sudo systemctl restart sshd', 'cat /etc/ssh/sshd_config'] + Executor.execute_commands(inventory_path, commands) + + + @staticmethod + def disable_firewall(inventory_path) -> None: + """ + Disables firewall + + Args: + inventory_path: host's inventory path + + """ + commands = ["sudo systemctl stop firewalld", "sudo systemctl disable firewalld"] + if GeneralComponentActions.isComponentActive(inventory_path, 'firewalld'): + Executor.execute_commands(inventory_path, commands) + + logger.info(f'Firewall disabled on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + else: + logger.info(f'No Firewall to disable on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + + + + def _extract_hosts(paths, is_aws): + if is_aws: + return [HostInformation.get_internal_ip_from_aws_dns(Utils.extract_ansible_host(path)) for path in paths] + else: + return [Utils.extract_ansible_host(path) for path in paths] + + @staticmethod + def certs_create(wazuh_version, master_path, dashboard_path, indexer_paths=[], worker_paths=[]) -> None: + """ + Creates wazuh certificates + + Args: + wazuh_version (str): wazuh version + master_path (str): wazuh master inventory_path + dashboard_path (str): wazuh dashboard inventory_path + indexer_paths (list): wazuh indexers list + workers_paths (list): wazuh worker paths list + + """ + current_directory = HostInformation.get_current_dir(master_path) + + wazuh_version = '.'.join(wazuh_version.split('.')[:2]) + + is_aws = 'amazonaws' in Utils.extract_ansible_host(master_path) + + master = HostConfiguration._extract_hosts([master_path], is_aws)[0] + dashboard = HostConfiguration._extract_hosts([dashboard_path], is_aws)[0] + indexers = HostConfiguration._extract_hosts(indexer_paths, is_aws) + workers = HostConfiguration._extract_hosts(worker_paths, is_aws) + + ##Basic commands to setup the config file, add the ip for the master & dashboard + os_name = HostInformation.get_os_name_from_inventory(master_path) + if os_name == 'debian': + commands = [ + f'wget https://packages.wazuh.com/{wazuh_version}/wazuh-install.sh', + f'wget https://packages.wazuh.com/{wazuh_version}/config.yml', + f"sed -i '/^\s*#/d' {current_directory}/config.yml" + ] + else: + commands = [ + f'curl -sO https://packages.wazuh.com/{wazuh_version}/wazuh-install.sh', + f'curl -sO https://packages.wazuh.com/{wazuh_version}/config.yml', + f"sed -i '/^\s*#/d' {current_directory}/config.yml" + ] + + # Add master tag if there are workers + if len(worker_paths) != 0: + commands.append(f"""sed -i '/ip: ""/a\ node_type: master' {current_directory}/config.yml""") + + # Add manager and dashboard IP + commands.extend([ + f"sed -i '0,//s//{master}/' {current_directory}/config.yml", + f"sed -i '0,//s//{dashboard}/' {current_directory}/config.yml" + ]) + + # Adding workers + for index, element in reversed(list(enumerate(workers))): + commands.append(f'sed -i \'/node_type: master/a\\ - name: wazuh-{index+2}\\n ip: ""\\n node_type: worker\' {current_directory}/config.yml') + + # Add as much indexers as indexer_paths were presented + for index, element in enumerate(indexers, start=1): + commands.append(f'sed -i \'/ip: ""/a\\ - name: node-{index+1}\\n ip: ""\' {current_directory}/config.yml') + commands.append(f"""sed -i '0,//s//{element}/' {current_directory}/config.yml""") + + # Remove the last indexer due to previous existance of index-1 in the config + for index, element in enumerate(indexers): + if index == len(indexers) - 1: + commands.append(f'''sed -i '/- name: node-{index+2}/,/^ *ip: ""/d' {current_directory}/config.yml''') + + for index, element in enumerate(workers): + commands.append(f"""sed -i '0,//s//{element}/' {current_directory}/config.yml""") + + ## Adding workers and indexer Ips + certs_creation = [ + 'bash wazuh-install.sh --generate-config-files --ignore-check' + ] + + commands.extend(certs_creation) + + Executor.execute_commands(master_path, commands) + + current_from_directory = HostInformation.get_current_dir(master_path) + + assert HostInformation.file_exists(master_path, f'{current_from_directory}/wazuh-install-files.tar'), logger.error('wazuh-install-files.tar not created, check config.yml information') + + @staticmethod + def scp_to(from_inventory_path, to_inventory_path, file_name) -> None: + """ + Send via SCP from one host to another host + + Args: + from_inventory_path (str): host that owns the file to be sent path + to_inventory_path (str): host that recieves the file path + file_name (str): file name that will be send to home/{user} + + """ + current_from_directory = HostInformation.get_current_dir(from_inventory_path) + current_to_directory = HostInformation.get_current_dir(to_inventory_path) + with open(from_inventory_path, 'r') as yaml_file: + from_inventory_data = yaml.safe_load(yaml_file) + + with open(to_inventory_path, 'r') as yaml_file: + to_inventory_data = yaml.safe_load(yaml_file) + + # Defining variables + from_host = socket.gethostbyname(from_inventory_data.get('ansible_host')) + from_key = from_inventory_data.get('ansible_ssh_private_key_file') + from_user = from_inventory_data.get('ansible_user') + from_port = from_inventory_data.get('ansible_port') + + to_host = socket.gethostbyname(to_inventory_data.get('ansible_host')) + to_key = to_inventory_data.get('ansible_ssh_private_key_file') + to_user = to_inventory_data.get('ansible_user') + to_port = to_inventory_data.get('ansible_port') + + # Allowing handling permissions + if file_name == 'wazuh-install-files.tar': + Executor.execute_command(from_inventory_path, f'chmod +rw {file_name}') + logger.info('File permissions modified to be handled') + + # SCP + if HostInformation.file_exists(from_inventory_path, f'{current_from_directory}/{file_name}'): + subprocess.run(f'scp -i {from_key} -o StrictHostKeyChecking=no -P {from_port} {from_user}@{from_host}:{current_from_directory}/{file_name} {Path(__file__).parent}', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + logger.info(f'File copied from {HostInformation.get_os_name_and_version_from_inventory(from_inventory_path)} ({from_host}) to {Path(__file__).parent}/{file_name}') + else: + logger.error(f'File is not present in {HostInformation.get_os_name_and_version_from_inventory(from_inventory_path)} ({from_host}) in {current_from_directory}/{file_name}') + if os.path.exists(f'{Path(__file__).parent}/wazuh-install-files.tar'): + subprocess.run(f'scp -i {to_key} -o StrictHostKeyChecking=no -P {to_port} {Path(__file__).parent}/{file_name} {to_user}@{to_host}:{current_to_directory}', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + logger.info(f'Sending file from {current_from_directory}/{file_name} to {HostInformation.get_os_name_and_version_from_inventory(to_inventory_path)} ({to_host})') + else: + logger.error(f'Failure sending the file from {current_from_directory}/{file_name} to {HostInformation.get_os_name_and_version_from_inventory(to_inventory_path)} ({to_host})') + + # Restoring permissions + if file_name == 'wazuh-install-files.tar': + Executor.execute_command(from_inventory_path, f'chmod 600 {file_name}') + Executor.execute_command(to_inventory_path, f'chmod 600 {file_name}') + logger.info('File permissions were restablished') + + # Deleting file from localhost + file_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), file_name) + + if os.path.exists(file_path): + os.remove(file_path) + logger.info(f"The file {file_name} deleted in {Path(__file__).parent}") + else: + logger.error(f"The file {file_name} does not exist") + + assert HostInformation.file_exists(to_inventory_path, f'{current_to_directory}/{file_name}'), logger.error(f'Failure sending the file: {file_name} to {HostInformation.get_os_name_and_version_from_inventory(to_inventory_path)}') +class HostMonitor: + + @staticmethod + def get_file_encoding(file_path: str) -> str: + """Detect and return the file encoding. + + Args: + file_path (str): File path to check. + + Returns: + encoding (str): File encoding. + """ + with open(file_path, 'rb') as f: + data = f.read() + if len(data) == 0: + return 'utf-8' + result = chardet.detect(data) + return result['encoding'] + + + @staticmethod + def file_monitor(monitored_file: str, target_string: str, timeout: int = 30) -> None: + """ + Monitor a file for a specific string. + + Args: + monitored_file (str): The file to monitor. + target_string (str): The string to look for in the file. + timeout (int, optional): The time to wait for the string to appear in the file. Defaults to 30. + + Returns: + None: Returns None if the string is not found within the timeout. + str: Returns the line containing the target string if found within the timeout. + """ + encoding = HostMonitor.get_file_encoding(monitored_file) + + # Check in the current file content for the string. + with open(monitored_file, encoding=encoding) as _file: + for line in _file: + if target_string in line: + return line + + # Start count to set the timeout. + start_time = time.time() + + # Start the file monitoring for future lines. + with open(monitored_file, encoding=encoding) as _file: + # Go to the end of the file. + _file.seek(0, 2) + while time.time() - start_time < timeout: + current_position = _file.tell() + line = _file.readline() + + if not line: + # No new line, wait for nex try. + _file.seek(current_position) + time.sleep(0.1) + else: + # New line, check if the string matches. + if target_string in line: + return line + + +class CheckFiles: + + @staticmethod + def _checkfiles(inventory_path, os_type, directory, filter= None, hash_algorithm='sha256') -> dict: + """ + It captures a structure of a directory + Returns: + Dict: dict of directories:hash + """ + if 'linux' in os_type or 'macos' in os_type: + command = f'sudo find {directory} -type f -exec sha256sum {{}} + {filter}' + result = Executor.execute_command(inventory_path, command) + + elif 'windows' in os_type: + command = 'dir /a-d /b /s | findstr /v /c:"\\.$" /c:"\\..$"| find /c ":"' + else: + logger.info(f'Unsupported operating system') + return None + snapshot = {} + for line in result.splitlines(): + hash_value, file_path = line.split(maxsplit=1) + snapshot[file_path] = hash_value + + return snapshot + + + @staticmethod + def _perform_scan(inventory_path, os_type, directories, filters): + logger.info(f'Generating Snapshot for Checkfile in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + return {directory: CheckFiles._checkfiles(inventory_path, os_type, directory, filters) for directory in directories} + + + @staticmethod + def _calculate_changes(initial_scan, second_scan): + added_files = list(set(second_scan) - set(initial_scan)) + removed_files = list(set(initial_scan) - set(second_scan)) + modified_files = [path for path in set(initial_scan) & set(second_scan) if initial_scan[path] != second_scan[path]] + + return {'added': added_files, 'removed': removed_files, 'modified': modified_files} + + + @staticmethod + def perform_action_and_scan(inventory_path, callback) -> dict: + """ + Performs an action (callback) and scans pre and post action + + Args: + inventory_path: host's inventory path + callback (callback): callback + + + Returns: + returns a dictionary that contains the changes between the pre and the post scan + """ + os_type = HostInformation.get_os_type(inventory_path) + + directories = ['/boot', '/usr/bin', '/root', '/usr/sbin'] + filters_keywords = ['grep', 'tar', 'coreutils', 'sed', 'procps', 'gawk', 'lsof', 'curl', 'openssl', 'libcap', 'apt-transport-https', 'libcap2-bin', 'software-properties-common', 'gnupg', 'gpg'] + filters = f"| grep -v {filters_keywords[0]}" + + for filter_ in filters_keywords[1:]: + filters+= f" | grep -v {filter_}" + + initial_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters) + callback() + second_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters) + changes = {directory: CheckFiles._calculate_changes(initial_scans[directory], second_scans[directory]) for directory in directories} + + return changes + +class GeneralComponentActions: + + @staticmethod + def get_component_status(inventory_path, host_role) -> str: + """ + Return the host status + + Args: + inventory_path: host's inventory path + host_role: role of the component + + Returns: + str: Role status + """ + logger.info(f'Getting status of {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + + return Executor.execute_command(inventory_path, f'systemctl status {host_role}') + + + @staticmethod + def component_stop(inventory_path, host_role) -> None: + """ + Stops the component + + Args: + inventory_path: host's inventory path + host_role: role of the component + """ + + logger.info(f'Stopping {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + Executor.execute_command(inventory_path, f'systemctl stop {host_role}') + + + @staticmethod + def component_restart(inventory_path, host_role) -> None: + """ + Restarts the component + + Args: + inventory_path: host's inventory path + host_role: role of the component + """ + + logger.info(f'Restarting {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + Executor.execute_command(inventory_path, f'systemctl restart {host_role}') + + + @staticmethod + def component_start(inventory_path, host_role) -> None: + """ + Starts the component + + Args: + inventory_path: host's inventory path + host_role: role of the component + """ + + logger.info(f'Starting {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + Executor.execute_command(inventory_path, f'systemctl restart {host_role}') + + + @staticmethod + def get_component_version(inventory_path) -> str: + """ + Returns the installed component version + + Args: + inventory_path: host's inventory path + + Returns: + str: version + """ + return Executor.execute_command(inventory_path, f'{WAZUH_CONTROL} info -v') + + + @staticmethod + def get_component_revision(inventory_path) -> str: + """ + Returns the Agent revision number + + Args: + inventory_path: host's inventory path + + Returns: + str: revision number + """ + return Executor.execute_command(inventory_path, f'{WAZUH_CONTROL} info -r') + + + @staticmethod + def hasAgentClientKeys(inventory_path) -> bool: + """ + Returns the True of False depending if in the component Client.keys exists + + Args: + inventory_path: host's inventory path + + Returns: + bool: True/False + """ + return 'true' in Executor.execute_command(inventory_path, f'[ -f {CLIENT_KEYS} ] && echo true || echo false') + + + @staticmethod + def isComponentActive(inventory_path, host_role) -> bool: + """ + Returns the True of False depending if the component is Active + + Args: + inventory_path: host's inventory path + host_role: role of the component + + Returns: + bool: True/False + """ + return 'active' == Executor.execute_command(inventory_path, f'systemctl is-active {host_role}').replace("\n", "") + +class Waits: + + @staticmethod + def dynamic_wait(expected_condition_func, cycles=10, waiting_time=10) -> None: + """ + Waits the process during assigned cycles for the assigned seconds + + Args: + expected_condition_func (lambda function): The function that returns True when the expected condition is met + cycles(int): Number of cycles + waiting_Time(int): Number of seconds per cycle + + """ + for _ in range(cycles): + if expected_condition_func(): + break + else: + time.sleep(waiting_time) + else: + logger.error('Time out, Expected condition was not met') + raise RuntimeError(f'Time out') diff --git a/deployability/modules/testing/tests/helpers/logger/__init__.py b/deployability/modules/testing/tests/helpers/logger/__init__.py new file mode 100644 index 0000000000..ea0d8aeea6 --- /dev/null +++ b/deployability/modules/testing/tests/helpers/logger/__init__.py @@ -0,0 +1,3 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 diff --git a/deployability/modules/testing/tests/helpers/logger/config.yaml b/deployability/modules/testing/tests/helpers/logger/config.yaml new file mode 100644 index 0000000000..b1f9d1efd2 --- /dev/null +++ b/deployability/modules/testing/tests/helpers/logger/config.yaml @@ -0,0 +1,22 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +version: 1 +formatters: + simple: + format: '[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s' +handlers: + console: + class: logging.StreamHandler + level: DEBUG + formatter: simple + stream: ext://sys.stdout + file: + class: logging.FileHandler + level: DEBUG + formatter: simple + filename: /tmp/workflow.log +root: + level: DEBUG + handlers: [console, file] diff --git a/deployability/modules/testing/tests/helpers/logger/filter.py b/deployability/modules/testing/tests/helpers/logger/filter.py new file mode 100644 index 0000000000..bc7d101ce8 --- /dev/null +++ b/deployability/modules/testing/tests/helpers/logger/filter.py @@ -0,0 +1,23 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import logging +import threading + +class ThreadIDFilter(logging.Filter): + """ + A filter that uppercases the name of the log record. + """ + def filter(self, record: str) -> bool: + """ + Inject thread_id to log records. + + Args: + record (LogRecord): The log record to filter. + + Returns: + bool: True if the record should be logged, False otherwise. + """ + record.thread_id = threading.get_native_id() + return record diff --git a/deployability/modules/testing/tests/helpers/logger/logger.py b/deployability/modules/testing/tests/helpers/logger/logger.py new file mode 100644 index 0000000000..2e02766f06 --- /dev/null +++ b/deployability/modules/testing/tests/helpers/logger/logger.py @@ -0,0 +1,24 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import logging +import logging.config +from pathlib import Path +import threading + +import yaml + + +def _load_config() -> None: + """ + Loads the logging configuration from 'config.yaml' file. + """ + config_path = Path(__file__).parent / 'config.yaml' + with open(config_path, 'r') as f: + config = yaml.safe_load(f.read()) + logging.config.dictConfig(config) + +_load_config() + +logger = logging.getLogger("Testing") diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py new file mode 100644 index 0000000000..30aefd8442 --- /dev/null +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -0,0 +1,358 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import requests +import socket + +from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT +from .executor import Executor, WazuhAPI +from .generic import HostInformation, CheckFiles +from .logger.logger import logger +from .utils import Utils + + +class WazuhManager: + + @staticmethod + def install_manager(inventory_path, node_name, wazuh_version) -> None: + """ + Installs Wazuh Manager in the host + + Args: + inventory_path (str): host's inventory path + node_name (str): manager node name + wazuh_version (str): major.minor.patch + + """ + wazuh_version = '.'.join(wazuh_version.split('.')[:2]) + os_name = HostInformation.get_os_name_from_inventory(inventory_path) + + if os_name == 'debian': + commands = [ + f"wget https://packages.wazuh.com/{wazuh_version}/wazuh-install.sh", + f"bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" + ] + else: + commands = [ + f"curl -sO https://packages.wazuh.com/{wazuh_version}/wazuh-install.sh", + f"bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" + ] + logger.info(f'Installing Manager in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + Executor.execute_commands(inventory_path, commands) + + + @staticmethod + def install_managers(inventories_paths=[], node_names=[], wazuh_versions=[]) -> None: + """ + Install Wazuh Managers in the hosts + + Args: + inventories_paths (list): list of hosts' inventory path + node_name (list): managers node names' in the same order than inventories_paths + wazuh_version (list): manager versions int he same order than inventories_paths + + """ + for inventory in inventories_paths: + for node_name in node_names: + for wazuh_version in wazuh_versions: + WazuhManager.install_manager(inventory, node_name, wazuh_version) + + + @staticmethod + def uninstall_manager(inventory_path) -> None: + """ + Unnstall Wazuh Manager in the host + + Args: + inventory_paths (str): hosts' inventory path + """ + distribution = HostInformation.get_linux_distribution(inventory_path) + commands = [] + + if distribution == 'rpm': + commands.extend([ + "yum remove wazuh-manager -y", + f"rm -rf {WAZUH_ROOT}" + ]) + elif distribution == 'deb': + commands.extend([ + "apt-get remove --purge wazuh-manager -y" + ]) + + system_commands = [ + "systemctl disable wazuh-manager", + "systemctl daemon-reload" + ] + + commands.extend(system_commands) + + logger.info(f'Uninstalling Manager in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + Executor.execute_commands(inventory_path, commands) + + + @staticmethod + def uninstall_managers(inventories_paths=[]) -> None: + """ + Unnstall Wazuh Managers in the hosts + + Args: + inventories_paths (list): list of hosts' inventory path + """ + for inventory in inventories_paths: + WazuhManager.uninstall_manager(inventory) + + + @staticmethod + def _install_manager_callback(wazuh_params, manager_name, manager_params): + WazuhManager.install_manager(manager_params, manager_name, wazuh_params['wazuh_version']) + + + @staticmethod + def _uninstall_manager_callback(manager_params): + WazuhManager.uninstall_manager(manager_params) + + + @staticmethod + def perform_action_and_scan(manager_params, action_callback) -> dict: + """ + Takes scans using filters, the callback action and compares the result + + Args: + agent_params (str): agent parameters + callbak (cb): callback (action) + + Returns: + result (dict): comparison brief + + """ + result = CheckFiles.perform_action_and_scan(manager_params, action_callback) + os_name = HostInformation.get_os_name_from_inventory(manager_params) + logger.info(f'Applying filters in checkfiles in {HostInformation.get_os_name_and_version_from_inventory(manager_params)}') + + if 'debian' in os_name: + filter_data = { + '/boot': {'added': [], 'removed': [], 'modified': ['grubenv']}, + '/usr/bin': { + 'added': [ + 'unattended-upgrade', 'gapplication', 'add-apt-repository', 'gpg-wks-server', 'pkexec', 'gpgsplit', + 'watchgnupg', 'pinentry-curses', 'gpg-zip', 'gsettings', 'gpg-agent', 'gresource', 'gdbus', + 'gpg-connect-agent', 'gpgconf', 'gpgparsemail', 'lspgpot', 'pkaction', 'pkttyagent', 'pkmon', + 'dirmngr', 'kbxutil', 'migrate-pubring-from-classic-gpg', 'gpgcompose', 'pkcheck', 'gpgsm', 'gio', + 'pkcon', 'gpgtar', 'dirmngr-client', 'gpg', 'filebeat', 'gawk', 'curl', 'update-mime-database', + 'dh_installxmlcatalogs', 'appstreamcli', 'lspgpot', 'symcryptrun' + ], + 'removed': [], + 'modified': [] + }, + '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, + '/usr/sbin': { + 'added': [ + 'update-catalog', 'applygnupgdefaults', 'addgnupghome', 'install-sgmlcatalog', 'update-xmlcatalog' + ], + 'removed': [], + 'modified': [] + } + } + else: + filter_data = { + '/boot': { + 'added': ['grub2', 'loader', 'vmlinuz', 'System.map', 'config-', 'initramfs'], + 'removed': [], + 'modified': ['grubenv'] + }, + '/usr/bin': {'added': ['filebeat'], 'removed': [], 'modified': []}, + '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, + '/usr/sbin': {'added': [], 'removed': [], 'modified': []} + } + + # Use of filters + for directory, changes in result.items(): + if directory in filter_data: + for change, files in changes.items(): + if change in filter_data[directory]: + result[directory][change] = [file for file in files if file.split('/')[-1] not in filter_data[directory][change]] + + return result + + @staticmethod + def perform_install_and_scan_for_manager(manager_params, manager_name, wazuh_params) -> None: + """ + Coordinates the action of install the manager and compares the checkfiles + + Args: + manager_params (str): manager parameters + wazuh_params (str): wazuh parameters + + """ + action_callback = lambda: WazuhManager._install_manager_callback(wazuh_params, manager_name, manager_params) + result = WazuhManager.perform_action_and_scan(manager_params, action_callback) + logger.info(f'Pre and post install checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(manager_params)}: {result}') + WazuhManager.assert_results(result) + + + @staticmethod + def perform_uninstall_and_scan_for_manager(manager_params) -> None: + """ + Coordinates the action of uninstall the manager and compares the checkfiles + + Args: + manager_params (str): manager parameters + wazuh_params (str): wazuh parameters + + """ + action_callback = lambda: WazuhManager._uninstall_manager_callback(manager_params) + result = WazuhManager.perform_action_and_scan(manager_params, action_callback) + logger.info(f'Pre and post uninstall checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(manager_params)}: {result}') + WazuhManager.assert_results(result) + + + @staticmethod + def assert_results(result) -> None: + """ + Gets the status of an agent given its name. + + Args: + result (dict): result of comparison between pre and post action scan + + """ + categories = ['/root', '/usr/bin', '/usr/sbin', '/boot'] + actions = ['added', 'modified', 'removed'] + # Testing the results + for category in categories: + for action in actions: + assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category}{action}') + + + @staticmethod + def get_cluster_info(inventory_path) -> None: + """ + Returns the cluster information + + Args: + inventory_path: host's inventory path + + Returns: + str: Cluster status + """ + + return Executor.execute_command(inventory_path, f'{CLUSTER_CONTROL} -l') + + + @staticmethod + def get_agent_control_info(inventory_path) -> None: + """ + Returns the Agent information from the manager + + Args: + inventory_path: host's inventory path + + Returns: + str: Agents status + """ + + return Executor.execute_command(inventory_path, f'{AGENT_CONTROL} -l') + + + @staticmethod + def configuring_clusters(inventory_path, node_name, node_type, node_to_connect_inventory, key, disabled) -> None: + """ + Configures the cluster in ossec.conf + + Args: + inventory_path: host's inventory path + node_name: host's inventory path + node_type: master/worker + node_to_connect_inventory: inventory path of the node to connect + key: hexadecimal 16 key + disabled: yes/no + + """ + master_dns = Utils.extract_ansible_host(node_to_connect_inventory) + commands = [ + f"sed -i 's/node01<\/node_name>/{node_name}<\/node_name>/' {WAZUH_CONF}", + f"sed -i 's/master<\/node_type>/{node_type}<\/node_type>/' {WAZUH_CONF}", + f"sed -i 's/<\/key>/{key}<\/key>/' {WAZUH_CONF}", + f"sed -i 's/NODE_IP<\/node>/{HostInformation.get_internal_ip_from_aws_dns(master_dns)}<\/node>/' {WAZUH_CONF}", + f"sed -i 's/yes<\/disabled>/{disabled}<\/disabled>/' {WAZUH_CONF}", + "systemctl restart wazuh-manager" + ] + + Executor.execute_commands(inventory_path, commands) + if node_name in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'): + logger.info(f'Cluster configured in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + else: + logger.error(f'Error configuring cluster information in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + + + def get_manager_version(wazuh_api: WazuhAPI) -> str: + """ + Get the version of the manager. + + Returns: + str: The version of the manager. + """ + try: + response = requests.get(f"{wazuh_api.api_url}/?pretty=true", headers=wazuh_api.headers, verify=False) + return eval(response.text)['data']['api_version'] + except Exception as e: + logger.error(f"Unexpected error: {e}") + return f"Unexpected error: {e}" + + + def get_manager_revision(wazuh_api: WazuhAPI) -> str: + """ + Get the revision of the manager. + + Returns: + str: The revision of the manager. + """ + try: + response = requests.get(f"{wazuh_api.api_url}/?pretty=true", headers=wazuh_api.headers, verify=False) + return eval(response.text)['data']['revision'] + except Exception as e: + logger.error(f"Unexpected error: {e}") + return f"Unexpected error: {e}" + + def get_manager_host_name(wazuh_api: WazuhAPI) -> str: + """ + Get the hostname of the manager. + + Returns: + str: The hostname of the manager. + """ + try: + response = requests.get(f"{wazuh_api.api_url}/?pretty=true", headers=wazuh_api.headers, verify=False) + return eval(response.text)['data']['hostname'] + except Exception as e: + logger.error(f"Unexpected error: {e}") + return f"Unexpected error: {e}" + + + def get_manager_nodes_status(wazuh_api: WazuhAPI) -> dict: + """ + Get the status of the manager's nodes. + + Returns: + Dict: The status of the manager's nodes. + """ + try: + response = requests.get(f"{wazuh_api.api_url}/manager/status", headers=wazuh_api.headers, verify=False) + return eval(response.text)['data']['affected_items'][0] + except Exception as e: + logger.error(f"Unexpected error: {e}") + return f"Unexpected error: {e}" + + def get_manager_logs(wazuh_api: WazuhAPI) -> list: + """ + Get the logs of the manager. + + Returns: + List: The logs of the manager. + """ + try: + response = requests.get(f"{wazuh_api.api_url}/manager/logs", headers=wazuh_api.headers, verify=False) + return eval(response.text)['data']['affected_items'] + except Exception as e: + logger.error(f"Unexpected error: {e}") + return f"Unexpected error: {e}" diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py new file mode 100644 index 0000000000..211ee78b8f --- /dev/null +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -0,0 +1,69 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import paramiko +import re +import yaml +import logging +import time + +from .logger.logger import logger + + +paramiko_logger = logging.getLogger("paramiko") +paramiko_logger.setLevel(logging.CRITICAL) + + +class Utils: + + @staticmethod + def extract_ansible_host(file_path) -> str: + with open(file_path, 'r') as yaml_file: + inventory_data = yaml.safe_load(yaml_file) + return inventory_data.get('ansible_host') + + @staticmethod + def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: + if 'manager' in inventory_path: + match = re.search(r'/manager-linux-([^-]+)-([^-]+)-', inventory_path) + elif 'agent' in inventory_path: + match = re.search(r'/agent-linux-([^-]+)-([^-]+)-', inventory_path) + if match: + os_name = match.group(1)+ '-' + match.group(2) + logger.info(f'Checking connection to {os_name}') + try: + with open(inventory_path, 'r') as file: + inventory_data = yaml.safe_load(file) + except FileNotFoundError: + raise FileNotFoundError(logger.error(f'File not found in {os_name}')) + except yaml.YAMLError: + raise ValueError(logger.error(f'Invalid inventory information in {os_name}')) + + host = inventory_data.get('ansible_host') + port = inventory_data.get('ansible_port') + private_key_path = inventory_data.get('ansible_ssh_private_key_file') + username = inventory_data.get('ansible_user') + + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + private_key = paramiko.RSAKey.from_private_key_file(private_key_path) + + for attempt in range(1, attempts + 1): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + private_key = paramiko.RSAKey.from_private_key_file(private_key_path) + try: + ssh.connect(hostname=host, port=port, username=username, pkey=private_key) + logger.info(f'Connection established successfully in {os_name}') + ssh.close() + return True + except paramiko.AuthenticationException: + logger.error(f'Authentication error. Check SSH credentials in {os_name}') + return False + except Exception as e: + logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') + time.sleep(sleep) + + logger.error(f'Connection attempts failed after {attempts} tries. Connection timeout in {os_name}') + return False diff --git a/deployability/modules/testing/tests/test_agent/__init__.py b/deployability/modules/testing/tests/test_agent/__init__.py new file mode 100644 index 0000000000..c7072ea9ec --- /dev/null +++ b/deployability/modules/testing/tests/test_agent/__init__.py @@ -0,0 +1,7 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from ..helpers.agent import WazuhAgent +from ..helpers.manager import WazuhManager +from ..helpers.generic import HostConfiguration, CheckFiles, HostInformation, GeneralComponentActions diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py new file mode 100644 index 0000000000..72a454bbef --- /dev/null +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -0,0 +1,105 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest +import re + +from ..helpers.agent import WazuhAgent +from ..helpers.constants import WAZUH_ROOT +from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions +from ..helpers.logger.logger import logger +from ..helpers.manager import WazuhManager +from ..helpers.utils import Utils + + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + live = request.config.getoption('--live') + + return { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets, + 'live': live + } + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['agents'] = [value for key, value in targets_dict.items() if key.startswith('agent')] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + wazuh_params['agents'] = {key + '-' + re.findall(r'agent-(.*?)/', value)[0].replace('.',''): value for key, value in targets_dict.items() if key.startswith('agent')} + + updated_agents = {} + for agent_name, agent_params in wazuh_params['agents'].items(): + Utils.check_inventory_connection(agent_params) + if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if HostInformation.get_client_keys(agent_params) != []: + client_name = HostInformation.get_client_keys(agent_params)[0]['name'] + updated_agents[client_name] = agent_params + else: + updated_agents[agent_name] = agent_params + if updated_agents != {}: + wazuh_params['agents'] = updated_agents + +def test_installation(wazuh_params): + # Checking connection + for manager_name, manager_params in wazuh_params['managers'].items(): + Utils.check_inventory_connection(manager_params) + + # Certs creation, firewall management and Manager installation + for agent_name, agent_params in wazuh_params['agents'].items(): + HostConfiguration.disable_firewall(agent_params) + + if HostInformation.dir_exists(wazuh_params['master'], WAZUH_ROOT): + logger.info(f'Manager is already installed in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])}') + else: + HostConfiguration.disable_firewall(manager_params) + HostConfiguration.certs_create(wazuh_params['wazuh_version'], wazuh_params['master'], wazuh_params['dashboard'], wazuh_params['indexers'], wazuh_params['workers']) + WazuhManager.install_manager(wazuh_params['master'], 'wazuh-1', wazuh_params['wazuh_version']) + assert HostInformation.dir_exists(wazuh_params['master'], WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])}') + + # Agent installation + for agent_names, agent_params in wazuh_params['agents'].items(): + WazuhAgent.perform_install_and_scan_for_agent(agent_params, agent_names, wazuh_params) + + # Testing installation directory + for agent in wazuh_params['agents'].values(): + assert HostInformation.dir_exists(agent, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent)}') + + +def test_status(wazuh_params): + for agent in wazuh_params['agents'].values(): + agent_status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent') + assert 'loaded' in agent_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded') + + +def test_version(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert wazuh_params['wazuh_version'] in GeneralComponentActions.get_component_version(agent_params), logger.error(f"The version {HostInformation.get_os_name_and_version_from_inventory(agent_params)} is not {wazuh_params['wazuh_version']} by command") + + +def test_revision(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert wazuh_params['wazuh_revision'] in GeneralComponentActions.get_component_revision(agent_params), logger.error(f"The revision {HostInformation.get_os_name_and_version_from_inventory(agent_params)} is not {wazuh_params['wazuh_revision']} by command") diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py new file mode 100644 index 0000000000..3d15ea5e15 --- /dev/null +++ b/deployability/modules/testing/tests/test_agent/test_registration.py @@ -0,0 +1,93 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest +import re + +from ..helpers.agent import WazuhAgent, WazuhAPI +from ..helpers.generic import HostInformation, GeneralComponentActions, Waits +from ..helpers.manager import WazuhManager, WazuhAPI +from ..helpers.logger.logger import logger +from ..helpers.utils import Utils + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + live = request.config.getoption('--live') + + return { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets, + 'live': live + } + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['agents'] = [value for key, value in targets_dict.items() if key.startswith('agent')] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + wazuh_params['agents'] = {key + '-' + re.findall(r'agent-(.*?)/', value)[0].replace('.',''): value for key, value in targets_dict.items() if key.startswith('agent')} + + updated_agents = {} + for agent_name, agent_params in wazuh_params['agents'].items(): + Utils.check_inventory_connection(agent_params) + if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if HostInformation.get_client_keys(agent_params) != []: + client_name = HostInformation.get_client_keys(agent_params)[0]['name'] + updated_agents[client_name] = agent_params + else: + updated_agents[agent_name] = agent_params + if updated_agents != {}: + wazuh_params['agents'] = updated_agents + +def test_registration(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + WazuhAgent.register_agent(agent_params, wazuh_params['master']) + + +def test_status(wazuh_params): + for agent in wazuh_params['agents'].values(): + assert 'active' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') + + +def test_connection(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert agent_names in WazuhManager.get_agent_control_info(wazuh_params['master']), f'The {agent_names} is not present in the master by command' + wazuh_api = WazuhAPI(wazuh_params['master']) + assert any(d.get('name') == agent_names for d in WazuhAgent.get_agents_information(wazuh_api)), logger.error(f'The {agent_names} is not present in the master by API') + + +def test_isActive(wazuh_params): + wazuh_api = WazuhAPI(wazuh_params['master']) + for agent_names, agent_params in wazuh_params['agents'].items(): + assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by API') + + expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) + Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) + + +def test_clientKeys(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert GeneralComponentActions.hasAgentClientKeys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py new file mode 100644 index 0000000000..5d7b4b5081 --- /dev/null +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -0,0 +1,86 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest +import re + +from ..helpers.generic import GeneralComponentActions, HostInformation +from ..helpers.logger.logger import logger +from ..helpers.manager import WazuhManager +from ..helpers.utils import Utils + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + live = request.config.getoption('--live') + + return { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets, + 'live': live + } + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['agents'] = [value for key, value in targets_dict.items() if key.startswith('agent')] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + wazuh_params['agents'] = {key + '-' + re.findall(r'agent-(.*?)/', value)[0].replace('.',''): value for key, value in targets_dict.items() if key.startswith('agent')} + + updated_agents = {} + for agent_name, agent_params in wazuh_params['agents'].items(): + Utils.check_inventory_connection(agent_params) + if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if HostInformation.get_client_keys(agent_params) != []: + client_name = HostInformation.get_client_keys(agent_params)[0]['name'] + updated_agents[client_name] = agent_params + else: + updated_agents[agent_name] = agent_params + if updated_agents != {}: + wazuh_params['agents'] = updated_agents + +def test_restart(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + GeneralComponentActions.component_restart(agent_params, 'wazuh-agent') + + +def test_status(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert 'active' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') + + +def test_connection(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert agent_names in WazuhManager.get_agent_control_info(wazuh_params['master']), logger.error(f'{agent_names} is not present in agent_control information') + + +def test_isActive(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') + + +def test_clientKeys(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert GeneralComponentActions.hasAgentClientKeys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py new file mode 100644 index 0000000000..1463862510 --- /dev/null +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -0,0 +1,78 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest +import re + +from ..helpers.agent import WazuhAgent, WazuhAPI +from ..helpers.generic import GeneralComponentActions, Waits, HostInformation +from ..helpers.logger.logger import logger +from ..helpers.utils import Utils + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + live = request.config.getoption('--live') + + params = { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets, + 'live': live + } + + yield params + + for agent_names, agent_params in params['agents'].items(): + GeneralComponentActions.component_restart(agent_params, 'wazuh-agent') + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['agents'] = [value for key, value in targets_dict.items() if key.startswith('agent')] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + wazuh_params['agents'] = {key + '-' + re.findall(r'agent-(.*?)/', value)[0].replace('.',''): value for key, value in targets_dict.items() if key.startswith('agent')} + + updated_agents = {} + for agent_name, agent_params in wazuh_params['agents'].items(): + Utils.check_inventory_connection(agent_params) + if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if HostInformation.get_client_keys(agent_params) != []: + client_name = HostInformation.get_client_keys(agent_params)[0]['name'] + updated_agents[client_name] = agent_params + else: + updated_agents[agent_name] = agent_params + if updated_agents != {}: + wazuh_params['agents'] = updated_agents + +def test_stop(wazuh_params): + wazuh_api = WazuhAPI(wazuh_params['master']) + for agent_names, agent_params in wazuh_params['agents'].items(): + GeneralComponentActions.component_stop(agent_params, 'wazuh-agent') + + for agent_names, agent_params in wazuh_params['agents'].items(): + assert 'inactive' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is still active by command') + assert not GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is still active by command') + + expected_condition_func = lambda: 'disconnected' == WazuhAgent.get_agent_status(wazuh_api, agent_names) + Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py new file mode 100644 index 0000000000..a5d44ac154 --- /dev/null +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -0,0 +1,91 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest +import re + +from ..helpers.agent import WazuhAgent +from ..helpers.constants import WAZUH_ROOT +from ..helpers.generic import HostInformation, GeneralComponentActions, Waits +from ..helpers.manager import WazuhManager, WazuhAPI +from ..helpers.logger.logger import logger +from ..helpers.utils import Utils + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + live = request.config.getoption('--live') + + return { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets, + 'live': live + } + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['agents'] = [value for key, value in targets_dict.items() if key.startswith('agent')] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + wazuh_params['agents'] = {key + '-' + re.findall(r'agent-(.*?)/', value)[0].replace('.',''): value for key, value in targets_dict.items() if key.startswith('agent')} + + updated_agents = {} + for agent_name, agent_params in wazuh_params['agents'].items(): + Utils.check_inventory_connection(agent_params) + if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if HostInformation.get_client_keys(agent_params) != []: + client_name = HostInformation.get_client_keys(agent_params)[0]['name'] + updated_agents[client_name] = agent_params + else: + updated_agents[agent_name] = agent_params + if updated_agents != {}: + wazuh_params['agents'] = updated_agents + +def test_uninstall(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not Active before the installation') + assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in the host {agent_names}') + + # Agent installation + for agent_names, agent_params in wazuh_params['agents'].items(): + WazuhAgent.perform_uninstall_and_scan_for_agent(agent_params,wazuh_params) + + # Manager uninstallation status check + for agent_names, agent_params in wazuh_params['agents'].items(): + assert 'Disconnected' in WazuhManager.get_agent_control_info(wazuh_params['master']), logger.error(f'{agent_names} is still connected in the Manager') + + +def test_agent_uninstalled_directory(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert not HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is still present in the agent {agent_names}') + + +def test_isActive(wazuh_params): + wazuh_api = WazuhAPI(wazuh_params['master']) + for agent_names, agent_params in wazuh_params['agents'].items(): + assert not GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not inactive by command') + + expected_condition_func = lambda: 'disconnected' == WazuhAgent.get_agent_status(wazuh_api, agent_names) + Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) \ No newline at end of file diff --git a/deployability/modules/testing/tests/test_manager/__init__.py b/deployability/modules/testing/tests/test_manager/__init__.py new file mode 100644 index 0000000000..184c7ae281 --- /dev/null +++ b/deployability/modules/testing/tests/test_manager/__init__.py @@ -0,0 +1,6 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from ..helpers.generic import HostConfiguration, CheckFiles, HostInformation, GeneralComponentActions +from ..helpers.manager import WazuhManager diff --git a/deployability/modules/testing/tests/test_manager/test_install.py b/deployability/modules/testing/tests/test_manager/test_install.py new file mode 100644 index 0000000000..408d1dcd7e --- /dev/null +++ b/deployability/modules/testing/tests/test_manager/test_install.py @@ -0,0 +1,109 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest + +from ..helpers.constants import WAZUH_ROOT +from ..helpers.executor import WazuhAPI +from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions +from ..helpers.manager import WazuhManager +from ..helpers.logger.logger import logger +from ..helpers.utils import Utils + + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + + return { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets + } + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + +def test_installation(wazuh_params): + # Disabling firewall for all managers + for manager_name, manager_params in wazuh_params['managers'].items(): + Utils.check_inventory_connection(manager_params) + HostConfiguration.disable_firewall(manager_params) + + # Certs create and scp from master to worker + HostConfiguration.certs_create(wazuh_params['wazuh_version'], wazuh_params['master'], wazuh_params['dashboard'], wazuh_params['indexers'], wazuh_params['workers']) + + for workers in wazuh_params['workers']: + HostConfiguration.scp_to(wazuh_params['master'], workers, 'wazuh-install-files.tar') + + # Install managers and perform checkfile testing + for manager_name, manager_params in wazuh_params['managers'].items(): + WazuhManager.perform_install_and_scan_for_manager(manager_params, manager_name, wazuh_params) + + # Validation of activity and directory of the managers + for manager in wazuh_params['managers'].values(): + manager_status = GeneralComponentActions.get_component_status(manager, 'wazuh-manager') + assert 'active' in manager_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(manager)} is not active') + assert HostInformation.dir_exists(manager, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(manager)}') + + # Configuring cluster for all managers + hex16_code = 'eecda366dded9b32bcfbf3b057bf3ede' + for manager_name, manager_params in wazuh_params['managers'].items(): + node_type = 'master' if manager_name == 'wazuh-1' else 'worker' + WazuhManager.configuring_clusters(manager_params, manager_name, node_type, wazuh_params['master'], hex16_code, 'no') + + # Cluster info check + cluster_info = WazuhManager.get_cluster_info(wazuh_params['master']) + for manager_name, manager_params in wazuh_params['managers'].items(): + rest_of_managers = [k for k in wazuh_params['managers'].keys() if k != manager_name] + assert manager_name in cluster_info, logger.error(f'The cluster {manager_name} is not connected to {rest_of_managers}') + + +def test_manager_status(wazuh_params): + for manager in wazuh_params['managers'].values(): + manager_status = GeneralComponentActions.get_component_status(manager, 'wazuh-manager') + assert 'active' in manager_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(manager)} is not active') + + +def test_manager_version(wazuh_params): + for manager in wazuh_params['managers'].values(): + manager_status = GeneralComponentActions.get_component_version(manager) + assert wazuh_params['wazuh_version'] in manager_status, logger.error(f"The version {HostInformation.get_os_name_and_version_from_inventory(manager)} is not {wazuh_params['wazuh_version']} by using commands") + wazuh_api = WazuhAPI(wazuh_params['master']) + assert wazuh_params['wazuh_version'] in WazuhManager.get_manager_version(wazuh_api), logger.error(f"The version {HostInformation.get_os_name_and_version_from_inventory(manager)} is not {wazuh_params['wazuh_version']} in the API") + + +def test_manager_revision(wazuh_params): + for manager in wazuh_params['managers'].values(): + manager_status = GeneralComponentActions.get_component_revision(manager) + assert wazuh_params['wazuh_revision'] in manager_status, logger.error(f"The revision {HostInformation.get_os_name_and_version_from_inventory(manager)} is not {wazuh_params['wazuh_revision']} by using commands") + wazuh_api = WazuhAPI(wazuh_params['master']) + assert wazuh_params['wazuh_revision'] in str(WazuhManager.get_manager_revision(wazuh_api)), logger.error(f"The revision {HostInformation.get_os_name_and_version_from_inventory(manager)} is not {wazuh_params['wazuh_revision']} in the API") + + +def test_manager_installed_directory(wazuh_params): + for manager in wazuh_params['managers'].values(): + assert HostInformation.dir_exists(manager, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(manager)}') diff --git a/deployability/modules/testing/tests/test_manager/test_restart.py b/deployability/modules/testing/tests/test_manager/test_restart.py new file mode 100644 index 0000000000..96268071fb --- /dev/null +++ b/deployability/modules/testing/tests/test_manager/test_restart.py @@ -0,0 +1,53 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest + +from ..helpers.generic import HostInformation, GeneralComponentActions +from ..helpers.logger.logger import logger + + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + + return { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets + } + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + +def test_restart(wazuh_params): + for worker in wazuh_params['workers']: + GeneralComponentActions.component_restart(worker, 'wazuh-manager') + + assert 'active ' in GeneralComponentActions.get_component_status(wazuh_params['master'], 'wazuh-manager'), logger.error(f"The {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is not active") + + for worker in wazuh_params['workers']: + assert 'active ' in GeneralComponentActions.get_component_status(worker, 'wazuh-manager'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(worker)} is not active') diff --git a/deployability/modules/testing/tests/test_manager/test_stop.py b/deployability/modules/testing/tests/test_manager/test_stop.py new file mode 100644 index 0000000000..7630c40fdc --- /dev/null +++ b/deployability/modules/testing/tests/test_manager/test_stop.py @@ -0,0 +1,55 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest + +from ..helpers.generic import HostInformation, GeneralComponentActions +from ..helpers.logger.logger import logger + + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + + params = { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets + } + yield params + for worker in params['workers']: + GeneralComponentActions.component_restart(worker, 'wazuh-manager') + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + +def test_stop(wazuh_params): + for workers in wazuh_params['workers']: + GeneralComponentActions.component_stop(workers, 'wazuh-manager') + + assert 'active ' in GeneralComponentActions.get_component_status(wazuh_params['master'], 'wazuh-manager'), logger.error(f"The {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is not active") + for worker in wazuh_params['workers']: + assert 'inactive ' in GeneralComponentActions.get_component_status(workers, 'wazuh-manager'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(worker)} is not active') diff --git a/deployability/modules/testing/tests/test_manager/test_uninstall.py b/deployability/modules/testing/tests/test_manager/test_uninstall.py new file mode 100644 index 0000000000..b246d8012a --- /dev/null +++ b/deployability/modules/testing/tests/test_manager/test_uninstall.py @@ -0,0 +1,59 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest + +from ..helpers.constants import WAZUH_ROOT +from ..helpers.generic import HostInformation, GeneralComponentActions +from ..helpers.manager import WazuhManager +from ..helpers.logger.logger import logger + + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + + return { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets + } + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + + +def test_uninstall(wazuh_params): + for manager in wazuh_params['managers'].values(): + manager_status = GeneralComponentActions.get_component_status(manager, 'wazuh-manager') + assert 'active' in manager_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(manager)} is not active') + for manager_name, manager_params in wazuh_params['managers'].items(): + WazuhManager.perform_uninstall_and_scan_for_manager(manager_params) + + +def test_manager_uninstalled_directory(wazuh_params): + for manager in wazuh_params['managers'].values(): + assert not HostInformation.dir_exists(manager, WAZUH_ROOT), logger.error(f'In {HostInformation.get_os_name_and_version_from_inventory(manager)} {WAZUH_ROOT} is still present') diff --git a/deployability/modules/testing/utils.py b/deployability/modules/testing/utils.py new file mode 100644 index 0000000000..ba5e1e41ba --- /dev/null +++ b/deployability/modules/testing/utils.py @@ -0,0 +1,7 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from modules.generic.logger import Logger + +logger = Logger("tester").get_logger() diff --git a/deployability/modules/workflow_engine/README.md b/deployability/modules/workflow_engine/README.MD old mode 100755 new mode 100644 similarity index 97% rename from deployability/modules/workflow_engine/README.md rename to deployability/modules/workflow_engine/README.MD index c6450d8fc5..51eaad4ad6 --- a/deployability/modules/workflow_engine/README.md +++ b/deployability/modules/workflow_engine/README.MD @@ -1,7 +1,7 @@ ## Workflow engine - -### User documentation - + +### User documentation + The execution of the Workflow is done through the installation of its library. Initially, Python libraries must be installed. It is recommended to use virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. @@ -14,10 +14,6 @@ Initially, Python libraries must be installed. It is recommended to use virtual 2. Clone the `wazuh-qa` repository: - ```bash - git clone {wazuh-qa} - ``` - Navigate to the project directory and switch to the project branch: ```bash @@ -44,10 +40,10 @@ Initially, Python libraries must be installed. It is recommended to use virtual It will be necessary to create a fixture (yaml file) where the infrastructure, provisioning, and tests to be executed will be declared. - >Note: It is possible to find some fixture examples in deployability/modules/workflow_engine/examples/ + >Note: It is possible to find some fixture examples in deployability/modules/workflow_engine/examples/ Example: - + ```bash version: 0.1 description: This workflow is used to test agents deployment por DDT1 PoC @@ -235,14 +231,14 @@ Initially, Python libraries must be installed. It is recommended to use virtual depends-on ``` - >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your test (allocation, provision, and test) + >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your test (allocation, provision, and test) 7. Execution of Command (local): Execute the command by referencing the parameters required by the library (launcher). - + ```bash - python3 -m workflow_engine {.yaml fixture path} + python3 -m workflow_engine {.yaml fixture path} ``` Example @@ -290,7 +286,7 @@ tasks: args: - modules/testing/main.py - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: + - dependencies: - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "install,register,stop" diff --git a/deployability/modules/workflow_engine/__init__.py b/deployability/modules/workflow_engine/__init__.py index 338f58e9fd..39cf4553b9 100755 --- a/deployability/modules/workflow_engine/__init__.py +++ b/deployability/modules/workflow_engine/__init__.py @@ -1 +1,4 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 from .workflow_processor import WorkflowProcessor diff --git a/deployability/modules/workflow_engine/__main__.py b/deployability/modules/workflow_engine/__main__.py index 5d9fe0a0c4..6a0a78fdc8 100755 --- a/deployability/modules/workflow_engine/__main__.py +++ b/deployability/modules/workflow_engine/__main__.py @@ -1,7 +1,6 @@ # Copyright (C) 2015, Wazuh Inc. # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - import os import sys import argparse @@ -33,7 +32,7 @@ def main() -> None: signal.signal(signal.SIGINT, processor.handle_interrupt) processor.run() except Exception as e: - sys.exit(f"Error while provisioning: {e}") + sys.exit(f"Error executing workflow: {e}") if __name__ == "__main__": main() diff --git a/deployability/modules/workflow_engine/examples/dtt1-agents-aws.yaml b/deployability/modules/workflow_engine/examples/dtt1-agents-aws.yaml new file mode 100755 index 0000000000..52e8244c87 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/dtt1-agents-aws.yaml @@ -0,0 +1,142 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-manager-{manager-os}" + - "allocate-agent-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/dtt1-agents-poc-aws.yaml b/deployability/modules/workflow_engine/examples/dtt1-agents-poc-aws.yaml new file mode 100644 index 0000000000..ddc11b67c8 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/dtt1-agents-poc-aws.yaml @@ -0,0 +1,106 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + + #- linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + + - linux-amazon-2023-amd64 + - linux-suse-15-amd64 + #- linux-opensuse-15-amd64 + + manager-os: + #- linux-oracle-9-amd64 + #- linux-ubuntu-20.04-amd64 + #- linux-centos-8-amd64 + - linux-redhat-7-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager}" + - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager}/track.yaml" + - label-termination-date: "1d" + - label-team: "QA" + foreach: + - variable: manager-os + as: manager + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "QA" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-tests" + description: "Run tests install for the agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" + - agent-1: "{working-dir}/agent-linux-ubuntu-20.04-amd64/inventory.yaml" + - agent-2: "{working-dir}/agent-linux-ubuntu-22.04-amd64/inventory.yaml" + - agent-3: "{working-dir}/agent-linux-oracle-9-amd64/inventory.yaml" + - agent-4: "{working-dir}/agent-linux-amazon-2-amd64/inventory.yaml" + #- agent-5: "{working-dir}/agent-linux-redhat-7-amd64/inventory.yaml" + - agent-5: "{working-dir}/agent-linux-redhat-8-amd64/inventory.yaml" + - agent-6: "{working-dir}/agent-linux-redhat-9-amd64/inventory.yaml" + - agent-7: "{working-dir}/agent-linux-centos-7-amd64/inventory.yaml" + - agent-8: "{working-dir}/agent-linux-centos-8-amd64/inventory.yaml" + - agent-9: "{working-dir}/agent-linux-debian-10-amd64/inventory.yaml" + - agent-10: "{working-dir}/agent-linux-debian-11-amd64/inventory.yaml" + - agent-11: "{working-dir}/agent-linux-debian-12-amd64/inventory.yaml" + #- agent-12: "{working-dir}/agent-linux-opensuse-15-amd64/inventory.yaml" + - agent-12: "{working-dir}/agent-linux-suse-15-amd64/inventory.yaml" + - agent-13: "{working-dir}/agent-linux-amazon-2023-amd64/inventory.yaml" + + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml b/deployability/modules/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml new file mode 100644 index 0000000000..fbbe151127 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml @@ -0,0 +1,105 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + + #- linux-amazon-2023-amd64 + #- linux-suse-15-amd64 + + + manager-os: + - linux-opensuse-15-amd64 + #- linux-ubuntu-22.04-amd64 + #- linux-debian-12-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager}" + - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager}/track.yaml" + - label-termination-date: "1d" + - label-team: "QA" + foreach: + - variable: manager-os + as: manager + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "QA" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-tests" + description: "Run tests install for the agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-opensuse-15-amd64/inventory.yaml" + - agent-1: "{working-dir}/agent-linux-ubuntu-20.04-amd64/inventory.yaml" + - agent-2: "{working-dir}/agent-linux-ubuntu-22.04-amd64/inventory.yaml" + - agent-3: "{working-dir}/agent-linux-oracle-9-amd64/inventory.yaml" + - agent-4: "{working-dir}/agent-linux-amazon-2-amd64/inventory.yaml" + - agent-5: "{working-dir}/agent-linux-centos-7-amd64/inventory.yaml" + - agent-6: "{working-dir}/agent-linux-centos-8-amd64/inventory.yaml" + - agent-7: "{working-dir}/agent-linux-redhat-7-amd64/inventory.yaml" + - agent-8: "{working-dir}/agent-linux-redhat-8-amd64/inventory.yaml" + - agent-9: "{working-dir}/agent-linux-redhat-9-amd64/inventory.yaml" + - agent-10: "{working-dir}/agent-linux-debian-10-amd64/inventory.yaml" + - agent-11: "{working-dir}/agent-linux-debian-11-amd64/inventory.yaml" + - agent-12: "{working-dir}/agent-linux-debian-12-amd64/inventory.yaml" + #- agent-12: "{working-dir}/agent-linux-opensuse-15-amd64/inventory.yaml" + #- agent-12: "{working-dir}/agent-linux-suse-15-amd64/inventory.yaml" + #- agent-13: "{working-dir}/agent-linux-amazon-2023-amd64/inventory.yaml" + + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/dtt1-agents-vagrant.yaml b/deployability/modules/workflow_engine/examples/dtt1-agents-vagrant.yaml new file mode 100755 index 0000000000..ca320a07e2 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/dtt1-agents-vagrant.yaml @@ -0,0 +1,138 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-manager-{manager-os}" + - "allocate-agent-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/dtt1-managers-poc-aws.yaml b/deployability/modules/workflow_engine/examples/dtt1-managers-poc-aws.yaml new file mode 100644 index 0000000000..91bae10b90 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/dtt1-managers-poc-aws.yaml @@ -0,0 +1,74 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-linux-centos-7-amd64" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "linux-centos-7-amd64" + - inventory-output: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" + - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" + + + # Unique manager allocate task + - task: "allocate-manager-linux-ubuntu-20.04-amd64" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "linux-ubuntu-20.04-amd64" + - inventory-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - track-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + + + + # Generic manager test task + - task: "run-manager-tests" + description: "Run tests install for the manager." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" + - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - tests: "stop" + - component: "manager" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + depends-on: + - "allocate-manager-linux-ubuntu-20.04-amd64" + - "allocate-manager-linux-centos-7-amd64" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml new file mode 100644 index 0000000000..35817f2169 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml @@ -0,0 +1,82 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-linux-centos-7-amd64" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "linux-centos-7-amd64" + - inventory-output: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" + - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" + + + # Unique manager allocate task + - task: "allocate-manager-linux-ubuntu-20.04-amd64" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "linux-ubuntu-20.04-amd64" + - inventory-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - track-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" + + + + # Generic manager test task + - task: "run-manager-tests" + description: "Run tests install for the manager." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" + - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - tests: "stop" + - component: "manager" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + depends-on: + - "allocate-manager-linux-ubuntu-20.04-amd64" + - "allocate-manager-linux-centos-7-amd64" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/test.yaml b/deployability/modules/workflow_engine/examples/test.yaml new file mode 100644 index 0000000000..9003f9d297 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/test.yaml @@ -0,0 +1,137 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "stop,restart,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" diff --git a/deployability/modules/workflow_engine/examples/test/aws/test-agent-complete.yaml b/deployability/modules/workflow_engine/examples/test/aws/test-agent-complete.yaml new file mode 100755 index 0000000000..e992dfdc08 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/test/aws/test-agent-complete.yaml @@ -0,0 +1,114 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml new file mode 100755 index 0000000000..ad39a8b3c5 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml @@ -0,0 +1,143 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "restart" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml new file mode 100755 index 0000000000..74da24139b --- /dev/null +++ b/deployability/modules/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml @@ -0,0 +1,143 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-20.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "stop" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" diff --git a/deployability/modules/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml b/deployability/modules/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml new file mode 100755 index 0000000000..8ab89589d6 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml @@ -0,0 +1,143 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-complete.yaml b/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-complete.yaml new file mode 100755 index 0000000000..a2cb5a2482 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-complete.yaml @@ -0,0 +1,114 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml new file mode 100755 index 0000000000..913dbaed3d --- /dev/null +++ b/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml @@ -0,0 +1,132 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "stop" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml new file mode 100755 index 0000000000..e61393c256 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml @@ -0,0 +1,133 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "stop" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml b/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml new file mode 100755 index 0000000000..5568493d71 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml @@ -0,0 +1,143 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/logger/__init__.py b/deployability/modules/workflow_engine/logger/__init__.py index e69de29bb2..ea0d8aeea6 100644 --- a/deployability/modules/workflow_engine/logger/__init__.py +++ b/deployability/modules/workflow_engine/logger/__init__.py @@ -0,0 +1,3 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 diff --git a/deployability/modules/workflow_engine/logger/config.yaml b/deployability/modules/workflow_engine/logger/config.yaml index 764f4cc7a3..e4c858047a 100644 --- a/deployability/modules/workflow_engine/logger/config.yaml +++ b/deployability/modules/workflow_engine/logger/config.yaml @@ -1,3 +1,6 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 version: 1 formatters: simple: @@ -22,7 +25,7 @@ handlers: class: logging.FileHandler level: DEBUG formatter: simple - filename: /tmp/flowbacca.log + filename: /tmp/workflow.log root: level: DEBUG handlers: [console, file] diff --git a/deployability/modules/workflow_engine/logger/filter.py b/deployability/modules/workflow_engine/logger/filter.py new file mode 100644 index 0000000000..d92114f52e --- /dev/null +++ b/deployability/modules/workflow_engine/logger/filter.py @@ -0,0 +1,23 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +import logging +import threading + + +class ThreadIDFilter(logging.Filter): + """ + A filter that uppercases the name of the log record. + """ + def filter(self, record: str) -> bool: + """ + Inject thread_id to log records. + + Args: + record (LogRecord): The log record to filter. + + Returns: + bool: True if the record should be logged, False otherwise. + """ + record.thread_id = threading.get_native_id() + return record diff --git a/deployability/modules/workflow_engine/logger/logger.py b/deployability/modules/workflow_engine/logger/logger.py index e090ff0c57..0a1b0b4809 100644 --- a/deployability/modules/workflow_engine/logger/logger.py +++ b/deployability/modules/workflow_engine/logger/logger.py @@ -1,25 +1,23 @@ # Copyright (C) 2015, Wazuh Inc. # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - import logging import logging.config + from pathlib import Path -import threading -import os import yaml + def _load_config() -> None: """ Loads the logging configuration from 'config.yaml' file. """ config_path = Path(__file__).parent / 'config.yaml' + with open(config_path, 'r') as f: + config = yaml.safe_load(f.read()) + logging.config.dictConfig(config) - if os.path.exists(config_path): - with open(config_path, 'r') as f: - config = yaml.safe_load(f.read()) - logging.config.dictConfig(config) _load_config() diff --git a/deployability/modules/workflow_engine/models.py b/deployability/modules/workflow_engine/models.py index c92d2a868f..5892028b67 100644 --- a/deployability/modules/workflow_engine/models.py +++ b/deployability/modules/workflow_engine/models.py @@ -1,7 +1,6 @@ # Copyright (C) 2015, Wazuh Inc. # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - from pathlib import Path from typing import Literal from pydantic import BaseModel diff --git a/deployability/modules/workflow_engine/schema_validator.py b/deployability/modules/workflow_engine/schema_validator.py index d04a9bb39d..7ac2639fc9 100755 --- a/deployability/modules/workflow_engine/schema_validator.py +++ b/deployability/modules/workflow_engine/schema_validator.py @@ -1,7 +1,6 @@ # Copyright (C) 2015, Wazuh Inc. # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - import jsonschema import json import os diff --git a/deployability/modules/workflow_engine/task.py b/deployability/modules/workflow_engine/task.py index b9e349a74b..d076b90b53 100755 --- a/deployability/modules/workflow_engine/task.py +++ b/deployability/modules/workflow_engine/task.py @@ -1,7 +1,6 @@ # Copyright (C) 2015, Wazuh Inc. # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - import subprocess import random import time diff --git a/deployability/modules/workflow_engine/workflow_processor.py b/deployability/modules/workflow_engine/workflow_processor.py index bd4b19ef7f..dfbf4610c9 100755 --- a/deployability/modules/workflow_engine/workflow_processor.py +++ b/deployability/modules/workflow_engine/workflow_processor.py @@ -1,7 +1,6 @@ # Copyright (C) 2015, Wazuh Inc. # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - import concurrent.futures import graphlib import json @@ -365,6 +364,7 @@ def run(self) -> None: dag = DAG(self.task_collection) logger.info("Execution plan: %s", json.dumps(dag.get_execution_plan(), indent=2)) + except Exception as e: logger.error("Error in Workflow: %s", e) diff --git a/deployability/plugins/__init__.py b/deployability/plugins/__init__.py new file mode 100755 index 0000000000..e69de29bb2 diff --git a/deployability/plugins/influxdb_reporter/.gitignore b/deployability/plugins/influxdb_reporter/.gitignore new file mode 100755 index 0000000000..1dbdafb32c --- /dev/null +++ b/deployability/plugins/influxdb_reporter/.gitignore @@ -0,0 +1,13 @@ +__pycache__/ +*.py[cod] +*$py.class +*.egg-info/ +.coverage +.cache/ +.report.json +build/ +dist/ +.pytest_cache/ +.coverage.* +.env +.vscode/ diff --git a/deployability/plugins/influxdb_reporter/README.md b/deployability/plugins/influxdb_reporter/README.md new file mode 100755 index 0000000000..3566184208 --- /dev/null +++ b/deployability/plugins/influxdb_reporter/README.md @@ -0,0 +1,62 @@ +# Wazuh's InfluxDB plugin for pytest + +This is a plugin to send the results of the tests to an InfluxDB database. + +## Installation + +> This plugin is not available in PyPI yet. You can install it from source. + +1. Clone the repository + ```shellsession + $ git clone https://github.com/wazuh/wazuh-qa.git -b enhancement/4736-dtt1-influxdb-plugin + ``` +2. Open the repository folder + ```shellsession + $ cd wazuh-qa/poc-test/src/plugins + ``` +3. Install the plugin + ```shellsession + $ pip install influxdb_reporter + ``` + +## Usage + +There are three ways to configure the plugin: by a configuration file, by command line arguments or by environment variables. In that order, the plugin will look for the configuration, if it is not found, it will look for the arguments, and if they are not found, it will look for the environment variables. + +### Using environment variables + +1. Configure the environment env on your system + ```bash + $ export INFLUXDB_URL="http://localhost:8086" + $ export INFLUXDB_TOKEN="my-token" + $ export INFLUXDB_BUCKET="my-bucket" + $ export INFLUXDB_ORG="my-org" + ``` +2. Execute the test using the `--influxdb-report` flag + ```bash + $ pytest test_name.py --influxdb-report + ``` + +### Using command line arguments + +1. Execute the test using the `--influxdb-report` flag and the required arguments + ```bash + $ pytest test_name.py --influxdb-report --influxdb-url "http://localhost:8086" --influxdb-token "my-token" --influxdb-bucket "my-bucket" --influxdb-org "my-org" + ``` + +### Using influxdb configuration file + +1. Execute the test using the `--influxdb-report` flag and the path to the configuration file + ```bash + $ pytest test_name.py --influxdb-report --influxdb-config-file "path/to/config/file" + ``` + + > The configuration file must be a json file with the following structure: + > ```json + > { + > "url": "http://localhost:8086", + > "token": "my-token", + > "bucket": "my-bucket", + > "org": "my-org" + > } + > ``` diff --git a/deployability/plugins/influxdb_reporter/pytest_influxdb/__init__.py b/deployability/plugins/influxdb_reporter/pytest_influxdb/__init__.py new file mode 100755 index 0000000000..e69de29bb2 diff --git a/deployability/plugins/influxdb_reporter/pytest_influxdb/plugin.py b/deployability/plugins/influxdb_reporter/pytest_influxdb/plugin.py new file mode 100755 index 0000000000..f028c9b919 --- /dev/null +++ b/deployability/plugins/influxdb_reporter/pytest_influxdb/plugin.py @@ -0,0 +1,56 @@ +from __future__ import print_function + +from . import reporter + + +def pytest_addoption(parser): + """ + Add options for the pytest command line. + + Args: + parser (argparsing.Parser): The parser for command line arguments and ini-file values. + """ + group = parser.getgroup('influxdb', 'reporting test results to influxdb') + group.addoption('--influxdb-report', default=False, action='store_true', help='send report to influxdb.') + group.addoption('--influxdb-url', default="http://localhost:8086", help='Influxdb host url.') + group.addoption('--influxdb-token', default=None, help='Token to use for influxdb connection.') + group.addoption('--influxdb-bucket', default=None, help='Influxdb bucket to store the data in.') + group.addoption('--influxdb-org', default=None, help='Influxdb organization name.') + group.addoption('--influxdb-config-file', default=None, help='File with the influxdb configuration.') + + +def pytest_configure(config): + """ + Allows plugins and conftest files to perform initial configuration. + + This hook is called for every plugin and initial conftest + file after command line options have been parsed. + + Args: + config (pytest.Config): The pytest config object. + """ + if not config.getoption("--influxdb-report"): + return + + if config_file := config.getoption("--influxdb-config-file"): + plugin = reporter.InfluxDBReporter(config, config_file) + else: + plugin = reporter.InfluxDBReporter(config) + + config._influxdb = plugin + config.pluginmanager.register(plugin) + + +def pytest_unconfigure(config): + """ + Allows plugins and conftest files to perform cleanup activities. + + This hook is called before test process is exited. + + Args: + config (pytest.Config): The pytest config object. + """ + plugin = getattr(config, '_influxdb', None) + if plugin is not None: + del config._influxdb + config.pluginmanager.unregister(plugin) diff --git a/deployability/plugins/influxdb_reporter/pytest_influxdb/reporter.py b/deployability/plugins/influxdb_reporter/pytest_influxdb/reporter.py new file mode 100755 index 0000000000..4d2c21c51a --- /dev/null +++ b/deployability/plugins/influxdb_reporter/pytest_influxdb/reporter.py @@ -0,0 +1,222 @@ +import os +import logging + +from typing import Union +from datetime import datetime + +import pytest + +from _pytest.config import ExitCode, Config +from _pytest.main import Session +from _pytest.terminal import TerminalReporter +from _pytest.reports import TestReport +from influxdb_client import InfluxDBClient, Point +from influxdb_client.client.write_api import SYNCHRONOUS + + +log = logging.getLogger(__name__) + + +class InfluxDBReporter: + """ + A class used to report test results to InfluxDB. + + Attributes: + config (pytest.Config): The pytest configuration object. + client (InfluxDBClient): The InfluxDB client. + uri (str): The URI of the InfluxDB server. + token (str): The token to authenticate with the InfluxDB server. + bucket (str): The bucket to write data to. + org (str): The organization to write data to. + error (str): Any error that occurred while reporting. + """ + + def __init__(self, config: Config, config_file: str = None) -> None: + """ + Constructs all the necessary attributes for the InfluxDBReporter object. + + Args: + config (pytest.Config): Pytest configuration object. + config_file (str | None): Path to the InfluxDB configuration file (default is None). + """ + self.error: str = None + + if config_file: + # When the config file is specified, it has the priority + self.client = InfluxDBClient.from_config_file(config_file) + return + + # Get attributes from command line or environment variables + self.uri: str = config.getoption('--influxdb-url') \ + or os.environ.get('INFLUXDB_URL') + self.token: str = config.getoption('--influxdb-token') \ + or os.environ.get('INFLUXDB_TOKEN') + self.bucket: str = config.getoption('--influxdb-bucket') \ + or os.environ.get('INFLUXDB_BUCKET') + self.org: str = config.getoption('--influxdb-org') \ + or os.environ.get('INFLUXDB_ORG') + + # Create client + self.client = InfluxDBClient(self.uri, self.token, org=self.org) + + def report(self, session: Session) -> None: + """ + Reports the test results to InfluxDB. + + Args: + session (pytest.Session): The pytest session object. + """ + if not self.__validate_parameters(): + self.error = 'Missing required connection parameters' + return + + terminal_reporter = self. __get_terminal_reporter(session) + # Special check for pytest-xdist plugin + if hasattr(terminal_reporter.config, 'workerinput'): + return + + points = self.__get_points(terminal_reporter.stats) + self.__write_points(points) + + # --- Pytest hooks --- + + @pytest.hookimpl(trylast=True) + def pytest_sessionfinish(self, session: Session, exitstatus: Union[int, ExitCode]) -> None: + """ + Pytest hook that is called when the test session finishes. + + Args: + session (pytest.Session): The pytest session object. + exitstatus (int | ExitCode): The exit status of the test session. + """ + try: + self.report(session) + except Exception as e: + self.error = f'InfluxDB report error: {self.uri} - {e}' + log.error(self.error) + + @pytest.hookimpl(trylast=True) + def pytest_terminal_summary(self, terminalreporter: TerminalReporter, + exitstatus: Union[int, ExitCode], config: Config) -> None: + """ + Pytest hook that is called to add an additional section in the terminal summary reporting. + + Args: + terminalreporter (pytest.TerminalReporter): The terminal reporter object. + exitstatus (int | ExitCode): The exit status of the test session. + config (Config): The pytest configuration object. + """ + if self.error: + terminalreporter.write_sep('-', 'Unable to send report to InfluxDB') + terminalreporter.write(f'\n{self.error}\n') + return + terminalreporter.write_sep('-', 'Report sent to InfluxDB successfully') + + # --- Private methods --- + + def __validate_parameters(self) -> bool: + """ + Validates the connection parameters. + + Returns: + bool: True if the connection parameters are valid, False otherwise. + """ + if None in [self.uri, self.bucket, self.token]: + return False + return True + + def __get_terminal_reporter(self, session: Session) -> TerminalReporter: + """ + Gets the terminal reporter plugin. + + Args: + session (pytest.Session): The pytest session object. + + Returns: + pytest.TerminalReporter: The terminal reporter plugin. + """ + plugin_manager = session.config.pluginmanager + return plugin_manager.get_plugin('terminalreporter') + + def __get_points(self, report_stats: dict) -> list[Point]: + """ + Gets the points to write to InfluxDB. + + Args: + report_stats (dict): The report statistics. + + Returns: + list[Point]: The points to write to InfluxDB. + """ + points = [] + now = str(datetime.now()) + for _, report_items in report_stats.items(): + for report in report_items: + if type(report) is not TestReport: + continue + data = self.__get_report_body(report, now) + points.append(Point.from_dict(data)) + return points + + def __get_report_body(self, test_report: TestReport, datetime: str) -> dict: + """ + Gets the body of the report. + + Args: + test_report (pytest.TestReport): The test report object. + datetime (str): The date and time of the report. + + Returns: + dict: The body of the report. + """ + fields = { + 'test_name': test_report.head_line, + 'node_id': test_report.nodeid, + 'date': datetime, + 'duration': test_report.duration, + 'result': test_report.outcome, + 'stage': test_report.when, + } + tags = { + 'test': test_report.fspath, + 'markers': self.__get_pytest_marks(test_report.keywords), + 'when': test_report.when, + } + full_body = { + 'measurement': 'test_results', + 'tags': tags, + 'fields': fields + } + return full_body + + def __get_pytest_marks(self, keywords: dict) -> list[str]: + """ + Extracts pytest marks from the given keywords. + + Args: + keywords (dict): The keywords dictionary. + + Returns: + list[str]: A list of pytest marks. + """ + marks = [] + for key, _ in keywords.items(): + if 'test_' in key or 'pytest' in key or '.py' in key: + continue + marks.append(key) + return marks + + def __write_points(self, points: list[Point]) -> None: + """ + Writes the given points to InfluxDB. + + Args: + points (list[Point]): The points to write to InfluxDB. + """ + try: + write_api = self.client.write_api(write_options=SYNCHRONOUS) + write_api.write(bucket=self.bucket, record=points) + write_api.close() + except Exception as e: + self.error = f'InfluxDB write error: {self.uri} - {e}' + log.error(self.error) diff --git a/deployability/plugins/influxdb_reporter/setup.cfg b/deployability/plugins/influxdb_reporter/setup.cfg new file mode 100755 index 0000000000..117669d817 --- /dev/null +++ b/deployability/plugins/influxdb_reporter/setup.cfg @@ -0,0 +1,9 @@ +[flake8] +max-line-length = 300 + +[pylint] +disable = + unused-argument, + too-few-public-methods, + too-many-public-methods, + protected-access, \ No newline at end of file diff --git a/deployability/plugins/influxdb_reporter/setup.py b/deployability/plugins/influxdb_reporter/setup.py new file mode 100755 index 0000000000..7bf0a3f324 --- /dev/null +++ b/deployability/plugins/influxdb_reporter/setup.py @@ -0,0 +1,38 @@ +from os import path + +from setuptools import setup + + +this_directory = path.abspath(path.dirname(__file__)) +with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f: + long_description = f.read() + + +setup( + name='pytest-influxdb', + description='A pytest plugin to report test results to influxdb', + long_description=long_description, + long_description_content_type='text/markdown', + packages=['pytest_influxdb'], + author='quebim', + author_email='kevinledesmam95@gmail.com', + install_requires=[ + 'pytest>=3.8.0', + 'influxdb-client>=1.38.0' + ], + entry_points={ + 'pytest11': [ + 'pytest_influxdb = pytest_influxdb.plugin', + ] + }, + classifiers=[ + 'Development Status :: Alpha', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Framework :: Pytest', + ], +) \ No newline at end of file From 7cb12fe3cd5dbdcf0eb26018a4b2e567b9eff95b Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Tue, 9 Apr 2024 17:07:04 -0300 Subject: [PATCH 002/195] Add fix for susse and example template and unit test --- .../modules/allocation/Allocation.drawio.zip | Bin 0 -> 1821 bytes .../modules/allocation/Allocation.jpg | Bin 0 -> 101220 bytes deployability/modules/allocation/README.MD | 345 ++++++++++++++++ .../allocation/aws/helpers/userData.sh | 2 +- .../modules/provision/tests/TESTING-README.md | 133 ++++++ .../modules/provision/tests/conftest.py | 42 ++ .../modules/provision/tests/test_actions.py | 155 +++++++ .../modules/provision/tests/test_handler.py | 173 ++++++++ .../modules/provision/tests/test_models.py | 54 +++ .../modules/provision/tests/test_provision.py | 294 +++++++++++++ .../examples/test/aws/test-agent-susse.yaml | 122 ++++++ .../workflow_engine/requirements-dev.txt | 2 + .../workflow_engine/tests/TESTING-README.md | 167 ++++++++ .../modules/workflow_engine/tests/conftest.py | 75 ++++ .../tests/data/wf-ko-no-path-on-cleanup.yaml | 169 ++++++++ .../tests/data/wf-ko-no-path-on-do.yaml | 169 ++++++++ .../tests/data/wf-ko-schema-error.yaml | 156 +++++++ .../workflow_engine/tests/data/wf-ok.yaml | 170 ++++++++ .../modules/workflow_engine/tests/test_dag.py | 294 +++++++++++++ .../tests/test_schema_validator.py | 119 ++++++ .../workflow_engine/tests/test_task.py | 114 ++++++ .../tests/test_workflow_file.py | 271 ++++++++++++ .../tests/test_workflow_processor.py | 385 ++++++++++++++++++ .../workflow_engine/workflow_processor.py | 2 +- 24 files changed, 3411 insertions(+), 2 deletions(-) create mode 100644 deployability/modules/allocation/Allocation.drawio.zip create mode 100644 deployability/modules/allocation/Allocation.jpg create mode 100644 deployability/modules/allocation/README.MD create mode 100644 deployability/modules/provision/tests/TESTING-README.md create mode 100644 deployability/modules/provision/tests/conftest.py create mode 100644 deployability/modules/provision/tests/test_actions.py create mode 100644 deployability/modules/provision/tests/test_handler.py create mode 100644 deployability/modules/provision/tests/test_models.py create mode 100644 deployability/modules/provision/tests/test_provision.py create mode 100644 deployability/modules/workflow_engine/examples/test/aws/test-agent-susse.yaml create mode 100644 deployability/modules/workflow_engine/requirements-dev.txt create mode 100644 deployability/modules/workflow_engine/tests/TESTING-README.md create mode 100644 deployability/modules/workflow_engine/tests/conftest.py create mode 100644 deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml create mode 100644 deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml create mode 100644 deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml create mode 100644 deployability/modules/workflow_engine/tests/data/wf-ok.yaml create mode 100644 deployability/modules/workflow_engine/tests/test_dag.py create mode 100644 deployability/modules/workflow_engine/tests/test_schema_validator.py create mode 100644 deployability/modules/workflow_engine/tests/test_task.py create mode 100644 deployability/modules/workflow_engine/tests/test_workflow_file.py create mode 100644 deployability/modules/workflow_engine/tests/test_workflow_processor.py diff --git a/deployability/modules/allocation/Allocation.drawio.zip b/deployability/modules/allocation/Allocation.drawio.zip new file mode 100644 index 0000000000000000000000000000000000000000..cffbaaedc3a60cf466e530fe430621d78e77db03 GIT binary patch literal 1821 zcmV+&2jcipO9KQH00ICA018QjSbP64hmi&V0J}B-02KfL06}bQZ)0I}X>V>WWO8A5 zX>TrgZEWRRU2~f_6n*cnVAz>X_rW&e+i~3)ubZTsb*A39ZPLDE6p(E;0%kF)epC+-lO>SaZBoXK|GUY zws~plEs0JJfU4J(U!`N@T$6^X0EPbn>BysKNM?A}qPHwhVMlmnC=JmMztmr4bc(0BK|3-SArj zKG#v~xhP6H7$KN>VS3<@AV3aF+K^H*O-~OAPVW-(X{{Oq2jcAL+;iEuE!&2Y9)CjK zXe{o;+2VGAqLVkGF?7jPG*bi|10{ql4-!aSgmHqLk8c$BBtJ?MJC+X$YkCjcOgjQ6 z?nSTb+Q7M~=_WS^#4(>apADM}2$aNu%SEHKrelwxK?ohxo^oPaW9H*FXl1_!5@RwX zsFt&zO-+G_j~Jaxyi?=T;znp1vNTPZ0jkQ+jTf{ug&82HceJ?ZHi||H;}N)2eFX=( z+&D>-P&Otb5c&0GFXQKn}I~1V=MZ1|R8H zaN7IL{o#%}7w)_h)`Vy8($T=e%apotkDslatZqEuW~H`UI^XaN%}ljB`_^S&{O+{( zm06Qyz|#8)X?T+N<*(q^bSQ=MWJE~3fJHy`CwJ|Op1jWW+kro|W^$)n^J!!gTG#BQ zgxpd4Mjn2||8=|TSZFN|uoiB=eXlRG`+|mlLP{olA3^_ZEL+ck4^^#}5N+oRueDrw zmHQT6Yqju#r$&Tg!-Iz+f>Oh+6Cc&;_ z8Ng|mH*rwog@H!S}{ee18PI85rm9|I4 zW!cO*q1Qu9rqWV*8^*wHca0dRKQ6c1K}Dy`5_bza&yWw(sHVcD87o45FRJTR6p}z` zNK!TjjQ{x4JF3OC%jVgz(``aPao;n4~9a5nomgB%p+|R(e*{w>etKr@$9xi3bJeT3p@`!$s$c zNDi1SIpcmqu2ITbK)8r95|cxSy;0D1IERR~1X-Q~aVI(Hd#=mZzn_hfnd$LbQx^|r zw5GbEx|w5I+jdCJLP-DTBH5$diV~K@<93aQgAIV2#@#}>X5K248(W5+dD%DdY%^dR zIa;?X5{n$-a-JXYU=HMBO>>7?MdoOo^T9L1uU7*~Kl0b?9j-Bj(5)48 zFPQMT5<0KXeY~B;rj+b;xDcyMg#~hZMFgYJx!xWL>)KvdQ^01ks%Ip|%PMbyrl66-5=f6JqZWfj&+;81Y_+h)TxW@tYJMA>XLl#Q6tTYS4iFXzsbgajk2-8 z&SW#_&>#H^(+aU`AD;BS9b3jlu5{lgQ$+h+=@ZW`qo?KASw4}o_C2YEy{M`f=GOD+ zLeN=-{v6)KW62AK6iV-W=zJdhx+mzM8t7SVnxh>s=z6igAH;e^#?Du%9f1$Vg#EBs0OHS(xe&*RSCP}&7QTJ`(@jqIzX zWJk053M=`TtFic+>%$w-DZW_y@a8{IO928u0~7!P00;mINrYH?|1gJ<1^@uNHUIz> z00000000000002AfdBvi06}bQZ)0I}X>V>WWO8A5X>TrgZER3W1qJ{B000310RTe) L006`W00000+52ef literal 0 HcmV?d00001 diff --git a/deployability/modules/allocation/Allocation.jpg b/deployability/modules/allocation/Allocation.jpg new file mode 100644 index 0000000000000000000000000000000000000000..59dd4269c7e1b870bd8bd4b407f9d3834d59b628 GIT binary patch literal 101220 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 literal 0 HcmV?d00001 diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD new file mode 100644 index 0000000000..fda5a9280d --- /dev/null +++ b/deployability/modules/allocation/README.MD @@ -0,0 +1,345 @@ +## Allocation Module + +### User documentation + +The Allocation module allows you to create and destroy VMs both locally and in AWS, it also gives the possibility of creating macOS VMs on Wazuh infrastructure or PowerPC VMs, as long as you have the necessary permissions to do so. VMs can be AMD64, ARM64, Windows, macOS (Intel and ARM), and PowerPC (CentOS and Debian). + +#### Set the environment + +The execution of the allocation is carried out through the Workflow engine library, or by executing them manually through commands. +Execution can be done from any operating system. + +Initially, You must install Python libraries. We recommend to use virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. + +1. Activate the environment: + + ```bash + source {venv directory}/bin/activate + ``` + +2. Clone the `wazuh-qa` repository: + + Navigate to the project directory and switch to the project branch: + + ```bash + git clone https://github.com/wazuh/wazuh-qa.git + cd wazuh-qa + git checkout {project-branch} + ``` +> Note: temporary dev project-branch is [enhancement/4495-DTT1](https://github.com/wazuh/wazuh-qa/tree/enhancement/4495-DTT1) + +3. Install requirements: + + ```bash + pip3 install -r deployability/deps/requirements.txt + ``` + +#### Use the Allocation module through the Workflow engine + +Now, it is possible to use the Worklow engine library to launch the provision module by doing the following steps: + +1. Install the Workflow engine library and its launcher: + + While in wazuh-qa: + + ```bash + cd modules + pip3 uninstall -y workflow_engine && pip3 install . + ``` + +2. Test Fixture to Execute: + + It will be necessary to create a fixture (yaml file) where the infrastructure, provisioning, and tests to be executed will be declared. + + >Note: It is possible to find some fixture examples in [deployability/modules/workflow_engine/examples/](../workflow_engine/examples) + + Example: + + ```bash + version: 0.1 + description: This workflow is used to test agents deployment por DDT1 PoC + variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + + tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + ``` + + Following the schema of the example: + + Configure the following parameters depending on your test case: + + ```yaml + variables/agent-os + variables/manager-os + infra-provider + working-dir + tasks + ``` + + Pay attention to the tasks: + + ```yaml + args + depends-on + ``` + + >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your test (allocation, provision, and test) + +3. Execution of Command (local): + + Execute the command by referencing the parameters required by the library (launcher). + + ```bash + python3 -m workflow_engine {.yaml fixture path} + ``` + + Example + + ```bash + python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml + ``` + + > Note The command execution can also be mediated through Jenkins. + +#### Manual execution of the Allocation module + +If one wishes to execute the allocaation module without installing the Workflow engine, they can proceed by using the launcher ([module/allocation/main.py](main.py)): + +1. Create + + While in wazuh-qa/deployability + +- Local deployment (Vagrant) + + ```bash + python3 modules/allocation/main.py --action create --provider '{{ vagrant }}' --size '{{ large }}' --composite-name '{{ composite-name }}' --inventory-output '{{ inventory }}' --track-output '{{ track }}' + + ``` + + Example: + ```bash + python3 modules/allocation/main.py --action create --provider vagrant --size large --composite-name linux-ubuntu-22.04-amd64 --inventory-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/inventory.yaml" --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" + ``` + +- AWS deployment + + ```bash + python3 modules/allocation/main.py --action create --provider '{{ aws }}' --size '{{ large }}' --composite-name '{{ composite-name }}' --inventory-output '{{ inventory }}' --track-output '{{ track }}' --label-termination-date '{{ termination-date }}' --label-team '{{ team }}' + + ``` + + >Note: In the case of AWS it is mandatory to define two arguments that are not necessary for Vagrant, --label-termination-date and --label-team. + --label-termination-date: This argument allows you to define the date on which the machine can be deleted. The allowed values are **1d** (where the **1** refers to the number of days the machine is needed) or with the following format **"2024-03-20 21:00:00"** + --label-team: This argument allows you to set the team that owns the VM to be able to track it. The valid options are: **qa**, **core**, **framework**, **devops**, **frontend**, **operations**, **cloud**, **threat-intel**, **marketing**, **documentation** + + + + Example: + ```bash + python3 modules/allocation/main.py --action create --provider aws --size large --composite-name linux-ubuntu-22.04-amd64 --inventory-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/inventory.yaml" --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" --label-termination-date "2024-03-20 21:00:00" --label-team devops + ``` + +2. Delete + + While in wazuh-qa/deployability + + ```bash + python3 modules/allocation/main.py --action delete --track-output '{{ track }}' + + ``` + + Example: + ```bash + python3 modules/allocation/main.py --action delete --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" + ``` + + >Note: The --track-output argument is mandatory for the delete action because this file contains all information of the VM that will be destroyed + +3. Arguments + + - --provider + This argument allows us to choose on with platform we will deploy our VM. The allowed values are **aws** or **vagrant**. + + - --size + This argument allows us to choose the resources of the VM. The allowed values are **micro**, **small**, **medium**, and **large**. + + - Vagrant equivalences + micro: CPU 1 - Memory 1024 + small: CPU 1 - Memory 2048 + medium: CPU 2 - Memory 4096 + large: CPU 4 - Memory 8192 + + - AWS equivalences + micro: t2.small (AMD64) - a1.medium (ARM64) + small: t3.small (AMD64) - a1.large (ARM64) + medium: t3a.medium (AMD64) - a1.xlarge (ARM64) + large: c5ad.xlarge (AMD64) - c6g.xlarge (ARM64) + + - --composite-name + This argument allows us to choose the OS, version, and architecture of the VM, example: **linux-centos-7-amd64** + + - --action + This argument defines the action that the module will perform. Allowed values are **create** or **delete**. By default: **create** + + - --ssh-key + This argument allows us to use a custom ssh key for the VM. + Considerations: + - must enter the path where the key is located, with the name of the complete key: **~/.ssh/allocation_test** + - on the same path you have to have the pair of keys (private and public key with the same name): **~/.ssh/allocation_test** **~/.ssh/allocation_test.pub** + - In the case of AWS, you must first create the key in the same region where you are going to deploy the instance. It is important that the key in AWS has the same name as your private key file. + + - --custom-provider-config + This argument allows us to provide a configuration file with all the VM definitions. + + - --track-output + This argument allows us to define which path we want the track file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/VAGRANT-F0753C57-713E-4294-9EA9-89D93D384844/track.yml** + + >Note: this argument is mandatory for delete action. + + - --inventory-output + This argument allows us to define which path we want the inventory file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/VAGRANT-F0753C57-713E-4294-9EA9-89D93D384844/inventory.yml** + + - --working-dir + This argument allows us to define in which directory the files referring to the VM will be generated. By default, **/tmp/wazuh-qa** + + - --label-issue + This argument is only used in the case of AWS and is not mandatory, it allows us to create a label to reference the created instance to an issue on GitHub. It has to be a GitHub URL of the Wazuh repository, for example: **https://github.com/wazuh/internal-devel-requests/issues/1008** + + - --label-team + This argument it is mandatory for AWS deploy, allows you to set the team that owns the VM to be able to track it. The valid options are: **qa**, **core**, **framework**, **devops**, **frontend**, **operations**, **cloud**, **threat-intel**, **marketing**, **documentation** + + - --label-termination-date + This argument it is mandatory for AWS deploy, allows you to define the date on which the machine can be deleted. The allowed values are **1d** (where the **1** refers to the number of days the machine is needed) or with the following format **"2024-03-20 21:00:00"** + + - --instance-name + This argument allows us to define a custom name for the instance, if this argument is not used, the instance name is defined by other parameters entered, such as --label-issue or --composite-name. +--- + +### Technical documentation + +The allocation module allows creating infrastructure on both AWS and locally (using Vagrant). + +Instructions can be initiated from the fixture and executed through the Workflow engine or executed using Python commands. + +In either case, the following information will be needed: + +```yaml + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" +``` + +In the provided fixture fragment, it is evident that to execute the Allocation module launcher ([allocation/main.py](main.py)), the action, provider, size, composite-name, inventory-output, and track-output must be specified. + +For manual execution, an example command would be: + +```bash +python3 modules/allocation/main.py --action create --provider vagrant --size large --composite-name linux-ubuntu-22.04-amd64 --inventory-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/inventory.yaml" --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" +``` + +#### General-specific functions + +- **Launcher** ([/wazuh-qa/deployability/modules/allocation/main.py](main.py)): The entry point for the workflow or the user who wishes to execute a test. + +- **Module functions** ([/wazuh-qa/deployability/modules/allocation/allocation.py](allocation.py)): Module-specific functions responsible for triggering the allocation. + +- **Static functions** ([/wazuh-qa/deployability/modules/allocation/static](static)): Templates and static information for infrastructure creation. + +#### Provider-specific functions + +- **AWS functions** ([/wazuh-qa/deployability/modules/allocation/aws](aws)): Module-specific functions responsible for triggering the allocation. + +- **Vagrant functions** ([/wazuh-qa/deployability/modules/allocation/vagrant](vagrant)): Module-specific functions responsible for triggering the allocation. + +- **Generic functions** ([/wazuh-qa/deployability/modules/allocation/generic](generic)): Module-specific functions responsible for triggering the allocation. + +#### Each provider will contain + +- **Modeler** (`/wazuh-qa/deployability/modules/allocation/{provider}/models.py`) +- **Credentials** (`/wazuh-qa/deployability/modules/allocation/{provider}/credentials.py`) +- **Provider** (`/wazuh-qa/deployability/modules/allocation/{provider}/provider.py`) +- **Information regarding the instance** (`/wazuh-qa/deployability/modules/allocation/{provider}/instance.py`) + +#### Diagram + +![image](Allocation.jpg) + + +[Allocation.drawio.zip](Allocation.drawio.zip) + + +### License + + +WAZUH Copyright (C) 2015 Wazuh Inc. (License GPLv2) diff --git a/deployability/modules/allocation/aws/helpers/userData.sh b/deployability/modules/allocation/aws/helpers/userData.sh index 062e50b179..07f556e6bd 100644 --- a/deployability/modules/allocation/aws/helpers/userData.sh +++ b/deployability/modules/allocation/aws/helpers/userData.sh @@ -128,7 +128,7 @@ if [ ! -r "/etc/os-release" ] || [ "$DIST_NAME" = "centos" ]; then fi fi -if [ "$DIST_NAME" = "amzn" ] || [ "$DIST_NAME" = "sles" ]; then +if [ "$DIST_NAME" = "amzn" ] || [ "$DIST_NAME" = "sles" ] || [ "$DIST_NAME" = "opensuse-leap" ]; then sudo sed -i "s/#Port\s22/Port ${SSH_PORT}/" /etc/ssh/sshd_config sudo systemctl restart sshd.service fi diff --git a/deployability/modules/provision/tests/TESTING-README.md b/deployability/modules/provision/tests/TESTING-README.md new file mode 100644 index 0000000000..e34369c48b --- /dev/null +++ b/deployability/modules/provision/tests/TESTING-README.md @@ -0,0 +1,133 @@ +# Provision module Unit Testing using Pytest + +The provision module includes pytest unit tests. + +## Requirements + +- Make sure you have Python installed on your system. You can download it from [python.org](https://www.python.org/downloads/). +- Clone the wazuh-qa repository in your local environment. +- Install the necessary dependencies by running: +```bash +git clone https://github.com/wazuh/wazuh-qa.git -b [your-branch] +cd wazuh-qa +pip install -r deployability/deps/requirements.txt +pip install -r deployability/deps/remote_requirements.txt +``` +- Configure the `PYTHONPATH` variable with the full path to the directory `deployability`, for example if you've +cloned the `wazuh-qa` repository into `/wazuh/wazuh-qa`, configure the `PYTHONPATH` in this way: +```bash +> pwd +/wazuh/wazuh-qa +> export PYTHONPATH=$PYTHONPATH:$PWD/deployability +> echo $PYTHONPATH +/wazuh/wazuh-qa/deployability +``` + +## Test Structure +The directory `deployability/modules/provision/tests/` contains the unit test files for the `provision` module. + +## Running Tests +To run the tests, make sure that your system meets the requirements by executing the following command from the project +root: + +```bash +pytest -vv deployability/modules/provision +``` +This command will run all tests in the `tests/` directory. Using additional arguments, You can also run specific tests +or directories. The output of this command looks like this: +```bash +pytest -vv deployability/modules/provision/ +=================================================================================== test session starts =================================================================================== +platform linux -- Python 3.10.13, pytest-8.0.1, pluggy-1.4.0 -- /home/marcelo/.pyenv/versions/wazuh-qa/bin/python +cachedir: .pytest_cache +rootdir: /home/marcelo/wazuh/wazuh-qa/deployability/modules +collected 51 items + +deployability/modules/provision/tests/test_actions.py::test_action_constructor[install-package0] PASSED [ 1%] +deployability/modules/provision/tests/test_actions.py::test_action_constructor[install-package1] PASSED [ 3%] +deployability/modules/provision/tests/test_actions.py::test_action_constructor[install-source] PASSED [ 5%] +deployability/modules/provision/tests/test_actions.py::test_action_execute[logger_mock0] PASSED [ 7%] +deployability/modules/provision/tests/test_actions.py::test_action_get_os_family[logger_mock0] PASSED [ 9%] +deployability/modules/provision/tests/test_actions.py::test_provision_handler_get_playbook PASSED [ 11%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-manager-install-package] PASSED [ 13%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-manager-install-assistant] PASSED [ 15%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-manager-install-source] PASSED [ 17%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-manager-uninstall-package] PASSED [ 19%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-manager-uninstall-assistant] PASSED [ 21%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-manager-uninstall-source] PASSED [ 23%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-agent-uninstall-source] PASSED [ 25%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-agent-uninstall-assistant] PASSED [ 27%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor_fail[wazuh-manager-INSTALL-package-Unsupported action: INSTALL] PASSED [ 29%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor_fail[wazuh-manager-UNINSTALL-assistant-Unsupported action: UNINSTALL] PASSED [ 31%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor_fail[wazuh-manager-other-source-Unsupported action: other] PASSED [ 33%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor_fail[wazuh-manager-uninstall-other-Unsupported method: other] PASSED [ 35%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor_fail[indexer-uninstall-assistant-Assistant actions is only supported for Wazuh components.] PASSED [ 37%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_path[wazuh-manager-package-install] PASSED [ 39%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_path[wazuh-manager-assistant-uninstall] PASSED [ 41%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_path[indexer-source-install] PASSED [ 43%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_order[wazuh-manager-package-install-expected_list0] PASSED [ 45%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_order[indexer-source-install-expected_list1] PASSED [ 47%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_order[wazuh-manager-assistant-uninstall-expected_list2] PASSED [ 49%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_order_fail PASSED [ 50%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_generate_dict[wazuh-manager-package-install] PASSED [ 52%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_generate_dict[wazuh-manager-assistant-uninstall] PASSED [ 54%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_generate_dict[indexer-source-install] PASSED [ 56%] +deployability/modules/provision/tests/test_models.py::test_input_payload_constructor_components[True] PASSED [ 58%] +deployability/modules/provision/tests/test_models.py::test_input_payload_constructor_components[False] PASSED [ 60%] +deployability/modules/provision/tests/test_models.py::test_input_payload_constructor_dependencies[None] PASSED [ 62%] +deployability/modules/provision/tests/test_models.py::test_input_payload_constructor_dependencies[dependencies1] PASSED [ 64%] +deployability/modules/provision/tests/test_models.py::test_input_payload_constructor_dependencies[[{'manager': 'path/to/inventory.yaml', 'agent': 'path/to/inventory.yaml'}]] PASSED [ 66%] +deployability/modules/provision/tests/test_models.py::test_input_payload_constructor_fail PASSED [ 68%] +deployability/modules/provision/tests/test_provision.py::test_provision_constructor PASSED [ 70%] +deployability/modules/provision/tests/test_provision.py::test_provision_run[logger_mock0-provision_mock0-stats0] PASSED [ 72%] +deployability/modules/provision/tests/test_provision.py::test_provision_run_fail[logger_mock0-provision_mock0] PASSED [ 74%] +deployability/modules/provision/tests/test_provision.py::test_provision_get_components[provision_mock0-True] PASSED [ 76%] +deployability/modules/provision/tests/test_provision.py::test_provision_get_components[provision_mock1-False] PASSED [ 78%] +deployability/modules/provision/tests/test_provision.py::test_provision_update_status[provision_mock0] PASSED [ 80%] +deployability/modules/provision/tests/test_provision.py::test_provision_provision[provision_mock0] PASSED [ 82%] +deployability/modules/provision/tests/test_provision.py::test_provision_load_ansible_data[provision_mock0] PASSED [ 84%] +deployability/modules/provision/tests/test_provision.py::test_provision_load_ansible_data_fail[logger_mock0-provision_mock0-Exception] PASSED [ 86%] +deployability/modules/provision/tests/test_provision.py::test_provision_load_ansible_data_fail[logger_mock1-provision_mock1-FileNotFoundError] PASSED [ 88%] +deployability/modules/provision/tests/test_provision.py::test_provision_get_deps_ips[provision_mock0-True] PASSED [ 90%] +deployability/modules/provision/tests/test_provision.py::test_provision_get_deps_ips[provision_mock1-False] PASSED [ 92%] +deployability/modules/provision/tests/test_provision.py::test_provision_get_deps_ips_fail[logger_mock0-provision_mock0] PASSED [ 94%] +deployability/modules/provision/tests/test_provision.py::test_provision_validate_component_deps[logger_mock0-provision_mock0-wazuh-agent-dependencies0] PASSED [ 96%] +deployability/modules/provision/tests/test_provision.py::test_provision_validate_component_deps[logger_mock1-provision_mock1-wazuh-manager-dependencies1] PASSED [ 98%] +deployability/modules/provision/tests/test_provision.py::test_provision_validate_component_deps_fail[provision_mock0] PASSED [100%] + +==================================================================================== warnings summary ===================================================================================== +deployability/modules/provision/models.py:36 + /home/marcelo/wazuh/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('dependencies', pre=True) + +deployability/modules/provision/models.py:64 + /home/marcelo/wazuh/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('install', 'uninstall', pre=True) + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +============================================================================= 51 passed, 2 warnings in 0.16s ============================================================================== +``` + +The `.github/workflow/provision-unit-tests.yaml` automatically runs the unit tests in the GitHub environment. +The run results are shown in the `checks` tab or your GitHub pull request. + +## Relevant Files +- `tests/test_[test name].py`: all the unit test files start with a `test_` prefix. There is one unit test file for + each tested class. +- `tests/conftest.py`: contains the fixtures used throughout the unit tests. + +## Unit test development guidelines and recommendations +- Use Python coding style standards and recommendations to develop unit tests: snake case for all variable and function + names, maximum line length of 120 characters, two empty lines must separate each function, typing all your functions + and return values, create Docstring for each function or method with numpy style. +- Develop unit tests for each function or method of the module. +- Error flows are usually created in a second unit test with the suffix `_fail`. For example, the + `test_provision_handler_constructor` found in the `deployability/modules/provision/tests/test_handler.py` is the + unit test normal flow for the `ProvisionHandler` class constructor method. The + `test_provision_handler_constructor_fail` unit test implements the error flow. +- Use the pytest's decorator `@pytest.mark.parametrize` to implement test cases for the same unit test. +- Mock the object instance and functions used by your tested function using the `unitest.mock.patch` and + `unitest.mock.patch.object` functions or decorators. +- Try to factorize your testing code using `pytest.fixtures`. The shared fixtures are in the `conftest.py` file. In + many unit tests of this project, the fixtures implement a `request` object that receives parameters from the + `pytest.mark.parametrize`. diff --git a/deployability/modules/provision/tests/conftest.py b/deployability/modules/provision/tests/conftest.py new file mode 100644 index 0000000000..c4077be190 --- /dev/null +++ b/deployability/modules/provision/tests/conftest.py @@ -0,0 +1,42 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""Common unit test fixtures.""" +from unittest.mock import patch +import pytest + +from modules.provision.models import InputPayload +from modules.provision.provision import Provision + + +@pytest.fixture +def logger_mock(request): + """Fixture to mock common logger methods.""" + logger_to_patch = request.param.get('logger_to_patch', "modules.provision.utils.logger") + with patch(logger_to_patch) as l_mock: + patch.object(l_mock, 'warning') + patch.object(l_mock, 'info') + patch.object(l_mock, 'debug') + patch.object(l_mock, 'error') + yield l_mock + + +@pytest.fixture +def provision_mock(request) -> Provision: + """Fixture to create Provision class instances.""" + components = request.param.get('components', []) + action = request.param.get('action', 'install') + ansible_data = request.param.get('action', {}) + dependencies = request.param.get('dependencies', {}) + + component_info = "{'component':'component', 'type':'component_type'}" + payload = InputPayload(inventory="path", dependencies=dependencies, + install=[component_info], uninstall=[component_info]) + with patch('modules.provision.provision.Provision.get_components'), \ + patch('modules.provision.provision.Provision._Provision__load_ansible_data'): + provision = Provision(payload) + provision.components = components + provision.action = action + provision.ansible_data = ansible_data + + return provision diff --git a/deployability/modules/provision/tests/test_actions.py b/deployability/modules/provision/tests/test_actions.py new file mode 100644 index 0000000000..266a003db9 --- /dev/null +++ b/deployability/modules/provision/tests/test_actions.py @@ -0,0 +1,155 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""Unit Tests for the Action class""" + +from unittest.mock import patch, MagicMock, call +import pytest + +from modules.generic import Ansible +from modules.provision.actions import Action +from modules.provision.models import ComponentInfo +from modules.provision.handler import ProvisionHandler + + +@pytest.mark.parametrize('action, component_type', + [('install', 'package'), + ('install', 'package'), + ('install', 'source')]) +def test_action_constructor(action: str, component_type: str): + """Test Action constructor. + + Parameters + ---------- + component : str + component type + action : str + valid values are install or uninstall + """ + component_info = ComponentInfo(component='myComponent', type=component_type) + ansible_data = {'ansible_host': '', 'ansible_user': '', 'ansible_port': 0, 'ansible_ssh_private_key_file': ''} + with patch('pathlib.Path.exists', return_value=True): + action : Action = Action(action=action, component_info=component_info, ansible_data=ansible_data) + assert isinstance(action.handler, ProvisionHandler) + assert isinstance(action.ansible, Ansible) + + + +@pytest.mark.parametrize('logger_mock', + [{'logger_to_patch': 'modules.provision.actions.logger'}], indirect=True) +def test_action_execute(logger_mock: MagicMock): + """Test Action.run method. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py + """ + ansible_host = 'myHost' + return_tasks = ['task1', 'task2'] + + ansible_task = [{ + 'name': 'Capture ansible_os_family', + 'set_fact': { + 'ansible_os_family': "{{ ansible_facts['distribution_file_variety'] }}", + 'cacheable': 'yes' + } + }] + + playbook = { + 'hosts': ansible_host, + 'become': True, + 'gather_facts': True, + 'tasks': ansible_task + } + status_mock = MagicMock() + component_info = ComponentInfo(component='myComponent', type='package') + ansible_data = {'ansible_host': ansible_host, 'ansible_user': '', + 'ansible_port': 0, 'ansible_ssh_private_key_file': ''} + + action : Action = Action(action='install', component_info=component_info, ansible_data=ansible_data) + with patch('modules.provision.actions.Action._get_playbook', return_value=playbook) as get_playbook_mock, \ + patch.object(action.ansible, 'render_playbooks', return_value=return_tasks) as render_mock, \ + patch.object(action.ansible, 'run_playbook', return_value=status_mock) as run_playbook_mock, \ + patch('modules.provision.actions.Action._get_os_family', return_value='linux') as get_os_mock: + result = action.execute() + + get_playbook_mock.assert_called_once_with(return_tasks) + get_os_mock.assert_called_once() + render_mock.assert_called_once_with(action.handler.variables_dict) + run_playbook_mock.assert_called_once_with(playbook) + assert result == status_mock + logger_mock.debug.assert_has_calls([ + call(f"Render playbook with vars: {action.handler.variables_dict}."), + call(f"Tasks to execute: {return_tasks}.") + ]) + logger_mock.info.assert_called_once_with( + f"Execute {action.handler.action} for {action.handler.component_info.component}.") + + +@pytest.mark.parametrize('logger_mock', + [{'logger_to_patch': 'modules.provision.actions.logger'}], indirect=True) +def test_action_get_os_family(logger_mock: MagicMock): + """Test Action._get_os_falily method. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py + """ + ansible_host = 'myHost' + ansible_task = [{ + 'name': 'Capture ansible_os_family', + 'set_fact': { + 'ansible_os_family': "{{ ansible_facts['distribution_file_variety'] }}", + 'cacheable': 'yes' + } + }] + + playbook = { + 'hosts': ansible_host, + 'become': True, + 'gather_facts': True, + 'tasks': ansible_task + } + fact_cache_mock = MagicMock() + fact_cache_mock.get.return_value = 'os_family' + status_mock = MagicMock() + status_mock.get_fact_cache.return_value = fact_cache_mock + + component_info = ComponentInfo(component='myComponent', type='package') + ansible_data = {'ansible_host': ansible_host, 'ansible_user': '', + 'ansible_port': 0, 'ansible_ssh_private_key_file': ''} + + action : Action = Action(action='install', component_info=component_info, ansible_data=ansible_data) + with patch('modules.provision.actions.Action._get_playbook', return_value=playbook) as get_playbook_mock, \ + patch.object(action.ansible, 'run_playbook', return_value=status_mock) as run_playbook_mock: + result = action._get_os_family() + + get_playbook_mock.assert_called_once_with(ansible_task) + run_playbook_mock.assert_called_once_with(playbook) + status_mock.get_fact_cache.assert_called_once_with(host=action.ansible.ansible_data.ansible_host) + fact_cache_mock.get.assert_has_calls([call('ansible_os_family'), call('ansible_os_family')]) + + assert result == 'os_family' + logger_mock.debug.assert_has_calls([ + call(f"Get OS family for {action.ansible.ansible_data.ansible_host}."), + call("OS family: os_family.") + ]) + + +def test_provision_handler_get_playbook(): + """Test ProvisionHandler._get_playbook method.""" + tasks = ['task1', 'task2'] + component_info = ComponentInfo(component='myComponent', type='package') + ansible_data = {'ansible_host': 'ansible_host', 'ansible_user': '', + 'ansible_port': 0, 'ansible_ssh_private_key_file': ''} + action = Action(action='install', component_info=component_info, ansible_data=ansible_data) + result = action._get_playbook(tasks=tasks) + playbook = { + 'hosts': action.ansible.ansible_data.ansible_host, + 'become': True, + 'gather_facts': True, + 'tasks': tasks, + } + assert result == playbook diff --git a/deployability/modules/provision/tests/test_handler.py b/deployability/modules/provision/tests/test_handler.py new file mode 100644 index 0000000000..dd9d63d093 --- /dev/null +++ b/deployability/modules/provision/tests/test_handler.py @@ -0,0 +1,173 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""Unit Tests for the ProvisionHandler class.""" +from unittest.mock import patch, MagicMock +import pytest + +from modules.provision.handler import ProvisionHandler +from modules.provision.models import ComponentInfo + +@pytest.mark.parametrize('component, action, method', + [('wazuh-manager', 'install', 'package'), + ('wazuh-manager', 'install', 'assistant'), + ('wazuh-manager', 'install', 'source'), + ('wazuh-manager', 'uninstall', 'package'), + ('wazuh-manager', 'uninstall', 'assistant'), + ('wazuh-manager', 'uninstall', 'source'), + ('wazuh-agent', 'uninstall', 'source'), + ('wazuh-agent', 'uninstall', 'assistant'), +]) +@pytest.mark.parametrize('logger_mock', + [{'logger_to_patch': 'modules.provision.handler.logger'}], + indirect=True) +def test_provision_handler_constructor(component: str, action: str, method: str, logger_mock: MagicMock): + """Test ProvisionHandler constructor. + + Parameters + ---------- + component : str + component type + action : str + valid values are install or uninstall + method : str + valid values are package, assistant, source + logger_mock : MagicMock + logger fixture defined en conftest.py + """ + info = ComponentInfo(component=component, type='type', version='version', dependencies={}) + with patch('modules.provision.handler.ProvisionHandler._get_templates_path', + return_value='path1'), \ + patch('modules.provision.handler.ProvisionHandler._get_templates_order', + return_value=["set_repo.j2", "install.j2", "register.j2", "service.j2"]), \ + patch('modules.provision.handler.ProvisionHandler._generate_dict', return_value={'key':'value'}): + handler = ProvisionHandler(component_info=info, action=action, method=method) + + if action == 'uninstall' and method == 'source': + logger_mock.warning.assert_called_once_with("Uninstall from source not supported. Using package.") + method = 'package' + if 'wazuh-agent' in component and method == 'assistant': + logger_mock.warning.assert_called_once_with("Agent can not be installed from assistant. Using package.") + method = 'package' + assert handler.component_info == info + assert handler.action == action.lower() + assert handler.method == method.lower() + assert handler.templates_path == 'path1' + assert handler.templates_order == ["set_repo.j2", "install.j2", "register.j2", "service.j2"] + assert handler.variables_dict == {'key':'value'} + + +@pytest.mark.parametrize('component, action, method, to_match', + [('wazuh-manager', 'INSTALL', 'package', 'Unsupported action: INSTALL'), + ('wazuh-manager', 'UNINSTALL', 'assistant', 'Unsupported action: UNINSTALL'), + ('wazuh-manager', 'other', 'source', 'Unsupported action: other'), + ('wazuh-manager', 'uninstall', 'other', 'Unsupported method: other'), + ('indexer', 'uninstall', 'assistant', + "Assistant actions is only supported for Wazuh components."), +]) +def test_provision_handler_constructor_fail(component: str, action: str, method: str, to_match: str): + """Test ProvisionHandler constructor failure flows. + + Parameters + ---------- + component : str + component type + action : str + valid values are install or uninstall + method : str + valid values are package, assistant, source + """ + info = ComponentInfo(component=component, version='version', dependencies={}) + # Use Package instead of ComponentType class because it is not posible to instantiate a class with + # with abstract methods. + with pytest.raises(ValueError, match=to_match): + ProvisionHandler(component_info=info, action=action, method=method) + + +@pytest.mark.parametrize('component, method, action', + [('wazuh-manager', 'package', 'install'), + ('wazuh-manager', 'assistant', 'uninstall'), + ('indexer', 'source', 'install'), +]) +def test_provision_handler_get_templates_path(component: str, method: str, action:str): + """Test ProvisionHandler.get_templates_path method. + + Parameters + ---------- + component : str + component type + action : str + valid values are install or uninstall + method : str + valid values are package, assistant, source + """ + info = ComponentInfo(component=component, version='version', dependencies={}) + with patch('modules.provision.handler.ProvisionHandler._get_templates_order', + return_value=["set_repo.j2", "install.j2", "register.j2", "service.j2"]): + handler = ProvisionHandler(component_info=info, action=action, method=method) + assert handler.templates_path == f"{handler._base_templates_path}/{handler.method}/{handler.action}" + + +@pytest.mark.parametrize('component, method, action, expected_list', + [('wazuh-manager', 'package', 'install', + ["set_repo.j2", "install.j2", "register.j2", "service.j2"]), + ('indexer', 'source', 'install', ['indexer.j2']), + ('wazuh-manager', 'assistant', 'uninstall', []), +]) +def test_provision_handler_get_templates_order(component: str, method: str, action:str, expected_list: list): + """Test ProvisionHandler._get_templates_order method. + + Parameters + ---------- + component : str + component type + action : str + valid values are install or uninstall + method : str + valid values are package, assistant, source + expected_list : list + expected result + """ + info = ComponentInfo(component=component, version='version', dependencies={}) + with patch('pathlib.Path.exists', return_value=True): + handler = ProvisionHandler(component_info=info, action=action, method=method) + assert handler.templates_order == expected_list + + +def test_provision_handler_get_templates_order_fail(): + """Test ProvisionHandler._get_templates_order method failure flow.""" + info = ComponentInfo(component='indexer', version='version', dependencies={}) + with pytest.raises(ValueError, match="Component source file indexer.j2 not found."): + ProvisionHandler(component_info=info, action='install', method='source') + + +@pytest.mark.parametrize('component, method, action', + [('wazuh-manager', 'package', 'install'), + ('wazuh-manager', 'assistant', 'uninstall'), + ('indexer', 'source', 'install'), +]) +def test_provision_handler_generate_dict(component: str, method: str, action:str): + """Test ProvisionHandler._generate_dict method. + + Parameters + ---------- + component : str + component type + method : str + valid values are package, assistant, source + action : str + valid values are install or uninstall + """ + info = ComponentInfo(component=component, version='version', dependencies={}) + with patch('pathlib.Path.exists', return_value=True): + handler = ProvisionHandler(component_info=info, action=action, method=method) + expected_dict = { + 'component': handler.component_info.component, + 'version': handler.component_info.version, + 'live': handler.component_info.live, + 'type': handler.component_info.type, + 'dependencies': handler.component_info.dependencies or None, + 'templates_path': handler.templates_path, + 'templates_order': handler.templates_order or None + } + assert handler.variables_dict == expected_dict diff --git a/deployability/modules/provision/tests/test_models.py b/deployability/modules/provision/tests/test_models.py new file mode 100644 index 0000000000..7d1afec555 --- /dev/null +++ b/deployability/modules/provision/tests/test_models.py @@ -0,0 +1,54 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""modules.provision.models Unit tests.""" +from pathlib import Path +import pytest + +from modules.provision.models import InputPayload, ComponentInfo + + +@pytest.mark.parametrize('install', [(True), (False)]) +def test_input_payload_constructor_components(install:bool): + """Test InputPayload constructor install and unininstall parameters. + + Parameters + ---------- + install : bool + parameters for install and uninstall InputPayload constructor parameters. + """ + path = '/my_inventory_path' + components = [ + "{'component':'component_1', 'type':'component_type_1'}", + "{'component':'component_2'}", + "{'component':'linux wazuh-agent'}"] + payload = InputPayload(inventory=path, + install=components if install else [], + uninstall=[] if install else components) + assert payload.inventory == Path(path) + comp_list = payload.install if install else payload.uninstall + assert comp_list[0] == ComponentInfo(component='component_1', type='component_type_1') + assert comp_list[1] == ComponentInfo(component='component_2', type='package') + assert comp_list[2] == ComponentInfo(component='linux wazuh-agent', type='package') + + +@pytest.mark.parametrize('dependencies',[ + (None), + (["{'manager': 'path/to/inventory.yaml'}", "{'agent': 'path/to/inventory.yaml'}"]), + ("[{'manager': 'path/to/inventory.yaml', 'agent': 'path/to/inventory.yaml'}]")]) +def test_input_payload_constructor_dependencies(dependencies:str): + """Test InputPayload constructor dependencies parameters.""" + path = '/my_inventory_path' + components = ["{'component':'component_1', 'type':'component_type_1'}"] + payload = InputPayload(inventory=path, install=components, uninstall=[], dependencies=dependencies) + if dependencies: + assert payload.dependencies.get('manager') == 'path/to/inventory.yaml' + assert payload.dependencies.get('agent') == 'path/to/inventory.yaml' + else: + assert not payload.dependencies + + +def test_input_payload_constructor_fail(): + """Test InputPayload constructor invalid parameters.""" + with pytest.raises(ValueError, match='Invalid action: "install" or "uninstall" must be provided.'): + InputPayload(inventory='/path', install=[], uninstall=[]) diff --git a/deployability/modules/provision/tests/test_provision.py b/deployability/modules/provision/tests/test_provision.py new file mode 100644 index 0000000000..d08d9ef981 --- /dev/null +++ b/deployability/modules/provision/tests/test_provision.py @@ -0,0 +1,294 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""Unit Tests for the Provision class""" + +from typing import List +from unittest.mock import patch, MagicMock, call +import pytest + +from modules.provision.models import InputPayload, ComponentInfo +from modules.provision.provision import Provision + + +def test_provision_constructor(): + """Test Provision constructor.""" + component_info = "{'component':'component', 'type':'component_type'}" + payload = InputPayload(inventory="path", dependencies={}, install=[component_info], uninstall=[component_info]) + with patch('modules.provision.provision.Provision.get_components') as get_comp_mock, \ + patch('modules.provision.provision.Provision._Provision__load_ansible_data') as load_ansible_mock: + prov = Provision(payload=payload) + assert len(prov.summary) == 0 + get_comp_mock.assert_called_once_with(payload) + load_ansible_mock.assert_called_once_with(payload.inventory) + + +@pytest.mark.parametrize('logger_mock, provision_mock, stats', + [( + {'logger_to_patch': 'modules.provision.provision.logger'}, + {'components': [ + ComponentInfo(component='component_1', type='type_1'), + ComponentInfo(component='component_2', type='type_2')]}, + {'component_1': {'stat_component_1': 'status component_1'}, + 'component_2': {'stat_component_2': 'status component_2'}}) + ], + indirect=['logger_mock', 'provision_mock']) +def test_provision_run(logger_mock: MagicMock, provision_mock: Provision, stats: List[dict]): + """Test Provision.run method. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py. + provision_mock : Provision + provision fixture defined in conftest.py + stats : List[dict] + keys to update mocking the __provision method + """ + with patch.object(provision_mock, '_Provision__provision', + side_effect=lambda c: provision_mock.summary.update(stats[c.component])) as provision_method_mock: + provision_mock.run() + + assert provision_method_mock.call_count == 2 + logger_mock.info.assert_has_calls([ + call('Initiating provisionment.'), + call('Provisioning "component_1"...'), + call('Provision of "component_1" complete successfully.'), + call('Provisioning "component_2"...'), + call('Provision of "component_2" complete successfully.'), + call('All components provisioned successfully.') + ]) + logger_mock.debug.assert_has_calls([ + call(f'Running action {provision_mock.action} for components: {provision_mock.components}'), + call(f'Provision summary: {provision_mock.summary}') + ]) + + +@pytest.mark.parametrize('logger_mock, provision_mock', + [( + {'logger_to_patch': 'modules.provision.provision.logger'}, + {'components': [ + ComponentInfo(component='component_1', type='type_1'), + ComponentInfo(component='component_2', type='type_2')]}) + ], + indirect=['logger_mock', 'provision_mock']) +def test_provision_run_fail(logger_mock: MagicMock, provision_mock: Provision): + """Test Provision.run method failure flow. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py. + provision_mock : Provision + provision fixture defined in conftest.py + """ + + with patch.object(provision_mock, '_Provision__provision', + side_effect=[None, Exception('Provision generated Exception')]): + with pytest.raises(Exception) as exc_info: + provision_mock.run() + + assert logger_mock.info.call_count == 4 + logger_mock.error.assert_called_once_with(f'Error while provisioning "component_2": {exc_info.value}') + + +@pytest.mark.parametrize('provision_mock, install',[({}, True), ({}, False)], indirect=['provision_mock']) +def test_provision_get_components(provision_mock: Provision, install: bool): + """Test Provision.get_component method. + + Parameters + ---------- + provision_mock : Provision + provision fixture defined in conftest.py. + install : bool + parameterization of the InputPayload constructor install and uninstall parameters + """ + component_info = ["{'component':'component_1', 'type':'component_type_1'}", + "{'component':'component_2', 'type':'component_type_2'}"] + if install: + payload = InputPayload(inventory="path", dependencies={}, install=component_info, uninstall=[]) + else: + payload = InputPayload(inventory="path", dependencies={}, install=[], uninstall=component_info) + with patch.object(provision_mock, '_Provision__get_deps_ips') as get_deps_mock, \ + patch.object(provision_mock, '_Provision__validate_component_deps') as validate_mock: + provision_mock.get_components(payload=payload) + get_deps_mock.assert_called_once_with(payload.dependencies) + assert validate_mock.call_count == 2 + + +@pytest.mark.parametrize('provision_mock', [{}], indirect=['provision_mock']) +def test_provision_update_status(provision_mock: Provision): + """Test Provision.update_status method. + + Parameters + ---------- + provision_mock : Provision + provision fixture defined in conftest.py. + """ + status = MagicMock() + status.stats = {'status_1': 'status 1', 'status_2': 'status 2'} + provision_mock.update_status(status) + assert provision_mock.summary == status.stats + + +@pytest.mark.parametrize('provision_mock', [{}], indirect=['provision_mock']) +def test_provision_provision(provision_mock: Provision): + """Test Provision.__provision method. + + Parameters + ---------- + provision_mock : Provision + provision fixture defined in conftest.py. + """ + component = MagicMock() + status = MagicMock() + action = MagicMock() + action.execute.return_value = status + with patch('modules.provision.provision.Action', return_value=action) as action_mock, \ + patch.object(provision_mock, 'update_status') as update_mock: + provision_mock._Provision__provision(component=component) + action_mock.assert_called_once_with(provision_mock.action, component, provision_mock.ansible_data) + action.execute.assert_called_once() + update_mock.assert_called_once_with(status) + + +@pytest.mark.parametrize('provision_mock', [{}], indirect=['provision_mock']) +def test_provision_load_ansible_data(provision_mock: Provision): + """Test Provision.__load_ansible_data method. + + Parameters + ---------- + provision_mock : Provision + provision fixture defined in conftest.py. + """ + inventory = '/inventory_path' + with patch('modules.provision.provision.Utils.load_from_yaml') as load_yaml_mock: + provision_mock._Provision__load_ansible_data(inventory) + load_yaml_mock.assert_called_once_with(inventory) + + +@pytest.mark.parametrize('logger_mock, provision_mock, exc', + [({'logger_to_patch': 'modules.provision.provision.logger'}, {}, + Exception), + ({'logger_to_patch': 'modules.provision.provision.logger'}, {}, + FileNotFoundError)], + indirect=['logger_mock', 'provision_mock']) +def test_provision_load_ansible_data_fail(logger_mock: MagicMock, provision_mock: Provision, exc: Exception): + """Test Provision.__load_ansible_data method failure flow. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py. + provision_mock : Provision + provision fixture defined in conftest.py. + exc : Exception + expected exception + """ + inventory = '/inventory_path' + with pytest.raises(exc) as exc_info: + with patch('modules.provision.provision.Utils.load_from_yaml', side_effect=exc) as load_yaml_mock: + provision_mock._Provision__load_ansible_data(inventory) + if isinstance(exc_info.value, FileNotFoundError): + logger_mock.error.assert_called_once_with(f'Inventory file "{inventory}" not found.') + else: + logger_mock.error.assert_called_once_with(f'Error loading inventory file "{inventory}": {exc_info.value}') + + +@pytest.mark.parametrize('provision_mock, empty', [({}, True), ({}, False)], indirect=['provision_mock']) +def test_provision_get_deps_ips(provision_mock: Provision, empty: bool): + """Test Provision.__get_deps_ips method. + + Parameters + ---------- + provision_mock : Provision + provision fixture defined in conftest.py. + empty : bool + dependencies are empty is True else dependecies are defined. + """ + dependencies = {} + paths = [] + if not empty: + dependencies = {'dependency_1': '/path_1', 'dependency_2': '/path_2'} + m1 = MagicMock() + m1.exists.return_value = True + m2 = MagicMock() + m2.exists.return_value = True + paths = [m1, m2] + with patch('modules.provision.provision.Utils.load_from_yaml', side_effect=paths) as load_yaml_mock, \ + patch('modules.provision.provision.Path', side_effect=paths) as path_mock: + dependencies_ips = provision_mock._Provision__get_deps_ips(dependencies) + if empty: + assert not dependencies + else: + load_yaml_mock.assert_has_calls([call(m1, specific_key='ansible_host'), + call(m2, specific_key='ansible_host'),]) + path_mock.assert_has_calls([call(dependencies['dependency_1']), + call(dependencies['dependency_2'])]) + assert dependencies_ips == {'dependency_1': m1, 'dependency_2': m2} + + +@pytest.mark.parametrize('logger_mock, provision_mock', + [({'logger_to_patch': 'modules.provision.provision.logger'}, {})], + indirect=['provision_mock', 'logger_mock']) +def test_provision_get_deps_ips_fail(logger_mock: MagicMock, provision_mock: Provision): + """Test Provision.__get_deps_ips method failure flow. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py. + provision_mock : Provision + provision fixture defined in conftest.py. + """ + dependencies = {'dependency_1': '/path_1', 'dependency_2': '/path_2'} + m1 = MagicMock() + m1.exists.return_value = False + m1.__str__.return_value = "/path_1" + with pytest.raises(FileNotFoundError, match=f'Inventory file "{m1}" not found.') as exc_info, \ + patch('modules.provision.provision.Path', return_value=m1): + provision_mock._Provision__get_deps_ips(dependencies) + logger_mock.error.assert_called_once_with(f'Error getting dependency IP: {exc_info.value}') + + +@pytest.mark.parametrize('logger_mock, provision_mock, component_name, dependencies', + [({'logger_to_patch': 'modules.provision.provision.logger'}, {}, + 'wazuh-agent', {'manager': 'wazuh-manager'}), + ({'logger_to_patch': 'modules.provision.provision.logger'}, {}, + 'wazuh-manager', {'other': 'other'})], + indirect=['provision_mock', 'logger_mock']) +def test_provision_validate_component_deps(logger_mock: MagicMock, provision_mock: Provision, component_name: str, + dependencies: dict): + """Test Provision.__validate_component_deps method. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py. + provision_mock : Provision + provision fixture defined in conftest.py. + component_name : str + componente name. + dependencies : dict + ComponentInfo dependencies parameter. + """ + component = ComponentInfo(component=component_name, type='type_1', dependencies=dependencies) + provision_mock._Provision__validate_component_deps(component) + logger_mock.debug.assert_called_once_with( + f"Setting dependencies: {dependencies} for {component.component} component.") + + + +@pytest.mark.parametrize('provision_mock', [({})], + indirect=['provision_mock']) +def test_provision_validate_component_deps_fail(provision_mock: Provision): + """Test Provision.__validate_component_deps method failure flow. + + Parameters + ---------- + provision_mock : Provision + provision fixture defined in conftest.py. + """ + component = ComponentInfo(component='wazuh-agent', type='type_1', dependencies={'other': 'other'}) + with pytest.raises(ValueError, match='Dependency IP is required to install Wazuh Agent.'): + provision_mock._Provision__validate_component_deps(component) diff --git a/deployability/modules/workflow_engine/examples/test/aws/test-agent-susse.yaml b/deployability/modules/workflow_engine/examples/test/aws/test-agent-susse.yaml new file mode 100644 index 0000000000..55392f2c44 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/test/aws/test-agent-susse.yaml @@ -0,0 +1,122 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-opensuse-15-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + - component: tar + - component: curl + depends-on: + - "allocate-manager-{manager-os}" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: curl + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/requirements-dev.txt b/deployability/modules/workflow_engine/requirements-dev.txt new file mode 100644 index 0000000000..05a93e2895 --- /dev/null +++ b/deployability/modules/workflow_engine/requirements-dev.txt @@ -0,0 +1,2 @@ +-r ../../deps/requirements.txt +-r ../../deps/remote_requirements.txt \ No newline at end of file diff --git a/deployability/modules/workflow_engine/tests/TESTING-README.md b/deployability/modules/workflow_engine/tests/TESTING-README.md new file mode 100644 index 0000000000..9144e08dc3 --- /dev/null +++ b/deployability/modules/workflow_engine/tests/TESTING-README.md @@ -0,0 +1,167 @@ +# Workflow engine Unit Testing using Pytest + +The workflow_engine module includes pytest unit tests. + +## Requirements + +- Make sure you have Python installed on your system. You can download it from + [python.org](https://www.python.org/downloads/). +- Clone the wazuh-qa repository in your local environment. +- Install the necessary dependencies by running: +```bash +git clone https://github.com/wazuh/wazuh-qa.git -b [your-branch] +cd wazuh-qa +pip install -r deployability/modules/workflow_engine/requirements-dev.txt +``` +- Configure the `PYTHONPATH` variable with the full path to the directory `deployability/modules`, for example if you've +cloned the `wazuh-qa` repository into `/wazuh/wazuh-qa`, configure the `PYTHONPATH` in this way: +```bash +> pwd +/wazuh/wazuh-qa +> export PYTHONPATH=$PYTHONPATH:$PWD/deployability/modules +> echo $PYTHONPATH +/wazuh/wazuh-qa/deployability/modules +``` + +## Test Structure +The directory `deployability/modules/workflow_engine/tests/` contains the unit test files for the `workflow_engine` +module. + +## Running Tests +To run the tests, make sure that your system meets the requirements by executing the following command from the project +root: + +```bash +pytest -vv deployability/modules/workflow_engine +``` +This command will run all tests in the `tests/` directory. Using additional arguments, You can also run specific tests +or directories. The output of this command looks like this: +```bash +pytest -vv deployability/modules/workflow_engine +============================================================================================== test session starts ============================================================================================== +platform linux -- Python 3.10.13, pytest-7.1.2, pluggy-1.3.0 -- /usr/local/bin/python3 +cachedir: .pytest_cache +metadata: {'Python': '3.10.13', 'Platform': 'Linux-5.15.146.1-microsoft-standard-WSL2-x86_64-with-glibc2.31', 'Packages': {'pytest': '7.1.2', 'pluggy': '1.3.0'}, 'Plugins': {'anyio': '4.2.0', 'testinfra': '5.0.0', 'metadata': '3.0.0', 'html': '3.1.1'}} +rootdir: /home/marcelo/wazuh/wazuh-qa/deployability/modules +plugins: anyio-4.2.0, testinfra-5.0.0, metadata-3.0.0, html-3.1.1 +collected 92 items + +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_constructor[True] PASSED [ 1%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_constructor[False] PASSED [ 2%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[True-dag0] PASSED [ 3%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[True-dag1] PASSED [ 4%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[False-dag0] PASSED [ 5%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[False-dag1] PASSED [ 6%] +deployability/modules/workflow_engine/tests/test_dag.py::test_get_execution_plan[dag0] PASSED [ 7%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-failed-dag0] PASSED [ 8%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-canceled-dag0] PASSED [ 9%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-successful-dag0] PASSED [ 10%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-non_existing_status-dag0] FAILED [ 11%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[non_existing_task-successful-dag0] PASSED [ 13%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[non_existing_task-non_existing_status-dag0] FAILED [ 14%] +deployability/modules/workflow_engine/tests/test_dag.py::test_should_be_canceled[True-dag0] PASSED [ 15%] +deployability/modules/workflow_engine/tests/test_dag.py::test_should_be_canceled[False-dag0] PASSED [ 16%] +deployability/modules/workflow_engine/tests/test_dag.py::test_build_dag[dag0] PASSED [ 17%] +deployability/modules/workflow_engine/tests/test_dag.py::test_build_dag[dag1] PASSED [ 18%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-all-to_be_canceled0-dag0] PASSED [ 19%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-all-to_be_canceled0-dag1] PASSED [ 20%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-related-flows-to_be_canceled1-dag0] FAILED [ 21%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-related-flows-to_be_canceled1-dag1] FAILED [ 22%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-continue-to_be_canceled2-dag0] FAILED [ 23%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-continue-to_be_canceled2-dag1] FAILED [ 25%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-all-to_be_canceled3-dag0] FAILED [ 26%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-all-to_be_canceled3-dag1] FAILED [ 27%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-related-flows-to_be_canceled4-dag0] FAILED [ 28%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-related-flows-to_be_canceled4-dag1] FAILED [ 29%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-continue-to_be_canceled5-dag0] FAILED [ 30%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-continue-to_be_canceled5-dag1] FAILED [ 31%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-all-to_be_canceled6-dag0] PASSED [ 32%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-all-to_be_canceled6-dag1] PASSED [ 33%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-related-flows-to_be_canceled7-dag0] PASSED [ 34%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-related-flows-to_be_canceled7-dag1] PASSED [ 35%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-continue-to_be_canceled8-dag0] FAILED [ 36%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-continue-to_be_canceled8-dag1] FAILED [ 38%] +deployability/modules/workflow_engine/tests/test_dag.py::test_create_execution_plan[dag0-exec_plan0] PASSED [ 39%] +deployability/modules/workflow_engine/tests/test_dag.py::test_create_execution_plan[dag1-exec_plan1] PASSED [ 40%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_schema_validator_constructor[logger_mock0] PASSED [ 41%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_schema_validator_constructor_ko PASSED [ 42%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data PASSED [ 43%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data_ko[wf-ko-no-path-on-do.yaml-Missing required properties in 'with' for task: {'task': 'run-agent-tests-{agent}'] PASSED [ 44%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data_ko[wf-ko-no-path-on-cleanup.yaml-Missing required properties in 'with' for task: {'task': 'allocate-manager'] PASSED [ 45%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_validate_schema PASSED [ 46%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_validate_schema_ko[logger_mock0] PASSED [ 47%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_constructor[task0] PASSED [ 48%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task0] PASSED [ 50%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task1] PASSED [ 51%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task2] PASSED [ 52%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task3] PASSED [ 53%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task4] PASSED [ 54%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc0-1-task0] PASSED [ 55%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc0-0-task0] PASSED [ 56%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc1-1-task0] PASSED [ 57%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc1-0-task0] PASSED [ 58%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_constructor PASSED [ 59%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_validate_schema[logger_mock0] PASSED [ 60%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_validate_schema_ko[logger_mock0] PASSED [ 61%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_load_workflow[logger_mock0] PASSED [ 63%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_load_workflow_ko[logger_mock0] PASSED [ 64%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_process_workflow[logger_mock0] PASSED [ 65%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_process_workflow_ok[logger_mock0] PASSED [ 66%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element0-values0-return_value0] PASSED [ 67%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element1-values1-return_value1] PASSED [ 68%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[string_element {value}-values2-string_element value] PASSED [ 69%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element3-None-return_value3] PASSED [ 70%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_expand_task[task0-return_value0-variables0] PASSED [ 71%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_expand_task[task1-return_value1-variables1] PASSED [ 72%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation PASSED [ 73%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation_ko[task_collection0-Duplicated task names: task 1] PASSED [ 75%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation_ko[task_collection1-Tasks do not exist: task 3, task 4] PASSED [ 76%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-False-1-info-schema.yaml] PASSED [ 77%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-True-1-debug-schema.yaml] PASSED [ 78%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-True-1-debug-None] PASSED [ 79%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task[logger_mock0-w_processor0-dag0-custom_action-True] PASSED [ 80%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task[logger_mock1-w_processor1-dag1-custom_action-False] PASSED [ 81%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock0-w_processor0-dag0-KeyboardInterrupt-None] PASSED [ 82%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock0-w_processor0-dag0-KeyboardInterrupt-abort-all] PASSED [ 83%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock1-w_processor1-dag1-Exception-None] PASSED [ 84%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock1-w_processor1-dag1-Exception-abort-all] PASSED [ 85%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-process] PASSED [ 86%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-dummy] PASSED [ 88%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-dummy-random] PASSED [ 89%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object_ko[w_processor0] PASSED [ 90%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel[logger_mock0-w_processor0-dag0-False] PASSED [ 91%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel[logger_mock0-w_processor0-dag0-True] PASSED [ 92%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel_ko[logger_mock0-w_processor0-dag0-False] PASSED [ 93%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel_ko[logger_mock0-w_processor0-dag0-True] PASSED [ 94%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_generate_futures[w_processor0] PASSED [ 95%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_generate_futures_reverse[w_processor0] PASSED [ 96%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_run[logger_mock0-w_processor0-False] PASSED [ 97%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_run[logger_mock0-w_processor0-True] PASSED [ 98%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_handle_interrupt[logger_mock0-w_processor0] PASSED [100%] + +=================================================================================================== FAILURES ==================================================================================================== +``` + +The `.github/workflow/workflow-engine-unit-tests.yaml` automatically runs the unit tests in the GitHub environment. +The run results are in the `checks` tab or your GitHub pull request. + +## Relevant Files +- `tests/test_[test name].py`: all the unit test files start with a `test_` prefix. There is one unit test file for + each tested class. +- `tests/conftest.py`: contains the fixtures used throughout the unit tests. + +## Unit test development guidelines and recommendations +- Use Python coding style standards and recommendations to develop unit tests: snake case for all variable and function + names, maximum line length of 120 characters, two empty lines must separate each function, typing all your functions + and return values, create Docstring for all functions with numpy style. +- Develop unit tests for each function or method of the module. +- Error flows are usually created in a second unit test with the suffix `_ko`. For example, the + `test_process_task_execute` found in the `deployability/modules/workflow_engine/tests/test_workflow_processor` is the + unit test normal flow for the `WorkflowProcessor.process_task_execute` method. The + `WorkflowProcessor.process_task_execute_ko` unit test implements the error flow. +- Use the pytest's decorator `@pytest.mark.parametrize` to implement test cases for the same unit test. +- Mock the object instance and functions used by your tested function using the `unitest.mock.patch` and + `unitest.mock.patch.object` functions or decorators. +- Try to factorize your testing code using `pytest.fixtures`. The shared fixtures are in the `conftest.py` file. In + many unit tests of this project, the fixtures implement a `request` object that receives parameters from the + `pytest.mark.parametrize`. diff --git a/deployability/modules/workflow_engine/tests/conftest.py b/deployability/modules/workflow_engine/tests/conftest.py new file mode 100644 index 0000000000..5de92b5222 --- /dev/null +++ b/deployability/modules/workflow_engine/tests/conftest.py @@ -0,0 +1,75 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""Common unit test fixtures.""" +import graphlib + +from unittest.mock import patch, MagicMock +import pytest + +from workflow_engine.workflow_processor import DAG, WorkflowProcessor + +DEFAULT_TASK_COLLECTION = [ + {'task': 'task1', 'path': '/cmd1', 'args': [{"param1": "value1"}]}, + {'task': 'task2', 'path': '/cmd2', 'args': [{"param1": "value1"}]}, + {'task': 'task3', 'path': '/cmd3', 'args': [{"param1": "value1"}]}, +] + + +@pytest.fixture +def logger_mock(request) -> MagicMock: + """Fixture to mock common logger methods.""" + logger_to_patch = request.param.get('logger_to_patch', "workflow_engine.workflow_processor.logger") + with patch(logger_to_patch) as l_mock: + patch.object(l_mock, 'warning') + patch.object(l_mock, 'info') + patch.object(l_mock, 'debug') + patch.object(l_mock, 'error') + yield l_mock + + +@pytest.fixture +def dag(request) -> DAG: + """Create a mocked DAG instance.""" + ret_dag: DAG + reverse = request.param.get('reverse', False) + task_collection = request.param.get('task_collection', DEFAULT_TASK_COLLECTION) + if request.param.get('patch', True): + execution_plan_dict = request.param.get('execution_plan_dict', {}) + gl_dag = graphlib.TopologicalSorter() + dep_dict = {'task1': 'task2'} + with patch.object(gl_dag, 'prepare'), \ + patch('workflow_engine.workflow_processor.DAG._DAG__build_dag', + return_value=(gl_dag, dep_dict)), \ + patch('workflow_engine.workflow_processor.DAG._DAG__create_execution_plan', + return_value=execution_plan_dict): + ret_dag = DAG(task_collection=task_collection, reverse=reverse) + else: + ret_dag = DAG(task_collection=task_collection, reverse=reverse) + + if finished_task_status := request.param.get('finished_task_status', False): + ret_dag.finished_tasks_status = finished_task_status + + return ret_dag + + +@pytest.fixture +def w_processor(request) -> WorkflowProcessor: + """Create a mocked WorkflowProcessor instance.""" + + workflow_file = request.param.get('workflow_file', 'workflow.yaml') + dry_run = request.param.get('dry_run', False) + threads = request.param.get('threads', 1) + log_level = request.param.get('log_level', 'info') + schema_file = request.param.get('schema_file', 'schema.yaml') + with patch("workflow_engine.workflow_processor.WorkflowFile") as file_mock: + workflow_file_instance = file_mock.return_value + workflow_file_instance.task_collection = request.param.get('task_collection', DEFAULT_TASK_COLLECTION) + if request.param.get('patch', True): + with patch('workflow_engine.workflow_processor.logger.setLevel'): + processor = WorkflowProcessor(workflow_file, dry_run, threads, + log_level, schema_file) + else: + processor = WorkflowProcessor(workflow_file, dry_run, + threads, log_level, schema_file) + return processor diff --git a/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml b/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml new file mode 100644 index 0000000000..a637e4184f --- /dev/null +++ b/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml @@ -0,0 +1,169 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test agents deployment with a single manager. +variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1 + +tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,register,stop" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-tests-uninstall-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "run-agent-tests-{agent}" + - "provision-uninstall-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: aio + version: "4.7.0" + depends-on: + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: aio + version: "4.8.0" + live: False + depends-on: + - "allocate-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent provision task + - task: "provision-uninstall-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - uninstall: + - component: wazuh-agent + type: package + depends-on: + - "provision-install-{agent}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent \ No newline at end of file diff --git a/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml b/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml new file mode 100644 index 0000000000..6b2d2512d0 --- /dev/null +++ b/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml @@ -0,0 +1,169 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test agents deployment with a single manager. +variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1 + +tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,register,stop" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-tests-uninstall-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "run-agent-tests-{agent}" + - "provision-uninstall-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: aio + version: "4.7.0" + depends-on: + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: aio + version: "4.8.0" + live: False + depends-on: + - "allocate-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent provision task + - task: "provision-uninstall-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - uninstall: + - component: wazuh-agent + type: package + depends-on: + - "provision-install-{agent}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent \ No newline at end of file diff --git a/deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml b/deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml new file mode 100644 index 0000000000..62c130f64b --- /dev/null +++ b/deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml @@ -0,0 +1,156 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test agents deployment with a single manager. +variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1 + +tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-tests-uninstall-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "run-agent-tests-{agent}" + - "provision-uninstall-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: aio + version: "4.7.0" + depends-on: + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: aio + version: "4.8.0" + live: False + depends-on: + - "allocate-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent provision task + - task: "provision-uninstall-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - uninstall: + - component: wazuh-agent + type: package + depends-on: + - "provision-install-{agent}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent \ No newline at end of file diff --git a/deployability/modules/workflow_engine/tests/data/wf-ok.yaml b/deployability/modules/workflow_engine/tests/data/wf-ok.yaml new file mode 100644 index 0000000000..fa979a6f81 --- /dev/null +++ b/deployability/modules/workflow_engine/tests/data/wf-ok.yaml @@ -0,0 +1,170 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test agents deployment with a single manager. +variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1 + +tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,register,stop" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-tests-uninstall-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "run-agent-tests-{agent}" + - "provision-uninstall-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: aio + version: "4.7.0" + depends-on: + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: aio + version: "4.8.0" + live: False + depends-on: + - "allocate-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent provision task + - task: "provision-uninstall-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - uninstall: + - component: wazuh-agent + type: package + depends-on: + - "provision-install-{agent}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent \ No newline at end of file diff --git a/deployability/modules/workflow_engine/tests/test_dag.py b/deployability/modules/workflow_engine/tests/test_dag.py new file mode 100644 index 0000000000..b3c6ec181e --- /dev/null +++ b/deployability/modules/workflow_engine/tests/test_dag.py @@ -0,0 +1,294 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +import graphlib + +from unittest.mock import patch, MagicMock, call +import pytest + +from workflow_engine.workflow_processor import DAG + + +@pytest.mark.parametrize("reverse", [True, False]) +@patch("workflow_engine.workflow_processor.DAG._DAG__build_dag") +@patch("workflow_engine.workflow_processor.DAG._DAG__create_execution_plan") +def test_dag_constructor(create_exec_plan_mock: MagicMock, build_dag_mock: MagicMock, reverse: bool): + """Test ProcessTask constructor + Check all the dag object state after initialization and if the private dag methods are called during the instance + construction. + + Parameters + ---------- + create_exec_plan_mock : MagicMock + Patch of the DAG.__create_execution_plan method. + build_dag_mock : MagicMock + Patch of the DAG.__build_dag_ method. + reverse : bool + Parametrized value used by the DAG constructor. + """ + task_collection = [ + {'task': 'task1', 'path': '/cmd1', 'args': [{"param1": "value1"}]}, + {'task': 'task2', 'path': '/cmd2', 'args': [{"param1": "value1"}]}, + {'task': 'task3', 'path': '/cmd3', 'args': [{"param1": "value1"}]}, + ] + gl_dag = graphlib.TopologicalSorter() + + dep_dict = {'task1': 'task2'} + build_dag_mock.return_value = (gl_dag, dep_dict) + plan_dict = {'task1', 'task2'} + create_exec_plan_mock.return_value = plan_dict + with patch.object(gl_dag, 'prepare') as prepare_mock: + dag = DAG(task_collection=task_collection, reverse=reverse) + + assert dag.task_collection == task_collection + assert dag.reverse == reverse + assert dag.dag == gl_dag + assert dag.dependency_tree == dep_dict + assert isinstance(dag.to_be_canceled, set) and not dag.to_be_canceled + assert dag.finished_tasks_status == { + 'failed': set(), + 'canceled': set(), + 'successful': set(), + } + assert dag.execution_plan == plan_dict + build_dag_mock.assert_called_once() + create_exec_plan_mock.assert_called_once_with(dep_dict) + prepare_mock.assert_called_once() + + +@pytest.mark.parametrize('dag', + [{'reverse': True}, {'reverse': False}], + indirect=True) +@pytest.mark.parametrize('is_active', [True, False]) +def test_dag_is_active(is_active: bool, dag: DAG): + """Test DAG.is_active method. + Check if dag.is_active method returns the value of the dag.dag.is_active() method. + + Parameters + ---------- + is_active : bool + Parametrized value returned by dag.dag.is_active + dag : DAG + DAG fixture defined in conftest.py. + """ + with patch.object(dag.dag, 'is_active', return_value=is_active) as is_active_mock: + assert dag.is_active() == is_active + is_active_mock.assert_called_once() + + +@pytest.mark.parametrize('dag', + [{'execution_plan_dict': {'task1', 'task2'} }], indirect=True) +def test_get_execution_plan(dag: DAG): + """Test DAG.get_execution_plan method. + Check if the dag.get_execution_plan returns the dag.execution_plan instance + + Parameters + ---------- + dag : DAG + DAG fixture defined in conftest.py. + """ + assert dag.get_execution_plan() == dag.execution_plan + + +@pytest.mark.parametrize('dag', [{}], indirect=True) +@pytest.mark.parametrize('task_name, status', [ + ('task1', 'failed'), + ('task1', 'canceled'), + ('task1', 'successful'), +]) +def test_set_status(task_name:str, status:str, dag: DAG): + """Test DAG.set_status method. + Check if the dag.dag.done mode is properly called and that the task is in the failed, canceled or + successful set. + + Parameters + ---------- + task_name : str + Parameterized value for the task name passed to dag.set_status method. + status : str + Parameterized value for the task name passed to dag.set_status method. + dag : DAG + DAG fixture defined in conftest.py. + """ + with patch.object(dag.dag, "done") as done_mock: + dag.set_status(task_name=task_name, status=status) + assert task_name in dag.finished_tasks_status[status] + done_mock.assert_called_once_with(task_name) + + +@pytest.mark.parametrize('dag', [{}], indirect=True) +@pytest.mark.parametrize('in_cancel', [True, False]) +def test_should_be_canceled(in_cancel:bool, dag: DAG): + """Test DAG.should_be_canceled method. + Check if dag.should_be_canceled returns True or False if the task is in the dab.to_be_canceled set. + + Parameters + ---------- + in_cancel : bool + Parameterized value to test the method dag.should_be_canceled with 'task1' + in the dag.to_be_canceled set or not. + dag : DAG + DAG fixture defined in conftest.py. + """ + if in_cancel: + dag.to_be_canceled.add('task1') + else: + if 'task1' in dag.to_be_canceled: + dag.to_be_canceled.remove('task1') + + assert dag.should_be_canceled(task_name='task1') == in_cancel + + +@pytest.mark.parametrize('dag', + [{ + 'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']} + ] + }, + {'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}], + 'reverse': True + } + ], + indirect=True) +def test_build_dag(dag: DAG): + """Test DAG.__build_dag method. + The test uses a task collection and checks the calls to the graphlib.TopologicalSorter.add. + The function calls depend on the dag.reverse instance variable, that it is also parameterized. + + Parameters + ---------- + dag : DAG + DAG fixture defined in conftest.py with task_collection parameterized. + """ + with patch('workflow_engine.workflow_processor.graphlib.TopologicalSorter.add') as mock_add: + res_dag, res_dependency_dict = dag._DAG__build_dag() + assert isinstance(res_dag, graphlib.TopologicalSorter) + call_list = [] + dependency_dict = {} + for task in dag.task_collection: + dependencies = task.get('depends-on', []) + task_name = task['task'] + if dag.reverse: + for dependency in dependencies: + call_list.append(call(dependency, task_name)) + else: + call_list.append(call(task_name, *dependencies)) + dependency_dict[task_name] = dependencies + + assert res_dependency_dict == dependency_dict + mock_add.assert_has_calls(call_list, any_order=True) + + +@pytest.mark.parametrize('dag', + [{ + 'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': []}, + {'task': 'task4', 'depends-on': []}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']} + ], + 'patch': False + }, + {'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': []}, + {'task': 'task4', 'depends-on': []}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}], + 'reverse': True, + 'patch': False, + 'finished_task_status': { + 'failed': set(), + 'canceled': set(), + 'successful': set()} + }, + ], + indirect=True) +@pytest.mark.parametrize('task, cancel_policy, to_be_canceled', + [('task1', 'abort-all', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task2', 'abort-all', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task5', 'abort-all', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task1', 'abort-related-flows', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task2', 'abort-related-flows', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task5', 'abort-related-flows', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task1', 'continue', set()), + ('task2', 'continue', set()), + ('task5', 'continue', set()), + ]) +def test_cancel_dependant_tasks(task: str, cancel_policy: str, to_be_canceled: set, dag: DAG): + """Test DAG.cancel_dependant_tasks method. + Check the to_be_canceled set after calling the cancel_dependant_tasks method with a parameterized task_collection + in reverse True and False test cases. + + Parameters + ---------- + task : str + Parameterized task name. + cancel_policy : str + Parameterized cancel policy using valid values (abort-all, abort-related-flows, continue). + to_be_canceled : set + [description] + dag : DAG + DAG fixture defined in conftest.py parameterized with complete object state + (task_collection, reverse, finished_task_status sets). The patch false parameter avoids patching the + DAG.__build_dag' and DAG.__create_execution_plan methods + """ + dag.cancel_dependant_tasks(task, cancel_policy=cancel_policy) + assert dag.to_be_canceled == to_be_canceled + + +@pytest.mark.parametrize('dag, exec_plan', + [( + {'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']} + ], + 'patch': False}, + {"task5": {"task2": {"task1": {}}, + "task3": {"task1": {}}, + "task4": {"task1": {}}}} + ), + ( + { + 'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}, + {'task': 'task6', 'depends-on': ['task5']} + ], + 'patch': False + }, + {"task6": {"task5": {"task2": {"task1": {}}, + "task3": {"task1": {}}, + "task4": {"task1": {}}}}} + ) + ], + indirect=['dag']) +def test_create_execution_plan(exec_plan: dict, dag: DAG): + """Test DAG._create_execution_plan method. + This private method is executed by the constructor. In this Test, + the results are left in the execution_plan instance variable. + + Parameters + ---------- + exec_plan : dict + execution plan. + dag : DAG + DAG fixture defined in conftest.py. + """ + assert dag.execution_plan == exec_plan diff --git a/deployability/modules/workflow_engine/tests/test_schema_validator.py b/deployability/modules/workflow_engine/tests/test_schema_validator.py new file mode 100644 index 0000000000..25a45db95a --- /dev/null +++ b/deployability/modules/workflow_engine/tests/test_schema_validator.py @@ -0,0 +1,119 @@ +# Copyright (C) 2015-2021, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""SchemaValidator unit tests.""" +import uuid +import random +from pathlib import Path +from unittest.mock import MagicMock, call, patch +import json +from ruamel.yaml import YAML +import pytest +from jsonschema.exceptions import ValidationError, UnknownType + +from workflow_engine.schema_validator import SchemaValidator + +@pytest.mark.parametrize('logger_mock', + [{'logger_to_patch':'workflow_engine.schema_validator.logger'}], + indirect=True) +def test_schema_validator_constructor(logger_mock: MagicMock): + """Test SchemaValidator constructor normal flow. + Check the state of the SchemaValidator instance variables after creation. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture to check debug calls + """ + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + with open(schema_path, 'r') as schema_file: + schema_data = json.load(schema_file) + + wf_file_path = Path(__file__).parent / 'data' / 'wf-ok.yaml' + with open(wf_file_path, 'r') as file: + yaml = YAML(typ='safe', pure=True) + yaml_data = yaml.load(file) + + validator = SchemaValidator(schema_path, wf_file_path) + assert validator.schema_data == schema_data + assert validator.yaml_data == yaml_data + calls = [call(f"Loading schema file: {schema_path}"), + call(f"Loading yaml file: {wf_file_path}")] + logger_mock.debug.assert_has_calls(calls) + + +def test_schema_validator_constructor_ko(): + """"Test SchemaValidator constructor error flows. + Check if the FileNotFoundError is raisen with a random file name. + """ + schema_path = str(uuid.UUID(int=random.randint(0, 2^32))) + with pytest.raises(FileNotFoundError, match=f'File "{schema_path}" not found.'): + SchemaValidator(schema_path, schema_path) + + +def test_preprocess_data(): + """Test SchemaValidator preprocess_data. + Check if the preprocess_data method does not raise exceptions with a valid file. + """ + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + wf_file_path = Path(__file__).parent / 'data' / 'wf-ok.yaml' + validator = SchemaValidator(schema_path, wf_file_path) + validator.preprocess_data() + + +@pytest.mark.parametrize('workflow_file, error_msg', + [('wf-ko-no-path-on-do.yaml', + "Missing required properties in 'with' for task: {'task': 'run-agent-tests-{agent}'"), + ('wf-ko-no-path-on-cleanup.yaml', + "Missing required properties in 'with' for task: {'task': 'allocate-manager'"),]) +def test_preprocess_data_ko(workflow_file: str, error_msg: str): + """Test SchemaValidator preprocess_data error flow. + Check the ValidationError generated by invalid yml files. + + Parameters + ---------- + workflow_file : str + workflow yml file name. + error_msg : str + Error message to check + """ + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + wf_file_path = Path(__file__).parent / 'data' / workflow_file + validator = SchemaValidator(schema_path, wf_file_path) + with pytest.raises(ValidationError, match=error_msg): + validator.preprocess_data() + + +def test_validate_schema(): + """Test SchemaValidator validate_schema with a valid yml file.""" + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + wf_file_path = Path(__file__).parent / 'data' / 'wf-ok.yaml' + validator = SchemaValidator(schema_path, wf_file_path) + validator.validateSchema() + + +@pytest.mark.parametrize('logger_mock', + [{'logger_to_patch':'workflow_engine.schema_validator.logger'}], + indirect=True) +def test_validate_schema_ko(logger_mock: MagicMock): + """Test SchemaValidator validate_schema error flows. + Check the messages sent to the log when an invalid workflow yml file is used. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py + """ + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + wf_file_path = Path(__file__).parent / 'data' / 'wf-ko-schema-error.yaml' + validator = SchemaValidator(schema_path, wf_file_path) + validator.validateSchema() + logger_mock.error.assert_called_once() + assert 'Schema validation error:' in logger_mock.error.call_args[0][0] + + logger_mock.error.reset_mock() + validator = SchemaValidator(schema_path, wf_file_path) + with patch('workflow_engine.schema_validator.jsonschema.validate', side_effect=UnknownType): + validator.validateSchema() + logger_mock.error.assert_called_once() + assert 'Unexpected error at schema validation:' in logger_mock.error.call_args[0][0] diff --git a/deployability/modules/workflow_engine/tests/test_task.py b/deployability/modules/workflow_engine/tests/test_task.py new file mode 100644 index 0000000000..604c8e2926 --- /dev/null +++ b/deployability/modules/workflow_engine/tests/test_task.py @@ -0,0 +1,114 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from typing import List, Tuple +from subprocess import CompletedProcess, CalledProcessError +from unittest.mock import patch, MagicMock, call +import pytest + +from workflow_engine.task import ProcessTask + +@pytest.fixture +def task(request) -> ProcessTask: + """Shared fixture to create task.""" + task_name, task_parms = request.param + return ProcessTask(task_name=task_name, task_parameters=task_parms) + + +@pytest.mark.parametrize("task", [('task1', {"param1": "value1"})], indirect=True) +def test_process_task_constructor(task: ProcessTask): + """Test ProcessTask constructor. + Check the task instance varialbes after constructing the ProcessTask. + + Parameters + ---------- + task : ProcessTask + The task fixture. + """ + assert task.task_name == 'task1' + assert task.task_parameters == {"param1": "value1"} + + +@pytest.mark.parametrize("task", [('task1', {"path": "/mypath", + "args": [{"param1": "value1"}]}), + ('task2', {"path": "/mypath", + "args": ["param1"]}), + ('task3', {"path": "/mypath", + "args": ["param1", "param2"]}), + ('task4', {"path": "/mypath", + "args": ["param1", {"param2": "value2"}]}), + ('task5', {"path": "/mypath", + "args": [{"param1": "value1"}, {"param2": "value2"}]}) + ], indirect=True) +@patch("workflow_engine.task.logger") +def test_process_task_execute(logger_mock: MagicMock, task: ProcessTask): + """Test ProcessTask.execute method normal flow. + Check that ProcessTask.execute calls subprocess.run to run commands with the defined parameters. The + task mock in conftest.py is used to thy diferent command argument formats. + + Parameters + ---------- + logger_mock : MagicMock + The logger mock defined in conftest.py + task : ProcessTask + The task fixture. + """ + results = {} + results["task1"] = {"parm_list": [task.task_parameters['path'], "--param1=value1"]} + results["task2"] = {"parm_list": [task.task_parameters['path'], "param1"]} + results["task3"] = {"parm_list": [task.task_parameters['path'], "param1", "param2"]} + results["task4"] = {"parm_list": [task.task_parameters['path'], "param1", + "--param2=value2"]} + results["task5"] = {"parm_list": [task.task_parameters['path'], "--param1=value1", + "--param2=value2"]} + result = CompletedProcess(args=results[task.task_name]["parm_list"][1:], + returncode=0, stdout="command output", + stderr="") + debug_calls = [call(f'Running task "{task.task_name}" with arguments: ' + f'{results[task.task_name]["parm_list"][1:]}')] + with patch("workflow_engine.task.subprocess.run", return_value=result) as proc_run_mock, \ + patch.object(logger_mock, "debug") as logger_debug_mock: + debug_calls.append(call(f'Finished task "{task.task_name}" execution ' + f'with result:\n{str(result.stdout)}')) + task.execute() + + logger_debug_mock.assert_has_calls(debug_calls) + proc_run_mock.assert_called_once_with(results[task.task_name]['parm_list'], check=True, + capture_output=True, text=True) + + +@pytest.mark.parametrize("task", [('task1', {"path": "/mypath", + "args": [{"param1": "value1"}]}), + ], indirect=True) +@pytest.mark.parametrize("subproc_retval", [1, 0]) +@pytest.mark.parametrize("subproc_run_exc", [(True, KeyboardInterrupt, "KeyboardInterrupt error"), + (True, Exception, "Other Error")]) +def test_process_task_execute_ko(subproc_retval: int, subproc_run_exc: List[Tuple], task: ProcessTask): + """Test ProcessTask.execute method exception flows. + Check ProcessTask.execute flow when the subprocess.run returns errors. + + Parameters + ---------- + subproc_retval : int + return code from subprocess.run + subproc_run_exc : bool + Tuple + task : ProcessTask + The task fixture. + """ + raise_exc, exception_type, stderr = subproc_run_exc + if exception_type is Exception: + match = f"Error executing process task {stderr}" + else: + match = "Error executing process task with keyboard interrupt." + result = CompletedProcess(args=["--param1=value1"], + returncode=subproc_retval, stdout="command output", + stderr=stderr) + with patch("workflow_engine.task.subprocess.run", return_value=result) as proc_run_mock: + if raise_exc: + proc_run_mock.side_effect = CalledProcessError(returncode=1, + cmd=task.task_parameters['path'], + stderr=stderr) + + with pytest.raises(exception_type, match=match): + task.execute() diff --git a/deployability/modules/workflow_engine/tests/test_workflow_file.py b/deployability/modules/workflow_engine/tests/test_workflow_file.py new file mode 100644 index 0000000000..a3b411899e --- /dev/null +++ b/deployability/modules/workflow_engine/tests/test_workflow_file.py @@ -0,0 +1,271 @@ +# Copyright (C) 2015-2021, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""WorkflowFile unit tests.""" +from typing import Any, List +from unittest.mock import patch, MagicMock, call, mock_open +import pytest + +from workflow_engine.workflow_processor import WorkflowFile + + +def test_workflow_file_constructor(): + """Test WorkflowFile constructor. + Check the function calls and instance variables after object creation.""" + with patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__validate_schema") as validate_mock, \ + patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__load_workflow", + return_value={'data': 'data'}) as load_mock, \ + patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__process_workflow") as process_mock, \ + patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__static_workflow_validation") \ + as static_validation_mock: + wf = WorkflowFile(workflow_file_path='my_file.yaml', schema_path='my_schema.yaml') + assert wf.schema_path == 'my_schema.yaml' + validate_mock.assert_called_once_with('my_file.yaml') + load_mock.assert_called_once_with('my_file.yaml') + assert wf.workflow_raw_data == {'data': 'data'} + process_mock.assert_called_once() + static_validation_mock.assert_called_once() + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +def test_workflow_file_validate_schema(logger_mock: MagicMock): + """Test WorkflowFile.__validate_schema. + Check debug messages and function called by the method. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + """ + wf = MagicMock() + wf.schema_path = 'my_schema_path.yaml' + workflow_file = 'my_file_path.yaml' + schema_validator = MagicMock() + with patch('workflow_engine.workflow_processor.SchemaValidator', + return_value=schema_validator) as schema_validator_mock: + with patch.object(schema_validator, 'preprocess_data') as preprocess_mock, \ + patch.object(schema_validator, 'validateSchema') as validate_schema_mock: + WorkflowFile._WorkflowFile__validate_schema(self=wf, workflow_file=workflow_file) + + logger_mock.debug.assert_called_once_with(f"Validating input file: {workflow_file}") + schema_validator_mock.assert_called_once_with(wf.schema_path, workflow_file) + preprocess_mock.assert_called_once() + validate_schema_mock.assert_called_once() + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +def test_workflow_file_validate_schema_ko(logger_mock: MagicMock): + """Test WorkflowFile.__validate_schema error flow. + Check logged messages and function calls of the method. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + """ + wf = MagicMock() + wf.schema_path = 'my_schema_path.yaml' + workflow_file = 'my_file_path.yaml' + file_exc = FileNotFoundError() + with patch('workflow_engine.workflow_processor.SchemaValidator', side_effect=file_exc) as schema_validator_mock, \ + pytest.raises(FileNotFoundError): + WorkflowFile._WorkflowFile__validate_schema(self=wf, workflow_file=workflow_file) + + logger_mock.debug.assert_called_once_with(f"Validating input file: {workflow_file}") + schema_validator_mock.assert_called_once_with(wf.schema_path, workflow_file) + logger_mock.error.assert_called_once_with("Error while validating schema [%s] with error: %s", + wf.schema_path, + file_exc) + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +@patch('builtins.open', new_callable=mock_open, read_data='YAML content') +def test_workflow_file_load_workflow(mock_open: MagicMock, logger_mock: MagicMock): + """Test WorkflowFile.__load_workflow. + Check logged messages and function calls of the method. + + Parameters + ---------- + mock_open : MagicMock + The mock fixture defined in conftest.py. + logger_mock : MagicMock + The logger fixture defined in conftest.py. + """ + wf = MagicMock() + wf.schema_path = 'my_schema_path.yaml' + workflow_file = 'my_file_path.yaml' + mock_open.return_value.__enter__.return_value = mock_open + with patch('workflow_engine.workflow_processor.os.path.exists', return_value=True) as path_exists_mock, \ + patch('workflow_engine.workflow_processor.yaml.safe_load') as safe_load_mock: + WorkflowFile._WorkflowFile__load_workflow(self=wf, file_path=workflow_file) + + path_exists_mock.assert_called_once_with(workflow_file) + logger_mock.debug.assert_called_once_with(f"Loading workflow file: {workflow_file}") + mock_open.assert_called_once_with(workflow_file, 'r', encoding='utf-8') + safe_load_mock.assert_called_once_with(mock_open) + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +@patch('builtins.open', new_callable=mock_open, read_data='YAML content') +def test_workflow_file_load_workflow_ko(mock_open: MagicMock, logger_mock: MagicMock): + """Test WorkflowFile.__load_workflow error flow. + Check if the FileNotFoundError exception is raised by the method. + + Parameters + ---------- + mock_open : MagicMock + unittest mock of the open function + logger_mock : MagicMock + The logger fixture defined in conftest.py + """ + wf = MagicMock() + wf.schema_path = 'my_schema_path.yaml' + workflow_file = 'my_file_path.yaml' + mock_open.return_value.__enter__.return_value = mock_open + with patch('workflow_engine.workflow_processor.os.path.exists', return_value=False) as path_exists_mock, \ + pytest.raises(FileNotFoundError, match=f'File "{workflow_file}" not found.') as file_exc: + WorkflowFile._WorkflowFile__load_workflow(self=wf, file_path=workflow_file) + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +def test_workflow_file_process_workflow(logger_mock: MagicMock): + """Test WorkflowFile.__process_workflow. + Check that the method calls the expand_task method of each task using a lambda as a side effect. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py + """ + variable_list = {'variable_1': 'value_1', 'variable_2': 'value_2'} + task_list = [{'task': 'task1'}, {'task': 'task2'}, {'task': 'task3'}] + expanded_task_list = [{'task': 'task3_1'}, {'task': 'task3_2'}] + wf = MagicMock() + wf.workflow_raw_data = {'tasks': task_list, 'variables': variable_list} + wf._WorkflowFile__expand_task.side_effect = lambda task, variables: [task] + \ + (expanded_task_list if task['task'] == 'task3' else []) + tasks = WorkflowFile._WorkflowFile__process_workflow(wf) + + logger_mock.debug.assert_called_once_with("Process workflow.") + calls = [call(task, variable_list) for task in task_list] + wf._WorkflowFile__expand_task.assert_has_calls(calls) + task_list.extend(expanded_task_list) + assert tasks == task_list + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +def test_workflow_file_process_workflow_ok(logger_mock: MagicMock): + """Test WorkflowFile.__process_workflow error flow. + Check that a ValueError is raised when no task are found in the workflow. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py + """ + wf = MagicMock() + wf.workflow_row_data = { + 'tasks': [] + } + wf.__expand_task.return_value = [] + with pytest.raises(ValueError, match="No tasks found in the workflow."): + tasks = WorkflowFile._WorkflowFile__process_workflow(self=wf) + + logger_mock.debug.assert_called_once_with("Process workflow.") + + +@pytest.mark.parametrize('element, values, return_value', + [({'key_1': 'key_1 {value_1}', 'key_2': 'key_2 {value_2}'}, + {'value_1': 'value_1', 'value_2': 'value_2'}, + {'key_1': 'key_1 value_1', 'key_2': 'key_2 value_2'}), + (['element_1 {value_1}', 'element_2 {value_2}', 'element_3 {value_3}'], + {'value_1': 'value_1', 'value_2': 'value_2', 'value_3': 'value_3'}, + ['element_1 value_1', 'element_2 value_2', 'element_3 value_3']), + ('string_element {value}', {'value': 'value'}, 'string_element value'), + ({1, 2}, None, {1, 2})]) +def test_workflow_file_replace_placeholder(element: Any, values: dict, return_value: Any): + """Test WorkflowFile.__replace_placeholder.""" + wf = MagicMock() + wf._WorkflowFile__replace_placeholders.side_effect = \ + lambda s, e, v: WorkflowFile._WorkflowFile__replace_placeholders(wf, s, e, v) + result = WorkflowFile._WorkflowFile__replace_placeholders(self=wf, element=element, values=values) + assert result == return_value + + +@pytest.mark.parametrize('task, return_value, variables', + [({'task': 'task: {as_variable_1}', 'param': '{as_variable_2}', + 'foreach': [{'variable': 'variable_1', 'as': 'as_variable_1'}, + {'variable': 'variable_2', 'as': 'as_variable_2'}]}, + [{"task": "task: value_1_1", 'param': 'value_2_1', + "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, + {"variable": "variable_2", "as": "as_variable_2"}]}, + {"task": "task: value_1_1", 'param': 'value_2_2', + "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, + {"variable": "variable_2", "as": "as_variable_2"}]}, + {"task": "task: value_1_2", 'param': 'value_2_1', + "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, + {"variable": "variable_2", "as": "as_variable_2"}]}, + {"task": "task: value_1_2", 'param': 'value_2_2', + "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, + {"variable": "variable_2", "as": "as_variable_2"}]}], + {'variable_1': ['value_1_1', 'value_1_2'], + 'variable_2': ['value_2_1', 'value_2_2']}), + ({'task': 'task1', 'placeholder': 'placeholder {variable_1}'}, + [{'task': 'task1', 'placeholder': 'placeholder value_1'}], + {'variable_1': 'value_1'}) + ]) +def test_workflow_file_expand_task(task: dict, return_value: dict, variables: dict): + """Test WorkflowFile.___expand_task. + Check the if the expand_task return dictionary is ok. + + Parameters + ---------- + task : dict + A task dictionary used as the input parameter for the expand_task method. + return_value : dict + The expected return value. + variables : dict + The variables dictionary used as the input parameter for the expand_task method. + """ + def side_effect(s, e, v = None): + return WorkflowFile._WorkflowFile__replace_placeholders(wf, s, e, v) + wf = MagicMock() + wf._WorkflowFile__replace_placeholders.side_effect = side_effect + + tasks = WorkflowFile._WorkflowFile__expand_task(wf, task, variables) + assert tasks == return_value + + +def test_workflow_file_static_workflow_validation(): + """Test WorkflowFile.__static_workflow_validation. + Check if no exception is raised with a valid task_collection""" + wf = MagicMock() + wf.task_collection = [{"task": "task 1", "param": "1"}, + {"task": "task 2", "param": "2", 'depends-on': ['task 1']} + ] + WorkflowFile._WorkflowFile__static_workflow_validation(wf) + + +@pytest.mark.parametrize('task_collection, error_msg', [ + ([{"task": "task 1", "param": "1"}, + {"task": "task 1", "param": "2", 'depends-on': ['task 1']}], + 'Duplicated task names: task 1'), + ([{"task": "task 1", "param": "1", 'depends-on': ['task 3', 'task 4']}, + {"task": "task 2", "param": "2", 'depends-on': ['task 3']}], + 'Tasks do not exist: task 3, task 4') +]) +def test_workflow_file_static_workflow_validation_ko(task_collection: List[dict], error_msg: str): + """Test WorkflowFile.__static_workflow_validation. + Check if the validation raises ValueError exceptions with invalid task collections. + + Parameters + ---------- + task_collection : List[dict] + List of tasks + error_msg : str + Expected exception errors + """ + wf = MagicMock() + wf.task_collection = task_collection + with pytest.raises(ValueError, match=error_msg): + WorkflowFile._WorkflowFile__static_workflow_validation(wf) diff --git a/deployability/modules/workflow_engine/tests/test_workflow_processor.py b/deployability/modules/workflow_engine/tests/test_workflow_processor.py new file mode 100644 index 0000000000..a0639c6ea0 --- /dev/null +++ b/deployability/modules/workflow_engine/tests/test_workflow_processor.py @@ -0,0 +1,385 @@ +# Copyright (C) 2015-2021, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""WorkflowProcessor Unit tests""" +import time +import json +from concurrent.futures import Future +from unittest.mock import patch, MagicMock, call +import pytest + +from workflow_engine.workflow_processor import WorkflowProcessor, DAG +from workflow_engine.task import ProcessTask, TASKS_HANDLERS + + +@pytest.mark.parametrize('workflow_file, dry_run, threads, log_level, schema_file', + [('workflow.yaml', False, 1, 'info', 'schema.yaml'), + ('workflow.yaml', True, 1, 'debug', 'schema.yaml'), + ('workflow.yaml', True, 1, 'debug', None), + ]) +@patch("workflow_engine.workflow_processor.logger") +@patch("workflow_engine.workflow_processor.WorkflowFile") +def test_workflow_processor_constructor(file_mock: MagicMock, logger_mock: MagicMock, + workflow_file:str, dry_run: bool, threads: int, log_level: str, + schema_file:str): + """Test WorkflowProcessor constructor. + Check the workflowprocessor instance variables after construction. + + Parameters + ---------- + file_mock : MagicMock + Mock of a WorkflowFile Constructor. + logger_mock : MagicMock + The logger fixture defined in conftest.py. + workflow_file : str + Path to workflow yaml file. + dry_run : bool + Define if the workflow will run or not + threads : int + number of threads + log_level : str + Log level string + schema_file : str + Path to the schema.yml file + """ + task_collection = [ + {'task': 'task1', 'path': '/cmd1', 'args': [{"param1": "value1"}]}, + {'task': 'task2', 'path': '/cmd2', 'args': [{"param1": "value1"}]}, + {'task': 'task3', 'path': '/cmd3', 'args': [{"param1": "value1"}]}, + ] + workflow_file_instance = file_mock.return_value + workflow_file_instance.task_collection = task_collection + with patch.object(logger_mock, 'setLevel') as set_level_mock: + processor = WorkflowProcessor(workflow_file, dry_run, threads, log_level, schema_file) + set_level_mock.assert_called_once_with(log_level) + file_mock.assert_called_once_with(workflow_file, schema_file) + assert processor.task_collection == task_collection + assert processor.dry_run == dry_run + assert processor.threads == threads + + +@pytest.mark.parametrize('logger_mock, w_processor, dag, action, should_be_canceled', + [({}, {}, {}, 'custom_action', True), + ({}, {}, {}, 'custom_action', False),], + indirect=["dag", "w_processor", "logger_mock"]) +def test_execute_task(logger_mock: MagicMock, w_processor: WorkflowProcessor, dag: DAG, action: str, + should_be_canceled: bool): + """Test WorflowProcessor.execute_task function normal + Check the execute_task method when log messages and function calls when the should_be_canceled return value + is True or False. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dag : DAG + The dag fixture defined in conftest.py. + action : str + action name + should_be_canceled : bool + should_be_canceled method patched return value. + + Returns + ------- + [type] + [description] + """ + start_time = time.time() + elapsed_time = 10 + def time_side_effect(): + nonlocal start_time + start_time=start_time + elapsed_time + return start_time + + task = {'task': 'task1'} + p_task = ProcessTask('task1', {}) + with patch.object(dag, 'should_be_canceled', return_value=should_be_canceled) as should_be_canceled_mock, \ + patch.object(w_processor, 'create_task_object', return_value=p_task) as create_task_mock, \ + patch.object(dag, 'set_status') as set_status_mock, \ + patch.object(p_task, 'execute') as exec_mock, \ + patch('workflow_engine.workflow_processor.time') as time_mock: + time_mock.time = MagicMock(side_effect=time_side_effect) + w_processor.execute_task(dag=dag, task=task, action=action) + should_be_canceled_mock.assert_called_once_with(task['task']) + if should_be_canceled: + logger_mock.warning.assert_called_once_with( + "[%s] Skipping task due to dependency failure.", task['task']) + set_status_mock.assert_called_once_with(task['task'], 'canceled') + else: + create_task_mock.assert_called_once_with(task, action) + exec_mock.assert_called_once() + logger_mock.info.assert_has_calls([ + call("[%s] Starting task.", task['task']), + call("[%s] Finished task in %.2f seconds.", task['task'], elapsed_time) + ] + ) + set_status_mock.assert_called_once_with(task['task'], 'successful') + + +@pytest.mark.parametrize('on_error', [None, 'abort-all']) +@pytest.mark.parametrize('logger_mock, w_processor, dag, exception', + [({}, {}, {}, KeyboardInterrupt), + ({}, {}, {}, Exception)], + indirect=["dag", "w_processor", "logger_mock"]) +def test_execute_task_ko(logger_mock: MagicMock, w_processor: WorkflowProcessor, dag: DAG, exception, + on_error: str): + """Test WorflowProcessor.execute_task function, error flows. + Check logged messages, set_status call and cancel_dependant_tasks in the failure flow. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dag : DAG + The dag fixture defined in conftest.py. + exception : [type] + Expected exception. + on_error : str + set on-error of the task. + """ + task = {'task': 'task1'} + task.update({'on-error': on_error} if on_error else {}) + p_task = ProcessTask('task1', {}) + exc = exception() + with patch.object(dag, 'should_be_canceled', return_value=False), \ + patch.object(w_processor, 'create_task_object', return_value=p_task), \ + patch.object(dag, 'set_status') as set_status_mock, \ + patch.object(p_task, 'execute', side_effect=exc), \ + patch('workflow_engine.workflow_processor.time'), \ + patch.object(dag, 'cancel_dependant_tasks') as cancel_mock, \ + pytest.raises(expected_exception=exception): + w_processor.execute_task(dag=dag, task=task, action='action') + + logger_mock.error.assert_called_once_with("[%s] Task failed with error: %s.", task['task'], exc) + set_status_mock.assert_called_once_with(task['task'], 'failed') + cancel_mock.assert_called_once_with(task['task'], on_error if on_error else 'abort-related-flows') + + +@pytest.mark.parametrize('task_type', ['process', 'dummy', 'dummy-random']) +@pytest.mark.parametrize('w_processor', [{}], indirect=True) +def test_create_task_object(w_processor: WorkflowProcessor, task_type: str): + """Test WorkfowProcess.create_task_object function normal flow. + Check the task type returned by the method. + + Parameters + ---------- + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + task_type : str + type of task + """ + task_dict = {'task': 'task1', 'action': {'this': task_type, 'with': {'param'}}} + task = w_processor.create_task_object(task_dict, 'action') + assert isinstance(task, TASKS_HANDLERS.get(task_type)) + + +@pytest.mark.parametrize('w_processor', [{}], indirect=True) +def test_create_task_object_ko(w_processor: WorkflowProcessor): + """Test WorkfowProcess.create_task_object function error flow. + Check that the create_task_object raise a ValueError exception for invalid types.} + + Parameters + ---------- + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + """ + task_type = 'unknown' + task_dict = {'task': 'task1', 'action': {'this': task_type, 'with': {'param'}}} + with pytest.raises(ValueError, match=f"Unknown task type '{task_type}'."): + w_processor.create_task_object(task_dict, 'action') + + +@pytest.mark.parametrize('reverse', [False, True]) +@pytest.mark.parametrize('logger_mock, w_processor, dag',[({}, {}, {})], + indirect=["dag", "w_processor", "logger_mock"]) +@patch('workflow_engine.workflow_processor.concurrent.futures.ThreadPoolExecutor') +def test_execute_tasks_parallel(executor_mock: MagicMock, logger_mock: MagicMock, w_processor: WorkflowProcessor, + dag: DAG, reverse: bool): + """Test WorkfowProcess.execute_task_parallel function. + Check if the logged messages and function calls of the method with reverse True and False cases. + + Parameters + ---------- + executor_mock : MagicMock + Mock of the ThreadPoolExecutor. + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dag : DAG + The dag fixture defined in conftest.py. + reverse : bool + Parameterized value for the execute__tasks_parallel reverse parameter. + """ + futures = MagicMock() + futures.values = MagicMock(return_value = (x := MagicMock())) + y = MagicMock() + y.__enter__ = MagicMock(return_value=y) + executor_mock.return_value = y + with patch('workflow_engine.workflow_processor.concurrent.futures.wait') as wait_mock, \ + patch.object(w_processor, 'generate_futures', return_value=futures) as gen_futures_mock: + w_processor.execute_tasks_parallel(dag, reverse=reverse) + logger_mock.info.assert_called_once_with("Executing tasks in parallel.") + executor_mock.assert_called_once_with(max_workers=w_processor.threads) + wait_mock.assert_called_once_with(x) + gen_futures_mock.assert_called_once_with(dag, y, reverse) + + +@pytest.mark.parametrize('reverse', [False, True]) +@pytest.mark.parametrize('logger_mock, w_processor, dag',[({}, {}, {})], + indirect=["dag", "w_processor", "logger_mock"]) +@patch('workflow_engine.workflow_processor.concurrent.futures.ThreadPoolExecutor') +def test_execute_tasks_parallel_ko(executor_mock: MagicMock, logger_mock: MagicMock, w_processor: WorkflowProcessor, + dag: DAG, reverse: bool): + """Test WorkfowProcess.execute_task_parallel function error flow. + Check function call message loggin and calls when the KeyboardInterrupt is generated while waiting the subprocess + to finish execution. + + Parameters + ---------- + executor_mock : MagicMock + not used, just patched + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dag : DAG + The dag fixture defined in conftest.py. + reverse : bool + Parameterized value for the execute__tasks_parallel reverse parameter. + """ + execute_parallel_mock = MagicMock() + def patch_recursive_and_return_exception(_): + w_processor.execute_tasks_parallel = execute_parallel_mock + raise KeyboardInterrupt() + + with patch('workflow_engine.workflow_processor.concurrent.futures.wait', + side_effect=patch_recursive_and_return_exception), \ + patch.object(w_processor, 'generate_futures'): + w_processor.execute_tasks_parallel(dag, reverse=reverse) + logger_mock.info.assert_called_once_with("Executing tasks in parallel.") + logger_mock.error.assert_called_once_with("User interrupt detected. Aborting execution...") + execute_parallel_mock.assert_called_once_with(dag, reverse=True) + + +@pytest.mark.parametrize('w_processor', + [{'task_collection': [ + {'task': 'task1'}, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],}, + ], + indirect=True) +def test_generate_futures(w_processor: WorkflowProcessor): + """Test WorkfowProcess.generate_futures function without reverse. + Check the futures returned by the method. + + Parameters + ---------- + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + """ + def submit_execute_task_side_effect(_, dag: DAG, task, __): + dag.set_status(task['task'], 'successful') + return Future() + + executor = MagicMock() + executor.submit.side_effect=submit_execute_task_side_effect + dag = DAG(task_collection=w_processor.task_collection) + futures = w_processor.generate_futures(dag, executor=executor) + assert len(futures) == len(w_processor.task_collection) and \ + all(isinstance(element, Future) for element in futures.values()) + + +@pytest.mark.parametrize('w_processor', + [{'task_collection': [ + {'task': 'task1'}, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],}, + ], + indirect=True) +def test_generate_futures_reverse(w_processor: WorkflowProcessor): + """Test WorkfowProcess.generate_futures function with reverse True. + Check that set_status with successful is called for the tasks. + + Parameters + ---------- + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + """ + + def set_status_side_effect(task, status): + dag.finished_tasks_status[status].add(task) + dag.dag.done(task) + + executor = MagicMock() + dag = DAG(task_collection=w_processor.task_collection, reverse=True) + with patch.object(dag, 'set_status', side_effect=set_status_side_effect) as set_status_mock: + futures = w_processor.generate_futures(dag, executor=executor, reverse=True) + calls = [call(task['task'], 'successful') for task in w_processor.task_collection] + set_status_mock.assert_has_calls(calls, any_order=True) + + +@pytest.mark.parametrize('dry_run', [False, True]) +@pytest.mark.parametrize('logger_mock, w_processor', + [({}, { + 'task_collection': [ + {'task': 'task1'}, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],})], + indirect=True) +def test_run(logger_mock: MagicMock, w_processor: WorkflowProcessor, dry_run: bool): + """Test WorkfowProcess.run function. + Check log message and execute_tasks_parallel call. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dry_run : bool + Parameterized value to test the run method. + """ + def dag_constructor(_, reverse=False): + return reverse_dag if reverse else dag + + w_processor.dry_run = dry_run + dag = DAG(w_processor.task_collection) + reverse_dag = DAG(w_processor.task_collection, reverse=True) + with patch.object(w_processor, 'execute_tasks_parallel') as exec_tasks_mock, \ + patch('workflow_engine.workflow_processor.DAG', side_effect=dag_constructor) as dag_mock: + w_processor.run() + if dry_run: + dag_mock.assert_called_once_with(w_processor.task_collection) + logger_mock.info.assert_called_once_with("Execution plan: %s", json.dumps(dag.get_execution_plan(), indent=2)) + else: + logger_mock.info.assert_has_calls([call("Executing DAG tasks."), call("Executing Reverse DAG tasks.")]) + exec_tasks_mock.assert_has_calls([call(dag), call(reverse_dag, reverse=True)]) + dag_mock.assert_has_calls([call(w_processor.task_collection), call(w_processor.task_collection, reverse=True)]) + + +@pytest.mark.parametrize('logger_mock, w_processor', [({}, {})], indirect=['logger_mock', 'w_processor']) +def test_handle_interrupt(logger_mock: MagicMock, w_processor: WorkflowProcessor): + """Test WorkfowProcess.handle_interrupt function. + Check logging when the handle_interrupt is called. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + """ + with pytest.raises(KeyboardInterrupt, match="User interrupt detected. End process..."): + w_processor.handle_interrupt(0, 0) + logger_mock.error.assert_called_once_with("User interrupt detected. End process...") diff --git a/deployability/modules/workflow_engine/workflow_processor.py b/deployability/modules/workflow_engine/workflow_processor.py index dfbf4610c9..85635aa6c9 100755 --- a/deployability/modules/workflow_engine/workflow_processor.py +++ b/deployability/modules/workflow_engine/workflow_processor.py @@ -330,7 +330,7 @@ def execute_tasks_parallel(self, dag: DAG, reverse: bool = False) -> None: logger.error("User interrupt detected. Aborting execution...") self.execute_tasks_parallel(dag, reverse=True) - def generate_futures(self, dag, executor, reverse: bool = False): + def generate_futures(self, dag: DAG, executor, reverse: bool = False): futures = {} while True: From 75e1e789a88d0a72b2523cec6e7df63385f5b840 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Tue, 9 Apr 2024 18:39:22 -0300 Subject: [PATCH 003/195] Fixed aio reference to assistant --- deployability/modules/provision/handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/provision/handler.py b/deployability/modules/provision/handler.py index e125d2284a..1f39e13b0c 100644 --- a/deployability/modules/provision/handler.py +++ b/deployability/modules/provision/handler.py @@ -78,7 +78,7 @@ def _get_templates_order(self) -> list[str]: match self.method: case 'package' if self.action == "install": return ["set_repo.j2", "install.j2", "register.j2", "service.j2"] - case 'aio': + case 'assistant': return ["download.j2", f"{self.action}.j2"] case 'source': # This will be kept as it could be used in the wazuh installation from sources. From e028d3b9acd30336c5991522b671e5e0a428c88c Mon Sep 17 00:00:00 2001 From: Carlos Bordon <64099752+c-bordon@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:15:24 -0300 Subject: [PATCH 004/195] Update deployability/modules/allocation/README.MD Co-authored-by: Raul Del Pozo Moreno --- deployability/modules/allocation/README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index fda5a9280d..96212b4b61 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -9,7 +9,7 @@ The Allocation module allows you to create and destroy VMs both locally and in A The execution of the allocation is carried out through the Workflow engine library, or by executing them manually through commands. Execution can be done from any operating system. -Initially, You must install Python libraries. We recommend to use virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. +Initially, you have to install the required Python libraries. We recommend using virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. 1. Activate the environment: From 8e9697773bdb6e771bdf0fcd9b3280c92eb21cd5 Mon Sep 17 00:00:00 2001 From: Carlos Bordon <64099752+c-bordon@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:16:01 -0300 Subject: [PATCH 005/195] Update deployability/modules/allocation/README.MD Co-authored-by: Raul Del Pozo Moreno --- deployability/modules/allocation/README.MD | 1 - 1 file changed, 1 deletion(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 96212b4b61..fb3bf57fce 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -26,7 +26,6 @@ Initially, you have to install the required Python libraries. We recommend using cd wazuh-qa git checkout {project-branch} ``` -> Note: temporary dev project-branch is [enhancement/4495-DTT1](https://github.com/wazuh/wazuh-qa/tree/enhancement/4495-DTT1) 3. Install requirements: From 37677366c42a001c66eb737d3a5fad1db572ca3c Mon Sep 17 00:00:00 2001 From: Carlos Bordon <64099752+c-bordon@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:16:21 -0300 Subject: [PATCH 006/195] Update deployability/modules/allocation/README.MD Co-authored-by: Raul Del Pozo Moreno --- deployability/modules/allocation/README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index fb3bf57fce..f6e580bcd3 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -56,7 +56,7 @@ Now, it is possible to use the Worklow engine library to launch the provision mo ```bash version: 0.1 - description: This workflow is used to test agents deployment por DDT1 PoC + description: This workflow is used to test agents' deployment for DDT1 PoC variables: agent-os: - linux-ubuntu-18.04-amd64 From 8aa685886c46ff108d079993a405e5c94c8bedb9 Mon Sep 17 00:00:00 2001 From: Carlos Bordon <64099752+c-bordon@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:16:49 -0300 Subject: [PATCH 007/195] Update deployability/modules/allocation/README.MD Co-authored-by: Raul Del Pozo Moreno --- deployability/modules/allocation/README.MD | 1 - 1 file changed, 1 deletion(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index f6e580bcd3..7b672184cb 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -157,7 +157,6 @@ Now, it is possible to use the Worklow engine library to launch the provision mo python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml ``` - > Note The command execution can also be mediated through Jenkins. #### Manual execution of the Allocation module From 32f47ec80981a626202c04b51ca7cf7ed272af0b Mon Sep 17 00:00:00 2001 From: Carlos Bordon <64099752+c-bordon@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:17:16 -0300 Subject: [PATCH 008/195] Update deployability/modules/allocation/README.MD Co-authored-by: Raul Del Pozo Moreno --- deployability/modules/allocation/README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 7b672184cb..11c8f855cc 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -260,7 +260,7 @@ If one wishes to execute the allocaation module without installing the Workflow This argument allows us to define in which directory the files referring to the VM will be generated. By default, **/tmp/wazuh-qa** - --label-issue - This argument is only used in the case of AWS and is not mandatory, it allows us to create a label to reference the created instance to an issue on GitHub. It has to be a GitHub URL of the Wazuh repository, for example: **https://github.com/wazuh/internal-devel-requests/issues/1008** + This argument is only used in the case of AWS and is not mandatory, it allows us to create a label to reference the created instance to an issue on GitHub. It has to be a GitHub URL of a Wazuh repository, for example: **https://github.com/wazuh/internal-devel-requests/issues/1008** - --label-team This argument it is mandatory for AWS deploy, allows you to set the team that owns the VM to be able to track it. The valid options are: **qa**, **core**, **framework**, **devops**, **frontend**, **operations**, **cloud**, **threat-intel**, **marketing**, **documentation** From ee7887d7a455888610ec83b2a57d513e0be1c3dd Mon Sep 17 00:00:00 2001 From: c-bordon Date: Wed, 10 Apr 2024 16:39:11 -0300 Subject: [PATCH 009/195] The changes suggested in the PR are applied --- .../modules/allocation/Allocation.drawio.zip | Bin 1821 -> 0 bytes .../modules/allocation/Allocation.jpg | Bin 101220 -> 0 bytes deployability/modules/allocation/README.MD | 4 ++-- .../modules/allocation/static/specs/os.yml | 2 +- .../allocation/static/templates/vagrant.j2 | 2 -- .../modules/allocation/vagrant/provider.py | 2 +- 6 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 deployability/modules/allocation/Allocation.drawio.zip delete mode 100644 deployability/modules/allocation/Allocation.jpg diff --git a/deployability/modules/allocation/Allocation.drawio.zip b/deployability/modules/allocation/Allocation.drawio.zip deleted file mode 100644 index cffbaaedc3a60cf466e530fe430621d78e77db03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1821 zcmV+&2jcipO9KQH00ICA018QjSbP64hmi&V0J}B-02KfL06}bQZ)0I}X>V>WWO8A5 zX>TrgZEWRRU2~f_6n*cnVAz>X_rW&e+i~3)ubZTsb*A39ZPLDE6p(E;0%kF)epC+-lO>SaZBoXK|GUY zws~plEs0JJfU4J(U!`N@T$6^X0EPbn>BysKNM?A}qPHwhVMlmnC=JmMztmr4bc(0BK|3-SArj zKG#v~xhP6H7$KN>VS3<@AV3aF+K^H*O-~OAPVW-(X{{Oq2jcAL+;iEuE!&2Y9)CjK zXe{o;+2VGAqLVkGF?7jPG*bi|10{ql4-!aSgmHqLk8c$BBtJ?MJC+X$YkCjcOgjQ6 z?nSTb+Q7M~=_WS^#4(>apADM}2$aNu%SEHKrelwxK?ohxo^oPaW9H*FXl1_!5@RwX zsFt&zO-+G_j~Jaxyi?=T;znp1vNTPZ0jkQ+jTf{ug&82HceJ?ZHi||H;}N)2eFX=( z+&D>-P&Otb5c&0GFXQKn}I~1V=MZ1|R8H zaN7IL{o#%}7w)_h)`Vy8($T=e%apotkDslatZqEuW~H`UI^XaN%}ljB`_^S&{O+{( zm06Qyz|#8)X?T+N<*(q^bSQ=MWJE~3fJHy`CwJ|Op1jWW+kro|W^$)n^J!!gTG#BQ zgxpd4Mjn2||8=|TSZFN|uoiB=eXlRG`+|mlLP{olA3^_ZEL+ck4^^#}5N+oRueDrw zmHQT6Yqju#r$&Tg!-Iz+f>Oh+6Cc&;_ z8Ng|mH*rwog@H!S}{ee18PI85rm9|I4 zW!cO*q1Qu9rqWV*8^*wHca0dRKQ6c1K}Dy`5_bza&yWw(sHVcD87o45FRJTR6p}z` zNK!TjjQ{x4JF3OC%jVgz(``aPao;n4~9a5nomgB%p+|R(e*{w>etKr@$9xi3bJeT3p@`!$s$c zNDi1SIpcmqu2ITbK)8r95|cxSy;0D1IERR~1X-Q~aVI(Hd#=mZzn_hfnd$LbQx^|r zw5GbEx|w5I+jdCJLP-DTBH5$diV~K@<93aQgAIV2#@#}>X5K248(W5+dD%DdY%^dR zIa;?X5{n$-a-JXYU=HMBO>>7?MdoOo^T9L1uU7*~Kl0b?9j-Bj(5)48 zFPQMT5<0KXeY~B;rj+b;xDcyMg#~hZMFgYJx!xWL>)KvdQ^01ks%Ip|%PMbyrl66-5=f6JqZWfj&+;81Y_+h)TxW@tYJMA>XLl#Q6tTYS4iFXzsbgajk2-8 z&SW#_&>#H^(+aU`AD;BS9b3jlu5{lgQ$+h+=@ZW`qo?KASw4}o_C2YEy{M`f=GOD+ zLeN=-{v6)KW62AK6iV-W=zJdhx+mzM8t7SVnxh>s=z6igAH;e^#?Du%9f1$Vg#EBs0OHS(xe&*RSCP}&7QTJ`(@jqIzX zWJk053M=`TtFic+>%$w-DZW_y@a8{IO928u0~7!P00;mINrYH?|1gJ<1^@uNHUIz> z00000000000002AfdBvi06}bQZ)0I}X>V>WWO8A5X>TrgZER3W1qJ{B000310RTe) L006`W00000+52ef diff --git a/deployability/modules/allocation/Allocation.jpg b/deployability/modules/allocation/Allocation.jpg deleted file mode 100644 index 59dd4269c7e1b870bd8bd4b407f9d3834d59b628..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101220 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 diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 11c8f855cc..28b15b513c 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -331,10 +331,10 @@ python3 modules/allocation/main.py --action create --provider vagrant --size lar #### Diagram -![image](Allocation.jpg) +![image](https://github.com/wazuh/wazuh-qa/assets/64099752/d302f96b-70c0-4fbc-869b-914973ee8186) -[Allocation.drawio.zip](Allocation.drawio.zip) +[Allocation.drawio.zip](https://github.com/wazuh/wazuh-qa/files/14868775/Allocation.drawio.zip) ### License diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index c493f3beb2..b0cd4e2063 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -368,7 +368,7 @@ aws: ami: ami-0a747df120215911a zone: us-east-1 user: Administrator - windows-desktop-11-amd64: # update with the correct AMI for userData + windows-desktop-11-amd64: ami: ami-09d8cef159442d5b0 zone: us-east-1 user: Jenkins diff --git a/deployability/modules/allocation/static/templates/vagrant.j2 b/deployability/modules/allocation/static/templates/vagrant.j2 index f151640214..e66a13dfc4 100755 --- a/deployability/modules/allocation/static/templates/vagrant.j2 +++ b/deployability/modules/allocation/static/templates/vagrant.j2 @@ -17,8 +17,6 @@ Vagrant.configure("2") do |config| config.vm.network "private_network", ip:"{{ config.ip }}" config.ssh.forward_agent = true # Create a file to indicate that the VM has been initialized - # This is required to correctly get the ssh-config of the VM - # TODO: find a better solution init_indicator = "./init" if not ::File.exists?(init_indicator) File.write(init_indicator, "initialized") diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 2484809204..6234cf5a87 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -361,7 +361,7 @@ def __remote_host(arch: str, action: str, os: str = None, instance_dir: Path = N ssh_user = client.get_secret_value(SecretId='devops_ppc64_centos_jenkins_user')['SecretString'] remote_host_parameters['host_provider'] = 'centos' except Exception as e: - raise ValueError('Could not get Centos ppc64 server IP: ' + str(e) + '.') + raise ValueError('Could not get CentOS ppc64 server IP: ' + str(e) + '.') try: tn = Telnet(server_ip, server_port, timeout) From 13137ceaecde266b9acd511e15e788463f7a80ba Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Tue, 9 Apr 2024 17:07:04 -0300 Subject: [PATCH 010/195] Add fix for SUSE and example template and unit test --- .../modules/allocation/Allocation.drawio.zip | Bin 0 -> 1821 bytes .../modules/allocation/Allocation.jpg | Bin 0 -> 101220 bytes deployability/modules/allocation/README.MD | 345 ++++++++++++++++ .../allocation/aws/helpers/userData.sh | 2 +- .../modules/provision/tests/TESTING-README.md | 133 ++++++ .../modules/provision/tests/conftest.py | 42 ++ .../modules/provision/tests/test_actions.py | 155 +++++++ .../modules/provision/tests/test_handler.py | 173 ++++++++ .../modules/provision/tests/test_models.py | 54 +++ .../modules/provision/tests/test_provision.py | 294 +++++++++++++ .../examples/test/aws/test-agent-susse.yaml | 122 ++++++ .../workflow_engine/requirements-dev.txt | 2 + .../workflow_engine/tests/TESTING-README.md | 167 ++++++++ .../modules/workflow_engine/tests/conftest.py | 75 ++++ .../tests/data/wf-ko-no-path-on-cleanup.yaml | 169 ++++++++ .../tests/data/wf-ko-no-path-on-do.yaml | 169 ++++++++ .../tests/data/wf-ko-schema-error.yaml | 156 +++++++ .../workflow_engine/tests/data/wf-ok.yaml | 170 ++++++++ .../modules/workflow_engine/tests/test_dag.py | 294 +++++++++++++ .../tests/test_schema_validator.py | 119 ++++++ .../workflow_engine/tests/test_task.py | 114 ++++++ .../tests/test_workflow_file.py | 271 ++++++++++++ .../tests/test_workflow_processor.py | 385 ++++++++++++++++++ .../workflow_engine/workflow_processor.py | 2 +- 24 files changed, 3411 insertions(+), 2 deletions(-) create mode 100644 deployability/modules/allocation/Allocation.drawio.zip create mode 100644 deployability/modules/allocation/Allocation.jpg create mode 100644 deployability/modules/allocation/README.MD create mode 100644 deployability/modules/provision/tests/TESTING-README.md create mode 100644 deployability/modules/provision/tests/conftest.py create mode 100644 deployability/modules/provision/tests/test_actions.py create mode 100644 deployability/modules/provision/tests/test_handler.py create mode 100644 deployability/modules/provision/tests/test_models.py create mode 100644 deployability/modules/provision/tests/test_provision.py create mode 100644 deployability/modules/workflow_engine/examples/test/aws/test-agent-susse.yaml create mode 100644 deployability/modules/workflow_engine/requirements-dev.txt create mode 100644 deployability/modules/workflow_engine/tests/TESTING-README.md create mode 100644 deployability/modules/workflow_engine/tests/conftest.py create mode 100644 deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml create mode 100644 deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml create mode 100644 deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml create mode 100644 deployability/modules/workflow_engine/tests/data/wf-ok.yaml create mode 100644 deployability/modules/workflow_engine/tests/test_dag.py create mode 100644 deployability/modules/workflow_engine/tests/test_schema_validator.py create mode 100644 deployability/modules/workflow_engine/tests/test_task.py create mode 100644 deployability/modules/workflow_engine/tests/test_workflow_file.py create mode 100644 deployability/modules/workflow_engine/tests/test_workflow_processor.py diff --git a/deployability/modules/allocation/Allocation.drawio.zip b/deployability/modules/allocation/Allocation.drawio.zip new file mode 100644 index 0000000000000000000000000000000000000000..cffbaaedc3a60cf466e530fe430621d78e77db03 GIT binary patch literal 1821 zcmV+&2jcipO9KQH00ICA018QjSbP64hmi&V0J}B-02KfL06}bQZ)0I}X>V>WWO8A5 zX>TrgZEWRRU2~f_6n*cnVAz>X_rW&e+i~3)ubZTsb*A39ZPLDE6p(E;0%kF)epC+-lO>SaZBoXK|GUY zws~plEs0JJfU4J(U!`N@T$6^X0EPbn>BysKNM?A}qPHwhVMlmnC=JmMztmr4bc(0BK|3-SArj zKG#v~xhP6H7$KN>VS3<@AV3aF+K^H*O-~OAPVW-(X{{Oq2jcAL+;iEuE!&2Y9)CjK zXe{o;+2VGAqLVkGF?7jPG*bi|10{ql4-!aSgmHqLk8c$BBtJ?MJC+X$YkCjcOgjQ6 z?nSTb+Q7M~=_WS^#4(>apADM}2$aNu%SEHKrelwxK?ohxo^oPaW9H*FXl1_!5@RwX zsFt&zO-+G_j~Jaxyi?=T;znp1vNTPZ0jkQ+jTf{ug&82HceJ?ZHi||H;}N)2eFX=( z+&D>-P&Otb5c&0GFXQKn}I~1V=MZ1|R8H zaN7IL{o#%}7w)_h)`Vy8($T=e%apotkDslatZqEuW~H`UI^XaN%}ljB`_^S&{O+{( zm06Qyz|#8)X?T+N<*(q^bSQ=MWJE~3fJHy`CwJ|Op1jWW+kro|W^$)n^J!!gTG#BQ zgxpd4Mjn2||8=|TSZFN|uoiB=eXlRG`+|mlLP{olA3^_ZEL+ck4^^#}5N+oRueDrw zmHQT6Yqju#r$&Tg!-Iz+f>Oh+6Cc&;_ z8Ng|mH*rwog@H!S}{ee18PI85rm9|I4 zW!cO*q1Qu9rqWV*8^*wHca0dRKQ6c1K}Dy`5_bza&yWw(sHVcD87o45FRJTR6p}z` zNK!TjjQ{x4JF3OC%jVgz(``aPao;n4~9a5nomgB%p+|R(e*{w>etKr@$9xi3bJeT3p@`!$s$c zNDi1SIpcmqu2ITbK)8r95|cxSy;0D1IERR~1X-Q~aVI(Hd#=mZzn_hfnd$LbQx^|r zw5GbEx|w5I+jdCJLP-DTBH5$diV~K@<93aQgAIV2#@#}>X5K248(W5+dD%DdY%^dR zIa;?X5{n$-a-JXYU=HMBO>>7?MdoOo^T9L1uU7*~Kl0b?9j-Bj(5)48 zFPQMT5<0KXeY~B;rj+b;xDcyMg#~hZMFgYJx!xWL>)KvdQ^01ks%Ip|%PMbyrl66-5=f6JqZWfj&+;81Y_+h)TxW@tYJMA>XLl#Q6tTYS4iFXzsbgajk2-8 z&SW#_&>#H^(+aU`AD;BS9b3jlu5{lgQ$+h+=@ZW`qo?KASw4}o_C2YEy{M`f=GOD+ zLeN=-{v6)KW62AK6iV-W=zJdhx+mzM8t7SVnxh>s=z6igAH;e^#?Du%9f1$Vg#EBs0OHS(xe&*RSCP}&7QTJ`(@jqIzX zWJk053M=`TtFic+>%$w-DZW_y@a8{IO928u0~7!P00;mINrYH?|1gJ<1^@uNHUIz> z00000000000002AfdBvi06}bQZ)0I}X>V>WWO8A5X>TrgZER3W1qJ{B000310RTe) L006`W00000+52ef literal 0 HcmV?d00001 diff --git a/deployability/modules/allocation/Allocation.jpg b/deployability/modules/allocation/Allocation.jpg new file mode 100644 index 0000000000000000000000000000000000000000..59dd4269c7e1b870bd8bd4b407f9d3834d59b628 GIT binary patch literal 101220 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 literal 0 HcmV?d00001 diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD new file mode 100644 index 0000000000..fda5a9280d --- /dev/null +++ b/deployability/modules/allocation/README.MD @@ -0,0 +1,345 @@ +## Allocation Module + +### User documentation + +The Allocation module allows you to create and destroy VMs both locally and in AWS, it also gives the possibility of creating macOS VMs on Wazuh infrastructure or PowerPC VMs, as long as you have the necessary permissions to do so. VMs can be AMD64, ARM64, Windows, macOS (Intel and ARM), and PowerPC (CentOS and Debian). + +#### Set the environment + +The execution of the allocation is carried out through the Workflow engine library, or by executing them manually through commands. +Execution can be done from any operating system. + +Initially, You must install Python libraries. We recommend to use virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. + +1. Activate the environment: + + ```bash + source {venv directory}/bin/activate + ``` + +2. Clone the `wazuh-qa` repository: + + Navigate to the project directory and switch to the project branch: + + ```bash + git clone https://github.com/wazuh/wazuh-qa.git + cd wazuh-qa + git checkout {project-branch} + ``` +> Note: temporary dev project-branch is [enhancement/4495-DTT1](https://github.com/wazuh/wazuh-qa/tree/enhancement/4495-DTT1) + +3. Install requirements: + + ```bash + pip3 install -r deployability/deps/requirements.txt + ``` + +#### Use the Allocation module through the Workflow engine + +Now, it is possible to use the Worklow engine library to launch the provision module by doing the following steps: + +1. Install the Workflow engine library and its launcher: + + While in wazuh-qa: + + ```bash + cd modules + pip3 uninstall -y workflow_engine && pip3 install . + ``` + +2. Test Fixture to Execute: + + It will be necessary to create a fixture (yaml file) where the infrastructure, provisioning, and tests to be executed will be declared. + + >Note: It is possible to find some fixture examples in [deployability/modules/workflow_engine/examples/](../workflow_engine/examples) + + Example: + + ```bash + version: 0.1 + description: This workflow is used to test agents deployment por DDT1 PoC + variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + + tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + ``` + + Following the schema of the example: + + Configure the following parameters depending on your test case: + + ```yaml + variables/agent-os + variables/manager-os + infra-provider + working-dir + tasks + ``` + + Pay attention to the tasks: + + ```yaml + args + depends-on + ``` + + >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your test (allocation, provision, and test) + +3. Execution of Command (local): + + Execute the command by referencing the parameters required by the library (launcher). + + ```bash + python3 -m workflow_engine {.yaml fixture path} + ``` + + Example + + ```bash + python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml + ``` + + > Note The command execution can also be mediated through Jenkins. + +#### Manual execution of the Allocation module + +If one wishes to execute the allocaation module without installing the Workflow engine, they can proceed by using the launcher ([module/allocation/main.py](main.py)): + +1. Create + + While in wazuh-qa/deployability + +- Local deployment (Vagrant) + + ```bash + python3 modules/allocation/main.py --action create --provider '{{ vagrant }}' --size '{{ large }}' --composite-name '{{ composite-name }}' --inventory-output '{{ inventory }}' --track-output '{{ track }}' + + ``` + + Example: + ```bash + python3 modules/allocation/main.py --action create --provider vagrant --size large --composite-name linux-ubuntu-22.04-amd64 --inventory-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/inventory.yaml" --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" + ``` + +- AWS deployment + + ```bash + python3 modules/allocation/main.py --action create --provider '{{ aws }}' --size '{{ large }}' --composite-name '{{ composite-name }}' --inventory-output '{{ inventory }}' --track-output '{{ track }}' --label-termination-date '{{ termination-date }}' --label-team '{{ team }}' + + ``` + + >Note: In the case of AWS it is mandatory to define two arguments that are not necessary for Vagrant, --label-termination-date and --label-team. + --label-termination-date: This argument allows you to define the date on which the machine can be deleted. The allowed values are **1d** (where the **1** refers to the number of days the machine is needed) or with the following format **"2024-03-20 21:00:00"** + --label-team: This argument allows you to set the team that owns the VM to be able to track it. The valid options are: **qa**, **core**, **framework**, **devops**, **frontend**, **operations**, **cloud**, **threat-intel**, **marketing**, **documentation** + + + + Example: + ```bash + python3 modules/allocation/main.py --action create --provider aws --size large --composite-name linux-ubuntu-22.04-amd64 --inventory-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/inventory.yaml" --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" --label-termination-date "2024-03-20 21:00:00" --label-team devops + ``` + +2. Delete + + While in wazuh-qa/deployability + + ```bash + python3 modules/allocation/main.py --action delete --track-output '{{ track }}' + + ``` + + Example: + ```bash + python3 modules/allocation/main.py --action delete --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" + ``` + + >Note: The --track-output argument is mandatory for the delete action because this file contains all information of the VM that will be destroyed + +3. Arguments + + - --provider + This argument allows us to choose on with platform we will deploy our VM. The allowed values are **aws** or **vagrant**. + + - --size + This argument allows us to choose the resources of the VM. The allowed values are **micro**, **small**, **medium**, and **large**. + + - Vagrant equivalences + micro: CPU 1 - Memory 1024 + small: CPU 1 - Memory 2048 + medium: CPU 2 - Memory 4096 + large: CPU 4 - Memory 8192 + + - AWS equivalences + micro: t2.small (AMD64) - a1.medium (ARM64) + small: t3.small (AMD64) - a1.large (ARM64) + medium: t3a.medium (AMD64) - a1.xlarge (ARM64) + large: c5ad.xlarge (AMD64) - c6g.xlarge (ARM64) + + - --composite-name + This argument allows us to choose the OS, version, and architecture of the VM, example: **linux-centos-7-amd64** + + - --action + This argument defines the action that the module will perform. Allowed values are **create** or **delete**. By default: **create** + + - --ssh-key + This argument allows us to use a custom ssh key for the VM. + Considerations: + - must enter the path where the key is located, with the name of the complete key: **~/.ssh/allocation_test** + - on the same path you have to have the pair of keys (private and public key with the same name): **~/.ssh/allocation_test** **~/.ssh/allocation_test.pub** + - In the case of AWS, you must first create the key in the same region where you are going to deploy the instance. It is important that the key in AWS has the same name as your private key file. + + - --custom-provider-config + This argument allows us to provide a configuration file with all the VM definitions. + + - --track-output + This argument allows us to define which path we want the track file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/VAGRANT-F0753C57-713E-4294-9EA9-89D93D384844/track.yml** + + >Note: this argument is mandatory for delete action. + + - --inventory-output + This argument allows us to define which path we want the inventory file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/VAGRANT-F0753C57-713E-4294-9EA9-89D93D384844/inventory.yml** + + - --working-dir + This argument allows us to define in which directory the files referring to the VM will be generated. By default, **/tmp/wazuh-qa** + + - --label-issue + This argument is only used in the case of AWS and is not mandatory, it allows us to create a label to reference the created instance to an issue on GitHub. It has to be a GitHub URL of the Wazuh repository, for example: **https://github.com/wazuh/internal-devel-requests/issues/1008** + + - --label-team + This argument it is mandatory for AWS deploy, allows you to set the team that owns the VM to be able to track it. The valid options are: **qa**, **core**, **framework**, **devops**, **frontend**, **operations**, **cloud**, **threat-intel**, **marketing**, **documentation** + + - --label-termination-date + This argument it is mandatory for AWS deploy, allows you to define the date on which the machine can be deleted. The allowed values are **1d** (where the **1** refers to the number of days the machine is needed) or with the following format **"2024-03-20 21:00:00"** + + - --instance-name + This argument allows us to define a custom name for the instance, if this argument is not used, the instance name is defined by other parameters entered, such as --label-issue or --composite-name. +--- + +### Technical documentation + +The allocation module allows creating infrastructure on both AWS and locally (using Vagrant). + +Instructions can be initiated from the fixture and executed through the Workflow engine or executed using Python commands. + +In either case, the following information will be needed: + +```yaml + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" +``` + +In the provided fixture fragment, it is evident that to execute the Allocation module launcher ([allocation/main.py](main.py)), the action, provider, size, composite-name, inventory-output, and track-output must be specified. + +For manual execution, an example command would be: + +```bash +python3 modules/allocation/main.py --action create --provider vagrant --size large --composite-name linux-ubuntu-22.04-amd64 --inventory-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/inventory.yaml" --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" +``` + +#### General-specific functions + +- **Launcher** ([/wazuh-qa/deployability/modules/allocation/main.py](main.py)): The entry point for the workflow or the user who wishes to execute a test. + +- **Module functions** ([/wazuh-qa/deployability/modules/allocation/allocation.py](allocation.py)): Module-specific functions responsible for triggering the allocation. + +- **Static functions** ([/wazuh-qa/deployability/modules/allocation/static](static)): Templates and static information for infrastructure creation. + +#### Provider-specific functions + +- **AWS functions** ([/wazuh-qa/deployability/modules/allocation/aws](aws)): Module-specific functions responsible for triggering the allocation. + +- **Vagrant functions** ([/wazuh-qa/deployability/modules/allocation/vagrant](vagrant)): Module-specific functions responsible for triggering the allocation. + +- **Generic functions** ([/wazuh-qa/deployability/modules/allocation/generic](generic)): Module-specific functions responsible for triggering the allocation. + +#### Each provider will contain + +- **Modeler** (`/wazuh-qa/deployability/modules/allocation/{provider}/models.py`) +- **Credentials** (`/wazuh-qa/deployability/modules/allocation/{provider}/credentials.py`) +- **Provider** (`/wazuh-qa/deployability/modules/allocation/{provider}/provider.py`) +- **Information regarding the instance** (`/wazuh-qa/deployability/modules/allocation/{provider}/instance.py`) + +#### Diagram + +![image](Allocation.jpg) + + +[Allocation.drawio.zip](Allocation.drawio.zip) + + +### License + + +WAZUH Copyright (C) 2015 Wazuh Inc. (License GPLv2) diff --git a/deployability/modules/allocation/aws/helpers/userData.sh b/deployability/modules/allocation/aws/helpers/userData.sh index 062e50b179..07f556e6bd 100644 --- a/deployability/modules/allocation/aws/helpers/userData.sh +++ b/deployability/modules/allocation/aws/helpers/userData.sh @@ -128,7 +128,7 @@ if [ ! -r "/etc/os-release" ] || [ "$DIST_NAME" = "centos" ]; then fi fi -if [ "$DIST_NAME" = "amzn" ] || [ "$DIST_NAME" = "sles" ]; then +if [ "$DIST_NAME" = "amzn" ] || [ "$DIST_NAME" = "sles" ] || [ "$DIST_NAME" = "opensuse-leap" ]; then sudo sed -i "s/#Port\s22/Port ${SSH_PORT}/" /etc/ssh/sshd_config sudo systemctl restart sshd.service fi diff --git a/deployability/modules/provision/tests/TESTING-README.md b/deployability/modules/provision/tests/TESTING-README.md new file mode 100644 index 0000000000..e34369c48b --- /dev/null +++ b/deployability/modules/provision/tests/TESTING-README.md @@ -0,0 +1,133 @@ +# Provision module Unit Testing using Pytest + +The provision module includes pytest unit tests. + +## Requirements + +- Make sure you have Python installed on your system. You can download it from [python.org](https://www.python.org/downloads/). +- Clone the wazuh-qa repository in your local environment. +- Install the necessary dependencies by running: +```bash +git clone https://github.com/wazuh/wazuh-qa.git -b [your-branch] +cd wazuh-qa +pip install -r deployability/deps/requirements.txt +pip install -r deployability/deps/remote_requirements.txt +``` +- Configure the `PYTHONPATH` variable with the full path to the directory `deployability`, for example if you've +cloned the `wazuh-qa` repository into `/wazuh/wazuh-qa`, configure the `PYTHONPATH` in this way: +```bash +> pwd +/wazuh/wazuh-qa +> export PYTHONPATH=$PYTHONPATH:$PWD/deployability +> echo $PYTHONPATH +/wazuh/wazuh-qa/deployability +``` + +## Test Structure +The directory `deployability/modules/provision/tests/` contains the unit test files for the `provision` module. + +## Running Tests +To run the tests, make sure that your system meets the requirements by executing the following command from the project +root: + +```bash +pytest -vv deployability/modules/provision +``` +This command will run all tests in the `tests/` directory. Using additional arguments, You can also run specific tests +or directories. The output of this command looks like this: +```bash +pytest -vv deployability/modules/provision/ +=================================================================================== test session starts =================================================================================== +platform linux -- Python 3.10.13, pytest-8.0.1, pluggy-1.4.0 -- /home/marcelo/.pyenv/versions/wazuh-qa/bin/python +cachedir: .pytest_cache +rootdir: /home/marcelo/wazuh/wazuh-qa/deployability/modules +collected 51 items + +deployability/modules/provision/tests/test_actions.py::test_action_constructor[install-package0] PASSED [ 1%] +deployability/modules/provision/tests/test_actions.py::test_action_constructor[install-package1] PASSED [ 3%] +deployability/modules/provision/tests/test_actions.py::test_action_constructor[install-source] PASSED [ 5%] +deployability/modules/provision/tests/test_actions.py::test_action_execute[logger_mock0] PASSED [ 7%] +deployability/modules/provision/tests/test_actions.py::test_action_get_os_family[logger_mock0] PASSED [ 9%] +deployability/modules/provision/tests/test_actions.py::test_provision_handler_get_playbook PASSED [ 11%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-manager-install-package] PASSED [ 13%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-manager-install-assistant] PASSED [ 15%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-manager-install-source] PASSED [ 17%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-manager-uninstall-package] PASSED [ 19%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-manager-uninstall-assistant] PASSED [ 21%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-manager-uninstall-source] PASSED [ 23%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-agent-uninstall-source] PASSED [ 25%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor[logger_mock0-wazuh-agent-uninstall-assistant] PASSED [ 27%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor_fail[wazuh-manager-INSTALL-package-Unsupported action: INSTALL] PASSED [ 29%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor_fail[wazuh-manager-UNINSTALL-assistant-Unsupported action: UNINSTALL] PASSED [ 31%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor_fail[wazuh-manager-other-source-Unsupported action: other] PASSED [ 33%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor_fail[wazuh-manager-uninstall-other-Unsupported method: other] PASSED [ 35%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_constructor_fail[indexer-uninstall-assistant-Assistant actions is only supported for Wazuh components.] PASSED [ 37%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_path[wazuh-manager-package-install] PASSED [ 39%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_path[wazuh-manager-assistant-uninstall] PASSED [ 41%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_path[indexer-source-install] PASSED [ 43%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_order[wazuh-manager-package-install-expected_list0] PASSED [ 45%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_order[indexer-source-install-expected_list1] PASSED [ 47%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_order[wazuh-manager-assistant-uninstall-expected_list2] PASSED [ 49%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_get_templates_order_fail PASSED [ 50%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_generate_dict[wazuh-manager-package-install] PASSED [ 52%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_generate_dict[wazuh-manager-assistant-uninstall] PASSED [ 54%] +deployability/modules/provision/tests/test_handler.py::test_provision_handler_generate_dict[indexer-source-install] PASSED [ 56%] +deployability/modules/provision/tests/test_models.py::test_input_payload_constructor_components[True] PASSED [ 58%] +deployability/modules/provision/tests/test_models.py::test_input_payload_constructor_components[False] PASSED [ 60%] +deployability/modules/provision/tests/test_models.py::test_input_payload_constructor_dependencies[None] PASSED [ 62%] +deployability/modules/provision/tests/test_models.py::test_input_payload_constructor_dependencies[dependencies1] PASSED [ 64%] +deployability/modules/provision/tests/test_models.py::test_input_payload_constructor_dependencies[[{'manager': 'path/to/inventory.yaml', 'agent': 'path/to/inventory.yaml'}]] PASSED [ 66%] +deployability/modules/provision/tests/test_models.py::test_input_payload_constructor_fail PASSED [ 68%] +deployability/modules/provision/tests/test_provision.py::test_provision_constructor PASSED [ 70%] +deployability/modules/provision/tests/test_provision.py::test_provision_run[logger_mock0-provision_mock0-stats0] PASSED [ 72%] +deployability/modules/provision/tests/test_provision.py::test_provision_run_fail[logger_mock0-provision_mock0] PASSED [ 74%] +deployability/modules/provision/tests/test_provision.py::test_provision_get_components[provision_mock0-True] PASSED [ 76%] +deployability/modules/provision/tests/test_provision.py::test_provision_get_components[provision_mock1-False] PASSED [ 78%] +deployability/modules/provision/tests/test_provision.py::test_provision_update_status[provision_mock0] PASSED [ 80%] +deployability/modules/provision/tests/test_provision.py::test_provision_provision[provision_mock0] PASSED [ 82%] +deployability/modules/provision/tests/test_provision.py::test_provision_load_ansible_data[provision_mock0] PASSED [ 84%] +deployability/modules/provision/tests/test_provision.py::test_provision_load_ansible_data_fail[logger_mock0-provision_mock0-Exception] PASSED [ 86%] +deployability/modules/provision/tests/test_provision.py::test_provision_load_ansible_data_fail[logger_mock1-provision_mock1-FileNotFoundError] PASSED [ 88%] +deployability/modules/provision/tests/test_provision.py::test_provision_get_deps_ips[provision_mock0-True] PASSED [ 90%] +deployability/modules/provision/tests/test_provision.py::test_provision_get_deps_ips[provision_mock1-False] PASSED [ 92%] +deployability/modules/provision/tests/test_provision.py::test_provision_get_deps_ips_fail[logger_mock0-provision_mock0] PASSED [ 94%] +deployability/modules/provision/tests/test_provision.py::test_provision_validate_component_deps[logger_mock0-provision_mock0-wazuh-agent-dependencies0] PASSED [ 96%] +deployability/modules/provision/tests/test_provision.py::test_provision_validate_component_deps[logger_mock1-provision_mock1-wazuh-manager-dependencies1] PASSED [ 98%] +deployability/modules/provision/tests/test_provision.py::test_provision_validate_component_deps_fail[provision_mock0] PASSED [100%] + +==================================================================================== warnings summary ===================================================================================== +deployability/modules/provision/models.py:36 + /home/marcelo/wazuh/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('dependencies', pre=True) + +deployability/modules/provision/models.py:64 + /home/marcelo/wazuh/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('install', 'uninstall', pre=True) + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +============================================================================= 51 passed, 2 warnings in 0.16s ============================================================================== +``` + +The `.github/workflow/provision-unit-tests.yaml` automatically runs the unit tests in the GitHub environment. +The run results are shown in the `checks` tab or your GitHub pull request. + +## Relevant Files +- `tests/test_[test name].py`: all the unit test files start with a `test_` prefix. There is one unit test file for + each tested class. +- `tests/conftest.py`: contains the fixtures used throughout the unit tests. + +## Unit test development guidelines and recommendations +- Use Python coding style standards and recommendations to develop unit tests: snake case for all variable and function + names, maximum line length of 120 characters, two empty lines must separate each function, typing all your functions + and return values, create Docstring for each function or method with numpy style. +- Develop unit tests for each function or method of the module. +- Error flows are usually created in a second unit test with the suffix `_fail`. For example, the + `test_provision_handler_constructor` found in the `deployability/modules/provision/tests/test_handler.py` is the + unit test normal flow for the `ProvisionHandler` class constructor method. The + `test_provision_handler_constructor_fail` unit test implements the error flow. +- Use the pytest's decorator `@pytest.mark.parametrize` to implement test cases for the same unit test. +- Mock the object instance and functions used by your tested function using the `unitest.mock.patch` and + `unitest.mock.patch.object` functions or decorators. +- Try to factorize your testing code using `pytest.fixtures`. The shared fixtures are in the `conftest.py` file. In + many unit tests of this project, the fixtures implement a `request` object that receives parameters from the + `pytest.mark.parametrize`. diff --git a/deployability/modules/provision/tests/conftest.py b/deployability/modules/provision/tests/conftest.py new file mode 100644 index 0000000000..c4077be190 --- /dev/null +++ b/deployability/modules/provision/tests/conftest.py @@ -0,0 +1,42 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""Common unit test fixtures.""" +from unittest.mock import patch +import pytest + +from modules.provision.models import InputPayload +from modules.provision.provision import Provision + + +@pytest.fixture +def logger_mock(request): + """Fixture to mock common logger methods.""" + logger_to_patch = request.param.get('logger_to_patch', "modules.provision.utils.logger") + with patch(logger_to_patch) as l_mock: + patch.object(l_mock, 'warning') + patch.object(l_mock, 'info') + patch.object(l_mock, 'debug') + patch.object(l_mock, 'error') + yield l_mock + + +@pytest.fixture +def provision_mock(request) -> Provision: + """Fixture to create Provision class instances.""" + components = request.param.get('components', []) + action = request.param.get('action', 'install') + ansible_data = request.param.get('action', {}) + dependencies = request.param.get('dependencies', {}) + + component_info = "{'component':'component', 'type':'component_type'}" + payload = InputPayload(inventory="path", dependencies=dependencies, + install=[component_info], uninstall=[component_info]) + with patch('modules.provision.provision.Provision.get_components'), \ + patch('modules.provision.provision.Provision._Provision__load_ansible_data'): + provision = Provision(payload) + provision.components = components + provision.action = action + provision.ansible_data = ansible_data + + return provision diff --git a/deployability/modules/provision/tests/test_actions.py b/deployability/modules/provision/tests/test_actions.py new file mode 100644 index 0000000000..266a003db9 --- /dev/null +++ b/deployability/modules/provision/tests/test_actions.py @@ -0,0 +1,155 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""Unit Tests for the Action class""" + +from unittest.mock import patch, MagicMock, call +import pytest + +from modules.generic import Ansible +from modules.provision.actions import Action +from modules.provision.models import ComponentInfo +from modules.provision.handler import ProvisionHandler + + +@pytest.mark.parametrize('action, component_type', + [('install', 'package'), + ('install', 'package'), + ('install', 'source')]) +def test_action_constructor(action: str, component_type: str): + """Test Action constructor. + + Parameters + ---------- + component : str + component type + action : str + valid values are install or uninstall + """ + component_info = ComponentInfo(component='myComponent', type=component_type) + ansible_data = {'ansible_host': '', 'ansible_user': '', 'ansible_port': 0, 'ansible_ssh_private_key_file': ''} + with patch('pathlib.Path.exists', return_value=True): + action : Action = Action(action=action, component_info=component_info, ansible_data=ansible_data) + assert isinstance(action.handler, ProvisionHandler) + assert isinstance(action.ansible, Ansible) + + + +@pytest.mark.parametrize('logger_mock', + [{'logger_to_patch': 'modules.provision.actions.logger'}], indirect=True) +def test_action_execute(logger_mock: MagicMock): + """Test Action.run method. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py + """ + ansible_host = 'myHost' + return_tasks = ['task1', 'task2'] + + ansible_task = [{ + 'name': 'Capture ansible_os_family', + 'set_fact': { + 'ansible_os_family': "{{ ansible_facts['distribution_file_variety'] }}", + 'cacheable': 'yes' + } + }] + + playbook = { + 'hosts': ansible_host, + 'become': True, + 'gather_facts': True, + 'tasks': ansible_task + } + status_mock = MagicMock() + component_info = ComponentInfo(component='myComponent', type='package') + ansible_data = {'ansible_host': ansible_host, 'ansible_user': '', + 'ansible_port': 0, 'ansible_ssh_private_key_file': ''} + + action : Action = Action(action='install', component_info=component_info, ansible_data=ansible_data) + with patch('modules.provision.actions.Action._get_playbook', return_value=playbook) as get_playbook_mock, \ + patch.object(action.ansible, 'render_playbooks', return_value=return_tasks) as render_mock, \ + patch.object(action.ansible, 'run_playbook', return_value=status_mock) as run_playbook_mock, \ + patch('modules.provision.actions.Action._get_os_family', return_value='linux') as get_os_mock: + result = action.execute() + + get_playbook_mock.assert_called_once_with(return_tasks) + get_os_mock.assert_called_once() + render_mock.assert_called_once_with(action.handler.variables_dict) + run_playbook_mock.assert_called_once_with(playbook) + assert result == status_mock + logger_mock.debug.assert_has_calls([ + call(f"Render playbook with vars: {action.handler.variables_dict}."), + call(f"Tasks to execute: {return_tasks}.") + ]) + logger_mock.info.assert_called_once_with( + f"Execute {action.handler.action} for {action.handler.component_info.component}.") + + +@pytest.mark.parametrize('logger_mock', + [{'logger_to_patch': 'modules.provision.actions.logger'}], indirect=True) +def test_action_get_os_family(logger_mock: MagicMock): + """Test Action._get_os_falily method. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py + """ + ansible_host = 'myHost' + ansible_task = [{ + 'name': 'Capture ansible_os_family', + 'set_fact': { + 'ansible_os_family': "{{ ansible_facts['distribution_file_variety'] }}", + 'cacheable': 'yes' + } + }] + + playbook = { + 'hosts': ansible_host, + 'become': True, + 'gather_facts': True, + 'tasks': ansible_task + } + fact_cache_mock = MagicMock() + fact_cache_mock.get.return_value = 'os_family' + status_mock = MagicMock() + status_mock.get_fact_cache.return_value = fact_cache_mock + + component_info = ComponentInfo(component='myComponent', type='package') + ansible_data = {'ansible_host': ansible_host, 'ansible_user': '', + 'ansible_port': 0, 'ansible_ssh_private_key_file': ''} + + action : Action = Action(action='install', component_info=component_info, ansible_data=ansible_data) + with patch('modules.provision.actions.Action._get_playbook', return_value=playbook) as get_playbook_mock, \ + patch.object(action.ansible, 'run_playbook', return_value=status_mock) as run_playbook_mock: + result = action._get_os_family() + + get_playbook_mock.assert_called_once_with(ansible_task) + run_playbook_mock.assert_called_once_with(playbook) + status_mock.get_fact_cache.assert_called_once_with(host=action.ansible.ansible_data.ansible_host) + fact_cache_mock.get.assert_has_calls([call('ansible_os_family'), call('ansible_os_family')]) + + assert result == 'os_family' + logger_mock.debug.assert_has_calls([ + call(f"Get OS family for {action.ansible.ansible_data.ansible_host}."), + call("OS family: os_family.") + ]) + + +def test_provision_handler_get_playbook(): + """Test ProvisionHandler._get_playbook method.""" + tasks = ['task1', 'task2'] + component_info = ComponentInfo(component='myComponent', type='package') + ansible_data = {'ansible_host': 'ansible_host', 'ansible_user': '', + 'ansible_port': 0, 'ansible_ssh_private_key_file': ''} + action = Action(action='install', component_info=component_info, ansible_data=ansible_data) + result = action._get_playbook(tasks=tasks) + playbook = { + 'hosts': action.ansible.ansible_data.ansible_host, + 'become': True, + 'gather_facts': True, + 'tasks': tasks, + } + assert result == playbook diff --git a/deployability/modules/provision/tests/test_handler.py b/deployability/modules/provision/tests/test_handler.py new file mode 100644 index 0000000000..dd9d63d093 --- /dev/null +++ b/deployability/modules/provision/tests/test_handler.py @@ -0,0 +1,173 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""Unit Tests for the ProvisionHandler class.""" +from unittest.mock import patch, MagicMock +import pytest + +from modules.provision.handler import ProvisionHandler +from modules.provision.models import ComponentInfo + +@pytest.mark.parametrize('component, action, method', + [('wazuh-manager', 'install', 'package'), + ('wazuh-manager', 'install', 'assistant'), + ('wazuh-manager', 'install', 'source'), + ('wazuh-manager', 'uninstall', 'package'), + ('wazuh-manager', 'uninstall', 'assistant'), + ('wazuh-manager', 'uninstall', 'source'), + ('wazuh-agent', 'uninstall', 'source'), + ('wazuh-agent', 'uninstall', 'assistant'), +]) +@pytest.mark.parametrize('logger_mock', + [{'logger_to_patch': 'modules.provision.handler.logger'}], + indirect=True) +def test_provision_handler_constructor(component: str, action: str, method: str, logger_mock: MagicMock): + """Test ProvisionHandler constructor. + + Parameters + ---------- + component : str + component type + action : str + valid values are install or uninstall + method : str + valid values are package, assistant, source + logger_mock : MagicMock + logger fixture defined en conftest.py + """ + info = ComponentInfo(component=component, type='type', version='version', dependencies={}) + with patch('modules.provision.handler.ProvisionHandler._get_templates_path', + return_value='path1'), \ + patch('modules.provision.handler.ProvisionHandler._get_templates_order', + return_value=["set_repo.j2", "install.j2", "register.j2", "service.j2"]), \ + patch('modules.provision.handler.ProvisionHandler._generate_dict', return_value={'key':'value'}): + handler = ProvisionHandler(component_info=info, action=action, method=method) + + if action == 'uninstall' and method == 'source': + logger_mock.warning.assert_called_once_with("Uninstall from source not supported. Using package.") + method = 'package' + if 'wazuh-agent' in component and method == 'assistant': + logger_mock.warning.assert_called_once_with("Agent can not be installed from assistant. Using package.") + method = 'package' + assert handler.component_info == info + assert handler.action == action.lower() + assert handler.method == method.lower() + assert handler.templates_path == 'path1' + assert handler.templates_order == ["set_repo.j2", "install.j2", "register.j2", "service.j2"] + assert handler.variables_dict == {'key':'value'} + + +@pytest.mark.parametrize('component, action, method, to_match', + [('wazuh-manager', 'INSTALL', 'package', 'Unsupported action: INSTALL'), + ('wazuh-manager', 'UNINSTALL', 'assistant', 'Unsupported action: UNINSTALL'), + ('wazuh-manager', 'other', 'source', 'Unsupported action: other'), + ('wazuh-manager', 'uninstall', 'other', 'Unsupported method: other'), + ('indexer', 'uninstall', 'assistant', + "Assistant actions is only supported for Wazuh components."), +]) +def test_provision_handler_constructor_fail(component: str, action: str, method: str, to_match: str): + """Test ProvisionHandler constructor failure flows. + + Parameters + ---------- + component : str + component type + action : str + valid values are install or uninstall + method : str + valid values are package, assistant, source + """ + info = ComponentInfo(component=component, version='version', dependencies={}) + # Use Package instead of ComponentType class because it is not posible to instantiate a class with + # with abstract methods. + with pytest.raises(ValueError, match=to_match): + ProvisionHandler(component_info=info, action=action, method=method) + + +@pytest.mark.parametrize('component, method, action', + [('wazuh-manager', 'package', 'install'), + ('wazuh-manager', 'assistant', 'uninstall'), + ('indexer', 'source', 'install'), +]) +def test_provision_handler_get_templates_path(component: str, method: str, action:str): + """Test ProvisionHandler.get_templates_path method. + + Parameters + ---------- + component : str + component type + action : str + valid values are install or uninstall + method : str + valid values are package, assistant, source + """ + info = ComponentInfo(component=component, version='version', dependencies={}) + with patch('modules.provision.handler.ProvisionHandler._get_templates_order', + return_value=["set_repo.j2", "install.j2", "register.j2", "service.j2"]): + handler = ProvisionHandler(component_info=info, action=action, method=method) + assert handler.templates_path == f"{handler._base_templates_path}/{handler.method}/{handler.action}" + + +@pytest.mark.parametrize('component, method, action, expected_list', + [('wazuh-manager', 'package', 'install', + ["set_repo.j2", "install.j2", "register.j2", "service.j2"]), + ('indexer', 'source', 'install', ['indexer.j2']), + ('wazuh-manager', 'assistant', 'uninstall', []), +]) +def test_provision_handler_get_templates_order(component: str, method: str, action:str, expected_list: list): + """Test ProvisionHandler._get_templates_order method. + + Parameters + ---------- + component : str + component type + action : str + valid values are install or uninstall + method : str + valid values are package, assistant, source + expected_list : list + expected result + """ + info = ComponentInfo(component=component, version='version', dependencies={}) + with patch('pathlib.Path.exists', return_value=True): + handler = ProvisionHandler(component_info=info, action=action, method=method) + assert handler.templates_order == expected_list + + +def test_provision_handler_get_templates_order_fail(): + """Test ProvisionHandler._get_templates_order method failure flow.""" + info = ComponentInfo(component='indexer', version='version', dependencies={}) + with pytest.raises(ValueError, match="Component source file indexer.j2 not found."): + ProvisionHandler(component_info=info, action='install', method='source') + + +@pytest.mark.parametrize('component, method, action', + [('wazuh-manager', 'package', 'install'), + ('wazuh-manager', 'assistant', 'uninstall'), + ('indexer', 'source', 'install'), +]) +def test_provision_handler_generate_dict(component: str, method: str, action:str): + """Test ProvisionHandler._generate_dict method. + + Parameters + ---------- + component : str + component type + method : str + valid values are package, assistant, source + action : str + valid values are install or uninstall + """ + info = ComponentInfo(component=component, version='version', dependencies={}) + with patch('pathlib.Path.exists', return_value=True): + handler = ProvisionHandler(component_info=info, action=action, method=method) + expected_dict = { + 'component': handler.component_info.component, + 'version': handler.component_info.version, + 'live': handler.component_info.live, + 'type': handler.component_info.type, + 'dependencies': handler.component_info.dependencies or None, + 'templates_path': handler.templates_path, + 'templates_order': handler.templates_order or None + } + assert handler.variables_dict == expected_dict diff --git a/deployability/modules/provision/tests/test_models.py b/deployability/modules/provision/tests/test_models.py new file mode 100644 index 0000000000..7d1afec555 --- /dev/null +++ b/deployability/modules/provision/tests/test_models.py @@ -0,0 +1,54 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""modules.provision.models Unit tests.""" +from pathlib import Path +import pytest + +from modules.provision.models import InputPayload, ComponentInfo + + +@pytest.mark.parametrize('install', [(True), (False)]) +def test_input_payload_constructor_components(install:bool): + """Test InputPayload constructor install and unininstall parameters. + + Parameters + ---------- + install : bool + parameters for install and uninstall InputPayload constructor parameters. + """ + path = '/my_inventory_path' + components = [ + "{'component':'component_1', 'type':'component_type_1'}", + "{'component':'component_2'}", + "{'component':'linux wazuh-agent'}"] + payload = InputPayload(inventory=path, + install=components if install else [], + uninstall=[] if install else components) + assert payload.inventory == Path(path) + comp_list = payload.install if install else payload.uninstall + assert comp_list[0] == ComponentInfo(component='component_1', type='component_type_1') + assert comp_list[1] == ComponentInfo(component='component_2', type='package') + assert comp_list[2] == ComponentInfo(component='linux wazuh-agent', type='package') + + +@pytest.mark.parametrize('dependencies',[ + (None), + (["{'manager': 'path/to/inventory.yaml'}", "{'agent': 'path/to/inventory.yaml'}"]), + ("[{'manager': 'path/to/inventory.yaml', 'agent': 'path/to/inventory.yaml'}]")]) +def test_input_payload_constructor_dependencies(dependencies:str): + """Test InputPayload constructor dependencies parameters.""" + path = '/my_inventory_path' + components = ["{'component':'component_1', 'type':'component_type_1'}"] + payload = InputPayload(inventory=path, install=components, uninstall=[], dependencies=dependencies) + if dependencies: + assert payload.dependencies.get('manager') == 'path/to/inventory.yaml' + assert payload.dependencies.get('agent') == 'path/to/inventory.yaml' + else: + assert not payload.dependencies + + +def test_input_payload_constructor_fail(): + """Test InputPayload constructor invalid parameters.""" + with pytest.raises(ValueError, match='Invalid action: "install" or "uninstall" must be provided.'): + InputPayload(inventory='/path', install=[], uninstall=[]) diff --git a/deployability/modules/provision/tests/test_provision.py b/deployability/modules/provision/tests/test_provision.py new file mode 100644 index 0000000000..d08d9ef981 --- /dev/null +++ b/deployability/modules/provision/tests/test_provision.py @@ -0,0 +1,294 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""Unit Tests for the Provision class""" + +from typing import List +from unittest.mock import patch, MagicMock, call +import pytest + +from modules.provision.models import InputPayload, ComponentInfo +from modules.provision.provision import Provision + + +def test_provision_constructor(): + """Test Provision constructor.""" + component_info = "{'component':'component', 'type':'component_type'}" + payload = InputPayload(inventory="path", dependencies={}, install=[component_info], uninstall=[component_info]) + with patch('modules.provision.provision.Provision.get_components') as get_comp_mock, \ + patch('modules.provision.provision.Provision._Provision__load_ansible_data') as load_ansible_mock: + prov = Provision(payload=payload) + assert len(prov.summary) == 0 + get_comp_mock.assert_called_once_with(payload) + load_ansible_mock.assert_called_once_with(payload.inventory) + + +@pytest.mark.parametrize('logger_mock, provision_mock, stats', + [( + {'logger_to_patch': 'modules.provision.provision.logger'}, + {'components': [ + ComponentInfo(component='component_1', type='type_1'), + ComponentInfo(component='component_2', type='type_2')]}, + {'component_1': {'stat_component_1': 'status component_1'}, + 'component_2': {'stat_component_2': 'status component_2'}}) + ], + indirect=['logger_mock', 'provision_mock']) +def test_provision_run(logger_mock: MagicMock, provision_mock: Provision, stats: List[dict]): + """Test Provision.run method. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py. + provision_mock : Provision + provision fixture defined in conftest.py + stats : List[dict] + keys to update mocking the __provision method + """ + with patch.object(provision_mock, '_Provision__provision', + side_effect=lambda c: provision_mock.summary.update(stats[c.component])) as provision_method_mock: + provision_mock.run() + + assert provision_method_mock.call_count == 2 + logger_mock.info.assert_has_calls([ + call('Initiating provisionment.'), + call('Provisioning "component_1"...'), + call('Provision of "component_1" complete successfully.'), + call('Provisioning "component_2"...'), + call('Provision of "component_2" complete successfully.'), + call('All components provisioned successfully.') + ]) + logger_mock.debug.assert_has_calls([ + call(f'Running action {provision_mock.action} for components: {provision_mock.components}'), + call(f'Provision summary: {provision_mock.summary}') + ]) + + +@pytest.mark.parametrize('logger_mock, provision_mock', + [( + {'logger_to_patch': 'modules.provision.provision.logger'}, + {'components': [ + ComponentInfo(component='component_1', type='type_1'), + ComponentInfo(component='component_2', type='type_2')]}) + ], + indirect=['logger_mock', 'provision_mock']) +def test_provision_run_fail(logger_mock: MagicMock, provision_mock: Provision): + """Test Provision.run method failure flow. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py. + provision_mock : Provision + provision fixture defined in conftest.py + """ + + with patch.object(provision_mock, '_Provision__provision', + side_effect=[None, Exception('Provision generated Exception')]): + with pytest.raises(Exception) as exc_info: + provision_mock.run() + + assert logger_mock.info.call_count == 4 + logger_mock.error.assert_called_once_with(f'Error while provisioning "component_2": {exc_info.value}') + + +@pytest.mark.parametrize('provision_mock, install',[({}, True), ({}, False)], indirect=['provision_mock']) +def test_provision_get_components(provision_mock: Provision, install: bool): + """Test Provision.get_component method. + + Parameters + ---------- + provision_mock : Provision + provision fixture defined in conftest.py. + install : bool + parameterization of the InputPayload constructor install and uninstall parameters + """ + component_info = ["{'component':'component_1', 'type':'component_type_1'}", + "{'component':'component_2', 'type':'component_type_2'}"] + if install: + payload = InputPayload(inventory="path", dependencies={}, install=component_info, uninstall=[]) + else: + payload = InputPayload(inventory="path", dependencies={}, install=[], uninstall=component_info) + with patch.object(provision_mock, '_Provision__get_deps_ips') as get_deps_mock, \ + patch.object(provision_mock, '_Provision__validate_component_deps') as validate_mock: + provision_mock.get_components(payload=payload) + get_deps_mock.assert_called_once_with(payload.dependencies) + assert validate_mock.call_count == 2 + + +@pytest.mark.parametrize('provision_mock', [{}], indirect=['provision_mock']) +def test_provision_update_status(provision_mock: Provision): + """Test Provision.update_status method. + + Parameters + ---------- + provision_mock : Provision + provision fixture defined in conftest.py. + """ + status = MagicMock() + status.stats = {'status_1': 'status 1', 'status_2': 'status 2'} + provision_mock.update_status(status) + assert provision_mock.summary == status.stats + + +@pytest.mark.parametrize('provision_mock', [{}], indirect=['provision_mock']) +def test_provision_provision(provision_mock: Provision): + """Test Provision.__provision method. + + Parameters + ---------- + provision_mock : Provision + provision fixture defined in conftest.py. + """ + component = MagicMock() + status = MagicMock() + action = MagicMock() + action.execute.return_value = status + with patch('modules.provision.provision.Action', return_value=action) as action_mock, \ + patch.object(provision_mock, 'update_status') as update_mock: + provision_mock._Provision__provision(component=component) + action_mock.assert_called_once_with(provision_mock.action, component, provision_mock.ansible_data) + action.execute.assert_called_once() + update_mock.assert_called_once_with(status) + + +@pytest.mark.parametrize('provision_mock', [{}], indirect=['provision_mock']) +def test_provision_load_ansible_data(provision_mock: Provision): + """Test Provision.__load_ansible_data method. + + Parameters + ---------- + provision_mock : Provision + provision fixture defined in conftest.py. + """ + inventory = '/inventory_path' + with patch('modules.provision.provision.Utils.load_from_yaml') as load_yaml_mock: + provision_mock._Provision__load_ansible_data(inventory) + load_yaml_mock.assert_called_once_with(inventory) + + +@pytest.mark.parametrize('logger_mock, provision_mock, exc', + [({'logger_to_patch': 'modules.provision.provision.logger'}, {}, + Exception), + ({'logger_to_patch': 'modules.provision.provision.logger'}, {}, + FileNotFoundError)], + indirect=['logger_mock', 'provision_mock']) +def test_provision_load_ansible_data_fail(logger_mock: MagicMock, provision_mock: Provision, exc: Exception): + """Test Provision.__load_ansible_data method failure flow. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py. + provision_mock : Provision + provision fixture defined in conftest.py. + exc : Exception + expected exception + """ + inventory = '/inventory_path' + with pytest.raises(exc) as exc_info: + with patch('modules.provision.provision.Utils.load_from_yaml', side_effect=exc) as load_yaml_mock: + provision_mock._Provision__load_ansible_data(inventory) + if isinstance(exc_info.value, FileNotFoundError): + logger_mock.error.assert_called_once_with(f'Inventory file "{inventory}" not found.') + else: + logger_mock.error.assert_called_once_with(f'Error loading inventory file "{inventory}": {exc_info.value}') + + +@pytest.mark.parametrize('provision_mock, empty', [({}, True), ({}, False)], indirect=['provision_mock']) +def test_provision_get_deps_ips(provision_mock: Provision, empty: bool): + """Test Provision.__get_deps_ips method. + + Parameters + ---------- + provision_mock : Provision + provision fixture defined in conftest.py. + empty : bool + dependencies are empty is True else dependecies are defined. + """ + dependencies = {} + paths = [] + if not empty: + dependencies = {'dependency_1': '/path_1', 'dependency_2': '/path_2'} + m1 = MagicMock() + m1.exists.return_value = True + m2 = MagicMock() + m2.exists.return_value = True + paths = [m1, m2] + with patch('modules.provision.provision.Utils.load_from_yaml', side_effect=paths) as load_yaml_mock, \ + patch('modules.provision.provision.Path', side_effect=paths) as path_mock: + dependencies_ips = provision_mock._Provision__get_deps_ips(dependencies) + if empty: + assert not dependencies + else: + load_yaml_mock.assert_has_calls([call(m1, specific_key='ansible_host'), + call(m2, specific_key='ansible_host'),]) + path_mock.assert_has_calls([call(dependencies['dependency_1']), + call(dependencies['dependency_2'])]) + assert dependencies_ips == {'dependency_1': m1, 'dependency_2': m2} + + +@pytest.mark.parametrize('logger_mock, provision_mock', + [({'logger_to_patch': 'modules.provision.provision.logger'}, {})], + indirect=['provision_mock', 'logger_mock']) +def test_provision_get_deps_ips_fail(logger_mock: MagicMock, provision_mock: Provision): + """Test Provision.__get_deps_ips method failure flow. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py. + provision_mock : Provision + provision fixture defined in conftest.py. + """ + dependencies = {'dependency_1': '/path_1', 'dependency_2': '/path_2'} + m1 = MagicMock() + m1.exists.return_value = False + m1.__str__.return_value = "/path_1" + with pytest.raises(FileNotFoundError, match=f'Inventory file "{m1}" not found.') as exc_info, \ + patch('modules.provision.provision.Path', return_value=m1): + provision_mock._Provision__get_deps_ips(dependencies) + logger_mock.error.assert_called_once_with(f'Error getting dependency IP: {exc_info.value}') + + +@pytest.mark.parametrize('logger_mock, provision_mock, component_name, dependencies', + [({'logger_to_patch': 'modules.provision.provision.logger'}, {}, + 'wazuh-agent', {'manager': 'wazuh-manager'}), + ({'logger_to_patch': 'modules.provision.provision.logger'}, {}, + 'wazuh-manager', {'other': 'other'})], + indirect=['provision_mock', 'logger_mock']) +def test_provision_validate_component_deps(logger_mock: MagicMock, provision_mock: Provision, component_name: str, + dependencies: dict): + """Test Provision.__validate_component_deps method. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py. + provision_mock : Provision + provision fixture defined in conftest.py. + component_name : str + componente name. + dependencies : dict + ComponentInfo dependencies parameter. + """ + component = ComponentInfo(component=component_name, type='type_1', dependencies=dependencies) + provision_mock._Provision__validate_component_deps(component) + logger_mock.debug.assert_called_once_with( + f"Setting dependencies: {dependencies} for {component.component} component.") + + + +@pytest.mark.parametrize('provision_mock', [({})], + indirect=['provision_mock']) +def test_provision_validate_component_deps_fail(provision_mock: Provision): + """Test Provision.__validate_component_deps method failure flow. + + Parameters + ---------- + provision_mock : Provision + provision fixture defined in conftest.py. + """ + component = ComponentInfo(component='wazuh-agent', type='type_1', dependencies={'other': 'other'}) + with pytest.raises(ValueError, match='Dependency IP is required to install Wazuh Agent.'): + provision_mock._Provision__validate_component_deps(component) diff --git a/deployability/modules/workflow_engine/examples/test/aws/test-agent-susse.yaml b/deployability/modules/workflow_engine/examples/test/aws/test-agent-susse.yaml new file mode 100644 index 0000000000..55392f2c44 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/test/aws/test-agent-susse.yaml @@ -0,0 +1,122 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-opensuse-15-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + - component: tar + - component: curl + depends-on: + - "allocate-manager-{manager-os}" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: curl + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/requirements-dev.txt b/deployability/modules/workflow_engine/requirements-dev.txt new file mode 100644 index 0000000000..05a93e2895 --- /dev/null +++ b/deployability/modules/workflow_engine/requirements-dev.txt @@ -0,0 +1,2 @@ +-r ../../deps/requirements.txt +-r ../../deps/remote_requirements.txt \ No newline at end of file diff --git a/deployability/modules/workflow_engine/tests/TESTING-README.md b/deployability/modules/workflow_engine/tests/TESTING-README.md new file mode 100644 index 0000000000..9144e08dc3 --- /dev/null +++ b/deployability/modules/workflow_engine/tests/TESTING-README.md @@ -0,0 +1,167 @@ +# Workflow engine Unit Testing using Pytest + +The workflow_engine module includes pytest unit tests. + +## Requirements + +- Make sure you have Python installed on your system. You can download it from + [python.org](https://www.python.org/downloads/). +- Clone the wazuh-qa repository in your local environment. +- Install the necessary dependencies by running: +```bash +git clone https://github.com/wazuh/wazuh-qa.git -b [your-branch] +cd wazuh-qa +pip install -r deployability/modules/workflow_engine/requirements-dev.txt +``` +- Configure the `PYTHONPATH` variable with the full path to the directory `deployability/modules`, for example if you've +cloned the `wazuh-qa` repository into `/wazuh/wazuh-qa`, configure the `PYTHONPATH` in this way: +```bash +> pwd +/wazuh/wazuh-qa +> export PYTHONPATH=$PYTHONPATH:$PWD/deployability/modules +> echo $PYTHONPATH +/wazuh/wazuh-qa/deployability/modules +``` + +## Test Structure +The directory `deployability/modules/workflow_engine/tests/` contains the unit test files for the `workflow_engine` +module. + +## Running Tests +To run the tests, make sure that your system meets the requirements by executing the following command from the project +root: + +```bash +pytest -vv deployability/modules/workflow_engine +``` +This command will run all tests in the `tests/` directory. Using additional arguments, You can also run specific tests +or directories. The output of this command looks like this: +```bash +pytest -vv deployability/modules/workflow_engine +============================================================================================== test session starts ============================================================================================== +platform linux -- Python 3.10.13, pytest-7.1.2, pluggy-1.3.0 -- /usr/local/bin/python3 +cachedir: .pytest_cache +metadata: {'Python': '3.10.13', 'Platform': 'Linux-5.15.146.1-microsoft-standard-WSL2-x86_64-with-glibc2.31', 'Packages': {'pytest': '7.1.2', 'pluggy': '1.3.0'}, 'Plugins': {'anyio': '4.2.0', 'testinfra': '5.0.0', 'metadata': '3.0.0', 'html': '3.1.1'}} +rootdir: /home/marcelo/wazuh/wazuh-qa/deployability/modules +plugins: anyio-4.2.0, testinfra-5.0.0, metadata-3.0.0, html-3.1.1 +collected 92 items + +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_constructor[True] PASSED [ 1%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_constructor[False] PASSED [ 2%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[True-dag0] PASSED [ 3%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[True-dag1] PASSED [ 4%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[False-dag0] PASSED [ 5%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[False-dag1] PASSED [ 6%] +deployability/modules/workflow_engine/tests/test_dag.py::test_get_execution_plan[dag0] PASSED [ 7%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-failed-dag0] PASSED [ 8%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-canceled-dag0] PASSED [ 9%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-successful-dag0] PASSED [ 10%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-non_existing_status-dag0] FAILED [ 11%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[non_existing_task-successful-dag0] PASSED [ 13%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[non_existing_task-non_existing_status-dag0] FAILED [ 14%] +deployability/modules/workflow_engine/tests/test_dag.py::test_should_be_canceled[True-dag0] PASSED [ 15%] +deployability/modules/workflow_engine/tests/test_dag.py::test_should_be_canceled[False-dag0] PASSED [ 16%] +deployability/modules/workflow_engine/tests/test_dag.py::test_build_dag[dag0] PASSED [ 17%] +deployability/modules/workflow_engine/tests/test_dag.py::test_build_dag[dag1] PASSED [ 18%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-all-to_be_canceled0-dag0] PASSED [ 19%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-all-to_be_canceled0-dag1] PASSED [ 20%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-related-flows-to_be_canceled1-dag0] FAILED [ 21%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-related-flows-to_be_canceled1-dag1] FAILED [ 22%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-continue-to_be_canceled2-dag0] FAILED [ 23%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-continue-to_be_canceled2-dag1] FAILED [ 25%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-all-to_be_canceled3-dag0] FAILED [ 26%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-all-to_be_canceled3-dag1] FAILED [ 27%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-related-flows-to_be_canceled4-dag0] FAILED [ 28%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-related-flows-to_be_canceled4-dag1] FAILED [ 29%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-continue-to_be_canceled5-dag0] FAILED [ 30%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-continue-to_be_canceled5-dag1] FAILED [ 31%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-all-to_be_canceled6-dag0] PASSED [ 32%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-all-to_be_canceled6-dag1] PASSED [ 33%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-related-flows-to_be_canceled7-dag0] PASSED [ 34%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-related-flows-to_be_canceled7-dag1] PASSED [ 35%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-continue-to_be_canceled8-dag0] FAILED [ 36%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-continue-to_be_canceled8-dag1] FAILED [ 38%] +deployability/modules/workflow_engine/tests/test_dag.py::test_create_execution_plan[dag0-exec_plan0] PASSED [ 39%] +deployability/modules/workflow_engine/tests/test_dag.py::test_create_execution_plan[dag1-exec_plan1] PASSED [ 40%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_schema_validator_constructor[logger_mock0] PASSED [ 41%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_schema_validator_constructor_ko PASSED [ 42%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data PASSED [ 43%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data_ko[wf-ko-no-path-on-do.yaml-Missing required properties in 'with' for task: {'task': 'run-agent-tests-{agent}'] PASSED [ 44%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data_ko[wf-ko-no-path-on-cleanup.yaml-Missing required properties in 'with' for task: {'task': 'allocate-manager'] PASSED [ 45%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_validate_schema PASSED [ 46%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_validate_schema_ko[logger_mock0] PASSED [ 47%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_constructor[task0] PASSED [ 48%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task0] PASSED [ 50%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task1] PASSED [ 51%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task2] PASSED [ 52%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task3] PASSED [ 53%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task4] PASSED [ 54%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc0-1-task0] PASSED [ 55%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc0-0-task0] PASSED [ 56%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc1-1-task0] PASSED [ 57%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc1-0-task0] PASSED [ 58%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_constructor PASSED [ 59%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_validate_schema[logger_mock0] PASSED [ 60%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_validate_schema_ko[logger_mock0] PASSED [ 61%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_load_workflow[logger_mock0] PASSED [ 63%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_load_workflow_ko[logger_mock0] PASSED [ 64%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_process_workflow[logger_mock0] PASSED [ 65%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_process_workflow_ok[logger_mock0] PASSED [ 66%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element0-values0-return_value0] PASSED [ 67%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element1-values1-return_value1] PASSED [ 68%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[string_element {value}-values2-string_element value] PASSED [ 69%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element3-None-return_value3] PASSED [ 70%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_expand_task[task0-return_value0-variables0] PASSED [ 71%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_expand_task[task1-return_value1-variables1] PASSED [ 72%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation PASSED [ 73%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation_ko[task_collection0-Duplicated task names: task 1] PASSED [ 75%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation_ko[task_collection1-Tasks do not exist: task 3, task 4] PASSED [ 76%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-False-1-info-schema.yaml] PASSED [ 77%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-True-1-debug-schema.yaml] PASSED [ 78%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-True-1-debug-None] PASSED [ 79%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task[logger_mock0-w_processor0-dag0-custom_action-True] PASSED [ 80%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task[logger_mock1-w_processor1-dag1-custom_action-False] PASSED [ 81%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock0-w_processor0-dag0-KeyboardInterrupt-None] PASSED [ 82%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock0-w_processor0-dag0-KeyboardInterrupt-abort-all] PASSED [ 83%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock1-w_processor1-dag1-Exception-None] PASSED [ 84%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock1-w_processor1-dag1-Exception-abort-all] PASSED [ 85%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-process] PASSED [ 86%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-dummy] PASSED [ 88%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-dummy-random] PASSED [ 89%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object_ko[w_processor0] PASSED [ 90%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel[logger_mock0-w_processor0-dag0-False] PASSED [ 91%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel[logger_mock0-w_processor0-dag0-True] PASSED [ 92%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel_ko[logger_mock0-w_processor0-dag0-False] PASSED [ 93%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel_ko[logger_mock0-w_processor0-dag0-True] PASSED [ 94%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_generate_futures[w_processor0] PASSED [ 95%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_generate_futures_reverse[w_processor0] PASSED [ 96%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_run[logger_mock0-w_processor0-False] PASSED [ 97%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_run[logger_mock0-w_processor0-True] PASSED [ 98%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_handle_interrupt[logger_mock0-w_processor0] PASSED [100%] + +=================================================================================================== FAILURES ==================================================================================================== +``` + +The `.github/workflow/workflow-engine-unit-tests.yaml` automatically runs the unit tests in the GitHub environment. +The run results are in the `checks` tab or your GitHub pull request. + +## Relevant Files +- `tests/test_[test name].py`: all the unit test files start with a `test_` prefix. There is one unit test file for + each tested class. +- `tests/conftest.py`: contains the fixtures used throughout the unit tests. + +## Unit test development guidelines and recommendations +- Use Python coding style standards and recommendations to develop unit tests: snake case for all variable and function + names, maximum line length of 120 characters, two empty lines must separate each function, typing all your functions + and return values, create Docstring for all functions with numpy style. +- Develop unit tests for each function or method of the module. +- Error flows are usually created in a second unit test with the suffix `_ko`. For example, the + `test_process_task_execute` found in the `deployability/modules/workflow_engine/tests/test_workflow_processor` is the + unit test normal flow for the `WorkflowProcessor.process_task_execute` method. The + `WorkflowProcessor.process_task_execute_ko` unit test implements the error flow. +- Use the pytest's decorator `@pytest.mark.parametrize` to implement test cases for the same unit test. +- Mock the object instance and functions used by your tested function using the `unitest.mock.patch` and + `unitest.mock.patch.object` functions or decorators. +- Try to factorize your testing code using `pytest.fixtures`. The shared fixtures are in the `conftest.py` file. In + many unit tests of this project, the fixtures implement a `request` object that receives parameters from the + `pytest.mark.parametrize`. diff --git a/deployability/modules/workflow_engine/tests/conftest.py b/deployability/modules/workflow_engine/tests/conftest.py new file mode 100644 index 0000000000..5de92b5222 --- /dev/null +++ b/deployability/modules/workflow_engine/tests/conftest.py @@ -0,0 +1,75 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""Common unit test fixtures.""" +import graphlib + +from unittest.mock import patch, MagicMock +import pytest + +from workflow_engine.workflow_processor import DAG, WorkflowProcessor + +DEFAULT_TASK_COLLECTION = [ + {'task': 'task1', 'path': '/cmd1', 'args': [{"param1": "value1"}]}, + {'task': 'task2', 'path': '/cmd2', 'args': [{"param1": "value1"}]}, + {'task': 'task3', 'path': '/cmd3', 'args': [{"param1": "value1"}]}, +] + + +@pytest.fixture +def logger_mock(request) -> MagicMock: + """Fixture to mock common logger methods.""" + logger_to_patch = request.param.get('logger_to_patch', "workflow_engine.workflow_processor.logger") + with patch(logger_to_patch) as l_mock: + patch.object(l_mock, 'warning') + patch.object(l_mock, 'info') + patch.object(l_mock, 'debug') + patch.object(l_mock, 'error') + yield l_mock + + +@pytest.fixture +def dag(request) -> DAG: + """Create a mocked DAG instance.""" + ret_dag: DAG + reverse = request.param.get('reverse', False) + task_collection = request.param.get('task_collection', DEFAULT_TASK_COLLECTION) + if request.param.get('patch', True): + execution_plan_dict = request.param.get('execution_plan_dict', {}) + gl_dag = graphlib.TopologicalSorter() + dep_dict = {'task1': 'task2'} + with patch.object(gl_dag, 'prepare'), \ + patch('workflow_engine.workflow_processor.DAG._DAG__build_dag', + return_value=(gl_dag, dep_dict)), \ + patch('workflow_engine.workflow_processor.DAG._DAG__create_execution_plan', + return_value=execution_plan_dict): + ret_dag = DAG(task_collection=task_collection, reverse=reverse) + else: + ret_dag = DAG(task_collection=task_collection, reverse=reverse) + + if finished_task_status := request.param.get('finished_task_status', False): + ret_dag.finished_tasks_status = finished_task_status + + return ret_dag + + +@pytest.fixture +def w_processor(request) -> WorkflowProcessor: + """Create a mocked WorkflowProcessor instance.""" + + workflow_file = request.param.get('workflow_file', 'workflow.yaml') + dry_run = request.param.get('dry_run', False) + threads = request.param.get('threads', 1) + log_level = request.param.get('log_level', 'info') + schema_file = request.param.get('schema_file', 'schema.yaml') + with patch("workflow_engine.workflow_processor.WorkflowFile") as file_mock: + workflow_file_instance = file_mock.return_value + workflow_file_instance.task_collection = request.param.get('task_collection', DEFAULT_TASK_COLLECTION) + if request.param.get('patch', True): + with patch('workflow_engine.workflow_processor.logger.setLevel'): + processor = WorkflowProcessor(workflow_file, dry_run, threads, + log_level, schema_file) + else: + processor = WorkflowProcessor(workflow_file, dry_run, + threads, log_level, schema_file) + return processor diff --git a/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml b/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml new file mode 100644 index 0000000000..a637e4184f --- /dev/null +++ b/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml @@ -0,0 +1,169 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test agents deployment with a single manager. +variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1 + +tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,register,stop" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-tests-uninstall-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "run-agent-tests-{agent}" + - "provision-uninstall-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: aio + version: "4.7.0" + depends-on: + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: aio + version: "4.8.0" + live: False + depends-on: + - "allocate-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent provision task + - task: "provision-uninstall-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - uninstall: + - component: wazuh-agent + type: package + depends-on: + - "provision-install-{agent}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent \ No newline at end of file diff --git a/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml b/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml new file mode 100644 index 0000000000..6b2d2512d0 --- /dev/null +++ b/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml @@ -0,0 +1,169 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test agents deployment with a single manager. +variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1 + +tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,register,stop" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-tests-uninstall-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "run-agent-tests-{agent}" + - "provision-uninstall-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: aio + version: "4.7.0" + depends-on: + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: aio + version: "4.8.0" + live: False + depends-on: + - "allocate-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent provision task + - task: "provision-uninstall-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - uninstall: + - component: wazuh-agent + type: package + depends-on: + - "provision-install-{agent}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent \ No newline at end of file diff --git a/deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml b/deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml new file mode 100644 index 0000000000..62c130f64b --- /dev/null +++ b/deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml @@ -0,0 +1,156 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test agents deployment with a single manager. +variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1 + +tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-tests-uninstall-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "run-agent-tests-{agent}" + - "provision-uninstall-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: aio + version: "4.7.0" + depends-on: + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: aio + version: "4.8.0" + live: False + depends-on: + - "allocate-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent provision task + - task: "provision-uninstall-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - uninstall: + - component: wazuh-agent + type: package + depends-on: + - "provision-install-{agent}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent \ No newline at end of file diff --git a/deployability/modules/workflow_engine/tests/data/wf-ok.yaml b/deployability/modules/workflow_engine/tests/data/wf-ok.yaml new file mode 100644 index 0000000000..fa979a6f81 --- /dev/null +++ b/deployability/modules/workflow_engine/tests/data/wf-ok.yaml @@ -0,0 +1,170 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test agents deployment with a single manager. +variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1 + +tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,register,stop" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-tests-uninstall-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "run-agent-tests-{agent}" + - "provision-uninstall-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: aio + version: "4.7.0" + depends-on: + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: aio + version: "4.8.0" + live: False + depends-on: + - "allocate-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent provision task + - task: "provision-uninstall-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - uninstall: + - component: wazuh-agent + type: package + depends-on: + - "provision-install-{agent}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent \ No newline at end of file diff --git a/deployability/modules/workflow_engine/tests/test_dag.py b/deployability/modules/workflow_engine/tests/test_dag.py new file mode 100644 index 0000000000..b3c6ec181e --- /dev/null +++ b/deployability/modules/workflow_engine/tests/test_dag.py @@ -0,0 +1,294 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +import graphlib + +from unittest.mock import patch, MagicMock, call +import pytest + +from workflow_engine.workflow_processor import DAG + + +@pytest.mark.parametrize("reverse", [True, False]) +@patch("workflow_engine.workflow_processor.DAG._DAG__build_dag") +@patch("workflow_engine.workflow_processor.DAG._DAG__create_execution_plan") +def test_dag_constructor(create_exec_plan_mock: MagicMock, build_dag_mock: MagicMock, reverse: bool): + """Test ProcessTask constructor + Check all the dag object state after initialization and if the private dag methods are called during the instance + construction. + + Parameters + ---------- + create_exec_plan_mock : MagicMock + Patch of the DAG.__create_execution_plan method. + build_dag_mock : MagicMock + Patch of the DAG.__build_dag_ method. + reverse : bool + Parametrized value used by the DAG constructor. + """ + task_collection = [ + {'task': 'task1', 'path': '/cmd1', 'args': [{"param1": "value1"}]}, + {'task': 'task2', 'path': '/cmd2', 'args': [{"param1": "value1"}]}, + {'task': 'task3', 'path': '/cmd3', 'args': [{"param1": "value1"}]}, + ] + gl_dag = graphlib.TopologicalSorter() + + dep_dict = {'task1': 'task2'} + build_dag_mock.return_value = (gl_dag, dep_dict) + plan_dict = {'task1', 'task2'} + create_exec_plan_mock.return_value = plan_dict + with patch.object(gl_dag, 'prepare') as prepare_mock: + dag = DAG(task_collection=task_collection, reverse=reverse) + + assert dag.task_collection == task_collection + assert dag.reverse == reverse + assert dag.dag == gl_dag + assert dag.dependency_tree == dep_dict + assert isinstance(dag.to_be_canceled, set) and not dag.to_be_canceled + assert dag.finished_tasks_status == { + 'failed': set(), + 'canceled': set(), + 'successful': set(), + } + assert dag.execution_plan == plan_dict + build_dag_mock.assert_called_once() + create_exec_plan_mock.assert_called_once_with(dep_dict) + prepare_mock.assert_called_once() + + +@pytest.mark.parametrize('dag', + [{'reverse': True}, {'reverse': False}], + indirect=True) +@pytest.mark.parametrize('is_active', [True, False]) +def test_dag_is_active(is_active: bool, dag: DAG): + """Test DAG.is_active method. + Check if dag.is_active method returns the value of the dag.dag.is_active() method. + + Parameters + ---------- + is_active : bool + Parametrized value returned by dag.dag.is_active + dag : DAG + DAG fixture defined in conftest.py. + """ + with patch.object(dag.dag, 'is_active', return_value=is_active) as is_active_mock: + assert dag.is_active() == is_active + is_active_mock.assert_called_once() + + +@pytest.mark.parametrize('dag', + [{'execution_plan_dict': {'task1', 'task2'} }], indirect=True) +def test_get_execution_plan(dag: DAG): + """Test DAG.get_execution_plan method. + Check if the dag.get_execution_plan returns the dag.execution_plan instance + + Parameters + ---------- + dag : DAG + DAG fixture defined in conftest.py. + """ + assert dag.get_execution_plan() == dag.execution_plan + + +@pytest.mark.parametrize('dag', [{}], indirect=True) +@pytest.mark.parametrize('task_name, status', [ + ('task1', 'failed'), + ('task1', 'canceled'), + ('task1', 'successful'), +]) +def test_set_status(task_name:str, status:str, dag: DAG): + """Test DAG.set_status method. + Check if the dag.dag.done mode is properly called and that the task is in the failed, canceled or + successful set. + + Parameters + ---------- + task_name : str + Parameterized value for the task name passed to dag.set_status method. + status : str + Parameterized value for the task name passed to dag.set_status method. + dag : DAG + DAG fixture defined in conftest.py. + """ + with patch.object(dag.dag, "done") as done_mock: + dag.set_status(task_name=task_name, status=status) + assert task_name in dag.finished_tasks_status[status] + done_mock.assert_called_once_with(task_name) + + +@pytest.mark.parametrize('dag', [{}], indirect=True) +@pytest.mark.parametrize('in_cancel', [True, False]) +def test_should_be_canceled(in_cancel:bool, dag: DAG): + """Test DAG.should_be_canceled method. + Check if dag.should_be_canceled returns True or False if the task is in the dab.to_be_canceled set. + + Parameters + ---------- + in_cancel : bool + Parameterized value to test the method dag.should_be_canceled with 'task1' + in the dag.to_be_canceled set or not. + dag : DAG + DAG fixture defined in conftest.py. + """ + if in_cancel: + dag.to_be_canceled.add('task1') + else: + if 'task1' in dag.to_be_canceled: + dag.to_be_canceled.remove('task1') + + assert dag.should_be_canceled(task_name='task1') == in_cancel + + +@pytest.mark.parametrize('dag', + [{ + 'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']} + ] + }, + {'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}], + 'reverse': True + } + ], + indirect=True) +def test_build_dag(dag: DAG): + """Test DAG.__build_dag method. + The test uses a task collection and checks the calls to the graphlib.TopologicalSorter.add. + The function calls depend on the dag.reverse instance variable, that it is also parameterized. + + Parameters + ---------- + dag : DAG + DAG fixture defined in conftest.py with task_collection parameterized. + """ + with patch('workflow_engine.workflow_processor.graphlib.TopologicalSorter.add') as mock_add: + res_dag, res_dependency_dict = dag._DAG__build_dag() + assert isinstance(res_dag, graphlib.TopologicalSorter) + call_list = [] + dependency_dict = {} + for task in dag.task_collection: + dependencies = task.get('depends-on', []) + task_name = task['task'] + if dag.reverse: + for dependency in dependencies: + call_list.append(call(dependency, task_name)) + else: + call_list.append(call(task_name, *dependencies)) + dependency_dict[task_name] = dependencies + + assert res_dependency_dict == dependency_dict + mock_add.assert_has_calls(call_list, any_order=True) + + +@pytest.mark.parametrize('dag', + [{ + 'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': []}, + {'task': 'task4', 'depends-on': []}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']} + ], + 'patch': False + }, + {'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': []}, + {'task': 'task4', 'depends-on': []}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}], + 'reverse': True, + 'patch': False, + 'finished_task_status': { + 'failed': set(), + 'canceled': set(), + 'successful': set()} + }, + ], + indirect=True) +@pytest.mark.parametrize('task, cancel_policy, to_be_canceled', + [('task1', 'abort-all', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task2', 'abort-all', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task5', 'abort-all', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task1', 'abort-related-flows', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task2', 'abort-related-flows', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task5', 'abort-related-flows', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task1', 'continue', set()), + ('task2', 'continue', set()), + ('task5', 'continue', set()), + ]) +def test_cancel_dependant_tasks(task: str, cancel_policy: str, to_be_canceled: set, dag: DAG): + """Test DAG.cancel_dependant_tasks method. + Check the to_be_canceled set after calling the cancel_dependant_tasks method with a parameterized task_collection + in reverse True and False test cases. + + Parameters + ---------- + task : str + Parameterized task name. + cancel_policy : str + Parameterized cancel policy using valid values (abort-all, abort-related-flows, continue). + to_be_canceled : set + [description] + dag : DAG + DAG fixture defined in conftest.py parameterized with complete object state + (task_collection, reverse, finished_task_status sets). The patch false parameter avoids patching the + DAG.__build_dag' and DAG.__create_execution_plan methods + """ + dag.cancel_dependant_tasks(task, cancel_policy=cancel_policy) + assert dag.to_be_canceled == to_be_canceled + + +@pytest.mark.parametrize('dag, exec_plan', + [( + {'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']} + ], + 'patch': False}, + {"task5": {"task2": {"task1": {}}, + "task3": {"task1": {}}, + "task4": {"task1": {}}}} + ), + ( + { + 'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}, + {'task': 'task6', 'depends-on': ['task5']} + ], + 'patch': False + }, + {"task6": {"task5": {"task2": {"task1": {}}, + "task3": {"task1": {}}, + "task4": {"task1": {}}}}} + ) + ], + indirect=['dag']) +def test_create_execution_plan(exec_plan: dict, dag: DAG): + """Test DAG._create_execution_plan method. + This private method is executed by the constructor. In this Test, + the results are left in the execution_plan instance variable. + + Parameters + ---------- + exec_plan : dict + execution plan. + dag : DAG + DAG fixture defined in conftest.py. + """ + assert dag.execution_plan == exec_plan diff --git a/deployability/modules/workflow_engine/tests/test_schema_validator.py b/deployability/modules/workflow_engine/tests/test_schema_validator.py new file mode 100644 index 0000000000..25a45db95a --- /dev/null +++ b/deployability/modules/workflow_engine/tests/test_schema_validator.py @@ -0,0 +1,119 @@ +# Copyright (C) 2015-2021, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""SchemaValidator unit tests.""" +import uuid +import random +from pathlib import Path +from unittest.mock import MagicMock, call, patch +import json +from ruamel.yaml import YAML +import pytest +from jsonschema.exceptions import ValidationError, UnknownType + +from workflow_engine.schema_validator import SchemaValidator + +@pytest.mark.parametrize('logger_mock', + [{'logger_to_patch':'workflow_engine.schema_validator.logger'}], + indirect=True) +def test_schema_validator_constructor(logger_mock: MagicMock): + """Test SchemaValidator constructor normal flow. + Check the state of the SchemaValidator instance variables after creation. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture to check debug calls + """ + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + with open(schema_path, 'r') as schema_file: + schema_data = json.load(schema_file) + + wf_file_path = Path(__file__).parent / 'data' / 'wf-ok.yaml' + with open(wf_file_path, 'r') as file: + yaml = YAML(typ='safe', pure=True) + yaml_data = yaml.load(file) + + validator = SchemaValidator(schema_path, wf_file_path) + assert validator.schema_data == schema_data + assert validator.yaml_data == yaml_data + calls = [call(f"Loading schema file: {schema_path}"), + call(f"Loading yaml file: {wf_file_path}")] + logger_mock.debug.assert_has_calls(calls) + + +def test_schema_validator_constructor_ko(): + """"Test SchemaValidator constructor error flows. + Check if the FileNotFoundError is raisen with a random file name. + """ + schema_path = str(uuid.UUID(int=random.randint(0, 2^32))) + with pytest.raises(FileNotFoundError, match=f'File "{schema_path}" not found.'): + SchemaValidator(schema_path, schema_path) + + +def test_preprocess_data(): + """Test SchemaValidator preprocess_data. + Check if the preprocess_data method does not raise exceptions with a valid file. + """ + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + wf_file_path = Path(__file__).parent / 'data' / 'wf-ok.yaml' + validator = SchemaValidator(schema_path, wf_file_path) + validator.preprocess_data() + + +@pytest.mark.parametrize('workflow_file, error_msg', + [('wf-ko-no-path-on-do.yaml', + "Missing required properties in 'with' for task: {'task': 'run-agent-tests-{agent}'"), + ('wf-ko-no-path-on-cleanup.yaml', + "Missing required properties in 'with' for task: {'task': 'allocate-manager'"),]) +def test_preprocess_data_ko(workflow_file: str, error_msg: str): + """Test SchemaValidator preprocess_data error flow. + Check the ValidationError generated by invalid yml files. + + Parameters + ---------- + workflow_file : str + workflow yml file name. + error_msg : str + Error message to check + """ + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + wf_file_path = Path(__file__).parent / 'data' / workflow_file + validator = SchemaValidator(schema_path, wf_file_path) + with pytest.raises(ValidationError, match=error_msg): + validator.preprocess_data() + + +def test_validate_schema(): + """Test SchemaValidator validate_schema with a valid yml file.""" + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + wf_file_path = Path(__file__).parent / 'data' / 'wf-ok.yaml' + validator = SchemaValidator(schema_path, wf_file_path) + validator.validateSchema() + + +@pytest.mark.parametrize('logger_mock', + [{'logger_to_patch':'workflow_engine.schema_validator.logger'}], + indirect=True) +def test_validate_schema_ko(logger_mock: MagicMock): + """Test SchemaValidator validate_schema error flows. + Check the messages sent to the log when an invalid workflow yml file is used. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py + """ + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + wf_file_path = Path(__file__).parent / 'data' / 'wf-ko-schema-error.yaml' + validator = SchemaValidator(schema_path, wf_file_path) + validator.validateSchema() + logger_mock.error.assert_called_once() + assert 'Schema validation error:' in logger_mock.error.call_args[0][0] + + logger_mock.error.reset_mock() + validator = SchemaValidator(schema_path, wf_file_path) + with patch('workflow_engine.schema_validator.jsonschema.validate', side_effect=UnknownType): + validator.validateSchema() + logger_mock.error.assert_called_once() + assert 'Unexpected error at schema validation:' in logger_mock.error.call_args[0][0] diff --git a/deployability/modules/workflow_engine/tests/test_task.py b/deployability/modules/workflow_engine/tests/test_task.py new file mode 100644 index 0000000000..604c8e2926 --- /dev/null +++ b/deployability/modules/workflow_engine/tests/test_task.py @@ -0,0 +1,114 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from typing import List, Tuple +from subprocess import CompletedProcess, CalledProcessError +from unittest.mock import patch, MagicMock, call +import pytest + +from workflow_engine.task import ProcessTask + +@pytest.fixture +def task(request) -> ProcessTask: + """Shared fixture to create task.""" + task_name, task_parms = request.param + return ProcessTask(task_name=task_name, task_parameters=task_parms) + + +@pytest.mark.parametrize("task", [('task1', {"param1": "value1"})], indirect=True) +def test_process_task_constructor(task: ProcessTask): + """Test ProcessTask constructor. + Check the task instance varialbes after constructing the ProcessTask. + + Parameters + ---------- + task : ProcessTask + The task fixture. + """ + assert task.task_name == 'task1' + assert task.task_parameters == {"param1": "value1"} + + +@pytest.mark.parametrize("task", [('task1', {"path": "/mypath", + "args": [{"param1": "value1"}]}), + ('task2', {"path": "/mypath", + "args": ["param1"]}), + ('task3', {"path": "/mypath", + "args": ["param1", "param2"]}), + ('task4', {"path": "/mypath", + "args": ["param1", {"param2": "value2"}]}), + ('task5', {"path": "/mypath", + "args": [{"param1": "value1"}, {"param2": "value2"}]}) + ], indirect=True) +@patch("workflow_engine.task.logger") +def test_process_task_execute(logger_mock: MagicMock, task: ProcessTask): + """Test ProcessTask.execute method normal flow. + Check that ProcessTask.execute calls subprocess.run to run commands with the defined parameters. The + task mock in conftest.py is used to thy diferent command argument formats. + + Parameters + ---------- + logger_mock : MagicMock + The logger mock defined in conftest.py + task : ProcessTask + The task fixture. + """ + results = {} + results["task1"] = {"parm_list": [task.task_parameters['path'], "--param1=value1"]} + results["task2"] = {"parm_list": [task.task_parameters['path'], "param1"]} + results["task3"] = {"parm_list": [task.task_parameters['path'], "param1", "param2"]} + results["task4"] = {"parm_list": [task.task_parameters['path'], "param1", + "--param2=value2"]} + results["task5"] = {"parm_list": [task.task_parameters['path'], "--param1=value1", + "--param2=value2"]} + result = CompletedProcess(args=results[task.task_name]["parm_list"][1:], + returncode=0, stdout="command output", + stderr="") + debug_calls = [call(f'Running task "{task.task_name}" with arguments: ' + f'{results[task.task_name]["parm_list"][1:]}')] + with patch("workflow_engine.task.subprocess.run", return_value=result) as proc_run_mock, \ + patch.object(logger_mock, "debug") as logger_debug_mock: + debug_calls.append(call(f'Finished task "{task.task_name}" execution ' + f'with result:\n{str(result.stdout)}')) + task.execute() + + logger_debug_mock.assert_has_calls(debug_calls) + proc_run_mock.assert_called_once_with(results[task.task_name]['parm_list'], check=True, + capture_output=True, text=True) + + +@pytest.mark.parametrize("task", [('task1', {"path": "/mypath", + "args": [{"param1": "value1"}]}), + ], indirect=True) +@pytest.mark.parametrize("subproc_retval", [1, 0]) +@pytest.mark.parametrize("subproc_run_exc", [(True, KeyboardInterrupt, "KeyboardInterrupt error"), + (True, Exception, "Other Error")]) +def test_process_task_execute_ko(subproc_retval: int, subproc_run_exc: List[Tuple], task: ProcessTask): + """Test ProcessTask.execute method exception flows. + Check ProcessTask.execute flow when the subprocess.run returns errors. + + Parameters + ---------- + subproc_retval : int + return code from subprocess.run + subproc_run_exc : bool + Tuple + task : ProcessTask + The task fixture. + """ + raise_exc, exception_type, stderr = subproc_run_exc + if exception_type is Exception: + match = f"Error executing process task {stderr}" + else: + match = "Error executing process task with keyboard interrupt." + result = CompletedProcess(args=["--param1=value1"], + returncode=subproc_retval, stdout="command output", + stderr=stderr) + with patch("workflow_engine.task.subprocess.run", return_value=result) as proc_run_mock: + if raise_exc: + proc_run_mock.side_effect = CalledProcessError(returncode=1, + cmd=task.task_parameters['path'], + stderr=stderr) + + with pytest.raises(exception_type, match=match): + task.execute() diff --git a/deployability/modules/workflow_engine/tests/test_workflow_file.py b/deployability/modules/workflow_engine/tests/test_workflow_file.py new file mode 100644 index 0000000000..a3b411899e --- /dev/null +++ b/deployability/modules/workflow_engine/tests/test_workflow_file.py @@ -0,0 +1,271 @@ +# Copyright (C) 2015-2021, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""WorkflowFile unit tests.""" +from typing import Any, List +from unittest.mock import patch, MagicMock, call, mock_open +import pytest + +from workflow_engine.workflow_processor import WorkflowFile + + +def test_workflow_file_constructor(): + """Test WorkflowFile constructor. + Check the function calls and instance variables after object creation.""" + with patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__validate_schema") as validate_mock, \ + patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__load_workflow", + return_value={'data': 'data'}) as load_mock, \ + patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__process_workflow") as process_mock, \ + patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__static_workflow_validation") \ + as static_validation_mock: + wf = WorkflowFile(workflow_file_path='my_file.yaml', schema_path='my_schema.yaml') + assert wf.schema_path == 'my_schema.yaml' + validate_mock.assert_called_once_with('my_file.yaml') + load_mock.assert_called_once_with('my_file.yaml') + assert wf.workflow_raw_data == {'data': 'data'} + process_mock.assert_called_once() + static_validation_mock.assert_called_once() + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +def test_workflow_file_validate_schema(logger_mock: MagicMock): + """Test WorkflowFile.__validate_schema. + Check debug messages and function called by the method. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + """ + wf = MagicMock() + wf.schema_path = 'my_schema_path.yaml' + workflow_file = 'my_file_path.yaml' + schema_validator = MagicMock() + with patch('workflow_engine.workflow_processor.SchemaValidator', + return_value=schema_validator) as schema_validator_mock: + with patch.object(schema_validator, 'preprocess_data') as preprocess_mock, \ + patch.object(schema_validator, 'validateSchema') as validate_schema_mock: + WorkflowFile._WorkflowFile__validate_schema(self=wf, workflow_file=workflow_file) + + logger_mock.debug.assert_called_once_with(f"Validating input file: {workflow_file}") + schema_validator_mock.assert_called_once_with(wf.schema_path, workflow_file) + preprocess_mock.assert_called_once() + validate_schema_mock.assert_called_once() + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +def test_workflow_file_validate_schema_ko(logger_mock: MagicMock): + """Test WorkflowFile.__validate_schema error flow. + Check logged messages and function calls of the method. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + """ + wf = MagicMock() + wf.schema_path = 'my_schema_path.yaml' + workflow_file = 'my_file_path.yaml' + file_exc = FileNotFoundError() + with patch('workflow_engine.workflow_processor.SchemaValidator', side_effect=file_exc) as schema_validator_mock, \ + pytest.raises(FileNotFoundError): + WorkflowFile._WorkflowFile__validate_schema(self=wf, workflow_file=workflow_file) + + logger_mock.debug.assert_called_once_with(f"Validating input file: {workflow_file}") + schema_validator_mock.assert_called_once_with(wf.schema_path, workflow_file) + logger_mock.error.assert_called_once_with("Error while validating schema [%s] with error: %s", + wf.schema_path, + file_exc) + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +@patch('builtins.open', new_callable=mock_open, read_data='YAML content') +def test_workflow_file_load_workflow(mock_open: MagicMock, logger_mock: MagicMock): + """Test WorkflowFile.__load_workflow. + Check logged messages and function calls of the method. + + Parameters + ---------- + mock_open : MagicMock + The mock fixture defined in conftest.py. + logger_mock : MagicMock + The logger fixture defined in conftest.py. + """ + wf = MagicMock() + wf.schema_path = 'my_schema_path.yaml' + workflow_file = 'my_file_path.yaml' + mock_open.return_value.__enter__.return_value = mock_open + with patch('workflow_engine.workflow_processor.os.path.exists', return_value=True) as path_exists_mock, \ + patch('workflow_engine.workflow_processor.yaml.safe_load') as safe_load_mock: + WorkflowFile._WorkflowFile__load_workflow(self=wf, file_path=workflow_file) + + path_exists_mock.assert_called_once_with(workflow_file) + logger_mock.debug.assert_called_once_with(f"Loading workflow file: {workflow_file}") + mock_open.assert_called_once_with(workflow_file, 'r', encoding='utf-8') + safe_load_mock.assert_called_once_with(mock_open) + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +@patch('builtins.open', new_callable=mock_open, read_data='YAML content') +def test_workflow_file_load_workflow_ko(mock_open: MagicMock, logger_mock: MagicMock): + """Test WorkflowFile.__load_workflow error flow. + Check if the FileNotFoundError exception is raised by the method. + + Parameters + ---------- + mock_open : MagicMock + unittest mock of the open function + logger_mock : MagicMock + The logger fixture defined in conftest.py + """ + wf = MagicMock() + wf.schema_path = 'my_schema_path.yaml' + workflow_file = 'my_file_path.yaml' + mock_open.return_value.__enter__.return_value = mock_open + with patch('workflow_engine.workflow_processor.os.path.exists', return_value=False) as path_exists_mock, \ + pytest.raises(FileNotFoundError, match=f'File "{workflow_file}" not found.') as file_exc: + WorkflowFile._WorkflowFile__load_workflow(self=wf, file_path=workflow_file) + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +def test_workflow_file_process_workflow(logger_mock: MagicMock): + """Test WorkflowFile.__process_workflow. + Check that the method calls the expand_task method of each task using a lambda as a side effect. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py + """ + variable_list = {'variable_1': 'value_1', 'variable_2': 'value_2'} + task_list = [{'task': 'task1'}, {'task': 'task2'}, {'task': 'task3'}] + expanded_task_list = [{'task': 'task3_1'}, {'task': 'task3_2'}] + wf = MagicMock() + wf.workflow_raw_data = {'tasks': task_list, 'variables': variable_list} + wf._WorkflowFile__expand_task.side_effect = lambda task, variables: [task] + \ + (expanded_task_list if task['task'] == 'task3' else []) + tasks = WorkflowFile._WorkflowFile__process_workflow(wf) + + logger_mock.debug.assert_called_once_with("Process workflow.") + calls = [call(task, variable_list) for task in task_list] + wf._WorkflowFile__expand_task.assert_has_calls(calls) + task_list.extend(expanded_task_list) + assert tasks == task_list + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +def test_workflow_file_process_workflow_ok(logger_mock: MagicMock): + """Test WorkflowFile.__process_workflow error flow. + Check that a ValueError is raised when no task are found in the workflow. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py + """ + wf = MagicMock() + wf.workflow_row_data = { + 'tasks': [] + } + wf.__expand_task.return_value = [] + with pytest.raises(ValueError, match="No tasks found in the workflow."): + tasks = WorkflowFile._WorkflowFile__process_workflow(self=wf) + + logger_mock.debug.assert_called_once_with("Process workflow.") + + +@pytest.mark.parametrize('element, values, return_value', + [({'key_1': 'key_1 {value_1}', 'key_2': 'key_2 {value_2}'}, + {'value_1': 'value_1', 'value_2': 'value_2'}, + {'key_1': 'key_1 value_1', 'key_2': 'key_2 value_2'}), + (['element_1 {value_1}', 'element_2 {value_2}', 'element_3 {value_3}'], + {'value_1': 'value_1', 'value_2': 'value_2', 'value_3': 'value_3'}, + ['element_1 value_1', 'element_2 value_2', 'element_3 value_3']), + ('string_element {value}', {'value': 'value'}, 'string_element value'), + ({1, 2}, None, {1, 2})]) +def test_workflow_file_replace_placeholder(element: Any, values: dict, return_value: Any): + """Test WorkflowFile.__replace_placeholder.""" + wf = MagicMock() + wf._WorkflowFile__replace_placeholders.side_effect = \ + lambda s, e, v: WorkflowFile._WorkflowFile__replace_placeholders(wf, s, e, v) + result = WorkflowFile._WorkflowFile__replace_placeholders(self=wf, element=element, values=values) + assert result == return_value + + +@pytest.mark.parametrize('task, return_value, variables', + [({'task': 'task: {as_variable_1}', 'param': '{as_variable_2}', + 'foreach': [{'variable': 'variable_1', 'as': 'as_variable_1'}, + {'variable': 'variable_2', 'as': 'as_variable_2'}]}, + [{"task": "task: value_1_1", 'param': 'value_2_1', + "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, + {"variable": "variable_2", "as": "as_variable_2"}]}, + {"task": "task: value_1_1", 'param': 'value_2_2', + "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, + {"variable": "variable_2", "as": "as_variable_2"}]}, + {"task": "task: value_1_2", 'param': 'value_2_1', + "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, + {"variable": "variable_2", "as": "as_variable_2"}]}, + {"task": "task: value_1_2", 'param': 'value_2_2', + "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, + {"variable": "variable_2", "as": "as_variable_2"}]}], + {'variable_1': ['value_1_1', 'value_1_2'], + 'variable_2': ['value_2_1', 'value_2_2']}), + ({'task': 'task1', 'placeholder': 'placeholder {variable_1}'}, + [{'task': 'task1', 'placeholder': 'placeholder value_1'}], + {'variable_1': 'value_1'}) + ]) +def test_workflow_file_expand_task(task: dict, return_value: dict, variables: dict): + """Test WorkflowFile.___expand_task. + Check the if the expand_task return dictionary is ok. + + Parameters + ---------- + task : dict + A task dictionary used as the input parameter for the expand_task method. + return_value : dict + The expected return value. + variables : dict + The variables dictionary used as the input parameter for the expand_task method. + """ + def side_effect(s, e, v = None): + return WorkflowFile._WorkflowFile__replace_placeholders(wf, s, e, v) + wf = MagicMock() + wf._WorkflowFile__replace_placeholders.side_effect = side_effect + + tasks = WorkflowFile._WorkflowFile__expand_task(wf, task, variables) + assert tasks == return_value + + +def test_workflow_file_static_workflow_validation(): + """Test WorkflowFile.__static_workflow_validation. + Check if no exception is raised with a valid task_collection""" + wf = MagicMock() + wf.task_collection = [{"task": "task 1", "param": "1"}, + {"task": "task 2", "param": "2", 'depends-on': ['task 1']} + ] + WorkflowFile._WorkflowFile__static_workflow_validation(wf) + + +@pytest.mark.parametrize('task_collection, error_msg', [ + ([{"task": "task 1", "param": "1"}, + {"task": "task 1", "param": "2", 'depends-on': ['task 1']}], + 'Duplicated task names: task 1'), + ([{"task": "task 1", "param": "1", 'depends-on': ['task 3', 'task 4']}, + {"task": "task 2", "param": "2", 'depends-on': ['task 3']}], + 'Tasks do not exist: task 3, task 4') +]) +def test_workflow_file_static_workflow_validation_ko(task_collection: List[dict], error_msg: str): + """Test WorkflowFile.__static_workflow_validation. + Check if the validation raises ValueError exceptions with invalid task collections. + + Parameters + ---------- + task_collection : List[dict] + List of tasks + error_msg : str + Expected exception errors + """ + wf = MagicMock() + wf.task_collection = task_collection + with pytest.raises(ValueError, match=error_msg): + WorkflowFile._WorkflowFile__static_workflow_validation(wf) diff --git a/deployability/modules/workflow_engine/tests/test_workflow_processor.py b/deployability/modules/workflow_engine/tests/test_workflow_processor.py new file mode 100644 index 0000000000..a0639c6ea0 --- /dev/null +++ b/deployability/modules/workflow_engine/tests/test_workflow_processor.py @@ -0,0 +1,385 @@ +# Copyright (C) 2015-2021, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""WorkflowProcessor Unit tests""" +import time +import json +from concurrent.futures import Future +from unittest.mock import patch, MagicMock, call +import pytest + +from workflow_engine.workflow_processor import WorkflowProcessor, DAG +from workflow_engine.task import ProcessTask, TASKS_HANDLERS + + +@pytest.mark.parametrize('workflow_file, dry_run, threads, log_level, schema_file', + [('workflow.yaml', False, 1, 'info', 'schema.yaml'), + ('workflow.yaml', True, 1, 'debug', 'schema.yaml'), + ('workflow.yaml', True, 1, 'debug', None), + ]) +@patch("workflow_engine.workflow_processor.logger") +@patch("workflow_engine.workflow_processor.WorkflowFile") +def test_workflow_processor_constructor(file_mock: MagicMock, logger_mock: MagicMock, + workflow_file:str, dry_run: bool, threads: int, log_level: str, + schema_file:str): + """Test WorkflowProcessor constructor. + Check the workflowprocessor instance variables after construction. + + Parameters + ---------- + file_mock : MagicMock + Mock of a WorkflowFile Constructor. + logger_mock : MagicMock + The logger fixture defined in conftest.py. + workflow_file : str + Path to workflow yaml file. + dry_run : bool + Define if the workflow will run or not + threads : int + number of threads + log_level : str + Log level string + schema_file : str + Path to the schema.yml file + """ + task_collection = [ + {'task': 'task1', 'path': '/cmd1', 'args': [{"param1": "value1"}]}, + {'task': 'task2', 'path': '/cmd2', 'args': [{"param1": "value1"}]}, + {'task': 'task3', 'path': '/cmd3', 'args': [{"param1": "value1"}]}, + ] + workflow_file_instance = file_mock.return_value + workflow_file_instance.task_collection = task_collection + with patch.object(logger_mock, 'setLevel') as set_level_mock: + processor = WorkflowProcessor(workflow_file, dry_run, threads, log_level, schema_file) + set_level_mock.assert_called_once_with(log_level) + file_mock.assert_called_once_with(workflow_file, schema_file) + assert processor.task_collection == task_collection + assert processor.dry_run == dry_run + assert processor.threads == threads + + +@pytest.mark.parametrize('logger_mock, w_processor, dag, action, should_be_canceled', + [({}, {}, {}, 'custom_action', True), + ({}, {}, {}, 'custom_action', False),], + indirect=["dag", "w_processor", "logger_mock"]) +def test_execute_task(logger_mock: MagicMock, w_processor: WorkflowProcessor, dag: DAG, action: str, + should_be_canceled: bool): + """Test WorflowProcessor.execute_task function normal + Check the execute_task method when log messages and function calls when the should_be_canceled return value + is True or False. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dag : DAG + The dag fixture defined in conftest.py. + action : str + action name + should_be_canceled : bool + should_be_canceled method patched return value. + + Returns + ------- + [type] + [description] + """ + start_time = time.time() + elapsed_time = 10 + def time_side_effect(): + nonlocal start_time + start_time=start_time + elapsed_time + return start_time + + task = {'task': 'task1'} + p_task = ProcessTask('task1', {}) + with patch.object(dag, 'should_be_canceled', return_value=should_be_canceled) as should_be_canceled_mock, \ + patch.object(w_processor, 'create_task_object', return_value=p_task) as create_task_mock, \ + patch.object(dag, 'set_status') as set_status_mock, \ + patch.object(p_task, 'execute') as exec_mock, \ + patch('workflow_engine.workflow_processor.time') as time_mock: + time_mock.time = MagicMock(side_effect=time_side_effect) + w_processor.execute_task(dag=dag, task=task, action=action) + should_be_canceled_mock.assert_called_once_with(task['task']) + if should_be_canceled: + logger_mock.warning.assert_called_once_with( + "[%s] Skipping task due to dependency failure.", task['task']) + set_status_mock.assert_called_once_with(task['task'], 'canceled') + else: + create_task_mock.assert_called_once_with(task, action) + exec_mock.assert_called_once() + logger_mock.info.assert_has_calls([ + call("[%s] Starting task.", task['task']), + call("[%s] Finished task in %.2f seconds.", task['task'], elapsed_time) + ] + ) + set_status_mock.assert_called_once_with(task['task'], 'successful') + + +@pytest.mark.parametrize('on_error', [None, 'abort-all']) +@pytest.mark.parametrize('logger_mock, w_processor, dag, exception', + [({}, {}, {}, KeyboardInterrupt), + ({}, {}, {}, Exception)], + indirect=["dag", "w_processor", "logger_mock"]) +def test_execute_task_ko(logger_mock: MagicMock, w_processor: WorkflowProcessor, dag: DAG, exception, + on_error: str): + """Test WorflowProcessor.execute_task function, error flows. + Check logged messages, set_status call and cancel_dependant_tasks in the failure flow. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dag : DAG + The dag fixture defined in conftest.py. + exception : [type] + Expected exception. + on_error : str + set on-error of the task. + """ + task = {'task': 'task1'} + task.update({'on-error': on_error} if on_error else {}) + p_task = ProcessTask('task1', {}) + exc = exception() + with patch.object(dag, 'should_be_canceled', return_value=False), \ + patch.object(w_processor, 'create_task_object', return_value=p_task), \ + patch.object(dag, 'set_status') as set_status_mock, \ + patch.object(p_task, 'execute', side_effect=exc), \ + patch('workflow_engine.workflow_processor.time'), \ + patch.object(dag, 'cancel_dependant_tasks') as cancel_mock, \ + pytest.raises(expected_exception=exception): + w_processor.execute_task(dag=dag, task=task, action='action') + + logger_mock.error.assert_called_once_with("[%s] Task failed with error: %s.", task['task'], exc) + set_status_mock.assert_called_once_with(task['task'], 'failed') + cancel_mock.assert_called_once_with(task['task'], on_error if on_error else 'abort-related-flows') + + +@pytest.mark.parametrize('task_type', ['process', 'dummy', 'dummy-random']) +@pytest.mark.parametrize('w_processor', [{}], indirect=True) +def test_create_task_object(w_processor: WorkflowProcessor, task_type: str): + """Test WorkfowProcess.create_task_object function normal flow. + Check the task type returned by the method. + + Parameters + ---------- + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + task_type : str + type of task + """ + task_dict = {'task': 'task1', 'action': {'this': task_type, 'with': {'param'}}} + task = w_processor.create_task_object(task_dict, 'action') + assert isinstance(task, TASKS_HANDLERS.get(task_type)) + + +@pytest.mark.parametrize('w_processor', [{}], indirect=True) +def test_create_task_object_ko(w_processor: WorkflowProcessor): + """Test WorkfowProcess.create_task_object function error flow. + Check that the create_task_object raise a ValueError exception for invalid types.} + + Parameters + ---------- + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + """ + task_type = 'unknown' + task_dict = {'task': 'task1', 'action': {'this': task_type, 'with': {'param'}}} + with pytest.raises(ValueError, match=f"Unknown task type '{task_type}'."): + w_processor.create_task_object(task_dict, 'action') + + +@pytest.mark.parametrize('reverse', [False, True]) +@pytest.mark.parametrize('logger_mock, w_processor, dag',[({}, {}, {})], + indirect=["dag", "w_processor", "logger_mock"]) +@patch('workflow_engine.workflow_processor.concurrent.futures.ThreadPoolExecutor') +def test_execute_tasks_parallel(executor_mock: MagicMock, logger_mock: MagicMock, w_processor: WorkflowProcessor, + dag: DAG, reverse: bool): + """Test WorkfowProcess.execute_task_parallel function. + Check if the logged messages and function calls of the method with reverse True and False cases. + + Parameters + ---------- + executor_mock : MagicMock + Mock of the ThreadPoolExecutor. + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dag : DAG + The dag fixture defined in conftest.py. + reverse : bool + Parameterized value for the execute__tasks_parallel reverse parameter. + """ + futures = MagicMock() + futures.values = MagicMock(return_value = (x := MagicMock())) + y = MagicMock() + y.__enter__ = MagicMock(return_value=y) + executor_mock.return_value = y + with patch('workflow_engine.workflow_processor.concurrent.futures.wait') as wait_mock, \ + patch.object(w_processor, 'generate_futures', return_value=futures) as gen_futures_mock: + w_processor.execute_tasks_parallel(dag, reverse=reverse) + logger_mock.info.assert_called_once_with("Executing tasks in parallel.") + executor_mock.assert_called_once_with(max_workers=w_processor.threads) + wait_mock.assert_called_once_with(x) + gen_futures_mock.assert_called_once_with(dag, y, reverse) + + +@pytest.mark.parametrize('reverse', [False, True]) +@pytest.mark.parametrize('logger_mock, w_processor, dag',[({}, {}, {})], + indirect=["dag", "w_processor", "logger_mock"]) +@patch('workflow_engine.workflow_processor.concurrent.futures.ThreadPoolExecutor') +def test_execute_tasks_parallel_ko(executor_mock: MagicMock, logger_mock: MagicMock, w_processor: WorkflowProcessor, + dag: DAG, reverse: bool): + """Test WorkfowProcess.execute_task_parallel function error flow. + Check function call message loggin and calls when the KeyboardInterrupt is generated while waiting the subprocess + to finish execution. + + Parameters + ---------- + executor_mock : MagicMock + not used, just patched + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dag : DAG + The dag fixture defined in conftest.py. + reverse : bool + Parameterized value for the execute__tasks_parallel reverse parameter. + """ + execute_parallel_mock = MagicMock() + def patch_recursive_and_return_exception(_): + w_processor.execute_tasks_parallel = execute_parallel_mock + raise KeyboardInterrupt() + + with patch('workflow_engine.workflow_processor.concurrent.futures.wait', + side_effect=patch_recursive_and_return_exception), \ + patch.object(w_processor, 'generate_futures'): + w_processor.execute_tasks_parallel(dag, reverse=reverse) + logger_mock.info.assert_called_once_with("Executing tasks in parallel.") + logger_mock.error.assert_called_once_with("User interrupt detected. Aborting execution...") + execute_parallel_mock.assert_called_once_with(dag, reverse=True) + + +@pytest.mark.parametrize('w_processor', + [{'task_collection': [ + {'task': 'task1'}, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],}, + ], + indirect=True) +def test_generate_futures(w_processor: WorkflowProcessor): + """Test WorkfowProcess.generate_futures function without reverse. + Check the futures returned by the method. + + Parameters + ---------- + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + """ + def submit_execute_task_side_effect(_, dag: DAG, task, __): + dag.set_status(task['task'], 'successful') + return Future() + + executor = MagicMock() + executor.submit.side_effect=submit_execute_task_side_effect + dag = DAG(task_collection=w_processor.task_collection) + futures = w_processor.generate_futures(dag, executor=executor) + assert len(futures) == len(w_processor.task_collection) and \ + all(isinstance(element, Future) for element in futures.values()) + + +@pytest.mark.parametrize('w_processor', + [{'task_collection': [ + {'task': 'task1'}, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],}, + ], + indirect=True) +def test_generate_futures_reverse(w_processor: WorkflowProcessor): + """Test WorkfowProcess.generate_futures function with reverse True. + Check that set_status with successful is called for the tasks. + + Parameters + ---------- + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + """ + + def set_status_side_effect(task, status): + dag.finished_tasks_status[status].add(task) + dag.dag.done(task) + + executor = MagicMock() + dag = DAG(task_collection=w_processor.task_collection, reverse=True) + with patch.object(dag, 'set_status', side_effect=set_status_side_effect) as set_status_mock: + futures = w_processor.generate_futures(dag, executor=executor, reverse=True) + calls = [call(task['task'], 'successful') for task in w_processor.task_collection] + set_status_mock.assert_has_calls(calls, any_order=True) + + +@pytest.mark.parametrize('dry_run', [False, True]) +@pytest.mark.parametrize('logger_mock, w_processor', + [({}, { + 'task_collection': [ + {'task': 'task1'}, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],})], + indirect=True) +def test_run(logger_mock: MagicMock, w_processor: WorkflowProcessor, dry_run: bool): + """Test WorkfowProcess.run function. + Check log message and execute_tasks_parallel call. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dry_run : bool + Parameterized value to test the run method. + """ + def dag_constructor(_, reverse=False): + return reverse_dag if reverse else dag + + w_processor.dry_run = dry_run + dag = DAG(w_processor.task_collection) + reverse_dag = DAG(w_processor.task_collection, reverse=True) + with patch.object(w_processor, 'execute_tasks_parallel') as exec_tasks_mock, \ + patch('workflow_engine.workflow_processor.DAG', side_effect=dag_constructor) as dag_mock: + w_processor.run() + if dry_run: + dag_mock.assert_called_once_with(w_processor.task_collection) + logger_mock.info.assert_called_once_with("Execution plan: %s", json.dumps(dag.get_execution_plan(), indent=2)) + else: + logger_mock.info.assert_has_calls([call("Executing DAG tasks."), call("Executing Reverse DAG tasks.")]) + exec_tasks_mock.assert_has_calls([call(dag), call(reverse_dag, reverse=True)]) + dag_mock.assert_has_calls([call(w_processor.task_collection), call(w_processor.task_collection, reverse=True)]) + + +@pytest.mark.parametrize('logger_mock, w_processor', [({}, {})], indirect=['logger_mock', 'w_processor']) +def test_handle_interrupt(logger_mock: MagicMock, w_processor: WorkflowProcessor): + """Test WorkfowProcess.handle_interrupt function. + Check logging when the handle_interrupt is called. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + """ + with pytest.raises(KeyboardInterrupt, match="User interrupt detected. End process..."): + w_processor.handle_interrupt(0, 0) + logger_mock.error.assert_called_once_with("User interrupt detected. End process...") diff --git a/deployability/modules/workflow_engine/workflow_processor.py b/deployability/modules/workflow_engine/workflow_processor.py index dfbf4610c9..85635aa6c9 100755 --- a/deployability/modules/workflow_engine/workflow_processor.py +++ b/deployability/modules/workflow_engine/workflow_processor.py @@ -330,7 +330,7 @@ def execute_tasks_parallel(self, dag: DAG, reverse: bool = False) -> None: logger.error("User interrupt detected. Aborting execution...") self.execute_tasks_parallel(dag, reverse=True) - def generate_futures(self, dag, executor, reverse: bool = False): + def generate_futures(self, dag: DAG, executor, reverse: bool = False): futures = {} while True: From ca1f734e5070ca2faaf9800a9fd41e44e6881608 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Tue, 9 Apr 2024 18:39:22 -0300 Subject: [PATCH 011/195] Fixed aio reference to assistant --- deployability/modules/provision/handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/provision/handler.py b/deployability/modules/provision/handler.py index e125d2284a..1f39e13b0c 100644 --- a/deployability/modules/provision/handler.py +++ b/deployability/modules/provision/handler.py @@ -78,7 +78,7 @@ def _get_templates_order(self) -> list[str]: match self.method: case 'package' if self.action == "install": return ["set_repo.j2", "install.j2", "register.j2", "service.j2"] - case 'aio': + case 'assistant': return ["download.j2", f"{self.action}.j2"] case 'source': # This will be kept as it could be used in the wazuh installation from sources. From a25a7c3f49566ba74903bd1cda41bc1ee88a6366 Mon Sep 17 00:00:00 2001 From: Carlos Bordon <64099752+c-bordon@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:15:24 -0300 Subject: [PATCH 012/195] Update deployability/modules/allocation/README.MD Co-authored-by: Raul Del Pozo Moreno --- deployability/modules/allocation/README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index fda5a9280d..96212b4b61 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -9,7 +9,7 @@ The Allocation module allows you to create and destroy VMs both locally and in A The execution of the allocation is carried out through the Workflow engine library, or by executing them manually through commands. Execution can be done from any operating system. -Initially, You must install Python libraries. We recommend to use virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. +Initially, you have to install the required Python libraries. We recommend using virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. 1. Activate the environment: From f86d59942b755a0a6f2ba9e2a9ea273dcd2cb114 Mon Sep 17 00:00:00 2001 From: Carlos Bordon <64099752+c-bordon@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:16:01 -0300 Subject: [PATCH 013/195] Update deployability/modules/allocation/README.MD Co-authored-by: Raul Del Pozo Moreno --- deployability/modules/allocation/README.MD | 1 - 1 file changed, 1 deletion(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 96212b4b61..fb3bf57fce 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -26,7 +26,6 @@ Initially, you have to install the required Python libraries. We recommend using cd wazuh-qa git checkout {project-branch} ``` -> Note: temporary dev project-branch is [enhancement/4495-DTT1](https://github.com/wazuh/wazuh-qa/tree/enhancement/4495-DTT1) 3. Install requirements: From b46933b51230c84b253603d44f35c497feca836a Mon Sep 17 00:00:00 2001 From: Carlos Bordon <64099752+c-bordon@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:16:21 -0300 Subject: [PATCH 014/195] Update deployability/modules/allocation/README.MD Co-authored-by: Raul Del Pozo Moreno --- deployability/modules/allocation/README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index fb3bf57fce..f6e580bcd3 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -56,7 +56,7 @@ Now, it is possible to use the Worklow engine library to launch the provision mo ```bash version: 0.1 - description: This workflow is used to test agents deployment por DDT1 PoC + description: This workflow is used to test agents' deployment for DDT1 PoC variables: agent-os: - linux-ubuntu-18.04-amd64 From 408b0845f0fb49032cadd6ef44c85505f665b7ff Mon Sep 17 00:00:00 2001 From: Carlos Bordon <64099752+c-bordon@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:16:49 -0300 Subject: [PATCH 015/195] Update deployability/modules/allocation/README.MD Co-authored-by: Raul Del Pozo Moreno --- deployability/modules/allocation/README.MD | 1 - 1 file changed, 1 deletion(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index f6e580bcd3..7b672184cb 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -157,7 +157,6 @@ Now, it is possible to use the Worklow engine library to launch the provision mo python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml ``` - > Note The command execution can also be mediated through Jenkins. #### Manual execution of the Allocation module From 4971e1974418f1528ca743fd3e1639dbf034dba6 Mon Sep 17 00:00:00 2001 From: Carlos Bordon <64099752+c-bordon@users.noreply.github.com> Date: Wed, 10 Apr 2024 16:17:16 -0300 Subject: [PATCH 016/195] Update deployability/modules/allocation/README.MD Co-authored-by: Raul Del Pozo Moreno --- deployability/modules/allocation/README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 7b672184cb..11c8f855cc 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -260,7 +260,7 @@ If one wishes to execute the allocaation module without installing the Workflow This argument allows us to define in which directory the files referring to the VM will be generated. By default, **/tmp/wazuh-qa** - --label-issue - This argument is only used in the case of AWS and is not mandatory, it allows us to create a label to reference the created instance to an issue on GitHub. It has to be a GitHub URL of the Wazuh repository, for example: **https://github.com/wazuh/internal-devel-requests/issues/1008** + This argument is only used in the case of AWS and is not mandatory, it allows us to create a label to reference the created instance to an issue on GitHub. It has to be a GitHub URL of a Wazuh repository, for example: **https://github.com/wazuh/internal-devel-requests/issues/1008** - --label-team This argument it is mandatory for AWS deploy, allows you to set the team that owns the VM to be able to track it. The valid options are: **qa**, **core**, **framework**, **devops**, **frontend**, **operations**, **cloud**, **threat-intel**, **marketing**, **documentation** From 11ca64af956a19fa45de31c651170fbc9d4b0ff4 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Wed, 10 Apr 2024 16:39:11 -0300 Subject: [PATCH 017/195] The changes suggested in the PR are applied --- .../modules/allocation/Allocation.drawio.zip | Bin 1821 -> 0 bytes .../modules/allocation/Allocation.jpg | Bin 101220 -> 0 bytes deployability/modules/allocation/README.MD | 4 ++-- .../modules/allocation/static/specs/os.yml | 2 +- .../allocation/static/templates/vagrant.j2 | 2 -- .../modules/allocation/vagrant/provider.py | 2 +- 6 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 deployability/modules/allocation/Allocation.drawio.zip delete mode 100644 deployability/modules/allocation/Allocation.jpg diff --git a/deployability/modules/allocation/Allocation.drawio.zip b/deployability/modules/allocation/Allocation.drawio.zip deleted file mode 100644 index cffbaaedc3a60cf466e530fe430621d78e77db03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1821 zcmV+&2jcipO9KQH00ICA018QjSbP64hmi&V0J}B-02KfL06}bQZ)0I}X>V>WWO8A5 zX>TrgZEWRRU2~f_6n*cnVAz>X_rW&e+i~3)ubZTsb*A39ZPLDE6p(E;0%kF)epC+-lO>SaZBoXK|GUY zws~plEs0JJfU4J(U!`N@T$6^X0EPbn>BysKNM?A}qPHwhVMlmnC=JmMztmr4bc(0BK|3-SArj zKG#v~xhP6H7$KN>VS3<@AV3aF+K^H*O-~OAPVW-(X{{Oq2jcAL+;iEuE!&2Y9)CjK zXe{o;+2VGAqLVkGF?7jPG*bi|10{ql4-!aSgmHqLk8c$BBtJ?MJC+X$YkCjcOgjQ6 z?nSTb+Q7M~=_WS^#4(>apADM}2$aNu%SEHKrelwxK?ohxo^oPaW9H*FXl1_!5@RwX zsFt&zO-+G_j~Jaxyi?=T;znp1vNTPZ0jkQ+jTf{ug&82HceJ?ZHi||H;}N)2eFX=( z+&D>-P&Otb5c&0GFXQKn}I~1V=MZ1|R8H zaN7IL{o#%}7w)_h)`Vy8($T=e%apotkDslatZqEuW~H`UI^XaN%}ljB`_^S&{O+{( zm06Qyz|#8)X?T+N<*(q^bSQ=MWJE~3fJHy`CwJ|Op1jWW+kro|W^$)n^J!!gTG#BQ zgxpd4Mjn2||8=|TSZFN|uoiB=eXlRG`+|mlLP{olA3^_ZEL+ck4^^#}5N+oRueDrw zmHQT6Yqju#r$&Tg!-Iz+f>Oh+6Cc&;_ z8Ng|mH*rwog@H!S}{ee18PI85rm9|I4 zW!cO*q1Qu9rqWV*8^*wHca0dRKQ6c1K}Dy`5_bza&yWw(sHVcD87o45FRJTR6p}z` zNK!TjjQ{x4JF3OC%jVgz(``aPao;n4~9a5nomgB%p+|R(e*{w>etKr@$9xi3bJeT3p@`!$s$c zNDi1SIpcmqu2ITbK)8r95|cxSy;0D1IERR~1X-Q~aVI(Hd#=mZzn_hfnd$LbQx^|r zw5GbEx|w5I+jdCJLP-DTBH5$diV~K@<93aQgAIV2#@#}>X5K248(W5+dD%DdY%^dR zIa;?X5{n$-a-JXYU=HMBO>>7?MdoOo^T9L1uU7*~Kl0b?9j-Bj(5)48 zFPQMT5<0KXeY~B;rj+b;xDcyMg#~hZMFgYJx!xWL>)KvdQ^01ks%Ip|%PMbyrl66-5=f6JqZWfj&+;81Y_+h)TxW@tYJMA>XLl#Q6tTYS4iFXzsbgajk2-8 z&SW#_&>#H^(+aU`AD;BS9b3jlu5{lgQ$+h+=@ZW`qo?KASw4}o_C2YEy{M`f=GOD+ zLeN=-{v6)KW62AK6iV-W=zJdhx+mzM8t7SVnxh>s=z6igAH;e^#?Du%9f1$Vg#EBs0OHS(xe&*RSCP}&7QTJ`(@jqIzX zWJk053M=`TtFic+>%$w-DZW_y@a8{IO928u0~7!P00;mINrYH?|1gJ<1^@uNHUIz> z00000000000002AfdBvi06}bQZ)0I}X>V>WWO8A5X>TrgZER3W1qJ{B000310RTe) L006`W00000+52ef diff --git a/deployability/modules/allocation/Allocation.jpg b/deployability/modules/allocation/Allocation.jpg deleted file mode 100644 index 59dd4269c7e1b870bd8bd4b407f9d3834d59b628..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101220 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 diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 11c8f855cc..28b15b513c 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -331,10 +331,10 @@ python3 modules/allocation/main.py --action create --provider vagrant --size lar #### Diagram -![image](Allocation.jpg) +![image](https://github.com/wazuh/wazuh-qa/assets/64099752/d302f96b-70c0-4fbc-869b-914973ee8186) -[Allocation.drawio.zip](Allocation.drawio.zip) +[Allocation.drawio.zip](https://github.com/wazuh/wazuh-qa/files/14868775/Allocation.drawio.zip) ### License diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index c493f3beb2..b0cd4e2063 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -368,7 +368,7 @@ aws: ami: ami-0a747df120215911a zone: us-east-1 user: Administrator - windows-desktop-11-amd64: # update with the correct AMI for userData + windows-desktop-11-amd64: ami: ami-09d8cef159442d5b0 zone: us-east-1 user: Jenkins diff --git a/deployability/modules/allocation/static/templates/vagrant.j2 b/deployability/modules/allocation/static/templates/vagrant.j2 index f151640214..e66a13dfc4 100755 --- a/deployability/modules/allocation/static/templates/vagrant.j2 +++ b/deployability/modules/allocation/static/templates/vagrant.j2 @@ -17,8 +17,6 @@ Vagrant.configure("2") do |config| config.vm.network "private_network", ip:"{{ config.ip }}" config.ssh.forward_agent = true # Create a file to indicate that the VM has been initialized - # This is required to correctly get the ssh-config of the VM - # TODO: find a better solution init_indicator = "./init" if not ::File.exists?(init_indicator) File.write(init_indicator, "initialized") diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 2484809204..6234cf5a87 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -361,7 +361,7 @@ def __remote_host(arch: str, action: str, os: str = None, instance_dir: Path = N ssh_user = client.get_secret_value(SecretId='devops_ppc64_centos_jenkins_user')['SecretString'] remote_host_parameters['host_provider'] = 'centos' except Exception as e: - raise ValueError('Could not get Centos ppc64 server IP: ' + str(e) + '.') + raise ValueError('Could not get CentOS ppc64 server IP: ' + str(e) + '.') try: tn = Telnet(server_ip, server_port, timeout) From 6311d92724994e505494d41ca00dd7d9cddda76f Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Wed, 10 Apr 2024 18:34:27 -0300 Subject: [PATCH 018/195] Fix requested review changes --- deployability/Jenkinsfiles/Launcher.groovy | 30 ---- deployability/Jenkinsfiles/Provision.groovy | 24 --- deployability/README.MD | 15 +- deployability/deps/remote_requirements.txt | 2 +- deployability/modules/provision/README.MD | 14 +- .../playbooks/source/install/python.j2 | 2 +- .../wazuh/assistant/install/install.j2 | 2 +- .../wazuh/assistant/uninstall/uninstall.j2 | 2 +- .../modules/provision/tests/TESTING-README.md | 22 +-- .../modules/provision/tests/test_models.py | 4 +- deployability/modules/testing/README.MD | 38 ++--- .../modules/testing/tests/helpers/manager.py | 14 +- .../modules/workflow_engine/README.MD | 20 +-- .../aws/test-agent-complete.yaml | 8 +- .../aws/test-agent-restart-ins-prov.yaml | 2 +- .../aws/test-agent-stop-ins-prov.yaml | 2 +- .../{test => agent}/aws/test-agent-susse.yaml | 15 +- .../aws/test-agent-uninstall-ins-prov.yaml | 2 +- .../vagrant/test-agent-complete.yaml | 3 +- .../vagrant/test-agent-restart-ins-prov.yaml | 143 ++++++++++++++++++ .../vagrant/test-agent-stop-ins-prov-2.yaml | 2 +- .../vagrant/test-agent-stop-ins-prov.yaml | 2 +- .../test-agent-uninstall-ins-prov.yaml | 2 +- .../examples/dtt1-agents-aws.yaml | 142 ----------------- .../examples/dtt1-agents-poc-aws.yaml | 106 ------------- .../examples/dtt1-agents-poc2-vagrant.yaml | 105 ------------- .../examples/dtt1-agents-vagrant.yaml | 138 ----------------- .../workflow_engine/examples/dtt1-agents.yaml | 120 --------------- .../examples/dtt1-managers.yaml | 103 ------------- .../aws/dtt1-managers-poc-aws-all.yaml | 77 ++++++++++ .../aws}/dtt1-managers-poc-aws.yaml | 6 +- .../vagrant/dtt1-managers-poc-aws-all.yaml | 77 ++++++++++ .../vagrant}/dtt1-managers-poc-vagrant.yaml | 4 +- .../workflow_engine/examples/test.yaml | 137 ----------------- .../tests/data/wf-ko-no-path-on-cleanup.yaml | 4 +- .../tests/data/wf-ko-no-path-on-do.yaml | 4 +- .../tests/data/wf-ko-schema-error.yaml | 4 +- .../workflow_engine/tests/data/wf-ok.yaml | 4 +- .../plugins/influxdb_reporter/README.md | 2 +- .../plugins/influxdb_reporter/setup.py | 4 +- 40 files changed, 407 insertions(+), 1000 deletions(-) delete mode 100755 deployability/Jenkinsfiles/Launcher.groovy delete mode 100755 deployability/Jenkinsfiles/Provision.groovy rename deployability/modules/workflow_engine/examples/{test => agent}/aws/test-agent-complete.yaml (92%) rename deployability/modules/workflow_engine/examples/{test => agent}/aws/test-agent-restart-ins-prov.yaml (98%) rename deployability/modules/workflow_engine/examples/{test => agent}/aws/test-agent-stop-ins-prov.yaml (98%) rename deployability/modules/workflow_engine/examples/{test => agent}/aws/test-agent-susse.yaml (92%) rename deployability/modules/workflow_engine/examples/{test => agent}/aws/test-agent-uninstall-ins-prov.yaml (98%) rename deployability/modules/workflow_engine/examples/{test => agent}/vagrant/test-agent-complete.yaml (97%) create mode 100755 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov.yaml rename deployability/modules/workflow_engine/examples/{test => agent}/vagrant/test-agent-stop-ins-prov-2.yaml (97%) rename deployability/modules/workflow_engine/examples/{test => agent}/vagrant/test-agent-stop-ins-prov.yaml (98%) rename deployability/modules/workflow_engine/examples/{test => agent}/vagrant/test-agent-uninstall-ins-prov.yaml (98%) delete mode 100755 deployability/modules/workflow_engine/examples/dtt1-agents-aws.yaml delete mode 100644 deployability/modules/workflow_engine/examples/dtt1-agents-poc-aws.yaml delete mode 100644 deployability/modules/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml delete mode 100755 deployability/modules/workflow_engine/examples/dtt1-agents-vagrant.yaml delete mode 100755 deployability/modules/workflow_engine/examples/dtt1-agents.yaml delete mode 100755 deployability/modules/workflow_engine/examples/dtt1-managers.yaml create mode 100644 deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws-all.yaml rename deployability/modules/workflow_engine/examples/{ => manager/aws}/dtt1-managers-poc-aws.yaml (92%) create mode 100644 deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-aws-all.yaml rename deployability/modules/workflow_engine/examples/{ => manager/vagrant}/dtt1-managers-poc-vagrant.yaml (96%) delete mode 100644 deployability/modules/workflow_engine/examples/test.yaml diff --git a/deployability/Jenkinsfiles/Launcher.groovy b/deployability/Jenkinsfiles/Launcher.groovy deleted file mode 100755 index 75e88108e8..0000000000 --- a/deployability/Jenkinsfiles/Launcher.groovy +++ /dev/null @@ -1,30 +0,0 @@ -String jenkins_reference = params.getOrDefault('JENKINS_REFERENCE', 'enhancement/4751-dtt1-iteration-2-poc') -String launcher_path = "modules/provision" -String task_flow_launcher = "main.py" -String workflow = "modules/workflow_engine/examples/dtt1-managers.yaml" -String schema = "modules/workflow_engine/schema.json" - -// Jenkinsfile - -node { - - try { - stage('Clone Repo') { - print("Clone repository") - git branch: "${JENKINS_REFERENCE}", url: 'https://github.com/wazuh/wazuh-qa.git' - } - - stage('Launch Task Flow') { - print("Launch Task Flow dry run") - sh "cd ${env.WORKSPACE}/deployability && python3 ${launcher_path}/${task_flow_launcher} ${workflow} --dry-run" - - print("Launch Task Flow") - sh "cd ${env.WORKSPACE}/deployability && python3 ${launcher_path}/${task_flow_launcher} ${workflow} ${schema}" - } - } - finally{ - stage('Remove venv') { - sh "rm -rf ${env.WORKSPACE}/deployability/venv" - } - } -} \ No newline at end of file diff --git a/deployability/Jenkinsfiles/Provision.groovy b/deployability/Jenkinsfiles/Provision.groovy deleted file mode 100755 index eb50ddc6dc..0000000000 --- a/deployability/Jenkinsfiles/Provision.groovy +++ /dev/null @@ -1,24 +0,0 @@ -String provision_path = "${WORKSPACE}/modules/provision" -String provision_script = "main.py" -String inventory = "inventory.yaml" -String jenkins_reference = params.getOrDefault('JENKINS_REFERENCE', 'enhancement/4665-dtt1-poc') - -// Jenkinsfile - -node { - - stage('Clone Repo') { - print("Clone repository") - git branch: "${JENKINS_REFERENCE}", url: 'https://github.com/wazuh/wazuh-qa.git' - } - - stage('Provision') { - print("Launch provision") - sh "python3 ${provision_path}/${provision_script} -i ${inventory}" - } - - stage('Remove venv') { - sh "rm -rf ${env.WORKSPACE}/poc-tests/venv" - } - -} \ No newline at end of file diff --git a/deployability/README.MD b/deployability/README.MD index f20c22dd01..d605359bff 100644 --- a/deployability/README.MD +++ b/deployability/README.MD @@ -1,6 +1,6 @@ ## Deployability general documentation -### User documentation +### User documentation To perform Deployability type tests, it is necessary to: @@ -55,7 +55,7 @@ Example: ``` version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: This workflow is used to test agents' deployment for DDT1 PoC variables: agents-os: - linux-ubuntu-22.04-amd64 @@ -247,7 +247,7 @@ depends-on Execute the command by referencing the parameters required by the library (launcher). ``` -python3 -m workflow_engine {.yaml fixture path} +python3 -m workflow_engine {.yaml fixture path} ``` Example @@ -256,7 +256,6 @@ Example python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml ``` -> Note: The command execution can also be mediated through Jenkins. --- @@ -269,9 +268,9 @@ The framework has 4 modules (`allocation`, `provision`, `testing`, `observabilit |Module|Description |--|--| |Workflow (Orchestrator)|Receives a YAML containing all the instructions to execute for the test development. It contains instructions for allocation, provision, and testing. -|Allocation| Receives instructions for the desired architecture and creates the structures, generating IPs, ports either in AWS or locally with Vagrant. +|Allocation| Receives instructions for the desired architecture and creates the structures, generating IPs, and ports either in AWS or locally with Vagrant. |Provision| Installs applications on the structures created in allocation. -|Testing| Executes tests on the previously defined structures and trigger actions depending the test. +|Testing| Executes tests on the previously defined structures and triggers actions depending on the test. |Observability| Allows the ordered and indexed visualization of data obtained in the 3 previous modules. - Directory Structure @@ -308,12 +307,12 @@ Deployability contains the following directories: ----- +---- [draw-plot.zip](https://github.com/wazuh/wazuh-qa/files/14330528/plot.zip) - + ### License diff --git a/deployability/deps/remote_requirements.txt b/deployability/deps/remote_requirements.txt index effc77f8c4..c81d95e18d 100755 --- a/deployability/deps/remote_requirements.txt +++ b/deployability/deps/remote_requirements.txt @@ -1,4 +1,4 @@ -# python3-pip +python3-pip pytest>=7.4.2,<8.0.0 chardet==5.2.0 pytest-tinybird==0.2.0 diff --git a/deployability/modules/provision/README.MD b/deployability/modules/provision/README.MD index 2e453cf2cd..d32e422f91 100644 --- a/deployability/modules/provision/README.MD +++ b/deployability/modules/provision/README.MD @@ -60,10 +60,10 @@ Initially, Python libraries must be installed. we recommended the use of virtual >Note: You can find some fixture examples in '[deployability/modules/workflow_engine/examples/](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/workflow_engine/examples)' Example: - + ```bash version: 0.1 - description: This workflow is used to provision agents hosts por DDT1 PoC + description: This workflow is used to provision agent hosts for DDT1 PoC variables: agents-os: - linux-ubuntu-22.04-amd64 @@ -146,12 +146,12 @@ Initially, Python libraries must be installed. we recommended the use of virtual depends-on ``` - >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your provision (allocation and provision) + >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your provision (allocation and provision) 7. Command execution (local): Execute the command by referencing the parameters required by the library (launcher). - + ```bash python3 -m workflow_engine {.yaml fixture path} ``` @@ -171,12 +171,12 @@ To execute the Allocation module without installing the Workflow engine, you can While in wazuh-qa/deployability ```bash - python3 module/provision/main.py --inventory-manager { inventory .yaml} --install "{'component': '{component}', 'type': '{type}', 'version': '{version}'}" + python3 module/provision/main.py --inventory-manager { inventory .yaml} --install "{'component': '{component}', 'type': '{type}', 'version': '{version}'}" ``` Example: ```bash - python3 module/provision/main.py --inventory-manager /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml --install "{'component': 'pyyaml', 'type': 'pip', 'version' : '6.0.1'}" + python3 module/provision/main.py --inventory-manager /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml --install "{'component': 'pyyaml', 'type': 'pip', 'version' : '6.0.1'}" ``` --- @@ -247,7 +247,7 @@ The module is composed of: ![image](https://github.com/wazuh/wazuh-qa/assets/125690423/e06fb59c-6497-4396-b20c-a21a68a5e883) - + [Provision.drawio.zip](https://github.com/wazuh/wazuh-qa/files/14241269/Provision.drawio.zip) diff --git a/deployability/modules/provision/playbooks/source/install/python.j2 b/deployability/modules/provision/playbooks/source/install/python.j2 index a6b7c5ab04..ecfbb08cbc 100644 --- a/deployability/modules/provision/playbooks/source/install/python.j2 +++ b/deployability/modules/provision/playbooks/source/install/python.j2 @@ -69,7 +69,7 @@ when: build_python.changed register: install_python -- name: Set default python to {{ version }} +- name: Set default Python version to {{ version }} shell: | sudo update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python{{ ".".join(version.split(".")[:-1]) }} 1 sudo update-alternatives --install /usr/bin/pip3 pip3 /usr/local/bin/pip{{ ".".join(version.split(".")[:-1]) }} 1 diff --git a/deployability/modules/provision/playbooks/wazuh/assistant/install/install.j2 b/deployability/modules/provision/playbooks/wazuh/assistant/install/install.j2 index fd6ee248db..a15e709994 100755 --- a/deployability/modules/provision/playbooks/wazuh/assistant/install/install.j2 +++ b/deployability/modules/provision/playbooks/wazuh/assistant/install/install.j2 @@ -1,2 +1,2 @@ -- name: Install {{ component }} with asistant +- name: Install {{ component }} with assistant shell: "bash ./wazuh-install.sh -a -i" diff --git a/deployability/modules/provision/playbooks/wazuh/assistant/uninstall/uninstall.j2 b/deployability/modules/provision/playbooks/wazuh/assistant/uninstall/uninstall.j2 index 12180fed90..9498abd8c4 100755 --- a/deployability/modules/provision/playbooks/wazuh/assistant/uninstall/uninstall.j2 +++ b/deployability/modules/provision/playbooks/wazuh/assistant/uninstall/uninstall.j2 @@ -1,2 +1,2 @@ -- name: Uninstall {{ component }} with asistant +- name: Uninstall {{ component }} with assistant shell: "bash ./wazuh-install.sh --uninstall" diff --git a/deployability/modules/provision/tests/TESTING-README.md b/deployability/modules/provision/tests/TESTING-README.md index e34369c48b..0fd2baeb84 100644 --- a/deployability/modules/provision/tests/TESTING-README.md +++ b/deployability/modules/provision/tests/TESTING-README.md @@ -27,13 +27,13 @@ cloned the `wazuh-qa` repository into `/wazuh/wazuh-qa`, configure the `PYTHONPA The directory `deployability/modules/provision/tests/` contains the unit test files for the `provision` module. ## Running Tests -To run the tests, make sure that your system meets the requirements by executing the following command from the project +To run the tests, make sure that your system meets the requirements by executing the following command from the project root: ```bash pytest -vv deployability/modules/provision ``` -This command will run all tests in the `tests/` directory. Using additional arguments, You can also run specific tests +This command will run all tests in the `tests/` directory. Using additional arguments, you can also run specific tests or directories. The output of this command looks like this: ```bash pytest -vv deployability/modules/provision/ @@ -41,7 +41,7 @@ pytest -vv deployability/modules/provision/ platform linux -- Python 3.10.13, pytest-8.0.1, pluggy-1.4.0 -- /home/marcelo/.pyenv/versions/wazuh-qa/bin/python cachedir: .pytest_cache rootdir: /home/marcelo/wazuh/wazuh-qa/deployability/modules -collected 51 items +collected 51 items deployability/modules/provision/tests/test_actions.py::test_action_constructor[install-package0] PASSED [ 1%] deployability/modules/provision/tests/test_actions.py::test_action_constructor[install-package1] PASSED [ 3%] @@ -112,22 +112,22 @@ The `.github/workflow/provision-unit-tests.yaml` automatically runs the unit tes The run results are shown in the `checks` tab or your GitHub pull request. ## Relevant Files -- `tests/test_[test name].py`: all the unit test files start with a `test_` prefix. There is one unit test file for - each tested class. +- `tests/test_[test name].py`: all the unit test files start with a `test_` prefix. There is one unit test file for + each tested class. - `tests/conftest.py`: contains the fixtures used throughout the unit tests. ## Unit test development guidelines and recommendations -- Use Python coding style standards and recommendations to develop unit tests: snake case for all variable and function - names, maximum line length of 120 characters, two empty lines must separate each function, typing all your functions +- Use Python coding style standards and recommendations to develop unit tests: snake case for all variable and function + names, maximum line length of 120 characters, two empty lines must separate each function, typing all your functions and return values, create Docstring for each function or method with numpy style. - Develop unit tests for each function or method of the module. -- Error flows are usually created in a second unit test with the suffix `_fail`. For example, the - `test_provision_handler_constructor` found in the `deployability/modules/provision/tests/test_handler.py` is the +- Error flows are usually created in a second unit test with the suffix `_fail`. For example, the + `test_provision_handler_constructor` found in the `deployability/modules/provision/tests/test_handler.py` is the unit test normal flow for the `ProvisionHandler` class constructor method. The `test_provision_handler_constructor_fail` unit test implements the error flow. - Use the pytest's decorator `@pytest.mark.parametrize` to implement test cases for the same unit test. -- Mock the object instance and functions used by your tested function using the `unitest.mock.patch` and +- Mock the object instance and functions used by your tested function using the `unitest.mock.patch` and `unitest.mock.patch.object` functions or decorators. - Try to factorize your testing code using `pytest.fixtures`. The shared fixtures are in the `conftest.py` file. In - many unit tests of this project, the fixtures implement a `request` object that receives parameters from the + many unit tests of this project, the fixtures implement a `request` object that receives parameters from the `pytest.mark.parametrize`. diff --git a/deployability/modules/provision/tests/test_models.py b/deployability/modules/provision/tests/test_models.py index 7d1afec555..62fb26e42f 100644 --- a/deployability/modules/provision/tests/test_models.py +++ b/deployability/modules/provision/tests/test_models.py @@ -10,7 +10,7 @@ @pytest.mark.parametrize('install', [(True), (False)]) def test_input_payload_constructor_components(install:bool): - """Test InputPayload constructor install and unininstall parameters. + """Test InputPayload constructor install and uninstall parameters. Parameters ---------- @@ -26,7 +26,7 @@ def test_input_payload_constructor_components(install:bool): install=components if install else [], uninstall=[] if install else components) assert payload.inventory == Path(path) - comp_list = payload.install if install else payload.uninstall + comp_list = payload.install if install else payload.uninstall assert comp_list[0] == ComponentInfo(component='component_1', type='component_type_1') assert comp_list[1] == ComponentInfo(component='component_2', type='package') assert comp_list[2] == ComponentInfo(component='linux wazuh-agent', type='package') diff --git a/deployability/modules/testing/README.MD b/deployability/modules/testing/README.MD index 6821ad98e5..682798e2f0 100644 --- a/deployability/modules/testing/README.MD +++ b/deployability/modules/testing/README.MD @@ -1,6 +1,6 @@ ## Testing Module - -### User documentation + +### User documentation The test module runs tests on the different components of Wazuh. It is designed so that you can perform installations, actions on components and uninstallations, performing validations at each step. @@ -15,7 +15,7 @@ This module can be executed as follows: The execution of the workflow is done through the installation of its library. -Initially, Python libraries must be installed. we recommended the use of virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. +Initially, Python libraries must be installed, we recommended the use of virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. 1. Create the python environment: @@ -64,14 +64,14 @@ Initially, Python libraries must be installed. we recommended the use of virtual >Note: It is possible to find some fixture examples in '[deployability/modules/workflow_engine/examples/](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/workflow_engine/examples)' Example: - + ```bash version: 0.1 - description: This workflow is used to test agents deployment por DDT1 PoC + description: This workflow is used to test agents' deployment for DDT1 PoC variables: agents-os: - linux-ubuntu-22.04-amd64 - manager-os: + manager-os: - linux-redhat-8-amd64 infra-provider: vagrant working-dir: /tmp/dtt1-poc @@ -169,14 +169,14 @@ Initially, Python libraries must be installed. we recommended the use of virtual depends-on ``` - >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your test (allocation, provision, and test) + >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your test (allocation, provision, and test) 7. Command execution (local): Execute the command by referencing the parameters required by the library (launcher). - + ```bash - python3 -m workflow_engine {.yaml fixture path} + python3 -m workflow_engine {.yaml fixture path} ``` Example @@ -192,7 +192,7 @@ To execute the testing module without installing the Workflow engine, it can be 1. Execution While in 'wazuh-qa/deployability' - + ```bash python3 modules/testing/main.py --wazuh-revision '{{ wazuh_revision }}' --wazuh-version '{{ wazuh_version }}' --component {{ component }} --tests 'install,restart,stop,uninstall' --targets '{"wazuh-1":"{{ inventory }}"}' --targets '{"wazuh-2":"{{ inventory }}"}' --live 'True' ``` @@ -212,7 +212,7 @@ To execute the testing module without installing the Workflow engine, it can be agent-{number}: They are the agents >If the manager component is tested, 'wazuh-' components must be entered in target. - If the agent component is tested, there must be a master and the rest must be 'agent-'s + If the agent component is tested, there must be a master and the rest must be 'agent-' --- @@ -256,19 +256,19 @@ For manual execution, an example command would be: The module is composed of: - **Launcher** ('[/wazuh-qa/deployability/modules/testing/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/main.py)'): Entry point for the workflow or the user who wishes to execute a test. - + - **Parameter validator** ('[/wazuh-qa/deployability/modules/testing/models.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/models.py)'): Validator for parameters entered by the Workflow or the user. - + - **Module functions** ('[/wazuh-qa/deployability/modules/testing/testing.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/testing.py)'): Module-specific functions responsible for triggering the test. - + - **Executor** ('[/wazuh-qa/deployability/modules/provision/actions.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/actions.py)'): Executes the provision using Actions in the generic module. - + - **Playbooks** ('[/wazuh-qa/deployability/modules/tests/playbooks](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/tests/playbooks)'): Contains setup, execution, and post-test cleanup playbooks. - + - **Tests** ('[/wazuh-qa/deployability/modules/tests](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/tests/)'): Contains the test_agent and test_manager directories where the tests to be executed are located. - + - **Helpers** ('[/wazuh-qa/deployability/modules/tests/helpers](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/tests/helpers)'): Contains files that enable the execution of the test. - + - **Conftest** ('[/wazuh-qa/deployability/modules/tests/conftest.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/tests/conftest.py)'): Contains information that will be transferred from the parameter validator to the tests. @@ -284,7 +284,7 @@ The test module must recieve the infrastructure generated and provisioned by the Testing of the manager component includes: `install`, `restart`, `stop` and `uninstall` Install should come at the beginning and uninstall at the end, other tests can change their order - + Testing of the agent component includes: `install`, `registration`, `restart`, `stop` and `uninstall` Install must come at the beginning followed by registration Uninstall must come at the end and the other tests can change their order diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index 30aefd8442..3611c7f5af 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -37,7 +37,7 @@ def install_manager(inventory_path, node_name, wazuh_version) -> None: commands = [ f"curl -sO https://packages.wazuh.com/{wazuh_version}/wazuh-install.sh", f"bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" - ] + ] logger.info(f'Installing Manager in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') Executor.execute_commands(inventory_path, commands) @@ -62,7 +62,7 @@ def install_managers(inventories_paths=[], node_names=[], wazuh_versions=[]) -> @staticmethod def uninstall_manager(inventory_path) -> None: """ - Unnstall Wazuh Manager in the host + Uninstall Wazuh Manager in the host Args: inventory_paths (str): hosts' inventory path @@ -94,7 +94,7 @@ def uninstall_manager(inventory_path) -> None: @staticmethod def uninstall_managers(inventories_paths=[]) -> None: """ - Unnstall Wazuh Managers in the hosts + Uninstall Wazuh Managers in the hosts Args: inventories_paths (list): list of hosts' inventory path @@ -117,7 +117,7 @@ def _uninstall_manager_callback(manager_params): def perform_action_and_scan(manager_params, action_callback) -> dict: """ Takes scans using filters, the callback action and compares the result - + Args: agent_params (str): agent parameters callbak (cb): callback (action) @@ -179,7 +179,7 @@ def perform_action_and_scan(manager_params, action_callback) -> dict: def perform_install_and_scan_for_manager(manager_params, manager_name, wazuh_params) -> None: """ Coordinates the action of install the manager and compares the checkfiles - + Args: manager_params (str): manager parameters wazuh_params (str): wazuh parameters @@ -195,7 +195,7 @@ def perform_install_and_scan_for_manager(manager_params, manager_name, wazuh_par def perform_uninstall_and_scan_for_manager(manager_params) -> None: """ Coordinates the action of uninstall the manager and compares the checkfiles - + Args: manager_params (str): manager parameters wazuh_params (str): wazuh parameters @@ -211,7 +211,7 @@ def perform_uninstall_and_scan_for_manager(manager_params) -> None: def assert_results(result) -> None: """ Gets the status of an agent given its name. - + Args: result (dict): result of comparison between pre and post action scan diff --git a/deployability/modules/workflow_engine/README.MD b/deployability/modules/workflow_engine/README.MD index 51eaad4ad6..95e6a0db5d 100644 --- a/deployability/modules/workflow_engine/README.MD +++ b/deployability/modules/workflow_engine/README.MD @@ -1,7 +1,7 @@ ## Workflow engine - -### User documentation - + +### User documentation + The execution of the Workflow is done through the installation of its library. Initially, Python libraries must be installed. It is recommended to use virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. @@ -40,13 +40,13 @@ Initially, Python libraries must be installed. It is recommended to use virtual It will be necessary to create a fixture (yaml file) where the infrastructure, provisioning, and tests to be executed will be declared. - >Note: It is possible to find some fixture examples in deployability/modules/workflow_engine/examples/ + >Note: It is possible to find some fixture examples in deployability/modules/workflow_engine/examples/ Example: - + ```bash version: 0.1 - description: This workflow is used to test agents deployment por DDT1 PoC + description: This workflow is used to test agents' deployment for DDT1 PoC variables: agents-os: - linux-ubuntu-22.04-amd64 @@ -231,14 +231,14 @@ Initially, Python libraries must be installed. It is recommended to use virtual depends-on ``` - >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your test (allocation, provision, and test) + >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your test (allocation, provision, and test) 7. Execution of Command (local): Execute the command by referencing the parameters required by the library (launcher). - + ```bash - python3 -m workflow_engine {.yaml fixture path} + python3 -m workflow_engine {.yaml fixture path} ``` Example @@ -286,7 +286,7 @@ tasks: args: - modules/testing/main.py - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: + - dependencies: - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "install,register,stop" diff --git a/deployability/modules/workflow_engine/examples/test/aws/test-agent-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml similarity index 92% rename from deployability/modules/workflow_engine/examples/test/aws/test-agent-complete.yaml rename to deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml index e992dfdc08..cba04ff94d 100755 --- a/deployability/modules/workflow_engine/examples/test/aws/test-agent-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml @@ -1,7 +1,13 @@ version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: This workflow is used to test agents' deployment for DDT1 PoC variables: agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 - linux-oracle-9-amd64 - linux-centos-7-amd64 - linux-centos-8-amd64 diff --git a/deployability/modules/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml similarity index 98% rename from deployability/modules/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml rename to deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml index ad39a8b3c5..bb71895416 100755 --- a/deployability/modules/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: Test agent restart with provisioning agents' with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml similarity index 98% rename from deployability/modules/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml rename to deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml index 74da24139b..cc541a7238 100755 --- a/deployability/modules/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: Test agent stop with provisioning agents' with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/test/aws/test-agent-susse.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-susse.yaml similarity index 92% rename from deployability/modules/workflow_engine/examples/test/aws/test-agent-susse.yaml rename to deployability/modules/workflow_engine/examples/agent/aws/test-agent-susse.yaml index 55392f2c44..effe9897c8 100644 --- a/deployability/modules/workflow_engine/examples/test/aws/test-agent-susse.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-susse.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: Test agent SUSE variables: agent-os: - linux-opensuse-15-amd64 @@ -25,6 +25,15 @@ tasks: - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" # Unique agent allocate task - task: "allocate-agent-{agent}" @@ -68,12 +77,12 @@ tasks: - modules/provision/main.py - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - install: + - component: tar + - component: curl - component: wazuh-manager type: assistant version: 4.7.3 live: True - - component: tar - - component: curl depends-on: - "allocate-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml similarity index 98% rename from deployability/modules/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml rename to deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml index 8ab89589d6..f5bbb7e7cc 100755 --- a/deployability/modules/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: Test agent uninstall with provisioning agents' with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-complete.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete.yaml similarity index 97% rename from deployability/modules/workflow_engine/examples/test/vagrant/test-agent-complete.yaml rename to deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete.yaml index a2cb5a2482..529814edd6 100755 --- a/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: This workflow is used to test agents' deployment for DDT1 PoC variables: agent-os: - linux-oracle-9-amd64 @@ -31,6 +31,7 @@ tasks: - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + on-error: "abort-all" cleanup: this: process with: diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov.yaml new file mode 100755 index 0000000000..cafcb44d7b --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov.yaml @@ -0,0 +1,143 @@ +version: 0.1 +description: Test agent restart with provisioning agents' with provision module +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "restart" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml similarity index 97% rename from deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml rename to deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml index 913dbaed3d..e2ec2581ba 100755 --- a/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: Test agent stop with provisioning agents' with provision module variables: agent-os: - linux-centos-7-amd64 diff --git a/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov.yaml similarity index 98% rename from deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml rename to deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov.yaml index e61393c256..7b2fc28b63 100755 --- a/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: Test agent stop with provisioning agents' with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov.yaml similarity index 98% rename from deployability/modules/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml rename to deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov.yaml index 5568493d71..5c753ccfc0 100755 --- a/deployability/modules/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: Test agent uninstall with provisioning agents' with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/dtt1-agents-aws.yaml b/deployability/modules/workflow_engine/examples/dtt1-agents-aws.yaml deleted file mode 100755 index 52e8244c87..0000000000 --- a/deployability/modules/workflow_engine/examples/dtt1-agents-aws.yaml +++ /dev/null @@ -1,142 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: aws - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: package - version: 4.7.3 - live: True - depends-on: - - "allocate-agent-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agents-os - as: agent - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "provision-manager-{manager-os}" - - "allocate-agent-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/dtt1-agents-poc-aws.yaml b/deployability/modules/workflow_engine/examples/dtt1-agents-poc-aws.yaml deleted file mode 100644 index ddc11b67c8..0000000000 --- a/deployability/modules/workflow_engine/examples/dtt1-agents-poc-aws.yaml +++ /dev/null @@ -1,106 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-oracle-9-amd64 - - linux-amazon-2-amd64 - - #- linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-centos-7-amd64 - - - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - - linux-amazon-2023-amd64 - - linux-suse-15-amd64 - #- linux-opensuse-15-amd64 - - manager-os: - #- linux-oracle-9-amd64 - #- linux-ubuntu-20.04-amd64 - #- linux-centos-8-amd64 - - linux-redhat-7-amd64 - infra-provider: aws - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager}" - - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager}/track.yaml" - - label-termination-date: "1d" - - label-team: "QA" - foreach: - - variable: manager-os - as: manager - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "QA" - foreach: - - variable: agent-os - as: agent - - # Generic agent test task - - task: "run-agent-tests" - description: "Run tests install for the agent." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" - - agent-1: "{working-dir}/agent-linux-ubuntu-20.04-amd64/inventory.yaml" - - agent-2: "{working-dir}/agent-linux-ubuntu-22.04-amd64/inventory.yaml" - - agent-3: "{working-dir}/agent-linux-oracle-9-amd64/inventory.yaml" - - agent-4: "{working-dir}/agent-linux-amazon-2-amd64/inventory.yaml" - #- agent-5: "{working-dir}/agent-linux-redhat-7-amd64/inventory.yaml" - - agent-5: "{working-dir}/agent-linux-redhat-8-amd64/inventory.yaml" - - agent-6: "{working-dir}/agent-linux-redhat-9-amd64/inventory.yaml" - - agent-7: "{working-dir}/agent-linux-centos-7-amd64/inventory.yaml" - - agent-8: "{working-dir}/agent-linux-centos-8-amd64/inventory.yaml" - - agent-9: "{working-dir}/agent-linux-debian-10-amd64/inventory.yaml" - - agent-10: "{working-dir}/agent-linux-debian-11-amd64/inventory.yaml" - - agent-11: "{working-dir}/agent-linux-debian-12-amd64/inventory.yaml" - #- agent-12: "{working-dir}/agent-linux-opensuse-15-amd64/inventory.yaml" - - agent-12: "{working-dir}/agent-linux-suse-15-amd64/inventory.yaml" - - agent-13: "{working-dir}/agent-linux-amazon-2023-amd64/inventory.yaml" - - - tests: "install,registration,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml b/deployability/modules/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml deleted file mode 100644 index fbbe151127..0000000000 --- a/deployability/modules/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml +++ /dev/null @@ -1,105 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-oracle-9-amd64 - - linux-amazon-2-amd64 - - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-centos-7-amd64 - - - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - #- linux-amazon-2023-amd64 - #- linux-suse-15-amd64 - - - manager-os: - - linux-opensuse-15-amd64 - #- linux-ubuntu-22.04-amd64 - #- linux-debian-12-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager}" - - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager}/track.yaml" - - label-termination-date: "1d" - - label-team: "QA" - foreach: - - variable: manager-os - as: manager - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "QA" - foreach: - - variable: agent-os - as: agent - - # Generic agent test task - - task: "run-agent-tests" - description: "Run tests install for the agent." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-linux-opensuse-15-amd64/inventory.yaml" - - agent-1: "{working-dir}/agent-linux-ubuntu-20.04-amd64/inventory.yaml" - - agent-2: "{working-dir}/agent-linux-ubuntu-22.04-amd64/inventory.yaml" - - agent-3: "{working-dir}/agent-linux-oracle-9-amd64/inventory.yaml" - - agent-4: "{working-dir}/agent-linux-amazon-2-amd64/inventory.yaml" - - agent-5: "{working-dir}/agent-linux-centos-7-amd64/inventory.yaml" - - agent-6: "{working-dir}/agent-linux-centos-8-amd64/inventory.yaml" - - agent-7: "{working-dir}/agent-linux-redhat-7-amd64/inventory.yaml" - - agent-8: "{working-dir}/agent-linux-redhat-8-amd64/inventory.yaml" - - agent-9: "{working-dir}/agent-linux-redhat-9-amd64/inventory.yaml" - - agent-10: "{working-dir}/agent-linux-debian-10-amd64/inventory.yaml" - - agent-11: "{working-dir}/agent-linux-debian-11-amd64/inventory.yaml" - - agent-12: "{working-dir}/agent-linux-debian-12-amd64/inventory.yaml" - #- agent-12: "{working-dir}/agent-linux-opensuse-15-amd64/inventory.yaml" - #- agent-12: "{working-dir}/agent-linux-suse-15-amd64/inventory.yaml" - #- agent-13: "{working-dir}/agent-linux-amazon-2023-amd64/inventory.yaml" - - - tests: "install,registration,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/dtt1-agents-vagrant.yaml b/deployability/modules/workflow_engine/examples/dtt1-agents-vagrant.yaml deleted file mode 100755 index ca320a07e2..0000000000 --- a/deployability/modules/workflow_engine/examples/dtt1-agents-vagrant.yaml +++ /dev/null @@ -1,138 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: package - version: 4.7.3 - live: True - depends-on: - - "allocate-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agents-os - as: agent - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "provision-manager-{manager-os}" - - "allocate-agent-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/dtt1-agents.yaml b/deployability/modules/workflow_engine/examples/dtt1-agents.yaml deleted file mode 100755 index e8a827282d..0000000000 --- a/deployability/modules/workflow_engine/examples/dtt1-agents.yaml +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -version: 0.1 -description: This workflow is used to test agents deployment. -variables: - agents-os: - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-fedora-37-amd64 - - linux-fedora-38-amd64 - - linux-suse-15-amd64 - - linux-opensuse-15-amd64 - - linux-oracle-9-amd64 - - linux-amazon-2-amd64 - - linux-amazon-2023-amd64 - - windows-10-amd64 - - windows-11-amd64 - - windows-server2012-amd64 - - windows-server2016-amd64 - - windows-server2019-amd64 - - windows-server2022-amd64 - - macos-13.3-amd64 - - macos-14.2-amd64 - manager-os: linux-amazon-2023-amd64 - -tasks: - # Generic agent test task - - task: "test-agent-{agent}" - description: "Run tests for the {agent} agent." - do: - this: process - with: - path: /bin/echo - args: - - -n - - "Running tests for {agent}" - depends-on: - - "provision-agent-{agent}" - foreach: - - variable: agents-os - as: agent - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: /bin/echo - args: - - -n - - "Running provision for manager" - depends-on: - - "allocate-manager-{manager-os}" - - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: /bin/echo - args: - - -n - - "Running allocate for manager" - cleanup: - this: process - with: - path: /bin/echo - args: - - -n - - "Running cleanup for manager" - - # Generic agent provision task - - task: "provision-agent-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: /bin/echo - args: - - -n - - "Running provision for {agent}" - depends-on: - - "allocate-agent-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agents-os - as: agent - - # Generic agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the {agent} agent." - do: - this: process - with: - path: /bin/echo - args: - - -n - - "Running allocate for {agent}" - cleanup: - this: process - with: - path: /bin/echo - args: - - -n - - "Running cleanup for allocate for {agent}" - foreach: - - variable: agents-os - as: agent \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/dtt1-managers.yaml b/deployability/modules/workflow_engine/examples/dtt1-managers.yaml deleted file mode 100755 index 503cd115a3..0000000000 --- a/deployability/modules/workflow_engine/examples/dtt1-managers.yaml +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -version: 0.1 -description: This workflow is used to test managers deployment. Two agents per manager are deployed. -variables: - agents-os: - - linux-debian-12-amd64 - - linux-ubuntu-22.04-amd64 - managers-os: - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-fedora-37-amd64 - - linux-fedora-38-amd64 - - linux-suse-15-amd64 - - linux-opensuse-15-amd64 - - linux-oracle-9-amd64 - - linux-amazon-2-amd64 - - linux-amazon-2023-amd64 -tasks: - # Generic manager test task - - task: "test-{manager}-{agent}" - do: - this: process - with: - path: /bin/echo - args: - - Executing tests for {manager} manager with {agent} agent. - depends-on: - - "provision-{manager}-manager" - - "provision-{agent}-agent-for-{manager}-manager" - foreach: - - variable: managers-os - as: manager - - variable: agents-os - as: agent - - # --------- Provision -------------- - # Generic manager provision task - - task: "provision-{manager}-manager" - do: - this: process - with: - path: /bin/echo - args: - - Executing provision for {manager} as a manager. - depends-on: - - "allocate-{manager}-manager" - foreach: - - variable: managers-os - as: manager - - # Generic agent provision task - - task: "provision-{agent}-agent-for-{manager}-manager" - do: - this: process - with: - path: /bin/echo - args: - - Executing provision for {agent} as an agent. - depends-on: - - "allocate-{agent}-agent-for-{manager}-manager" - foreach: - - variable: managers-os - as: manager - - variable: agents-os - as: agent - - # --------- Allocate -------------- - # Generic manager allocate task - - task: "allocate-{manager}-manager" - do: - this: process - with: - path: /bin/echo - args: - - Executing allocation for {manager} as a manager. - foreach: - - variable: managers-os - as: manager - - # Generic agent allocate task - - task: "allocate-{agent}-agent-for-{manager}-manager" - do: - this: process - with: - path: /bin/echo - args: - - Executing allocation for {agent} as an agent. - foreach: - - variable: managers-os - as: manager - - variable: agents-os - as: agent diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws-all.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws-all.yaml new file mode 100644 index 0000000000..d1e999994d --- /dev/null +++ b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws-all.yaml @@ -0,0 +1,77 @@ +version: 0.1 +description: This workflow is used to test managers' deployment for DDT1 PoC +variables: + manager-os: + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager}" + - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager}/track.yaml" + #- ssh-key: "/home/akim/Desktop/personal/Ephemeral" + foreach: + - variable: manager-os + as: manager + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + + # Generic manager test task + - task: "run-manager-tests" + description: "Run tests install for the manager." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" + - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - wazuh-3: "{working-dir}/manager-linux-ubuntu-22.04-amd64/inventory.yaml" + - wazuh-4: "{working-dir}/manager-linux-oracle-9-amd64/inventory.yaml" + - wazuh-5: "{working-dir}/manager-linux-amazon-2-amd64/inventory.yaml" + - wazuh-6: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" + - wazuh-7: "{working-dir}/manager-linux-redhat-8-amd64/inventory.yaml" + - wazuh-8: "{working-dir}/manager-linux-redhat-9-amd64/inventory.yaml" + - wazuh-9: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" + - wazuh-10: "{working-dir}/manager-linux-debian-10-amd64/inventory.yaml" + - wazuh-11: "{working-dir}/manager-linux-debian-11-amd64/inventory.yaml" + - wazuh-12: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" + - tests: "install,restart,stop,uninstall" + - component: "manager" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" diff --git a/deployability/modules/workflow_engine/examples/dtt1-managers-poc-aws.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml similarity index 92% rename from deployability/modules/workflow_engine/examples/dtt1-managers-poc-aws.yaml rename to deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml index 91bae10b90..1f685bdd4b 100644 --- a/deployability/modules/workflow_engine/examples/dtt1-managers-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: This workflow is used to test manager deployment for DDT1 PoC variables: infra-provider: aws working-dir: /tmp/dtt1-poc @@ -64,11 +64,11 @@ tasks: - targets: - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - - tests: "stop" + - tests: "install,registration,restart,stop,uninstall" - component: "manager" - wazuh-version: "4.7.3" - wazuh-revision: "40714" - live: "True" depends-on: - "allocate-manager-linux-ubuntu-20.04-amd64" - - "allocate-manager-linux-centos-7-amd64" \ No newline at end of file + - "allocate-manager-linux-centos-7-amd64" diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-aws-all.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-aws-all.yaml new file mode 100644 index 0000000000..4666da8eae --- /dev/null +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-aws-all.yaml @@ -0,0 +1,77 @@ +version: 0.1 +description: This workflow is used to test managers' deployment for DDT1 PoC +variables: + manager-os: + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager}" + - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager}/track.yaml" + #- ssh-key: "/home/akim/Desktop/personal/Ephemeral" + foreach: + - variable: manager-os + as: manager + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + + # Generic manager test task + - task: "run-manager-tests" + description: "Run tests install for the manager." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" + - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - wazuh-3: "{working-dir}/manager-linux-ubuntu-22.04-amd64/inventory.yaml" + - wazuh-4: "{working-dir}/manager-linux-oracle-9-amd64/inventory.yaml" + - wazuh-5: "{working-dir}/manager-linux-amazon-2-amd64/inventory.yaml" + - wazuh-6: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" + - wazuh-7: "{working-dir}/manager-linux-redhat-8-amd64/inventory.yaml" + - wazuh-8: "{working-dir}/manager-linux-redhat-9-amd64/inventory.yaml" + - wazuh-9: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" + - wazuh-10: "{working-dir}/manager-linux-debian-10-amd64/inventory.yaml" + - wazuh-11: "{working-dir}/manager-linux-debian-11-amd64/inventory.yaml" + - wazuh-12: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" + - tests: "install,restart,stop,uninstall" + - component: "manager" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" diff --git a/deployability/modules/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml similarity index 96% rename from deployability/modules/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml rename to deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index 35817f2169..0336f611e3 100644 --- a/deployability/modules/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -72,11 +72,11 @@ tasks: - targets: - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - - tests: "stop" + - tests: "install,restart,stop,uninstall" - component: "manager" - wazuh-version: "4.7.3" - wazuh-revision: "40714" - live: "True" depends-on: - "allocate-manager-linux-ubuntu-20.04-amd64" - - "allocate-manager-linux-centos-7-amd64" \ No newline at end of file + - "allocate-manager-linux-centos-7-amd64" diff --git a/deployability/modules/workflow_engine/examples/test.yaml b/deployability/modules/workflow_engine/examples/test.yaml deleted file mode 100644 index 9003f9d297..0000000000 --- a/deployability/modules/workflow_engine/examples/test.yaml +++ /dev/null @@ -1,137 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: package - version: 4.7.3 - live: True - depends-on: - - "allocate-agent-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agent-os - as: agent - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "stop,restart,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "provision-install-{agent}" diff --git a/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml b/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml index a637e4184f..44e26cdca2 100644 --- a/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml +++ b/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml @@ -70,7 +70,7 @@ tasks: - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - install: - component: wazuh-manager - type: aio + type: assistant version: "4.7.0" depends-on: - "allocate-manager" @@ -111,7 +111,7 @@ tasks: - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - install: - component: wazuh-agent - type: aio + type: assistant version: "4.8.0" live: False depends-on: diff --git a/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml b/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml index 6b2d2512d0..b1d56bea03 100644 --- a/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml +++ b/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml @@ -69,7 +69,7 @@ tasks: - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - install: - component: wazuh-manager - type: aio + type: assistant version: "4.7.0" depends-on: - "allocate-manager" @@ -111,7 +111,7 @@ tasks: - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - install: - component: wazuh-agent - type: aio + type: assistant version: "4.8.0" live: False depends-on: diff --git a/deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml b/deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml index 62c130f64b..3cff365d08 100644 --- a/deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml +++ b/deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml @@ -56,7 +56,7 @@ tasks: - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - install: - component: wazuh-manager - type: aio + type: assistant version: "4.7.0" depends-on: - "allocate-manager" @@ -98,7 +98,7 @@ tasks: - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - install: - component: wazuh-agent - type: aio + type: assistant version: "4.8.0" live: False depends-on: diff --git a/deployability/modules/workflow_engine/tests/data/wf-ok.yaml b/deployability/modules/workflow_engine/tests/data/wf-ok.yaml index fa979a6f81..5c6fec8589 100644 --- a/deployability/modules/workflow_engine/tests/data/wf-ok.yaml +++ b/deployability/modules/workflow_engine/tests/data/wf-ok.yaml @@ -70,7 +70,7 @@ tasks: - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - install: - component: wazuh-manager - type: aio + type: assistant version: "4.7.0" depends-on: - "allocate-manager" @@ -112,7 +112,7 @@ tasks: - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - install: - component: wazuh-agent - type: aio + type: assistant version: "4.8.0" live: False depends-on: diff --git a/deployability/plugins/influxdb_reporter/README.md b/deployability/plugins/influxdb_reporter/README.md index 3566184208..63a02bdf1f 100755 --- a/deployability/plugins/influxdb_reporter/README.md +++ b/deployability/plugins/influxdb_reporter/README.md @@ -8,7 +8,7 @@ This is a plugin to send the results of the tests to an InfluxDB database. 1. Clone the repository ```shellsession - $ git clone https://github.com/wazuh/wazuh-qa.git -b enhancement/4736-dtt1-influxdb-plugin + $ git clone https://github.com/wazuh/wazuh-qa.git -b ``` 2. Open the repository folder ```shellsession diff --git a/deployability/plugins/influxdb_reporter/setup.py b/deployability/plugins/influxdb_reporter/setup.py index 7bf0a3f324..122d7bf069 100755 --- a/deployability/plugins/influxdb_reporter/setup.py +++ b/deployability/plugins/influxdb_reporter/setup.py @@ -14,8 +14,8 @@ long_description=long_description, long_description_content_type='text/markdown', packages=['pytest_influxdb'], - author='quebim', - author_email='kevinledesmam95@gmail.com', + author='AUTHOR', + author_email='EMAIL', install_requires=[ 'pytest>=3.8.0', 'influxdb-client>=1.38.0' From bfbe44483fa790fcea60c3e3a122febd5e33f410 Mon Sep 17 00:00:00 2001 From: Marcelo Ariel Hamra Date: Thu, 11 Apr 2024 13:00:12 -0300 Subject: [PATCH 019/195] Replace os.getlogin by getpass.getuser --- deployability/modules/testing/testing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployability/modules/testing/testing.py b/deployability/modules/testing/testing.py index 4a7cfbe648..72070eaa52 100644 --- a/deployability/modules/testing/testing.py +++ b/deployability/modules/testing/testing.py @@ -3,7 +3,7 @@ # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 import json -import os +import getpass from modules.generic import Ansible, Inventory from modules.generic.utils import Utils @@ -50,7 +50,7 @@ def run(cls, payload: InputPayload) -> None: # Set extra vars extra_vars['local_host_path'] = str(Path(__file__).parent.parent.parent) - extra_vars['current_user'] = os.getlogin() + extra_vars['current_user'] = getpass.getuser() logger.debug(f"Using extra vars: {extra_vars}") From 4db5df48f2fffb02a146a16818f4029d890ee646 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Thu, 11 Apr 2024 19:17:29 -0300 Subject: [PATCH 020/195] Fix SUSE file name and PoC example --- .../agent/aws/{test-agent-susse.yaml => test-agent-suse.yaml} | 0 .../examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename deployability/modules/workflow_engine/examples/agent/aws/{test-agent-susse.yaml => test-agent-suse.yaml} (100%) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-susse.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/test-agent-susse.yaml rename to deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index 0336f611e3..6d6510d2a1 100644 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: This workflow is used to test agents' deployment for DDT1 PoC variables: infra-provider: vagrant working-dir: /tmp/dtt1-poc From 97ccd972abd6599d92770f7034532083c28cb36c Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 12 Apr 2024 12:51:13 -0300 Subject: [PATCH 021/195] Rollback in case of connection error --- .../modules/allocation/allocation.py | 82 ++++++++++++++++++- .../modules/allocation/aws/instance.py | 2 +- .../modules/allocation/generic/models.py | 1 + deployability/modules/allocation/main.py | 1 + .../modules/allocation/static/specs/os.yml | 2 +- .../modules/allocation/vagrant/instance.py | 2 +- 6 files changed, 85 insertions(+), 5 deletions(-) diff --git a/deployability/modules/allocation/allocation.py b/deployability/modules/allocation/allocation.py index 46158d6896..b1df115de9 100755 --- a/deployability/modules/allocation/allocation.py +++ b/deployability/modules/allocation/allocation.py @@ -3,6 +3,8 @@ # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 import yaml +import paramiko, logging, time +import winrm from pathlib import Path @@ -58,8 +60,18 @@ def __create(cls, payload: models.CreationPayload): instance.start() logger.info(f"Instance {instance.identifier} started.") # Generate the inventory and track files. - cls.__generate_inventory(instance, payload.inventory_output) - cls.__generate_track_file(instance, payload.provider, payload.track_output) + inventory = cls.__generate_inventory(instance, payload.inventory_output) + # Validate connection + check_connection = cls.__check_connection(inventory) + track_file = cls.__generate_track_file(instance, payload.provider, payload.track_output) + if check_connection: + if payload.rollback: + logger.warning(f"Rolling back instance creation.") + track_payload = {'track_output': track_file} + cls.__delete(track_payload) + logger.info(f"Instance {instance.identifier} deleted.") + else: + logger.warning(f'The VM will not be automatically removed. Please remove it executing Allocation module with --action delete.') @classmethod def __delete(cls, payload: models.InstancePayload) -> None: @@ -138,6 +150,7 @@ def __generate_inventory(instance: Instance, inventory_path: Path) -> None: with open(inventory_path, 'w') as f: yaml.dump(inventory.model_dump(exclude_none=True), f) logger.info(f"Inventory file generated at {inventory_path}") + return inventory @staticmethod def __generate_track_file(instance: Instance, provider_name: str, track_path: Path) -> None: @@ -172,3 +185,68 @@ def __generate_track_file(instance: Instance, provider_name: str, track_path: P if Path(str(instance.path) + "/ppc-key").exists(): Path(str(instance.path) + "/ppc-key").unlink() logger.info(f"Track file generated at {track_path}") + return track_path + + @staticmethod + def __check_connection(inventory: models.InventoryOutput, attempts=10, sleep=60) -> None: + """ + Checks if the ssh connection is successful. + + Args: + inventory (InventoryOutput): The inventory object. + attempts (int): The number of attempts to try the connection. + sleep (int): The time to wait between attempts. + + """ + + for attempt in range(1, attempts + 1): + if inventory.ansible_connection == 'winrm': + if inventory.ansible_port == 5986: + protocol = 'https' + else: + protocol = 'http' + endpoint_url = f'{protocol}://{inventory.ansible_host}:{inventory.ansible_port}' + try: + session = winrm.Session(endpoint_url, auth=(inventory.ansible_user, inventory.ansible_password),transport='ntlm', server_cert_validation='ignore') + cmd = session.run_cmd('ipconfig') + if cmd.status_code == 0: + logger.info("WinRM connection successful.") + return False + else: + logger.error(f'WinRM connection failed. Check the credentials in the inventory file.') + return True + except Exception as e: + logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') + time.sleep(sleep) + else: + ssh = paramiko.SSHClient() + paramiko.util.get_logger("paramiko").setLevel(logging.WARNING) + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + if inventory.ansible_ssh_private_key_file: + ssh_parameters = { + 'hostname': inventory.ansible_host, + 'port': inventory.ansible_port, + 'username': inventory.ansible_user, + 'key_filename': inventory.ansible_ssh_private_key_file + } + else: + ssh_parameters = { + 'hostname': inventory.ansible_host, + 'port': inventory.ansible_port, + 'username': inventory.ansible_user, + 'password': inventory.ansible_password + } + try: + ssh.connect(**ssh_parameters) + logger.info("SSH connection successful.") + ssh.close() + return False + except paramiko.AuthenticationException: + logger.error(f'Authentication error. Check the credentials in the inventory file.') + return True + except Exception as e: + logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') + time.sleep(sleep) + + logger.error(f'Connection attempts failed after {attempts} tries. Connection timeout.') + return True diff --git a/deployability/modules/allocation/aws/instance.py b/deployability/modules/allocation/aws/instance.py index e4978d1048..f02cfbd710 100644 --- a/deployability/modules/allocation/aws/instance.py +++ b/deployability/modules/allocation/aws/instance.py @@ -77,7 +77,7 @@ def ssh_connection_info(self) -> ConnectionInfo: if self.platform == 'windows': return ConnectionInfo(hostname=self._instance.public_dns_name, user=self._user, - port=3389, + port=5986, password=str(self.credentials.name)) else: return ConnectionInfo(hostname=self._instance.public_dns_name, diff --git a/deployability/modules/allocation/generic/models.py b/deployability/modules/allocation/generic/models.py index 807ef6e576..fc5274a558 100644 --- a/deployability/modules/allocation/generic/models.py +++ b/deployability/modules/allocation/generic/models.py @@ -59,6 +59,7 @@ class InputPayload(BaseModel): label_team: str | None = None label_termination_date: str | None = None instance_name: str | None = None + rollback: bool class CreationPayload(InputPayload): provider: str diff --git a/deployability/modules/allocation/main.py b/deployability/modules/allocation/main.py index ddb4dfb8e8..1f92ff9377 100755 --- a/deployability/modules/allocation/main.py +++ b/deployability/modules/allocation/main.py @@ -27,6 +27,7 @@ def parse_arguments(): parser.add_argument("--label-team", required=False, default=None) parser.add_argument("--label-termination-date", required=False, default=None) parser.add_argument("--instance-name", required=False, default=None) + parser.add_argument("--rollback", choices=['True', 'False'], required=False, default=True) return parser.parse_args() diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index b0cd4e2063..574ff9e7a3 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -377,7 +377,7 @@ aws: zone: us-east-1 user: Administrator windows-server-2016-amd64: - ami: ami-04d7825822fe66af3 + ami: ami-0f8b5c22ce0a7cf3b zone: us-east-1 user: Administrator windows-server-2019-amd64: diff --git a/deployability/modules/allocation/vagrant/instance.py b/deployability/modules/allocation/vagrant/instance.py index d0a9deb4ad..f5bb5bfedc 100755 --- a/deployability/modules/allocation/vagrant/instance.py +++ b/deployability/modules/allocation/vagrant/instance.py @@ -182,7 +182,7 @@ def ssh_connection_info(self) -> ConnectionInfo: if match and key == 'hostname': ip = match.group(1).strip() ssh_config['hostname'] = ip - ssh_config['port'] = 3389 + ssh_config['port'] = 5985 ssh_config['user'] = 'vagrant' ssh_config['password'] = 'vagrant' else: From 0b1abf4aed57ef00c7592dbae1dca788e89e226a Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 12 Apr 2024 17:57:08 +0200 Subject: [PATCH 022/195] fix(#5210): Fixing yamls --- .../aws/test-agent-restart-ins-prov.yaml | 2 +- .../agent/aws/test-agent-stop-ins-prov.yaml | 8 +- .../aws/test-agent-uninstall-ins-prov.yaml | 4 +- .../vagrant/test-agent-restart-ins-prov.yaml | 4 - .../vagrant/test-agent-stop-ins-prov-2.yaml | 132 ------------------ .../vagrant/test-agent-stop-ins-prov.yaml | 6 + .../test-agent-uninstall-ins-prov.yaml | 4 - .../aws/dtt1-managers-poc-aws-all.yaml | 77 ---------- .../manager/aws/dtt1-managers-poc-aws.yaml | 70 ++++------ .../vagrant/dtt1-managers-poc-aws-all.yaml | 77 ---------- .../vagrant/dtt1-managers-poc-vagrant.yaml | 80 +++++------ 11 files changed, 76 insertions(+), 388 deletions(-) delete mode 100755 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml delete mode 100644 deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws-all.yaml delete mode 100644 deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-aws-all.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml index bb71895416..cafcb44d7b 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml @@ -16,7 +16,7 @@ variables: - linux-redhat-9-amd64 - linux-amazon-2-amd64 manager-os: linux-ubuntu-22.04-amd64 - infra-provider: aws + infra-provider: vagrant working-dir: /tmp/dtt1-poc tasks: diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml index cc541a7238..29238a9a8f 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent stop with provisioning agents' with provision module +description: Test agent restart with provisioning agents' with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 @@ -15,8 +15,8 @@ variables: - linux-redhat-8-amd64 - linux-redhat-9-amd64 - linux-amazon-2-amd64 - manager-os: linux-ubuntu-20.04-amd64 - infra-provider: aws + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant working-dir: /tmp/dtt1-poc tasks: @@ -140,4 +140,4 @@ tasks: - variable: agent-os as: agent depends-on: - - "provision-install-{agent}" + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml index f5bbb7e7cc..13bf298afe 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent uninstall with provisioning agents' with provision module +description: Test agent restart with provisioning agents' with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 @@ -16,7 +16,7 @@ variables: - linux-redhat-9-amd64 - linux-amazon-2-amd64 manager-os: linux-ubuntu-22.04-amd64 - infra-provider: aws + infra-provider: vagrant working-dir: /tmp/dtt1-poc tasks: diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov.yaml index cafcb44d7b..fd3a44ebf4 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov.yaml @@ -35,8 +35,6 @@ tasks: - composite-name: "{manager-os}" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" on-error: "abort-all" cleanup: this: process @@ -62,8 +60,6 @@ tasks: - composite-name: "{agent}" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml deleted file mode 100755 index e2ec2581ba..0000000000 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml +++ /dev/null @@ -1,132 +0,0 @@ -version: 0.1 -description: Test agent stop with provisioning agents' with provision module -variables: - agent-os: - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - on-error: "abort-all" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: package - version: 4.7.3 - live: True - depends-on: - - "allocate-agent-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agent-os - as: agent - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "stop" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov.yaml index 7b2fc28b63..318d85c711 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov.yaml @@ -9,6 +9,12 @@ variables: - linux-debian-11-amd64 - linux-debian-12-amd64 - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 manager-os: linux-ubuntu-22.04-amd64 infra-provider: vagrant working-dir: /tmp/dtt1-poc diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov.yaml index 5c753ccfc0..932c429e6a 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov.yaml @@ -35,8 +35,6 @@ tasks: - composite-name: "{manager-os}" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" on-error: "abort-all" cleanup: this: process @@ -62,8 +60,6 @@ tasks: - composite-name: "{agent}" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws-all.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws-all.yaml deleted file mode 100644 index d1e999994d..0000000000 --- a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws-all.yaml +++ /dev/null @@ -1,77 +0,0 @@ -version: 0.1 -description: This workflow is used to test managers' deployment for DDT1 PoC -variables: - manager-os: - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-oracle-9-amd64 - - linux-amazon-2-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - infra-provider: aws - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager}" - - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager}/track.yaml" - #- ssh-key: "/home/akim/Desktop/personal/Ephemeral" - foreach: - - variable: manager-os - as: manager - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - - # Generic manager test task - - task: "run-manager-tests" - description: "Run tests install for the manager." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - - wazuh-3: "{working-dir}/manager-linux-ubuntu-22.04-amd64/inventory.yaml" - - wazuh-4: "{working-dir}/manager-linux-oracle-9-amd64/inventory.yaml" - - wazuh-5: "{working-dir}/manager-linux-amazon-2-amd64/inventory.yaml" - - wazuh-6: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" - - wazuh-7: "{working-dir}/manager-linux-redhat-8-amd64/inventory.yaml" - - wazuh-8: "{working-dir}/manager-linux-redhat-9-amd64/inventory.yaml" - - wazuh-9: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" - - wazuh-10: "{working-dir}/manager-linux-debian-10-amd64/inventory.yaml" - - wazuh-11: "{working-dir}/manager-linux-debian-11-amd64/inventory.yaml" - - wazuh-12: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" - - tests: "install,restart,stop,uninstall" - - component: "manager" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml index 1f685bdd4b..e11f90c20b 100644 --- a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml @@ -1,12 +1,24 @@ version: 0.1 -description: This workflow is used to test manager deployment for DDT1 PoC +description: This workflow is used to test agents deployment por DDT1 PoC variables: + manager-os: + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-amazon-2-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 infra-provider: aws working-dir: /tmp/dtt1-poc tasks: # Unique manager allocate task - - task: "allocate-manager-linux-centos-7-amd64" + - task: "allocate-manager-{manager}" description: "Allocate resources for the manager." do: this: process @@ -17,40 +29,14 @@ tasks: - action: create - provider: "{infra-provider}" - size: large - - composite-name: "linux-centos-7-amd64" - - inventory-output: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" + - composite-name: "{manager}" + - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager}/track.yaml" - label-termination-date: "1d" - label-team: "qa" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" - - - # Unique manager allocate task - - task: "allocate-manager-linux-ubuntu-20.04-amd64" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "linux-ubuntu-20.04-amd64" - - inventory-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - - track-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - - + foreach: + - variable: manager-os + as: manager # Generic manager test task - task: "run-manager-tests" @@ -64,11 +50,17 @@ tasks: - targets: - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" + - wazuh-3: "{working-dir}/manager-linux-ubuntu-22.04-amd64/inventory.yaml" + - wazuh-4: "{working-dir}/manager-linux-amazon-2-amd64/inventory.yaml" + - wazuh-5: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" + - wazuh-6: "{working-dir}/manager-linux-redhat-8-amd64/inventory.yaml" + - wazuh-7: "{working-dir}/manager-linux-redhat-9-amd64/inventory.yaml" + - wazuh-8: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" + - wazuh-9: "{working-dir}/manager-linux-debian-10-amd64/inventory.yaml" + - wazuh-10: "{working-dir}/manager-linux-debian-11-amd64/inventory.yaml" + - wazuh-11: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" + - tests: "install,restart,stop,uninstall" - component: "manager" - wazuh-version: "4.7.3" - wazuh-revision: "40714" - - live: "True" - depends-on: - - "allocate-manager-linux-ubuntu-20.04-amd64" - - "allocate-manager-linux-centos-7-amd64" + - live: "True" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-aws-all.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-aws-all.yaml deleted file mode 100644 index 4666da8eae..0000000000 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-aws-all.yaml +++ /dev/null @@ -1,77 +0,0 @@ -version: 0.1 -description: This workflow is used to test managers' deployment for DDT1 PoC -variables: - manager-os: - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-oracle-9-amd64 - - linux-amazon-2-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager}" - - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager}/track.yaml" - #- ssh-key: "/home/akim/Desktop/personal/Ephemeral" - foreach: - - variable: manager-os - as: manager - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - - # Generic manager test task - - task: "run-manager-tests" - description: "Run tests install for the manager." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - - wazuh-3: "{working-dir}/manager-linux-ubuntu-22.04-amd64/inventory.yaml" - - wazuh-4: "{working-dir}/manager-linux-oracle-9-amd64/inventory.yaml" - - wazuh-5: "{working-dir}/manager-linux-amazon-2-amd64/inventory.yaml" - - wazuh-6: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" - - wazuh-7: "{working-dir}/manager-linux-redhat-8-amd64/inventory.yaml" - - wazuh-8: "{working-dir}/manager-linux-redhat-9-amd64/inventory.yaml" - - wazuh-9: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" - - wazuh-10: "{working-dir}/manager-linux-debian-10-amd64/inventory.yaml" - - wazuh-11: "{working-dir}/manager-linux-debian-11-amd64/inventory.yaml" - - wazuh-12: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" - - tests: "install,restart,stop,uninstall" - - component: "manager" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index 6d6510d2a1..92a42d2d47 100644 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -1,12 +1,25 @@ version: 0.1 -description: This workflow is used to test agents' deployment for DDT1 PoC +description: This workflow is used to test agents deployment por DDT1 PoC variables: + manager-os: + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 infra-provider: vagrant working-dir: /tmp/dtt1-poc tasks: # Unique manager allocate task - - task: "allocate-manager-linux-centos-7-amd64" + - task: "allocate-manager-{manager}" description: "Allocate resources for the manager." do: this: process @@ -17,48 +30,12 @@ tasks: - action: create - provider: "{infra-provider}" - size: large - - composite-name: "linux-centos-7-amd64" - - inventory-output: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" - - - # Unique manager allocate task - - task: "allocate-manager-linux-ubuntu-20.04-amd64" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "linux-ubuntu-20.04-amd64" - - inventory-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - - track-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" - - + - composite-name: "{manager}" + - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager}/track.yaml" + foreach: + - variable: manager-os + as: manager # Generic manager test task - task: "run-manager-tests" @@ -72,11 +49,18 @@ tasks: - targets: - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - wazuh-3: "{working-dir}/manager-linux-ubuntu-22.04-amd64/inventory.yaml" + - wazuh-4: "{working-dir}/manager-linux-oracle-9-amd64/inventory.yaml" + - wazuh-5: "{working-dir}/manager-linux-amazon-2-amd64/inventory.yaml" + - wazuh-6: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" + - wazuh-7: "{working-dir}/manager-linux-redhat-8-amd64/inventory.yaml" + - wazuh-8: "{working-dir}/manager-linux-redhat-9-amd64/inventory.yaml" + - wazuh-9: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" + - wazuh-10: "{working-dir}/manager-linux-debian-10-amd64/inventory.yaml" + - wazuh-11: "{working-dir}/manager-linux-debian-11-amd64/inventory.yaml" + - wazuh-12: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" - tests: "install,restart,stop,uninstall" - component: "manager" - wazuh-version: "4.7.3" - wazuh-revision: "40714" - - live: "True" - depends-on: - - "allocate-manager-linux-ubuntu-20.04-amd64" - - "allocate-manager-linux-centos-7-amd64" + - live: "True" \ No newline at end of file From 8209f63685412b5ac17145eba38a3fc6865193f7 Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 12 Apr 2024 18:11:59 +0200 Subject: [PATCH 023/195] fix(#5210): Additional fixes created by copy and pasting --- .../build/lib/workflow_engine/README.MD | 321 +++++++++++++++ .../build/lib/workflow_engine/__init__.py | 5 + .../build/lib/workflow_engine/__main__.py | 39 ++ .../examples/dtt1-agents-aws.yaml | 142 +++++++ .../examples/dtt1-agents-poc-aws.yaml | 106 +++++ .../examples/dtt1-agents-poc2-vagrant.yaml | 105 +++++ .../examples/dtt1-agents-vagrant.yaml | 138 +++++++ .../workflow_engine/examples/dtt1-agents.yaml | 120 ++++++ .../examples/dtt1-managers-poc-aws.yaml | 74 ++++ .../examples/dtt1-managers-poc-vagrant.yaml | 82 ++++ .../examples/dtt1-managers.yaml | 103 +++++ .../lib/workflow_engine/examples/test.yaml | 72 ++++ .../test/aws/test-agent-complete.yaml | 114 ++++++ .../test/aws/test-agent-restart-ins-prov.yaml | 143 +++++++ .../test/aws/test-agent-stop-ins-prov.yaml | 143 +++++++ .../examples/test/aws/test-agent-suse.yaml | 122 ++++++ .../aws/test-agent-uninstall-ins-prov.yaml | 143 +++++++ .../test/vagrant/test-agent-complete.yaml | 114 ++++++ .../vagrant/test-agent-stop-ins-prov-2.yaml | 132 ++++++ .../vagrant/test-agent-stop-ins-prov.yaml | 133 ++++++ .../test-agent-uninstall-ins-prov.yaml | 143 +++++++ .../lib/workflow_engine/logger/__init__.py | 3 + .../lib/workflow_engine/logger/config.yaml | 32 ++ .../lib/workflow_engine/logger/filter.py | 24 ++ .../lib/workflow_engine/logger/logger.py | 25 ++ .../build/lib/workflow_engine/models.py | 15 + .../lib/workflow_engine/requirements-dev.txt | 2 + .../lib/workflow_engine/schema_validator.py | 90 ++++ .../workflow_engine/schemas/schema_v1.json | 118 ++++++ .../modules/build/lib/workflow_engine/task.py | 102 +++++ .../workflow_engine/tests/TESTING-README.md | 167 ++++++++ .../lib/workflow_engine/tests/conftest.py | 75 ++++ .../tests/data/wf-ko-no-path-on-cleanup.yaml | 169 ++++++++ .../tests/data/wf-ko-no-path-on-do.yaml | 169 ++++++++ .../tests/data/wf-ko-schema-error.yaml | 156 +++++++ .../lib/workflow_engine/tests/data/wf-ok.yaml | 170 ++++++++ .../lib/workflow_engine/tests/test_dag.py | 294 +++++++++++++ .../tests/test_schema_validator.py | 119 ++++++ .../lib/workflow_engine/tests/test_task.py | 114 ++++++ .../tests/test_workflow_file.py | 271 ++++++++++++ .../tests/test_workflow_processor.py | 385 ++++++++++++++++++ .../lib/workflow_engine/workflow_processor.py | 385 ++++++++++++++++++ .../modules/workflow_engine.egg-info/PKG-INFO | 8 + .../workflow_engine.egg-info/SOURCES.txt | 49 +++ .../dependency_links.txt | 1 + .../workflow_engine.egg-info/entry_points.txt | 2 + .../workflow_engine.egg-info/not-zip-safe | 1 + .../workflow_engine.egg-info/top_level.txt | 1 + .../aws/test-agent-restart-ins-prov.yaml | 2 +- .../agent/aws/test-agent-stop-ins-prov.yaml | 6 +- .../aws/test-agent-uninstall-ins-prov.yaml | 4 +- .../manager/aws/dtt1-managers-poc-aws.yaml | 2 +- .../vagrant/dtt1-managers-poc-vagrant.yaml | 4 +- 53 files changed, 5450 insertions(+), 9 deletions(-) create mode 100644 deployability/modules/build/lib/workflow_engine/README.MD create mode 100755 deployability/modules/build/lib/workflow_engine/__init__.py create mode 100755 deployability/modules/build/lib/workflow_engine/__main__.py create mode 100755 deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-aws.yaml create mode 100644 deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc-aws.yaml create mode 100644 deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml create mode 100755 deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-vagrant.yaml create mode 100755 deployability/modules/build/lib/workflow_engine/examples/dtt1-agents.yaml create mode 100644 deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-aws.yaml create mode 100644 deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml create mode 100755 deployability/modules/build/lib/workflow_engine/examples/dtt1-managers.yaml create mode 100644 deployability/modules/build/lib/workflow_engine/examples/test.yaml create mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-complete.yaml create mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml create mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml create mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-suse.yaml create mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml create mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-complete.yaml create mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml create mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml create mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml create mode 100644 deployability/modules/build/lib/workflow_engine/logger/__init__.py create mode 100644 deployability/modules/build/lib/workflow_engine/logger/config.yaml create mode 100644 deployability/modules/build/lib/workflow_engine/logger/filter.py create mode 100644 deployability/modules/build/lib/workflow_engine/logger/logger.py create mode 100644 deployability/modules/build/lib/workflow_engine/models.py create mode 100644 deployability/modules/build/lib/workflow_engine/requirements-dev.txt create mode 100755 deployability/modules/build/lib/workflow_engine/schema_validator.py create mode 100644 deployability/modules/build/lib/workflow_engine/schemas/schema_v1.json create mode 100755 deployability/modules/build/lib/workflow_engine/task.py create mode 100644 deployability/modules/build/lib/workflow_engine/tests/TESTING-README.md create mode 100644 deployability/modules/build/lib/workflow_engine/tests/conftest.py create mode 100644 deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml create mode 100644 deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml create mode 100644 deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-schema-error.yaml create mode 100644 deployability/modules/build/lib/workflow_engine/tests/data/wf-ok.yaml create mode 100644 deployability/modules/build/lib/workflow_engine/tests/test_dag.py create mode 100644 deployability/modules/build/lib/workflow_engine/tests/test_schema_validator.py create mode 100644 deployability/modules/build/lib/workflow_engine/tests/test_task.py create mode 100644 deployability/modules/build/lib/workflow_engine/tests/test_workflow_file.py create mode 100644 deployability/modules/build/lib/workflow_engine/tests/test_workflow_processor.py create mode 100755 deployability/modules/build/lib/workflow_engine/workflow_processor.py create mode 100644 deployability/modules/workflow_engine.egg-info/PKG-INFO create mode 100644 deployability/modules/workflow_engine.egg-info/SOURCES.txt create mode 100644 deployability/modules/workflow_engine.egg-info/dependency_links.txt create mode 100644 deployability/modules/workflow_engine.egg-info/entry_points.txt create mode 100644 deployability/modules/workflow_engine.egg-info/not-zip-safe create mode 100644 deployability/modules/workflow_engine.egg-info/top_level.txt diff --git a/deployability/modules/build/lib/workflow_engine/README.MD b/deployability/modules/build/lib/workflow_engine/README.MD new file mode 100644 index 0000000000..51eaad4ad6 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/README.MD @@ -0,0 +1,321 @@ +## Workflow engine + +### User documentation + +The execution of the Workflow is done through the installation of its library. + +Initially, Python libraries must be installed. It is recommended to use virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. + +1. Activate the environment: + + ```bash + source {venv directory}/bin/activate + ``` + +2. Clone the `wazuh-qa` repository: + + Navigate to the project directory and switch to the project branch: + + ```bash + cd wazuh-qa + git checkout {project-branch} + ``` + +3. Install requirements: + + ```bash + pip3 install -r deployability/deps/requirements.txt + ``` + +4. Install the Workflow engine library and its launcher: + + While in wazuh-qa: + + ```bash + cd modules + pip3 uninstall -y workflow_engine && pip3 install . + ``` + +5. Test Fixture to Execute: + + It will be necessary to create a fixture (yaml file) where the infrastructure, provisioning, and tests to be executed will be declared. + + >Note: It is possible to find some fixture examples in deployability/modules/workflow_engine/examples/ + + Example: + + ```bash + version: 0.1 + description: This workflow is used to test agents deployment por DDT1 PoC + variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + + tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,register,stop" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-tests-uninstall-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "run-agent-tests-{agent}" + - "provision-uninstall-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: package + depends-on: + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + - component: curl + depends-on: + - "allocate-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent provision task + - task: "provision-uninstall-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - uninstall: + - component: wazuh-agent + type: package + depends-on: + - "provision-install-{agent}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent + ``` + + Following the schema of the example: + + Configure the following parameters depending on your test case: + + ```yaml + variables/agent-os + variables/manager-os + infra-provider + working-dir + tasks + ``` + + Pay attention to the tasks: + + ```yaml + args + depends-on + ``` + + >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your test (allocation, provision, and test) + +7. Execution of Command (local): + + Execute the command by referencing the parameters required by the library (launcher). + + ```bash + python3 -m workflow_engine {.yaml fixture path} + ``` + + Example + + ```bash + python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml + ``` + + > Note The command execution can also be mediated through Jenkins. + +--- + +### Technical documentation + +`Workflow Engine` is the orchestrator of the deployability test architecture. + +Its function is to allow the ordered and structured execution in steps of allocation, provision, and testing. + +`The Workflow Engine` receives instructions through a `YAML document`, the structure of which can be exemplified in tests found in: +`wazuh-qa/deployability/modules/workflow_engine/examples` + +**In these tests**: + - Tasks: define the steps. + - Task: defines a step. + +**Within Task**: + - description: description of the task. + - do: instructions for the task. + - this: nature of the task. + - with: tools with which the task will be executed. + - path: executable. + - args: arguments. it receives the binary or file to execute and the parameters. + - depends-on: steps prior to the execution of that task. + - foreach: loop that executes the task on the previously declared hosts. + +```bash +tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,register,stop" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent +``` + +These tasks are executed by the `Workflow Engine` launcher installed as workflow_engine library in your virtual environment. + +This launcher receives the parameters, sets up the test logs, and proceeds with the ordered execution. + +The parameters sent from the launcher are processed by deployability/modules/workflow_engine/models.py, which checks the nature of the parameters sent and filters out incorrect parameters. + +![image](https://github.com/wazuh/wazuh-qa/assets/125690423/32aa77b7-f294-41ac-af93-db8a084dbad1) + +These are then sent to `deployability/modules/workflow_engine/workflow_processor.py`, where using `deployability/modules/schemas`, instructions in YAML are received and the schema of the instructions is checked. + +The commands are executed in the WorkflowProcessor of the same file, which also handles parallel executions and aborts failed executions. + +[WF.drawio.zip](https://github.com/wazuh/wazuh-qa/files/14167559/WF.drawio.zip) + + +### License + +WAZUH Copyright (C) 2015 Wazuh Inc. (License GPLv2) diff --git a/deployability/modules/build/lib/workflow_engine/__init__.py b/deployability/modules/build/lib/workflow_engine/__init__.py new file mode 100755 index 0000000000..a7a9cec0c7 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from .workflow_processor import WorkflowProcessor diff --git a/deployability/modules/build/lib/workflow_engine/__main__.py b/deployability/modules/build/lib/workflow_engine/__main__.py new file mode 100755 index 0000000000..ad5bc67f92 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/__main__.py @@ -0,0 +1,39 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import os +import sys +import argparse +import signal + +project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')) +sys.path.append(project_root) + +from workflow_engine.workflow_processor import WorkflowProcessor +from workflow_engine.models import InputPayload + + +def parse_arguments() -> argparse.Namespace: + """Parse command line arguments.""" + parser = argparse.ArgumentParser(description='Execute tasks in a workflow.') + parser.add_argument('workflow_file', type=str,help='Path to the workflow file (YAML format).') + parser.add_argument('--threads', type=int, default=1, required=False, help='Number of threads to use for parallel execution.') + parser.add_argument('--dry-run', action='store_true', required=False, help='Display the plan without executing tasks.') + parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='INFO', + help='Log level.') + parser.add_argument('--schema_file', required=False, type=str, help='Path to the schema file (YAML format)') + return parser.parse_args() + +def main() -> None: + """Main entry point.""" + try: + args = parse_arguments() + processor = WorkflowProcessor(**dict(InputPayload(**vars(args)))) + signal.signal(signal.SIGINT, processor.handle_interrupt) + processor.run() + except Exception as e: + sys.exit(f"Error executing workflow: {e}") + +if __name__ == "__main__": + main() diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-aws.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-aws.yaml new file mode 100755 index 0000000000..52e8244c87 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-aws.yaml @@ -0,0 +1,142 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-manager-{manager-os}" + - "allocate-agent-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc-aws.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc-aws.yaml new file mode 100644 index 0000000000..ddc11b67c8 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc-aws.yaml @@ -0,0 +1,106 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + + #- linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + + - linux-amazon-2023-amd64 + - linux-suse-15-amd64 + #- linux-opensuse-15-amd64 + + manager-os: + #- linux-oracle-9-amd64 + #- linux-ubuntu-20.04-amd64 + #- linux-centos-8-amd64 + - linux-redhat-7-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager}" + - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager}/track.yaml" + - label-termination-date: "1d" + - label-team: "QA" + foreach: + - variable: manager-os + as: manager + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "QA" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-tests" + description: "Run tests install for the agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" + - agent-1: "{working-dir}/agent-linux-ubuntu-20.04-amd64/inventory.yaml" + - agent-2: "{working-dir}/agent-linux-ubuntu-22.04-amd64/inventory.yaml" + - agent-3: "{working-dir}/agent-linux-oracle-9-amd64/inventory.yaml" + - agent-4: "{working-dir}/agent-linux-amazon-2-amd64/inventory.yaml" + #- agent-5: "{working-dir}/agent-linux-redhat-7-amd64/inventory.yaml" + - agent-5: "{working-dir}/agent-linux-redhat-8-amd64/inventory.yaml" + - agent-6: "{working-dir}/agent-linux-redhat-9-amd64/inventory.yaml" + - agent-7: "{working-dir}/agent-linux-centos-7-amd64/inventory.yaml" + - agent-8: "{working-dir}/agent-linux-centos-8-amd64/inventory.yaml" + - agent-9: "{working-dir}/agent-linux-debian-10-amd64/inventory.yaml" + - agent-10: "{working-dir}/agent-linux-debian-11-amd64/inventory.yaml" + - agent-11: "{working-dir}/agent-linux-debian-12-amd64/inventory.yaml" + #- agent-12: "{working-dir}/agent-linux-opensuse-15-amd64/inventory.yaml" + - agent-12: "{working-dir}/agent-linux-suse-15-amd64/inventory.yaml" + - agent-13: "{working-dir}/agent-linux-amazon-2023-amd64/inventory.yaml" + + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml new file mode 100644 index 0000000000..fbbe151127 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml @@ -0,0 +1,105 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + + #- linux-amazon-2023-amd64 + #- linux-suse-15-amd64 + + + manager-os: + - linux-opensuse-15-amd64 + #- linux-ubuntu-22.04-amd64 + #- linux-debian-12-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager}" + - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager}/track.yaml" + - label-termination-date: "1d" + - label-team: "QA" + foreach: + - variable: manager-os + as: manager + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "QA" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-tests" + description: "Run tests install for the agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-opensuse-15-amd64/inventory.yaml" + - agent-1: "{working-dir}/agent-linux-ubuntu-20.04-amd64/inventory.yaml" + - agent-2: "{working-dir}/agent-linux-ubuntu-22.04-amd64/inventory.yaml" + - agent-3: "{working-dir}/agent-linux-oracle-9-amd64/inventory.yaml" + - agent-4: "{working-dir}/agent-linux-amazon-2-amd64/inventory.yaml" + - agent-5: "{working-dir}/agent-linux-centos-7-amd64/inventory.yaml" + - agent-6: "{working-dir}/agent-linux-centos-8-amd64/inventory.yaml" + - agent-7: "{working-dir}/agent-linux-redhat-7-amd64/inventory.yaml" + - agent-8: "{working-dir}/agent-linux-redhat-8-amd64/inventory.yaml" + - agent-9: "{working-dir}/agent-linux-redhat-9-amd64/inventory.yaml" + - agent-10: "{working-dir}/agent-linux-debian-10-amd64/inventory.yaml" + - agent-11: "{working-dir}/agent-linux-debian-11-amd64/inventory.yaml" + - agent-12: "{working-dir}/agent-linux-debian-12-amd64/inventory.yaml" + #- agent-12: "{working-dir}/agent-linux-opensuse-15-amd64/inventory.yaml" + #- agent-12: "{working-dir}/agent-linux-suse-15-amd64/inventory.yaml" + #- agent-13: "{working-dir}/agent-linux-amazon-2023-amd64/inventory.yaml" + + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-vagrant.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-vagrant.yaml new file mode 100755 index 0000000000..ca320a07e2 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-vagrant.yaml @@ -0,0 +1,138 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-manager-{manager-os}" + - "allocate-agent-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents.yaml new file mode 100755 index 0000000000..e8a827282d --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents.yaml @@ -0,0 +1,120 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test agents deployment. +variables: + agents-os: + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-fedora-37-amd64 + - linux-fedora-38-amd64 + - linux-suse-15-amd64 + - linux-opensuse-15-amd64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + - linux-amazon-2023-amd64 + - windows-10-amd64 + - windows-11-amd64 + - windows-server2012-amd64 + - windows-server2016-amd64 + - windows-server2019-amd64 + - windows-server2022-amd64 + - macos-13.3-amd64 + - macos-14.2-amd64 + manager-os: linux-amazon-2023-amd64 + +tasks: + # Generic agent test task + - task: "test-agent-{agent}" + description: "Run tests for the {agent} agent." + do: + this: process + with: + path: /bin/echo + args: + - -n + - "Running tests for {agent}" + depends-on: + - "provision-agent-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: /bin/echo + args: + - -n + - "Running provision for manager" + depends-on: + - "allocate-manager-{manager-os}" + + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: /bin/echo + args: + - -n + - "Running allocate for manager" + cleanup: + this: process + with: + path: /bin/echo + args: + - -n + - "Running cleanup for manager" + + # Generic agent provision task + - task: "provision-agent-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: /bin/echo + args: + - -n + - "Running provision for {agent}" + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: /bin/echo + args: + - -n + - "Running allocate for {agent}" + cleanup: + this: process + with: + path: /bin/echo + args: + - -n + - "Running cleanup for allocate for {agent}" + foreach: + - variable: agents-os + as: agent \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-aws.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-aws.yaml new file mode 100644 index 0000000000..91bae10b90 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-aws.yaml @@ -0,0 +1,74 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-linux-centos-7-amd64" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "linux-centos-7-amd64" + - inventory-output: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" + - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" + + + # Unique manager allocate task + - task: "allocate-manager-linux-ubuntu-20.04-amd64" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "linux-ubuntu-20.04-amd64" + - inventory-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - track-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + + + + # Generic manager test task + - task: "run-manager-tests" + description: "Run tests install for the manager." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" + - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - tests: "stop" + - component: "manager" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + depends-on: + - "allocate-manager-linux-ubuntu-20.04-amd64" + - "allocate-manager-linux-centos-7-amd64" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml new file mode 100644 index 0000000000..35817f2169 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml @@ -0,0 +1,82 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-linux-centos-7-amd64" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "linux-centos-7-amd64" + - inventory-output: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" + - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" + + + # Unique manager allocate task + - task: "allocate-manager-linux-ubuntu-20.04-amd64" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "linux-ubuntu-20.04-amd64" + - inventory-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - track-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" + + + + # Generic manager test task + - task: "run-manager-tests" + description: "Run tests install for the manager." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" + - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - tests: "stop" + - component: "manager" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + depends-on: + - "allocate-manager-linux-ubuntu-20.04-amd64" + - "allocate-manager-linux-centos-7-amd64" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers.yaml new file mode 100755 index 0000000000..503cd115a3 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers.yaml @@ -0,0 +1,103 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test managers deployment. Two agents per manager are deployed. +variables: + agents-os: + - linux-debian-12-amd64 + - linux-ubuntu-22.04-amd64 + managers-os: + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-fedora-37-amd64 + - linux-fedora-38-amd64 + - linux-suse-15-amd64 + - linux-opensuse-15-amd64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + - linux-amazon-2023-amd64 +tasks: + # Generic manager test task + - task: "test-{manager}-{agent}" + do: + this: process + with: + path: /bin/echo + args: + - Executing tests for {manager} manager with {agent} agent. + depends-on: + - "provision-{manager}-manager" + - "provision-{agent}-agent-for-{manager}-manager" + foreach: + - variable: managers-os + as: manager + - variable: agents-os + as: agent + + # --------- Provision -------------- + # Generic manager provision task + - task: "provision-{manager}-manager" + do: + this: process + with: + path: /bin/echo + args: + - Executing provision for {manager} as a manager. + depends-on: + - "allocate-{manager}-manager" + foreach: + - variable: managers-os + as: manager + + # Generic agent provision task + - task: "provision-{agent}-agent-for-{manager}-manager" + do: + this: process + with: + path: /bin/echo + args: + - Executing provision for {agent} as an agent. + depends-on: + - "allocate-{agent}-agent-for-{manager}-manager" + foreach: + - variable: managers-os + as: manager + - variable: agents-os + as: agent + + # --------- Allocate -------------- + # Generic manager allocate task + - task: "allocate-{manager}-manager" + do: + this: process + with: + path: /bin/echo + args: + - Executing allocation for {manager} as a manager. + foreach: + - variable: managers-os + as: manager + + # Generic agent allocate task + - task: "allocate-{agent}-agent-for-{manager}-manager" + do: + this: process + with: + path: /bin/echo + args: + - Executing allocation for {agent} as an agent. + foreach: + - variable: managers-os + as: manager + - variable: agents-os + as: agent diff --git a/deployability/modules/build/lib/workflow_engine/examples/test.yaml b/deployability/modules/build/lib/workflow_engine/examples/test.yaml new file mode 100644 index 0000000000..e6cc336bfe --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/test.yaml @@ -0,0 +1,72 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + + manager-os: linux-centos-7-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + + # Generic agent test task + - task: "run-agent-ubuntu-18.04-tests" + description: "Run tests install for the agent ubuntu-18.04." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-ubuntu-18.04/inventory.yaml" + - tests: "stop" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + depends-on: + - "provision-manager-{manager-os}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-complete.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-complete.yaml new file mode 100755 index 0000000000..e992dfdc08 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-complete.yaml @@ -0,0 +1,114 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml new file mode 100755 index 0000000000..ad39a8b3c5 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml @@ -0,0 +1,143 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "restart" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml new file mode 100755 index 0000000000..74da24139b --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml @@ -0,0 +1,143 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-20.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "stop" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-suse.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-suse.yaml new file mode 100755 index 0000000000..55392f2c44 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-suse.yaml @@ -0,0 +1,122 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-opensuse-15-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + - component: tar + - component: curl + depends-on: + - "allocate-manager-{manager-os}" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: curl + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml new file mode 100755 index 0000000000..8ab89589d6 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml @@ -0,0 +1,143 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-complete.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-complete.yaml new file mode 100755 index 0000000000..a2cb5a2482 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-complete.yaml @@ -0,0 +1,114 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml new file mode 100755 index 0000000000..913dbaed3d --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml @@ -0,0 +1,132 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "stop" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml new file mode 100755 index 0000000000..e61393c256 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml @@ -0,0 +1,133 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "stop" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml new file mode 100755 index 0000000000..5568493d71 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml @@ -0,0 +1,143 @@ +version: 0.1 +description: This workflow is used to test agents deployment por DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/logger/__init__.py b/deployability/modules/build/lib/workflow_engine/logger/__init__.py new file mode 100644 index 0000000000..ea0d8aeea6 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/logger/__init__.py @@ -0,0 +1,3 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 diff --git a/deployability/modules/build/lib/workflow_engine/logger/config.yaml b/deployability/modules/build/lib/workflow_engine/logger/config.yaml new file mode 100644 index 0000000000..134b67dfdc --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/logger/config.yaml @@ -0,0 +1,32 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +version: 1 +formatters: + simple: + format: '[%(asctime)s] [%(levelname)s] [%(process)d] [%(threadName)s] [%(name)s]: %(message)s' + colored: + (): colorlog.ColoredFormatter + format: '%(log_color)s[%(asctime)s] [%(levelname)s] [%(process)d] [%(threadName)s] [%(name)s]: %(message)s' + datefmt: '%Y-%m-%d %H:%M:%S' + log_colors: + DEBUG: cyan + INFO: green + WARNING: yellow + ERROR: red + CRITICAL: red,bg_white +handlers: + console: + class: colorlog.StreamHandler + level: DEBUG + formatter: colored + stream: ext://sys.stdout + file: + class: logging.FileHandler + level: DEBUG + formatter: simple + filename: /tmp/workflow2.log +root: + level: DEBUG + handlers: [console, file] diff --git a/deployability/modules/build/lib/workflow_engine/logger/filter.py b/deployability/modules/build/lib/workflow_engine/logger/filter.py new file mode 100644 index 0000000000..f75009e364 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/logger/filter.py @@ -0,0 +1,24 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import logging +import threading + + +class ThreadIDFilter(logging.Filter): + """ + A filter that uppercases the name of the log record. + """ + def filter(self, record: str) -> bool: + """ + Inject thread_id to log records. + + Args: + record (LogRecord): The log record to filter. + + Returns: + bool: True if the record should be logged, False otherwise. + """ + record.thread_id = threading.get_native_id() + return record diff --git a/deployability/modules/build/lib/workflow_engine/logger/logger.py b/deployability/modules/build/lib/workflow_engine/logger/logger.py new file mode 100644 index 0000000000..3f8e73587d --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/logger/logger.py @@ -0,0 +1,25 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import logging +import logging.config + +from pathlib import Path + +import yaml + + +def _load_config() -> None: + """ + Loads the logging configuration from 'config.yaml' file. + """ + config_path = Path(__file__).parent / 'config.yaml' + with open(config_path, 'r') as f: + config = yaml.safe_load(f.read()) + logging.config.dictConfig(config) + + +_load_config() + +logger = logging.getLogger("workflow_engine") diff --git a/deployability/modules/build/lib/workflow_engine/models.py b/deployability/modules/build/lib/workflow_engine/models.py new file mode 100644 index 0000000000..c92d2a868f --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/models.py @@ -0,0 +1,15 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from pathlib import Path +from typing import Literal +from pydantic import BaseModel + + +class InputPayload(BaseModel): + workflow_file: str | Path + threads: int = 1 + dry_run: bool = False + log_level: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] = 'INFO' + schema_file: str | Path | None = None diff --git a/deployability/modules/build/lib/workflow_engine/requirements-dev.txt b/deployability/modules/build/lib/workflow_engine/requirements-dev.txt new file mode 100644 index 0000000000..05a93e2895 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/requirements-dev.txt @@ -0,0 +1,2 @@ +-r ../../deps/requirements.txt +-r ../../deps/remote_requirements.txt \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/schema_validator.py b/deployability/modules/build/lib/workflow_engine/schema_validator.py new file mode 100755 index 0000000000..d04a9bb39d --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/schema_validator.py @@ -0,0 +1,90 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import jsonschema +import json +import os + +from jsonschema.exceptions import ValidationError +from pathlib import Path +from ruamel.yaml import YAML + +from workflow_engine.logger.logger import logger + +class SchemaValidator: + """ + A SchemaValidator class that validates a YAML file against a JSON schema. + + Attributes: + schema_data (dict): The schema data. + yaml_data (dict): The YAML data. + """ + + def __init__(self, schema: Path | str, to_validate: Path | str): + """ + Initializes the SchemaValidator object. + + Args: + schema (Path, str): The path to the schema file. + to_validate (Path, str): The path to the YAML file to validate. + """ + schema_data: str = None + yaml_data: str = None + + self.logger = logger + + if not os.path.exists(schema): + raise FileNotFoundError(f'File "{schema}" not found.') + + with open(schema, 'r') as schema_file: + self.logger.debug(f"Loading schema file: {schema}") + schema_data = json.load(schema_file) + + if not os.path.exists(to_validate): + raise FileNotFoundError(f'File "{to_validate}" not found.') + + with open(to_validate, 'r') as file: + self.logger.debug(f"Loading yaml file: {to_validate}") + yaml = YAML(typ='safe', pure=True) + yaml_data = yaml.load(file) + + self.schema_data = schema_data + self.yaml_data = yaml_data + + def preprocess_data(self) -> None: + """ + Preprocess the YAML data to be validated. + + Raises: + ValidationError: If the YAML data is not valid. + """ + for task in self.yaml_data.get('tasks', []): + do_with = task.get('do', {}).get('with', {}) + this_value = task.get('do', {}).get('this', '') + + if this_value == 'process': + if 'path' not in do_with or 'args' not in do_with: + raise ValidationError(f"Missing required properties in 'with' for task: {task}") + + do_with = task.get('cleanup', {}).get('with', {}) + this_value = task.get('cleanup', {}).get('this', '') + + if this_value == 'process': + if 'path' not in do_with or 'args' not in do_with: + raise ValidationError(f"Missing required properties in 'with' for task: {task}") + + def validateSchema(self) -> None: + """ + Validate the Workflow schema + + Raises: + ValidationError: If the YAML data is not valid. + Exception: If an unexpected error occurs. + """ + try: + jsonschema.validate(self.yaml_data, self.schema_data) + except ValidationError as e: + self.logger.error(f"Schema validation error: {e}") + except Exception as e: + self.logger.error(f"Unexpected error at schema validation: {e}") diff --git a/deployability/modules/build/lib/workflow_engine/schemas/schema_v1.json b/deployability/modules/build/lib/workflow_engine/schemas/schema_v1.json new file mode 100644 index 0000000000..81f8e7c587 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/schemas/schema_v1.json @@ -0,0 +1,118 @@ +{ + "type": "object", + "properties": { + "name": {"type": "string"}, + "description": {"type": "string"}, + "version": {"type": "number"}, + "tasks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "task": {"type": "string"}, + "do": { + "type": "object", + "properties": { + "this": {"type": "string"}, + "with": { + "type": "object", + "properties": { + "this": {"type": "string"}, + "args": { + "type": "array", + "items": { + "oneOf": [ + {"type": "string"}, + {"type": "array"}, + {"type": "object"} + ] + } + }, + "path": {"type": "string"} + } + } + }, + "required": ["this"] + }, + "cleanup": { + "type": "object", + "properties": { + "this": {"type": "string"}, + "with": { + "type": "object", + "properties": { + "this": {"type": "string"}, + "args": { + "type": "array", + "items": { + "oneOf": [ + {"type": "string"}, + {"type": "array"}, + {"type": "object"} + ] + } + }, + "path": {"type": "string"} + } + } + }, + "required": ["this"] + }, + "depends-on": { + "type": "array", + "items": {"type": "string"} + }, + "foreach": { + "type": "array", + "items": { + "type": "object", + "properties": { + "variable": {"type": "string"}, + "as": {"type": "string"}, + "foreach": { + "type": "array", + "items": { + "type": "object", + "properties": { + "variable": {"type": "string"}, + "as": {"type": "string"} + } + } + } + }, + "required": ["variable", "as"] + } + } + }, + "required": ["task", "do"] + + }, + "minItems": 1 + }, + "variables": { + "type": "object", + "properties": { + "agent-os": { + "type": "array", + "items": { + "oneOf": [ + {"type": "string"}, + {"type": "array"} + ] + } + }, + "managers-os": { + "type": "array", + "items": { + "oneOf": [ + {"type": "string"}, + {"type": "array"} + ] + } + } + } + } + }, + "required": ["tasks", "variables", "version"], + "additionalProperties": false + } \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/task.py b/deployability/modules/build/lib/workflow_engine/task.py new file mode 100755 index 0000000000..b9e349a74b --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/task.py @@ -0,0 +1,102 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import subprocess +import random +import time + +from abc import ABC, abstractmethod +from workflow_engine.logger.logger import logger + +class Task(ABC): + """Abstract base class for tasks.""" + + @abstractmethod + def execute(self) -> None: + """Execute the task.""" + pass + +class ProcessTask(Task): + """Task for executing a process.""" + + def __init__(self, task_name: str, task_parameters: dict): + """ + Initialize ProcessTask. + + Args: + task_name (str): Name of the task. + task_parameters (dict): Parameters for the task. + logger (logging.Logger): Logger instance. + """ + self.task_name = task_name + self.task_parameters = task_parameters + self.logger = logger + + def execute(self) -> None: + """Execute the process task.""" + + task_args = [] + if self.task_parameters.get('args') is None: + raise ValueError(f'Not argument found in {self.task_name}') + + for arg in self.task_parameters['args']: + if isinstance(arg, str): + task_args.append(arg) + elif isinstance(arg, dict): + key, value = list(arg.items())[0] + if isinstance(value, list): + task_args.extend([f"--{key}={argvalue}" for argvalue in value]) + else: + task_args.append(f"--{key}={value}") + else: + logger.error(f'Could not parse arguments {arg}') + + logger.debug(f'Running task "{self.task_name}" with arguments: {task_args}') + + result = None + try: + result = subprocess.run( + [self.task_parameters['path']] + task_args, + check=True, + capture_output=True, + text=True, + ) + logger.debug(f'Finished task "{self.task_name}" execution with result:\n{str(result.stdout)}') + + if result.returncode != 0: + raise subprocess.CalledProcessError(returncode=result.returncode, cmd=result.args, output=result.stdout) + except subprocess.CalledProcessError as e: + error_msg = e.stderr + if "KeyboardInterrupt" in error_msg: + raise KeyboardInterrupt(f"Error executing process task with keyboard interrupt.") + raise Exception(f"Error executing process task {e.stderr}") + +class DummyTask(Task): + def __init__(self, task_name, task_parameters): + self.task_name = task_name + self.task_parameters = task_parameters + + def execute(self): + message = self.task_parameters.get('message', 'No message provided') + logger.info("%s: %s", message, self.task_name, extra={'tag': self.task_name}) + +class DummyRandomTask(Task): + def __init__(self, task_name, task_parameters): + self.task_name = task_name + self.task_parameters = task_parameters + + def execute(self): + time_interval = self.task_parameters.get('time-seconds', [1, 5]) + sleep_time = random.uniform(time_interval[0], time_interval[1]) + + message = self.task_parameters.get('message', 'No message provided') + logger.info("%s: %s (Sleeping for %.2f seconds)", message, self.task_name, sleep_time, extra={'tag': self.task_name}) + + time.sleep(sleep_time) + +TASKS_HANDLERS = { + 'process': ProcessTask, + 'dummy': DummyTask, + 'dummy-random': DummyRandomTask, +} diff --git a/deployability/modules/build/lib/workflow_engine/tests/TESTING-README.md b/deployability/modules/build/lib/workflow_engine/tests/TESTING-README.md new file mode 100644 index 0000000000..9144e08dc3 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/tests/TESTING-README.md @@ -0,0 +1,167 @@ +# Workflow engine Unit Testing using Pytest + +The workflow_engine module includes pytest unit tests. + +## Requirements + +- Make sure you have Python installed on your system. You can download it from + [python.org](https://www.python.org/downloads/). +- Clone the wazuh-qa repository in your local environment. +- Install the necessary dependencies by running: +```bash +git clone https://github.com/wazuh/wazuh-qa.git -b [your-branch] +cd wazuh-qa +pip install -r deployability/modules/workflow_engine/requirements-dev.txt +``` +- Configure the `PYTHONPATH` variable with the full path to the directory `deployability/modules`, for example if you've +cloned the `wazuh-qa` repository into `/wazuh/wazuh-qa`, configure the `PYTHONPATH` in this way: +```bash +> pwd +/wazuh/wazuh-qa +> export PYTHONPATH=$PYTHONPATH:$PWD/deployability/modules +> echo $PYTHONPATH +/wazuh/wazuh-qa/deployability/modules +``` + +## Test Structure +The directory `deployability/modules/workflow_engine/tests/` contains the unit test files for the `workflow_engine` +module. + +## Running Tests +To run the tests, make sure that your system meets the requirements by executing the following command from the project +root: + +```bash +pytest -vv deployability/modules/workflow_engine +``` +This command will run all tests in the `tests/` directory. Using additional arguments, You can also run specific tests +or directories. The output of this command looks like this: +```bash +pytest -vv deployability/modules/workflow_engine +============================================================================================== test session starts ============================================================================================== +platform linux -- Python 3.10.13, pytest-7.1.2, pluggy-1.3.0 -- /usr/local/bin/python3 +cachedir: .pytest_cache +metadata: {'Python': '3.10.13', 'Platform': 'Linux-5.15.146.1-microsoft-standard-WSL2-x86_64-with-glibc2.31', 'Packages': {'pytest': '7.1.2', 'pluggy': '1.3.0'}, 'Plugins': {'anyio': '4.2.0', 'testinfra': '5.0.0', 'metadata': '3.0.0', 'html': '3.1.1'}} +rootdir: /home/marcelo/wazuh/wazuh-qa/deployability/modules +plugins: anyio-4.2.0, testinfra-5.0.0, metadata-3.0.0, html-3.1.1 +collected 92 items + +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_constructor[True] PASSED [ 1%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_constructor[False] PASSED [ 2%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[True-dag0] PASSED [ 3%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[True-dag1] PASSED [ 4%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[False-dag0] PASSED [ 5%] +deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[False-dag1] PASSED [ 6%] +deployability/modules/workflow_engine/tests/test_dag.py::test_get_execution_plan[dag0] PASSED [ 7%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-failed-dag0] PASSED [ 8%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-canceled-dag0] PASSED [ 9%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-successful-dag0] PASSED [ 10%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-non_existing_status-dag0] FAILED [ 11%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[non_existing_task-successful-dag0] PASSED [ 13%] +deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[non_existing_task-non_existing_status-dag0] FAILED [ 14%] +deployability/modules/workflow_engine/tests/test_dag.py::test_should_be_canceled[True-dag0] PASSED [ 15%] +deployability/modules/workflow_engine/tests/test_dag.py::test_should_be_canceled[False-dag0] PASSED [ 16%] +deployability/modules/workflow_engine/tests/test_dag.py::test_build_dag[dag0] PASSED [ 17%] +deployability/modules/workflow_engine/tests/test_dag.py::test_build_dag[dag1] PASSED [ 18%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-all-to_be_canceled0-dag0] PASSED [ 19%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-all-to_be_canceled0-dag1] PASSED [ 20%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-related-flows-to_be_canceled1-dag0] FAILED [ 21%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-related-flows-to_be_canceled1-dag1] FAILED [ 22%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-continue-to_be_canceled2-dag0] FAILED [ 23%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-continue-to_be_canceled2-dag1] FAILED [ 25%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-all-to_be_canceled3-dag0] FAILED [ 26%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-all-to_be_canceled3-dag1] FAILED [ 27%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-related-flows-to_be_canceled4-dag0] FAILED [ 28%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-related-flows-to_be_canceled4-dag1] FAILED [ 29%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-continue-to_be_canceled5-dag0] FAILED [ 30%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-continue-to_be_canceled5-dag1] FAILED [ 31%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-all-to_be_canceled6-dag0] PASSED [ 32%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-all-to_be_canceled6-dag1] PASSED [ 33%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-related-flows-to_be_canceled7-dag0] PASSED [ 34%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-related-flows-to_be_canceled7-dag1] PASSED [ 35%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-continue-to_be_canceled8-dag0] FAILED [ 36%] +deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-continue-to_be_canceled8-dag1] FAILED [ 38%] +deployability/modules/workflow_engine/tests/test_dag.py::test_create_execution_plan[dag0-exec_plan0] PASSED [ 39%] +deployability/modules/workflow_engine/tests/test_dag.py::test_create_execution_plan[dag1-exec_plan1] PASSED [ 40%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_schema_validator_constructor[logger_mock0] PASSED [ 41%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_schema_validator_constructor_ko PASSED [ 42%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data PASSED [ 43%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data_ko[wf-ko-no-path-on-do.yaml-Missing required properties in 'with' for task: {'task': 'run-agent-tests-{agent}'] PASSED [ 44%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data_ko[wf-ko-no-path-on-cleanup.yaml-Missing required properties in 'with' for task: {'task': 'allocate-manager'] PASSED [ 45%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_validate_schema PASSED [ 46%] +deployability/modules/workflow_engine/tests/test_schema_validator.py::test_validate_schema_ko[logger_mock0] PASSED [ 47%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_constructor[task0] PASSED [ 48%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task0] PASSED [ 50%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task1] PASSED [ 51%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task2] PASSED [ 52%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task3] PASSED [ 53%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task4] PASSED [ 54%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc0-1-task0] PASSED [ 55%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc0-0-task0] PASSED [ 56%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc1-1-task0] PASSED [ 57%] +deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc1-0-task0] PASSED [ 58%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_constructor PASSED [ 59%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_validate_schema[logger_mock0] PASSED [ 60%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_validate_schema_ko[logger_mock0] PASSED [ 61%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_load_workflow[logger_mock0] PASSED [ 63%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_load_workflow_ko[logger_mock0] PASSED [ 64%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_process_workflow[logger_mock0] PASSED [ 65%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_process_workflow_ok[logger_mock0] PASSED [ 66%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element0-values0-return_value0] PASSED [ 67%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element1-values1-return_value1] PASSED [ 68%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[string_element {value}-values2-string_element value] PASSED [ 69%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element3-None-return_value3] PASSED [ 70%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_expand_task[task0-return_value0-variables0] PASSED [ 71%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_expand_task[task1-return_value1-variables1] PASSED [ 72%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation PASSED [ 73%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation_ko[task_collection0-Duplicated task names: task 1] PASSED [ 75%] +deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation_ko[task_collection1-Tasks do not exist: task 3, task 4] PASSED [ 76%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-False-1-info-schema.yaml] PASSED [ 77%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-True-1-debug-schema.yaml] PASSED [ 78%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-True-1-debug-None] PASSED [ 79%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task[logger_mock0-w_processor0-dag0-custom_action-True] PASSED [ 80%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task[logger_mock1-w_processor1-dag1-custom_action-False] PASSED [ 81%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock0-w_processor0-dag0-KeyboardInterrupt-None] PASSED [ 82%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock0-w_processor0-dag0-KeyboardInterrupt-abort-all] PASSED [ 83%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock1-w_processor1-dag1-Exception-None] PASSED [ 84%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock1-w_processor1-dag1-Exception-abort-all] PASSED [ 85%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-process] PASSED [ 86%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-dummy] PASSED [ 88%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-dummy-random] PASSED [ 89%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object_ko[w_processor0] PASSED [ 90%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel[logger_mock0-w_processor0-dag0-False] PASSED [ 91%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel[logger_mock0-w_processor0-dag0-True] PASSED [ 92%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel_ko[logger_mock0-w_processor0-dag0-False] PASSED [ 93%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel_ko[logger_mock0-w_processor0-dag0-True] PASSED [ 94%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_generate_futures[w_processor0] PASSED [ 95%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_generate_futures_reverse[w_processor0] PASSED [ 96%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_run[logger_mock0-w_processor0-False] PASSED [ 97%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_run[logger_mock0-w_processor0-True] PASSED [ 98%] +deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_handle_interrupt[logger_mock0-w_processor0] PASSED [100%] + +=================================================================================================== FAILURES ==================================================================================================== +``` + +The `.github/workflow/workflow-engine-unit-tests.yaml` automatically runs the unit tests in the GitHub environment. +The run results are in the `checks` tab or your GitHub pull request. + +## Relevant Files +- `tests/test_[test name].py`: all the unit test files start with a `test_` prefix. There is one unit test file for + each tested class. +- `tests/conftest.py`: contains the fixtures used throughout the unit tests. + +## Unit test development guidelines and recommendations +- Use Python coding style standards and recommendations to develop unit tests: snake case for all variable and function + names, maximum line length of 120 characters, two empty lines must separate each function, typing all your functions + and return values, create Docstring for all functions with numpy style. +- Develop unit tests for each function or method of the module. +- Error flows are usually created in a second unit test with the suffix `_ko`. For example, the + `test_process_task_execute` found in the `deployability/modules/workflow_engine/tests/test_workflow_processor` is the + unit test normal flow for the `WorkflowProcessor.process_task_execute` method. The + `WorkflowProcessor.process_task_execute_ko` unit test implements the error flow. +- Use the pytest's decorator `@pytest.mark.parametrize` to implement test cases for the same unit test. +- Mock the object instance and functions used by your tested function using the `unitest.mock.patch` and + `unitest.mock.patch.object` functions or decorators. +- Try to factorize your testing code using `pytest.fixtures`. The shared fixtures are in the `conftest.py` file. In + many unit tests of this project, the fixtures implement a `request` object that receives parameters from the + `pytest.mark.parametrize`. diff --git a/deployability/modules/build/lib/workflow_engine/tests/conftest.py b/deployability/modules/build/lib/workflow_engine/tests/conftest.py new file mode 100644 index 0000000000..5de92b5222 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/tests/conftest.py @@ -0,0 +1,75 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""Common unit test fixtures.""" +import graphlib + +from unittest.mock import patch, MagicMock +import pytest + +from workflow_engine.workflow_processor import DAG, WorkflowProcessor + +DEFAULT_TASK_COLLECTION = [ + {'task': 'task1', 'path': '/cmd1', 'args': [{"param1": "value1"}]}, + {'task': 'task2', 'path': '/cmd2', 'args': [{"param1": "value1"}]}, + {'task': 'task3', 'path': '/cmd3', 'args': [{"param1": "value1"}]}, +] + + +@pytest.fixture +def logger_mock(request) -> MagicMock: + """Fixture to mock common logger methods.""" + logger_to_patch = request.param.get('logger_to_patch', "workflow_engine.workflow_processor.logger") + with patch(logger_to_patch) as l_mock: + patch.object(l_mock, 'warning') + patch.object(l_mock, 'info') + patch.object(l_mock, 'debug') + patch.object(l_mock, 'error') + yield l_mock + + +@pytest.fixture +def dag(request) -> DAG: + """Create a mocked DAG instance.""" + ret_dag: DAG + reverse = request.param.get('reverse', False) + task_collection = request.param.get('task_collection', DEFAULT_TASK_COLLECTION) + if request.param.get('patch', True): + execution_plan_dict = request.param.get('execution_plan_dict', {}) + gl_dag = graphlib.TopologicalSorter() + dep_dict = {'task1': 'task2'} + with patch.object(gl_dag, 'prepare'), \ + patch('workflow_engine.workflow_processor.DAG._DAG__build_dag', + return_value=(gl_dag, dep_dict)), \ + patch('workflow_engine.workflow_processor.DAG._DAG__create_execution_plan', + return_value=execution_plan_dict): + ret_dag = DAG(task_collection=task_collection, reverse=reverse) + else: + ret_dag = DAG(task_collection=task_collection, reverse=reverse) + + if finished_task_status := request.param.get('finished_task_status', False): + ret_dag.finished_tasks_status = finished_task_status + + return ret_dag + + +@pytest.fixture +def w_processor(request) -> WorkflowProcessor: + """Create a mocked WorkflowProcessor instance.""" + + workflow_file = request.param.get('workflow_file', 'workflow.yaml') + dry_run = request.param.get('dry_run', False) + threads = request.param.get('threads', 1) + log_level = request.param.get('log_level', 'info') + schema_file = request.param.get('schema_file', 'schema.yaml') + with patch("workflow_engine.workflow_processor.WorkflowFile") as file_mock: + workflow_file_instance = file_mock.return_value + workflow_file_instance.task_collection = request.param.get('task_collection', DEFAULT_TASK_COLLECTION) + if request.param.get('patch', True): + with patch('workflow_engine.workflow_processor.logger.setLevel'): + processor = WorkflowProcessor(workflow_file, dry_run, threads, + log_level, schema_file) + else: + processor = WorkflowProcessor(workflow_file, dry_run, + threads, log_level, schema_file) + return processor diff --git a/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml b/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml new file mode 100644 index 0000000000..a637e4184f --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml @@ -0,0 +1,169 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test agents deployment with a single manager. +variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1 + +tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,register,stop" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-tests-uninstall-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "run-agent-tests-{agent}" + - "provision-uninstall-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: aio + version: "4.7.0" + depends-on: + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: aio + version: "4.8.0" + live: False + depends-on: + - "allocate-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent provision task + - task: "provision-uninstall-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - uninstall: + - component: wazuh-agent + type: package + depends-on: + - "provision-install-{agent}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml b/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml new file mode 100644 index 0000000000..6b2d2512d0 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml @@ -0,0 +1,169 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test agents deployment with a single manager. +variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1 + +tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,register,stop" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-tests-uninstall-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "run-agent-tests-{agent}" + - "provision-uninstall-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: aio + version: "4.7.0" + depends-on: + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: aio + version: "4.8.0" + live: False + depends-on: + - "allocate-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent provision task + - task: "provision-uninstall-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - uninstall: + - component: wazuh-agent + type: package + depends-on: + - "provision-install-{agent}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-schema-error.yaml b/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-schema-error.yaml new file mode 100644 index 0000000000..62c130f64b --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-schema-error.yaml @@ -0,0 +1,156 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test agents deployment with a single manager. +variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1 + +tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-tests-uninstall-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "run-agent-tests-{agent}" + - "provision-uninstall-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: aio + version: "4.7.0" + depends-on: + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: aio + version: "4.8.0" + live: False + depends-on: + - "allocate-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent provision task + - task: "provision-uninstall-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - uninstall: + - component: wazuh-agent + type: package + depends-on: + - "provision-install-{agent}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/tests/data/wf-ok.yaml b/deployability/modules/build/lib/workflow_engine/tests/data/wf-ok.yaml new file mode 100644 index 0000000000..fa979a6f81 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/tests/data/wf-ok.yaml @@ -0,0 +1,170 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +version: 0.1 +description: This workflow is used to test agents deployment with a single manager. +variables: + agents-os: + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1 + +tasks: + # Generic agent test task + - task: "run-agent-tests-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,register,stop" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "provision-install-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent test task + - task: "run-agent-tests-uninstall-{agent}" + description: "Run tests uninstall for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.1" + - wazuh-revision: "40709" + depends-on: + - "run-agent-tests-{agent}" + - "provision-uninstall-{agent}" + foreach: + - variable: agents-os + as: agent + + # Unique manager provision task + - task: "provision-manager" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: aio + version: "4.7.0" + depends-on: + - "allocate-manager" + + # Unique manager allocate task + - task: "allocate-manager" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: aio + version: "4.8.0" + live: False + depends-on: + - "allocate-{agent}" + - "provision-manager" + foreach: + - variable: agents-os + as: agent + + # Generic agent provision task + - task: "provision-uninstall-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" + - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - uninstall: + - component: wazuh-agent + type: package + depends-on: + - "provision-install-{agent}" + foreach: + - variable: agents-os + as: agent + + # Generic agent allocate task + - task: "allocate-{agent}" + description: "Allocate resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + foreach: + - variable: agents-os + as: agent \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/tests/test_dag.py b/deployability/modules/build/lib/workflow_engine/tests/test_dag.py new file mode 100644 index 0000000000..b3c6ec181e --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/tests/test_dag.py @@ -0,0 +1,294 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +import graphlib + +from unittest.mock import patch, MagicMock, call +import pytest + +from workflow_engine.workflow_processor import DAG + + +@pytest.mark.parametrize("reverse", [True, False]) +@patch("workflow_engine.workflow_processor.DAG._DAG__build_dag") +@patch("workflow_engine.workflow_processor.DAG._DAG__create_execution_plan") +def test_dag_constructor(create_exec_plan_mock: MagicMock, build_dag_mock: MagicMock, reverse: bool): + """Test ProcessTask constructor + Check all the dag object state after initialization and if the private dag methods are called during the instance + construction. + + Parameters + ---------- + create_exec_plan_mock : MagicMock + Patch of the DAG.__create_execution_plan method. + build_dag_mock : MagicMock + Patch of the DAG.__build_dag_ method. + reverse : bool + Parametrized value used by the DAG constructor. + """ + task_collection = [ + {'task': 'task1', 'path': '/cmd1', 'args': [{"param1": "value1"}]}, + {'task': 'task2', 'path': '/cmd2', 'args': [{"param1": "value1"}]}, + {'task': 'task3', 'path': '/cmd3', 'args': [{"param1": "value1"}]}, + ] + gl_dag = graphlib.TopologicalSorter() + + dep_dict = {'task1': 'task2'} + build_dag_mock.return_value = (gl_dag, dep_dict) + plan_dict = {'task1', 'task2'} + create_exec_plan_mock.return_value = plan_dict + with patch.object(gl_dag, 'prepare') as prepare_mock: + dag = DAG(task_collection=task_collection, reverse=reverse) + + assert dag.task_collection == task_collection + assert dag.reverse == reverse + assert dag.dag == gl_dag + assert dag.dependency_tree == dep_dict + assert isinstance(dag.to_be_canceled, set) and not dag.to_be_canceled + assert dag.finished_tasks_status == { + 'failed': set(), + 'canceled': set(), + 'successful': set(), + } + assert dag.execution_plan == plan_dict + build_dag_mock.assert_called_once() + create_exec_plan_mock.assert_called_once_with(dep_dict) + prepare_mock.assert_called_once() + + +@pytest.mark.parametrize('dag', + [{'reverse': True}, {'reverse': False}], + indirect=True) +@pytest.mark.parametrize('is_active', [True, False]) +def test_dag_is_active(is_active: bool, dag: DAG): + """Test DAG.is_active method. + Check if dag.is_active method returns the value of the dag.dag.is_active() method. + + Parameters + ---------- + is_active : bool + Parametrized value returned by dag.dag.is_active + dag : DAG + DAG fixture defined in conftest.py. + """ + with patch.object(dag.dag, 'is_active', return_value=is_active) as is_active_mock: + assert dag.is_active() == is_active + is_active_mock.assert_called_once() + + +@pytest.mark.parametrize('dag', + [{'execution_plan_dict': {'task1', 'task2'} }], indirect=True) +def test_get_execution_plan(dag: DAG): + """Test DAG.get_execution_plan method. + Check if the dag.get_execution_plan returns the dag.execution_plan instance + + Parameters + ---------- + dag : DAG + DAG fixture defined in conftest.py. + """ + assert dag.get_execution_plan() == dag.execution_plan + + +@pytest.mark.parametrize('dag', [{}], indirect=True) +@pytest.mark.parametrize('task_name, status', [ + ('task1', 'failed'), + ('task1', 'canceled'), + ('task1', 'successful'), +]) +def test_set_status(task_name:str, status:str, dag: DAG): + """Test DAG.set_status method. + Check if the dag.dag.done mode is properly called and that the task is in the failed, canceled or + successful set. + + Parameters + ---------- + task_name : str + Parameterized value for the task name passed to dag.set_status method. + status : str + Parameterized value for the task name passed to dag.set_status method. + dag : DAG + DAG fixture defined in conftest.py. + """ + with patch.object(dag.dag, "done") as done_mock: + dag.set_status(task_name=task_name, status=status) + assert task_name in dag.finished_tasks_status[status] + done_mock.assert_called_once_with(task_name) + + +@pytest.mark.parametrize('dag', [{}], indirect=True) +@pytest.mark.parametrize('in_cancel', [True, False]) +def test_should_be_canceled(in_cancel:bool, dag: DAG): + """Test DAG.should_be_canceled method. + Check if dag.should_be_canceled returns True or False if the task is in the dab.to_be_canceled set. + + Parameters + ---------- + in_cancel : bool + Parameterized value to test the method dag.should_be_canceled with 'task1' + in the dag.to_be_canceled set or not. + dag : DAG + DAG fixture defined in conftest.py. + """ + if in_cancel: + dag.to_be_canceled.add('task1') + else: + if 'task1' in dag.to_be_canceled: + dag.to_be_canceled.remove('task1') + + assert dag.should_be_canceled(task_name='task1') == in_cancel + + +@pytest.mark.parametrize('dag', + [{ + 'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']} + ] + }, + {'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}], + 'reverse': True + } + ], + indirect=True) +def test_build_dag(dag: DAG): + """Test DAG.__build_dag method. + The test uses a task collection and checks the calls to the graphlib.TopologicalSorter.add. + The function calls depend on the dag.reverse instance variable, that it is also parameterized. + + Parameters + ---------- + dag : DAG + DAG fixture defined in conftest.py with task_collection parameterized. + """ + with patch('workflow_engine.workflow_processor.graphlib.TopologicalSorter.add') as mock_add: + res_dag, res_dependency_dict = dag._DAG__build_dag() + assert isinstance(res_dag, graphlib.TopologicalSorter) + call_list = [] + dependency_dict = {} + for task in dag.task_collection: + dependencies = task.get('depends-on', []) + task_name = task['task'] + if dag.reverse: + for dependency in dependencies: + call_list.append(call(dependency, task_name)) + else: + call_list.append(call(task_name, *dependencies)) + dependency_dict[task_name] = dependencies + + assert res_dependency_dict == dependency_dict + mock_add.assert_has_calls(call_list, any_order=True) + + +@pytest.mark.parametrize('dag', + [{ + 'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': []}, + {'task': 'task4', 'depends-on': []}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']} + ], + 'patch': False + }, + {'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': []}, + {'task': 'task4', 'depends-on': []}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}], + 'reverse': True, + 'patch': False, + 'finished_task_status': { + 'failed': set(), + 'canceled': set(), + 'successful': set()} + }, + ], + indirect=True) +@pytest.mark.parametrize('task, cancel_policy, to_be_canceled', + [('task1', 'abort-all', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task2', 'abort-all', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task5', 'abort-all', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task1', 'abort-related-flows', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task2', 'abort-related-flows', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task5', 'abort-related-flows', {'task4', 'task3', 'task2', 'task5', 'task1'}), + ('task1', 'continue', set()), + ('task2', 'continue', set()), + ('task5', 'continue', set()), + ]) +def test_cancel_dependant_tasks(task: str, cancel_policy: str, to_be_canceled: set, dag: DAG): + """Test DAG.cancel_dependant_tasks method. + Check the to_be_canceled set after calling the cancel_dependant_tasks method with a parameterized task_collection + in reverse True and False test cases. + + Parameters + ---------- + task : str + Parameterized task name. + cancel_policy : str + Parameterized cancel policy using valid values (abort-all, abort-related-flows, continue). + to_be_canceled : set + [description] + dag : DAG + DAG fixture defined in conftest.py parameterized with complete object state + (task_collection, reverse, finished_task_status sets). The patch false parameter avoids patching the + DAG.__build_dag' and DAG.__create_execution_plan methods + """ + dag.cancel_dependant_tasks(task, cancel_policy=cancel_policy) + assert dag.to_be_canceled == to_be_canceled + + +@pytest.mark.parametrize('dag, exec_plan', + [( + {'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']} + ], + 'patch': False}, + {"task5": {"task2": {"task1": {}}, + "task3": {"task1": {}}, + "task4": {"task1": {}}}} + ), + ( + { + 'task_collection': [ + {'task': 'task1', }, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}, + {'task': 'task6', 'depends-on': ['task5']} + ], + 'patch': False + }, + {"task6": {"task5": {"task2": {"task1": {}}, + "task3": {"task1": {}}, + "task4": {"task1": {}}}}} + ) + ], + indirect=['dag']) +def test_create_execution_plan(exec_plan: dict, dag: DAG): + """Test DAG._create_execution_plan method. + This private method is executed by the constructor. In this Test, + the results are left in the execution_plan instance variable. + + Parameters + ---------- + exec_plan : dict + execution plan. + dag : DAG + DAG fixture defined in conftest.py. + """ + assert dag.execution_plan == exec_plan diff --git a/deployability/modules/build/lib/workflow_engine/tests/test_schema_validator.py b/deployability/modules/build/lib/workflow_engine/tests/test_schema_validator.py new file mode 100644 index 0000000000..25a45db95a --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/tests/test_schema_validator.py @@ -0,0 +1,119 @@ +# Copyright (C) 2015-2021, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""SchemaValidator unit tests.""" +import uuid +import random +from pathlib import Path +from unittest.mock import MagicMock, call, patch +import json +from ruamel.yaml import YAML +import pytest +from jsonschema.exceptions import ValidationError, UnknownType + +from workflow_engine.schema_validator import SchemaValidator + +@pytest.mark.parametrize('logger_mock', + [{'logger_to_patch':'workflow_engine.schema_validator.logger'}], + indirect=True) +def test_schema_validator_constructor(logger_mock: MagicMock): + """Test SchemaValidator constructor normal flow. + Check the state of the SchemaValidator instance variables after creation. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture to check debug calls + """ + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + with open(schema_path, 'r') as schema_file: + schema_data = json.load(schema_file) + + wf_file_path = Path(__file__).parent / 'data' / 'wf-ok.yaml' + with open(wf_file_path, 'r') as file: + yaml = YAML(typ='safe', pure=True) + yaml_data = yaml.load(file) + + validator = SchemaValidator(schema_path, wf_file_path) + assert validator.schema_data == schema_data + assert validator.yaml_data == yaml_data + calls = [call(f"Loading schema file: {schema_path}"), + call(f"Loading yaml file: {wf_file_path}")] + logger_mock.debug.assert_has_calls(calls) + + +def test_schema_validator_constructor_ko(): + """"Test SchemaValidator constructor error flows. + Check if the FileNotFoundError is raisen with a random file name. + """ + schema_path = str(uuid.UUID(int=random.randint(0, 2^32))) + with pytest.raises(FileNotFoundError, match=f'File "{schema_path}" not found.'): + SchemaValidator(schema_path, schema_path) + + +def test_preprocess_data(): + """Test SchemaValidator preprocess_data. + Check if the preprocess_data method does not raise exceptions with a valid file. + """ + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + wf_file_path = Path(__file__).parent / 'data' / 'wf-ok.yaml' + validator = SchemaValidator(schema_path, wf_file_path) + validator.preprocess_data() + + +@pytest.mark.parametrize('workflow_file, error_msg', + [('wf-ko-no-path-on-do.yaml', + "Missing required properties in 'with' for task: {'task': 'run-agent-tests-{agent}'"), + ('wf-ko-no-path-on-cleanup.yaml', + "Missing required properties in 'with' for task: {'task': 'allocate-manager'"),]) +def test_preprocess_data_ko(workflow_file: str, error_msg: str): + """Test SchemaValidator preprocess_data error flow. + Check the ValidationError generated by invalid yml files. + + Parameters + ---------- + workflow_file : str + workflow yml file name. + error_msg : str + Error message to check + """ + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + wf_file_path = Path(__file__).parent / 'data' / workflow_file + validator = SchemaValidator(schema_path, wf_file_path) + with pytest.raises(ValidationError, match=error_msg): + validator.preprocess_data() + + +def test_validate_schema(): + """Test SchemaValidator validate_schema with a valid yml file.""" + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + wf_file_path = Path(__file__).parent / 'data' / 'wf-ok.yaml' + validator = SchemaValidator(schema_path, wf_file_path) + validator.validateSchema() + + +@pytest.mark.parametrize('logger_mock', + [{'logger_to_patch':'workflow_engine.schema_validator.logger'}], + indirect=True) +def test_validate_schema_ko(logger_mock: MagicMock): + """Test SchemaValidator validate_schema error flows. + Check the messages sent to the log when an invalid workflow yml file is used. + + Parameters + ---------- + logger_mock : MagicMock + logger fixture defined in conftest.py + """ + schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' + wf_file_path = Path(__file__).parent / 'data' / 'wf-ko-schema-error.yaml' + validator = SchemaValidator(schema_path, wf_file_path) + validator.validateSchema() + logger_mock.error.assert_called_once() + assert 'Schema validation error:' in logger_mock.error.call_args[0][0] + + logger_mock.error.reset_mock() + validator = SchemaValidator(schema_path, wf_file_path) + with patch('workflow_engine.schema_validator.jsonschema.validate', side_effect=UnknownType): + validator.validateSchema() + logger_mock.error.assert_called_once() + assert 'Unexpected error at schema validation:' in logger_mock.error.call_args[0][0] diff --git a/deployability/modules/build/lib/workflow_engine/tests/test_task.py b/deployability/modules/build/lib/workflow_engine/tests/test_task.py new file mode 100644 index 0000000000..604c8e2926 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/tests/test_task.py @@ -0,0 +1,114 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from typing import List, Tuple +from subprocess import CompletedProcess, CalledProcessError +from unittest.mock import patch, MagicMock, call +import pytest + +from workflow_engine.task import ProcessTask + +@pytest.fixture +def task(request) -> ProcessTask: + """Shared fixture to create task.""" + task_name, task_parms = request.param + return ProcessTask(task_name=task_name, task_parameters=task_parms) + + +@pytest.mark.parametrize("task", [('task1', {"param1": "value1"})], indirect=True) +def test_process_task_constructor(task: ProcessTask): + """Test ProcessTask constructor. + Check the task instance varialbes after constructing the ProcessTask. + + Parameters + ---------- + task : ProcessTask + The task fixture. + """ + assert task.task_name == 'task1' + assert task.task_parameters == {"param1": "value1"} + + +@pytest.mark.parametrize("task", [('task1', {"path": "/mypath", + "args": [{"param1": "value1"}]}), + ('task2', {"path": "/mypath", + "args": ["param1"]}), + ('task3', {"path": "/mypath", + "args": ["param1", "param2"]}), + ('task4', {"path": "/mypath", + "args": ["param1", {"param2": "value2"}]}), + ('task5', {"path": "/mypath", + "args": [{"param1": "value1"}, {"param2": "value2"}]}) + ], indirect=True) +@patch("workflow_engine.task.logger") +def test_process_task_execute(logger_mock: MagicMock, task: ProcessTask): + """Test ProcessTask.execute method normal flow. + Check that ProcessTask.execute calls subprocess.run to run commands with the defined parameters. The + task mock in conftest.py is used to thy diferent command argument formats. + + Parameters + ---------- + logger_mock : MagicMock + The logger mock defined in conftest.py + task : ProcessTask + The task fixture. + """ + results = {} + results["task1"] = {"parm_list": [task.task_parameters['path'], "--param1=value1"]} + results["task2"] = {"parm_list": [task.task_parameters['path'], "param1"]} + results["task3"] = {"parm_list": [task.task_parameters['path'], "param1", "param2"]} + results["task4"] = {"parm_list": [task.task_parameters['path'], "param1", + "--param2=value2"]} + results["task5"] = {"parm_list": [task.task_parameters['path'], "--param1=value1", + "--param2=value2"]} + result = CompletedProcess(args=results[task.task_name]["parm_list"][1:], + returncode=0, stdout="command output", + stderr="") + debug_calls = [call(f'Running task "{task.task_name}" with arguments: ' + f'{results[task.task_name]["parm_list"][1:]}')] + with patch("workflow_engine.task.subprocess.run", return_value=result) as proc_run_mock, \ + patch.object(logger_mock, "debug") as logger_debug_mock: + debug_calls.append(call(f'Finished task "{task.task_name}" execution ' + f'with result:\n{str(result.stdout)}')) + task.execute() + + logger_debug_mock.assert_has_calls(debug_calls) + proc_run_mock.assert_called_once_with(results[task.task_name]['parm_list'], check=True, + capture_output=True, text=True) + + +@pytest.mark.parametrize("task", [('task1', {"path": "/mypath", + "args": [{"param1": "value1"}]}), + ], indirect=True) +@pytest.mark.parametrize("subproc_retval", [1, 0]) +@pytest.mark.parametrize("subproc_run_exc", [(True, KeyboardInterrupt, "KeyboardInterrupt error"), + (True, Exception, "Other Error")]) +def test_process_task_execute_ko(subproc_retval: int, subproc_run_exc: List[Tuple], task: ProcessTask): + """Test ProcessTask.execute method exception flows. + Check ProcessTask.execute flow when the subprocess.run returns errors. + + Parameters + ---------- + subproc_retval : int + return code from subprocess.run + subproc_run_exc : bool + Tuple + task : ProcessTask + The task fixture. + """ + raise_exc, exception_type, stderr = subproc_run_exc + if exception_type is Exception: + match = f"Error executing process task {stderr}" + else: + match = "Error executing process task with keyboard interrupt." + result = CompletedProcess(args=["--param1=value1"], + returncode=subproc_retval, stdout="command output", + stderr=stderr) + with patch("workflow_engine.task.subprocess.run", return_value=result) as proc_run_mock: + if raise_exc: + proc_run_mock.side_effect = CalledProcessError(returncode=1, + cmd=task.task_parameters['path'], + stderr=stderr) + + with pytest.raises(exception_type, match=match): + task.execute() diff --git a/deployability/modules/build/lib/workflow_engine/tests/test_workflow_file.py b/deployability/modules/build/lib/workflow_engine/tests/test_workflow_file.py new file mode 100644 index 0000000000..a3b411899e --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/tests/test_workflow_file.py @@ -0,0 +1,271 @@ +# Copyright (C) 2015-2021, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""WorkflowFile unit tests.""" +from typing import Any, List +from unittest.mock import patch, MagicMock, call, mock_open +import pytest + +from workflow_engine.workflow_processor import WorkflowFile + + +def test_workflow_file_constructor(): + """Test WorkflowFile constructor. + Check the function calls and instance variables after object creation.""" + with patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__validate_schema") as validate_mock, \ + patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__load_workflow", + return_value={'data': 'data'}) as load_mock, \ + patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__process_workflow") as process_mock, \ + patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__static_workflow_validation") \ + as static_validation_mock: + wf = WorkflowFile(workflow_file_path='my_file.yaml', schema_path='my_schema.yaml') + assert wf.schema_path == 'my_schema.yaml' + validate_mock.assert_called_once_with('my_file.yaml') + load_mock.assert_called_once_with('my_file.yaml') + assert wf.workflow_raw_data == {'data': 'data'} + process_mock.assert_called_once() + static_validation_mock.assert_called_once() + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +def test_workflow_file_validate_schema(logger_mock: MagicMock): + """Test WorkflowFile.__validate_schema. + Check debug messages and function called by the method. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + """ + wf = MagicMock() + wf.schema_path = 'my_schema_path.yaml' + workflow_file = 'my_file_path.yaml' + schema_validator = MagicMock() + with patch('workflow_engine.workflow_processor.SchemaValidator', + return_value=schema_validator) as schema_validator_mock: + with patch.object(schema_validator, 'preprocess_data') as preprocess_mock, \ + patch.object(schema_validator, 'validateSchema') as validate_schema_mock: + WorkflowFile._WorkflowFile__validate_schema(self=wf, workflow_file=workflow_file) + + logger_mock.debug.assert_called_once_with(f"Validating input file: {workflow_file}") + schema_validator_mock.assert_called_once_with(wf.schema_path, workflow_file) + preprocess_mock.assert_called_once() + validate_schema_mock.assert_called_once() + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +def test_workflow_file_validate_schema_ko(logger_mock: MagicMock): + """Test WorkflowFile.__validate_schema error flow. + Check logged messages and function calls of the method. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + """ + wf = MagicMock() + wf.schema_path = 'my_schema_path.yaml' + workflow_file = 'my_file_path.yaml' + file_exc = FileNotFoundError() + with patch('workflow_engine.workflow_processor.SchemaValidator', side_effect=file_exc) as schema_validator_mock, \ + pytest.raises(FileNotFoundError): + WorkflowFile._WorkflowFile__validate_schema(self=wf, workflow_file=workflow_file) + + logger_mock.debug.assert_called_once_with(f"Validating input file: {workflow_file}") + schema_validator_mock.assert_called_once_with(wf.schema_path, workflow_file) + logger_mock.error.assert_called_once_with("Error while validating schema [%s] with error: %s", + wf.schema_path, + file_exc) + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +@patch('builtins.open', new_callable=mock_open, read_data='YAML content') +def test_workflow_file_load_workflow(mock_open: MagicMock, logger_mock: MagicMock): + """Test WorkflowFile.__load_workflow. + Check logged messages and function calls of the method. + + Parameters + ---------- + mock_open : MagicMock + The mock fixture defined in conftest.py. + logger_mock : MagicMock + The logger fixture defined in conftest.py. + """ + wf = MagicMock() + wf.schema_path = 'my_schema_path.yaml' + workflow_file = 'my_file_path.yaml' + mock_open.return_value.__enter__.return_value = mock_open + with patch('workflow_engine.workflow_processor.os.path.exists', return_value=True) as path_exists_mock, \ + patch('workflow_engine.workflow_processor.yaml.safe_load') as safe_load_mock: + WorkflowFile._WorkflowFile__load_workflow(self=wf, file_path=workflow_file) + + path_exists_mock.assert_called_once_with(workflow_file) + logger_mock.debug.assert_called_once_with(f"Loading workflow file: {workflow_file}") + mock_open.assert_called_once_with(workflow_file, 'r', encoding='utf-8') + safe_load_mock.assert_called_once_with(mock_open) + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +@patch('builtins.open', new_callable=mock_open, read_data='YAML content') +def test_workflow_file_load_workflow_ko(mock_open: MagicMock, logger_mock: MagicMock): + """Test WorkflowFile.__load_workflow error flow. + Check if the FileNotFoundError exception is raised by the method. + + Parameters + ---------- + mock_open : MagicMock + unittest mock of the open function + logger_mock : MagicMock + The logger fixture defined in conftest.py + """ + wf = MagicMock() + wf.schema_path = 'my_schema_path.yaml' + workflow_file = 'my_file_path.yaml' + mock_open.return_value.__enter__.return_value = mock_open + with patch('workflow_engine.workflow_processor.os.path.exists', return_value=False) as path_exists_mock, \ + pytest.raises(FileNotFoundError, match=f'File "{workflow_file}" not found.') as file_exc: + WorkflowFile._WorkflowFile__load_workflow(self=wf, file_path=workflow_file) + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +def test_workflow_file_process_workflow(logger_mock: MagicMock): + """Test WorkflowFile.__process_workflow. + Check that the method calls the expand_task method of each task using a lambda as a side effect. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py + """ + variable_list = {'variable_1': 'value_1', 'variable_2': 'value_2'} + task_list = [{'task': 'task1'}, {'task': 'task2'}, {'task': 'task3'}] + expanded_task_list = [{'task': 'task3_1'}, {'task': 'task3_2'}] + wf = MagicMock() + wf.workflow_raw_data = {'tasks': task_list, 'variables': variable_list} + wf._WorkflowFile__expand_task.side_effect = lambda task, variables: [task] + \ + (expanded_task_list if task['task'] == 'task3' else []) + tasks = WorkflowFile._WorkflowFile__process_workflow(wf) + + logger_mock.debug.assert_called_once_with("Process workflow.") + calls = [call(task, variable_list) for task in task_list] + wf._WorkflowFile__expand_task.assert_has_calls(calls) + task_list.extend(expanded_task_list) + assert tasks == task_list + + +@pytest.mark.parametrize('logger_mock', [{}], indirect=True) +def test_workflow_file_process_workflow_ok(logger_mock: MagicMock): + """Test WorkflowFile.__process_workflow error flow. + Check that a ValueError is raised when no task are found in the workflow. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py + """ + wf = MagicMock() + wf.workflow_row_data = { + 'tasks': [] + } + wf.__expand_task.return_value = [] + with pytest.raises(ValueError, match="No tasks found in the workflow."): + tasks = WorkflowFile._WorkflowFile__process_workflow(self=wf) + + logger_mock.debug.assert_called_once_with("Process workflow.") + + +@pytest.mark.parametrize('element, values, return_value', + [({'key_1': 'key_1 {value_1}', 'key_2': 'key_2 {value_2}'}, + {'value_1': 'value_1', 'value_2': 'value_2'}, + {'key_1': 'key_1 value_1', 'key_2': 'key_2 value_2'}), + (['element_1 {value_1}', 'element_2 {value_2}', 'element_3 {value_3}'], + {'value_1': 'value_1', 'value_2': 'value_2', 'value_3': 'value_3'}, + ['element_1 value_1', 'element_2 value_2', 'element_3 value_3']), + ('string_element {value}', {'value': 'value'}, 'string_element value'), + ({1, 2}, None, {1, 2})]) +def test_workflow_file_replace_placeholder(element: Any, values: dict, return_value: Any): + """Test WorkflowFile.__replace_placeholder.""" + wf = MagicMock() + wf._WorkflowFile__replace_placeholders.side_effect = \ + lambda s, e, v: WorkflowFile._WorkflowFile__replace_placeholders(wf, s, e, v) + result = WorkflowFile._WorkflowFile__replace_placeholders(self=wf, element=element, values=values) + assert result == return_value + + +@pytest.mark.parametrize('task, return_value, variables', + [({'task': 'task: {as_variable_1}', 'param': '{as_variable_2}', + 'foreach': [{'variable': 'variable_1', 'as': 'as_variable_1'}, + {'variable': 'variable_2', 'as': 'as_variable_2'}]}, + [{"task": "task: value_1_1", 'param': 'value_2_1', + "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, + {"variable": "variable_2", "as": "as_variable_2"}]}, + {"task": "task: value_1_1", 'param': 'value_2_2', + "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, + {"variable": "variable_2", "as": "as_variable_2"}]}, + {"task": "task: value_1_2", 'param': 'value_2_1', + "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, + {"variable": "variable_2", "as": "as_variable_2"}]}, + {"task": "task: value_1_2", 'param': 'value_2_2', + "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, + {"variable": "variable_2", "as": "as_variable_2"}]}], + {'variable_1': ['value_1_1', 'value_1_2'], + 'variable_2': ['value_2_1', 'value_2_2']}), + ({'task': 'task1', 'placeholder': 'placeholder {variable_1}'}, + [{'task': 'task1', 'placeholder': 'placeholder value_1'}], + {'variable_1': 'value_1'}) + ]) +def test_workflow_file_expand_task(task: dict, return_value: dict, variables: dict): + """Test WorkflowFile.___expand_task. + Check the if the expand_task return dictionary is ok. + + Parameters + ---------- + task : dict + A task dictionary used as the input parameter for the expand_task method. + return_value : dict + The expected return value. + variables : dict + The variables dictionary used as the input parameter for the expand_task method. + """ + def side_effect(s, e, v = None): + return WorkflowFile._WorkflowFile__replace_placeholders(wf, s, e, v) + wf = MagicMock() + wf._WorkflowFile__replace_placeholders.side_effect = side_effect + + tasks = WorkflowFile._WorkflowFile__expand_task(wf, task, variables) + assert tasks == return_value + + +def test_workflow_file_static_workflow_validation(): + """Test WorkflowFile.__static_workflow_validation. + Check if no exception is raised with a valid task_collection""" + wf = MagicMock() + wf.task_collection = [{"task": "task 1", "param": "1"}, + {"task": "task 2", "param": "2", 'depends-on': ['task 1']} + ] + WorkflowFile._WorkflowFile__static_workflow_validation(wf) + + +@pytest.mark.parametrize('task_collection, error_msg', [ + ([{"task": "task 1", "param": "1"}, + {"task": "task 1", "param": "2", 'depends-on': ['task 1']}], + 'Duplicated task names: task 1'), + ([{"task": "task 1", "param": "1", 'depends-on': ['task 3', 'task 4']}, + {"task": "task 2", "param": "2", 'depends-on': ['task 3']}], + 'Tasks do not exist: task 3, task 4') +]) +def test_workflow_file_static_workflow_validation_ko(task_collection: List[dict], error_msg: str): + """Test WorkflowFile.__static_workflow_validation. + Check if the validation raises ValueError exceptions with invalid task collections. + + Parameters + ---------- + task_collection : List[dict] + List of tasks + error_msg : str + Expected exception errors + """ + wf = MagicMock() + wf.task_collection = task_collection + with pytest.raises(ValueError, match=error_msg): + WorkflowFile._WorkflowFile__static_workflow_validation(wf) diff --git a/deployability/modules/build/lib/workflow_engine/tests/test_workflow_processor.py b/deployability/modules/build/lib/workflow_engine/tests/test_workflow_processor.py new file mode 100644 index 0000000000..a0639c6ea0 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/tests/test_workflow_processor.py @@ -0,0 +1,385 @@ +# Copyright (C) 2015-2021, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 +"""WorkflowProcessor Unit tests""" +import time +import json +from concurrent.futures import Future +from unittest.mock import patch, MagicMock, call +import pytest + +from workflow_engine.workflow_processor import WorkflowProcessor, DAG +from workflow_engine.task import ProcessTask, TASKS_HANDLERS + + +@pytest.mark.parametrize('workflow_file, dry_run, threads, log_level, schema_file', + [('workflow.yaml', False, 1, 'info', 'schema.yaml'), + ('workflow.yaml', True, 1, 'debug', 'schema.yaml'), + ('workflow.yaml', True, 1, 'debug', None), + ]) +@patch("workflow_engine.workflow_processor.logger") +@patch("workflow_engine.workflow_processor.WorkflowFile") +def test_workflow_processor_constructor(file_mock: MagicMock, logger_mock: MagicMock, + workflow_file:str, dry_run: bool, threads: int, log_level: str, + schema_file:str): + """Test WorkflowProcessor constructor. + Check the workflowprocessor instance variables after construction. + + Parameters + ---------- + file_mock : MagicMock + Mock of a WorkflowFile Constructor. + logger_mock : MagicMock + The logger fixture defined in conftest.py. + workflow_file : str + Path to workflow yaml file. + dry_run : bool + Define if the workflow will run or not + threads : int + number of threads + log_level : str + Log level string + schema_file : str + Path to the schema.yml file + """ + task_collection = [ + {'task': 'task1', 'path': '/cmd1', 'args': [{"param1": "value1"}]}, + {'task': 'task2', 'path': '/cmd2', 'args': [{"param1": "value1"}]}, + {'task': 'task3', 'path': '/cmd3', 'args': [{"param1": "value1"}]}, + ] + workflow_file_instance = file_mock.return_value + workflow_file_instance.task_collection = task_collection + with patch.object(logger_mock, 'setLevel') as set_level_mock: + processor = WorkflowProcessor(workflow_file, dry_run, threads, log_level, schema_file) + set_level_mock.assert_called_once_with(log_level) + file_mock.assert_called_once_with(workflow_file, schema_file) + assert processor.task_collection == task_collection + assert processor.dry_run == dry_run + assert processor.threads == threads + + +@pytest.mark.parametrize('logger_mock, w_processor, dag, action, should_be_canceled', + [({}, {}, {}, 'custom_action', True), + ({}, {}, {}, 'custom_action', False),], + indirect=["dag", "w_processor", "logger_mock"]) +def test_execute_task(logger_mock: MagicMock, w_processor: WorkflowProcessor, dag: DAG, action: str, + should_be_canceled: bool): + """Test WorflowProcessor.execute_task function normal + Check the execute_task method when log messages and function calls when the should_be_canceled return value + is True or False. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dag : DAG + The dag fixture defined in conftest.py. + action : str + action name + should_be_canceled : bool + should_be_canceled method patched return value. + + Returns + ------- + [type] + [description] + """ + start_time = time.time() + elapsed_time = 10 + def time_side_effect(): + nonlocal start_time + start_time=start_time + elapsed_time + return start_time + + task = {'task': 'task1'} + p_task = ProcessTask('task1', {}) + with patch.object(dag, 'should_be_canceled', return_value=should_be_canceled) as should_be_canceled_mock, \ + patch.object(w_processor, 'create_task_object', return_value=p_task) as create_task_mock, \ + patch.object(dag, 'set_status') as set_status_mock, \ + patch.object(p_task, 'execute') as exec_mock, \ + patch('workflow_engine.workflow_processor.time') as time_mock: + time_mock.time = MagicMock(side_effect=time_side_effect) + w_processor.execute_task(dag=dag, task=task, action=action) + should_be_canceled_mock.assert_called_once_with(task['task']) + if should_be_canceled: + logger_mock.warning.assert_called_once_with( + "[%s] Skipping task due to dependency failure.", task['task']) + set_status_mock.assert_called_once_with(task['task'], 'canceled') + else: + create_task_mock.assert_called_once_with(task, action) + exec_mock.assert_called_once() + logger_mock.info.assert_has_calls([ + call("[%s] Starting task.", task['task']), + call("[%s] Finished task in %.2f seconds.", task['task'], elapsed_time) + ] + ) + set_status_mock.assert_called_once_with(task['task'], 'successful') + + +@pytest.mark.parametrize('on_error', [None, 'abort-all']) +@pytest.mark.parametrize('logger_mock, w_processor, dag, exception', + [({}, {}, {}, KeyboardInterrupt), + ({}, {}, {}, Exception)], + indirect=["dag", "w_processor", "logger_mock"]) +def test_execute_task_ko(logger_mock: MagicMock, w_processor: WorkflowProcessor, dag: DAG, exception, + on_error: str): + """Test WorflowProcessor.execute_task function, error flows. + Check logged messages, set_status call and cancel_dependant_tasks in the failure flow. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dag : DAG + The dag fixture defined in conftest.py. + exception : [type] + Expected exception. + on_error : str + set on-error of the task. + """ + task = {'task': 'task1'} + task.update({'on-error': on_error} if on_error else {}) + p_task = ProcessTask('task1', {}) + exc = exception() + with patch.object(dag, 'should_be_canceled', return_value=False), \ + patch.object(w_processor, 'create_task_object', return_value=p_task), \ + patch.object(dag, 'set_status') as set_status_mock, \ + patch.object(p_task, 'execute', side_effect=exc), \ + patch('workflow_engine.workflow_processor.time'), \ + patch.object(dag, 'cancel_dependant_tasks') as cancel_mock, \ + pytest.raises(expected_exception=exception): + w_processor.execute_task(dag=dag, task=task, action='action') + + logger_mock.error.assert_called_once_with("[%s] Task failed with error: %s.", task['task'], exc) + set_status_mock.assert_called_once_with(task['task'], 'failed') + cancel_mock.assert_called_once_with(task['task'], on_error if on_error else 'abort-related-flows') + + +@pytest.mark.parametrize('task_type', ['process', 'dummy', 'dummy-random']) +@pytest.mark.parametrize('w_processor', [{}], indirect=True) +def test_create_task_object(w_processor: WorkflowProcessor, task_type: str): + """Test WorkfowProcess.create_task_object function normal flow. + Check the task type returned by the method. + + Parameters + ---------- + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + task_type : str + type of task + """ + task_dict = {'task': 'task1', 'action': {'this': task_type, 'with': {'param'}}} + task = w_processor.create_task_object(task_dict, 'action') + assert isinstance(task, TASKS_HANDLERS.get(task_type)) + + +@pytest.mark.parametrize('w_processor', [{}], indirect=True) +def test_create_task_object_ko(w_processor: WorkflowProcessor): + """Test WorkfowProcess.create_task_object function error flow. + Check that the create_task_object raise a ValueError exception for invalid types.} + + Parameters + ---------- + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + """ + task_type = 'unknown' + task_dict = {'task': 'task1', 'action': {'this': task_type, 'with': {'param'}}} + with pytest.raises(ValueError, match=f"Unknown task type '{task_type}'."): + w_processor.create_task_object(task_dict, 'action') + + +@pytest.mark.parametrize('reverse', [False, True]) +@pytest.mark.parametrize('logger_mock, w_processor, dag',[({}, {}, {})], + indirect=["dag", "w_processor", "logger_mock"]) +@patch('workflow_engine.workflow_processor.concurrent.futures.ThreadPoolExecutor') +def test_execute_tasks_parallel(executor_mock: MagicMock, logger_mock: MagicMock, w_processor: WorkflowProcessor, + dag: DAG, reverse: bool): + """Test WorkfowProcess.execute_task_parallel function. + Check if the logged messages and function calls of the method with reverse True and False cases. + + Parameters + ---------- + executor_mock : MagicMock + Mock of the ThreadPoolExecutor. + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dag : DAG + The dag fixture defined in conftest.py. + reverse : bool + Parameterized value for the execute__tasks_parallel reverse parameter. + """ + futures = MagicMock() + futures.values = MagicMock(return_value = (x := MagicMock())) + y = MagicMock() + y.__enter__ = MagicMock(return_value=y) + executor_mock.return_value = y + with patch('workflow_engine.workflow_processor.concurrent.futures.wait') as wait_mock, \ + patch.object(w_processor, 'generate_futures', return_value=futures) as gen_futures_mock: + w_processor.execute_tasks_parallel(dag, reverse=reverse) + logger_mock.info.assert_called_once_with("Executing tasks in parallel.") + executor_mock.assert_called_once_with(max_workers=w_processor.threads) + wait_mock.assert_called_once_with(x) + gen_futures_mock.assert_called_once_with(dag, y, reverse) + + +@pytest.mark.parametrize('reverse', [False, True]) +@pytest.mark.parametrize('logger_mock, w_processor, dag',[({}, {}, {})], + indirect=["dag", "w_processor", "logger_mock"]) +@patch('workflow_engine.workflow_processor.concurrent.futures.ThreadPoolExecutor') +def test_execute_tasks_parallel_ko(executor_mock: MagicMock, logger_mock: MagicMock, w_processor: WorkflowProcessor, + dag: DAG, reverse: bool): + """Test WorkfowProcess.execute_task_parallel function error flow. + Check function call message loggin and calls when the KeyboardInterrupt is generated while waiting the subprocess + to finish execution. + + Parameters + ---------- + executor_mock : MagicMock + not used, just patched + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dag : DAG + The dag fixture defined in conftest.py. + reverse : bool + Parameterized value for the execute__tasks_parallel reverse parameter. + """ + execute_parallel_mock = MagicMock() + def patch_recursive_and_return_exception(_): + w_processor.execute_tasks_parallel = execute_parallel_mock + raise KeyboardInterrupt() + + with patch('workflow_engine.workflow_processor.concurrent.futures.wait', + side_effect=patch_recursive_and_return_exception), \ + patch.object(w_processor, 'generate_futures'): + w_processor.execute_tasks_parallel(dag, reverse=reverse) + logger_mock.info.assert_called_once_with("Executing tasks in parallel.") + logger_mock.error.assert_called_once_with("User interrupt detected. Aborting execution...") + execute_parallel_mock.assert_called_once_with(dag, reverse=True) + + +@pytest.mark.parametrize('w_processor', + [{'task_collection': [ + {'task': 'task1'}, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],}, + ], + indirect=True) +def test_generate_futures(w_processor: WorkflowProcessor): + """Test WorkfowProcess.generate_futures function without reverse. + Check the futures returned by the method. + + Parameters + ---------- + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + """ + def submit_execute_task_side_effect(_, dag: DAG, task, __): + dag.set_status(task['task'], 'successful') + return Future() + + executor = MagicMock() + executor.submit.side_effect=submit_execute_task_side_effect + dag = DAG(task_collection=w_processor.task_collection) + futures = w_processor.generate_futures(dag, executor=executor) + assert len(futures) == len(w_processor.task_collection) and \ + all(isinstance(element, Future) for element in futures.values()) + + +@pytest.mark.parametrize('w_processor', + [{'task_collection': [ + {'task': 'task1'}, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],}, + ], + indirect=True) +def test_generate_futures_reverse(w_processor: WorkflowProcessor): + """Test WorkfowProcess.generate_futures function with reverse True. + Check that set_status with successful is called for the tasks. + + Parameters + ---------- + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + """ + + def set_status_side_effect(task, status): + dag.finished_tasks_status[status].add(task) + dag.dag.done(task) + + executor = MagicMock() + dag = DAG(task_collection=w_processor.task_collection, reverse=True) + with patch.object(dag, 'set_status', side_effect=set_status_side_effect) as set_status_mock: + futures = w_processor.generate_futures(dag, executor=executor, reverse=True) + calls = [call(task['task'], 'successful') for task in w_processor.task_collection] + set_status_mock.assert_has_calls(calls, any_order=True) + + +@pytest.mark.parametrize('dry_run', [False, True]) +@pytest.mark.parametrize('logger_mock, w_processor', + [({}, { + 'task_collection': [ + {'task': 'task1'}, + {'task': 'task2', 'depends-on': ['task1']}, + {'task': 'task3', 'depends-on': ['task1']}, + {'task': 'task4', 'depends-on': ['task1']}, + {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],})], + indirect=True) +def test_run(logger_mock: MagicMock, w_processor: WorkflowProcessor, dry_run: bool): + """Test WorkfowProcess.run function. + Check log message and execute_tasks_parallel call. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + dry_run : bool + Parameterized value to test the run method. + """ + def dag_constructor(_, reverse=False): + return reverse_dag if reverse else dag + + w_processor.dry_run = dry_run + dag = DAG(w_processor.task_collection) + reverse_dag = DAG(w_processor.task_collection, reverse=True) + with patch.object(w_processor, 'execute_tasks_parallel') as exec_tasks_mock, \ + patch('workflow_engine.workflow_processor.DAG', side_effect=dag_constructor) as dag_mock: + w_processor.run() + if dry_run: + dag_mock.assert_called_once_with(w_processor.task_collection) + logger_mock.info.assert_called_once_with("Execution plan: %s", json.dumps(dag.get_execution_plan(), indent=2)) + else: + logger_mock.info.assert_has_calls([call("Executing DAG tasks."), call("Executing Reverse DAG tasks.")]) + exec_tasks_mock.assert_has_calls([call(dag), call(reverse_dag, reverse=True)]) + dag_mock.assert_has_calls([call(w_processor.task_collection), call(w_processor.task_collection, reverse=True)]) + + +@pytest.mark.parametrize('logger_mock, w_processor', [({}, {})], indirect=['logger_mock', 'w_processor']) +def test_handle_interrupt(logger_mock: MagicMock, w_processor: WorkflowProcessor): + """Test WorkfowProcess.handle_interrupt function. + Check logging when the handle_interrupt is called. + + Parameters + ---------- + logger_mock : MagicMock + The logger fixture defined in conftest.py. + w_processor : WorkflowProcessor + The workflow processor fixture defined in conftest.py. + """ + with pytest.raises(KeyboardInterrupt, match="User interrupt detected. End process..."): + w_processor.handle_interrupt(0, 0) + logger_mock.error.assert_called_once_with("User interrupt detected. End process...") diff --git a/deployability/modules/build/lib/workflow_engine/workflow_processor.py b/deployability/modules/build/lib/workflow_engine/workflow_processor.py new file mode 100755 index 0000000000..b8177b2129 --- /dev/null +++ b/deployability/modules/build/lib/workflow_engine/workflow_processor.py @@ -0,0 +1,385 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import concurrent.futures +import graphlib +import json +import time +import yaml +import os + +from pathlib import Path +from itertools import product + +from workflow_engine.logger.logger import logger +from workflow_engine.schema_validator import SchemaValidator +from workflow_engine.task import Task, TASKS_HANDLERS + +class WorkflowFile: + """Class for loading and processing a workflow file.""" + schema_path = Path(__file__).parent / 'schemas' / 'schema_v1.json' + + def __init__(self, workflow_file_path: Path | str, schema_path: Path | str = None) -> None: + self.schema_path = schema_path or self.schema_path + self.__validate_schema(workflow_file_path) + self.workflow_raw_data = self.__load_workflow(workflow_file_path) + self.task_collection = self.__process_workflow() + self.__static_workflow_validation() + + def __validate_schema(self, workflow_file: Path | str) -> None: + """ + Validate the workflow file against the schema. + + Args: + workflow_file (Path | str): Path to the workflow file. + """ + try: + logger.debug(f"Validating input file: {workflow_file}") + validator = SchemaValidator(self.schema_path, workflow_file) + validator.preprocess_data() + validator.validateSchema() + except Exception as e: + logger.error("Error while validating schema [%s] with error: %s", self.schema_path, e) + raise + + def __load_workflow(self, file_path: str) -> dict: + """ + Load the workflow data from a file. + + Args: + file_path (str): Path to the workflow file. + + Returns: + dict: Workflow data. + """ + + if not os.path.exists(file_path): + raise FileNotFoundError(f'File "{file_path}" not found.') + + logger.debug(f"Loading workflow file: {file_path}") + + with open(file_path, 'r', encoding='utf-8') as file: + return yaml.safe_load(file) + + def __process_workflow(self): + """Process the workflow and return a list of tasks.""" + logger.debug("Process workflow.") + task_collection = [] + variables = self.workflow_raw_data.get('variables', {}) + for task in self.workflow_raw_data.get('tasks', []): + task_collection.extend(self.__expand_task(task, variables)) + + if not task_collection: + raise ValueError("No tasks found in the workflow.") + return task_collection + + def __replace_placeholders(self, element: str, values: dict, parent_key: str = None): + """ + Recursively replace placeholders in a dictionary or list. + + Args: + element (Any): The element to process. + values (dict): The values to replace placeholders. + parent_key (str): The parent key for nested replacements. + + Returns: + Any: The processed element. + """ + if isinstance(element, dict): + return {key: self.__replace_placeholders(value, values, key) for key, value in element.items()} + if isinstance(element, list): + return [self.__replace_placeholders(sub_element, values, parent_key) for sub_element in element] + if isinstance(element, str): + return element.format_map(values) + return element + + def __expand_task(self, task: dict, variables: dict): + """ + Expand a task with variable values. + + Args: + task (dict): The task to expand. + variables (dict): Variable values. + + Returns: + List[dict]: List of expanded tasks. + """ + expanded_tasks = [] + + if 'foreach' in task: + loop_variables = task.get('foreach', [{}]) + + variable_names = [loop_variable_data.get('variable') for loop_variable_data in loop_variables] + as_identifiers = [loop_variable_data.get('as') for loop_variable_data in loop_variables] + + variable_values = [variables.get(name, []) for name in variable_names] + + for combination in product(*variable_values): + variables_with_items = {**variables, **dict(zip(as_identifiers, combination))} + expanded_tasks.append(self.__replace_placeholders(task, variables_with_items)) + else: + expanded_tasks.append(self.__replace_placeholders(task, variables)) + + return expanded_tasks + + def __static_workflow_validation(self): + """Validate the workflow against static criteria.""" + def check_duplicated_tasks(self): + """Validate task name duplication.""" + task_name_counts = {task['task']: 0 for task in self.task_collection} + + for task in self.task_collection: + task_name_counts[task['task']] += 1 + + duplicates = [name for name, count in task_name_counts.items() if count > 1] + + if duplicates: + raise ValueError(f"Duplicated task names: {', '.join(duplicates)}") + + def check_not_existing_tasks(self): + """Validate task existance.""" + task_names = {task['task'] for task in self.task_collection} + + for dependencies in [task.get('depends-on', []) for task in self.task_collection]: + non_existing_dependencies = [dependency for dependency in dependencies if dependency not in task_names] + if non_existing_dependencies: + raise ValueError(f"Tasks do not exist: {', '.join(non_existing_dependencies)}") + + validations = [check_duplicated_tasks, check_not_existing_tasks] + for validation in validations: + validation(self) + + +class DAG(): + """Class for creating a dependency graph.""" + def __init__(self, task_collection: list, reverse: bool = False): + self.task_collection = task_collection + self.reverse = reverse + self.dag, self.dependency_tree = self.__build_dag() + self.to_be_canceled = set() + self.finished_tasks_status = { + 'failed': set(), + 'canceled': set(), + 'successful': set(), + } + self.execution_plan = self.__create_execution_plan(self.dependency_tree) + self.dag.prepare() + + def is_active(self) -> bool: + """Check if the DAG is active.""" + return self.dag.is_active() + + def get_available_tasks(self) -> list: + """Get the available tasks.""" + return self.dag.get_ready() + + def get_execution_plan(self) -> dict: + """Get the execution plan.""" + return self.execution_plan + + def set_status(self, task_name: str, status: str): + """Set the status of a task.""" + self.finished_tasks_status[status].add(task_name) + self.dag.done(task_name) + + def should_be_canceled(self, task_name: str) -> bool: + """Check if a task should be canceled.""" + return task_name in self.to_be_canceled + + def __build_dag(self): + """Build a dependency graph for the tasks.""" + dependency_dict = {} + dag = graphlib.TopologicalSorter() + + for task in self.task_collection: + task_name = task['task'] + dependencies = task.get('depends-on', []) + + if self.reverse: + for dependency in dependencies: + dag.add(dependency, task_name) + else: + dag.add(task_name, *dependencies) + + dependency_dict[task_name] = dependencies + + return dag, dependency_dict + + def cancel_dependant_tasks(self, task_name, cancel_policy) -> None: + """Cancel all tasks that depend on a failed task.""" + def get_all_task_set(tasks): + task_set = set() + + for task, sub_tasks in tasks.items(): + task_set.add(task) + task_set.update(get_all_task_set(sub_tasks)) + + return task_set + + if cancel_policy == 'continue': + return + + not_cancelled_tasks = self.finished_tasks_status['failed'].union(self.finished_tasks_status['successful']) + for root_task, sub_tasks in self.execution_plan.items(): + task_set = get_all_task_set({root_task: sub_tasks}) + if cancel_policy == 'abort-all': + self.to_be_canceled.update(task_set) + elif cancel_policy == 'abort-related-flows': + if task_name in task_set: + self.to_be_canceled.update(task_set - not_cancelled_tasks) + else: + raise ValueError(f"Unknown cancel policy '{cancel_policy}'.") + + def __create_execution_plan(self, dependency_dict: dict) -> dict: + + execution_plan = {} + + def get_root_tasks(dependency_dict: dict) -> set: + """Get root tasks from the dependency dictionary.""" + all_tasks = set(dependency_dict.keys()) + dependent_tasks = set(dep for dependents in dependency_dict.values() for dep in dependents) + return all_tasks - dependent_tasks + + def get_subtask_plan(task_name: str, dependency_dict: dict, level: int = 0) -> dict: + """Create the execution plan recursively as a dictionary.""" + if task_name not in dependency_dict: + return {task_name: {}} + + dependencies = dependency_dict[task_name] + plan = {task_name: {}} + + for dependency in dependencies: + sub_plan = get_subtask_plan(dependency, dependency_dict, level + 1) + plan[task_name].update(sub_plan) + + return plan + + root_tasks = get_root_tasks(dependency_dict) + for root_task in root_tasks: + execution_plan.update(get_subtask_plan(root_task, dependency_dict)) + + return execution_plan + + +class WorkflowProcessor: + """Class for processing a workflow.""" + + def __init__(self, workflow_file: str, dry_run: bool, threads: int, log_level: str = 'INFO', schema_file: Path | str = None): + """ + Initialize WorkflowProcessor. + + Args: + workflow_file (str): Path to the workflow file (YAML format). + dry_run (bool): Display the plan without executing tasks. + threads (int): Number of threads to use for parallel execution. + log_level (str): Log level. + schema_file (Path | str): Path to the schema file (YAML format). + """ + logger.setLevel(log_level) + # Initialize the instance variables. + self.task_collection = WorkflowFile(workflow_file, schema_file).task_collection + self.dry_run = dry_run + self.threads = threads + + def execute_task(self, dag: DAG, task: dict, action) -> None: + """Execute a task.""" + task_name = task['task'] + if dag.should_be_canceled(task_name): + logger.warning("[%s] Skipping task due to dependency failure.", task_name) + dag.set_status(task_name, 'canceled') + else: + try: + task_object = self.create_task_object(task, action) + + logger.info("[%s] Starting task.", task_name) + start_time = time.time() + task_object.execute() + logger.info("[%s] Finished task in %.2f seconds.", task_name, time.time() - start_time) + dag.set_status(task_name, 'successful') + except KeyboardInterrupt as e: + logger.error("[%s] Task failed with error: %s.", task_name, e) + dag.set_status(task_name, 'failed') + dag.cancel_dependant_tasks(task_name, task.get('on-error', 'abort-related-flows')) + raise KeyboardInterrupt + except Exception as e: + logger.error("[%s] Task failed with error: %s.", task_name, e) + dag.set_status(task_name, 'failed') + dag.cancel_dependant_tasks(task_name, task.get('on-error', 'abort-related-flows')) + raise + + + def create_task_object(self, task: dict, action) -> Task: + """Create and return a Task object based on task type.""" + task_type = task[action]['this'] + + task_handler = TASKS_HANDLERS.get(task_type) + + if task_handler is not None: + return task_handler(task['task'], task[action]['with']) + + raise ValueError(f"Unknown task type '{task_type}'.") + + def execute_tasks_parallel(self, dag: DAG, reverse: bool = False) -> None: + """Execute tasks in parallel.""" + logger.info("Executing tasks in parallel.") + try: + with concurrent.futures.ThreadPoolExecutor(max_workers=self.threads) as executor: + futures = self.generate_futures(dag, executor, reverse) + concurrent.futures.wait(futures.values()) + except KeyboardInterrupt: + logger.error("User interrupt detected. Aborting execution...") + self.execute_tasks_parallel(dag, reverse=True) + + def generate_futures(self, dag: DAG, executor, reverse: bool = False): + futures = {} + + while True: + if not dag.is_active(): + break + + for task_name in list(dag.get_available_tasks()): + task = next(t for t in self.task_collection if t['task'] == task_name) + action = 'cleanup' if reverse and 'cleanup' in task else 'do' + if reverse and 'cleanup' not in task: + dag.set_status(task_name, 'successful') + else: + future = executor.submit(self.execute_task, dag, task, action) + futures[task_name] = future + + return futures + + + def run(self) -> None: + """Main entry point.""" + try: + if not self.dry_run: + logger.info("Executing DAG tasks.") + dag = DAG(self.task_collection) + self.execute_tasks_parallel(dag) + + logger.info("Executing Reverse DAG tasks.") + reversed_dag = DAG(self.task_collection, reverse=True) + self.execute_tasks_parallel(reversed_dag, reverse=True) + else: + dag = DAG(self.task_collection) + logger.info("Execution plan: %s", json.dumps(dag.get_execution_plan(), indent=2)) + + + except Exception as e: + logger.error("Error in Workflow: %s", e) + + + def handle_interrupt(self, signum, frame): + logger.error("User interrupt detected. End process...") + raise KeyboardInterrupt("User interrupt detected. End process...") + + def abort_execution(self, futures) -> None: + """Abort the execution of tasks.""" + with concurrent.futures.ThreadPoolExecutor(max_workers=self.threads) as executor: + for future in concurrent.futures.as_completed(futures.values()): + try: + _ = future.result() + except Exception as e: + logger.error("Error in aborted task: %s", e) + executor.shutdown(wait=False, cancel_futures=True) diff --git a/deployability/modules/workflow_engine.egg-info/PKG-INFO b/deployability/modules/workflow_engine.egg-info/PKG-INFO new file mode 100644 index 0000000000..7bc805e400 --- /dev/null +++ b/deployability/modules/workflow_engine.egg-info/PKG-INFO @@ -0,0 +1,8 @@ +Metadata-Version: 2.1 +Name: workflow_engine +Version: 1.0 +Summary: Wazuh testing utilities to help programmers automate deployment tests +Home-page: https://github.com/wazuh +Author: Wazuh +Author-email: hello@wazuh.com +License: GPLv2 diff --git a/deployability/modules/workflow_engine.egg-info/SOURCES.txt b/deployability/modules/workflow_engine.egg-info/SOURCES.txt new file mode 100644 index 0000000000..670c24e979 --- /dev/null +++ b/deployability/modules/workflow_engine.egg-info/SOURCES.txt @@ -0,0 +1,49 @@ +setup.py +workflow_engine/README.MD +workflow_engine/__init__.py +workflow_engine/__main__.py +workflow_engine/models.py +workflow_engine/requirements-dev.txt +workflow_engine/schema_validator.py +workflow_engine/task.py +workflow_engine/workflow_processor.py +workflow_engine.egg-info/PKG-INFO +workflow_engine.egg-info/SOURCES.txt +workflow_engine.egg-info/dependency_links.txt +workflow_engine.egg-info/entry_points.txt +workflow_engine.egg-info/not-zip-safe +workflow_engine.egg-info/top_level.txt +workflow_engine/examples/dtt1-agents-aws.yaml +workflow_engine/examples/dtt1-agents-poc-aws.yaml +workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml +workflow_engine/examples/dtt1-agents-vagrant.yaml +workflow_engine/examples/dtt1-agents.yaml +workflow_engine/examples/dtt1-managers-poc-aws.yaml +workflow_engine/examples/dtt1-managers-poc-vagrant.yaml +workflow_engine/examples/dtt1-managers.yaml +workflow_engine/examples/test.yaml +workflow_engine/examples/test/aws/test-agent-complete.yaml +workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml +workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml +workflow_engine/examples/test/aws/test-agent-suse.yaml +workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml +workflow_engine/examples/test/vagrant/test-agent-complete.yaml +workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml +workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml +workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml +workflow_engine/logger/__init__.py +workflow_engine/logger/config.yaml +workflow_engine/logger/filter.py +workflow_engine/logger/logger.py +workflow_engine/schemas/schema_v1.json +workflow_engine/tests/TESTING-README.md +workflow_engine/tests/conftest.py +workflow_engine/tests/test_dag.py +workflow_engine/tests/test_schema_validator.py +workflow_engine/tests/test_task.py +workflow_engine/tests/test_workflow_file.py +workflow_engine/tests/test_workflow_processor.py +workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml +workflow_engine/tests/data/wf-ko-no-path-on-do.yaml +workflow_engine/tests/data/wf-ko-schema-error.yaml +workflow_engine/tests/data/wf-ok.yaml \ No newline at end of file diff --git a/deployability/modules/workflow_engine.egg-info/dependency_links.txt b/deployability/modules/workflow_engine.egg-info/dependency_links.txt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/deployability/modules/workflow_engine.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/deployability/modules/workflow_engine.egg-info/entry_points.txt b/deployability/modules/workflow_engine.egg-info/entry_points.txt new file mode 100644 index 0000000000..bd39e78207 --- /dev/null +++ b/deployability/modules/workflow_engine.egg-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +engine = workflow_engine.__main__:main diff --git a/deployability/modules/workflow_engine.egg-info/not-zip-safe b/deployability/modules/workflow_engine.egg-info/not-zip-safe new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/deployability/modules/workflow_engine.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/deployability/modules/workflow_engine.egg-info/top_level.txt b/deployability/modules/workflow_engine.egg-info/top_level.txt new file mode 100644 index 0000000000..16d139c485 --- /dev/null +++ b/deployability/modules/workflow_engine.egg-info/top_level.txt @@ -0,0 +1 @@ +workflow_engine diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml index cafcb44d7b..bb71895416 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml @@ -16,7 +16,7 @@ variables: - linux-redhat-9-amd64 - linux-amazon-2-amd64 manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant + infra-provider: aws working-dir: /tmp/dtt1-poc tasks: diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml index 29238a9a8f..5d045b1961 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent restart with provisioning agents' with provision module +description: Test agent stop with provisioning agents' with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 @@ -16,7 +16,7 @@ variables: - linux-redhat-9-amd64 - linux-amazon-2-amd64 manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant + infra-provider: aws working-dir: /tmp/dtt1-poc tasks: @@ -140,4 +140,4 @@ tasks: - variable: agent-os as: agent depends-on: - - "provision-install-{agent}" \ No newline at end of file + - "provision-install-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml index 13bf298afe..f5bbb7e7cc 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent restart with provisioning agents' with provision module +description: Test agent uninstall with provisioning agents' with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 @@ -16,7 +16,7 @@ variables: - linux-redhat-9-amd64 - linux-amazon-2-amd64 manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant + infra-provider: aws working-dir: /tmp/dtt1-poc tasks: diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml index e11f90c20b..47d649795e 100644 --- a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: This workflow is used to test manager deployment for DDT1 PoC variables: manager-os: - linux-ubuntu-20.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index 92a42d2d47..f18f90abb4 100644 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC +description: This workflow is used to test manager deployment for DDT1 PoC variables: manager-os: - linux-ubuntu-20.04-amd64 @@ -63,4 +63,4 @@ tasks: - component: "manager" - wazuh-version: "4.7.3" - wazuh-revision: "40714" - - live: "True" \ No newline at end of file + - live: "True" From f80e7a84a904d0708fe7ada4f14537bef3665dad Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 12 Apr 2024 18:14:15 +0200 Subject: [PATCH 024/195] fix(#5210): Removing system files --- .../build/lib/workflow_engine/README.MD | 321 --------------- .../build/lib/workflow_engine/__init__.py | 5 - .../build/lib/workflow_engine/__main__.py | 39 -- .../examples/dtt1-agents-aws.yaml | 142 ------- .../examples/dtt1-agents-poc-aws.yaml | 106 ----- .../examples/dtt1-agents-poc2-vagrant.yaml | 105 ----- .../examples/dtt1-agents-vagrant.yaml | 138 ------- .../workflow_engine/examples/dtt1-agents.yaml | 120 ------ .../examples/dtt1-managers-poc-aws.yaml | 74 ---- .../examples/dtt1-managers-poc-vagrant.yaml | 82 ---- .../examples/dtt1-managers.yaml | 103 ----- .../lib/workflow_engine/examples/test.yaml | 72 ---- .../test/aws/test-agent-complete.yaml | 114 ------ .../test/aws/test-agent-restart-ins-prov.yaml | 143 ------- .../test/aws/test-agent-stop-ins-prov.yaml | 143 ------- .../examples/test/aws/test-agent-suse.yaml | 122 ------ .../aws/test-agent-uninstall-ins-prov.yaml | 143 ------- .../test/vagrant/test-agent-complete.yaml | 114 ------ .../vagrant/test-agent-stop-ins-prov-2.yaml | 132 ------ .../vagrant/test-agent-stop-ins-prov.yaml | 133 ------ .../test-agent-uninstall-ins-prov.yaml | 143 ------- .../lib/workflow_engine/logger/__init__.py | 3 - .../lib/workflow_engine/logger/config.yaml | 32 -- .../lib/workflow_engine/logger/filter.py | 24 -- .../lib/workflow_engine/logger/logger.py | 25 -- .../build/lib/workflow_engine/models.py | 15 - .../lib/workflow_engine/requirements-dev.txt | 2 - .../lib/workflow_engine/schema_validator.py | 90 ---- .../workflow_engine/schemas/schema_v1.json | 118 ------ .../modules/build/lib/workflow_engine/task.py | 102 ----- .../workflow_engine/tests/TESTING-README.md | 167 -------- .../lib/workflow_engine/tests/conftest.py | 75 ---- .../tests/data/wf-ko-no-path-on-cleanup.yaml | 169 -------- .../tests/data/wf-ko-no-path-on-do.yaml | 169 -------- .../tests/data/wf-ko-schema-error.yaml | 156 ------- .../lib/workflow_engine/tests/data/wf-ok.yaml | 170 -------- .../lib/workflow_engine/tests/test_dag.py | 294 ------------- .../tests/test_schema_validator.py | 119 ------ .../lib/workflow_engine/tests/test_task.py | 114 ------ .../tests/test_workflow_file.py | 271 ------------ .../tests/test_workflow_processor.py | 385 ------------------ .../lib/workflow_engine/workflow_processor.py | 385 ------------------ .../modules/workflow_engine.egg-info/PKG-INFO | 8 - .../workflow_engine.egg-info/SOURCES.txt | 49 --- .../dependency_links.txt | 1 - .../workflow_engine.egg-info/entry_points.txt | 2 - .../workflow_engine.egg-info/not-zip-safe | 1 - .../workflow_engine.egg-info/top_level.txt | 1 - 48 files changed, 5441 deletions(-) delete mode 100644 deployability/modules/build/lib/workflow_engine/README.MD delete mode 100755 deployability/modules/build/lib/workflow_engine/__init__.py delete mode 100755 deployability/modules/build/lib/workflow_engine/__main__.py delete mode 100755 deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-aws.yaml delete mode 100644 deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc-aws.yaml delete mode 100644 deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml delete mode 100755 deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-vagrant.yaml delete mode 100755 deployability/modules/build/lib/workflow_engine/examples/dtt1-agents.yaml delete mode 100644 deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-aws.yaml delete mode 100644 deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml delete mode 100755 deployability/modules/build/lib/workflow_engine/examples/dtt1-managers.yaml delete mode 100644 deployability/modules/build/lib/workflow_engine/examples/test.yaml delete mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-complete.yaml delete mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml delete mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml delete mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-suse.yaml delete mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml delete mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-complete.yaml delete mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml delete mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml delete mode 100755 deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml delete mode 100644 deployability/modules/build/lib/workflow_engine/logger/__init__.py delete mode 100644 deployability/modules/build/lib/workflow_engine/logger/config.yaml delete mode 100644 deployability/modules/build/lib/workflow_engine/logger/filter.py delete mode 100644 deployability/modules/build/lib/workflow_engine/logger/logger.py delete mode 100644 deployability/modules/build/lib/workflow_engine/models.py delete mode 100644 deployability/modules/build/lib/workflow_engine/requirements-dev.txt delete mode 100755 deployability/modules/build/lib/workflow_engine/schema_validator.py delete mode 100644 deployability/modules/build/lib/workflow_engine/schemas/schema_v1.json delete mode 100755 deployability/modules/build/lib/workflow_engine/task.py delete mode 100644 deployability/modules/build/lib/workflow_engine/tests/TESTING-README.md delete mode 100644 deployability/modules/build/lib/workflow_engine/tests/conftest.py delete mode 100644 deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml delete mode 100644 deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml delete mode 100644 deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-schema-error.yaml delete mode 100644 deployability/modules/build/lib/workflow_engine/tests/data/wf-ok.yaml delete mode 100644 deployability/modules/build/lib/workflow_engine/tests/test_dag.py delete mode 100644 deployability/modules/build/lib/workflow_engine/tests/test_schema_validator.py delete mode 100644 deployability/modules/build/lib/workflow_engine/tests/test_task.py delete mode 100644 deployability/modules/build/lib/workflow_engine/tests/test_workflow_file.py delete mode 100644 deployability/modules/build/lib/workflow_engine/tests/test_workflow_processor.py delete mode 100755 deployability/modules/build/lib/workflow_engine/workflow_processor.py delete mode 100644 deployability/modules/workflow_engine.egg-info/PKG-INFO delete mode 100644 deployability/modules/workflow_engine.egg-info/SOURCES.txt delete mode 100644 deployability/modules/workflow_engine.egg-info/dependency_links.txt delete mode 100644 deployability/modules/workflow_engine.egg-info/entry_points.txt delete mode 100644 deployability/modules/workflow_engine.egg-info/not-zip-safe delete mode 100644 deployability/modules/workflow_engine.egg-info/top_level.txt diff --git a/deployability/modules/build/lib/workflow_engine/README.MD b/deployability/modules/build/lib/workflow_engine/README.MD deleted file mode 100644 index 51eaad4ad6..0000000000 --- a/deployability/modules/build/lib/workflow_engine/README.MD +++ /dev/null @@ -1,321 +0,0 @@ -## Workflow engine - -### User documentation - -The execution of the Workflow is done through the installation of its library. - -Initially, Python libraries must be installed. It is recommended to use virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. - -1. Activate the environment: - - ```bash - source {venv directory}/bin/activate - ``` - -2. Clone the `wazuh-qa` repository: - - Navigate to the project directory and switch to the project branch: - - ```bash - cd wazuh-qa - git checkout {project-branch} - ``` - -3. Install requirements: - - ```bash - pip3 install -r deployability/deps/requirements.txt - ``` - -4. Install the Workflow engine library and its launcher: - - While in wazuh-qa: - - ```bash - cd modules - pip3 uninstall -y workflow_engine && pip3 install . - ``` - -5. Test Fixture to Execute: - - It will be necessary to create a fixture (yaml file) where the infrastructure, provisioning, and tests to be executed will be declared. - - >Note: It is possible to find some fixture examples in deployability/modules/workflow_engine/examples/ - - Example: - - ```bash - version: 0.1 - description: This workflow is used to test agents deployment por DDT1 PoC - variables: - agents-os: - - linux-ubuntu-22.04-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - - tasks: - # Generic agent test task - - task: "run-agent-tests-{agent}" - description: "Run tests uninstall for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,register,stop" - - component: "agent" - - wazuh-version: "4.7.1" - - wazuh-revision: "40709" - depends-on: - - "provision-install-{agent}" - - "provision-manager" - foreach: - - variable: agents-os - as: agent - - # Generic agent test task - - task: "run-agent-tests-uninstall-{agent}" - description: "Run tests uninstall for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - tests: "uninstall" - - component: "agent" - - wazuh-version: "4.7.1" - - wazuh-revision: "40709" - depends-on: - - "run-agent-tests-{agent}" - - "provision-uninstall-{agent}" - foreach: - - variable: agents-os - as: agent - - # Unique manager provision task - - task: "provision-manager" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: package - depends-on: - - "allocate-manager" - - # Unique manager allocate task - - task: "allocate-manager" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: package - - component: curl - depends-on: - - "allocate-{agent}" - - "provision-manager" - foreach: - - variable: agents-os - as: agent - - # Generic agent provision task - - task: "provision-uninstall-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - uninstall: - - component: wazuh-agent - type: package - depends-on: - - "provision-install-{agent}" - foreach: - - variable: agents-os - as: agent - - # Generic agent allocate task - - task: "allocate-{agent}" - description: "Allocate resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - foreach: - - variable: agents-os - as: agent - ``` - - Following the schema of the example: - - Configure the following parameters depending on your test case: - - ```yaml - variables/agent-os - variables/manager-os - infra-provider - working-dir - tasks - ``` - - Pay attention to the tasks: - - ```yaml - args - depends-on - ``` - - >Note: In args, configure the launcher's path correctly (main.py files in each module), and to fill `depends-on`, consider the steps of your test (allocation, provision, and test) - -7. Execution of Command (local): - - Execute the command by referencing the parameters required by the library (launcher). - - ```bash - python3 -m workflow_engine {.yaml fixture path} - ``` - - Example - - ```bash - python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml - ``` - - > Note The command execution can also be mediated through Jenkins. - ---- - -### Technical documentation - -`Workflow Engine` is the orchestrator of the deployability test architecture. - -Its function is to allow the ordered and structured execution in steps of allocation, provision, and testing. - -`The Workflow Engine` receives instructions through a `YAML document`, the structure of which can be exemplified in tests found in: -`wazuh-qa/deployability/modules/workflow_engine/examples` - -**In these tests**: - - Tasks: define the steps. - - Task: defines a step. - -**Within Task**: - - description: description of the task. - - do: instructions for the task. - - this: nature of the task. - - with: tools with which the task will be executed. - - path: executable. - - args: arguments. it receives the binary or file to execute and the parameters. - - depends-on: steps prior to the execution of that task. - - foreach: loop that executes the task on the previously declared hosts. - -```bash -tasks: - # Generic agent test task - - task: "run-agent-tests-{agent}" - description: "Run tests uninstall for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,register,stop" - - component: "agent" - - wazuh-version: "4.7.1" - - wazuh-revision: "40709" - depends-on: - - "provision-install-{agent}" - - "provision-manager" - foreach: - - variable: agents-os - as: agent -``` - -These tasks are executed by the `Workflow Engine` launcher installed as workflow_engine library in your virtual environment. - -This launcher receives the parameters, sets up the test logs, and proceeds with the ordered execution. - -The parameters sent from the launcher are processed by deployability/modules/workflow_engine/models.py, which checks the nature of the parameters sent and filters out incorrect parameters. - -![image](https://github.com/wazuh/wazuh-qa/assets/125690423/32aa77b7-f294-41ac-af93-db8a084dbad1) - -These are then sent to `deployability/modules/workflow_engine/workflow_processor.py`, where using `deployability/modules/schemas`, instructions in YAML are received and the schema of the instructions is checked. - -The commands are executed in the WorkflowProcessor of the same file, which also handles parallel executions and aborts failed executions. - -[WF.drawio.zip](https://github.com/wazuh/wazuh-qa/files/14167559/WF.drawio.zip) - - -### License - -WAZUH Copyright (C) 2015 Wazuh Inc. (License GPLv2) diff --git a/deployability/modules/build/lib/workflow_engine/__init__.py b/deployability/modules/build/lib/workflow_engine/__init__.py deleted file mode 100755 index a7a9cec0c7..0000000000 --- a/deployability/modules/build/lib/workflow_engine/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - -from .workflow_processor import WorkflowProcessor diff --git a/deployability/modules/build/lib/workflow_engine/__main__.py b/deployability/modules/build/lib/workflow_engine/__main__.py deleted file mode 100755 index ad5bc67f92..0000000000 --- a/deployability/modules/build/lib/workflow_engine/__main__.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - -import os -import sys -import argparse -import signal - -project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')) -sys.path.append(project_root) - -from workflow_engine.workflow_processor import WorkflowProcessor -from workflow_engine.models import InputPayload - - -def parse_arguments() -> argparse.Namespace: - """Parse command line arguments.""" - parser = argparse.ArgumentParser(description='Execute tasks in a workflow.') - parser.add_argument('workflow_file', type=str,help='Path to the workflow file (YAML format).') - parser.add_argument('--threads', type=int, default=1, required=False, help='Number of threads to use for parallel execution.') - parser.add_argument('--dry-run', action='store_true', required=False, help='Display the plan without executing tasks.') - parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='INFO', - help='Log level.') - parser.add_argument('--schema_file', required=False, type=str, help='Path to the schema file (YAML format)') - return parser.parse_args() - -def main() -> None: - """Main entry point.""" - try: - args = parse_arguments() - processor = WorkflowProcessor(**dict(InputPayload(**vars(args)))) - signal.signal(signal.SIGINT, processor.handle_interrupt) - processor.run() - except Exception as e: - sys.exit(f"Error executing workflow: {e}") - -if __name__ == "__main__": - main() diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-aws.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-aws.yaml deleted file mode 100755 index 52e8244c87..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-aws.yaml +++ /dev/null @@ -1,142 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: aws - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: package - version: 4.7.3 - live: True - depends-on: - - "allocate-agent-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agents-os - as: agent - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "provision-manager-{manager-os}" - - "allocate-agent-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc-aws.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc-aws.yaml deleted file mode 100644 index ddc11b67c8..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc-aws.yaml +++ /dev/null @@ -1,106 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-oracle-9-amd64 - - linux-amazon-2-amd64 - - #- linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-centos-7-amd64 - - - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - - linux-amazon-2023-amd64 - - linux-suse-15-amd64 - #- linux-opensuse-15-amd64 - - manager-os: - #- linux-oracle-9-amd64 - #- linux-ubuntu-20.04-amd64 - #- linux-centos-8-amd64 - - linux-redhat-7-amd64 - infra-provider: aws - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager}" - - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager}/track.yaml" - - label-termination-date: "1d" - - label-team: "QA" - foreach: - - variable: manager-os - as: manager - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "QA" - foreach: - - variable: agent-os - as: agent - - # Generic agent test task - - task: "run-agent-tests" - description: "Run tests install for the agent." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" - - agent-1: "{working-dir}/agent-linux-ubuntu-20.04-amd64/inventory.yaml" - - agent-2: "{working-dir}/agent-linux-ubuntu-22.04-amd64/inventory.yaml" - - agent-3: "{working-dir}/agent-linux-oracle-9-amd64/inventory.yaml" - - agent-4: "{working-dir}/agent-linux-amazon-2-amd64/inventory.yaml" - #- agent-5: "{working-dir}/agent-linux-redhat-7-amd64/inventory.yaml" - - agent-5: "{working-dir}/agent-linux-redhat-8-amd64/inventory.yaml" - - agent-6: "{working-dir}/agent-linux-redhat-9-amd64/inventory.yaml" - - agent-7: "{working-dir}/agent-linux-centos-7-amd64/inventory.yaml" - - agent-8: "{working-dir}/agent-linux-centos-8-amd64/inventory.yaml" - - agent-9: "{working-dir}/agent-linux-debian-10-amd64/inventory.yaml" - - agent-10: "{working-dir}/agent-linux-debian-11-amd64/inventory.yaml" - - agent-11: "{working-dir}/agent-linux-debian-12-amd64/inventory.yaml" - #- agent-12: "{working-dir}/agent-linux-opensuse-15-amd64/inventory.yaml" - - agent-12: "{working-dir}/agent-linux-suse-15-amd64/inventory.yaml" - - agent-13: "{working-dir}/agent-linux-amazon-2023-amd64/inventory.yaml" - - - tests: "install,registration,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml deleted file mode 100644 index fbbe151127..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml +++ /dev/null @@ -1,105 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-oracle-9-amd64 - - linux-amazon-2-amd64 - - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-centos-7-amd64 - - - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - #- linux-amazon-2023-amd64 - #- linux-suse-15-amd64 - - - manager-os: - - linux-opensuse-15-amd64 - #- linux-ubuntu-22.04-amd64 - #- linux-debian-12-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager}" - - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager}/track.yaml" - - label-termination-date: "1d" - - label-team: "QA" - foreach: - - variable: manager-os - as: manager - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "QA" - foreach: - - variable: agent-os - as: agent - - # Generic agent test task - - task: "run-agent-tests" - description: "Run tests install for the agent." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-linux-opensuse-15-amd64/inventory.yaml" - - agent-1: "{working-dir}/agent-linux-ubuntu-20.04-amd64/inventory.yaml" - - agent-2: "{working-dir}/agent-linux-ubuntu-22.04-amd64/inventory.yaml" - - agent-3: "{working-dir}/agent-linux-oracle-9-amd64/inventory.yaml" - - agent-4: "{working-dir}/agent-linux-amazon-2-amd64/inventory.yaml" - - agent-5: "{working-dir}/agent-linux-centos-7-amd64/inventory.yaml" - - agent-6: "{working-dir}/agent-linux-centos-8-amd64/inventory.yaml" - - agent-7: "{working-dir}/agent-linux-redhat-7-amd64/inventory.yaml" - - agent-8: "{working-dir}/agent-linux-redhat-8-amd64/inventory.yaml" - - agent-9: "{working-dir}/agent-linux-redhat-9-amd64/inventory.yaml" - - agent-10: "{working-dir}/agent-linux-debian-10-amd64/inventory.yaml" - - agent-11: "{working-dir}/agent-linux-debian-11-amd64/inventory.yaml" - - agent-12: "{working-dir}/agent-linux-debian-12-amd64/inventory.yaml" - #- agent-12: "{working-dir}/agent-linux-opensuse-15-amd64/inventory.yaml" - #- agent-12: "{working-dir}/agent-linux-suse-15-amd64/inventory.yaml" - #- agent-13: "{working-dir}/agent-linux-amazon-2023-amd64/inventory.yaml" - - - tests: "install,registration,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-vagrant.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-vagrant.yaml deleted file mode 100755 index ca320a07e2..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents-vagrant.yaml +++ /dev/null @@ -1,138 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: package - version: 4.7.3 - live: True - depends-on: - - "allocate-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agents-os - as: agent - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "provision-manager-{manager-os}" - - "allocate-agent-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents.yaml deleted file mode 100755 index e8a827282d..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/dtt1-agents.yaml +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -version: 0.1 -description: This workflow is used to test agents deployment. -variables: - agents-os: - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-fedora-37-amd64 - - linux-fedora-38-amd64 - - linux-suse-15-amd64 - - linux-opensuse-15-amd64 - - linux-oracle-9-amd64 - - linux-amazon-2-amd64 - - linux-amazon-2023-amd64 - - windows-10-amd64 - - windows-11-amd64 - - windows-server2012-amd64 - - windows-server2016-amd64 - - windows-server2019-amd64 - - windows-server2022-amd64 - - macos-13.3-amd64 - - macos-14.2-amd64 - manager-os: linux-amazon-2023-amd64 - -tasks: - # Generic agent test task - - task: "test-agent-{agent}" - description: "Run tests for the {agent} agent." - do: - this: process - with: - path: /bin/echo - args: - - -n - - "Running tests for {agent}" - depends-on: - - "provision-agent-{agent}" - foreach: - - variable: agents-os - as: agent - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: /bin/echo - args: - - -n - - "Running provision for manager" - depends-on: - - "allocate-manager-{manager-os}" - - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: /bin/echo - args: - - -n - - "Running allocate for manager" - cleanup: - this: process - with: - path: /bin/echo - args: - - -n - - "Running cleanup for manager" - - # Generic agent provision task - - task: "provision-agent-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: /bin/echo - args: - - -n - - "Running provision for {agent}" - depends-on: - - "allocate-agent-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agents-os - as: agent - - # Generic agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the {agent} agent." - do: - this: process - with: - path: /bin/echo - args: - - -n - - "Running allocate for {agent}" - cleanup: - this: process - with: - path: /bin/echo - args: - - -n - - "Running cleanup for allocate for {agent}" - foreach: - - variable: agents-os - as: agent \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-aws.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-aws.yaml deleted file mode 100644 index 91bae10b90..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-aws.yaml +++ /dev/null @@ -1,74 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - infra-provider: aws - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-linux-centos-7-amd64" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "linux-centos-7-amd64" - - inventory-output: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" - - - # Unique manager allocate task - - task: "allocate-manager-linux-ubuntu-20.04-amd64" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "linux-ubuntu-20.04-amd64" - - inventory-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - - track-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - - - - # Generic manager test task - - task: "run-manager-tests" - description: "Run tests install for the manager." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - - tests: "stop" - - component: "manager" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - depends-on: - - "allocate-manager-linux-ubuntu-20.04-amd64" - - "allocate-manager-linux-centos-7-amd64" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml deleted file mode 100644 index 35817f2169..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers-poc-vagrant.yaml +++ /dev/null @@ -1,82 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-linux-centos-7-amd64" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "linux-centos-7-amd64" - - inventory-output: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" - - - # Unique manager allocate task - - task: "allocate-manager-linux-ubuntu-20.04-amd64" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "linux-ubuntu-20.04-amd64" - - inventory-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - - track-output: "{working-dir}/manager-linux-ubuntu-20.04-amd64/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-linux-centos-7-amd64/track.yaml" - - - - # Generic manager test task - - task: "run-manager-tests" - description: "Run tests install for the manager." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - - tests: "stop" - - component: "manager" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - depends-on: - - "allocate-manager-linux-ubuntu-20.04-amd64" - - "allocate-manager-linux-centos-7-amd64" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers.yaml b/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers.yaml deleted file mode 100755 index 503cd115a3..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/dtt1-managers.yaml +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -version: 0.1 -description: This workflow is used to test managers deployment. Two agents per manager are deployed. -variables: - agents-os: - - linux-debian-12-amd64 - - linux-ubuntu-22.04-amd64 - managers-os: - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-fedora-37-amd64 - - linux-fedora-38-amd64 - - linux-suse-15-amd64 - - linux-opensuse-15-amd64 - - linux-oracle-9-amd64 - - linux-amazon-2-amd64 - - linux-amazon-2023-amd64 -tasks: - # Generic manager test task - - task: "test-{manager}-{agent}" - do: - this: process - with: - path: /bin/echo - args: - - Executing tests for {manager} manager with {agent} agent. - depends-on: - - "provision-{manager}-manager" - - "provision-{agent}-agent-for-{manager}-manager" - foreach: - - variable: managers-os - as: manager - - variable: agents-os - as: agent - - # --------- Provision -------------- - # Generic manager provision task - - task: "provision-{manager}-manager" - do: - this: process - with: - path: /bin/echo - args: - - Executing provision for {manager} as a manager. - depends-on: - - "allocate-{manager}-manager" - foreach: - - variable: managers-os - as: manager - - # Generic agent provision task - - task: "provision-{agent}-agent-for-{manager}-manager" - do: - this: process - with: - path: /bin/echo - args: - - Executing provision for {agent} as an agent. - depends-on: - - "allocate-{agent}-agent-for-{manager}-manager" - foreach: - - variable: managers-os - as: manager - - variable: agents-os - as: agent - - # --------- Allocate -------------- - # Generic manager allocate task - - task: "allocate-{manager}-manager" - do: - this: process - with: - path: /bin/echo - args: - - Executing allocation for {manager} as a manager. - foreach: - - variable: managers-os - as: manager - - # Generic agent allocate task - - task: "allocate-{agent}-agent-for-{manager}-manager" - do: - this: process - with: - path: /bin/echo - args: - - Executing allocation for {agent} as an agent. - foreach: - - variable: managers-os - as: manager - - variable: agents-os - as: agent diff --git a/deployability/modules/build/lib/workflow_engine/examples/test.yaml b/deployability/modules/build/lib/workflow_engine/examples/test.yaml deleted file mode 100644 index e6cc336bfe..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/test.yaml +++ /dev/null @@ -1,72 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-18.04-amd64 - - manager-os: linux-centos-7-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - - # Generic agent test task - - task: "run-agent-ubuntu-18.04-tests" - description: "Run tests install for the agent ubuntu-18.04." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-ubuntu-18.04/inventory.yaml" - - tests: "stop" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - depends-on: - - "provision-manager-{manager-os}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-complete.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-complete.yaml deleted file mode 100755 index e992dfdc08..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-complete.yaml +++ /dev/null @@ -1,114 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: aws - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - depends-on: - - "provision-manager-{manager-os}" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "allocate-agent-{agent}" diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml deleted file mode 100755 index ad39a8b3c5..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml +++ /dev/null @@ -1,143 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: aws - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - on-error: "abort-all" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: package - version: 4.7.3 - live: True - depends-on: - - "allocate-agent-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agent-os - as: agent - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "restart" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml deleted file mode 100755 index 74da24139b..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml +++ /dev/null @@ -1,143 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 - manager-os: linux-ubuntu-20.04-amd64 - infra-provider: aws - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - on-error: "abort-all" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: package - version: 4.7.3 - live: True - depends-on: - - "allocate-agent-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agent-os - as: agent - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "stop" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "provision-install-{agent}" diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-suse.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-suse.yaml deleted file mode 100755 index 55392f2c44..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-suse.yaml +++ /dev/null @@ -1,122 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-opensuse-15-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: aws - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - depends-on: - - "provision-manager-{manager-os}" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - - component: tar - - component: curl - depends-on: - - "allocate-manager-{manager-os}" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: curl - depends-on: - - "allocate-agent-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agent-os - as: agent - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "allocate-agent-{agent}" diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml deleted file mode 100755 index 8ab89589d6..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml +++ /dev/null @@ -1,143 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: aws - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - on-error: "abort-all" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: package - version: 4.7.3 - live: True - depends-on: - - "allocate-agent-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agent-os - as: agent - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-complete.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-complete.yaml deleted file mode 100755 index a2cb5a2482..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-complete.yaml +++ /dev/null @@ -1,114 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - depends-on: - - "provision-manager-{manager-os}" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "allocate-agent-{agent}" diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml deleted file mode 100755 index 913dbaed3d..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml +++ /dev/null @@ -1,132 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - on-error: "abort-all" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: package - version: 4.7.3 - live: True - depends-on: - - "allocate-agent-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agent-os - as: agent - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "stop" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml deleted file mode 100755 index e61393c256..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml +++ /dev/null @@ -1,133 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-oracle-9-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - on-error: "abort-all" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: package - version: 4.7.3 - live: True - depends-on: - - "allocate-agent-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agent-os - as: agent - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "stop" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml b/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml deleted file mode 100755 index 5568493d71..0000000000 --- a/deployability/modules/build/lib/workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml +++ /dev/null @@ -1,143 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment por DDT1 PoC -variables: - agent-os: - - linux-ubuntu-18.04-amd64 - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 - - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - on-error: "abort-all" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: package - version: 4.7.3 - live: True - depends-on: - - "allocate-agent-{agent}" - - "provision-manager-{manager-os}" - foreach: - - variable: agent-os - as: agent - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/logger/__init__.py b/deployability/modules/build/lib/workflow_engine/logger/__init__.py deleted file mode 100644 index ea0d8aeea6..0000000000 --- a/deployability/modules/build/lib/workflow_engine/logger/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 diff --git a/deployability/modules/build/lib/workflow_engine/logger/config.yaml b/deployability/modules/build/lib/workflow_engine/logger/config.yaml deleted file mode 100644 index 134b67dfdc..0000000000 --- a/deployability/modules/build/lib/workflow_engine/logger/config.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - -version: 1 -formatters: - simple: - format: '[%(asctime)s] [%(levelname)s] [%(process)d] [%(threadName)s] [%(name)s]: %(message)s' - colored: - (): colorlog.ColoredFormatter - format: '%(log_color)s[%(asctime)s] [%(levelname)s] [%(process)d] [%(threadName)s] [%(name)s]: %(message)s' - datefmt: '%Y-%m-%d %H:%M:%S' - log_colors: - DEBUG: cyan - INFO: green - WARNING: yellow - ERROR: red - CRITICAL: red,bg_white -handlers: - console: - class: colorlog.StreamHandler - level: DEBUG - formatter: colored - stream: ext://sys.stdout - file: - class: logging.FileHandler - level: DEBUG - formatter: simple - filename: /tmp/workflow2.log -root: - level: DEBUG - handlers: [console, file] diff --git a/deployability/modules/build/lib/workflow_engine/logger/filter.py b/deployability/modules/build/lib/workflow_engine/logger/filter.py deleted file mode 100644 index f75009e364..0000000000 --- a/deployability/modules/build/lib/workflow_engine/logger/filter.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - -import logging -import threading - - -class ThreadIDFilter(logging.Filter): - """ - A filter that uppercases the name of the log record. - """ - def filter(self, record: str) -> bool: - """ - Inject thread_id to log records. - - Args: - record (LogRecord): The log record to filter. - - Returns: - bool: True if the record should be logged, False otherwise. - """ - record.thread_id = threading.get_native_id() - return record diff --git a/deployability/modules/build/lib/workflow_engine/logger/logger.py b/deployability/modules/build/lib/workflow_engine/logger/logger.py deleted file mode 100644 index 3f8e73587d..0000000000 --- a/deployability/modules/build/lib/workflow_engine/logger/logger.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - -import logging -import logging.config - -from pathlib import Path - -import yaml - - -def _load_config() -> None: - """ - Loads the logging configuration from 'config.yaml' file. - """ - config_path = Path(__file__).parent / 'config.yaml' - with open(config_path, 'r') as f: - config = yaml.safe_load(f.read()) - logging.config.dictConfig(config) - - -_load_config() - -logger = logging.getLogger("workflow_engine") diff --git a/deployability/modules/build/lib/workflow_engine/models.py b/deployability/modules/build/lib/workflow_engine/models.py deleted file mode 100644 index c92d2a868f..0000000000 --- a/deployability/modules/build/lib/workflow_engine/models.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - -from pathlib import Path -from typing import Literal -from pydantic import BaseModel - - -class InputPayload(BaseModel): - workflow_file: str | Path - threads: int = 1 - dry_run: bool = False - log_level: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] = 'INFO' - schema_file: str | Path | None = None diff --git a/deployability/modules/build/lib/workflow_engine/requirements-dev.txt b/deployability/modules/build/lib/workflow_engine/requirements-dev.txt deleted file mode 100644 index 05a93e2895..0000000000 --- a/deployability/modules/build/lib/workflow_engine/requirements-dev.txt +++ /dev/null @@ -1,2 +0,0 @@ --r ../../deps/requirements.txt --r ../../deps/remote_requirements.txt \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/schema_validator.py b/deployability/modules/build/lib/workflow_engine/schema_validator.py deleted file mode 100755 index d04a9bb39d..0000000000 --- a/deployability/modules/build/lib/workflow_engine/schema_validator.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - -import jsonschema -import json -import os - -from jsonschema.exceptions import ValidationError -from pathlib import Path -from ruamel.yaml import YAML - -from workflow_engine.logger.logger import logger - -class SchemaValidator: - """ - A SchemaValidator class that validates a YAML file against a JSON schema. - - Attributes: - schema_data (dict): The schema data. - yaml_data (dict): The YAML data. - """ - - def __init__(self, schema: Path | str, to_validate: Path | str): - """ - Initializes the SchemaValidator object. - - Args: - schema (Path, str): The path to the schema file. - to_validate (Path, str): The path to the YAML file to validate. - """ - schema_data: str = None - yaml_data: str = None - - self.logger = logger - - if not os.path.exists(schema): - raise FileNotFoundError(f'File "{schema}" not found.') - - with open(schema, 'r') as schema_file: - self.logger.debug(f"Loading schema file: {schema}") - schema_data = json.load(schema_file) - - if not os.path.exists(to_validate): - raise FileNotFoundError(f'File "{to_validate}" not found.') - - with open(to_validate, 'r') as file: - self.logger.debug(f"Loading yaml file: {to_validate}") - yaml = YAML(typ='safe', pure=True) - yaml_data = yaml.load(file) - - self.schema_data = schema_data - self.yaml_data = yaml_data - - def preprocess_data(self) -> None: - """ - Preprocess the YAML data to be validated. - - Raises: - ValidationError: If the YAML data is not valid. - """ - for task in self.yaml_data.get('tasks', []): - do_with = task.get('do', {}).get('with', {}) - this_value = task.get('do', {}).get('this', '') - - if this_value == 'process': - if 'path' not in do_with or 'args' not in do_with: - raise ValidationError(f"Missing required properties in 'with' for task: {task}") - - do_with = task.get('cleanup', {}).get('with', {}) - this_value = task.get('cleanup', {}).get('this', '') - - if this_value == 'process': - if 'path' not in do_with or 'args' not in do_with: - raise ValidationError(f"Missing required properties in 'with' for task: {task}") - - def validateSchema(self) -> None: - """ - Validate the Workflow schema - - Raises: - ValidationError: If the YAML data is not valid. - Exception: If an unexpected error occurs. - """ - try: - jsonschema.validate(self.yaml_data, self.schema_data) - except ValidationError as e: - self.logger.error(f"Schema validation error: {e}") - except Exception as e: - self.logger.error(f"Unexpected error at schema validation: {e}") diff --git a/deployability/modules/build/lib/workflow_engine/schemas/schema_v1.json b/deployability/modules/build/lib/workflow_engine/schemas/schema_v1.json deleted file mode 100644 index 81f8e7c587..0000000000 --- a/deployability/modules/build/lib/workflow_engine/schemas/schema_v1.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "type": "object", - "properties": { - "name": {"type": "string"}, - "description": {"type": "string"}, - "version": {"type": "number"}, - "tasks": { - "type": "array", - "items": { - "type": "object", - "properties": { - "task": {"type": "string"}, - "do": { - "type": "object", - "properties": { - "this": {"type": "string"}, - "with": { - "type": "object", - "properties": { - "this": {"type": "string"}, - "args": { - "type": "array", - "items": { - "oneOf": [ - {"type": "string"}, - {"type": "array"}, - {"type": "object"} - ] - } - }, - "path": {"type": "string"} - } - } - }, - "required": ["this"] - }, - "cleanup": { - "type": "object", - "properties": { - "this": {"type": "string"}, - "with": { - "type": "object", - "properties": { - "this": {"type": "string"}, - "args": { - "type": "array", - "items": { - "oneOf": [ - {"type": "string"}, - {"type": "array"}, - {"type": "object"} - ] - } - }, - "path": {"type": "string"} - } - } - }, - "required": ["this"] - }, - "depends-on": { - "type": "array", - "items": {"type": "string"} - }, - "foreach": { - "type": "array", - "items": { - "type": "object", - "properties": { - "variable": {"type": "string"}, - "as": {"type": "string"}, - "foreach": { - "type": "array", - "items": { - "type": "object", - "properties": { - "variable": {"type": "string"}, - "as": {"type": "string"} - } - } - } - }, - "required": ["variable", "as"] - } - } - }, - "required": ["task", "do"] - - }, - "minItems": 1 - }, - "variables": { - "type": "object", - "properties": { - "agent-os": { - "type": "array", - "items": { - "oneOf": [ - {"type": "string"}, - {"type": "array"} - ] - } - }, - "managers-os": { - "type": "array", - "items": { - "oneOf": [ - {"type": "string"}, - {"type": "array"} - ] - } - } - } - } - }, - "required": ["tasks", "variables", "version"], - "additionalProperties": false - } \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/task.py b/deployability/modules/build/lib/workflow_engine/task.py deleted file mode 100755 index b9e349a74b..0000000000 --- a/deployability/modules/build/lib/workflow_engine/task.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - -import subprocess -import random -import time - -from abc import ABC, abstractmethod -from workflow_engine.logger.logger import logger - -class Task(ABC): - """Abstract base class for tasks.""" - - @abstractmethod - def execute(self) -> None: - """Execute the task.""" - pass - -class ProcessTask(Task): - """Task for executing a process.""" - - def __init__(self, task_name: str, task_parameters: dict): - """ - Initialize ProcessTask. - - Args: - task_name (str): Name of the task. - task_parameters (dict): Parameters for the task. - logger (logging.Logger): Logger instance. - """ - self.task_name = task_name - self.task_parameters = task_parameters - self.logger = logger - - def execute(self) -> None: - """Execute the process task.""" - - task_args = [] - if self.task_parameters.get('args') is None: - raise ValueError(f'Not argument found in {self.task_name}') - - for arg in self.task_parameters['args']: - if isinstance(arg, str): - task_args.append(arg) - elif isinstance(arg, dict): - key, value = list(arg.items())[0] - if isinstance(value, list): - task_args.extend([f"--{key}={argvalue}" for argvalue in value]) - else: - task_args.append(f"--{key}={value}") - else: - logger.error(f'Could not parse arguments {arg}') - - logger.debug(f'Running task "{self.task_name}" with arguments: {task_args}') - - result = None - try: - result = subprocess.run( - [self.task_parameters['path']] + task_args, - check=True, - capture_output=True, - text=True, - ) - logger.debug(f'Finished task "{self.task_name}" execution with result:\n{str(result.stdout)}') - - if result.returncode != 0: - raise subprocess.CalledProcessError(returncode=result.returncode, cmd=result.args, output=result.stdout) - except subprocess.CalledProcessError as e: - error_msg = e.stderr - if "KeyboardInterrupt" in error_msg: - raise KeyboardInterrupt(f"Error executing process task with keyboard interrupt.") - raise Exception(f"Error executing process task {e.stderr}") - -class DummyTask(Task): - def __init__(self, task_name, task_parameters): - self.task_name = task_name - self.task_parameters = task_parameters - - def execute(self): - message = self.task_parameters.get('message', 'No message provided') - logger.info("%s: %s", message, self.task_name, extra={'tag': self.task_name}) - -class DummyRandomTask(Task): - def __init__(self, task_name, task_parameters): - self.task_name = task_name - self.task_parameters = task_parameters - - def execute(self): - time_interval = self.task_parameters.get('time-seconds', [1, 5]) - sleep_time = random.uniform(time_interval[0], time_interval[1]) - - message = self.task_parameters.get('message', 'No message provided') - logger.info("%s: %s (Sleeping for %.2f seconds)", message, self.task_name, sleep_time, extra={'tag': self.task_name}) - - time.sleep(sleep_time) - -TASKS_HANDLERS = { - 'process': ProcessTask, - 'dummy': DummyTask, - 'dummy-random': DummyRandomTask, -} diff --git a/deployability/modules/build/lib/workflow_engine/tests/TESTING-README.md b/deployability/modules/build/lib/workflow_engine/tests/TESTING-README.md deleted file mode 100644 index 9144e08dc3..0000000000 --- a/deployability/modules/build/lib/workflow_engine/tests/TESTING-README.md +++ /dev/null @@ -1,167 +0,0 @@ -# Workflow engine Unit Testing using Pytest - -The workflow_engine module includes pytest unit tests. - -## Requirements - -- Make sure you have Python installed on your system. You can download it from - [python.org](https://www.python.org/downloads/). -- Clone the wazuh-qa repository in your local environment. -- Install the necessary dependencies by running: -```bash -git clone https://github.com/wazuh/wazuh-qa.git -b [your-branch] -cd wazuh-qa -pip install -r deployability/modules/workflow_engine/requirements-dev.txt -``` -- Configure the `PYTHONPATH` variable with the full path to the directory `deployability/modules`, for example if you've -cloned the `wazuh-qa` repository into `/wazuh/wazuh-qa`, configure the `PYTHONPATH` in this way: -```bash -> pwd -/wazuh/wazuh-qa -> export PYTHONPATH=$PYTHONPATH:$PWD/deployability/modules -> echo $PYTHONPATH -/wazuh/wazuh-qa/deployability/modules -``` - -## Test Structure -The directory `deployability/modules/workflow_engine/tests/` contains the unit test files for the `workflow_engine` -module. - -## Running Tests -To run the tests, make sure that your system meets the requirements by executing the following command from the project -root: - -```bash -pytest -vv deployability/modules/workflow_engine -``` -This command will run all tests in the `tests/` directory. Using additional arguments, You can also run specific tests -or directories. The output of this command looks like this: -```bash -pytest -vv deployability/modules/workflow_engine -============================================================================================== test session starts ============================================================================================== -platform linux -- Python 3.10.13, pytest-7.1.2, pluggy-1.3.0 -- /usr/local/bin/python3 -cachedir: .pytest_cache -metadata: {'Python': '3.10.13', 'Platform': 'Linux-5.15.146.1-microsoft-standard-WSL2-x86_64-with-glibc2.31', 'Packages': {'pytest': '7.1.2', 'pluggy': '1.3.0'}, 'Plugins': {'anyio': '4.2.0', 'testinfra': '5.0.0', 'metadata': '3.0.0', 'html': '3.1.1'}} -rootdir: /home/marcelo/wazuh/wazuh-qa/deployability/modules -plugins: anyio-4.2.0, testinfra-5.0.0, metadata-3.0.0, html-3.1.1 -collected 92 items - -deployability/modules/workflow_engine/tests/test_dag.py::test_dag_constructor[True] PASSED [ 1%] -deployability/modules/workflow_engine/tests/test_dag.py::test_dag_constructor[False] PASSED [ 2%] -deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[True-dag0] PASSED [ 3%] -deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[True-dag1] PASSED [ 4%] -deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[False-dag0] PASSED [ 5%] -deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[False-dag1] PASSED [ 6%] -deployability/modules/workflow_engine/tests/test_dag.py::test_get_execution_plan[dag0] PASSED [ 7%] -deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-failed-dag0] PASSED [ 8%] -deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-canceled-dag0] PASSED [ 9%] -deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-successful-dag0] PASSED [ 10%] -deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-non_existing_status-dag0] FAILED [ 11%] -deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[non_existing_task-successful-dag0] PASSED [ 13%] -deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[non_existing_task-non_existing_status-dag0] FAILED [ 14%] -deployability/modules/workflow_engine/tests/test_dag.py::test_should_be_canceled[True-dag0] PASSED [ 15%] -deployability/modules/workflow_engine/tests/test_dag.py::test_should_be_canceled[False-dag0] PASSED [ 16%] -deployability/modules/workflow_engine/tests/test_dag.py::test_build_dag[dag0] PASSED [ 17%] -deployability/modules/workflow_engine/tests/test_dag.py::test_build_dag[dag1] PASSED [ 18%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-all-to_be_canceled0-dag0] PASSED [ 19%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-all-to_be_canceled0-dag1] PASSED [ 20%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-related-flows-to_be_canceled1-dag0] FAILED [ 21%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-related-flows-to_be_canceled1-dag1] FAILED [ 22%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-continue-to_be_canceled2-dag0] FAILED [ 23%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-continue-to_be_canceled2-dag1] FAILED [ 25%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-all-to_be_canceled3-dag0] FAILED [ 26%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-all-to_be_canceled3-dag1] FAILED [ 27%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-related-flows-to_be_canceled4-dag0] FAILED [ 28%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-related-flows-to_be_canceled4-dag1] FAILED [ 29%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-continue-to_be_canceled5-dag0] FAILED [ 30%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-continue-to_be_canceled5-dag1] FAILED [ 31%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-all-to_be_canceled6-dag0] PASSED [ 32%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-all-to_be_canceled6-dag1] PASSED [ 33%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-related-flows-to_be_canceled7-dag0] PASSED [ 34%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-related-flows-to_be_canceled7-dag1] PASSED [ 35%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-continue-to_be_canceled8-dag0] FAILED [ 36%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-continue-to_be_canceled8-dag1] FAILED [ 38%] -deployability/modules/workflow_engine/tests/test_dag.py::test_create_execution_plan[dag0-exec_plan0] PASSED [ 39%] -deployability/modules/workflow_engine/tests/test_dag.py::test_create_execution_plan[dag1-exec_plan1] PASSED [ 40%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_schema_validator_constructor[logger_mock0] PASSED [ 41%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_schema_validator_constructor_ko PASSED [ 42%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data PASSED [ 43%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data_ko[wf-ko-no-path-on-do.yaml-Missing required properties in 'with' for task: {'task': 'run-agent-tests-{agent}'] PASSED [ 44%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data_ko[wf-ko-no-path-on-cleanup.yaml-Missing required properties in 'with' for task: {'task': 'allocate-manager'] PASSED [ 45%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_validate_schema PASSED [ 46%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_validate_schema_ko[logger_mock0] PASSED [ 47%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_constructor[task0] PASSED [ 48%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task0] PASSED [ 50%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task1] PASSED [ 51%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task2] PASSED [ 52%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task3] PASSED [ 53%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task4] PASSED [ 54%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc0-1-task0] PASSED [ 55%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc0-0-task0] PASSED [ 56%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc1-1-task0] PASSED [ 57%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc1-0-task0] PASSED [ 58%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_constructor PASSED [ 59%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_validate_schema[logger_mock0] PASSED [ 60%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_validate_schema_ko[logger_mock0] PASSED [ 61%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_load_workflow[logger_mock0] PASSED [ 63%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_load_workflow_ko[logger_mock0] PASSED [ 64%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_process_workflow[logger_mock0] PASSED [ 65%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_process_workflow_ok[logger_mock0] PASSED [ 66%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element0-values0-return_value0] PASSED [ 67%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element1-values1-return_value1] PASSED [ 68%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[string_element {value}-values2-string_element value] PASSED [ 69%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element3-None-return_value3] PASSED [ 70%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_expand_task[task0-return_value0-variables0] PASSED [ 71%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_expand_task[task1-return_value1-variables1] PASSED [ 72%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation PASSED [ 73%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation_ko[task_collection0-Duplicated task names: task 1] PASSED [ 75%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation_ko[task_collection1-Tasks do not exist: task 3, task 4] PASSED [ 76%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-False-1-info-schema.yaml] PASSED [ 77%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-True-1-debug-schema.yaml] PASSED [ 78%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-True-1-debug-None] PASSED [ 79%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task[logger_mock0-w_processor0-dag0-custom_action-True] PASSED [ 80%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task[logger_mock1-w_processor1-dag1-custom_action-False] PASSED [ 81%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock0-w_processor0-dag0-KeyboardInterrupt-None] PASSED [ 82%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock0-w_processor0-dag0-KeyboardInterrupt-abort-all] PASSED [ 83%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock1-w_processor1-dag1-Exception-None] PASSED [ 84%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock1-w_processor1-dag1-Exception-abort-all] PASSED [ 85%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-process] PASSED [ 86%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-dummy] PASSED [ 88%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-dummy-random] PASSED [ 89%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object_ko[w_processor0] PASSED [ 90%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel[logger_mock0-w_processor0-dag0-False] PASSED [ 91%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel[logger_mock0-w_processor0-dag0-True] PASSED [ 92%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel_ko[logger_mock0-w_processor0-dag0-False] PASSED [ 93%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel_ko[logger_mock0-w_processor0-dag0-True] PASSED [ 94%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_generate_futures[w_processor0] PASSED [ 95%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_generate_futures_reverse[w_processor0] PASSED [ 96%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_run[logger_mock0-w_processor0-False] PASSED [ 97%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_run[logger_mock0-w_processor0-True] PASSED [ 98%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_handle_interrupt[logger_mock0-w_processor0] PASSED [100%] - -=================================================================================================== FAILURES ==================================================================================================== -``` - -The `.github/workflow/workflow-engine-unit-tests.yaml` automatically runs the unit tests in the GitHub environment. -The run results are in the `checks` tab or your GitHub pull request. - -## Relevant Files -- `tests/test_[test name].py`: all the unit test files start with a `test_` prefix. There is one unit test file for - each tested class. -- `tests/conftest.py`: contains the fixtures used throughout the unit tests. - -## Unit test development guidelines and recommendations -- Use Python coding style standards and recommendations to develop unit tests: snake case for all variable and function - names, maximum line length of 120 characters, two empty lines must separate each function, typing all your functions - and return values, create Docstring for all functions with numpy style. -- Develop unit tests for each function or method of the module. -- Error flows are usually created in a second unit test with the suffix `_ko`. For example, the - `test_process_task_execute` found in the `deployability/modules/workflow_engine/tests/test_workflow_processor` is the - unit test normal flow for the `WorkflowProcessor.process_task_execute` method. The - `WorkflowProcessor.process_task_execute_ko` unit test implements the error flow. -- Use the pytest's decorator `@pytest.mark.parametrize` to implement test cases for the same unit test. -- Mock the object instance and functions used by your tested function using the `unitest.mock.patch` and - `unitest.mock.patch.object` functions or decorators. -- Try to factorize your testing code using `pytest.fixtures`. The shared fixtures are in the `conftest.py` file. In - many unit tests of this project, the fixtures implement a `request` object that receives parameters from the - `pytest.mark.parametrize`. diff --git a/deployability/modules/build/lib/workflow_engine/tests/conftest.py b/deployability/modules/build/lib/workflow_engine/tests/conftest.py deleted file mode 100644 index 5de92b5222..0000000000 --- a/deployability/modules/build/lib/workflow_engine/tests/conftest.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -"""Common unit test fixtures.""" -import graphlib - -from unittest.mock import patch, MagicMock -import pytest - -from workflow_engine.workflow_processor import DAG, WorkflowProcessor - -DEFAULT_TASK_COLLECTION = [ - {'task': 'task1', 'path': '/cmd1', 'args': [{"param1": "value1"}]}, - {'task': 'task2', 'path': '/cmd2', 'args': [{"param1": "value1"}]}, - {'task': 'task3', 'path': '/cmd3', 'args': [{"param1": "value1"}]}, -] - - -@pytest.fixture -def logger_mock(request) -> MagicMock: - """Fixture to mock common logger methods.""" - logger_to_patch = request.param.get('logger_to_patch', "workflow_engine.workflow_processor.logger") - with patch(logger_to_patch) as l_mock: - patch.object(l_mock, 'warning') - patch.object(l_mock, 'info') - patch.object(l_mock, 'debug') - patch.object(l_mock, 'error') - yield l_mock - - -@pytest.fixture -def dag(request) -> DAG: - """Create a mocked DAG instance.""" - ret_dag: DAG - reverse = request.param.get('reverse', False) - task_collection = request.param.get('task_collection', DEFAULT_TASK_COLLECTION) - if request.param.get('patch', True): - execution_plan_dict = request.param.get('execution_plan_dict', {}) - gl_dag = graphlib.TopologicalSorter() - dep_dict = {'task1': 'task2'} - with patch.object(gl_dag, 'prepare'), \ - patch('workflow_engine.workflow_processor.DAG._DAG__build_dag', - return_value=(gl_dag, dep_dict)), \ - patch('workflow_engine.workflow_processor.DAG._DAG__create_execution_plan', - return_value=execution_plan_dict): - ret_dag = DAG(task_collection=task_collection, reverse=reverse) - else: - ret_dag = DAG(task_collection=task_collection, reverse=reverse) - - if finished_task_status := request.param.get('finished_task_status', False): - ret_dag.finished_tasks_status = finished_task_status - - return ret_dag - - -@pytest.fixture -def w_processor(request) -> WorkflowProcessor: - """Create a mocked WorkflowProcessor instance.""" - - workflow_file = request.param.get('workflow_file', 'workflow.yaml') - dry_run = request.param.get('dry_run', False) - threads = request.param.get('threads', 1) - log_level = request.param.get('log_level', 'info') - schema_file = request.param.get('schema_file', 'schema.yaml') - with patch("workflow_engine.workflow_processor.WorkflowFile") as file_mock: - workflow_file_instance = file_mock.return_value - workflow_file_instance.task_collection = request.param.get('task_collection', DEFAULT_TASK_COLLECTION) - if request.param.get('patch', True): - with patch('workflow_engine.workflow_processor.logger.setLevel'): - processor = WorkflowProcessor(workflow_file, dry_run, threads, - log_level, schema_file) - else: - processor = WorkflowProcessor(workflow_file, dry_run, - threads, log_level, schema_file) - return processor diff --git a/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml b/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml deleted file mode 100644 index a637e4184f..0000000000 --- a/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml +++ /dev/null @@ -1,169 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -version: 0.1 -description: This workflow is used to test agents deployment with a single manager. -variables: - agents-os: - - linux-ubuntu-22.04-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1 - -tasks: - # Generic agent test task - - task: "run-agent-tests-{agent}" - description: "Run tests uninstall for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,register,stop" - - component: "agent" - - wazuh-version: "4.7.1" - - wazuh-revision: "40709" - depends-on: - - "provision-install-{agent}" - - "provision-manager" - foreach: - - variable: agents-os - as: agent - - # Generic agent test task - - task: "run-agent-tests-uninstall-{agent}" - description: "Run tests uninstall for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - tests: "uninstall" - - component: "agent" - - wazuh-version: "4.7.1" - - wazuh-revision: "40709" - depends-on: - - "run-agent-tests-{agent}" - - "provision-uninstall-{agent}" - foreach: - - variable: agents-os - as: agent - - # Unique manager provision task - - task: "provision-manager" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: aio - version: "4.7.0" - depends-on: - - "allocate-manager" - - # Unique manager allocate task - - task: "allocate-manager" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - cleanup: - this: process - with: - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: aio - version: "4.8.0" - live: False - depends-on: - - "allocate-{agent}" - - "provision-manager" - foreach: - - variable: agents-os - as: agent - - # Generic agent provision task - - task: "provision-uninstall-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - uninstall: - - component: wazuh-agent - type: package - depends-on: - - "provision-install-{agent}" - foreach: - - variable: agents-os - as: agent - - # Generic agent allocate task - - task: "allocate-{agent}" - description: "Allocate resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - foreach: - - variable: agents-os - as: agent \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml b/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml deleted file mode 100644 index 6b2d2512d0..0000000000 --- a/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml +++ /dev/null @@ -1,169 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -version: 0.1 -description: This workflow is used to test agents deployment with a single manager. -variables: - agents-os: - - linux-ubuntu-22.04-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1 - -tasks: - # Generic agent test task - - task: "run-agent-tests-{agent}" - description: "Run tests uninstall for the {agent} agent." - do: - this: process - with: - args: - - modules/testing/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,register,stop" - - component: "agent" - - wazuh-version: "4.7.1" - - wazuh-revision: "40709" - depends-on: - - "provision-install-{agent}" - - "provision-manager" - foreach: - - variable: agents-os - as: agent - - # Generic agent test task - - task: "run-agent-tests-uninstall-{agent}" - description: "Run tests uninstall for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - tests: "uninstall" - - component: "agent" - - wazuh-version: "4.7.1" - - wazuh-revision: "40709" - depends-on: - - "run-agent-tests-{agent}" - - "provision-uninstall-{agent}" - foreach: - - variable: agents-os - as: agent - - # Unique manager provision task - - task: "provision-manager" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: aio - version: "4.7.0" - depends-on: - - "allocate-manager" - - # Unique manager allocate task - - task: "allocate-manager" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: aio - version: "4.8.0" - live: False - depends-on: - - "allocate-{agent}" - - "provision-manager" - foreach: - - variable: agents-os - as: agent - - # Generic agent provision task - - task: "provision-uninstall-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - uninstall: - - component: wazuh-agent - type: package - depends-on: - - "provision-install-{agent}" - foreach: - - variable: agents-os - as: agent - - # Generic agent allocate task - - task: "allocate-{agent}" - description: "Allocate resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - foreach: - - variable: agents-os - as: agent \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-schema-error.yaml b/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-schema-error.yaml deleted file mode 100644 index 62c130f64b..0000000000 --- a/deployability/modules/build/lib/workflow_engine/tests/data/wf-ko-schema-error.yaml +++ /dev/null @@ -1,156 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -version: 0.1 -description: This workflow is used to test agents deployment with a single manager. -variables: - agents-os: - - linux-ubuntu-22.04-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1 - -tasks: - # Generic agent test task - - task: "run-agent-tests-{agent}" - description: "Run tests uninstall for the {agent} agent." - depends-on: - - "provision-install-{agent}" - - "provision-manager" - foreach: - - variable: agents-os - as: agent - - # Generic agent test task - - task: "run-agent-tests-uninstall-{agent}" - description: "Run tests uninstall for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - tests: "uninstall" - - component: "agent" - - wazuh-version: "4.7.1" - - wazuh-revision: "40709" - depends-on: - - "run-agent-tests-{agent}" - - "provision-uninstall-{agent}" - foreach: - - variable: agents-os - as: agent - - # Unique manager provision task - - task: "provision-manager" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: aio - version: "4.7.0" - depends-on: - - "allocate-manager" - - # Unique manager allocate task - - task: "allocate-manager" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: aio - version: "4.8.0" - live: False - depends-on: - - "allocate-{agent}" - - "provision-manager" - foreach: - - variable: agents-os - as: agent - - # Generic agent provision task - - task: "provision-uninstall-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - uninstall: - - component: wazuh-agent - type: package - depends-on: - - "provision-install-{agent}" - foreach: - - variable: agents-os - as: agent - - # Generic agent allocate task - - task: "allocate-{agent}" - description: "Allocate resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - foreach: - - variable: agents-os - as: agent \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/tests/data/wf-ok.yaml b/deployability/modules/build/lib/workflow_engine/tests/data/wf-ok.yaml deleted file mode 100644 index fa979a6f81..0000000000 --- a/deployability/modules/build/lib/workflow_engine/tests/data/wf-ok.yaml +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -version: 0.1 -description: This workflow is used to test agents deployment with a single manager. -variables: - agents-os: - - linux-ubuntu-22.04-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1 - -tasks: - # Generic agent test task - - task: "run-agent-tests-{agent}" - description: "Run tests uninstall for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,register,stop" - - component: "agent" - - wazuh-version: "4.7.1" - - wazuh-revision: "40709" - depends-on: - - "provision-install-{agent}" - - "provision-manager" - foreach: - - variable: agents-os - as: agent - - # Generic agent test task - - task: "run-agent-tests-uninstall-{agent}" - description: "Run tests uninstall for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - inventory: "{working-dir}/agent-{agent}/inventory.yaml" - - dependencies: - - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - tests: "uninstall" - - component: "agent" - - wazuh-version: "4.7.1" - - wazuh-revision: "40709" - depends-on: - - "run-agent-tests-{agent}" - - "provision-uninstall-{agent}" - foreach: - - variable: agents-os - as: agent - - # Unique manager provision task - - task: "provision-manager" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: aio - version: "4.7.0" - depends-on: - - "allocate-manager" - - # Unique manager allocate task - - task: "allocate-manager" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Generic agent provision task - - task: "provision-install-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-agent - type: aio - version: "4.8.0" - live: False - depends-on: - - "allocate-{agent}" - - "provision-manager" - foreach: - - variable: agents-os - as: agent - - # Generic agent provision task - - task: "provision-uninstall-{agent}" - description: "Provision resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory-agent: "{working-dir}/agent-{agent}/inventory.yaml" - - inventory-manager: "{working-dir}/manager-{manager-os}/inventory.yaml" - - uninstall: - - component: wazuh-agent - type: package - depends-on: - - "provision-install-{agent}" - foreach: - - variable: agents-os - as: agent - - # Generic agent allocate task - - task: "allocate-{agent}" - description: "Allocate resources for the {agent} agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - foreach: - - variable: agents-os - as: agent \ No newline at end of file diff --git a/deployability/modules/build/lib/workflow_engine/tests/test_dag.py b/deployability/modules/build/lib/workflow_engine/tests/test_dag.py deleted file mode 100644 index b3c6ec181e..0000000000 --- a/deployability/modules/build/lib/workflow_engine/tests/test_dag.py +++ /dev/null @@ -1,294 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import graphlib - -from unittest.mock import patch, MagicMock, call -import pytest - -from workflow_engine.workflow_processor import DAG - - -@pytest.mark.parametrize("reverse", [True, False]) -@patch("workflow_engine.workflow_processor.DAG._DAG__build_dag") -@patch("workflow_engine.workflow_processor.DAG._DAG__create_execution_plan") -def test_dag_constructor(create_exec_plan_mock: MagicMock, build_dag_mock: MagicMock, reverse: bool): - """Test ProcessTask constructor - Check all the dag object state after initialization and if the private dag methods are called during the instance - construction. - - Parameters - ---------- - create_exec_plan_mock : MagicMock - Patch of the DAG.__create_execution_plan method. - build_dag_mock : MagicMock - Patch of the DAG.__build_dag_ method. - reverse : bool - Parametrized value used by the DAG constructor. - """ - task_collection = [ - {'task': 'task1', 'path': '/cmd1', 'args': [{"param1": "value1"}]}, - {'task': 'task2', 'path': '/cmd2', 'args': [{"param1": "value1"}]}, - {'task': 'task3', 'path': '/cmd3', 'args': [{"param1": "value1"}]}, - ] - gl_dag = graphlib.TopologicalSorter() - - dep_dict = {'task1': 'task2'} - build_dag_mock.return_value = (gl_dag, dep_dict) - plan_dict = {'task1', 'task2'} - create_exec_plan_mock.return_value = plan_dict - with patch.object(gl_dag, 'prepare') as prepare_mock: - dag = DAG(task_collection=task_collection, reverse=reverse) - - assert dag.task_collection == task_collection - assert dag.reverse == reverse - assert dag.dag == gl_dag - assert dag.dependency_tree == dep_dict - assert isinstance(dag.to_be_canceled, set) and not dag.to_be_canceled - assert dag.finished_tasks_status == { - 'failed': set(), - 'canceled': set(), - 'successful': set(), - } - assert dag.execution_plan == plan_dict - build_dag_mock.assert_called_once() - create_exec_plan_mock.assert_called_once_with(dep_dict) - prepare_mock.assert_called_once() - - -@pytest.mark.parametrize('dag', - [{'reverse': True}, {'reverse': False}], - indirect=True) -@pytest.mark.parametrize('is_active', [True, False]) -def test_dag_is_active(is_active: bool, dag: DAG): - """Test DAG.is_active method. - Check if dag.is_active method returns the value of the dag.dag.is_active() method. - - Parameters - ---------- - is_active : bool - Parametrized value returned by dag.dag.is_active - dag : DAG - DAG fixture defined in conftest.py. - """ - with patch.object(dag.dag, 'is_active', return_value=is_active) as is_active_mock: - assert dag.is_active() == is_active - is_active_mock.assert_called_once() - - -@pytest.mark.parametrize('dag', - [{'execution_plan_dict': {'task1', 'task2'} }], indirect=True) -def test_get_execution_plan(dag: DAG): - """Test DAG.get_execution_plan method. - Check if the dag.get_execution_plan returns the dag.execution_plan instance - - Parameters - ---------- - dag : DAG - DAG fixture defined in conftest.py. - """ - assert dag.get_execution_plan() == dag.execution_plan - - -@pytest.mark.parametrize('dag', [{}], indirect=True) -@pytest.mark.parametrize('task_name, status', [ - ('task1', 'failed'), - ('task1', 'canceled'), - ('task1', 'successful'), -]) -def test_set_status(task_name:str, status:str, dag: DAG): - """Test DAG.set_status method. - Check if the dag.dag.done mode is properly called and that the task is in the failed, canceled or - successful set. - - Parameters - ---------- - task_name : str - Parameterized value for the task name passed to dag.set_status method. - status : str - Parameterized value for the task name passed to dag.set_status method. - dag : DAG - DAG fixture defined in conftest.py. - """ - with patch.object(dag.dag, "done") as done_mock: - dag.set_status(task_name=task_name, status=status) - assert task_name in dag.finished_tasks_status[status] - done_mock.assert_called_once_with(task_name) - - -@pytest.mark.parametrize('dag', [{}], indirect=True) -@pytest.mark.parametrize('in_cancel', [True, False]) -def test_should_be_canceled(in_cancel:bool, dag: DAG): - """Test DAG.should_be_canceled method. - Check if dag.should_be_canceled returns True or False if the task is in the dab.to_be_canceled set. - - Parameters - ---------- - in_cancel : bool - Parameterized value to test the method dag.should_be_canceled with 'task1' - in the dag.to_be_canceled set or not. - dag : DAG - DAG fixture defined in conftest.py. - """ - if in_cancel: - dag.to_be_canceled.add('task1') - else: - if 'task1' in dag.to_be_canceled: - dag.to_be_canceled.remove('task1') - - assert dag.should_be_canceled(task_name='task1') == in_cancel - - -@pytest.mark.parametrize('dag', - [{ - 'task_collection': [ - {'task': 'task1', }, - {'task': 'task2', 'depends-on': ['task1']}, - {'task': 'task3', 'depends-on': ['task1']}, - {'task': 'task4', 'depends-on': ['task1']}, - {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']} - ] - }, - {'task_collection': [ - {'task': 'task1', }, - {'task': 'task2', 'depends-on': ['task1']}, - {'task': 'task3', 'depends-on': ['task1']}, - {'task': 'task4', 'depends-on': ['task1']}, - {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}], - 'reverse': True - } - ], - indirect=True) -def test_build_dag(dag: DAG): - """Test DAG.__build_dag method. - The test uses a task collection and checks the calls to the graphlib.TopologicalSorter.add. - The function calls depend on the dag.reverse instance variable, that it is also parameterized. - - Parameters - ---------- - dag : DAG - DAG fixture defined in conftest.py with task_collection parameterized. - """ - with patch('workflow_engine.workflow_processor.graphlib.TopologicalSorter.add') as mock_add: - res_dag, res_dependency_dict = dag._DAG__build_dag() - assert isinstance(res_dag, graphlib.TopologicalSorter) - call_list = [] - dependency_dict = {} - for task in dag.task_collection: - dependencies = task.get('depends-on', []) - task_name = task['task'] - if dag.reverse: - for dependency in dependencies: - call_list.append(call(dependency, task_name)) - else: - call_list.append(call(task_name, *dependencies)) - dependency_dict[task_name] = dependencies - - assert res_dependency_dict == dependency_dict - mock_add.assert_has_calls(call_list, any_order=True) - - -@pytest.mark.parametrize('dag', - [{ - 'task_collection': [ - {'task': 'task1', }, - {'task': 'task2', 'depends-on': ['task1']}, - {'task': 'task3', 'depends-on': []}, - {'task': 'task4', 'depends-on': []}, - {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']} - ], - 'patch': False - }, - {'task_collection': [ - {'task': 'task1', }, - {'task': 'task2', 'depends-on': ['task1']}, - {'task': 'task3', 'depends-on': []}, - {'task': 'task4', 'depends-on': []}, - {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}], - 'reverse': True, - 'patch': False, - 'finished_task_status': { - 'failed': set(), - 'canceled': set(), - 'successful': set()} - }, - ], - indirect=True) -@pytest.mark.parametrize('task, cancel_policy, to_be_canceled', - [('task1', 'abort-all', {'task4', 'task3', 'task2', 'task5', 'task1'}), - ('task2', 'abort-all', {'task4', 'task3', 'task2', 'task5', 'task1'}), - ('task5', 'abort-all', {'task4', 'task3', 'task2', 'task5', 'task1'}), - ('task1', 'abort-related-flows', {'task4', 'task3', 'task2', 'task5', 'task1'}), - ('task2', 'abort-related-flows', {'task4', 'task3', 'task2', 'task5', 'task1'}), - ('task5', 'abort-related-flows', {'task4', 'task3', 'task2', 'task5', 'task1'}), - ('task1', 'continue', set()), - ('task2', 'continue', set()), - ('task5', 'continue', set()), - ]) -def test_cancel_dependant_tasks(task: str, cancel_policy: str, to_be_canceled: set, dag: DAG): - """Test DAG.cancel_dependant_tasks method. - Check the to_be_canceled set after calling the cancel_dependant_tasks method with a parameterized task_collection - in reverse True and False test cases. - - Parameters - ---------- - task : str - Parameterized task name. - cancel_policy : str - Parameterized cancel policy using valid values (abort-all, abort-related-flows, continue). - to_be_canceled : set - [description] - dag : DAG - DAG fixture defined in conftest.py parameterized with complete object state - (task_collection, reverse, finished_task_status sets). The patch false parameter avoids patching the - DAG.__build_dag' and DAG.__create_execution_plan methods - """ - dag.cancel_dependant_tasks(task, cancel_policy=cancel_policy) - assert dag.to_be_canceled == to_be_canceled - - -@pytest.mark.parametrize('dag, exec_plan', - [( - {'task_collection': [ - {'task': 'task1', }, - {'task': 'task2', 'depends-on': ['task1']}, - {'task': 'task3', 'depends-on': ['task1']}, - {'task': 'task4', 'depends-on': ['task1']}, - {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']} - ], - 'patch': False}, - {"task5": {"task2": {"task1": {}}, - "task3": {"task1": {}}, - "task4": {"task1": {}}}} - ), - ( - { - 'task_collection': [ - {'task': 'task1', }, - {'task': 'task2', 'depends-on': ['task1']}, - {'task': 'task3', 'depends-on': ['task1']}, - {'task': 'task4', 'depends-on': ['task1']}, - {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}, - {'task': 'task6', 'depends-on': ['task5']} - ], - 'patch': False - }, - {"task6": {"task5": {"task2": {"task1": {}}, - "task3": {"task1": {}}, - "task4": {"task1": {}}}}} - ) - ], - indirect=['dag']) -def test_create_execution_plan(exec_plan: dict, dag: DAG): - """Test DAG._create_execution_plan method. - This private method is executed by the constructor. In this Test, - the results are left in the execution_plan instance variable. - - Parameters - ---------- - exec_plan : dict - execution plan. - dag : DAG - DAG fixture defined in conftest.py. - """ - assert dag.execution_plan == exec_plan diff --git a/deployability/modules/build/lib/workflow_engine/tests/test_schema_validator.py b/deployability/modules/build/lib/workflow_engine/tests/test_schema_validator.py deleted file mode 100644 index 25a45db95a..0000000000 --- a/deployability/modules/build/lib/workflow_engine/tests/test_schema_validator.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright (C) 2015-2021, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 -"""SchemaValidator unit tests.""" -import uuid -import random -from pathlib import Path -from unittest.mock import MagicMock, call, patch -import json -from ruamel.yaml import YAML -import pytest -from jsonschema.exceptions import ValidationError, UnknownType - -from workflow_engine.schema_validator import SchemaValidator - -@pytest.mark.parametrize('logger_mock', - [{'logger_to_patch':'workflow_engine.schema_validator.logger'}], - indirect=True) -def test_schema_validator_constructor(logger_mock: MagicMock): - """Test SchemaValidator constructor normal flow. - Check the state of the SchemaValidator instance variables after creation. - - Parameters - ---------- - logger_mock : MagicMock - logger fixture to check debug calls - """ - schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' - with open(schema_path, 'r') as schema_file: - schema_data = json.load(schema_file) - - wf_file_path = Path(__file__).parent / 'data' / 'wf-ok.yaml' - with open(wf_file_path, 'r') as file: - yaml = YAML(typ='safe', pure=True) - yaml_data = yaml.load(file) - - validator = SchemaValidator(schema_path, wf_file_path) - assert validator.schema_data == schema_data - assert validator.yaml_data == yaml_data - calls = [call(f"Loading schema file: {schema_path}"), - call(f"Loading yaml file: {wf_file_path}")] - logger_mock.debug.assert_has_calls(calls) - - -def test_schema_validator_constructor_ko(): - """"Test SchemaValidator constructor error flows. - Check if the FileNotFoundError is raisen with a random file name. - """ - schema_path = str(uuid.UUID(int=random.randint(0, 2^32))) - with pytest.raises(FileNotFoundError, match=f'File "{schema_path}" not found.'): - SchemaValidator(schema_path, schema_path) - - -def test_preprocess_data(): - """Test SchemaValidator preprocess_data. - Check if the preprocess_data method does not raise exceptions with a valid file. - """ - schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' - wf_file_path = Path(__file__).parent / 'data' / 'wf-ok.yaml' - validator = SchemaValidator(schema_path, wf_file_path) - validator.preprocess_data() - - -@pytest.mark.parametrize('workflow_file, error_msg', - [('wf-ko-no-path-on-do.yaml', - "Missing required properties in 'with' for task: {'task': 'run-agent-tests-{agent}'"), - ('wf-ko-no-path-on-cleanup.yaml', - "Missing required properties in 'with' for task: {'task': 'allocate-manager'"),]) -def test_preprocess_data_ko(workflow_file: str, error_msg: str): - """Test SchemaValidator preprocess_data error flow. - Check the ValidationError generated by invalid yml files. - - Parameters - ---------- - workflow_file : str - workflow yml file name. - error_msg : str - Error message to check - """ - schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' - wf_file_path = Path(__file__).parent / 'data' / workflow_file - validator = SchemaValidator(schema_path, wf_file_path) - with pytest.raises(ValidationError, match=error_msg): - validator.preprocess_data() - - -def test_validate_schema(): - """Test SchemaValidator validate_schema with a valid yml file.""" - schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' - wf_file_path = Path(__file__).parent / 'data' / 'wf-ok.yaml' - validator = SchemaValidator(schema_path, wf_file_path) - validator.validateSchema() - - -@pytest.mark.parametrize('logger_mock', - [{'logger_to_patch':'workflow_engine.schema_validator.logger'}], - indirect=True) -def test_validate_schema_ko(logger_mock: MagicMock): - """Test SchemaValidator validate_schema error flows. - Check the messages sent to the log when an invalid workflow yml file is used. - - Parameters - ---------- - logger_mock : MagicMock - logger fixture defined in conftest.py - """ - schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' - wf_file_path = Path(__file__).parent / 'data' / 'wf-ko-schema-error.yaml' - validator = SchemaValidator(schema_path, wf_file_path) - validator.validateSchema() - logger_mock.error.assert_called_once() - assert 'Schema validation error:' in logger_mock.error.call_args[0][0] - - logger_mock.error.reset_mock() - validator = SchemaValidator(schema_path, wf_file_path) - with patch('workflow_engine.schema_validator.jsonschema.validate', side_effect=UnknownType): - validator.validateSchema() - logger_mock.error.assert_called_once() - assert 'Unexpected error at schema validation:' in logger_mock.error.call_args[0][0] diff --git a/deployability/modules/build/lib/workflow_engine/tests/test_task.py b/deployability/modules/build/lib/workflow_engine/tests/test_task.py deleted file mode 100644 index 604c8e2926..0000000000 --- a/deployability/modules/build/lib/workflow_engine/tests/test_task.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -from typing import List, Tuple -from subprocess import CompletedProcess, CalledProcessError -from unittest.mock import patch, MagicMock, call -import pytest - -from workflow_engine.task import ProcessTask - -@pytest.fixture -def task(request) -> ProcessTask: - """Shared fixture to create task.""" - task_name, task_parms = request.param - return ProcessTask(task_name=task_name, task_parameters=task_parms) - - -@pytest.mark.parametrize("task", [('task1', {"param1": "value1"})], indirect=True) -def test_process_task_constructor(task: ProcessTask): - """Test ProcessTask constructor. - Check the task instance varialbes after constructing the ProcessTask. - - Parameters - ---------- - task : ProcessTask - The task fixture. - """ - assert task.task_name == 'task1' - assert task.task_parameters == {"param1": "value1"} - - -@pytest.mark.parametrize("task", [('task1', {"path": "/mypath", - "args": [{"param1": "value1"}]}), - ('task2', {"path": "/mypath", - "args": ["param1"]}), - ('task3', {"path": "/mypath", - "args": ["param1", "param2"]}), - ('task4', {"path": "/mypath", - "args": ["param1", {"param2": "value2"}]}), - ('task5', {"path": "/mypath", - "args": [{"param1": "value1"}, {"param2": "value2"}]}) - ], indirect=True) -@patch("workflow_engine.task.logger") -def test_process_task_execute(logger_mock: MagicMock, task: ProcessTask): - """Test ProcessTask.execute method normal flow. - Check that ProcessTask.execute calls subprocess.run to run commands with the defined parameters. The - task mock in conftest.py is used to thy diferent command argument formats. - - Parameters - ---------- - logger_mock : MagicMock - The logger mock defined in conftest.py - task : ProcessTask - The task fixture. - """ - results = {} - results["task1"] = {"parm_list": [task.task_parameters['path'], "--param1=value1"]} - results["task2"] = {"parm_list": [task.task_parameters['path'], "param1"]} - results["task3"] = {"parm_list": [task.task_parameters['path'], "param1", "param2"]} - results["task4"] = {"parm_list": [task.task_parameters['path'], "param1", - "--param2=value2"]} - results["task5"] = {"parm_list": [task.task_parameters['path'], "--param1=value1", - "--param2=value2"]} - result = CompletedProcess(args=results[task.task_name]["parm_list"][1:], - returncode=0, stdout="command output", - stderr="") - debug_calls = [call(f'Running task "{task.task_name}" with arguments: ' - f'{results[task.task_name]["parm_list"][1:]}')] - with patch("workflow_engine.task.subprocess.run", return_value=result) as proc_run_mock, \ - patch.object(logger_mock, "debug") as logger_debug_mock: - debug_calls.append(call(f'Finished task "{task.task_name}" execution ' - f'with result:\n{str(result.stdout)}')) - task.execute() - - logger_debug_mock.assert_has_calls(debug_calls) - proc_run_mock.assert_called_once_with(results[task.task_name]['parm_list'], check=True, - capture_output=True, text=True) - - -@pytest.mark.parametrize("task", [('task1', {"path": "/mypath", - "args": [{"param1": "value1"}]}), - ], indirect=True) -@pytest.mark.parametrize("subproc_retval", [1, 0]) -@pytest.mark.parametrize("subproc_run_exc", [(True, KeyboardInterrupt, "KeyboardInterrupt error"), - (True, Exception, "Other Error")]) -def test_process_task_execute_ko(subproc_retval: int, subproc_run_exc: List[Tuple], task: ProcessTask): - """Test ProcessTask.execute method exception flows. - Check ProcessTask.execute flow when the subprocess.run returns errors. - - Parameters - ---------- - subproc_retval : int - return code from subprocess.run - subproc_run_exc : bool - Tuple - task : ProcessTask - The task fixture. - """ - raise_exc, exception_type, stderr = subproc_run_exc - if exception_type is Exception: - match = f"Error executing process task {stderr}" - else: - match = "Error executing process task with keyboard interrupt." - result = CompletedProcess(args=["--param1=value1"], - returncode=subproc_retval, stdout="command output", - stderr=stderr) - with patch("workflow_engine.task.subprocess.run", return_value=result) as proc_run_mock: - if raise_exc: - proc_run_mock.side_effect = CalledProcessError(returncode=1, - cmd=task.task_parameters['path'], - stderr=stderr) - - with pytest.raises(exception_type, match=match): - task.execute() diff --git a/deployability/modules/build/lib/workflow_engine/tests/test_workflow_file.py b/deployability/modules/build/lib/workflow_engine/tests/test_workflow_file.py deleted file mode 100644 index a3b411899e..0000000000 --- a/deployability/modules/build/lib/workflow_engine/tests/test_workflow_file.py +++ /dev/null @@ -1,271 +0,0 @@ -# Copyright (C) 2015-2021, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 -"""WorkflowFile unit tests.""" -from typing import Any, List -from unittest.mock import patch, MagicMock, call, mock_open -import pytest - -from workflow_engine.workflow_processor import WorkflowFile - - -def test_workflow_file_constructor(): - """Test WorkflowFile constructor. - Check the function calls and instance variables after object creation.""" - with patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__validate_schema") as validate_mock, \ - patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__load_workflow", - return_value={'data': 'data'}) as load_mock, \ - patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__process_workflow") as process_mock, \ - patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__static_workflow_validation") \ - as static_validation_mock: - wf = WorkflowFile(workflow_file_path='my_file.yaml', schema_path='my_schema.yaml') - assert wf.schema_path == 'my_schema.yaml' - validate_mock.assert_called_once_with('my_file.yaml') - load_mock.assert_called_once_with('my_file.yaml') - assert wf.workflow_raw_data == {'data': 'data'} - process_mock.assert_called_once() - static_validation_mock.assert_called_once() - - -@pytest.mark.parametrize('logger_mock', [{}], indirect=True) -def test_workflow_file_validate_schema(logger_mock: MagicMock): - """Test WorkflowFile.__validate_schema. - Check debug messages and function called by the method. - - Parameters - ---------- - logger_mock : MagicMock - The logger fixture defined in conftest.py. - """ - wf = MagicMock() - wf.schema_path = 'my_schema_path.yaml' - workflow_file = 'my_file_path.yaml' - schema_validator = MagicMock() - with patch('workflow_engine.workflow_processor.SchemaValidator', - return_value=schema_validator) as schema_validator_mock: - with patch.object(schema_validator, 'preprocess_data') as preprocess_mock, \ - patch.object(schema_validator, 'validateSchema') as validate_schema_mock: - WorkflowFile._WorkflowFile__validate_schema(self=wf, workflow_file=workflow_file) - - logger_mock.debug.assert_called_once_with(f"Validating input file: {workflow_file}") - schema_validator_mock.assert_called_once_with(wf.schema_path, workflow_file) - preprocess_mock.assert_called_once() - validate_schema_mock.assert_called_once() - - -@pytest.mark.parametrize('logger_mock', [{}], indirect=True) -def test_workflow_file_validate_schema_ko(logger_mock: MagicMock): - """Test WorkflowFile.__validate_schema error flow. - Check logged messages and function calls of the method. - - Parameters - ---------- - logger_mock : MagicMock - The logger fixture defined in conftest.py. - """ - wf = MagicMock() - wf.schema_path = 'my_schema_path.yaml' - workflow_file = 'my_file_path.yaml' - file_exc = FileNotFoundError() - with patch('workflow_engine.workflow_processor.SchemaValidator', side_effect=file_exc) as schema_validator_mock, \ - pytest.raises(FileNotFoundError): - WorkflowFile._WorkflowFile__validate_schema(self=wf, workflow_file=workflow_file) - - logger_mock.debug.assert_called_once_with(f"Validating input file: {workflow_file}") - schema_validator_mock.assert_called_once_with(wf.schema_path, workflow_file) - logger_mock.error.assert_called_once_with("Error while validating schema [%s] with error: %s", - wf.schema_path, - file_exc) - - -@pytest.mark.parametrize('logger_mock', [{}], indirect=True) -@patch('builtins.open', new_callable=mock_open, read_data='YAML content') -def test_workflow_file_load_workflow(mock_open: MagicMock, logger_mock: MagicMock): - """Test WorkflowFile.__load_workflow. - Check logged messages and function calls of the method. - - Parameters - ---------- - mock_open : MagicMock - The mock fixture defined in conftest.py. - logger_mock : MagicMock - The logger fixture defined in conftest.py. - """ - wf = MagicMock() - wf.schema_path = 'my_schema_path.yaml' - workflow_file = 'my_file_path.yaml' - mock_open.return_value.__enter__.return_value = mock_open - with patch('workflow_engine.workflow_processor.os.path.exists', return_value=True) as path_exists_mock, \ - patch('workflow_engine.workflow_processor.yaml.safe_load') as safe_load_mock: - WorkflowFile._WorkflowFile__load_workflow(self=wf, file_path=workflow_file) - - path_exists_mock.assert_called_once_with(workflow_file) - logger_mock.debug.assert_called_once_with(f"Loading workflow file: {workflow_file}") - mock_open.assert_called_once_with(workflow_file, 'r', encoding='utf-8') - safe_load_mock.assert_called_once_with(mock_open) - - -@pytest.mark.parametrize('logger_mock', [{}], indirect=True) -@patch('builtins.open', new_callable=mock_open, read_data='YAML content') -def test_workflow_file_load_workflow_ko(mock_open: MagicMock, logger_mock: MagicMock): - """Test WorkflowFile.__load_workflow error flow. - Check if the FileNotFoundError exception is raised by the method. - - Parameters - ---------- - mock_open : MagicMock - unittest mock of the open function - logger_mock : MagicMock - The logger fixture defined in conftest.py - """ - wf = MagicMock() - wf.schema_path = 'my_schema_path.yaml' - workflow_file = 'my_file_path.yaml' - mock_open.return_value.__enter__.return_value = mock_open - with patch('workflow_engine.workflow_processor.os.path.exists', return_value=False) as path_exists_mock, \ - pytest.raises(FileNotFoundError, match=f'File "{workflow_file}" not found.') as file_exc: - WorkflowFile._WorkflowFile__load_workflow(self=wf, file_path=workflow_file) - - -@pytest.mark.parametrize('logger_mock', [{}], indirect=True) -def test_workflow_file_process_workflow(logger_mock: MagicMock): - """Test WorkflowFile.__process_workflow. - Check that the method calls the expand_task method of each task using a lambda as a side effect. - - Parameters - ---------- - logger_mock : MagicMock - The logger fixture defined in conftest.py - """ - variable_list = {'variable_1': 'value_1', 'variable_2': 'value_2'} - task_list = [{'task': 'task1'}, {'task': 'task2'}, {'task': 'task3'}] - expanded_task_list = [{'task': 'task3_1'}, {'task': 'task3_2'}] - wf = MagicMock() - wf.workflow_raw_data = {'tasks': task_list, 'variables': variable_list} - wf._WorkflowFile__expand_task.side_effect = lambda task, variables: [task] + \ - (expanded_task_list if task['task'] == 'task3' else []) - tasks = WorkflowFile._WorkflowFile__process_workflow(wf) - - logger_mock.debug.assert_called_once_with("Process workflow.") - calls = [call(task, variable_list) for task in task_list] - wf._WorkflowFile__expand_task.assert_has_calls(calls) - task_list.extend(expanded_task_list) - assert tasks == task_list - - -@pytest.mark.parametrize('logger_mock', [{}], indirect=True) -def test_workflow_file_process_workflow_ok(logger_mock: MagicMock): - """Test WorkflowFile.__process_workflow error flow. - Check that a ValueError is raised when no task are found in the workflow. - - Parameters - ---------- - logger_mock : MagicMock - The logger fixture defined in conftest.py - """ - wf = MagicMock() - wf.workflow_row_data = { - 'tasks': [] - } - wf.__expand_task.return_value = [] - with pytest.raises(ValueError, match="No tasks found in the workflow."): - tasks = WorkflowFile._WorkflowFile__process_workflow(self=wf) - - logger_mock.debug.assert_called_once_with("Process workflow.") - - -@pytest.mark.parametrize('element, values, return_value', - [({'key_1': 'key_1 {value_1}', 'key_2': 'key_2 {value_2}'}, - {'value_1': 'value_1', 'value_2': 'value_2'}, - {'key_1': 'key_1 value_1', 'key_2': 'key_2 value_2'}), - (['element_1 {value_1}', 'element_2 {value_2}', 'element_3 {value_3}'], - {'value_1': 'value_1', 'value_2': 'value_2', 'value_3': 'value_3'}, - ['element_1 value_1', 'element_2 value_2', 'element_3 value_3']), - ('string_element {value}', {'value': 'value'}, 'string_element value'), - ({1, 2}, None, {1, 2})]) -def test_workflow_file_replace_placeholder(element: Any, values: dict, return_value: Any): - """Test WorkflowFile.__replace_placeholder.""" - wf = MagicMock() - wf._WorkflowFile__replace_placeholders.side_effect = \ - lambda s, e, v: WorkflowFile._WorkflowFile__replace_placeholders(wf, s, e, v) - result = WorkflowFile._WorkflowFile__replace_placeholders(self=wf, element=element, values=values) - assert result == return_value - - -@pytest.mark.parametrize('task, return_value, variables', - [({'task': 'task: {as_variable_1}', 'param': '{as_variable_2}', - 'foreach': [{'variable': 'variable_1', 'as': 'as_variable_1'}, - {'variable': 'variable_2', 'as': 'as_variable_2'}]}, - [{"task": "task: value_1_1", 'param': 'value_2_1', - "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, - {"variable": "variable_2", "as": "as_variable_2"}]}, - {"task": "task: value_1_1", 'param': 'value_2_2', - "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, - {"variable": "variable_2", "as": "as_variable_2"}]}, - {"task": "task: value_1_2", 'param': 'value_2_1', - "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, - {"variable": "variable_2", "as": "as_variable_2"}]}, - {"task": "task: value_1_2", 'param': 'value_2_2', - "foreach": [{"variable": "variable_1", "as": "as_variable_1"}, - {"variable": "variable_2", "as": "as_variable_2"}]}], - {'variable_1': ['value_1_1', 'value_1_2'], - 'variable_2': ['value_2_1', 'value_2_2']}), - ({'task': 'task1', 'placeholder': 'placeholder {variable_1}'}, - [{'task': 'task1', 'placeholder': 'placeholder value_1'}], - {'variable_1': 'value_1'}) - ]) -def test_workflow_file_expand_task(task: dict, return_value: dict, variables: dict): - """Test WorkflowFile.___expand_task. - Check the if the expand_task return dictionary is ok. - - Parameters - ---------- - task : dict - A task dictionary used as the input parameter for the expand_task method. - return_value : dict - The expected return value. - variables : dict - The variables dictionary used as the input parameter for the expand_task method. - """ - def side_effect(s, e, v = None): - return WorkflowFile._WorkflowFile__replace_placeholders(wf, s, e, v) - wf = MagicMock() - wf._WorkflowFile__replace_placeholders.side_effect = side_effect - - tasks = WorkflowFile._WorkflowFile__expand_task(wf, task, variables) - assert tasks == return_value - - -def test_workflow_file_static_workflow_validation(): - """Test WorkflowFile.__static_workflow_validation. - Check if no exception is raised with a valid task_collection""" - wf = MagicMock() - wf.task_collection = [{"task": "task 1", "param": "1"}, - {"task": "task 2", "param": "2", 'depends-on': ['task 1']} - ] - WorkflowFile._WorkflowFile__static_workflow_validation(wf) - - -@pytest.mark.parametrize('task_collection, error_msg', [ - ([{"task": "task 1", "param": "1"}, - {"task": "task 1", "param": "2", 'depends-on': ['task 1']}], - 'Duplicated task names: task 1'), - ([{"task": "task 1", "param": "1", 'depends-on': ['task 3', 'task 4']}, - {"task": "task 2", "param": "2", 'depends-on': ['task 3']}], - 'Tasks do not exist: task 3, task 4') -]) -def test_workflow_file_static_workflow_validation_ko(task_collection: List[dict], error_msg: str): - """Test WorkflowFile.__static_workflow_validation. - Check if the validation raises ValueError exceptions with invalid task collections. - - Parameters - ---------- - task_collection : List[dict] - List of tasks - error_msg : str - Expected exception errors - """ - wf = MagicMock() - wf.task_collection = task_collection - with pytest.raises(ValueError, match=error_msg): - WorkflowFile._WorkflowFile__static_workflow_validation(wf) diff --git a/deployability/modules/build/lib/workflow_engine/tests/test_workflow_processor.py b/deployability/modules/build/lib/workflow_engine/tests/test_workflow_processor.py deleted file mode 100644 index a0639c6ea0..0000000000 --- a/deployability/modules/build/lib/workflow_engine/tests/test_workflow_processor.py +++ /dev/null @@ -1,385 +0,0 @@ -# Copyright (C) 2015-2021, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 -"""WorkflowProcessor Unit tests""" -import time -import json -from concurrent.futures import Future -from unittest.mock import patch, MagicMock, call -import pytest - -from workflow_engine.workflow_processor import WorkflowProcessor, DAG -from workflow_engine.task import ProcessTask, TASKS_HANDLERS - - -@pytest.mark.parametrize('workflow_file, dry_run, threads, log_level, schema_file', - [('workflow.yaml', False, 1, 'info', 'schema.yaml'), - ('workflow.yaml', True, 1, 'debug', 'schema.yaml'), - ('workflow.yaml', True, 1, 'debug', None), - ]) -@patch("workflow_engine.workflow_processor.logger") -@patch("workflow_engine.workflow_processor.WorkflowFile") -def test_workflow_processor_constructor(file_mock: MagicMock, logger_mock: MagicMock, - workflow_file:str, dry_run: bool, threads: int, log_level: str, - schema_file:str): - """Test WorkflowProcessor constructor. - Check the workflowprocessor instance variables after construction. - - Parameters - ---------- - file_mock : MagicMock - Mock of a WorkflowFile Constructor. - logger_mock : MagicMock - The logger fixture defined in conftest.py. - workflow_file : str - Path to workflow yaml file. - dry_run : bool - Define if the workflow will run or not - threads : int - number of threads - log_level : str - Log level string - schema_file : str - Path to the schema.yml file - """ - task_collection = [ - {'task': 'task1', 'path': '/cmd1', 'args': [{"param1": "value1"}]}, - {'task': 'task2', 'path': '/cmd2', 'args': [{"param1": "value1"}]}, - {'task': 'task3', 'path': '/cmd3', 'args': [{"param1": "value1"}]}, - ] - workflow_file_instance = file_mock.return_value - workflow_file_instance.task_collection = task_collection - with patch.object(logger_mock, 'setLevel') as set_level_mock: - processor = WorkflowProcessor(workflow_file, dry_run, threads, log_level, schema_file) - set_level_mock.assert_called_once_with(log_level) - file_mock.assert_called_once_with(workflow_file, schema_file) - assert processor.task_collection == task_collection - assert processor.dry_run == dry_run - assert processor.threads == threads - - -@pytest.mark.parametrize('logger_mock, w_processor, dag, action, should_be_canceled', - [({}, {}, {}, 'custom_action', True), - ({}, {}, {}, 'custom_action', False),], - indirect=["dag", "w_processor", "logger_mock"]) -def test_execute_task(logger_mock: MagicMock, w_processor: WorkflowProcessor, dag: DAG, action: str, - should_be_canceled: bool): - """Test WorflowProcessor.execute_task function normal - Check the execute_task method when log messages and function calls when the should_be_canceled return value - is True or False. - - Parameters - ---------- - logger_mock : MagicMock - The logger fixture defined in conftest.py. - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. - dag : DAG - The dag fixture defined in conftest.py. - action : str - action name - should_be_canceled : bool - should_be_canceled method patched return value. - - Returns - ------- - [type] - [description] - """ - start_time = time.time() - elapsed_time = 10 - def time_side_effect(): - nonlocal start_time - start_time=start_time + elapsed_time - return start_time - - task = {'task': 'task1'} - p_task = ProcessTask('task1', {}) - with patch.object(dag, 'should_be_canceled', return_value=should_be_canceled) as should_be_canceled_mock, \ - patch.object(w_processor, 'create_task_object', return_value=p_task) as create_task_mock, \ - patch.object(dag, 'set_status') as set_status_mock, \ - patch.object(p_task, 'execute') as exec_mock, \ - patch('workflow_engine.workflow_processor.time') as time_mock: - time_mock.time = MagicMock(side_effect=time_side_effect) - w_processor.execute_task(dag=dag, task=task, action=action) - should_be_canceled_mock.assert_called_once_with(task['task']) - if should_be_canceled: - logger_mock.warning.assert_called_once_with( - "[%s] Skipping task due to dependency failure.", task['task']) - set_status_mock.assert_called_once_with(task['task'], 'canceled') - else: - create_task_mock.assert_called_once_with(task, action) - exec_mock.assert_called_once() - logger_mock.info.assert_has_calls([ - call("[%s] Starting task.", task['task']), - call("[%s] Finished task in %.2f seconds.", task['task'], elapsed_time) - ] - ) - set_status_mock.assert_called_once_with(task['task'], 'successful') - - -@pytest.mark.parametrize('on_error', [None, 'abort-all']) -@pytest.mark.parametrize('logger_mock, w_processor, dag, exception', - [({}, {}, {}, KeyboardInterrupt), - ({}, {}, {}, Exception)], - indirect=["dag", "w_processor", "logger_mock"]) -def test_execute_task_ko(logger_mock: MagicMock, w_processor: WorkflowProcessor, dag: DAG, exception, - on_error: str): - """Test WorflowProcessor.execute_task function, error flows. - Check logged messages, set_status call and cancel_dependant_tasks in the failure flow. - - Parameters - ---------- - logger_mock : MagicMock - The logger fixture defined in conftest.py. - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. - dag : DAG - The dag fixture defined in conftest.py. - exception : [type] - Expected exception. - on_error : str - set on-error of the task. - """ - task = {'task': 'task1'} - task.update({'on-error': on_error} if on_error else {}) - p_task = ProcessTask('task1', {}) - exc = exception() - with patch.object(dag, 'should_be_canceled', return_value=False), \ - patch.object(w_processor, 'create_task_object', return_value=p_task), \ - patch.object(dag, 'set_status') as set_status_mock, \ - patch.object(p_task, 'execute', side_effect=exc), \ - patch('workflow_engine.workflow_processor.time'), \ - patch.object(dag, 'cancel_dependant_tasks') as cancel_mock, \ - pytest.raises(expected_exception=exception): - w_processor.execute_task(dag=dag, task=task, action='action') - - logger_mock.error.assert_called_once_with("[%s] Task failed with error: %s.", task['task'], exc) - set_status_mock.assert_called_once_with(task['task'], 'failed') - cancel_mock.assert_called_once_with(task['task'], on_error if on_error else 'abort-related-flows') - - -@pytest.mark.parametrize('task_type', ['process', 'dummy', 'dummy-random']) -@pytest.mark.parametrize('w_processor', [{}], indirect=True) -def test_create_task_object(w_processor: WorkflowProcessor, task_type: str): - """Test WorkfowProcess.create_task_object function normal flow. - Check the task type returned by the method. - - Parameters - ---------- - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. - task_type : str - type of task - """ - task_dict = {'task': 'task1', 'action': {'this': task_type, 'with': {'param'}}} - task = w_processor.create_task_object(task_dict, 'action') - assert isinstance(task, TASKS_HANDLERS.get(task_type)) - - -@pytest.mark.parametrize('w_processor', [{}], indirect=True) -def test_create_task_object_ko(w_processor: WorkflowProcessor): - """Test WorkfowProcess.create_task_object function error flow. - Check that the create_task_object raise a ValueError exception for invalid types.} - - Parameters - ---------- - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. - """ - task_type = 'unknown' - task_dict = {'task': 'task1', 'action': {'this': task_type, 'with': {'param'}}} - with pytest.raises(ValueError, match=f"Unknown task type '{task_type}'."): - w_processor.create_task_object(task_dict, 'action') - - -@pytest.mark.parametrize('reverse', [False, True]) -@pytest.mark.parametrize('logger_mock, w_processor, dag',[({}, {}, {})], - indirect=["dag", "w_processor", "logger_mock"]) -@patch('workflow_engine.workflow_processor.concurrent.futures.ThreadPoolExecutor') -def test_execute_tasks_parallel(executor_mock: MagicMock, logger_mock: MagicMock, w_processor: WorkflowProcessor, - dag: DAG, reverse: bool): - """Test WorkfowProcess.execute_task_parallel function. - Check if the logged messages and function calls of the method with reverse True and False cases. - - Parameters - ---------- - executor_mock : MagicMock - Mock of the ThreadPoolExecutor. - logger_mock : MagicMock - The logger fixture defined in conftest.py. - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. - dag : DAG - The dag fixture defined in conftest.py. - reverse : bool - Parameterized value for the execute__tasks_parallel reverse parameter. - """ - futures = MagicMock() - futures.values = MagicMock(return_value = (x := MagicMock())) - y = MagicMock() - y.__enter__ = MagicMock(return_value=y) - executor_mock.return_value = y - with patch('workflow_engine.workflow_processor.concurrent.futures.wait') as wait_mock, \ - patch.object(w_processor, 'generate_futures', return_value=futures) as gen_futures_mock: - w_processor.execute_tasks_parallel(dag, reverse=reverse) - logger_mock.info.assert_called_once_with("Executing tasks in parallel.") - executor_mock.assert_called_once_with(max_workers=w_processor.threads) - wait_mock.assert_called_once_with(x) - gen_futures_mock.assert_called_once_with(dag, y, reverse) - - -@pytest.mark.parametrize('reverse', [False, True]) -@pytest.mark.parametrize('logger_mock, w_processor, dag',[({}, {}, {})], - indirect=["dag", "w_processor", "logger_mock"]) -@patch('workflow_engine.workflow_processor.concurrent.futures.ThreadPoolExecutor') -def test_execute_tasks_parallel_ko(executor_mock: MagicMock, logger_mock: MagicMock, w_processor: WorkflowProcessor, - dag: DAG, reverse: bool): - """Test WorkfowProcess.execute_task_parallel function error flow. - Check function call message loggin and calls when the KeyboardInterrupt is generated while waiting the subprocess - to finish execution. - - Parameters - ---------- - executor_mock : MagicMock - not used, just patched - logger_mock : MagicMock - The logger fixture defined in conftest.py. - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. - dag : DAG - The dag fixture defined in conftest.py. - reverse : bool - Parameterized value for the execute__tasks_parallel reverse parameter. - """ - execute_parallel_mock = MagicMock() - def patch_recursive_and_return_exception(_): - w_processor.execute_tasks_parallel = execute_parallel_mock - raise KeyboardInterrupt() - - with patch('workflow_engine.workflow_processor.concurrent.futures.wait', - side_effect=patch_recursive_and_return_exception), \ - patch.object(w_processor, 'generate_futures'): - w_processor.execute_tasks_parallel(dag, reverse=reverse) - logger_mock.info.assert_called_once_with("Executing tasks in parallel.") - logger_mock.error.assert_called_once_with("User interrupt detected. Aborting execution...") - execute_parallel_mock.assert_called_once_with(dag, reverse=True) - - -@pytest.mark.parametrize('w_processor', - [{'task_collection': [ - {'task': 'task1'}, - {'task': 'task2', 'depends-on': ['task1']}, - {'task': 'task3', 'depends-on': ['task1']}, - {'task': 'task4', 'depends-on': ['task1']}, - {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],}, - ], - indirect=True) -def test_generate_futures(w_processor: WorkflowProcessor): - """Test WorkfowProcess.generate_futures function without reverse. - Check the futures returned by the method. - - Parameters - ---------- - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. - """ - def submit_execute_task_side_effect(_, dag: DAG, task, __): - dag.set_status(task['task'], 'successful') - return Future() - - executor = MagicMock() - executor.submit.side_effect=submit_execute_task_side_effect - dag = DAG(task_collection=w_processor.task_collection) - futures = w_processor.generate_futures(dag, executor=executor) - assert len(futures) == len(w_processor.task_collection) and \ - all(isinstance(element, Future) for element in futures.values()) - - -@pytest.mark.parametrize('w_processor', - [{'task_collection': [ - {'task': 'task1'}, - {'task': 'task2', 'depends-on': ['task1']}, - {'task': 'task3', 'depends-on': ['task1']}, - {'task': 'task4', 'depends-on': ['task1']}, - {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],}, - ], - indirect=True) -def test_generate_futures_reverse(w_processor: WorkflowProcessor): - """Test WorkfowProcess.generate_futures function with reverse True. - Check that set_status with successful is called for the tasks. - - Parameters - ---------- - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. - """ - - def set_status_side_effect(task, status): - dag.finished_tasks_status[status].add(task) - dag.dag.done(task) - - executor = MagicMock() - dag = DAG(task_collection=w_processor.task_collection, reverse=True) - with patch.object(dag, 'set_status', side_effect=set_status_side_effect) as set_status_mock: - futures = w_processor.generate_futures(dag, executor=executor, reverse=True) - calls = [call(task['task'], 'successful') for task in w_processor.task_collection] - set_status_mock.assert_has_calls(calls, any_order=True) - - -@pytest.mark.parametrize('dry_run', [False, True]) -@pytest.mark.parametrize('logger_mock, w_processor', - [({}, { - 'task_collection': [ - {'task': 'task1'}, - {'task': 'task2', 'depends-on': ['task1']}, - {'task': 'task3', 'depends-on': ['task1']}, - {'task': 'task4', 'depends-on': ['task1']}, - {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],})], - indirect=True) -def test_run(logger_mock: MagicMock, w_processor: WorkflowProcessor, dry_run: bool): - """Test WorkfowProcess.run function. - Check log message and execute_tasks_parallel call. - - Parameters - ---------- - logger_mock : MagicMock - The logger fixture defined in conftest.py. - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. - dry_run : bool - Parameterized value to test the run method. - """ - def dag_constructor(_, reverse=False): - return reverse_dag if reverse else dag - - w_processor.dry_run = dry_run - dag = DAG(w_processor.task_collection) - reverse_dag = DAG(w_processor.task_collection, reverse=True) - with patch.object(w_processor, 'execute_tasks_parallel') as exec_tasks_mock, \ - patch('workflow_engine.workflow_processor.DAG', side_effect=dag_constructor) as dag_mock: - w_processor.run() - if dry_run: - dag_mock.assert_called_once_with(w_processor.task_collection) - logger_mock.info.assert_called_once_with("Execution plan: %s", json.dumps(dag.get_execution_plan(), indent=2)) - else: - logger_mock.info.assert_has_calls([call("Executing DAG tasks."), call("Executing Reverse DAG tasks.")]) - exec_tasks_mock.assert_has_calls([call(dag), call(reverse_dag, reverse=True)]) - dag_mock.assert_has_calls([call(w_processor.task_collection), call(w_processor.task_collection, reverse=True)]) - - -@pytest.mark.parametrize('logger_mock, w_processor', [({}, {})], indirect=['logger_mock', 'w_processor']) -def test_handle_interrupt(logger_mock: MagicMock, w_processor: WorkflowProcessor): - """Test WorkfowProcess.handle_interrupt function. - Check logging when the handle_interrupt is called. - - Parameters - ---------- - logger_mock : MagicMock - The logger fixture defined in conftest.py. - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. - """ - with pytest.raises(KeyboardInterrupt, match="User interrupt detected. End process..."): - w_processor.handle_interrupt(0, 0) - logger_mock.error.assert_called_once_with("User interrupt detected. End process...") diff --git a/deployability/modules/build/lib/workflow_engine/workflow_processor.py b/deployability/modules/build/lib/workflow_engine/workflow_processor.py deleted file mode 100755 index b8177b2129..0000000000 --- a/deployability/modules/build/lib/workflow_engine/workflow_processor.py +++ /dev/null @@ -1,385 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - -import concurrent.futures -import graphlib -import json -import time -import yaml -import os - -from pathlib import Path -from itertools import product - -from workflow_engine.logger.logger import logger -from workflow_engine.schema_validator import SchemaValidator -from workflow_engine.task import Task, TASKS_HANDLERS - -class WorkflowFile: - """Class for loading and processing a workflow file.""" - schema_path = Path(__file__).parent / 'schemas' / 'schema_v1.json' - - def __init__(self, workflow_file_path: Path | str, schema_path: Path | str = None) -> None: - self.schema_path = schema_path or self.schema_path - self.__validate_schema(workflow_file_path) - self.workflow_raw_data = self.__load_workflow(workflow_file_path) - self.task_collection = self.__process_workflow() - self.__static_workflow_validation() - - def __validate_schema(self, workflow_file: Path | str) -> None: - """ - Validate the workflow file against the schema. - - Args: - workflow_file (Path | str): Path to the workflow file. - """ - try: - logger.debug(f"Validating input file: {workflow_file}") - validator = SchemaValidator(self.schema_path, workflow_file) - validator.preprocess_data() - validator.validateSchema() - except Exception as e: - logger.error("Error while validating schema [%s] with error: %s", self.schema_path, e) - raise - - def __load_workflow(self, file_path: str) -> dict: - """ - Load the workflow data from a file. - - Args: - file_path (str): Path to the workflow file. - - Returns: - dict: Workflow data. - """ - - if not os.path.exists(file_path): - raise FileNotFoundError(f'File "{file_path}" not found.') - - logger.debug(f"Loading workflow file: {file_path}") - - with open(file_path, 'r', encoding='utf-8') as file: - return yaml.safe_load(file) - - def __process_workflow(self): - """Process the workflow and return a list of tasks.""" - logger.debug("Process workflow.") - task_collection = [] - variables = self.workflow_raw_data.get('variables', {}) - for task in self.workflow_raw_data.get('tasks', []): - task_collection.extend(self.__expand_task(task, variables)) - - if not task_collection: - raise ValueError("No tasks found in the workflow.") - return task_collection - - def __replace_placeholders(self, element: str, values: dict, parent_key: str = None): - """ - Recursively replace placeholders in a dictionary or list. - - Args: - element (Any): The element to process. - values (dict): The values to replace placeholders. - parent_key (str): The parent key for nested replacements. - - Returns: - Any: The processed element. - """ - if isinstance(element, dict): - return {key: self.__replace_placeholders(value, values, key) for key, value in element.items()} - if isinstance(element, list): - return [self.__replace_placeholders(sub_element, values, parent_key) for sub_element in element] - if isinstance(element, str): - return element.format_map(values) - return element - - def __expand_task(self, task: dict, variables: dict): - """ - Expand a task with variable values. - - Args: - task (dict): The task to expand. - variables (dict): Variable values. - - Returns: - List[dict]: List of expanded tasks. - """ - expanded_tasks = [] - - if 'foreach' in task: - loop_variables = task.get('foreach', [{}]) - - variable_names = [loop_variable_data.get('variable') for loop_variable_data in loop_variables] - as_identifiers = [loop_variable_data.get('as') for loop_variable_data in loop_variables] - - variable_values = [variables.get(name, []) for name in variable_names] - - for combination in product(*variable_values): - variables_with_items = {**variables, **dict(zip(as_identifiers, combination))} - expanded_tasks.append(self.__replace_placeholders(task, variables_with_items)) - else: - expanded_tasks.append(self.__replace_placeholders(task, variables)) - - return expanded_tasks - - def __static_workflow_validation(self): - """Validate the workflow against static criteria.""" - def check_duplicated_tasks(self): - """Validate task name duplication.""" - task_name_counts = {task['task']: 0 for task in self.task_collection} - - for task in self.task_collection: - task_name_counts[task['task']] += 1 - - duplicates = [name for name, count in task_name_counts.items() if count > 1] - - if duplicates: - raise ValueError(f"Duplicated task names: {', '.join(duplicates)}") - - def check_not_existing_tasks(self): - """Validate task existance.""" - task_names = {task['task'] for task in self.task_collection} - - for dependencies in [task.get('depends-on', []) for task in self.task_collection]: - non_existing_dependencies = [dependency for dependency in dependencies if dependency not in task_names] - if non_existing_dependencies: - raise ValueError(f"Tasks do not exist: {', '.join(non_existing_dependencies)}") - - validations = [check_duplicated_tasks, check_not_existing_tasks] - for validation in validations: - validation(self) - - -class DAG(): - """Class for creating a dependency graph.""" - def __init__(self, task_collection: list, reverse: bool = False): - self.task_collection = task_collection - self.reverse = reverse - self.dag, self.dependency_tree = self.__build_dag() - self.to_be_canceled = set() - self.finished_tasks_status = { - 'failed': set(), - 'canceled': set(), - 'successful': set(), - } - self.execution_plan = self.__create_execution_plan(self.dependency_tree) - self.dag.prepare() - - def is_active(self) -> bool: - """Check if the DAG is active.""" - return self.dag.is_active() - - def get_available_tasks(self) -> list: - """Get the available tasks.""" - return self.dag.get_ready() - - def get_execution_plan(self) -> dict: - """Get the execution plan.""" - return self.execution_plan - - def set_status(self, task_name: str, status: str): - """Set the status of a task.""" - self.finished_tasks_status[status].add(task_name) - self.dag.done(task_name) - - def should_be_canceled(self, task_name: str) -> bool: - """Check if a task should be canceled.""" - return task_name in self.to_be_canceled - - def __build_dag(self): - """Build a dependency graph for the tasks.""" - dependency_dict = {} - dag = graphlib.TopologicalSorter() - - for task in self.task_collection: - task_name = task['task'] - dependencies = task.get('depends-on', []) - - if self.reverse: - for dependency in dependencies: - dag.add(dependency, task_name) - else: - dag.add(task_name, *dependencies) - - dependency_dict[task_name] = dependencies - - return dag, dependency_dict - - def cancel_dependant_tasks(self, task_name, cancel_policy) -> None: - """Cancel all tasks that depend on a failed task.""" - def get_all_task_set(tasks): - task_set = set() - - for task, sub_tasks in tasks.items(): - task_set.add(task) - task_set.update(get_all_task_set(sub_tasks)) - - return task_set - - if cancel_policy == 'continue': - return - - not_cancelled_tasks = self.finished_tasks_status['failed'].union(self.finished_tasks_status['successful']) - for root_task, sub_tasks in self.execution_plan.items(): - task_set = get_all_task_set({root_task: sub_tasks}) - if cancel_policy == 'abort-all': - self.to_be_canceled.update(task_set) - elif cancel_policy == 'abort-related-flows': - if task_name in task_set: - self.to_be_canceled.update(task_set - not_cancelled_tasks) - else: - raise ValueError(f"Unknown cancel policy '{cancel_policy}'.") - - def __create_execution_plan(self, dependency_dict: dict) -> dict: - - execution_plan = {} - - def get_root_tasks(dependency_dict: dict) -> set: - """Get root tasks from the dependency dictionary.""" - all_tasks = set(dependency_dict.keys()) - dependent_tasks = set(dep for dependents in dependency_dict.values() for dep in dependents) - return all_tasks - dependent_tasks - - def get_subtask_plan(task_name: str, dependency_dict: dict, level: int = 0) -> dict: - """Create the execution plan recursively as a dictionary.""" - if task_name not in dependency_dict: - return {task_name: {}} - - dependencies = dependency_dict[task_name] - plan = {task_name: {}} - - for dependency in dependencies: - sub_plan = get_subtask_plan(dependency, dependency_dict, level + 1) - plan[task_name].update(sub_plan) - - return plan - - root_tasks = get_root_tasks(dependency_dict) - for root_task in root_tasks: - execution_plan.update(get_subtask_plan(root_task, dependency_dict)) - - return execution_plan - - -class WorkflowProcessor: - """Class for processing a workflow.""" - - def __init__(self, workflow_file: str, dry_run: bool, threads: int, log_level: str = 'INFO', schema_file: Path | str = None): - """ - Initialize WorkflowProcessor. - - Args: - workflow_file (str): Path to the workflow file (YAML format). - dry_run (bool): Display the plan without executing tasks. - threads (int): Number of threads to use for parallel execution. - log_level (str): Log level. - schema_file (Path | str): Path to the schema file (YAML format). - """ - logger.setLevel(log_level) - # Initialize the instance variables. - self.task_collection = WorkflowFile(workflow_file, schema_file).task_collection - self.dry_run = dry_run - self.threads = threads - - def execute_task(self, dag: DAG, task: dict, action) -> None: - """Execute a task.""" - task_name = task['task'] - if dag.should_be_canceled(task_name): - logger.warning("[%s] Skipping task due to dependency failure.", task_name) - dag.set_status(task_name, 'canceled') - else: - try: - task_object = self.create_task_object(task, action) - - logger.info("[%s] Starting task.", task_name) - start_time = time.time() - task_object.execute() - logger.info("[%s] Finished task in %.2f seconds.", task_name, time.time() - start_time) - dag.set_status(task_name, 'successful') - except KeyboardInterrupt as e: - logger.error("[%s] Task failed with error: %s.", task_name, e) - dag.set_status(task_name, 'failed') - dag.cancel_dependant_tasks(task_name, task.get('on-error', 'abort-related-flows')) - raise KeyboardInterrupt - except Exception as e: - logger.error("[%s] Task failed with error: %s.", task_name, e) - dag.set_status(task_name, 'failed') - dag.cancel_dependant_tasks(task_name, task.get('on-error', 'abort-related-flows')) - raise - - - def create_task_object(self, task: dict, action) -> Task: - """Create and return a Task object based on task type.""" - task_type = task[action]['this'] - - task_handler = TASKS_HANDLERS.get(task_type) - - if task_handler is not None: - return task_handler(task['task'], task[action]['with']) - - raise ValueError(f"Unknown task type '{task_type}'.") - - def execute_tasks_parallel(self, dag: DAG, reverse: bool = False) -> None: - """Execute tasks in parallel.""" - logger.info("Executing tasks in parallel.") - try: - with concurrent.futures.ThreadPoolExecutor(max_workers=self.threads) as executor: - futures = self.generate_futures(dag, executor, reverse) - concurrent.futures.wait(futures.values()) - except KeyboardInterrupt: - logger.error("User interrupt detected. Aborting execution...") - self.execute_tasks_parallel(dag, reverse=True) - - def generate_futures(self, dag: DAG, executor, reverse: bool = False): - futures = {} - - while True: - if not dag.is_active(): - break - - for task_name in list(dag.get_available_tasks()): - task = next(t for t in self.task_collection if t['task'] == task_name) - action = 'cleanup' if reverse and 'cleanup' in task else 'do' - if reverse and 'cleanup' not in task: - dag.set_status(task_name, 'successful') - else: - future = executor.submit(self.execute_task, dag, task, action) - futures[task_name] = future - - return futures - - - def run(self) -> None: - """Main entry point.""" - try: - if not self.dry_run: - logger.info("Executing DAG tasks.") - dag = DAG(self.task_collection) - self.execute_tasks_parallel(dag) - - logger.info("Executing Reverse DAG tasks.") - reversed_dag = DAG(self.task_collection, reverse=True) - self.execute_tasks_parallel(reversed_dag, reverse=True) - else: - dag = DAG(self.task_collection) - logger.info("Execution plan: %s", json.dumps(dag.get_execution_plan(), indent=2)) - - - except Exception as e: - logger.error("Error in Workflow: %s", e) - - - def handle_interrupt(self, signum, frame): - logger.error("User interrupt detected. End process...") - raise KeyboardInterrupt("User interrupt detected. End process...") - - def abort_execution(self, futures) -> None: - """Abort the execution of tasks.""" - with concurrent.futures.ThreadPoolExecutor(max_workers=self.threads) as executor: - for future in concurrent.futures.as_completed(futures.values()): - try: - _ = future.result() - except Exception as e: - logger.error("Error in aborted task: %s", e) - executor.shutdown(wait=False, cancel_futures=True) diff --git a/deployability/modules/workflow_engine.egg-info/PKG-INFO b/deployability/modules/workflow_engine.egg-info/PKG-INFO deleted file mode 100644 index 7bc805e400..0000000000 --- a/deployability/modules/workflow_engine.egg-info/PKG-INFO +++ /dev/null @@ -1,8 +0,0 @@ -Metadata-Version: 2.1 -Name: workflow_engine -Version: 1.0 -Summary: Wazuh testing utilities to help programmers automate deployment tests -Home-page: https://github.com/wazuh -Author: Wazuh -Author-email: hello@wazuh.com -License: GPLv2 diff --git a/deployability/modules/workflow_engine.egg-info/SOURCES.txt b/deployability/modules/workflow_engine.egg-info/SOURCES.txt deleted file mode 100644 index 670c24e979..0000000000 --- a/deployability/modules/workflow_engine.egg-info/SOURCES.txt +++ /dev/null @@ -1,49 +0,0 @@ -setup.py -workflow_engine/README.MD -workflow_engine/__init__.py -workflow_engine/__main__.py -workflow_engine/models.py -workflow_engine/requirements-dev.txt -workflow_engine/schema_validator.py -workflow_engine/task.py -workflow_engine/workflow_processor.py -workflow_engine.egg-info/PKG-INFO -workflow_engine.egg-info/SOURCES.txt -workflow_engine.egg-info/dependency_links.txt -workflow_engine.egg-info/entry_points.txt -workflow_engine.egg-info/not-zip-safe -workflow_engine.egg-info/top_level.txt -workflow_engine/examples/dtt1-agents-aws.yaml -workflow_engine/examples/dtt1-agents-poc-aws.yaml -workflow_engine/examples/dtt1-agents-poc2-vagrant.yaml -workflow_engine/examples/dtt1-agents-vagrant.yaml -workflow_engine/examples/dtt1-agents.yaml -workflow_engine/examples/dtt1-managers-poc-aws.yaml -workflow_engine/examples/dtt1-managers-poc-vagrant.yaml -workflow_engine/examples/dtt1-managers.yaml -workflow_engine/examples/test.yaml -workflow_engine/examples/test/aws/test-agent-complete.yaml -workflow_engine/examples/test/aws/test-agent-restart-ins-prov.yaml -workflow_engine/examples/test/aws/test-agent-stop-ins-prov.yaml -workflow_engine/examples/test/aws/test-agent-suse.yaml -workflow_engine/examples/test/aws/test-agent-uninstall-ins-prov.yaml -workflow_engine/examples/test/vagrant/test-agent-complete.yaml -workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov-2.yaml -workflow_engine/examples/test/vagrant/test-agent-stop-ins-prov.yaml -workflow_engine/examples/test/vagrant/test-agent-uninstall-ins-prov.yaml -workflow_engine/logger/__init__.py -workflow_engine/logger/config.yaml -workflow_engine/logger/filter.py -workflow_engine/logger/logger.py -workflow_engine/schemas/schema_v1.json -workflow_engine/tests/TESTING-README.md -workflow_engine/tests/conftest.py -workflow_engine/tests/test_dag.py -workflow_engine/tests/test_schema_validator.py -workflow_engine/tests/test_task.py -workflow_engine/tests/test_workflow_file.py -workflow_engine/tests/test_workflow_processor.py -workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml -workflow_engine/tests/data/wf-ko-no-path-on-do.yaml -workflow_engine/tests/data/wf-ko-schema-error.yaml -workflow_engine/tests/data/wf-ok.yaml \ No newline at end of file diff --git a/deployability/modules/workflow_engine.egg-info/dependency_links.txt b/deployability/modules/workflow_engine.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789179..0000000000 --- a/deployability/modules/workflow_engine.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/deployability/modules/workflow_engine.egg-info/entry_points.txt b/deployability/modules/workflow_engine.egg-info/entry_points.txt deleted file mode 100644 index bd39e78207..0000000000 --- a/deployability/modules/workflow_engine.egg-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[console_scripts] -engine = workflow_engine.__main__:main diff --git a/deployability/modules/workflow_engine.egg-info/not-zip-safe b/deployability/modules/workflow_engine.egg-info/not-zip-safe deleted file mode 100644 index 8b13789179..0000000000 --- a/deployability/modules/workflow_engine.egg-info/not-zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/deployability/modules/workflow_engine.egg-info/top_level.txt b/deployability/modules/workflow_engine.egg-info/top_level.txt deleted file mode 100644 index 16d139c485..0000000000 --- a/deployability/modules/workflow_engine.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -workflow_engine From 90931c8e2c68889fcc1426bf0f5c673a7d51e60c Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 12 Apr 2024 18:15:40 +0200 Subject: [PATCH 025/195] fix(#5210): Restoring os in test --- .../examples/agent/aws/test-agent-restart-ins-prov.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml index bb71895416..c7bed4b594 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml @@ -15,7 +15,7 @@ variables: - linux-redhat-8-amd64 - linux-redhat-9-amd64 - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-20.04-amd64 infra-provider: aws working-dir: /tmp/dtt1-poc From 17db6c3ce22a83f381b8121a5c3abaefd157915f Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 12 Apr 2024 18:16:23 +0200 Subject: [PATCH 026/195] fix(#5210): Restoring os in test --- .../examples/agent/aws/test-agent-restart-ins-prov.yaml | 2 +- .../examples/agent/aws/test-agent-stop-ins-prov.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml index c7bed4b594..bb71895416 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml @@ -15,7 +15,7 @@ variables: - linux-redhat-8-amd64 - linux-redhat-9-amd64 - linux-amazon-2-amd64 - manager-os: linux-ubuntu-20.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 infra-provider: aws working-dir: /tmp/dtt1-poc diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml index 5d045b1961..cc541a7238 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml @@ -15,7 +15,7 @@ variables: - linux-redhat-8-amd64 - linux-redhat-9-amd64 - linux-amazon-2-amd64 - manager-os: linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-20.04-amd64 infra-provider: aws working-dir: /tmp/dtt1-poc From 864dbc2f1abe4681c2474f9b42a090623a2b514f Mon Sep 17 00:00:00 2001 From: Carlos Bordon Date: Fri, 12 Apr 2024 20:32:16 -0300 Subject: [PATCH 027/195] Fixed delete windows machines --- .../aws/helpers/windowsUserData.ps1 | 31 ++++++++++++++----- .../modules/allocation/aws/provider.py | 16 +++++----- .../modules/allocation/static/specs/os.yml | 2 +- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/deployability/modules/allocation/aws/helpers/windowsUserData.ps1 b/deployability/modules/allocation/aws/helpers/windowsUserData.ps1 index 18d18a20a0..f9043f51c4 100644 --- a/deployability/modules/allocation/aws/helpers/windowsUserData.ps1 +++ b/deployability/modules/allocation/aws/helpers/windowsUserData.ps1 @@ -12,12 +12,27 @@ try { $_.Exception.Message "Error enabling WinRM on HTTPS." } -New-LocalUser "Administrator" -Password ChangeMe -FullName "Administrator" -Description "Administrator user for remote desktop" -Add-LocalGroupMember -Group "Remote Desktop Users" -Member "Administrator" -# Set Administrator user to administrator group -Add-LocalGroupMember -Group "Administrators" -Member "Administrator" -# Set the password for the Administrator account -$admin = [ADSI]"WinNT://./Administrator, user" -$admin.SetPassword("ChangeMe") -$admin.SetInfo() +# Check if Administrator user exists +if (-not (Get-LocalUser -Name "Administrator" -ErrorAction SilentlyContinue)) { + # Create Administrator user + Write-Output "Creating Administrator user" + $password = ConvertTo-SecureString "ChangeMe" -AsPlainText -Force + New-LocalUser "Administrator" -Password $password -FullName "Administrator" -Description "Administrator user for remote desktop" + + Write-Output "Adding Administrator user to RDP group." + # Add Administrator to Remote Desktop Users group + Add-LocalGroupMember -Group "Remote Desktop Users" -Member "Administrator" + + Write-Output "Adding Administrator user to Administrators group." + # Add Administrator to Administrators group + Add-LocalGroupMember -Group "Administrators" -Member "Administrator" +} else { + Write-Output "Administrator user already exists." + # Set the password for the Administrator account + $admin = [ADSI]"WinNT://./Administrator, user" + $password = "ChangeMe" + $admin.SetPassword($password) + $admin.SetInfo() + Write-Output "Administrator password changed successfully." +} diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py index 287a4c17fd..380401c884 100644 --- a/deployability/modules/allocation/aws/provider.py +++ b/deployability/modules/allocation/aws/provider.py @@ -119,10 +119,11 @@ def _create_instance(cls, base_dir: Path, params: CreationPayload, config: AWSCo instance_dir = Path(base_dir, instance_id) logger.debug(f"Renaming temp {temp_dir} directory to {instance_dir}") os.rename(temp_dir, instance_dir) - if not ssh_key: - credentials.key_path = (instance_dir / credentials.name) - else: - credentials.key_path = (os.path.splitext(ssh_key)[0]) + if platform != "windows": + if not ssh_key: + credentials.key_path = (instance_dir / credentials.name) + else: + credentials.key_path = (os.path.splitext(ssh_key)[0]) instance_params = {} instance_params['instance_dir'] = instance_dir @@ -159,8 +160,9 @@ def _destroy_instance(cls, destroy_parameters: InstancePayload) -> None: destroy_parameters (InstancePayload): The parameters for destroying the instance. """ credentials = AWSCredentials() - key_id = os.path.basename(destroy_parameters.key_path) - credentials.load(key_id) + if destroy_parameters.platform != 'windows': + key_id = os.path.basename(destroy_parameters.key_path) + credentials.load(key_id) instance_params = {} instance_params['instance_dir'] = destroy_parameters.instance_dir instance_params['identifier'] = destroy_parameters.identifier @@ -168,7 +170,7 @@ def _destroy_instance(cls, destroy_parameters: InstancePayload) -> None: instance_params['host_identifier'] = destroy_parameters.host_identifier instance = AWSInstance(InstancePayload(**instance_params), credentials) - if os.path.dirname(destroy_parameters.key_path) == str(destroy_parameters.instance_dir): + if os.path.dirname(destroy_parameters.key_path) == str(destroy_parameters.instance_dir) and destroy_parameters.platform != 'windows': logger.debug(f"Deleting credentials: {instance.credentials.name}") instance.credentials.delete() instance.delete() diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 574ff9e7a3..d89a25775f 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -377,7 +377,7 @@ aws: zone: us-east-1 user: Administrator windows-server-2016-amd64: - ami: ami-0f8b5c22ce0a7cf3b + ami: ami-0f279f6c0764bef8f zone: us-east-1 user: Administrator windows-server-2019-amd64: From f23035e343db57b6709049905fb28753c010de2d Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 15 Apr 2024 13:29:33 +0200 Subject: [PATCH 028/195] fix(#5210): Fixes after testing --- deployability/modules/provision/main.py | 2 + .../agent/aws/test-agent-complete.yaml | 4 +- .../aws/test-agent-restart-ins-prov.yaml | 2 + .../agent/aws/test-agent-stop-ins-prov.yaml | 6 +- .../examples/agent/aws/test-agent-suse.yaml | 3 + .../aws/test-agent-uninstall-ins-prov.yaml | 4 +- .../agent/vagrant/test-agent-complete-1.yaml | 116 +++++++++++++++ ...mplete.yaml => test-agent-complete-2.yaml} | 3 +- ...aml => test-agent-restart-ins-prov-1.yaml} | 9 +- .../test-agent-restart-ins-prov-2.yaml | 134 ++++++++++++++++++ ...v.yaml => test-agent-stop-ins-prov-1.yaml} | 9 +- .../vagrant/test-agent-stop-ins-prov-2.yaml | 134 ++++++++++++++++++ ...l => test-agent-uninstall-ins-prov-1.yaml} | 9 +- .../test-agent-uninstall-ins-prov-2.yaml | 134 ++++++++++++++++++ .../manager/aws/dtt1-managers-poc-aws.yaml | 1 + .../vagrant/dtt1-managers-poc-vagrant.yaml | 1 + 16 files changed, 545 insertions(+), 26 deletions(-) create mode 100755 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml rename deployability/modules/workflow_engine/examples/agent/vagrant/{test-agent-complete.yaml => test-agent-complete-2.yaml} (98%) rename deployability/modules/workflow_engine/examples/agent/vagrant/{test-agent-restart-ins-prov.yaml => test-agent-restart-ins-prov-1.yaml} (95%) create mode 100755 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml rename deployability/modules/workflow_engine/examples/agent/vagrant/{test-agent-stop-ins-prov.yaml => test-agent-stop-ins-prov-1.yaml} (95%) create mode 100755 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml rename deployability/modules/workflow_engine/examples/agent/vagrant/{test-agent-uninstall-ins-prov.yaml => test-agent-uninstall-ins-prov-1.yaml} (95%) create mode 100755 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml diff --git a/deployability/modules/provision/main.py b/deployability/modules/provision/main.py index 9d9e43419a..2ad17cbe9c 100755 --- a/deployability/modules/provision/main.py +++ b/deployability/modules/provision/main.py @@ -5,6 +5,8 @@ import os import sys +import time + project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')) sys.path.append(project_root) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml index cba04ff94d..dcfe7f0ecd 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml @@ -37,6 +37,7 @@ tasks: - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + on-error: "abort-all" cleanup: this: process with: @@ -63,6 +64,7 @@ tasks: - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + on-error: "abort-all" foreach: - variable: agent-os as: agent @@ -94,7 +96,7 @@ tasks: live: True depends-on: - "allocate-manager-{manager-os}" - + on-error: "abort-all" # Generic agent test task - task: "run-agent-{agent}-tests" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml index bb71895416..b7002c02c6 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml @@ -64,6 +64,7 @@ tasks: - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + on-error: "abort-all" foreach: - variable: agent-os as: agent @@ -115,6 +116,7 @@ tasks: depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" + on-error: "abort-all" foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml index cc541a7238..0e97045f32 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent stop with provisioning agents' with provision module +description: Test agent restart with provisioning agents' with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 @@ -15,7 +15,7 @@ variables: - linux-redhat-8-amd64 - linux-redhat-9-amd64 - linux-amazon-2-amd64 - manager-os: linux-ubuntu-20.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 infra-provider: aws working-dir: /tmp/dtt1-poc @@ -64,6 +64,7 @@ tasks: - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + on-error: "abort-all" foreach: - variable: agent-os as: agent @@ -115,6 +116,7 @@ tasks: depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" + on-error: "abort-all" foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml index effe9897c8..e97b123b8b 100644 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml @@ -52,6 +52,7 @@ tasks: - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + on-error: "abort-all" foreach: - variable: agent-os as: agent @@ -85,6 +86,7 @@ tasks: live: True depends-on: - "allocate-manager-{manager-os}" + on-error: "abort-all" # Generic agent provision task - task: "provision-install-{agent}" @@ -103,6 +105,7 @@ tasks: depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" + on-error: "abort-all" foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml index f5bbb7e7cc..90c6145862 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent uninstall with provisioning agents' with provision module +description: Test agent restart with provisioning agents' with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 @@ -64,6 +64,7 @@ tasks: - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + on-error: "abort-all" foreach: - variable: agent-os as: agent @@ -115,6 +116,7 @@ tasks: depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" + on-error: "abort-all" foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml new file mode 100755 index 0000000000..ea9907ff0a --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml @@ -0,0 +1,116 @@ +version: 0.1 +description: This workflow is used to test agents' deployment for DDT1 PoC +variables: + agent-os: + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-20.04-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml similarity index 98% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete.yaml rename to deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml index 529814edd6..428e6e5baa 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml @@ -2,7 +2,6 @@ version: 0.1 description: This workflow is used to test agents' deployment for DDT1 PoC variables: agent-os: - - linux-oracle-9-amd64 - linux-centos-7-amd64 - linux-centos-8-amd64 - linux-redhat-7-amd64 @@ -58,6 +57,7 @@ tasks: - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + on-error: "abort-all" foreach: - variable: agent-os as: agent @@ -89,6 +89,7 @@ tasks: live: True depends-on: - "allocate-manager-{manager-os}" + on-error: "abort-all" # Generic agent test task diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml similarity index 95% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov.yaml rename to deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml index fd3a44ebf4..dea050edb9 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml @@ -4,17 +4,10 @@ variables: agent-os: - linux-ubuntu-18.04-amd64 - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - linux-debian-10-amd64 - linux-debian-11-amd64 - linux-debian-12-amd64 - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 manager-os: linux-ubuntu-22.04-amd64 infra-provider: vagrant working-dir: /tmp/dtt1-poc @@ -60,6 +53,7 @@ tasks: - composite-name: "{agent}" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" + on-error: "abort-all" foreach: - variable: agent-os as: agent @@ -111,6 +105,7 @@ tasks: depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" + on-error: "abort-all" foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml new file mode 100755 index 0000000000..7b7a3b03f0 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml @@ -0,0 +1,134 @@ +version: 0.1 +description: Test agent restart with provisioning agents' with provision module +variables: + agent-os: + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "restart" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml similarity index 95% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov.yaml rename to deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml index 318d85c711..8a7c959ca4 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml @@ -4,17 +4,10 @@ variables: agent-os: - linux-ubuntu-18.04-amd64 - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - linux-debian-10-amd64 - linux-debian-11-amd64 - linux-debian-12-amd64 - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 manager-os: linux-ubuntu-22.04-amd64 infra-provider: vagrant working-dir: /tmp/dtt1-poc @@ -60,6 +53,7 @@ tasks: - composite-name: "{agent}" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" + on-error: "abort-all" foreach: - variable: agent-os as: agent @@ -111,6 +105,7 @@ tasks: depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" + on-error: "abort-all" foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml new file mode 100755 index 0000000000..1e58fd2593 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml @@ -0,0 +1,134 @@ +version: 0.1 +description: Test agent stop with provisioning agents' with provision module +variables: + agent-os: + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "stop" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml similarity index 95% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov.yaml rename to deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml index 932c429e6a..91e83fdce7 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml @@ -4,17 +4,10 @@ variables: agent-os: - linux-ubuntu-18.04-amd64 - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - linux-debian-10-amd64 - linux-debian-11-amd64 - linux-debian-12-amd64 - linux-oracle-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-amazon-2-amd64 manager-os: linux-ubuntu-22.04-amd64 infra-provider: vagrant working-dir: /tmp/dtt1-poc @@ -60,6 +53,7 @@ tasks: - composite-name: "{agent}" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" + on-error: "abort-all" foreach: - variable: agent-os as: agent @@ -111,6 +105,7 @@ tasks: depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" + on-error: "abort-all" foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml new file mode 100755 index 0000000000..13a7db7c09 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml @@ -0,0 +1,134 @@ +version: 0.1 +description: Test agent uninstall with provisioning agents' with provision module +variables: + agent-os: + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-amazon-2-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent provision task + - task: "provision-install-{agent}" + description: "Provision resources for the {agent} agent." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/agent-{agent}/inventory.yaml" + - dependencies: + - manager: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-agent + type: package + version: 4.7.3 + live: True + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "provision-install-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml index 47d649795e..2471660ebe 100644 --- a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml @@ -34,6 +34,7 @@ tasks: - track-output: "{working-dir}/manager-{manager}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + on-error: "abort-all" foreach: - variable: manager-os as: manager diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index f18f90abb4..4b2fa717a2 100644 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -33,6 +33,7 @@ tasks: - composite-name: "{manager}" - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" - track-output: "{working-dir}/manager-{manager}/track.yaml" + on-error: "abort-all" foreach: - variable: manager-os as: manager From 318679a36f95b0eb109356c006f4af58f52e831b Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 15 Apr 2024 13:30:53 +0200 Subject: [PATCH 029/195] fix(#5210): Removing library time from main in provision --- deployability/modules/provision/main.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/deployability/modules/provision/main.py b/deployability/modules/provision/main.py index 2ad17cbe9c..9d9e43419a 100755 --- a/deployability/modules/provision/main.py +++ b/deployability/modules/provision/main.py @@ -5,8 +5,6 @@ import os import sys -import time - project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')) sys.path.append(project_root) From d51a4f3f0ca12bb4bddee2a9a52e9412047e0b27 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Mon, 15 Apr 2024 16:44:08 -0300 Subject: [PATCH 030/195] Fixed support for User data for Windows systems --- .../modules/allocation/allocation.py | 2 +- .../aws/helpers/windowsUserData.ps1 | 54 +++++++++++++------ .../modules/allocation/static/specs/os.yml | 20 +++---- 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/deployability/modules/allocation/allocation.py b/deployability/modules/allocation/allocation.py index b1df115de9..73aa98bb67 100755 --- a/deployability/modules/allocation/allocation.py +++ b/deployability/modules/allocation/allocation.py @@ -188,7 +188,7 @@ def __generate_track_file(instance: Instance, provider_name: str, track_path: P return track_path @staticmethod - def __check_connection(inventory: models.InventoryOutput, attempts=10, sleep=60) -> None: + def __check_connection(inventory: models.InventoryOutput, attempts=15, sleep=60) -> None: """ Checks if the ssh connection is successful. diff --git a/deployability/modules/allocation/aws/helpers/windowsUserData.ps1 b/deployability/modules/allocation/aws/helpers/windowsUserData.ps1 index f9043f51c4..f0fc48086b 100644 --- a/deployability/modules/allocation/aws/helpers/windowsUserData.ps1 +++ b/deployability/modules/allocation/aws/helpers/windowsUserData.ps1 @@ -4,35 +4,59 @@ try { + Write-Output "Executing winrm quickconfig" + if (-not ([Net.ServicePointManager]::SecurityProtocol -band [Net.SecurityProtocolType]::Tls12)) { + Write-Output "Enabling TLS 1.2" + [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 + } + # Check if WinRM HTTPS listener is configured + $httpsListener = Get-Item -Path WSMan:\LocalHost\Listener\* | Where-Object { $_.Keys -contains 'Transport' -and $_.Transport -eq 'HTTPS' } + + if ($httpsListener) { + # Remove existing HTTPS listener + Write-Output "Removing existing HTTPS listener." + Remove-Item -Path WSMan:\LocalHost\Listener\$($httpsListener.Name) -Force + } + # Create a self-signed certificate + $cert = New-SelfSignedCertificate -CertstoreLocation Cert:\LocalMachine\My -DnsName $env:COMPUTERNAME + + # Enable PSRemoting and set up HTTPS listener + Enable-PSRemoting -SkipNetworkProfileCheck -Force + New-Item -Path WSMan:\LocalHost\Listener -Transport HTTPS -Address * -CertificateThumbPrint $cert.Thumbprint -Force + + # Add firewall rule for WinRM HTTPS + New-NetFirewallRule -DisplayName "Windows Remote Management (HTTPS-In)" -Name "Windows Remote Management (HTTPS-In)" -Profile Any -LocalPort 5986 -Protocol TCP $url = "https://raw.githubusercontent.com/ansible/ansible/6e325d9e4dbdc020eb520a81148866d988a5dbc5/examples/scripts/ConfigureRemotingForAnsible.ps1" $file = "$env:temp\ConfigureRemotingForAnsible.ps1" (New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file) powershell.exe -ExecutionPolicy ByPass -File $file + Write-Output "WinRM enabled on HTTPS." } catch { $_.Exception.Message "Error enabling WinRM on HTTPS." + Write-Output "Error enabling WinRM on HTTPS." } -# Check if Administrator user exists -if (-not (Get-LocalUser -Name "Administrator" -ErrorAction SilentlyContinue)) { - # Create Administrator user - Write-Output "Creating Administrator user" +# Check if wazuh-user user exists +if (-not (Get-LocalUser -Name "wazuh-user" -ErrorAction SilentlyContinue)) { + # Create wazuh-user user + Write-Output "Creating wazuh-user user" $password = ConvertTo-SecureString "ChangeMe" -AsPlainText -Force - New-LocalUser "Administrator" -Password $password -FullName "Administrator" -Description "Administrator user for remote desktop" + New-LocalUser "wazuh-user" -Password $password -FullName "wazuh-user" -Description "wazuh-user user for remote desktop" - Write-Output "Adding Administrator user to RDP group." - # Add Administrator to Remote Desktop Users group - Add-LocalGroupMember -Group "Remote Desktop Users" -Member "Administrator" + Write-Output "Adding wazuh-user user to RDP group." + # Add wazuh-user to Remote Desktop Users group + Add-LocalGroupMember -Group "Remote Desktop Users" -Member "wazuh-user" - Write-Output "Adding Administrator user to Administrators group." - # Add Administrator to Administrators group - Add-LocalGroupMember -Group "Administrators" -Member "Administrator" + Write-Output "Adding wazuh-user user to Administrators group." + # Add wazuh-user to wazuh-users group + Add-LocalGroupMember -Group "Administrators" -Member "wazuh-user" } else { - Write-Output "Administrator user already exists." - # Set the password for the Administrator account - $admin = [ADSI]"WinNT://./Administrator, user" + Write-Output "wazuh-user user already exists." + # Set the password for the wazuh-user account + $admin = [ADSI]"WinNT://./wazuh-user, user" $password = "ChangeMe" $admin.SetPassword($password) $admin.SetInfo() - Write-Output "Administrator password changed successfully." + Write-Output "wazuh-user password changed successfully." } diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index d89a25775f..0e3914f957 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -367,24 +367,24 @@ aws: windows-desktop-10-amd64: ami: ami-0a747df120215911a zone: us-east-1 - user: Administrator - windows-desktop-11-amd64: - ami: ami-09d8cef159442d5b0 - zone: us-east-1 - user: Jenkins + user: wazuh-user + #windows-desktop-11-amd64: Issue related https://github.com/wazuh/wazuh-automation/issues/1582 + # ami: ami-0115c7e59b426bffb + # zone: us-east-1 + # user: Jenkins windows-server-2012r2-amd64: - ami: ami-09a3ef2bc0c6a252f + ami: ami-05710c71113d5a40e zone: us-east-1 - user: Administrator + user: wazuh-user windows-server-2016-amd64: ami: ami-0f279f6c0764bef8f zone: us-east-1 - user: Administrator + user: wazuh-user windows-server-2019-amd64: ami: ami-06cc514f1012a7431 zone: us-east-1 - user: Administrator + user: wazuh-user windows-server-2022-amd64: ami: ami-0f9c44e98edf38a2b zone: us-east-1 - user: Administrator + user: wazuh-user From 8d83fd53d9f936804e79707b39c28c4189a7d2b4 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Mon, 15 Apr 2024 16:48:16 -0300 Subject: [PATCH 031/195] Changed return value --- deployability/modules/allocation/allocation.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/deployability/modules/allocation/allocation.py b/deployability/modules/allocation/allocation.py index 73aa98bb67..1818aa8633 100755 --- a/deployability/modules/allocation/allocation.py +++ b/deployability/modules/allocation/allocation.py @@ -64,7 +64,7 @@ def __create(cls, payload: models.CreationPayload): # Validate connection check_connection = cls.__check_connection(inventory) track_file = cls.__generate_track_file(instance, payload.provider, payload.track_output) - if check_connection: + if check_connection is False: if payload.rollback: logger.warning(f"Rolling back instance creation.") track_payload = {'track_output': track_file} @@ -211,10 +211,10 @@ def __check_connection(inventory: models.InventoryOutput, attempts=15, sleep=60) cmd = session.run_cmd('ipconfig') if cmd.status_code == 0: logger.info("WinRM connection successful.") - return False + return True else: logger.error(f'WinRM connection failed. Check the credentials in the inventory file.') - return True + return False except Exception as e: logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') time.sleep(sleep) @@ -240,13 +240,13 @@ def __check_connection(inventory: models.InventoryOutput, attempts=15, sleep=60) ssh.connect(**ssh_parameters) logger.info("SSH connection successful.") ssh.close() - return False + return True except paramiko.AuthenticationException: logger.error(f'Authentication error. Check the credentials in the inventory file.') - return True + return False except Exception as e: logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') time.sleep(sleep) logger.error(f'Connection attempts failed after {attempts} tries. Connection timeout.') - return True + return False From ebd91a23d3f75d67a264861990f1e1505831469a Mon Sep 17 00:00:00 2001 From: Marcelo Ariel Hamra Date: Mon, 15 Apr 2024 17:56:58 -0300 Subject: [PATCH 032/195] Fix loggers --- deployability/modules/generic/ansible.py | 4 ++-- deployability/modules/generic/logger/config.yaml | 13 +++++++++++++ deployability/modules/provision/actions.py | 2 +- deployability/modules/testing/testing.py | 2 +- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/deployability/modules/generic/ansible.py b/deployability/modules/generic/ansible.py index 6eaf083418..0829c84b3e 100755 --- a/deployability/modules/generic/ansible.py +++ b/deployability/modules/generic/ansible.py @@ -20,11 +20,11 @@ class Inventory(BaseModel): class Ansible: - def __init__(self, ansible_data: dict | Inventory, playbooks_path: str | Path = None): + def __init__(self, ansible_data: dict | Inventory, logger: Logger, playbooks_path: str | Path = None): self.playbooks_path = playbooks_path self.ansible_data = Inventory(**dict(ansible_data)) self.inventory = self.generate_inventory() - self.logger = Logger(Path(__file__).stem).get_logger() + self.logger = logger def render_playbooks(self, rendering_variables: dict) -> list[str]: """ diff --git a/deployability/modules/generic/logger/config.yaml b/deployability/modules/generic/logger/config.yaml index 1f86bf764a..dee20af028 100644 --- a/deployability/modules/generic/logger/config.yaml +++ b/deployability/modules/generic/logger/config.yaml @@ -25,6 +25,19 @@ handlers: formatter: simple filename: /tmp/workflow.log filters: [uppercase] +loggers: + allocator: + level: DEBUG + handlers: [console, file] + propagate: no + provisioner: + level: DEBUG + handlers: [console, file] + propagate: no + tester: + level: DEBUG + handlers: [console, file] + propagate: no root: level: DEBUG handlers: [console, file] diff --git a/deployability/modules/provision/actions.py b/deployability/modules/provision/actions.py index f6032c5c56..f7fd949b04 100644 --- a/deployability/modules/provision/actions.py +++ b/deployability/modules/provision/actions.py @@ -29,7 +29,7 @@ def __init__(self, action: str, component_info: ComponentInfo, ansible_data: dic component_info = ComponentInfo(**dict(component_info)) action_type = component_info.type self.handler = ProvisionHandler(component_info, action, action_type) - self.ansible = Ansible(ansible_data, playbooks_path=self.handler.templates_path) + self.ansible = Ansible(ansible_data, logger=logger, playbooks_path=self.handler.templates_path) def execute(self) -> dict: """ diff --git a/deployability/modules/testing/testing.py b/deployability/modules/testing/testing.py index 72070eaa52..f6e7c5b881 100644 --- a/deployability/modules/testing/testing.py +++ b/deployability/modules/testing/testing.py @@ -56,7 +56,7 @@ def run(cls, payload: InputPayload) -> None: # Setup and run tests target_inventory = Inventory(**Utils.load_from_yaml(str(list(eval(payload.targets[0]).values())[0]))) - ansible = Ansible(ansible_data=target_inventory.model_dump()) + ansible = Ansible(ansible_data=target_inventory.model_dump(), logger=logger) cls._setup(ansible, extra_vars) cls._run_tests(payload.tests, ansible, extra_vars) From f2701cf8a7cabcee0159baaacc230053586912f4 Mon Sep 17 00:00:00 2001 From: Marcelo Ariel Hamra Date: Tue, 16 Apr 2024 10:39:41 -0300 Subject: [PATCH 033/195] Remove logger files from helpers and replace. Rever all logger imports in testing with logger from generic. --- .../modules/testing/tests/helpers/generic.py | 2 +- .../testing/tests/helpers/logger/__init__.py | 3 --- .../testing/tests/helpers/logger/config.yaml | 22 ----------------- .../testing/tests/helpers/logger/filter.py | 23 ------------------ .../testing/tests/helpers/logger/logger.py | 24 ------------------- .../testing/tests/test_agent/test_install.py | 2 +- .../tests/test_agent/test_registration.py | 2 +- .../testing/tests/test_agent/test_restart.py | 2 +- .../testing/tests/test_agent/test_stop.py | 2 +- .../tests/test_agent/test_uninstall.py | 2 +- .../tests/test_manager/test_install.py | 2 +- .../tests/test_manager/test_restart.py | 2 +- .../testing/tests/test_manager/test_stop.py | 2 +- .../tests/test_manager/test_uninstall.py | 2 +- 14 files changed, 10 insertions(+), 82 deletions(-) delete mode 100644 deployability/modules/testing/tests/helpers/logger/__init__.py delete mode 100644 deployability/modules/testing/tests/helpers/logger/config.yaml delete mode 100644 deployability/modules/testing/tests/helpers/logger/filter.py delete mode 100644 deployability/modules/testing/tests/helpers/logger/logger.py diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 25308fd2cc..63e5140ec9 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -14,7 +14,7 @@ from pathlib import Path from .constants import WAZUH_CONTROL, CLIENT_KEYS from .executor import Executor -from ..helpers.logger.logger import logger +from modules.generic.logger import logger from .utils import Utils diff --git a/deployability/modules/testing/tests/helpers/logger/__init__.py b/deployability/modules/testing/tests/helpers/logger/__init__.py deleted file mode 100644 index ea0d8aeea6..0000000000 --- a/deployability/modules/testing/tests/helpers/logger/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 diff --git a/deployability/modules/testing/tests/helpers/logger/config.yaml b/deployability/modules/testing/tests/helpers/logger/config.yaml deleted file mode 100644 index b1f9d1efd2..0000000000 --- a/deployability/modules/testing/tests/helpers/logger/config.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - -version: 1 -formatters: - simple: - format: '[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s' -handlers: - console: - class: logging.StreamHandler - level: DEBUG - formatter: simple - stream: ext://sys.stdout - file: - class: logging.FileHandler - level: DEBUG - formatter: simple - filename: /tmp/workflow.log -root: - level: DEBUG - handlers: [console, file] diff --git a/deployability/modules/testing/tests/helpers/logger/filter.py b/deployability/modules/testing/tests/helpers/logger/filter.py deleted file mode 100644 index bc7d101ce8..0000000000 --- a/deployability/modules/testing/tests/helpers/logger/filter.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - -import logging -import threading - -class ThreadIDFilter(logging.Filter): - """ - A filter that uppercases the name of the log record. - """ - def filter(self, record: str) -> bool: - """ - Inject thread_id to log records. - - Args: - record (LogRecord): The log record to filter. - - Returns: - bool: True if the record should be logged, False otherwise. - """ - record.thread_id = threading.get_native_id() - return record diff --git a/deployability/modules/testing/tests/helpers/logger/logger.py b/deployability/modules/testing/tests/helpers/logger/logger.py deleted file mode 100644 index 2e02766f06..0000000000 --- a/deployability/modules/testing/tests/helpers/logger/logger.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) 2015, Wazuh Inc. -# Created by Wazuh, Inc. . -# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 - -import logging -import logging.config -from pathlib import Path -import threading - -import yaml - - -def _load_config() -> None: - """ - Loads the logging configuration from 'config.yaml' file. - """ - config_path = Path(__file__).parent / 'config.yaml' - with open(config_path, 'r') as f: - config = yaml.safe_load(f.read()) - logging.config.dictConfig(config) - -_load_config() - -logger = logging.getLogger("Testing") diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index 72a454bbef..87fc6ac6c7 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -8,7 +8,7 @@ from ..helpers.agent import WazuhAgent from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions -from ..helpers.logger.logger import logger +from modules.generic.logger import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py index 3d15ea5e15..cc62fc7844 100644 --- a/deployability/modules/testing/tests/test_agent/test_registration.py +++ b/deployability/modules/testing/tests/test_agent/test_registration.py @@ -8,7 +8,7 @@ from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from ..helpers.logger.logger import logger +from modules.generic.logger import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index 5d7b4b5081..3f876b9b4c 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -6,7 +6,7 @@ import re from ..helpers.generic import GeneralComponentActions, HostInformation -from ..helpers.logger.logger import logger +from modules.generic.logger import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index 1463862510..97808d5d24 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -7,7 +7,7 @@ from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import GeneralComponentActions, Waits, HostInformation -from ..helpers.logger.logger import logger +from modules.generic.logger import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index a5d44ac154..ba9448a0f1 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -9,7 +9,7 @@ from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from ..helpers.logger.logger import logger +from modules.generic.logger import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_manager/test_install.py b/deployability/modules/testing/tests/test_manager/test_install.py index 408d1dcd7e..52ea66d71b 100644 --- a/deployability/modules/testing/tests/test_manager/test_install.py +++ b/deployability/modules/testing/tests/test_manager/test_install.py @@ -8,7 +8,7 @@ from ..helpers.executor import WazuhAPI from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions from ..helpers.manager import WazuhManager -from ..helpers.logger.logger import logger +from modules.generic.logger import logger from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_manager/test_restart.py b/deployability/modules/testing/tests/test_manager/test_restart.py index 96268071fb..e1afa015ac 100644 --- a/deployability/modules/testing/tests/test_manager/test_restart.py +++ b/deployability/modules/testing/tests/test_manager/test_restart.py @@ -5,7 +5,7 @@ import pytest from ..helpers.generic import HostInformation, GeneralComponentActions -from ..helpers.logger.logger import logger +from modules.generic.logger import logger @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_manager/test_stop.py b/deployability/modules/testing/tests/test_manager/test_stop.py index 7630c40fdc..afb357ccfc 100644 --- a/deployability/modules/testing/tests/test_manager/test_stop.py +++ b/deployability/modules/testing/tests/test_manager/test_stop.py @@ -5,7 +5,7 @@ import pytest from ..helpers.generic import HostInformation, GeneralComponentActions -from ..helpers.logger.logger import logger +from modules.generic.logger import logger @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_manager/test_uninstall.py b/deployability/modules/testing/tests/test_manager/test_uninstall.py index b246d8012a..c06d1c0b72 100644 --- a/deployability/modules/testing/tests/test_manager/test_uninstall.py +++ b/deployability/modules/testing/tests/test_manager/test_uninstall.py @@ -7,7 +7,7 @@ from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostInformation, GeneralComponentActions from ..helpers.manager import WazuhManager -from ..helpers.logger.logger import logger +from modules.generic.logger import logger @pytest.fixture(scope="module", autouse=True) From 1ffd41c6b559154eb37015b3393dc097e5032ddb Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 16 Apr 2024 15:53:12 -0300 Subject: [PATCH 034/195] Added pywinrm as dependencie and removed Windows 11 references --- deployability/deps/requirements.txt | 1 + deployability/modules/allocation/static/specs/os.yml | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/deployability/deps/requirements.txt b/deployability/deps/requirements.txt index 1e4478b9ec..5213aad50d 100755 --- a/deployability/deps/requirements.txt +++ b/deployability/deps/requirements.txt @@ -12,3 +12,4 @@ pytest==7.4.4 paramiko==3.4.0 requests==2.31.0 chardet==5.2.0 +pywinrm==0.3.0 diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 0e3914f957..aadd5fc5dc 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -368,10 +368,6 @@ aws: ami: ami-0a747df120215911a zone: us-east-1 user: wazuh-user - #windows-desktop-11-amd64: Issue related https://github.com/wazuh/wazuh-automation/issues/1582 - # ami: ami-0115c7e59b426bffb - # zone: us-east-1 - # user: Jenkins windows-server-2012r2-amd64: ami: ami-05710c71113d5a40e zone: us-east-1 From 2988be05578f1c3862addd738f75ef163b388752 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 16 Apr 2024 16:30:40 -0300 Subject: [PATCH 035/195] Fixed deploy for macOS in AWS --- .../modules/allocation/aws/provider.py | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py index 287a4c17fd..50d49bcc59 100644 --- a/deployability/modules/allocation/aws/provider.py +++ b/deployability/modules/allocation/aws/provider.py @@ -297,19 +297,23 @@ def _generate_dedicated_host(config: AWSConfig, arch: str) -> str: client = boto3.client('ec2') dedicated_host_name = str(config.name) + '-Host-' + arch logger.info(f"Creating dedicated host: {dedicated_host_name}") - host = client.allocate_hosts(InstanceType=config.type, - AutoPlacement='on', - AvailabilityZone=config.zone, - Quantity=1, - TagSpecifications=[{ - 'ResourceType': 'dedicated-host', - 'Tags': [ - {'Key': 'Name', 'Value': dedicated_host_name}, - {'Key': 'termination_date', 'Value': config.termination_date}, - {'Key': 'issue', 'Value': config.issue}, - {'Key': 'team', 'Value': config.team} - ] - }]) + params = { + 'InstanceType': config.type, + 'AutoPlacement': 'on', + 'AvailabilityZone': config.zone, + 'Quantity': 1, + 'TagSpecifications': [{ + 'ResourceType': 'dedicated-host', + 'Tags': [ + {'Key': 'Name', 'Value': config.name}, + {'Key': 'termination_date', 'Value': config.termination_date}, + {'Key': 'team', 'Value': config.team} + ] + }] + } + if config.issue: + params['TagSpecifications'][0]['Tags'].append({'Key': 'issue', 'Value': config.issue}) + host = client.allocate_hosts(**params) logger.info(f"Dedicated host created: {host['HostIds'][0]}") return host['HostIds'][0] @@ -330,7 +334,7 @@ def _release_dedicated_host(host_identifier: str) -> str: if host['Unsuccessful']: unsuccessful_messages = [item['Error']['Message'] for item in host['Unsuccessful']] for message in unsuccessful_messages: - logger.info(f"{message}") + logger.warning(f"{message}") else: logger.info(f"Dedicated host released: {host_identifier}") From 74916cdd4ab1c4331cbb2603760507a567fa908a Mon Sep 17 00:00:00 2001 From: Marcelo Ariel Hamra Date: Wed, 17 Apr 2024 11:12:50 -0300 Subject: [PATCH 036/195] Fix logger imports --- deployability/modules/testing/testing.py | 2 +- deployability/modules/testing/tests/helpers/agent.py | 2 +- deployability/modules/testing/tests/helpers/generic.py | 2 +- deployability/modules/testing/tests/helpers/manager.py | 2 +- deployability/modules/testing/tests/helpers/utils.py | 2 +- deployability/modules/testing/tests/test_agent/test_install.py | 2 +- .../modules/testing/tests/test_agent/test_registration.py | 2 +- deployability/modules/testing/tests/test_agent/test_restart.py | 2 +- deployability/modules/testing/tests/test_agent/test_stop.py | 2 +- .../modules/testing/tests/test_agent/test_uninstall.py | 2 +- .../modules/testing/tests/test_manager/test_install.py | 2 +- .../modules/testing/tests/test_manager/test_restart.py | 2 +- deployability/modules/testing/tests/test_manager/test_stop.py | 2 +- .../modules/testing/tests/test_manager/test_uninstall.py | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/deployability/modules/testing/testing.py b/deployability/modules/testing/testing.py index f6e7c5b881..c0f7ee985e 100644 --- a/deployability/modules/testing/testing.py +++ b/deployability/modules/testing/testing.py @@ -9,7 +9,7 @@ from modules.generic.utils import Utils from pathlib import Path from .models import InputPayload, ExtraVars -from .utils import logger +from modules.testing.utils import logger class Tester: diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 7e60c5fbca..bc7d0d0452 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -8,7 +8,7 @@ from .constants import WAZUH_CONF, WAZUH_ROOT from .executor import Executor, WazuhAPI from .generic import HostInformation, CheckFiles -from .logger.logger import logger +from modules.testing.utils import logger class WazuhAgent: diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 63e5140ec9..e7c2ba4a58 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -14,8 +14,8 @@ from pathlib import Path from .constants import WAZUH_CONTROL, CLIENT_KEYS from .executor import Executor -from modules.generic.logger import logger from .utils import Utils +from modules.testing.utils import logger class HostInformation: diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index 3611c7f5af..2ac8811ab7 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -8,7 +8,7 @@ from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT from .executor import Executor, WazuhAPI from .generic import HostInformation, CheckFiles -from .logger.logger import logger +from modules.testing.utils import logger from .utils import Utils diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index 211ee78b8f..aea456c151 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -8,7 +8,7 @@ import logging import time -from .logger.logger import logger +from modules.testing.utils import logger paramiko_logger = logging.getLogger("paramiko") diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index 87fc6ac6c7..ffc4711689 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -8,7 +8,7 @@ from ..helpers.agent import WazuhAgent from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py index cc62fc7844..3f02779ba2 100644 --- a/deployability/modules/testing/tests/test_agent/test_registration.py +++ b/deployability/modules/testing/tests/test_agent/test_registration.py @@ -8,7 +8,7 @@ from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index 3f876b9b4c..0d44e890c8 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -6,7 +6,7 @@ import re from ..helpers.generic import GeneralComponentActions, HostInformation -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index 97808d5d24..f409bccd14 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -7,7 +7,7 @@ from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import GeneralComponentActions, Waits, HostInformation -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index ba9448a0f1..c5e507203a 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -9,7 +9,7 @@ from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_manager/test_install.py b/deployability/modules/testing/tests/test_manager/test_install.py index 52ea66d71b..0c0aa2eac6 100644 --- a/deployability/modules/testing/tests/test_manager/test_install.py +++ b/deployability/modules/testing/tests/test_manager/test_install.py @@ -8,7 +8,7 @@ from ..helpers.executor import WazuhAPI from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions from ..helpers.manager import WazuhManager -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_manager/test_restart.py b/deployability/modules/testing/tests/test_manager/test_restart.py index e1afa015ac..8c5c54f213 100644 --- a/deployability/modules/testing/tests/test_manager/test_restart.py +++ b/deployability/modules/testing/tests/test_manager/test_restart.py @@ -5,7 +5,7 @@ import pytest from ..helpers.generic import HostInformation, GeneralComponentActions -from modules.generic.logger import logger +from modules.testing.utils import logger @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_manager/test_stop.py b/deployability/modules/testing/tests/test_manager/test_stop.py index afb357ccfc..4b31594c43 100644 --- a/deployability/modules/testing/tests/test_manager/test_stop.py +++ b/deployability/modules/testing/tests/test_manager/test_stop.py @@ -5,7 +5,7 @@ import pytest from ..helpers.generic import HostInformation, GeneralComponentActions -from modules.generic.logger import logger +from modules.testing.utils import logger @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_manager/test_uninstall.py b/deployability/modules/testing/tests/test_manager/test_uninstall.py index c06d1c0b72..f212cd4109 100644 --- a/deployability/modules/testing/tests/test_manager/test_uninstall.py +++ b/deployability/modules/testing/tests/test_manager/test_uninstall.py @@ -7,7 +7,7 @@ from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostInformation, GeneralComponentActions from ..helpers.manager import WazuhManager -from modules.generic.logger import logger +from modules.testing.utils import logger @pytest.fixture(scope="module", autouse=True) From 7170ffc281bd6c8d8e357d64be2295c7ac026282 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Wed, 17 Apr 2024 12:46:42 -0300 Subject: [PATCH 037/195] Reduced the sleep time and increased the attempts --- deployability/modules/allocation/allocation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/allocation.py b/deployability/modules/allocation/allocation.py index 1818aa8633..802da474b6 100755 --- a/deployability/modules/allocation/allocation.py +++ b/deployability/modules/allocation/allocation.py @@ -188,7 +188,7 @@ def __generate_track_file(instance: Instance, provider_name: str, track_path: P return track_path @staticmethod - def __check_connection(inventory: models.InventoryOutput, attempts=15, sleep=60) -> None: + def __check_connection(inventory: models.InventoryOutput, attempts=30, sleep=30) -> None: """ Checks if the ssh connection is successful. From aaac82611ed1e93e806eb6db46aa96a185edf840 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Thu, 18 Apr 2024 09:39:48 -0300 Subject: [PATCH 038/195] Fixed know_host files for ssh connection --- .../modules/allocation/allocation.py | 6 ++-- .../modules/allocation/generic/models.py | 1 + .../modules/allocation/vagrant/provider.py | 31 ++++++++++++------- .../modules/allocation/vagrant/utils.py | 2 +- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/deployability/modules/allocation/allocation.py b/deployability/modules/allocation/allocation.py index 802da474b6..644e9b7b7a 100755 --- a/deployability/modules/allocation/allocation.py +++ b/deployability/modules/allocation/allocation.py @@ -140,13 +140,15 @@ def __generate_inventory(instance: Instance, inventory_path: Path) -> None: ansible_user=ssh_config.user, ansible_port=ssh_config.port, ansible_connection='ssh', - ansible_password=ssh_config.password) + ansible_password=ssh_config.password, + ansible_ssh_common_args='-o StrictHostKeyChecking=no') else: inventory = models.InventoryOutput(ansible_host=ssh_config.hostname, ansible_user=ssh_config.user, ansible_port=ssh_config.port, ansible_connection='ssh', - ansible_ssh_private_key_file=str(ssh_config.private_key)) + ansible_ssh_private_key_file=str(ssh_config.private_key), + ansible_ssh_common_args='-o StrictHostKeyChecking=no') with open(inventory_path, 'w') as f: yaml.dump(inventory.model_dump(exclude_none=True), f) logger.info(f"Inventory file generated at {inventory_path}") diff --git a/deployability/modules/allocation/generic/models.py b/deployability/modules/allocation/generic/models.py index fc5274a558..4f809043c3 100644 --- a/deployability/modules/allocation/generic/models.py +++ b/deployability/modules/allocation/generic/models.py @@ -31,6 +31,7 @@ class InventoryOutput(BaseModel): ansible_password: str | None = None ansible_connection: Literal['ssh', 'winrm'] | None = None ansible_winrm_server_cert_validation: Literal['ignore'] | None = None + ansible_ssh_common_args: Literal['-o StrictHostKeyChecking=no'] | None = None class TrackOutput(BaseModel): diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 6234cf5a87..d08f5f9d3b 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -294,9 +294,12 @@ def __remote_host(arch: str, action: str, os: str = None, instance_dir: Path = N if conn_ok: if action == 'create': - cmd = "sudo /usr/local/bin/prlctl list -j" - prlctl_output = subprocess.Popen(f"sshpass -p {ssh_password} ssh {ssh_user}@{server_ip} {cmd}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') - data_list = json.loads(prlctl_output) + try: + cmd = "sudo /usr/local/bin/prlctl list -j" + prlctl_output = subprocess.Popen(f"sshpass -p {ssh_password} ssh -o 'StrictHostKeyChecking no' {ssh_user}@{server_ip} {cmd}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') + data_list = json.loads(prlctl_output) + except Exception as e: + raise ValueError('Could not get VMs running on macStadium server: ' + str(e) + '.') uuid_count = 0 for item in data_list: if 'uuid' in item: @@ -330,12 +333,15 @@ def __remote_host(arch: str, action: str, os: str = None, instance_dir: Path = N if conn_ok: if action == 'create': - loadav_command = "\'python3 -c \"import psutil; print(psutil.getloadavg()[0])\"\'" - cpu_command = "\'python3 -c \"import psutil; print(psutil.getloadavg()[0]/ psutil.cpu_count() * 100)\"\'" - memory_command = "\'python3 -c \"import psutil; print(psutil.virtual_memory().percent)\"\'" - load_average = subprocess.Popen(f"sshpass -p {ssh_password} ssh {ssh_user}@{server_ip} {loadav_command}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') - cpu_usage = subprocess.Popen(f"sshpass -p {ssh_password} ssh {ssh_user}@{server_ip} {cpu_command}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') - memory_usage = subprocess.Popen(f"sshpass -p {ssh_password} ssh {ssh_user}@{server_ip} {memory_command}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') + try: + loadav_command = "\'python3 -c \"import psutil; print(psutil.getloadavg()[0])\"\'" + cpu_command = "\'python3 -c \"import psutil; print(psutil.getloadavg()[0]/ psutil.cpu_count() * 100)\"\'" + memory_command = "\'python3 -c \"import psutil; print(psutil.virtual_memory().percent)\"\'" + load_average = subprocess.Popen(f"sshpass -p {ssh_password} ssh -o 'StrictHostKeyChecking no' {ssh_user}@{server_ip} {loadav_command}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') + cpu_usage = subprocess.Popen(f"sshpass -p {ssh_password} ssh -o 'StrictHostKeyChecking no' {ssh_user}@{server_ip} {cpu_command}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') + memory_usage = subprocess.Popen(f"sshpass -p {ssh_password} ssh -o 'StrictHostKeyChecking no' {ssh_user}@{server_ip} {memory_command}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') + except Exception as e: + raise ValueError('Could not get server load average: ' + str(e) + '.') if float(load_average) <= 10.0 and float(cpu_usage) <= 70.0 and float(memory_usage) <= 75.0: logger.info(f"Using the black mini server to deploy.") @@ -382,8 +388,11 @@ def __remote_host(arch: str, action: str, os: str = None, instance_dir: Path = N if conn_ok: if action == 'create': - cmd = "sudo docker ps -a" - output = subprocess.Popen(f"ssh -i {ssh_key} {ssh_user}@{server_ip} {cmd}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') + try: + cmd = "sudo docker ps -a" + output = subprocess.Popen(f"ssh -i {ssh_key} {ssh_user}@{server_ip} {cmd}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') + except Exception as e: + raise ValueError('Could not get docker containers running on ppc64 server: ' + str(e) + '.') if '2222' in output and '8080' in output: raise ValueError(f"ppc64 server has full capacity, cannot host a new container") else: diff --git a/deployability/modules/allocation/vagrant/utils.py b/deployability/modules/allocation/vagrant/utils.py index d27b2dab80..aacd54b56b 100644 --- a/deployability/modules/allocation/vagrant/utils.py +++ b/deployability/modules/allocation/vagrant/utils.py @@ -23,7 +23,7 @@ def remote_command(cls, command: str | list, remote_host_parameters: dict) -> st ssh_user = remote_host_parameters['ssh_user'] if remote_host_parameters.get('ssh_password'): ssh_password = remote_host_parameters['ssh_password'] - ssh_command = f"sshpass -p {ssh_password} ssh {ssh_user}@{server_ip} {command}" + ssh_command = f"sshpass -p {ssh_password} ssh -o 'StrictHostKeyChecking no' {ssh_user}@{server_ip} {command}" if remote_host_parameters.get('ssh_key'): ssh_key = remote_host_parameters['ssh_key'] ssh_command = f"ssh -i {ssh_key} {ssh_user}@{server_ip} \"{command}\"" From 98abbd8d780b91b59889d9980f48f5a96a197e0c Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 18 Apr 2024 16:06:49 +0200 Subject: [PATCH 039/195] enhancement(#5229): macOS tests added --- deployability/modules/generic/ansible.py | 10 +- .../modules/testing/tests/helpers/agent.py | 116 ++++++++------ .../modules/testing/tests/helpers/executor.py | 54 ++++--- .../modules/testing/tests/helpers/generic.py | 143 ++++++++++++++---- .../modules/testing/tests/helpers/utils.py | 69 ++++++--- .../testing/tests/test_agent/test_install.py | 11 +- .../tests/test_agent/test_registration.py | 4 +- .../testing/tests/test_agent/test_restart.py | 2 +- .../testing/tests/test_agent/test_stop.py | 3 +- .../tests/test_agent/test_uninstall.py | 8 +- 10 files changed, 288 insertions(+), 132 deletions(-) diff --git a/deployability/modules/generic/ansible.py b/deployability/modules/generic/ansible.py index 0829c84b3e..06ccb9072b 100755 --- a/deployability/modules/generic/ansible.py +++ b/deployability/modules/generic/ansible.py @@ -1,8 +1,10 @@ # Copyright (C) 2015, Wazuh Inc. # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + import ansible_runner import jinja2 +from typing import Optional import yaml from pathlib import Path @@ -16,7 +18,8 @@ class Inventory(BaseModel): ansible_host: str | IPvAnyAddress ansible_user: str ansible_port: int - ansible_ssh_private_key_file: str + ansible_ssh_private_key_file: Optional[str] = None + ansible_password: Optional[str] = None class Ansible: @@ -118,10 +121,13 @@ def generate_inventory(self) -> dict: self.ansible_data.ansible_host: { 'ansible_port': self.ansible_data.ansible_port, 'ansible_user': self.ansible_data.ansible_user, - 'ansible_ssh_private_key_file': self.ansible_data.ansible_ssh_private_key_file + **({'ansible_ssh_private_key_file': self.ansible_data.ansible_ssh_private_key_file} + if hasattr(self.ansible_data, 'ansible_ssh_private_key_file') + else {'ansible_password': self.ansible_data.ansible_password}) } } } } + return inventory_data diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 7e60c5fbca..fdb1d2a869 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -23,6 +23,7 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv release = 'pre-release' os_type = HostInformation.get_os_type(inventory_path) + architecture = HostInformation.get_architecture(inventory_path) commands = [] if 'linux' in os_type: distribution = HostInformation.get_linux_distribution(inventory_path) @@ -64,11 +65,11 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv "NET STATUS WazuhSvc" ]) elif 'macos' in os_type: - if 'intel' in architecture: + if 'x86_64' in architecture: commands.extend([ f'curl -so wazuh-agent.pkg https://{s3_url}.wazuh.com/{release}/macos/wazuh-agent-{wazuh_version}-1.intel64.pkg && echo "WAZUH_MANAGER=\'MANAGER_IP\' && WAZUH_AGENT_NAME=\'{agent_name}\'" > /tmp/wazuh_envs && sudo installer -pkg ./wazuh-agent.pkg -target /' ]) - elif 'apple' in architecture: + elif 'arm64' in architecture: commands.extend([ f'curl -so wazuh-agent.pkg https://{s3_url}.wazuh.com/{release}/macos/wazuh-agent-{wazuh_version}-1.arm64.pkg && echo "WAZUH_MANAGER=\'MANAGER_IP\' && WAZUH_AGENT_NAME=\'{agent_name}\'" > /tmp/wazuh_envs && sudo installer -pkg ./wazuh-agent.pkg -target /' ]) @@ -95,15 +96,23 @@ def register_agent(inventory_path, manager_path): manager_path = yaml.safe_load(yaml_file) host = manager_path.get('ansible_host') - internal_ip = HostInformation.get_internal_ip_from_aws_dns(host) if 'amazonaws' in host else host - - commands = [ - f"sed -i 's/
MANAGER_IP<\/address>/
{internal_ip}<\/address>/g' {WAZUH_CONF}", - "systemctl restart wazuh-agent" - ] - - Executor.execute_commands(inventory_path, commands) - assert internal_ip in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the Manager IP ({internal_ip})in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + if 'linux' in inventory_path: + host_ip = HostInformation.get_internal_ip_from_aws_dns(host) if 'amazonaws' in host else host + commands = [ + f"sed -i 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_CONF}", + "systemctl restart wazuh-agent" + ] + Executor.execute_commands(inventory_path, commands) + assert host_ip in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + + elif 'macos' in inventory_path: + host_ip = HostInformation.get_public_ip_from_aws_dns(host) if 'amazonaws' in host else host + commands = [ + f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' /Library/Ossec/etc/ossec.conf", + "/Library/Ossec/bin/wazuh-control restart" + ] + Executor.execute_commands(inventory_path, commands) + assert host_ip in Executor.execute_command(inventory_path, f'cat /Library/Ossec/etc/ossec.conf'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') @staticmethod @@ -190,41 +199,47 @@ def perform_action_and_scan(agent_params, action_callback) -> dict: os_name = HostInformation.get_os_name_from_inventory(agent_params) logger.info(f'Applying filters in checkfiles in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') - if 'debian' in os_name: - filter_data = { - '/boot': {'added': [], 'removed': [], 'modified': ['grubenv']}, - '/usr/bin': { - 'added': [ - 'unattended-upgrade', 'gapplication', 'add-apt-repository', 'gpg-wks-server', 'pkexec', 'gpgsplit', - 'watchgnupg', 'pinentry-curses', 'gpg-zip', 'gsettings', 'gpg-agent', 'gresource', 'gdbus', - 'gpg-connect-agent', 'gpgconf', 'gpgparsemail', 'lspgpot', 'pkaction', 'pkttyagent', 'pkmon', - 'dirmngr', 'kbxutil', 'migrate-pubring-from-classic-gpg', 'gpgcompose', 'pkcheck', 'gpgsm', 'gio', - 'pkcon', 'gpgtar', 'dirmngr-client', 'gpg', 'filebeat', 'gawk', 'curl', 'update-mime-database', - 'dh_installxmlcatalogs', 'appstreamcli', 'lspgpot', 'symcryptrun' - ], - 'removed': [], - 'modified': [] - }, - '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, - '/usr/sbin': { - 'added': [ - 'update-catalog', 'applygnupgdefaults', 'addgnupghome', 'install-sgmlcatalog', 'update-xmlcatalog' - ], - 'removed': [], - 'modified': [] + if 'linux' in agent_params: + if 'debian' in os_name: + filter_data = { + '/boot': {'added': [], 'removed': [], 'modified': ['grubenv']}, + '/usr/bin': { + 'added': [ + 'unattended-upgrade', 'gapplication', 'add-apt-repository', 'gpg-wks-server', 'pkexec', 'gpgsplit', + 'watchgnupg', 'pinentry-curses', 'gpg-zip', 'gsettings', 'gpg-agent', 'gresource', 'gdbus', + 'gpg-connect-agent', 'gpgconf', 'gpgparsemail', 'lspgpot', 'pkaction', 'pkttyagent', 'pkmon', + 'dirmngr', 'kbxutil', 'migrate-pubring-from-classic-gpg', 'gpgcompose', 'pkcheck', 'gpgsm', 'gio', + 'pkcon', 'gpgtar', 'dirmngr-client', 'gpg', 'filebeat', 'gawk', 'curl', 'update-mime-database', + 'dh_installxmlcatalogs', 'appstreamcli', 'lspgpot', 'symcryptrun' + ], + 'removed': [], + 'modified': [] + }, + '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, + '/usr/sbin': { + 'added': [ + 'update-catalog', 'applygnupgdefaults', 'addgnupghome', 'install-sgmlcatalog', 'update-xmlcatalog' + ], + 'removed': [], + 'modified': [] + } + } + else: + filter_data = { + '/boot': { + 'added': ['grub2', 'loader', 'vmlinuz', 'System.map', 'config-', 'initramfs'], + 'removed': [], + 'modified': ['grubenv'] + }, + '/usr/bin': {'added': ['filebeat'], 'removed': [], 'modified': []}, + '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, + '/usr/sbin': {'added': [], 'removed': [], 'modified': []} + } + elif 'macos' in agent_params: + filter_data = { + '/usr/bin': {'added': [], 'removed': [], 'modified': []}, + '/usr/sbin': {'added': [], 'removed': [], 'modified': []} } - } - else: - filter_data = { - '/boot': { - 'added': ['grub2', 'loader', 'vmlinuz', 'System.map', 'config-', 'initramfs'], - 'removed': [], - 'modified': ['grubenv'] - }, - '/usr/bin': {'added': ['filebeat'], 'removed': [], 'modified': []}, - '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, - '/usr/sbin': {'added': [], 'removed': [], 'modified': []} - } # Use of filters for directory, changes in result.items(): @@ -248,7 +263,7 @@ def perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) - action_callback = lambda: WazuhAgent._install_agent_callback(wazuh_params, agent_name, agent_params) result = WazuhAgent.perform_action_and_scan(agent_params, action_callback) logger.info(f'Pre and post install checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}: {result}') - WazuhAgent.assert_results(result) + WazuhAgent.assert_results(result, agent_params) @staticmethod @@ -264,11 +279,11 @@ def perform_uninstall_and_scan_for_agent(agent_params, wazuh_params) -> None: action_callback = lambda: WazuhAgent._uninstall_agent_callback(wazuh_params, agent_params) result = WazuhAgent.perform_action_and_scan(agent_params, action_callback) logger.info(f'Pre and post uninstall checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}: {result}') - WazuhAgent.assert_results(result) + WazuhAgent.assert_results(result, agent_params) @staticmethod - def assert_results(result) -> None: + def assert_results(result, agent_params) -> None: """ Gets the status of an agent given its name. @@ -276,7 +291,10 @@ def assert_results(result) -> None: result (dict): result of comparison between pre and post action scan """ - categories = ['/root', '/usr/bin', '/usr/sbin', '/boot'] + if 'linux' in agent_params: + categories = ['/root', '/usr/bin', '/usr/sbin', '/boot'] + elif 'macos' in agent_params: + categories = ['/usr/bin', '/usr/sbin'] actions = ['added', 'modified', 'removed'] # Testing the results for category in categories: diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index 6d204d6ba8..f77c924c05 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -3,6 +3,7 @@ # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 import json +import paramiko import requests import subprocess import urllib3 @@ -19,24 +20,41 @@ def execute_command(inventory_path, command) -> str: with open(inventory_path, 'r') as yaml_file: inventory_data = yaml.safe_load(yaml_file) - host = inventory_data.get('ansible_host') - port = inventory_data.get('ansible_port') - private_key_path = inventory_data.get('ansible_ssh_private_key_file') - username = inventory_data.get('ansible_user') - - ssh_command = [ - "ssh", - "-i", private_key_path, - "-o", "StrictHostKeyChecking=no", - "-o", "UserKnownHostsFile=/dev/null", - "-p", str(port), - f"{username}@{host}", - "sudo", - command - ] - result = subprocess.run(ssh_command, stdout=subprocess.PIPE, text=True) - - return result.stdout + if 'ansible_ssh_private_key_file' in inventory_data: + host = inventory_data.get('ansible_host') + port = inventory_data.get('ansible_port') + private_key_path = inventory_data.get('ansible_ssh_private_key_file') + username = inventory_data.get('ansible_user') + + ssh_command = [ + "ssh", + "-i", private_key_path, + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-p", str(port), + f"{username}@{host}", + "sudo", + command + ] + result = subprocess.run(ssh_command, stdout=subprocess.PIPE, text=True) + + return result.stdout + else: + host = inventory_data.get('ansible_host', None) + port = inventory_data.get('ansible_port', 22) + user = inventory_data.get('ansible_user', None) + password = inventory_data.get('ansible_password', None) + + ssh_client = paramiko.SSHClient() + ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh_client.connect(hostname=host, port=port, username=user, password=password) + stdin, stdout, stderr = ssh_client.exec_command(f"sudo {command}") + + result = ''.join(stdout.readlines()) + + ssh_client.close() + + return result @staticmethod diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 63e5140ec9..3d33cd0187 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -32,7 +32,11 @@ def dir_exists(inventory_path, dir_path) -> str: Returns: bool: True or False """ - return 'true' in Executor.execute_command(inventory_path, f'test -d {dir_path} && echo "true" || echo "false"') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + return 'true' in Executor.execute_command(inventory_path, f'test -d {dir_path} && echo "true" || echo "false"') + elif os_type == 'macos': + return 'true' in Executor.execute_command(inventory_path, f'stat {dir_path} >/dev/null 2>&1 && echo "true" || echo "false"') @staticmethod @@ -47,7 +51,11 @@ def file_exists(inventory_path, file_path) -> bool: Returns: bool: True or False """ - return 'true' in Executor.execute_command(inventory_path, f'test -f {file_path} && echo "true" || echo "false"') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + return 'true' in Executor.execute_command(inventory_path, f'test -f {file_path} && echo "true" || echo "false"') + elif os_type == 'macos': + return 'true' in Executor.execute_command(inventory_path, f'stat {file_path} >/dev/null 2>&1 && echo "true" || echo "false"') @staticmethod @@ -61,8 +69,16 @@ def get_os_type(inventory_path) -> str: Returns: str: type of host (windows, linux, macos) """ - system = Executor.execute_command(inventory_path, 'uname') - return system.lower() + if 'manager' in inventory_path: + pattern = r'manager-(\w+)-' + elif 'agent' in inventory_path: + pattern = r'agent-(\w+)-' + result = re.search(pattern, inventory_path) + if result: + return result.group(1) + else: + return None + @staticmethod @@ -115,9 +131,9 @@ def get_os_name_from_inventory(inventory_path) -> str: str: linux os name (debian, ubuntu, opensuse, amazon, centos, redhat) """ if 'manager' in inventory_path: - os_name = re.search(r'/manager-linux-([^-]+)-', inventory_path).group(1) + os_name = re.search(r'/manager-[^-]+-([^-]+)-', inventory_path).group(1) elif 'agent' in inventory_path: - os_name = re.search(r'/agent-linux-([^-]+)-', inventory_path).group(1) + os_name = re.search(r'/agent-[^-]+-([^-]+)-', inventory_path).group(1) return os_name @@ -133,9 +149,9 @@ def get_os_name_and_version_from_inventory(inventory_path) -> tuple: tuple: linux os name and version (e.g., ('ubuntu', '22.04')) """ if 'manager' in inventory_path: - match = re.search(r'/manager-linux-([^-]+)-([^-]+)-', inventory_path) + match = re.search(r'/manager-[^-]+-([^-]+)-([^-]+)-', inventory_path) elif 'agent' in inventory_path: - match = re.search(r'/agent-linux-([^-]+)-([^-]+)-', inventory_path) + match = re.search(r'/agent-[^-]+-([^-]+)-([^-]+)-', inventory_path) if match: os_name = match.group(1) version = match.group(2) @@ -158,7 +174,7 @@ def get_current_dir(inventory_path) -> str: return Executor.execute_command(inventory_path, 'pwd').replace("\n","") @staticmethod - def get_internal_ip_from_aws_dns(dns_name): + def get_internal_ip_from_aws_dns(dns_name) -> str: """ It returns the private AWS IP from dns_name @@ -176,6 +192,24 @@ def get_internal_ip_from_aws_dns(dns_name): else: return None + @staticmethod + def get_public_ip_from_aws_dns(dns_name) -> str: + """ + It returns the public AWS IP from dns_name + + Args: + dns_name (str): host's dns public dns name + + Returns: + str: public ip + """ + try: + ip_address = socket.gethostbyname(dns_name) + return ip_address + except socket.gaierror as e: + logger.error("Error obtaining IP address:", e) + return None + @staticmethod def get_client_keys(inventory_path) -> list[dict]: """ @@ -188,7 +222,11 @@ def get_client_keys(inventory_path) -> list[dict]: list: List of dictionaries with the client keys. """ clients = [] - client_key = Executor.execute_command(inventory_path, f'cat {CLIENT_KEYS}') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + client_key = Executor.execute_command(inventory_path, f'cat {CLIENT_KEYS}') + elif os_type == 'macos': + client_key = Executor.execute_command(inventory_path, f'cat /Library/Ossec/etc/client.keys') lines = client_key.split('\n')[:-1] for line in lines: _id, name, address, password = line.strip().split() @@ -227,14 +265,18 @@ def disable_firewall(inventory_path) -> None: inventory_path: host's inventory path """ - commands = ["sudo systemctl stop firewalld", "sudo systemctl disable firewalld"] - if GeneralComponentActions.isComponentActive(inventory_path, 'firewalld'): - Executor.execute_commands(inventory_path, commands) + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + commands = ["sudo systemctl stop firewalld", "sudo systemctl disable firewalld"] + if GeneralComponentActions.isComponentActive(inventory_path, 'firewalld'): + Executor.execute_commands(inventory_path, commands) + logger.info(f'Firewall disabled on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + else: + logger.info(f'No Firewall to disable on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + elif os_type == 'macos': logger.info(f'Firewall disabled on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - else: - logger.info(f'No Firewall to disable on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - + Executor.execute_command(inventory_path, 'sudo pfctl -d') def _extract_hosts(paths, is_aws): @@ -457,10 +499,12 @@ def _checkfiles(inventory_path, os_type, directory, filter= None, hash_algorithm Returns: Dict: dict of directories:hash """ - if 'linux' in os_type or 'macos' in os_type: + if 'linux' in os_type: command = f'sudo find {directory} -type f -exec sha256sum {{}} + {filter}' result = Executor.execute_command(inventory_path, command) - + elif 'macos' in os_type: + command = f'sudo find {directory} -type f -exec shasum -a 256 {{}} \; {filter}' + result = Executor.execute_command(inventory_path, command) elif 'windows' in os_type: command = 'dir /a-d /b /s | findstr /v /c:"\\.$" /c:"\\..$"| find /c ":"' else: @@ -504,9 +548,15 @@ def perform_action_and_scan(inventory_path, callback) -> dict: """ os_type = HostInformation.get_os_type(inventory_path) - directories = ['/boot', '/usr/bin', '/root', '/usr/sbin'] - filters_keywords = ['grep', 'tar', 'coreutils', 'sed', 'procps', 'gawk', 'lsof', 'curl', 'openssl', 'libcap', 'apt-transport-https', 'libcap2-bin', 'software-properties-common', 'gnupg', 'gpg'] - filters = f"| grep -v {filters_keywords[0]}" + if 'linux' in inventory_path: + directories = ['/boot', '/usr/bin', '/root', '/usr/sbin'] + filters_keywords = ['grep', 'tar', 'coreutils', 'sed', 'procps', 'gawk', 'lsof', 'curl', 'openssl', 'libcap', 'apt-transport-https', 'libcap2-bin', 'software-properties-common', 'gnupg', 'gpg'] + filters = f"| grep -v {filters_keywords[0]}" + + elif 'macos' in inventory_path: + directories = ['/usr/bin', '/usr/sbin'] + filters_keywords = ['grep'] + filters = f"| grep -v {filters_keywords[0]}" for filter_ in filters_keywords[1:]: filters+= f" | grep -v {filter_}" @@ -533,8 +583,11 @@ def get_component_status(inventory_path, host_role) -> str: str: Role status """ logger.info(f'Getting status of {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - - return Executor.execute_command(inventory_path, f'systemctl status {host_role}') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + return Executor.execute_command(inventory_path, f'systemctl status {host_role}') + elif os_type == 'macos': + return Executor.execute_command(inventory_path, f'/Library/Ossec/bin/wazuh-control status | grep {host_role}') @staticmethod @@ -546,9 +599,12 @@ def component_stop(inventory_path, host_role) -> None: inventory_path: host's inventory path host_role: role of the component """ - logger.info(f'Stopping {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_command(inventory_path, f'systemctl stop {host_role}') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + return Executor.execute_command(inventory_path, f'systemctl stop {host_role}') + elif os_type == 'macos': + return Executor.execute_command(inventory_path, f'/Library/Ossec/bin/wazuh-control stop | grep {host_role}') @staticmethod @@ -562,7 +618,11 @@ def component_restart(inventory_path, host_role) -> None: """ logger.info(f'Restarting {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_command(inventory_path, f'systemctl restart {host_role}') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + return Executor.execute_command(inventory_path, f'systemctl restart {host_role}') + elif os_type == 'macos': + return Executor.execute_command(inventory_path, f'/Library/Ossec/bin/wazuh-control restart | grep {host_role}') @staticmethod @@ -576,7 +636,11 @@ def component_start(inventory_path, host_role) -> None: """ logger.info(f'Starting {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_command(inventory_path, f'systemctl restart {host_role}') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + return Executor.execute_command(inventory_path, f'systemctl start {host_role}') + elif os_type == 'macos': + return Executor.execute_command(inventory_path, f'/Library/Ossec/bin/wazuh-control start | grep {host_role}') @staticmethod @@ -590,7 +654,11 @@ def get_component_version(inventory_path) -> str: Returns: str: version """ - return Executor.execute_command(inventory_path, f'{WAZUH_CONTROL} info -v') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + return Executor.execute_command(inventory_path, f'{WAZUH_CONTROL} info -v') + elif os_type == 'macos': + return Executor.execute_command(inventory_path, f'/Library/Ossec/bin/wazuh-control info -v') @staticmethod @@ -604,7 +672,11 @@ def get_component_revision(inventory_path) -> str: Returns: str: revision number """ - return Executor.execute_command(inventory_path, f'{WAZUH_CONTROL} info -r') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + return Executor.execute_command(inventory_path, f'{WAZUH_CONTROL} info -r') + elif os_type == 'macos': + return Executor.execute_command(inventory_path, f'/Library/Ossec/bin/wazuh-control info -r') @staticmethod @@ -618,7 +690,11 @@ def hasAgentClientKeys(inventory_path) -> bool: Returns: bool: True/False """ - return 'true' in Executor.execute_command(inventory_path, f'[ -f {CLIENT_KEYS} ] && echo true || echo false') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + return HostInformation.file_exists(inventory_path,{CLIENT_KEYS}) + elif os_type == 'macos': + return HostInformation.file_exists(inventory_path, '/Library/Ossec/etc/client.keys') @staticmethod @@ -633,7 +709,12 @@ def isComponentActive(inventory_path, host_role) -> bool: Returns: bool: True/False """ - return 'active' == Executor.execute_command(inventory_path, f'systemctl is-active {host_role}').replace("\n", "") + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + return 'active' == Executor.execute_command(inventory_path, f'systemctl is-active {host_role}').replace("\n", "") + elif os_type == 'macos': + return f'com.{host_role.replace("-", ".")}' in Executor.execute_command(inventory_path, f'launchctl list | grep com.{host_role.replace("-", ".")}') + class Waits: diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index 211ee78b8f..5b7c4068fd 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -26,9 +26,9 @@ def extract_ansible_host(file_path) -> str: @staticmethod def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: if 'manager' in inventory_path: - match = re.search(r'/manager-linux-([^-]+)-([^-]+)-', inventory_path) + match = re.search(r'/manager-[^-]+-([^-]+)-([^-]+)-', inventory_path) elif 'agent' in inventory_path: - match = re.search(r'/agent-linux-([^-]+)-([^-]+)-', inventory_path) + match = re.search(r'/agent-[^-]+-([^-]+)-([^-]+)-', inventory_path) if match: os_name = match.group(1)+ '-' + match.group(2) logger.info(f'Checking connection to {os_name}') @@ -40,30 +40,55 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: except yaml.YAMLError: raise ValueError(logger.error(f'Invalid inventory information in {os_name}')) - host = inventory_data.get('ansible_host') - port = inventory_data.get('ansible_port') - private_key_path = inventory_data.get('ansible_ssh_private_key_file') - username = inventory_data.get('ansible_user') - - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - private_key = paramiko.RSAKey.from_private_key_file(private_key_path) + + if 'ansible_ssh_private_key_file' in inventory_data: + host = inventory_data.get('ansible_host') + port = inventory_data.get('ansible_port') + private_key_path = inventory_data.get('ansible_ssh_private_key_file') + username = inventory_data.get('ansible_user') - for attempt in range(1, attempts + 1): ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) private_key = paramiko.RSAKey.from_private_key_file(private_key_path) - try: - ssh.connect(hostname=host, port=port, username=username, pkey=private_key) - logger.info(f'Connection established successfully in {os_name}') - ssh.close() - return True - except paramiko.AuthenticationException: - logger.error(f'Authentication error. Check SSH credentials in {os_name}') - return False - except Exception as e: - logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') - time.sleep(sleep) + + for attempt in range(1, attempts + 1): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + private_key = paramiko.RSAKey.from_private_key_file(private_key_path) + try: + ssh.connect(hostname=host, port=port, username=username, pkey=private_key) + logger.info(f'Connection established successfully in {os_name}') + ssh.close() + return True + except paramiko.AuthenticationException: + logger.error(f'Authentication error. Check SSH credentials in {os_name}') + return False + except Exception as e: + logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') + time.sleep(sleep) + else: + host = inventory_data.get('ansible_host', None) + port = inventory_data.get('ansible_port', 22) + user = inventory_data.get('ansible_user', None) + password = inventory_data.get('ansible_password', None) + + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + for attempt in range(1, attempts + 1): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + ssh.connect(hostname=host, port=port, username=user, password=password) + logger.info(f'Connection established successfully in {os_name}') + ssh.close() + return True + except paramiko.AuthenticationException: + logger.error(f'Authentication error. Check SSH credentials in {os_name}') + return False + except Exception as e: + logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') + time.sleep(sleep) logger.error(f'Connection attempts failed after {attempts} tries. Connection timeout in {os_name}') return False diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index 87fc6ac6c7..90ebec0363 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -54,6 +54,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) + if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] @@ -84,15 +85,19 @@ def test_installation(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): WazuhAgent.perform_install_and_scan_for_agent(agent_params, agent_names, wazuh_params) + # Testing installation directory - for agent in wazuh_params['agents'].values(): - assert HostInformation.dir_exists(agent, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent)}') + for agent_names, agent_params in wazuh_params['agents'].items(): + if HostInformation.get_os_type(agent_params) == 'linux': + assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') + elif HostInformation.get_os_type(agent_params) == 'macos': + assert HostInformation.dir_exists(agent_params, '/Library/Ossec'), logger.error(f'The /Library/Ossec is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') def test_status(wazuh_params): for agent in wazuh_params['agents'].values(): agent_status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent') - assert 'loaded' in agent_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded') + assert 'loaded' in agent_status or 'not running' in agent_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded') def test_version(wazuh_params): diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py index cc62fc7844..a1b4659e8a 100644 --- a/deployability/modules/testing/tests/test_agent/test_registration.py +++ b/deployability/modules/testing/tests/test_agent/test_registration.py @@ -69,7 +69,7 @@ def test_registration(wazuh_params): def test_status(wazuh_params): for agent in wazuh_params['agents'].values(): - assert 'active' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') + assert 'active' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent') or 'is running' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') def test_connection(wazuh_params): @@ -82,7 +82,7 @@ def test_connection(wazuh_params): def test_isActive(wazuh_params): wazuh_api = WazuhAPI(wazuh_params['master']) for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by API') + assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index 3f876b9b4c..5585212c45 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -68,7 +68,7 @@ def test_restart(wazuh_params): def test_status(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert 'active' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') + assert 'active' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent') or 'is running' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') def test_connection(wazuh_params): diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index 97808d5d24..320dd73008 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -71,8 +71,7 @@ def test_stop(wazuh_params): GeneralComponentActions.component_stop(agent_params, 'wazuh-agent') for agent_names, agent_params in wazuh_params['agents'].items(): - assert 'inactive' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is still active by command') - assert not GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is still active by command') + assert 'inactive' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent') or 'not running' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is still active by command') expected_condition_func = lambda: 'disconnected' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index ba9448a0f1..77c0b46515 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -66,12 +66,16 @@ def setup_test_environment(wazuh_params): def test_uninstall(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not Active before the installation') - assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in the host {agent_names}') + if 'linux' in agent_params: + assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in the host {agent_names}') + elif 'macos' in wazuh_params: + assert HostInformation.dir_exists(agent_params, '/Library/Ossec'), logger.error(f'The /Library/Ossec is not present in the host {agent_names}') - # Agent installation + # Agent uninstallation for agent_names, agent_params in wazuh_params['agents'].items(): WazuhAgent.perform_uninstall_and_scan_for_agent(agent_params,wazuh_params) + # Manager uninstallation status check for agent_names, agent_params in wazuh_params['agents'].items(): assert 'Disconnected' in WazuhManager.get_agent_control_info(wazuh_params['master']), logger.error(f'{agent_names} is still connected in the Manager') From 2c925fa95bb9aa0668a23b8f8aeb52ec7b7d1664 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 18 Apr 2024 16:21:27 +0200 Subject: [PATCH 040/195] enhancement(#5229): Enhancement of os validation --- .../modules/testing/tests/helpers/agent.py | 16 ++- .../modules/testing/tests/helpers/generic.py | 6 +- .../agent/vagrant/test-agent-complete.yaml | 110 ++++++++++++++++++ .../examples/agent/vagrant/test.yaml | 34 ++++++ 4 files changed, 157 insertions(+), 9 deletions(-) create mode 100644 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete.yaml create mode 100755 deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index fdb1d2a869..5f6a24b78a 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -94,9 +94,11 @@ def register_agent(inventory_path, manager_path): with open(manager_path, 'r') as yaml_file: manager_path = yaml.safe_load(yaml_file) + host = manager_path.get('ansible_host') - if 'linux' in inventory_path: + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': host_ip = HostInformation.get_internal_ip_from_aws_dns(host) if 'amazonaws' in host else host commands = [ f"sed -i 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_CONF}", @@ -105,7 +107,7 @@ def register_agent(inventory_path, manager_path): Executor.execute_commands(inventory_path, commands) assert host_ip in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') - elif 'macos' in inventory_path: + elif os_type == 'macos': host_ip = HostInformation.get_public_ip_from_aws_dns(host) if 'amazonaws' in host else host commands = [ f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' /Library/Ossec/etc/ossec.conf", @@ -199,7 +201,7 @@ def perform_action_and_scan(agent_params, action_callback) -> dict: os_name = HostInformation.get_os_name_from_inventory(agent_params) logger.info(f'Applying filters in checkfiles in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') - if 'linux' in agent_params: + if 'linux' == HostInformation.get_os_type(agent_params): if 'debian' in os_name: filter_data = { '/boot': {'added': [], 'removed': [], 'modified': ['grubenv']}, @@ -235,7 +237,7 @@ def perform_action_and_scan(agent_params, action_callback) -> dict: '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, '/usr/sbin': {'added': [], 'removed': [], 'modified': []} } - elif 'macos' in agent_params: + elif 'macos' == HostInformation.get_os_type(agent_params): filter_data = { '/usr/bin': {'added': [], 'removed': [], 'modified': []}, '/usr/sbin': {'added': [], 'removed': [], 'modified': []} @@ -291,11 +293,13 @@ def assert_results(result, agent_params) -> None: result (dict): result of comparison between pre and post action scan """ - if 'linux' in agent_params: + os_name = HostInformation.get_os_name_from_inventory(agent_params) + if os_name == 'linux': categories = ['/root', '/usr/bin', '/usr/sbin', '/boot'] - elif 'macos' in agent_params: + elif os_name == 'macos': categories = ['/usr/bin', '/usr/sbin'] actions = ['added', 'modified', 'removed'] + # Testing the results for category in categories: for action in actions: diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 3d33cd0187..316866aff1 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -499,13 +499,13 @@ def _checkfiles(inventory_path, os_type, directory, filter= None, hash_algorithm Returns: Dict: dict of directories:hash """ - if 'linux' in os_type: + if 'linux' == os_type: command = f'sudo find {directory} -type f -exec sha256sum {{}} + {filter}' result = Executor.execute_command(inventory_path, command) - elif 'macos' in os_type: + elif 'macos' == os_type: command = f'sudo find {directory} -type f -exec shasum -a 256 {{}} \; {filter}' result = Executor.execute_command(inventory_path, command) - elif 'windows' in os_type: + elif 'windows' == os_type: command = 'dir /a-d /b /s | findstr /v /c:"\\.$" /c:"\\..$"| find /c ":"' else: logger.info(f'Unsupported operating system') diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete.yaml new file mode 100644 index 0000000000..648ea9788d --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete.yaml @@ -0,0 +1,110 @@ +version: 0.1 +description: This workflow is used to test agents' deployment for DDT1 PoC +variables: + agent-os: + - linux-oracle-9-amd64 + + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml new file mode 100755 index 0000000000..253016fae3 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml @@ -0,0 +1,34 @@ +version: 0.1 +description: Test agent restart with provisioning agents' with provision module +variables: + agent-os: + - macos-ventura-13.4.1-arm64 + manager-os: linux-ubuntu-18.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + + + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + From 8aa767e4d2da811d0b836cec543ab77d7adb5907 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 18 Apr 2024 16:26:28 +0200 Subject: [PATCH 041/195] enhancement(#5229): logger fixes --- deployability/modules/testing/tests/helpers/agent.py | 7 ++++--- deployability/modules/testing/tests/helpers/manager.py | 2 +- deployability/modules/testing/tests/helpers/utils.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 5f6a24b78a..57fd67ff74 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -8,7 +8,7 @@ from .constants import WAZUH_CONF, WAZUH_ROOT from .executor import Executor, WazuhAPI from .generic import HostInformation, CheckFiles -from .logger.logger import logger +from modules.generic.logger import logger class WazuhAgent: @@ -199,9 +199,10 @@ def perform_action_and_scan(agent_params, action_callback) -> dict: """ result = CheckFiles.perform_action_and_scan(agent_params, action_callback) os_name = HostInformation.get_os_name_from_inventory(agent_params) + os_type = HostInformation.get_os_type(agent_params) logger.info(f'Applying filters in checkfiles in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') - if 'linux' == HostInformation.get_os_type(agent_params): + if os_type == 'linux': if 'debian' in os_name: filter_data = { '/boot': {'added': [], 'removed': [], 'modified': ['grubenv']}, @@ -237,7 +238,7 @@ def perform_action_and_scan(agent_params, action_callback) -> dict: '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, '/usr/sbin': {'added': [], 'removed': [], 'modified': []} } - elif 'macos' == HostInformation.get_os_type(agent_params): + elif os_type == 'macos': filter_data = { '/usr/bin': {'added': [], 'removed': [], 'modified': []}, '/usr/sbin': {'added': [], 'removed': [], 'modified': []} diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index 3611c7f5af..20b0d4e4c1 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -8,7 +8,7 @@ from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT from .executor import Executor, WazuhAPI from .generic import HostInformation, CheckFiles -from .logger.logger import logger +from modules.generic.logger import logger from .utils import Utils diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index 5b7c4068fd..78cf3a676f 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -8,7 +8,7 @@ import logging import time -from .logger.logger import logger +from modules.generic.logger import logger paramiko_logger = logging.getLogger("paramiko") From 5045ba3d756fd731e2162484d1c75a7487ffc914 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 18 Apr 2024 19:18:04 +0200 Subject: [PATCH 042/195] fix(#5215): Adding granularity --- .../modules/testing/tests/helpers/agent.py | 32 +++++- .../modules/testing/tests/helpers/generic.py | 10 ++ .../tests/test_agent/test_basic_info.py | 84 +++++++++++++++ .../tests/test_agent/test_connection.py | 100 ++++++++++++++++++ .../testing/tests/test_agent/test_install.py | 10 -- .../tests/test_agent/test_registration.py | 7 +- .../testing/tests/test_agent/test_restart.py | 11 ++ .../testing/tests/test_agent/test_stop.py | 12 ++- .../tests/test_agent/test_uninstall.py | 14 ++- .../agent/aws/test-agent-complete.yaml | 2 +- .../agent/vagrant/test-agent-complete-1.yaml | 2 +- .../agent/vagrant/test-agent-complete-2.yaml | 2 +- 12 files changed, 265 insertions(+), 21 deletions(-) create mode 100644 deployability/modules/testing/tests/test_agent/test_basic_info.py create mode 100644 deployability/modules/testing/tests/test_agent/test_connection.py diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 7e60c5fbca..f699bd998e 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -103,7 +103,18 @@ def register_agent(inventory_path, manager_path): ] Executor.execute_commands(inventory_path, commands) - assert internal_ip in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the Manager IP ({internal_ip})in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + assert internal_ip in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the Manager IP ({internal_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + + + @staticmethod + def set_protocol_agent_connection(inventory_path, protocol): + commands = [ + f"sed -i 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_CONF}", + "systemctl restart wazuh-agent" + ] + + Executor.execute_commands(inventory_path, commands) + assert protocol in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') @staticmethod @@ -283,6 +294,25 @@ def assert_results(result) -> None: for action in actions: assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category}{action}') + def areAgent_processes_active(agent_params): + """ + Check if agent processes are active + Args: + agent_name (str): Agent name. + Returns: + str: Os name. + """ + return bool([int(numero) for numero in Executor.execute_command(agent_params, 'pgrep wazuh').splitlines()]) + + def isAgent_port_open(agent_params): + """ + Check if agent port is open + Args: + agent_name (str): Agent name. + Returns: + str: Os name. + """ + return 'ESTAB' in Executor.execute_command(agent_params, 'ss -t -a -n | grep ":1514" | grep ESTAB') def get_agents_information(wazuh_api: WazuhAPI) -> list: """ diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 63e5140ec9..933e681c56 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -143,6 +143,16 @@ def get_os_name_and_version_from_inventory(inventory_path) -> tuple: else: return None + @staticmethod + def get_os_version_from_inventory(inventory_path) -> str: + if 'manager' in inventory_path: + os_version = re.search(r".*?/manager-linux-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) + elif 'agent' in inventory_path: + os_version = re.search(r".*?/agent-linux-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) + return os_version + else: + return None + @staticmethod def get_current_dir(inventory_path) -> str: """ diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py new file mode 100644 index 0000000000..5df7c38220 --- /dev/null +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -0,0 +1,84 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest +import re + +from ..helpers.agent import WazuhAgent, WazuhAPI +from ..helpers.constants import WAZUH_ROOT +from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions, Waits +from modules.generic.logger import logger +from ..helpers.manager import WazuhManager +from ..helpers.utils import Utils + + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + live = request.config.getoption('--live') + + return { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets, + 'live': live + } + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['agents'] = [value for key, value in targets_dict.items() if key.startswith('agent')] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + wazuh_params['agents'] = {key + '-' + re.findall(r'agent-(.*?)/', value)[0].replace('.',''): value for key, value in targets_dict.items() if key.startswith('agent')} + + updated_agents = {} + for agent_name, agent_params in wazuh_params['agents'].items(): + Utils.check_inventory_connection(agent_params) + if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if HostInformation.get_client_keys(agent_params) != []: + client_name = HostInformation.get_client_keys(agent_params)[0]['name'] + updated_agents[client_name] = agent_params + else: + updated_agents[agent_name] = agent_params + if updated_agents != {}: + wazuh_params['agents'] = updated_agents + + +def test_wazuh_os_version(wazuh_params): + wazuh_api = WazuhAPI(wazuh_params['master']) + for agent_names, agent_params in wazuh_params['agents'].items(): + assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') + expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) + Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) + assert HostInformation.get_os_version_from_inventory(agent_params) in WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names), logger.error('There is a mismatch between the OS version and the OS version of the installed agent') + assert HostInformation.get_os_name_from_inventory(agent_params) in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') + + +def test_wazuh_version(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert wazuh_params['wazuh_version'] in GeneralComponentActions.get_component_version(agent_params), logger.error(f"The version {HostInformation.get_os_name_and_version_from_inventory(agent_params)} is not {wazuh_params['wazuh_version']} by command") + + +def test_wazuh_revision(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert wazuh_params['wazuh_revision'] in GeneralComponentActions.get_component_revision(agent_params), logger.error(f"The revision {HostInformation.get_os_name_and_version_from_inventory(agent_params)} is not {wazuh_params['wazuh_revision']} by command") \ No newline at end of file diff --git a/deployability/modules/testing/tests/test_agent/test_connection.py b/deployability/modules/testing/tests/test_agent/test_connection.py new file mode 100644 index 0000000000..2be700e372 --- /dev/null +++ b/deployability/modules/testing/tests/test_agent/test_connection.py @@ -0,0 +1,100 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest +import re + +from ..helpers.agent import WazuhAgent, WazuhAPI +from ..helpers.generic import HostInformation, GeneralComponentActions, Waits +from ..helpers.manager import WazuhManager, WazuhAPI +from modules.generic.logger import logger +from ..helpers.utils import Utils + + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + live = request.config.getoption('--live') + + return { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets, + 'live': live + } + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['agents'] = [value for key, value in targets_dict.items() if key.startswith('agent')] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + wazuh_params['agents'] = {key + '-' + re.findall(r'agent-(.*?)/', value)[0].replace('.',''): value for key, value in targets_dict.items() if key.startswith('agent')} + + updated_agents = {} + for agent_name, agent_params in wazuh_params['agents'].items(): + Utils.check_inventory_connection(agent_params) + if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if HostInformation.get_client_keys(agent_params) != []: + client_name = HostInformation.get_client_keys(agent_params)[0]['name'] + updated_agents[client_name] = agent_params + else: + updated_agents[agent_name] = agent_params + if updated_agents != {}: + wazuh_params['agents'] = updated_agents + + +def test_connection(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + WazuhAgent.set_protocol_agent_connection(agent_params, 'tcp') + assert agent_names in WazuhManager.get_agent_control_info(wazuh_params['master']), f'The {agent_names} is not present in the master by command' + wazuh_api = WazuhAPI(wazuh_params['master']) + assert any(d.get('name') == agent_names for d in WazuhAgent.get_agents_information(wazuh_api)), logger.error(f'The {agent_names} is not present in the master by API') + + +def test_status(wazuh_params): + for agent in wazuh_params['agents'].values(): + assert 'active' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') + + +def test_service(wazuh_params): + wazuh_api = WazuhAPI(wazuh_params['master']) + for agent_names, agent_params in wazuh_params['agents'].items(): + assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by API') + + expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) + Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) + + +def test_clientKeys(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert GeneralComponentActions.hasAgentClientKeys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') + + +def test_port(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is closed') + + +def test_processes(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are not active') \ No newline at end of file diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index 87fc6ac6c7..7f78425cd7 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -93,13 +93,3 @@ def test_status(wazuh_params): for agent in wazuh_params['agents'].values(): agent_status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent') assert 'loaded' in agent_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded') - - -def test_version(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): - assert wazuh_params['wazuh_version'] in GeneralComponentActions.get_component_version(agent_params), logger.error(f"The version {HostInformation.get_os_name_and_version_from_inventory(agent_params)} is not {wazuh_params['wazuh_version']} by command") - - -def test_revision(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): - assert wazuh_params['wazuh_revision'] in GeneralComponentActions.get_component_revision(agent_params), logger.error(f"The revision {HostInformation.get_os_name_and_version_from_inventory(agent_params)} is not {wazuh_params['wazuh_revision']} by command") diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py index cc62fc7844..88c018c629 100644 --- a/deployability/modules/testing/tests/test_agent/test_registration.py +++ b/deployability/modules/testing/tests/test_agent/test_registration.py @@ -62,12 +62,11 @@ def setup_test_environment(wazuh_params): if updated_agents != {}: wazuh_params['agents'] = updated_agents -def test_registration(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): - WazuhAgent.register_agent(agent_params, wazuh_params['master']) def test_status(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + WazuhAgent.register_agent(agent_params, wazuh_params['master']) for agent in wazuh_params['agents'].values(): assert 'active' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') @@ -79,7 +78,7 @@ def test_connection(wazuh_params): assert any(d.get('name') == agent_names for d in WazuhAgent.get_agents_information(wazuh_api)), logger.error(f'The {agent_names} is not present in the master by API') -def test_isActive(wazuh_params): +def test_service(wazuh_params): wazuh_api = WazuhAPI(wazuh_params['master']) for agent_names, agent_params in wazuh_params['agents'].items(): assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by API') diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index 3f876b9b4c..b4af0c9e26 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -5,6 +5,7 @@ import pytest import re +from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import GeneralComponentActions, HostInformation from modules.generic.logger import logger from ..helpers.manager import WazuhManager @@ -84,3 +85,13 @@ def test_isActive(wazuh_params): def test_clientKeys(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): assert GeneralComponentActions.hasAgentClientKeys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') + + +def test_port(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is closed') + + +def test_processes(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are not active') \ No newline at end of file diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index 97808d5d24..e070af193e 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -65,7 +65,7 @@ def setup_test_environment(wazuh_params): if updated_agents != {}: wazuh_params['agents'] = updated_agents -def test_stop(wazuh_params): +def test_service(wazuh_params): wazuh_api = WazuhAPI(wazuh_params['master']) for agent_names, agent_params in wazuh_params['agents'].items(): GeneralComponentActions.component_stop(agent_params, 'wazuh-agent') @@ -76,3 +76,13 @@ def test_stop(wazuh_params): expected_condition_func = lambda: 'disconnected' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) + + +def test_port(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert not WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is still opened') + + +def test_processes(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') \ No newline at end of file diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index ba9448a0f1..5eea05b3be 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -82,10 +82,20 @@ def test_agent_uninstalled_directory(wazuh_params): assert not HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is still present in the agent {agent_names}') -def test_isActive(wazuh_params): +def test_service(wazuh_params): wazuh_api = WazuhAPI(wazuh_params['master']) for agent_names, agent_params in wazuh_params['agents'].items(): assert not GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not inactive by command') expected_condition_func = lambda: 'disconnected' == WazuhAgent.get_agent_status(wazuh_api, agent_names) - Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) \ No newline at end of file + Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) + + +def test_port(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert not WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is still opened') + + +def test_processes(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml index dcfe7f0ecd..d9d581b111 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml @@ -110,7 +110,7 @@ tasks: - targets: - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" + - tests: "install,registration,connection,basic_info,restart,stop,uninstall" - component: "agent" - wazuh-version: "4.7.3" - wazuh-revision: "40714" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml index ea9907ff0a..0d1780775e 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml @@ -104,7 +104,7 @@ tasks: - targets: - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" + - tests: "install,registration,connection,basic_info,restart,stop,uninstall" - component: "agent" - wazuh-version: "4.7.3" - wazuh-revision: "40714" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml index 428e6e5baa..2af6a19463 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml @@ -104,7 +104,7 @@ tasks: - targets: - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" + - tests: "install,registration,connection,basic_info,restart,stop,uninstall" - component: "agent" - wazuh-version: "4.7.3" - wazuh-revision: "40714" From 852bbe56dbdc694289258ff99b32a359bccc47b3 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 18 Apr 2024 19:23:37 +0200 Subject: [PATCH 043/195] fix(#5215): Adding missing part --- .../modules/testing/tests/helpers/agent.py | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index f699bd998e..fb5e59c495 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -368,6 +368,42 @@ def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): logger.error(f"Unexpected error: {e}") return [None, None, None] + def get_agent_os_version_by_name(wazuh_api: WazuhAPI, agent_name): + """ + Get Agent os version by Agent name + Args: + agent_name (str): Agent name. + Returns: + str: Os version. + """ + response = requests.get(f"{wazuh_api.api_url}/agents", headers=wazuh_api.headers, verify=False) + try: + for agent_data in eval(response.text)['data']['affected_items']: + if agent_data.get('name') == agent_name: + return agent_data.get('os', {}).get('version') + except Exception as e: + logger.error(f"Unexpected error: {e}") + return f"Unexpected error: {e}" + + + def get_agent_os_name_by_name(wazuh_api: WazuhAPI, agent_name): + """ + Get Agent os name by Agent name + Args: + agent_name (str): Agent name. + Returns: + str: Os name. + """ + response = requests.get(f"{wazuh_api.api_url}/agents", headers=wazuh_api.headers, verify=False) + try: + for agent_data in eval(response.text)['data']['affected_items']: + if agent_data.get('name') == agent_name: + return 'suse' if agent_data.get('os', {}).get('name', '').lower() == 'sles' else agent_data.get('os', {}).get('name', '').lower() + except Exception as e: + logger.error(f"Unexpected error: {e}") + return f"Unexpected error: {e}" + return None + def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): """ @@ -389,6 +425,43 @@ def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): return [None, None, None] + def get_agent_os_version_by_name(wazuh_api: WazuhAPI, agent_name): + """ + Get Agent os version by Agent name + Args: + agent_name (str): Agent name. + Returns: + str: Os version. + """ + response = requests.get(f"{wazuh_api.api_url}/agents", headers=wazuh_api.headers, verify=False) + try: + for agent_data in eval(response.text)['data']['affected_items']: + if agent_data.get('name') == agent_name: + return agent_data.get('os', {}).get('version') + except Exception as e: + logger.error(f"Unexpected error: {e}") + return f"Unexpected error: {e}" + + + def get_agent_os_name_by_name(wazuh_api: WazuhAPI, agent_name): + """ + Get Agent os name by Agent name + Args: + agent_name (str): Agent name. + Returns: + str: Os name. + """ + response = requests.get(f"{wazuh_api.api_url}/agents", headers=wazuh_api.headers, verify=False) + try: + for agent_data in eval(response.text)['data']['affected_items']: + if agent_data.get('name') == agent_name: + return 'suse' if agent_data.get('os', {}).get('name', '').lower() == 'sles' else agent_data.get('os', {}).get('name', '').lower() + except Exception as e: + logger.error(f"Unexpected error: {e}") + return f"Unexpected error: {e}" + return None + + def add_agent_to_manager(wazuh_api: WazuhAPI, name, ip) -> str: """ Add an agent to the manager. From a0e82bbfbb34e030034c76ed65b3c484cd1aa7cf Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 18 Apr 2024 19:25:51 +0200 Subject: [PATCH 044/195] fix(#5215): Removing duplicated part --- .../modules/testing/tests/helpers/agent.py | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index fb5e59c495..0fbceca699 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -368,42 +368,6 @@ def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): logger.error(f"Unexpected error: {e}") return [None, None, None] - def get_agent_os_version_by_name(wazuh_api: WazuhAPI, agent_name): - """ - Get Agent os version by Agent name - Args: - agent_name (str): Agent name. - Returns: - str: Os version. - """ - response = requests.get(f"{wazuh_api.api_url}/agents", headers=wazuh_api.headers, verify=False) - try: - for agent_data in eval(response.text)['data']['affected_items']: - if agent_data.get('name') == agent_name: - return agent_data.get('os', {}).get('version') - except Exception as e: - logger.error(f"Unexpected error: {e}") - return f"Unexpected error: {e}" - - - def get_agent_os_name_by_name(wazuh_api: WazuhAPI, agent_name): - """ - Get Agent os name by Agent name - Args: - agent_name (str): Agent name. - Returns: - str: Os name. - """ - response = requests.get(f"{wazuh_api.api_url}/agents", headers=wazuh_api.headers, verify=False) - try: - for agent_data in eval(response.text)['data']['affected_items']: - if agent_data.get('name') == agent_name: - return 'suse' if agent_data.get('os', {}).get('name', '').lower() == 'sles' else agent_data.get('os', {}).get('name', '').lower() - except Exception as e: - logger.error(f"Unexpected error: {e}") - return f"Unexpected error: {e}" - return None - def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): """ From ba5e41076e2a675dab1cd8d9de4ea8f04b10d471 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 18 Apr 2024 19:27:34 +0200 Subject: [PATCH 045/195] fix(#5215): indentation fixes --- deployability/modules/testing/tests/helpers/agent.py | 4 ++++ .../modules/testing/tests/test_agent/test_basic_info.py | 2 +- .../modules/testing/tests/test_agent/test_connection.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 0fbceca699..981d72f800 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -392,8 +392,10 @@ def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): def get_agent_os_version_by_name(wazuh_api: WazuhAPI, agent_name): """ Get Agent os version by Agent name + Args: agent_name (str): Agent name. + Returns: str: Os version. """ @@ -410,8 +412,10 @@ def get_agent_os_version_by_name(wazuh_api: WazuhAPI, agent_name): def get_agent_os_name_by_name(wazuh_api: WazuhAPI, agent_name): """ Get Agent os name by Agent name + Args: agent_name (str): Agent name. + Returns: str: Os name. """ diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index 5df7c38220..a4fe8da828 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -81,4 +81,4 @@ def test_wazuh_version(wazuh_params): def test_wazuh_revision(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert wazuh_params['wazuh_revision'] in GeneralComponentActions.get_component_revision(agent_params), logger.error(f"The revision {HostInformation.get_os_name_and_version_from_inventory(agent_params)} is not {wazuh_params['wazuh_revision']} by command") \ No newline at end of file + assert wazuh_params['wazuh_revision'] in GeneralComponentActions.get_component_revision(agent_params), logger.error(f"The revision {HostInformation.get_os_name_and_version_from_inventory(agent_params)} is not {wazuh_params['wazuh_revision']} by command") diff --git a/deployability/modules/testing/tests/test_agent/test_connection.py b/deployability/modules/testing/tests/test_agent/test_connection.py index 2be700e372..144fbc7cbb 100644 --- a/deployability/modules/testing/tests/test_agent/test_connection.py +++ b/deployability/modules/testing/tests/test_agent/test_connection.py @@ -97,4 +97,4 @@ def test_port(wazuh_params): def test_processes(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are not active') \ No newline at end of file + assert WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are not active') From cd9f2e3cf4f9faa4940a3da60ba24fe8bd3747b6 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 18 Apr 2024 19:29:36 +0200 Subject: [PATCH 046/195] fix(#5215): space fixes --- deployability/modules/testing/tests/helpers/agent.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 981d72f800..965f2f39de 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -297,8 +297,10 @@ def assert_results(result) -> None: def areAgent_processes_active(agent_params): """ Check if agent processes are active + Args: agent_name (str): Agent name. + Returns: str: Os name. """ From 5d21f057836c651bb980246279cad16287278801 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 18 Apr 2024 19:30:11 +0200 Subject: [PATCH 047/195] fix(#5215): space fixes2 --- deployability/modules/testing/tests/helpers/agent.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 965f2f39de..f3bb1efacf 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -309,8 +309,10 @@ def areAgent_processes_active(agent_params): def isAgent_port_open(agent_params): """ Check if agent port is open + Args: agent_name (str): Agent name. + Returns: str: Os name. """ From db0825d5f8d0fcd9940aeff10e6bd9f6487d71e8 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 18 Apr 2024 19:31:56 +0200 Subject: [PATCH 048/195] fix(#5215): space fixes3 --- deployability/modules/testing/tests/test_agent/test_stop.py | 2 +- .../modules/testing/tests/test_agent/test_uninstall.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index e070af193e..3ab709ff4d 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -85,4 +85,4 @@ def test_port(wazuh_params): def test_processes(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') \ No newline at end of file + assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index 5eea05b3be..71c78297aa 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -98,4 +98,4 @@ def test_port(wazuh_params): def test_processes(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') \ No newline at end of file + assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') From b28f2ad5aca8c9e9983ac19013a8c06f96c093f0 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 18 Apr 2024 20:31:14 +0200 Subject: [PATCH 049/195] fix(#5215): Fixing test log imports --- deployability/modules/testing/testing.py | 2 +- deployability/modules/testing/tests/helpers/agent.py | 2 +- deployability/modules/testing/tests/helpers/generic.py | 2 +- deployability/modules/testing/tests/helpers/manager.py | 2 +- deployability/modules/testing/tests/helpers/utils.py | 2 +- .../modules/testing/tests/test_agent/test_basic_info.py | 2 +- .../modules/testing/tests/test_agent/test_connection.py | 2 +- deployability/modules/testing/tests/test_agent/test_install.py | 2 +- .../modules/testing/tests/test_agent/test_registration.py | 2 +- deployability/modules/testing/tests/test_agent/test_restart.py | 2 +- deployability/modules/testing/tests/test_agent/test_stop.py | 2 +- .../modules/testing/tests/test_agent/test_uninstall.py | 2 +- .../modules/testing/tests/test_manager/test_install.py | 2 +- .../modules/testing/tests/test_manager/test_restart.py | 2 +- deployability/modules/testing/tests/test_manager/test_stop.py | 2 +- .../modules/testing/tests/test_manager/test_uninstall.py | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/deployability/modules/testing/testing.py b/deployability/modules/testing/testing.py index f6e7c5b881..c0f7ee985e 100644 --- a/deployability/modules/testing/testing.py +++ b/deployability/modules/testing/testing.py @@ -9,7 +9,7 @@ from modules.generic.utils import Utils from pathlib import Path from .models import InputPayload, ExtraVars -from .utils import logger +from modules.testing.utils import logger class Tester: diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index f3bb1efacf..3815bbd4ea 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -8,7 +8,7 @@ from .constants import WAZUH_CONF, WAZUH_ROOT from .executor import Executor, WazuhAPI from .generic import HostInformation, CheckFiles -from .logger.logger import logger +from modules.testing.utils import logger class WazuhAgent: diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 933e681c56..c584ebb741 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -14,7 +14,7 @@ from pathlib import Path from .constants import WAZUH_CONTROL, CLIENT_KEYS from .executor import Executor -from modules.generic.logger import logger +from modules.testing.utils import logger from .utils import Utils diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index 3611c7f5af..2ac8811ab7 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -8,7 +8,7 @@ from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT from .executor import Executor, WazuhAPI from .generic import HostInformation, CheckFiles -from .logger.logger import logger +from modules.testing.utils import logger from .utils import Utils diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index 211ee78b8f..aea456c151 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -8,7 +8,7 @@ import logging import time -from .logger.logger import logger +from modules.testing.utils import logger paramiko_logger = logging.getLogger("paramiko") diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index a4fe8da828..3b81a0765f 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -8,7 +8,7 @@ from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions, Waits -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_agent/test_connection.py b/deployability/modules/testing/tests/test_agent/test_connection.py index 144fbc7cbb..4a7297596a 100644 --- a/deployability/modules/testing/tests/test_agent/test_connection.py +++ b/deployability/modules/testing/tests/test_agent/test_connection.py @@ -8,7 +8,7 @@ from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index 7f78425cd7..059d46273f 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -8,7 +8,7 @@ from ..helpers.agent import WazuhAgent from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py index 88c018c629..dc756410d5 100644 --- a/deployability/modules/testing/tests/test_agent/test_registration.py +++ b/deployability/modules/testing/tests/test_agent/test_registration.py @@ -8,7 +8,7 @@ from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index b4af0c9e26..734e4791e3 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -7,7 +7,7 @@ from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import GeneralComponentActions, HostInformation -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index 3ab709ff4d..a347bce95f 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -7,7 +7,7 @@ from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import GeneralComponentActions, Waits, HostInformation -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index 71c78297aa..953e73bc5e 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -9,7 +9,7 @@ from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_manager/test_install.py b/deployability/modules/testing/tests/test_manager/test_install.py index 52ea66d71b..0c0aa2eac6 100644 --- a/deployability/modules/testing/tests/test_manager/test_install.py +++ b/deployability/modules/testing/tests/test_manager/test_install.py @@ -8,7 +8,7 @@ from ..helpers.executor import WazuhAPI from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions from ..helpers.manager import WazuhManager -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_manager/test_restart.py b/deployability/modules/testing/tests/test_manager/test_restart.py index e1afa015ac..8c5c54f213 100644 --- a/deployability/modules/testing/tests/test_manager/test_restart.py +++ b/deployability/modules/testing/tests/test_manager/test_restart.py @@ -5,7 +5,7 @@ import pytest from ..helpers.generic import HostInformation, GeneralComponentActions -from modules.generic.logger import logger +from modules.testing.utils import logger @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_manager/test_stop.py b/deployability/modules/testing/tests/test_manager/test_stop.py index afb357ccfc..4b31594c43 100644 --- a/deployability/modules/testing/tests/test_manager/test_stop.py +++ b/deployability/modules/testing/tests/test_manager/test_stop.py @@ -5,7 +5,7 @@ import pytest from ..helpers.generic import HostInformation, GeneralComponentActions -from modules.generic.logger import logger +from modules.testing.utils import logger @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_manager/test_uninstall.py b/deployability/modules/testing/tests/test_manager/test_uninstall.py index c06d1c0b72..f212cd4109 100644 --- a/deployability/modules/testing/tests/test_manager/test_uninstall.py +++ b/deployability/modules/testing/tests/test_manager/test_uninstall.py @@ -7,7 +7,7 @@ from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostInformation, GeneralComponentActions from ..helpers.manager import WazuhManager -from modules.generic.logger import logger +from modules.testing.utils import logger @pytest.fixture(scope="module", autouse=True) From 684ea5d21e36c8b665e73937593e7fefeeb51f37 Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 19 Apr 2024 12:30:09 +0200 Subject: [PATCH 050/195] enhancement(#5229): Improving get_os_type and arch --- .../modules/testing/tests/helpers/agent.py | 10 +-- .../modules/testing/tests/helpers/generic.py | 43 ++++++--- .../modules/testing/tests/helpers/manager.py | 2 +- .../modules/testing/tests/helpers/utils.py | 2 +- .../testing/tests/test_agent/test_install.py | 2 +- .../tests/test_agent/test_registration.py | 2 +- .../testing/tests/test_agent/test_restart.py | 2 +- .../testing/tests/test_agent/test_stop.py | 2 +- .../tests/test_agent/test_uninstall.py | 2 +- .../examples/agent/vagrant/test.yaml | 88 +++++++++++++++++-- 10 files changed, 124 insertions(+), 31 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 57fd67ff74..758eea6a2e 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -8,7 +8,7 @@ from .constants import WAZUH_CONF, WAZUH_ROOT from .executor import Executor, WazuhAPI from .generic import HostInformation, CheckFiles -from modules.generic.logger import logger +from modules.testing.utils import logger class WazuhAgent: @@ -29,19 +29,19 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv distribution = HostInformation.get_linux_distribution(inventory_path) architecture = HostInformation.get_architecture(inventory_path) - if distribution == 'rpm' and 'x86_64' in architecture: + if distribution == 'rpm' and 'amd64' in architecture: commands.extend([ f"curl -o wazuh-agent-{wazuh_version}-1.x86_64.rpm https://{s3_url}.wazuh.com/{release}/yum/wazuh-agent-{wazuh_version}-1.x86_64.rpm && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' rpm -ihv wazuh-agent-{wazuh_version}-1.x86_64.rpm" ]) - elif distribution == 'rpm' and 'aarch64' in architecture: + elif distribution == 'rpm' and 'arm64' in architecture: commands.extend([ f"curl -o wazuh-agent-{wazuh_version}-1aarch64.rpm https://{s3_url}.wazuh.com/{release}/yum/wazuh-agent-{wazuh_version}-1.aarch64.rpm && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' rpm -ihv wazuh-agent-{wazuh_version}-1.aarch64.rpm" ]) - elif distribution == 'deb' and 'x86_64' in architecture: + elif distribution == 'deb' and 'amd64' in architecture: commands.extend([ f"wget https://{s3_url}.wazuh.com/{release}/apt/pool/main/w/wazuh-agent/wazuh-agent_{wazuh_version}-1_amd64.deb && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' dpkg -i ./wazuh-agent_{wazuh_version}-1_amd64.deb" ]) - elif distribution == 'deb' and 'aarch64' in architecture: + elif distribution == 'deb' and 'arm64' in architecture: commands.extend([ f"wget https://{s3_url}.wazuh.com/{release}/apt/pool/main/w/wazuh-agent/wazuh-agent_{wazuh_version}-1_arm64.deb && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' dpkg -i ./wazuh-agent_{wazuh_version}-1arm64.deb" ]) diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 316866aff1..fd142fb3d8 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -14,7 +14,7 @@ from pathlib import Path from .constants import WAZUH_CONTROL, CLIENT_KEYS from .executor import Executor -from modules.generic.logger import logger +from modules.testing.utils import logger from .utils import Utils @@ -69,16 +69,19 @@ def get_os_type(inventory_path) -> str: Returns: str: type of host (windows, linux, macos) """ - if 'manager' in inventory_path: - pattern = r'manager-(\w+)-' - elif 'agent' in inventory_path: - pattern = r'agent-(\w+)-' - result = re.search(pattern, inventory_path) - if result: - return result.group(1) - else: - return None - + try: + with open(inventory_path.replace('inventory', 'track'), 'r') as file: + data = yaml.safe_load(file) + if 'platform' in data: + return data['platform'] + else: + raise KeyError("The 'platform' key was not found in the YAML file.") + except FileNotFoundError: + logger.error(f"The YAML file '{inventory_path}' was not found.") + except yaml.YAMLError as e: + logger.error(f"Error while loading the YAML file: {e}") + except Exception as e: + logger.error(f"An unexpected error occurred: {e}") @staticmethod @@ -90,9 +93,21 @@ def get_architecture(inventory_path) -> str: inventory_path: host's inventory path Returns: - str: architecture (aarch64, x86_64, intel, apple) + str: architecture (amd64, arm64, intel, apple) """ - return Executor.execute_command(inventory_path, 'uname -m') + try: + with open(inventory_path.replace('inventory', 'track'), 'r') as file: + data = yaml.safe_load(file) + if 'platform' in data: + return data['arch'] + else: + raise KeyError("The 'platform' key was not found in the YAML file.") + except FileNotFoundError: + logger.error(f"The YAML file '{inventory_path}' was not found.") + except yaml.YAMLError as e: + logger.error(f"Error while loading the YAML file: {e}") + except Exception as e: + logger.error(f"An unexpected error occurred: {e}") @staticmethod @@ -692,7 +707,7 @@ def hasAgentClientKeys(inventory_path) -> bool: """ os_type = HostInformation.get_os_type(inventory_path) if os_type == 'linux': - return HostInformation.file_exists(inventory_path,{CLIENT_KEYS}) + return HostInformation.file_exists(inventory_path, {CLIENT_KEYS}) elif os_type == 'macos': return HostInformation.file_exists(inventory_path, '/Library/Ossec/etc/client.keys') diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index 20b0d4e4c1..2ac8811ab7 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -8,7 +8,7 @@ from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT from .executor import Executor, WazuhAPI from .generic import HostInformation, CheckFiles -from modules.generic.logger import logger +from modules.testing.utils import logger from .utils import Utils diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index 78cf3a676f..24dd874c54 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -8,7 +8,7 @@ import logging import time -from modules.generic.logger import logger +from modules.testing.utils import logger paramiko_logger = logging.getLogger("paramiko") diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index 90ebec0363..bca54f2af7 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -8,7 +8,7 @@ from ..helpers.agent import WazuhAgent from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py index a1b4659e8a..05231a1594 100644 --- a/deployability/modules/testing/tests/test_agent/test_registration.py +++ b/deployability/modules/testing/tests/test_agent/test_registration.py @@ -8,7 +8,7 @@ from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index 5585212c45..b162cc9a7b 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -6,7 +6,7 @@ import re from ..helpers.generic import GeneralComponentActions, HostInformation -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index 320dd73008..510ce26a76 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -7,7 +7,7 @@ from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import GeneralComponentActions, Waits, HostInformation -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index 77c0b46515..e15daf6756 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -9,7 +9,7 @@ from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from modules.generic.logger import logger +from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml index 253016fae3..9174420f15 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml @@ -1,14 +1,91 @@ version: 0.1 -description: Test agent restart with provisioning agents' with provision module +description: This workflow is used to test agents' deployment for DDT1 PoC variables: agent-os: - - macos-ventura-13.4.1-arm64 - manager-os: linux-ubuntu-18.04-amd64 - infra-provider: aws + - linux-ubuntu-18.04-amd64 + + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant working-dir: /tmp/dtt1-poc tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" # Generic agent test task @@ -31,4 +108,5 @@ tasks: foreach: - variable: agent-os as: agent - + depends-on: + - "allocate-agent-{agent}" From f9f548ef61b7d5cf315b2b9b9d35d77e2cb46293 Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 19 Apr 2024 13:55:59 +0200 Subject: [PATCH 051/195] fix(#5229): Fixing clientkey checks --- deployability/modules/testing/tests/helpers/generic.py | 3 +-- .../modules/workflow_engine/examples/agent/vagrant/test.yaml | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index af8b18b7c6..fc3b6a6782 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -16,7 +16,6 @@ from .executor import Executor from modules.testing.utils import logger from .utils import Utils -from modules.testing.utils import logger class HostInformation: @@ -708,7 +707,7 @@ def hasAgentClientKeys(inventory_path) -> bool: """ os_type = HostInformation.get_os_type(inventory_path) if os_type == 'linux': - return HostInformation.file_exists(inventory_path, {CLIENT_KEYS}) + return HostInformation.file_exists(inventory_path, CLIENT_KEYS) elif os_type == 'macos': return HostInformation.file_exists(inventory_path, '/Library/Ossec/etc/client.keys') diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml index 9174420f15..88e0a753a0 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml @@ -2,8 +2,8 @@ version: 0.1 description: This workflow is used to test agents' deployment for DDT1 PoC variables: agent-os: + - macos-sonoma-14.0-arm64 - linux-ubuntu-18.04-amd64 - manager-os: linux-ubuntu-22.04-amd64 infra-provider: vagrant working-dir: /tmp/dtt1-poc @@ -19,13 +19,14 @@ tasks: args: - modules/allocation/main.py - action: create - - provider: "{infra-provider}" + - provider: "aws" - size: large - composite-name: "{manager-os}" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + - ssh-key: "/home/akim/Desktop/personal/Ephemeral" on-error: "abort-all" cleanup: this: process From 1f80707ebca5dd5c0c5743ab9924912926c444c8 Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 19 Apr 2024 17:05:56 +0200 Subject: [PATCH 052/195] fix(#5229): Adapting some validations --- .../modules/testing/tests/helpers/agent.py | 27 +++++++++++++++++-- .../tests/test_agent/test_connection.py | 2 +- .../testing/tests/test_agent/test_install.py | 2 +- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 98e92d93be..bcabfb114d 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -65,7 +65,7 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv "NET STATUS WazuhSvc" ]) elif 'macos' in os_type: - if 'x86_64' in architecture: + if 'amd64' in architecture: commands.extend([ f'curl -so wazuh-agent.pkg https://{s3_url}.wazuh.com/{release}/macos/wazuh-agent-{wazuh_version}-1.intel64.pkg && echo "WAZUH_MANAGER=\'MANAGER_IP\' && WAZUH_AGENT_NAME=\'{agent_name}\'" > /tmp/wazuh_envs && sudo installer -pkg ./wazuh-agent.pkg -target /' ]) @@ -117,6 +117,25 @@ def register_agent(inventory_path, manager_path): assert host_ip in Executor.execute_command(inventory_path, f'cat /Library/Ossec/etc/ossec.conf'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + @staticmethod + def set_protocol_agent_connection(inventory_path, protocol): + os_type = HostInformation.get_os_type(inventory_path) + if 'linux' in os_type: + commands = [ + f"sed -i 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_CONF}", + "systemctl restart wazuh-agent" + ] + Executor.execute_commands(inventory_path, commands) + assert protocol in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + elif 'macos' in os_type: + commands = [ + f"sed -i '' 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' /Library/Ossec/etc/ossec.conf", + "/Library/Ossec/bin/wazuh-control restart" + ] + Executor.execute_commands(inventory_path, commands) + assert protocol in Executor.execute_command(inventory_path, f'cat /Library/Ossec/etc/ossec.conf'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + + @staticmethod def uninstall_agent(inventory_path, wazuh_version=None, wazuh_revision=None) -> None: os_type = HostInformation.get_os_type(inventory_path) @@ -328,7 +347,11 @@ def isAgent_port_open(agent_params): Returns: str: Os name. """ - return 'ESTAB' in Executor.execute_command(agent_params, 'ss -t -a -n | grep ":1514" | grep ESTAB') + os_name = HostInformation.get_os_name_from_inventory(agent_params) + if os_name == 'linux': + return 'ESTAB' in Executor.execute_command(agent_params, 'ss -t -a -n | grep ":1514" | grep ESTAB') + elif os_name == 'macos': + return 'ESTABLISHED' in Executor.execute_command(agent_params, 'netstat -an | grep ".1514 " | grep ESTABLISHED') def get_agents_information(wazuh_api: WazuhAPI) -> list: """ diff --git a/deployability/modules/testing/tests/test_agent/test_connection.py b/deployability/modules/testing/tests/test_agent/test_connection.py index 4a7297596a..6d2377dc6f 100644 --- a/deployability/modules/testing/tests/test_agent/test_connection.py +++ b/deployability/modules/testing/tests/test_agent/test_connection.py @@ -73,7 +73,7 @@ def test_connection(wazuh_params): def test_status(wazuh_params): for agent in wazuh_params['agents'].values(): - assert 'active' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') + assert 'active' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent') or 'is running' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') def test_service(wazuh_params): diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index c922c0230a..043e290707 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -97,4 +97,4 @@ def test_installation(wazuh_params): def test_status(wazuh_params): for agent in wazuh_params['agents'].values(): agent_status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent') - assert 'loaded' in agent_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded') + assert 'loaded' in agent_status or 'not running' in agent_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded') From 6d22b61d2fa44842d6afd33a6e21a6823d8caa56 Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 22 Apr 2024 11:35:20 +0200 Subject: [PATCH 053/195] fix(#5229): Fixing connection check ostype --- .../modules/testing/tests/helpers/agent.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index bcabfb114d..79ea11044d 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -313,10 +313,10 @@ def assert_results(result, agent_params) -> None: result (dict): result of comparison between pre and post action scan """ - os_name = HostInformation.get_os_name_from_inventory(agent_params) - if os_name == 'linux': + os_type = HostInformation.get_os_type(agent_params) + if os_type == 'linux': categories = ['/root', '/usr/bin', '/usr/sbin', '/boot'] - elif os_name == 'macos': + elif os_type == 'macos': categories = ['/usr/bin', '/usr/sbin'] actions = ['added', 'modified', 'removed'] @@ -337,21 +337,21 @@ def areAgent_processes_active(agent_params): """ return bool([int(numero) for numero in Executor.execute_command(agent_params, 'pgrep wazuh').splitlines()]) - def isAgent_port_open(agent_params): + def isAgent_port_open(inventory_path): """ Check if agent port is open Args: - agent_name (str): Agent name. + inventory_path (str): Agent inventory path. Returns: str: Os name. """ - os_name = HostInformation.get_os_name_from_inventory(agent_params) - if os_name == 'linux': - return 'ESTAB' in Executor.execute_command(agent_params, 'ss -t -a -n | grep ":1514" | grep ESTAB') - elif os_name == 'macos': - return 'ESTABLISHED' in Executor.execute_command(agent_params, 'netstat -an | grep ".1514 " | grep ESTABLISHED') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + return 'ESTAB' in Executor.execute_command(inventory_path, 'ss -t -a -n | grep ":1514" | grep ESTAB') + elif os_type == 'macos': + return 'ESTABLISHED' in Executor.execute_command(inventory_path, 'netstat -an | grep ".1514 " | grep ESTABLISHED') def get_agents_information(wazuh_api: WazuhAPI) -> list: """ From 1e32d4dd086fe468247f12fd70871e3214a17a96 Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 22 Apr 2024 13:25:28 +0200 Subject: [PATCH 054/195] fix(#5229): Fixing validations in basic_info --- deployability/modules/testing/tests/helpers/agent.py | 2 +- deployability/modules/testing/tests/helpers/generic.py | 4 ++-- .../modules/testing/tests/test_agent/test_basic_info.py | 6 +++++- .../examples/agent/{vagrant => aws}/test.yaml | 8 +++----- 4 files changed, 11 insertions(+), 9 deletions(-) rename deployability/modules/workflow_engine/examples/agent/{vagrant => aws}/test.yaml (93%) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 79ea11044d..87ff4d2ee6 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -108,7 +108,7 @@ def register_agent(inventory_path, manager_path): assert host_ip in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') elif os_type == 'macos': - host_ip = HostInformation.get_public_ip_from_aws_dns(host) if 'amazonaws' in host else host + host_ip = HostInformation.get_internal_ip_from_aws_dns(host) if 'amazonaws' in host else HostInformation.get_public_ip_from_aws_dns(host) commands = [ f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' /Library/Ossec/etc/ossec.conf", "/Library/Ossec/bin/wazuh-control restart" diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index b55ecc75a0..eaeee50211 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -177,9 +177,9 @@ def get_os_name_and_version_from_inventory(inventory_path) -> tuple: @staticmethod def get_os_version_from_inventory(inventory_path) -> str: if 'manager' in inventory_path: - os_version = re.search(r".*?/manager-linux-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) + os_version = re.search(r".*?/manager-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) elif 'agent' in inventory_path: - os_version = re.search(r".*?/agent-linux-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) + os_version = re.search(r".*?/agent-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) return os_version else: return None diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index 3b81a0765f..b2f2b46dd7 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -67,7 +67,11 @@ def setup_test_environment(wazuh_params): def test_wazuh_os_version(wazuh_params): wazuh_api = WazuhAPI(wazuh_params['master']) for agent_names, agent_params in wazuh_params['agents'].items(): - assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') + if HostInformation.get_os_type(agent_params) == 'linux': + assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') + elif HostInformation.get_os_type(agent_params) == 'macos': + assert HostInformation.dir_exists(agent_params, '/Library/Ossec'), logger.error(f'The /Library/Ossec is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') + expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) assert HostInformation.get_os_version_from_inventory(agent_params) in WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names), logger.error('There is a mismatch between the OS version and the OS version of the installed agent') diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test.yaml similarity index 93% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml rename to deployability/modules/workflow_engine/examples/agent/aws/test.yaml index 88e0a753a0..dca534b7f0 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test.yaml @@ -2,10 +2,9 @@ version: 0.1 description: This workflow is used to test agents' deployment for DDT1 PoC variables: agent-os: - - macos-sonoma-14.0-arm64 - - linux-ubuntu-18.04-amd64 + - macos-sonoma-14.3-arm64 manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant + infra-provider: aws working-dir: /tmp/dtt1-poc tasks: @@ -26,7 +25,6 @@ tasks: - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - label-termination-date: "1d" - label-team: "qa" - - ssh-key: "/home/akim/Desktop/personal/Ephemeral" on-error: "abort-all" cleanup: this: process @@ -101,7 +99,7 @@ tasks: - targets: - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - component: "agent" - wazuh-version: "4.7.3" - wazuh-revision: "40714" From 0062465dc9714d82f7c0c63066d66d5ffd75ae2f Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 22 Apr 2024 13:26:21 +0200 Subject: [PATCH 055/195] fix(#5229): Renaming test file --- .../examples/agent/aws/{test.yaml => test-agent-macOs.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename deployability/modules/workflow_engine/examples/agent/aws/{test.yaml => test-agent-macOs.yaml} (100%) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-macOs.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/test.yaml rename to deployability/modules/workflow_engine/examples/agent/aws/test-agent-macOs.yaml From ddd25c48984b6125a1a8d93b489fc22c823865df Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 22 Apr 2024 18:21:01 +0200 Subject: [PATCH 056/195] fix(#5229): Fixes around install validation and test yamls --- .../modules/testing/tests/helpers/agent.py | 14 ++++++++++---- .../testing/tests/test_agent/test_basic_info.py | 5 ++++- .../testing/tests/test_agent/test_install.py | 2 -- .../test-agent-complete-macOs.yaml} | 12 +++++++++--- .../examples/agent/aws/test-agent-suse.yaml | 2 +- .../test-agent-complete-macOS.yaml} | 7 +++++-- 6 files changed, 29 insertions(+), 13 deletions(-) rename deployability/modules/workflow_engine/examples/agent/{vagrant/test-agent-complete.yaml => aws/test-agent-complete-macOs.yaml} (89%) mode change 100644 => 100755 rename deployability/modules/workflow_engine/examples/agent/{aws/test-agent-macOs.yaml => vagrant/test-agent-complete-macOS.yaml} (95%) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 87ff4d2ee6..857174117d 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -93,13 +93,16 @@ def install_agents(inventories_paths=[], wazuh_versions=[], wazuh_revisions=[], def register_agent(inventory_path, manager_path): with open(manager_path, 'r') as yaml_file: - manager_path = yaml.safe_load(yaml_file) + manager_path_yaml = yaml.safe_load(yaml_file) + manager_host = manager_path_yaml.get('ansible_host') - host = manager_path.get('ansible_host') + with open(inventory_path, 'r') as yaml_file: + inventory_path_yaml = yaml.safe_load(yaml_file) + agent_host = inventory_path_yaml.get('ansible_host') os_type = HostInformation.get_os_type(inventory_path) if os_type == 'linux': - host_ip = HostInformation.get_internal_ip_from_aws_dns(host) if 'amazonaws' in host else host + host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) if 'amazonaws' in manager_host else manager_host commands = [ f"sed -i 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_CONF}", "systemctl restart wazuh-agent" @@ -108,7 +111,10 @@ def register_agent(inventory_path, manager_path): assert host_ip in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') elif os_type == 'macos': - host_ip = HostInformation.get_internal_ip_from_aws_dns(host) if 'amazonaws' in host else HostInformation.get_public_ip_from_aws_dns(host) + if 'amazonaws' in manager_host and 'amazonaws' in agent_host: + host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) + else: + host_ip = HostInformation.get_public_ip_from_aws_dns(manager_host) commands = [ f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' /Library/Ossec/etc/ossec.conf", "/Library/Ossec/bin/wazuh-control restart" diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index b2f2b46dd7..1caa6c8cce 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -75,7 +75,10 @@ def test_wazuh_os_version(wazuh_params): expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) assert HostInformation.get_os_version_from_inventory(agent_params) in WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names), logger.error('There is a mismatch between the OS version and the OS version of the installed agent') - assert HostInformation.get_os_name_from_inventory(agent_params) in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') + if HostInformation.get_os_type(agent_params) == 'linux': + assert HostInformation.get_os_name_from_inventory(agent_params) in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') + elif HostInformation.get_os_type(agent_params) == 'macos': + assert 'macos' in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') def test_wazuh_version(wazuh_params): diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index 043e290707..b06c00b668 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -54,7 +54,6 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] @@ -85,7 +84,6 @@ def test_installation(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): WazuhAgent.perform_install_and_scan_for_agent(agent_params, agent_names, wazuh_params) - # Testing installation directory for agent_names, agent_params in wazuh_params['agents'].items(): if HostInformation.get_os_type(agent_params) == 'linux': diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml old mode 100644 new mode 100755 similarity index 89% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete.yaml rename to deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml index 648ea9788d..3a87f0527c --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml @@ -2,10 +2,14 @@ version: 0.1 description: This workflow is used to test agents' deployment for DDT1 PoC variables: agent-os: - - linux-oracle-9-amd64 + - macos-sonoma-14.3-arm64 + # Run one at a time as there are limitations on the number of hosts + #- macos-ventura-13.6.4-arm64 + #- macos-sonoma-14.3-amd64 + #- macos-ventura-13.6.4-amd64 manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant + infra-provider: aws working-dir: /tmp/dtt1-poc tasks: @@ -53,6 +57,7 @@ tasks: - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + on-error: "abort-all" foreach: - variable: agent-os as: agent @@ -84,6 +89,7 @@ tasks: live: True depends-on: - "allocate-manager-{manager-os}" + on-error: "abort-all" # Generic agent test task @@ -98,7 +104,7 @@ tasks: - targets: - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - component: "agent" - wazuh-version: "4.7.3" - wazuh-revision: "40714" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml index e97b123b8b..5f2c1e1047 100644 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml @@ -122,7 +122,7 @@ tasks: - targets: - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - component: "agent" - wazuh-version: "4.7.3" - wazuh-revision: "40714" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-macOs.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml similarity index 95% rename from deployability/modules/workflow_engine/examples/agent/aws/test-agent-macOs.yaml rename to deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml index dca534b7f0..bba60dedd8 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-macOs.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml @@ -2,9 +2,12 @@ version: 0.1 description: This workflow is used to test agents' deployment for DDT1 PoC variables: agent-os: - - macos-sonoma-14.3-arm64 + - macos-sonoma-14.0-arm64 + + # Run one at a time as there are limitations on the number of hosts + #- macos-ventura-13.4.1-arm64 manager-os: linux-ubuntu-22.04-amd64 - infra-provider: aws + infra-provider: vagrant working-dir: /tmp/dtt1-poc tasks: From 6b5cc1bd29bfa3ab6a439d3fc5ac271d9210595f Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 22 Apr 2024 18:40:53 +0200 Subject: [PATCH 057/195] fix(#5229): Fixes around test yamls --- ...t-agent-complete-macOS.yaml => test-agent-complete-macOs.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename deployability/modules/workflow_engine/examples/agent/vagrant/{test-agent-complete-macOS.yaml => test-agent-complete-macOs.yaml} (100%) diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml rename to deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml From 26def58c092bfc81769203a19eae03072d00c45f Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 22 Apr 2024 22:26:32 +0200 Subject: [PATCH 058/195] fix(#5229): AWS test comment --- .../examples/agent/aws/test-agent-complete-macOs.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml index 3a87f0527c..2ae2d26e5f 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml @@ -2,9 +2,8 @@ version: 0.1 description: This workflow is used to test agents' deployment for DDT1 PoC variables: agent-os: + # Run one at a time as there are limitations on the number of hosts (Inform @dev-devops-team about your usage, dedicated hosts) - macos-sonoma-14.3-arm64 - - # Run one at a time as there are limitations on the number of hosts #- macos-ventura-13.6.4-arm64 #- macos-sonoma-14.3-amd64 #- macos-ventura-13.6.4-amd64 From 333a6b7ac09b93dbd9e59b2eca5c5f2a7e69ba32 Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 22 Apr 2024 23:38:30 +0200 Subject: [PATCH 059/195] fix(#5229): Vagrant test comment --- .../examples/agent/vagrant/test-agent-complete-macOs.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml index bba60dedd8..d1e35d3e71 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml @@ -2,9 +2,8 @@ version: 0.1 description: This workflow is used to test agents' deployment for DDT1 PoC variables: agent-os: - - macos-sonoma-14.0-arm64 - # Run one at a time as there are limitations on the number of hosts + - macos-sonoma-14.0-arm64 #- macos-ventura-13.4.1-arm64 manager-os: linux-ubuntu-22.04-amd64 infra-provider: vagrant From 13f67e271495cc86ef5a5704367623b3c8f243b8 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 23 Apr 2024 10:02:50 -0300 Subject: [PATCH 060/195] Added Windows sign AMI --- deployability/modules/allocation/static/specs/os.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index aadd5fc5dc..1e49ce4db3 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -368,6 +368,10 @@ aws: ami: ami-0a747df120215911a zone: us-east-1 user: wazuh-user + windows-sign-10-amd64: + ami: ami-078255d5475b46db5 + zone: us-east-1 + user: wazuh-user windows-server-2012r2-amd64: ami: ami-05710c71113d5a40e zone: us-east-1 From 656a1174c4ff49dad4cb6f289eb71633e2c9dd6a Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 23 Apr 2024 16:38:22 -0300 Subject: [PATCH 061/195] Added random assignment for IP address for Vagrant local deployment --- deployability/modules/allocation/vagrant/provider.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index d08f5f9d3b..38f64678e1 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -6,6 +6,7 @@ import platform, json import subprocess import boto3 +import random from jinja2 import Environment, FileSystemLoader from pathlib import Path @@ -244,8 +245,8 @@ def check_ip(ip): if response != 0: return ip - for i in range(2, 254): - ip = f"192.168.57.{i}" + for i in range(254): + ip = f"192.168.57.{random.randint(2, 253)}" if check_ip(ip): return ip From f6a5efb20ce036ef7052c840fd93246508af1ed1 Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 26 Apr 2024 11:52:16 +0200 Subject: [PATCH 062/195] fix-Removing python-pip from requirements --- deployability/deps/remote_requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/deployability/deps/remote_requirements.txt b/deployability/deps/remote_requirements.txt index c81d95e18d..97f0ec6efb 100755 --- a/deployability/deps/remote_requirements.txt +++ b/deployability/deps/remote_requirements.txt @@ -1,4 +1,3 @@ -python3-pip pytest>=7.4.2,<8.0.0 chardet==5.2.0 pytest-tinybird==0.2.0 From 0dabce17d866adf572128254f367d3c218ca234f Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 26 Apr 2024 18:27:47 +0200 Subject: [PATCH 063/195] enhancement(#5229): Merging macos and windows --- deployability/deps/requirements.txt | 2 +- .../modules/allocation/static/specs/os.yml | 4 - .../modules/allocation/vagrant/provider.py | 5 +- deployability/modules/generic/ansible.py | 9 +- .../modules/testing/tests/helpers/agent.py | 266 ++++++++++----- .../testing/tests/helpers/constants.py | 14 + .../modules/testing/tests/helpers/executor.py | 137 ++++++-- .../modules/testing/tests/helpers/generic.py | 307 ++++++++++++++---- .../modules/testing/tests/helpers/manager.py | 20 +- .../modules/testing/tests/helpers/utils.py | 86 +++-- .../tests/test_agent/test_basic_info.py | 23 +- .../tests/test_agent/test_connection.py | 3 +- .../testing/tests/test_agent/test_install.py | 18 +- .../tests/test_agent/test_registration.py | 16 +- .../testing/tests/test_agent/test_restart.py | 4 +- .../testing/tests/test_agent/test_stop.py | 4 +- .../tests/test_agent/test_uninstall.py | 22 +- .../agent/aws/test-agent-complete-macOS.yaml | 115 +++++++ .../examples/agent/aws/test-agent-suse.yaml | 2 +- .../aws/test-agent-windows-complete.yaml | 119 +++++++ .../agent/aws/test-agent-windows-install.yaml | 28 ++ .../vagrant/test-agent-complete-macOS.yaml | 113 +++++++ 22 files changed, 1082 insertions(+), 235 deletions(-) create mode 100644 deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml create mode 100755 deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml create mode 100755 deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml create mode 100644 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml diff --git a/deployability/deps/requirements.txt b/deployability/deps/requirements.txt index 5213aad50d..b71cb57648 100755 --- a/deployability/deps/requirements.txt +++ b/deployability/deps/requirements.txt @@ -12,4 +12,4 @@ pytest==7.4.4 paramiko==3.4.0 requests==2.31.0 chardet==5.2.0 -pywinrm==0.3.0 +pywinrm==0.4.0 diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 1e49ce4db3..aadd5fc5dc 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -368,10 +368,6 @@ aws: ami: ami-0a747df120215911a zone: us-east-1 user: wazuh-user - windows-sign-10-amd64: - ami: ami-078255d5475b46db5 - zone: us-east-1 - user: wazuh-user windows-server-2012r2-amd64: ami: ami-05710c71113d5a40e zone: us-east-1 diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 38f64678e1..d08f5f9d3b 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -6,7 +6,6 @@ import platform, json import subprocess import boto3 -import random from jinja2 import Environment, FileSystemLoader from pathlib import Path @@ -245,8 +244,8 @@ def check_ip(ip): if response != 0: return ip - for i in range(254): - ip = f"192.168.57.{random.randint(2, 253)}" + for i in range(2, 254): + ip = f"192.168.57.{i}" if check_ip(ip): return ip diff --git a/deployability/modules/generic/ansible.py b/deployability/modules/generic/ansible.py index 0829c84b3e..5260ecd92a 100755 --- a/deployability/modules/generic/ansible.py +++ b/deployability/modules/generic/ansible.py @@ -1,12 +1,14 @@ # Copyright (C) 2015, Wazuh Inc. # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + import ansible_runner import jinja2 import yaml from pathlib import Path from pydantic import BaseModel, IPvAnyAddress +from typing import Optional from modules.generic.utils import Utils from modules.generic.logger import Logger @@ -16,7 +18,8 @@ class Inventory(BaseModel): ansible_host: str | IPvAnyAddress ansible_user: str ansible_port: int - ansible_ssh_private_key_file: str + ansible_ssh_private_key_file: Optional[str] = None + ansible_password: Optional[str] = None class Ansible: @@ -118,7 +121,9 @@ def generate_inventory(self) -> dict: self.ansible_data.ansible_host: { 'ansible_port': self.ansible_data.ansible_port, 'ansible_user': self.ansible_data.ansible_user, - 'ansible_ssh_private_key_file': self.ansible_data.ansible_ssh_private_key_file + **({'ansible_ssh_private_key_file': self.ansible_data.ansible_ssh_private_key_file} + if hasattr(self.ansible_data, 'ansible_ssh_private_key_file') + else {'ansible_password': self.ansible_data.ansible_password}) } } } diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 3815bbd4ea..f0dd5133ea 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -5,8 +5,8 @@ import yaml from typing import List, Optional -from .constants import WAZUH_CONF, WAZUH_ROOT -from .executor import Executor, WazuhAPI +from .constants import WAZUH_CONF, WAZUH_ROOT, WAZUH_WINDOWS_CONF +from .executor import WazuhAPI, ConnectionManager from .generic import HostInformation, CheckFiles from modules.testing.utils import logger @@ -15,32 +15,33 @@ class WazuhAgent: @staticmethod def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, live) -> None: - if live == True: + if live: s3_url = 'packages' - release = wazuh_version[0:3] + release = wazuh_version[:1] + ".x" else: s3_url = 'packages-dev' release = 'pre-release' os_type = HostInformation.get_os_type(inventory_path) commands = [] + if 'linux' in os_type: distribution = HostInformation.get_linux_distribution(inventory_path) architecture = HostInformation.get_architecture(inventory_path) - if distribution == 'rpm' and 'x86_64' in architecture: + if distribution == 'rpm' and 'amd64' in architecture: commands.extend([ f"curl -o wazuh-agent-{wazuh_version}-1.x86_64.rpm https://{s3_url}.wazuh.com/{release}/yum/wazuh-agent-{wazuh_version}-1.x86_64.rpm && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' rpm -ihv wazuh-agent-{wazuh_version}-1.x86_64.rpm" ]) - elif distribution == 'rpm' and 'aarch64' in architecture: + elif distribution == 'rpm' and 'arm64' in architecture: commands.extend([ f"curl -o wazuh-agent-{wazuh_version}-1aarch64.rpm https://{s3_url}.wazuh.com/{release}/yum/wazuh-agent-{wazuh_version}-1.aarch64.rpm && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' rpm -ihv wazuh-agent-{wazuh_version}-1.aarch64.rpm" ]) - elif distribution == 'deb' and 'x86_64' in architecture: + elif distribution == 'deb' and 'amd64' in architecture: commands.extend([ f"wget https://{s3_url}.wazuh.com/{release}/apt/pool/main/w/wazuh-agent/wazuh-agent_{wazuh_version}-1_amd64.deb && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' dpkg -i ./wazuh-agent_{wazuh_version}-1_amd64.deb" ]) - elif distribution == 'deb' and 'aarch64' in architecture: + elif distribution == 'deb' and 'arm64' in architecture: commands.extend([ f"wget https://{s3_url}.wazuh.com/{release}/apt/pool/main/w/wazuh-agent/wazuh-agent_{wazuh_version}-1_arm64.deb && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' dpkg -i ./wazuh-agent_{wazuh_version}-1arm64.deb" ]) @@ -54,21 +55,22 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv commands.extend(system_commands) elif 'windows' in os_type : commands.extend([ - f"Invoke-WebRequest -Uri https://packages.wazuh.com/{release}/windows/wazuh-agent-{wazuh_version}-1.msi" - "-OutFile ${env.tmp}\wazuh-agent;" - "msiexec.exe /i ${env.tmp}\wazuh-agent /q" - f"WAZUH_MANAGER='MANAGER_IP'" - f"WAZUH_AGENT_NAME='{agent_name}'" - f"WAZUH_REGISTRATION_SERVER='MANAGER_IP'", - "NET START WazuhSvc", - "NET STATUS WazuhSvc" - ]) + f"Invoke-WebRequest -Uri https://packages.wazuh.com/{release}/windows/wazuh-agent-{wazuh_version}-1.msi " + "-OutFile $env:TEMP\wazuh-agent.msi" + ]) + commands.extend([ + "msiexec.exe /i $env:TEMP\wazuh-agent.msi /q " + f"WAZUH_MANAGER='MANAGER_IP' " + f"WAZUH_AGENT_NAME='{agent_name}' " + f"WAZUH_REGISTRATION_SERVER='MANAGER_IP' " + ]) + commands.extend(["NET START WazuhSvc"]) elif 'macos' in os_type: - if 'intel' in architecture: + if 'amd64' in architecture: commands.extend([ f'curl -so wazuh-agent.pkg https://{s3_url}.wazuh.com/{release}/macos/wazuh-agent-{wazuh_version}-1.intel64.pkg && echo "WAZUH_MANAGER=\'MANAGER_IP\' && WAZUH_AGENT_NAME=\'{agent_name}\'" > /tmp/wazuh_envs && sudo installer -pkg ./wazuh-agent.pkg -target /' ]) - elif 'apple' in architecture: + elif 'arm64' in architecture: commands.extend([ f'curl -so wazuh-agent.pkg https://{s3_url}.wazuh.com/{release}/macos/wazuh-agent-{wazuh_version}-1.arm64.pkg && echo "WAZUH_MANAGER=\'MANAGER_IP\' && WAZUH_AGENT_NAME=\'{agent_name}\'" > /tmp/wazuh_envs && sudo installer -pkg ./wazuh-agent.pkg -target /' ]) @@ -79,7 +81,7 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv commands.extend(system_commands) logger.info(f'Installing Agent in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_commands(inventory_path, commands) + ConnectionManager.execute_commands(inventory_path, commands) @staticmethod @@ -92,29 +94,83 @@ def install_agents(inventories_paths=[], wazuh_versions=[], wazuh_revisions=[], def register_agent(inventory_path, manager_path): with open(manager_path, 'r') as yaml_file: - manager_path = yaml.safe_load(yaml_file) - host = manager_path.get('ansible_host') + manager_path_yaml = yaml.safe_load(yaml_file) + manager_host = manager_path_yaml.get('ansible_host') - internal_ip = HostInformation.get_internal_ip_from_aws_dns(host) if 'amazonaws' in host else host + with open(inventory_path, 'r') as yaml_file: + inventory_path_yaml = yaml.safe_load(yaml_file) + agent_host = inventory_path_yaml.get('ansible_host') - commands = [ - f"sed -i 's/
MANAGER_IP<\/address>/
{internal_ip}<\/address>/g' {WAZUH_CONF}", - "systemctl restart wazuh-agent" - ] + os_type = HostInformation.get_os_type(inventory_path) + logger.info(f'Registering agent in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_commands(inventory_path, commands) - assert internal_ip in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the Manager IP ({internal_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) if 'amazonaws' in manager_host else manager_host + commands = [ + f"sed -i 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_CONF}", + "systemctl restart wazuh-agent" + ] + ConnectionManager.execute_commands(inventory_path, commands) + assert host_ip in ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + + elif os_type == 'macos': + if 'amazonaws' in manager_host and 'amazonaws' in agent_host: + host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) + else: + host_ip = HostInformation.get_public_ip_from_aws_dns(manager_host) + commands = [ + f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' /Library/Ossec/etc/ossec.conf", + "/Library/Ossec/bin/wazuh-control restart" + ] + ConnectionManager.execute_commands(inventory_path, commands) + assert host_ip in ConnectionManager.execute_commands(inventory_path, f'cat /Library/Ossec/etc/ossec.conf'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + + elif 'windows' in os_type : + try: + host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) if 'amazonaws' in manager_host else manager_host + commands = [ + f'(Get-Content -Path "{WAZUH_WINDOWS_CONF}" -Raw) -replace "
MANAGER_IP
", "
{host_ip}
" | Set-Content -Path "{WAZUH_WINDOWS_CONF}"', + "NET START WazuhSvc" + ] + ConnectionManager.execute_commands(inventory_path, commands) + except Exception as e: + raise Exception(f'Error registering agent. Error executing: {commands} with error: {e}') + + result = ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WAZUH_WINDOWS_CONF}"') + assert host_ip in result.get('output'), logger.error(f'Error configuring the Manager IP ({host_ip})in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') @staticmethod def set_protocol_agent_connection(inventory_path, protocol): - commands = [ - f"sed -i 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_CONF}", - "systemctl restart wazuh-agent" - ] + os_type = HostInformation.get_os_type(inventory_path) + + if 'linux' in os_type: + commands = [ + f"sed -i 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_CONF}", + "systemctl restart wazuh-agent" + ] + + ConnectionManager.execute_commands(inventory_path, commands) + result = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}') + assert protocol in result.get('output'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') - Executor.execute_commands(inventory_path, commands) - assert protocol in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + elif 'macos' in os_type: + commands = [ + f"sed -i '' 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' /Library/Ossec/etc/ossec.conf", + "/Library/Ossec/bin/wazuh-control restart" + ] + ConnectionManager.execute_commands(inventory_path, commands) + assert protocol in ConnectionManager.execute_commands(inventory_path, f'cat /Library/Ossec/etc/ossec.conf'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + + elif 'windows' in os_type : + commands = [ + f"(Get-Content -Path '{WAZUH_WINDOWS_CONF}') -replace '[^<]*<\/protocol>', '{protocol}' | Set-Content -Path '{WAZUH_WINDOWS_CONF}'" + ] + + ConnectionManager.execute_commands(inventory_path, commands) + result = ConnectionManager.execute_commands(inventory_path, f'Get-Content -Path "{WAZUH_WINDOWS_CONF}"') + assert protocol in result.get('output'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') @staticmethod @@ -150,7 +206,7 @@ def uninstall_agent(inventory_path, wazuh_version=None, wazuh_revision=None) -> commands.extend(system_commands) elif 'windows' in os_type: commands.extend([ - f"msiexec.exe /x wazuh-agent-{wazuh_version}-1.msi /qn" + f"msiexec.exe /x $env:TEMP\wazuh-agent.msi /qn" ]) elif 'macos' in os_type: commands.extend([ @@ -165,7 +221,7 @@ def uninstall_agent(inventory_path, wazuh_version=None, wazuh_revision=None) -> ]) logger.info(f'Uninstalling Agent in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_commands(inventory_path, commands) + ConnectionManager.execute_commands(inventory_path, commands) @staticmethod @@ -188,7 +244,7 @@ def _uninstall_agent_callback(wazuh_params, agent_params): def perform_action_and_scan(agent_params, action_callback) -> dict: """ Takes scans using filters, the callback action and compares the result - + Args: agent_params (str): agent parameters callbak (cb): callback (action) @@ -200,42 +256,49 @@ def perform_action_and_scan(agent_params, action_callback) -> dict: result = CheckFiles.perform_action_and_scan(agent_params, action_callback) os_name = HostInformation.get_os_name_from_inventory(agent_params) logger.info(f'Applying filters in checkfiles in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') - - if 'debian' in os_name: - filter_data = { - '/boot': {'added': [], 'removed': [], 'modified': ['grubenv']}, - '/usr/bin': { - 'added': [ - 'unattended-upgrade', 'gapplication', 'add-apt-repository', 'gpg-wks-server', 'pkexec', 'gpgsplit', - 'watchgnupg', 'pinentry-curses', 'gpg-zip', 'gsettings', 'gpg-agent', 'gresource', 'gdbus', - 'gpg-connect-agent', 'gpgconf', 'gpgparsemail', 'lspgpot', 'pkaction', 'pkttyagent', 'pkmon', - 'dirmngr', 'kbxutil', 'migrate-pubring-from-classic-gpg', 'gpgcompose', 'pkcheck', 'gpgsm', 'gio', - 'pkcon', 'gpgtar', 'dirmngr-client', 'gpg', 'filebeat', 'gawk', 'curl', 'update-mime-database', - 'dh_installxmlcatalogs', 'appstreamcli', 'lspgpot', 'symcryptrun' - ], - 'removed': [], - 'modified': [] - }, - '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, - '/usr/sbin': { - 'added': [ - 'update-catalog', 'applygnupgdefaults', 'addgnupghome', 'install-sgmlcatalog', 'update-xmlcatalog' - ], - 'removed': [], - 'modified': [] + os_type = HostInformation.get_os_type(agent_params) + + if os_type == 'linux': + if 'debian' in os_name: + filter_data = { + '/boot': {'added': [], 'removed': [], 'modified': ['grubenv']}, + '/usr/bin': { + 'added': [ + 'unattended-upgrade', 'gapplication', 'add-apt-repository', 'gpg-wks-server', 'pkexec', 'gpgsplit', + 'watchgnupg', 'pinentry-curses', 'gpg-zip', 'gsettings', 'gpg-agent', 'gresource', 'gdbus', + 'gpg-connect-agent', 'gpgconf', 'gpgparsemail', 'lspgpot', 'pkaction', 'pkttyagent', 'pkmon', + 'dirmngr', 'kbxutil', 'migrate-pubring-from-classic-gpg', 'gpgcompose', 'pkcheck', 'gpgsm', 'gio', + 'pkcon', 'gpgtar', 'dirmngr-client', 'gpg', 'filebeat', 'gawk', 'curl', 'update-mime-database', + 'dh_installxmlcatalogs', 'appstreamcli', 'lspgpot', 'symcryptrun' + ], + 'removed': [], + 'modified': [] + }, + '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, + '/usr/sbin': { + 'added': [ + 'update-catalog', 'applygnupgdefaults', 'addgnupghome', 'install-sgmlcatalog', 'update-xmlcatalog' + ], + 'removed': [], + 'modified': [] + } + } + else: + filter_data = { + '/boot': { + 'added': ['grub2', 'loader', 'vmlinuz', 'System.map', 'config-', 'initramfs'], + 'removed': [], + 'modified': ['grubenv'] + }, + '/usr/bin': {'added': ['filebeat'], 'removed': [], 'modified': []}, + '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, + '/usr/sbin': {'added': [], 'removed': [], 'modified': []} + } + elif os_type == 'macos': + filter_data = { + '/usr/bin': {'added': [], 'removed': [], 'modified': []}, + '/usr/sbin': {'added': [], 'removed': [], 'modified': []} } - } - else: - filter_data = { - '/boot': { - 'added': ['grub2', 'loader', 'vmlinuz', 'System.map', 'config-', 'initramfs'], - 'removed': [], - 'modified': ['grubenv'] - }, - '/usr/bin': {'added': ['filebeat'], 'removed': [], 'modified': []}, - '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, - '/usr/sbin': {'added': [], 'removed': [], 'modified': []} - } # Use of filters for directory, changes in result.items(): @@ -250,7 +313,7 @@ def perform_action_and_scan(agent_params, action_callback) -> dict: def perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) -> None: """ Coordinates the action of install the agent and compares the checkfiles - + Args: agent_params (str): agent parameters wazuh_params (str): wazuh parameters @@ -259,14 +322,14 @@ def perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) - action_callback = lambda: WazuhAgent._install_agent_callback(wazuh_params, agent_name, agent_params) result = WazuhAgent.perform_action_and_scan(agent_params, action_callback) logger.info(f'Pre and post install checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}: {result}') - WazuhAgent.assert_results(result) + WazuhAgent.assert_results(result, agent_params) @staticmethod def perform_uninstall_and_scan_for_agent(agent_params, wazuh_params) -> None: """ Coordinates the action of uninstall the agent and compares the checkfiles - + Args: agent_params (str): agent parameters wazuh_params (str): wazuh parameters @@ -275,23 +338,32 @@ def perform_uninstall_and_scan_for_agent(agent_params, wazuh_params) -> None: action_callback = lambda: WazuhAgent._uninstall_agent_callback(wazuh_params, agent_params) result = WazuhAgent.perform_action_and_scan(agent_params, action_callback) logger.info(f'Pre and post uninstall checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}: {result}') - WazuhAgent.assert_results(result) + WazuhAgent.assert_results(result, agent_params) @staticmethod - def assert_results(result) -> None: + def assert_results(result, params = None) -> None: """ Gets the status of an agent given its name. - + Args: result (dict): result of comparison between pre and post action scan """ - categories = ['/root', '/usr/bin', '/usr/sbin', '/boot'] + os_type = HostInformation.get_os_type(params) + + if os_type == 'linux': + categories = ['/root', '/usr/bin', '/usr/sbin', '/boot'] + elif os_type == 'windows': + categories = ['C:\\Program Files', 'C:\\Program Files (x86)','C:\\Users\\vagrant'] + elif os_type == 'macos': + categories = ['/usr/bin', '/usr/sbin'] + actions = ['added', 'modified', 'removed'] # Testing the results for category in categories: for action in actions: + assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category}{action}') def areAgent_processes_active(agent_params): @@ -304,7 +376,21 @@ def areAgent_processes_active(agent_params): Returns: str: Os name. """ - return bool([int(numero) for numero in Executor.execute_command(agent_params, 'pgrep wazuh').splitlines()]) + os_type = HostInformation.get_os_type(agent_params) + + if 'linux' in os_type: + result = ConnectionManager.execute_commands(agent_params, 'pgrep wazuh') + if result.get('success'): + return bool([int(numero) for numero in result.get('output').splitlines()]) + else: + return False + elif 'windows' in os_type: + result = ConnectionManager.execute_commands(agent_params, 'Get-Process -Name "wazuh-agent" | Format-Table -HideTableHeaders ProcessName') + if result.get('success'): + return 'wazuh-agent' in result.get('output') + else: + return False + def isAgent_port_open(agent_params): """ @@ -316,7 +402,16 @@ def isAgent_port_open(agent_params): Returns: str: Os name. """ - return 'ESTAB' in Executor.execute_command(agent_params, 'ss -t -a -n | grep ":1514" | grep ESTAB') + + os_type = HostInformation.get_os_type(agent_params) + if 'linux' in os_type: + result = ConnectionManager.execute_commands(agent_params, 'ss -t -a -n | grep ":1514" | grep ESTAB') + return result.get('success') + elif 'windows' in os_type : + result = ConnectionManager.execute_commands(agent_params, 'netstat -ano | Select-String -Pattern "TCP" | Select-String -Pattern "ESTABLISHED" | Select-String -Pattern ":1514"') + return 'ESTABLISHED' in result.get('output') + elif os_type == 'macos': + return 'ESTABLISHED' in ConnectionManager.execute_commands(agent_params, 'netstat -an | grep ".1514 " | grep ESTABLISHED') def get_agents_information(wazuh_api: WazuhAPI) -> list: """ @@ -337,17 +432,20 @@ def get_agents_information(wazuh_api: WazuhAPI) -> list: def get_agent_status(wazuh_api: WazuhAPI, agent_name) -> str: """ Function to get the status of an agent given its name. - + Args: - agents_data (list): List of dictionaries containing agents' data. - agent_name (str): Name of the agent whose status is to be obtained. - + Returns: - str: Status of the agent if found in the data, otherwise returns None. """ response = requests.get(f"{wazuh_api.api_url}/agents", headers=wazuh_api.headers, verify=False) + for agent in eval(response.text)['data']['affected_items']: if agent.get('name') == agent_name: + + return agent.get('status') return None diff --git a/deployability/modules/testing/tests/helpers/constants.py b/deployability/modules/testing/tests/helpers/constants.py index ea0c6a8098..95c1c470a2 100755 --- a/deployability/modules/testing/tests/helpers/constants.py +++ b/deployability/modules/testing/tests/helpers/constants.py @@ -10,6 +10,20 @@ CONFIGURATIONS_DIR = Path(WAZUH_ROOT, "etc") WAZUH_CONF = Path(CONFIGURATIONS_DIR, "ossec.conf") CLIENT_KEYS = Path(CONFIGURATIONS_DIR, "client.keys") + +WINDOWS_ROOT_DIR = Path("C:", "Program Files (x86)", "ossec-agent") +WINDOWS_CONFIGURATIONS_DIR = Path(WINDOWS_ROOT_DIR, "etc") +WAZUH_WINDOWS_CONF = Path(WINDOWS_ROOT_DIR, "ossec.conf") +WINDOWS_CLIENT_KEYS = Path(WINDOWS_ROOT_DIR, "client.keys") +WINDOWS_VERSION = Path(WINDOWS_ROOT_DIR, "VERSION") +WINDOWS_REVISION = Path(WINDOWS_ROOT_DIR, "REVISION") + + +MACOS_ROOT_DIR = Path("/Library", "Ossec") +MACOS_CONFIGURATIONS_DIR = Path(MACOS_ROOT_DIR, "etc") +WAZUH_MACOS_CONF = Path(MACOS_CONFIGURATIONS_DIR, "ossec.conf") +MACOS_CLIENT_KEYS = Path(MACOS_CONFIGURATIONS_DIR, "client.keys") + # Binaries paths BINARIES_DIR = Path(WAZUH_ROOT, "bin") WAZUH_CONTROL = Path(BINARIES_DIR, "wazuh-control") diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index 6d204d6ba8..34fad17fcd 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -4,49 +4,133 @@ import json import requests +import paramiko import subprocess import urllib3 import yaml +import winrm from base64 import b64encode - -class Executor: +class ConectionInventory(): + host: str + port: int + password: str | None = None + username: str + private_key_path: str | None = None @staticmethod - def execute_command(inventory_path, command) -> str: - + def _get_inventory_data(inventory_path) -> dict: with open(inventory_path, 'r') as yaml_file: inventory_data = yaml.safe_load(yaml_file) - host = inventory_data.get('ansible_host') - port = inventory_data.get('ansible_port') - private_key_path = inventory_data.get('ansible_ssh_private_key_file') - username = inventory_data.get('ansible_user') + return { + 'host': inventory_data.get('ansible_host'), + 'port': inventory_data.get('ansible_port'), + 'password': inventory_data.get('ansible_password', None), + 'username': inventory_data.get('ansible_user'), + 'private_key_path': inventory_data.get('ansible_ssh_private_key_file', None) + } + +class ConnectionManager: + @staticmethod + def _get_executor(inventory_path) -> type: + from .generic import HostInformation + + os_type = HostInformation.get_os_type(inventory_path) + if os_type == "windows": + return WindowsExecutor + elif os_type == "linux": + return UnixExecutor + elif os_type == "macos": + return MacosExecutor + + @staticmethod + def execute_commands(inventory_path, commands) -> dict: + executor = ConnectionManager._get_executor(inventory_path) + if isinstance(commands, str): + try: + result = executor._execute_command(ConectionInventory._get_inventory_data(inventory_path), commands) + except Exception as e: + raise Exception(f'Error executing command: {commands} with error: {e}') + return result + else: + results = {} + for command in commands: + result = executor._execute_command(ConectionInventory._get_inventory_data(inventory_path), command) + results[command] = result + return results + +class WindowsExecutor(): + @staticmethod + def _execute_command(data: ConectionInventory, command) -> dict: + if data.get('port') == 5986: + protocol = 'https' + else: + protocol = 'http' + + endpoint_url = f"{protocol}://{data.get('host')}:{data.get('port')}" + + try: + session = winrm.Session(endpoint_url, auth=(data.get('username'), data.get('password')),transport='ntlm', server_cert_validation='ignore') + ret = session.run_ps(command) + + if ret.status_code == 0: + return {'success': True, 'output': ret.std_out.decode('utf-8').strip()} + else: + return {'success': False, 'output': ret.std_err.decode('utf-8').strip()} + except Exception as e: + raise Exception(f'Error executing command: {command} with error: {e}') + +class UnixExecutor(): + @staticmethod + def _execute_command(data, command) -> dict: ssh_command = [ "ssh", - "-i", private_key_path, + "-i", data.get('private_key_path'), "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", - "-p", str(port), - f"{username}@{host}", - "sudo", + "-p", str(data.get('port')), + f"{data.get('username')}@{data.get('host')}", + "sudo", command ] - result = subprocess.run(ssh_command, stdout=subprocess.PIPE, text=True) - return result.stdout + try: + ret = subprocess.run(ssh_command, stdout=subprocess.PIPE, text=True) + if ret.stdout: + return {'success': True, 'output': ret.stdout.replace('\n', '')} + if ret.stderr: + return {'success': False, 'output': ret.stderr.replace('\n', '')} + return {'success': False, 'output': None} + + except Exception as e: + #return {'success': False, 'output': ret.stderr} + raise Exception(f'Error executing command: {command} with error: {e}') +class MacosExecutor(): @staticmethod - def execute_commands(inventory_path, commands=[]) -> dict: + def _execute_command(data, command) -> dict: + + try: + ssh_client = paramiko.SSHClient() + ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh_client.connect(hostname=data.get('host'), port=data.get('port'), username=data.get('username'), password=data.get('username')) + stdin, stdout, stderr = ssh_client.exec_command(f"sudo {command}") + + result = ''.join(stdout.readlines()) - results = {} - for command in commands: - results[command] = Executor.execute_command(inventory_path, command) + ssh_client.close() - return results + return result + + except Exception as e: + #return {'success': False, 'output': ret.stderr} + raise Exception(f'Error executing command: {command} with error: {e}') + +# ------------------------------------------------------ class WazuhAPI: @@ -62,14 +146,17 @@ def _authenticate(self): inventory_data = yaml.safe_load(yaml_file) user = 'wazuh' - + #----Patch issue https://github.com/wazuh/wazuh-packages/issues/2883------------- - file_path = Executor.execute_command(self.inventory_path, 'pwd').replace("\n","") + '/wazuh-install-files/wazuh-passwords.txt' - if not 'true' in Executor.execute_command(self.inventory_path, f'test -f {file_path} && echo "true" || echo "false"'): - Executor.execute_command(self.inventory_path, 'tar -xvf wazuh-install-files.tar') - password = Executor.execute_command(self.inventory_path, "grep api_password wazuh-install-files/wazuh-passwords.txt | head -n 1 | awk '{print $NF}'").replace("'","").replace("\n","") + result = ConnectionManager.execute_commands(self.inventory_path, 'pwd') + file_path = result.get('output') + '/wazuh-install-files/wazuh-passwords.txt' + result = ConnectionManager.execute_commands(self.inventory_path, f'test -f {file_path} && echo "true" || echo "false"') + if not 'true' in result.get('output'): + ConnectionManager.execute_commands(self.inventory_path, 'tar -xvf wazuh-install-files.tar') + result = ConnectionManager.execute_commands(self.inventory_path, "grep api_password wazuh-install-files/wazuh-passwords.txt | head -n 1 | awk '{print $NF}'") + password = result.get('output')[1:-1] #-------------------------------------------------------------------------------- - + login_endpoint = 'security/user/authenticate' host = inventory_data.get('ansible_host') port = '55000' diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 7c989028bf..219344019f 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -12,9 +12,8 @@ import yaml from pathlib import Path -from .constants import WAZUH_CONTROL, CLIENT_KEYS -from .executor import Executor -from .utils import Utils +from .constants import WAZUH_CONTROL, CLIENT_KEYS, WINDOWS_CLIENT_KEYS, WINDOWS_VERSION, WINDOWS_REVISION +from .executor import ConnectionManager from modules.testing.utils import logger @@ -32,8 +31,16 @@ def dir_exists(inventory_path, dir_path) -> str: Returns: bool: True or False """ - return 'true' in Executor.execute_command(inventory_path, f'test -d {dir_path} && echo "true" || echo "false"') + os_type = HostInformation.get_os_type(inventory_path) + + if os_type == 'linux': + result = ConnectionManager.execute_commands(inventory_path, f'test -d {dir_path} && echo "True" || echo "False"') + return result.get('output') + elif os_type == 'windows': + return ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{dir_path}"').get('success') + elif os_type == 'macos': + return 'true' in ConnectionManager.execute_commands(inventory_path, f'stat {dir_path} >/dev/null 2>&1 && echo "true" || echo "false"') @staticmethod def file_exists(inventory_path, file_path) -> bool: @@ -47,8 +54,14 @@ def file_exists(inventory_path, file_path) -> bool: Returns: bool: True or False """ - return 'true' in Executor.execute_command(inventory_path, f'test -f {file_path} && echo "true" || echo "false"') - + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + result = ConnectionManager.execute_commands(inventory_path, f'test -f {file_path} && echo "True" || echo "False"') + return result.get('output') + elif os_type == 'windows': + return ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{file_path}"').get('output') + elif os_type == 'macos': + return 'true' in ConnectionManager.execute_commands(inventory_path, f'stat {file_path} >/dev/null 2>&1 && echo "true" || echo "false"') @staticmethod def get_os_type(inventory_path) -> str: @@ -61,8 +74,19 @@ def get_os_type(inventory_path) -> str: Returns: str: type of host (windows, linux, macos) """ - system = Executor.execute_command(inventory_path, 'uname') - return system.lower() + try: + with open(inventory_path.replace('inventory', 'track'), 'r') as file: + data = yaml.safe_load(file) + if 'platform' in data: + return data['platform'] + else: + raise KeyError("The 'platform' key was not found in the YAML file.") + except FileNotFoundError: + logger.error(f"The YAML file '{inventory_path}' was not found.") + except yaml.YAMLError as e: + logger.error(f"Error while loading the YAML file: {e}") + except Exception as e: + logger.error(f"An unexpected error occurred: {e}") @staticmethod @@ -74,9 +98,21 @@ def get_architecture(inventory_path) -> str: inventory_path: host's inventory path Returns: - str: architecture (aarch64, x86_64, intel, apple) + str: architecture (amd64, arm64, intel, apple) """ - return Executor.execute_command(inventory_path, 'uname -m') + try: + with open(inventory_path.replace('inventory', 'track'), 'r') as file: + data = yaml.safe_load(file) + if 'platform' in data: + return data['arch'] + else: + raise KeyError("The 'platform' key was not found in the YAML file.") + except FileNotFoundError: + logger.error(f"The YAML file '{inventory_path}' was not found.") + except yaml.YAMLError as e: + logger.error(f"Error while loading the YAML file: {e}") + except Exception as e: + logger.error(f"An unexpected error occurred: {e}") @staticmethod @@ -91,9 +127,9 @@ def get_linux_distribution(inventory_path) -> str: str: linux distribution (deb, rpm) """ if 'manager' in inventory_path: - os_name = re.search(r'/manager-linux-([^-]+)-', inventory_path).group(1) + os_name = re.search(r'/manager-[^-]+-([^-]+)-', inventory_path).group(1) elif 'agent' in inventory_path: - os_name = re.search(r'/agent-linux-([^-]+)-', inventory_path).group(1) + os_name = re.search(r'/agent-[^-]+-([^-]+)-', inventory_path).group(1) if os_name == 'ubuntu' or os_name == 'debian': linux_distribution = 'deb' @@ -115,9 +151,9 @@ def get_os_name_from_inventory(inventory_path) -> str: str: linux os name (debian, ubuntu, opensuse, amazon, centos, redhat) """ if 'manager' in inventory_path: - os_name = re.search(r'/manager-linux-([^-]+)-', inventory_path).group(1) + os_name = re.search(r'/manager-[^-]+-([^-]+)-', inventory_path).group(1) elif 'agent' in inventory_path: - os_name = re.search(r'/agent-linux-([^-]+)-', inventory_path).group(1) + os_name = re.search(r'/agent-[^-]+-([^-]+)-', inventory_path).group(1) return os_name @@ -133,9 +169,9 @@ def get_os_name_and_version_from_inventory(inventory_path) -> tuple: tuple: linux os name and version (e.g., ('ubuntu', '22.04')) """ if 'manager' in inventory_path: - match = re.search(r'/manager-linux-([^-]+)-([^-]+)-', inventory_path) + match = re.search(r'/manager-[^-]+-([^-]+)-([^-]+)-', inventory_path) elif 'agent' in inventory_path: - match = re.search(r'/agent-linux-([^-]+)-([^-]+)-', inventory_path) + match = re.search(r'/agent-[^-]+-([^-]+)-([^-]+)-', inventory_path) if match: os_name = match.group(1) version = match.group(2) @@ -145,13 +181,22 @@ def get_os_name_and_version_from_inventory(inventory_path) -> tuple: @staticmethod def get_os_version_from_inventory(inventory_path) -> str: - if 'manager' in inventory_path: - os_version = re.search(r".*?/manager-linux-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) - elif 'agent' in inventory_path: - os_version = re.search(r".*?/agent-linux-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) - return os_version - else: - return None + os_type = HostInformation.get_os_type(inventory_path) + + if os_type == 'linux': + if 'manager' in inventory_path: + os_version = re.search(r".*?/manager-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) + elif 'agent' in inventory_path: + os_version = re.search(r".*?/agent-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) + return os_version + else: + return None + elif os_type == 'windows': + if 'agent' in inventory_path: + os_version = re.search(r".*?/agent-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path)[1:3] + return os_version + else: + return None @staticmethod def get_current_dir(inventory_path) -> str: @@ -164,8 +209,13 @@ def get_current_dir(inventory_path) -> str: Returns: str: current directory """ + os_type = HostInformation.get_os_type(inventory_path) - return Executor.execute_command(inventory_path, 'pwd').replace("\n","") + if os_type == 'linux': + result = ConnectionManager.execute_commands(inventory_path, 'pwd') + return result.get('output').replace("\n","") + elif os_type == 'windows': + return ConnectionManager.execute_commands(inventory_path, '(Get-Location).Path').get('output') @staticmethod def get_internal_ip_from_aws_dns(dns_name): @@ -186,6 +236,22 @@ def get_internal_ip_from_aws_dns(dns_name): else: return None + @staticmethod + def get_public_ip_from_aws_dns(dns_name) -> str: + """ + It returns the public AWS IP from dns_name + Args: + dns_name (str): host's dns public dns name + Returns: + str: public ip + """ + try: + ip_address = socket.gethostbyname(dns_name) + return ip_address + except socket.gaierror as e: + logger.error("Error obtaining IP address:", e) + return None + @staticmethod def get_client_keys(inventory_path) -> list[dict]: """ @@ -198,7 +264,16 @@ def get_client_keys(inventory_path) -> list[dict]: list: List of dictionaries with the client keys. """ clients = [] - client_key = Executor.execute_command(inventory_path, f'cat {CLIENT_KEYS}') + + os_type = HostInformation.get_os_type(inventory_path) + + if os_type == 'linux': + client_key = ConnectionManager.execute_commands(inventory_path, f'cat {CLIENT_KEYS}').get('output') + elif os_type == 'windows': + client_key = ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WINDOWS_CLIENT_KEYS}"').get('output') + elif os_type == 'macos': + client_key = ConnectionManager.execute_commands(inventory_path, f'cat /Library/Ossec/etc/client.keys') + lines = client_key.split('\n')[:-1] for line in lines: _id, name, address, password = line.strip().split() @@ -225,7 +300,7 @@ def sshd_config(inventory_path) -> None: """ commands = ["sudo sed -i '/^PasswordAuthentication/s/^/#/' /etc/ssh/sshd_config", "sudo sed -i '/^PermitRootLogin no/s/^/#/' /etc/ssh/sshd_config", 'echo -e "PasswordAuthentication yes\nPermitRootLogin yes" | sudo tee -a /etc/ssh/sshd_config', 'sudo systemctl restart sshd', 'cat /etc/ssh/sshd_config'] - Executor.execute_commands(inventory_path, commands) + ConnectionManager.execute_commands(inventory_path, commands) @staticmethod @@ -237,17 +312,28 @@ def disable_firewall(inventory_path) -> None: inventory_path: host's inventory path """ - commands = ["sudo systemctl stop firewalld", "sudo systemctl disable firewalld"] - if GeneralComponentActions.isComponentActive(inventory_path, 'firewalld'): - Executor.execute_commands(inventory_path, commands) + commands = [] - logger.info(f'Firewall disabled on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - else: - logger.info(f'No Firewall to disable on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + commands = ["sudo systemctl stop firewalld", "sudo systemctl disable firewalld"] + if GeneralComponentActions.isComponentActive(inventory_path, 'firewalld'): + ConnectionManager.execute_commands(inventory_path, commands) + logger.info(f'Firewall disabled on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + else: + logger.info(f'No Firewall to disable on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + elif os_type == 'windows': + logger.info(f'Firewall disabled on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + commands = ["Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False"] + ConnectionManager.execute_commands(inventory_path, commands) + elif os_type == 'macos': + logger.info(f'Firewall disabled on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + ConnectionManager.execute_commands(inventory_path, 'sudo pfctl -d') def _extract_hosts(paths, is_aws): + from .utils import Utils if is_aws: return [HostInformation.get_internal_ip_from_aws_dns(Utils.extract_ansible_host(path)) for path in paths] else: @@ -266,6 +352,8 @@ def certs_create(wazuh_version, master_path, dashboard_path, indexer_paths=[], w workers_paths (list): wazuh worker paths list """ + from .utils import Utils + current_directory = HostInformation.get_current_dir(master_path) wazuh_version = '.'.join(wazuh_version.split('.')[:2]) @@ -326,7 +414,7 @@ def certs_create(wazuh_version, master_path, dashboard_path, indexer_paths=[], w commands.extend(certs_creation) - Executor.execute_commands(master_path, commands) + ConnectionManager.execute_commands(master_path, commands) current_from_directory = HostInformation.get_current_dir(master_path) @@ -364,7 +452,7 @@ def scp_to(from_inventory_path, to_inventory_path, file_name) -> None: # Allowing handling permissions if file_name == 'wazuh-install-files.tar': - Executor.execute_command(from_inventory_path, f'chmod +rw {file_name}') + ConnectionManager.execute_commands(from_inventory_path, f'chmod +rw {file_name}') logger.info('File permissions modified to be handled') # SCP @@ -381,8 +469,8 @@ def scp_to(from_inventory_path, to_inventory_path, file_name) -> None: # Restoring permissions if file_name == 'wazuh-install-files.tar': - Executor.execute_command(from_inventory_path, f'chmod 600 {file_name}') - Executor.execute_command(to_inventory_path, f'chmod 600 {file_name}') + ConnectionManager.execute_commands(from_inventory_path, f'chmod 600 {file_name}') + ConnectionManager.execute_commands(to_inventory_path, f'chmod 600 {file_name}') logger.info('File permissions were restablished') # Deleting file from localhost @@ -461,18 +549,50 @@ def file_monitor(monitored_file: str, target_string: str, timeout: int = 30) -> class CheckFiles: @staticmethod - def _checkfiles(inventory_path, os_type, directory, filter= None, hash_algorithm='sha256') -> dict: + def _checkfiles(inventory_path, os_type, directory, filters_keywords= None, hash_algorithm='sha256') -> dict: """ It captures a structure of a directory Returns: Dict: dict of directories:hash """ - if 'linux' in os_type or 'macos' in os_type: + if 'linux' == os_type: command = f'sudo find {directory} -type f -exec sha256sum {{}} + {filter}' - result = Executor.execute_command(inventory_path, command) - + result = ConnectionManager.execute_commands(inventory_path, command) + elif 'macos' == os_type: + command = f'sudo find {directory} -type f -exec shasum -a 256 {{}} \; {filter}' + result = ConnectionManager.execute_commands(inventory_path, command) elif 'windows' in os_type: - command = 'dir /a-d /b /s | findstr /v /c:"\\.$" /c:"\\..$"| find /c ":"' + quoted_filters = ['"{}"'.format(keyword) for keyword in filters_keywords] + filter_files = ",".join(quoted_filters) + command = f"$includedDirectories = @('{directory}') " + command += f"\n$excludedPatterns = @({filter_files})" + command += """ + try { + foreach ($dir in $includedDirectories) { + Get-ChildItem -Path "$dir" -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object { + $fileName = $_.FullName + $hash = Get-FileHash -Path $fileName -Algorithm SHA256 -ErrorAction SilentlyContinue + if ($hash) { + $exclude = $false + foreach ($pattern in $excludedPatterns) { + if ($fileName -like "*$pattern*") { + $exclude = $true + break + } + } + if (-not $exclude) { + Write-Output "$($hash.Hash) $fileName" + } + } + } + } + } catch { + Write-Host "Error: $_" + } + + """ + + result = ConnectionManager.execute_commands(inventory_path, command).get('output') else: logger.info(f'Unsupported operating system') return None @@ -485,9 +605,9 @@ def _checkfiles(inventory_path, os_type, directory, filter= None, hash_algorithm @staticmethod - def _perform_scan(inventory_path, os_type, directories, filters): + def _perform_scan(inventory_path, os_type, directories, filters_keywords): logger.info(f'Generating Snapshot for Checkfile in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - return {directory: CheckFiles._checkfiles(inventory_path, os_type, directory, filters) for directory in directories} + return {directory: CheckFiles._checkfiles(inventory_path, os_type, directory, filters_keywords) for directory in directories} @staticmethod @@ -514,16 +634,22 @@ def perform_action_and_scan(inventory_path, callback) -> dict: """ os_type = HostInformation.get_os_type(inventory_path) - directories = ['/boot', '/usr/bin', '/root', '/usr/sbin'] - filters_keywords = ['grep', 'tar', 'coreutils', 'sed', 'procps', 'gawk', 'lsof', 'curl', 'openssl', 'libcap', 'apt-transport-https', 'libcap2-bin', 'software-properties-common', 'gnupg', 'gpg'] - filters = f"| grep -v {filters_keywords[0]}" + if os_type == 'linux': + directories = ['/boot', '/usr/bin', '/root', '/usr/sbin'] + filters_keywords = ['grep', 'tar', 'coreutils', 'sed', 'procps', 'gawk', 'lsof', 'curl', 'openssl', 'libcap', 'apt-transport-https', 'libcap2-bin', 'software-properties-common', 'gnupg', 'gpg'] + elif os_type == 'windows': + directories = ['C:\\Program Files', 'C:\\Program Files (x86)','C:\\Users\\vagrant'] + filters_keywords = ['log','tmp','ossec-agent', 'EdgeUpdate'] + elif 'macos' in inventory_path: + directories = ['/usr/bin', '/usr/sbin'] + filters_keywords = ['grep'] + filters = f"| grep -v {filters_keywords[0]}" for filter_ in filters_keywords[1:]: - filters+= f" | grep -v {filter_}" - - initial_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters) + filters+= f" | grep -v {filter_}" + initial_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters_keywords) callback() - second_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters) + second_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters_keywords) changes = {directory: CheckFiles._calculate_changes(initial_scans[directory], second_scans[directory]) for directory in directories} return changes @@ -543,9 +669,16 @@ def get_component_status(inventory_path, host_role) -> str: str: Role status """ logger.info(f'Getting status of {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + os_type = HostInformation.get_os_type(inventory_path) - return Executor.execute_command(inventory_path, f'systemctl status {host_role}') - + if os_type == 'linux': + return ConnectionManager.execute_commands(inventory_path, f'systemctl status {host_role}').get('output') + elif os_type == 'windows': + result = ConnectionManager.execute_commands(inventory_path, "Get-Service -Name 'Wazuh' | Format-Table -HideTableHeaders Status") + if result.get('success'): + return result.get('output') + elif os_type == 'macos': + return ConnectionManager.execute_commands(inventory_path, f'/Library/Ossec/bin/wazuh-control status | grep {host_role}') @staticmethod def component_stop(inventory_path, host_role) -> None: @@ -558,8 +691,14 @@ def component_stop(inventory_path, host_role) -> None: """ logger.info(f'Stopping {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_command(inventory_path, f'systemctl stop {host_role}') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + ConnectionManager.execute_commands(inventory_path, f'systemctl stop {host_role}') + elif os_type == 'windows': + ConnectionManager.execute_commands(inventory_path, f'NET STOP Wazuh') + elif os_type == 'macos': + return ConnectionManager.execute_commands(inventory_path, f'/Library/Ossec/bin/wazuh-control stop | grep {host_role}') @staticmethod def component_restart(inventory_path, host_role) -> None: @@ -572,8 +711,15 @@ def component_restart(inventory_path, host_role) -> None: """ logger.info(f'Restarting {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_command(inventory_path, f'systemctl restart {host_role}') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + ConnectionManager.execute_commands(inventory_path, f'systemctl restart {host_role}') + elif os_type == 'windows': + ConnectionManager.execute_commands(inventory_path, 'NET STOP Wazuh') + ConnectionManager.execute_commands(inventory_path, 'NET START Wazuh') + elif os_type == 'macos': + ConnectionManager.execute_commands(inventory_path, f'/Library/Ossec/bin/wazuh-control restart | grep {host_role}') @staticmethod def component_start(inventory_path, host_role) -> None: @@ -586,8 +732,15 @@ def component_start(inventory_path, host_role) -> None: """ logger.info(f'Starting {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_command(inventory_path, f'systemctl restart {host_role}') + os_type = HostInformation.get_os_type(inventory_path) + + if os_type == 'linux': + ConnectionManager.execute_commands(inventory_path, f'systemctl start {host_role}') + elif os_type == 'windows': + ConnectionManager.execute_commands(inventory_path, 'NET START Wazuh') + elif os_type == 'macos': + ConnectionManager.execute_commands(inventory_path, f'/Library/Ossec/bin/wazuh-control start | grep {host_role}') @staticmethod def get_component_version(inventory_path) -> str: @@ -600,8 +753,14 @@ def get_component_version(inventory_path) -> str: Returns: str: version """ - return Executor.execute_command(inventory_path, f'{WAZUH_CONTROL} info -v') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': + return ConnectionManager.execute_commands(inventory_path, f'{WAZUH_CONTROL} info -v').get('output') + elif os_type == 'windows': + return ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WINDOWS_VERSION}"').get('output')#.replace("\n", "")) + elif os_type == 'macos': + ConnectionManager.execute_commands(inventory_path, f'/Library/Ossec/bin/wazuh-control info -v') @staticmethod def get_component_revision(inventory_path) -> str: @@ -614,8 +773,15 @@ def get_component_revision(inventory_path) -> str: Returns: str: revision number """ - return Executor.execute_command(inventory_path, f'{WAZUH_CONTROL} info -r') + os_type = HostInformation.get_os_type(inventory_path) + + if os_type == 'linux': + return ConnectionManager.execute_commands(inventory_path, f'{WAZUH_CONTROL} info -r').get('output') + elif os_type == 'windows': + return ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WINDOWS_REVISION}"').get('output') + elif os_type == 'macos': + return ConnectionManager.execute_commands(inventory_path, f'/Library/Ossec/bin/wazuh-control info -r') @staticmethod def hasAgentClientKeys(inventory_path) -> bool: @@ -628,8 +794,19 @@ def hasAgentClientKeys(inventory_path) -> bool: Returns: bool: True/False """ - return 'true' in Executor.execute_command(inventory_path, f'[ -f {CLIENT_KEYS} ] && echo true || echo false') + os_type = HostInformation.get_os_type(inventory_path) + + if os_type == 'linux': + result = ConnectionManager.execute_commands(inventory_path, f'[ -f {CLIENT_KEYS} ] && echo true || echo false') + return 'true' in result.get('output') + elif os_type == 'windows': + result = ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{WINDOWS_CLIENT_KEYS}"') + if result.get('success'): + return result.get('output', '') + return False + elif os_type == 'macos': + return HostInformation.file_exists(inventory_path, '/Library/Ossec/etc/client.keys') @staticmethod def isComponentActive(inventory_path, host_role) -> bool: @@ -643,7 +820,19 @@ def isComponentActive(inventory_path, host_role) -> bool: Returns: bool: True/False """ - return 'active' == Executor.execute_command(inventory_path, f'systemctl is-active {host_role}').replace("\n", "") + + os_type = HostInformation.get_os_type(inventory_path) + + if os_type == 'linux': + + return 'active' == ConnectionManager.execute_commands(inventory_path, f'systemctl is-active {host_role}').get('output').replace("\n", "") + elif os_type == 'windows': + result = ConnectionManager.execute_commands(inventory_path, "Get-Service -Name 'Wazuh'") + return result.get('success') + elif os_type == 'macos': + return f'com.{host_role.replace("-", ".")}' in ConnectionManager.execute_commands(inventory_path, f'launchctl list | grep com.{host_role.replace("-", ".")}') + + class Waits: diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index 2ac8811ab7..75ebf346e2 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -6,7 +6,7 @@ import socket from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT -from .executor import Executor, WazuhAPI +from .executor import WazuhAPI, ConnectionManager from .generic import HostInformation, CheckFiles from modules.testing.utils import logger from .utils import Utils @@ -39,7 +39,7 @@ def install_manager(inventory_path, node_name, wazuh_version) -> None: f"bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" ] logger.info(f'Installing Manager in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_commands(inventory_path, commands) + ConnectionManager.execute_commands(inventory_path, commands) @staticmethod @@ -88,7 +88,7 @@ def uninstall_manager(inventory_path) -> None: commands.extend(system_commands) logger.info(f'Uninstalling Manager in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_commands(inventory_path, commands) + ConnectionManager.execute_commands(inventory_path, commands) @staticmethod @@ -188,7 +188,7 @@ def perform_install_and_scan_for_manager(manager_params, manager_name, wazuh_par action_callback = lambda: WazuhManager._install_manager_callback(wazuh_params, manager_name, manager_params) result = WazuhManager.perform_action_and_scan(manager_params, action_callback) logger.info(f'Pre and post install checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(manager_params)}: {result}') - WazuhManager.assert_results(result) + WazuhManager.assert_results(result, manager_params) @staticmethod @@ -204,7 +204,7 @@ def perform_uninstall_and_scan_for_manager(manager_params) -> None: action_callback = lambda: WazuhManager._uninstall_manager_callback(manager_params) result = WazuhManager.perform_action_and_scan(manager_params, action_callback) logger.info(f'Pre and post uninstall checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(manager_params)}: {result}') - WazuhManager.assert_results(result) + WazuhManager.assert_results(result, manager_params) @staticmethod @@ -236,7 +236,7 @@ def get_cluster_info(inventory_path) -> None: str: Cluster status """ - return Executor.execute_command(inventory_path, f'{CLUSTER_CONTROL} -l') + return ConnectionManager.execute_commands(inventory_path, f'{CLUSTER_CONTROL} -l').get('output') @staticmethod @@ -250,8 +250,10 @@ def get_agent_control_info(inventory_path) -> None: Returns: str: Agents status """ + result = ConnectionManager.execute_commands(inventory_path, f'{AGENT_CONTROL} -l') - return Executor.execute_command(inventory_path, f'{AGENT_CONTROL} -l') + + return result.get('output') @staticmethod @@ -278,8 +280,8 @@ def configuring_clusters(inventory_path, node_name, node_type, node_to_connect_i "systemctl restart wazuh-manager" ] - Executor.execute_commands(inventory_path, commands) - if node_name in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'): + ConnectionManager.execute_commands(inventory_path, commands) + if node_name in ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}').get('output'): logger.info(f'Cluster configured in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') else: logger.error(f'Error configuring cluster information in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index aea456c151..c79dec259f 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -7,16 +7,17 @@ import yaml import logging import time +import winrm from modules.testing.utils import logger - +from .generic import HostInformation paramiko_logger = logging.getLogger("paramiko") paramiko_logger.setLevel(logging.CRITICAL) class Utils: - + @staticmethod def extract_ansible_host(file_path) -> str: with open(file_path, 'r') as yaml_file: @@ -26,9 +27,9 @@ def extract_ansible_host(file_path) -> str: @staticmethod def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: if 'manager' in inventory_path: - match = re.search(r'/manager-linux-([^-]+)-([^-]+)-', inventory_path) + match = re.search(r'/manager-[^-]+-([^-]+)-([^-]+)-', inventory_path) elif 'agent' in inventory_path: - match = re.search(r'/agent-linux-([^-]+)-([^-]+)-', inventory_path) + match = re.search(r'/agent-[^-]+-([^-]+)-([^-]+)-', inventory_path) if match: os_name = match.group(1)+ '-' + match.group(2) logger.info(f'Checking connection to {os_name}') @@ -39,31 +40,74 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: raise FileNotFoundError(logger.error(f'File not found in {os_name}')) except yaml.YAMLError: raise ValueError(logger.error(f'Invalid inventory information in {os_name}')) - + host = inventory_data.get('ansible_host') port = inventory_data.get('ansible_port') - private_key_path = inventory_data.get('ansible_ssh_private_key_file') + private_key_path = inventory_data.get('ansible_ssh_private_key_file', None) username = inventory_data.get('ansible_user') + password = inventory_data.get('ansible_password', None) - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - private_key = paramiko.RSAKey.from_private_key_file(private_key_path) - for attempt in range(1, attempts + 1): + os_type = HostInformation.get_os_type(inventory_path) + + if os_type == 'linux': ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) private_key = paramiko.RSAKey.from_private_key_file(private_key_path) - try: - ssh.connect(hostname=host, port=port, username=username, pkey=private_key) - logger.info(f'Connection established successfully in {os_name}') - ssh.close() - return True - except paramiko.AuthenticationException: - logger.error(f'Authentication error. Check SSH credentials in {os_name}') - return False - except Exception as e: - logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') - time.sleep(sleep) + + for attempt in range(1, attempts + 1): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + private_key = paramiko.RSAKey.from_private_key_file(private_key_path) + try: + ssh.connect(hostname=host, port=port, username=username, pkey=private_key) + logger.info(f'Connection established successfully in {os_name}') + ssh.close() + return True + except paramiko.AuthenticationException: + logger.error(f'Authentication error. Check SSH credentials in {os_name}') + return False + except Exception as e: + logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') + time.sleep(sleep) + elif os_type == 'windows': + if port == 5986: + protocol = 'https' + else: + protocol = 'http' + endpoint_url = f'{protocol}://{host}:{port}' + + for attempt in range(1, attempts + 1): + try: + session = winrm.Session(endpoint_url, auth=(username, password),transport='ntlm', server_cert_validation='ignore') + cmd = session.run_cmd('ipconfig') + if cmd.status_code == 0: + logger.info("WinRM connection successful.") + return True + else: + logger.error(f'WinRM connection failed. Check the credentials in the inventory file.') + return False + except Exception as e: + logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') + time.sleep(sleep) + elif os_type == 'macos': + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + for attempt in range(1, attempts + 1): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + ssh.connect(hostname=host, port=port, username=username, password=password) + logger.info(f'Connection established successfully in {os_name}') + ssh.close() + return True + except paramiko.AuthenticationException: + logger.error(f'Authentication error. Check SSH credentials in {os_name}') + return False + except Exception as e: + logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') + time.sleep(sleep) logger.error(f'Connection attempts failed after {attempts} tries. Connection timeout in {os_name}') return False diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index 3b81a0765f..77ab0f8a87 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -6,7 +6,7 @@ import re from ..helpers.agent import WazuhAgent, WazuhAPI -from ..helpers.constants import WAZUH_ROOT +from ..helpers.constants import WAZUH_ROOT, WINDOWS_ROOT_DIR, MACOS_ROOT_DIR from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions, Waits from modules.testing.utils import logger from ..helpers.manager import WazuhManager @@ -67,12 +67,27 @@ def setup_test_environment(wazuh_params): def test_wazuh_os_version(wazuh_params): wazuh_api = WazuhAPI(wazuh_params['master']) for agent_names, agent_params in wazuh_params['agents'].items(): - assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') + path_to_check = '' + os_type = HostInformation.get_os_type(agent_params) + + if os_type == 'linux': + path_to_check = WAZUH_ROOT + elif os_type == 'windows': + path_to_check = WINDOWS_ROOT_DIR + elif os_type == 'macos': + path_to_check = MACOS_ROOT_DIR + + assert HostInformation.dir_exists(agent_params, path_to_check), logger.error(f'The {path_to_check} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') + expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) - assert HostInformation.get_os_version_from_inventory(agent_params) in WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names), logger.error('There is a mismatch between the OS version and the OS version of the installed agent') - assert HostInformation.get_os_name_from_inventory(agent_params) in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') + if not os_type == 'windows': + assert HostInformation.get_os_version_from_inventory(agent_params) in WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names), logger.error('There is a mismatch between the OS version and the OS version of the installed agent') + if os_type == 'linux': + assert HostInformation.get_os_name_from_inventory(agent_params) in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') + elif os_type == 'macos': + assert 'macos' in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') def test_wazuh_version(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): diff --git a/deployability/modules/testing/tests/test_agent/test_connection.py b/deployability/modules/testing/tests/test_agent/test_connection.py index 4a7297596a..c777858ca2 100644 --- a/deployability/modules/testing/tests/test_agent/test_connection.py +++ b/deployability/modules/testing/tests/test_agent/test_connection.py @@ -73,7 +73,8 @@ def test_connection(wazuh_params): def test_status(wazuh_params): for agent in wazuh_params['agents'].values(): - assert 'active' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') + status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent') + assert 'active' in status or 'connected' in status or "Running" in status or "is running" in status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') def test_service(wazuh_params): diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index 059d46273f..dbb0f9b71f 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -6,7 +6,7 @@ import re from ..helpers.agent import WazuhAgent -from ..helpers.constants import WAZUH_ROOT +from ..helpers.constants import WAZUH_ROOT, WINDOWS_ROOT_DIR, MACOS_ROOT_DIR from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions from modules.testing.utils import logger from ..helpers.manager import WazuhManager @@ -81,15 +81,23 @@ def test_installation(wazuh_params): assert HostInformation.dir_exists(wazuh_params['master'], WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])}') # Agent installation - for agent_names, agent_params in wazuh_params['agents'].items(): - WazuhAgent.perform_install_and_scan_for_agent(agent_params, agent_names, wazuh_params) + for agent_name, agent_params in wazuh_params['agents'].items(): + WazuhAgent.perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) + #WazuhAgent.install_agent(agent_params, agent_name, wazuh_params['wazuh_version'], wazuh_params['wazuh_revision'], wazuh_params['live']) # Testing installation directory for agent in wazuh_params['agents'].values(): - assert HostInformation.dir_exists(agent, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent)}') + os_type = HostInformation.get_os_type(agent) + if os_type == 'linux': + path_to_check = WAZUH_ROOT + elif os_type == 'windows': + path_to_check = WINDOWS_ROOT_DIR + elif os_type == 'macos': + path_to_check = MACOS_ROOT_DIR + assert HostInformation.dir_exists(agent, path_to_check), logger.error(f'The {path_to_check} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent)}') def test_status(wazuh_params): for agent in wazuh_params['agents'].values(): agent_status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent') - assert 'loaded' in agent_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded') + assert 'loaded' in agent_status or 'Stopped' in agent_status or 'not running' in agent_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded') diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py index dc756410d5..4447db4923 100644 --- a/deployability/modules/testing/tests/test_agent/test_registration.py +++ b/deployability/modules/testing/tests/test_agent/test_registration.py @@ -68,14 +68,8 @@ def test_status(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): WazuhAgent.register_agent(agent_params, wazuh_params['master']) for agent in wazuh_params['agents'].values(): - assert 'active' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') - - -def test_connection(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): - assert agent_names in WazuhManager.get_agent_control_info(wazuh_params['master']), f'The {agent_names} is not present in the master by command' - wazuh_api = WazuhAPI(wazuh_params['master']) - assert any(d.get('name') == agent_names for d in WazuhAgent.get_agents_information(wazuh_api)), logger.error(f'The {agent_names} is not present in the master by API') + status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent') + assert 'active' in status or 'Running' in status or 'is running' in status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') def test_service(wazuh_params): @@ -86,6 +80,12 @@ def test_service(wazuh_params): expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) +def test_connection(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + wazuh_api = WazuhAPI(wazuh_params['master']) + assert agent_names in WazuhManager.get_agent_control_info(wazuh_params['master']), f'The {agent_names} is not present in the master by command' + assert any(d.get('name') == agent_names for d in WazuhAgent.get_agents_information(wazuh_api)), logger.error(f'The {agent_names} is not present in the master by API') + def test_clientKeys(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index 734e4791e3..5f0ba90e99 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -69,8 +69,8 @@ def test_restart(wazuh_params): def test_status(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert 'active' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') - + status = GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent') + assert 'active' in status or 'Running' in status or 'is running' in status, logger.error(f'{agent_names} is not active by command') def test_connection(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index a347bce95f..b673726002 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -71,8 +71,8 @@ def test_service(wazuh_params): GeneralComponentActions.component_stop(agent_params, 'wazuh-agent') for agent_names, agent_params in wazuh_params['agents'].items(): - assert 'inactive' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is still active by command') - assert not GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is still active by command') + status = GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent') + assert 'inactive' in status or 'Stopped' in status or 'StopPending' in status or 'not running' in status, logger.error(f'{agent_names} is still active by command') expected_condition_func = lambda: 'disconnected' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index 953e73bc5e..9535ae6c90 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -6,7 +6,7 @@ import re from ..helpers.agent import WazuhAgent -from ..helpers.constants import WAZUH_ROOT +from ..helpers.constants import WAZUH_ROOT, WINDOWS_CONFIGURATIONS_DIR, WINDOWS_ROOT_DIR, MACOS_ROOT_DIR from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI from modules.testing.utils import logger @@ -66,9 +66,18 @@ def setup_test_environment(wazuh_params): def test_uninstall(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not Active before the installation') - assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in the host {agent_names}') - # Agent installation + os_type = HostInformation.get_os_type(agent_params) + if os_type == 'linux': + path_to_check = WAZUH_ROOT + elif os_type == 'windows': + path_to_check = WINDOWS_ROOT_DIR + elif os_type == 'macos': + path_to_check = MACOS_ROOT_DIR + + assert HostInformation.dir_exists(agent_params, path_to_check), logger.error(f'The {path_to_check} is not present in the host {agent_names}') + + # Agent uninstallation for agent_names, agent_params in wazuh_params['agents'].items(): WazuhAgent.perform_uninstall_and_scan_for_agent(agent_params,wazuh_params) @@ -79,7 +88,12 @@ def test_uninstall(wazuh_params): def test_agent_uninstalled_directory(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert not HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is still present in the agent {agent_names}') + os_type = HostInformation.get_os_type(agent_params) + if os_type == 'linux': + path_to_check = WAZUH_ROOT + elif os_type == 'windows': + path_to_check = WINDOWS_CONFIGURATIONS_DIR + assert HostInformation.dir_exists(agent_params, path_to_check), logger.error(f'The {path_to_check} is still present in the agent {agent_names}') def test_service(wazuh_params): diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml new file mode 100644 index 0000000000..773eb70f9e --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml @@ -0,0 +1,115 @@ +version: 0.1 +description: This workflow is used to test agents' deployment for DDT1 PoC +variables: + agent-os: + # Run one at a time as there are limitations on the number of hosts (Inform @dev-devops-team about your usage, dedicated hosts) + - macos-sonoma-14.3-arm64 + #- macos-ventura-13.6.4-arm64 + #- macos-sonoma-14.3-amd64 + #- macos-ventura-13.6.4-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml index e97b123b8b..5f2c1e1047 100644 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml @@ -122,7 +122,7 @@ tasks: - targets: - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,restart,stop,uninstall" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - component: "agent" - wazuh-version: "4.7.3" - wazuh-revision: "40714" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml new file mode 100755 index 0000000000..c65d56e65e --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml @@ -0,0 +1,119 @@ +version: 0.1 +description: This workflow is used to test agents' deployment for DDT1 PoC +variables: + agent-os: + - linux-ubuntu-20.04-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-8-amd64 + - linux-redhat-9-amd64 + - windows-desktop-10-amd64 + - windows-server-2012r2-amd64 + - windows-server-2016-amd64 + - windows-server-2019-amd64 + - windows-server-2022-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: medium + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,connection,basic_info,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml new file mode 100755 index 0000000000..1c8a8f1faf --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml @@ -0,0 +1,28 @@ +version: 0.1 +description: This workflow is used to test agents' deployment for DDT1 PoC +variables: + agent-os: + - windows-server-2016-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Generic agent test task + - task: "run-agent-windows-server-2016-amd64-tests" + description: "Run tests install for the agent windows-server-2016-amd64." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-windows-server-2016-amd64/inventory.yaml" + - tests: "install" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml new file mode 100644 index 0000000000..115bd0d96a --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml @@ -0,0 +1,113 @@ +version: 0.1 +description: This workflow is used to test agents' deployment for DDT1 PoC +variables: + agent-os: + # Run one at a time as there are limitations on the number of hosts + - macos-sonoma-14.0-arm64 + #- macos-ventura-13.4.1-arm64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "aws" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" \ No newline at end of file From b60486689e1a48a6d85e94cf7198fff8a499b5bf Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 29 Apr 2024 12:09:32 +0200 Subject: [PATCH 064/195] fix(#5229): Linux os fixed --- .../modules/testing/tests/helpers/agent.py | 67 +++++++++++-------- .../modules/testing/tests/helpers/generic.py | 19 ++++-- .../modules/testing/tests/helpers/utils.py | 2 + .../testing/tests/test_agent/test_install.py | 1 - 4 files changed, 52 insertions(+), 37 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index f0dd5133ea..18ef2aa10b 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -25,7 +25,7 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv os_type = HostInformation.get_os_type(inventory_path) commands = [] - if 'linux' in os_type: + if os_type == 'linux': distribution = HostInformation.get_linux_distribution(inventory_path) architecture = HostInformation.get_architecture(inventory_path) @@ -53,7 +53,7 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv ] commands.extend(system_commands) - elif 'windows' in os_type : + elif os_type == 'windows' : commands.extend([ f"Invoke-WebRequest -Uri https://packages.wazuh.com/{release}/windows/wazuh-agent-{wazuh_version}-1.msi " "-OutFile $env:TEMP\wazuh-agent.msi" @@ -65,7 +65,7 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv f"WAZUH_REGISTRATION_SERVER='MANAGER_IP' " ]) commands.extend(["NET START WazuhSvc"]) - elif 'macos' in os_type: + elif os_type == 'macos': if 'amd64' in architecture: commands.extend([ f'curl -so wazuh-agent.pkg https://{s3_url}.wazuh.com/{release}/macos/wazuh-agent-{wazuh_version}-1.intel64.pkg && echo "WAZUH_MANAGER=\'MANAGER_IP\' && WAZUH_AGENT_NAME=\'{agent_name}\'" > /tmp/wazuh_envs && sudo installer -pkg ./wazuh-agent.pkg -target /' @@ -104,29 +104,38 @@ def register_agent(inventory_path, manager_path): os_type = HostInformation.get_os_type(inventory_path) logger.info(f'Registering agent in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - os_type = HostInformation.get_os_type(inventory_path) if os_type == 'linux': - host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) if 'amazonaws' in manager_host else manager_host - commands = [ - f"sed -i 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_CONF}", - "systemctl restart wazuh-agent" - ] - ConnectionManager.execute_commands(inventory_path, commands) - assert host_ip in ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + try: + host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) if 'amazonaws' in manager_host else manager_host + commands = [ + f"sed -i 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_CONF}", + "systemctl restart wazuh-agent" + ] + ConnectionManager.execute_commands(inventory_path, commands) + except Exception as e: + raise Exception(f'Error registering agent. Error executing: {commands} with error: {e}') + + result = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}') + assert host_ip in result.get('output'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') elif os_type == 'macos': - if 'amazonaws' in manager_host and 'amazonaws' in agent_host: - host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) - else: - host_ip = HostInformation.get_public_ip_from_aws_dns(manager_host) - commands = [ - f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' /Library/Ossec/etc/ossec.conf", - "/Library/Ossec/bin/wazuh-control restart" - ] - ConnectionManager.execute_commands(inventory_path, commands) - assert host_ip in ConnectionManager.execute_commands(inventory_path, f'cat /Library/Ossec/etc/ossec.conf'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + try: + if 'amazonaws' in manager_host and 'amazonaws' in agent_host: + host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) + else: + host_ip = HostInformation.get_public_ip_from_aws_dns(manager_host) + commands = [ + f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' /Library/Ossec/etc/ossec.conf", + "/Library/Ossec/bin/wazuh-control restart" + ] + ConnectionManager.execute_commands(inventory_path, commands) + except Exception as e: + raise Exception(f'Error registering agent. Error executing: {commands} with error: {e}') - elif 'windows' in os_type : + result = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}') + assert host_ip in result.get('output'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + + elif os_type == 'windows': try: host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) if 'amazonaws' in manager_host else manager_host commands = [ @@ -139,13 +148,13 @@ def register_agent(inventory_path, manager_path): raise Exception(f'Error registering agent. Error executing: {commands} with error: {e}') result = ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WAZUH_WINDOWS_CONF}"') - assert host_ip in result.get('output'), logger.error(f'Error configuring the Manager IP ({host_ip})in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + assert host_ip in result.get('output'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') @staticmethod def set_protocol_agent_connection(inventory_path, protocol): os_type = HostInformation.get_os_type(inventory_path) - if 'linux' in os_type: + if os_type == 'linux': commands = [ f"sed -i 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_CONF}", "systemctl restart wazuh-agent" @@ -155,7 +164,7 @@ def set_protocol_agent_connection(inventory_path, protocol): result = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}') assert protocol in result.get('output'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') - elif 'macos' in os_type: + elif os_type == 'macos': commands = [ f"sed -i '' 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' /Library/Ossec/etc/ossec.conf", "/Library/Ossec/bin/wazuh-control restart" @@ -163,7 +172,7 @@ def set_protocol_agent_connection(inventory_path, protocol): ConnectionManager.execute_commands(inventory_path, commands) assert protocol in ConnectionManager.execute_commands(inventory_path, f'cat /Library/Ossec/etc/ossec.conf'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') - elif 'windows' in os_type : + elif os_type == 'windows': commands = [ f"(Get-Content -Path '{WAZUH_WINDOWS_CONF}') -replace '[^<]*<\/protocol>', '{protocol}' | Set-Content -Path '{WAZUH_WINDOWS_CONF}'" ] @@ -177,7 +186,7 @@ def set_protocol_agent_connection(inventory_path, protocol): def uninstall_agent(inventory_path, wazuh_version=None, wazuh_revision=None) -> None: os_type = HostInformation.get_os_type(inventory_path) commands = [] - if 'linux' in os_type: + if os_type == 'linux': distribution = HostInformation.get_linux_distribution(inventory_path) os_name = HostInformation.get_os_name_from_inventory(inventory_path) if os_name == 'opensuse' or os_name == 'suse': @@ -204,11 +213,11 @@ def uninstall_agent(inventory_path, wazuh_version=None, wazuh_revision=None) -> ] commands.extend(system_commands) - elif 'windows' in os_type: + elif os_type == 'windows': commands.extend([ f"msiexec.exe /x $env:TEMP\wazuh-agent.msi /qn" ]) - elif 'macos' in os_type: + elif os_type == 'macos': commands.extend([ "/Library/Ossec/bin/wazuh-control stop", "/bin/rm -r /Library/Ossec", diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 219344019f..f274152fd3 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -556,9 +556,16 @@ def _checkfiles(inventory_path, os_type, directory, filters_keywords= None, hash Dict: dict of directories:hash """ if 'linux' == os_type: - command = f'sudo find {directory} -type f -exec sha256sum {{}} + {filter}' - result = ConnectionManager.execute_commands(inventory_path, command) + filters = f"| grep -v {filters_keywords[0]}" + for filter_ in filters_keywords[1:]: + filters += f" | grep -v {filter_}" + command = f'sudo find {directory} -type f -exec sha256sum {{}} + {filters}' + result = ConnectionManager.execute_commands(inventory_path, command).get('output') + elif 'macos' == os_type: + filters = f"| grep -v {filters_keywords[0]}" + for filter_ in filters_keywords[1:]: + filters += f" | grep -v {filter_}" command = f'sudo find {directory} -type f -exec shasum -a 256 {{}} \; {filter}' result = ConnectionManager.execute_commands(inventory_path, command) elif 'windows' in os_type: @@ -640,13 +647,10 @@ def perform_action_and_scan(inventory_path, callback) -> dict: elif os_type == 'windows': directories = ['C:\\Program Files', 'C:\\Program Files (x86)','C:\\Users\\vagrant'] filters_keywords = ['log','tmp','ossec-agent', 'EdgeUpdate'] - elif 'macos' in inventory_path: + elif os_type == 'macos': directories = ['/usr/bin', '/usr/sbin'] filters_keywords = ['grep'] - filters = f"| grep -v {filters_keywords[0]}" - for filter_ in filters_keywords[1:]: - filters+= f" | grep -v {filter_}" initial_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters_keywords) callback() second_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters_keywords) @@ -824,11 +828,12 @@ def isComponentActive(inventory_path, host_role) -> bool: os_type = HostInformation.get_os_type(inventory_path) if os_type == 'linux': - return 'active' == ConnectionManager.execute_commands(inventory_path, f'systemctl is-active {host_role}').get('output').replace("\n", "") + elif os_type == 'windows': result = ConnectionManager.execute_commands(inventory_path, "Get-Service -Name 'Wazuh'") return result.get('success') + elif os_type == 'macos': return f'com.{host_role.replace("-", ".")}' in ConnectionManager.execute_commands(inventory_path, f'launchctl list | grep com.{host_role.replace("-", ".")}') diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index c79dec259f..cb785ced49 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -70,6 +70,7 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: except Exception as e: logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') time.sleep(sleep) + elif os_type == 'windows': if port == 5986: protocol = 'https' @@ -90,6 +91,7 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: except Exception as e: logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') time.sleep(sleep) + elif os_type == 'macos': ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index dbb0f9b71f..f5e5ba9322 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -83,7 +83,6 @@ def test_installation(wazuh_params): # Agent installation for agent_name, agent_params in wazuh_params['agents'].items(): WazuhAgent.perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) - #WazuhAgent.install_agent(agent_params, agent_name, wazuh_params['wazuh_version'], wazuh_params['wazuh_revision'], wazuh_params['live']) # Testing installation directory for agent in wazuh_params['agents'].values(): From 9e07b4dfeeac16f9a53094f27e3b6bf76fec2d4c Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 29 Apr 2024 14:09:23 +0200 Subject: [PATCH 065/195] fix(#5229): Filters fixed --- .../modules/testing/tests/helpers/agent.py | 16 +++++++++++----- .../modules/testing/tests/helpers/generic.py | 1 + .../modules/testing/tests/helpers/manager.py | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 18ef2aa10b..ec46e025e7 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -304,10 +304,16 @@ def perform_action_and_scan(agent_params, action_callback) -> dict: '/usr/sbin': {'added': [], 'removed': [], 'modified': []} } elif os_type == 'macos': - filter_data = { - '/usr/bin': {'added': [], 'removed': [], 'modified': []}, - '/usr/sbin': {'added': [], 'removed': [], 'modified': []} - } + filter_data = { + '/usr/bin': {'added': [], 'removed': [], 'modified': []}, + '/usr/sbin': {'added': [], 'removed': [], 'modified': []} + } + elif os_type == 'windows': + filter_data = { + 'C:\\Program Files': {'added': [], 'removed': [], 'modified': []}, + 'C:\\Program Files (x86)': {'added': [], 'removed': [], 'modified': []}, + 'C:\\Users\\vagrant' : {'added': [], 'removed': [], 'modified': []} + } # Use of filters for directory, changes in result.items(): @@ -373,7 +379,7 @@ def assert_results(result, params = None) -> None: for category in categories: for action in actions: - assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category}{action}') + assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category} {action}') def areAgent_processes_active(agent_params): """ diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index f274152fd3..3df56ce747 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -568,6 +568,7 @@ def _checkfiles(inventory_path, os_type, directory, filters_keywords= None, hash filters += f" | grep -v {filter_}" command = f'sudo find {directory} -type f -exec shasum -a 256 {{}} \; {filter}' result = ConnectionManager.execute_commands(inventory_path, command) + elif 'windows' in os_type: quoted_filters = ['"{}"'.format(keyword) for keyword in filters_keywords] filter_files = ",".join(quoted_filters) diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index 75ebf346e2..f8d36ba33e 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -221,7 +221,7 @@ def assert_results(result) -> None: # Testing the results for category in categories: for action in actions: - assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category}{action}') + assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category} {action}') @staticmethod From 8196c70252c99fe1c2f1a7cce16c2a6909b981e8 Mon Sep 17 00:00:00 2001 From: Marcelo Ariel Hamra Date: Mon, 29 Apr 2024 10:31:18 -0300 Subject: [PATCH 066/195] Fix task dependencies --- .../manager/aws/dtt1-managers-poc-aws.yaml | 14 +++++++++++++- .../manager/vagrant/dtt1-managers-poc-vagrant.yaml | 13 +++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml index 2471660ebe..424756c6f1 100644 --- a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml @@ -64,4 +64,16 @@ tasks: - component: "manager" - wazuh-version: "4.7.3" - wazuh-revision: "40714" - - live: "True" \ No newline at end of file + - live: "True" + depends-on: + - "allocate-manager-linux-ubuntu-20.04-amd64" + - "allocate-manager-linux-ubuntu-22.04-amd64" + - "allocate-manager-linux-amazon-2-amd64" + - "allocate-manager-linux-redhat-7-amd64" + - "allocate-manager-linux-redhat-8-amd64" + - "allocate-manager-linux-redhat-9-amd64" + - "allocate-manager-linux-centos-7-amd64" + - "allocate-manager-linux-centos-8-amd64" + - "allocate-manager-linux-debian-10-amd64" + - "allocate-manager-linux-debian-11-amd64" + - "allocate-manager-linux-debian-12-amd64" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index 4b2fa717a2..0fbf128343 100644 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -65,3 +65,16 @@ tasks: - wazuh-version: "4.7.3" - wazuh-revision: "40714" - live: "True" + depends-on: + - "allocate-manager-linux-ubuntu-20.04-amd64" + - "allocate-manager-linux-ubuntu-22.04-amd64" + - "allocate-manager-linux-amazon-2-amd64" + - "allocate-manager-linux-redhat-7-amd64" + - "allocate-manager-linux-redhat-8-amd64" + - "allocate-manager-linux-redhat-9-amd64" + - "allocate-manager-linux-centos-7-amd64" + - "allocate-manager-linux-centos-8-amd64" + - "allocate-manager-linux-debian-10-amd64" + - "allocate-manager-linux-debian-11-amd64" + - "allocate-manager-linux-debian-12-amd64" + - "allocate-manager-linux-oracle-9-amd64" \ No newline at end of file From 7490c95d9d7853e2c56ce62d7fd51ffbd2760e4f Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 29 Apr 2024 17:14:21 +0200 Subject: [PATCH 067/195] fix(#5229): Fixing validations after uninstall --- .../modules/testing/tests/helpers/agent.py | 20 ++++++++++++------- .../testing/tests/helpers/constants.py | 3 ++- .../modules/testing/tests/helpers/generic.py | 1 + .../tests/test_agent/test_uninstall.py | 7 +++++-- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index ec46e025e7..b2b3a2b4d3 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -5,7 +5,7 @@ import yaml from typing import List, Optional -from .constants import WAZUH_CONF, WAZUH_ROOT, WAZUH_WINDOWS_CONF +from .constants import WAZUH_CONF, WAZUH_ROOT, WAZUH_WINDOWS_CONF, WAZUH_MACOS_CONF from .executor import WazuhAPI, ConnectionManager from .generic import HostInformation, CheckFiles from modules.testing.utils import logger @@ -23,11 +23,11 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv release = 'pre-release' os_type = HostInformation.get_os_type(inventory_path) + architecture = HostInformation.get_architecture(inventory_path) commands = [] if os_type == 'linux': distribution = HostInformation.get_linux_distribution(inventory_path) - architecture = HostInformation.get_architecture(inventory_path) if distribution == 'rpm' and 'amd64' in architecture: commands.extend([ @@ -66,11 +66,11 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv ]) commands.extend(["NET START WazuhSvc"]) elif os_type == 'macos': - if 'amd64' in architecture: + if architecture == 'amd64': commands.extend([ f'curl -so wazuh-agent.pkg https://{s3_url}.wazuh.com/{release}/macos/wazuh-agent-{wazuh_version}-1.intel64.pkg && echo "WAZUH_MANAGER=\'MANAGER_IP\' && WAZUH_AGENT_NAME=\'{agent_name}\'" > /tmp/wazuh_envs && sudo installer -pkg ./wazuh-agent.pkg -target /' ]) - elif 'arm64' in architecture: + elif architecture == 'arm64': commands.extend([ f'curl -so wazuh-agent.pkg https://{s3_url}.wazuh.com/{release}/macos/wazuh-agent-{wazuh_version}-1.arm64.pkg && echo "WAZUH_MANAGER=\'MANAGER_IP\' && WAZUH_AGENT_NAME=\'{agent_name}\'" > /tmp/wazuh_envs && sudo installer -pkg ./wazuh-agent.pkg -target /' ]) @@ -125,7 +125,7 @@ def register_agent(inventory_path, manager_path): else: host_ip = HostInformation.get_public_ip_from_aws_dns(manager_host) commands = [ - f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' /Library/Ossec/etc/ossec.conf", + f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_MACOS_CONF}", "/Library/Ossec/bin/wazuh-control restart" ] ConnectionManager.execute_commands(inventory_path, commands) @@ -393,13 +393,19 @@ def areAgent_processes_active(agent_params): """ os_type = HostInformation.get_os_type(agent_params) - if 'linux' in os_type: + if os_type == 'linux': result = ConnectionManager.execute_commands(agent_params, 'pgrep wazuh') if result.get('success'): return bool([int(numero) for numero in result.get('output').splitlines()]) else: return False - elif 'windows' in os_type: + + if os_type == 'macos': + result = ConnectionManager.execute_commands(agent_params, 'pgrep wazuh') + return bool([int(numero) for numero in result.splitlines()]) + + + elif os_type == 'windows': result = ConnectionManager.execute_commands(agent_params, 'Get-Process -Name "wazuh-agent" | Format-Table -HideTableHeaders ProcessName') if result.get('success'): return 'wazuh-agent' in result.get('output') diff --git a/deployability/modules/testing/tests/helpers/constants.py b/deployability/modules/testing/tests/helpers/constants.py index 95c1c470a2..76e2585954 100755 --- a/deployability/modules/testing/tests/helpers/constants.py +++ b/deployability/modules/testing/tests/helpers/constants.py @@ -18,11 +18,12 @@ WINDOWS_VERSION = Path(WINDOWS_ROOT_DIR, "VERSION") WINDOWS_REVISION = Path(WINDOWS_ROOT_DIR, "REVISION") - MACOS_ROOT_DIR = Path("/Library", "Ossec") MACOS_CONFIGURATIONS_DIR = Path(MACOS_ROOT_DIR, "etc") WAZUH_MACOS_CONF = Path(MACOS_CONFIGURATIONS_DIR, "ossec.conf") MACOS_CLIENT_KEYS = Path(MACOS_CONFIGURATIONS_DIR, "client.keys") +MACOS_VERSION = Path(MACOS_ROOT_DIR, "VERSION") +MACOS_REVISION = Path(MACOS_ROOT_DIR, "REVISION") # Binaries paths BINARIES_DIR = Path(WAZUH_ROOT, "bin") diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 3df56ce747..647a77b287 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -39,6 +39,7 @@ def dir_exists(inventory_path, dir_path) -> str: return result.get('output') elif os_type == 'windows': return ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{dir_path}"').get('success') + elif os_type == 'macos': return 'true' in ConnectionManager.execute_commands(inventory_path, f'stat {dir_path} >/dev/null 2>&1 && echo "true" || echo "false"') diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index 9535ae6c90..83fd4bd6c0 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -6,7 +6,7 @@ import re from ..helpers.agent import WazuhAgent -from ..helpers.constants import WAZUH_ROOT, WINDOWS_CONFIGURATIONS_DIR, WINDOWS_ROOT_DIR, MACOS_ROOT_DIR +from ..helpers.constants import WAZUH_ROOT, WINDOWS_CONFIGURATIONS_DIR, WINDOWS_ROOT_DIR, MACOS_ROOT_DIR, MACOS_CONFIGURATIONS_DIR from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI from modules.testing.utils import logger @@ -93,7 +93,10 @@ def test_agent_uninstalled_directory(wazuh_params): path_to_check = WAZUH_ROOT elif os_type == 'windows': path_to_check = WINDOWS_CONFIGURATIONS_DIR - assert HostInformation.dir_exists(agent_params, path_to_check), logger.error(f'The {path_to_check} is still present in the agent {agent_names}') + elif os_type == 'macos': + path_to_check = MACOS_CONFIGURATIONS_DIR + + assert not HostInformation.dir_exists(agent_params, path_to_check), logger.error(f'The {path_to_check} is still present in the agent {agent_names}') def test_service(wazuh_params): From e07fb3fa433858244698a77a6c422b24de827861 Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 29 Apr 2024 17:28:16 +0200 Subject: [PATCH 068/195] fix(#5229): Fixing basic_info validations --- .../modules/testing/tests/helpers/agent.py | 2 +- .../modules/testing/tests/helpers/generic.py | 22 +++++++------------ .../tests/test_agent/test_basic_info.py | 5 +++-- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index b2b3a2b4d3..9a265bcc1a 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -133,7 +133,7 @@ def register_agent(inventory_path, manager_path): raise Exception(f'Error registering agent. Error executing: {commands} with error: {e}') result = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}') - assert host_ip in result.get('output'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + assert host_ip in result, logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') elif os_type == 'windows': try: diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 647a77b287..404583a272 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -184,20 +184,14 @@ def get_os_name_and_version_from_inventory(inventory_path) -> tuple: def get_os_version_from_inventory(inventory_path) -> str: os_type = HostInformation.get_os_type(inventory_path) - if os_type == 'linux': - if 'manager' in inventory_path: - os_version = re.search(r".*?/manager-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) - elif 'agent' in inventory_path: - os_version = re.search(r".*?/agent-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) - return os_version - else: - return None - elif os_type == 'windows': - if 'agent' in inventory_path: - os_version = re.search(r".*?/agent-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path)[1:3] - return os_version - else: - return None + if 'manager' in inventory_path: + os_version = re.search(r".*?/manager-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) + elif 'agent' in inventory_path: + os_version = re.search(r".*?/agent-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) + return os_version + else: + return None + @staticmethod def get_current_dir(inventory_path) -> str: diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index 77ab0f8a87..63d502c4a2 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -85,9 +85,10 @@ def test_wazuh_os_version(wazuh_params): if not os_type == 'windows': assert HostInformation.get_os_version_from_inventory(agent_params) in WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names), logger.error('There is a mismatch between the OS version and the OS version of the installed agent') if os_type == 'linux': - assert HostInformation.get_os_name_from_inventory(agent_params) in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') + os_name = HostInformation.get_os_name_from_inventory(agent_params) elif os_type == 'macos': - assert 'macos' in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') + os_name = 'macos' + assert os_name in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') def test_wazuh_version(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): From a6d89aa66404adfcd622a8d3d576e2d0e9e2b272 Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 30 Apr 2024 10:15:24 +0200 Subject: [PATCH 069/195] fix(#5229): Fixing validations of basic info and install --- .../modules/testing/tests/helpers/agent.py | 61 ++++++++++------ .../testing/tests/helpers/constants.py | 11 +++ .../modules/testing/tests/helpers/executor.py | 13 ++-- .../modules/testing/tests/helpers/generic.py | 69 +++++++++++-------- .../modules/testing/tests/helpers/manager.py | 4 +- .../tests/test_agent/test_basic_info.py | 1 + .../testing/tests/test_agent/test_install.py | 3 +- 7 files changed, 101 insertions(+), 61 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 9a265bcc1a..ed7c80579d 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -65,6 +65,7 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv f"WAZUH_REGISTRATION_SERVER='MANAGER_IP' " ]) commands.extend(["NET START WazuhSvc"]) + elif os_type == 'macos': if architecture == 'amd64': commands.extend([ @@ -78,8 +79,8 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv '/Library/Ossec/bin/wazuh-control start', '/Library/Ossec/bin/wazuh-control status' ] - commands.extend(system_commands) + logger.info(f'Installing Agent in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') ConnectionManager.execute_commands(inventory_path, commands) @@ -132,7 +133,7 @@ def register_agent(inventory_path, manager_path): except Exception as e: raise Exception(f'Error registering agent. Error executing: {commands} with error: {e}') - result = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}') + result = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_MACOS_CONF}').get('output') assert host_ip in result, logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') elif os_type == 'windows': @@ -159,24 +160,22 @@ def set_protocol_agent_connection(inventory_path, protocol): f"sed -i 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_CONF}", "systemctl restart wazuh-agent" ] - ConnectionManager.execute_commands(inventory_path, commands) result = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}') assert protocol in result.get('output'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') elif os_type == 'macos': - commands = [ - f"sed -i '' 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' /Library/Ossec/etc/ossec.conf", - "/Library/Ossec/bin/wazuh-control restart" - ] - ConnectionManager.execute_commands(inventory_path, commands) - assert protocol in ConnectionManager.execute_commands(inventory_path, f'cat /Library/Ossec/etc/ossec.conf'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + commands = [ + f"sed -i '' 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_MACOS_CONF}", + "/Library/Ossec/bin/wazuh-control restart" + ] + ConnectionManager.execute_commands(inventory_path, commands) + assert protocol in ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_MACOS_CONF}').get('output'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') elif os_type == 'windows': commands = [ f"(Get-Content -Path '{WAZUH_WINDOWS_CONF}') -replace '[^<]*<\/protocol>', '{protocol}' | Set-Content -Path '{WAZUH_WINDOWS_CONF}'" ] - ConnectionManager.execute_commands(inventory_path, commands) result = ConnectionManager.execute_commands(inventory_path, f'Get-Content -Path "{WAZUH_WINDOWS_CONF}"') assert protocol in result.get('output'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') @@ -186,9 +185,11 @@ def set_protocol_agent_connection(inventory_path, protocol): def uninstall_agent(inventory_path, wazuh_version=None, wazuh_revision=None) -> None: os_type = HostInformation.get_os_type(inventory_path) commands = [] + if os_type == 'linux': distribution = HostInformation.get_linux_distribution(inventory_path) os_name = HostInformation.get_os_name_from_inventory(inventory_path) + if os_name == 'opensuse' or os_name == 'suse': commands.extend([ "zypper remove --no-confirm wazuh-agent", @@ -198,25 +199,23 @@ def uninstall_agent(inventory_path, wazuh_version=None, wazuh_revision=None) -> if distribution == 'deb': commands.extend([ "apt-get remove --purge wazuh-agent -y" - ]) elif distribution == 'rpm': commands.extend([ "yum remove wazuh-agent -y", f"rm -rf {WAZUH_ROOT}" ]) - - system_commands = [ "systemctl disable wazuh-agent", "systemctl daemon-reload" ] - commands.extend(system_commands) + elif os_type == 'windows': commands.extend([ f"msiexec.exe /x $env:TEMP\wazuh-agent.msi /qn" ]) + elif os_type == 'macos': commands.extend([ "/Library/Ossec/bin/wazuh-control stop", @@ -303,11 +302,13 @@ def perform_action_and_scan(agent_params, action_callback) -> dict: '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, '/usr/sbin': {'added': [], 'removed': [], 'modified': []} } + elif os_type == 'macos': filter_data = { '/usr/bin': {'added': [], 'removed': [], 'modified': []}, '/usr/sbin': {'added': [], 'removed': [], 'modified': []} } + elif os_type == 'windows': filter_data = { 'C:\\Program Files': {'added': [], 'removed': [], 'modified': []}, @@ -369,8 +370,10 @@ def assert_results(result, params = None) -> None: if os_type == 'linux': categories = ['/root', '/usr/bin', '/usr/sbin', '/boot'] + elif os_type == 'windows': categories = ['C:\\Program Files', 'C:\\Program Files (x86)','C:\\Users\\vagrant'] + elif os_type == 'macos': categories = ['/usr/bin', '/usr/sbin'] @@ -396,14 +399,16 @@ def areAgent_processes_active(agent_params): if os_type == 'linux': result = ConnectionManager.execute_commands(agent_params, 'pgrep wazuh') if result.get('success'): - return bool([int(numero) for numero in result.get('output').splitlines()]) + return bool([int(number) for number in result.get('output').splitlines()]) else: return False if os_type == 'macos': result = ConnectionManager.execute_commands(agent_params, 'pgrep wazuh') - return bool([int(numero) for numero in result.splitlines()]) - + if result.get('success'): + return bool([int(number) for number in result.get('output').splitlines()]) + else: + return False elif os_type == 'windows': result = ConnectionManager.execute_commands(agent_params, 'Get-Process -Name "wazuh-agent" | Format-Table -HideTableHeaders ProcessName') @@ -425,14 +430,26 @@ def isAgent_port_open(agent_params): """ os_type = HostInformation.get_os_type(agent_params) - if 'linux' in os_type: + if os_type == 'linux': result = ConnectionManager.execute_commands(agent_params, 'ss -t -a -n | grep ":1514" | grep ESTAB') - return result.get('success') - elif 'windows' in os_type : + if result.get('success'): + return 'ESTAB' in result.get('output') + else: + return False + + elif os_type == 'windows': result = ConnectionManager.execute_commands(agent_params, 'netstat -ano | Select-String -Pattern "TCP" | Select-String -Pattern "ESTABLISHED" | Select-String -Pattern ":1514"') - return 'ESTABLISHED' in result.get('output') + if result.get('success'): + return 'ESTABLISHED' in result.get('output') + else: + return False + elif os_type == 'macos': - return 'ESTABLISHED' in ConnectionManager.execute_commands(agent_params, 'netstat -an | grep ".1514 " | grep ESTABLISHED') + result = ConnectionManager.execute_commands(agent_params, 'netstat -an | grep ".1514 " | grep ESTABLISHED') + if result.get('success'): + return 'ESTABLISHED' in result.get('output') + else: + return False def get_agents_information(wazuh_api: WazuhAPI) -> list: """ diff --git a/deployability/modules/testing/tests/helpers/constants.py b/deployability/modules/testing/tests/helpers/constants.py index 76e2585954..93657c2ee8 100755 --- a/deployability/modules/testing/tests/helpers/constants.py +++ b/deployability/modules/testing/tests/helpers/constants.py @@ -25,16 +25,27 @@ MACOS_VERSION = Path(MACOS_ROOT_DIR, "VERSION") MACOS_REVISION = Path(MACOS_ROOT_DIR, "REVISION") + # Binaries paths BINARIES_DIR = Path(WAZUH_ROOT, "bin") WAZUH_CONTROL = Path(BINARIES_DIR, "wazuh-control") AGENT_CONTROL = Path(BINARIES_DIR, "agent_control") CLUSTER_CONTROL = Path(BINARIES_DIR, "cluster_control") + +MACOS_BINARIES_DIR = Path(MACOS_ROOT_DIR, "bin") +MACOS_WAZUH_CONTROL = Path(MACOS_BINARIES_DIR, "wazuh-control") + # Logs paths LOGS_DIR = Path(WAZUH_ROOT, "logs") WAZUH_LOG = Path(LOGS_DIR, "ossec.log") ALERTS_DIR = Path(LOGS_DIR, "alerts") ALERTS_JSON = Path(ALERTS_DIR, "alerts.json") + +MACOS_LOGS_DIR = Path(MACOS_ROOT_DIR, "logs") +WAZUH_MACOS_LOG = Path(MACOS_LOGS_DIR, "ossec.log") +MACOS_ALERTS_DIR = Path(MACOS_LOGS_DIR, "alerts") +MACOS_ALERTS_JSON = Path(MACOS_ALERTS_DIR, "alerts.json") + # Daemons running paths DAEMONS_DIR = Path(WAZUH_ROOT, "var", "run") AGENTD_STATE = Path(DAEMONS_DIR, "wazuh-agentd.state") diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index 34fad17fcd..aa38ac662c 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -113,21 +113,24 @@ def _execute_command(data, command) -> dict: class MacosExecutor(): @staticmethod def _execute_command(data, command) -> dict: - try: ssh_client = paramiko.SSHClient() ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh_client.connect(hostname=data.get('host'), port=data.get('port'), username=data.get('username'), password=data.get('username')) + ssh_client.connect(hostname=data.get('host'), port=data.get('port'), username=data.get('username'), password=data.get('password')) stdin, stdout, stderr = ssh_client.exec_command(f"sudo {command}") - result = ''.join(stdout.readlines()) + stdout_str = ''.join(stdout.readlines()) + stderr_str = ''.join(stderr.readlines()) ssh_client.close() - return result + if stdout_str: + return {'success': True, 'output': stdout_str.replace('\n', '')} + if stderr_str: + return {'success': False, 'output': stderr_str.replace('\n', '')} + return {'success': False, 'output': None} except Exception as e: - #return {'success': False, 'output': ret.stderr} raise Exception(f'Error executing command: {command} with error: {e}') # ------------------------------------------------------ diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 404583a272..975cc1578b 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -12,7 +12,7 @@ import yaml from pathlib import Path -from .constants import WAZUH_CONTROL, CLIENT_KEYS, WINDOWS_CLIENT_KEYS, WINDOWS_VERSION, WINDOWS_REVISION +from .constants import WAZUH_CONTROL, CLIENT_KEYS, WINDOWS_CLIENT_KEYS, WINDOWS_VERSION, WINDOWS_REVISION, MACOS_WAZUH_CONTROL, MACOS_CLIENT_KEYS from .executor import ConnectionManager from modules.testing.utils import logger @@ -34,14 +34,13 @@ def dir_exists(inventory_path, dir_path) -> str: os_type = HostInformation.get_os_type(inventory_path) if os_type == 'linux': - result = ConnectionManager.execute_commands(inventory_path, f'test -d {dir_path} && echo "True" || echo "False"') + return 'True' in ConnectionManager.execute_commands(inventory_path, f'test -d {dir_path} && echo "True" || echo "False"').get('output') - return result.get('output') elif os_type == 'windows': return ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{dir_path}"').get('success') elif os_type == 'macos': - return 'true' in ConnectionManager.execute_commands(inventory_path, f'stat {dir_path} >/dev/null 2>&1 && echo "true" || echo "false"') + return 'true' in ConnectionManager.execute_commands(inventory_path, f'stat {dir_path} >/dev/null 2>&1 && echo "true" || echo "false"').get('output') @staticmethod def file_exists(inventory_path, file_path) -> bool: @@ -57,12 +56,11 @@ def file_exists(inventory_path, file_path) -> bool: """ os_type = HostInformation.get_os_type(inventory_path) if os_type == 'linux': - result = ConnectionManager.execute_commands(inventory_path, f'test -f {file_path} && echo "True" || echo "False"') - return result.get('output') + return ConnectionManager.execute_commands(inventory_path, f'test -f {file_path} && echo "True" || echo "False"').get('output') elif os_type == 'windows': return ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{file_path}"').get('output') elif os_type == 'macos': - return 'true' in ConnectionManager.execute_commands(inventory_path, f'stat {file_path} >/dev/null 2>&1 && echo "true" || echo "false"') + return 'true' in ConnectionManager.execute_commands(inventory_path, f'stat {file_path} >/dev/null 2>&1 && echo "true" || echo "false"').get('output') @staticmethod def get_os_type(inventory_path) -> str: @@ -211,6 +209,9 @@ def get_current_dir(inventory_path) -> str: return result.get('output').replace("\n","") elif os_type == 'windows': return ConnectionManager.execute_commands(inventory_path, '(Get-Location).Path').get('output') + elif os_type == 'macos': + result = ConnectionManager.execute_commands(inventory_path, 'pwd').get('output') + return result.replace("\n","") @staticmethod def get_internal_ip_from_aws_dns(dns_name): @@ -267,20 +268,22 @@ def get_client_keys(inventory_path) -> list[dict]: elif os_type == 'windows': client_key = ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WINDOWS_CLIENT_KEYS}"').get('output') elif os_type == 'macos': - client_key = ConnectionManager.execute_commands(inventory_path, f'cat /Library/Ossec/etc/client.keys') - - lines = client_key.split('\n')[:-1] - for line in lines: - _id, name, address, password = line.strip().split() - client_info = { - "id": _id, - "name": name, - "address": address, - "password": password - } - clients.append(client_info) - return clients - + client_key = ConnectionManager.execute_commands(inventory_path, f'cat {MACOS_CLIENT_KEYS}').get('output') + + if client_key != None: + lines = client_key.split('\n')[:-1] + for line in lines: + _id, name, address, password = line.strip().split() + client_info = { + "id": _id, + "name": name, + "address": address, + "password": password + } + clients.append(client_info) + return clients + else: + return [] class HostConfiguration: @@ -562,7 +565,7 @@ def _checkfiles(inventory_path, os_type, directory, filters_keywords= None, hash for filter_ in filters_keywords[1:]: filters += f" | grep -v {filter_}" command = f'sudo find {directory} -type f -exec shasum -a 256 {{}} \; {filter}' - result = ConnectionManager.execute_commands(inventory_path, command) + result = ConnectionManager.execute_commands(inventory_path, command).get('output') elif 'windows' in os_type: quoted_filters = ['"{}"'.format(keyword) for keyword in filters_keywords] @@ -678,7 +681,7 @@ def get_component_status(inventory_path, host_role) -> str: if result.get('success'): return result.get('output') elif os_type == 'macos': - return ConnectionManager.execute_commands(inventory_path, f'/Library/Ossec/bin/wazuh-control status | grep {host_role}') + return ConnectionManager.execute_commands(inventory_path, f'{MACOS_WAZUH_CONTROL} status | grep {host_role}').get('output') @staticmethod def component_stop(inventory_path, host_role) -> None: @@ -698,7 +701,7 @@ def component_stop(inventory_path, host_role) -> None: elif os_type == 'windows': ConnectionManager.execute_commands(inventory_path, f'NET STOP Wazuh') elif os_type == 'macos': - return ConnectionManager.execute_commands(inventory_path, f'/Library/Ossec/bin/wazuh-control stop | grep {host_role}') + ConnectionManager.execute_commands(inventory_path, f'{MACOS_WAZUH_CONTROL} stop | grep {host_role}') @staticmethod def component_restart(inventory_path, host_role) -> None: @@ -719,7 +722,7 @@ def component_restart(inventory_path, host_role) -> None: ConnectionManager.execute_commands(inventory_path, 'NET STOP Wazuh') ConnectionManager.execute_commands(inventory_path, 'NET START Wazuh') elif os_type == 'macos': - ConnectionManager.execute_commands(inventory_path, f'/Library/Ossec/bin/wazuh-control restart | grep {host_role}') + ConnectionManager.execute_commands(inventory_path, f'{MACOS_WAZUH_CONTROL} restart | grep {host_role}') @staticmethod def component_start(inventory_path, host_role) -> None: @@ -740,7 +743,7 @@ def component_start(inventory_path, host_role) -> None: elif os_type == 'windows': ConnectionManager.execute_commands(inventory_path, 'NET START Wazuh') elif os_type == 'macos': - ConnectionManager.execute_commands(inventory_path, f'/Library/Ossec/bin/wazuh-control start | grep {host_role}') + ConnectionManager.execute_commands(inventory_path, f'{MACOS_WAZUH_CONTROL} start | grep {host_role}') @staticmethod def get_component_version(inventory_path) -> str: @@ -757,10 +760,12 @@ def get_component_version(inventory_path) -> str: if os_type == 'linux': return ConnectionManager.execute_commands(inventory_path, f'{WAZUH_CONTROL} info -v').get('output') + elif os_type == 'windows': return ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WINDOWS_VERSION}"').get('output')#.replace("\n", "")) + elif os_type == 'macos': - ConnectionManager.execute_commands(inventory_path, f'/Library/Ossec/bin/wazuh-control info -v') + return ConnectionManager.execute_commands(inventory_path, f'{MACOS_WAZUH_CONTROL} info -v').get('output') @staticmethod def get_component_revision(inventory_path) -> str: @@ -781,7 +786,7 @@ def get_component_revision(inventory_path) -> str: elif os_type == 'windows': return ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WINDOWS_REVISION}"').get('output') elif os_type == 'macos': - return ConnectionManager.execute_commands(inventory_path, f'/Library/Ossec/bin/wazuh-control info -r') + return ConnectionManager.execute_commands(inventory_path, f'{MACOS_WAZUH_CONTROL} info -r').get('output') @staticmethod def hasAgentClientKeys(inventory_path) -> bool: @@ -806,7 +811,7 @@ def hasAgentClientKeys(inventory_path) -> bool: return result.get('output', '') return False elif os_type == 'macos': - return HostInformation.file_exists(inventory_path, '/Library/Ossec/etc/client.keys') + return HostInformation.file_exists(inventory_path, f'{MACOS_CLIENT_KEYS}') @staticmethod def isComponentActive(inventory_path, host_role) -> bool: @@ -831,7 +836,11 @@ def isComponentActive(inventory_path, host_role) -> bool: return result.get('success') elif os_type == 'macos': - return f'com.{host_role.replace("-", ".")}' in ConnectionManager.execute_commands(inventory_path, f'launchctl list | grep com.{host_role.replace("-", ".")}') + result = ConnectionManager.execute_commands(inventory_path, f'launchctl list | grep com.{host_role.replace("-", ".")}').get('output') + if result == None: + return False + else: + return f'com.{host_role.replace("-", ".")}' in result diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index f8d36ba33e..8ac7630e9e 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -250,10 +250,8 @@ def get_agent_control_info(inventory_path) -> None: Returns: str: Agents status """ - result = ConnectionManager.execute_commands(inventory_path, f'{AGENT_CONTROL} -l') - - return result.get('output') + return ConnectionManager.execute_commands(inventory_path, f'{AGENT_CONTROL} -l').get('output') @staticmethod diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index 63d502c4a2..ae507ee533 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -88,6 +88,7 @@ def test_wazuh_os_version(wazuh_params): os_name = HostInformation.get_os_name_from_inventory(agent_params) elif os_type == 'macos': os_name = 'macos' + assert os_name in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') def test_wazuh_version(wazuh_params): diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index f5e5ba9322..2c9b84734d 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -11,7 +11,7 @@ from modules.testing.utils import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils - +from ..helpers.executor import ConnectionManager @pytest.fixture(scope="module", autouse=True) def wazuh_params(request): @@ -72,6 +72,7 @@ def test_installation(wazuh_params): for agent_name, agent_params in wazuh_params['agents'].items(): HostConfiguration.disable_firewall(agent_params) + if HostInformation.dir_exists(wazuh_params['master'], WAZUH_ROOT): logger.info(f'Manager is already installed in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])}') else: From 0ac62a3c78fd49827deb6e4af589684b299b17b0 Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 30 Apr 2024 10:22:24 +0200 Subject: [PATCH 070/195] fix(#5229): White spaces added --- .../examples/agent/aws/test-agent-complete-macOS.yaml | 2 +- .../examples/agent/vagrant/test-agent-complete-macOS.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml index 773eb70f9e..2ae2d26e5f 100644 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml @@ -112,4 +112,4 @@ tasks: - variable: agent-os as: agent depends-on: - - "allocate-agent-{agent}" \ No newline at end of file + - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml index 115bd0d96a..d1e35d3e71 100644 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml @@ -110,4 +110,4 @@ tasks: - variable: agent-os as: agent depends-on: - - "allocate-agent-{agent}" \ No newline at end of file + - "allocate-agent-{agent}" From c55fd32c94d8502746a5e096c79a6052df1ee700 Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 30 Apr 2024 10:36:31 +0200 Subject: [PATCH 071/195] enhancement(#5229): Removing unused imports --- .../modules/testing/tests/test_agent/test_basic_info.py | 2 +- deployability/modules/testing/tests/test_agent/test_install.py | 1 - deployability/modules/testing/tests/test_agent/test_restart.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index ae507ee533..4170bfea4f 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -7,7 +7,7 @@ from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.constants import WAZUH_ROOT, WINDOWS_ROOT_DIR, MACOS_ROOT_DIR -from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions, Waits +from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from modules.testing.utils import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index 2c9b84734d..a1058454b6 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -11,7 +11,6 @@ from modules.testing.utils import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils -from ..helpers.executor import ConnectionManager @pytest.fixture(scope="module", autouse=True) def wazuh_params(request): diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index 5f0ba90e99..bd0a90c9a6 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -5,7 +5,7 @@ import pytest import re -from ..helpers.agent import WazuhAgent, WazuhAPI +from ..helpers.agent import WazuhAgent from ..helpers.generic import GeneralComponentActions, HostInformation from modules.testing.utils import logger from ..helpers.manager import WazuhManager From 26986c23ff3dd4bd446a5b2c97183b235dc026f5 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 30 Apr 2024 07:05:35 -0300 Subject: [PATCH 072/195] Storage is added according to the size of the AWS instance --- deployability/modules/allocation/aws/models.py | 1 + deployability/modules/allocation/aws/provider.py | 15 +++++++++++++++ .../modules/allocation/static/specs/size.yml | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/deployability/modules/allocation/aws/models.py b/deployability/modules/allocation/aws/models.py index f862f0e1e7..471b846eb9 100644 --- a/deployability/modules/allocation/aws/models.py +++ b/deployability/modules/allocation/aws/models.py @@ -11,6 +11,7 @@ class AWSConfig(ProviderConfig): user: str key_name: str type: str + storage: int security_groups: list[str] termination_date: str issue: str | None = None diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py index 9aaf277565..66e393cce0 100644 --- a/deployability/modules/allocation/aws/provider.py +++ b/deployability/modules/allocation/aws/provider.py @@ -197,6 +197,7 @@ def __create_ec2_instance(config: AWSConfig) -> str: with open(windosUserData_file, 'r') as file: userData = file.read() userData = userData.replace('ChangeMe', config.key_name) + config.storage = 60 else: with open(userData_file, 'r') as file: userData = file.read() @@ -204,6 +205,17 @@ def __create_ec2_instance(config: AWSConfig) -> str: 'ImageId': config.ami, 'InstanceType': config.type, 'SecurityGroupIds': config.security_groups, + 'BlockDeviceMappings': [ + { + 'DeviceName': '/dev/sda1', + 'Ebs': { + + 'DeleteOnTermination': True, + 'VolumeSize': config.storage, + 'VolumeType': 'gp2' + }, + }, + ], 'MinCount': 1, 'MaxCount': 1, 'UserData': userData, @@ -262,12 +274,15 @@ def __parse_config(cls, params: CreationPayload, credentials: AWSCredentials, is os_specs['zone'] = os_specs['zone'] + 'c' if arch == 'arm64': config['type'] = 'mac2.metal' + config['storage'] = 100 if arch == 'amd64': config['type'] = 'mac1.metal' + config['storage'] = 100 else: for spec in size_specs: if fnmatch.fnmatch(arch, spec): config['type'] = size_specs[spec]['type'] + config['storage'] = int(size_specs[spec]['storage']) break config['ami'] = os_specs['ami'] diff --git a/deployability/modules/allocation/static/specs/size.yml b/deployability/modules/allocation/static/specs/size.yml index 947ba65730..2ddfa52003 100755 --- a/deployability/modules/allocation/static/specs/size.yml +++ b/deployability/modules/allocation/static/specs/size.yml @@ -15,20 +15,28 @@ aws: micro: amd64: type: t2.small + storage: 20 arm64: type: a1.medium + storage: 20 small: amd64: type: t3.small + storage: 20 arm64: type: a1.large + storage: 20 medium: amd64: type: t3a.medium + storage: 20 arm64: type: a1.xlarge + storage: 20 large: amd64: type: c5ad.xlarge + storage: 30 arm64: type: c6g.xlarge + storage: 30 From e25374893a8dbc4b5c503cd4f4425998d0364c33 Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 30 Apr 2024 13:12:24 +0200 Subject: [PATCH 073/195] enhancement(#5229): Fixing dir_exists validation for windows --- .../modules/testing/tests/helpers/generic.py | 4 +- .../test-agent-complete-OS-integration.yaml | 170 ++++++++++++++++++ 2 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-OS-integration.yaml diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 975cc1578b..3124de4fa4 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -37,7 +37,9 @@ def dir_exists(inventory_path, dir_path) -> str: return 'True' in ConnectionManager.execute_commands(inventory_path, f'test -d {dir_path} && echo "True" || echo "False"').get('output') elif os_type == 'windows': - return ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{dir_path}"').get('success') + result = ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{dir_path}"') + if result.get('success'): + return 'True' in result.get('output') elif os_type == 'macos': return 'true' in ConnectionManager.execute_commands(inventory_path, f'stat {dir_path} >/dev/null 2>&1 && echo "true" || echo "false"').get('output') diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-OS-integration.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-OS-integration.yaml new file mode 100644 index 0000000000..6e8d2f8f42 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-OS-integration.yaml @@ -0,0 +1,170 @@ +version: 0.1 +description: This workflow is used to test agents' deployment for DDT1 PoC +variables: + agent-os: + - windows-server-2022-amd64 + - macos-sonoma-14.0-arm64 + - linux-ubuntu-22.04-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "aws" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-windows-server-2022-amd64" + description: "Allocate resources for the agent windows." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "aws" + - size: small + - composite-name: "windows-server-2022-amd64" + - inventory-output: "{working-dir}/agent-windows-server-2022-amd64/inventory.yaml" + - track-output: "{working-dir}/agent-windows-server-2022-amd64/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-windows-server-2022-amd64/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique agent allocate task + - task: "allocate-agent-macos-sonoma-14.0-arm64" + description: "Allocate resources for the agent sonoma." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "vagrant" + - size: small + - composite-name: "macos-sonoma-14.0-arm64" + - inventory-output: "{working-dir}/agent-macos-sonoma-14.0-arm64/inventory.yaml" + - track-output: "{working-dir}/agent-macos-sonoma-14.0-arm64/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-macos-sonoma-14.0-arm64/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique agent allocate task + - task: "allocate-agent-linux-ubuntu-22.04-amd64" + description: "Allocate resources for the agent ubuntu." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "vagrant" + - size: small + - composite-name: "linux-ubuntu-22.04-amd64" + - inventory-output: "{working-dir}/agent-linux-ubuntu-22.04-amd64/inventory.yaml" + - track-output: "{working-dir}/agent-linux-ubuntu-22.04-amd64/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-linux-ubuntu-22.04-amd64/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-windows-server-2022-amd64" + - "allocate-agent-linux-ubuntu-22.04-amd64" + - "allocate-agent-macos-sonoma-14.0-arm64" From 19c4f0d3b2182f8d2e40f993ddd53989a997117f Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 30 Apr 2024 08:27:32 -0300 Subject: [PATCH 074/195] Replace black mini parameters --- .../modules/allocation/vagrant/provider.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 38f64678e1..007eee5d50 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -279,14 +279,14 @@ def __remote_host(arch: str, action: str, os: str = None, instance_dir: Path = N ssh_password = client.get_secret_value(SecretId='devops_macstadium_m1_jenkins_password')['SecretString'] ssh_user = client.get_secret_value(SecretId='devops_macstadium_m1_jenkins_user')['SecretString'] except Exception as e: - raise ValueError('Could not get macOS macStadium server IP: ' + str(e) + '.') + raise ValueError('Could not get macOS macStadium ARM server IP: ' + str(e) + '.') try: tn = Telnet(server_ip, server_port, timeout) conn_ok = True tn.close() except Exception as e: - raise ValueError('Could not connect to macOS macStadium server: ' + str(e) + '.') + raise ValueError('Could not connect to macOS macStadium ARM server: ' + str(e) + '.') remote_host_parameters['server_ip'] = server_ip remote_host_parameters['ssh_password'] = ssh_password @@ -300,37 +300,37 @@ def __remote_host(arch: str, action: str, os: str = None, instance_dir: Path = N prlctl_output = subprocess.Popen(f"sshpass -p {ssh_password} ssh -o 'StrictHostKeyChecking no' {ssh_user}@{server_ip} {cmd}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0].decode('utf-8') data_list = json.loads(prlctl_output) except Exception as e: - raise ValueError('Could not get VMs running on macStadium server: ' + str(e) + '.') + raise ValueError('Could not get VMs running on macStadium ARM server: ' + str(e) + '.') uuid_count = 0 for item in data_list: if 'uuid' in item: uuid_count += 1 if uuid_count < 2: - logger.info(f"macStadium server has less than 2 VMs running, deploying in this host.") + logger.info(f"macStadium ARM server has less than 2 VMs running, deploying in this host.") return remote_host_parameters else: - raise ValueError(f"macStadium server is full capacity, use AWS provider.") + raise ValueError(f"macStadium ARM server is full capacity, use AWS provider.") else: return remote_host_parameters if arch == 'amd64': try: - server_ip = client.get_secret_value(SecretId='devops_black_mini_jenkins_ip')['SecretString'] - ssh_password = client.get_secret_value(SecretId='devops_black_mini_jenkins_password')['SecretString'] - ssh_user = client.get_secret_value(SecretId='devops_black_mini_jenkins_user')['SecretString'] + server_ip = client.get_secret_value(SecretId='devops_macstadium_intel_ip')['SecretString'] + ssh_password = client.get_secret_value(SecretId='devops_macstadium_intel_password')['SecretString'] + ssh_user = client.get_secret_value(SecretId='devops_macstadium_intel_user')['SecretString'] except Exception as e: - raise ValueError('Could not get macOS Black mini server IP: ' + str(e) + '.') + raise ValueError('Could not get macOS macStadium Intel server IP: ' + str(e) + '.') try: tn = Telnet(server_ip, server_port, timeout) conn_ok = True tn.close() except Exception as e: - raise ValueError('Could not connect to macOS Black mini server: ' + str(e) + '.') + raise ValueError('Could not connect to macOS macStadium Intel server: ' + str(e) + '.') remote_host_parameters['server_ip'] = server_ip remote_host_parameters['ssh_password'] = ssh_password remote_host_parameters['ssh_user'] = ssh_user - remote_host_parameters['host_provider'] = 'black_mini' + remote_host_parameters['host_provider'] = 'macstadium' if conn_ok: if action == 'create': @@ -345,10 +345,10 @@ def __remote_host(arch: str, action: str, os: str = None, instance_dir: Path = N raise ValueError('Could not get server load average: ' + str(e) + '.') if float(load_average) <= 10.0 and float(cpu_usage) <= 70.0 and float(memory_usage) <= 75.0: - logger.info(f"Using the black mini server to deploy.") + logger.info(f"Using the macStadium Intel server to deploy.") return remote_host_parameters else: - raise ValueError(f"Black mini server is under heavy load, use AWS provider.") + raise ValueError(f"macStadium Intel server is under heavy load, use AWS provider.") else: return remote_host_parameters From 263373d8e34b7df35c752892b38cb838265af8d6 Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 30 Apr 2024 13:50:23 +0200 Subject: [PATCH 075/195] enhancement(#5229): Fixing validations in basic_info agent name --- .../modules/testing/tests/test_agent/test_basic_info.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index 4170bfea4f..59d79181f6 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -84,10 +84,11 @@ def test_wazuh_os_version(wazuh_params): if not os_type == 'windows': assert HostInformation.get_os_version_from_inventory(agent_params) in WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names), logger.error('There is a mismatch between the OS version and the OS version of the installed agent') - if os_type == 'linux': - os_name = HostInformation.get_os_name_from_inventory(agent_params) - elif os_type == 'macos': + + if os_type == 'macos': os_name = 'macos' + else: + os_name = HostInformation.get_os_name_from_inventory(agent_params) assert os_name in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') From 1d7f29ea14797a320239bef5e5fb07beb0ad998d Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 30 Apr 2024 17:19:11 +0200 Subject: [PATCH 076/195] enhancement(#5229): Fixing validations in basic_info agent name part 2 --- .../modules/testing/tests/test_agent/test_basic_info.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index 59d79181f6..6edc2fc8f0 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -87,7 +87,9 @@ def test_wazuh_os_version(wazuh_params): if os_type == 'macos': os_name = 'macos' - else: + elif os_type == 'windows': + os_name = 'windows' + elif os_type == 'linux': os_name = HostInformation.get_os_name_from_inventory(agent_params) assert os_name in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') From b9b6a7c4523dccab2f1f26cf8069e63094a5ef4b Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 30 Apr 2024 18:20:48 +0200 Subject: [PATCH 077/195] enhancement(#5229): Removing integration test --- .../test-agent-complete-OS-integration.yaml | 170 ------------------ 1 file changed, 170 deletions(-) delete mode 100644 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-OS-integration.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-OS-integration.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-OS-integration.yaml deleted file mode 100644 index 6e8d2f8f42..0000000000 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-OS-integration.yaml +++ /dev/null @@ -1,170 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents' deployment for DDT1 PoC -variables: - agent-os: - - windows-server-2022-amd64 - - macos-sonoma-14.0-arm64 - - linux-ubuntu-22.04-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "aws" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-windows-server-2022-amd64" - description: "Allocate resources for the agent windows." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "aws" - - size: small - - composite-name: "windows-server-2022-amd64" - - inventory-output: "{working-dir}/agent-windows-server-2022-amd64/inventory.yaml" - - track-output: "{working-dir}/agent-windows-server-2022-amd64/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-windows-server-2022-amd64/track.yaml" - depends-on: - - "provision-manager-{manager-os}" - - # Unique agent allocate task - - task: "allocate-agent-macos-sonoma-14.0-arm64" - description: "Allocate resources for the agent sonoma." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "vagrant" - - size: small - - composite-name: "macos-sonoma-14.0-arm64" - - inventory-output: "{working-dir}/agent-macos-sonoma-14.0-arm64/inventory.yaml" - - track-output: "{working-dir}/agent-macos-sonoma-14.0-arm64/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-macos-sonoma-14.0-arm64/track.yaml" - depends-on: - - "provision-manager-{manager-os}" - - # Unique agent allocate task - - task: "allocate-agent-linux-ubuntu-22.04-amd64" - description: "Allocate resources for the agent ubuntu." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "vagrant" - - size: small - - composite-name: "linux-ubuntu-22.04-amd64" - - inventory-output: "{working-dir}/agent-linux-ubuntu-22.04-amd64/inventory.yaml" - - track-output: "{working-dir}/agent-linux-ubuntu-22.04-amd64/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-linux-ubuntu-22.04-amd64/track.yaml" - depends-on: - - "provision-manager-{manager-os}" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - on-error: "abort-all" - - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "allocate-agent-windows-server-2022-amd64" - - "allocate-agent-linux-ubuntu-22.04-amd64" - - "allocate-agent-macos-sonoma-14.0-arm64" From 65ca9b4687cd5d98cb49b609aa5d1f9a662d666c Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 10:16:14 +0200 Subject: [PATCH 078/195] fix(#5229): Fixes after review --- .../modules/testing/tests/helpers/executor.py | 2 +- .../modules/testing/tests/helpers/generic.py | 9 +++++++++ .../modules/testing/tests/helpers/manager.py | 1 - .../modules/testing/tests/helpers/utils.py | 2 +- .../testing/tests/test_agent/test_basic_info.py | 9 ++++----- .../testing/tests/test_agent/test_connection.py | 11 ++++++----- .../testing/tests/test_agent/test_install.py | 9 +++++---- .../testing/tests/test_agent/test_registration.py | 6 +++--- .../testing/tests/test_agent/test_restart.py | 13 +++++++------ .../modules/testing/tests/test_agent/test_stop.py | 11 ++++++----- .../testing/tests/test_agent/test_uninstall.py | 8 ++++---- .../testing/tests/test_manager/test_uninstall.py | 2 +- 12 files changed, 47 insertions(+), 36 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index aa38ac662c..828c958053 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -154,7 +154,7 @@ def _authenticate(self): result = ConnectionManager.execute_commands(self.inventory_path, 'pwd') file_path = result.get('output') + '/wazuh-install-files/wazuh-passwords.txt' result = ConnectionManager.execute_commands(self.inventory_path, f'test -f {file_path} && echo "true" || echo "false"') - if not 'true' in result.get('output'): + if 'true' not in result.get('output'): ConnectionManager.execute_commands(self.inventory_path, 'tar -xvf wazuh-install-files.tar') result = ConnectionManager.execute_commands(self.inventory_path, "grep api_password wazuh-install-files/wazuh-passwords.txt | head -n 1 | awk '{print $NF}'") password = result.get('output')[1:-1] diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 3124de4fa4..fd07bc6e35 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -182,6 +182,15 @@ def get_os_name_and_version_from_inventory(inventory_path) -> tuple: @staticmethod def get_os_version_from_inventory(inventory_path) -> str: + """ + It returns the os version from the inventory information + + Args: + inventory_path: host's inventory path + + Returns: + str: os version + """ os_type = HostInformation.get_os_type(inventory_path) if 'manager' in inventory_path: diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index 8ac7630e9e..16afbd7ef2 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -3,7 +3,6 @@ # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 import requests -import socket from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT from .executor import WazuhAPI, ConnectionManager diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index cb785ced49..2e133dc0f5 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -86,7 +86,7 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: logger.info("WinRM connection successful.") return True else: - logger.error(f'WinRM connection failed. Check the credentials in the inventory file.') + logger.error('WinRM connection failed. Check the credentials in the inventory file.') return False except Exception as e: logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index 6edc2fc8f0..a10fad9d57 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -2,14 +2,13 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest +from modules.testing.utils import logger from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.constants import WAZUH_ROOT, WINDOWS_ROOT_DIR, MACOS_ROOT_DIR from ..helpers.generic import HostInformation, GeneralComponentActions, Waits -from modules.testing.utils import logger -from ..helpers.manager import WazuhManager from ..helpers.utils import Utils @@ -95,10 +94,10 @@ def test_wazuh_os_version(wazuh_params): assert os_name in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') def test_wazuh_version(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert wazuh_params['wazuh_version'] in GeneralComponentActions.get_component_version(agent_params), logger.error(f"The version {HostInformation.get_os_name_and_version_from_inventory(agent_params)} is not {wazuh_params['wazuh_version']} by command") def test_wazuh_revision(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert wazuh_params['wazuh_revision'] in GeneralComponentActions.get_component_revision(agent_params), logger.error(f"The revision {HostInformation.get_os_name_and_version_from_inventory(agent_params)} is not {wazuh_params['wazuh_revision']} by command") diff --git a/deployability/modules/testing/tests/test_agent/test_connection.py b/deployability/modules/testing/tests/test_agent/test_connection.py index c777858ca2..8cbad363e9 100644 --- a/deployability/modules/testing/tests/test_agent/test_connection.py +++ b/deployability/modules/testing/tests/test_agent/test_connection.py @@ -2,13 +2,13 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest +from modules.testing.utils import logger from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from modules.testing.utils import logger from ..helpers.utils import Utils @@ -74,7 +74,8 @@ def test_connection(wazuh_params): def test_status(wazuh_params): for agent in wazuh_params['agents'].values(): status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent') - assert 'active' in status or 'connected' in status or "Running" in status or "is running" in status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') + valid_statuses = ['active', 'connected', 'Running', 'is running'] + assert any(valid_status in status for valid_status in valid_statuses), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') def test_service(wazuh_params): @@ -92,10 +93,10 @@ def test_clientKeys(wazuh_params): def test_port(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is closed') def test_processes(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are not active') diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index a1058454b6..7b2bd6d3ee 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -2,13 +2,13 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest +from modules.testing.utils import logger from ..helpers.agent import WazuhAgent from ..helpers.constants import WAZUH_ROOT, WINDOWS_ROOT_DIR, MACOS_ROOT_DIR from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions -from modules.testing.utils import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils @@ -64,7 +64,7 @@ def setup_test_environment(wazuh_params): def test_installation(wazuh_params): # Checking connection - for manager_name, manager_params in wazuh_params['managers'].items(): + for _, manager_params in wazuh_params['managers'].items(): Utils.check_inventory_connection(manager_params) # Certs creation, firewall management and Manager installation @@ -99,4 +99,5 @@ def test_installation(wazuh_params): def test_status(wazuh_params): for agent in wazuh_params['agents'].values(): agent_status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent') - assert 'loaded' in agent_status or 'Stopped' in agent_status or 'not running' in agent_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded') + valid_statuses = ['loaded', 'Stopped', 'not running'] + assert any(valid_status in agent_status for valid_status in valid_statuses), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded') diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py index 4447db4923..a757d9317d 100644 --- a/deployability/modules/testing/tests/test_agent/test_registration.py +++ b/deployability/modules/testing/tests/test_agent/test_registration.py @@ -2,13 +2,13 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest +from modules.testing.utils import logger from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) @@ -65,7 +65,7 @@ def setup_test_environment(wazuh_params): def test_status(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): WazuhAgent.register_agent(agent_params, wazuh_params['master']) for agent in wazuh_params['agents'].values(): status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent') diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index bd0a90c9a6..fe166c0593 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -2,12 +2,12 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest +from modules.testing.utils import logger from ..helpers.agent import WazuhAgent from ..helpers.generic import GeneralComponentActions, HostInformation -from modules.testing.utils import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils @@ -63,14 +63,15 @@ def setup_test_environment(wazuh_params): wazuh_params['agents'] = updated_agents def test_restart(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): GeneralComponentActions.component_restart(agent_params, 'wazuh-agent') def test_status(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): status = GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent') - assert 'active' in status or 'Running' in status or 'is running' in status, logger.error(f'{agent_names} is not active by command') + valid_statuses = ['active', 'Running', 'is running'] + assert any(valid_status in status for valid_status in valid_statuses), logger.error(f'{agent_names} is not active by command') def test_connection(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): @@ -88,10 +89,10 @@ def test_clientKeys(wazuh_params): def test_port(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is closed') def test_processes(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are not active') \ No newline at end of file diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index b673726002..8211b981ae 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -2,12 +2,12 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest +from modules.testing.utils import logger from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import GeneralComponentActions, Waits, HostInformation -from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) @@ -72,17 +72,18 @@ def test_service(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): status = GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent') - assert 'inactive' in status or 'Stopped' in status or 'StopPending' in status or 'not running' in status, logger.error(f'{agent_names} is still active by command') + valid_statuses = ['inactive', 'Stopped', 'StopPending', 'not running'] + assert any(valid_status in status for valid_status in valid_statuses), logger.error(f'{agent_names} is still active by command') expected_condition_func = lambda: 'disconnected' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) def test_port(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert not WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is still opened') def test_processes(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index 83fd4bd6c0..15622a0c67 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -2,14 +2,14 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest +from modules.testing.utils import logger from ..helpers.agent import WazuhAgent from ..helpers.constants import WAZUH_ROOT, WINDOWS_CONFIGURATIONS_DIR, WINDOWS_ROOT_DIR, MACOS_ROOT_DIR, MACOS_CONFIGURATIONS_DIR from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) @@ -109,10 +109,10 @@ def test_service(wazuh_params): def test_port(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert not WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is still opened') def test_processes(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') diff --git a/deployability/modules/testing/tests/test_manager/test_uninstall.py b/deployability/modules/testing/tests/test_manager/test_uninstall.py index f212cd4109..f21880bb22 100644 --- a/deployability/modules/testing/tests/test_manager/test_uninstall.py +++ b/deployability/modules/testing/tests/test_manager/test_uninstall.py @@ -50,7 +50,7 @@ def test_uninstall(wazuh_params): for manager in wazuh_params['managers'].values(): manager_status = GeneralComponentActions.get_component_status(manager, 'wazuh-manager') assert 'active' in manager_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(manager)} is not active') - for manager_name, manager_params in wazuh_params['managers'].items(): + for _, manager_params in wazuh_params['managers'].items(): WazuhManager.perform_uninstall_and_scan_for_manager(manager_params) From d24b525da6aafbb1f03a975f3a10c72dcef42003 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 11:28:23 +0200 Subject: [PATCH 079/195] fix(#5229): Fixing to snake_case --- deployability/modules/testing/tests/helpers/agent.py | 10 +++++----- deployability/modules/testing/tests/helpers/generic.py | 6 +++--- .../testing/tests/test_agent/test_basic_info.py | 2 +- .../testing/tests/test_agent/test_connection.py | 8 ++++---- .../modules/testing/tests/test_agent/test_install.py | 2 +- .../testing/tests/test_agent/test_registration.py | 6 +++--- .../modules/testing/tests/test_agent/test_restart.py | 8 ++++---- .../modules/testing/tests/test_agent/test_stop.py | 4 ++-- .../modules/testing/tests/test_agent/test_uninstall.py | 8 ++++---- 9 files changed, 27 insertions(+), 27 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 970e23627f..ac3e940dc9 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -420,7 +420,7 @@ def areAgent_processes_active(agent_params): return False - def isAgent_port_open(inventory_path): + def is_agent_port_open(inventory_path): """ Check if agent port is open @@ -430,23 +430,23 @@ def isAgent_port_open(inventory_path): Returns: str: Os name. """ - os_type = HostInformation.get_os_type(agent_params) + os_type = HostInformation.get_os_type(inventory_path) if os_type == 'linux': - result = ConnectionManager.execute_commands(agent_params, 'ss -t -a -n | grep ":1514" | grep ESTAB') + result = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":1514" | grep ESTAB') if result.get('success'): return 'ESTAB' in result.get('output') else: return False elif os_type == 'windows': - result = ConnectionManager.execute_commands(agent_params, 'netstat -ano | Select-String -Pattern "TCP" | Select-String -Pattern "ESTABLISHED" | Select-String -Pattern ":1514"') + result = ConnectionManager.execute_commands(inventory_path, 'netstat -ano | Select-String -Pattern "TCP" | Select-String -Pattern "ESTABLISHED" | Select-String -Pattern ":1514"') if result.get('success'): return 'ESTABLISHED' in result.get('output') else: return False elif os_type == 'macos': - result = ConnectionManager.execute_commands(agent_params, 'netstat -an | grep ".1514 " | grep ESTABLISHED') + result = ConnectionManager.execute_commands(inventory_path, 'netstat -an | grep ".1514 " | grep ESTABLISHED') if result.get('success'): return 'ESTABLISHED' in result.get('output') else: diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 5cc2a49204..5b1da0b618 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -330,7 +330,7 @@ def disable_firewall(inventory_path) -> None: if os_type == 'linux': commands = ["sudo systemctl stop firewalld", "sudo systemctl disable firewalld"] - if GeneralComponentActions.isComponentActive(inventory_path, 'firewalld'): + if GeneralComponentActions.is_component_active(inventory_path, 'firewalld'): ConnectionManager.execute_commands(inventory_path, commands) logger.info(f'Firewall disabled on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') @@ -801,7 +801,7 @@ def get_component_revision(inventory_path) -> str: return ConnectionManager.execute_commands(inventory_path, f'{MACOS_WAZUH_CONTROL} info -r').get('output') @staticmethod - def hasAgentClientKeys(inventory_path) -> bool: + def has_agent_client_keys(inventory_path) -> bool: """ Returns the True of False depending if in the component Client.keys exists @@ -825,7 +825,7 @@ def hasAgentClientKeys(inventory_path) -> bool: return HostInformation.file_exists(inventory_path, f'{MACOS_CLIENT_KEYS}') @staticmethod - def isComponentActive(inventory_path, host_role) -> bool: + def is_component_active(inventory_path, host_role) -> bool: """ Returns the True of False depending if the component is Active diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index a10fad9d57..a4c6b80d0e 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -53,7 +53,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params diff --git a/deployability/modules/testing/tests/test_agent/test_connection.py b/deployability/modules/testing/tests/test_agent/test_connection.py index 8cbad363e9..c2163ddb08 100644 --- a/deployability/modules/testing/tests/test_agent/test_connection.py +++ b/deployability/modules/testing/tests/test_agent/test_connection.py @@ -53,7 +53,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params @@ -81,7 +81,7 @@ def test_status(wazuh_params): def test_service(wazuh_params): wazuh_api = WazuhAPI(wazuh_params['master']) for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by API') + assert GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by API') expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) @@ -89,12 +89,12 @@ def test_service(wazuh_params): def test_clientKeys(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.hasAgentClientKeys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') + assert GeneralComponentActions.has_agent_client_keys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') def test_port(wazuh_params): for _, agent_params in wazuh_params['agents'].items(): - assert WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is closed') + assert WazuhAgent.is_agent_port_open(agent_params), logger.error('Port is closed') def test_processes(wazuh_params): diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index 7b2bd6d3ee..b0f1c687f8 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -53,7 +53,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py index 40c81bc3ac..416a3f96e0 100644 --- a/deployability/modules/testing/tests/test_agent/test_registration.py +++ b/deployability/modules/testing/tests/test_agent/test_registration.py @@ -53,7 +53,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params @@ -75,7 +75,7 @@ def test_status(wazuh_params): def test_service(wazuh_params): wazuh_api = WazuhAPI(wazuh_params['master']) for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') + assert GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) @@ -89,4 +89,4 @@ def test_connection(wazuh_params): def test_clientKeys(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.hasAgentClientKeys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') + assert GeneralComponentActions.has_agent_client_keys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index 09ad6c2582..366bf80ea7 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -53,7 +53,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params @@ -81,17 +81,17 @@ def test_connection(wazuh_params): def test_isActive(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') + assert GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') def test_clientKeys(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.hasAgentClientKeys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') + assert GeneralComponentActions.has_agent_client_keys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') def test_port(wazuh_params): for _, agent_params in wazuh_params['agents'].items(): - assert WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is closed') + assert WazuhAgent.is_agent_port_open(agent_params), logger.error('Port is closed') def test_processes(wazuh_params): diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index 180f1410a0..9a770e4607 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -56,7 +56,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params @@ -82,7 +82,7 @@ def test_service(wazuh_params): def test_port(wazuh_params): for _, agent_params in wazuh_params['agents'].items(): - assert not WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is still opened') + assert not WazuhAgent.is_agent_port_open(agent_params), logger.error('Port is still opened') def test_processes(wazuh_params): diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index 1fc09b3066..286e7110be 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -54,7 +54,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params @@ -65,7 +65,7 @@ def setup_test_environment(wazuh_params): def test_uninstall(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not Active before the installation') + assert GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not Active before the installation') os_type = HostInformation.get_os_type(agent_params) if os_type == 'linux': @@ -103,7 +103,7 @@ def test_agent_uninstalled_directory(wazuh_params): def test_service(wazuh_params): wazuh_api = WazuhAPI(wazuh_params['master']) for agent_names, agent_params in wazuh_params['agents'].items(): - assert not GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not inactive by command') + assert not GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not inactive by command') expected_condition_func = lambda: 'disconnected' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) @@ -111,7 +111,7 @@ def test_service(wazuh_params): def test_port(wazuh_params): for _, agent_params in wazuh_params['agents'].items(): - assert not WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is still opened') + assert not WazuhAgent.is_agent_port_open(agent_params), logger.error('Port is still opened') def test_processes(wazuh_params): From f111a8314a1c16b06d0da8e4287dd46dfc7f928e Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 11:34:43 +0200 Subject: [PATCH 080/195] fix(#5229): Restore allocation settings --- deployability/modules/allocation/static/specs/os.yml | 4 ++++ deployability/modules/allocation/vagrant/provider.py | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index aadd5fc5dc..1e49ce4db3 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -368,6 +368,10 @@ aws: ami: ami-0a747df120215911a zone: us-east-1 user: wazuh-user + windows-sign-10-amd64: + ami: ami-078255d5475b46db5 + zone: us-east-1 + user: wazuh-user windows-server-2012r2-amd64: ami: ami-05710c71113d5a40e zone: us-east-1 diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index d08f5f9d3b..38f64678e1 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -6,6 +6,7 @@ import platform, json import subprocess import boto3 +import random from jinja2 import Environment, FileSystemLoader from pathlib import Path @@ -244,8 +245,8 @@ def check_ip(ip): if response != 0: return ip - for i in range(2, 254): - ip = f"192.168.57.{i}" + for i in range(254): + ip = f"192.168.57.{random.randint(2, 253)}" if check_ip(ip): return ip From 433f6d75c36f5c96ed9304be7af5632516f3e604 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 14:05:08 +0200 Subject: [PATCH 081/195] enhancement(#5219): Integrating AIO tests with macOS and Windows Agent tests --- deployability/deps/requirements.txt | 2 +- deployability/modules/generic/ansible.py | 1 + deployability/modules/testing/main.py | 2 +- deployability/modules/testing/models.py | 2 +- .../modules/testing/tests/helpers/agent.py | 242 ++++++++++----- .../modules/testing/tests/helpers/central.py | 177 +++++++++++ .../testing/tests/helpers/constants.py | 26 ++ .../testing/tests/helpers/dashboard.py | 109 +++++++ .../modules/testing/tests/helpers/executor.py | 177 ++++++++--- .../modules/testing/tests/helpers/generic.py | 287 +++++++++++++----- .../modules/testing/tests/helpers/indexer.py | 110 +++++++ .../modules/testing/tests/helpers/manager.py | 104 ++++++- .../modules/testing/tests/helpers/utils.py | 53 +++- .../tests/test_agent/test_basic_info.py | 46 +-- .../tests/test_agent/test_connection.py | 20 +- .../testing/tests/test_agent/test_install.py | 33 +- .../tests/test_agent/test_registration.py | 28 +- .../testing/tests/test_agent/test_restart.py | 24 +- .../testing/tests/test_agent/test_stop.py | 17 +- .../tests/test_agent/test_uninstall.py | 42 ++- .../tests/test_central_components/__init__.py | 6 + .../test_central_components/test_install.py | 164 ++++++++++ .../test_central_components/test_restart.py | 94 ++++++ .../test_central_components/test_stop.py | 102 +++++++ .../test_central_components/test_uninstall.py | 84 +++++ .../tests/test_manager/test_uninstall.py | 2 +- .../agent/aws/test-agent-complete-macOS.yaml | 115 +++++++ .../agent/aws/test-agent-complete-macOs.yaml | 2 +- .../agent/aws/test-agent-complete.yaml | 2 +- .../aws/test-agent-restart-ins-prov.yaml | 2 +- .../agent/aws/test-agent-stop-ins-prov.yaml | 2 +- .../aws/test-agent-uninstall-ins-prov.yaml | 2 +- .../aws/test-agent-windows-complete.yaml | 119 ++++++++ .../agent/aws/test-agent-windows-install.yaml | 28 ++ .../agent/vagrant/test-agent-complete-1.yaml | 2 +- .../agent/vagrant/test-agent-complete-2.yaml | 2 +- .../vagrant/test-agent-complete-macOS.yaml | 113 +++++++ .../vagrant/test-agent-complete-macOs.yaml | 2 +- .../test-agent-restart-ins-prov-1.yaml | 2 +- .../test-agent-restart-ins-prov-2.yaml | 2 +- .../vagrant/test-agent-stop-ins-prov-1.yaml | 2 +- .../vagrant/test-agent-stop-ins-prov-2.yaml | 2 +- .../test-agent-uninstall-ins-prov-1.yaml | 2 +- .../test-agent-uninstall-ins-prov-2.yaml | 2 +- .../aws/dtt1-central_components-poc-aws.yaml | 69 +++++ .../dtt1-central_components-poc-vagrant.yaml | 60 ++++ 46 files changed, 2151 insertions(+), 335 deletions(-) create mode 100644 deployability/modules/testing/tests/helpers/central.py create mode 100644 deployability/modules/testing/tests/helpers/dashboard.py create mode 100644 deployability/modules/testing/tests/helpers/indexer.py create mode 100644 deployability/modules/testing/tests/test_central_components/__init__.py create mode 100644 deployability/modules/testing/tests/test_central_components/test_install.py create mode 100644 deployability/modules/testing/tests/test_central_components/test_restart.py create mode 100644 deployability/modules/testing/tests/test_central_components/test_stop.py create mode 100644 deployability/modules/testing/tests/test_central_components/test_uninstall.py create mode 100644 deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml create mode 100755 deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml create mode 100755 deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml create mode 100644 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml create mode 100644 deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml create mode 100644 deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml diff --git a/deployability/deps/requirements.txt b/deployability/deps/requirements.txt index 5213aad50d..b71cb57648 100755 --- a/deployability/deps/requirements.txt +++ b/deployability/deps/requirements.txt @@ -12,4 +12,4 @@ pytest==7.4.4 paramiko==3.4.0 requests==2.31.0 chardet==5.2.0 -pywinrm==0.3.0 +pywinrm==0.4.0 diff --git a/deployability/modules/generic/ansible.py b/deployability/modules/generic/ansible.py index 06ccb9072b..cbe78aa867 100755 --- a/deployability/modules/generic/ansible.py +++ b/deployability/modules/generic/ansible.py @@ -9,6 +9,7 @@ from pathlib import Path from pydantic import BaseModel, IPvAnyAddress +from typing import Optional from modules.generic.utils import Utils from modules.generic.logger import Logger diff --git a/deployability/modules/testing/main.py b/deployability/modules/testing/main.py index 3cf78f564f..fc71d3ef89 100755 --- a/deployability/modules/testing/main.py +++ b/deployability/modules/testing/main.py @@ -16,7 +16,7 @@ def parse_arguments(): parser = argparse.ArgumentParser(description="Wazuh testing tool") parser.add_argument("--targets", action='append', default=[], required=True) parser.add_argument("--tests", required=True) - parser.add_argument("--component", choices=['manager', 'agent'], required=True) + parser.add_argument("--component", choices=['manager', 'agent', 'central_components'], required=True) parser.add_argument("--dependencies", action='append', default=[], required=False) parser.add_argument("--cleanup", required=False, default=True) parser.add_argument("--wazuh-version", required=True) diff --git a/deployability/modules/testing/models.py b/deployability/modules/testing/models.py index f4e168efa8..179ae852aa 100644 --- a/deployability/modules/testing/models.py +++ b/deployability/modules/testing/models.py @@ -7,7 +7,7 @@ class ExtraVars(BaseModel): """Extra vars for testing module.""" - component: Literal['manager', 'agent'] + component: Literal['manager', 'agent', 'central_components'] wazuh_version: str wazuh_revision: str wazuh_branch: str | None = None diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 857174117d..ac3e940dc9 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -5,8 +5,8 @@ import yaml from typing import List, Optional -from .constants import WAZUH_CONF, WAZUH_ROOT -from .executor import Executor, WazuhAPI +from .constants import WAZUH_CONF, WAZUH_ROOT, WAZUH_WINDOWS_CONF, WAZUH_MACOS_CONF +from .executor import WazuhAPI, ConnectionManager from .generic import HostInformation, CheckFiles from modules.testing.utils import logger @@ -15,9 +15,9 @@ class WazuhAgent: @staticmethod def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, live) -> None: - if live == True: + if live: s3_url = 'packages' - release = wazuh_version[0:3] + release = wazuh_version[:1] + ".x" else: s3_url = 'packages-dev' release = 'pre-release' @@ -25,9 +25,9 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv os_type = HostInformation.get_os_type(inventory_path) architecture = HostInformation.get_architecture(inventory_path) commands = [] - if 'linux' in os_type: + + if os_type == 'linux': distribution = HostInformation.get_linux_distribution(inventory_path) - architecture = HostInformation.get_architecture(inventory_path) if distribution == 'rpm' and 'amd64' in architecture: commands.extend([ @@ -53,23 +53,25 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv ] commands.extend(system_commands) - elif 'windows' in os_type : + elif os_type == 'windows' : commands.extend([ - f"Invoke-WebRequest -Uri https://packages.wazuh.com/{release}/windows/wazuh-agent-{wazuh_version}-1.msi" - "-OutFile ${env.tmp}\wazuh-agent;" - "msiexec.exe /i ${env.tmp}\wazuh-agent /q" - f"WAZUH_MANAGER='MANAGER_IP'" - f"WAZUH_AGENT_NAME='{agent_name}'" - f"WAZUH_REGISTRATION_SERVER='MANAGER_IP'", - "NET START WazuhSvc", - "NET STATUS WazuhSvc" - ]) - elif 'macos' in os_type: - if 'amd64' in architecture: + f"Invoke-WebRequest -Uri https://packages.wazuh.com/{release}/windows/wazuh-agent-{wazuh_version}-1.msi " + "-OutFile $env:TEMP\wazuh-agent.msi" + ]) + commands.extend([ + "msiexec.exe /i $env:TEMP\wazuh-agent.msi /q " + f"WAZUH_MANAGER='MANAGER_IP' " + f"WAZUH_AGENT_NAME='{agent_name}' " + f"WAZUH_REGISTRATION_SERVER='MANAGER_IP' " + ]) + commands.extend(["NET START WazuhSvc"]) + + elif os_type == 'macos': + if architecture == 'amd64': commands.extend([ f'curl -so wazuh-agent.pkg https://{s3_url}.wazuh.com/{release}/macos/wazuh-agent-{wazuh_version}-1.intel64.pkg && echo "WAZUH_MANAGER=\'MANAGER_IP\' && WAZUH_AGENT_NAME=\'{agent_name}\'" > /tmp/wazuh_envs && sudo installer -pkg ./wazuh-agent.pkg -target /' ]) - elif 'arm64' in architecture: + elif architecture == 'arm64': commands.extend([ f'curl -so wazuh-agent.pkg https://{s3_url}.wazuh.com/{release}/macos/wazuh-agent-{wazuh_version}-1.arm64.pkg && echo "WAZUH_MANAGER=\'MANAGER_IP\' && WAZUH_AGENT_NAME=\'{agent_name}\'" > /tmp/wazuh_envs && sudo installer -pkg ./wazuh-agent.pkg -target /' ]) @@ -77,10 +79,10 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv '/Library/Ossec/bin/wazuh-control start', '/Library/Ossec/bin/wazuh-control status' ] - commands.extend(system_commands) + logger.info(f'Installing Agent in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_commands(inventory_path, commands) + ConnectionManager.execute_commands(inventory_path, commands) @staticmethod @@ -101,54 +103,91 @@ def register_agent(inventory_path, manager_path): agent_host = inventory_path_yaml.get('ansible_host') os_type = HostInformation.get_os_type(inventory_path) + logger.info(f'Registering agent in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + if os_type == 'linux': - host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) if 'amazonaws' in manager_host else manager_host - commands = [ - f"sed -i 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_CONF}", - "systemctl restart wazuh-agent" - ] - Executor.execute_commands(inventory_path, commands) - assert host_ip in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + try: + host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) if 'amazonaws' in manager_host else manager_host + commands = [ + f"sed -i 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_CONF}", + "systemctl restart wazuh-agent" + ] + ConnectionManager.execute_commands(inventory_path, commands) + except Exception as e: + raise Exception(f'Error registering agent. Error executing: {commands} with error: {e}') + + result = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}') + assert host_ip in result.get('output'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') elif os_type == 'macos': - if 'amazonaws' in manager_host and 'amazonaws' in agent_host: - host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) - else: - host_ip = HostInformation.get_public_ip_from_aws_dns(manager_host) - commands = [ - f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' /Library/Ossec/etc/ossec.conf", - "/Library/Ossec/bin/wazuh-control restart" - ] - Executor.execute_commands(inventory_path, commands) - assert host_ip in Executor.execute_command(inventory_path, f'cat /Library/Ossec/etc/ossec.conf'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') - + try: + if 'amazonaws' in manager_host and 'amazonaws' in agent_host: + host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) + else: + host_ip = HostInformation.get_public_ip_from_aws_dns(manager_host) + commands = [ + f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_MACOS_CONF}", + "/Library/Ossec/bin/wazuh-control restart" + ] + ConnectionManager.execute_commands(inventory_path, commands) + except Exception as e: + raise Exception(f'Error registering agent. Error executing: {commands} with error: {e}') + + result = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_MACOS_CONF}').get('output') + assert host_ip in result, logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + + elif os_type == 'windows': + try: + host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) if 'amazonaws' in manager_host else manager_host + commands = [ + f'(Get-Content -Path "{WAZUH_WINDOWS_CONF}" -Raw) -replace "
MANAGER_IP
", "
{host_ip}
" | Set-Content -Path "{WAZUH_WINDOWS_CONF}"', + "NET START WazuhSvc" + ] + + ConnectionManager.execute_commands(inventory_path, commands) + except Exception as e: + raise Exception(f'Error registering agent. Error executing: {commands} with error: {e}') + + result = ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WAZUH_WINDOWS_CONF}"') + assert host_ip in result.get('output'), logger.error(f'Error configuring the Manager IP ({host_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') @staticmethod def set_protocol_agent_connection(inventory_path, protocol): os_type = HostInformation.get_os_type(inventory_path) - if 'linux' in os_type: + if os_type == 'linux': commands = [ f"sed -i 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_CONF}", "systemctl restart wazuh-agent" ] - Executor.execute_commands(inventory_path, commands) - assert protocol in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') - elif 'macos' in os_type: + ConnectionManager.execute_commands(inventory_path, commands) + result = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}') + assert protocol in result.get('output'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + + elif os_type == 'macos': commands = [ - f"sed -i '' 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' /Library/Ossec/etc/ossec.conf", + f"sed -i '' 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_MACOS_CONF}", "/Library/Ossec/bin/wazuh-control restart" ] - Executor.execute_commands(inventory_path, commands) - assert protocol in Executor.execute_command(inventory_path, f'cat /Library/Ossec/etc/ossec.conf'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') - + ConnectionManager.execute_commands(inventory_path, commands) + assert protocol in ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_MACOS_CONF}').get('output'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') + + elif os_type == 'windows': + commands = [ + f"(Get-Content -Path '{WAZUH_WINDOWS_CONF}') -replace '[^<]*<\/protocol>', '{protocol}' | Set-Content -Path '{WAZUH_WINDOWS_CONF}'" + ] + ConnectionManager.execute_commands(inventory_path, commands) + result = ConnectionManager.execute_commands(inventory_path, f'Get-Content -Path "{WAZUH_WINDOWS_CONF}"') + assert protocol in result.get('output'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent') @staticmethod def uninstall_agent(inventory_path, wazuh_version=None, wazuh_revision=None) -> None: os_type = HostInformation.get_os_type(inventory_path) commands = [] - if 'linux' in os_type: + + if os_type == 'linux': distribution = HostInformation.get_linux_distribution(inventory_path) os_name = HostInformation.get_os_name_from_inventory(inventory_path) + if os_name == 'opensuse' or os_name == 'suse': commands.extend([ "zypper remove --no-confirm wazuh-agent", @@ -158,26 +197,24 @@ def uninstall_agent(inventory_path, wazuh_version=None, wazuh_revision=None) -> if distribution == 'deb': commands.extend([ "apt-get remove --purge wazuh-agent -y" - ]) elif distribution == 'rpm': commands.extend([ "yum remove wazuh-agent -y", f"rm -rf {WAZUH_ROOT}" ]) - - system_commands = [ "systemctl disable wazuh-agent", "systemctl daemon-reload" ] - commands.extend(system_commands) - elif 'windows' in os_type: + + elif os_type == 'windows': commands.extend([ - f"msiexec.exe /x wazuh-agent-{wazuh_version}-1.msi /qn" + f"msiexec.exe /x $env:TEMP\wazuh-agent.msi /qn" ]) - elif 'macos' in os_type: + + elif os_type == 'macos': commands.extend([ "/Library/Ossec/bin/wazuh-control stop", "/bin/rm -r /Library/Ossec", @@ -190,7 +227,7 @@ def uninstall_agent(inventory_path, wazuh_version=None, wazuh_revision=None) -> ]) logger.info(f'Uninstalling Agent in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_commands(inventory_path, commands) + ConnectionManager.execute_commands(inventory_path, commands) @staticmethod @@ -213,7 +250,7 @@ def _uninstall_agent_callback(wazuh_params, agent_params): def perform_action_and_scan(agent_params, action_callback) -> dict: """ Takes scans using filters, the callback action and compares the result - + Args: agent_params (str): agent parameters callbak (cb): callback (action) @@ -226,6 +263,7 @@ def perform_action_and_scan(agent_params, action_callback) -> dict: os_name = HostInformation.get_os_name_from_inventory(agent_params) os_type = HostInformation.get_os_type(agent_params) logger.info(f'Applying filters in checkfiles in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') + os_type = HostInformation.get_os_type(agent_params) if os_type == 'linux': if 'debian' in os_name: @@ -263,11 +301,20 @@ def perform_action_and_scan(agent_params, action_callback) -> dict: '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': []}, '/usr/sbin': {'added': [], 'removed': [], 'modified': []} } + elif os_type == 'macos': - filter_data = { - '/usr/bin': {'added': [], 'removed': [], 'modified': []}, - '/usr/sbin': {'added': [], 'removed': [], 'modified': []} - } + filter_data = { + '/usr/bin': {'added': [], 'removed': [], 'modified': []}, + '/usr/sbin': {'added': [], 'removed': [], 'modified': []} + } + + elif os_type == 'windows': + filter_data = { + 'C:\\Program Files': {'added': [], 'removed': [], 'modified': []}, + 'C:\\Program Files (x86)': {'added': [], 'removed': [], 'modified': []}, + 'C:\\Users\\vagrant' : {'added': [], 'removed': [], 'modified': []} + } + # Use of filters for directory, changes in result.items(): @@ -282,7 +329,7 @@ def perform_action_and_scan(agent_params, action_callback) -> dict: def perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) -> None: """ Coordinates the action of install the agent and compares the checkfiles - + Args: agent_params (str): agent parameters wazuh_params (str): wazuh parameters @@ -298,7 +345,7 @@ def perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) - def perform_uninstall_and_scan_for_agent(agent_params, wazuh_params) -> None: """ Coordinates the action of uninstall the agent and compares the checkfiles - + Args: agent_params (str): agent parameters wazuh_params (str): wazuh parameters @@ -311,25 +358,33 @@ def perform_uninstall_and_scan_for_agent(agent_params, wazuh_params) -> None: @staticmethod - def assert_results(result, agent_params) -> None: + def assert_results(result, params = None) -> None: + """ Gets the status of an agent given its name. - + Args: result (dict): result of comparison between pre and post action scan """ - os_type = HostInformation.get_os_type(agent_params) + os_type = HostInformation.get_os_type(params) + if os_type == 'linux': categories = ['/root', '/usr/bin', '/usr/sbin', '/boot'] + + elif os_type == 'windows': + categories = ['C:\\Program Files', 'C:\\Program Files (x86)','C:\\Users\\vagrant'] + elif os_type == 'macos': categories = ['/usr/bin', '/usr/sbin'] + actions = ['added', 'modified', 'removed'] # Testing the results for category in categories: for action in actions: - assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category}{action}') + + assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category} {action}') def areAgent_processes_active(agent_params): """ @@ -341,9 +396,31 @@ def areAgent_processes_active(agent_params): Returns: str: Os name. """ - return bool([int(numero) for numero in Executor.execute_command(agent_params, 'pgrep wazuh').splitlines()]) + os_type = HostInformation.get_os_type(agent_params) - def isAgent_port_open(inventory_path): + if os_type == 'linux': + result = ConnectionManager.execute_commands(agent_params, 'pgrep wazuh') + if result.get('success'): + return bool([int(number) for number in result.get('output').splitlines()]) + else: + return False + + if os_type == 'macos': + result = ConnectionManager.execute_commands(agent_params, 'pgrep wazuh') + if result.get('success'): + return bool([int(number) for number in result.get('output').splitlines()]) + else: + return False + + elif os_type == 'windows': + result = ConnectionManager.execute_commands(agent_params, 'Get-Process -Name "wazuh-agent" | Format-Table -HideTableHeaders ProcessName') + if result.get('success'): + return 'wazuh-agent' in result.get('output') + else: + return False + + + def is_agent_port_open(inventory_path): """ Check if agent port is open @@ -355,9 +432,25 @@ def isAgent_port_open(inventory_path): """ os_type = HostInformation.get_os_type(inventory_path) if os_type == 'linux': - return 'ESTAB' in Executor.execute_command(inventory_path, 'ss -t -a -n | grep ":1514" | grep ESTAB') + result = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":1514" | grep ESTAB') + if result.get('success'): + return 'ESTAB' in result.get('output') + else: + return False + + elif os_type == 'windows': + result = ConnectionManager.execute_commands(inventory_path, 'netstat -ano | Select-String -Pattern "TCP" | Select-String -Pattern "ESTABLISHED" | Select-String -Pattern ":1514"') + if result.get('success'): + return 'ESTABLISHED' in result.get('output') + else: + return False + elif os_type == 'macos': - return 'ESTABLISHED' in Executor.execute_command(inventory_path, 'netstat -an | grep ".1514 " | grep ESTABLISHED') + result = ConnectionManager.execute_commands(inventory_path, 'netstat -an | grep ".1514 " | grep ESTABLISHED') + if result.get('success'): + return 'ESTABLISHED' in result.get('output') + else: + return False def get_agents_information(wazuh_api: WazuhAPI) -> list: """ @@ -378,17 +471,20 @@ def get_agents_information(wazuh_api: WazuhAPI) -> list: def get_agent_status(wazuh_api: WazuhAPI, agent_name) -> str: """ Function to get the status of an agent given its name. - + Args: - agents_data (list): List of dictionaries containing agents' data. - agent_name (str): Name of the agent whose status is to be obtained. - + Returns: - str: Status of the agent if found in the data, otherwise returns None. """ response = requests.get(f"{wazuh_api.api_url}/agents", headers=wazuh_api.headers, verify=False) + for agent in eval(response.text)['data']['affected_items']: if agent.get('name') == agent_name: + + return agent.get('status') return None diff --git a/deployability/modules/testing/tests/helpers/central.py b/deployability/modules/testing/tests/helpers/central.py new file mode 100644 index 0000000000..8aeea1e969 --- /dev/null +++ b/deployability/modules/testing/tests/helpers/central.py @@ -0,0 +1,177 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import requests +import socket + +from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT +from .executor import ConnectionManager, WazuhAPI +from .generic import HostInformation, CheckFiles +from modules.testing.utils import logger +from .utils import Utils + + +class WazuhCentralComponents: + + @staticmethod + def install_aio(inventory_path, wazuh_version) -> None: + """ + Installs Wazuh central components (AIO) in the host + + Args: + inventory_path (str): host's inventory path + wazuh_version (str): major.minor.patch + + """ + wazuh_version = '.'.join(wazuh_version.split('.')[:2]) + os_name = HostInformation.get_os_name_from_inventory(inventory_path) + + if HostInformation.has_curl(inventory_path): + commands = [ + f"curl -sO https://packages.wazuh.com/{wazuh_version}/wazuh-install.sh && sudo bash ./wazuh-install.sh -a --ignore-check" + ] + else: + commands = [ + f"wget https://packages.wazuh.com/{wazuh_version}/wazuh-install.sh && sudo bash ./wazuh-install.sh -a --ignore-check" + ] + + + logger.info(f'Installing Wazuh central components (AIO) in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + ConnectionManager.execute_commands(inventory_path, commands) + + + @staticmethod + def uninstall_aio(inventory_path) -> None: + """ + Uninstall Wazuh Central Components (AIO) in the host + + Args: + inventory_paths (str): hosts' inventory path + """ + + commands = ['bash wazuh-install.sh --uninstall --ignore-check'] + + logger.info(f'Uninstalling Wazuh central components (AIO) in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + ConnectionManager.execute_commands(inventory_path, commands) + + + @staticmethod + def _install_aio_callback(wazuh_params, host_params): + WazuhCentralComponents.install_aio(host_params, wazuh_params['wazuh_version']) + + + @staticmethod + def _uninstall_aio_callback(host_params): + WazuhCentralComponents.uninstall_aio(host_params) + + + @staticmethod + def perform_action_and_scan(host_params, action_callback) -> dict: + """ + Takes scans using filters, the callback action and compares the result + + Args: + host_params (str): host parameters + callback (cb): callback (action) + + Returns: + result (dict): comparison brief + + """ + result = CheckFiles.perform_action_and_scan(host_params, action_callback) + os_name = HostInformation.get_os_name_from_inventory(host_params) + logger.info(f'Applying filters in checkfiles in {HostInformation.get_os_name_and_version_from_inventory(host_params)}') + + if 'debian' in os_name: + filter_data = { + '/boot': {'added': [], 'removed': [], 'modified': ['grubenv']}, + '/usr/bin': { + 'added': [ + 'unattended-upgrade', 'gapplication', 'add-apt-repository', 'gpg-wks-server', 'pkexec', 'gpgsplit', + 'watchgnupg', 'pinentry-curses', 'gpg-zip', 'gsettings', 'gpg-agent', 'gresource', 'gdbus', + 'gpg-connect-agent', 'gpgconf', 'gpgparsemail', 'lspgpot', 'pkaction', 'pkttyagent', 'pkmon', + 'dirmngr', 'kbxutil', 'migrate-pubring-from-classic-gpg', 'gpgcompose', 'pkcheck', 'gpgsm', 'gio', + 'pkcon', 'gpgtar', 'dirmngr-client', 'gpg', 'filebeat', 'gawk', 'curl', 'update-mime-database', + 'dh_installxmlcatalogs', 'appstreamcli', 'lspgpot', 'symcryptrun' + ], + 'removed': ['filebeat'], + 'modified': [] + }, + '/root': {'added': ['trustdb.gpg', 'lesshst', 'ssh'], 'removed': ['filebeat'], 'modified': []}, + '/usr/sbin': { + 'added': [ + 'update-catalog', 'applygnupgdefaults', 'addgnupghome', 'install-sgmlcatalog', 'update-xmlcatalog' + ], + 'removed': [], + 'modified': [] + } + } + else: + filter_data = { + '/boot': { + 'added': ['grub2', 'loader', 'vmlinuz', 'System.map', 'config-', 'initramfs'], + 'removed': [], + 'modified': ['grubenv'] + }, + '/usr/bin': {'added': ['filebeat'], 'removed': ['filebeat'], 'modified': []}, + '/root': {'added': ['trustdb.gpg', 'lesshst'], 'removed': [], 'modified': ['.rnd']}, + '/usr/sbin': {'added': [], 'removed': [], 'modified': []} + } + + # Use of filters + for directory, changes in result.items(): + if directory in filter_data: + for change, files in changes.items(): + if change in filter_data[directory]: + result[directory][change] = [file for file in files if file.split('/')[-1] not in filter_data[directory][change]] + + return result + + @staticmethod + def perform_install_and_scan_for_aio(host_params, wazuh_params) -> None: + """ + Coordinates the action of install the Wazuh central components (AIO) and compares the checkfiles + + Args: + host_params (str): host parameters + wazuh_params (str): wazuh parameters + + """ + action_callback = lambda: WazuhCentralComponents._install_aio_callback(wazuh_params, host_params) + result = WazuhCentralComponents.perform_action_and_scan(host_params, action_callback) + logger.info(f'Pre and post install checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(host_params)}: {result}') + WazuhCentralComponents.assert_results(result) + + + @staticmethod + def perform_uninstall_and_scan_for_aio(host_params) -> None: + """ + Coordinates the action of uninstall the Wazuh central components (AIO) and compares the checkfiles + + Args: + host_params (str): host parameters + wazuh_params (str): wazuh parameters + + """ + action_callback = lambda: WazuhCentralComponents._uninstall_aio_callback(host_params) + result = WazuhCentralComponents.perform_action_and_scan(host_params, action_callback) + logger.info(f'Pre and post uninstall checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(host_params)}: {result}') + WazuhCentralComponents.assert_results(result) + + + @staticmethod + def assert_results(result) -> None: + """ + Gets the status of an agent given its name. + + Args: + result (dict): result of comparison between pre and post action scan + + """ + categories = ['/root', '/usr/bin', '/usr/sbin', '/boot'] + actions = ['added', 'modified', 'removed'] + # Testing the results + for category in categories: + for action in actions: + assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category} {action}') diff --git a/deployability/modules/testing/tests/helpers/constants.py b/deployability/modules/testing/tests/helpers/constants.py index ea0c6a8098..93657c2ee8 100755 --- a/deployability/modules/testing/tests/helpers/constants.py +++ b/deployability/modules/testing/tests/helpers/constants.py @@ -10,16 +10,42 @@ CONFIGURATIONS_DIR = Path(WAZUH_ROOT, "etc") WAZUH_CONF = Path(CONFIGURATIONS_DIR, "ossec.conf") CLIENT_KEYS = Path(CONFIGURATIONS_DIR, "client.keys") + +WINDOWS_ROOT_DIR = Path("C:", "Program Files (x86)", "ossec-agent") +WINDOWS_CONFIGURATIONS_DIR = Path(WINDOWS_ROOT_DIR, "etc") +WAZUH_WINDOWS_CONF = Path(WINDOWS_ROOT_DIR, "ossec.conf") +WINDOWS_CLIENT_KEYS = Path(WINDOWS_ROOT_DIR, "client.keys") +WINDOWS_VERSION = Path(WINDOWS_ROOT_DIR, "VERSION") +WINDOWS_REVISION = Path(WINDOWS_ROOT_DIR, "REVISION") + +MACOS_ROOT_DIR = Path("/Library", "Ossec") +MACOS_CONFIGURATIONS_DIR = Path(MACOS_ROOT_DIR, "etc") +WAZUH_MACOS_CONF = Path(MACOS_CONFIGURATIONS_DIR, "ossec.conf") +MACOS_CLIENT_KEYS = Path(MACOS_CONFIGURATIONS_DIR, "client.keys") +MACOS_VERSION = Path(MACOS_ROOT_DIR, "VERSION") +MACOS_REVISION = Path(MACOS_ROOT_DIR, "REVISION") + + # Binaries paths BINARIES_DIR = Path(WAZUH_ROOT, "bin") WAZUH_CONTROL = Path(BINARIES_DIR, "wazuh-control") AGENT_CONTROL = Path(BINARIES_DIR, "agent_control") CLUSTER_CONTROL = Path(BINARIES_DIR, "cluster_control") + +MACOS_BINARIES_DIR = Path(MACOS_ROOT_DIR, "bin") +MACOS_WAZUH_CONTROL = Path(MACOS_BINARIES_DIR, "wazuh-control") + # Logs paths LOGS_DIR = Path(WAZUH_ROOT, "logs") WAZUH_LOG = Path(LOGS_DIR, "ossec.log") ALERTS_DIR = Path(LOGS_DIR, "alerts") ALERTS_JSON = Path(ALERTS_DIR, "alerts.json") + +MACOS_LOGS_DIR = Path(MACOS_ROOT_DIR, "logs") +WAZUH_MACOS_LOG = Path(MACOS_LOGS_DIR, "ossec.log") +MACOS_ALERTS_DIR = Path(MACOS_LOGS_DIR, "alerts") +MACOS_ALERTS_JSON = Path(MACOS_ALERTS_DIR, "alerts.json") + # Daemons running paths DAEMONS_DIR = Path(WAZUH_ROOT, "var", "run") AGENTD_STATE = Path(DAEMONS_DIR, "wazuh-agentd.state") diff --git a/deployability/modules/testing/tests/helpers/dashboard.py b/deployability/modules/testing/tests/helpers/dashboard.py new file mode 100644 index 0000000000..a02be6e577 --- /dev/null +++ b/deployability/modules/testing/tests/helpers/dashboard.py @@ -0,0 +1,109 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import requests +import socket +import json +import time + +from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT +from .executor import ConnectionManager, WazuhAPI +from .generic import HostInformation, CheckFiles +from modules.testing.utils import logger +from .utils import Utils + + +class WazuhDashboard: + + @staticmethod + def get_dashboard_version(inventory_path) -> str: + """ + Returns Wazuh dashboard version + + Args: + inventory_path (str): host's inventory path + + Returns: + - str: Version of the Wazuh dashboard. + """ + + return ConnectionManager.execute_commands(inventory_path,'cat /usr/share/wazuh-dashboard/VERSION').get('output').strip() + + + @staticmethod + def is_dashboard_active(inventory_path) -> bool: + """ + Returns True/False depending if the dashboard service is active or not + + Args: + inventory_path (str): host's inventory path + + Returns: + - bool: Status of the Wazuh dashboard service. + """ + + return '200' in ConnectionManager.execute_commands(inventory_path, 'curl -Is -k https://localhost/app/login?nextUrl=%2F | head -n 1').get('output') + + + @staticmethod + def is_dashboard_keystore_working(inventory_path) -> bool: + """ + Returns True/False depending if the Wazuh dashboard keystore is active or not + + Args: + inventory_path (str): host's inventory path + + Returns: + - bool: Status of the Wazuh dashboard keystore. + """ + + return 'No such file or directory' not in ConnectionManager.execute_commands(inventory_path, '/usr/share/wazuh-dashboard/bin/opensearch-dashboards-keystore list --allow-root').get('output') + + + @staticmethod + def are_dashboard_nodes_working(wazuh_api: WazuhAPI) -> str: + """ + Returns True/False depending the status of Wazuh dashboard nodes + + Returns: + - bool: True/False depending on the status. + """ + response = requests.get(f"{wazuh_api.api_url}/api/status", auth=(wazuh_api.username, wazuh_api.password), verify=False) + + result = True + if response.status_code == 200: + for status in json.loads((response.text))['status']['statuses']: + if status['state'] == 'green' or status['state'] == 'yellow': + result = True + else: + result = False + return result + + else: + logger.error(f'The Wazuh dashboard API returned: {response.status_code}') + + @staticmethod + def is_dashboard_port_open(inventory_path, wait=10, cycles=50): + """ + Check if the Wazuh dashboard port is open + + Args: + inventory_path (str): Wazuh dashboard inventory. + + Returns: + str: OS name. + """ + wait_cycles = 0 + while wait_cycles < cycles: + ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":443"').get('output').strip().split('\n') + for port in ports: + if any(state in port for state in ['ESTAB', 'LISTEN']): + continue + else: + time.sleep(wait) + wait_cycles += 1 + break + else: + return True + return False diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index f77c924c05..2c188c648f 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -5,89 +5,161 @@ import json import paramiko import requests +import paramiko import subprocess import urllib3 import yaml +import winrm from base64 import b64encode - -class Executor: +class ConectionInventory(): + host: str + port: int + password: str | None = None + username: str + private_key_path: str | None = None @staticmethod - def execute_command(inventory_path, command) -> str: - + def _get_inventory_data(inventory_path) -> dict: with open(inventory_path, 'r') as yaml_file: inventory_data = yaml.safe_load(yaml_file) + return { + 'host': inventory_data.get('ansible_host'), + 'port': inventory_data.get('ansible_port'), + 'password': inventory_data.get('ansible_password', None), + 'username': inventory_data.get('ansible_user'), + 'private_key_path': inventory_data.get('ansible_ssh_private_key_file', None) + } + +class ConnectionManager: + @staticmethod + def _get_executor(inventory_path) -> type: + from .generic import HostInformation + + os_type = HostInformation.get_os_type(inventory_path) + if os_type == "windows": + return WindowsExecutor + elif os_type == "linux": + return UnixExecutor + elif os_type == "macos": + return MacosExecutor - if 'ansible_ssh_private_key_file' in inventory_data: - host = inventory_data.get('ansible_host') - port = inventory_data.get('ansible_port') - private_key_path = inventory_data.get('ansible_ssh_private_key_file') - username = inventory_data.get('ansible_user') - - ssh_command = [ - "ssh", - "-i", private_key_path, - "-o", "StrictHostKeyChecking=no", - "-o", "UserKnownHostsFile=/dev/null", - "-p", str(port), - f"{username}@{host}", - "sudo", - command - ] - result = subprocess.run(ssh_command, stdout=subprocess.PIPE, text=True) - - return result.stdout + @staticmethod + def execute_commands(inventory_path, commands) -> dict: + executor = ConnectionManager._get_executor(inventory_path) + if isinstance(commands, str): + try: + result = executor._execute_command(ConectionInventory._get_inventory_data(inventory_path), commands) + except Exception as e: + raise Exception(f'Error executing command: {commands} with error: {e}') + return result else: - host = inventory_data.get('ansible_host', None) - port = inventory_data.get('ansible_port', 22) - user = inventory_data.get('ansible_user', None) - password = inventory_data.get('ansible_password', None) + results = {} + for command in commands: + result = executor._execute_command(ConectionInventory._get_inventory_data(inventory_path), command) + results[command] = result + return results + +class WindowsExecutor(): + @staticmethod + def _execute_command(data: ConectionInventory, command) -> dict: + if data.get('port') == 5986: + protocol = 'https' + else: + protocol = 'http' + + endpoint_url = f"{protocol}://{data.get('host')}:{data.get('port')}" + + try: + session = winrm.Session(endpoint_url, auth=(data.get('username'), data.get('password')),transport='ntlm', server_cert_validation='ignore') + ret = session.run_ps(command) + if ret.status_code == 0: + return {'success': True, 'output': ret.std_out.decode('utf-8').strip()} + else: + return {'success': False, 'output': ret.std_err.decode('utf-8').strip()} + except Exception as e: + raise Exception(f'Error executing command: {command} with error: {e}') + +class UnixExecutor(): + @staticmethod + def _execute_command(data, command) -> dict: + + ssh_command = [ + "ssh", + "-i", data.get('private_key_path'), + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-p", str(data.get('port')), + f"{data.get('username')}@{data.get('host')}", + "sudo", + command + ] + + try: + ret = subprocess.run(ssh_command, stdout=subprocess.PIPE, text=True) + if ret.stdout: + return {'success': True, 'output': ret.stdout.replace('\n', '')} + if ret.stderr: + return {'success': False, 'output': ret.stderr.replace('\n', '')} + return {'success': False, 'output': None} + + except Exception as e: + #return {'success': False, 'output': ret.stderr} + raise Exception(f'Error executing command: {command} with error: {e}') + + +class MacosExecutor(): + @staticmethod + def _execute_command(data, command) -> dict: + try: ssh_client = paramiko.SSHClient() ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh_client.connect(hostname=host, port=port, username=user, password=password) + ssh_client.connect(hostname=data.get('host'), port=data.get('port'), username=data.get('username'), password=data.get('password')) stdin, stdout, stderr = ssh_client.exec_command(f"sudo {command}") - - result = ''.join(stdout.readlines()) - ssh_client.close() - - return result + stdout_str = ''.join(stdout.readlines()) + stderr_str = ''.join(stderr.readlines()) + ssh_client.close() - @staticmethod - def execute_commands(inventory_path, commands=[]) -> dict: + if stdout_str: + return {'success': True, 'output': stdout_str.replace('\n', '')} + if stderr_str: + return {'success': False, 'output': stderr_str.replace('\n', '')} + return {'success': False, 'output': None} - results = {} - for command in commands: - results[command] = Executor.execute_command(inventory_path, command) + except Exception as e: + raise Exception(f'Error executing command: {command} with error: {e}') - return results +# ------------------------------------------------------ class WazuhAPI: - def __init__(self, inventory_path): + def __init__(self, inventory_path, component=None): self.inventory_path = inventory_path self.api_url = None self.headers = None + self.component = component + self.username = None + self.password = None urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) self._authenticate() + def _extract_password(self, file_path, keyword): + if not 'true' in ConnectionManager.execute_commands(self.inventory_path, f'test -f {file_path} && echo "true" || echo "false"').get('output'): + ConnectionManager.execute_commands(self.inventory_path, 'tar -xvf wazuh-install-files.tar') + return ConnectionManager.execute_command(self.inventory_path, f"grep {keyword} {file_path} | head -n 1 | awk '{{print $NF}}'").get('output').replace("'", "").replace("\n", "") + def _authenticate(self): with open(self.inventory_path, 'r') as yaml_file: inventory_data = yaml.safe_load(yaml_file) user = 'wazuh' - - #----Patch issue https://github.com/wazuh/wazuh-packages/issues/2883------------- - file_path = Executor.execute_command(self.inventory_path, 'pwd').replace("\n","") + '/wazuh-install-files/wazuh-passwords.txt' - if not 'true' in Executor.execute_command(self.inventory_path, f'test -f {file_path} && echo "true" || echo "false"'): - Executor.execute_command(self.inventory_path, 'tar -xvf wazuh-install-files.tar') - password = Executor.execute_command(self.inventory_path, "grep api_password wazuh-install-files/wazuh-passwords.txt | head -n 1 | awk '{print $NF}'").replace("'","").replace("\n","") - #-------------------------------------------------------------------------------- - + file_path = ConnectionManager.execute_commands(self.inventory_path, 'pwd').get('output').replace("\n", "") + '/wazuh-install-files/wazuh-passwords.txt' + password = self._extract_password(file_path, 'api_password') + login_endpoint = 'security/user/authenticate' host = inventory_data.get('ansible_host') port = '55000' @@ -98,8 +170,15 @@ def _authenticate(self): token = json.loads(requests.post(login_url, headers=login_headers, verify=False).content.decode())['data']['token'] - self.api_url = f'https://{host}:{port}' self.headers = { 'Content-Type': 'application/json', 'Authorization': f'Bearer {token}' } + + self.api_url = f'https://{host}:{port}' + + if self.component == 'dashboard' or self.component == 'indexer': + self.username = 'admin' + password = self._extract_password(file_path, 'indexer_password') + self.password = password + self.api_url = f'https://{host}' if self.component == 'dashboard' else f'https://127.0.0.1:9200' diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index eaeee50211..929c3bcdd2 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -12,8 +12,8 @@ import yaml from pathlib import Path -from .constants import WAZUH_CONTROL, CLIENT_KEYS -from .executor import Executor +from .constants import WAZUH_CONTROL, CLIENT_KEYS, WINDOWS_CLIENT_KEYS, WINDOWS_VERSION, WINDOWS_REVISION, MACOS_WAZUH_CONTROL, MACOS_CLIENT_KEYS +from .executor import ConnectionManager from modules.testing.utils import logger from .utils import Utils @@ -33,11 +33,17 @@ def dir_exists(inventory_path, dir_path) -> str: bool: True or False """ os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': - return 'true' in Executor.execute_command(inventory_path, f'test -d {dir_path} && echo "true" || echo "false"') - elif os_type == 'macos': - return 'true' in Executor.execute_command(inventory_path, f'stat {dir_path} >/dev/null 2>&1 && echo "true" || echo "false"') + return 'True' in ConnectionManager.execute_commands(inventory_path, f'test -d {dir_path} && echo "True" || echo "False"').get('output') + elif os_type == 'windows': + result = ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{dir_path}"') + if result.get('success'): + return 'True' in result.get('output') + + elif os_type == 'macos': + return 'true' in ConnectionManager.execute_commands(inventory_path, f'stat {dir_path} >/dev/null 2>&1 && echo "true" || echo "false"').get('output') @staticmethod def file_exists(inventory_path, file_path) -> bool: @@ -53,10 +59,11 @@ def file_exists(inventory_path, file_path) -> bool: """ os_type = HostInformation.get_os_type(inventory_path) if os_type == 'linux': - return 'true' in Executor.execute_command(inventory_path, f'test -f {file_path} && echo "true" || echo "false"') + return ConnectionManager.execute_commands(inventory_path, f'test -f {file_path} && echo "True" || echo "False"').get('output') + elif os_type == 'windows': + return ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{file_path}"').get('output') elif os_type == 'macos': - return 'true' in Executor.execute_command(inventory_path, f'stat {file_path} >/dev/null 2>&1 && echo "true" || echo "false"') - + return 'true' in ConnectionManager.execute_commands(inventory_path, f'stat {file_path} >/dev/null 2>&1 && echo "true" || echo "false"').get('output') @staticmethod def get_os_type(inventory_path) -> str: @@ -93,7 +100,7 @@ def get_architecture(inventory_path) -> str: inventory_path: host's inventory path Returns: - str: architecture (amd64, arm64, intel, apple) + str: architecture (amd64, arm64) """ try: with open(inventory_path.replace('inventory', 'track'), 'r') as file: @@ -122,9 +129,11 @@ def get_linux_distribution(inventory_path) -> str: str: linux distribution (deb, rpm) """ if 'manager' in inventory_path: - os_name = re.search(r'/manager-linux-([^-]+)-', inventory_path).group(1) + os_name = re.search(r'/manager-[^-]+-([^-]+)-', inventory_path).group(1) elif 'agent' in inventory_path: - os_name = re.search(r'/agent-linux-([^-]+)-', inventory_path).group(1) + os_name = re.search(r'/agent-[^-]+-([^-]+)-', inventory_path).group(1) + elif 'central_components' in inventory_path: + os_name = re.search(r'/central_components-[^-]+-([^-]+)-', inventory_path).group(1) if os_name == 'ubuntu' or os_name == 'debian': linux_distribution = 'deb' @@ -149,6 +158,8 @@ def get_os_name_from_inventory(inventory_path) -> str: os_name = re.search(r'/manager-[^-]+-([^-]+)-', inventory_path).group(1) elif 'agent' in inventory_path: os_name = re.search(r'/agent-[^-]+-([^-]+)-', inventory_path).group(1) + elif 'central_components' in inventory_path: + os_name = re.search(r'/central_components-[^-]+-([^-]+)-', inventory_path).group(1) return os_name @@ -167,6 +178,8 @@ def get_os_name_and_version_from_inventory(inventory_path) -> tuple: match = re.search(r'/manager-[^-]+-([^-]+)-([^-]+)-', inventory_path) elif 'agent' in inventory_path: match = re.search(r'/agent-[^-]+-([^-]+)-([^-]+)-', inventory_path) + elif 'central_components' in inventory_path: + match = re.search(r'/central_components-[^-]+-([^-]+)-([^-]+)-', inventory_path) if match: os_name = match.group(1) version = match.group(2) @@ -176,14 +189,28 @@ def get_os_name_and_version_from_inventory(inventory_path) -> tuple: @staticmethod def get_os_version_from_inventory(inventory_path) -> str: + """ + It returns the os version from the inventory information + + Args: + inventory_path: host's inventory path + + Returns: + str: os version + """ + os_type = HostInformation.get_os_type(inventory_path) + if 'manager' in inventory_path: os_version = re.search(r".*?/manager-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) elif 'agent' in inventory_path: os_version = re.search(r".*?/agent-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) + elif 'central_components' in inventory_path: + os_version = re.search(r".*?/central_components-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) return os_version else: return None + @staticmethod def get_current_dir(inventory_path) -> str: """ @@ -195,8 +222,16 @@ def get_current_dir(inventory_path) -> str: Returns: str: current directory """ + os_type = HostInformation.get_os_type(inventory_path) - return Executor.execute_command(inventory_path, 'pwd').replace("\n","") + if os_type == 'linux': + result = ConnectionManager.execute_commands(inventory_path, 'pwd') + return result.get('output').replace("\n","") + elif os_type == 'windows': + return ConnectionManager.execute_commands(inventory_path, '(Get-Location).Path').get('output') + elif os_type == 'macos': + result = ConnectionManager.execute_commands(inventory_path, 'pwd').get('output') + return result.replace("\n","") @staticmethod def get_internal_ip_from_aws_dns(dns_name) -> str: @@ -224,7 +259,7 @@ def get_public_ip_from_aws_dns(dns_name) -> str: Args: dns_name (str): host's dns public dns name - + Returns: str: public ip """ @@ -247,23 +282,41 @@ def get_client_keys(inventory_path) -> list[dict]: list: List of dictionaries with the client keys. """ clients = [] + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': - client_key = Executor.execute_command(inventory_path, f'cat {CLIENT_KEYS}') + client_key = ConnectionManager.execute_commands(inventory_path, f'cat {CLIENT_KEYS}').get('output') + elif os_type == 'windows': + client_key = ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WINDOWS_CLIENT_KEYS}"').get('output') elif os_type == 'macos': - client_key = Executor.execute_command(inventory_path, f'cat /Library/Ossec/etc/client.keys') - lines = client_key.split('\n')[:-1] - for line in lines: - _id, name, address, password = line.strip().split() - client_info = { - "id": _id, - "name": name, - "address": address, - "password": password - } - clients.append(client_info) - return clients + client_key = ConnectionManager.execute_commands(inventory_path, f'cat {MACOS_CLIENT_KEYS}').get('output') + + if client_key != None: + lines = client_key.split('\n')[:-1] + for line in lines: + _id, name, address, password = line.strip().split() + client_info = { + "id": _id, + "name": name, + "address": address, + "password": password + } + clients.append(client_info) + return clients + else: + return [] + @staticmethod + def has_curl(inventory_path) -> bool: + """ + Returns yes in case that curl is installed in Linux/macOS. + Args: + inventory_path (str): host's inventory path + Returns: + bool: True/False. + """ + return 'curl' in ConnectionManager.execute_commands(inventory_path, 'which curl').get('output') class HostConfiguration: @@ -278,7 +331,7 @@ def sshd_config(inventory_path) -> None: """ commands = ["sudo sed -i '/^PasswordAuthentication/s/^/#/' /etc/ssh/sshd_config", "sudo sed -i '/^PermitRootLogin no/s/^/#/' /etc/ssh/sshd_config", 'echo -e "PasswordAuthentication yes\nPermitRootLogin yes" | sudo tee -a /etc/ssh/sshd_config', 'sudo systemctl restart sshd', 'cat /etc/ssh/sshd_config'] - Executor.execute_commands(inventory_path, commands) + ConnectionManager.execute_commands(inventory_path, commands) @staticmethod @@ -290,21 +343,28 @@ def disable_firewall(inventory_path) -> None: inventory_path: host's inventory path """ + commands = [] + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': commands = ["sudo systemctl stop firewalld", "sudo systemctl disable firewalld"] - if GeneralComponentActions.isComponentActive(inventory_path, 'firewalld'): - Executor.execute_commands(inventory_path, commands) + if GeneralComponentActions.is_component_active(inventory_path, 'firewalld'): + ConnectionManager.execute_commands(inventory_path, commands) logger.info(f'Firewall disabled on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') else: logger.info(f'No Firewall to disable on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + elif os_type == 'windows': + logger.info(f'Firewall disabled on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + commands = ["Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False"] + ConnectionManager.execute_commands(inventory_path, commands) elif os_type == 'macos': logger.info(f'Firewall disabled on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_command(inventory_path, 'sudo pfctl -d') - + ConnectionManager.execute_commands(inventory_path, 'sudo pfctl -d') def _extract_hosts(paths, is_aws): + from .utils import Utils if is_aws: return [HostInformation.get_internal_ip_from_aws_dns(Utils.extract_ansible_host(path)) for path in paths] else: @@ -323,6 +383,8 @@ def certs_create(wazuh_version, master_path, dashboard_path, indexer_paths=[], w workers_paths (list): wazuh worker paths list """ + from .utils import Utils + current_directory = HostInformation.get_current_dir(master_path) wazuh_version = '.'.join(wazuh_version.split('.')[:2]) @@ -383,7 +445,7 @@ def certs_create(wazuh_version, master_path, dashboard_path, indexer_paths=[], w commands.extend(certs_creation) - Executor.execute_commands(master_path, commands) + ConnectionManager.execute_commands(master_path, commands) current_from_directory = HostInformation.get_current_dir(master_path) @@ -421,7 +483,7 @@ def scp_to(from_inventory_path, to_inventory_path, file_name) -> None: # Allowing handling permissions if file_name == 'wazuh-install-files.tar': - Executor.execute_command(from_inventory_path, f'chmod +rw {file_name}') + ConnectionManager.execute_commands(from_inventory_path, f'chmod +rw {file_name}') logger.info('File permissions modified to be handled') # SCP @@ -438,8 +500,8 @@ def scp_to(from_inventory_path, to_inventory_path, file_name) -> None: # Restoring permissions if file_name == 'wazuh-install-files.tar': - Executor.execute_command(from_inventory_path, f'chmod 600 {file_name}') - Executor.execute_command(to_inventory_path, f'chmod 600 {file_name}') + ConnectionManager.execute_commands(from_inventory_path, f'chmod 600 {file_name}') + ConnectionManager.execute_commands(to_inventory_path, f'chmod 600 {file_name}') logger.info('File permissions were restablished') # Deleting file from localhost @@ -518,20 +580,58 @@ def file_monitor(monitored_file: str, target_string: str, timeout: int = 30) -> class CheckFiles: @staticmethod - def _checkfiles(inventory_path, os_type, directory, filter= None, hash_algorithm='sha256') -> dict: + def _checkfiles(inventory_path, os_type, directory, filters_keywords= None, hash_algorithm='sha256') -> dict: """ It captures a structure of a directory Returns: Dict: dict of directories:hash """ if 'linux' == os_type: - command = f'sudo find {directory} -type f -exec sha256sum {{}} + {filter}' - result = Executor.execute_command(inventory_path, command) + filters = f"| grep -v {filters_keywords[0]}" + for filter_ in filters_keywords[1:]: + filters += f" | grep -v {filter_}" + command = f'sudo find {directory} -type f -exec sha256sum {{}} + {filters}' + result = ConnectionManager.execute_commands(inventory_path, command).get('output') + elif 'macos' == os_type: + filters = f"| grep -v {filters_keywords[0]}" + for filter_ in filters_keywords[1:]: + filters += f" | grep -v {filter_}" command = f'sudo find {directory} -type f -exec shasum -a 256 {{}} \; {filter}' - result = Executor.execute_command(inventory_path, command) - elif 'windows' == os_type: - command = 'dir /a-d /b /s | findstr /v /c:"\\.$" /c:"\\..$"| find /c ":"' + result = ConnectionManager.execute_commands(inventory_path, command).get('output') + + elif 'windows' in os_type: + quoted_filters = ['"{}"'.format(keyword) for keyword in filters_keywords] + filter_files = ",".join(quoted_filters) + command = f"$includedDirectories = @('{directory}') " + command += f"\n$excludedPatterns = @({filter_files})" + command += """ + try { + foreach ($dir in $includedDirectories) { + Get-ChildItem -Path "$dir" -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object { + $fileName = $_.FullName + $hash = Get-FileHash -Path $fileName -Algorithm SHA256 -ErrorAction SilentlyContinue + if ($hash) { + $exclude = $false + foreach ($pattern in $excludedPatterns) { + if ($fileName -like "*$pattern*") { + $exclude = $true + break + } + } + if (-not $exclude) { + Write-Output "$($hash.Hash) $fileName" + } + } + } + } + } catch { + Write-Host "Error: $_" + } + + """ + + result = ConnectionManager.execute_commands(inventory_path, command).get('output') else: logger.info(f'Unsupported operating system') return None @@ -544,9 +644,9 @@ def _checkfiles(inventory_path, os_type, directory, filter= None, hash_algorithm @staticmethod - def _perform_scan(inventory_path, os_type, directories, filters): + def _perform_scan(inventory_path, os_type, directories, filters_keywords): logger.info(f'Generating Snapshot for Checkfile in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - return {directory: CheckFiles._checkfiles(inventory_path, os_type, directory, filters) for directory in directories} + return {directory: CheckFiles._checkfiles(inventory_path, os_type, directory, filters_keywords) for directory in directories} @staticmethod @@ -573,22 +673,19 @@ def perform_action_and_scan(inventory_path, callback) -> dict: """ os_type = HostInformation.get_os_type(inventory_path) - if 'linux' in inventory_path: + if os_type == 'linux': directories = ['/boot', '/usr/bin', '/root', '/usr/sbin'] filters_keywords = ['grep', 'tar', 'coreutils', 'sed', 'procps', 'gawk', 'lsof', 'curl', 'openssl', 'libcap', 'apt-transport-https', 'libcap2-bin', 'software-properties-common', 'gnupg', 'gpg'] - filters = f"| grep -v {filters_keywords[0]}" - - elif 'macos' in inventory_path: + elif os_type == 'windows': + directories = ['C:\\Program Files', 'C:\\Program Files (x86)','C:\\Users\\vagrant'] + filters_keywords = ['log','tmp','ossec-agent', 'EdgeUpdate'] + elif os_type == 'macos': directories = ['/usr/bin', '/usr/sbin'] filters_keywords = ['grep'] - filters = f"| grep -v {filters_keywords[0]}" - for filter_ in filters_keywords[1:]: - filters+= f" | grep -v {filter_}" - - initial_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters) + initial_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters_keywords) callback() - second_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters) + second_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters_keywords) changes = {directory: CheckFiles._calculate_changes(initial_scans[directory], second_scans[directory]) for directory in directories} return changes @@ -609,11 +706,15 @@ def get_component_status(inventory_path, host_role) -> str: """ logger.info(f'Getting status of {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': - return Executor.execute_command(inventory_path, f'systemctl status {host_role}') + return ConnectionManager.execute_commands(inventory_path, f'systemctl status {host_role}').get('output') + elif os_type == 'windows': + result = ConnectionManager.execute_commands(inventory_path, "Get-Service -Name 'Wazuh' | Format-Table -HideTableHeaders Status") + if result.get('success'): + return result.get('output') elif os_type == 'macos': - return Executor.execute_command(inventory_path, f'/Library/Ossec/bin/wazuh-control status | grep {host_role}') - + return ConnectionManager.execute_commands(inventory_path, f'{MACOS_WAZUH_CONTROL} status | grep {host_role}').get('output') @staticmethod def component_stop(inventory_path, host_role) -> None: @@ -626,11 +727,13 @@ def component_stop(inventory_path, host_role) -> None: """ logger.info(f'Stopping {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': - return Executor.execute_command(inventory_path, f'systemctl stop {host_role}') + ConnectionManager.execute_commands(inventory_path, f'systemctl stop {host_role}') + elif os_type == 'windows': + ConnectionManager.execute_commands(inventory_path, f'NET STOP Wazuh') elif os_type == 'macos': - return Executor.execute_command(inventory_path, f'/Library/Ossec/bin/wazuh-control stop | grep {host_role}') - + ConnectionManager.execute_commands(inventory_path, f'{MACOS_WAZUH_CONTROL} stop | grep {host_role}') @staticmethod def component_restart(inventory_path, host_role) -> None: @@ -644,11 +747,14 @@ def component_restart(inventory_path, host_role) -> None: logger.info(f'Restarting {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': - return Executor.execute_command(inventory_path, f'systemctl restart {host_role}') + ConnectionManager.execute_commands(inventory_path, f'systemctl restart {host_role}') + elif os_type == 'windows': + ConnectionManager.execute_commands(inventory_path, 'NET STOP Wazuh') + ConnectionManager.execute_commands(inventory_path, 'NET START Wazuh') elif os_type == 'macos': - return Executor.execute_command(inventory_path, f'/Library/Ossec/bin/wazuh-control restart | grep {host_role}') - + ConnectionManager.execute_commands(inventory_path, f'{MACOS_WAZUH_CONTROL} restart | grep {host_role}') @staticmethod def component_start(inventory_path, host_role) -> None: @@ -661,12 +767,15 @@ def component_start(inventory_path, host_role) -> None: """ logger.info(f'Starting {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') + os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': - return Executor.execute_command(inventory_path, f'systemctl start {host_role}') + ConnectionManager.execute_commands(inventory_path, f'systemctl start {host_role}') + elif os_type == 'windows': + ConnectionManager.execute_commands(inventory_path, 'NET START Wazuh') elif os_type == 'macos': - return Executor.execute_command(inventory_path, f'/Library/Ossec/bin/wazuh-control start | grep {host_role}') - + ConnectionManager.execute_commands(inventory_path, f'{MACOS_WAZUH_CONTROL} start | grep {host_role}') @staticmethod def get_component_version(inventory_path) -> str: @@ -680,11 +789,15 @@ def get_component_version(inventory_path) -> str: str: version """ os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': - return Executor.execute_command(inventory_path, f'{WAZUH_CONTROL} info -v') - elif os_type == 'macos': - return Executor.execute_command(inventory_path, f'/Library/Ossec/bin/wazuh-control info -v') + return ConnectionManager.execute_commands(inventory_path, f'{WAZUH_CONTROL} info -v').get('output') + elif os_type == 'windows': + return ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WINDOWS_VERSION}"').get('output')#.replace("\n", "")) + + elif os_type == 'macos': + return ConnectionManager.execute_commands(inventory_path, f'{MACOS_WAZUH_CONTROL} info -v').get('output') @staticmethod def get_component_revision(inventory_path) -> str: @@ -698,14 +811,16 @@ def get_component_revision(inventory_path) -> str: str: revision number """ os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': - return Executor.execute_command(inventory_path, f'{WAZUH_CONTROL} info -r') + return ConnectionManager.execute_commands(inventory_path, f'{WAZUH_CONTROL} info -r').get('output') + elif os_type == 'windows': + return ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WINDOWS_REVISION}"').get('output') elif os_type == 'macos': - return Executor.execute_command(inventory_path, f'/Library/Ossec/bin/wazuh-control info -r') - + return ConnectionManager.execute_commands(inventory_path, f'{MACOS_WAZUH_CONTROL} info -r').get('output') @staticmethod - def hasAgentClientKeys(inventory_path) -> bool: + def has_agent_client_keys(inventory_path) -> bool: """ Returns the True of False depending if in the component Client.keys exists @@ -716,14 +831,20 @@ def hasAgentClientKeys(inventory_path) -> bool: bool: True/False """ os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': - return HostInformation.file_exists(inventory_path, CLIENT_KEYS) + result = ConnectionManager.execute_commands(inventory_path, f'[ -f {CLIENT_KEYS} ] && echo true || echo false') + return 'true' in result.get('output') + elif os_type == 'windows': + result = ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{WINDOWS_CLIENT_KEYS}"') + if result.get('success'): + return result.get('output', '') + return False elif os_type == 'macos': - return HostInformation.file_exists(inventory_path, '/Library/Ossec/etc/client.keys') - + return HostInformation.file_exists(inventory_path, f'{MACOS_CLIENT_KEYS}') @staticmethod - def isComponentActive(inventory_path, host_role) -> bool: + def is_component_active(inventory_path, host_role) -> bool: """ Returns the True of False depending if the component is Active @@ -735,10 +856,20 @@ def isComponentActive(inventory_path, host_role) -> bool: bool: True/False """ os_type = HostInformation.get_os_type(inventory_path) + if os_type == 'linux': - return 'active' == Executor.execute_command(inventory_path, f'systemctl is-active {host_role}').replace("\n", "") + return 'active' == ConnectionManager.execute_commands(inventory_path, f'systemctl is-active {host_role}').get('output').replace("\n", "") + + elif os_type == 'windows': + result = ConnectionManager.execute_commands(inventory_path, "Get-Service -Name 'Wazuh'") + return result.get('success') + elif os_type == 'macos': - return f'com.{host_role.replace("-", ".")}' in Executor.execute_command(inventory_path, f'launchctl list | grep com.{host_role.replace("-", ".")}') + result = ConnectionManager.execute_commands(inventory_path, f'launchctl list | grep com.{host_role.replace("-", ".")}').get('output') + if result == None: + return False + else: + return f'com.{host_role.replace("-", ".")}' in result class Waits: diff --git a/deployability/modules/testing/tests/helpers/indexer.py b/deployability/modules/testing/tests/helpers/indexer.py new file mode 100644 index 0000000000..fd674c4f42 --- /dev/null +++ b/deployability/modules/testing/tests/helpers/indexer.py @@ -0,0 +1,110 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import time + +from .executor import ConnectionManager, WazuhAPI +from modules.testing.utils import logger + +class WazuhIndexer: + + @staticmethod + def get_indexer_version(inventory_path) -> str: + """ + Returns the Wazuh indexer version + + Args: + inventory_path (str): host's inventory path + + Returns: + - str: Version of the Wazuh indexer. + """ + + return ConnectionManager.execute_commands(inventory_path,'cat /usr/share/wazuh-indexer/VERSION').get('output').strip() + + + @staticmethod + def are_indexer_internal_users_complete(inventory_path) -> bool: + """ + Returns True/False depending on the existance of all the expected internal users + + Args: + inventory_path (str): host's inventory path + + Returns: + - bool: True/False depending on the status. + """ + + users_to_check = [ + 'admin', + 'kibanaserver', + 'kibanaro', + 'logstash', + 'readall', + 'snapshotrestore' + ] + report_of_users = ConnectionManager.execute_commands(inventory_path, "cat /etc/wazuh-indexer/opensearch-security/internal_users.yml | grep '^[a-z]'").get('output') + for user in users_to_check: + if user not in report_of_users: + return False + return True + + + @staticmethod + def are_indexes_working(wazuh_api: WazuhAPI, inventory_path) -> bool: + """ + Returns True/False depending on the working status of the Wazuh indexes + + Args: + inventory_path (str): host's inventory path + + Returns: + - bool: True/False depending on the status. + """ + indexes = ConnectionManager.execute_commands(inventory_path, f"curl -k -u {wazuh_api.username}:{wazuh_api.password} {wazuh_api.api_url}/_cat/indices/?pretty").get('output').strip().split('\n') + for index in indexes: + if 'red' not in index: + return True + return False + + + @staticmethod + def is_index_cluster_working(wazuh_api: WazuhAPI, inventory_path) -> bool: + """ + Returns True/False depending on the status of the Wazuh indexer cluster + + Args: + inventory_path (str): host's inventory path + + Returns: + - bool: True/False depending on the status. + """ + response = ConnectionManager.execute_commands(inventory_path, f"curl -k -u {wazuh_api.username}:{wazuh_api.password} {wazuh_api.api_url}/_cat/health").get('output') + return 'green' in response + + + @staticmethod + def is_indexer_port_open(inventory_path, wait=10, cycles=50) -> bool: + """ + Check if the Wazuh indexer port is open + + Args: + inventory_path (str): Wazuh indexer inventory. + + Returns: + str: OS name. + """ + wait_cycles = 0 + while wait_cycles < cycles: + ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":9200"').get('output').strip().split('\n') + for port in ports: + if any(state in port for state in ['ESTAB', 'LISTEN']): + continue + else: + time.sleep(wait) + wait_cycles += 1 + break + else: + return True + return False diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index 2ac8811ab7..a46fa7a7e5 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -3,10 +3,10 @@ # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 import requests -import socket +import time from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT -from .executor import Executor, WazuhAPI +from .executor import WazuhAPI, ConnectionManager from .generic import HostInformation, CheckFiles from modules.testing.utils import logger from .utils import Utils @@ -39,7 +39,7 @@ def install_manager(inventory_path, node_name, wazuh_version) -> None: f"bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" ] logger.info(f'Installing Manager in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_commands(inventory_path, commands) + ConnectionManager.execute_commands(inventory_path, commands) @staticmethod @@ -88,7 +88,7 @@ def uninstall_manager(inventory_path) -> None: commands.extend(system_commands) logger.info(f'Uninstalling Manager in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') - Executor.execute_commands(inventory_path, commands) + ConnectionManager.execute_commands(inventory_path, commands) @staticmethod @@ -119,8 +119,8 @@ def perform_action_and_scan(manager_params, action_callback) -> dict: Takes scans using filters, the callback action and compares the result Args: - agent_params (str): agent parameters - callbak (cb): callback (action) + manager_params (str): manager parameters + callback (cb): callback (action) Returns: result (dict): comparison brief @@ -182,13 +182,14 @@ def perform_install_and_scan_for_manager(manager_params, manager_name, wazuh_par Args: manager_params (str): manager parameters + manager_name (str): manager name wazuh_params (str): wazuh parameters """ action_callback = lambda: WazuhManager._install_manager_callback(wazuh_params, manager_name, manager_params) result = WazuhManager.perform_action_and_scan(manager_params, action_callback) logger.info(f'Pre and post install checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(manager_params)}: {result}') - WazuhManager.assert_results(result) + WazuhManager.assert_results(result, manager_params) @staticmethod @@ -198,19 +199,18 @@ def perform_uninstall_and_scan_for_manager(manager_params) -> None: Args: manager_params (str): manager parameters - wazuh_params (str): wazuh parameters """ action_callback = lambda: WazuhManager._uninstall_manager_callback(manager_params) result = WazuhManager.perform_action_and_scan(manager_params, action_callback) logger.info(f'Pre and post uninstall checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(manager_params)}: {result}') - WazuhManager.assert_results(result) + WazuhManager.assert_results(result, manager_params) @staticmethod def assert_results(result) -> None: """ - Gets the status of an agent given its name. + Assert status of checkfiles Args: result (dict): result of comparison between pre and post action scan @@ -221,13 +221,85 @@ def assert_results(result) -> None: # Testing the results for category in categories: for action in actions: - assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category}{action}') + assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category} {action}') + + + @staticmethod + def is_wazuh_api_port_open(inventory_path, wait=10, cycles=50) -> bool: + """ + Check if the Wazuh manager API port is open + Args: + inventory_path (str): Wazuh manager inventory. + Returns: + bool: True if port is opened. + """ + wait_cycles = 0 + while wait_cycles < cycles: + ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":55000"').get('output').strip().split('\n') + for port in ports: + if any(state in port for state in ['ESTAB', 'LISTEN']): + continue + else: + time.sleep(wait) + wait_cycles += 1 + break + else: + return True + return False + + @staticmethod + def is_wazuh_agent_port_open(inventory_path, wait=10, cycles=50) -> bool: + """ + Check if the Wazuh manager port is open + Args: + inventory_path (str): Manager inventory. + + Returns: + bool: True if port is opened. + """ + wait_cycles = 0 + while wait_cycles < cycles: + ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":1514"').get('output').strip().split('\n') + for port in ports: + if any(state in port for state in ['ESTAB', 'LISTEN']): + continue + else: + time.sleep(wait) + wait_cycles += 1 + break + else: + return True + return False + + @staticmethod + def is_wazuh_agent_enrollment_port_open(inventory_path, wait=10, cycles=50) -> bool: + """ + Check if Wazuh manager's agent enrollment port is open + Args: + inventory_path (str): Manager inventory. + + Returns: + bool: True if port is opened. + """ + wait_cycles = 0 + while wait_cycles < cycles: + ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":443"').get('output').strip().split('\n') + for port in ports: + if any(state in port for state in ['ESTAB', 'LISTEN']): + continue + else: + time.sleep(wait) + wait_cycles += 1 + break + else: + return True + return False @staticmethod def get_cluster_info(inventory_path) -> None: """ - Returns the cluster information + Returns the Wazuh cluster information from the Wazuh manager Args: inventory_path: host's inventory path @@ -236,7 +308,7 @@ def get_cluster_info(inventory_path) -> None: str: Cluster status """ - return Executor.execute_command(inventory_path, f'{CLUSTER_CONTROL} -l') + return ConnectionManager.execute_commands(inventory_path, f'{CLUSTER_CONTROL} -l').get('output') @staticmethod @@ -251,7 +323,7 @@ def get_agent_control_info(inventory_path) -> None: str: Agents status """ - return Executor.execute_command(inventory_path, f'{AGENT_CONTROL} -l') + return ConnectionManager.execute_commands(inventory_path, f'{AGENT_CONTROL} -l').get('output') @staticmethod @@ -278,8 +350,8 @@ def configuring_clusters(inventory_path, node_name, node_type, node_to_connect_i "systemctl restart wazuh-manager" ] - Executor.execute_commands(inventory_path, commands) - if node_name in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'): + ConnectionManager.execute_commands(inventory_path, commands) + if node_name in ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}').get('output'): logger.info(f'Cluster configured in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') else: logger.error(f'Error configuring cluster information in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index 24dd874c54..0bd9389fb1 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -7,16 +7,17 @@ import yaml import logging import time +import winrm from modules.testing.utils import logger - +from .generic import HostInformation paramiko_logger = logging.getLogger("paramiko") paramiko_logger.setLevel(logging.CRITICAL) class Utils: - + @staticmethod def extract_ansible_host(file_path) -> str: with open(file_path, 'r') as yaml_file: @@ -29,6 +30,8 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: match = re.search(r'/manager-[^-]+-([^-]+)-([^-]+)-', inventory_path) elif 'agent' in inventory_path: match = re.search(r'/agent-[^-]+-([^-]+)-([^-]+)-', inventory_path) + elif 'central_components' in inventory_path: + match = re.search(r'/central_components-[^-]+-([^-]+)-([^-]+)-', inventory_path) if match: os_name = match.group(1)+ '-' + match.group(2) logger.info(f'Checking connection to {os_name}') @@ -39,14 +42,17 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: raise FileNotFoundError(logger.error(f'File not found in {os_name}')) except yaml.YAMLError: raise ValueError(logger.error(f'Invalid inventory information in {os_name}')) - - - if 'ansible_ssh_private_key_file' in inventory_data: - host = inventory_data.get('ansible_host') - port = inventory_data.get('ansible_port') - private_key_path = inventory_data.get('ansible_ssh_private_key_file') - username = inventory_data.get('ansible_user') + host = inventory_data.get('ansible_host') + port = inventory_data.get('ansible_port') + private_key_path = inventory_data.get('ansible_ssh_private_key_file', None) + username = inventory_data.get('ansible_user') + password = inventory_data.get('ansible_password', None) + + + os_type = HostInformation.get_os_type(inventory_path) + + if os_type == 'linux': ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) private_key = paramiko.RSAKey.from_private_key_file(private_key_path) @@ -66,12 +72,29 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: except Exception as e: logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') time.sleep(sleep) - else: - host = inventory_data.get('ansible_host', None) - port = inventory_data.get('ansible_port', 22) - user = inventory_data.get('ansible_user', None) - password = inventory_data.get('ansible_password', None) + elif os_type == 'windows': + if port == 5986: + protocol = 'https' + else: + protocol = 'http' + endpoint_url = f'{protocol}://{host}:{port}' + + for attempt in range(1, attempts + 1): + try: + session = winrm.Session(endpoint_url, auth=(username, password),transport='ntlm', server_cert_validation='ignore') + cmd = session.run_cmd('ipconfig') + if cmd.status_code == 0: + logger.info("WinRM connection successful.") + return True + else: + logger.error('WinRM connection failed. Check the credentials in the inventory file.') + return False + except Exception as e: + logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') + time.sleep(sleep) + + elif os_type == 'macos': ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) @@ -79,7 +102,7 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: - ssh.connect(hostname=host, port=port, username=user, password=password) + ssh.connect(hostname=host, port=port, username=username, password=password) logger.info(f'Connection established successfully in {os_name}') ssh.close() return True diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index 1caa6c8cce..a4c6b80d0e 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -2,14 +2,13 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest -from ..helpers.agent import WazuhAgent, WazuhAPI -from ..helpers.constants import WAZUH_ROOT -from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions, Waits from modules.testing.utils import logger -from ..helpers.manager import WazuhManager +from ..helpers.agent import WazuhAgent, WazuhAPI +from ..helpers.constants import WAZUH_ROOT, WINDOWS_ROOT_DIR, MACOS_ROOT_DIR +from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.utils import Utils @@ -54,7 +53,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params @@ -67,25 +66,38 @@ def setup_test_environment(wazuh_params): def test_wazuh_os_version(wazuh_params): wazuh_api = WazuhAPI(wazuh_params['master']) for agent_names, agent_params in wazuh_params['agents'].items(): - if HostInformation.get_os_type(agent_params) == 'linux': - assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') - elif HostInformation.get_os_type(agent_params) == 'macos': - assert HostInformation.dir_exists(agent_params, '/Library/Ossec'), logger.error(f'The /Library/Ossec is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') + path_to_check = '' + os_type = HostInformation.get_os_type(agent_params) + + if os_type == 'linux': + path_to_check = WAZUH_ROOT + elif os_type == 'windows': + path_to_check = WINDOWS_ROOT_DIR + elif os_type == 'macos': + path_to_check = MACOS_ROOT_DIR + + assert HostInformation.dir_exists(agent_params, path_to_check), logger.error(f'The {path_to_check} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) - assert HostInformation.get_os_version_from_inventory(agent_params) in WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names), logger.error('There is a mismatch between the OS version and the OS version of the installed agent') - if HostInformation.get_os_type(agent_params) == 'linux': - assert HostInformation.get_os_name_from_inventory(agent_params) in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') - elif HostInformation.get_os_type(agent_params) == 'macos': - assert 'macos' in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') + if not os_type == 'windows': + assert HostInformation.get_os_version_from_inventory(agent_params) in WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names), logger.error('There is a mismatch between the OS version and the OS version of the installed agent') + + if os_type == 'macos': + os_name = 'macos' + elif os_type == 'windows': + os_name = 'windows' + elif os_type == 'linux': + os_name = HostInformation.get_os_name_from_inventory(agent_params) + + assert os_name in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent') def test_wazuh_version(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert wazuh_params['wazuh_version'] in GeneralComponentActions.get_component_version(agent_params), logger.error(f"The version {HostInformation.get_os_name_and_version_from_inventory(agent_params)} is not {wazuh_params['wazuh_version']} by command") def test_wazuh_revision(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert wazuh_params['wazuh_revision'] in GeneralComponentActions.get_component_revision(agent_params), logger.error(f"The revision {HostInformation.get_os_name_and_version_from_inventory(agent_params)} is not {wazuh_params['wazuh_revision']} by command") diff --git a/deployability/modules/testing/tests/test_agent/test_connection.py b/deployability/modules/testing/tests/test_agent/test_connection.py index 6d2377dc6f..c2163ddb08 100644 --- a/deployability/modules/testing/tests/test_agent/test_connection.py +++ b/deployability/modules/testing/tests/test_agent/test_connection.py @@ -2,13 +2,13 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest +from modules.testing.utils import logger from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from modules.testing.utils import logger from ..helpers.utils import Utils @@ -53,7 +53,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params @@ -73,13 +73,15 @@ def test_connection(wazuh_params): def test_status(wazuh_params): for agent in wazuh_params['agents'].values(): - assert 'active' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent') or 'is running' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') + status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent') + valid_statuses = ['active', 'connected', 'Running', 'is running'] + assert any(valid_status in status for valid_status in valid_statuses), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') def test_service(wazuh_params): wazuh_api = WazuhAPI(wazuh_params['master']) for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by API') + assert GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by API') expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) @@ -87,14 +89,14 @@ def test_service(wazuh_params): def test_clientKeys(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.hasAgentClientKeys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') + assert GeneralComponentActions.has_agent_client_keys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') def test_port(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): - assert WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is closed') + for _, agent_params in wazuh_params['agents'].items(): + assert WazuhAgent.is_agent_port_open(agent_params), logger.error('Port is closed') def test_processes(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are not active') diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index b06c00b668..b0f1c687f8 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -2,17 +2,16 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest +from modules.testing.utils import logger from ..helpers.agent import WazuhAgent -from ..helpers.constants import WAZUH_ROOT +from ..helpers.constants import WAZUH_ROOT, WINDOWS_ROOT_DIR, MACOS_ROOT_DIR from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions -from modules.testing.utils import logger from ..helpers.manager import WazuhManager from ..helpers.utils import Utils - @pytest.fixture(scope="module", autouse=True) def wazuh_params(request): wazuh_version = request.config.getoption('--wazuh_version') @@ -54,7 +53,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params @@ -65,13 +64,14 @@ def setup_test_environment(wazuh_params): def test_installation(wazuh_params): # Checking connection - for manager_name, manager_params in wazuh_params['managers'].items(): + for _, manager_params in wazuh_params['managers'].items(): Utils.check_inventory_connection(manager_params) # Certs creation, firewall management and Manager installation for agent_name, agent_params in wazuh_params['agents'].items(): HostConfiguration.disable_firewall(agent_params) + if HostInformation.dir_exists(wazuh_params['master'], WAZUH_ROOT): logger.info(f'Manager is already installed in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])}') else: @@ -81,18 +81,23 @@ def test_installation(wazuh_params): assert HostInformation.dir_exists(wazuh_params['master'], WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])}') # Agent installation - for agent_names, agent_params in wazuh_params['agents'].items(): - WazuhAgent.perform_install_and_scan_for_agent(agent_params, agent_names, wazuh_params) + for agent_name, agent_params in wazuh_params['agents'].items(): + WazuhAgent.perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) # Testing installation directory - for agent_names, agent_params in wazuh_params['agents'].items(): - if HostInformation.get_os_type(agent_params) == 'linux': - assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') - elif HostInformation.get_os_type(agent_params) == 'macos': - assert HostInformation.dir_exists(agent_params, '/Library/Ossec'), logger.error(f'The /Library/Ossec is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}') + for agent in wazuh_params['agents'].values(): + os_type = HostInformation.get_os_type(agent) + if os_type == 'linux': + path_to_check = WAZUH_ROOT + elif os_type == 'windows': + path_to_check = WINDOWS_ROOT_DIR + elif os_type == 'macos': + path_to_check = MACOS_ROOT_DIR + assert HostInformation.dir_exists(agent, path_to_check), logger.error(f'The {path_to_check} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent)}') def test_status(wazuh_params): for agent in wazuh_params['agents'].values(): agent_status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent') - assert 'loaded' in agent_status or 'not running' in agent_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded') + valid_statuses = ['loaded', 'Stopped', 'not running'] + assert any(valid_status in agent_status for valid_status in valid_statuses), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded') diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py index fa1a5d911f..416a3f96e0 100644 --- a/deployability/modules/testing/tests/test_agent/test_registration.py +++ b/deployability/modules/testing/tests/test_agent/test_registration.py @@ -2,13 +2,13 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest +from modules.testing.utils import logger from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) @@ -53,7 +53,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params @@ -65,28 +65,28 @@ def setup_test_environment(wazuh_params): def test_status(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): WazuhAgent.register_agent(agent_params, wazuh_params['master']) for agent in wazuh_params['agents'].values(): - assert 'active' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent') or 'is running' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') - - -def test_connection(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): - assert agent_names in WazuhManager.get_agent_control_info(wazuh_params['master']), f'The {agent_names} is not present in the master by command' - wazuh_api = WazuhAPI(wazuh_params['master']) - assert any(d.get('name') == agent_names for d in WazuhAgent.get_agents_information(wazuh_api)), logger.error(f'The {agent_names} is not present in the master by API') + status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent') + assert 'active' in status or 'Running' in status or 'is running' in status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active') def test_service(wazuh_params): wazuh_api = WazuhAPI(wazuh_params['master']) for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') + assert GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) +def test_connection(wazuh_params): + for agent_names, agent_params in wazuh_params['agents'].items(): + wazuh_api = WazuhAPI(wazuh_params['master']) + assert agent_names in WazuhManager.get_agent_control_info(wazuh_params['master']), f'The {agent_names} is not present in the master by command' + assert any(d.get('name') == agent_names for d in WazuhAgent.get_agents_information(wazuh_api)), logger.error(f'The {agent_names} is not present in the master by API') + def test_clientKeys(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.hasAgentClientKeys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') + assert GeneralComponentActions.has_agent_client_keys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index 6fee856ec0..366bf80ea7 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -2,12 +2,12 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest -from ..helpers.agent import WazuhAgent, WazuhAPI -from ..helpers.generic import GeneralComponentActions, HostInformation from modules.testing.utils import logger +from ..helpers.agent import WazuhAgent +from ..helpers.generic import GeneralComponentActions, HostInformation from ..helpers.manager import WazuhManager from ..helpers.utils import Utils @@ -53,7 +53,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params @@ -63,13 +63,15 @@ def setup_test_environment(wazuh_params): wazuh_params['agents'] = updated_agents def test_restart(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): GeneralComponentActions.component_restart(agent_params, 'wazuh-agent') def test_status(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert 'active' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent') or 'is running' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') + status = GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent') + valid_statuses = ['active', 'Running', 'is running'] + assert any(valid_status in status for valid_status in valid_statuses), logger.error(f'{agent_names} is not active by command') def test_connection(wazuh_params): @@ -79,19 +81,19 @@ def test_connection(wazuh_params): def test_isActive(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') + assert GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command') def test_clientKeys(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.hasAgentClientKeys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') + assert GeneralComponentActions.has_agent_client_keys(agent_params), logger.error(f'{agent_names} has not ClientKeys file') def test_port(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): - assert WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is closed') + for _, agent_params in wazuh_params['agents'].items(): + assert WazuhAgent.is_agent_port_open(agent_params), logger.error('Port is closed') def test_processes(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are not active') \ No newline at end of file diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index 7d7eda4bfc..9a770e4607 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -2,12 +2,12 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest +from modules.testing.utils import logger from ..helpers.agent import WazuhAgent, WazuhAPI from ..helpers.generic import GeneralComponentActions, Waits, HostInformation -from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) @@ -56,7 +56,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params @@ -71,17 +71,20 @@ def test_service(wazuh_params): GeneralComponentActions.component_stop(agent_params, 'wazuh-agent') for agent_names, agent_params in wazuh_params['agents'].items(): - assert 'inactive' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent') or 'not running' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is still active by command') + status = GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent') + valid_statuses = ['inactive', 'Stopped', 'StopPending', 'not running'] + assert any(valid_status in status for valid_status in valid_statuses), logger.error(f'{agent_names} is still active by command') + expected_condition_func = lambda: 'disconnected' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) def test_port(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): - assert not WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is still opened') + for _, agent_params in wazuh_params['agents'].items(): + assert not WazuhAgent.is_agent_port_open(agent_params), logger.error('Port is still opened') def test_processes(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index b61e952f34..286e7110be 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -2,14 +2,14 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import pytest import re +import pytest +from modules.testing.utils import logger from ..helpers.agent import WazuhAgent -from ..helpers.constants import WAZUH_ROOT +from ..helpers.constants import WAZUH_ROOT, WINDOWS_CONFIGURATIONS_DIR, WINDOWS_ROOT_DIR, MACOS_ROOT_DIR, MACOS_CONFIGURATIONS_DIR from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.manager import WazuhManager, WazuhAPI -from modules.testing.utils import logger from ..helpers.utils import Utils @pytest.fixture(scope="module", autouse=True) @@ -54,7 +54,7 @@ def setup_test_environment(wazuh_params): updated_agents = {} for agent_name, agent_params in wazuh_params['agents'].items(): Utils.check_inventory_connection(agent_params) - if GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') and GeneralComponentActions.hasAgentClientKeys(agent_params): + if GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent') and GeneralComponentActions.has_agent_client_keys(agent_params): if HostInformation.get_client_keys(agent_params) != []: client_name = HostInformation.get_client_keys(agent_params)[0]['name'] updated_agents[client_name] = agent_params @@ -65,11 +65,17 @@ def setup_test_environment(wazuh_params): def test_uninstall(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not Active before the installation') - if 'linux' in agent_params: - assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in the host {agent_names}') - elif 'macos' in wazuh_params: - assert HostInformation.dir_exists(agent_params, '/Library/Ossec'), logger.error(f'The /Library/Ossec is not present in the host {agent_names}') + assert GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not Active before the installation') + + os_type = HostInformation.get_os_type(agent_params) + if os_type == 'linux': + path_to_check = WAZUH_ROOT + elif os_type == 'windows': + path_to_check = WINDOWS_ROOT_DIR + elif os_type == 'macos': + path_to_check = MACOS_ROOT_DIR + + assert HostInformation.dir_exists(agent_params, path_to_check), logger.error(f'The {path_to_check} is not present in the host {agent_names}') # Agent uninstallation for agent_names, agent_params in wazuh_params['agents'].items(): @@ -83,23 +89,31 @@ def test_uninstall(wazuh_params): def test_agent_uninstalled_directory(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert not HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is still present in the agent {agent_names}') + os_type = HostInformation.get_os_type(agent_params) + if os_type == 'linux': + path_to_check = WAZUH_ROOT + elif os_type == 'windows': + path_to_check = WINDOWS_CONFIGURATIONS_DIR + elif os_type == 'macos': + path_to_check = MACOS_CONFIGURATIONS_DIR + + assert not HostInformation.dir_exists(agent_params, path_to_check), logger.error(f'The {path_to_check} is still present in the agent {agent_names}') def test_service(wazuh_params): wazuh_api = WazuhAPI(wazuh_params['master']) for agent_names, agent_params in wazuh_params['agents'].items(): - assert not GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not inactive by command') + assert not GeneralComponentActions.is_component_active(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not inactive by command') expected_condition_func = lambda: 'disconnected' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) def test_port(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): - assert not WazuhAgent.isAgent_port_open(agent_params), logger.error('Port is still opened') + for _, agent_params in wazuh_params['agents'].items(): + assert not WazuhAgent.is_agent_port_open(agent_params), logger.error('Port is still opened') def test_processes(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for _, agent_params in wazuh_params['agents'].items(): assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') diff --git a/deployability/modules/testing/tests/test_central_components/__init__.py b/deployability/modules/testing/tests/test_central_components/__init__.py new file mode 100644 index 0000000000..184c7ae281 --- /dev/null +++ b/deployability/modules/testing/tests/test_central_components/__init__.py @@ -0,0 +1,6 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +from ..helpers.generic import HostConfiguration, CheckFiles, HostInformation, GeneralComponentActions +from ..helpers.manager import WazuhManager diff --git a/deployability/modules/testing/tests/test_central_components/test_install.py b/deployability/modules/testing/tests/test_central_components/test_install.py new file mode 100644 index 0000000000..f3654d7617 --- /dev/null +++ b/deployability/modules/testing/tests/test_central_components/test_install.py @@ -0,0 +1,164 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest + +from ..helpers.constants import WAZUH_ROOT +from ..helpers.executor import WazuhAPI +from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions +from ..helpers.manager import WazuhManager +from ..helpers.indexer import WazuhIndexer +from ..helpers.dashboard import WazuhDashboard +from ..helpers.central import WazuhCentralComponents +from modules.testing.utils import logger +from ..helpers.utils import Utils + + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + + return { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets + } + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + + +def test_installation(wazuh_params): + # Disabling firewall for all managers + for manager_name, manager_params in wazuh_params['managers'].items(): + Utils.check_inventory_connection(manager_params) + HostConfiguration.disable_firewall(manager_params) + + # Certs create and scp from master to worker + HostConfiguration.certs_create(wazuh_params['wazuh_version'], wazuh_params['master'], wazuh_params['dashboard'], wazuh_params['indexers'], wazuh_params['workers']) + + # Install central components and perform checkfile testing + for manager_name, manager_params in wazuh_params['managers'].items(): + WazuhCentralComponents.perform_install_and_scan_for_aio(manager_params, wazuh_params) + + # Validation of directory of the components + for manager in wazuh_params['managers'].values(): + assert HostInformation.dir_exists(manager, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(manager)}') + + +def test_manager_status(wazuh_params): + assert 'active' in GeneralComponentActions.get_component_status(wazuh_params['master'], 'wazuh-manager'), logger.error(f'The Wazuh manager in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])} is not active') + +def test_manager_version(wazuh_params): + for manager in wazuh_params['managers'].values(): + manager_status = GeneralComponentActions.get_component_version(manager) + assert wazuh_params['wazuh_version'] in manager_status, logger.error(f"The version {HostInformation.get_os_name_and_version_from_inventory(manager)} is not {wazuh_params['wazuh_version']} by using commands") + wazuh_api = WazuhAPI(wazuh_params['master']) + assert wazuh_params['wazuh_version'] in WazuhManager.get_manager_version(wazuh_api), logger.error(f"The version {HostInformation.get_os_name_and_version_from_inventory(manager)} is not {wazuh_params['wazuh_version']} in the API") + + +def test_manager_revision(wazuh_params): + for manager in wazuh_params['managers'].values(): + manager_status = GeneralComponentActions.get_component_revision(manager) + assert wazuh_params['wazuh_revision'] in manager_status, logger.error(f"The revision {HostInformation.get_os_name_and_version_from_inventory(manager)} is not {wazuh_params['wazuh_revision']} by using commands") + wazuh_api = WazuhAPI(wazuh_params['master']) + assert wazuh_params['wazuh_revision'] in str(WazuhManager.get_manager_revision(wazuh_api)), logger.error(f"The revision {HostInformation.get_os_name_and_version_from_inventory(manager)} is not {wazuh_params['wazuh_revision']} in the API") + + +def test_manager_installed_directory(wazuh_params): + for manager in wazuh_params['managers'].values(): + assert HostInformation.dir_exists(manager, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(manager)}') + + +def test_manager_api_port(wazuh_params): + assert WazuhManager.is_wazuh_api_port_open(wazuh_params['master']), logger.error(f"The Wazuh manager's API port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is closed") + + +def test_manager_agent_port(wazuh_params): + assert WazuhManager.is_wazuh_agent_port_open(wazuh_params['master']), logger.error(f"The Wazuh manager port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is closed") + + +def test_manager_agent_enrollment_port(wazuh_params): + assert WazuhManager.is_wazuh_agent_enrollment_port_open(wazuh_params['master']), logger.error(f"The Wazuh manager agent enrollment port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is closed") + + +def test_dashboard_status(wazuh_params): + assert 'active' in GeneralComponentActions.get_component_status(wazuh_params['dashboard'], 'wazuh-dashboard'), logger.error(f"The dashboard in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['dashboard'])} is not active") + wazuh_api = WazuhAPI(wazuh_params['dashboard'], component='dashboard') + assert WazuhDashboard.is_dashboard_active(wazuh_params['dashboard']), logger.error(f"The Wazuh dashboard is not active in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['dashboard'])}") + + +def test_dashboard_version(wazuh_params): + assert wazuh_params['wazuh_version'] == WazuhDashboard.get_dashboard_version(wazuh_params['dashboard']), logger.error(f"There is dismatch in the Wazuh dashboard version in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['dashboard'])}") + + +def test_dashboard_nodes(wazuh_params): + wazuh_api = WazuhAPI(wazuh_params['dashboard'], component='dashboard') + assert WazuhDashboard.are_dashboard_nodes_working(wazuh_api), logger.error(f"There is a problem in the Wazuh dashboard node in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['dashboard'])}") + + +def test_dashboard_keystore(wazuh_params): + assert WazuhDashboard.is_dashboard_keystore_working(wazuh_params['dashboard']), logger.error(f"There is a problem in the Wazuh dashboard keystore in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['dashboard'])}") + + +def test_dashboard_port(wazuh_params): + assert WazuhDashboard.is_dashboard_port_open(wazuh_params['dashboard']), logger.error(f"The Wazuh dashboard port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['dashboard'])} is closed") + + +def test_indexer_status(wazuh_params): + for indexer_params in wazuh_params['indexers']: + assert 'active' in GeneralComponentActions.get_component_status(indexer_params, 'wazuh-indexer'), logger.error(f'The Wazuh indexer in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)} is not active') + + +def test_indexer_clusters_status(wazuh_params): + for indexer_params in wazuh_params['indexers']: + wazuh_api = WazuhAPI(indexer_params, component='indexer') + assert WazuhIndexer.is_index_cluster_working(wazuh_api, indexer_params), logger.error(f'There is a problem in a Wazuh indexer cluster in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)}') + + +def test_indexer_indexes(wazuh_params): + for indexer_params in wazuh_params['indexers']: + wazuh_api = WazuhAPI(indexer_params, component='indexer') + assert WazuhIndexer.are_indexes_working(wazuh_api, indexer_params), logger.error(f'There is a problem in a Wazuh indexer index in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)}') + + +def test_indexer_internalUsers(wazuh_params): + for indexer_params in wazuh_params['indexers']: + assert WazuhIndexer.are_indexer_internal_users_complete(indexer_params), logger.error(f'There is a problem in a Wazuh indexer internal user in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)}') + + +def test_indexer_version(wazuh_params): + for indexer_params in wazuh_params['indexers']: + assert wazuh_params['wazuh_version'] == WazuhIndexer.get_indexer_version(indexer_params), logger.error(f'There is dismatch in Wazuh indexer version in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)}') + + +def test_indexer_port(wazuh_params): + for indexer_params in wazuh_params['indexers']: + assert WazuhIndexer.is_indexer_port_open(indexer_params), logger.error(f"Some Wazuh indexer port in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)} is closed") + + +def test_filebeat_status(wazuh_params): + assert 'active' in GeneralComponentActions.get_component_status(wazuh_params['master'], 'filebeat'), logger.error(f"The Filebeat in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is not active") diff --git a/deployability/modules/testing/tests/test_central_components/test_restart.py b/deployability/modules/testing/tests/test_central_components/test_restart.py new file mode 100644 index 0000000000..0400043b31 --- /dev/null +++ b/deployability/modules/testing/tests/test_central_components/test_restart.py @@ -0,0 +1,94 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest + +from ..helpers.generic import HostInformation, GeneralComponentActions +from ..helpers.manager import WazuhManager +from ..helpers.dashboard import WazuhDashboard +from ..helpers.indexer import WazuhIndexer +from modules.testing.utils import logger + + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + + return { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets + } + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + +def test_restart(wazuh_params): + GeneralComponentActions.component_restart(wazuh_params['master'], 'wazuh-manager') + + for indexer_params in wazuh_params['indexers']: + GeneralComponentActions.component_restart(indexer_params, 'wazuh-indexer') + + GeneralComponentActions.component_restart(wazuh_params['dashboard'], 'wazuh-dashboard') + GeneralComponentActions.component_restart(wazuh_params['master'], 'filebeat') + + +def test_manager_status(wazuh_params): + assert 'active' in GeneralComponentActions.get_component_status(wazuh_params['master'], 'wazuh-manager'), logger.error(f"The Wazuh manager in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is not active") + + +def test_dashboard_status(wazuh_params): + assert 'active' in GeneralComponentActions.get_component_status(wazuh_params['dashboard'], 'wazuh-dashboard'), logger.error(f"The Wazuh dashboard in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['dashboard'])} is not active") + + +def test_indexer_status(wazuh_params): + for indexer_params in wazuh_params['indexers']: + assert 'active' in GeneralComponentActions.get_component_status(indexer_params, 'wazuh-indexer'), logger.error(f'The Wazuh indexer in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)} is not active') + + +def test_filebeat_status(wazuh_params): + assert 'active' in GeneralComponentActions.get_component_status(wazuh_params['master'], 'filebeat'), logger.error(f"The Filebeat in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is not active") + + +def test_manager_api_port(wazuh_params): + assert WazuhManager.is_wazuh_api_port_open(wazuh_params['master']),logger.error(f"The Wazuh manager API port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is closed") + + +def test_manager_agent_port(wazuh_params): + assert WazuhManager.is_wazuh_agent_port_open(wazuh_params['master']),logger.error(f"The Wazuh manager API port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is closed") + + +def test_manager_agent_enrollment_port(wazuh_params): + assert WazuhManager.is_wazuh_agent_enrollment_port_open(wazuh_params['master']),logger.error(f"The Wazuh manager API port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is closed") + + +def test_dashboard_port(wazuh_params): + assert WazuhDashboard.is_dashboard_port_open(wazuh_params['dashboard']),logger.error(f"The Wazuh dashboard port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['dashboard'])} is closed") + + +def test_indexer_port(wazuh_params): + for indexer_params in wazuh_params['indexers']: + assert WazuhIndexer.is_indexer_port_open(indexer_params),logger.error(f'Some Wazuh indexer port in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)} is closed') diff --git a/deployability/modules/testing/tests/test_central_components/test_stop.py b/deployability/modules/testing/tests/test_central_components/test_stop.py new file mode 100644 index 0000000000..218dbbb762 --- /dev/null +++ b/deployability/modules/testing/tests/test_central_components/test_stop.py @@ -0,0 +1,102 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest + +from ..helpers.generic import HostInformation, GeneralComponentActions +from ..helpers.manager import WazuhManager +from ..helpers.dashboard import WazuhDashboard +from ..helpers.indexer import WazuhIndexer +from modules.testing.utils import logger + + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + + params = { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets + } + yield params + logger.info('Restoring Wazuh central components statuses') + GeneralComponentActions.component_restart(params['master'], 'wazuh-manager') + + for indexer_params in params['indexers']: + GeneralComponentActions.component_restart(indexer_params, 'wazuh-indexer') + + GeneralComponentActions.component_restart(params['dashboard'], 'wazuh-dashboard') + GeneralComponentActions.component_restart(params['master'], 'filebeat') + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + +def test_stop(wazuh_params): + GeneralComponentActions.component_stop(wazuh_params['master'], 'wazuh-manager') + + for indexer_params in wazuh_params['indexers']: + GeneralComponentActions.component_stop(indexer_params, 'wazuh-indexer') + + GeneralComponentActions.component_stop(wazuh_params['dashboard'], 'wazuh-dashboard') + GeneralComponentActions.component_stop(wazuh_params['master'], 'filebeat') + +def test_manager_status(wazuh_params): + assert 'inactive' in GeneralComponentActions.get_component_status(wazuh_params['master'], 'wazuh-manager'), logger.error(f"The Wazuh manager in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is not active") + + +def test_dashboard_status(wazuh_params): + assert 'inactive' in GeneralComponentActions.get_component_status(wazuh_params['dashboard'], 'wazuh-dashboard'), logger.error(f"The Wazuh dashboard in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['dashboard'])} is not active") + + +def test_indexer_status(wazuh_params): + for indexer_params in wazuh_params['indexers']: + assert 'inactive' in GeneralComponentActions.get_component_status(indexer_params, 'wazuh-indexer'), logger.error(f'The Wazuh indexer in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)} is not active') + + +def test_filebeat_status(wazuh_params): + assert 'inactive' in GeneralComponentActions.get_component_status(wazuh_params['master'], 'filebeat'), logger.error(f"The Filebeat in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is not active") + + +def test_manager_api_port(wazuh_params): + assert not WazuhManager.is_wazuh_api_port_open(wazuh_params['master'], cycles=1, wait=1), logger.error(f"The Wazuh manager API port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is still active") + + +def test_manager_agent_port(wazuh_params): + assert not WazuhManager.is_wazuh_agent_port_open(wazuh_params['master'], cycles=1, wait=1), logger.error(f"The Wazuh manager API port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is still active") + + +def test_manager_agent_enrollment_port(wazuh_params): + assert not WazuhManager.is_wazuh_agent_enrollment_port_open(wazuh_params['master'], cycles=1, wait=1), logger.error(f"The Wazuh manager API port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is still active") + + +def test_dashboard_port(wazuh_params): + assert not WazuhDashboard.is_dashboard_port_open(wazuh_params['dashboard'], cycles=1, wait=1), logger.error(f"The Wazuh dashboard port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['dashboard'])} is still active") + + +def test_indexer_port(wazuh_params): + for indexer_params in wazuh_params['indexers']: + assert not WazuhIndexer.is_indexer_port_open(indexer_params, cycles=1, wait=1), logger.error(f"Some Wazuh indexer port in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)} is still active") diff --git a/deployability/modules/testing/tests/test_central_components/test_uninstall.py b/deployability/modules/testing/tests/test_central_components/test_uninstall.py new file mode 100644 index 0000000000..5d5370533b --- /dev/null +++ b/deployability/modules/testing/tests/test_central_components/test_uninstall.py @@ -0,0 +1,84 @@ +# Copyright (C) 2015, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import pytest + +from ..helpers.constants import WAZUH_ROOT +from ..helpers.generic import HostInformation, GeneralComponentActions +from ..helpers.manager import WazuhManager +from ..helpers.dashboard import WazuhDashboard +from ..helpers.indexer import WazuhIndexer +from ..helpers.central import WazuhCentralComponents +from modules.testing.utils import logger + + +@pytest.fixture(scope="module", autouse=True) +def wazuh_params(request): + wazuh_version = request.config.getoption('--wazuh_version') + wazuh_revision = request.config.getoption('--wazuh_revision') + dependencies = request.config.getoption('--dependencies') + targets = request.config.getoption('--targets') + + return { + 'wazuh_version': wazuh_version, + 'wazuh_revision': wazuh_revision, + 'dependencies': dependencies, + 'targets': targets + } + + +@pytest.fixture(scope="module", autouse=True) +def setup_test_environment(wazuh_params): + targets = wazuh_params['targets'] + # Clean the string and split it into key-value pairs + targets = targets.replace(' ', '') + targets = targets.replace(' ', '') + pairs = [pair.strip() for pair in targets.strip('{}').split(',')] + targets_dict = dict(pair.split(':') for pair in pairs) + + wazuh_params['master'] = targets_dict.get('wazuh-1') + wazuh_params['workers'] = [value for key, value in targets_dict.items() if key.startswith('wazuh-') and key != 'wazuh-1'] + wazuh_params['indexers'] = [value for key, value in targets_dict.items() if key.startswith('node-')] + wazuh_params['dashboard'] = targets_dict.get('dashboard', wazuh_params['master']) + + # If there are no indexers, we choose wazuh-1 by default + if not wazuh_params['indexers']: + wazuh_params['indexers'].append(wazuh_params['master']) + + wazuh_params['managers'] = {key: value for key, value in targets_dict.items() if key.startswith('wazuh-')} + + +def test_uninstall(wazuh_params): + assert 'active' in GeneralComponentActions.get_component_status(wazuh_params['master'], 'wazuh-manager'), logger.error(f'The manager in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])} is not active') + assert 'active' in GeneralComponentActions.get_component_status(wazuh_params['dashboard'], 'wazuh-dashboard'), logger.error(f'The dashboard in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["dashboard"])} is not active') + for indexer_params in wazuh_params['indexers']: + assert 'active' in GeneralComponentActions.get_component_status(indexer_params, 'wazuh-indexer'), logger.error(f'The indexer in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)} is not active') + assert 'active' in GeneralComponentActions.get_component_status(wazuh_params['master'], 'filebeat'), logger.error(f'The filebeat in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])} is not active') + + WazuhCentralComponents.perform_uninstall_and_scan_for_aio(wazuh_params['master']) + + +def test_component_uninstalled_directory(wazuh_params): + assert not HostInformation.dir_exists(wazuh_params['master'], WAZUH_ROOT), logger.error(f"In {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} {WAZUH_ROOT} is still present") + + +def test_manager_api_port(wazuh_params): + assert not WazuhManager.is_wazuh_api_port_open(wazuh_params['master'], cycles=1, wait=1), logger.error(f"The Wazuh manager API port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is still active") + + +def test_manager_agent_port(wazuh_params): + assert not WazuhManager.is_wazuh_agent_port_open(wazuh_params['master'], cycles=1, wait=1), logger.error(f"The Wazuh manager API port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is still active") + + +def test_manager_agent_enrollment_port(wazuh_params): + assert not WazuhManager.is_wazuh_agent_enrollment_port_open(wazuh_params['master'], cycles=1, wait=1), logger.error(f"The Wazuh manager API port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is still active") + + +def test_dashboard_port(wazuh_params): + assert not WazuhDashboard.is_dashboard_port_open(wazuh_params['dashboard'], cycles=1, wait=1), logger.error(f"The Wazuh dashboard port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['dashboard'])} is still active") + + +def test_indexer_port(wazuh_params): + for indexer_params in wazuh_params['indexers']: + assert not WazuhIndexer.is_indexer_port_open(indexer_params, cycles=1, wait=1), logger.error(f"Some Wazuh indexer port in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)} is still active") diff --git a/deployability/modules/testing/tests/test_manager/test_uninstall.py b/deployability/modules/testing/tests/test_manager/test_uninstall.py index f212cd4109..f21880bb22 100644 --- a/deployability/modules/testing/tests/test_manager/test_uninstall.py +++ b/deployability/modules/testing/tests/test_manager/test_uninstall.py @@ -50,7 +50,7 @@ def test_uninstall(wazuh_params): for manager in wazuh_params['managers'].values(): manager_status = GeneralComponentActions.get_component_status(manager, 'wazuh-manager') assert 'active' in manager_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(manager)} is not active') - for manager_name, manager_params in wazuh_params['managers'].items(): + for _, manager_params in wazuh_params['managers'].items(): WazuhManager.perform_uninstall_and_scan_for_manager(manager_params) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml new file mode 100644 index 0000000000..1db2fb91cc --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml @@ -0,0 +1,115 @@ +version: 0.1 +description: This workflow is used to test agents deployment for DDT1 PoC +variables: + agent-os: + # Run one at a time as there are limitations on the number of hosts (Inform @dev-devops-team about your usage, dedicated hosts) + - macos-sonoma-14.3-arm64 + #- macos-ventura-13.6.4-arm64 + #- macos-sonoma-14.3-amd64 + #- macos-ventura-13.6.4-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml index 2ae2d26e5f..1db2fb91cc 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents' deployment for DDT1 PoC +description: This workflow is used to test agents deployment for DDT1 PoC variables: agent-os: # Run one at a time as there are limitations on the number of hosts (Inform @dev-devops-team about your usage, dedicated hosts) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml index d9d581b111..8c21f1e8f4 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents' deployment for DDT1 PoC +description: This workflow is used to test agents deployment for DDT1 PoC variables: agent-os: - linux-ubuntu-18.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml index b7002c02c6..cd12531247 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent restart with provisioning agents' with provision module +description: Test agent restart with provisioning agents with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml index 0e97045f32..c01800b3f5 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent restart with provisioning agents' with provision module +description: Test agent restart with provisioning agents with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml index 90c6145862..4cc042b41e 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent restart with provisioning agents' with provision module +description: Test agent restart with provisioning agents with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml new file mode 100755 index 0000000000..9c820e127f --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml @@ -0,0 +1,119 @@ +version: 0.1 +description: This workflow is used to test agents deployment for DDT1 PoC +variables: + agent-os: + - linux-ubuntu-20.04-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-8-amd64 + - linux-redhat-9-amd64 + - windows-desktop-10-amd64 + - windows-server-2012r2-amd64 + - windows-server-2016-amd64 + - windows-server-2019-amd64 + - windows-server-2022-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: medium + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,connection,basic_info,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml new file mode 100755 index 0000000000..9b237868aa --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml @@ -0,0 +1,28 @@ +version: 0.1 +description: This workflow is used to test agents deployment for DDT1 PoC +variables: + agent-os: + - windows-server-2016-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Generic agent test task + - task: "run-agent-windows-server-2016-amd64-tests" + description: "Run tests install for the agent windows-server-2016-amd64." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-windows-server-2016-amd64/inventory.yaml" + - tests: "install" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml index 0d1780775e..1b10763219 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents' deployment for DDT1 PoC +description: This workflow is used to test agents deployment for DDT1 PoC variables: agent-os: - linux-ubuntu-18.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml index 2af6a19463..db0299dbb1 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents' deployment for DDT1 PoC +description: This workflow is used to test agents deployment for DDT1 PoC variables: agent-os: - linux-centos-7-amd64 diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml new file mode 100644 index 0000000000..e5dbe718c9 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml @@ -0,0 +1,113 @@ +version: 0.1 +description: This workflow is used to test agents deployment for DDT1 PoC +variables: + agent-os: + # Run one at a time as there are limitations on the number of hosts + - macos-sonoma-14.0-arm64 + #- macos-ventura-13.4.1-arm64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "aws" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.3 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml index d1e35d3e71..e5dbe718c9 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: This workflow is used to test agents' deployment for DDT1 PoC +description: This workflow is used to test agents deployment for DDT1 PoC variables: agent-os: # Run one at a time as there are limitations on the number of hosts diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml index dea050edb9..7a41aa7c4f 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent restart with provisioning agents' with provision module +description: Test agent restart with provisioning agents with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml index 7b7a3b03f0..7c36f9c872 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent restart with provisioning agents' with provision module +description: Test agent restart with provisioning agents with provision module variables: agent-os: - linux-centos-7-amd64 diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml index 8a7c959ca4..1f2330125b 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent stop with provisioning agents' with provision module +description: Test agent stop with provisioning agents with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml index 1e58fd2593..b09c24c05e 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent stop with provisioning agents' with provision module +description: Test agent stop with provisioning agents with provision module variables: agent-os: - linux-centos-7-amd64 diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml index 91e83fdce7..cb2dff4cb3 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent uninstall with provisioning agents' with provision module +description: Test agent uninstall with provisioning agents with provision module variables: agent-os: - linux-ubuntu-18.04-amd64 diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml index 13a7db7c09..be28e87d6b 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml @@ -1,5 +1,5 @@ version: 0.1 -description: Test agent uninstall with provisioning agents' with provision module +description: Test agent uninstall with provisioning agents with provision module variables: agent-os: - linux-centos-7-amd64 diff --git a/deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml b/deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml new file mode 100644 index 0000000000..4f210e8b92 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml @@ -0,0 +1,69 @@ +version: 0.1 +description: This workflow is used to test the Wazuh manager deployment for DDT1 PoC +variables: + central_components-os: + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-amazon-2-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique central components allocate task + - task: "allocate-central_components-{central_components}" + description: "Allocate resources for the central_components." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{central_components}" + - inventory-output: "{working-dir}/central_components-{central_components}/inventory.yaml" + - track-output: "{working-dir}/central_components-{central_components}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: central_components-os + as: central_components + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/central_components-{central_components-os}/track.yaml" + + # Generic manager test task + - task: "run-central_components-{central_components}-tests" + description: "Run tests install for the central_components." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/central_components-{central_components}/inventory.yaml" + - tests: "install,restart,stop,uninstall" + - component: "central_components" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + on-error: "abort-all" + foreach: + - variable: central_components-os + as: central_components diff --git a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml new file mode 100644 index 0000000000..165032ca53 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml @@ -0,0 +1,60 @@ +version: 0.1 +description: This workflow is used to test the Wazuh manager deployment for DDT1 PoC +variables: + central_components-os: + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique central components allocate task + - task: "allocate-central_components-{central_components}" + description: "Allocate resources for the central_components." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{central_components}" + - inventory-output: "{working-dir}/central_components-{central_components}/inventory.yaml" + - track-output: "{working-dir}/central_components-{central_components}/track.yaml" + on-error: "abort-all" + foreach: + - variable: central_components-os + as: central_components + + # Generic manager test task + - task: "run-central_components-{central_components}-tests" + description: "Run tests install for the central_components." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/central_components-{central_components}/inventory.yaml" + - tests: "install,restart,stop,uninstall" + - component: "central_components" + - wazuh-version: "4.7.3" + - wazuh-revision: "40714" + - live: "True" + on-error: "abort-all" + foreach: + - variable: central_components-os + as: central_components From cff53a33c94a7ed8795387ca3b4dbd3ffb9a7f6e Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 14:17:00 +0200 Subject: [PATCH 082/195] fix(#5229): Removing repeated imports --- deployability/modules/generic/ansible.py | 1 - deployability/modules/testing/tests/helpers/executor.py | 1 - 2 files changed, 2 deletions(-) diff --git a/deployability/modules/generic/ansible.py b/deployability/modules/generic/ansible.py index cbe78aa867..eccabba0ad 100755 --- a/deployability/modules/generic/ansible.py +++ b/deployability/modules/generic/ansible.py @@ -4,7 +4,6 @@ import ansible_runner import jinja2 -from typing import Optional import yaml from pathlib import Path diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index da24d65cf5..b288cef4bd 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -3,7 +3,6 @@ # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 import json -import paramiko import requests import paramiko import subprocess From 3ed8c0512095e8bba02efe8673add299e7a1f39c Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 14:22:43 +0200 Subject: [PATCH 083/195] fix(#5219): Removing repeated imports --- deployability/modules/generic/ansible.py | 1 - .../modules/testing/tests/helpers/executor.py | 1 - .../dtt1-central_components-poc-vagrant.yaml | 22 +++++++++---------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/deployability/modules/generic/ansible.py b/deployability/modules/generic/ansible.py index cbe78aa867..eccabba0ad 100755 --- a/deployability/modules/generic/ansible.py +++ b/deployability/modules/generic/ansible.py @@ -4,7 +4,6 @@ import ansible_runner import jinja2 -from typing import Optional import yaml from pathlib import Path diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index 2c188c648f..edeb0b6a25 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -3,7 +3,6 @@ # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 import json -import paramiko import requests import paramiko import subprocess diff --git a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml index 165032ca53..b232ccf741 100644 --- a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml @@ -3,17 +3,17 @@ description: This workflow is used to test the Wazuh manager deployment for DDT1 variables: central_components-os: - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-oracle-9-amd64 - - linux-amazon-2-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 + #- linux-ubuntu-22.04-amd64 + #- linux-oracle-9-amd64 + #- linux-amazon-2-amd64 + #- linux-redhat-7-amd64 + #- linux-redhat-8-amd64 + #- linux-redhat-9-amd64 + #- linux-centos-7-amd64 + #- linux-centos-8-amd64 + #- linux-debian-10-amd64 + #- linux-debian-11-amd64 + #- linux-debian-12-amd64 infra-provider: vagrant working-dir: /tmp/dtt1-poc From e2a5722b8fcc5729081670a94e39eeda73e1987f Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 14:48:41 +0200 Subject: [PATCH 084/195] fix(#5229): Removing duplicated get_agent_ip_status_and_name_by_id method --- .../modules/testing/tests/helpers/agent.py | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index ac3e940dc9..b7004e0fd9 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -510,26 +510,6 @@ def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): return [None, None, None] - def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): - """ - Get IP status and name by ID. - - Args: - identifier (str): Agent ID. - - Returns: - List: IP, name, and status of the agent. - """ - try: - agents_information = wazuh_api.get_agents_information() - for element in agents_information: - if element['id'] == identifier: - return [element['ip'], element['name'], element['status']] - except Exception as e: - logger.error(f"Unexpected error: {e}") - return [None, None, None] - - def get_agent_os_version_by_name(wazuh_api: WazuhAPI, agent_name): """ Get Agent os version by Agent name From b1712ec86ca12fdc5c8469f100bdd7b487838276 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 15:44:02 +0200 Subject: [PATCH 085/195] fix(#5229): camel_case to snake_case --- .../modules/testing/tests/helpers/agent.py | 25 +++++++++++++++++-- .../tests/test_agent/test_connection.py | 2 +- .../testing/tests/test_agent/test_restart.py | 2 +- .../testing/tests/test_agent/test_stop.py | 2 +- .../tests/test_agent/test_uninstall.py | 2 +- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index b7004e0fd9..4401d86149 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -386,7 +386,8 @@ def assert_results(result, params = None) -> None: assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category} {action}') - def areAgent_processes_active(agent_params): + @staticmethod + def are_agent_processes_active(agent_params): """ Check if agent processes are active @@ -419,7 +420,7 @@ def areAgent_processes_active(agent_params): else: return False - + @staticmethod def is_agent_port_open(inventory_path): """ Check if agent port is open @@ -510,6 +511,26 @@ def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): return [None, None, None] + def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): + """ + Get IP status and name by ID. + + Args: + identifier (str): Agent ID. + + Returns: + List: IP, name, and status of the agent. + """ + try: + agents_information = wazuh_api.get_agents_information() + for element in agents_information: + if element['id'] == identifier: + return [element['ip'], element['name'], element['status']] + except Exception as e: + logger.error(f"Unexpected error: {e}") + return [None, None, None] + + def get_agent_os_version_by_name(wazuh_api: WazuhAPI, agent_name): """ Get Agent os version by Agent name diff --git a/deployability/modules/testing/tests/test_agent/test_connection.py b/deployability/modules/testing/tests/test_agent/test_connection.py index c2163ddb08..2e89b5b370 100644 --- a/deployability/modules/testing/tests/test_agent/test_connection.py +++ b/deployability/modules/testing/tests/test_agent/test_connection.py @@ -99,4 +99,4 @@ def test_port(wazuh_params): def test_processes(wazuh_params): for _, agent_params in wazuh_params['agents'].items(): - assert WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are not active') + assert WazuhAgent.are_agent_processes_active(agent_params), logger.error('Agent processes are not active') diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index 366bf80ea7..0f33a53c41 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -96,4 +96,4 @@ def test_port(wazuh_params): def test_processes(wazuh_params): for _, agent_params in wazuh_params['agents'].items(): - assert WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are not active') \ No newline at end of file + assert WazuhAgent.are_agent_processes_active(agent_params), logger.error('Agent processes are not active') \ No newline at end of file diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index 9a770e4607..b8cef0fcc9 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -87,4 +87,4 @@ def test_port(wazuh_params): def test_processes(wazuh_params): for _, agent_params in wazuh_params['agents'].items(): - assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') + assert not WazuhAgent.are_agent_processes_active(agent_params), logger.error('Agent processes are still active') diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index 286e7110be..673ee9f6d7 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -116,4 +116,4 @@ def test_port(wazuh_params): def test_processes(wazuh_params): for _, agent_params in wazuh_params['agents'].items(): - assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') + assert not WazuhAgent.are_agent_processes_active(agent_params), logger.error('Agent processes are still active') From 9b15424b384ad617db0dca8d278a7c671819b179 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 15:48:09 +0200 Subject: [PATCH 086/195] fix(#5219): Adding fixes from macOS and windows PR --- .../modules/testing/tests/helpers/__init__.py | 5 +++- .../modules/testing/tests/helpers/agent.py | 26 +++---------------- .../modules/testing/tests/helpers/central.py | 7 +---- .../modules/testing/tests/helpers/utils.py | 16 +++++++++--- .../tests/test_agent/test_connection.py | 2 +- .../tests/test_agent/test_registration.py | 2 +- .../testing/tests/test_agent/test_restart.py | 4 +-- .../testing/tests/test_agent/test_stop.py | 2 +- .../tests/test_agent/test_uninstall.py | 2 +- .../test_central_components/test_install.py | 2 +- .../test_central_components/test_restart.py | 2 +- .../test_central_components/test_stop.py | 2 +- .../test_central_components/test_uninstall.py | 2 +- .../tests/test_manager/test_install.py | 2 +- .../tests/test_manager/test_restart.py | 2 +- .../testing/tests/test_manager/test_stop.py | 3 ++- .../tests/test_manager/test_uninstall.py | 3 ++- .../dtt1-central_components-poc-vagrant.yaml | 4 +-- 18 files changed, 39 insertions(+), 49 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/__init__.py b/deployability/modules/testing/tests/helpers/__init__.py index 6249db074a..c89955f5a2 100755 --- a/deployability/modules/testing/tests/helpers/__init__.py +++ b/deployability/modules/testing/tests/helpers/__init__.py @@ -2,6 +2,9 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -from .agent import WazuhAgent from .generic import HostConfiguration, HostInformation, HostMonitor, CheckFiles +from .agent import WazuhAgent from .manager import WazuhManager +from .indexer import WazuhIndexer +from .dashboard import WazuhDashboard +from .central import WazuhCentralComponents \ No newline at end of file diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index ac3e940dc9..2709879d46 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -359,7 +359,6 @@ def perform_uninstall_and_scan_for_agent(agent_params, wazuh_params) -> None: @staticmethod def assert_results(result, params = None) -> None: - """ Gets the status of an agent given its name. @@ -386,7 +385,8 @@ def assert_results(result, params = None) -> None: assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category} {action}') - def areAgent_processes_active(agent_params): + @staticmethod + def are_agent_processes_active(agent_params): """ Check if agent processes are active @@ -419,7 +419,7 @@ def areAgent_processes_active(agent_params): else: return False - + @staticmethod def is_agent_port_open(inventory_path): """ Check if agent port is open @@ -510,26 +510,6 @@ def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): return [None, None, None] - def get_agent_ip_status_and_name_by_id(wazuh_api: WazuhAPI, identifier): - """ - Get IP status and name by ID. - - Args: - identifier (str): Agent ID. - - Returns: - List: IP, name, and status of the agent. - """ - try: - agents_information = wazuh_api.get_agents_information() - for element in agents_information: - if element['id'] == identifier: - return [element['ip'], element['name'], element['status']] - except Exception as e: - logger.error(f"Unexpected error: {e}") - return [None, None, None] - - def get_agent_os_version_by_name(wazuh_api: WazuhAPI, agent_name): """ Get Agent os version by Agent name diff --git a/deployability/modules/testing/tests/helpers/central.py b/deployability/modules/testing/tests/helpers/central.py index 8aeea1e969..3da620cc9d 100644 --- a/deployability/modules/testing/tests/helpers/central.py +++ b/deployability/modules/testing/tests/helpers/central.py @@ -2,14 +2,9 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import requests -import socket - -from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT -from .executor import ConnectionManager, WazuhAPI +from .executor import ConnectionManager from .generic import HostInformation, CheckFiles from modules.testing.utils import logger -from .utils import Utils class WazuhCentralComponents: diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index 0bd9389fb1..05e7622f75 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -10,7 +10,6 @@ import winrm from modules.testing.utils import logger -from .generic import HostInformation paramiko_logger = logging.getLogger("paramiko") paramiko_logger.setLevel(logging.CRITICAL) @@ -49,8 +48,19 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: username = inventory_data.get('ansible_user') password = inventory_data.get('ansible_password', None) - - os_type = HostInformation.get_os_type(inventory_path) + try: + with open(inventory_path.replace('inventory', 'track'), 'r') as file: + data = yaml.safe_load(file) + if 'platform' in data: + os_type = data['platform'] + else: + raise KeyError("The 'platform' key was not found in the YAML file.") + except FileNotFoundError: + logger.error(f"The YAML file '{inventory_path}' was not found.") + except yaml.YAMLError as e: + logger.error(f"Error while loading the YAML file: {e}") + except Exception as e: + logger.error(f"An unexpected error occurred: {e}") if os_type == 'linux': ssh = paramiko.SSHClient() diff --git a/deployability/modules/testing/tests/test_agent/test_connection.py b/deployability/modules/testing/tests/test_agent/test_connection.py index c2163ddb08..2e89b5b370 100644 --- a/deployability/modules/testing/tests/test_agent/test_connection.py +++ b/deployability/modules/testing/tests/test_agent/test_connection.py @@ -99,4 +99,4 @@ def test_port(wazuh_params): def test_processes(wazuh_params): for _, agent_params in wazuh_params['agents'].items(): - assert WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are not active') + assert WazuhAgent.are_agent_processes_active(agent_params), logger.error('Agent processes are not active') diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py index 416a3f96e0..34bf358e61 100644 --- a/deployability/modules/testing/tests/test_agent/test_registration.py +++ b/deployability/modules/testing/tests/test_agent/test_registration.py @@ -81,7 +81,7 @@ def test_service(wazuh_params): Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) def test_connection(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for agent_names, _ in wazuh_params['agents'].items(): wazuh_api = WazuhAPI(wazuh_params['master']) assert agent_names in WazuhManager.get_agent_control_info(wazuh_params['master']), f'The {agent_names} is not present in the master by command' assert any(d.get('name') == agent_names for d in WazuhAgent.get_agents_information(wazuh_api)), logger.error(f'The {agent_names} is not present in the master by API') diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index 366bf80ea7..71d0da2fbb 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -75,7 +75,7 @@ def test_status(wazuh_params): def test_connection(wazuh_params): - for agent_names, agent_params in wazuh_params['agents'].items(): + for agent_names, _ in wazuh_params['agents'].items(): assert agent_names in WazuhManager.get_agent_control_info(wazuh_params['master']), logger.error(f'{agent_names} is not present in agent_control information') @@ -96,4 +96,4 @@ def test_port(wazuh_params): def test_processes(wazuh_params): for _, agent_params in wazuh_params['agents'].items(): - assert WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are not active') \ No newline at end of file + assert WazuhAgent.are_agent_processes_active(agent_params), logger.error('Agent processes are not active') \ No newline at end of file diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index 9a770e4607..b8cef0fcc9 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -87,4 +87,4 @@ def test_port(wazuh_params): def test_processes(wazuh_params): for _, agent_params in wazuh_params['agents'].items(): - assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') + assert not WazuhAgent.are_agent_processes_active(agent_params), logger.error('Agent processes are still active') diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index 286e7110be..673ee9f6d7 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -116,4 +116,4 @@ def test_port(wazuh_params): def test_processes(wazuh_params): for _, agent_params in wazuh_params['agents'].items(): - assert not WazuhAgent.areAgent_processes_active(agent_params), logger.error('Agent processes are still active') + assert not WazuhAgent.are_agent_processes_active(agent_params), logger.error('Agent processes are still active') diff --git a/deployability/modules/testing/tests/test_central_components/test_install.py b/deployability/modules/testing/tests/test_central_components/test_install.py index f3654d7617..eb160c9046 100644 --- a/deployability/modules/testing/tests/test_central_components/test_install.py +++ b/deployability/modules/testing/tests/test_central_components/test_install.py @@ -4,6 +4,7 @@ import pytest +from modules.testing.utils import logger from ..helpers.constants import WAZUH_ROOT from ..helpers.executor import WazuhAPI from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions @@ -11,7 +12,6 @@ from ..helpers.indexer import WazuhIndexer from ..helpers.dashboard import WazuhDashboard from ..helpers.central import WazuhCentralComponents -from modules.testing.utils import logger from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_central_components/test_restart.py b/deployability/modules/testing/tests/test_central_components/test_restart.py index 0400043b31..7138e43d8c 100644 --- a/deployability/modules/testing/tests/test_central_components/test_restart.py +++ b/deployability/modules/testing/tests/test_central_components/test_restart.py @@ -4,11 +4,11 @@ import pytest +from modules.testing.utils import logger from ..helpers.generic import HostInformation, GeneralComponentActions from ..helpers.manager import WazuhManager from ..helpers.dashboard import WazuhDashboard from ..helpers.indexer import WazuhIndexer -from modules.testing.utils import logger @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_central_components/test_stop.py b/deployability/modules/testing/tests/test_central_components/test_stop.py index 218dbbb762..e99b1a9353 100644 --- a/deployability/modules/testing/tests/test_central_components/test_stop.py +++ b/deployability/modules/testing/tests/test_central_components/test_stop.py @@ -4,11 +4,11 @@ import pytest +from modules.testing.utils import logger from ..helpers.generic import HostInformation, GeneralComponentActions from ..helpers.manager import WazuhManager from ..helpers.dashboard import WazuhDashboard from ..helpers.indexer import WazuhIndexer -from modules.testing.utils import logger @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_central_components/test_uninstall.py b/deployability/modules/testing/tests/test_central_components/test_uninstall.py index 5d5370533b..82d7ab2099 100644 --- a/deployability/modules/testing/tests/test_central_components/test_uninstall.py +++ b/deployability/modules/testing/tests/test_central_components/test_uninstall.py @@ -4,13 +4,13 @@ import pytest +from modules.testing.utils import logger from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostInformation, GeneralComponentActions from ..helpers.manager import WazuhManager from ..helpers.dashboard import WazuhDashboard from ..helpers.indexer import WazuhIndexer from ..helpers.central import WazuhCentralComponents -from modules.testing.utils import logger @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_manager/test_install.py b/deployability/modules/testing/tests/test_manager/test_install.py index 0c0aa2eac6..28b346f986 100644 --- a/deployability/modules/testing/tests/test_manager/test_install.py +++ b/deployability/modules/testing/tests/test_manager/test_install.py @@ -4,11 +4,11 @@ import pytest +from modules.testing.utils import logger from ..helpers.constants import WAZUH_ROOT from ..helpers.executor import WazuhAPI from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions from ..helpers.manager import WazuhManager -from modules.testing.utils import logger from ..helpers.utils import Utils diff --git a/deployability/modules/testing/tests/test_manager/test_restart.py b/deployability/modules/testing/tests/test_manager/test_restart.py index 8c5c54f213..13ad39e8c3 100644 --- a/deployability/modules/testing/tests/test_manager/test_restart.py +++ b/deployability/modules/testing/tests/test_manager/test_restart.py @@ -4,8 +4,8 @@ import pytest -from ..helpers.generic import HostInformation, GeneralComponentActions from modules.testing.utils import logger +from ..helpers.generic import HostInformation, GeneralComponentActions @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_manager/test_stop.py b/deployability/modules/testing/tests/test_manager/test_stop.py index 4b31594c43..ddb479a4c9 100644 --- a/deployability/modules/testing/tests/test_manager/test_stop.py +++ b/deployability/modules/testing/tests/test_manager/test_stop.py @@ -4,8 +4,9 @@ import pytest -from ..helpers.generic import HostInformation, GeneralComponentActions from modules.testing.utils import logger +from ..helpers.generic import HostInformation, GeneralComponentActions + @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/testing/tests/test_manager/test_uninstall.py b/deployability/modules/testing/tests/test_manager/test_uninstall.py index f21880bb22..7554d5a7b8 100644 --- a/deployability/modules/testing/tests/test_manager/test_uninstall.py +++ b/deployability/modules/testing/tests/test_manager/test_uninstall.py @@ -4,10 +4,11 @@ import pytest +from modules.testing.utils import logger from ..helpers.constants import WAZUH_ROOT from ..helpers.generic import HostInformation, GeneralComponentActions from ..helpers.manager import WazuhManager -from modules.testing.utils import logger + @pytest.fixture(scope="module", autouse=True) diff --git a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml index b232ccf741..c420b94181 100644 --- a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml @@ -2,14 +2,14 @@ version: 0.1 description: This workflow is used to test the Wazuh manager deployment for DDT1 PoC variables: central_components-os: - - linux-ubuntu-20.04-amd64 + #- linux-ubuntu-22.04-amd64 #- linux-ubuntu-22.04-amd64 #- linux-oracle-9-amd64 #- linux-amazon-2-amd64 #- linux-redhat-7-amd64 #- linux-redhat-8-amd64 #- linux-redhat-9-amd64 - #- linux-centos-7-amd64 + - linux-centos-7-amd64 #- linux-centos-8-amd64 #- linux-debian-10-amd64 #- linux-debian-11-amd64 From 1a1bb8acf4882bc24e32be8232b2b612f7e03bff Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 17:45:07 +0200 Subject: [PATCH 087/195] fix(#5229): Circular reference fix --- .../modules/testing/tests/helpers/__init__.py | 2 +- .../modules/testing/tests/helpers/utils.py | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/__init__.py b/deployability/modules/testing/tests/helpers/__init__.py index 6249db074a..f0b9ffca40 100755 --- a/deployability/modules/testing/tests/helpers/__init__.py +++ b/deployability/modules/testing/tests/helpers/__init__.py @@ -2,6 +2,6 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -from .agent import WazuhAgent from .generic import HostConfiguration, HostInformation, HostMonitor, CheckFiles +from .agent import WazuhAgent from .manager import WazuhManager diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index 2e133dc0f5..dbf73e34af 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -10,7 +10,6 @@ import winrm from modules.testing.utils import logger -from .generic import HostInformation paramiko_logger = logging.getLogger("paramiko") paramiko_logger.setLevel(logging.CRITICAL) @@ -48,7 +47,19 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: password = inventory_data.get('ansible_password', None) - os_type = HostInformation.get_os_type(inventory_path) + try: + with open(inventory_path.replace('inventory', 'track'), 'r') as file: + data = yaml.safe_load(file) + if 'platform' in data: + os_type = data['platform'] + else: + raise KeyError("The 'platform' key was not found in the YAML file.") + except FileNotFoundError: + logger.error(f"The YAML file '{inventory_path}' was not found.") + except yaml.YAMLError as e: + logger.error(f"Error while loading the YAML file: {e}") + except Exception as e: + logger.error(f"An unexpected error occurred: {e}") if os_type == 'linux': ssh = paramiko.SSHClient() From abccdeaa9fa1ab5803cafc6d3d753d419d0e2133 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 18:09:01 +0200 Subject: [PATCH 088/195] fix(#5219): Port tests fixed --- .../testing/tests/helpers/dashboard.py | 7 ++--- .../modules/testing/tests/helpers/executor.py | 2 +- .../modules/testing/tests/helpers/indexer.py | 3 ++- .../modules/testing/tests/helpers/manager.py | 9 ++++--- .../testing/tests/test_agent/test_install.py | 3 ++- .../tests/test_agent/test_uninstall.py | 4 +-- .../test_central_components/test_install.py | 7 ++--- .../test_central_components/test_stop.py | 4 +-- .../test_central_components/test_uninstall.py | 4 +-- .../tests/test_manager/test_install.py | 3 ++- .../tests/test_manager/test_uninstall.py | 4 +-- .../dtt1-central_components-poc-vagrant.yaml | 26 +++++++++---------- 12 files changed, 40 insertions(+), 36 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/dashboard.py b/deployability/modules/testing/tests/helpers/dashboard.py index a02be6e577..0348f49829 100644 --- a/deployability/modules/testing/tests/helpers/dashboard.py +++ b/deployability/modules/testing/tests/helpers/dashboard.py @@ -3,15 +3,11 @@ # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 import requests -import socket import json import time -from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT from .executor import ConnectionManager, WazuhAPI -from .generic import HostInformation, CheckFiles from modules.testing.utils import logger -from .utils import Utils class WazuhDashboard: @@ -96,7 +92,8 @@ def is_dashboard_port_open(inventory_path, wait=10, cycles=50): """ wait_cycles = 0 while wait_cycles < cycles: - ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":443"').get('output').strip().split('\n') + ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":443"').get('output') or "" + ports = ports.strip().split('\n') for port in ports: if any(state in port for state in ['ESTAB', 'LISTEN']): continue diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index edeb0b6a25..843704e630 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -149,7 +149,7 @@ def __init__(self, inventory_path, component=None): def _extract_password(self, file_path, keyword): if not 'true' in ConnectionManager.execute_commands(self.inventory_path, f'test -f {file_path} && echo "true" || echo "false"').get('output'): ConnectionManager.execute_commands(self.inventory_path, 'tar -xvf wazuh-install-files.tar') - return ConnectionManager.execute_command(self.inventory_path, f"grep {keyword} {file_path} | head -n 1 | awk '{{print $NF}}'").get('output').replace("'", "").replace("\n", "") + return ConnectionManager.execute_commands(self.inventory_path, f"grep {keyword} {file_path} | head -n 1 | awk '{{print $NF}}'").get('output').replace("'", "").replace("\n", "") def _authenticate(self): with open(self.inventory_path, 'r') as yaml_file: diff --git a/deployability/modules/testing/tests/helpers/indexer.py b/deployability/modules/testing/tests/helpers/indexer.py index fd674c4f42..a790b9236d 100644 --- a/deployability/modules/testing/tests/helpers/indexer.py +++ b/deployability/modules/testing/tests/helpers/indexer.py @@ -97,7 +97,8 @@ def is_indexer_port_open(inventory_path, wait=10, cycles=50) -> bool: """ wait_cycles = 0 while wait_cycles < cycles: - ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":9200"').get('output').strip().split('\n') + ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":9200"').get('output') or "" + ports = ports.strip().split('\n') for port in ports: if any(state in port for state in ['ESTAB', 'LISTEN']): continue diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index a46fa7a7e5..96614dc1cb 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -235,7 +235,8 @@ def is_wazuh_api_port_open(inventory_path, wait=10, cycles=50) -> bool: """ wait_cycles = 0 while wait_cycles < cycles: - ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":55000"').get('output').strip().split('\n') + ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":443"').get('output') or "" + ports = ports.strip().split('\n') for port in ports: if any(state in port for state in ['ESTAB', 'LISTEN']): continue @@ -259,7 +260,8 @@ def is_wazuh_agent_port_open(inventory_path, wait=10, cycles=50) -> bool: """ wait_cycles = 0 while wait_cycles < cycles: - ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":1514"').get('output').strip().split('\n') + ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":1514"').get('output') or "" + ports = ports.strip().split('\n') for port in ports: if any(state in port for state in ['ESTAB', 'LISTEN']): continue @@ -283,7 +285,8 @@ def is_wazuh_agent_enrollment_port_open(inventory_path, wait=10, cycles=50) -> b """ wait_cycles = 0 while wait_cycles < cycles: - ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":443"').get('output').strip().split('\n') + ports = ConnectionManager.execute_commands(inventory_path, 'ss -t -a -n | grep ":443"').get('output') or "" + ports = ports.strip().split('\n') for port in ports: if any(state in port for state in ['ESTAB', 'LISTEN']): continue diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index b0f1c687f8..afd65f13f6 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -82,7 +82,8 @@ def test_installation(wazuh_params): # Agent installation for agent_name, agent_params in wazuh_params['agents'].items(): - WazuhAgent.perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) + #WazuhAgent.perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) + WazuhAgent.install_agent(agent_params, agent_name, wazuh_params['wazuh_version'], wazuh_params['wazuh_revision'], wazuh_params['live']) # Testing installation directory for agent in wazuh_params['agents'].values(): diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index 673ee9f6d7..2abea5d57c 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -79,8 +79,8 @@ def test_uninstall(wazuh_params): # Agent uninstallation for agent_names, agent_params in wazuh_params['agents'].items(): - WazuhAgent.perform_uninstall_and_scan_for_agent(agent_params,wazuh_params) - + #WazuhAgent.perform_uninstall_and_scan_for_agent(agent_params,wazuh_params) + WazuhAgent.uninstall_agent(agent_params, wazuh_params['wazuh_version'], wazuh_params['wazuh_revision']) # Manager uninstallation status check for agent_names, agent_params in wazuh_params['agents'].items(): diff --git a/deployability/modules/testing/tests/test_central_components/test_install.py b/deployability/modules/testing/tests/test_central_components/test_install.py index eb160c9046..5be509c8f7 100644 --- a/deployability/modules/testing/tests/test_central_components/test_install.py +++ b/deployability/modules/testing/tests/test_central_components/test_install.py @@ -53,7 +53,7 @@ def setup_test_environment(wazuh_params): def test_installation(wazuh_params): # Disabling firewall for all managers - for manager_name, manager_params in wazuh_params['managers'].items(): + for _, manager_params in wazuh_params['managers'].items(): Utils.check_inventory_connection(manager_params) HostConfiguration.disable_firewall(manager_params) @@ -61,8 +61,9 @@ def test_installation(wazuh_params): HostConfiguration.certs_create(wazuh_params['wazuh_version'], wazuh_params['master'], wazuh_params['dashboard'], wazuh_params['indexers'], wazuh_params['workers']) # Install central components and perform checkfile testing - for manager_name, manager_params in wazuh_params['managers'].items(): - WazuhCentralComponents.perform_install_and_scan_for_aio(manager_params, wazuh_params) + for _, manager_params in wazuh_params['managers'].items(): + #WazuhCentralComponents.perform_install_and_scan_for_aio(manager_params, wazuh_params) + WazuhCentralComponents.install_aio(manager_params, wazuh_params['wazuh_version']) # Validation of directory of the components for manager in wazuh_params['managers'].values(): diff --git a/deployability/modules/testing/tests/test_central_components/test_stop.py b/deployability/modules/testing/tests/test_central_components/test_stop.py index e99b1a9353..74059d87ed 100644 --- a/deployability/modules/testing/tests/test_central_components/test_stop.py +++ b/deployability/modules/testing/tests/test_central_components/test_stop.py @@ -86,11 +86,11 @@ def test_manager_api_port(wazuh_params): def test_manager_agent_port(wazuh_params): - assert not WazuhManager.is_wazuh_agent_port_open(wazuh_params['master'], cycles=1, wait=1), logger.error(f"The Wazuh manager API port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is still active") + assert not WazuhManager.is_wazuh_agent_port_open(wazuh_params['master'], cycles=1, wait=1), logger.error(f"The Wazuh manager port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is still active") def test_manager_agent_enrollment_port(wazuh_params): - assert not WazuhManager.is_wazuh_agent_enrollment_port_open(wazuh_params['master'], cycles=1, wait=1), logger.error(f"The Wazuh manager API port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is still active") + assert not WazuhManager.is_wazuh_agent_enrollment_port_open(wazuh_params['master'], cycles=1, wait=1), logger.error(f"The Wazuh manager agent enrollment port in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is still active") def test_dashboard_port(wazuh_params): diff --git a/deployability/modules/testing/tests/test_central_components/test_uninstall.py b/deployability/modules/testing/tests/test_central_components/test_uninstall.py index 82d7ab2099..8129d056e5 100644 --- a/deployability/modules/testing/tests/test_central_components/test_uninstall.py +++ b/deployability/modules/testing/tests/test_central_components/test_uninstall.py @@ -56,8 +56,8 @@ def test_uninstall(wazuh_params): assert 'active' in GeneralComponentActions.get_component_status(indexer_params, 'wazuh-indexer'), logger.error(f'The indexer in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)} is not active') assert 'active' in GeneralComponentActions.get_component_status(wazuh_params['master'], 'filebeat'), logger.error(f'The filebeat in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])} is not active') - WazuhCentralComponents.perform_uninstall_and_scan_for_aio(wazuh_params['master']) - + #WazuhCentralComponents.perform_uninstall_and_scan_for_aio(wazuh_params['master']) + WazuhCentralComponents.uninstall_aio(wazuh_params['master']) def test_component_uninstalled_directory(wazuh_params): assert not HostInformation.dir_exists(wazuh_params['master'], WAZUH_ROOT), logger.error(f"In {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} {WAZUH_ROOT} is still present") diff --git a/deployability/modules/testing/tests/test_manager/test_install.py b/deployability/modules/testing/tests/test_manager/test_install.py index 28b346f986..7735ec648d 100644 --- a/deployability/modules/testing/tests/test_manager/test_install.py +++ b/deployability/modules/testing/tests/test_manager/test_install.py @@ -61,7 +61,8 @@ def test_installation(wazuh_params): # Install managers and perform checkfile testing for manager_name, manager_params in wazuh_params['managers'].items(): - WazuhManager.perform_install_and_scan_for_manager(manager_params, manager_name, wazuh_params) + #WazuhManager.perform_install_and_scan_for_manager(manager_params, manager_name, wazuh_params) + WazuhManager.install_manager(manager_params, manager_name, wazuh_params['wazuh_version']) # Validation of activity and directory of the managers for manager in wazuh_params['managers'].values(): diff --git a/deployability/modules/testing/tests/test_manager/test_uninstall.py b/deployability/modules/testing/tests/test_manager/test_uninstall.py index 7554d5a7b8..0b0d88c15a 100644 --- a/deployability/modules/testing/tests/test_manager/test_uninstall.py +++ b/deployability/modules/testing/tests/test_manager/test_uninstall.py @@ -52,8 +52,8 @@ def test_uninstall(wazuh_params): manager_status = GeneralComponentActions.get_component_status(manager, 'wazuh-manager') assert 'active' in manager_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(manager)} is not active') for _, manager_params in wazuh_params['managers'].items(): - WazuhManager.perform_uninstall_and_scan_for_manager(manager_params) - + #WazuhManager.perform_uninstall_and_scan_for_manager(manager_params) + WazuhManager.uninstall_manager(manager_params) def test_manager_uninstalled_directory(wazuh_params): for manager in wazuh_params['managers'].values(): diff --git a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml index c420b94181..952dcfc953 100644 --- a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml @@ -2,18 +2,18 @@ version: 0.1 description: This workflow is used to test the Wazuh manager deployment for DDT1 PoC variables: central_components-os: - #- linux-ubuntu-22.04-amd64 - #- linux-ubuntu-22.04-amd64 - #- linux-oracle-9-amd64 - #- linux-amazon-2-amd64 - #- linux-redhat-7-amd64 - #- linux-redhat-8-amd64 - #- linux-redhat-9-amd64 + - linux-ubuntu-22.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 - linux-centos-7-amd64 - #- linux-centos-8-amd64 - #- linux-debian-10-amd64 - #- linux-debian-11-amd64 - #- linux-debian-12-amd64 + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 infra-provider: vagrant working-dir: /tmp/dtt1-poc @@ -51,8 +51,8 @@ tasks: - wazuh-1: "{working-dir}/central_components-{central_components}/inventory.yaml" - tests: "install,restart,stop,uninstall" - component: "central_components" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" on-error: "abort-all" foreach: From c1a494d931cc256cc66927bd9b101b4df28b7612 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Thu, 2 May 2024 14:57:48 -0300 Subject: [PATCH 089/195] MacOS Intel machine deployments are updated --- .../modules/allocation/static/specs/os.yml | 6 +++++ ...t_black_mini.j2 => vagrant_Virtual_box.j2} | 4 ---- ...macStadium.j2 => vagrant_parallels_arm.j2} | 0 .../templates/vagrant_parallels_intel.j2 | 19 +++++++++++++++ .../vagrant/helpers/vagrant_script.sh | 23 +++++++++++++++++++ .../modules/allocation/vagrant/instance.py | 13 ++++------- .../modules/allocation/vagrant/provider.py | 12 +++++++--- 7 files changed, 62 insertions(+), 15 deletions(-) rename deployability/modules/allocation/static/templates/{vagrant_black_mini.j2 => vagrant_Virtual_box.j2} (71%) rename deployability/modules/allocation/static/templates/{vagrant_macStadium.j2 => vagrant_parallels_arm.j2} (100%) mode change 100755 => 100644 create mode 100755 deployability/modules/allocation/static/templates/vagrant_parallels_intel.j2 create mode 100644 deployability/modules/allocation/vagrant/helpers/vagrant_script.sh diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 1e49ce4db3..05b36a2ce6 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -126,9 +126,15 @@ vagrant: macos-monterey-12.0.1-amd64: box: development/macos-monterey box_version: 0.0.0 + macos-ventura-13.4.1-amd64: + box: development/macos-ventura + box_version: 0.0.0 macos-ventura-13.4.1-arm64: box: macos-13 box_version: 0.0.0 + macos-sonoma-14.4.1-amd64: + box: development/macos-sonoma + box_version: 0.0.0 macos-sonoma-14.0-arm64: box: macos-14 box_version: 0.0.0 diff --git a/deployability/modules/allocation/static/templates/vagrant_black_mini.j2 b/deployability/modules/allocation/static/templates/vagrant_Virtual_box.j2 similarity index 71% rename from deployability/modules/allocation/static/templates/vagrant_black_mini.j2 rename to deployability/modules/allocation/static/templates/vagrant_Virtual_box.j2 index 870e3ea9ea..4643206504 100644 --- a/deployability/modules/allocation/static/templates/vagrant_black_mini.j2 +++ b/deployability/modules/allocation/static/templates/vagrant_Virtual_box.j2 @@ -3,10 +3,6 @@ Vagrant.configure("2") do |config| config.vm.network :private_network, type: "dhcp" config.vm.network "forwarded_port", guest: 22, host: "{{ config.port }}" - config.vm.provision "file", source: "~/.ssh/vagrant_rsa.pub", destination: "~/.ssh/vagrant_rsa.pub" - config.vm.provision "file", source: "~/.ssh/authorized_keys", destination: "~/.ssh/authorized_keys" - - config.vm.synced_folder ".", "/vagrant", disabled: true config.vm.provider "virtualbox" do |vb| diff --git a/deployability/modules/allocation/static/templates/vagrant_macStadium.j2 b/deployability/modules/allocation/static/templates/vagrant_parallels_arm.j2 old mode 100755 new mode 100644 similarity index 100% rename from deployability/modules/allocation/static/templates/vagrant_macStadium.j2 rename to deployability/modules/allocation/static/templates/vagrant_parallels_arm.j2 diff --git a/deployability/modules/allocation/static/templates/vagrant_parallels_intel.j2 b/deployability/modules/allocation/static/templates/vagrant_parallels_intel.j2 new file mode 100755 index 0000000000..6d539bef36 --- /dev/null +++ b/deployability/modules/allocation/static/templates/vagrant_parallels_intel.j2 @@ -0,0 +1,19 @@ +Vagrant.configure("2") do |config| + # Box image settings + config.vm.box = "{{ config.box }}" + + # VirtualBox specific settings + config.vm.provider "parallels" do |v| + v.memory = "4096" + v.cpus = "2" + v.name = "{{ config.name }}" + v.linked_clone = false + end + + # Network settings + #config.vm.network :private_network, type: "dhcp" + config.ssh.forward_agent = true + config.vm.network "forwarded_port", guest: 22, host: "{{ config.port }}" + #config.vm.network "public_network", bridge: "en0" + config.vm.synced_folder ".", "/vagrant", disabled: true +end diff --git a/deployability/modules/allocation/vagrant/helpers/vagrant_script.sh b/deployability/modules/allocation/vagrant/helpers/vagrant_script.sh new file mode 100644 index 0000000000..f91a83d611 --- /dev/null +++ b/deployability/modules/allocation/vagrant/helpers/vagrant_script.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Set the path to Vagrant directory +VAGRANT_DIR="/usr/local/bin" +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" + +# Add Vagrant directory to PATH +export PATH=$PATH:$VAGRANT_DIR + +# Check if an argument is provided +if [ $# -eq 0 ] +then + echo "Usage: $0 [up | destroy | status | ...]" + exit 1 +fi + +if [ $1 == "destroy" ]; then + VAGRANT_CWD=$SCRIPT_DIR vagrant $1 -f +else + VAGRANT_CWD=$SCRIPT_DIR vagrant $1 +fi + +exit 0 \ No newline at end of file diff --git a/deployability/modules/allocation/vagrant/instance.py b/deployability/modules/allocation/vagrant/instance.py index f5bb5bfedc..4351b74f08 100755 --- a/deployability/modules/allocation/vagrant/instance.py +++ b/deployability/modules/allocation/vagrant/instance.py @@ -105,13 +105,10 @@ def delete(self) -> None: if str(self.host_identifier) == "macstadium": logger.debug(f"Deleting remote directory {self.host_instance_dir}") VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) - logger.debug(f"Killing remote process on port {self.ssh_port}") - proccess = VagrantUtils.remote_command(f"sudo lsof -Pi :{self.ssh_port} -sTCP:LISTEN -t", self.remote_host_parameters) - VagrantUtils.remote_command(f"sudo kill -9 {proccess}", self.remote_host_parameters) - if str(self.host_identifier) == "black_mini": - logger.debug(f"Deleting remote directory {self.host_instance_dir}") - VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) - + if self.arch == 'arm64': + logger.debug(f"Killing remote process on port {self.ssh_port}") + proccess = VagrantUtils.remote_command(f"sudo lsof -Pi :{self.ssh_port} -sTCP:LISTEN -t", self.remote_host_parameters) + VagrantUtils.remote_command(f"sudo kill -9 {proccess}", self.remote_host_parameters) def status(self) -> str: """ @@ -209,7 +206,7 @@ def __run_vagrant_command(self, command: str | list) -> str: if isinstance(command, str): command = [command] if self.platform == 'macos': - cmd = f"sudo VAGRANT_CWD={self.host_instance_dir} /usr/local/bin/vagrant " + ' '.join(command) + cmd = f"sudo {self.host_instance_dir}/vagrant_script.sh " + ' '.join(command) output = VagrantUtils.remote_command(cmd, self.remote_host_parameters) return output else: diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 007eee5d50..89a1f41c98 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -92,6 +92,9 @@ def _create_instance(cls, base_dir: Path, params: CreationPayload, config: Vagra if platform == 'macos': vagrant_file = str(instance_dir) + '/Vagrantfile' VagrantUtils.remote_copy(vagrant_file, host_instance_dir, remote_host_parameters) + VagrantUtils.remote_copy(Path(__file__).parent.parent / 'vagrant' / 'helpers' / 'vagrant_script.sh', host_instance_dir, remote_host_parameters) + cmd = f"chmod 700 {host_instance_dir}/vagrant_script.sh" + VagrantUtils.remote_command(cmd, remote_host_parameters) instance_params = {} instance_params['instance_dir'] = instance_dir @@ -185,10 +188,13 @@ def __render_vagrantfile(cls, config: VagrantConfig) -> str: """ environment = Environment(loader=FileSystemLoader(cls.TEMPLATES_DIR)) if config.platform == 'macos': - if config.arch == 'arm64': - template = environment.get_template("vagrant_macStadium.j2") + if config.arch == 'amd64': + if config.box != 'development/macos-high-sierra' and config.box != 'development/macos-mojave' and config.box != 'development/macos-sierra' and config.box != 'development/macos-sierra_cmake': + template = environment.get_template("vagrant_parallels_intel.j2") + else: + template = environment.get_template("vagrant_Virtual_box.j2") else: - template = environment.get_template("vagrant_black_mini.j2") + template = environment.get_template("vagrant_parallels_arm.j2") else: template = environment.get_template("vagrant.j2") return template.render(config=config) From adf78603c16f45e8594e340c097cf36e61a1c40f Mon Sep 17 00:00:00 2001 From: c-bordon Date: Thu, 2 May 2024 15:40:33 -0300 Subject: [PATCH 090/195] macOS sign box added to Allocation module --- deployability/modules/allocation/static/specs/os.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 1e49ce4db3..b746797d05 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -132,6 +132,9 @@ vagrant: macos-sonoma-14.0-arm64: box: macos-14 box_version: 0.0.0 + macos-ventura-sign-arm64: + box: macos-ventura-sign + box_version: 0.0.0 # Windows windows-desktop-10-amd64: box: gusztavvargadr/windows-10 From 47a2c61acb919f59a589f56c95129c8abc1c71df Mon Sep 17 00:00:00 2001 From: c-bordon Date: Thu, 2 May 2024 15:45:24 -0300 Subject: [PATCH 091/195] Removed commented configurations in intel vagrantfile --- .../allocation/static/templates/vagrant_parallels_intel.j2 | 2 -- .../modules/allocation/vagrant/helpers/vagrant_script.sh | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/deployability/modules/allocation/static/templates/vagrant_parallels_intel.j2 b/deployability/modules/allocation/static/templates/vagrant_parallels_intel.j2 index 6d539bef36..1af532acd1 100755 --- a/deployability/modules/allocation/static/templates/vagrant_parallels_intel.j2 +++ b/deployability/modules/allocation/static/templates/vagrant_parallels_intel.j2 @@ -11,9 +11,7 @@ Vagrant.configure("2") do |config| end # Network settings - #config.vm.network :private_network, type: "dhcp" config.ssh.forward_agent = true config.vm.network "forwarded_port", guest: 22, host: "{{ config.port }}" - #config.vm.network "public_network", bridge: "en0" config.vm.synced_folder ".", "/vagrant", disabled: true end diff --git a/deployability/modules/allocation/vagrant/helpers/vagrant_script.sh b/deployability/modules/allocation/vagrant/helpers/vagrant_script.sh index f91a83d611..236c77606f 100644 --- a/deployability/modules/allocation/vagrant/helpers/vagrant_script.sh +++ b/deployability/modules/allocation/vagrant/helpers/vagrant_script.sh @@ -10,8 +10,8 @@ export PATH=$PATH:$VAGRANT_DIR # Check if an argument is provided if [ $# -eq 0 ] then - echo "Usage: $0 [up | destroy | status | ...]" - exit 1 + echo "Usage: $0 [up | destroy | status | ...]" + exit 1 fi if [ $1 == "destroy" ]; then @@ -20,4 +20,4 @@ else VAGRANT_CWD=$SCRIPT_DIR vagrant $1 fi -exit 0 \ No newline at end of file +exit 0 From 7534151c68096d5e2d20f068676db169b5205ecb Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 20:52:17 +0200 Subject: [PATCH 092/195] fix(#5219): os_version fixed --- .../modules/testing/tests/helpers/generic.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 929c3bcdd2..2d402bc6ae 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -198,19 +198,18 @@ def get_os_version_from_inventory(inventory_path) -> str: Returns: str: os version """ - os_type = HostInformation.get_os_type(inventory_path) - + logger.error(inventory_path) if 'manager' in inventory_path: - os_version = re.search(r".*?/manager-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) + match = re.search(r".*?/manager-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path) elif 'agent' in inventory_path: - os_version = re.search(r".*?/agent-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) + match = re.search(r".*?/agent-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path) elif 'central_components' in inventory_path: - os_version = re.search(r".*?/central_components-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1) - return os_version + match = re.search(r".*?/central_components-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path) + if match: + return match.group(1) else: return None - @staticmethod def get_current_dir(inventory_path) -> str: """ From ca276c8cc80d4cbd1f237aa8d41227fca3dcf48e Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 20:54:54 +0200 Subject: [PATCH 093/195] fix(#5219): additional regex fixes --- .../modules/testing/tests/helpers/generic.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 2d402bc6ae..94c82e5983 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -155,13 +155,15 @@ def get_os_name_from_inventory(inventory_path) -> str: str: linux os name (debian, ubuntu, opensuse, amazon, centos, redhat) """ if 'manager' in inventory_path: - os_name = re.search(r'/manager-[^-]+-([^-]+)-', inventory_path).group(1) + match = re.search(r'/manager-[^-]+-([^-]+)-', inventory_path) elif 'agent' in inventory_path: - os_name = re.search(r'/agent-[^-]+-([^-]+)-', inventory_path).group(1) + match = re.search(r'/agent-[^-]+-([^-]+)-', inventory_path) elif 'central_components' in inventory_path: - os_name = re.search(r'/central_components-[^-]+-([^-]+)-', inventory_path).group(1) - - return os_name + match = re.search(r'/central_components-[^-]+-([^-]+)-', inventory_path) + if match: + return match.group(1) + else: + return None @staticmethod def get_os_name_and_version_from_inventory(inventory_path) -> tuple: From 0250f72b34eb7bea21210c39cbbc947e9f8a3307 Mon Sep 17 00:00:00 2001 From: Antonio Date: Thu, 2 May 2024 21:22:21 +0200 Subject: [PATCH 094/195] fix(#5219): Removing debug log --- deployability/modules/testing/tests/helpers/generic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 94c82e5983..747e668d2d 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -200,7 +200,6 @@ def get_os_version_from_inventory(inventory_path) -> str: Returns: str: os version """ - logger.error(inventory_path) if 'manager' in inventory_path: match = re.search(r".*?/manager-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path) elif 'agent' in inventory_path: From d3a2c09d2504cfc5e26fc9be6939934cae56ddbe Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 3 May 2024 09:38:33 +0200 Subject: [PATCH 095/195] fix(#5229): Fixes after review, repeated tests removed, depends on windows test added --- .../agent/aws/test-agent-complete-macOs.yaml | 115 ------------------ .../aws/test-agent-windows-complete.yaml | 1 + .../vagrant/test-agent-complete-macOS.yaml | 0 .../vagrant/test-agent-complete-macOs.yaml | 113 ----------------- 4 files changed, 1 insertion(+), 228 deletions(-) delete mode 100755 deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml mode change 100644 => 100755 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml delete mode 100755 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml deleted file mode 100755 index 2ae2d26e5f..0000000000 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml +++ /dev/null @@ -1,115 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents' deployment for DDT1 PoC -variables: - agent-os: - # Run one at a time as there are limitations on the number of hosts (Inform @dev-devops-team about your usage, dedicated hosts) - - macos-sonoma-14.3-arm64 - #- macos-ventura-13.6.4-arm64 - #- macos-sonoma-14.3-amd64 - #- macos-ventura-13.6.4-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: aws - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - depends-on: - - "provision-manager-{manager-os}" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - on-error: "abort-all" - - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml index c65d56e65e..a7356ee47e 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml @@ -117,3 +117,4 @@ tasks: as: agent depends-on: - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml old mode 100644 new mode 100755 diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml deleted file mode 100755 index d1e35d3e71..0000000000 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml +++ /dev/null @@ -1,113 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents' deployment for DDT1 PoC -variables: - agent-os: - # Run one at a time as there are limitations on the number of hosts - - macos-sonoma-14.0-arm64 - #- macos-ventura-13.4.1-arm64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "aws" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - depends-on: - - "provision-manager-{manager-os}" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - on-error: "abort-all" - - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "allocate-agent-{agent}" From 8447966eb591384bd32edf0e2a8a97a5cc34e6f8 Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 3 May 2024 10:08:19 +0200 Subject: [PATCH 096/195] fix(#5229): macOS AWS connection fix --- .../modules/testing/tests/helpers/executor.py | 54 +++++++++---- .../modules/testing/tests/helpers/utils.py | 80 ++++++++++--------- 2 files changed, 80 insertions(+), 54 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index b288cef4bd..609a431098 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -112,25 +112,49 @@ def _execute_command(data, command) -> dict: class MacosExecutor(): @staticmethod def _execute_command(data, command) -> dict: - try: - ssh_client = paramiko.SSHClient() - ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh_client.connect(hostname=data.get('host'), port=data.get('port'), username=data.get('username'), password=data.get('password')) - stdin, stdout, stderr = ssh_client.exec_command(f"sudo {command}") + if data.get('private_key_path') == None: + try: + ssh_client = paramiko.SSHClient() + ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh_client.connect(hostname=data.get('host'), port=data.get('port'), username=data.get('username'), password=data.get('password')) + stdin, stdout, stderr = ssh_client.exec_command(f"sudo {command}") - stdout_str = ''.join(stdout.readlines()) - stderr_str = ''.join(stderr.readlines()) + stdout_str = ''.join(stdout.readlines()) + stderr_str = ''.join(stderr.readlines()) - ssh_client.close() + ssh_client.close() - if stdout_str: - return {'success': True, 'output': stdout_str.replace('\n', '')} - if stderr_str: - return {'success': False, 'output': stderr_str.replace('\n', '')} - return {'success': False, 'output': None} + if stdout_str: + return {'success': True, 'output': stdout_str.replace('\n', '')} + if stderr_str: + return {'success': False, 'output': stderr_str.replace('\n', '')} + return {'success': False, 'output': None} - except Exception as e: - raise Exception(f'Error executing command: {command} with error: {e}') + except Exception as e: + raise Exception(f'Error executing command: {command} with error: {e}') + else: + ssh_command = [ + "ssh", + "-i", data.get('private_key_path'), + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-p", str(data.get('port')), + f"{data.get('username')}@{data.get('host')}", + "sudo", + command + ] + + try: + ret = subprocess.run(ssh_command, stdout=subprocess.PIPE, text=True) + if ret.stdout: + return {'success': True, 'output': ret.stdout.replace('\n', '')} + if ret.stderr: + return {'success': False, 'output': ret.stderr.replace('\n', '')} + return {'success': False, 'output': None} + + except Exception as e: + #return {'success': False, 'output': ret.stderr} + raise Exception(f'Error executing command: {command} with error: {e}') # ------------------------------------------------------ diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index dbf73e34af..b1bbccadb0 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -29,6 +29,8 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: match = re.search(r'/manager-[^-]+-([^-]+)-([^-]+)-', inventory_path) elif 'agent' in inventory_path: match = re.search(r'/agent-[^-]+-([^-]+)-([^-]+)-', inventory_path) + elif 'central_components' in inventory_path: + match = re.search(r'/central_components-[^-]+-([^-]+)-([^-]+)-', inventory_path) if match: os_name = match.group(1)+ '-' + match.group(2) logger.info(f'Checking connection to {os_name}') @@ -46,7 +48,6 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: username = inventory_data.get('ansible_user') password = inventory_data.get('ansible_password', None) - try: with open(inventory_path.replace('inventory', 'track'), 'r') as file: data = yaml.safe_load(file) @@ -61,7 +62,7 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: except Exception as e: logger.error(f"An unexpected error occurred: {e}") - if os_type == 'linux': + if private_key_path != None: ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) private_key = paramiko.RSAKey.from_private_key_file(private_key_path) @@ -82,45 +83,46 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') time.sleep(sleep) - elif os_type == 'windows': - if port == 5986: - protocol = 'https' - else: - protocol = 'http' - endpoint_url = f'{protocol}://{host}:{port}' + else: + if os_type == 'windows': + if port == 5986: + protocol = 'https' + else: + protocol = 'http' + endpoint_url = f'{protocol}://{host}:{port}' + + for attempt in range(1, attempts + 1): + try: + session = winrm.Session(endpoint_url, auth=(username, password),transport='ntlm', server_cert_validation='ignore') + cmd = session.run_cmd('ipconfig') + if cmd.status_code == 0: + logger.info("WinRM connection successful.") + return True + else: + logger.error('WinRM connection failed. Check the credentials in the inventory file.') + return False + except Exception as e: + logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') + time.sleep(sleep) + + elif os_type == 'macos': + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - for attempt in range(1, attempts + 1): - try: - session = winrm.Session(endpoint_url, auth=(username, password),transport='ntlm', server_cert_validation='ignore') - cmd = session.run_cmd('ipconfig') - if cmd.status_code == 0: - logger.info("WinRM connection successful.") + for attempt in range(1, attempts + 1): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + ssh.connect(hostname=host, port=port, username=username, password=password) + logger.info(f'Connection established successfully in {os_name}') + ssh.close() return True - else: - logger.error('WinRM connection failed. Check the credentials in the inventory file.') + except paramiko.AuthenticationException: + logger.error(f'Authentication error. Check SSH credentials in {os_name}') return False - except Exception as e: - logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') - time.sleep(sleep) - - elif os_type == 'macos': - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - for attempt in range(1, attempts + 1): - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - try: - ssh.connect(hostname=host, port=port, username=username, password=password) - logger.info(f'Connection established successfully in {os_name}') - ssh.close() - return True - except paramiko.AuthenticationException: - logger.error(f'Authentication error. Check SSH credentials in {os_name}') - return False - except Exception as e: - logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') - time.sleep(sleep) + except Exception as e: + logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') + time.sleep(sleep) logger.error(f'Connection attempts failed after {attempts} tries. Connection timeout in {os_name}') - return False + return False \ No newline at end of file From f8484fd5773193e603ec45b44e0a55639092263e Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 3 May 2024 10:13:41 +0200 Subject: [PATCH 097/195] fix(#5229): Adding white spaces --- deployability/modules/testing/tests/helpers/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index b1bbccadb0..3a78cfb739 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -125,4 +125,4 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: time.sleep(sleep) logger.error(f'Connection attempts failed after {attempts} tries. Connection timeout in {os_name}') - return False \ No newline at end of file + return False From 5829eb499e9c491cd7ae24c594f44f2421cd171c Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 3 May 2024 10:52:43 +0200 Subject: [PATCH 098/195] fix(#5219): version fixes in examples + ssh connection for aws macOS fixed --- .../modules/testing/tests/helpers/executor.py | 54 +++++--- .../modules/testing/tests/helpers/utils.py | 75 ++++++------ .../agent/aws/test-agent-complete-macOS.yaml | 6 +- .../agent/aws/test-agent-complete-macOs.yaml | 115 ------------------ .../agent/aws/test-agent-complete.yaml | 6 +- .../aws/test-agent-restart-ins-prov.yaml | 8 +- .../agent/aws/test-agent-stop-ins-prov.yaml | 8 +- .../examples/agent/aws/test-agent-suse.yaml | 6 +- .../aws/test-agent-uninstall-ins-prov.yaml | 8 +- .../aws/test-agent-windows-complete.yaml | 7 +- .../agent/aws/test-agent-windows-install.yaml | 4 +- .../agent/vagrant/test-agent-complete-1.yaml | 6 +- .../agent/vagrant/test-agent-complete-2.yaml | 6 +- .../vagrant/test-agent-complete-macOS.yaml | 6 +- .../vagrant/test-agent-complete-macOs.yaml | 113 ----------------- .../test-agent-restart-ins-prov-1.yaml | 8 +- .../test-agent-restart-ins-prov-2.yaml | 8 +- .../vagrant/test-agent-stop-ins-prov-1.yaml | 8 +- .../vagrant/test-agent-stop-ins-prov-2.yaml | 8 +- .../test-agent-uninstall-ins-prov-1.yaml | 8 +- .../test-agent-uninstall-ins-prov-2.yaml | 8 +- .../aws/dtt1-central_components-poc-aws.yaml | 4 +- .../dtt1-central_components-poc-vagrant.yaml | 13 +- .../manager/aws/dtt1-managers-poc-aws.yaml | 4 +- .../vagrant/dtt1-managers-poc-vagrant.yaml | 4 +- 25 files changed, 154 insertions(+), 347 deletions(-) delete mode 100755 deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml delete mode 100755 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index 843704e630..4f7b17d78b 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -112,25 +112,49 @@ def _execute_command(data, command) -> dict: class MacosExecutor(): @staticmethod def _execute_command(data, command) -> dict: - try: - ssh_client = paramiko.SSHClient() - ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh_client.connect(hostname=data.get('host'), port=data.get('port'), username=data.get('username'), password=data.get('password')) - stdin, stdout, stderr = ssh_client.exec_command(f"sudo {command}") + if data.get('private_key_path') == None: + try: + ssh_client = paramiko.SSHClient() + ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh_client.connect(hostname=data.get('host'), port=data.get('port'), username=data.get('username'), password=data.get('password')) + stdin, stdout, stderr = ssh_client.exec_command(f"sudo {command}") - stdout_str = ''.join(stdout.readlines()) - stderr_str = ''.join(stderr.readlines()) + stdout_str = ''.join(stdout.readlines()) + stderr_str = ''.join(stderr.readlines()) - ssh_client.close() + ssh_client.close() - if stdout_str: - return {'success': True, 'output': stdout_str.replace('\n', '')} - if stderr_str: - return {'success': False, 'output': stderr_str.replace('\n', '')} - return {'success': False, 'output': None} + if stdout_str: + return {'success': True, 'output': stdout_str.replace('\n', '')} + if stderr_str: + return {'success': False, 'output': stderr_str.replace('\n', '')} + return {'success': False, 'output': None} - except Exception as e: - raise Exception(f'Error executing command: {command} with error: {e}') + except Exception as e: + raise Exception(f'Error executing command: {command} with error: {e}') + else: + ssh_command = [ + "ssh", + "-i", data.get('private_key_path'), + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-p", str(data.get('port')), + f"{data.get('username')}@{data.get('host')}", + "sudo", + command + ] + + try: + ret = subprocess.run(ssh_command, stdout=subprocess.PIPE, text=True) + if ret.stdout: + return {'success': True, 'output': ret.stdout.replace('\n', '')} + if ret.stderr: + return {'success': False, 'output': ret.stderr.replace('\n', '')} + return {'success': False, 'output': None} + + except Exception as e: + #return {'success': False, 'output': ret.stderr} + raise Exception(f'Error executing command: {command} with error: {e}') # ------------------------------------------------------ diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py index 05e7622f75..3a78cfb739 100644 --- a/deployability/modules/testing/tests/helpers/utils.py +++ b/deployability/modules/testing/tests/helpers/utils.py @@ -62,7 +62,7 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: except Exception as e: logger.error(f"An unexpected error occurred: {e}") - if os_type == 'linux': + if private_key_path != None: ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) private_key = paramiko.RSAKey.from_private_key_file(private_key_path) @@ -83,45 +83,46 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool: logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') time.sleep(sleep) - elif os_type == 'windows': - if port == 5986: - protocol = 'https' - else: - protocol = 'http' - endpoint_url = f'{protocol}://{host}:{port}' + else: + if os_type == 'windows': + if port == 5986: + protocol = 'https' + else: + protocol = 'http' + endpoint_url = f'{protocol}://{host}:{port}' + + for attempt in range(1, attempts + 1): + try: + session = winrm.Session(endpoint_url, auth=(username, password),transport='ntlm', server_cert_validation='ignore') + cmd = session.run_cmd('ipconfig') + if cmd.status_code == 0: + logger.info("WinRM connection successful.") + return True + else: + logger.error('WinRM connection failed. Check the credentials in the inventory file.') + return False + except Exception as e: + logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') + time.sleep(sleep) + + elif os_type == 'macos': + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - for attempt in range(1, attempts + 1): - try: - session = winrm.Session(endpoint_url, auth=(username, password),transport='ntlm', server_cert_validation='ignore') - cmd = session.run_cmd('ipconfig') - if cmd.status_code == 0: - logger.info("WinRM connection successful.") + for attempt in range(1, attempts + 1): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + ssh.connect(hostname=host, port=port, username=username, password=password) + logger.info(f'Connection established successfully in {os_name}') + ssh.close() return True - else: - logger.error('WinRM connection failed. Check the credentials in the inventory file.') + except paramiko.AuthenticationException: + logger.error(f'Authentication error. Check SSH credentials in {os_name}') return False - except Exception as e: - logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') - time.sleep(sleep) - - elif os_type == 'macos': - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - for attempt in range(1, attempts + 1): - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - try: - ssh.connect(hostname=host, port=port, username=username, password=password) - logger.info(f'Connection established successfully in {os_name}') - ssh.close() - return True - except paramiko.AuthenticationException: - logger.error(f'Authentication error. Check SSH credentials in {os_name}') - return False - except Exception as e: - logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') - time.sleep(sleep) + except Exception as e: + logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') + time.sleep(sleep) logger.error(f'Connection attempts failed after {attempts} tries. Connection timeout in {os_name}') return False diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml index 1db2fb91cc..dd067ecbcd 100644 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml @@ -84,7 +84,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -105,8 +105,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml deleted file mode 100755 index 1db2fb91cc..0000000000 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOs.yaml +++ /dev/null @@ -1,115 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment for DDT1 PoC -variables: - agent-os: - # Run one at a time as there are limitations on the number of hosts (Inform @dev-devops-team about your usage, dedicated hosts) - - macos-sonoma-14.3-arm64 - #- macos-ventura-13.6.4-arm64 - #- macos-sonoma-14.3-amd64 - #- macos-ventura-13.6.4-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: aws - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - depends-on: - - "provision-manager-{manager-os}" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - on-error: "abort-all" - - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml index 8c21f1e8f4..6d5f0a247a 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml @@ -92,7 +92,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -112,8 +112,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "install,registration,connection,basic_info,restart,stop,uninstall" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml index cd12531247..bc1d4970e6 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml @@ -90,7 +90,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -111,7 +111,7 @@ tasks: - install: - component: wazuh-agent type: package - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-agent-{agent}" @@ -135,8 +135,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "restart" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml index c01800b3f5..08caa17057 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml @@ -90,7 +90,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -111,7 +111,7 @@ tasks: - install: - component: wazuh-agent type: package - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-agent-{agent}" @@ -135,8 +135,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "stop" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml index 5f2c1e1047..88eaaa4823 100644 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml @@ -82,7 +82,7 @@ tasks: - component: curl - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -124,8 +124,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml index 4cc042b41e..4c33dc5e45 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml @@ -90,7 +90,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -111,7 +111,7 @@ tasks: - install: - component: wazuh-agent type: package - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-agent-{agent}" @@ -135,8 +135,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "uninstall" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml index 9c820e127f..e657bbf8ad 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml @@ -89,7 +89,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -109,11 +109,12 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "install,registration,connection,basic_info,restart,stop,uninstall" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os as: agent depends-on: - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml index 9b237868aa..3ef28dac15 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml @@ -22,7 +22,7 @@ tasks: - agent: "{working-dir}/agent-windows-server-2016-amd64/inventory.yaml" - tests: "install" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml index 1b10763219..bee7abe6f2 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml @@ -85,7 +85,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -106,8 +106,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "install,registration,connection,basic_info,restart,stop,uninstall" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml index db0299dbb1..4c799c6530 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml @@ -85,7 +85,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -106,8 +106,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "install,registration,connection,basic_info,restart,stop,uninstall" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml index e5dbe718c9..202cda44bd 100644 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml @@ -82,7 +82,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -103,8 +103,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml deleted file mode 100755 index e5dbe718c9..0000000000 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOs.yaml +++ /dev/null @@ -1,113 +0,0 @@ -version: 0.1 -description: This workflow is used to test agents deployment for DDT1 PoC -variables: - agent-os: - # Run one at a time as there are limitations on the number of hosts - - macos-sonoma-14.0-arm64 - #- macos-ventura-13.4.1-arm64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Unique manager allocate task - - task: "allocate-manager-{manager-os}" - description: "Allocate resources for the manager." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "aws" - - size: large - - composite-name: "{manager-os}" - - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - - # Unique agent allocate task - - task: "allocate-agent-{agent}" - description: "Allocate resources for the agent." - do: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: create - - provider: "{infra-provider}" - - size: small - - composite-name: "{agent}" - - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - - track-output: "{working-dir}/agent-{agent}/track.yaml" - - label-termination-date: "1d" - - label-team: "qa" - on-error: "abort-all" - foreach: - - variable: agent-os - as: agent - cleanup: - this: process - with: - path: python3 - args: - - modules/allocation/main.py - - action: delete - - track-output: "{working-dir}/agent-{agent}/track.yaml" - depends-on: - - "provision-manager-{manager-os}" - - # Unique manager provision task - - task: "provision-manager-{manager-os}" - description: "Provision the manager." - do: - this: process - with: - path: python3 - args: - - modules/provision/main.py - - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - - install: - - component: wazuh-manager - type: assistant - version: 4.7.3 - live: True - depends-on: - - "allocate-manager-{manager-os}" - on-error: "abort-all" - - - # Generic agent test task - - task: "run-agent-{agent}-tests" - description: "Run tests install for the agent {agent}." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-{agent}/inventory.yaml" - - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" - - live: "True" - foreach: - - variable: agent-os - as: agent - depends-on: - - "allocate-agent-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml index 7a41aa7c4f..4402fc08ad 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml @@ -79,7 +79,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -100,7 +100,7 @@ tasks: - install: - component: wazuh-agent type: package - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-agent-{agent}" @@ -124,8 +124,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "restart" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml index 7c36f9c872..aecb476577 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml @@ -79,7 +79,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -100,7 +100,7 @@ tasks: - install: - component: wazuh-agent type: package - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-agent-{agent}" @@ -124,8 +124,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "restart" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml index 1f2330125b..9f1dc32ac9 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml @@ -79,7 +79,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -100,7 +100,7 @@ tasks: - install: - component: wazuh-agent type: package - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-agent-{agent}" @@ -124,8 +124,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "stop" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml index b09c24c05e..2b42d06698 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml @@ -79,7 +79,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -100,7 +100,7 @@ tasks: - install: - component: wazuh-agent type: package - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-agent-{agent}" @@ -124,8 +124,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "stop" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml index cb2dff4cb3..01c9f95c9c 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml @@ -79,7 +79,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -100,7 +100,7 @@ tasks: - install: - component: wazuh-agent type: package - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-agent-{agent}" @@ -124,8 +124,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "uninstall" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml index be28e87d6b..f917254e44 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml @@ -79,7 +79,7 @@ tasks: - install: - component: wazuh-manager type: assistant - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-manager-{manager-os}" @@ -100,7 +100,7 @@ tasks: - install: - component: wazuh-agent type: package - version: 4.7.3 + version: 4.7.4 live: True depends-on: - "allocate-agent-{agent}" @@ -124,8 +124,8 @@ tasks: - agent: "{working-dir}/agent-{agent}/inventory.yaml" - tests: "uninstall" - component: "agent" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" foreach: - variable: agent-os diff --git a/deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml b/deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml index 4f210e8b92..d5781b5538 100644 --- a/deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml @@ -60,8 +60,8 @@ tasks: - wazuh-1: "{working-dir}/central_components-{central_components}/inventory.yaml" - tests: "install,restart,stop,uninstall" - component: "central_components" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" on-error: "abort-all" foreach: diff --git a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml index 952dcfc953..dd18640ae6 100644 --- a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml @@ -2,9 +2,8 @@ version: 0.1 description: This workflow is used to test the Wazuh manager deployment for DDT1 PoC variables: central_components-os: + - linux-ubuntu-20.04-amd64 - linux-ubuntu-22.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-oracle-9-amd64 - linux-amazon-2-amd64 - linux-redhat-7-amd64 - linux-redhat-8-amd64 @@ -33,10 +32,20 @@ tasks: - composite-name: "{central_components}" - inventory-output: "{working-dir}/central_components-{central_components}/inventory.yaml" - track-output: "{working-dir}/central_components-{central_components}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" on-error: "abort-all" foreach: - variable: central_components-os as: central_components + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/central_components-{central_components-os}/track.yaml" # Generic manager test task - task: "run-central_components-{central_components}-tests" diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml index 2471660ebe..762609c97b 100644 --- a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml @@ -62,6 +62,6 @@ tasks: - wazuh-11: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" - tests: "install,restart,stop,uninstall" - component: "manager" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index 4b2fa717a2..cf38c86619 100644 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -62,6 +62,6 @@ tasks: - wazuh-12: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" - tests: "install,restart,stop,uninstall" - component: "manager" - - wazuh-version: "4.7.3" - - wazuh-revision: "40714" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" - live: "True" From d3a89011a913f58b3291d93802944fcd1fcb677e Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 3 May 2024 09:12:24 -0300 Subject: [PATCH 099/195] Added new virtualbox validations --- deployability/modules/allocation/vagrant/provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 89a1f41c98..dbfe05de53 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -189,7 +189,7 @@ def __render_vagrantfile(cls, config: VagrantConfig) -> str: environment = Environment(loader=FileSystemLoader(cls.TEMPLATES_DIR)) if config.platform == 'macos': if config.arch == 'amd64': - if config.box != 'development/macos-high-sierra' and config.box != 'development/macos-mojave' and config.box != 'development/macos-sierra' and config.box != 'development/macos-sierra_cmake': + if config.box != 'development/macos-high-sierra' and config.box != 'development/macos-mojave' and config.box != 'development/macos-sierra' and config.box != 'development/macos-sierra_cmake' and config.box != 'development/macos-sierra_gcc9': template = environment.get_template("vagrant_parallels_intel.j2") else: template = environment.get_template("vagrant_Virtual_box.j2") From 4da365d684b5bacddf432f38beb4e7ec9bff0e78 Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 3 May 2024 15:12:53 +0200 Subject: [PATCH 100/195] fix(#5219): Solving additional merge conflicts --- deployability/modules/testing/tests/helpers/__init__.py | 2 +- deployability/modules/testing/tests/helpers/agent.py | 4 ---- .../examples/manager/aws/dtt1-managers-poc-aws.yaml | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/__init__.py b/deployability/modules/testing/tests/helpers/__init__.py index c89955f5a2..e0b327ddb9 100755 --- a/deployability/modules/testing/tests/helpers/__init__.py +++ b/deployability/modules/testing/tests/helpers/__init__.py @@ -7,4 +7,4 @@ from .manager import WazuhManager from .indexer import WazuhIndexer from .dashboard import WazuhDashboard -from .central import WazuhCentralComponents \ No newline at end of file +from .central import WazuhCentralComponents diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 7441e29a96..2709879d46 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -359,10 +359,6 @@ def perform_uninstall_and_scan_for_agent(agent_params, wazuh_params) -> None: @staticmethod def assert_results(result, params = None) -> None: -<<<<<<< HEAD -======= - ->>>>>>> 4495-dtt1-release """ Gets the status of an agent given its name. diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml index 762609c97b..96ddc41186 100644 --- a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml @@ -64,4 +64,4 @@ tasks: - component: "manager" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" \ No newline at end of file + - live: "True" From 64ef9eecb0ea47428988ec7e557428e7c7aff94a Mon Sep 17 00:00:00 2001 From: Antonio Date: Fri, 3 May 2024 15:25:32 +0200 Subject: [PATCH 101/195] fix(#5219): Adding depend on in manager --- .../examples/manager/aws/dtt1-managers-poc-aws.yaml | 12 ++++++++++++ .../manager/vagrant/dtt1-managers-poc-vagrant.yaml | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml index 96ddc41186..3a4539dbb9 100644 --- a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml @@ -65,3 +65,15 @@ tasks: - wazuh-version: "4.7.4" - wazuh-revision: "40717" - live: "True" + depends-on: + - "allocate-manager-linux-ubuntu-20.04-amd64" + - "allocate-manager-linux-ubuntu-22.04-amd64" + - "allocate-manager-linux-amazon-2-amd64" + - "allocate-manager-linux-redhat-7-amd64" + - "allocate-manager-linux-redhat-8-amd64" + - "allocate-manager-linux-redhat-9-amd64" + - "allocate-manager-linux-centos-7-amd64" + - "allocate-manager-linux-centos-8-amd64" + - "allocate-manager-linux-debian-10-amd64" + - "allocate-manager-linux-debian-11-amd64" + - "allocate-manager-linux-debian-12-amd64" diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index cf38c86619..6a04352b85 100644 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -65,3 +65,16 @@ tasks: - wazuh-version: "4.7.4" - wazuh-revision: "40717" - live: "True" + depends-on: + - "allocate-manager-linux-ubuntu-20.04-amd64" + - "allocate-manager-linux-ubuntu-22.04-amd64" + - "allocate-manager-linux-amazon-2-amd64" + - "allocate-manager-linux-redhat-7-amd64" + - "allocate-manager-linux-redhat-8-amd64" + - "allocate-manager-linux-redhat-9-amd64" + - "allocate-manager-linux-centos-7-amd64" + - "allocate-manager-linux-centos-8-amd64" + - "allocate-manager-linux-debian-10-amd64" + - "allocate-manager-linux-debian-11-amd64" + - "allocate-manager-linux-debian-12-amd64" + - "allocate-manager-linux-oracle-9-amd64" From 2caeab6815a657f55acc5efc883eb0f6eb0de025 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 3 May 2024 11:16:11 -0300 Subject: [PATCH 102/195] Added validation for Allocation module dependencies --- .../modules/allocation/vagrant/provider.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 38f64678e1..91f222a6bd 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -45,6 +45,7 @@ def _create_instance(cls, base_dir: Path, params: CreationPayload, config: Vagra Returns: VagrantInstance: The created Vagrant instance. """ + cls.validate_dependencies(params.composite_name) if params.instance_name: instance_id = params.instance_name else: @@ -400,3 +401,53 @@ def __remote_host(arch: str, action: str, os: str = None, instance_dir: Path = N return remote_host_parameters else: return remote_host_parameters + + @staticmethod + def validate_dependencies(composite_name: str): + """ + Validates the dependencies for the Vagrant provider. + + Args: + composite_name (str): The composite name of the instance. + + Raises: + ValueError: If the dependencies are not met. + """ + packages = ['vagrant', 'openssh-client', 'sshpass', 'virtualbox'] + installed_packages = [] + missing_packages = [] + platform = str(composite_name.split("-")[0]) + arch = str(composite_name.split("-")[3]) + + # Check if yum or apt is installed + package_manager = None + for manager in ['yum', 'apt']: + result = subprocess.run(['which', manager], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if result.returncode == 0: + package_manager = manager + break + + if not package_manager: + raise ValueError("Neither yum nor apt is available on this system.") + + for package in packages: + if package_manager == 'yum': + result = subprocess.run(['rpm', '-q', package], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if package_manager == 'apt': + result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{package}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if result.returncode == 0: + installed_packages.append(package) + else: + missing_packages.append(package) + + for package in missing_packages: + if platform == 'macos' or arch == 'ppc64': + if package == 'openssh-client': + raise ValueError(f"Missing package: {package}") + if package == 'sshpass': + raise ValueError(f"Missing package: {package}") + else: + if package == 'virtualbox': + raise ValueError(f"Missing package: {package}") + if package == 'vagrant': + raise ValueError(f"Missing package: {package}") From bdac6c1207d159efc459512e4f9ee1a5ee0f6e04 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 3 May 2024 11:40:54 -0300 Subject: [PATCH 103/195] Added error message for yum sistems --- deployability/modules/allocation/vagrant/provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 91f222a6bd..399526f6aa 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -432,7 +432,7 @@ def validate_dependencies(composite_name: str): for package in packages: if package_manager == 'yum': - result = subprocess.run(['rpm', '-q', package], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + raise ValueError("Yum is not supported for Allocation Module.") if package_manager == 'apt': result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{package}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if result.returncode == 0: From 0efab9aea74405d3039c10c84c978be73dc85e6b Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 3 May 2024 12:25:13 -0300 Subject: [PATCH 104/195] Added validation for awscli --- .../modules/allocation/aws/provider.py | 41 +++++++++++++++++++ .../modules/allocation/vagrant/provider.py | 4 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py index 66e393cce0..8dc522462f 100644 --- a/deployability/modules/allocation/aws/provider.py +++ b/deployability/modules/allocation/aws/provider.py @@ -9,6 +9,7 @@ import random from pathlib import Path from datetime import datetime, timedelta +import subprocess from modules.allocation.generic import Provider from modules.allocation.generic.models import CreationPayload, InstancePayload, InstancePayload @@ -43,6 +44,7 @@ def _create_instance(cls, base_dir: Path, params: CreationPayload, config: AWSCo Returns: AWSInstance: Created AWSInstance object. """ + cls.validate_dependencies() temp_id = cls._generate_instance_id(cls.provider_name) temp_dir = base_dir / temp_id credentials = AWSCredentials() @@ -373,3 +375,42 @@ def generate_repository_name(repository: str) -> str: return matches[1] else: return repository + + @staticmethod + def validate_dependencies(): + """ + Validates the dependencies for the Vagrant provider. + + Raises: + ValueError: If the dependencies are not met. + """ + packages = ['openssh-client', 'awscli'] + installed_packages = [] + missing_packages = [] + + # Check if yum or apt is installed + package_manager = None + for manager in ['yum', 'apt']: + result = subprocess.run(['which', manager], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if result.returncode == 0: + package_manager = manager + break + + if not package_manager: + raise ValueError("Neither yum nor apt is available on this system.") + + for package in packages: + if package_manager == 'yum': + raise ValueError("Yum is not supported for Allocation Module.") + if package_manager == 'apt': + result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{package}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if result.returncode == 0: + installed_packages.append(package) + else: + missing_packages.append(package) + + for package in missing_packages: + if package == 'openssh-client': + raise ValueError(f"Missing package: {package}") + if package == 'awscli': + raise ValueError(f"Missing package: {package}") diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 399526f6aa..a3186d988c 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -413,7 +413,7 @@ def validate_dependencies(composite_name: str): Raises: ValueError: If the dependencies are not met. """ - packages = ['vagrant', 'openssh-client', 'sshpass', 'virtualbox'] + packages = ['vagrant', 'openssh-client', 'sshpass', 'virtualbox', 'awscli'] installed_packages = [] missing_packages = [] platform = str(composite_name.split("-")[0]) @@ -446,6 +446,8 @@ def validate_dependencies(composite_name: str): raise ValueError(f"Missing package: {package}") if package == 'sshpass': raise ValueError(f"Missing package: {package}") + if package == 'awscli': + raise ValueError(f"Missing package: {package}") else: if package == 'virtualbox': raise ValueError(f"Missing package: {package}") From 97c3ee9d58298f953c63c77c879853f0a3dd9e1e Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 3 May 2024 12:51:12 -0300 Subject: [PATCH 105/195] Fix validation for aws cli --- deployability/modules/allocation/aws/provider.py | 3 ++- deployability/modules/allocation/vagrant/provider.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py index 8dc522462f..4f5ffe3f2d 100644 --- a/deployability/modules/allocation/aws/provider.py +++ b/deployability/modules/allocation/aws/provider.py @@ -413,4 +413,5 @@ def validate_dependencies(): if package == 'openssh-client': raise ValueError(f"Missing package: {package}") if package == 'awscli': - raise ValueError(f"Missing package: {package}") + if not subprocess.run(['which', '/usr/local/bin/aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE): + raise ValueError(f"Missing package: {package}") diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index a3186d988c..9315fdb1ba 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -447,7 +447,8 @@ def validate_dependencies(composite_name: str): if package == 'sshpass': raise ValueError(f"Missing package: {package}") if package == 'awscli': - raise ValueError(f"Missing package: {package}") + if not subprocess.run(['which', '/usr/local/bin/aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE): + raise ValueError(f"Missing package: {package}") else: if package == 'virtualbox': raise ValueError(f"Missing package: {package}") From 72f6ed8c8a25c7d6817cb7d847ecf4147caa6b8e Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 3 May 2024 14:03:27 -0300 Subject: [PATCH 106/195] Removed YUM validation and added error message for not apt systems --- .../modules/allocation/aws/provider.py | 18 ++++-------------- .../modules/allocation/vagrant/provider.py | 18 ++++-------------- 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py index 4f5ffe3f2d..a5968c34ba 100644 --- a/deployability/modules/allocation/aws/provider.py +++ b/deployability/modules/allocation/aws/provider.py @@ -388,22 +388,12 @@ def validate_dependencies(): installed_packages = [] missing_packages = [] - # Check if yum or apt is installed - package_manager = None - for manager in ['yum', 'apt']: - result = subprocess.run(['which', manager], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - if result.returncode == 0: - package_manager = manager - break - - if not package_manager: - raise ValueError("Neither yum nor apt is available on this system.") + result = subprocess.run(['which', 'apt'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if result.returncode != 0: + raise ValueError("The Allocation module works on systems with APT as packages systems.") for package in packages: - if package_manager == 'yum': - raise ValueError("Yum is not supported for Allocation Module.") - if package_manager == 'apt': - result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{package}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{package}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if result.returncode == 0: installed_packages.append(package) else: diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 9315fdb1ba..0f8ae93769 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -419,22 +419,12 @@ def validate_dependencies(composite_name: str): platform = str(composite_name.split("-")[0]) arch = str(composite_name.split("-")[3]) - # Check if yum or apt is installed - package_manager = None - for manager in ['yum', 'apt']: - result = subprocess.run(['which', manager], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - if result.returncode == 0: - package_manager = manager - break - - if not package_manager: - raise ValueError("Neither yum nor apt is available on this system.") + result = subprocess.run(['which', 'apt'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if result.returncode != 0: + raise ValueError("The Allocation module works on systems with APT as packages systems.") for package in packages: - if package_manager == 'yum': - raise ValueError("Yum is not supported for Allocation Module.") - if package_manager == 'apt': - result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{package}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{package}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if result.returncode == 0: installed_packages.append(package) else: From ec3848451763d053b2f7b139f64c8f13dda65c60 Mon Sep 17 00:00:00 2001 From: Marcelo Ariel Hamra Date: Fri, 3 May 2024 14:46:09 -0300 Subject: [PATCH 107/195] List command dependencies on the README file --- deployability/modules/allocation/README.MD | 11 +++++++++++ deployability/modules/testing/README.MD | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 28b15b513c..3ec69ba989 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -33,6 +33,17 @@ Initially, you have to install the required Python libraries. We recommend using pip3 install -r deployability/deps/requirements.txt ``` +4. The module may execute any of these commands: + + - ssh + - ssh-keygen + - sshpass + - cat + - vagrant + - chmod + + These commands must be available to run on the Host Operating System. + #### Use the Allocation module through the Workflow engine Now, it is possible to use the Worklow engine library to launch the provision module by doing the following steps: diff --git a/deployability/modules/testing/README.MD b/deployability/modules/testing/README.MD index 682798e2f0..df9aeb9ad2 100644 --- a/deployability/modules/testing/README.MD +++ b/deployability/modules/testing/README.MD @@ -55,9 +55,16 @@ Initially, Python libraries must be installed, we recommended the use of virtual pip3 uninstall -y workflow_engine && pip3 install . ``` +6. The module may execute any of these commands: + + - ssh + - scp + + These commands must be available to run on the Host Operating System. + Run the module by doing the following steps: -6. Test fixture to execute: +7. Test fixture to execute: It will be necessary to create a fixture (YAML file) where the infrastructure, provisioning, and tests to be executed will be declared. From 9b3b339556a9dbe96d89500674b49e25eccd1b49 Mon Sep 17 00:00:00 2001 From: Marcelo Ariel Hamra Date: Fri, 3 May 2024 14:53:57 -0300 Subject: [PATCH 108/195] Add the Python and pip version to all README fles --- deployability/modules/allocation/README.MD | 2 ++ deployability/modules/provision/README.MD | 2 ++ deployability/modules/testing/README.MD | 2 ++ deployability/modules/workflow_engine/README.MD | 2 ++ 4 files changed, 8 insertions(+) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 3ec69ba989..b30c182265 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -11,6 +11,8 @@ Execution can be done from any operating system. Initially, you have to install the required Python libraries. We recommend using virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. +The Python version to use is 3.10.13 with pip3 version 22.0.2 or higher. + 1. Activate the environment: ```bash diff --git a/deployability/modules/provision/README.MD b/deployability/modules/provision/README.MD index d32e422f91..387945bbc9 100644 --- a/deployability/modules/provision/README.MD +++ b/deployability/modules/provision/README.MD @@ -14,6 +14,8 @@ The execution of the workflow is done through the installation of its library. Initially, Python libraries must be installed. we recommended the use of virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. +The Python version to use is 3.10.13 with pip3 version 22.0.2 or higher. + 1. Create the python environment: ```bash diff --git a/deployability/modules/testing/README.MD b/deployability/modules/testing/README.MD index df9aeb9ad2..e5c38d79a8 100644 --- a/deployability/modules/testing/README.MD +++ b/deployability/modules/testing/README.MD @@ -17,6 +17,8 @@ The execution of the workflow is done through the installation of its library. Initially, Python libraries must be installed, we recommended the use of virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. +The Python version to use is 3.10.13 with pip3 version 22.0.2 or higher. + 1. Create the python environment: ```bash diff --git a/deployability/modules/workflow_engine/README.MD b/deployability/modules/workflow_engine/README.MD index 95e6a0db5d..085f38922a 100644 --- a/deployability/modules/workflow_engine/README.MD +++ b/deployability/modules/workflow_engine/README.MD @@ -6,6 +6,8 @@ The execution of the Workflow is done through the installation of its library. Initially, Python libraries must be installed. It is recommended to use virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. +The Python version to use is 3.10.13 with pip3 version 22.0.2 or higher. + 1. Activate the environment: ```bash From d435b4e2779ab06d5be175917dabd4f1c9d38484 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 3 May 2024 15:01:53 -0300 Subject: [PATCH 109/195] The validation of dependencies according to the deploy is improved --- .../modules/allocation/aws/provider.py | 28 ++++++------- .../modules/allocation/vagrant/provider.py | 41 +++++++++---------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py index a5968c34ba..d302d802be 100644 --- a/deployability/modules/allocation/aws/provider.py +++ b/deployability/modules/allocation/aws/provider.py @@ -384,24 +384,24 @@ def validate_dependencies(): Raises: ValueError: If the dependencies are not met. """ - packages = ['openssh-client', 'awscli'] - installed_packages = [] - missing_packages = [] + dependencies = ['openssh-client', 'awscli'] + installed_dependencies = [] + missing_dependencies = [] result = subprocess.run(['which', 'apt'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if result.returncode != 0: raise ValueError("The Allocation module works on systems with APT as packages systems.") - for package in packages: - result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{package}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + for dependency in dependencies: + result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{dependency}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if result.returncode == 0: - installed_packages.append(package) + installed_dependencies.append(dependency) else: - missing_packages.append(package) - - for package in missing_packages: - if package == 'openssh-client': - raise ValueError(f"Missing package: {package}") - if package == 'awscli': - if not subprocess.run(['which', '/usr/local/bin/aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE): - raise ValueError(f"Missing package: {package}") + if dependency == 'awscli': + if not subprocess.run(['which', '/usr/local/bin/aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE): + missing_dependencies.append(dependency) + else: + missing_dependencies.append(dependency) + + for missing_dependency in missing_dependencies: + raise ValueError(f"Missing package: {missing_dependency}") diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 0f8ae93769..7c51f6a107 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -413,9 +413,10 @@ def validate_dependencies(composite_name: str): Raises: ValueError: If the dependencies are not met. """ - packages = ['vagrant', 'openssh-client', 'sshpass', 'virtualbox', 'awscli'] - installed_packages = [] - missing_packages = [] + remote_deploy_dependencies = ['openssh-client', 'sshpass', 'awscli'] + local_deploy_dependencies = ['vagrant', 'virtualbox'] + installed_dependencies = [] + missing_dependencies = [] platform = str(composite_name.split("-")[0]) arch = str(composite_name.split("-")[3]) @@ -423,24 +424,22 @@ def validate_dependencies(composite_name: str): if result.returncode != 0: raise ValueError("The Allocation module works on systems with APT as packages systems.") - for package in packages: - result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{package}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + if platform == 'macos' or arch == 'ppc64': + dependencies = remote_deploy_dependencies + else: + dependencies = local_deploy_dependencies + + for dependency in dependencies: + result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{dependency}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if result.returncode == 0: - installed_packages.append(package) + installed_dependencies.append(dependency) else: - missing_packages.append(package) - - for package in missing_packages: - if platform == 'macos' or arch == 'ppc64': - if package == 'openssh-client': - raise ValueError(f"Missing package: {package}") - if package == 'sshpass': - raise ValueError(f"Missing package: {package}") - if package == 'awscli': + if dependency == 'awscli': if not subprocess.run(['which', '/usr/local/bin/aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE): - raise ValueError(f"Missing package: {package}") - else: - if package == 'virtualbox': - raise ValueError(f"Missing package: {package}") - if package == 'vagrant': - raise ValueError(f"Missing package: {package}") + missing_dependencies.append(dependency) + else: + missing_dependencies.append(dependency) + + for missing_dependecy in missing_dependencies: + raise ValueError(f"Missing package: {missing_dependecy}") From 0b6f86cf83d0293cb456d8a73bcce7765685c596 Mon Sep 17 00:00:00 2001 From: Marcelo Ariel Hamra Date: Fri, 3 May 2024 15:10:08 -0300 Subject: [PATCH 110/195] README improvements. --- deployability/modules/allocation/README.MD | 2 +- deployability/modules/provision/README.MD | 2 +- deployability/modules/testing/README.MD | 2 +- deployability/modules/workflow_engine/README.MD | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index b30c182265..8444c1b602 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -11,7 +11,7 @@ Execution can be done from any operating system. Initially, you have to install the required Python libraries. We recommend using virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. -The Python version to use is 3.10.13 with pip3 version 22.0.2 or higher. +To use this module, you should use a Debian-based system, we recommend using Ubuntu 22.04 for full compatibility, you must have installed at least Python 3.10.12 and pip3 22.0.2 1. Activate the environment: diff --git a/deployability/modules/provision/README.MD b/deployability/modules/provision/README.MD index 387945bbc9..11049eeafe 100644 --- a/deployability/modules/provision/README.MD +++ b/deployability/modules/provision/README.MD @@ -14,7 +14,7 @@ The execution of the workflow is done through the installation of its library. Initially, Python libraries must be installed. we recommended the use of virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. -The Python version to use is 3.10.13 with pip3 version 22.0.2 or higher. +To use this module, you should use a Debian-based system, we recommend using Ubuntu 22.04 for full compatibility, you must have installed at least Python 3.10.12 and pip3 22.0.2 1. Create the python environment: diff --git a/deployability/modules/testing/README.MD b/deployability/modules/testing/README.MD index e5c38d79a8..f2b625e3f4 100644 --- a/deployability/modules/testing/README.MD +++ b/deployability/modules/testing/README.MD @@ -17,7 +17,7 @@ The execution of the workflow is done through the installation of its library. Initially, Python libraries must be installed, we recommended the use of virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. -The Python version to use is 3.10.13 with pip3 version 22.0.2 or higher. +To use this module, you should use a Debian-based system, we recommend using Ubuntu 22.04 for full compatibility, you must have installed at least Python 3.10.12 and pip3 22.0.2 1. Create the python environment: diff --git a/deployability/modules/workflow_engine/README.MD b/deployability/modules/workflow_engine/README.MD index 085f38922a..c4430d97a4 100644 --- a/deployability/modules/workflow_engine/README.MD +++ b/deployability/modules/workflow_engine/README.MD @@ -6,7 +6,7 @@ The execution of the Workflow is done through the installation of its library. Initially, Python libraries must be installed. It is recommended to use virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. -The Python version to use is 3.10.13 with pip3 version 22.0.2 or higher. +To use this module, you should use a Debian-based system, we recommend using Ubuntu 22.04 for full compatibility, you must have installed at least Python 3.10.12 and pip3 22.0.2 1. Activate the environment: From 5c3676d366655ec3ae52a84cf178eeeff004cd9f Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 3 May 2024 15:26:13 -0300 Subject: [PATCH 111/195] Fixed aws cli binary validation --- deployability/modules/allocation/aws/provider.py | 3 ++- deployability/modules/allocation/vagrant/provider.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py index d302d802be..b9b151a333 100644 --- a/deployability/modules/allocation/aws/provider.py +++ b/deployability/modules/allocation/aws/provider.py @@ -398,7 +398,8 @@ def validate_dependencies(): installed_dependencies.append(dependency) else: if dependency == 'awscli': - if not subprocess.run(['which', '/usr/local/bin/aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE): + aws_binary = subprocess.run(['which', '/usr/local/bin/aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if aws_binary.returncode != 0: missing_dependencies.append(dependency) else: missing_dependencies.append(dependency) diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 7c51f6a107..6a58964e32 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -436,7 +436,8 @@ def validate_dependencies(composite_name: str): installed_dependencies.append(dependency) else: if dependency == 'awscli': - if not subprocess.run(['which', '/usr/local/bin/aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE): + aws_binary = subprocess.run(['which', '/usr/local/bin/aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if aws_binary.returncode != 0: missing_dependencies.append(dependency) else: missing_dependencies.append(dependency) From 76edd595e08bd04f5a5977a6f56b9428aa7979c7 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 3 May 2024 15:38:56 -0300 Subject: [PATCH 112/195] enhancement in the missing dependencies print --- deployability/modules/allocation/aws/provider.py | 7 +++++-- deployability/modules/allocation/vagrant/provider.py | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py index b9b151a333..66b06c4de2 100644 --- a/deployability/modules/allocation/aws/provider.py +++ b/deployability/modules/allocation/aws/provider.py @@ -404,5 +404,8 @@ def validate_dependencies(): else: missing_dependencies.append(dependency) - for missing_dependency in missing_dependencies: - raise ValueError(f"Missing package: {missing_dependency}") + if len(missing_dependencies) > 0: + if len(missing_dependencies) == 1: + raise ValueError(f"Missing dependency: {missing_dependencies[0]}") + else: + raise ValueError(f"Missing dependencies: {missing_dependencies}") diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 6a58964e32..df8ea809a1 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -442,5 +442,8 @@ def validate_dependencies(composite_name: str): else: missing_dependencies.append(dependency) - for missing_dependecy in missing_dependencies: - raise ValueError(f"Missing package: {missing_dependecy}") + if len(missing_dependencies) > 0: + if len(missing_dependencies) == 1: + raise ValueError(f"Missing dependency: {missing_dependencies[0]}") + else: + raise ValueError(f"Missing dependencies: {missing_dependencies}") From e9c00de4e9cc5c4f35eb22b2dc7db346c141d10c Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 3 May 2024 15:54:20 -0300 Subject: [PATCH 113/195] Removed installed_dependencies list --- deployability/modules/allocation/aws/provider.py | 5 +---- deployability/modules/allocation/vagrant/provider.py | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py index 66b06c4de2..bdf18be111 100644 --- a/deployability/modules/allocation/aws/provider.py +++ b/deployability/modules/allocation/aws/provider.py @@ -385,7 +385,6 @@ def validate_dependencies(): ValueError: If the dependencies are not met. """ dependencies = ['openssh-client', 'awscli'] - installed_dependencies = [] missing_dependencies = [] result = subprocess.run(['which', 'apt'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -394,9 +393,7 @@ def validate_dependencies(): for dependency in dependencies: result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{dependency}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - if result.returncode == 0: - installed_dependencies.append(dependency) - else: + if result.returncode != 0: if dependency == 'awscli': aws_binary = subprocess.run(['which', '/usr/local/bin/aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if aws_binary.returncode != 0: diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index df8ea809a1..f70ada5096 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -415,7 +415,6 @@ def validate_dependencies(composite_name: str): """ remote_deploy_dependencies = ['openssh-client', 'sshpass', 'awscli'] local_deploy_dependencies = ['vagrant', 'virtualbox'] - installed_dependencies = [] missing_dependencies = [] platform = str(composite_name.split("-")[0]) arch = str(composite_name.split("-")[3]) @@ -432,9 +431,7 @@ def validate_dependencies(composite_name: str): for dependency in dependencies: result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{dependency}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - if result.returncode == 0: - installed_dependencies.append(dependency) - else: + if result.returncode != 0: if dependency == 'awscli': aws_binary = subprocess.run(['which', '/usr/local/bin/aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if aws_binary.returncode != 0: From 0883d308d9c70d99981d79d25a3c4e130eecdab6 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Mon, 6 May 2024 14:43:44 -0300 Subject: [PATCH 114/195] Force instance name to be defined in vagrant deploy --- .../modules/allocation/aws/provider.py | 19 ------------ .../modules/allocation/generic/provider.py | 20 ++++++++++++ .../modules/allocation/vagrant/provider.py | 31 ++++++++++++++++--- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py index 66e393cce0..05ba003b88 100644 --- a/deployability/modules/allocation/aws/provider.py +++ b/deployability/modules/allocation/aws/provider.py @@ -354,22 +354,3 @@ def _release_dedicated_host(host_identifier: str) -> str: logger.warning(f"{message}") else: logger.info(f"Dedicated host released: {host_identifier}") - - @staticmethod - def generate_repository_name(repository: str) -> str: - """ - Generate a repository name for the instance. - - Args: - repository (str): Repository name. - - Returns: - str: Repository name for the instance. - """ - matches = re.findall(r'(\w+)', repository) - if len(matches) == 3: - return ''.join([c[0] for c in matches]) - elif len(matches) == 2: - return matches[1] - else: - return repository diff --git a/deployability/modules/allocation/generic/provider.py b/deployability/modules/allocation/generic/provider.py index 651e61494a..6551beb563 100644 --- a/deployability/modules/allocation/generic/provider.py +++ b/deployability/modules/allocation/generic/provider.py @@ -5,6 +5,7 @@ import shutil import uuid import yaml +import re from abc import ABC, abstractmethod from pathlib import Path @@ -202,3 +203,22 @@ def _get_misc_specs(cls) -> dict: """ with open(cls.MISC_PATH, "r") as f: return yaml.safe_load(f).get(cls.provider_name) + + @staticmethod + def generate_repository_name(repository: str) -> str: + """ + Generate a repository name for the instance. + + Args: + repository (str): Repository name. + + Returns: + str: Repository name for the instance. + """ + matches = re.findall(r'(\w+)', repository) + if len(matches) == 3: + return ''.join([c[0] for c in matches]) + elif len(matches) == 2: + return matches[1] + else: + return repository diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 38f64678e1..640d2c1227 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -7,6 +7,7 @@ import subprocess import boto3 import random +import re from jinja2 import Environment, FileSystemLoader from pathlib import Path @@ -46,12 +47,28 @@ def _create_instance(cls, base_dir: Path, params: CreationPayload, config: Vagra VagrantInstance: The created Vagrant instance. """ if params.instance_name: - instance_id = params.instance_name + name = params.instance_name + elif params.label_issue: + issue = params.label_issue + url_regex = "(https:\/\/|http:\/\/)?[github]{2,}(\.[com]{2,})?\/wazuh\/[a-zA-Z0-9_-]+(?:-[a-zA-Z0-9_-]+)?\/issues\/[0-9]{2,}" + if not re.match(url_regex, issue): + raise ValueError(f"The issue label was not provided or is of incorrect format, example: https://github.com/wazuh//issues/") + issue_name= re.search(r'github\.com\/wazuh\/([^\/]+)\/issues', issue) + repository = cls.generate_repository_name(str(issue_name.group(1))) + name = repository + "-" + str(re.search(r'(\d+)$', issue).group(1)) + "-" + str(params.composite_name.split("-")[1]) + "-" + str(params.composite_name.split("-")[2]) else: - instance_id = cls._generate_instance_id(cls.provider_name) + raise ValueError("Instance name or issue label is required.") + + instance_id = name + "-" + str(random.randint(1000, 9999)) + # Create the instance directory. instance_dir = base_dir / instance_id - instance_dir.mkdir(parents=True, exist_ok=True) + try: + instance_dir.mkdir(parents=True) + except FileExistsError: + instance_id = name + "-" + str(random.randint(1000, 9999)) + instance_dir = base_dir / instance_id + instance_dir.mkdir(parents=True) platform = str(params.composite_name.split("-")[0]) host_instance_dir = None host_identifier = None @@ -60,10 +77,14 @@ def _create_instance(cls, base_dir: Path, params: CreationPayload, config: Vagra # Used for macOS deployments if platform == 'macos': host_instance_dir = "/Users/jenkins/testing/" + instance_id - logger.debug(f"Creating instance directory on remote host") - cmd = f"mkdir {host_instance_dir}" remote_host_parameters = cls.__remote_host(arch, 'create') host_identifier = remote_host_parameters['host_provider'] + logger.debug(f"Checking if instance directory exists on remote host") + cmd = f"ls {host_instance_dir} > /dev/null 2>&1" + if VagrantUtils.remote_command(cmd, remote_host_parameters): + raise ValueError(f"Instance directory {host_instance_dir} already exists on remote host, please delete it before creating a new instance.") + logger.debug(f"Creating instance directory on remote host") + cmd = f"mkdir {host_instance_dir}" VagrantUtils.remote_command(cmd, remote_host_parameters) credentials = VagrantCredentials() if arch == 'ppc64': From b962c4ed3cb0bcbaa1660ae4c3a731615b99df7f Mon Sep 17 00:00:00 2001 From: c-bordon Date: Mon, 6 May 2024 15:00:53 -0300 Subject: [PATCH 115/195] Added debug message --- deployability/modules/allocation/vagrant/provider.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 640d2c1227..9899904003 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -59,14 +59,15 @@ def _create_instance(cls, base_dir: Path, params: CreationPayload, config: Vagra else: raise ValueError("Instance name or issue label is required.") - instance_id = name + "-" + str(random.randint(1000, 9999)) + instance_id = name + "-" + str(random.randint(0000, 9999)) # Create the instance directory. instance_dir = base_dir / instance_id try: instance_dir.mkdir(parents=True) except FileExistsError: - instance_id = name + "-" + str(random.randint(1000, 9999)) + logger.debug(f"Instance directory {instance_dir} already exists. Assigning new suffix.") + instance_id = name + "-" + str(random.randint(0000, 9999)) instance_dir = base_dir / instance_id instance_dir.mkdir(parents=True) platform = str(params.composite_name.split("-")[0]) From a7d727d88a854f74bd78fe01a155758d295271c2 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Mon, 6 May 2024 15:38:25 -0300 Subject: [PATCH 116/195] Added loop for dir validation --- .../modules/allocation/vagrant/provider.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 9899904003..b29bc032ae 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -60,16 +60,15 @@ def _create_instance(cls, base_dir: Path, params: CreationPayload, config: Vagra raise ValueError("Instance name or issue label is required.") instance_id = name + "-" + str(random.randint(0000, 9999)) - # Create the instance directory. - instance_dir = base_dir / instance_id - try: - instance_dir.mkdir(parents=True) - except FileExistsError: - logger.debug(f"Instance directory {instance_dir} already exists. Assigning new suffix.") - instance_id = name + "-" + str(random.randint(0000, 9999)) - instance_dir = base_dir / instance_id - instance_dir.mkdir(parents=True) + while True: + try: + instance_dir = base_dir / instance_id + instance_dir.mkdir(parents=True) + break + except FileExistsError: + logger.debug(f"Instance directory {instance_dir} already exists. Assigning new suffix.") + instance_id = name + "-" + str(random.randint(0000, 9999)) platform = str(params.composite_name.split("-")[0]) host_instance_dir = None host_identifier = None From 6c5698eb83faeb7b7da03d049216f7310ebbb34e Mon Sep 17 00:00:00 2001 From: c-bordon Date: Mon, 6 May 2024 16:34:12 -0300 Subject: [PATCH 117/195] enhancement on the remote host message --- deployability/modules/allocation/vagrant/provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 4603dafcd2..dd325bba7f 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -83,7 +83,7 @@ def _create_instance(cls, base_dir: Path, params: CreationPayload, config: Vagra logger.debug(f"Checking if instance directory exists on remote host") cmd = f"ls {host_instance_dir} > /dev/null 2>&1" if VagrantUtils.remote_command(cmd, remote_host_parameters): - raise ValueError(f"Instance directory {host_instance_dir} already exists on remote host, please delete it before creating a new instance.") + raise ValueError(f"Instance directory {host_instance_dir} already exists on remote host, Check if it is possible to delete the directory on the remote host.") logger.debug(f"Creating instance directory on remote host") cmd = f"mkdir {host_instance_dir}" VagrantUtils.remote_command(cmd, remote_host_parameters) From 8e1fa9b5eb76908279f65a0333e891de8b06e88a Mon Sep 17 00:00:00 2001 From: c-bordon Date: Mon, 6 May 2024 16:36:39 -0300 Subject: [PATCH 118/195] Changed , to . --- deployability/modules/allocation/vagrant/provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index dd325bba7f..2b0e3436ba 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -83,7 +83,7 @@ def _create_instance(cls, base_dir: Path, params: CreationPayload, config: Vagra logger.debug(f"Checking if instance directory exists on remote host") cmd = f"ls {host_instance_dir} > /dev/null 2>&1" if VagrantUtils.remote_command(cmd, remote_host_parameters): - raise ValueError(f"Instance directory {host_instance_dir} already exists on remote host, Check if it is possible to delete the directory on the remote host.") + raise ValueError(f"Instance directory {host_instance_dir} already exists on remote host. Check if it is possible to delete the directory on the remote host.") logger.debug(f"Creating instance directory on remote host") cmd = f"mkdir {host_instance_dir}" VagrantUtils.remote_command(cmd, remote_host_parameters) From 41bf6c382d0671dd708e6aa77a765197f602e8af Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 7 May 2024 10:14:55 +0200 Subject: [PATCH 119/195] fix(#5219): Removing commented checkfiles --- .../testing/tests/test_agent/test_install.py | 1 - .../tests/test_agent/test_uninstall.py | 1 - .../test_central_components/test_install.py | 1 - .../test_central_components/test_uninstall.py | 1 - .../tests/test_manager/test_install.py | 1 - .../tests/test_manager/test_uninstall.py | 1 - .../vagrant/test-agent-windows-complete.yaml | 122 ++++++++++++++++++ 7 files changed, 122 insertions(+), 6 deletions(-) create mode 100755 deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index 19275584bb..dd5247f8cf 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -82,7 +82,6 @@ def test_installation(wazuh_params): # Agent installation for agent_name, agent_params in wazuh_params['agents'].items(): - #WazuhAgent.perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) WazuhAgent.install_agent(agent_params, agent_name, wazuh_params['wazuh_version'], wazuh_params['wazuh_revision'], wazuh_params['live']) diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index 2abea5d57c..daef7085f4 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -79,7 +79,6 @@ def test_uninstall(wazuh_params): # Agent uninstallation for agent_names, agent_params in wazuh_params['agents'].items(): - #WazuhAgent.perform_uninstall_and_scan_for_agent(agent_params,wazuh_params) WazuhAgent.uninstall_agent(agent_params, wazuh_params['wazuh_version'], wazuh_params['wazuh_revision']) # Manager uninstallation status check diff --git a/deployability/modules/testing/tests/test_central_components/test_install.py b/deployability/modules/testing/tests/test_central_components/test_install.py index 5be509c8f7..d8ecc71d66 100644 --- a/deployability/modules/testing/tests/test_central_components/test_install.py +++ b/deployability/modules/testing/tests/test_central_components/test_install.py @@ -62,7 +62,6 @@ def test_installation(wazuh_params): # Install central components and perform checkfile testing for _, manager_params in wazuh_params['managers'].items(): - #WazuhCentralComponents.perform_install_and_scan_for_aio(manager_params, wazuh_params) WazuhCentralComponents.install_aio(manager_params, wazuh_params['wazuh_version']) # Validation of directory of the components diff --git a/deployability/modules/testing/tests/test_central_components/test_uninstall.py b/deployability/modules/testing/tests/test_central_components/test_uninstall.py index 8129d056e5..dc4623f427 100644 --- a/deployability/modules/testing/tests/test_central_components/test_uninstall.py +++ b/deployability/modules/testing/tests/test_central_components/test_uninstall.py @@ -56,7 +56,6 @@ def test_uninstall(wazuh_params): assert 'active' in GeneralComponentActions.get_component_status(indexer_params, 'wazuh-indexer'), logger.error(f'The indexer in {HostInformation.get_os_name_and_version_from_inventory(indexer_params)} is not active') assert 'active' in GeneralComponentActions.get_component_status(wazuh_params['master'], 'filebeat'), logger.error(f'The filebeat in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])} is not active') - #WazuhCentralComponents.perform_uninstall_and_scan_for_aio(wazuh_params['master']) WazuhCentralComponents.uninstall_aio(wazuh_params['master']) def test_component_uninstalled_directory(wazuh_params): diff --git a/deployability/modules/testing/tests/test_manager/test_install.py b/deployability/modules/testing/tests/test_manager/test_install.py index 7735ec648d..d9a0f5582c 100644 --- a/deployability/modules/testing/tests/test_manager/test_install.py +++ b/deployability/modules/testing/tests/test_manager/test_install.py @@ -61,7 +61,6 @@ def test_installation(wazuh_params): # Install managers and perform checkfile testing for manager_name, manager_params in wazuh_params['managers'].items(): - #WazuhManager.perform_install_and_scan_for_manager(manager_params, manager_name, wazuh_params) WazuhManager.install_manager(manager_params, manager_name, wazuh_params['wazuh_version']) # Validation of activity and directory of the managers diff --git a/deployability/modules/testing/tests/test_manager/test_uninstall.py b/deployability/modules/testing/tests/test_manager/test_uninstall.py index 0b0d88c15a..fde50a7deb 100644 --- a/deployability/modules/testing/tests/test_manager/test_uninstall.py +++ b/deployability/modules/testing/tests/test_manager/test_uninstall.py @@ -52,7 +52,6 @@ def test_uninstall(wazuh_params): manager_status = GeneralComponentActions.get_component_status(manager, 'wazuh-manager') assert 'active' in manager_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(manager)} is not active') for _, manager_params in wazuh_params['managers'].items(): - #WazuhManager.perform_uninstall_and_scan_for_manager(manager_params) WazuhManager.uninstall_manager(manager_params) def test_manager_uninstalled_directory(wazuh_params): diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml new file mode 100755 index 0000000000..b91e178825 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml @@ -0,0 +1,122 @@ +version: 0.1 +description: This workflow is used to test agents deployment for DDT1 PoC + +variables: + agent-os: + - linux-ubuntu-20.04-amd64 + - linux-debian-12-amd64 + - linux-oracle-9-amd64 + - linux-centos-8-amd64 + - linux-redhat-9-amd64 + - windows-desktop-10-amd64 + - windows-server-2012r2-amd64 + - windows-server-2016-amd64 + - windows-server-2019-amd64 + - windows-server-2022-amd64 + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: medium + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + depends-on: + - "provision-manager-{manager-os}" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.4 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,connection,basic_info,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + From 7f46ed4d16cf63dba4f0a508c0414edb6c30e8b2 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 7 May 2024 08:45:46 -0300 Subject: [PATCH 120/195] Added support for Windows sign AMI and validation with ssh for this AMI --- .../modules/allocation/allocation.py | 28 +++++++++++-------- .../modules/allocation/static/specs/os.yml | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/deployability/modules/allocation/allocation.py b/deployability/modules/allocation/allocation.py index 644e9b7b7a..f653404741 100755 --- a/deployability/modules/allocation/allocation.py +++ b/deployability/modules/allocation/allocation.py @@ -60,7 +60,7 @@ def __create(cls, payload: models.CreationPayload): instance.start() logger.info(f"Instance {instance.identifier} started.") # Generate the inventory and track files. - inventory = cls.__generate_inventory(instance, payload.inventory_output) + inventory = cls.__generate_inventory(instance, payload.inventory_output, instance_params.composite_name) # Validate connection check_connection = cls.__check_connection(inventory) track_file = cls.__generate_track_file(instance, payload.provider, payload.track_output) @@ -113,13 +113,14 @@ def ___get_custom_config(payload: models.CreationPayload) -> models.ProviderConf return config @staticmethod - def __generate_inventory(instance: Instance, inventory_path: Path) -> None: + def __generate_inventory(instance: Instance, inventory_path: Path, composite_name: str) -> None: """ Generates an inventory file. Args: instance (Instance): The instance for which the inventory file is generated. inventory_path (Path): The path where the inventory file will be generated. + composite_name (str): The name of the composite. """ if inventory_path is None: inventory_path = Path(instance.path, 'inventory.yml') @@ -129,12 +130,20 @@ def __generate_inventory(instance: Instance, inventory_path: Path) -> None: inventory_path.parent.mkdir(parents=True, exist_ok=True) ssh_config = instance.ssh_connection_info() if instance.platform == 'windows': - inventory = models.InventoryOutput(ansible_host=ssh_config.hostname, - ansible_user=ssh_config.user, - ansible_port=ssh_config.port, - ansible_password=ssh_config.password, - ansible_connection='winrm', - ansible_winrm_server_cert_validation='ignore') + if str(composite_name.split("-")[1]) == 'sign': + inventory = models.InventoryOutput(ansible_host=ssh_config.hostname, + ansible_user=ssh_config.user, + ansible_port=2200, + ansible_connection='ssh', + ansible_password=ssh_config.password, + ansible_ssh_common_args='-o StrictHostKeyChecking=no') + else: + inventory = models.InventoryOutput(ansible_host=ssh_config.hostname, + ansible_user=ssh_config.user, + ansible_port=ssh_config.port, + ansible_password=ssh_config.password, + ansible_connection='winrm', + ansible_winrm_server_cert_validation='ignore') elif not ssh_config.private_key: inventory = models.InventoryOutput(ansible_host=ssh_config.hostname, ansible_user=ssh_config.user, @@ -243,9 +252,6 @@ def __check_connection(inventory: models.InventoryOutput, attempts=30, sleep=30) logger.info("SSH connection successful.") ssh.close() return True - except paramiko.AuthenticationException: - logger.error(f'Authentication error. Check the credentials in the inventory file.') - return False except Exception as e: logger.warning(f'Error on attempt {attempt} of {attempts}: {e}') time.sleep(sleep) diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index b746797d05..50e4af74fb 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -372,7 +372,7 @@ aws: zone: us-east-1 user: wazuh-user windows-sign-10-amd64: - ami: ami-078255d5475b46db5 + ami: ami-05fb40a2b48162ac3 zone: us-east-1 user: wazuh-user windows-server-2012r2-amd64: From 079f1ae3119bf35125f7551f281d1542c7cb4b20 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 7 May 2024 08:50:22 -0300 Subject: [PATCH 121/195] Changed composite name description --- deployability/modules/allocation/allocation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/allocation.py b/deployability/modules/allocation/allocation.py index f653404741..4b3b973eea 100755 --- a/deployability/modules/allocation/allocation.py +++ b/deployability/modules/allocation/allocation.py @@ -120,7 +120,7 @@ def __generate_inventory(instance: Instance, inventory_path: Path, composite_nam Args: instance (Instance): The instance for which the inventory file is generated. inventory_path (Path): The path where the inventory file will be generated. - composite_name (str): The name of the composite. + composite_name (str): Composite name of the instance to be provisioned. """ if inventory_path is None: inventory_path = Path(instance.path, 'inventory.yml') From f4fac3143aaf12a57439dcc0fde9e228f270fa1b Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 7 May 2024 14:31:31 -0300 Subject: [PATCH 122/195] macOS Intel available boxes updated --- deployability/modules/allocation/static/specs/os.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index d62ad648cf..35980524d5 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -99,12 +99,6 @@ vagrant: box: generic/rocky9 box_version: 4.3.12 # Macos - macos-bigsur-11.0-amd64: - box: development/macos-big-sur - box_version: 0.0.0 - macos-catalina-10.15.1-amd64: - box: development/macos-catalina - box_version: 0.0.0 macos-highsierra-10.13.6-amd64: box: development/macos-high-sierra box_version: 0.0.0 @@ -123,9 +117,6 @@ vagrant: macos-monterey-12.6-arm64: box: macos-12 box_version: 0.0.0 - macos-monterey-12.0.1-amd64: - box: development/macos-monterey - box_version: 0.0.0 macos-ventura-13.4.1-amd64: box: development/macos-ventura box_version: 0.0.0 From b43a25b40037ea2e59ed03a32c5e832bc7fbe06b Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 7 May 2024 14:46:30 -0300 Subject: [PATCH 123/195] Improvement in virtualbox box validation --- deployability/modules/allocation/vagrant/provider.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 1b41962448..01105d35de 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -190,7 +190,8 @@ def __render_vagrantfile(cls, config: VagrantConfig) -> str: environment = Environment(loader=FileSystemLoader(cls.TEMPLATES_DIR)) if config.platform == 'macos': if config.arch == 'amd64': - if config.box != 'development/macos-high-sierra' and config.box != 'development/macos-mojave' and config.box != 'development/macos-sierra' and config.box != 'development/macos-sierra_cmake' and config.box != 'development/macos-sierra_gcc9': + virtualbox_boxes = ['development/macos-high-sierra', 'development/macos-mojave', 'development/macos-sierra', 'development/macos-sierra_cmake', 'development/macos-sierra_gcc9'] + if config.box not in virtualbox_boxes: template = environment.get_template("vagrant_parallels_intel.j2") else: template = environment.get_template("vagrant_Virtual_box.j2") From 3746d9526576b4d6b6bcf01b6c95df1f0e7d24d8 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 7 May 2024 16:36:25 -0300 Subject: [PATCH 124/195] Added new validations for vagrant status and ssh remote command --- .../modules/allocation/vagrant/instance.py | 17 +++++++- .../modules/allocation/vagrant/utils.py | 41 ++++++++++++------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/deployability/modules/allocation/vagrant/instance.py b/deployability/modules/allocation/vagrant/instance.py index f5bb5bfedc..7963530fad 100755 --- a/deployability/modules/allocation/vagrant/instance.py +++ b/deployability/modules/allocation/vagrant/instance.py @@ -121,7 +121,18 @@ def status(self) -> str: str: The status of the instance. """ output = self.__run_vagrant_command('status') - return self.__parse_vagrant_status(output) + vagrant_status = self.__parse_vagrant_status(output) + if vagrant_status == None: + if VagrantUtils.remote_command(f"ls {self.host_instance_dir} > /dev/null 2>&1", self.remote_host_parameters): + if VagrantUtils.remote_command(f"prlctl list -a | grep {self.identifier} > /dev/null 2>&1", self.remote_host_parameters): + return "running" + else: + VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) + raise ValueError(f"Instance {self.identifier} is not running, remote instance dir {self.host_instance_dir} was removed.") + else: + raise ValueError(f"Instance {self.identifier} not found.") + else: + return self.__parse_vagrant_status(output) def ssh_connection_info(self) -> ConnectionInfo: """ @@ -238,6 +249,10 @@ def __parse_vagrant_status(self, message: str) -> str: Returns: str: The parsed status. """ + if message is None: + logger.error("Received None message when parsing Vagrant status") + return None + lines = message.split('\n') for line in lines: if 'Current machine states:' in line: diff --git a/deployability/modules/allocation/vagrant/utils.py b/deployability/modules/allocation/vagrant/utils.py index aacd54b56b..35b641ddb5 100644 --- a/deployability/modules/allocation/vagrant/utils.py +++ b/deployability/modules/allocation/vagrant/utils.py @@ -3,6 +3,7 @@ # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 import subprocess +import time from pathlib import Path from modules.allocation.generic.utils import logger @@ -28,23 +29,33 @@ def remote_command(cls, command: str | list, remote_host_parameters: dict) -> st ssh_key = remote_host_parameters['ssh_key'] ssh_command = f"ssh -i {ssh_key} {ssh_user}@{server_ip} \"{command}\"" - try: - output = subprocess.Popen(f"{ssh_command}", - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - stdout_data, stderr_data = output.communicate() # Capture stdout and stderr data - stdout_text = stdout_data.decode('utf-8') if stdout_data else "" # Decode stdout bytes to string - stderr_text = stderr_data.decode('utf-8') if stderr_data else "" # Decode stderr bytes to string + max_retry = 3 + for attempt in range(max_retry): + try: + output = subprocess.Popen(f"{ssh_command}", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout_data, stderr_data = output.communicate() # Capture stdout and stderr data + stdout_text = stdout_data.decode('utf-8') if stdout_data else "" # Decode stdout bytes to string + stderr_text = stderr_data.decode('utf-8') if stderr_data else "" # Decode stderr bytes to string - if stderr_text: - logger.error(f"Command failed: {stderr_text}") - return None + if stderr_text: + if "Connection reset" in stderr_text or "Connection timed out" in stderr_text or "Broken pipe" in stderr_text: + if attempt < max_retry - 1: + logger.warning(f"SSH connection error: {stderr_text}. Retrying...") + time.sleep(30) + continue + else: + raise ValueError(f"SSH connection error: {stderr_text}") + else: + logger.error(f"Command failed: {stderr_text}") + return None - return stdout_text - except subprocess.CalledProcessError as e: - logger.error(f"Command failed: {e.stderr.decode('utf-8')}") - return None + return stdout_text + except subprocess.CalledProcessError as e: + logger.error(f"Command failed: {e.stderr.decode('utf-8')}") + return None @classmethod def remote_copy(cls, instance_dir: Path, host_instance_dir: Path, remote_host_parameters: dict) -> str: From 9131aa80d50fbe6a491212dc711dbf2f067acb75 Mon Sep 17 00:00:00 2001 From: Marcelo Ariel Hamra Date: Wed, 8 May 2024 14:54:54 -0300 Subject: [PATCH 125/195] Fix the command that installs the arm agent. --- deployability/modules/testing/tests/helpers/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 2709879d46..be8890d79a 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -43,7 +43,7 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv ]) elif distribution == 'deb' and 'arm64' in architecture: commands.extend([ - f"wget https://{s3_url}.wazuh.com/{release}/apt/pool/main/w/wazuh-agent/wazuh-agent_{wazuh_version}-1_arm64.deb && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' dpkg -i ./wazuh-agent_{wazuh_version}-1arm64.deb" + f"wget https://{s3_url}.wazuh.com/{release}/apt/pool/main/w/wazuh-agent/wazuh-agent_{wazuh_version}-1_arm64.deb && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' dpkg -i ./wazuh-agent_{wazuh_version}-1_arm64.deb" ]) system_commands = [ "systemctl daemon-reload", From 6d050435917ef8ba67451182114c549e173d7049 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Wed, 8 May 2024 15:39:29 -0300 Subject: [PATCH 126/195] Added new SSH connection method with paramiko library --- .../modules/allocation/vagrant/instance.py | 2 +- .../modules/allocation/vagrant/utils.py | 66 +++++++++---------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/deployability/modules/allocation/vagrant/instance.py b/deployability/modules/allocation/vagrant/instance.py index e4dcce42ea..055c575073 100755 --- a/deployability/modules/allocation/vagrant/instance.py +++ b/deployability/modules/allocation/vagrant/instance.py @@ -127,7 +127,7 @@ def status(self) -> str: VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) raise ValueError(f"Instance {self.identifier} is not running, remote instance dir {self.host_instance_dir} was removed.") else: - raise ValueError(f"Instance {self.identifier} not found.") + raise ValueError(f"Instance {self.host_instance_dir} not found.") else: return self.__parse_vagrant_status(output) diff --git a/deployability/modules/allocation/vagrant/utils.py b/deployability/modules/allocation/vagrant/utils.py index 35b641ddb5..c17311759f 100644 --- a/deployability/modules/allocation/vagrant/utils.py +++ b/deployability/modules/allocation/vagrant/utils.py @@ -2,9 +2,15 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -import subprocess import time +import subprocess from pathlib import Path +import logging +import random +import socket + +import paramiko + from modules.allocation.generic.utils import logger @@ -19,43 +25,36 @@ def remote_command(cls, command: str | list, remote_host_parameters: dict) -> st Returns: str: The output of the command. """ - ssh_command = None - server_ip = remote_host_parameters['server_ip'] - ssh_user = remote_host_parameters['ssh_user'] - if remote_host_parameters.get('ssh_password'): - ssh_password = remote_host_parameters['ssh_password'] - ssh_command = f"sshpass -p {ssh_password} ssh -o 'StrictHostKeyChecking no' {ssh_user}@{server_ip} {command}" + ssh = paramiko.SSHClient() + paramiko.util.get_logger("paramiko").setLevel(logging.WARNING) + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh_parameters = { + 'hostname': remote_host_parameters['server_ip'], + 'port': 22, + 'username': remote_host_parameters['ssh_user'] + } if remote_host_parameters.get('ssh_key'): - ssh_key = remote_host_parameters['ssh_key'] - ssh_command = f"ssh -i {ssh_key} {ssh_user}@{server_ip} \"{command}\"" + ssh_parameters['key_filename'] = remote_host_parameters['ssh_key'] + else: + ssh_parameters['password'] = remote_host_parameters['ssh_password'] max_retry = 3 for attempt in range(max_retry): try: - output = subprocess.Popen(f"{ssh_command}", - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - stdout_data, stderr_data = output.communicate() # Capture stdout and stderr data - stdout_text = stdout_data.decode('utf-8') if stdout_data else "" # Decode stdout bytes to string - stderr_text = stderr_data.decode('utf-8') if stderr_data else "" # Decode stderr bytes to string - - if stderr_text: - if "Connection reset" in stderr_text or "Connection timed out" in stderr_text or "Broken pipe" in stderr_text: - if attempt < max_retry - 1: - logger.warning(f"SSH connection error: {stderr_text}. Retrying...") - time.sleep(30) - continue - else: - raise ValueError(f"SSH connection error: {stderr_text}") - else: - logger.error(f"Command failed: {stderr_text}") - return None + ssh.connect(**ssh_parameters) + stdin_data, stdout_data, stderr_data = ssh.exec_command(command, timeout = 300) + stdout_text = stdout_data.read().decode('utf-8') + ssh.close() return stdout_text - except subprocess.CalledProcessError as e: - logger.error(f"Command failed: {e.stderr.decode('utf-8')}") - return None + except (subprocess.CalledProcessError, paramiko.AuthenticationException, paramiko.SSHException, socket.timeout) as e: + if attempt < max_retry - 1: + logger.warning(f"SSH connection error: {str(e)}. Retrying in 30 seconds...") + time.sleep(30) + continue + else: + ssh.close() + raise ValueError(f"Command failed: {str(e)}") @classmethod def remote_copy(cls, instance_dir: Path, host_instance_dir: Path, remote_host_parameters: dict) -> str: @@ -111,9 +110,10 @@ def get_port(cls, remote_host_parameters: dict, arch: str = None) -> int: raise ValueError(f"ppc64 server has no available SSH ports.") else: - for i in range(20, 40): - port = f"432{i}" + for i in range(20): + port = f"432{random.randint(20, 40)}" cmd = f"sudo lsof -i:{port}" output = cls.remote_command(cmd, remote_host_parameters) if not output: return port + raise ValueError(f"Server has no available SSH ports.") From 772de9a1b8b67cdf63b3e857ec2074226aef436d Mon Sep 17 00:00:00 2001 From: c-bordon Date: Wed, 8 May 2024 15:42:45 -0300 Subject: [PATCH 127/195] Changed Command failed message --- deployability/modules/allocation/vagrant/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/vagrant/utils.py b/deployability/modules/allocation/vagrant/utils.py index c17311759f..c32d947a98 100644 --- a/deployability/modules/allocation/vagrant/utils.py +++ b/deployability/modules/allocation/vagrant/utils.py @@ -54,7 +54,7 @@ def remote_command(cls, command: str | list, remote_host_parameters: dict) -> st continue else: ssh.close() - raise ValueError(f"Command failed: {str(e)}") + raise ValueError(f"Remote command execution failed: {str(e)}") @classmethod def remote_copy(cls, instance_dir: Path, host_instance_dir: Path, remote_host_parameters: dict) -> str: From 34867ceb04444e18b1cd5d56df02ca5f6085b8c5 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Wed, 8 May 2024 16:56:37 -0300 Subject: [PATCH 128/195] Improvement in error message --- deployability/modules/allocation/vagrant/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deployability/modules/allocation/vagrant/utils.py b/deployability/modules/allocation/vagrant/utils.py index c32d947a98..ff9dcd03d1 100644 --- a/deployability/modules/allocation/vagrant/utils.py +++ b/deployability/modules/allocation/vagrant/utils.py @@ -39,6 +39,7 @@ def remote_command(cls, command: str | list, remote_host_parameters: dict) -> st ssh_parameters['password'] = remote_host_parameters['ssh_password'] max_retry = 3 + ssh_exceptions = (subprocess.CalledProcessError, paramiko.AuthenticationException, paramiko.SSHException, socket.timeout, ConnectionResetError) for attempt in range(max_retry): try: ssh.connect(**ssh_parameters) @@ -47,7 +48,7 @@ def remote_command(cls, command: str | list, remote_host_parameters: dict) -> st ssh.close() return stdout_text - except (subprocess.CalledProcessError, paramiko.AuthenticationException, paramiko.SSHException, socket.timeout) as e: + except ssh_exceptions as e: if attempt < max_retry - 1: logger.warning(f"SSH connection error: {str(e)}. Retrying in 30 seconds...") time.sleep(30) From 6467edab2b3a232c8cfa59b380bebba8f1dfe8e0 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Thu, 9 May 2024 11:21:09 -0300 Subject: [PATCH 129/195] Vagrant status None output is improved --- deployability/modules/allocation/vagrant/instance.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/deployability/modules/allocation/vagrant/instance.py b/deployability/modules/allocation/vagrant/instance.py index 055c575073..50df0e63a1 100755 --- a/deployability/modules/allocation/vagrant/instance.py +++ b/deployability/modules/allocation/vagrant/instance.py @@ -120,9 +120,10 @@ def status(self) -> str: output = self.__run_vagrant_command('status') vagrant_status = self.__parse_vagrant_status(output) if vagrant_status == None: - if VagrantUtils.remote_command(f"ls {self.host_instance_dir} > /dev/null 2>&1", self.remote_host_parameters): - if VagrantUtils.remote_command(f"prlctl list -a | grep {self.identifier} > /dev/null 2>&1", self.remote_host_parameters): - return "running" + if VagrantUtils.remote_command(f"sudo ls {self.host_instance_dir} > /dev/null 2>&1", self.remote_host_parameters): + if VagrantUtils.remote_command(f"sudo /usr/local/bin/prlctl list -a | grep {self.identifier} > /dev/null 2>&1", self.remote_host_parameters): + logger.warning(f"The instance was found, it will be deleted. The creation of the instance must be restarted again.") + self.delete() else: VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) raise ValueError(f"Instance {self.identifier} is not running, remote instance dir {self.host_instance_dir} was removed.") From cfd54c0a4e97c0892fd4ade740072b07d8f322d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Correa=20Rodr=C3=ADguez?= Date: Thu, 9 May 2024 18:42:20 +0200 Subject: [PATCH 130/195] macOS composite names simplified --- .../modules/allocation/static/specs/os.yml | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index e9600c2dc7..7722510d95 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -99,34 +99,34 @@ vagrant: box: generic/rocky9 box_version: 4.3.12 # Macos - macos-highsierra-10.13.6-amd64: + macos-highsierra-10.13-amd64: box: development/macos-high-sierra box_version: 0.0.0 - macos-mojave-10.14.3-amd64: + macos-mojave-10.14-amd64: box: development/macos-mojave box_version: 0.0.0 - macos-sierra-10.12.6-amd64: + macos-sierra-10.12-amd64: box: development/macos-sierra box_version: 0.0.0 - macos-sierracmake-10.12.6-amd64: + macos-sierracmake-10.12-amd64: box: development/macos-sierra_cmake box_version: 0.0.0 - macos-sierragcc9-10.12.6-amd64: + macos-sierragcc9-10.12-amd64: box: development/macos-sierra_gcc9 box_version: 0.0.0 - macos-monterey-12.6-arm64: + macos-monterey-12-arm64: box: macos-12 box_version: 0.0.0 - macos-ventura-13.4.1-amd64: + macos-ventura-13-amd64: box: development/macos-ventura box_version: 0.0.0 - macos-ventura-13.4.1-arm64: + macos-ventura-13-arm64: box: macos-13 box_version: 0.0.0 - macos-sonoma-14.4.1-amd64: + macos-sonoma-14-amd64: box: development/macos-sonoma box_version: 0.0.0 - macos-sonoma-14.0-arm64: + macos-sonoma-14-arm64: box: macos-14 box_version: 0.0.0 macos-ventura-sign-arm64: @@ -339,27 +339,27 @@ aws: zone: us-east-1 user: rocky # Macos - macos-ventura-13.6.4-amd64: + macos-ventura-13-amd64: ami: ami-0e94a57427e4e0611 zone: us-east-1 user: ec2-user - macos-ventura-13.6.4-arm64: + macos-ventura-13-arm64: ami: ami-0b288e8fb0e803b12 zone: us-east-1 user: ec2-user - macos-sonoma-14.3-amd64: + macos-sonoma-14-amd64: ami: ami-0873bd33f11079049 zone: us-east-1 user: ec2-user - macos-sonoma-14.3-arm64: + macos-sonoma-14-arm64: ami: ami-0dd07a50e5e3d3ccb zone: us-east-1 user: ec2-user - macos-monterey-12.7.3-arm64: + macos-monterey-12-arm64: ami: ami-0225fc57a58689798 zone: us-east-1 user: ec2-user - macos-monterey-12.7.3-amd64: + macos-monterey-12-amd64: ami: ami-0a410356d12928dbc zone: us-east-1 user: ec2-user From f079746e271acefb8fa76bc203ba8639f7f6cbf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Correa=20Rodr=C3=ADguez?= Date: Fri, 10 May 2024 10:27:29 +0200 Subject: [PATCH 131/195] Updated Windows 2019 box version --- deployability/modules/allocation/static/specs/os.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 7722510d95..92cc1879ab 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -150,7 +150,7 @@ vagrant: box_version: 0.3.0 windows-server-2019-amd64: box: gusztavvargadr/windows-server-2019-standard - box_version: 1809.0.2310 + box_version: 1809.0.2402 windows-server-2022-amd64: box: gusztavvargadr/windows-server-2022-standard box_version: 2102.0.2312 From 851c3cdad6b7280d4a1e97d0a5492bf0c8759733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Correa=20Rodr=C3=ADguez?= Date: Fri, 10 May 2024 12:30:55 +0200 Subject: [PATCH 132/195] Added `--instance-name` as compulsory in Vagrant deploy --- deployability/modules/allocation/README.MD | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 8444c1b602..cf308536cf 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -182,13 +182,13 @@ If one wishes to execute the allocaation module without installing the Workflow - Local deployment (Vagrant) ```bash - python3 modules/allocation/main.py --action create --provider '{{ vagrant }}' --size '{{ large }}' --composite-name '{{ composite-name }}' --inventory-output '{{ inventory }}' --track-output '{{ track }}' + python3 modules/allocation/main.py --action create --provider '{{ vagrant }}' --size '{{ large }}' --composite-name '{{ composite-name }}' --instance-name '{{ name }}' --inventory-output '{{ inventory }}' --track-output '{{ track }}' ``` Example: ```bash - python3 modules/allocation/main.py --action create --provider vagrant --size large --composite-name linux-ubuntu-22.04-amd64 --inventory-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/inventory.yaml" --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" + python3 modules/allocation/main.py --action create --provider vagrant --size large --composite-name linux-ubuntu-22.04-amd64 --instance-name "ubuntu_2204_amd_large" --inventory-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/inventory.yaml" --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" ``` - AWS deployment @@ -209,6 +209,16 @@ If one wishes to execute the allocaation module without installing the Workflow python3 modules/allocation/main.py --action create --provider aws --size large --composite-name linux-ubuntu-22.04-amd64 --inventory-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/inventory.yaml" --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" --label-termination-date "2024-03-20 21:00:00" --label-team devops ``` + Example 2: + ```bash + python3 modules/allocation/main.py --action create --provider aws --size small --composite-name linux-ubuntu-22.04-amd64 --inventory-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/inventory.yaml" --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" --label-termination-date "3d" --label-team devops --label-issue "https://github.com/wazuh/internal-devel-requests/issues/1115" + ``` + + Example 3: + ```bash + python3 modules/allocation/main.py --action create --provider aws --size small --composite-name linux-ubuntu-22.04-amd64 --inventory-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/inventory.yaml" --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" --label-termination-date "1d" --label-team devops --instance-name "ubuntu_2204_amd_large" + ``` + 2. Delete While in wazuh-qa/deployability @@ -282,7 +292,7 @@ If one wishes to execute the allocaation module without installing the Workflow This argument it is mandatory for AWS deploy, allows you to define the date on which the machine can be deleted. The allowed values are **1d** (where the **1** refers to the number of days the machine is needed) or with the following format **"2024-03-20 21:00:00"** - --instance-name - This argument allows us to define a custom name for the instance, if this argument is not used, the instance name is defined by other parameters entered, such as --label-issue or --composite-name. + This argument allows us to define a custom name for the instance. It is compulsory for the Vagrant deploy. For the AWS deploy, if this argument is not used, the instance name is defined by other parameters entered, such as --label-issue or --composite-name. --- ### Technical documentation From 6fe1ba024d5747b3826af14a6411e7e3f943a905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Correa=20Rodr=C3=ADguez?= Date: Fri, 10 May 2024 15:19:12 +0200 Subject: [PATCH 133/195] Improved `--instance-name` and `label-issue` documentation --- deployability/modules/allocation/README.MD | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index cf308536cf..3eb014aade 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -182,15 +182,22 @@ If one wishes to execute the allocaation module without installing the Workflow - Local deployment (Vagrant) ```bash - python3 modules/allocation/main.py --action create --provider '{{ vagrant }}' --size '{{ large }}' --composite-name '{{ composite-name }}' --instance-name '{{ name }}' --inventory-output '{{ inventory }}' --track-output '{{ track }}' + python3 modules/allocation/main.py --action create --provider '{{ vagrant }}' --size '{{ large }}' --composite-name '{{ composite-name }}' --instance-name '{{ name }}' --inventory-output '{{ inventory }}' --track-output '{{ track }}' ``` + >Note: In the case of Vagrant it is mandatory to specify either --instance-name or --label-issue parameters. + Example: ```bash python3 modules/allocation/main.py --action create --provider vagrant --size large --composite-name linux-ubuntu-22.04-amd64 --instance-name "ubuntu_2204_amd_large" --inventory-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/inventory.yaml" --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" ``` + Example 2: + ```bash + python3 modules/allocation/main.py --action create --provider vagrant --size micro --composite-name linux-ubuntu-22.04-amd64 --label-issue "https://github.com/wazuh/internal-devel-requests/issues/1115" --inventory-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/inventory.yaml" --track-output "/tmp/dtt1-poc/agent-linux-ubuntu-22.04-amd64/track.yaml" + ``` + - AWS deployment ```bash @@ -283,7 +290,7 @@ If one wishes to execute the allocaation module without installing the Workflow This argument allows us to define in which directory the files referring to the VM will be generated. By default, **/tmp/wazuh-qa** - --label-issue - This argument is only used in the case of AWS and is not mandatory, it allows us to create a label to reference the created instance to an issue on GitHub. It has to be a GitHub URL of a Wazuh repository, for example: **https://github.com/wazuh/internal-devel-requests/issues/1008** + This argument is mandatory for the Vagrant deploy if the **--instance-name** is not specified. For AWS deploy, it is not mandatory. It allows to create a label to reference the created instance to an issue on GitHub. It has to be a GitHub URL of a Wazuh repository, for example: **https://github.com/wazuh/internal-devel-requests/issues/1008** - --label-team This argument it is mandatory for AWS deploy, allows you to set the team that owns the VM to be able to track it. The valid options are: **qa**, **core**, **framework**, **devops**, **frontend**, **operations**, **cloud**, **threat-intel**, **marketing**, **documentation** @@ -292,7 +299,7 @@ If one wishes to execute the allocaation module without installing the Workflow This argument it is mandatory for AWS deploy, allows you to define the date on which the machine can be deleted. The allowed values are **1d** (where the **1** refers to the number of days the machine is needed) or with the following format **"2024-03-20 21:00:00"** - --instance-name - This argument allows us to define a custom name for the instance. It is compulsory for the Vagrant deploy. For the AWS deploy, if this argument is not used, the instance name is defined by other parameters entered, such as --label-issue or --composite-name. + This argument is mandatory for the Vagrant if the **--label-issue** is not specified. It allows us to define a custom name for the instance. In AWS deploy, if this argument is not used, the instance name is defined by other parameters entered, such as --label-issue or --composite-name. --- ### Technical documentation From 7df50d15c914e3e1b75cf0023015e99937bf4657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Correa=20Rodr=C3=ADguez?= Date: Fri, 10 May 2024 15:24:23 +0200 Subject: [PATCH 134/195] Improved parameter error message --- deployability/modules/allocation/vagrant/provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 5c86f29327..59bd155921 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -58,7 +58,7 @@ def _create_instance(cls, base_dir: Path, params: CreationPayload, config: Vagra repository = cls.generate_repository_name(str(issue_name.group(1))) name = repository + "-" + str(re.search(r'(\d+)$', issue).group(1)) + "-" + str(params.composite_name.split("-")[1]) + "-" + str(params.composite_name.split("-")[2]) else: - raise ValueError("Instance name or issue label is required.") + raise ValueError("Either --instance-name or --label-issue parameter is required.") instance_id = name + "-" + str(random.randint(0000, 9999)) # Create the instance directory. From 94bb6b84531ad3f8ee92d58f5b78c0fcd3da2aa6 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 10 May 2024 11:43:35 -0300 Subject: [PATCH 135/195] Improved port mapping --- deployability/modules/allocation/vagrant/utils.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/deployability/modules/allocation/vagrant/utils.py b/deployability/modules/allocation/vagrant/utils.py index ff9dcd03d1..4e80726173 100644 --- a/deployability/modules/allocation/vagrant/utils.py +++ b/deployability/modules/allocation/vagrant/utils.py @@ -111,10 +111,19 @@ def get_port(cls, remote_host_parameters: dict, arch: str = None) -> int: raise ValueError(f"ppc64 server has no available SSH ports.") else: + used_ports = [] for i in range(20): - port = f"432{random.randint(20, 40)}" + if used_ports == []: + port = f"432{random.randint(20, 40)}" + else: + while used_ports: + port = f"432{random.randint(20, 40)}" + if port not in used_ports: + break cmd = f"sudo lsof -i:{port}" output = cls.remote_command(cmd, remote_host_parameters) if not output: return port + else: + used_ports.append(port) raise ValueError(f"Server has no available SSH ports.") From 3430a10b2a09a8295335c92388d39d4a2db85747 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 10 May 2024 12:51:20 -0300 Subject: [PATCH 136/195] Improved port validation --- .../modules/allocation/vagrant/utils.py | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/deployability/modules/allocation/vagrant/utils.py b/deployability/modules/allocation/vagrant/utils.py index 4e80726173..7c50bf8c1e 100644 --- a/deployability/modules/allocation/vagrant/utils.py +++ b/deployability/modules/allocation/vagrant/utils.py @@ -112,18 +112,15 @@ def get_port(cls, remote_host_parameters: dict, arch: str = None) -> int: raise ValueError(f"ppc64 server has no available SSH ports.") else: used_ports = [] - for i in range(20): - if used_ports == []: - port = f"432{random.randint(20, 40)}" - else: - while used_ports: - port = f"432{random.randint(20, 40)}" - if port not in used_ports: - break - cmd = f"sudo lsof -i:{port}" - output = cls.remote_command(cmd, remote_host_parameters) - if not output: - return port - else: - used_ports.append(port) - raise ValueError(f"Server has no available SSH ports.") + all_ports = [f"432{i}" for i in range(20, 40)] + random.shuffle(all_ports) + for port in all_ports: + if port not in used_ports: + cmd = f"sudo lsof -i:{port}" + output = cls.remote_command(cmd, remote_host_parameters) + if not output: + return port + else: + used_ports.append(port) + else: + raise ValueError(f"The server has no available ports in the range 43220 to 43240.") From 93643a4b9711d4ef72f2f6489a6c492a8082895f Mon Sep 17 00:00:00 2001 From: c-bordon Date: Mon, 13 May 2024 08:26:02 -0300 Subject: [PATCH 137/195] Adding an exit for unexpected errors in the remote_command method --- deployability/modules/allocation/vagrant/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deployability/modules/allocation/vagrant/utils.py b/deployability/modules/allocation/vagrant/utils.py index 7c50bf8c1e..1ecd039532 100644 --- a/deployability/modules/allocation/vagrant/utils.py +++ b/deployability/modules/allocation/vagrant/utils.py @@ -56,6 +56,9 @@ def remote_command(cls, command: str | list, remote_host_parameters: dict) -> st else: ssh.close() raise ValueError(f"Remote command execution failed: {str(e)}") + except Exception as e: + ssh.close() + raise ValueError(f"An unexpected error occurred when executing the remote command: {str(e)}") @classmethod def remote_copy(cls, instance_dir: Path, host_instance_dir: Path, remote_host_parameters: dict) -> str: From 1d6421183af1095b2ebc00ed6f9cac3e78a0fb72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Correa=20Rodr=C3=ADguez?= Date: Tue, 14 May 2024 11:28:53 +0200 Subject: [PATCH 138/195] Added macOS Sonoma 14.5 beta to allocator --- deployability/modules/allocation/static/specs/os.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 92cc1879ab..4bf086744b 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -126,6 +126,9 @@ vagrant: macos-sonoma-14-amd64: box: development/macos-sonoma box_version: 0.0.0 + macos-sonoma-14.5-amd64: + box: development/macos-sonoma-1450 + box_version: 0.0.0 macos-sonoma-14-arm64: box: macos-14 box_version: 0.0.0 From b2029ec05cb26565490b862d65a3c59bd3f441ff Mon Sep 17 00:00:00 2001 From: Raul Del Pozo Moreno Date: Wed, 15 May 2024 14:54:39 +0200 Subject: [PATCH 139/195] Fixed agent RPM aarch64 nomenclature --- deployability/modules/testing/tests/helpers/agent.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index be8890d79a..c1ea49f2a9 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -35,7 +35,7 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv ]) elif distribution == 'rpm' and 'arm64' in architecture: commands.extend([ - f"curl -o wazuh-agent-{wazuh_version}-1aarch64.rpm https://{s3_url}.wazuh.com/{release}/yum/wazuh-agent-{wazuh_version}-1.aarch64.rpm && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' rpm -ihv wazuh-agent-{wazuh_version}-1.aarch64.rpm" + f"curl -o wazuh-agent-{wazuh_version}-1.aarch64.rpm https://{s3_url}.wazuh.com/{release}/yum/wazuh-agent-{wazuh_version}-1.aarch64.rpm && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' rpm -ihv wazuh-agent-{wazuh_version}-1.aarch64.rpm" ]) elif distribution == 'deb' and 'amd64' in architecture: commands.extend([ @@ -122,7 +122,7 @@ def register_agent(inventory_path, manager_path): elif os_type == 'macos': try: if 'amazonaws' in manager_host and 'amazonaws' in agent_host: - host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) + host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) else: host_ip = HostInformation.get_public_ip_from_aws_dns(manager_host) commands = [ From 0bbab70a5508a76195454e5ab34908f64408b274 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Wed, 15 May 2024 12:07:37 -0300 Subject: [PATCH 140/195] Adding new validation method for inventory and track files, and adding configurations for forcing ssh connection only for private ip for macOS vagrant deployments --- .../modules/allocation/allocation.py | 65 ++++++++++++++----- .../modules/allocation/generic/models.py | 2 + .../modules/allocation/static/specs/os.yml | 49 ++++++++++++++ .../static/templates/vagrant_Virtual_box.j2 | 2 +- .../templates/vagrant_parallels_intel.j2 | 1 - .../modules/allocation/vagrant/instance.py | 5 +- .../modules/allocation/vagrant/models.py | 1 + .../modules/allocation/vagrant/provider.py | 11 ++-- .../modules/allocation/vagrant/utils.py | 2 +- 9 files changed, 112 insertions(+), 26 deletions(-) diff --git a/deployability/modules/allocation/allocation.py b/deployability/modules/allocation/allocation.py index 4b3b973eea..77d86fba4a 100755 --- a/deployability/modules/allocation/allocation.py +++ b/deployability/modules/allocation/allocation.py @@ -2,12 +2,15 @@ # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +import time +import os +from pathlib import Path + import yaml -import paramiko, logging, time +import paramiko +import logging import winrm -from pathlib import Path - from .aws.provider import AWSProvider, AWSConfig from .generic import Instance, Provider, models from .generic.utils import logger @@ -60,10 +63,12 @@ def __create(cls, payload: models.CreationPayload): instance.start() logger.info(f"Instance {instance.identifier} started.") # Generate the inventory and track files. - inventory = cls.__generate_inventory(instance, payload.inventory_output, instance_params.composite_name) + inventory_path = cls.__get_auxiliar_files_path(instance_params.inventory_output, instance, 'inventory') + inventory = cls.__generate_inventory(instance, inventory_path, instance_params.composite_name) # Validate connection check_connection = cls.__check_connection(inventory) - track_file = cls.__generate_track_file(instance, payload.provider, payload.track_output) + track_path = cls.__get_auxiliar_files_path(instance_params.track_output, instance, 'track') + track_file = cls.__generate_track_file(instance, payload.provider, track_path, inventory_path) if check_connection is False: if payload.rollback: logger.warning(f"Rolling back instance creation.") @@ -122,10 +127,6 @@ def __generate_inventory(instance: Instance, inventory_path: Path, composite_nam inventory_path (Path): The path where the inventory file will be generated. composite_name (str): Composite name of the instance to be provisioned. """ - if inventory_path is None: - inventory_path = Path(instance.path, 'inventory.yml') - if not str(inventory_path).endswith('.yml') and not str(inventory_path).endswith('.yaml'): - inventory_path = Path(inventory_path, 'inventory.yml') if not inventory_path.parent.exists(): inventory_path.parent.mkdir(parents=True, exist_ok=True) ssh_config = instance.ssh_connection_info() @@ -164,7 +165,7 @@ def __generate_inventory(instance: Instance, inventory_path: Path, composite_nam return inventory @staticmethod - def __generate_track_file(instance: Instance, provider_name: str, track_path: Path) -> None: + def __generate_track_file(instance: Instance, provider_name: str, track_path: Path, inventory_path: Path) -> None: """ Generates a track file. @@ -172,23 +173,23 @@ def __generate_track_file(instance: Instance, provider_name: str, track_path: P instance (Instance): The instance for which the track file is to be generated. provider_name (str): The name of the provider. track_path (Path): The path where the track file will be generated. + inventory_path (Path): The path of the inventory file. """ - if track_path is None: - track_path = Path(instance.path, 'track.yml') - if not str(track_path).endswith('.yml') and not str(track_path).endswith('.yaml'): - track_path = Path(track_path, 'track.yml') if not track_path.parent.exists(): track_path.parent.mkdir(parents=True, exist_ok=True) - ssh_config = instance.ssh_connection_info() + with open(str(inventory_path), 'r') as f: + inventory = models.InventoryOutput(**yaml.safe_load(f)) + port = inventory.ansible_port track = models.TrackOutput(identifier=instance.identifier, provider=provider_name, instance_dir=str(instance.path), key_path=str(instance.credentials.key_path), host_identifier=str(instance.host_identifier), host_instance_dir=str(instance.host_instance_dir), - ssh_port=ssh_config.port, + ssh_port=port, platform=instance.platform, - arch=instance.arch) + arch=instance.arch, + virtualizer=instance.virtualizer) with open(track_path, 'w') as f: yaml.dump(track.model_dump(), f) if Path(str(instance.path) + "/port.txt").exists(): @@ -258,3 +259,33 @@ def __check_connection(inventory: models.InventoryOutput, attempts=30, sleep=30) logger.error(f'Connection attempts failed after {attempts} tries. Connection timeout.') return False + + @staticmethod + def __get_auxiliar_files_path(path: Path, instance: Instance, file: str) -> Path: + """ + Get the path of the auxiliar files. + + Args: + path (Path): The path of the instance. + instance (Instance): The instance object. + file (str): The type of file. + + Returns: + Path: The path of the auxiliar files. + """ + if path is None: + return Path(os.path.join(instance.path, file + '.yml')) + if path.exists() and path.is_dir(): + return Path(os.path.join(path, file + '.yml')) + if str(path).endswith('.yml') or str(path).endswith('.yaml'): + if file in str(path): + return path + elif file == 'inventory' and 'track' in str(path): + return Path(str(path).replace('track', 'inventory')) + elif file == 'track' and 'inventory' in str(path): + return Path(str(path).replace('inventory', 'track')) + else: + base, ext = os.path.splitext(path) + return Path(f"{base}-{file}{ext}") + else: + raise ValueError(f"Invalid path for auxiliary file, must be a yaml file: {path}") diff --git a/deployability/modules/allocation/generic/models.py b/deployability/modules/allocation/generic/models.py index 4f809043c3..afd014f200 100644 --- a/deployability/modules/allocation/generic/models.py +++ b/deployability/modules/allocation/generic/models.py @@ -44,6 +44,7 @@ class TrackOutput(BaseModel): ssh_port: int | None = None platform: str arch: str + virtualizer: str | None = None class InputPayload(BaseModel): @@ -125,6 +126,7 @@ class InstancePayload(BaseModel): arch: str | None = None user: str | None = None docker_image: str | None = None + virtualizer: str | None = None @field_validator('ssh_port', mode='before') def validate_port(cls, value) -> str | None: diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 4bf086744b..613176220d 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -3,160 +3,209 @@ vagrant: linux-ubuntu-16.04-amd64: box: generic/ubuntu1604 box_version: 4.3.8 + virtualizer: virtualbox linux-ubuntu-18.04-amd64: box: ubuntu/bionic64 box_version: 20230607.0.0 + virtualizer: virtualbox linux-ubuntu-20.04-amd64: box: ubuntu/focal64 box_version: 20240130.0.0 + virtualizer: virtualbox linux-ubuntu-22.04-amd64: box: ubuntu/jammy64 box_version: 20240131.0.0 + virtualizer: virtualbox # Debian linux-debian-9-amd64: box: generic/debian9 box_version: 4.3.8 + virtualizer: virtualbox linux-debian-9-ppc64: box: testing-debian-image box_version: latest + virtualizer: docker linux-debian-10-amd64: box: generic/debian10 box_version: 4.3.8 + virtualizer: virtualbox linux-debian-11-amd64: box: debian/bullseye64 box_version: 11.20231211.1 + virtualizer: virtualbox linux-debian-12-amd64: box: debian/bookworm64 box_version: 12.20231211.1 + virtualizer: virtualbox # Oracle Linux linux-oracle-7-amd64: box: generic/oracle7 box_version: 4.3.12 + virtualizer: virtualbox linux-oracle-8-amd64: box: generic/oracle8 box_version: 4.3.12 + virtualizer: virtualbox linux-oracle-9-amd64: box: generic/oracle9 box_version: 4.3.12 + virtualizer: virtualbox # openSUSE Linux linux-opensuse-15-amd64: box: generic/opensuse15 box_version: 4.3.8 + virtualizer: virtualbox linux-opensuse-tumbleweed-amd64: box: opensuse/Tumbleweed.x86_64 box_version: 1.0.20240207 + virtualizer: virtualbox # Centos linux-centos-7-amd64: box: centos/7 box_version: 2004.01 + virtualizer: virtualbox linux-centos-7-ppc64: box: testing-centos-image box_version: latest + virtualizer: docker linux-centos-8-amd64: box: generic/centos8 box_version: 4.3.8 + virtualizer: virtualbox linux-centos-9-amd64: box: generic/centos9s box_version: 4.3.12 + virtualizer: virtualbox # Redhat linux-redhat-7-amd64: box: generic/rhel7 box_version: 3.6.8 + virtualizer: virtualbox linux-redhat-8-amd64: box: generic/rhel8 box_version: 3.6.8 + virtualizer: virtualbox linux-redhat-9-amd64: box: generic/rhel9 box_version: 4.3.12 + virtualizer: virtualbox # Amazon linux-amazon-2-amd64: box: bento/amazonlinux-2 box_version: 202305.26.0 + virtualizer: virtualbox # Fedora linux-fedora-28-amd64: box: generic/fedora28 box_version: 4.3.8 + virtualizer: virtualbox linux-fedora-29-amd64: box: generic/fedora29 box_version: 4.3.8 + virtualizer: virtualbox linux-fedora-31-amd64: box: generic/fedora31 box_version: 4.3.8 + virtualizer: virtualbox linux-fedora-32-amd64: box: generic/fedora32 box_version: 4.3.8 + virtualizer: virtualbox linux-fedora-37-amd64: box: generic/fedora37 box_version: 4.3.8 + virtualizer: virtualbox linux-fedora-38-amd64: box: alvistack/fedora-38 box_version: 20240206.1.1 + virtualizer: virtualbox # Rocky Linux linux-rocky-8-amd64: box: generic/rocky8 box_version: 4.3.12 + virtualizer: virtualbox linux-rocky-9-amd64: box: generic/rocky9 box_version: 4.3.12 + virtualizer: virtualbox # Macos macos-highsierra-10.13-amd64: box: development/macos-high-sierra box_version: 0.0.0 + virtualizer: virtualbox macos-mojave-10.14-amd64: box: development/macos-mojave box_version: 0.0.0 + virtualizer: virtualbox macos-sierra-10.12-amd64: box: development/macos-sierra box_version: 0.0.0 + virtualizer: virtualbox macos-sierracmake-10.12-amd64: box: development/macos-sierra_cmake box_version: 0.0.0 + virtualizer: virtualbox macos-sierragcc9-10.12-amd64: box: development/macos-sierra_gcc9 box_version: 0.0.0 + virtualizer: virtualbox macos-monterey-12-arm64: box: macos-12 box_version: 0.0.0 + virtualizer: parallels macos-ventura-13-amd64: box: development/macos-ventura box_version: 0.0.0 + virtualizer: parallels macos-ventura-13-arm64: box: macos-13 box_version: 0.0.0 + virtualizer: parallels macos-sonoma-14-amd64: box: development/macos-sonoma box_version: 0.0.0 + virtualizer: parallels macos-sonoma-14.5-amd64: box: development/macos-sonoma-1450 box_version: 0.0.0 + virtualizer: parallels macos-sonoma-14-arm64: box: macos-14 box_version: 0.0.0 + virtualizer: parallels macos-ventura-sign-arm64: box: macos-ventura-sign box_version: 0.0.0 + virtualizer: parallels # Windows windows-desktop-10-amd64: box: gusztavvargadr/windows-10 box_version: 2202.0.2312 + virtualizer: virtualbox windows-desktop-11-amd64: box: gusztavvargadr/windows-11 box_version: 2302.0.2312 + virtualizer: virtualbox windows-server-2012-amd64: box: jborean93/WindowsServer2012 box_version: 1.2.0 + virtualizer: virtualbox windows-server-2012r2-amd64: box: addle/windows-server-2012-r2 box_version: 1.20170205.1 + virtualizer: virtualbox windows-server-2016-amd64: box: mwrock/Windows2016 box_version: 0.3.0 + virtualizer: virtualbox windows-server-2019-amd64: box: gusztavvargadr/windows-server-2019-standard box_version: 1809.0.2402 + virtualizer: virtualbox windows-server-2022-amd64: box: gusztavvargadr/windows-server-2022-standard box_version: 2102.0.2312 + virtualizer: virtualbox aws: # Ubuntu diff --git a/deployability/modules/allocation/static/templates/vagrant_Virtual_box.j2 b/deployability/modules/allocation/static/templates/vagrant_Virtual_box.j2 index 4643206504..379c3898f2 100644 --- a/deployability/modules/allocation/static/templates/vagrant_Virtual_box.j2 +++ b/deployability/modules/allocation/static/templates/vagrant_Virtual_box.j2 @@ -1,7 +1,7 @@ Vagrant.configure("2") do |config| config.vm.box = "{{ config.box }}" config.vm.network :private_network, type: "dhcp" - config.vm.network "forwarded_port", guest: 22, host: "{{ config.port }}" + config.vm.network "forwarded_port", guest: 22, host: "{{ config.port }}", host_ip: "{{ config.ip }}" config.vm.synced_folder ".", "/vagrant", disabled: true diff --git a/deployability/modules/allocation/static/templates/vagrant_parallels_intel.j2 b/deployability/modules/allocation/static/templates/vagrant_parallels_intel.j2 index 1af532acd1..742a999c3a 100755 --- a/deployability/modules/allocation/static/templates/vagrant_parallels_intel.j2 +++ b/deployability/modules/allocation/static/templates/vagrant_parallels_intel.j2 @@ -12,6 +12,5 @@ Vagrant.configure("2") do |config| # Network settings config.ssh.forward_agent = true - config.vm.network "forwarded_port", guest: 22, host: "{{ config.port }}" config.vm.synced_folder ".", "/vagrant", disabled: true end diff --git a/deployability/modules/allocation/vagrant/instance.py b/deployability/modules/allocation/vagrant/instance.py index 50df0e63a1..efffa23f1c 100755 --- a/deployability/modules/allocation/vagrant/instance.py +++ b/deployability/modules/allocation/vagrant/instance.py @@ -47,6 +47,7 @@ def __init__(self, instance_parameters: InstancePayload, credentials: VagrantCre self.platform: str = instance_parameters.platform self.arch: str = instance_parameters.arch self.docker_image: str = instance_parameters.docker_image + self.virtualizer: str = instance_parameters.virtualizer def start(self) -> None: """ @@ -105,7 +106,7 @@ def delete(self) -> None: if str(self.host_identifier) == "macstadium": logger.debug(f"Deleting remote directory {self.host_instance_dir}") VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) - if self.arch == 'arm64': + if self.virtualizer == 'parallels': logger.debug(f"Killing remote process on port {self.ssh_port}") proccess = VagrantUtils.remote_command(f"sudo lsof -Pi :{self.ssh_port} -sTCP:LISTEN -t", self.remote_host_parameters) VagrantUtils.remote_command(f"sudo kill -9 {proccess}", self.remote_host_parameters) @@ -170,7 +171,7 @@ def ssh_connection_info(self) -> ConnectionInfo: if str(self.host_identifier) == "macstadium": if not Path(tmp_port_file).exists(): port = VagrantUtils.get_port(self.remote_host_parameters) - cmd = f"sudo /usr/bin/ssh -i /Users/jenkins/.ssh/localhost -L {server_ip}:{port}:{ip}:22 -N 127.0.0.1 -f" + cmd = f"/usr/bin/ssh -i /Users/jenkins/.ssh/localhost -L {server_ip}:{port}:{ip}:22 -N 127.0.0.1 -f" VagrantUtils.remote_command(cmd, self.remote_host_parameters) with open(tmp_port_file, 'w') as f: f.write(port) diff --git a/deployability/modules/allocation/vagrant/models.py b/deployability/modules/allocation/vagrant/models.py index 54f5a50ee8..99a3b9ad06 100644 --- a/deployability/modules/allocation/vagrant/models.py +++ b/deployability/modules/allocation/vagrant/models.py @@ -20,6 +20,7 @@ class VagrantConfig(ProviderConfig): port: int = None platform: str arch: str + virtualizer: str @field_validator('public_key', mode='before') @classmethod diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 5c86f29327..1e87dd1a23 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -128,6 +128,7 @@ def _create_instance(cls, base_dir: Path, params: CreationPayload, config: Vagra instance_params['arch'] = arch instance_params['docker_image'] = config.box instance_params['ssh_port'] = config.port + instance_params['virtualizer'] = config.virtualizer return VagrantInstance(InstancePayload(**instance_params), credentials) @staticmethod @@ -174,6 +175,7 @@ def _destroy_instance(cls, destroy_parameters: InstancePayload) -> None: instance_params['remote_host_parameters'] = remote_host_parameters instance_params['arch'] = destroy_parameters.arch instance_params['ssh_port'] = destroy_parameters.ssh_port + instance_params['virtualizer'] = destroy_parameters.virtualizer instance = VagrantInstance(InstancePayload(**instance_params)) logger.debug(f"Destroying instance {destroy_parameters.identifier}") instance.delete() @@ -211,8 +213,7 @@ def __render_vagrantfile(cls, config: VagrantConfig) -> str: environment = Environment(loader=FileSystemLoader(cls.TEMPLATES_DIR)) if config.platform == 'macos': if config.arch == 'amd64': - virtualbox_boxes = ['development/macos-high-sierra', 'development/macos-mojave', 'development/macos-sierra', 'development/macos-sierra_cmake', 'development/macos-sierra_gcc9'] - if config.box not in virtualbox_boxes: + if config.virtualizer == 'parallels': template = environment.get_template("vagrant_parallels_intel.j2") else: template = environment.get_template("vagrant_Virtual_box.j2") @@ -239,9 +240,9 @@ def __parse_config(cls, params: CreationPayload, credentials: VagrantCredentials size_specs = cls._get_size_specs(params.size) os_specs = cls._get_os_specs(params.composite_name) # Parse the configuration. - config['ip'] = cls.__get_available_ip() config['box'] = str(os_specs['box']) config['box_version'] = str(os_specs['box_version']) + config['virtualizer'] = str(os_specs['virtualizer']) config['private_key'] = str(credentials.key_path) config['public_key'] = str(credentials.key_path.with_suffix('.pub')) config['cpu'] = size_specs['cpu'] @@ -249,12 +250,14 @@ def __parse_config(cls, params: CreationPayload, credentials: VagrantCredentials config['name'] = instance_id config['platform'] = params.composite_name.split("-")[0] config['arch'] = params.composite_name.split("-")[3] + config['ip'] = cls.__get_available_ip() - if params.composite_name.startswith("macos") and params.composite_name.endswith("amd64") or params.composite_name.split("-")[3] == 'ppc64': + if config['platform'] == 'macos' and config['virtualizer'] == 'virtualbox' or config['virtualizer'] == 'docker': tmp_port_file = str(instance_dir) + "/port.txt" config['port'] = VagrantUtils.get_port(remote_host_parameters, config['arch']) with open(tmp_port_file, 'w') as f: f.write(config['port']) + config['ip'] = remote_host_parameters['server_ip'] return VagrantConfig(**config) diff --git a/deployability/modules/allocation/vagrant/utils.py b/deployability/modules/allocation/vagrant/utils.py index 1ecd039532..78b4cf4134 100644 --- a/deployability/modules/allocation/vagrant/utils.py +++ b/deployability/modules/allocation/vagrant/utils.py @@ -34,7 +34,7 @@ def remote_command(cls, command: str | list, remote_host_parameters: dict) -> st 'username': remote_host_parameters['ssh_user'] } if remote_host_parameters.get('ssh_key'): - ssh_parameters['key_filename'] = remote_host_parameters['ssh_key'] + ssh_parameters['key_filename'] = str(remote_host_parameters['ssh_key']) else: ssh_parameters['password'] = remote_host_parameters['ssh_password'] From e1971c1d1dd55a31a53f8779705bfb71b1523915 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Wed, 15 May 2024 23:43:50 -0300 Subject: [PATCH 141/195] Add the possibility of installing from live and pre-release is added --- deployability/modules/provision/models.py | 2 +- deployability/modules/testing/main.py | 2 +- .../modules/testing/tests/helpers/agent.py | 12 +++--- .../modules/testing/tests/helpers/central.py | 22 +++++++--- .../modules/testing/tests/helpers/manager.py | 22 ++++++---- .../test_central_components/test_install.py | 6 ++- .../tests/test_manager/test_install.py | 6 ++- .../dtt1-central_components-poc-vagrant.yaml | 15 ++----- .../vagrant/dtt1-managers-poc-vagrant.yaml | 40 ++++--------------- 9 files changed, 58 insertions(+), 69 deletions(-) diff --git a/deployability/modules/provision/models.py b/deployability/modules/provision/models.py index e80e9b4271..e0c44581e1 100755 --- a/deployability/modules/provision/models.py +++ b/deployability/modules/provision/models.py @@ -11,7 +11,7 @@ class ComponentInfo(BaseModel): type: str = "package" version: str = "" dependencies: dict | None = None - live : bool = False + live : bool = True class InputPayload(BaseModel): diff --git a/deployability/modules/testing/main.py b/deployability/modules/testing/main.py index fc71d3ef89..27f55322ee 100755 --- a/deployability/modules/testing/main.py +++ b/deployability/modules/testing/main.py @@ -22,7 +22,7 @@ def parse_arguments(): parser.add_argument("--wazuh-version", required=True) parser.add_argument("--wazuh-revision", required=True) parser.add_argument("--wazuh-branch", required=False) - parser.add_argument("--live", required=False) + parser.add_argument("--live", required=False, default=True) return parser.parse_args() diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index be8890d79a..c7268f25cb 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -15,12 +15,12 @@ class WazuhAgent: @staticmethod def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, live) -> None: - if live: - s3_url = 'packages' - release = wazuh_version[:1] + ".x" - else: + if live == "False": s3_url = 'packages-dev' - release = 'pre-release' + else: + s3_url = 'packages' + + release = '.'.join(wazuh_version.split('.')[:2]) os_type = HostInformation.get_os_type(inventory_path) architecture = HostInformation.get_architecture(inventory_path) @@ -122,7 +122,7 @@ def register_agent(inventory_path, manager_path): elif os_type == 'macos': try: if 'amazonaws' in manager_host and 'amazonaws' in agent_host: - host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) + host_ip = HostInformation.get_internal_ip_from_aws_dns(manager_host) else: host_ip = HostInformation.get_public_ip_from_aws_dns(manager_host) commands = [ diff --git a/deployability/modules/testing/tests/helpers/central.py b/deployability/modules/testing/tests/helpers/central.py index 3da620cc9d..2fa4155ddd 100644 --- a/deployability/modules/testing/tests/helpers/central.py +++ b/deployability/modules/testing/tests/helpers/central.py @@ -10,7 +10,7 @@ class WazuhCentralComponents: @staticmethod - def install_aio(inventory_path, wazuh_version) -> None: + def install_aio(inventory_path, wazuh_version, live) -> None: """ Installs Wazuh central components (AIO) in the host @@ -19,23 +19,33 @@ def install_aio(inventory_path, wazuh_version) -> None: wazuh_version (str): major.minor.patch """ - wazuh_version = '.'.join(wazuh_version.split('.')[:2]) os_name = HostInformation.get_os_name_from_inventory(inventory_path) + if live == "False": + s3_url = 'packages-dev.wazuh.com' + else: + s3_url = 'packages.wazuh.com' + + release = '.'.join(wazuh_version.split('.')[:2]) + + + logger.info(f'Installing Manager with https://{s3_url}/{release}/wazuh-install.sh') + if HostInformation.has_curl(inventory_path): commands = [ - f"curl -sO https://packages.wazuh.com/{wazuh_version}/wazuh-install.sh && sudo bash ./wazuh-install.sh -a --ignore-check" + f"curl -sO https://{s3_url}/{release}/wazuh-install.sh", + f"sudo bash ./wazuh-install.sh -a --ignore-check" ] else: commands = [ - f"wget https://packages.wazuh.com/{wazuh_version}/wazuh-install.sh && sudo bash ./wazuh-install.sh -a --ignore-check" + f"wget https://{s3_url}/{release}/wazuh-install.sh", + f"sudo bash ./wazuh-install.sh -a --ignore-check" ] logger.info(f'Installing Wazuh central components (AIO) in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') ConnectionManager.execute_commands(inventory_path, commands) - @staticmethod def uninstall_aio(inventory_path) -> None: """ @@ -53,7 +63,7 @@ def uninstall_aio(inventory_path) -> None: @staticmethod def _install_aio_callback(wazuh_params, host_params): - WazuhCentralComponents.install_aio(host_params, wazuh_params['wazuh_version']) + WazuhCentralComponents.install_aio(host_params, wazuh_params['wazuh_version'], wazuh_params['live']) @staticmethod diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index a76659eb85..c4e673c710 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -15,7 +15,7 @@ class WazuhManager: @staticmethod - def install_manager(inventory_path, node_name, wazuh_version) -> None: + def install_manager(inventory_path, node_name, wazuh_version, live: bool) -> None: """ Installs Wazuh Manager in the host @@ -25,17 +25,25 @@ def install_manager(inventory_path, node_name, wazuh_version) -> None: wazuh_version (str): major.minor.patch """ - wazuh_version = '.'.join(wazuh_version.split('.')[:2]) os_name = HostInformation.get_os_name_from_inventory(inventory_path) + if live == "False": + s3_url = 'packages-dev.wazuh.com' + else: + s3_url = 'packages.wazuh.com' + + release = '.'.join(wazuh_version.split('.')[:2]) + + logger.info(f'Installing Manager with https://{s3_url}/{release}/wazuh-install.sh') + if os_name == 'debian': commands = [ - f"wget https://packages.wazuh.com/{wazuh_version}/wazuh-install.sh", + f"wget https://{s3_url}/{release}/wazuh-install.sh", f"bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" ] else: commands = [ - f"curl -sO https://packages.wazuh.com/{wazuh_version}/wazuh-install.sh", + f"curl -sO https://{s3_url}/{release}/wazuh-install.sh", f"bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" ] logger.info(f'Installing Manager in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') @@ -43,7 +51,7 @@ def install_manager(inventory_path, node_name, wazuh_version) -> None: @staticmethod - def install_managers(inventories_paths=[], node_names=[], wazuh_versions=[]) -> None: + def install_managers(inventories_paths=[], node_names=[], wazuh_versions=[], live=[]) -> None: """ Install Wazuh Managers in the hosts @@ -56,7 +64,7 @@ def install_managers(inventories_paths=[], node_names=[], wazuh_versions=[]) -> for inventory in inventories_paths: for node_name in node_names: for wazuh_version in wazuh_versions: - WazuhManager.install_manager(inventory, node_name, wazuh_version) + WazuhManager.install_manager(inventory, node_name, wazuh_version, live[index]) @staticmethod @@ -105,7 +113,7 @@ def uninstall_managers(inventories_paths=[]) -> None: @staticmethod def _install_manager_callback(wazuh_params, manager_name, manager_params): - WazuhManager.install_manager(manager_params, manager_name, wazuh_params['wazuh_version']) + WazuhManager.install_manager(manager_params, manager_name, wazuh_params['wazuh_version'], wazuh_params['live']) @staticmethod diff --git a/deployability/modules/testing/tests/test_central_components/test_install.py b/deployability/modules/testing/tests/test_central_components/test_install.py index d8ecc71d66..78a780a276 100644 --- a/deployability/modules/testing/tests/test_central_components/test_install.py +++ b/deployability/modules/testing/tests/test_central_components/test_install.py @@ -21,12 +21,14 @@ def wazuh_params(request): wazuh_revision = request.config.getoption('--wazuh_revision') dependencies = request.config.getoption('--dependencies') targets = request.config.getoption('--targets') + live = request.config.getoption('--live') return { 'wazuh_version': wazuh_version, 'wazuh_revision': wazuh_revision, 'dependencies': dependencies, - 'targets': targets + 'targets': targets, + 'live': live } @@ -62,7 +64,7 @@ def test_installation(wazuh_params): # Install central components and perform checkfile testing for _, manager_params in wazuh_params['managers'].items(): - WazuhCentralComponents.install_aio(manager_params, wazuh_params['wazuh_version']) + WazuhCentralComponents.install_aio(manager_params, wazuh_params['wazuh_version'], wazuh_params['live']) # Validation of directory of the components for manager in wazuh_params['managers'].values(): diff --git a/deployability/modules/testing/tests/test_manager/test_install.py b/deployability/modules/testing/tests/test_manager/test_install.py index d9a0f5582c..39279c672a 100644 --- a/deployability/modules/testing/tests/test_manager/test_install.py +++ b/deployability/modules/testing/tests/test_manager/test_install.py @@ -18,12 +18,14 @@ def wazuh_params(request): wazuh_revision = request.config.getoption('--wazuh_revision') dependencies = request.config.getoption('--dependencies') targets = request.config.getoption('--targets') + live = request.config.getoption('--live') return { 'wazuh_version': wazuh_version, 'wazuh_revision': wazuh_revision, 'dependencies': dependencies, - 'targets': targets + 'targets': targets, + 'live': live } @@ -61,7 +63,7 @@ def test_installation(wazuh_params): # Install managers and perform checkfile testing for manager_name, manager_params in wazuh_params['managers'].items(): - WazuhManager.install_manager(manager_params, manager_name, wazuh_params['wazuh_version']) + WazuhManager.install_manager(manager_params, manager_name, wazuh_params['wazuh_version'], wazuh_params['live']) # Validation of activity and directory of the managers for manager in wazuh_params['managers'].values(): diff --git a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml index dd18640ae6..6ed6a32e41 100644 --- a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml @@ -2,17 +2,7 @@ version: 0.1 description: This workflow is used to test the Wazuh manager deployment for DDT1 PoC variables: central_components-os: - - linux-ubuntu-20.04-amd64 - linux-ubuntu-22.04-amd64 - - linux-amazon-2-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 infra-provider: vagrant working-dir: /tmp/dtt1-poc @@ -30,6 +20,7 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{central_components}" + - instance-name: "{central_components}" - inventory-output: "{working-dir}/central_components-{central_components}/inventory.yaml" - track-output: "{working-dir}/central_components-{central_components}/track.yaml" - label-termination-date: "1d" @@ -62,8 +53,10 @@ tasks: - component: "central_components" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False on-error: "abort-all" foreach: - variable: central_components-os as: central_components + depends-on: + - "allocate-central_components-{central_components}" diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index 6a04352b85..d29a62360d 100644 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -2,18 +2,8 @@ version: 0.1 description: This workflow is used to test manager deployment for DDT1 PoC variables: manager-os: - - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-oracle-9-amd64 - - linux-amazon-2-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - linux-centos-7-amd64 - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 infra-provider: vagrant working-dir: /tmp/dtt1-poc @@ -31,8 +21,11 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{manager}" + - instance-name: "{manager}" - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" - track-output: "{working-dir}/manager-{manager}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" on-error: "abort-all" foreach: - variable: manager-os @@ -49,32 +42,13 @@ tasks: - modules/testing/main.py - targets: - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - - wazuh-3: "{working-dir}/manager-linux-ubuntu-22.04-amd64/inventory.yaml" - - wazuh-4: "{working-dir}/manager-linux-oracle-9-amd64/inventory.yaml" - - wazuh-5: "{working-dir}/manager-linux-amazon-2-amd64/inventory.yaml" - - wazuh-6: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" - - wazuh-7: "{working-dir}/manager-linux-redhat-8-amd64/inventory.yaml" - - wazuh-8: "{working-dir}/manager-linux-redhat-9-amd64/inventory.yaml" - - wazuh-9: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" - - wazuh-10: "{working-dir}/manager-linux-debian-10-amd64/inventory.yaml" - - wazuh-11: "{working-dir}/manager-linux-debian-11-amd64/inventory.yaml" - - wazuh-12: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" - - tests: "install,restart,stop,uninstall" + - wazuh-2: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" + - tests: "install" - component: "manager" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False depends-on: - - "allocate-manager-linux-ubuntu-20.04-amd64" - - "allocate-manager-linux-ubuntu-22.04-amd64" - - "allocate-manager-linux-amazon-2-amd64" - - "allocate-manager-linux-redhat-7-amd64" - - "allocate-manager-linux-redhat-8-amd64" - - "allocate-manager-linux-redhat-9-amd64" - "allocate-manager-linux-centos-7-amd64" - "allocate-manager-linux-centos-8-amd64" - - "allocate-manager-linux-debian-10-amd64" - - "allocate-manager-linux-debian-11-amd64" - - "allocate-manager-linux-debian-12-amd64" - - "allocate-manager-linux-oracle-9-amd64" + From 5d981c1e5083f7c3df04135e3dec6b367ff41d90 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Wed, 15 May 2024 23:44:56 -0300 Subject: [PATCH 142/195] Add to ignore workflow instalation files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 9518d0ec7d..4c6a71ac34 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ __pycache__ venv wazuh_testing.egg-info dist +deployability/modules/build +deployability/modules/workflow_engine.egg-info # Python bytecode files *.pyc From 8ab9e0e3386009c6fb06f17ae18322ee10a2a9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Correa=20Rodr=C3=ADguez?= Date: Thu, 16 May 2024 11:25:47 +0200 Subject: [PATCH 143/195] Changed ARM family type to t4g in allocator --- deployability/modules/allocation/static/specs/size.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deployability/modules/allocation/static/specs/size.yml b/deployability/modules/allocation/static/specs/size.yml index 2ddfa52003..1ce30847ad 100755 --- a/deployability/modules/allocation/static/specs/size.yml +++ b/deployability/modules/allocation/static/specs/size.yml @@ -17,21 +17,21 @@ aws: type: t2.small storage: 20 arm64: - type: a1.medium + type: t4g.micro storage: 20 small: amd64: type: t3.small storage: 20 arm64: - type: a1.large + type: t4g.small storage: 20 medium: amd64: type: t3a.medium storage: 20 arm64: - type: a1.xlarge + type: t4g.medium storage: 20 large: amd64: From e82c63d71b8d402c7afb7ecba9c38e4324e8b300 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Thu, 16 May 2024 09:38:08 -0300 Subject: [PATCH 144/195] Fix agent installation --- .../modules/testing/tests/helpers/agent.py | 8 +- .../vagrant/dtt1-managers-poc-vagrant.yaml | 1 - .../vagrant/test-manager-live-default.log | 355 ++++++++++++++++++ 3 files changed, 359 insertions(+), 5 deletions(-) create mode 100644 deployability/modules/workflow_engine/examples/manager/vagrant/test-manager-live-default.log diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 71eb127de4..fb1b44442a 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -16,11 +16,11 @@ class WazuhAgent: def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, live) -> None: if live == "False": - s3_url = 'packages-dev' - else: s3_url = 'packages' - - release = '.'.join(wazuh_version.split('.')[:2]) + release = wazuh_version[:1] + ".x" + else: + s3_url = 'packages-dev' + release = 'pre-release' os_type = HostInformation.get_os_type(inventory_path) architecture = HostInformation.get_architecture(inventory_path) diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index d29a62360d..a83a3ce468 100644 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -47,7 +47,6 @@ tasks: - component: "manager" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: False depends-on: - "allocate-manager-linux-centos-7-amd64" - "allocate-manager-linux-centos-8-amd64" diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/test-manager-live-default.log b/deployability/modules/workflow_engine/examples/manager/vagrant/test-manager-live-default.log new file mode 100644 index 0000000000..59e08d9ba6 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/test-manager-live-default.log @@ -0,0 +1,355 @@ +[2024-05-16 00:49:24] [DEBUG] [90938] [MainThread] [workflow_engine]: Validating input file: modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +[2024-05-16 00:49:24] [DEBUG] [90938] [MainThread] [workflow_engine]: Loading schema file: /home/fcaffieri/repos/wazuh-qa/.venv/lib/python3.10/site-packages/workflow_engine/schemas/schema_v1.json +[2024-05-16 00:49:24] [DEBUG] [90938] [MainThread] [workflow_engine]: Loading yaml file: modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +[2024-05-16 00:49:24] [DEBUG] [90938] [MainThread] [workflow_engine]: Loading workflow file: modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +[2024-05-16 00:49:24] [DEBUG] [90938] [MainThread] [workflow_engine]: Process workflow. +[2024-05-16 00:49:24] [INFO] [90938] [MainThread] [workflow_engine]: Executing DAG tasks. +[2024-05-16 00:49:24] [INFO] [90938] [MainThread] [workflow_engine]: Executing tasks in parallel. +[2024-05-16 00:49:24] [INFO] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: [allocate-manager-linux-centos-7-amd64] Starting task. +[2024-05-16 00:49:24] [DEBUG] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: Running task "allocate-manager-linux-centos-7-amd64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=vagrant', '--size=large', '--composite-name=linux-centos-7-amd64', '--instance-name=linux-centos-7-amd64', '--inventory-output=/tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml', '--track-output=/tmp/dtt1-poc/manager-linux-centos-7-amd64/track.yaml', '--label-termination-date=1d', '--label-team=qa'] +[2024-05-16 00:49:24] [INFO] [90938] [ThreadPoolExecutor-0_1] [workflow_engine]: [allocate-manager-linux-centos-8-amd64] Starting task. +[2024-05-16 00:49:24] [DEBUG] [90938] [ThreadPoolExecutor-0_1] [workflow_engine]: Running task "allocate-manager-linux-centos-8-amd64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=vagrant', '--size=large', '--composite-name=linux-centos-8-amd64', '--instance-name=linux-centos-8-amd64', '--inventory-output=/tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml', '--track-output=/tmp/dtt1-poc/manager-linux-centos-8-amd64/track.yaml', '--label-termination-date=1d', '--label-team=qa'] +[2024-05-16 00:50:38] [DEBUG] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: Finished task "allocate-manager-linux-centos-7-amd64" execution with result: +[2024-05-16 00:49:25] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-16 00:49:25] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-16 00:49:25] [INFO] ALLOCATOR: Creating instance at /tmp/wazuh-qa +[2024-05-16 00:49:26] [DEBUG] ALLOCATOR: No config provided. Generating from payload +[2024-05-16 00:49:26] [DEBUG] ALLOCATOR: Generating new key pair +[2024-05-16 00:49:30] [DEBUG] ALLOCATOR: Vagrantfile created. Creating instance. +[2024-05-16 00:49:30] [INFO] ALLOCATOR: Instance linux-centos-7-amd64-195 created. +[2024-05-16 00:50:21] [INFO] ALLOCATOR: Instance linux-centos-7-amd64-195 started. +[2024-05-16 00:50:30] [INFO] ALLOCATOR: Inventory file generated at /tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml +[2024-05-16 00:50:30] [INFO] ALLOCATOR: SSH connection successful. +[2024-05-16 00:50:38] [INFO] ALLOCATOR: Track file generated at /tmp/dtt1-poc/manager-linux-centos-7-amd64/track.yaml + +[2024-05-16 00:50:38] [INFO] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: [allocate-manager-linux-centos-7-amd64] Finished task in 74.08 seconds. +[2024-05-16 00:50:53] [DEBUG] [90938] [ThreadPoolExecutor-0_1] [workflow_engine]: Finished task "allocate-manager-linux-centos-8-amd64" execution with result: +[2024-05-16 00:49:25] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-16 00:49:25] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-16 00:49:25] [INFO] ALLOCATOR: Creating instance at /tmp/wazuh-qa +[2024-05-16 00:49:26] [DEBUG] ALLOCATOR: No config provided. Generating from payload +[2024-05-16 00:49:26] [DEBUG] ALLOCATOR: Generating new key pair +[2024-05-16 00:49:30] [DEBUG] ALLOCATOR: Vagrantfile created. Creating instance. +[2024-05-16 00:49:30] [INFO] ALLOCATOR: Instance linux-centos-8-amd64-5503 created. +[2024-05-16 00:50:38] [INFO] ALLOCATOR: Instance linux-centos-8-amd64-5503 started. +[2024-05-16 00:50:45] [INFO] ALLOCATOR: Inventory file generated at /tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml +[2024-05-16 00:50:45] [INFO] ALLOCATOR: SSH connection successful. +[2024-05-16 00:50:53] [INFO] ALLOCATOR: Track file generated at /tmp/dtt1-poc/manager-linux-centos-8-amd64/track.yaml + +[2024-05-16 00:50:53] [INFO] [90938] [ThreadPoolExecutor-0_1] [workflow_engine]: [allocate-manager-linux-centos-8-amd64] Finished task in 88.46 seconds. +[2024-05-16 00:50:53] [INFO] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: [run-manager-tests] Starting task. +[2024-05-16 00:50:53] [DEBUG] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: Running task "run-manager-tests" with arguments: ['modules/testing/main.py', "--targets={'wazuh-1': '/tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml'}", "--targets={'wazuh-2': '/tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml'}", '--tests=install', '--component=manager', '--wazuh-version=4.7.4', '--wazuh-revision=40717'] +[2024-05-16 01:00:45] [DEBUG] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: Finished task "run-manager-tests" execution with result: +[2024-05-16 00:50:54] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-16 00:50:54] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-16 00:50:54] [INFO] TESTER: Running tests for 192.168.57.58 +[2024-05-16 00:50:54] [INFO] TESTER: Running tests for 192.168.57.113 +[2024-05-16 00:50:54] [DEBUG] TESTER: Using extra vars: {'component': 'manager', 'wazuh_version': '4.7.4', 'wazuh_revision': '40717', 'wazuh_branch': None, 'working_dir': '/tmp/tests', 'live': True, 'hosts_ip': ['192.168.57.58', '192.168.57.113'], 'targets': '{wazuh-1: /tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml, wazuh-2: /tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml}', 'dependencies': '{}', 'local_host_path': '/home/fcaffieri/repos/wazuh-qa/deployability', 'current_user': 'fcaffieri'} +[2024-05-16 00:50:54] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/setup.yml +[2024-05-16 00:50:54] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'192.168.57.58': {'ansible_port': 22, 'ansible_user': 'vagrant', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/linux-centos-7-amd64-195/instance_key'}}}} +[2024-05-16 00:50:54] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['192.168.57.58', '192.168.57.113']}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Cleaning old key ssh-keygen registries] ********************************** +changed: [localhost] => (item=192.168.57.58) => changed=true  + ansible_loop_var: item + cmd: + - ssh-keygen + - -f + - /home/fcaffieri/.ssh/known_hosts + - -R + - '' + delta: '0:00:00.019959' + end: '2024-05-16 00:51:00.103521' + item: 192.168.57.58 + msg: '' + rc: 0 + start: '2024-05-16 00:51:00.083562' + stderr: Host not found in /home/fcaffieri/.ssh/known_hosts + stderr_lines:  + stdout: '' + stdout_lines:  +changed: [localhost] => (item=192.168.57.113) => changed=true  + ansible_loop_var: item + cmd: + - ssh-keygen + - -f + - /home/fcaffieri/.ssh/known_hosts + - -R + - '' + delta: '0:00:00.008028' + end: '2024-05-16 00:51:00.446146' + item: 192.168.57.113 + msg: '' + rc: 0 + start: '2024-05-16 00:51:00.438118' + stderr: Host not found in /home/fcaffieri/.ssh/known_hosts + stderr_lines:  + stdout: '' + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-16 00:51:00] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['192.168.57.58', '192.168.57.113']}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-16 00:51:00] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml +[2024-05-16 00:51:00] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'192.168.57.58': {'ansible_port': 22, 'ansible_user': 'vagrant', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/linux-centos-7-amd64-195/instance_key'}}}} +[2024-05-16 00:51:00] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for manager', 'command': "python3 -m pytest modules/testing/tests/test_manager/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=manager --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml, wazuh-2: /tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Test install for manager] ************************************************ +changed: [localhost] => changed=true  + cmd: + - python3 + - -m + - pytest + - modules/testing/tests/test_manager/test_install.py + - -v + - --wazuh_version=4.7.4 + - --wazuh_revision=40717 + - --component=manager + - --dependencies={} + - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml, wazuh-2: /tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml}' + - --live=True + - -s + delta: '0:09:29.449453' + end: '2024-05-16 01:00:34.724341' + msg: '' + rc: 0 + start: '2024-05-16 00:51:05.274888' + stderr: |- + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Removed /etc/systemd/system/multi-user.target.wants/firewalld.service. + Removed /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. + Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. + stderr_lines:  + stdout: |- + [37m[2024-05-16 00:51:06] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m + [37m[2024-05-16 00:51:06] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m + ============================= test session starts ============================== + platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 + cachedir: .pytest_cache + rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules + collecting ... collected 5 items +  + modules/testing/tests/test_manager/test_install.py::test_installation [32m[2024-05-16 00:51:06] [INFO] TESTER: Checking connection to centos-7[0m + [32m[2024-05-16 00:51:07] [INFO] TESTER: Connection established successfully in centos-7[0m + [32m[2024-05-16 00:51:08] [INFO] TESTER: No Firewall to disable on centos-7[0m + [32m[2024-05-16 00:51:08] [INFO] TESTER: Checking connection to centos-8[0m + [32m[2024-05-16 00:51:09] [INFO] TESTER: Connection established successfully in centos-8[0m + [32m[2024-05-16 00:51:11] [INFO] TESTER: Firewall disabled on centos-8[0m + [32m[2024-05-16 00:52:10] [INFO] TESTER: File permissions modified to be handled[0m + [32m[2024-05-16 00:52:11] [INFO] TESTER: File copied from centos-7 (192.168.57.58) to /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/wazuh-install-files.tar[0m + [32m[2024-05-16 00:52:12] [INFO] TESTER: Sending file from /home/vagrant/wazuh-install-files.tar to centos-8 (192.168.57.113)[0m + [32m[2024-05-16 00:52:13] [INFO] TESTER: File permissions were restablished[0m + [32m[2024-05-16 00:52:13] [INFO] TESTER: The file wazuh-install-files.tar deleted in /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers[0m + [32m[2024-05-16 00:52:14] [INFO] TESTER: Installing Manager with https://packages.wazuh.com/4.7/wazuh-install.sh[0m + [32m[2024-05-16 00:52:14] [INFO] TESTER: Installing Manager in centos-7[0m + [32m[2024-05-16 00:54:58] [INFO] TESTER: Installing Manager with https://packages.wazuh.com/4.7/wazuh-install.sh[0m + [32m[2024-05-16 00:54:58] [INFO] TESTER: Installing Manager in centos-8[0m + [32m[2024-05-16 00:58:59] [INFO] TESTER: Getting status of centos-7[0m + [32m[2024-05-16 00:59:01] [INFO] TESTER: Getting status of centos-8[0m + [32m[2024-05-16 00:59:34] [INFO] TESTER: Cluster configured in: centos-7[0m + [32m[2024-05-16 01:00:03] [INFO] TESTER: Cluster configured in: centos-8[0m + PASSED + modules/testing/tests/test_manager/test_install.py::test_manager_status [32m[2024-05-16 01:00:06] [INFO] TESTER: Getting status of centos-7[0m + [32m[2024-05-16 01:00:07] [INFO] TESTER: Getting status of centos-8[0m + PASSED + modules/testing/tests/test_manager/test_install.py::test_manager_version PASSED + modules/testing/tests/test_manager/test_install.py::test_manager_revision PASSED + modules/testing/tests/test_manager/test_install.py::test_manager_installed_directory PASSED +  + =============================== warnings summary =============================== + modules/provision/models.py:36 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('dependencies', pre=True) +  + modules/provision/models.py:64 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('install', 'uninstall', pre=True) +  + modules/testing/tests/helpers/agent.py:59 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:59: DeprecationWarning: invalid escape sequence '\w' + "-OutFile $env:TEMP\wazuh-agent.msi" +  + modules/testing/tests/helpers/agent.py:62 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:62: DeprecationWarning: invalid escape sequence '\w' + "msiexec.exe /i $env:TEMP\wazuh-agent.msi /q " +  + modules/testing/tests/helpers/agent.py:112 + modules/testing/tests/helpers/agent.py:112 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:112: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_CONF}", +  + modules/testing/tests/helpers/agent.py:129 + modules/testing/tests/helpers/agent.py:129 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:129: DeprecationWarning: invalid escape sequence '\/' + f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_MACOS_CONF}", +  + modules/testing/tests/helpers/agent.py:159 + modules/testing/tests/helpers/agent.py:159 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:159: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_CONF}", +  + modules/testing/tests/helpers/agent.py:168 + modules/testing/tests/helpers/agent.py:168 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:168: DeprecationWarning: invalid escape sequence '\/' + f"sed -i '' 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_MACOS_CONF}", +  + modules/testing/tests/helpers/agent.py:176 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:176: DeprecationWarning: invalid escape sequence '\/' + f"(Get-Content -Path '{WAZUH_WINDOWS_CONF}') -replace '[^<]*<\/protocol>', '{protocol}' | Set-Content -Path '{WAZUH_WINDOWS_CONF}'" +  + modules/testing/tests/helpers/agent.py:214 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:214: DeprecationWarning: invalid escape sequence '\w' + f"msiexec.exe /x $env:TEMP\wazuh-agent.msi /qn" +  + modules/testing/tests/helpers/manager.py:355 + modules/testing/tests/helpers/manager.py:355 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:355: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/node01<\/node_name>/{node_name}<\/node_name>/' {WAZUH_CONF}", +  + modules/testing/tests/helpers/manager.py:356 + modules/testing/tests/helpers/manager.py:356 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:356: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/master<\/node_type>/{node_type}<\/node_type>/' {WAZUH_CONF}", +  + modules/testing/tests/helpers/manager.py:357 + modules/testing/tests/helpers/manager.py:357 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:357: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/<\/key>/{key}<\/key>/' {WAZUH_CONF}", +  + modules/testing/tests/helpers/manager.py:358 + modules/testing/tests/helpers/manager.py:358 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:358: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/NODE_IP<\/node>/{HostInformation.get_internal_ip_from_aws_dns(master_dns)}<\/node>/' {WAZUH_CONF}", +  + modules/testing/tests/helpers/manager.py:359 + modules/testing/tests/helpers/manager.py:359 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:359: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/yes<\/disabled>/{disabled}<\/disabled>/' {WAZUH_CONF}", +  + -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html + ================== 5 passed, 24 warnings in 567.62s (0:09:27) ================== + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-16 01:00:35] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for manager', 'command': "python3 -m pytest modules/testing/tests/test_manager/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=manager --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml, wazuh-2: /tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-16 01:00:35] [INFO] TESTER: Cleaning up +[2024-05-16 01:00:35] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'192.168.57.58': {'ansible_port': 22, 'ansible_user': 'vagrant', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/linux-centos-7-amd64-195/instance_key'}}}} +[2024-05-16 01:00:35] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml +Using /etc/ansible/ansible.cfg as config file + +PLAY [all] ********************************************************************* + +TASK [Gathering Facts] ********************************************************* +ok: [192.168.57.58] + +TASK [Clean test directory] **************************************************** +ok: [192.168.57.58] => changed=false  + path: /tmp/tests + state: absent + +PLAY RECAP ********************************************************************* +192.168.57.58 : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-16 01:00:40] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'192.168.57.58': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'192.168.57.58': 1}, 'changed': {}} +[2024-05-16 01:00:40] [INFO] TESTER: Cleaning up +[2024-05-16 01:00:40] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'192.168.57.58': {'ansible_port': 22, 'ansible_user': 'vagrant', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/linux-centos-7-amd64-195/instance_key'}}}} +[2024-05-16 01:00:40] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml +Using /etc/ansible/ansible.cfg as config file + +PLAY [all] ********************************************************************* + +TASK [Gathering Facts] ********************************************************* +ok: [192.168.57.58] + +TASK [Clean test directory] **************************************************** +ok: [192.168.57.58] => changed=false  + path: /tmp/tests + state: absent + +PLAY RECAP ********************************************************************* +192.168.57.58 : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-16 01:00:45] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'192.168.57.58': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'192.168.57.58': 1}, 'changed': {}} + +[2024-05-16 01:00:45] [INFO] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: [run-manager-tests] Finished task in 592.55 seconds. +[2024-05-16 01:00:45] [INFO] [90938] [MainThread] [workflow_engine]: Executing Reverse DAG tasks. +[2024-05-16 01:00:45] [INFO] [90938] [MainThread] [workflow_engine]: Executing tasks in parallel. From 41234c5359c4fa73e3a28a8621e0a8883152625f Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Thu, 16 May 2024 09:39:31 -0300 Subject: [PATCH 145/195] Remove log file --- .../vagrant/test-manager-live-default.log | 355 ------------------ 1 file changed, 355 deletions(-) delete mode 100644 deployability/modules/workflow_engine/examples/manager/vagrant/test-manager-live-default.log diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/test-manager-live-default.log b/deployability/modules/workflow_engine/examples/manager/vagrant/test-manager-live-default.log deleted file mode 100644 index 59e08d9ba6..0000000000 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/test-manager-live-default.log +++ /dev/null @@ -1,355 +0,0 @@ -[2024-05-16 00:49:24] [DEBUG] [90938] [MainThread] [workflow_engine]: Validating input file: modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml -[2024-05-16 00:49:24] [DEBUG] [90938] [MainThread] [workflow_engine]: Loading schema file: /home/fcaffieri/repos/wazuh-qa/.venv/lib/python3.10/site-packages/workflow_engine/schemas/schema_v1.json -[2024-05-16 00:49:24] [DEBUG] [90938] [MainThread] [workflow_engine]: Loading yaml file: modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml -[2024-05-16 00:49:24] [DEBUG] [90938] [MainThread] [workflow_engine]: Loading workflow file: modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml -[2024-05-16 00:49:24] [DEBUG] [90938] [MainThread] [workflow_engine]: Process workflow. -[2024-05-16 00:49:24] [INFO] [90938] [MainThread] [workflow_engine]: Executing DAG tasks. -[2024-05-16 00:49:24] [INFO] [90938] [MainThread] [workflow_engine]: Executing tasks in parallel. -[2024-05-16 00:49:24] [INFO] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: [allocate-manager-linux-centos-7-amd64] Starting task. -[2024-05-16 00:49:24] [DEBUG] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: Running task "allocate-manager-linux-centos-7-amd64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=vagrant', '--size=large', '--composite-name=linux-centos-7-amd64', '--instance-name=linux-centos-7-amd64', '--inventory-output=/tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml', '--track-output=/tmp/dtt1-poc/manager-linux-centos-7-amd64/track.yaml', '--label-termination-date=1d', '--label-team=qa'] -[2024-05-16 00:49:24] [INFO] [90938] [ThreadPoolExecutor-0_1] [workflow_engine]: [allocate-manager-linux-centos-8-amd64] Starting task. -[2024-05-16 00:49:24] [DEBUG] [90938] [ThreadPoolExecutor-0_1] [workflow_engine]: Running task "allocate-manager-linux-centos-8-amd64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=vagrant', '--size=large', '--composite-name=linux-centos-8-amd64', '--instance-name=linux-centos-8-amd64', '--inventory-output=/tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml', '--track-output=/tmp/dtt1-poc/manager-linux-centos-8-amd64/track.yaml', '--label-termination-date=1d', '--label-team=qa'] -[2024-05-16 00:50:38] [DEBUG] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: Finished task "allocate-manager-linux-centos-7-amd64" execution with result: -[2024-05-16 00:49:25] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-16 00:49:25] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-16 00:49:25] [INFO] ALLOCATOR: Creating instance at /tmp/wazuh-qa -[2024-05-16 00:49:26] [DEBUG] ALLOCATOR: No config provided. Generating from payload -[2024-05-16 00:49:26] [DEBUG] ALLOCATOR: Generating new key pair -[2024-05-16 00:49:30] [DEBUG] ALLOCATOR: Vagrantfile created. Creating instance. -[2024-05-16 00:49:30] [INFO] ALLOCATOR: Instance linux-centos-7-amd64-195 created. -[2024-05-16 00:50:21] [INFO] ALLOCATOR: Instance linux-centos-7-amd64-195 started. -[2024-05-16 00:50:30] [INFO] ALLOCATOR: Inventory file generated at /tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml -[2024-05-16 00:50:30] [INFO] ALLOCATOR: SSH connection successful. -[2024-05-16 00:50:38] [INFO] ALLOCATOR: Track file generated at /tmp/dtt1-poc/manager-linux-centos-7-amd64/track.yaml - -[2024-05-16 00:50:38] [INFO] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: [allocate-manager-linux-centos-7-amd64] Finished task in 74.08 seconds. -[2024-05-16 00:50:53] [DEBUG] [90938] [ThreadPoolExecutor-0_1] [workflow_engine]: Finished task "allocate-manager-linux-centos-8-amd64" execution with result: -[2024-05-16 00:49:25] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-16 00:49:25] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-16 00:49:25] [INFO] ALLOCATOR: Creating instance at /tmp/wazuh-qa -[2024-05-16 00:49:26] [DEBUG] ALLOCATOR: No config provided. Generating from payload -[2024-05-16 00:49:26] [DEBUG] ALLOCATOR: Generating new key pair -[2024-05-16 00:49:30] [DEBUG] ALLOCATOR: Vagrantfile created. Creating instance. -[2024-05-16 00:49:30] [INFO] ALLOCATOR: Instance linux-centos-8-amd64-5503 created. -[2024-05-16 00:50:38] [INFO] ALLOCATOR: Instance linux-centos-8-amd64-5503 started. -[2024-05-16 00:50:45] [INFO] ALLOCATOR: Inventory file generated at /tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml -[2024-05-16 00:50:45] [INFO] ALLOCATOR: SSH connection successful. -[2024-05-16 00:50:53] [INFO] ALLOCATOR: Track file generated at /tmp/dtt1-poc/manager-linux-centos-8-amd64/track.yaml - -[2024-05-16 00:50:53] [INFO] [90938] [ThreadPoolExecutor-0_1] [workflow_engine]: [allocate-manager-linux-centos-8-amd64] Finished task in 88.46 seconds. -[2024-05-16 00:50:53] [INFO] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: [run-manager-tests] Starting task. -[2024-05-16 00:50:53] [DEBUG] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: Running task "run-manager-tests" with arguments: ['modules/testing/main.py', "--targets={'wazuh-1': '/tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml'}", "--targets={'wazuh-2': '/tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml'}", '--tests=install', '--component=manager', '--wazuh-version=4.7.4', '--wazuh-revision=40717'] -[2024-05-16 01:00:45] [DEBUG] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: Finished task "run-manager-tests" execution with result: -[2024-05-16 00:50:54] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-16 00:50:54] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-16 00:50:54] [INFO] TESTER: Running tests for 192.168.57.58 -[2024-05-16 00:50:54] [INFO] TESTER: Running tests for 192.168.57.113 -[2024-05-16 00:50:54] [DEBUG] TESTER: Using extra vars: {'component': 'manager', 'wazuh_version': '4.7.4', 'wazuh_revision': '40717', 'wazuh_branch': None, 'working_dir': '/tmp/tests', 'live': True, 'hosts_ip': ['192.168.57.58', '192.168.57.113'], 'targets': '{wazuh-1: /tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml, wazuh-2: /tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml}', 'dependencies': '{}', 'local_host_path': '/home/fcaffieri/repos/wazuh-qa/deployability', 'current_user': 'fcaffieri'} -[2024-05-16 00:50:54] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/setup.yml -[2024-05-16 00:50:54] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'192.168.57.58': {'ansible_port': 22, 'ansible_user': 'vagrant', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/linux-centos-7-amd64-195/instance_key'}}}} -[2024-05-16 00:50:54] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['192.168.57.58', '192.168.57.113']}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Cleaning old key ssh-keygen registries] ********************************** -changed: [localhost] => (item=192.168.57.58) => changed=true  - ansible_loop_var: item - cmd: - - ssh-keygen - - -f - - /home/fcaffieri/.ssh/known_hosts - - -R - - '' - delta: '0:00:00.019959' - end: '2024-05-16 00:51:00.103521' - item: 192.168.57.58 - msg: '' - rc: 0 - start: '2024-05-16 00:51:00.083562' - stderr: Host not found in /home/fcaffieri/.ssh/known_hosts - stderr_lines:  - stdout: '' - stdout_lines:  -changed: [localhost] => (item=192.168.57.113) => changed=true  - ansible_loop_var: item - cmd: - - ssh-keygen - - -f - - /home/fcaffieri/.ssh/known_hosts - - -R - - '' - delta: '0:00:00.008028' - end: '2024-05-16 00:51:00.446146' - item: 192.168.57.113 - msg: '' - rc: 0 - start: '2024-05-16 00:51:00.438118' - stderr: Host not found in /home/fcaffieri/.ssh/known_hosts - stderr_lines:  - stdout: '' - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-16 00:51:00] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['192.168.57.58', '192.168.57.113']}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-16 00:51:00] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml -[2024-05-16 00:51:00] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'192.168.57.58': {'ansible_port': 22, 'ansible_user': 'vagrant', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/linux-centos-7-amd64-195/instance_key'}}}} -[2024-05-16 00:51:00] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for manager', 'command': "python3 -m pytest modules/testing/tests/test_manager/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=manager --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml, wazuh-2: /tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Test install for manager] ************************************************ -changed: [localhost] => changed=true  - cmd: - - python3 - - -m - - pytest - - modules/testing/tests/test_manager/test_install.py - - -v - - --wazuh_version=4.7.4 - - --wazuh_revision=40717 - - --component=manager - - --dependencies={} - - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml, wazuh-2: /tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml}' - - --live=True - - -s - delta: '0:09:29.449453' - end: '2024-05-16 01:00:34.724341' - msg: '' - rc: 0 - start: '2024-05-16 00:51:05.274888' - stderr: |- - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Removed /etc/systemd/system/multi-user.target.wants/firewalld.service. - Removed /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.58' (ECDSA) to the list of known hosts. - Warning: Permanently added '192.168.57.113' (RSA) to the list of known hosts. - stderr_lines:  - stdout: |- - [37m[2024-05-16 00:51:06] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m - [37m[2024-05-16 00:51:06] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m - ============================= test session starts ============================== - platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 - cachedir: .pytest_cache - rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules - collecting ... collected 5 items -  - modules/testing/tests/test_manager/test_install.py::test_installation [32m[2024-05-16 00:51:06] [INFO] TESTER: Checking connection to centos-7[0m - [32m[2024-05-16 00:51:07] [INFO] TESTER: Connection established successfully in centos-7[0m - [32m[2024-05-16 00:51:08] [INFO] TESTER: No Firewall to disable on centos-7[0m - [32m[2024-05-16 00:51:08] [INFO] TESTER: Checking connection to centos-8[0m - [32m[2024-05-16 00:51:09] [INFO] TESTER: Connection established successfully in centos-8[0m - [32m[2024-05-16 00:51:11] [INFO] TESTER: Firewall disabled on centos-8[0m - [32m[2024-05-16 00:52:10] [INFO] TESTER: File permissions modified to be handled[0m - [32m[2024-05-16 00:52:11] [INFO] TESTER: File copied from centos-7 (192.168.57.58) to /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/wazuh-install-files.tar[0m - [32m[2024-05-16 00:52:12] [INFO] TESTER: Sending file from /home/vagrant/wazuh-install-files.tar to centos-8 (192.168.57.113)[0m - [32m[2024-05-16 00:52:13] [INFO] TESTER: File permissions were restablished[0m - [32m[2024-05-16 00:52:13] [INFO] TESTER: The file wazuh-install-files.tar deleted in /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers[0m - [32m[2024-05-16 00:52:14] [INFO] TESTER: Installing Manager with https://packages.wazuh.com/4.7/wazuh-install.sh[0m - [32m[2024-05-16 00:52:14] [INFO] TESTER: Installing Manager in centos-7[0m - [32m[2024-05-16 00:54:58] [INFO] TESTER: Installing Manager with https://packages.wazuh.com/4.7/wazuh-install.sh[0m - [32m[2024-05-16 00:54:58] [INFO] TESTER: Installing Manager in centos-8[0m - [32m[2024-05-16 00:58:59] [INFO] TESTER: Getting status of centos-7[0m - [32m[2024-05-16 00:59:01] [INFO] TESTER: Getting status of centos-8[0m - [32m[2024-05-16 00:59:34] [INFO] TESTER: Cluster configured in: centos-7[0m - [32m[2024-05-16 01:00:03] [INFO] TESTER: Cluster configured in: centos-8[0m - PASSED - modules/testing/tests/test_manager/test_install.py::test_manager_status [32m[2024-05-16 01:00:06] [INFO] TESTER: Getting status of centos-7[0m - [32m[2024-05-16 01:00:07] [INFO] TESTER: Getting status of centos-8[0m - PASSED - modules/testing/tests/test_manager/test_install.py::test_manager_version PASSED - modules/testing/tests/test_manager/test_install.py::test_manager_revision PASSED - modules/testing/tests/test_manager/test_install.py::test_manager_installed_directory PASSED -  - =============================== warnings summary =============================== - modules/provision/models.py:36 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('dependencies', pre=True) -  - modules/provision/models.py:64 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('install', 'uninstall', pre=True) -  - modules/testing/tests/helpers/agent.py:59 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:59: DeprecationWarning: invalid escape sequence '\w' - "-OutFile $env:TEMP\wazuh-agent.msi" -  - modules/testing/tests/helpers/agent.py:62 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:62: DeprecationWarning: invalid escape sequence '\w' - "msiexec.exe /i $env:TEMP\wazuh-agent.msi /q " -  - modules/testing/tests/helpers/agent.py:112 - modules/testing/tests/helpers/agent.py:112 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:112: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_CONF}", -  - modules/testing/tests/helpers/agent.py:129 - modules/testing/tests/helpers/agent.py:129 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:129: DeprecationWarning: invalid escape sequence '\/' - f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_MACOS_CONF}", -  - modules/testing/tests/helpers/agent.py:159 - modules/testing/tests/helpers/agent.py:159 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:159: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_CONF}", -  - modules/testing/tests/helpers/agent.py:168 - modules/testing/tests/helpers/agent.py:168 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:168: DeprecationWarning: invalid escape sequence '\/' - f"sed -i '' 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_MACOS_CONF}", -  - modules/testing/tests/helpers/agent.py:176 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:176: DeprecationWarning: invalid escape sequence '\/' - f"(Get-Content -Path '{WAZUH_WINDOWS_CONF}') -replace '[^<]*<\/protocol>', '{protocol}' | Set-Content -Path '{WAZUH_WINDOWS_CONF}'" -  - modules/testing/tests/helpers/agent.py:214 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:214: DeprecationWarning: invalid escape sequence '\w' - f"msiexec.exe /x $env:TEMP\wazuh-agent.msi /qn" -  - modules/testing/tests/helpers/manager.py:355 - modules/testing/tests/helpers/manager.py:355 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:355: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/node01<\/node_name>/{node_name}<\/node_name>/' {WAZUH_CONF}", -  - modules/testing/tests/helpers/manager.py:356 - modules/testing/tests/helpers/manager.py:356 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:356: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/master<\/node_type>/{node_type}<\/node_type>/' {WAZUH_CONF}", -  - modules/testing/tests/helpers/manager.py:357 - modules/testing/tests/helpers/manager.py:357 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:357: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/<\/key>/{key}<\/key>/' {WAZUH_CONF}", -  - modules/testing/tests/helpers/manager.py:358 - modules/testing/tests/helpers/manager.py:358 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:358: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/NODE_IP<\/node>/{HostInformation.get_internal_ip_from_aws_dns(master_dns)}<\/node>/' {WAZUH_CONF}", -  - modules/testing/tests/helpers/manager.py:359 - modules/testing/tests/helpers/manager.py:359 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:359: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/yes<\/disabled>/{disabled}<\/disabled>/' {WAZUH_CONF}", -  - -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html - ================== 5 passed, 24 warnings in 567.62s (0:09:27) ================== - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-16 01:00:35] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for manager', 'command': "python3 -m pytest modules/testing/tests/test_manager/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=manager --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-centos-7-amd64/inventory.yaml, wazuh-2: /tmp/dtt1-poc/manager-linux-centos-8-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-16 01:00:35] [INFO] TESTER: Cleaning up -[2024-05-16 01:00:35] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'192.168.57.58': {'ansible_port': 22, 'ansible_user': 'vagrant', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/linux-centos-7-amd64-195/instance_key'}}}} -[2024-05-16 01:00:35] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml -Using /etc/ansible/ansible.cfg as config file - -PLAY [all] ********************************************************************* - -TASK [Gathering Facts] ********************************************************* -ok: [192.168.57.58] - -TASK [Clean test directory] **************************************************** -ok: [192.168.57.58] => changed=false  - path: /tmp/tests - state: absent - -PLAY RECAP ********************************************************************* -192.168.57.58 : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-16 01:00:40] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'192.168.57.58': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'192.168.57.58': 1}, 'changed': {}} -[2024-05-16 01:00:40] [INFO] TESTER: Cleaning up -[2024-05-16 01:00:40] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'192.168.57.58': {'ansible_port': 22, 'ansible_user': 'vagrant', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/linux-centos-7-amd64-195/instance_key'}}}} -[2024-05-16 01:00:40] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml -Using /etc/ansible/ansible.cfg as config file - -PLAY [all] ********************************************************************* - -TASK [Gathering Facts] ********************************************************* -ok: [192.168.57.58] - -TASK [Clean test directory] **************************************************** -ok: [192.168.57.58] => changed=false  - path: /tmp/tests - state: absent - -PLAY RECAP ********************************************************************* -192.168.57.58 : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-16 01:00:45] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'192.168.57.58': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'192.168.57.58': 1}, 'changed': {}} - -[2024-05-16 01:00:45] [INFO] [90938] [ThreadPoolExecutor-0_0] [workflow_engine]: [run-manager-tests] Finished task in 592.55 seconds. -[2024-05-16 01:00:45] [INFO] [90938] [MainThread] [workflow_engine]: Executing Reverse DAG tasks. -[2024-05-16 01:00:45] [INFO] [90938] [MainThread] [workflow_engine]: Executing tasks in parallel. From a9201646131a4d2f01623413bde8bba2c4916a3f Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Thu, 16 May 2024 09:42:32 -0300 Subject: [PATCH 146/195] Set live to False by default --- deployability/modules/testing/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/testing/main.py b/deployability/modules/testing/main.py index 27f55322ee..2c9988971d 100755 --- a/deployability/modules/testing/main.py +++ b/deployability/modules/testing/main.py @@ -22,7 +22,7 @@ def parse_arguments(): parser.add_argument("--wazuh-version", required=True) parser.add_argument("--wazuh-revision", required=True) parser.add_argument("--wazuh-branch", required=False) - parser.add_argument("--live", required=False, default=True) + parser.add_argument("--live", required=False, default=False) return parser.parse_args() From a7aa6062b7d70d81d64975f698f29e7d4959c353 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Thu, 16 May 2024 10:01:38 -0300 Subject: [PATCH 147/195] Fix review comments --- deployability/modules/provision/models.py | 2 +- deployability/modules/testing/models.py | 4 +-- .../modules/testing/tests/helpers/agent.py | 7 ++-- .../modules/testing/tests/helpers/central.py | 5 ++- .../modules/testing/tests/helpers/manager.py | 8 ++--- .../dtt1-central_components-poc-vagrant.yaml | 10 ++++++ .../vagrant/dtt1-managers-poc-vagrant.yaml | 34 +++++++++++++++++-- 7 files changed, 54 insertions(+), 16 deletions(-) diff --git a/deployability/modules/provision/models.py b/deployability/modules/provision/models.py index e0c44581e1..e80e9b4271 100755 --- a/deployability/modules/provision/models.py +++ b/deployability/modules/provision/models.py @@ -11,7 +11,7 @@ class ComponentInfo(BaseModel): type: str = "package" version: str = "" dependencies: dict | None = None - live : bool = True + live : bool = False class InputPayload(BaseModel): diff --git a/deployability/modules/testing/models.py b/deployability/modules/testing/models.py index 179ae852aa..522ce9e07b 100644 --- a/deployability/modules/testing/models.py +++ b/deployability/modules/testing/models.py @@ -12,7 +12,7 @@ class ExtraVars(BaseModel): wazuh_revision: str wazuh_branch: str | None = None working_dir: str = '/tmp/tests' - live: bool = True + live: bool = False class InputPayload(ExtraVars): """Input payload for testing module.""" @@ -20,7 +20,7 @@ class InputPayload(ExtraVars): targets: list[str] dependencies: list[str] | None = None cleanup: bool = True - live: bool = True + live: bool = False @field_validator('tests', mode='before') diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index fb1b44442a..718bbbc0d2 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -16,11 +16,12 @@ class WazuhAgent: def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, live) -> None: if live == "False": - s3_url = 'packages' - release = wazuh_version[:1] + ".x" - else: s3_url = 'packages-dev' release = 'pre-release' + else: + s3_url = 'packages' + release = wazuh_version[:1] + ".x" + os_type = HostInformation.get_os_type(inventory_path) architecture = HostInformation.get_architecture(inventory_path) diff --git a/deployability/modules/testing/tests/helpers/central.py b/deployability/modules/testing/tests/helpers/central.py index 2fa4155ddd..f8702e7f58 100644 --- a/deployability/modules/testing/tests/helpers/central.py +++ b/deployability/modules/testing/tests/helpers/central.py @@ -29,12 +29,11 @@ def install_aio(inventory_path, wazuh_version, live) -> None: release = '.'.join(wazuh_version.split('.')[:2]) - logger.info(f'Installing Manager with https://{s3_url}/{release}/wazuh-install.sh') + logger.info(f'Installing the Wazuh manager with https://{s3_url}/{release}/wazuh-install.sh') if HostInformation.has_curl(inventory_path): commands = [ - f"curl -sO https://{s3_url}/{release}/wazuh-install.sh", - f"sudo bash ./wazuh-install.sh -a --ignore-check" + f"curl -sO https://{s3_url}/{release}/wazuh-install.sh && sudo bash ./wazuh-install.sh -a --ignore-check" ] else: commands = [ diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index c4e673c710..a3742964e9 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -34,17 +34,15 @@ def install_manager(inventory_path, node_name, wazuh_version, live: bool) -> Non release = '.'.join(wazuh_version.split('.')[:2]) - logger.info(f'Installing Manager with https://{s3_url}/{release}/wazuh-install.sh') + logger.info(f'Installing the Wazuh manager with https://{s3_url}/{release}/wazuh-install.sh') if os_name == 'debian': commands = [ - f"wget https://{s3_url}/{release}/wazuh-install.sh", - f"bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" + f"wget https://{s3_url}/{release}/wazuh-install.sh && bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" ] else: commands = [ - f"curl -sO https://{s3_url}/{release}/wazuh-install.sh", - f"bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" + f"curl -sO https://{s3_url}/{release}/wazuh-install.sh && bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" ] logger.info(f'Installing Manager in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') ConnectionManager.execute_commands(inventory_path, commands) diff --git a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml index 6ed6a32e41..8603b18cfb 100644 --- a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml @@ -2,7 +2,17 @@ version: 0.1 description: This workflow is used to test the Wazuh manager deployment for DDT1 PoC variables: central_components-os: + - linux-ubuntu-20.04-amd64 - linux-ubuntu-22.04-amd64 + - linux-amazon-2-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 infra-provider: vagrant working-dir: /tmp/dtt1-poc diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index a83a3ce468..cfe7f41023 100644 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -2,8 +2,18 @@ version: 0.1 description: This workflow is used to test manager deployment for DDT1 PoC variables: manager-os: + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 - linux-centos-7-amd64 - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 infra-provider: vagrant working-dir: /tmp/dtt1-poc @@ -42,12 +52,32 @@ tasks: - modules/testing/main.py - targets: - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - - wazuh-2: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" - - tests: "install" + - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - wazuh-3: "{working-dir}/manager-linux-ubuntu-22.04-amd64/inventory.yaml" + - wazuh-4: "{working-dir}/manager-linux-oracle-9-amd64/inventory.yaml" + - wazuh-5: "{working-dir}/manager-linux-amazon-2-amd64/inventory.yaml" + - wazuh-6: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" + - wazuh-7: "{working-dir}/manager-linux-redhat-8-amd64/inventory.yaml" + - wazuh-8: "{working-dir}/manager-linux-redhat-9-amd64/inventory.yaml" + - wazuh-9: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" + - wazuh-10: "{working-dir}/manager-linux-debian-10-amd64/inventory.yaml" + - wazuh-11: "{working-dir}/manager-linux-debian-11-amd64/inventory.yaml" + - wazuh-12: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" + - tests: "install,restart,stop,uninstall" - component: "manager" - wazuh-version: "4.7.4" - wazuh-revision: "40717" depends-on: + - "allocate-manager-linux-ubuntu-20.04-amd64" + - "allocate-manager-linux-ubuntu-22.04-amd64" + - "allocate-manager-linux-amazon-2-amd64" + - "allocate-manager-linux-redhat-7-amd64" + - "allocate-manager-linux-redhat-8-amd64" + - "allocate-manager-linux-redhat-9-amd64" - "allocate-manager-linux-centos-7-amd64" - "allocate-manager-linux-centos-8-amd64" + - "allocate-manager-linux-debian-10-amd64" + - "allocate-manager-linux-debian-11-amd64" + - "allocate-manager-linux-debian-12-amd64" + - "allocate-manager-linux-oracle-9-amd64" From 68157a35f64a2e2d1d250ca5a8c76ed06931a155 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Thu, 16 May 2024 10:13:42 -0300 Subject: [PATCH 148/195] Fix review comments 2 --- deployability/modules/testing/tests/helpers/central.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/central.py b/deployability/modules/testing/tests/helpers/central.py index f8702e7f58..db96452a90 100644 --- a/deployability/modules/testing/tests/helpers/central.py +++ b/deployability/modules/testing/tests/helpers/central.py @@ -37,8 +37,7 @@ def install_aio(inventory_path, wazuh_version, live) -> None: ] else: commands = [ - f"wget https://{s3_url}/{release}/wazuh-install.sh", - f"sudo bash ./wazuh-install.sh -a --ignore-check" + f"wget https://{s3_url}/{release}/wazuh-install.sh && sudo bash ./wazuh-install.sh -a --ignore-check" ] From 84e8a14092e323ad9c2d9593edecbbbf271f85ea Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Thu, 16 May 2024 10:14:45 -0300 Subject: [PATCH 149/195] Remove bool from definition --- deployability/modules/testing/tests/helpers/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index a3742964e9..5140a9f27e 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -15,7 +15,7 @@ class WazuhManager: @staticmethod - def install_manager(inventory_path, node_name, wazuh_version, live: bool) -> None: + def install_manager(inventory_path, node_name, wazuh_version, live) -> None: """ Installs Wazuh Manager in the host From faea3df5c9b27fe1f60fe4c6609fd201d0fe13a3 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Thu, 16 May 2024 10:15:52 -0300 Subject: [PATCH 150/195] Remove unused implementation method --- deployability/modules/testing/tests/helpers/manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index 5140a9f27e..d4bfc26ab3 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -49,7 +49,7 @@ def install_manager(inventory_path, node_name, wazuh_version, live) -> None: @staticmethod - def install_managers(inventories_paths=[], node_names=[], wazuh_versions=[], live=[]) -> None: + def install_managers(inventories_paths=[], node_names=[], wazuh_versions=[]) -> None: """ Install Wazuh Managers in the hosts @@ -62,7 +62,7 @@ def install_managers(inventories_paths=[], node_names=[], wazuh_versions=[], liv for inventory in inventories_paths: for node_name in node_names: for wazuh_version in wazuh_versions: - WazuhManager.install_manager(inventory, node_name, wazuh_version, live[index]) + WazuhManager.install_manager(inventory, node_name, wazuh_version) @staticmethod From 8ff9bf1a3e63d3d7b1df642e9b21355ea3cef6f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Correa=20Rodr=C3=ADguez?= Date: Thu, 16 May 2024 15:28:33 +0200 Subject: [PATCH 151/195] Added known issues paragraph to allocation documentation --- deployability/modules/allocation/README.MD | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 3eb014aade..96e303cf32 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -366,6 +366,16 @@ python3 modules/allocation/main.py --action create --provider vagrant --size lar [Allocation.drawio.zip](https://github.com/wazuh/wazuh-qa/files/14868775/Allocation.drawio.zip) +### Known issues + +Currently, the Allocation module may encounter the following issues: + +1. **Rocky Linux 9 ARM instance fails to start with micro size on AWS** + + - **Issue:** When attempting to launch a Rocky Linux 9 ARM instance on AWS using the micro size, the instance fails to start. This occurs because the specified micro size is not supported by the AMI. + - **Symptoms:** The Allocator module fails to launch the instance. The error reported says that "The instance configuration for this AWS Marketplace product is not supported". + - **Workaround:**: This AMI does not support the `micro` size. Choose the `small` size or higher to launch this instance. + ### License From 5640275321f5931a0766438bd179ec24c75dd5e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Correa=20Rodr=C3=ADguez?= Date: Thu, 16 May 2024 15:41:53 +0200 Subject: [PATCH 152/195] Added single quotes to the size options in the Allocator documentation --- deployability/modules/allocation/README.MD | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 96e303cf32..19da40356a 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -370,9 +370,9 @@ python3 modules/allocation/main.py --action create --provider vagrant --size lar Currently, the Allocation module may encounter the following issues: -1. **Rocky Linux 9 ARM instance fails to start with micro size on AWS** +1. **Rocky Linux 9 ARM instance fails to start with `micro` size on AWS** - - **Issue:** When attempting to launch a Rocky Linux 9 ARM instance on AWS using the micro size, the instance fails to start. This occurs because the specified micro size is not supported by the AMI. + - **Issue:** When attempting to launch a Rocky Linux 9 ARM instance on AWS using the `micro` size, the instance fails to start. This occurs because the specified `micro` size is not supported by the AMI. - **Symptoms:** The Allocator module fails to launch the instance. The error reported says that "The instance configuration for this AWS Marketplace product is not supported". - **Workaround:**: This AMI does not support the `micro` size. Choose the `small` size or higher to launch this instance. From 88c852a34fb2d12ca8ed7b9c5930a302d04682b8 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Thu, 16 May 2024 10:50:57 -0300 Subject: [PATCH 153/195] Fix all yaml template examples --- .../examples/agent/aws/test-agent-complete-macOS.yaml | 2 +- .../workflow_engine/examples/agent/aws/test-agent-complete.yaml | 2 +- .../examples/agent/aws/test-agent-restart-ins-prov.yaml | 2 +- .../examples/agent/aws/test-agent-stop-ins-prov.yaml | 2 +- .../workflow_engine/examples/agent/aws/test-agent-suse.yaml | 2 +- .../examples/agent/aws/test-agent-uninstall-ins-prov.yaml | 2 +- .../examples/agent/aws/test-agent-windows-complete.yaml | 2 +- .../examples/agent/aws/test-agent-windows-install.yaml | 2 +- .../examples/agent/vagrant/test-agent-complete-1.yaml | 2 +- .../examples/agent/vagrant/test-agent-complete-2.yaml | 2 +- .../examples/agent/vagrant/test-agent-complete-macOS.yaml | 2 +- .../examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml | 2 +- .../examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml | 2 +- .../examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml | 2 +- .../examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml | 2 +- .../examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml | 2 +- .../examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml | 2 +- .../examples/agent/vagrant/test-agent-windows-complete.yaml | 2 +- .../central_components/aws/dtt1-central_components-poc-aws.yaml | 2 +- .../examples/manager/aws/dtt1-managers-poc-aws.yaml | 2 +- .../examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml | 1 + 21 files changed, 21 insertions(+), 20 deletions(-) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml index dd067ecbcd..ca5dac69d1 100644 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml @@ -107,7 +107,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml index 6d5f0a247a..7fedb07200 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml @@ -114,7 +114,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml index bc1d4970e6..d928e00da2 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml @@ -137,7 +137,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml index 08caa17057..3b711792d3 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml @@ -137,7 +137,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml index 88eaaa4823..394e7c14a7 100644 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml @@ -126,7 +126,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml index 4c33dc5e45..8132580b63 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml @@ -137,7 +137,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml index 518817c6f0..4dabebf87c 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml @@ -112,7 +112,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml index 4f64e76f6e..f83d99c72b 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml @@ -26,5 +26,5 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml index bee7abe6f2..3c034ca209 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml @@ -108,7 +108,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml index 4c799c6530..945754ca58 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml @@ -108,7 +108,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml index 202cda44bd..78d5a59462 100644 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml @@ -105,7 +105,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml index 4402fc08ad..a01234550d 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml @@ -126,7 +126,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml index aecb476577..6744d36178 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml @@ -126,7 +126,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml index 9f1dc32ac9..2963aa7847 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml @@ -126,7 +126,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml index 2b42d06698..a9d39734a2 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml @@ -126,7 +126,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml index 01c9f95c9c..fcde1cc3c4 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml @@ -126,7 +126,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml index f917254e44..a73a8aa56b 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml @@ -126,7 +126,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml index b91e178825..296b813d9d 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml @@ -112,7 +112,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml b/deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml index d5781b5538..316aaf58c1 100644 --- a/deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml @@ -62,7 +62,7 @@ tasks: - component: "central_components" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False on-error: "abort-all" foreach: - variable: central_components-os diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml index 3a4539dbb9..a634e5e226 100644 --- a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml @@ -64,7 +64,7 @@ tasks: - component: "manager" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False depends-on: - "allocate-manager-linux-ubuntu-20.04-amd64" - "allocate-manager-linux-ubuntu-22.04-amd64" diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index cfe7f41023..83f68f4c07 100644 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -67,6 +67,7 @@ tasks: - component: "manager" - wazuh-version: "4.7.4" - wazuh-revision: "40717" + - live: False depends-on: - "allocate-manager-linux-ubuntu-20.04-amd64" - "allocate-manager-linux-ubuntu-22.04-amd64" From 6d206a3d2cb55a54774c1e413798289f60079851 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Thu, 16 May 2024 10:53:39 -0300 Subject: [PATCH 154/195] Fix all yaml template examples for provision --- .../examples/agent/aws/test-agent-complete-macOS.yaml | 2 +- .../examples/agent/aws/test-agent-complete.yaml | 2 +- .../examples/agent/aws/test-agent-restart-ins-prov.yaml | 4 ++-- .../examples/agent/aws/test-agent-stop-ins-prov.yaml | 4 ++-- .../workflow_engine/examples/agent/aws/test-agent-suse.yaml | 2 +- .../examples/agent/aws/test-agent-uninstall-ins-prov.yaml | 4 ++-- .../examples/agent/aws/test-agent-windows-complete.yaml | 2 +- .../examples/agent/vagrant/test-agent-complete-1.yaml | 2 +- .../examples/agent/vagrant/test-agent-complete-2.yaml | 2 +- .../examples/agent/vagrant/test-agent-complete-macOS.yaml | 2 +- .../examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml | 4 ++-- .../examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml | 4 ++-- .../examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml | 4 ++-- .../examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml | 4 ++-- .../agent/vagrant/test-agent-uninstall-ins-prov-1.yaml | 4 ++-- .../agent/vagrant/test-agent-uninstall-ins-prov-2.yaml | 4 ++-- .../examples/agent/vagrant/test-agent-windows-complete.yaml | 2 +- 17 files changed, 26 insertions(+), 26 deletions(-) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml index ca5dac69d1..3f67741d80 100644 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml @@ -85,7 +85,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml index 7fedb07200..dab80b8546 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml @@ -93,7 +93,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml index d928e00da2..56a21db2aa 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml @@ -91,7 +91,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" @@ -112,7 +112,7 @@ tasks: - component: wazuh-agent type: package version: 4.7.4 - live: True + live: False depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml index 3b711792d3..cbc38ce0b0 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml @@ -91,7 +91,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" @@ -112,7 +112,7 @@ tasks: - component: wazuh-agent type: package version: 4.7.4 - live: True + live: False depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml index 394e7c14a7..425fd9fd4c 100644 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml @@ -83,7 +83,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml index 8132580b63..3b68dfec8c 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml @@ -91,7 +91,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" @@ -112,7 +112,7 @@ tasks: - component: wazuh-agent type: package version: 4.7.4 - live: True + live: False depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml index 4dabebf87c..f2f024a603 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml @@ -91,7 +91,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml index 3c034ca209..aa4392a5ac 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml @@ -86,7 +86,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml index 945754ca58..91bb485a25 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml @@ -86,7 +86,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml index 78d5a59462..918ed5bd70 100644 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml @@ -83,7 +83,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml index a01234550d..b809d79e31 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml @@ -80,7 +80,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" @@ -101,7 +101,7 @@ tasks: - component: wazuh-agent type: package version: 4.7.4 - live: True + live: False depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml index 6744d36178..a875e87dec 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml @@ -80,7 +80,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" @@ -101,7 +101,7 @@ tasks: - component: wazuh-agent type: package version: 4.7.4 - live: True + live: False depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml index 2963aa7847..bf5195a845 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml @@ -80,7 +80,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" @@ -101,7 +101,7 @@ tasks: - component: wazuh-agent type: package version: 4.7.4 - live: True + live: False depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml index a9d39734a2..8a0b9de3fc 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml @@ -80,7 +80,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" @@ -101,7 +101,7 @@ tasks: - component: wazuh-agent type: package version: 4.7.4 - live: True + live: False depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml index fcde1cc3c4..f64635fb81 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml @@ -80,7 +80,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" @@ -101,7 +101,7 @@ tasks: - component: wazuh-agent type: package version: 4.7.4 - live: True + live: False depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml index a73a8aa56b..9207f4f50c 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml @@ -80,7 +80,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" @@ -101,7 +101,7 @@ tasks: - component: wazuh-agent type: package version: 4.7.4 - live: True + live: False depends-on: - "allocate-agent-{agent}" - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml index 296b813d9d..59f8204c84 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml @@ -91,7 +91,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: True + live: False depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" From 4f8a0bf8cb72c0ea7fb33e9e02baa36aa42a79df Mon Sep 17 00:00:00 2001 From: c-bordon Date: Thu, 16 May 2024 10:55:58 -0300 Subject: [PATCH 155/195] Modifications in the validation methods of auxiliary files --- .../modules/allocation/allocation.py | 66 ++++++++++++------- .../modules/allocation/vagrant/provider.py | 2 +- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/deployability/modules/allocation/allocation.py b/deployability/modules/allocation/allocation.py index 77d86fba4a..647a4bcc26 100755 --- a/deployability/modules/allocation/allocation.py +++ b/deployability/modules/allocation/allocation.py @@ -63,12 +63,12 @@ def __create(cls, payload: models.CreationPayload): instance.start() logger.info(f"Instance {instance.identifier} started.") # Generate the inventory and track files. - inventory_path = cls.__get_auxiliar_files_path(instance_params.inventory_output, instance, 'inventory') - inventory = cls.__generate_inventory(instance, inventory_path, instance_params.composite_name) + inventory = cls.__generate_inventory(instance, instance_params.composite_name) + track_file = cls.__generate_track_file(instance, payload.provider) + cls.__check_auxiliar_files_path(cls, instance_params.inventory_output, instance, 'inventory') + track_file = cls.__check_auxiliar_files_path(cls, instance_params.track_output, instance, 'track') # Validate connection check_connection = cls.__check_connection(inventory) - track_path = cls.__get_auxiliar_files_path(instance_params.track_output, instance, 'track') - track_file = cls.__generate_track_file(instance, payload.provider, track_path, inventory_path) if check_connection is False: if payload.rollback: logger.warning(f"Rolling back instance creation.") @@ -77,6 +77,7 @@ def __create(cls, payload: models.CreationPayload): logger.info(f"Instance {instance.identifier} deleted.") else: logger.warning(f'The VM will not be automatically removed. Please remove it executing Allocation module with --action delete.') + logger.info(f"Instance {instance.identifier} created successfully.") @classmethod def __delete(cls, payload: models.InstancePayload) -> None: @@ -118,17 +119,14 @@ def ___get_custom_config(payload: models.CreationPayload) -> models.ProviderConf return config @staticmethod - def __generate_inventory(instance: Instance, inventory_path: Path, composite_name: str) -> None: + def __generate_inventory(instance: Instance, composite_name: str) -> None: """ Generates an inventory file. Args: instance (Instance): The instance for which the inventory file is generated. - inventory_path (Path): The path where the inventory file will be generated. composite_name (str): Composite name of the instance to be provisioned. """ - if not inventory_path.parent.exists(): - inventory_path.parent.mkdir(parents=True, exist_ok=True) ssh_config = instance.ssh_connection_info() if instance.platform == 'windows': if str(composite_name.split("-")[1]) == 'sign': @@ -159,24 +157,21 @@ def __generate_inventory(instance: Instance, inventory_path: Path, composite_nam ansible_connection='ssh', ansible_ssh_private_key_file=str(ssh_config.private_key), ansible_ssh_common_args='-o StrictHostKeyChecking=no') + inventory_path = Path(os.path.join(instance.path, 'inventory.yaml')) with open(inventory_path, 'w') as f: yaml.dump(inventory.model_dump(exclude_none=True), f) - logger.info(f"Inventory file generated at {inventory_path}") return inventory @staticmethod - def __generate_track_file(instance: Instance, provider_name: str, track_path: Path, inventory_path: Path) -> None: + def __generate_track_file(instance: Instance, provider_name: str) -> None: """ Generates a track file. Args: instance (Instance): The instance for which the track file is to be generated. provider_name (str): The name of the provider. - track_path (Path): The path where the track file will be generated. - inventory_path (Path): The path of the inventory file. """ - if not track_path.parent.exists(): - track_path.parent.mkdir(parents=True, exist_ok=True) + inventory_path = Path(os.path.join(instance.path, 'inventory.yaml')) with open(str(inventory_path), 'r') as f: inventory = models.InventoryOutput(**yaml.safe_load(f)) port = inventory.ansible_port @@ -190,13 +185,13 @@ def __generate_track_file(instance: Instance, provider_name: str, track_path: P platform=instance.platform, arch=instance.arch, virtualizer=instance.virtualizer) + track_path = Path(os.path.join(instance.path, 'track.yaml')) with open(track_path, 'w') as f: yaml.dump(track.model_dump(), f) if Path(str(instance.path) + "/port.txt").exists(): Path(str(instance.path) + "/port.txt").unlink() if Path(str(instance.path) + "/ppc-key").exists(): Path(str(instance.path) + "/ppc-key").unlink() - logger.info(f"Track file generated at {track_path}") return track_path @staticmethod @@ -261,9 +256,9 @@ def __check_connection(inventory: models.InventoryOutput, attempts=30, sleep=30) return False @staticmethod - def __get_auxiliar_files_path(path: Path, instance: Instance, file: str) -> Path: + def __check_auxiliar_files_path(cls, path: Path, instance: Instance, file: str) -> Path: """ - Get the path of the auxiliar files. + Check the path of the auxiliar files. Args: path (Path): The path of the instance. @@ -273,19 +268,40 @@ def __get_auxiliar_files_path(path: Path, instance: Instance, file: str) -> Path Returns: Path: The path of the auxiliar files. """ + source = Path(os.path.join(instance.path, file + '.yaml')) + destination = path + track_file = Path(os.path.join(instance.path, 'track.yaml')) + track_payload = {'track_output': track_file} + if path is None: - return Path(os.path.join(instance.path, file + '.yml')) + logger.info(f"The {file} file generated at {source}") + return source if path.exists() and path.is_dir(): - return Path(os.path.join(path, file + '.yml')) + destination = destination / (file + '.yaml') + os.replace(source, destination) + logger.info(f"The {file} file generated at {destination}") + return destination if str(path).endswith('.yml') or str(path).endswith('.yaml'): - if file in str(path): - return path - elif file == 'inventory' and 'track' in str(path): - return Path(str(path).replace('track', 'inventory')) - elif file == 'track' and 'inventory' in str(path): - return Path(str(path).replace('inventory', 'track')) + if file in os.path.basename(path): + os.replace(source, destination) + logger.info(f"The {file} file generated at {destination}") + return destination + elif file == 'inventory' and 'track' in os.path.basename(path): + logger.error(f"Confusing definition in the name of the inventory file, we recommend that it contain the word inventory to facilitate identification of the file type") + logger.error(f"Rolling back instance creation.") + cls.__delete(track_payload) + raise ValueError(f"Confusing definition in the name of the inventory file, we recommend that it contain the word inventory to facilitate identification of the file type") + elif file == 'track' and 'inventory' in os.path.basename(path): + logger.error(f"Confusing definition in the name of the track file, we recommend that it contain the word track to facilitate identification of the file type") + logger.error(f"Rolling back instance creation.") + cls.__delete(track_payload) + raise ValueError(f"Confusing definition in the name of the track file, we recommend that it contain the word track to facilitate identification of the file type") else: base, ext = os.path.splitext(path) + os.replace(source, Path(f"{base}-{file}{ext}")) + logger.info(f"The {file} file generated at {base}-{file}{ext}") return Path(f"{base}-{file}{ext}") else: + logger.error(f"Rolling back instance creation.") + cls.__delete(track_payload) raise ValueError(f"Invalid path for auxiliary file, must be a yaml file: {path}") diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 1e87dd1a23..71efbddd72 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -240,6 +240,7 @@ def __parse_config(cls, params: CreationPayload, credentials: VagrantCredentials size_specs = cls._get_size_specs(params.size) os_specs = cls._get_os_specs(params.composite_name) # Parse the configuration. + config['ip'] = cls.__get_available_ip() config['box'] = str(os_specs['box']) config['box_version'] = str(os_specs['box_version']) config['virtualizer'] = str(os_specs['virtualizer']) @@ -250,7 +251,6 @@ def __parse_config(cls, params: CreationPayload, credentials: VagrantCredentials config['name'] = instance_id config['platform'] = params.composite_name.split("-")[0] config['arch'] = params.composite_name.split("-")[3] - config['ip'] = cls.__get_available_ip() if config['platform'] == 'macos' and config['virtualizer'] == 'virtualbox' or config['virtualizer'] == 'docker': tmp_port_file = str(instance_dir) + "/port.txt" From d3bb0c6e8ad7c483bc9634dd43b1c77a67545a5b Mon Sep 17 00:00:00 2001 From: c-bordon Date: Thu, 16 May 2024 11:19:27 -0300 Subject: [PATCH 156/195] Updated the documentation for auxiliar files --- deployability/modules/allocation/README.MD | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 19da40356a..9ab419fb75 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -43,7 +43,7 @@ To use this module, you should use a Debian-based system, we recommend using Ubu - cat - vagrant - chmod - + These commands must be available to run on the Host Operating System. #### Use the Allocation module through the Workflow engine @@ -279,12 +279,24 @@ If one wishes to execute the allocaation module without installing the Workflow This argument allows us to provide a configuration file with all the VM definitions. - --track-output - This argument allows us to define which path we want the track file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/VAGRANT-F0753C57-713E-4294-9EA9-89D93D384844/track.yml** + This argument allows us to define which path we want the track file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/allocation-test-4262/track.yml** + Valid options: + - Custom dir: **~/allocation-test** + - Custom filename: **~/allocation-test/manager-track.yml** + - Default (without parameter): **\/\/track.yml** + - File extension: **.yml** or **.yaml** + - The file name must contain **track** string for easy identification >Note: this argument is mandatory for delete action. - --inventory-output - This argument allows us to define which path we want the inventory file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/VAGRANT-F0753C57-713E-4294-9EA9-89D93D384844/inventory.yml** + This argument allows us to define which path we want the inventory file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/allocation-test-4262/inventory.yml** + Valid options: + - Custom dir: **~/allocation-test** + - Custom filename: **~/allocation-test/manager-inventory.yml** + - Default (without parameter): **\/\/inventory.yml** + - File extension: **.yml** or **.yaml** + - The file name must contain **inventory** string for easy identification - --working-dir This argument allows us to define in which directory the files referring to the VM will be generated. By default, **/tmp/wazuh-qa** From bd717eaa6b929bed5ad5b9dd7e85368bf0c84cc6 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Thu, 16 May 2024 11:21:46 -0300 Subject: [PATCH 157/195] Fixed formating --- deployability/modules/allocation/README.MD | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 9ab419fb75..b9245d7931 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -280,23 +280,23 @@ If one wishes to execute the allocaation module without installing the Workflow - --track-output This argument allows us to define which path we want the track file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/allocation-test-4262/track.yml** - Valid options: - - Custom dir: **~/allocation-test** - - Custom filename: **~/allocation-test/manager-track.yml** - - Default (without parameter): **\/\/track.yml** - - File extension: **.yml** or **.yaml** - - The file name must contain **track** string for easy identification + Valid options: + - Custom dir: **~/allocation-test** + - Custom filename: **~/allocation-test/manager-track.yml** + - Default (without parameter): **\/\/track.yml** + - File extension: **.yml** or **.yaml** + - The file name must contain **track** string for easy identification >Note: this argument is mandatory for delete action. - --inventory-output This argument allows us to define which path we want the inventory file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/allocation-test-4262/inventory.yml** - Valid options: - - Custom dir: **~/allocation-test** - - Custom filename: **~/allocation-test/manager-inventory.yml** - - Default (without parameter): **\/\/inventory.yml** - - File extension: **.yml** or **.yaml** - - The file name must contain **inventory** string for easy identification + Valid options: + - Custom dir: **~/allocation-test** + - Custom filename: **~/allocation-test/manager-inventory.yml** + - Default (without parameter): **\/\/inventory.yml** + - File extension: **.yml** or **.yaml** + - The file name must contain **inventory** string for easy identification - --working-dir This argument allows us to define in which directory the files referring to the VM will be generated. By default, **/tmp/wazuh-qa** From f6869a68639e180bfc21a4dd50ce274e8cb08c4b Mon Sep 17 00:00:00 2001 From: c-bordon Date: Thu, 16 May 2024 11:26:59 -0300 Subject: [PATCH 158/195] Line break added --- deployability/modules/allocation/README.MD | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index b9245d7931..327f0138ec 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -269,7 +269,7 @@ If one wishes to execute the allocaation module without installing the Workflow This argument defines the action that the module will perform. Allowed values are **create** or **delete**. By default: **create** - --ssh-key - This argument allows us to use a custom ssh key for the VM. + This argument allows us to use a custom ssh key for the VM. \n Considerations: - must enter the path where the key is located, with the name of the complete key: **~/.ssh/allocation_test** - on the same path you have to have the pair of keys (private and public key with the same name): **~/.ssh/allocation_test** **~/.ssh/allocation_test.pub** @@ -279,7 +279,7 @@ If one wishes to execute the allocaation module without installing the Workflow This argument allows us to provide a configuration file with all the VM definitions. - --track-output - This argument allows us to define which path we want the track file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/allocation-test-4262/track.yml** + This argument allows us to define which path we want the track file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/allocation-test-4262/track.yml** \n Valid options: - Custom dir: **~/allocation-test** - Custom filename: **~/allocation-test/manager-track.yml** @@ -290,7 +290,7 @@ If one wishes to execute the allocaation module without installing the Workflow >Note: this argument is mandatory for delete action. - --inventory-output - This argument allows us to define which path we want the inventory file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/allocation-test-4262/inventory.yml** + This argument allows us to define which path we want the inventory file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/allocation-test-4262/inventory.yml** \n Valid options: - Custom dir: **~/allocation-test** - Custom filename: **~/allocation-test/manager-inventory.yml** From d5fca2cf3ffc2a1e06dc87a0a44ac0f928846172 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Thu, 16 May 2024 11:31:30 -0300 Subject: [PATCH 159/195] Changed break symbol --- deployability/modules/allocation/README.MD | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index 327f0138ec..d90929fdc0 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -269,7 +269,7 @@ If one wishes to execute the allocaation module without installing the Workflow This argument defines the action that the module will perform. Allowed values are **create** or **delete**. By default: **create** - --ssh-key - This argument allows us to use a custom ssh key for the VM. \n + This argument allows us to use a custom ssh key for the VM.
Considerations: - must enter the path where the key is located, with the name of the complete key: **~/.ssh/allocation_test** - on the same path you have to have the pair of keys (private and public key with the same name): **~/.ssh/allocation_test** **~/.ssh/allocation_test.pub** @@ -279,7 +279,7 @@ If one wishes to execute the allocaation module without installing the Workflow This argument allows us to provide a configuration file with all the VM definitions. - --track-output - This argument allows us to define which path we want the track file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/allocation-test-4262/track.yml** \n + This argument allows us to define which path we want the track file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/allocation-test-4262/track.yml**
Valid options: - Custom dir: **~/allocation-test** - Custom filename: **~/allocation-test/manager-track.yml** @@ -290,7 +290,7 @@ If one wishes to execute the allocaation module without installing the Workflow >Note: this argument is mandatory for delete action. - --inventory-output - This argument allows us to define which path we want the inventory file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/allocation-test-4262/inventory.yml** \n + This argument allows us to define which path we want the inventory file to write to. By default, it is saved in the instance directory, for example: **/tmp/wazuh-qa/allocation-test-4262/inventory.yml**
Valid options: - Custom dir: **~/allocation-test** - Custom filename: **~/allocation-test/manager-inventory.yml** From 8828172eefddffaeda6174105730ea2f8979ad68 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 17 May 2024 09:01:55 -0300 Subject: [PATCH 160/195] Added virtualizer parameter for AWS --- deployability/modules/allocation/allocation.py | 2 +- deployability/modules/allocation/aws/instance.py | 1 + deployability/modules/allocation/generic/instance.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/deployability/modules/allocation/allocation.py b/deployability/modules/allocation/allocation.py index 647a4bcc26..e539b31040 100755 --- a/deployability/modules/allocation/allocation.py +++ b/deployability/modules/allocation/allocation.py @@ -184,7 +184,7 @@ def __generate_track_file(instance: Instance, provider_name: str) -> None: ssh_port=port, platform=instance.platform, arch=instance.arch, - virtualizer=instance.virtualizer) + virtualizer=str(instance.virtualizer)) track_path = Path(os.path.join(instance.path, 'track.yaml')) with open(track_path, 'w') as f: yaml.dump(track.model_dump(), f) diff --git a/deployability/modules/allocation/aws/instance.py b/deployability/modules/allocation/aws/instance.py index f02cfbd710..1af0e15a6a 100644 --- a/deployability/modules/allocation/aws/instance.py +++ b/deployability/modules/allocation/aws/instance.py @@ -43,6 +43,7 @@ def __init__(self, instance_parameters: InstancePayload, credentials: AWSCredent self.arch = instance_parameters.arch self.ssh_port = instance_parameters.ssh_port self._user = instance_parameters.user + self.virtualizer = instance_parameters.virtualizer def start(self) -> None: """Start the AWS EC2 instance.""" diff --git a/deployability/modules/allocation/generic/instance.py b/deployability/modules/allocation/generic/instance.py index 72a6713e7a..58b8f4a9de 100644 --- a/deployability/modules/allocation/generic/instance.py +++ b/deployability/modules/allocation/generic/instance.py @@ -51,6 +51,7 @@ def __init__(self, instance_parameters: InstancePayload, credentials: Credential self.platform: str = instance_parameters.platform self.arch: str = instance_parameters.arch self.user: str = instance_parameters.user + self.virtualizer: str = instance_parameters.virtualizer @abstractmethod def start(self) -> None: From 8ea49cc844467b6731fe1182a0568594211ee220 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Mon, 20 May 2024 12:43:38 -0300 Subject: [PATCH 161/195] Fixed dir creation for auxiliar files --- deployability/modules/allocation/allocation.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/deployability/modules/allocation/allocation.py b/deployability/modules/allocation/allocation.py index e539b31040..0815a02440 100755 --- a/deployability/modules/allocation/allocation.py +++ b/deployability/modules/allocation/allocation.py @@ -276,11 +276,18 @@ def __check_auxiliar_files_path(cls, path: Path, instance: Instance, file: str) if path is None: logger.info(f"The {file} file generated at {source}") return source - if path.exists() and path.is_dir(): - destination = destination / (file + '.yaml') + + if str(path).endswith('.yml') or str(path).endswith('.yaml'): + path.parent.mkdir(parents=True, exist_ok=True) + if not path.suffix.__contains__('.'): + path.mkdir(parents=True, exist_ok=True) + + if path.is_dir(): + destination = path / (file + '.yaml') os.replace(source, destination) logger.info(f"The {file} file generated at {destination}") return destination + if str(path).endswith('.yml') or str(path).endswith('.yaml'): if file in os.path.basename(path): os.replace(source, destination) From baa3750a332b9b0d29326a9e9887590ed68d259e Mon Sep 17 00:00:00 2001 From: c-bordon Date: Mon, 20 May 2024 14:08:19 -0300 Subject: [PATCH 162/195] Validations are improved --- deployability/modules/allocation/allocation.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/deployability/modules/allocation/allocation.py b/deployability/modules/allocation/allocation.py index 0815a02440..7a95175ea2 100755 --- a/deployability/modules/allocation/allocation.py +++ b/deployability/modules/allocation/allocation.py @@ -277,18 +277,16 @@ def __check_auxiliar_files_path(cls, path: Path, instance: Instance, file: str) logger.info(f"The {file} file generated at {source}") return source - if str(path).endswith('.yml') or str(path).endswith('.yaml'): - path.parent.mkdir(parents=True, exist_ok=True) if not path.suffix.__contains__('.'): - path.mkdir(parents=True, exist_ok=True) - - if path.is_dir(): + if not path.exists(): + path.mkdir(parents=True, exist_ok=True) destination = path / (file + '.yaml') os.replace(source, destination) logger.info(f"The {file} file generated at {destination}") return destination if str(path).endswith('.yml') or str(path).endswith('.yaml'): + path.parent.mkdir(parents=True, exist_ok=True) if file in os.path.basename(path): os.replace(source, destination) logger.info(f"The {file} file generated at {destination}") From f52ed0a3236494234c100d7dc82c8cb9055c41f1 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Mon, 20 May 2024 17:59:41 -0300 Subject: [PATCH 163/195] Add modifications to obtain os-version from all agents (Linux, MacOs and Windows) --- .../modules/testing/tests/helpers/agent.py | 146 ++++++++++++- .../tests/test_agent/test_basic_info.py | 11 +- .../aws/test-agent-basic-info-vagrant.yaml | 203 ++++++++++++++++++ .../agent/aws/test-agent-basic-info.yaml | 203 ++++++++++++++++++ .../examples/agent/aws/test-agent-time.yaml | 203 ++++++++++++++++++ 5 files changed, 755 insertions(+), 11 deletions(-) create mode 100755 deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info-vagrant.yaml create mode 100755 deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml create mode 100644 deployability/modules/workflow_engine/examples/agent/aws/test-agent-time.yaml diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 718bbbc0d2..31fc60c4df 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -1,27 +1,39 @@ # Copyright (C) 2015, Wazuh Inc. # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 +from typing import List, Optional +from abc import abstractmethod +import re import requests import yaml -from typing import List, Optional +from modules.testing.utils import logger from .constants import WAZUH_CONF, WAZUH_ROOT, WAZUH_WINDOWS_CONF, WAZUH_MACOS_CONF from .executor import WazuhAPI, ConnectionManager from .generic import HostInformation, CheckFiles -from modules.testing.utils import logger + class WazuhAgent: + """Root class for wazuh agents.""" + def __init__(self, inventory_path: str): + self.inventory_path = inventory_path + track_file = inventory_path.replace('inventory', 'track') + with open(track_file, 'r', encoding='utf-8') as yaml_file: + self.inventory_dict = yaml.safe_load(yaml_file) + + @abstractmethod + def get_os_version(self) -> str: + """Get the os version abstract method.""" + @staticmethod def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, live) -> None: - - if live == "False": - s3_url = 'packages-dev' - release = 'pre-release' - else: + if live: s3_url = 'packages' release = wazuh_version[:1] + ".x" - + else: + s3_url = 'packages-dev' + release = 'pre-release' os_type = HostInformation.get_os_type(inventory_path) architecture = HostInformation.get_architecture(inventory_path) @@ -607,3 +619,121 @@ def agent_status_report(wazuh_api: WazuhAPI) -> dict: logger.error(f"Unexpected error: {e}") return {} + +class LinuxAgent(WazuhAgent): + """Linux Agent Class.""" + + @staticmethod + def __parse_systemd(cmd_output: str) -> str: + pattern = r'VERSION_ID="([0-9.]*)"' + match = re.search(pattern, cmd_output) + return match.group(1) if match else "" + + @staticmethod + def __parse_centos(cmd_output: str) -> str: + pattern = r".* ([0-9]{1,2})\.*[0-9]{0,2}.*" + match = re.search(pattern, cmd_output) + return match.group(1) if match else "" + + @staticmethod + def __parse_amazon(cmd_output: str) -> str: + pattern = r"Amazon Linux release (\d+)\.\d+\.\d+" + match = re.search(pattern, cmd_output) + return match.group(1) if match else "" + + @staticmethod + def __parse_rhel(cmd_output: str) -> str: + pattern = r".* ([0-9]{1,2})\.*[0-9]{0,2}.*" + match = re.search(pattern, cmd_output) + return match.group(1) if match else "" + + @staticmethod + def __parse_ubuntu(cmd_output: str) -> str: + pattern = r'VERSION_ID="([0-9.]*)"' + match = re.search(pattern, cmd_output) + return match.group(1) if match else "" + + __linux_version_cmds = { + "systemd": {"command": "cat /etc/os-release", "parser": __parse_systemd}, + "centos": {"command": "cat /etc/centos-release", "parser": __parse_centos}, + "amazon": {"command": "cat /etc/system-release", "parser": __parse_amazon}, + "rhel": {"command": "cat /etc/redhat-release", "parser": __parse_rhel}, + "oracle": {"command": "cat /etc/redhat-release", "parser": __parse_rhel}, + "debian": {"command": "cat /etc/debian_version", "parser": lambda x: x}, + "ubuntu": {"command": "cat /etc/lsb-release", "parser": __parse_ubuntu}, + } + + def __init__(self, inventory_path: str): + """LinuxAgent constructor. """ + super().__init__(inventory_path=inventory_path) + + def _get_os_version(self, platform: str) -> str: + cmd = LinuxAgent.__linux_version_cmds[platform]["command"] + parser = LinuxAgent.__linux_version_cmds[platform]["parser"] + cmd_output = ConnectionManager.execute_commands(self.inventory_path, cmd) + return parser(cmd_output.get('output')) if cmd_output.get('output', None) else "" + + def get_os_version(self) -> str: + """Get os version from the OS platform.""" + # detect platform + os_version: str = "" + if not (os_version := self._get_os_version('systemd')): + for platform in LinuxAgent.__linux_version_cmds: + if (os_version := self._get_os_version(platform)): + logger.info(f"OS Version get using {platform} method.") + break + else: + logger.info("OS Version get using systemd method.") + return os_version + + +class WindowsAgent(WazuhAgent): + """Windows Agent Class.""" + + def get_os_version(self) -> str: + """Get os version from the platform.""" + + cmd = "[System.Environment]::OSVersion.Version.Major" + version_major = ConnectionManager.execute_commands(self.inventory_path, cmd) + + cmd = "[System.Environment]::OSVersion.Version.Minor" + version_minor = ConnectionManager.execute_commands(self.inventory_path, cmd) + + os_version: str = f"{version_major.get('output')}.{version_minor.get('output')}" + + return os_version + +class MacOsAgent(WazuhAgent): + """Windows Agent Class.""" + + def get_os_version(self) -> str: + """Get os version from the platform.""" + cmd = "uname" + name = ConnectionManager.execute_commands(self.inventory_path, cmd) + + if name.get('output') == 'Darwin': + cmd = "sw_vers -productVersion" + version = ConnectionManager.execute_commands(self.inventory_path, cmd) + return version.get('output') + + +__platform_map = { + 'linux': LinuxAgent, + 'macos': MacOsAgent, + 'windows': WindowsAgent, +} + + +def get_agent_from_inventory(inventory_path: str) -> WazuhAgent: + """Create a new WazuhAgent instance from the inventory path information.""" + track_file = inventory_path.replace('inventory', 'track') + with open(track_file, 'r', encoding='utf-8') as yaml_file: + inventory_dict = yaml.safe_load(yaml_file) + + os_type = inventory_dict.get('platform', None) + agent_class: WazuhAgent = __platform_map.get(os_type, None) + + if not agent_class: + raise ValueError(f"Invalid OS type {os_type}") + + return agent_class(inventory_path=inventory_path) diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index a4c6b80d0e..935d62e2fe 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -6,7 +6,7 @@ import pytest from modules.testing.utils import logger -from ..helpers.agent import WazuhAgent, WazuhAPI +from ..helpers.agent import WazuhAgent, WazuhAPI, get_agent_from_inventory from ..helpers.constants import WAZUH_ROOT, WINDOWS_ROOT_DIR, MACOS_ROOT_DIR from ..helpers.generic import HostInformation, GeneralComponentActions, Waits from ..helpers.utils import Utils @@ -81,8 +81,13 @@ def test_wazuh_os_version(wazuh_params): expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) - if not os_type == 'windows': - assert HostInformation.get_os_version_from_inventory(agent_params) in WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names), logger.error('There is a mismatch between the OS version and the OS version of the installed agent') + #if not os_type == 'windows': + agent = get_agent_from_inventory(agent_params) + agent_version = agent.get_os_version() + agent_api_version = WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names) + logger.info(f"Version obtained from agent: {agent_version}") + logger.info(f"Version obtained from Wazuh API: {agent_api_version}") + assert agent_version in agent_api_version, logger.error('There is a mismatch between the OS version and the OS version of the installed agent') if os_type == 'macos': os_name = 'macos' diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info-vagrant.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info-vagrant.yaml new file mode 100755 index 0000000000..37331ee37b --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info-vagrant.yaml @@ -0,0 +1,203 @@ +version: 0.1 +description: This workflow is used to test agents deployment for DDT1 PoC +variables: + agent-os: + #- linux-redhat-7-amd64 + #- linux-redhat-7-arm64 + #- linux-redhat-8-amd64 + #- linux-redhat-8-arm64 + #- linux-redhat-9-amd64 + #- linux-redhat-9-arm64 + #- linux-centos-7-amd64 + #- linux-centos-7-arm64 + #- linux-centos-8-amd64 + #- linux-centos-8-arm64 + #- linux-debian-10-amd64 + #- linux-debian-10-arm64 + #- linux-debian-11-amd64 + #- linux-debian-11-arm64 + #- linux-debian-12-amd64 + #- linux-debian-12-arm64 + #- linux-ubuntu-22.04-amd64 + #- linux-ubuntu-22.04-arm64 + #- linux-ubuntu-18.04-amd64 + #- linux-ubuntu-18.04-arm64 + #- linux-ubuntu-20.04-amd64 + #- linux-ubuntu-20.04-arm64 + #- linux-oracle-9-amd64 + #- linux-amazon-2-amd64 + #- linux-amazon-2-arm64 + #- linux-amazon-2023-amd64 + #- linux-amazon-2023-arm64 + #- windows-desktop-10-amd64 + #- windows-server-2012r2-amd64 + #- windows-server-2016-amd64 + - windows-server-2019-amd64 + #- windows-server-2022-amd64 + + macos-agent-os: + - macos-ventura-13.4.1-amd64 + - macos-sonoma-14.4.1-amd64 + + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: vagrant + macos-infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + #cleanup: + # this: process + # with: + # path: python3 + # args: + # - modules/allocation/main.py + # - action: delete + # - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + #cleanup: + # this: process + # with: + # path: python3 + # args: + # - modules/allocation/main.py + # - action: delete + # - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique macOS agent allocate task + #- task: "allocate-macos-agent-{agent}" + # description: "Allocate resources for the agent." + # do: + # this: process + # with: + # path: python3 + # args: + # - modules/allocation/main.py + # - action: create + # - provider: "{macos-infra-provider}" + # - size: small + # - composite-name: "{agent}" + # - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + # - track-output: "{working-dir}/agent-{agent}/track.yaml" + # - label-termination-date: "1d" + # - label-team: "qa" + # - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + # on-error: "abort-all" + # foreach: + # - variable: macos-agent-os + # as: agent + # cleanup: + # this: process + # with: + # path: python3 + # args: + # - modules/allocation/main.py + # - action: delete + # - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + #- task: "provision-manager-{manager-os}" + # description: "Provision the manager." + # do: + # this: process + # with: + # path: python3 + # args: + # - modules/provision/main.py + # - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + # - install: + # - component: wazuh-manager + # type: assistant + # version: 4.7.4 + # live: True + # depends-on: + # - "allocate-manager-{manager-os}" + # on-error: "abort-all" + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info" + - component: "agent" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" + #- "provision-manager-{manager-os}" + + # Generic macOS agent test task + #- task: "run-agent-{agent}-tests" + # description: "Run tests install for the agent {agent}." + # do: + # this: process + # with: + # path: python3 + # args: + # - modules/testing/main.py + # - targets: + # - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + # - agent: "{working-dir}/agent-{agent}/inventory.yaml" + # - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + # - component: "agent" + # - wazuh-version: "4.7.4" + # - wazuh-revision: "40717" + # - live: "True" + # foreach: + # - variable: macos-agent-os + # as: agent + # depends-on: + # - "allocate-macos-agent-{agent}" + # - "provision-manager-{manager-os}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml new file mode 100755 index 0000000000..5c31dbf323 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml @@ -0,0 +1,203 @@ +version: 0.1 +description: This workflow is used to test agents deployment for DDT1 PoC +variables: + #agent-os: + #- linux-redhat-7-amd64 + #- linux-redhat-7-arm64 + #- linux-redhat-8-amd64 + #- linux-redhat-8-arm64 + #- linux-redhat-9-amd64 + #- linux-redhat-9-arm64 + #- linux-centos-7-amd64 + #- linux-centos-7-arm64 + #- linux-centos-8-amd64 + #- linux-centos-8-arm64 + #- linux-debian-10-amd64 + #- linux-debian-10-arm64 + #- linux-debian-11-amd64 + #- linux-debian-11-arm64 + #- linux-debian-12-amd64 + #- linux-debian-12-arm64 + #- linux-ubuntu-22.04-amd64 + #- linux-ubuntu-22.04-arm64 + #- linux-ubuntu-18.04-amd64 + #- linux-ubuntu-18.04-arm64 + #- linux-ubuntu-20.04-amd64 + #- linux-ubuntu-20.04-arm64 + #- linux-oracle-9-amd64 + #- linux-amazon-2-amd64 + #- linux-amazon-2-arm64 + #- linux-amazon-2023-amd64 + #- linux-amazon-2023-arm64 + #- windows-desktop-10-amd64 + #- windows-server-2012r2-amd64 + #- windows-server-2016-amd64 + #- windows-server-2019-amd64 + #- windows-server-2022-amd64 + + macos-agent-os: + #- macos-ventura-13.4.1-amd64 + - macos-sonoma-14.4.1-amd64 + + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + macos-infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + #- task: "allocate-agent-{agent}" + # description: "Allocate resources for the agent." + # do: + # this: process + # with: + # path: python3 + # args: + # - modules/allocation/main.py + # - action: create + # - provider: "{infra-provider}" + # - size: small + # - composite-name: "{agent}" + # - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + # - track-output: "{working-dir}/agent-{agent}/track.yaml" + # - label-termination-date: "1d" + # - label-team: "qa" + # - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + # on-error: "abort-all" + # foreach: + # - variable: agent-os + # as: agent + # cleanup: + # this: process + # with: + # path: python3 + # args: + # - modules/allocation/main.py + # - action: delete + # - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique macOS agent allocate task + - task: "allocate-macos-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{macos-infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: macos-agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.4 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info" + - component: "agent" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: "True" + foreach: + - variable: macos-agent-os + as: agent + depends-on: + - "allocate-macos-agent-{agent}" + - "provision-manager-{manager-os}" + + # Generic macOS agent test task + #- task: "run-agent-{agent}-tests" + # description: "Run tests install for the agent {agent}." + # do: + # this: process + # with: + # path: python3 + # args: + # - modules/testing/main.py + # - targets: + # - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + # - agent: "{working-dir}/agent-{agent}/inventory.yaml" + # - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + # - component: "agent" + # - wazuh-version: "4.7.4" + # - wazuh-revision: "40717" + # - live: "True" + # foreach: + # - variable: macos-agent-os + # as: agent + # depends-on: + # - "allocate-macos-agent-{agent}" + # - "provision-manager-{manager-os}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-time.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-time.yaml new file mode 100644 index 0000000000..62497a9414 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-time.yaml @@ -0,0 +1,203 @@ +version: 0.1 +description: This workflow is used to test agents deployment for DDT1 PoC +variables: + agent-os: + - linux-redhat-7-amd64 + - linux-redhat-7-arm64 + - linux-redhat-8-amd64 + - linux-redhat-8-arm64 + - linux-redhat-9-amd64 + - linux-redhat-9-arm64 + - linux-centos-7-amd64 + - linux-centos-7-arm64 + - linux-centos-8-amd64 + - linux-centos-8-arm64 + - linux-debian-10-amd64 + - linux-debian-10-arm64 + - linux-debian-11-amd64 + - linux-debian-11-arm64 + - linux-debian-12-amd64 + - linux-debian-12-arm64 + - linux-ubuntu-22.04-amd64 + - linux-ubuntu-22.04-arm64 + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-18.04-arm64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-20.04-arm64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + - linux-amazon-2-arm64 + - linux-amazon-2023-amd64 + - linux-amazon-2023-arm64 + - windows-desktop-10-amd64 + - windows-server-2012r2-amd64 + - windows-server-2016-amd64 + - windows-server-2019-amd64 + - windows-server-2022-amd64 + + macos-agent-os: + - macos-ventura-13.4.1-amd64 + - macos-sonoma-14.4.1-amd64 + + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + macos-infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique macOS agent allocate task + - task: "allocate-macos-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{macos-infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: macos-agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.4 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,connection,basic_info,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: "True" + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + + # Generic macOS agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: "True" + foreach: + - variable: macos-agent-os + as: agent + depends-on: + - "allocate-macos-agent-{agent}" + - "provision-manager-{manager-os}" \ No newline at end of file From 3c4aa6b46a484eb3c96f30bd914fe8dde0af3fed Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Mon, 20 May 2024 18:02:20 -0300 Subject: [PATCH 164/195] Fix live validation --- deployability/modules/testing/tests/helpers/agent.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 31fc60c4df..e190345c42 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -28,12 +28,12 @@ def get_os_version(self) -> str: @staticmethod def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, live) -> None: - if live: - s3_url = 'packages' - release = wazuh_version[:1] + ".x" - else: + if live == "False": s3_url = 'packages-dev' release = 'pre-release' + else: + s3_url = 'packages' + release = wazuh_version[:1] + ".x" os_type = HostInformation.get_os_type(inventory_path) architecture = HostInformation.get_architecture(inventory_path) From 79902fcff871113312404fa9e5d7296cd2e29186 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Mon, 20 May 2024 18:27:39 -0300 Subject: [PATCH 165/195] Remove commented code --- .../tests/test_agent/test_basic_info.py | 5 +- .../agent/aws/test-agent-basic-info.yaml | 6 +- .../agent/aws/test-agents-basic-info-2.log | 1367 +++++++++++++++++ 3 files changed, 1373 insertions(+), 5 deletions(-) create mode 100644 deployability/modules/workflow_engine/examples/agent/aws/test-agents-basic-info-2.log diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py index 935d62e2fe..0509936ce1 100644 --- a/deployability/modules/testing/tests/test_agent/test_basic_info.py +++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py @@ -81,12 +81,11 @@ def test_wazuh_os_version(wazuh_params): expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30) - #if not os_type == 'windows': agent = get_agent_from_inventory(agent_params) agent_version = agent.get_os_version() agent_api_version = WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names) - logger.info(f"Version obtained from agent: {agent_version}") - logger.info(f"Version obtained from Wazuh API: {agent_api_version}") + logger.debug(f"Version obtained from agent: {agent_version}") + logger.debug(f"Version obtained from Wazuh API: {agent_api_version}") assert agent_version in agent_api_version, logger.error('There is a mismatch between the OS version and the OS version of the installed agent') if os_type == 'macos': diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml index 5c31dbf323..5994968a4a 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml @@ -36,8 +36,10 @@ variables: #- windows-server-2022-amd64 macos-agent-os: - #- macos-ventura-13.4.1-amd64 - - macos-sonoma-14.4.1-amd64 + - macos-ventura-13-amd64 + - macos-ventura-13-arm64 + - macos-sonoma-14-amd64 + - macos-sonoma-14-arm64 manager-os: linux-ubuntu-22.04-amd64 infra-provider: aws diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agents-basic-info-2.log b/deployability/modules/workflow_engine/examples/agent/aws/test-agents-basic-info-2.log new file mode 100644 index 0000000000..1d70ed78e7 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agents-basic-info-2.log @@ -0,0 +1,1367 @@ +[2024-05-20 18:14:30] [DEBUG] [486264] [MainThread] [workflow_engine]: Validating input file: modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml +[2024-05-20 18:14:30] [DEBUG] [486264] [MainThread] [workflow_engine]: Loading schema file: /home/fcaffieri/repos/wazuh-qa/.venv/lib/python3.10/site-packages/workflow_engine/schemas/schema_v1.json +[2024-05-20 18:14:30] [DEBUG] [486264] [MainThread] [workflow_engine]: Loading yaml file: modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml +[2024-05-20 18:14:31] [DEBUG] [486264] [MainThread] [workflow_engine]: Loading workflow file: modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml +[2024-05-20 18:14:31] [DEBUG] [486264] [MainThread] [workflow_engine]: Process workflow. +[2024-05-20 18:14:31] [INFO] [486264] [MainThread] [workflow_engine]: Executing DAG tasks. +[2024-05-20 18:14:31] [INFO] [486264] [MainThread] [workflow_engine]: Executing tasks in parallel. +[2024-05-20 18:14:31] [INFO] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: [allocate-manager-linux-ubuntu-22.04-amd64] Starting task. +[2024-05-20 18:14:31] [DEBUG] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: Running task "allocate-manager-linux-ubuntu-22.04-amd64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=aws', '--size=large', '--composite-name=linux-ubuntu-22.04-amd64', '--inventory-output=/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml', '--track-output=/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/track.yaml', '--label-termination-date=1d', '--label-team=qa', '--label-issue=https://github.com/wazuh/wazuh-qa/issues/5191'] +[2024-05-20 18:14:31] [INFO] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-amd64] Starting task. +[2024-05-20 18:14:31] [DEBUG] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: Running task "allocate-macos-agent-macos-ventura-13-amd64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=vagrant', '--size=small', '--composite-name=macos-ventura-13-amd64', '--inventory-output=/tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml', '--track-output=/tmp/dtt1-poc/agent-macos-ventura-13-amd64/track.yaml', '--label-termination-date=1d', '--label-team=qa', '--label-issue=https://github.com/wazuh/wazuh-qa/issues/5191'] +[2024-05-20 18:14:31] [INFO] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-arm64] Starting task. +[2024-05-20 18:14:31] [DEBUG] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: Running task "allocate-macos-agent-macos-ventura-13-arm64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=vagrant', '--size=small', '--composite-name=macos-ventura-13-arm64', '--inventory-output=/tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml', '--track-output=/tmp/dtt1-poc/agent-macos-ventura-13-arm64/track.yaml', '--label-termination-date=1d', '--label-team=qa', '--label-issue=https://github.com/wazuh/wazuh-qa/issues/5191'] +[2024-05-20 18:14:31] [INFO] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-amd64] Starting task. +[2024-05-20 18:14:31] [DEBUG] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: Running task "allocate-macos-agent-macos-sonoma-14-amd64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=vagrant', '--size=small', '--composite-name=macos-sonoma-14-amd64', '--inventory-output=/tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml', '--track-output=/tmp/dtt1-poc/agent-macos-sonoma-14-amd64/track.yaml', '--label-termination-date=1d', '--label-team=qa', '--label-issue=https://github.com/wazuh/wazuh-qa/issues/5191'] +[2024-05-20 18:14:31] [INFO] [486264] [ThreadPoolExecutor-0_4] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-arm64] Starting task. +[2024-05-20 18:14:31] [DEBUG] [486264] [ThreadPoolExecutor-0_4] [workflow_engine]: Running task "allocate-macos-agent-macos-sonoma-14-arm64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=vagrant', '--size=small', '--composite-name=macos-sonoma-14-arm64', '--inventory-output=/tmp/dtt1-poc/agent-macos-sonoma-14-arm64/inventory.yaml', '--track-output=/tmp/dtt1-poc/agent-macos-sonoma-14-arm64/track.yaml', '--label-termination-date=1d', '--label-team=qa', '--label-issue=https://github.com/wazuh/wazuh-qa/issues/5191'] +[2024-05-20 18:15:25] [DEBUG] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: Finished task "allocate-manager-linux-ubuntu-22.04-amd64" execution with result: +[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-20 18:14:31] [INFO] ALLOCATOR: Creating instance at /tmp/wazuh-qa +[2024-05-20 18:14:32] [DEBUG] ALLOCATOR: No config provided. Generating from payload +[2024-05-20 18:14:32] [DEBUG] ALLOCATOR: Generating new key pair +[2024-05-20 18:14:32] [DEBUG] ALLOCATOR: Creating base directory: /tmp/wazuh-qa/AWS-CE3FCC29-7398-43F4-B26B-52F48AECDD5F +[2024-05-20 18:14:51] [DEBUG] ALLOCATOR: Renaming temp /tmp/wazuh-qa/AWS-CE3FCC29-7398-43F4-B26B-52F48AECDD5F directory to /tmp/wazuh-qa/i-0e4256e5c7bc34023 +[2024-05-20 18:14:51] [INFO] ALLOCATOR: Instance i-0e4256e5c7bc34023 created. +[2024-05-20 18:14:53] [INFO] ALLOCATOR: Instance i-0e4256e5c7bc34023 started. +[2024-05-20 18:14:53] [INFO] ALLOCATOR: The inventory file generated at /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml +[2024-05-20 18:14:53] [INFO] ALLOCATOR: The track file generated at /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/track.yaml +[2024-05-20 18:14:53] [WARNING] ALLOCATOR: Error on attempt 1 of 30: [Errno None] Unable to connect to port 2200 on 54.144.70.59 +[2024-05-20 18:15:25] [INFO] ALLOCATOR: SSH connection successful. +[2024-05-20 18:15:25] [INFO] ALLOCATOR: Instance i-0e4256e5c7bc34023 created successfully. + +[2024-05-20 18:15:25] [INFO] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: [allocate-manager-linux-ubuntu-22.04-amd64] Finished task in 54.27 seconds. +[2024-05-20 18:15:25] [INFO] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: [provision-manager-linux-ubuntu-22.04-amd64] Starting task. +[2024-05-20 18:15:25] [DEBUG] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: Running task "provision-manager-linux-ubuntu-22.04-amd64" with arguments: ['modules/provision/main.py', '--inventory=/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml', "--install={'component': 'wazuh-manager', 'type': 'assistant', 'version': '4.7.4', 'live': True}"] +[2024-05-20 18:16:58] [DEBUG] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: Finished task "allocate-macos-agent-macos-sonoma-14-amd64" execution with result: +[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-20 18:14:31] [INFO] ALLOCATOR: Creating instance at /tmp/wazuh-qa +[2024-05-20 18:14:48] [INFO] ALLOCATOR: Using the macStadium Intel server to deploy. +[2024-05-20 18:14:48] [DEBUG] ALLOCATOR: Checking if instance directory exists on remote host +[2024-05-20 18:14:53] [DEBUG] ALLOCATOR: Creating instance directory on remote host +[2024-05-20 18:14:59] [DEBUG] ALLOCATOR: No config provided. Generating from payload +[2024-05-20 18:14:59] [DEBUG] ALLOCATOR: Generating new key pair +[2024-05-20 18:15:03] [DEBUG] ALLOCATOR: Vagrantfile created. Creating instance. +[2024-05-20 18:15:18] [INFO] ALLOCATOR: Instance qa-5191-sonoma-14-9588 created. +[2024-05-20 18:16:24] [INFO] ALLOCATOR: Instance qa-5191-sonoma-14-9588 started. +[2024-05-20 18:16:54] [INFO] ALLOCATOR: The inventory file generated at /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml +[2024-05-20 18:16:54] [INFO] ALLOCATOR: The track file generated at /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/track.yaml +[2024-05-20 18:16:58] [INFO] ALLOCATOR: SSH connection successful. +[2024-05-20 18:16:58] [INFO] ALLOCATOR: Instance qa-5191-sonoma-14-9588 created successfully. + +[2024-05-20 18:16:58] [INFO] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-amd64] Finished task in 147.71 seconds. +[2024-05-20 18:16:59] [DEBUG] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: Finished task "allocate-macos-agent-macos-ventura-13-amd64" execution with result: +[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-20 18:14:31] [INFO] ALLOCATOR: Creating instance at /tmp/wazuh-qa +[2024-05-20 18:14:48] [INFO] ALLOCATOR: Using the macStadium Intel server to deploy. +[2024-05-20 18:14:48] [DEBUG] ALLOCATOR: Checking if instance directory exists on remote host +[2024-05-20 18:14:53] [DEBUG] ALLOCATOR: Creating instance directory on remote host +[2024-05-20 18:14:59] [DEBUG] ALLOCATOR: No config provided. Generating from payload +[2024-05-20 18:14:59] [DEBUG] ALLOCATOR: Generating new key pair +[2024-05-20 18:15:03] [DEBUG] ALLOCATOR: Vagrantfile created. Creating instance. +[2024-05-20 18:15:18] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-3162 created. +[2024-05-20 18:16:20] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-3162 started. +[2024-05-20 18:16:55] [INFO] ALLOCATOR: The inventory file generated at /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml +[2024-05-20 18:16:55] [INFO] ALLOCATOR: The track file generated at /tmp/dtt1-poc/agent-macos-ventura-13-amd64/track.yaml +[2024-05-20 18:16:59] [INFO] ALLOCATOR: SSH connection successful. +[2024-05-20 18:16:59] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-3162 created successfully. + +[2024-05-20 18:16:59] [INFO] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-amd64] Finished task in 148.75 seconds. +[2024-05-20 18:17:06] [DEBUG] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: Finished task "allocate-macos-agent-macos-ventura-13-arm64" execution with result: +[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-20 18:14:31] [INFO] ALLOCATOR: Creating instance at /tmp/wazuh-qa +[2024-05-20 18:14:39] [INFO] ALLOCATOR: macStadium ARM server has less than 2 VMs running, deploying in this host. +[2024-05-20 18:14:39] [DEBUG] ALLOCATOR: Checking if instance directory exists on remote host +[2024-05-20 18:14:45] [DEBUG] ALLOCATOR: Creating instance directory on remote host +[2024-05-20 18:14:50] [DEBUG] ALLOCATOR: No config provided. Generating from payload +[2024-05-20 18:14:50] [DEBUG] ALLOCATOR: Generating new key pair +[2024-05-20 18:14:53] [DEBUG] ALLOCATOR: Vagrantfile created. Creating instance. +[2024-05-20 18:15:10] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-2119 created. +[2024-05-20 18:16:36] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-2119 started. +[2024-05-20 18:17:01] [INFO] ALLOCATOR: The inventory file generated at /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml +[2024-05-20 18:17:01] [INFO] ALLOCATOR: The track file generated at /tmp/dtt1-poc/agent-macos-ventura-13-arm64/track.yaml +[2024-05-20 18:17:06] [INFO] ALLOCATOR: SSH connection successful. +[2024-05-20 18:17:06] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-2119 created successfully. + +[2024-05-20 18:17:06] [INFO] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-arm64] Finished task in 155.43 seconds. +[2024-05-20 18:20:32] [DEBUG] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: Finished task "provision-manager-linux-ubuntu-22.04-amd64" execution with result: +[2024-05-20 18:15:25] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-20 18:15:25] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-20 18:15:25] [DEBUG] PROVISIONER: Setting dependencies: {} for wazuh-manager component. +[2024-05-20 18:15:25] [INFO] PROVISIONER: Initiating provisionment. +[2024-05-20 18:15:25] [DEBUG] PROVISIONER: Running action install for components: [ComponentInfo(component='wazuh-manager', type='assistant', version='4.7.4', dependencies=None, live=True)] +[2024-05-20 18:15:25] [INFO] PROVISIONER: Provisioning "wazuh-manager"... +[2024-05-20 18:15:25] [DEBUG] PROVISIONER: Get OS family for ec2-54-144-70-59.compute-1.amazonaws.com. +[2024-05-20 18:15:25] [DEBUG] PROVISIONER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:15:25] [DEBUG] PROVISIONER: Running playbook: {'hosts': 'ec2-54-144-70-59.compute-1.amazonaws.com', 'become': True, 'gather_facts': True, 'tasks': [{'name': 'Capture ansible_os_family', 'set_fact': {'ansible_os_family': "{{ ansible_facts['distribution_file_variety'] }}", 'cacheable': 'yes'}}]} +Using /etc/ansible/ansible.cfg as config file + +PLAY [ec2-54-144-70-59.compute-1.amazonaws.com] ******************************** + +TASK [Gathering Facts] ********************************************************* +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] + +TASK [Capture ansible_os_family] *********************************************** +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  + ansible_facts: + ansible_os_family: Debian + +PLAY RECAP ********************************************************************* +ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Playbook {'hosts': 'ec2-54-144-70-59.compute-1.amazonaws.com', 'become': True, 'gather_facts': True, 'tasks': [{'name': 'Capture ansible_os_family', 'set_fact': {'ansible_os_family': "{{ ansible_facts['distribution_file_variety'] }}", 'cacheable': 'yes'}}]} finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} +[2024-05-20 18:15:36] [DEBUG] PROVISIONER: OS family: Debian. +[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Render playbook with vars: {'component': 'wazuh-manager', 'version': '4.7.4', 'live': True, 'type': 'assistant', 'dependencies': None, 'templates_path': '/home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/playbooks/wazuh/assistant/install', 'templates_order': ['download.j2', 'install.j2'], 'ansible_os_family': 'Debian'}. +[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Templates found: ['download.j2', 'install.j2'] +[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Rendering template download.j2 +[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Rendering template install.j2 +[2024-05-20 18:15:36] [DEBUG] PROVISIONER: [{'name': 'Install the required packages', 'shell': '\nsudo apt-get update && apt-get -y install curl\n'}, {'name': 'Download the Wazuh installation assistant', 'shell': 'curl -sO https://packages.wazuh.com/4.7/wazuh-install.sh'}, {'name': 'Install wazuh-manager with assistant', 'shell': 'bash ./wazuh-install.sh -a -i'}] +[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Tasks to execute: [{'name': 'Install the required packages', 'shell': '\nsudo apt-get update && apt-get -y install curl\n'}, {'name': 'Download the Wazuh installation assistant', 'shell': 'curl -sO https://packages.wazuh.com/4.7/wazuh-install.sh'}, {'name': 'Install wazuh-manager with assistant', 'shell': 'bash ./wazuh-install.sh -a -i'}]. +[2024-05-20 18:15:36] [INFO] PROVISIONER: Execute install for wazuh-manager. +[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Running playbook: {'hosts': 'ec2-54-144-70-59.compute-1.amazonaws.com', 'become': True, 'gather_facts': True, 'tasks': [{'name': 'Install the required packages', 'shell': '\nsudo apt-get update && apt-get -y install curl\n'}, {'name': 'Download the Wazuh installation assistant', 'shell': 'curl -sO https://packages.wazuh.com/4.7/wazuh-install.sh'}, {'name': 'Install wazuh-manager with assistant', 'shell': 'bash ./wazuh-install.sh -a -i'}]} +Using /etc/ansible/ansible.cfg as config file + +PLAY [ec2-54-144-70-59.compute-1.amazonaws.com] ******************************** + +TASK [Gathering Facts] ********************************************************* +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] + +TASK [Install the required packages] ******************************************* +changed: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=true  + cmd: |2- +  + sudo apt-get update && apt-get -y install curl + delta: '0:00:16.030227' + end: '2024-05-20 21:16:03.129051' + msg: '' + rc: 0 + start: '2024-05-20 21:15:47.098824' + stderr: '' + stderr_lines:  + stdout: |- + Hit:1 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy InRelease + Get:2 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates InRelease [119 kB] + Get:3 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports InRelease [109 kB] + Get:4 http://security.ubuntu.com/ubuntu jammy-security InRelease [110 kB] + Get:5 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy/universe amd64 Packages [14.1 MB] + Get:6 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy/universe Translation-en [5652 kB] + Get:7 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy/universe amd64 c-n-f Metadata [286 kB] + Get:8 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy/multiverse amd64 Packages [217 kB] + Get:9 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy/multiverse Translation-en [112 kB] + Get:10 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy/multiverse amd64 c-n-f Metadata [8372 B] + Get:11 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/main amd64 Packages [1681 kB] + Get:12 http://security.ubuntu.com/ubuntu jammy-security/main amd64 Packages [1468 kB] + Get:13 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/main Translation-en [311 kB] + Get:14 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/main amd64 c-n-f Metadata [16.1 kB] + Get:15 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/restricted amd64 Packages [1928 kB] + Get:16 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/restricted Translation-en [327 kB] + Get:17 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/restricted amd64 c-n-f Metadata [520 B] + Get:18 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/universe amd64 Packages [1074 kB] + Get:19 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/universe Translation-en [246 kB] + Get:20 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/universe amd64 c-n-f Metadata [22.1 kB] + Get:21 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/multiverse amd64 Packages [42.7 kB] + Get:22 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/multiverse Translation-en [10.4 kB] + Get:23 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/multiverse amd64 c-n-f Metadata [472 B] + Get:24 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/main amd64 Packages [67.1 kB] + Get:25 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/main Translation-en [11.0 kB] + Get:26 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/main amd64 c-n-f Metadata [388 B] + Get:27 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/restricted amd64 c-n-f Metadata [116 B] + Get:28 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/universe amd64 Packages [27.2 kB] + Get:29 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/universe Translation-en [16.2 kB] + Get:30 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/universe amd64 c-n-f Metadata [644 B] + Get:31 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/multiverse amd64 c-n-f Metadata [116 B] + Get:32 http://security.ubuntu.com/ubuntu jammy-security/main Translation-en [253 kB] + Get:33 http://security.ubuntu.com/ubuntu jammy-security/main amd64 c-n-f Metadata [11.4 kB] + Get:34 http://security.ubuntu.com/ubuntu jammy-security/restricted amd64 Packages [1871 kB] + Get:35 http://security.ubuntu.com/ubuntu jammy-security/restricted Translation-en [317 kB] + Get:36 http://security.ubuntu.com/ubuntu jammy-security/restricted amd64 c-n-f Metadata [520 B] + Get:37 http://security.ubuntu.com/ubuntu jammy-security/universe amd64 Packages [852 kB] + Get:38 http://security.ubuntu.com/ubuntu jammy-security/universe Translation-en [164 kB] + Get:39 http://security.ubuntu.com/ubuntu jammy-security/universe amd64 c-n-f Metadata [16.8 kB] + Get:40 http://security.ubuntu.com/ubuntu jammy-security/multiverse amd64 Packages [37.2 kB] + Get:41 http://security.ubuntu.com/ubuntu jammy-security/multiverse Translation-en [7588 B] + Get:42 http://security.ubuntu.com/ubuntu jammy-security/multiverse amd64 c-n-f Metadata [260 B] + Fetched 31.5 MB in 3s (10.1 MB/s) + Reading package lists... + Reading package lists... + Building dependency tree... + Reading state information... + The following additional packages will be installed: + libcurl4 + The following packages will be upgraded: + curl libcurl4 + 2 upgraded, 0 newly installed, 0 to remove and 192 not upgraded. + Need to get 484 kB of archives. + After this operation, 0 B of additional disk space will be used. + Get:1 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/main amd64 curl amd64 7.81.0-1ubuntu1.16 [194 kB] + Get:2 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/main amd64 libcurl4 amd64 7.81.0-1ubuntu1.16 [290 kB] + Fetched 484 kB in 0s (16.0 MB/s) + (Reading database ... (Reading database ... 5%(Reading database ... 10%(Reading database ... 15%(Reading database ... 20%(Reading database ... 25%(Reading database ... 30%(Reading database ... 35%(Reading database ... 40%(Reading database ... 45%(Reading database ... 50%(Reading database ... 55%(Reading database ... 60%(Reading database ... 65%(Reading database ... 70%(Reading database ... 75%(Reading database ... 80%(Reading database ... 85%(Reading database ... 90%(Reading database ... 95%(Reading database ... 100%(Reading database ... 64295 files and directories currently installed.) + Preparing to unpack .../curl_7.81.0-1ubuntu1.16_amd64.deb ... + Unpacking curl (7.81.0-1ubuntu1.16) over (7.81.0-1ubuntu1.10) ... + Preparing to unpack .../libcurl4_7.81.0-1ubuntu1.16_amd64.deb ... + Unpacking libcurl4:amd64 (7.81.0-1ubuntu1.16) over (7.81.0-1ubuntu1.10) ... + Setting up libcurl4:amd64 (7.81.0-1ubuntu1.16) ... + Setting up curl (7.81.0-1ubuntu1.16) ... + Processing triggers for man-db (2.10.2-1) ... + Processing triggers for libc-bin (2.35-0ubuntu3.1) ... + stdout_lines:  + +TASK [Download the Wazuh installation assistant] ******************************* +changed: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=true  + cmd: curl -sO https://packages.wazuh.com/4.7/wazuh-install.sh + delta: '0:00:00.078395' + end: '2024-05-20 21:16:07.505254' + msg: '' + rc: 0 + start: '2024-05-20 21:16:07.426859' + stderr: '' + stderr_lines:  + stdout: '' + stdout_lines:  + +TASK [Install wazuh-manager with assistant] ************************************ +changed: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=true  + cmd: bash ./wazuh-install.sh -a -i + delta: '0:04:19.755051' + end: '2024-05-20 21:20:31.584216' + msg: '' + rc: 0 + start: '2024-05-20 21:16:11.829165' + stderr: '' + stderr_lines:  + stdout: |- + 20/05/2024 21:16:11 INFO: Starting Wazuh installation assistant. Wazuh version: 4.7.4 + 20/05/2024 21:16:11 INFO: Verbose logging redirected to /var/log/wazuh-install.log + 20/05/2024 21:16:16 WARNING: Hardware and system checks ignored. + 20/05/2024 21:16:16 INFO: Wazuh web interface port will be 443. + 20/05/2024 21:16:19 INFO: --- Dependencies ---- + 20/05/2024 21:16:19 INFO: Installing apt-transport-https. + 20/05/2024 21:16:26 INFO: Wazuh repository added. + 20/05/2024 21:16:26 INFO: --- Configuration files --- + 20/05/2024 21:16:26 INFO: Generating configuration files. + 20/05/2024 21:16:28 INFO: Created wazuh-install-files.tar. It contains the Wazuh cluster key, certificates, and passwords necessary for installation. + 20/05/2024 21:16:28 INFO: --- Wazuh indexer --- + 20/05/2024 21:16:28 INFO: Starting Wazuh indexer installation. + 20/05/2024 21:17:44 INFO: Wazuh indexer installation finished. + 20/05/2024 21:17:44 INFO: Wazuh indexer post-install configuration finished. + 20/05/2024 21:17:44 INFO: Starting service wazuh-indexer. + 20/05/2024 21:17:55 INFO: wazuh-indexer service started. + 20/05/2024 21:17:55 INFO: Initializing Wazuh indexer cluster security settings. + 20/05/2024 21:18:06 INFO: Wazuh indexer cluster initialized. + 20/05/2024 21:18:06 INFO: --- Wazuh server --- + 20/05/2024 21:18:06 INFO: Starting the Wazuh manager installation. + 20/05/2024 21:18:56 INFO: Wazuh manager installation finished. + 20/05/2024 21:18:56 INFO: Starting service wazuh-manager. + 20/05/2024 21:19:11 INFO: wazuh-manager service started. + 20/05/2024 21:19:11 INFO: Starting Filebeat installation. + 20/05/2024 21:19:18 INFO: Filebeat installation finished. + 20/05/2024 21:19:18 INFO: Filebeat post-install configuration finished. + 20/05/2024 21:19:18 INFO: Starting service filebeat. + 20/05/2024 21:19:19 INFO: filebeat service started. + 20/05/2024 21:19:19 INFO: --- Wazuh dashboard --- + 20/05/2024 21:19:19 INFO: Starting Wazuh dashboard installation. + 20/05/2024 21:20:11 INFO: Wazuh dashboard installation finished. + 20/05/2024 21:20:11 INFO: Wazuh dashboard post-install configuration finished. + 20/05/2024 21:20:11 INFO: Starting service wazuh-dashboard. + 20/05/2024 21:20:11 INFO: wazuh-dashboard service started. + 20/05/2024 21:20:31 INFO: Initializing Wazuh dashboard web application. + 20/05/2024 21:20:31 INFO: Wazuh dashboard web application initialized. + 20/05/2024 21:20:31 INFO: --- Summary --- + 20/05/2024 21:20:31 INFO: You can access the web interface https://:443 + User: admin + Password: +DOEDrBMF+4i77PRfjxLSPBSg1h?p5Qz + 20/05/2024 21:20:31 INFO: Installation finished. + stdout_lines:  + +PLAY RECAP ********************************************************************* +ec2-54-144-70-59.compute-1.amazonaws.com : ok=4  changed=3  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:20:32] [DEBUG] PROVISIONER: Playbook {'hosts': 'ec2-54-144-70-59.compute-1.amazonaws.com', 'become': True, 'gather_facts': True, 'tasks': [{'name': 'Install the required packages', 'shell': '\nsudo apt-get update && apt-get -y install curl\n'}, {'name': 'Download the Wazuh installation assistant', 'shell': 'curl -sO https://packages.wazuh.com/4.7/wazuh-install.sh'}, {'name': 'Install wazuh-manager with assistant', 'shell': 'bash ./wazuh-install.sh -a -i'}]} finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 4}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 3}} +[2024-05-20 18:20:32] [INFO] PROVISIONER: Provision of "wazuh-manager" complete successfully. +[2024-05-20 18:20:32] [INFO] PROVISIONER: All components provisioned successfully. +[2024-05-20 18:20:32] [DEBUG] PROVISIONER: Provision summary: {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 4}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 3}} + +[2024-05-20 18:20:32] [INFO] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: [provision-manager-linux-ubuntu-22.04-amd64] Finished task in 307.12 seconds. +[2024-05-20 18:20:32] [INFO] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: [run-agent-macos-ventura-13-amd64-tests] Starting task. +[2024-05-20 18:20:32] [INFO] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: [run-agent-macos-ventura-13-arm64-tests] Starting task. +[2024-05-20 18:20:32] [DEBUG] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: Running task "run-agent-macos-ventura-13-amd64-tests" with arguments: ['modules/testing/main.py', "--targets={'wazuh-1': '/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml'}", "--targets={'agent': '/tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml'}", '--tests=install,registration,basic_info', '--component=agent', '--wazuh-version=4.7.4', '--wazuh-revision=40717', '--live=True'] +[2024-05-20 18:20:32] [INFO] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: [run-agent-macos-sonoma-14-amd64-tests] Starting task. +[2024-05-20 18:20:32] [DEBUG] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: Running task "run-agent-macos-ventura-13-arm64-tests" with arguments: ['modules/testing/main.py', "--targets={'wazuh-1': '/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml'}", "--targets={'agent': '/tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml'}", '--tests=install,registration,basic_info', '--component=agent', '--wazuh-version=4.7.4', '--wazuh-revision=40717', '--live=True'] +[2024-05-20 18:20:32] [DEBUG] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: Running task "run-agent-macos-sonoma-14-amd64-tests" with arguments: ['modules/testing/main.py', "--targets={'wazuh-1': '/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml'}", "--targets={'agent': '/tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml'}", '--tests=install,registration,basic_info', '--component=agent', '--wazuh-version=4.7.4', '--wazuh-revision=40717', '--live=True'] +[2024-05-20 18:22:52] [ERROR] [486264] [ThreadPoolExecutor-0_4] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-arm64] Task failed with error: Error executing process task Traceback (most recent call last): + File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/main.py", line 39, in + main() + File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/main.py", line 35, in main + Allocator.run(InputPayload(**vars(parse_arguments()))) + File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/allocation.py", line 40, in run + return cls.__create(payload) + File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/allocation.py", line 66, in __create + inventory = cls.__generate_inventory(instance, instance_params.composite_name) + File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/allocation.py", line 130, in __generate_inventory + ssh_config = instance.ssh_connection_info() + File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/vagrant/instance.py", line 174, in ssh_connection_info + cmd = f"/usr/bin/ssh -i /Users/jenkins/.ssh/localhost -L {server_ip}:{port}:{ip}:22 -N 127.0.0.1 -f" +UnboundLocalError: local variable 'ip' referenced before assignment +. +[2024-05-20 18:22:52] [WARNING] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: [run-agent-macos-sonoma-14-arm64-tests] Skipping task due to dependency failure. +[2024-05-20 18:24:20] [DEBUG] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: Finished task "run-agent-macos-sonoma-14-amd64-tests" execution with result: +[2024-05-20 18:20:32] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-20 18:20:32] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-20 18:20:33] [INFO] TESTER: Running tests for ec2-54-144-70-59.compute-1.amazonaws.com +[2024-05-20 18:20:33] [INFO] TESTER: Running tests for 10.10.0.249 +[2024-05-20 18:20:33] [DEBUG] TESTER: Using extra vars: {'component': 'agent', 'wazuh_version': '4.7.4', 'wazuh_revision': '40717', 'wazuh_branch': None, 'working_dir': '/tmp/tests', 'live': True, 'hosts_ip': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.249'], 'targets': '{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}', 'dependencies': '{}', 'local_host_path': '/home/fcaffieri/repos/wazuh-qa/deployability', 'current_user': 'fcaffieri'} +[2024-05-20 18:20:33] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/setup.yml +[2024-05-20 18:20:33] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:20:33] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.249']}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Cleaning old key ssh-keygen registries] ********************************** +changed: [localhost] => (item=ec2-54-144-70-59.compute-1.amazonaws.com) => changed=true  + ansible_loop_var: item + cmd: + - ssh-keygen + - -f + - /home/fcaffieri/.ssh/known_hosts + - -R + - '' + delta: '0:00:00.005604' + end: '2024-05-20 18:20:36.070220' + item: ec2-54-144-70-59.compute-1.amazonaws.com + msg: '' + rc: 0 + start: '2024-05-20 18:20:36.064616' + stderr: Host not found in /home/fcaffieri/.ssh/known_hosts + stderr_lines:  + stdout: '' + stdout_lines:  +changed: [localhost] => (item=10.10.0.249) => changed=true  + ansible_loop_var: item + cmd: + - ssh-keygen + - -f + - /home/fcaffieri/.ssh/known_hosts + - -R + - '' + delta: '0:00:00.005677' + end: '2024-05-20 18:20:36.278792' + item: 10.10.0.249 + msg: '' + rc: 0 + start: '2024-05-20 18:20:36.273115' + stderr: Host not found in /home/fcaffieri/.ssh/known_hosts + stderr_lines:  + stdout: '' + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:20:36] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.249']}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-20 18:20:36] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml +[2024-05-20 18:20:36] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:20:36] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Test install for agent] ************************************************** +changed: [localhost] => changed=true  + cmd: + - python3 + - -m + - pytest + - modules/testing/tests/test_agent/test_install.py + - -v + - --wazuh_version=4.7.4 + - --wazuh_revision=40717 + - --component=agent + - --dependencies={} + - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' + - --live=True + - -s + delta: '0:00:55.232144' + end: '2024-05-20 18:21:35.603508' + msg: '' + rc: 0 + start: '2024-05-20 18:20:40.371364' + stderr: |- + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + stderr_lines:  + stdout: |- + [37m[2024-05-20 18:20:40] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m + [37m[2024-05-20 18:20:40] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m + ============================= test session starts ============================== + platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 + cachedir: .pytest_cache + rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules + collecting ... collected 2 items +  + modules/testing/tests/test_agent/test_install.py::test_installation [32m[2024-05-20 18:20:41] [INFO] TESTER: Checking connection to sonoma-14[0m + [32m[2024-05-20 18:20:45] [INFO] TESTER: Connection established successfully in sonoma-14[0m + [32m[2024-05-20 18:20:50] [INFO] TESTER: Checking connection to ubuntu-22.04[0m + [32m[2024-05-20 18:20:52] [INFO] TESTER: Connection established successfully in ubuntu-22.04[0m + [32m[2024-05-20 18:20:52] [INFO] TESTER: Firewall disabled on sonoma-14[0m + [32m[2024-05-20 18:21:00] [INFO] TESTER: Manager is already installed in ubuntu-22.04[0m + [32m[2024-05-20 18:21:03] [INFO] TESTER: Installing Agent in sonoma-14[0m + PASSED + modules/testing/tests/test_agent/test_install.py::test_status [32m[2024-05-20 18:21:30] [INFO] TESTER: Getting status of sonoma-14[0m + PASSED +  + =============================== warnings summary =============================== + modules/provision/models.py:36 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('dependencies', pre=True) +  + modules/provision/models.py:64 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('install', 'uninstall', pre=True) +  + -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html + ======================== 2 passed, 2 warnings in 54.32s ======================== + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:21:35] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-20 18:21:35] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml +[2024-05-20 18:21:35] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:21:35] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test registration for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_registration.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Test registration for agent] ********************************************* +changed: [localhost] => changed=true  + cmd: + - python3 + - -m + - pytest + - modules/testing/tests/test_agent/test_registration.py + - -v + - --wazuh_version=4.7.4 + - --wazuh_revision=40717 + - --component=agent + - --dependencies={} + - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' + - --live=True + - -s + delta: '0:01:20.275258' + end: '2024-05-20 18:22:58.379567' + msg: '' + rc: 0 + start: '2024-05-20 18:21:38.104309' + stderr: |- + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + stderr_lines:  + stdout: |- + [37m[2024-05-20 18:21:38] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m + [37m[2024-05-20 18:21:38] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m + ============================= test session starts ============================== + platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 + cachedir: .pytest_cache + rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules + collecting ... collected 4 items +  + modules/testing/tests/test_agent/test_registration.py::test_status [32m[2024-05-20 18:21:38] [INFO] TESTER: Checking connection to sonoma-14[0m + [32m[2024-05-20 18:21:43] [INFO] TESTER: Connection established successfully in sonoma-14[0m + [32m[2024-05-20 18:21:57] [INFO] TESTER: Registering agent in sonoma-14[0m + [32m[2024-05-20 18:22:20] [INFO] TESTER: Getting status of sonoma-14[0m + PASSED + modules/testing/tests/test_agent/test_registration.py::test_service PASSED + modules/testing/tests/test_agent/test_registration.py::test_connection PASSED + modules/testing/tests/test_agent/test_registration.py::test_clientKeys PASSED +  + =============================== warnings summary =============================== + modules/provision/models.py:36 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('dependencies', pre=True) +  + modules/provision/models.py:64 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('install', 'uninstall', pre=True) +  + -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html + =================== 4 passed, 2 warnings in 79.47s (0:01:19) =================== + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:22:58] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test registration for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_registration.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-20 18:22:58] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml +[2024-05-20 18:22:58] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:22:58] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test basic_info for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_basic_info.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Test basic_info for agent] *********************************************** +changed: [localhost] => changed=true  + cmd: + - python3 + - -m + - pytest + - modules/testing/tests/test_agent/test_basic_info.py + - -v + - --wazuh_version=4.7.4 + - --wazuh_revision=40717 + - --component=agent + - --dependencies={} + - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' + - --live=True + - -s + delta: '0:00:56.345181' + end: '2024-05-20 18:23:57.581912' + msg: '' + rc: 0 + start: '2024-05-20 18:23:01.236731' + stderr: |- + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + stderr_lines:  + stdout: |- + [37m[2024-05-20 18:23:01] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m + [37m[2024-05-20 18:23:01] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m + ============================= test session starts ============================== + platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 + cachedir: .pytest_cache + rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules + collecting ... collected 3 items +  + modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_os_version [32m[2024-05-20 18:23:02] [INFO] TESTER: Checking connection to sonoma-14[0m + [32m[2024-05-20 18:23:06] [INFO] TESTER: Connection established successfully in sonoma-14[0m + [32m[2024-05-20 18:23:47] [INFO] TESTER: Version obtained from agent: 14.4.1[0m + [32m[2024-05-20 18:23:47] [INFO] TESTER: Version obtained from Wazuh API: 14.4.1[0m + PASSED + modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_version PASSED + modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_revision PASSED +  + =============================== warnings summary =============================== + modules/provision/models.py:36 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('dependencies', pre=True) +  + modules/provision/models.py:64 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('install', 'uninstall', pre=True) +  + -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html + ======================== 3 passed, 2 warnings in 55.47s ======================== + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:23:57] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test basic_info for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_basic_info.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-20 18:23:57] [INFO] TESTER: Cleaning up +[2024-05-20 18:23:57] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:23:57] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml +Using /etc/ansible/ansible.cfg as config file + +PLAY [all] ********************************************************************* + +TASK [Gathering Facts] ********************************************************* +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] + +TASK [Clean test directory] **************************************************** +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  + path: /tmp/tests + state: absent + +PLAY RECAP ********************************************************************* +ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:24:10] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} +[2024-05-20 18:24:10] [INFO] TESTER: Cleaning up +[2024-05-20 18:24:10] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:24:10] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml +Using /etc/ansible/ansible.cfg as config file + +PLAY [all] ********************************************************************* + +TASK [Gathering Facts] ********************************************************* +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] + +TASK [Clean test directory] **************************************************** +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  + path: /tmp/tests + state: absent + +PLAY RECAP ********************************************************************* +ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:24:20] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} + +[2024-05-20 18:24:20] [INFO] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: [run-agent-macos-sonoma-14-amd64-tests] Finished task in 228.14 seconds. +[2024-05-20 18:24:21] [DEBUG] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: Finished task "run-agent-macos-ventura-13-amd64-tests" execution with result: +[2024-05-20 18:20:32] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-20 18:20:32] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-20 18:20:33] [INFO] TESTER: Running tests for ec2-54-144-70-59.compute-1.amazonaws.com +[2024-05-20 18:20:33] [INFO] TESTER: Running tests for 10.10.0.249 +[2024-05-20 18:20:33] [DEBUG] TESTER: Using extra vars: {'component': 'agent', 'wazuh_version': '4.7.4', 'wazuh_revision': '40717', 'wazuh_branch': None, 'working_dir': '/tmp/tests', 'live': True, 'hosts_ip': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.249'], 'targets': '{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}', 'dependencies': '{}', 'local_host_path': '/home/fcaffieri/repos/wazuh-qa/deployability', 'current_user': 'fcaffieri'} +[2024-05-20 18:20:33] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/setup.yml +[2024-05-20 18:20:33] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:20:33] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.249']}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Cleaning old key ssh-keygen registries] ********************************** +changed: [localhost] => (item=ec2-54-144-70-59.compute-1.amazonaws.com) => changed=true  + ansible_loop_var: item + cmd: + - ssh-keygen + - -f + - /home/fcaffieri/.ssh/known_hosts + - -R + - '' + delta: '0:00:00.005746' + end: '2024-05-20 18:20:36.066950' + item: ec2-54-144-70-59.compute-1.amazonaws.com + msg: '' + rc: 0 + start: '2024-05-20 18:20:36.061204' + stderr: Host not found in /home/fcaffieri/.ssh/known_hosts + stderr_lines:  + stdout: '' + stdout_lines:  +changed: [localhost] => (item=10.10.0.249) => changed=true  + ansible_loop_var: item + cmd: + - ssh-keygen + - -f + - /home/fcaffieri/.ssh/known_hosts + - -R + - '' + delta: '0:00:00.005489' + end: '2024-05-20 18:20:36.283322' + item: 10.10.0.249 + msg: '' + rc: 0 + start: '2024-05-20 18:20:36.277833' + stderr: Host not found in /home/fcaffieri/.ssh/known_hosts + stderr_lines:  + stdout: '' + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:20:36] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.249']}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-20 18:20:36] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml +[2024-05-20 18:20:36] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:20:36] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Test install for agent] ************************************************** +changed: [localhost] => changed=true  + cmd: + - python3 + - -m + - pytest + - modules/testing/tests/test_agent/test_install.py + - -v + - --wazuh_version=4.7.4 + - --wazuh_revision=40717 + - --component=agent + - --dependencies={} + - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' + - --live=True + - -s + delta: '0:00:54.122628' + end: '2024-05-20 18:21:33.448564' + msg: '' + rc: 0 + start: '2024-05-20 18:20:39.325936' + stderr: |- + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + stderr_lines:  + stdout: |- + [37m[2024-05-20 18:20:39] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m + [37m[2024-05-20 18:20:39] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m + ============================= test session starts ============================== + platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 + cachedir: .pytest_cache + rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules + collecting ... collected 2 items +  + modules/testing/tests/test_agent/test_install.py::test_installation [32m[2024-05-20 18:20:40] [INFO] TESTER: Checking connection to ventura-13[0m + [32m[2024-05-20 18:20:44] [INFO] TESTER: Connection established successfully in ventura-13[0m + [32m[2024-05-20 18:20:49] [INFO] TESTER: Checking connection to ubuntu-22.04[0m + [32m[2024-05-20 18:20:50] [INFO] TESTER: Connection established successfully in ubuntu-22.04[0m + [32m[2024-05-20 18:20:50] [INFO] TESTER: Firewall disabled on ventura-13[0m + [32m[2024-05-20 18:20:58] [INFO] TESTER: Manager is already installed in ubuntu-22.04[0m + [32m[2024-05-20 18:21:01] [INFO] TESTER: Installing Agent in ventura-13[0m + PASSED + modules/testing/tests/test_agent/test_install.py::test_status [32m[2024-05-20 18:21:28] [INFO] TESTER: Getting status of ventura-13[0m + PASSED +  + =============================== warnings summary =============================== + modules/provision/models.py:36 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('dependencies', pre=True) +  + modules/provision/models.py:64 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('install', 'uninstall', pre=True) +  + modules/testing/tests/helpers/generic.py:406 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/generic.py:406: DeprecationWarning: invalid escape sequence '\s' + f"sed -i '/^\s*#/d' {current_directory}/config.yml" +  + modules/testing/tests/helpers/generic.py:412 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/generic.py:412: DeprecationWarning: invalid escape sequence '\s' + f"sed -i '/^\s*#/d' {current_directory}/config.yml" +  + modules/testing/tests/helpers/generic.py:417 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/generic.py:417: DeprecationWarning: invalid escape sequence '\ ' + commands.append(f"""sed -i '/ip: ""/a\ node_type: master' {current_directory}/config.yml""") +  + modules/testing/tests/helpers/generic.py:601 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/generic.py:601: DeprecationWarning: invalid escape sequence '\;' + command = f'sudo find {directory} -type f -exec shasum -a 256 {{}} \; {filter}' +  + modules/testing/tests/helpers/agent.py:72 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:72: DeprecationWarning: invalid escape sequence '\w' + "-OutFile $env:TEMP\wazuh-agent.msi" +  + modules/testing/tests/helpers/agent.py:75 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:75: DeprecationWarning: invalid escape sequence '\w' + "msiexec.exe /i $env:TEMP\wazuh-agent.msi /q " +  + modules/testing/tests/helpers/agent.py:125 + modules/testing/tests/helpers/agent.py:125 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:125: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_CONF}", +  + modules/testing/tests/helpers/agent.py:142 + modules/testing/tests/helpers/agent.py:142 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:142: DeprecationWarning: invalid escape sequence '\/' + f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_MACOS_CONF}", +  + modules/testing/tests/helpers/agent.py:172 + modules/testing/tests/helpers/agent.py:172 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:172: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_CONF}", +  + modules/testing/tests/helpers/agent.py:181 + modules/testing/tests/helpers/agent.py:181 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:181: DeprecationWarning: invalid escape sequence '\/' + f"sed -i '' 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_MACOS_CONF}", +  + modules/testing/tests/helpers/agent.py:189 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:189: DeprecationWarning: invalid escape sequence '\/' + f"(Get-Content -Path '{WAZUH_WINDOWS_CONF}') -replace '[^<]*<\/protocol>', '{protocol}' | Set-Content -Path '{WAZUH_WINDOWS_CONF}'" +  + modules/testing/tests/helpers/agent.py:227 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:227: DeprecationWarning: invalid escape sequence '\w' + f"msiexec.exe /x $env:TEMP\wazuh-agent.msi /qn" +  + modules/testing/tests/helpers/manager.py:353 + modules/testing/tests/helpers/manager.py:353 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:353: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/node01<\/node_name>/{node_name}<\/node_name>/' {WAZUH_CONF}", +  + modules/testing/tests/helpers/manager.py:354 + modules/testing/tests/helpers/manager.py:354 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:354: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/master<\/node_type>/{node_type}<\/node_type>/' {WAZUH_CONF}", +  + modules/testing/tests/helpers/manager.py:355 + modules/testing/tests/helpers/manager.py:355 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:355: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/<\/key>/{key}<\/key>/' {WAZUH_CONF}", +  + modules/testing/tests/helpers/manager.py:356 + modules/testing/tests/helpers/manager.py:356 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:356: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/NODE_IP<\/node>/{HostInformation.get_internal_ip_from_aws_dns(master_dns)}<\/node>/' {WAZUH_CONF}", +  + modules/testing/tests/helpers/manager.py:357 + modules/testing/tests/helpers/manager.py:357 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:357: DeprecationWarning: invalid escape sequence '\/' + f"sed -i 's/yes<\/disabled>/{disabled}<\/disabled>/' {WAZUH_CONF}", +  + -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html + ======================= 2 passed, 28 warnings in 53.14s ======================== + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:21:33] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-20 18:21:33] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml +[2024-05-20 18:21:33] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:21:33] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test registration for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_registration.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Test registration for agent] ********************************************* +changed: [localhost] => changed=true  + cmd: + - python3 + - -m + - pytest + - modules/testing/tests/test_agent/test_registration.py + - -v + - --wazuh_version=4.7.4 + - --wazuh_revision=40717 + - --component=agent + - --dependencies={} + - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' + - --live=True + - -s + delta: '0:01:22.305398' + end: '2024-05-20 18:22:58.367417' + msg: '' + rc: 0 + start: '2024-05-20 18:21:36.062019' + stderr: |- + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + stderr_lines:  + stdout: |- + [37m[2024-05-20 18:21:36] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m + [37m[2024-05-20 18:21:36] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m + ============================= test session starts ============================== + platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 + cachedir: .pytest_cache + rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules + collecting ... collected 4 items +  + modules/testing/tests/test_agent/test_registration.py::test_status [32m[2024-05-20 18:21:36] [INFO] TESTER: Checking connection to ventura-13[0m + [32m[2024-05-20 18:21:41] [INFO] TESTER: Connection established successfully in ventura-13[0m + [32m[2024-05-20 18:21:55] [INFO] TESTER: Registering agent in ventura-13[0m + [32m[2024-05-20 18:22:19] [INFO] TESTER: Getting status of ventura-13[0m + PASSED + modules/testing/tests/test_agent/test_registration.py::test_service PASSED + modules/testing/tests/test_agent/test_registration.py::test_connection PASSED + modules/testing/tests/test_agent/test_registration.py::test_clientKeys PASSED +  + =============================== warnings summary =============================== + modules/provision/models.py:36 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('dependencies', pre=True) +  + modules/provision/models.py:64 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('install', 'uninstall', pre=True) +  + -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html + =================== 4 passed, 2 warnings in 81.43s (0:01:21) =================== + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:22:58] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test registration for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_registration.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-20 18:22:58] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml +[2024-05-20 18:22:58] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:22:58] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test basic_info for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_basic_info.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Test basic_info for agent] *********************************************** +changed: [localhost] => changed=true  + cmd: + - python3 + - -m + - pytest + - modules/testing/tests/test_agent/test_basic_info.py + - -v + - --wazuh_version=4.7.4 + - --wazuh_revision=40717 + - --component=agent + - --dependencies={} + - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' + - --live=True + - -s + delta: '0:00:56.721239' + end: '2024-05-20 18:23:57.957751' + msg: '' + rc: 0 + start: '2024-05-20 18:23:01.236512' + stderr: |- + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + stderr_lines:  + stdout: |- + [37m[2024-05-20 18:23:01] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m + [37m[2024-05-20 18:23:01] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m + ============================= test session starts ============================== + platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 + cachedir: .pytest_cache + rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules + collecting ... collected 3 items +  + modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_os_version [32m[2024-05-20 18:23:02] [INFO] TESTER: Checking connection to ventura-13[0m + [32m[2024-05-20 18:23:06] [INFO] TESTER: Connection established successfully in ventura-13[0m + [32m[2024-05-20 18:23:47] [INFO] TESTER: Version obtained from agent: 13.6.6[0m + [32m[2024-05-20 18:23:47] [INFO] TESTER: Version obtained from Wazuh API: 13.6.6[0m + PASSED + modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_version PASSED + modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_revision PASSED +  + =============================== warnings summary =============================== + modules/provision/models.py:36 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('dependencies', pre=True) +  + modules/provision/models.py:64 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('install', 'uninstall', pre=True) +  + -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html + ======================== 3 passed, 2 warnings in 55.84s ======================== + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:23:58] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test basic_info for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_basic_info.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-20 18:23:58] [INFO] TESTER: Cleaning up +[2024-05-20 18:23:58] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:23:58] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml +Using /etc/ansible/ansible.cfg as config file + +PLAY [all] ********************************************************************* + +TASK [Gathering Facts] ********************************************************* +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] + +TASK [Clean test directory] **************************************************** +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  + path: /tmp/tests + state: absent + +PLAY RECAP ********************************************************************* +ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:24:11] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} +[2024-05-20 18:24:11] [INFO] TESTER: Cleaning up +[2024-05-20 18:24:11] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:24:11] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml +Using /etc/ansible/ansible.cfg as config file + +PLAY [all] ********************************************************************* + +TASK [Gathering Facts] ********************************************************* +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] + +TASK [Clean test directory] **************************************************** +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  + path: /tmp/tests + state: absent + +PLAY RECAP ********************************************************************* +ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:24:21] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} + +[2024-05-20 18:24:21] [INFO] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: [run-agent-macos-ventura-13-amd64-tests] Finished task in 228.86 seconds. +[2024-05-20 18:24:23] [DEBUG] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: Finished task "run-agent-macos-ventura-13-arm64-tests" execution with result: +[2024-05-20 18:20:33] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-20 18:20:33] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-20 18:20:33] [INFO] TESTER: Running tests for ec2-54-144-70-59.compute-1.amazonaws.com +[2024-05-20 18:20:33] [INFO] TESTER: Running tests for 10.10.0.250 +[2024-05-20 18:20:33] [DEBUG] TESTER: Using extra vars: {'component': 'agent', 'wazuh_version': '4.7.4', 'wazuh_revision': '40717', 'wazuh_branch': None, 'working_dir': '/tmp/tests', 'live': True, 'hosts_ip': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.250'], 'targets': '{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}', 'dependencies': '{}', 'local_host_path': '/home/fcaffieri/repos/wazuh-qa/deployability', 'current_user': 'fcaffieri'} +[2024-05-20 18:20:33] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/setup.yml +[2024-05-20 18:20:33] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:20:33] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.250']}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Cleaning old key ssh-keygen registries] ********************************** +changed: [localhost] => (item=ec2-54-144-70-59.compute-1.amazonaws.com) => changed=true  + ansible_loop_var: item + cmd: + - ssh-keygen + - -f + - /home/fcaffieri/.ssh/known_hosts + - -R + - '' + delta: '0:00:00.005471' + end: '2024-05-20 18:20:36.139850' + item: ec2-54-144-70-59.compute-1.amazonaws.com + msg: '' + rc: 0 + start: '2024-05-20 18:20:36.134379' + stderr: Host not found in /home/fcaffieri/.ssh/known_hosts + stderr_lines:  + stdout: '' + stdout_lines:  +changed: [localhost] => (item=10.10.0.250) => changed=true  + ansible_loop_var: item + cmd: + - ssh-keygen + - -f + - /home/fcaffieri/.ssh/known_hosts + - -R + - '' + delta: '0:00:00.005582' + end: '2024-05-20 18:20:36.363270' + item: 10.10.0.250 + msg: '' + rc: 0 + start: '2024-05-20 18:20:36.357688' + stderr: Host not found in /home/fcaffieri/.ssh/known_hosts + stderr_lines:  + stdout: '' + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:20:36] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.250']}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-20 18:20:36] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml +[2024-05-20 18:20:36] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:20:36] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Test install for agent] ************************************************** +changed: [localhost] => changed=true  + cmd: + - python3 + - -m + - pytest + - modules/testing/tests/test_agent/test_install.py + - -v + - --wazuh_version=4.7.4 + - --wazuh_revision=40717 + - --component=agent + - --dependencies={} + - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' + - --live=True + - -s + delta: '0:00:51.893566' + end: '2024-05-20 18:21:31.286397' + msg: '' + rc: 0 + start: '2024-05-20 18:20:39.392831' + stderr: |- + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + stderr_lines:  + stdout: |- + [37m[2024-05-20 18:20:40] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m + [37m[2024-05-20 18:20:40] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m + ============================= test session starts ============================== + platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 + cachedir: .pytest_cache + rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules + collecting ... collected 2 items +  + modules/testing/tests/test_agent/test_install.py::test_installation [32m[2024-05-20 18:20:40] [INFO] TESTER: Checking connection to ventura-13[0m + [32m[2024-05-20 18:20:44] [INFO] TESTER: Connection established successfully in ventura-13[0m + [32m[2024-05-20 18:20:49] [INFO] TESTER: Checking connection to ubuntu-22.04[0m + [32m[2024-05-20 18:20:50] [INFO] TESTER: Connection established successfully in ubuntu-22.04[0m + [32m[2024-05-20 18:20:50] [INFO] TESTER: Firewall disabled on ventura-13[0m + [32m[2024-05-20 18:20:58] [INFO] TESTER: Manager is already installed in ubuntu-22.04[0m + [32m[2024-05-20 18:21:01] [INFO] TESTER: Installing Agent in ventura-13[0m + PASSED + modules/testing/tests/test_agent/test_install.py::test_status [32m[2024-05-20 18:21:26] [INFO] TESTER: Getting status of ventura-13[0m + PASSED +  + =============================== warnings summary =============================== + modules/provision/models.py:36 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('dependencies', pre=True) +  + modules/provision/models.py:64 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('install', 'uninstall', pre=True) +  + -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html + ======================== 2 passed, 2 warnings in 50.92s ======================== + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:21:31] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-20 18:21:31] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml +[2024-05-20 18:21:31] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:21:31] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test registration for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_registration.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Test registration for agent] ********************************************* +changed: [localhost] => changed=true  + cmd: + - python3 + - -m + - pytest + - modules/testing/tests/test_agent/test_registration.py + - -v + - --wazuh_version=4.7.4 + - --wazuh_revision=40717 + - --component=agent + - --dependencies={} + - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' + - --live=True + - -s + delta: '0:01:21.782904' + end: '2024-05-20 18:22:55.412542' + msg: '' + rc: 0 + start: '2024-05-20 18:21:33.629638' + stderr: |- + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + stderr_lines:  + stdout: |- + [37m[2024-05-20 18:21:34] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m + [37m[2024-05-20 18:21:34] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m + ============================= test session starts ============================== + platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 + cachedir: .pytest_cache + rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules + collecting ... collected 4 items +  + modules/testing/tests/test_agent/test_registration.py::test_status [32m[2024-05-20 18:21:34] [INFO] TESTER: Checking connection to ventura-13[0m + [32m[2024-05-20 18:21:38] [INFO] TESTER: Connection established successfully in ventura-13[0m + [32m[2024-05-20 18:21:53] [INFO] TESTER: Registering agent in ventura-13[0m + [32m[2024-05-20 18:22:17] [INFO] TESTER: Getting status of ventura-13[0m + PASSED + modules/testing/tests/test_agent/test_registration.py::test_service PASSED + modules/testing/tests/test_agent/test_registration.py::test_connection PASSED + modules/testing/tests/test_agent/test_registration.py::test_clientKeys PASSED +  + =============================== warnings summary =============================== + modules/provision/models.py:36 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('dependencies', pre=True) +  + modules/provision/models.py:64 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('install', 'uninstall', pre=True) +  + -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html + =================== 4 passed, 2 warnings in 80.90s (0:01:20) =================== + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:22:55] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test registration for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_registration.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-20 18:22:55] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml +[2024-05-20 18:22:55] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:22:55] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test basic_info for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_basic_info.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] +Using /etc/ansible/ansible.cfg as config file + +PLAY [localhost] *************************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [localhost] + +TASK [Test basic_info for agent] *********************************************** +changed: [localhost] => changed=true  + cmd: + - python3 + - -m + - pytest + - modules/testing/tests/test_agent/test_basic_info.py + - -v + - --wazuh_version=4.7.4 + - --wazuh_revision=40717 + - --component=agent + - --dependencies={} + - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' + - --live=True + - -s + delta: '0:01:04.969172' + end: '2024-05-20 18:24:02.641926' + msg: '' + rc: 0 + start: '2024-05-20 18:22:57.672754' + stderr: |- + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. + stderr_lines:  + stdout: |- + [37m[2024-05-20 18:22:58] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m + [37m[2024-05-20 18:22:58] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m + ============================= test session starts ============================== + platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 + cachedir: .pytest_cache + rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules + collecting ... collected 3 items +  + modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_os_version [32m[2024-05-20 18:22:58] [INFO] TESTER: Checking connection to ventura-13[0m + [32m[2024-05-20 18:23:12] [INFO] TESTER: Connection established successfully in ventura-13[0m + [32m[2024-05-20 18:23:52] [INFO] TESTER: Version obtained from agent: 13.4.1[0m + [32m[2024-05-20 18:23:52] [INFO] TESTER: Version obtained from Wazuh API: 13.4.1[0m + PASSED + modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_version PASSED + modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_revision PASSED +  + =============================== warnings summary =============================== + modules/provision/models.py:36 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('dependencies', pre=True) +  + modules/provision/models.py:64 + /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ + @validator('install', 'uninstall', pre=True) +  + -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html + =================== 3 passed, 2 warnings in 64.17s (0:01:04) =================== + stdout_lines:  + +PLAY RECAP ********************************************************************* +localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:24:02] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test basic_info for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_basic_info.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} +[2024-05-20 18:24:02] [INFO] TESTER: Cleaning up +[2024-05-20 18:24:02] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:24:02] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml +Using /etc/ansible/ansible.cfg as config file + +PLAY [all] ********************************************************************* + +TASK [Gathering Facts] ********************************************************* +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] + +TASK [Clean test directory] **************************************************** +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  + path: /tmp/tests + state: absent + +PLAY RECAP ********************************************************************* +ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:24:13] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} +[2024-05-20 18:24:13] [INFO] TESTER: Cleaning up +[2024-05-20 18:24:13] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} +[2024-05-20 18:24:13] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml +Using /etc/ansible/ansible.cfg as config file + +PLAY [all] ********************************************************************* + +TASK [Gathering Facts] ********************************************************* +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] + +TASK [Clean test directory] **************************************************** +ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  + path: /tmp/tests + state: absent + +PLAY RECAP ********************************************************************* +ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +[2024-05-20 18:24:23] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} + +[2024-05-20 18:24:23] [INFO] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: [run-agent-macos-ventura-13-arm64-tests] Finished task in 230.94 seconds. +[2024-05-20 18:24:23] [INFO] [486264] [MainThread] [workflow_engine]: Executing Reverse DAG tasks. +[2024-05-20 18:24:23] [INFO] [486264] [MainThread] [workflow_engine]: Executing tasks in parallel. +[2024-05-20 18:24:23] [INFO] [486264] [ThreadPoolExecutor-1_0] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-amd64] Starting task. +[2024-05-20 18:24:23] [DEBUG] [486264] [ThreadPoolExecutor-1_0] [workflow_engine]: Running task "allocate-macos-agent-macos-ventura-13-amd64" with arguments: ['modules/allocation/main.py', '--action=delete', '--track-output=/tmp/dtt1-poc/agent-macos-ventura-13-amd64/track.yaml'] +[2024-05-20 18:24:23] [INFO] [486264] [ThreadPoolExecutor-1_1] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-arm64] Starting task. +[2024-05-20 18:24:23] [INFO] [486264] [ThreadPoolExecutor-1_2] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-amd64] Starting task. +[2024-05-20 18:24:23] [DEBUG] [486264] [ThreadPoolExecutor-1_1] [workflow_engine]: Running task "allocate-macos-agent-macos-ventura-13-arm64" with arguments: ['modules/allocation/main.py', '--action=delete', '--track-output=/tmp/dtt1-poc/agent-macos-ventura-13-arm64/track.yaml'] +[2024-05-20 18:24:23] [INFO] [486264] [ThreadPoolExecutor-1_3] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-arm64] Starting task. +[2024-05-20 18:24:23] [INFO] [486264] [ThreadPoolExecutor-1_4] [workflow_engine]: [allocate-manager-linux-ubuntu-22.04-amd64] Starting task. +[2024-05-20 18:24:23] [DEBUG] [486264] [ThreadPoolExecutor-1_2] [workflow_engine]: Running task "allocate-macos-agent-macos-sonoma-14-amd64" with arguments: ['modules/allocation/main.py', '--action=delete', '--track-output=/tmp/dtt1-poc/agent-macos-sonoma-14-amd64/track.yaml'] +[2024-05-20 18:24:23] [DEBUG] [486264] [ThreadPoolExecutor-1_3] [workflow_engine]: Running task "allocate-macos-agent-macos-sonoma-14-arm64" with arguments: ['modules/allocation/main.py', '--action=delete', '--track-output=/tmp/dtt1-poc/agent-macos-sonoma-14-arm64/track.yaml'] +[2024-05-20 18:24:23] [DEBUG] [486264] [ThreadPoolExecutor-1_4] [workflow_engine]: Running task "allocate-manager-linux-ubuntu-22.04-amd64" with arguments: ['modules/allocation/main.py', '--action=delete', '--track-output=/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/track.yaml'] +[2024-05-20 18:24:24] [ERROR] [486264] [ThreadPoolExecutor-1_3] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-arm64] Task failed with error: Error executing process task Traceback (most recent call last): + File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/main.py", line 39, in + main() + File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/main.py", line 35, in main + Allocator.run(InputPayload(**vars(parse_arguments()))) + File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/allocation.py", line 43, in run + return cls.__delete(payload) + File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/allocation.py", line 93, in __delete + with open(payload.track_output, 'r') as f: +FileNotFoundError: [Errno 2] No such file or directory: '/tmp/dtt1-poc/agent-macos-sonoma-14-arm64/track.yaml' +. +[2024-05-20 18:24:59] [DEBUG] [486264] [ThreadPoolExecutor-1_1] [workflow_engine]: Finished task "allocate-macos-agent-macos-ventura-13-arm64" execution with result: +[2024-05-20 18:24:24] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-20 18:24:24] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-20 18:24:24] [INFO] ALLOCATOR: Deleting instance from trackfile /tmp/dtt1-poc/agent-macos-ventura-13-arm64/track.yaml +[2024-05-20 18:24:25] [DEBUG] ALLOCATOR: Destroying instance qa-5191-ventura-13-2119 +[2024-05-20 18:24:42] [DEBUG] ALLOCATOR: Deleting remote directory /Users/jenkins/testing/qa-5191-ventura-13-2119 +[2024-05-20 18:24:48] [DEBUG] ALLOCATOR: Killing remote process on port 43222 +[2024-05-20 18:24:59] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-2119 deleted. + +[2024-05-20 18:24:59] [INFO] [486264] [ThreadPoolExecutor-1_1] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-arm64] Finished task in 36.48 seconds. +[2024-05-20 18:25:06] [DEBUG] [486264] [ThreadPoolExecutor-1_0] [workflow_engine]: Finished task "allocate-macos-agent-macos-ventura-13-amd64" execution with result: +[2024-05-20 18:24:23] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-20 18:24:23] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-20 18:24:23] [INFO] ALLOCATOR: Deleting instance from trackfile /tmp/dtt1-poc/agent-macos-ventura-13-amd64/track.yaml +[2024-05-20 18:24:25] [DEBUG] ALLOCATOR: Destroying instance qa-5191-ventura-13-3162 +[2024-05-20 18:24:48] [DEBUG] ALLOCATOR: Deleting remote directory /Users/jenkins/testing/qa-5191-ventura-13-3162 +[2024-05-20 18:24:54] [DEBUG] ALLOCATOR: Killing remote process on port 43230 +[2024-05-20 18:25:06] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-3162 deleted. + +[2024-05-20 18:25:06] [INFO] [486264] [ThreadPoolExecutor-1_0] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-amd64] Finished task in 42.92 seconds. +[2024-05-20 18:25:06] [DEBUG] [486264] [ThreadPoolExecutor-1_2] [workflow_engine]: Finished task "allocate-macos-agent-macos-sonoma-14-amd64" execution with result: +[2024-05-20 18:24:23] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-20 18:24:23] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-20 18:24:24] [INFO] ALLOCATOR: Deleting instance from trackfile /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/track.yaml +[2024-05-20 18:24:25] [DEBUG] ALLOCATOR: Destroying instance qa-5191-sonoma-14-9588 +[2024-05-20 18:24:48] [DEBUG] ALLOCATOR: Deleting remote directory /Users/jenkins/testing/qa-5191-sonoma-14-9588 +[2024-05-20 18:24:54] [DEBUG] ALLOCATOR: Killing remote process on port 43239 +[2024-05-20 18:25:06] [INFO] ALLOCATOR: Instance qa-5191-sonoma-14-9588 deleted. + +[2024-05-20 18:25:06] [INFO] [486264] [ThreadPoolExecutor-1_2] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-amd64] Finished task in 42.95 seconds. +[2024-05-20 18:25:28] [DEBUG] [486264] [ThreadPoolExecutor-1_4] [workflow_engine]: Finished task "allocate-manager-linux-ubuntu-22.04-amd64" execution with result: +[2024-05-20 18:24:24] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' +[2024-05-20 18:24:24] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' +[2024-05-20 18:24:24] [INFO] ALLOCATOR: Deleting instance from trackfile /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/track.yaml +[2024-05-20 18:24:25] [DEBUG] ALLOCATOR: Deleting credentials: qa-5191-ubuntu-22.04-key-3891 +[2024-05-20 18:25:27] [INFO] ALLOCATOR: Instance i-0e4256e5c7bc34023 deleted. + +[2024-05-20 18:25:28] [INFO] [486264] [ThreadPoolExecutor-1_4] [workflow_engine]: [allocate-manager-linux-ubuntu-22.04-amd64] Finished task in 64.69 seconds. From 69322fa0de4d73bfaf2121ac547c7e727aa15ef2 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Mon, 20 May 2024 18:32:00 -0300 Subject: [PATCH 166/195] Fix examples, remove commented lines --- .../aws/test-agent-basic-info-vagrant.yaml | 232 +-- .../agent/aws/test-agent-basic-info.yaml | 170 +- .../agent/aws/test-agents-basic-info-2.log | 1367 ----------------- 3 files changed, 201 insertions(+), 1568 deletions(-) delete mode 100644 deployability/modules/workflow_engine/examples/agent/aws/test-agents-basic-info-2.log diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info-vagrant.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info-vagrant.yaml index 37331ee37b..e0259b9d73 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info-vagrant.yaml @@ -2,38 +2,38 @@ version: 0.1 description: This workflow is used to test agents deployment for DDT1 PoC variables: agent-os: - #- linux-redhat-7-amd64 - #- linux-redhat-7-arm64 - #- linux-redhat-8-amd64 - #- linux-redhat-8-arm64 - #- linux-redhat-9-amd64 - #- linux-redhat-9-arm64 - #- linux-centos-7-amd64 - #- linux-centos-7-arm64 - #- linux-centos-8-amd64 - #- linux-centos-8-arm64 - #- linux-debian-10-amd64 - #- linux-debian-10-arm64 - #- linux-debian-11-amd64 - #- linux-debian-11-arm64 - #- linux-debian-12-amd64 - #- linux-debian-12-arm64 - #- linux-ubuntu-22.04-amd64 - #- linux-ubuntu-22.04-arm64 - #- linux-ubuntu-18.04-amd64 - #- linux-ubuntu-18.04-arm64 - #- linux-ubuntu-20.04-amd64 - #- linux-ubuntu-20.04-arm64 - #- linux-oracle-9-amd64 - #- linux-amazon-2-amd64 - #- linux-amazon-2-arm64 - #- linux-amazon-2023-amd64 - #- linux-amazon-2023-arm64 - #- windows-desktop-10-amd64 - #- windows-server-2012r2-amd64 - #- windows-server-2016-amd64 + - linux-redhat-7-amd64 + - linux-redhat-7-arm64 + - linux-redhat-8-amd64 + - linux-redhat-8-arm64 + - linux-redhat-9-amd64 + - linux-redhat-9-arm64 + - linux-centos-7-amd64 + - linux-centos-7-arm64 + - linux-centos-8-amd64 + - linux-centos-8-arm64 + - linux-debian-10-amd64 + - linux-debian-10-arm64 + - linux-debian-11-amd64 + - linux-debian-11-arm64 + - linux-debian-12-amd64 + - linux-debian-12-arm64 + - linux-ubuntu-22.04-amd64 + - linux-ubuntu-22.04-arm64 + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-18.04-arm64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-20.04-arm64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + - linux-amazon-2-arm64 + - linux-amazon-2023-amd64 + - linux-amazon-2023-arm64 + - windows-desktop-10-amd64 + - windows-server-2012r2-amd64 + - windows-server-2016-amd64 - windows-server-2019-amd64 - #- windows-server-2022-amd64 + - windows-server-2022-amd64 macos-agent-os: - macos-ventura-13.4.1-amd64 @@ -64,14 +64,14 @@ tasks: - label-team: "qa" - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" on-error: "abort-all" - #cleanup: - # this: process - # with: - # path: python3 - # args: - # - modules/allocation/main.py - # - action: delete - # - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" # Unique agent allocate task - task: "allocate-agent-{agent}" @@ -95,64 +95,64 @@ tasks: foreach: - variable: agent-os as: agent - #cleanup: - # this: process - # with: - # path: python3 - # args: - # - modules/allocation/main.py - # - action: delete - # - track-output: "{working-dir}/agent-{agent}/track.yaml" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" # Unique macOS agent allocate task - #- task: "allocate-macos-agent-{agent}" - # description: "Allocate resources for the agent." - # do: - # this: process - # with: - # path: python3 - # args: - # - modules/allocation/main.py - # - action: create - # - provider: "{macos-infra-provider}" - # - size: small - # - composite-name: "{agent}" - # - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - # - track-output: "{working-dir}/agent-{agent}/track.yaml" - # - label-termination-date: "1d" - # - label-team: "qa" - # - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" - # on-error: "abort-all" - # foreach: - # - variable: macos-agent-os - # as: agent - # cleanup: - # this: process - # with: - # path: python3 - # args: - # - modules/allocation/main.py - # - action: delete - # - track-output: "{working-dir}/agent-{agent}/track.yaml" + - task: "allocate-macos-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{macos-infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: macos-agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" # Unique manager provision task - #- task: "provision-manager-{manager-os}" - # description: "Provision the manager." - # do: - # this: process - # with: - # path: python3 - # args: - # - modules/provision/main.py - # - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" - # - install: - # - component: wazuh-manager - # type: assistant - # version: 4.7.4 - # live: True - # depends-on: - # - "allocate-manager-{manager-os}" - # on-error: "abort-all" + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.4 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" # Generic agent test task - task: "run-agent-{agent}-tests" @@ -176,28 +176,28 @@ tasks: as: agent depends-on: - "allocate-agent-{agent}" - #- "provision-manager-{manager-os}" + - "provision-manager-{manager-os}" # Generic macOS agent test task - #- task: "run-agent-{agent}-tests" - # description: "Run tests install for the agent {agent}." - # do: - # this: process - # with: - # path: python3 - # args: - # - modules/testing/main.py - # - targets: - # - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - # - agent: "{working-dir}/agent-{agent}/inventory.yaml" - # - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - # - component: "agent" - # - wazuh-version: "4.7.4" - # - wazuh-revision: "40717" - # - live: "True" - # foreach: - # - variable: macos-agent-os - # as: agent - # depends-on: - # - "allocate-macos-agent-{agent}" - # - "provision-manager-{manager-os}" \ No newline at end of file + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: "True" + foreach: + - variable: macos-agent-os + as: agent + depends-on: + - "allocate-macos-agent-{agent}" + - "provision-manager-{manager-os}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml index 5994968a4a..0eef8e3863 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml @@ -1,39 +1,39 @@ version: 0.1 description: This workflow is used to test agents deployment for DDT1 PoC variables: - #agent-os: - #- linux-redhat-7-amd64 - #- linux-redhat-7-arm64 - #- linux-redhat-8-amd64 - #- linux-redhat-8-arm64 - #- linux-redhat-9-amd64 - #- linux-redhat-9-arm64 - #- linux-centos-7-amd64 - #- linux-centos-7-arm64 - #- linux-centos-8-amd64 - #- linux-centos-8-arm64 - #- linux-debian-10-amd64 - #- linux-debian-10-arm64 - #- linux-debian-11-amd64 - #- linux-debian-11-arm64 - #- linux-debian-12-amd64 - #- linux-debian-12-arm64 - #- linux-ubuntu-22.04-amd64 - #- linux-ubuntu-22.04-arm64 - #- linux-ubuntu-18.04-amd64 - #- linux-ubuntu-18.04-arm64 - #- linux-ubuntu-20.04-amd64 - #- linux-ubuntu-20.04-arm64 - #- linux-oracle-9-amd64 - #- linux-amazon-2-amd64 - #- linux-amazon-2-arm64 - #- linux-amazon-2023-amd64 - #- linux-amazon-2023-arm64 - #- windows-desktop-10-amd64 - #- windows-server-2012r2-amd64 - #- windows-server-2016-amd64 - #- windows-server-2019-amd64 - #- windows-server-2022-amd64 + agent-os: + - linux-redhat-7-amd64 + - linux-redhat-7-arm64 + - linux-redhat-8-amd64 + - linux-redhat-8-arm64 + - linux-redhat-9-amd64 + - linux-redhat-9-arm64 + - linux-centos-7-amd64 + - linux-centos-7-arm64 + - linux-centos-8-amd64 + - linux-centos-8-arm64 + - linux-debian-10-amd64 + - linux-debian-10-arm64 + - linux-debian-11-amd64 + - linux-debian-11-arm64 + - linux-debian-12-amd64 + - linux-debian-12-arm64 + - linux-ubuntu-22.04-amd64 + - linux-ubuntu-22.04-arm64 + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-18.04-arm64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-20.04-arm64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + - linux-amazon-2-arm64 + - linux-amazon-2023-amd64 + - linux-amazon-2023-arm64 + - windows-desktop-10-amd64 + - windows-server-2012r2-amd64 + - windows-server-2016-amd64 + - windows-server-2019-amd64 + - windows-server-2022-amd64 macos-agent-os: - macos-ventura-13-amd64 @@ -76,35 +76,35 @@ tasks: - track-output: "{working-dir}/manager-{manager-os}/track.yaml" # Unique agent allocate task - #- task: "allocate-agent-{agent}" - # description: "Allocate resources for the agent." - # do: - # this: process - # with: - # path: python3 - # args: - # - modules/allocation/main.py - # - action: create - # - provider: "{infra-provider}" - # - size: small - # - composite-name: "{agent}" - # - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - # - track-output: "{working-dir}/agent-{agent}/track.yaml" - # - label-termination-date: "1d" - # - label-team: "qa" - # - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" - # on-error: "abort-all" - # foreach: - # - variable: agent-os - # as: agent - # cleanup: - # this: process - # with: - # path: python3 - # args: - # - modules/allocation/main.py - # - action: delete - # - track-output: "{working-dir}/agent-{agent}/track.yaml" + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" # Unique macOS agent allocate task - task: "allocate-macos-agent-{agent}" @@ -177,29 +177,29 @@ tasks: - variable: macos-agent-os as: agent depends-on: - - "allocate-macos-agent-{agent}" + - "allocate-agent-{agent}" - "provision-manager-{manager-os}" # Generic macOS agent test task - #- task: "run-agent-{agent}-tests" - # description: "Run tests install for the agent {agent}." - # do: - # this: process - # with: - # path: python3 - # args: - # - modules/testing/main.py - # - targets: - # - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - # - agent: "{working-dir}/agent-{agent}/inventory.yaml" - # - tests: "install,registration,basic_info,connection,restart,stop,uninstall" - # - component: "agent" - # - wazuh-version: "4.7.4" - # - wazuh-revision: "40717" - # - live: "True" - # foreach: - # - variable: macos-agent-os - # as: agent - # depends-on: - # - "allocate-macos-agent-{agent}" - # - "provision-manager-{manager-os}" \ No newline at end of file + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: "True" + foreach: + - variable: macos-agent-os + as: agent + depends-on: + - "allocate-macos-agent-{agent}" + - "provision-manager-{manager-os}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agents-basic-info-2.log b/deployability/modules/workflow_engine/examples/agent/aws/test-agents-basic-info-2.log deleted file mode 100644 index 1d70ed78e7..0000000000 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agents-basic-info-2.log +++ /dev/null @@ -1,1367 +0,0 @@ -[2024-05-20 18:14:30] [DEBUG] [486264] [MainThread] [workflow_engine]: Validating input file: modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml -[2024-05-20 18:14:30] [DEBUG] [486264] [MainThread] [workflow_engine]: Loading schema file: /home/fcaffieri/repos/wazuh-qa/.venv/lib/python3.10/site-packages/workflow_engine/schemas/schema_v1.json -[2024-05-20 18:14:30] [DEBUG] [486264] [MainThread] [workflow_engine]: Loading yaml file: modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml -[2024-05-20 18:14:31] [DEBUG] [486264] [MainThread] [workflow_engine]: Loading workflow file: modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml -[2024-05-20 18:14:31] [DEBUG] [486264] [MainThread] [workflow_engine]: Process workflow. -[2024-05-20 18:14:31] [INFO] [486264] [MainThread] [workflow_engine]: Executing DAG tasks. -[2024-05-20 18:14:31] [INFO] [486264] [MainThread] [workflow_engine]: Executing tasks in parallel. -[2024-05-20 18:14:31] [INFO] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: [allocate-manager-linux-ubuntu-22.04-amd64] Starting task. -[2024-05-20 18:14:31] [DEBUG] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: Running task "allocate-manager-linux-ubuntu-22.04-amd64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=aws', '--size=large', '--composite-name=linux-ubuntu-22.04-amd64', '--inventory-output=/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml', '--track-output=/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/track.yaml', '--label-termination-date=1d', '--label-team=qa', '--label-issue=https://github.com/wazuh/wazuh-qa/issues/5191'] -[2024-05-20 18:14:31] [INFO] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-amd64] Starting task. -[2024-05-20 18:14:31] [DEBUG] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: Running task "allocate-macos-agent-macos-ventura-13-amd64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=vagrant', '--size=small', '--composite-name=macos-ventura-13-amd64', '--inventory-output=/tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml', '--track-output=/tmp/dtt1-poc/agent-macos-ventura-13-amd64/track.yaml', '--label-termination-date=1d', '--label-team=qa', '--label-issue=https://github.com/wazuh/wazuh-qa/issues/5191'] -[2024-05-20 18:14:31] [INFO] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-arm64] Starting task. -[2024-05-20 18:14:31] [DEBUG] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: Running task "allocate-macos-agent-macos-ventura-13-arm64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=vagrant', '--size=small', '--composite-name=macos-ventura-13-arm64', '--inventory-output=/tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml', '--track-output=/tmp/dtt1-poc/agent-macos-ventura-13-arm64/track.yaml', '--label-termination-date=1d', '--label-team=qa', '--label-issue=https://github.com/wazuh/wazuh-qa/issues/5191'] -[2024-05-20 18:14:31] [INFO] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-amd64] Starting task. -[2024-05-20 18:14:31] [DEBUG] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: Running task "allocate-macos-agent-macos-sonoma-14-amd64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=vagrant', '--size=small', '--composite-name=macos-sonoma-14-amd64', '--inventory-output=/tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml', '--track-output=/tmp/dtt1-poc/agent-macos-sonoma-14-amd64/track.yaml', '--label-termination-date=1d', '--label-team=qa', '--label-issue=https://github.com/wazuh/wazuh-qa/issues/5191'] -[2024-05-20 18:14:31] [INFO] [486264] [ThreadPoolExecutor-0_4] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-arm64] Starting task. -[2024-05-20 18:14:31] [DEBUG] [486264] [ThreadPoolExecutor-0_4] [workflow_engine]: Running task "allocate-macos-agent-macos-sonoma-14-arm64" with arguments: ['modules/allocation/main.py', '--action=create', '--provider=vagrant', '--size=small', '--composite-name=macos-sonoma-14-arm64', '--inventory-output=/tmp/dtt1-poc/agent-macos-sonoma-14-arm64/inventory.yaml', '--track-output=/tmp/dtt1-poc/agent-macos-sonoma-14-arm64/track.yaml', '--label-termination-date=1d', '--label-team=qa', '--label-issue=https://github.com/wazuh/wazuh-qa/issues/5191'] -[2024-05-20 18:15:25] [DEBUG] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: Finished task "allocate-manager-linux-ubuntu-22.04-amd64" execution with result: -[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-20 18:14:31] [INFO] ALLOCATOR: Creating instance at /tmp/wazuh-qa -[2024-05-20 18:14:32] [DEBUG] ALLOCATOR: No config provided. Generating from payload -[2024-05-20 18:14:32] [DEBUG] ALLOCATOR: Generating new key pair -[2024-05-20 18:14:32] [DEBUG] ALLOCATOR: Creating base directory: /tmp/wazuh-qa/AWS-CE3FCC29-7398-43F4-B26B-52F48AECDD5F -[2024-05-20 18:14:51] [DEBUG] ALLOCATOR: Renaming temp /tmp/wazuh-qa/AWS-CE3FCC29-7398-43F4-B26B-52F48AECDD5F directory to /tmp/wazuh-qa/i-0e4256e5c7bc34023 -[2024-05-20 18:14:51] [INFO] ALLOCATOR: Instance i-0e4256e5c7bc34023 created. -[2024-05-20 18:14:53] [INFO] ALLOCATOR: Instance i-0e4256e5c7bc34023 started. -[2024-05-20 18:14:53] [INFO] ALLOCATOR: The inventory file generated at /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml -[2024-05-20 18:14:53] [INFO] ALLOCATOR: The track file generated at /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/track.yaml -[2024-05-20 18:14:53] [WARNING] ALLOCATOR: Error on attempt 1 of 30: [Errno None] Unable to connect to port 2200 on 54.144.70.59 -[2024-05-20 18:15:25] [INFO] ALLOCATOR: SSH connection successful. -[2024-05-20 18:15:25] [INFO] ALLOCATOR: Instance i-0e4256e5c7bc34023 created successfully. - -[2024-05-20 18:15:25] [INFO] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: [allocate-manager-linux-ubuntu-22.04-amd64] Finished task in 54.27 seconds. -[2024-05-20 18:15:25] [INFO] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: [provision-manager-linux-ubuntu-22.04-amd64] Starting task. -[2024-05-20 18:15:25] [DEBUG] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: Running task "provision-manager-linux-ubuntu-22.04-amd64" with arguments: ['modules/provision/main.py', '--inventory=/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml', "--install={'component': 'wazuh-manager', 'type': 'assistant', 'version': '4.7.4', 'live': True}"] -[2024-05-20 18:16:58] [DEBUG] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: Finished task "allocate-macos-agent-macos-sonoma-14-amd64" execution with result: -[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-20 18:14:31] [INFO] ALLOCATOR: Creating instance at /tmp/wazuh-qa -[2024-05-20 18:14:48] [INFO] ALLOCATOR: Using the macStadium Intel server to deploy. -[2024-05-20 18:14:48] [DEBUG] ALLOCATOR: Checking if instance directory exists on remote host -[2024-05-20 18:14:53] [DEBUG] ALLOCATOR: Creating instance directory on remote host -[2024-05-20 18:14:59] [DEBUG] ALLOCATOR: No config provided. Generating from payload -[2024-05-20 18:14:59] [DEBUG] ALLOCATOR: Generating new key pair -[2024-05-20 18:15:03] [DEBUG] ALLOCATOR: Vagrantfile created. Creating instance. -[2024-05-20 18:15:18] [INFO] ALLOCATOR: Instance qa-5191-sonoma-14-9588 created. -[2024-05-20 18:16:24] [INFO] ALLOCATOR: Instance qa-5191-sonoma-14-9588 started. -[2024-05-20 18:16:54] [INFO] ALLOCATOR: The inventory file generated at /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml -[2024-05-20 18:16:54] [INFO] ALLOCATOR: The track file generated at /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/track.yaml -[2024-05-20 18:16:58] [INFO] ALLOCATOR: SSH connection successful. -[2024-05-20 18:16:58] [INFO] ALLOCATOR: Instance qa-5191-sonoma-14-9588 created successfully. - -[2024-05-20 18:16:58] [INFO] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-amd64] Finished task in 147.71 seconds. -[2024-05-20 18:16:59] [DEBUG] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: Finished task "allocate-macos-agent-macos-ventura-13-amd64" execution with result: -[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-20 18:14:31] [INFO] ALLOCATOR: Creating instance at /tmp/wazuh-qa -[2024-05-20 18:14:48] [INFO] ALLOCATOR: Using the macStadium Intel server to deploy. -[2024-05-20 18:14:48] [DEBUG] ALLOCATOR: Checking if instance directory exists on remote host -[2024-05-20 18:14:53] [DEBUG] ALLOCATOR: Creating instance directory on remote host -[2024-05-20 18:14:59] [DEBUG] ALLOCATOR: No config provided. Generating from payload -[2024-05-20 18:14:59] [DEBUG] ALLOCATOR: Generating new key pair -[2024-05-20 18:15:03] [DEBUG] ALLOCATOR: Vagrantfile created. Creating instance. -[2024-05-20 18:15:18] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-3162 created. -[2024-05-20 18:16:20] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-3162 started. -[2024-05-20 18:16:55] [INFO] ALLOCATOR: The inventory file generated at /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml -[2024-05-20 18:16:55] [INFO] ALLOCATOR: The track file generated at /tmp/dtt1-poc/agent-macos-ventura-13-amd64/track.yaml -[2024-05-20 18:16:59] [INFO] ALLOCATOR: SSH connection successful. -[2024-05-20 18:16:59] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-3162 created successfully. - -[2024-05-20 18:16:59] [INFO] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-amd64] Finished task in 148.75 seconds. -[2024-05-20 18:17:06] [DEBUG] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: Finished task "allocate-macos-agent-macos-ventura-13-arm64" execution with result: -[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-20 18:14:31] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-20 18:14:31] [INFO] ALLOCATOR: Creating instance at /tmp/wazuh-qa -[2024-05-20 18:14:39] [INFO] ALLOCATOR: macStadium ARM server has less than 2 VMs running, deploying in this host. -[2024-05-20 18:14:39] [DEBUG] ALLOCATOR: Checking if instance directory exists on remote host -[2024-05-20 18:14:45] [DEBUG] ALLOCATOR: Creating instance directory on remote host -[2024-05-20 18:14:50] [DEBUG] ALLOCATOR: No config provided. Generating from payload -[2024-05-20 18:14:50] [DEBUG] ALLOCATOR: Generating new key pair -[2024-05-20 18:14:53] [DEBUG] ALLOCATOR: Vagrantfile created. Creating instance. -[2024-05-20 18:15:10] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-2119 created. -[2024-05-20 18:16:36] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-2119 started. -[2024-05-20 18:17:01] [INFO] ALLOCATOR: The inventory file generated at /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml -[2024-05-20 18:17:01] [INFO] ALLOCATOR: The track file generated at /tmp/dtt1-poc/agent-macos-ventura-13-arm64/track.yaml -[2024-05-20 18:17:06] [INFO] ALLOCATOR: SSH connection successful. -[2024-05-20 18:17:06] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-2119 created successfully. - -[2024-05-20 18:17:06] [INFO] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-arm64] Finished task in 155.43 seconds. -[2024-05-20 18:20:32] [DEBUG] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: Finished task "provision-manager-linux-ubuntu-22.04-amd64" execution with result: -[2024-05-20 18:15:25] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-20 18:15:25] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-20 18:15:25] [DEBUG] PROVISIONER: Setting dependencies: {} for wazuh-manager component. -[2024-05-20 18:15:25] [INFO] PROVISIONER: Initiating provisionment. -[2024-05-20 18:15:25] [DEBUG] PROVISIONER: Running action install for components: [ComponentInfo(component='wazuh-manager', type='assistant', version='4.7.4', dependencies=None, live=True)] -[2024-05-20 18:15:25] [INFO] PROVISIONER: Provisioning "wazuh-manager"... -[2024-05-20 18:15:25] [DEBUG] PROVISIONER: Get OS family for ec2-54-144-70-59.compute-1.amazonaws.com. -[2024-05-20 18:15:25] [DEBUG] PROVISIONER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:15:25] [DEBUG] PROVISIONER: Running playbook: {'hosts': 'ec2-54-144-70-59.compute-1.amazonaws.com', 'become': True, 'gather_facts': True, 'tasks': [{'name': 'Capture ansible_os_family', 'set_fact': {'ansible_os_family': "{{ ansible_facts['distribution_file_variety'] }}", 'cacheable': 'yes'}}]} -Using /etc/ansible/ansible.cfg as config file - -PLAY [ec2-54-144-70-59.compute-1.amazonaws.com] ******************************** - -TASK [Gathering Facts] ********************************************************* -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] - -TASK [Capture ansible_os_family] *********************************************** -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  - ansible_facts: - ansible_os_family: Debian - -PLAY RECAP ********************************************************************* -ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Playbook {'hosts': 'ec2-54-144-70-59.compute-1.amazonaws.com', 'become': True, 'gather_facts': True, 'tasks': [{'name': 'Capture ansible_os_family', 'set_fact': {'ansible_os_family': "{{ ansible_facts['distribution_file_variety'] }}", 'cacheable': 'yes'}}]} finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} -[2024-05-20 18:15:36] [DEBUG] PROVISIONER: OS family: Debian. -[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Render playbook with vars: {'component': 'wazuh-manager', 'version': '4.7.4', 'live': True, 'type': 'assistant', 'dependencies': None, 'templates_path': '/home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/playbooks/wazuh/assistant/install', 'templates_order': ['download.j2', 'install.j2'], 'ansible_os_family': 'Debian'}. -[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Templates found: ['download.j2', 'install.j2'] -[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Rendering template download.j2 -[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Rendering template install.j2 -[2024-05-20 18:15:36] [DEBUG] PROVISIONER: [{'name': 'Install the required packages', 'shell': '\nsudo apt-get update && apt-get -y install curl\n'}, {'name': 'Download the Wazuh installation assistant', 'shell': 'curl -sO https://packages.wazuh.com/4.7/wazuh-install.sh'}, {'name': 'Install wazuh-manager with assistant', 'shell': 'bash ./wazuh-install.sh -a -i'}] -[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Tasks to execute: [{'name': 'Install the required packages', 'shell': '\nsudo apt-get update && apt-get -y install curl\n'}, {'name': 'Download the Wazuh installation assistant', 'shell': 'curl -sO https://packages.wazuh.com/4.7/wazuh-install.sh'}, {'name': 'Install wazuh-manager with assistant', 'shell': 'bash ./wazuh-install.sh -a -i'}]. -[2024-05-20 18:15:36] [INFO] PROVISIONER: Execute install for wazuh-manager. -[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:15:36] [DEBUG] PROVISIONER: Running playbook: {'hosts': 'ec2-54-144-70-59.compute-1.amazonaws.com', 'become': True, 'gather_facts': True, 'tasks': [{'name': 'Install the required packages', 'shell': '\nsudo apt-get update && apt-get -y install curl\n'}, {'name': 'Download the Wazuh installation assistant', 'shell': 'curl -sO https://packages.wazuh.com/4.7/wazuh-install.sh'}, {'name': 'Install wazuh-manager with assistant', 'shell': 'bash ./wazuh-install.sh -a -i'}]} -Using /etc/ansible/ansible.cfg as config file - -PLAY [ec2-54-144-70-59.compute-1.amazonaws.com] ******************************** - -TASK [Gathering Facts] ********************************************************* -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] - -TASK [Install the required packages] ******************************************* -changed: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=true  - cmd: |2- -  - sudo apt-get update && apt-get -y install curl - delta: '0:00:16.030227' - end: '2024-05-20 21:16:03.129051' - msg: '' - rc: 0 - start: '2024-05-20 21:15:47.098824' - stderr: '' - stderr_lines:  - stdout: |- - Hit:1 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy InRelease - Get:2 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates InRelease [119 kB] - Get:3 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports InRelease [109 kB] - Get:4 http://security.ubuntu.com/ubuntu jammy-security InRelease [110 kB] - Get:5 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy/universe amd64 Packages [14.1 MB] - Get:6 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy/universe Translation-en [5652 kB] - Get:7 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy/universe amd64 c-n-f Metadata [286 kB] - Get:8 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy/multiverse amd64 Packages [217 kB] - Get:9 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy/multiverse Translation-en [112 kB] - Get:10 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy/multiverse amd64 c-n-f Metadata [8372 B] - Get:11 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/main amd64 Packages [1681 kB] - Get:12 http://security.ubuntu.com/ubuntu jammy-security/main amd64 Packages [1468 kB] - Get:13 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/main Translation-en [311 kB] - Get:14 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/main amd64 c-n-f Metadata [16.1 kB] - Get:15 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/restricted amd64 Packages [1928 kB] - Get:16 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/restricted Translation-en [327 kB] - Get:17 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/restricted amd64 c-n-f Metadata [520 B] - Get:18 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/universe amd64 Packages [1074 kB] - Get:19 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/universe Translation-en [246 kB] - Get:20 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/universe amd64 c-n-f Metadata [22.1 kB] - Get:21 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/multiverse amd64 Packages [42.7 kB] - Get:22 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/multiverse Translation-en [10.4 kB] - Get:23 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/multiverse amd64 c-n-f Metadata [472 B] - Get:24 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/main amd64 Packages [67.1 kB] - Get:25 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/main Translation-en [11.0 kB] - Get:26 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/main amd64 c-n-f Metadata [388 B] - Get:27 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/restricted amd64 c-n-f Metadata [116 B] - Get:28 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/universe amd64 Packages [27.2 kB] - Get:29 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/universe Translation-en [16.2 kB] - Get:30 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/universe amd64 c-n-f Metadata [644 B] - Get:31 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-backports/multiverse amd64 c-n-f Metadata [116 B] - Get:32 http://security.ubuntu.com/ubuntu jammy-security/main Translation-en [253 kB] - Get:33 http://security.ubuntu.com/ubuntu jammy-security/main amd64 c-n-f Metadata [11.4 kB] - Get:34 http://security.ubuntu.com/ubuntu jammy-security/restricted amd64 Packages [1871 kB] - Get:35 http://security.ubuntu.com/ubuntu jammy-security/restricted Translation-en [317 kB] - Get:36 http://security.ubuntu.com/ubuntu jammy-security/restricted amd64 c-n-f Metadata [520 B] - Get:37 http://security.ubuntu.com/ubuntu jammy-security/universe amd64 Packages [852 kB] - Get:38 http://security.ubuntu.com/ubuntu jammy-security/universe Translation-en [164 kB] - Get:39 http://security.ubuntu.com/ubuntu jammy-security/universe amd64 c-n-f Metadata [16.8 kB] - Get:40 http://security.ubuntu.com/ubuntu jammy-security/multiverse amd64 Packages [37.2 kB] - Get:41 http://security.ubuntu.com/ubuntu jammy-security/multiverse Translation-en [7588 B] - Get:42 http://security.ubuntu.com/ubuntu jammy-security/multiverse amd64 c-n-f Metadata [260 B] - Fetched 31.5 MB in 3s (10.1 MB/s) - Reading package lists... - Reading package lists... - Building dependency tree... - Reading state information... - The following additional packages will be installed: - libcurl4 - The following packages will be upgraded: - curl libcurl4 - 2 upgraded, 0 newly installed, 0 to remove and 192 not upgraded. - Need to get 484 kB of archives. - After this operation, 0 B of additional disk space will be used. - Get:1 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/main amd64 curl amd64 7.81.0-1ubuntu1.16 [194 kB] - Get:2 http://us-east-1.ec2.archive.ubuntu.com/ubuntu jammy-updates/main amd64 libcurl4 amd64 7.81.0-1ubuntu1.16 [290 kB] - Fetched 484 kB in 0s (16.0 MB/s) - (Reading database ... (Reading database ... 5%(Reading database ... 10%(Reading database ... 15%(Reading database ... 20%(Reading database ... 25%(Reading database ... 30%(Reading database ... 35%(Reading database ... 40%(Reading database ... 45%(Reading database ... 50%(Reading database ... 55%(Reading database ... 60%(Reading database ... 65%(Reading database ... 70%(Reading database ... 75%(Reading database ... 80%(Reading database ... 85%(Reading database ... 90%(Reading database ... 95%(Reading database ... 100%(Reading database ... 64295 files and directories currently installed.) - Preparing to unpack .../curl_7.81.0-1ubuntu1.16_amd64.deb ... - Unpacking curl (7.81.0-1ubuntu1.16) over (7.81.0-1ubuntu1.10) ... - Preparing to unpack .../libcurl4_7.81.0-1ubuntu1.16_amd64.deb ... - Unpacking libcurl4:amd64 (7.81.0-1ubuntu1.16) over (7.81.0-1ubuntu1.10) ... - Setting up libcurl4:amd64 (7.81.0-1ubuntu1.16) ... - Setting up curl (7.81.0-1ubuntu1.16) ... - Processing triggers for man-db (2.10.2-1) ... - Processing triggers for libc-bin (2.35-0ubuntu3.1) ... - stdout_lines:  - -TASK [Download the Wazuh installation assistant] ******************************* -changed: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=true  - cmd: curl -sO https://packages.wazuh.com/4.7/wazuh-install.sh - delta: '0:00:00.078395' - end: '2024-05-20 21:16:07.505254' - msg: '' - rc: 0 - start: '2024-05-20 21:16:07.426859' - stderr: '' - stderr_lines:  - stdout: '' - stdout_lines:  - -TASK [Install wazuh-manager with assistant] ************************************ -changed: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=true  - cmd: bash ./wazuh-install.sh -a -i - delta: '0:04:19.755051' - end: '2024-05-20 21:20:31.584216' - msg: '' - rc: 0 - start: '2024-05-20 21:16:11.829165' - stderr: '' - stderr_lines:  - stdout: |- - 20/05/2024 21:16:11 INFO: Starting Wazuh installation assistant. Wazuh version: 4.7.4 - 20/05/2024 21:16:11 INFO: Verbose logging redirected to /var/log/wazuh-install.log - 20/05/2024 21:16:16 WARNING: Hardware and system checks ignored. - 20/05/2024 21:16:16 INFO: Wazuh web interface port will be 443. - 20/05/2024 21:16:19 INFO: --- Dependencies ---- - 20/05/2024 21:16:19 INFO: Installing apt-transport-https. - 20/05/2024 21:16:26 INFO: Wazuh repository added. - 20/05/2024 21:16:26 INFO: --- Configuration files --- - 20/05/2024 21:16:26 INFO: Generating configuration files. - 20/05/2024 21:16:28 INFO: Created wazuh-install-files.tar. It contains the Wazuh cluster key, certificates, and passwords necessary for installation. - 20/05/2024 21:16:28 INFO: --- Wazuh indexer --- - 20/05/2024 21:16:28 INFO: Starting Wazuh indexer installation. - 20/05/2024 21:17:44 INFO: Wazuh indexer installation finished. - 20/05/2024 21:17:44 INFO: Wazuh indexer post-install configuration finished. - 20/05/2024 21:17:44 INFO: Starting service wazuh-indexer. - 20/05/2024 21:17:55 INFO: wazuh-indexer service started. - 20/05/2024 21:17:55 INFO: Initializing Wazuh indexer cluster security settings. - 20/05/2024 21:18:06 INFO: Wazuh indexer cluster initialized. - 20/05/2024 21:18:06 INFO: --- Wazuh server --- - 20/05/2024 21:18:06 INFO: Starting the Wazuh manager installation. - 20/05/2024 21:18:56 INFO: Wazuh manager installation finished. - 20/05/2024 21:18:56 INFO: Starting service wazuh-manager. - 20/05/2024 21:19:11 INFO: wazuh-manager service started. - 20/05/2024 21:19:11 INFO: Starting Filebeat installation. - 20/05/2024 21:19:18 INFO: Filebeat installation finished. - 20/05/2024 21:19:18 INFO: Filebeat post-install configuration finished. - 20/05/2024 21:19:18 INFO: Starting service filebeat. - 20/05/2024 21:19:19 INFO: filebeat service started. - 20/05/2024 21:19:19 INFO: --- Wazuh dashboard --- - 20/05/2024 21:19:19 INFO: Starting Wazuh dashboard installation. - 20/05/2024 21:20:11 INFO: Wazuh dashboard installation finished. - 20/05/2024 21:20:11 INFO: Wazuh dashboard post-install configuration finished. - 20/05/2024 21:20:11 INFO: Starting service wazuh-dashboard. - 20/05/2024 21:20:11 INFO: wazuh-dashboard service started. - 20/05/2024 21:20:31 INFO: Initializing Wazuh dashboard web application. - 20/05/2024 21:20:31 INFO: Wazuh dashboard web application initialized. - 20/05/2024 21:20:31 INFO: --- Summary --- - 20/05/2024 21:20:31 INFO: You can access the web interface https://:443 - User: admin - Password: +DOEDrBMF+4i77PRfjxLSPBSg1h?p5Qz - 20/05/2024 21:20:31 INFO: Installation finished. - stdout_lines:  - -PLAY RECAP ********************************************************************* -ec2-54-144-70-59.compute-1.amazonaws.com : ok=4  changed=3  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:20:32] [DEBUG] PROVISIONER: Playbook {'hosts': 'ec2-54-144-70-59.compute-1.amazonaws.com', 'become': True, 'gather_facts': True, 'tasks': [{'name': 'Install the required packages', 'shell': '\nsudo apt-get update && apt-get -y install curl\n'}, {'name': 'Download the Wazuh installation assistant', 'shell': 'curl -sO https://packages.wazuh.com/4.7/wazuh-install.sh'}, {'name': 'Install wazuh-manager with assistant', 'shell': 'bash ./wazuh-install.sh -a -i'}]} finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 4}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 3}} -[2024-05-20 18:20:32] [INFO] PROVISIONER: Provision of "wazuh-manager" complete successfully. -[2024-05-20 18:20:32] [INFO] PROVISIONER: All components provisioned successfully. -[2024-05-20 18:20:32] [DEBUG] PROVISIONER: Provision summary: {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 4}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 3}} - -[2024-05-20 18:20:32] [INFO] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: [provision-manager-linux-ubuntu-22.04-amd64] Finished task in 307.12 seconds. -[2024-05-20 18:20:32] [INFO] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: [run-agent-macos-ventura-13-amd64-tests] Starting task. -[2024-05-20 18:20:32] [INFO] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: [run-agent-macos-ventura-13-arm64-tests] Starting task. -[2024-05-20 18:20:32] [DEBUG] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: Running task "run-agent-macos-ventura-13-amd64-tests" with arguments: ['modules/testing/main.py', "--targets={'wazuh-1': '/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml'}", "--targets={'agent': '/tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml'}", '--tests=install,registration,basic_info', '--component=agent', '--wazuh-version=4.7.4', '--wazuh-revision=40717', '--live=True'] -[2024-05-20 18:20:32] [INFO] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: [run-agent-macos-sonoma-14-amd64-tests] Starting task. -[2024-05-20 18:20:32] [DEBUG] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: Running task "run-agent-macos-ventura-13-arm64-tests" with arguments: ['modules/testing/main.py', "--targets={'wazuh-1': '/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml'}", "--targets={'agent': '/tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml'}", '--tests=install,registration,basic_info', '--component=agent', '--wazuh-version=4.7.4', '--wazuh-revision=40717', '--live=True'] -[2024-05-20 18:20:32] [DEBUG] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: Running task "run-agent-macos-sonoma-14-amd64-tests" with arguments: ['modules/testing/main.py', "--targets={'wazuh-1': '/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml'}", "--targets={'agent': '/tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml'}", '--tests=install,registration,basic_info', '--component=agent', '--wazuh-version=4.7.4', '--wazuh-revision=40717', '--live=True'] -[2024-05-20 18:22:52] [ERROR] [486264] [ThreadPoolExecutor-0_4] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-arm64] Task failed with error: Error executing process task Traceback (most recent call last): - File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/main.py", line 39, in - main() - File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/main.py", line 35, in main - Allocator.run(InputPayload(**vars(parse_arguments()))) - File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/allocation.py", line 40, in run - return cls.__create(payload) - File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/allocation.py", line 66, in __create - inventory = cls.__generate_inventory(instance, instance_params.composite_name) - File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/allocation.py", line 130, in __generate_inventory - ssh_config = instance.ssh_connection_info() - File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/vagrant/instance.py", line 174, in ssh_connection_info - cmd = f"/usr/bin/ssh -i /Users/jenkins/.ssh/localhost -L {server_ip}:{port}:{ip}:22 -N 127.0.0.1 -f" -UnboundLocalError: local variable 'ip' referenced before assignment -. -[2024-05-20 18:22:52] [WARNING] [486264] [ThreadPoolExecutor-0_0] [workflow_engine]: [run-agent-macos-sonoma-14-arm64-tests] Skipping task due to dependency failure. -[2024-05-20 18:24:20] [DEBUG] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: Finished task "run-agent-macos-sonoma-14-amd64-tests" execution with result: -[2024-05-20 18:20:32] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-20 18:20:32] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-20 18:20:33] [INFO] TESTER: Running tests for ec2-54-144-70-59.compute-1.amazonaws.com -[2024-05-20 18:20:33] [INFO] TESTER: Running tests for 10.10.0.249 -[2024-05-20 18:20:33] [DEBUG] TESTER: Using extra vars: {'component': 'agent', 'wazuh_version': '4.7.4', 'wazuh_revision': '40717', 'wazuh_branch': None, 'working_dir': '/tmp/tests', 'live': True, 'hosts_ip': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.249'], 'targets': '{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}', 'dependencies': '{}', 'local_host_path': '/home/fcaffieri/repos/wazuh-qa/deployability', 'current_user': 'fcaffieri'} -[2024-05-20 18:20:33] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/setup.yml -[2024-05-20 18:20:33] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:20:33] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.249']}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Cleaning old key ssh-keygen registries] ********************************** -changed: [localhost] => (item=ec2-54-144-70-59.compute-1.amazonaws.com) => changed=true  - ansible_loop_var: item - cmd: - - ssh-keygen - - -f - - /home/fcaffieri/.ssh/known_hosts - - -R - - '' - delta: '0:00:00.005604' - end: '2024-05-20 18:20:36.070220' - item: ec2-54-144-70-59.compute-1.amazonaws.com - msg: '' - rc: 0 - start: '2024-05-20 18:20:36.064616' - stderr: Host not found in /home/fcaffieri/.ssh/known_hosts - stderr_lines:  - stdout: '' - stdout_lines:  -changed: [localhost] => (item=10.10.0.249) => changed=true  - ansible_loop_var: item - cmd: - - ssh-keygen - - -f - - /home/fcaffieri/.ssh/known_hosts - - -R - - '' - delta: '0:00:00.005677' - end: '2024-05-20 18:20:36.278792' - item: 10.10.0.249 - msg: '' - rc: 0 - start: '2024-05-20 18:20:36.273115' - stderr: Host not found in /home/fcaffieri/.ssh/known_hosts - stderr_lines:  - stdout: '' - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:20:36] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.249']}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-20 18:20:36] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml -[2024-05-20 18:20:36] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:20:36] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Test install for agent] ************************************************** -changed: [localhost] => changed=true  - cmd: - - python3 - - -m - - pytest - - modules/testing/tests/test_agent/test_install.py - - -v - - --wazuh_version=4.7.4 - - --wazuh_revision=40717 - - --component=agent - - --dependencies={} - - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' - - --live=True - - -s - delta: '0:00:55.232144' - end: '2024-05-20 18:21:35.603508' - msg: '' - rc: 0 - start: '2024-05-20 18:20:40.371364' - stderr: |- - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - stderr_lines:  - stdout: |- - [37m[2024-05-20 18:20:40] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m - [37m[2024-05-20 18:20:40] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m - ============================= test session starts ============================== - platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 - cachedir: .pytest_cache - rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules - collecting ... collected 2 items -  - modules/testing/tests/test_agent/test_install.py::test_installation [32m[2024-05-20 18:20:41] [INFO] TESTER: Checking connection to sonoma-14[0m - [32m[2024-05-20 18:20:45] [INFO] TESTER: Connection established successfully in sonoma-14[0m - [32m[2024-05-20 18:20:50] [INFO] TESTER: Checking connection to ubuntu-22.04[0m - [32m[2024-05-20 18:20:52] [INFO] TESTER: Connection established successfully in ubuntu-22.04[0m - [32m[2024-05-20 18:20:52] [INFO] TESTER: Firewall disabled on sonoma-14[0m - [32m[2024-05-20 18:21:00] [INFO] TESTER: Manager is already installed in ubuntu-22.04[0m - [32m[2024-05-20 18:21:03] [INFO] TESTER: Installing Agent in sonoma-14[0m - PASSED - modules/testing/tests/test_agent/test_install.py::test_status [32m[2024-05-20 18:21:30] [INFO] TESTER: Getting status of sonoma-14[0m - PASSED -  - =============================== warnings summary =============================== - modules/provision/models.py:36 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('dependencies', pre=True) -  - modules/provision/models.py:64 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('install', 'uninstall', pre=True) -  - -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html - ======================== 2 passed, 2 warnings in 54.32s ======================== - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:21:35] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-20 18:21:35] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml -[2024-05-20 18:21:35] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:21:35] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test registration for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_registration.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Test registration for agent] ********************************************* -changed: [localhost] => changed=true  - cmd: - - python3 - - -m - - pytest - - modules/testing/tests/test_agent/test_registration.py - - -v - - --wazuh_version=4.7.4 - - --wazuh_revision=40717 - - --component=agent - - --dependencies={} - - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' - - --live=True - - -s - delta: '0:01:20.275258' - end: '2024-05-20 18:22:58.379567' - msg: '' - rc: 0 - start: '2024-05-20 18:21:38.104309' - stderr: |- - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - stderr_lines:  - stdout: |- - [37m[2024-05-20 18:21:38] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m - [37m[2024-05-20 18:21:38] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m - ============================= test session starts ============================== - platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 - cachedir: .pytest_cache - rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules - collecting ... collected 4 items -  - modules/testing/tests/test_agent/test_registration.py::test_status [32m[2024-05-20 18:21:38] [INFO] TESTER: Checking connection to sonoma-14[0m - [32m[2024-05-20 18:21:43] [INFO] TESTER: Connection established successfully in sonoma-14[0m - [32m[2024-05-20 18:21:57] [INFO] TESTER: Registering agent in sonoma-14[0m - [32m[2024-05-20 18:22:20] [INFO] TESTER: Getting status of sonoma-14[0m - PASSED - modules/testing/tests/test_agent/test_registration.py::test_service PASSED - modules/testing/tests/test_agent/test_registration.py::test_connection PASSED - modules/testing/tests/test_agent/test_registration.py::test_clientKeys PASSED -  - =============================== warnings summary =============================== - modules/provision/models.py:36 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('dependencies', pre=True) -  - modules/provision/models.py:64 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('install', 'uninstall', pre=True) -  - -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html - =================== 4 passed, 2 warnings in 79.47s (0:01:19) =================== - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:22:58] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test registration for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_registration.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-20 18:22:58] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml -[2024-05-20 18:22:58] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:22:58] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test basic_info for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_basic_info.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Test basic_info for agent] *********************************************** -changed: [localhost] => changed=true  - cmd: - - python3 - - -m - - pytest - - modules/testing/tests/test_agent/test_basic_info.py - - -v - - --wazuh_version=4.7.4 - - --wazuh_revision=40717 - - --component=agent - - --dependencies={} - - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' - - --live=True - - -s - delta: '0:00:56.345181' - end: '2024-05-20 18:23:57.581912' - msg: '' - rc: 0 - start: '2024-05-20 18:23:01.236731' - stderr: |- - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - stderr_lines:  - stdout: |- - [37m[2024-05-20 18:23:01] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m - [37m[2024-05-20 18:23:01] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m - ============================= test session starts ============================== - platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 - cachedir: .pytest_cache - rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules - collecting ... collected 3 items -  - modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_os_version [32m[2024-05-20 18:23:02] [INFO] TESTER: Checking connection to sonoma-14[0m - [32m[2024-05-20 18:23:06] [INFO] TESTER: Connection established successfully in sonoma-14[0m - [32m[2024-05-20 18:23:47] [INFO] TESTER: Version obtained from agent: 14.4.1[0m - [32m[2024-05-20 18:23:47] [INFO] TESTER: Version obtained from Wazuh API: 14.4.1[0m - PASSED - modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_version PASSED - modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_revision PASSED -  - =============================== warnings summary =============================== - modules/provision/models.py:36 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('dependencies', pre=True) -  - modules/provision/models.py:64 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('install', 'uninstall', pre=True) -  - -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html - ======================== 3 passed, 2 warnings in 55.47s ======================== - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:23:57] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test basic_info for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_basic_info.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-20 18:23:57] [INFO] TESTER: Cleaning up -[2024-05-20 18:23:57] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:23:57] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml -Using /etc/ansible/ansible.cfg as config file - -PLAY [all] ********************************************************************* - -TASK [Gathering Facts] ********************************************************* -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] - -TASK [Clean test directory] **************************************************** -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  - path: /tmp/tests - state: absent - -PLAY RECAP ********************************************************************* -ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:24:10] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} -[2024-05-20 18:24:10] [INFO] TESTER: Cleaning up -[2024-05-20 18:24:10] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:24:10] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml -Using /etc/ansible/ansible.cfg as config file - -PLAY [all] ********************************************************************* - -TASK [Gathering Facts] ********************************************************* -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] - -TASK [Clean test directory] **************************************************** -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  - path: /tmp/tests - state: absent - -PLAY RECAP ********************************************************************* -ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:24:20] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} - -[2024-05-20 18:24:20] [INFO] [486264] [ThreadPoolExecutor-0_2] [workflow_engine]: [run-agent-macos-sonoma-14-amd64-tests] Finished task in 228.14 seconds. -[2024-05-20 18:24:21] [DEBUG] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: Finished task "run-agent-macos-ventura-13-amd64-tests" execution with result: -[2024-05-20 18:20:32] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-20 18:20:32] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-20 18:20:33] [INFO] TESTER: Running tests for ec2-54-144-70-59.compute-1.amazonaws.com -[2024-05-20 18:20:33] [INFO] TESTER: Running tests for 10.10.0.249 -[2024-05-20 18:20:33] [DEBUG] TESTER: Using extra vars: {'component': 'agent', 'wazuh_version': '4.7.4', 'wazuh_revision': '40717', 'wazuh_branch': None, 'working_dir': '/tmp/tests', 'live': True, 'hosts_ip': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.249'], 'targets': '{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}', 'dependencies': '{}', 'local_host_path': '/home/fcaffieri/repos/wazuh-qa/deployability', 'current_user': 'fcaffieri'} -[2024-05-20 18:20:33] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/setup.yml -[2024-05-20 18:20:33] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:20:33] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.249']}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Cleaning old key ssh-keygen registries] ********************************** -changed: [localhost] => (item=ec2-54-144-70-59.compute-1.amazonaws.com) => changed=true  - ansible_loop_var: item - cmd: - - ssh-keygen - - -f - - /home/fcaffieri/.ssh/known_hosts - - -R - - '' - delta: '0:00:00.005746' - end: '2024-05-20 18:20:36.066950' - item: ec2-54-144-70-59.compute-1.amazonaws.com - msg: '' - rc: 0 - start: '2024-05-20 18:20:36.061204' - stderr: Host not found in /home/fcaffieri/.ssh/known_hosts - stderr_lines:  - stdout: '' - stdout_lines:  -changed: [localhost] => (item=10.10.0.249) => changed=true  - ansible_loop_var: item - cmd: - - ssh-keygen - - -f - - /home/fcaffieri/.ssh/known_hosts - - -R - - '' - delta: '0:00:00.005489' - end: '2024-05-20 18:20:36.283322' - item: 10.10.0.249 - msg: '' - rc: 0 - start: '2024-05-20 18:20:36.277833' - stderr: Host not found in /home/fcaffieri/.ssh/known_hosts - stderr_lines:  - stdout: '' - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:20:36] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.249']}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-20 18:20:36] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml -[2024-05-20 18:20:36] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:20:36] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Test install for agent] ************************************************** -changed: [localhost] => changed=true  - cmd: - - python3 - - -m - - pytest - - modules/testing/tests/test_agent/test_install.py - - -v - - --wazuh_version=4.7.4 - - --wazuh_revision=40717 - - --component=agent - - --dependencies={} - - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' - - --live=True - - -s - delta: '0:00:54.122628' - end: '2024-05-20 18:21:33.448564' - msg: '' - rc: 0 - start: '2024-05-20 18:20:39.325936' - stderr: |- - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - stderr_lines:  - stdout: |- - [37m[2024-05-20 18:20:39] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m - [37m[2024-05-20 18:20:39] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m - ============================= test session starts ============================== - platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 - cachedir: .pytest_cache - rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules - collecting ... collected 2 items -  - modules/testing/tests/test_agent/test_install.py::test_installation [32m[2024-05-20 18:20:40] [INFO] TESTER: Checking connection to ventura-13[0m - [32m[2024-05-20 18:20:44] [INFO] TESTER: Connection established successfully in ventura-13[0m - [32m[2024-05-20 18:20:49] [INFO] TESTER: Checking connection to ubuntu-22.04[0m - [32m[2024-05-20 18:20:50] [INFO] TESTER: Connection established successfully in ubuntu-22.04[0m - [32m[2024-05-20 18:20:50] [INFO] TESTER: Firewall disabled on ventura-13[0m - [32m[2024-05-20 18:20:58] [INFO] TESTER: Manager is already installed in ubuntu-22.04[0m - [32m[2024-05-20 18:21:01] [INFO] TESTER: Installing Agent in ventura-13[0m - PASSED - modules/testing/tests/test_agent/test_install.py::test_status [32m[2024-05-20 18:21:28] [INFO] TESTER: Getting status of ventura-13[0m - PASSED -  - =============================== warnings summary =============================== - modules/provision/models.py:36 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('dependencies', pre=True) -  - modules/provision/models.py:64 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('install', 'uninstall', pre=True) -  - modules/testing/tests/helpers/generic.py:406 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/generic.py:406: DeprecationWarning: invalid escape sequence '\s' - f"sed -i '/^\s*#/d' {current_directory}/config.yml" -  - modules/testing/tests/helpers/generic.py:412 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/generic.py:412: DeprecationWarning: invalid escape sequence '\s' - f"sed -i '/^\s*#/d' {current_directory}/config.yml" -  - modules/testing/tests/helpers/generic.py:417 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/generic.py:417: DeprecationWarning: invalid escape sequence '\ ' - commands.append(f"""sed -i '/ip: ""/a\ node_type: master' {current_directory}/config.yml""") -  - modules/testing/tests/helpers/generic.py:601 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/generic.py:601: DeprecationWarning: invalid escape sequence '\;' - command = f'sudo find {directory} -type f -exec shasum -a 256 {{}} \; {filter}' -  - modules/testing/tests/helpers/agent.py:72 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:72: DeprecationWarning: invalid escape sequence '\w' - "-OutFile $env:TEMP\wazuh-agent.msi" -  - modules/testing/tests/helpers/agent.py:75 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:75: DeprecationWarning: invalid escape sequence '\w' - "msiexec.exe /i $env:TEMP\wazuh-agent.msi /q " -  - modules/testing/tests/helpers/agent.py:125 - modules/testing/tests/helpers/agent.py:125 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:125: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_CONF}", -  - modules/testing/tests/helpers/agent.py:142 - modules/testing/tests/helpers/agent.py:142 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:142: DeprecationWarning: invalid escape sequence '\/' - f"sed -i '.bak' 's/
MANAGER_IP<\/address>/
{host_ip}<\/address>/g' {WAZUH_MACOS_CONF}", -  - modules/testing/tests/helpers/agent.py:172 - modules/testing/tests/helpers/agent.py:172 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:172: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_CONF}", -  - modules/testing/tests/helpers/agent.py:181 - modules/testing/tests/helpers/agent.py:181 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:181: DeprecationWarning: invalid escape sequence '\/' - f"sed -i '' 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_MACOS_CONF}", -  - modules/testing/tests/helpers/agent.py:189 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:189: DeprecationWarning: invalid escape sequence '\/' - f"(Get-Content -Path '{WAZUH_WINDOWS_CONF}') -replace '[^<]*<\/protocol>', '{protocol}' | Set-Content -Path '{WAZUH_WINDOWS_CONF}'" -  - modules/testing/tests/helpers/agent.py:227 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/agent.py:227: DeprecationWarning: invalid escape sequence '\w' - f"msiexec.exe /x $env:TEMP\wazuh-agent.msi /qn" -  - modules/testing/tests/helpers/manager.py:353 - modules/testing/tests/helpers/manager.py:353 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:353: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/node01<\/node_name>/{node_name}<\/node_name>/' {WAZUH_CONF}", -  - modules/testing/tests/helpers/manager.py:354 - modules/testing/tests/helpers/manager.py:354 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:354: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/master<\/node_type>/{node_type}<\/node_type>/' {WAZUH_CONF}", -  - modules/testing/tests/helpers/manager.py:355 - modules/testing/tests/helpers/manager.py:355 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:355: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/<\/key>/{key}<\/key>/' {WAZUH_CONF}", -  - modules/testing/tests/helpers/manager.py:356 - modules/testing/tests/helpers/manager.py:356 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:356: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/NODE_IP<\/node>/{HostInformation.get_internal_ip_from_aws_dns(master_dns)}<\/node>/' {WAZUH_CONF}", -  - modules/testing/tests/helpers/manager.py:357 - modules/testing/tests/helpers/manager.py:357 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/tests/helpers/manager.py:357: DeprecationWarning: invalid escape sequence '\/' - f"sed -i 's/yes<\/disabled>/{disabled}<\/disabled>/' {WAZUH_CONF}", -  - -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html - ======================= 2 passed, 28 warnings in 53.14s ======================== - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:21:33] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-20 18:21:33] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml -[2024-05-20 18:21:33] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:21:33] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test registration for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_registration.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Test registration for agent] ********************************************* -changed: [localhost] => changed=true  - cmd: - - python3 - - -m - - pytest - - modules/testing/tests/test_agent/test_registration.py - - -v - - --wazuh_version=4.7.4 - - --wazuh_revision=40717 - - --component=agent - - --dependencies={} - - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' - - --live=True - - -s - delta: '0:01:22.305398' - end: '2024-05-20 18:22:58.367417' - msg: '' - rc: 0 - start: '2024-05-20 18:21:36.062019' - stderr: |- - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - stderr_lines:  - stdout: |- - [37m[2024-05-20 18:21:36] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m - [37m[2024-05-20 18:21:36] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m - ============================= test session starts ============================== - platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 - cachedir: .pytest_cache - rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules - collecting ... collected 4 items -  - modules/testing/tests/test_agent/test_registration.py::test_status [32m[2024-05-20 18:21:36] [INFO] TESTER: Checking connection to ventura-13[0m - [32m[2024-05-20 18:21:41] [INFO] TESTER: Connection established successfully in ventura-13[0m - [32m[2024-05-20 18:21:55] [INFO] TESTER: Registering agent in ventura-13[0m - [32m[2024-05-20 18:22:19] [INFO] TESTER: Getting status of ventura-13[0m - PASSED - modules/testing/tests/test_agent/test_registration.py::test_service PASSED - modules/testing/tests/test_agent/test_registration.py::test_connection PASSED - modules/testing/tests/test_agent/test_registration.py::test_clientKeys PASSED -  - =============================== warnings summary =============================== - modules/provision/models.py:36 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('dependencies', pre=True) -  - modules/provision/models.py:64 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('install', 'uninstall', pre=True) -  - -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html - =================== 4 passed, 2 warnings in 81.43s (0:01:21) =================== - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:22:58] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test registration for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_registration.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-20 18:22:58] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml -[2024-05-20 18:22:58] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:22:58] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test basic_info for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_basic_info.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Test basic_info for agent] *********************************************** -changed: [localhost] => changed=true  - cmd: - - python3 - - -m - - pytest - - modules/testing/tests/test_agent/test_basic_info.py - - -v - - --wazuh_version=4.7.4 - - --wazuh_revision=40717 - - --component=agent - - --dependencies={} - - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' - - --live=True - - -s - delta: '0:00:56.721239' - end: '2024-05-20 18:23:57.957751' - msg: '' - rc: 0 - start: '2024-05-20 18:23:01.236512' - stderr: |- - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - stderr_lines:  - stdout: |- - [37m[2024-05-20 18:23:01] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m - [37m[2024-05-20 18:23:01] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m - ============================= test session starts ============================== - platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 - cachedir: .pytest_cache - rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules - collecting ... collected 3 items -  - modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_os_version [32m[2024-05-20 18:23:02] [INFO] TESTER: Checking connection to ventura-13[0m - [32m[2024-05-20 18:23:06] [INFO] TESTER: Connection established successfully in ventura-13[0m - [32m[2024-05-20 18:23:47] [INFO] TESTER: Version obtained from agent: 13.6.6[0m - [32m[2024-05-20 18:23:47] [INFO] TESTER: Version obtained from Wazuh API: 13.6.6[0m - PASSED - modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_version PASSED - modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_revision PASSED -  - =============================== warnings summary =============================== - modules/provision/models.py:36 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('dependencies', pre=True) -  - modules/provision/models.py:64 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('install', 'uninstall', pre=True) -  - -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html - ======================== 3 passed, 2 warnings in 55.84s ======================== - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:23:58] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test basic_info for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_basic_info.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-amd64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-20 18:23:58] [INFO] TESTER: Cleaning up -[2024-05-20 18:23:58] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:23:58] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml -Using /etc/ansible/ansible.cfg as config file - -PLAY [all] ********************************************************************* - -TASK [Gathering Facts] ********************************************************* -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] - -TASK [Clean test directory] **************************************************** -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  - path: /tmp/tests - state: absent - -PLAY RECAP ********************************************************************* -ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:24:11] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} -[2024-05-20 18:24:11] [INFO] TESTER: Cleaning up -[2024-05-20 18:24:11] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:24:11] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml -Using /etc/ansible/ansible.cfg as config file - -PLAY [all] ********************************************************************* - -TASK [Gathering Facts] ********************************************************* -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] - -TASK [Clean test directory] **************************************************** -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  - path: /tmp/tests - state: absent - -PLAY RECAP ********************************************************************* -ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:24:21] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} - -[2024-05-20 18:24:21] [INFO] [486264] [ThreadPoolExecutor-0_3] [workflow_engine]: [run-agent-macos-ventura-13-amd64-tests] Finished task in 228.86 seconds. -[2024-05-20 18:24:23] [DEBUG] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: Finished task "run-agent-macos-ventura-13-arm64-tests" execution with result: -[2024-05-20 18:20:33] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-20 18:20:33] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-20 18:20:33] [INFO] TESTER: Running tests for ec2-54-144-70-59.compute-1.amazonaws.com -[2024-05-20 18:20:33] [INFO] TESTER: Running tests for 10.10.0.250 -[2024-05-20 18:20:33] [DEBUG] TESTER: Using extra vars: {'component': 'agent', 'wazuh_version': '4.7.4', 'wazuh_revision': '40717', 'wazuh_branch': None, 'working_dir': '/tmp/tests', 'live': True, 'hosts_ip': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.250'], 'targets': '{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}', 'dependencies': '{}', 'local_host_path': '/home/fcaffieri/repos/wazuh-qa/deployability', 'current_user': 'fcaffieri'} -[2024-05-20 18:20:33] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/setup.yml -[2024-05-20 18:20:33] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:20:33] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.250']}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Cleaning old key ssh-keygen registries] ********************************** -changed: [localhost] => (item=ec2-54-144-70-59.compute-1.amazonaws.com) => changed=true  - ansible_loop_var: item - cmd: - - ssh-keygen - - -f - - /home/fcaffieri/.ssh/known_hosts - - -R - - '' - delta: '0:00:00.005471' - end: '2024-05-20 18:20:36.139850' - item: ec2-54-144-70-59.compute-1.amazonaws.com - msg: '' - rc: 0 - start: '2024-05-20 18:20:36.134379' - stderr: Host not found in /home/fcaffieri/.ssh/known_hosts - stderr_lines:  - stdout: '' - stdout_lines:  -changed: [localhost] => (item=10.10.0.250) => changed=true  - ansible_loop_var: item - cmd: - - ssh-keygen - - -f - - /home/fcaffieri/.ssh/known_hosts - - -R - - '' - delta: '0:00:00.005582' - end: '2024-05-20 18:20:36.363270' - item: 10.10.0.250 - msg: '' - rc: 0 - start: '2024-05-20 18:20:36.357688' - stderr: Host not found in /home/fcaffieri/.ssh/known_hosts - stderr_lines:  - stdout: '' - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:20:36] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Cleaning old key ssh-keygen registries', 'ansible.builtin.command': {'cmd': "ssh-keygen -f /home/fcaffieri/.ssh/known_hosts -R ''"}, 'loop': ['ec2-54-144-70-59.compute-1.amazonaws.com', '10.10.0.250']}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-20 18:20:36] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml -[2024-05-20 18:20:36] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:20:36] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Test install for agent] ************************************************** -changed: [localhost] => changed=true  - cmd: - - python3 - - -m - - pytest - - modules/testing/tests/test_agent/test_install.py - - -v - - --wazuh_version=4.7.4 - - --wazuh_revision=40717 - - --component=agent - - --dependencies={} - - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' - - --live=True - - -s - delta: '0:00:51.893566' - end: '2024-05-20 18:21:31.286397' - msg: '' - rc: 0 - start: '2024-05-20 18:20:39.392831' - stderr: |- - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - stderr_lines:  - stdout: |- - [37m[2024-05-20 18:20:40] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m - [37m[2024-05-20 18:20:40] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m - ============================= test session starts ============================== - platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 - cachedir: .pytest_cache - rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules - collecting ... collected 2 items -  - modules/testing/tests/test_agent/test_install.py::test_installation [32m[2024-05-20 18:20:40] [INFO] TESTER: Checking connection to ventura-13[0m - [32m[2024-05-20 18:20:44] [INFO] TESTER: Connection established successfully in ventura-13[0m - [32m[2024-05-20 18:20:49] [INFO] TESTER: Checking connection to ubuntu-22.04[0m - [32m[2024-05-20 18:20:50] [INFO] TESTER: Connection established successfully in ubuntu-22.04[0m - [32m[2024-05-20 18:20:50] [INFO] TESTER: Firewall disabled on ventura-13[0m - [32m[2024-05-20 18:20:58] [INFO] TESTER: Manager is already installed in ubuntu-22.04[0m - [32m[2024-05-20 18:21:01] [INFO] TESTER: Installing Agent in ventura-13[0m - PASSED - modules/testing/tests/test_agent/test_install.py::test_status [32m[2024-05-20 18:21:26] [INFO] TESTER: Getting status of ventura-13[0m - PASSED -  - =============================== warnings summary =============================== - modules/provision/models.py:36 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('dependencies', pre=True) -  - modules/provision/models.py:64 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('install', 'uninstall', pre=True) -  - -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html - ======================== 2 passed, 2 warnings in 50.92s ======================== - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:21:31] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test install for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_install.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-20 18:21:31] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml -[2024-05-20 18:21:31] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:21:31] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test registration for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_registration.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Test registration for agent] ********************************************* -changed: [localhost] => changed=true  - cmd: - - python3 - - -m - - pytest - - modules/testing/tests/test_agent/test_registration.py - - -v - - --wazuh_version=4.7.4 - - --wazuh_revision=40717 - - --component=agent - - --dependencies={} - - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' - - --live=True - - -s - delta: '0:01:21.782904' - end: '2024-05-20 18:22:55.412542' - msg: '' - rc: 0 - start: '2024-05-20 18:21:33.629638' - stderr: |- - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - stderr_lines:  - stdout: |- - [37m[2024-05-20 18:21:34] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m - [37m[2024-05-20 18:21:34] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m - ============================= test session starts ============================== - platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 - cachedir: .pytest_cache - rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules - collecting ... collected 4 items -  - modules/testing/tests/test_agent/test_registration.py::test_status [32m[2024-05-20 18:21:34] [INFO] TESTER: Checking connection to ventura-13[0m - [32m[2024-05-20 18:21:38] [INFO] TESTER: Connection established successfully in ventura-13[0m - [32m[2024-05-20 18:21:53] [INFO] TESTER: Registering agent in ventura-13[0m - [32m[2024-05-20 18:22:17] [INFO] TESTER: Getting status of ventura-13[0m - PASSED - modules/testing/tests/test_agent/test_registration.py::test_service PASSED - modules/testing/tests/test_agent/test_registration.py::test_connection PASSED - modules/testing/tests/test_agent/test_registration.py::test_clientKeys PASSED -  - =============================== warnings summary =============================== - modules/provision/models.py:36 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('dependencies', pre=True) -  - modules/provision/models.py:64 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('install', 'uninstall', pre=True) -  - -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html - =================== 4 passed, 2 warnings in 80.90s (0:01:20) =================== - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:22:55] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test registration for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_registration.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-20 18:22:55] [DEBUG] TESTER: Rendering template /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/test.yml -[2024-05-20 18:22:55] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:22:55] [DEBUG] TESTER: Running playbook: [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test basic_info for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_basic_info.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] -Using /etc/ansible/ansible.cfg as config file - -PLAY [localhost] *************************************************************** - -TASK [Gathering Facts] ********************************************************* -ok: [localhost] - -TASK [Test basic_info for agent] *********************************************** -changed: [localhost] => changed=true  - cmd: - - python3 - - -m - - pytest - - modules/testing/tests/test_agent/test_basic_info.py - - -v - - --wazuh_version=4.7.4 - - --wazuh_revision=40717 - - --component=agent - - --dependencies={} - - '--targets={wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' - - --live=True - - -s - delta: '0:01:04.969172' - end: '2024-05-20 18:24:02.641926' - msg: '' - rc: 0 - start: '2024-05-20 18:22:57.672754' - stderr: |- - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - Warning: Permanently added '[ec2-54-144-70-59.compute-1.amazonaws.com]:2200,[54.144.70.59]:2200' (ECDSA) to the list of known hosts. - stderr_lines:  - stdout: |- - [37m[2024-05-20 18:22:58] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi'[0m - [37m[2024-05-20 18:22:58] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi'[0m - ============================= test session starts ============================== - platform linux -- Python 3.10.14, pytest-7.4.4, pluggy-1.4.0 -- /home/fcaffieri/repos/wazuh-qa/.venv/bin/python3 - cachedir: .pytest_cache - rootdir: /home/fcaffieri/repos/wazuh-qa/deployability/modules - collecting ... collected 3 items -  - modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_os_version [32m[2024-05-20 18:22:58] [INFO] TESTER: Checking connection to ventura-13[0m - [32m[2024-05-20 18:23:12] [INFO] TESTER: Connection established successfully in ventura-13[0m - [32m[2024-05-20 18:23:52] [INFO] TESTER: Version obtained from agent: 13.4.1[0m - [32m[2024-05-20 18:23:52] [INFO] TESTER: Version obtained from Wazuh API: 13.4.1[0m - PASSED - modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_version PASSED - modules/testing/tests/test_agent/test_basic_info.py::test_wazuh_revision PASSED -  - =============================== warnings summary =============================== - modules/provision/models.py:36 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:36: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('dependencies', pre=True) -  - modules/provision/models.py:64 - /home/fcaffieri/repos/wazuh-qa/deployability/modules/provision/models.py:64: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/ - @validator('install', 'uninstall', pre=True) -  - -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html - =================== 3 passed, 2 warnings in 64.17s (0:01:04) =================== - stdout_lines:  - -PLAY RECAP ********************************************************************* -localhost : ok=2  changed=1  unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:24:02] [DEBUG] TESTER: Playbook [{'hosts': 'localhost', 'become': True, 'become_user': 'fcaffieri', 'tasks': [{'name': 'Test basic_info for agent', 'command': "python3 -m pytest modules/testing/tests/test_agent/test_basic_info.py -v --wazuh_version=4.7.4 --wazuh_revision=40717 --component=agent --dependencies='{}' --targets='{wazuh-1: /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/inventory.yaml, agent: /tmp/dtt1-poc/agent-macos-ventura-13-arm64/inventory.yaml}' --live=True -s", 'args': {'chdir': '/home/fcaffieri/repos/wazuh-qa/deployability'}}]}] finished with status {'skipped': {}, 'ok': {'localhost': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'localhost': 1}, 'changed': {'localhost': 1}} -[2024-05-20 18:24:02] [INFO] TESTER: Cleaning up -[2024-05-20 18:24:02] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:24:02] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml -Using /etc/ansible/ansible.cfg as config file - -PLAY [all] ********************************************************************* - -TASK [Gathering Facts] ********************************************************* -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] - -TASK [Clean test directory] **************************************************** -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  - path: /tmp/tests - state: absent - -PLAY RECAP ********************************************************************* -ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:24:13] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} -[2024-05-20 18:24:13] [INFO] TESTER: Cleaning up -[2024-05-20 18:24:13] [DEBUG] TESTER: Using inventory: {'all': {'hosts': {'ec2-54-144-70-59.compute-1.amazonaws.com': {'ansible_port': 2200, 'ansible_user': 'ubuntu', 'ansible_ssh_private_key_file': '/tmp/wazuh-qa/i-0e4256e5c7bc34023/qa-5191-ubuntu-22.04-key-3891'}}}} -[2024-05-20 18:24:13] [DEBUG] TESTER: Running playbook: /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml -Using /etc/ansible/ansible.cfg as config file - -PLAY [all] ********************************************************************* - -TASK [Gathering Facts] ********************************************************* -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] - -TASK [Clean test directory] **************************************************** -ok: [ec2-54-144-70-59.compute-1.amazonaws.com] => changed=false  - path: /tmp/tests - state: absent - -PLAY RECAP ********************************************************************* -ec2-54-144-70-59.compute-1.amazonaws.com : ok=2  changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -[2024-05-20 18:24:23] [DEBUG] TESTER: Playbook /home/fcaffieri/repos/wazuh-qa/deployability/modules/testing/playbooks/cleanup.yml finished with status {'skipped': {}, 'ok': {'ec2-54-144-70-59.compute-1.amazonaws.com': 2}, 'dark': {}, 'failures': {}, 'ignored': {}, 'rescued': {}, 'processed': {'ec2-54-144-70-59.compute-1.amazonaws.com': 1}, 'changed': {}} - -[2024-05-20 18:24:23] [INFO] [486264] [ThreadPoolExecutor-0_1] [workflow_engine]: [run-agent-macos-ventura-13-arm64-tests] Finished task in 230.94 seconds. -[2024-05-20 18:24:23] [INFO] [486264] [MainThread] [workflow_engine]: Executing Reverse DAG tasks. -[2024-05-20 18:24:23] [INFO] [486264] [MainThread] [workflow_engine]: Executing tasks in parallel. -[2024-05-20 18:24:23] [INFO] [486264] [ThreadPoolExecutor-1_0] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-amd64] Starting task. -[2024-05-20 18:24:23] [DEBUG] [486264] [ThreadPoolExecutor-1_0] [workflow_engine]: Running task "allocate-macos-agent-macos-ventura-13-amd64" with arguments: ['modules/allocation/main.py', '--action=delete', '--track-output=/tmp/dtt1-poc/agent-macos-ventura-13-amd64/track.yaml'] -[2024-05-20 18:24:23] [INFO] [486264] [ThreadPoolExecutor-1_1] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-arm64] Starting task. -[2024-05-20 18:24:23] [INFO] [486264] [ThreadPoolExecutor-1_2] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-amd64] Starting task. -[2024-05-20 18:24:23] [DEBUG] [486264] [ThreadPoolExecutor-1_1] [workflow_engine]: Running task "allocate-macos-agent-macos-ventura-13-arm64" with arguments: ['modules/allocation/main.py', '--action=delete', '--track-output=/tmp/dtt1-poc/agent-macos-ventura-13-arm64/track.yaml'] -[2024-05-20 18:24:23] [INFO] [486264] [ThreadPoolExecutor-1_3] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-arm64] Starting task. -[2024-05-20 18:24:23] [INFO] [486264] [ThreadPoolExecutor-1_4] [workflow_engine]: [allocate-manager-linux-ubuntu-22.04-amd64] Starting task. -[2024-05-20 18:24:23] [DEBUG] [486264] [ThreadPoolExecutor-1_2] [workflow_engine]: Running task "allocate-macos-agent-macos-sonoma-14-amd64" with arguments: ['modules/allocation/main.py', '--action=delete', '--track-output=/tmp/dtt1-poc/agent-macos-sonoma-14-amd64/track.yaml'] -[2024-05-20 18:24:23] [DEBUG] [486264] [ThreadPoolExecutor-1_3] [workflow_engine]: Running task "allocate-macos-agent-macos-sonoma-14-arm64" with arguments: ['modules/allocation/main.py', '--action=delete', '--track-output=/tmp/dtt1-poc/agent-macos-sonoma-14-arm64/track.yaml'] -[2024-05-20 18:24:23] [DEBUG] [486264] [ThreadPoolExecutor-1_4] [workflow_engine]: Running task "allocate-manager-linux-ubuntu-22.04-amd64" with arguments: ['modules/allocation/main.py', '--action=delete', '--track-output=/tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/track.yaml'] -[2024-05-20 18:24:24] [ERROR] [486264] [ThreadPoolExecutor-1_3] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-arm64] Task failed with error: Error executing process task Traceback (most recent call last): - File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/main.py", line 39, in - main() - File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/main.py", line 35, in main - Allocator.run(InputPayload(**vars(parse_arguments()))) - File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/allocation.py", line 43, in run - return cls.__delete(payload) - File "/home/fcaffieri/repos/wazuh-qa/deployability/modules/allocation/allocation.py", line 93, in __delete - with open(payload.track_output, 'r') as f: -FileNotFoundError: [Errno 2] No such file or directory: '/tmp/dtt1-poc/agent-macos-sonoma-14-arm64/track.yaml' -. -[2024-05-20 18:24:59] [DEBUG] [486264] [ThreadPoolExecutor-1_1] [workflow_engine]: Finished task "allocate-macos-agent-macos-ventura-13-arm64" execution with result: -[2024-05-20 18:24:24] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-20 18:24:24] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-20 18:24:24] [INFO] ALLOCATOR: Deleting instance from trackfile /tmp/dtt1-poc/agent-macos-ventura-13-arm64/track.yaml -[2024-05-20 18:24:25] [DEBUG] ALLOCATOR: Destroying instance qa-5191-ventura-13-2119 -[2024-05-20 18:24:42] [DEBUG] ALLOCATOR: Deleting remote directory /Users/jenkins/testing/qa-5191-ventura-13-2119 -[2024-05-20 18:24:48] [DEBUG] ALLOCATOR: Killing remote process on port 43222 -[2024-05-20 18:24:59] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-2119 deleted. - -[2024-05-20 18:24:59] [INFO] [486264] [ThreadPoolExecutor-1_1] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-arm64] Finished task in 36.48 seconds. -[2024-05-20 18:25:06] [DEBUG] [486264] [ThreadPoolExecutor-1_0] [workflow_engine]: Finished task "allocate-macos-agent-macos-ventura-13-amd64" execution with result: -[2024-05-20 18:24:23] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-20 18:24:23] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-20 18:24:23] [INFO] ALLOCATOR: Deleting instance from trackfile /tmp/dtt1-poc/agent-macos-ventura-13-amd64/track.yaml -[2024-05-20 18:24:25] [DEBUG] ALLOCATOR: Destroying instance qa-5191-ventura-13-3162 -[2024-05-20 18:24:48] [DEBUG] ALLOCATOR: Deleting remote directory /Users/jenkins/testing/qa-5191-ventura-13-3162 -[2024-05-20 18:24:54] [DEBUG] ALLOCATOR: Killing remote process on port 43230 -[2024-05-20 18:25:06] [INFO] ALLOCATOR: Instance qa-5191-ventura-13-3162 deleted. - -[2024-05-20 18:25:06] [INFO] [486264] [ThreadPoolExecutor-1_0] [workflow_engine]: [allocate-macos-agent-macos-ventura-13-amd64] Finished task in 42.92 seconds. -[2024-05-20 18:25:06] [DEBUG] [486264] [ThreadPoolExecutor-1_2] [workflow_engine]: Finished task "allocate-macos-agent-macos-sonoma-14-amd64" execution with result: -[2024-05-20 18:24:23] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-20 18:24:23] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-20 18:24:24] [INFO] ALLOCATOR: Deleting instance from trackfile /tmp/dtt1-poc/agent-macos-sonoma-14-amd64/track.yaml -[2024-05-20 18:24:25] [DEBUG] ALLOCATOR: Destroying instance qa-5191-sonoma-14-9588 -[2024-05-20 18:24:48] [DEBUG] ALLOCATOR: Deleting remote directory /Users/jenkins/testing/qa-5191-sonoma-14-9588 -[2024-05-20 18:24:54] [DEBUG] ALLOCATOR: Killing remote process on port 43239 -[2024-05-20 18:25:06] [INFO] ALLOCATOR: Instance qa-5191-sonoma-14-9588 deleted. - -[2024-05-20 18:25:06] [INFO] [486264] [ThreadPoolExecutor-1_2] [workflow_engine]: [allocate-macos-agent-macos-sonoma-14-amd64] Finished task in 42.95 seconds. -[2024-05-20 18:25:28] [DEBUG] [486264] [ThreadPoolExecutor-1_4] [workflow_engine]: Finished task "allocate-manager-linux-ubuntu-22.04-amd64" execution with result: -[2024-05-20 18:24:24] [DEBUG] SPNEGO._GSS: Python gssapi not available, cannot use any GSSAPIProxy protocols: No module named 'gssapi' -[2024-05-20 18:24:24] [DEBUG] SPNEGO._GSS: Python gssapi IOV extension not available: No module named 'gssapi' -[2024-05-20 18:24:24] [INFO] ALLOCATOR: Deleting instance from trackfile /tmp/dtt1-poc/manager-linux-ubuntu-22.04-amd64/track.yaml -[2024-05-20 18:24:25] [DEBUG] ALLOCATOR: Deleting credentials: qa-5191-ubuntu-22.04-key-3891 -[2024-05-20 18:25:27] [INFO] ALLOCATOR: Instance i-0e4256e5c7bc34023 deleted. - -[2024-05-20 18:25:28] [INFO] [486264] [ThreadPoolExecutor-1_4] [workflow_engine]: [allocate-manager-linux-ubuntu-22.04-amd64] Finished task in 64.69 seconds. From 22f7dc2d803f9b2fbbc28bc9be9d753748b87170 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Mon, 20 May 2024 18:33:32 -0300 Subject: [PATCH 167/195] Fix vagrant example --- .../agent/{aws => vagrant}/test-agent-basic-info-vagrant.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename deployability/modules/workflow_engine/examples/agent/{aws => vagrant}/test-agent-basic-info-vagrant.yaml (100%) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info-vagrant.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info-vagrant.yaml rename to deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml From 9e922d98bfb106b811f91a29940f703366345787 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Tue, 21 May 2024 13:15:17 -0300 Subject: [PATCH 168/195] Fix all examples --- .../agent/aws/test-agent-basic-info.yaml | 4 +-- .../examples/agent/aws/test-agent-time.yaml | 2 +- .../agent/aws/test-agent-windows-install.yaml | 30 ------------------- .../test-agent-basic-info-vagrant.yaml | 2 +- .../test-agent-restart-ins-prov-1.yaml | 2 +- .../test-agent-restart-ins-prov-2.yaml | 2 +- .../vagrant/test-agent-stop-ins-prov-1.yaml | 2 +- .../vagrant/test-agent-stop-ins-prov-2.yaml | 2 +- .../test-agent-uninstall-ins-prov-1.yaml | 2 +- .../test-agent-uninstall-ins-prov-2.yaml | 2 +- .../manager/aws/dtt1-managers-poc-aws.yaml | 8 +++++ .../vagrant/dtt1-managers-poc-vagrant.yaml | 8 +++++ 12 files changed, 26 insertions(+), 40 deletions(-) delete mode 100755 deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml index 0eef8e3863..0ad7ae8b69 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml @@ -174,7 +174,7 @@ tasks: - wazuh-revision: "40717" - live: "True" foreach: - - variable: macos-agent-os + - variable: agent-os as: agent depends-on: - "allocate-agent-{agent}" @@ -202,4 +202,4 @@ tasks: as: agent depends-on: - "allocate-macos-agent-{agent}" - - "provision-manager-{manager-os}" \ No newline at end of file + - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-time.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-time.yaml index 62497a9414..edb90bd885 100644 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-time.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-time.yaml @@ -200,4 +200,4 @@ tasks: as: agent depends-on: - "allocate-macos-agent-{agent}" - - "provision-manager-{manager-os}" \ No newline at end of file + - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml deleted file mode 100755 index f83d99c72b..0000000000 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml +++ /dev/null @@ -1,30 +0,0 @@ -version: 0.1 - -description: This workflow is used to test agents deployment for DDT1 PoC - -variables: - agent-os: - - windows-server-2016-amd64 - manager-os: linux-ubuntu-22.04-amd64 - infra-provider: vagrant - working-dir: /tmp/dtt1-poc - -tasks: - # Generic agent test task - - task: "run-agent-windows-server-2016-amd64-tests" - description: "Run tests install for the agent windows-server-2016-amd64." - do: - this: process - with: - path: python3 - args: - - modules/testing/main.py - - targets: - - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" - - agent: "{working-dir}/agent-windows-server-2016-amd64/inventory.yaml" - - tests: "install" - - component: "agent" - - wazuh-version: "4.7.4" - - wazuh-revision: "40717" - - live: False - diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml index e0259b9d73..6b94fadd93 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml @@ -200,4 +200,4 @@ tasks: as: agent depends-on: - "allocate-macos-agent-{agent}" - - "provision-manager-{manager-os}" \ No newline at end of file + - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml index b809d79e31..2c0215cfda 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml @@ -131,4 +131,4 @@ tasks: - variable: agent-os as: agent depends-on: - - "provision-install-{agent}" \ No newline at end of file + - "provision-install-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml index a875e87dec..9ef8f73540 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml @@ -131,4 +131,4 @@ tasks: - variable: agent-os as: agent depends-on: - - "provision-install-{agent}" \ No newline at end of file + - "provision-install-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml index bf5195a845..7c776fb2aa 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml @@ -131,4 +131,4 @@ tasks: - variable: agent-os as: agent depends-on: - - "provision-install-{agent}" \ No newline at end of file + - "provision-install-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml index 8a0b9de3fc..7bf65cc472 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml @@ -131,4 +131,4 @@ tasks: - variable: agent-os as: agent depends-on: - - "provision-install-{agent}" \ No newline at end of file + - "provision-install-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml index f64635fb81..3495ff25c1 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml @@ -131,4 +131,4 @@ tasks: - variable: agent-os as: agent depends-on: - - "provision-install-{agent}" \ No newline at end of file + - "provision-install-{agent}" diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml index 9207f4f50c..0d875b00f4 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml +++ b/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml @@ -131,4 +131,4 @@ tasks: - variable: agent-os as: agent depends-on: - - "provision-install-{agent}" \ No newline at end of file + - "provision-install-{agent}" diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml index a634e5e226..d7c11a4485 100644 --- a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml @@ -38,6 +38,14 @@ tasks: foreach: - variable: manager-os as: manager + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager}/track.yaml" # Generic manager test task - task: "run-manager-tests" diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index 83f68f4c07..d324d86285 100644 --- a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -40,6 +40,14 @@ tasks: foreach: - variable: manager-os as: manager + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager}/track.yaml" # Generic manager test task - task: "run-manager-tests" From 4406a973732c2ab038d91e798ad95dc9a74584fc Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Tue, 21 May 2024 15:46:39 -0300 Subject: [PATCH 169/195] Fix MacOS description class --- deployability/modules/testing/tests/helpers/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index e190345c42..c0bf9b988a 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -704,7 +704,7 @@ def get_os_version(self) -> str: return os_version class MacOsAgent(WazuhAgent): - """Windows Agent Class.""" + """MacOS Agent Class.""" def get_os_version(self) -> str: """Get os version from the platform.""" From 0c293f6353b2864b1eb1bb43f8c3e58404ca6dd7 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Wed, 22 May 2024 15:43:59 -0300 Subject: [PATCH 170/195] Add modification to fix renndering templates and report failures into playbooks to Workflow --- .../modules/testing/playbooks/setup.yml | 5 +- deployability/modules/testing/testing.py | 18 +- .../modules/testing/tests/helpers/manager.py | 6 +- .../agent/aws/Jenkins-agents-test.yaml | 172 ++++++++++++++++++ .../agent/aws/test-agent-basic-info.yaml | 4 +- .../aws/Jenkins-central-component-test.yaml | 59 ++++++ .../manager/aws/Jenkins-manager-test.yaml | 58 ++++++ .../manager/aws/dtt1-managers-poc-aws.yaml | 33 +--- 8 files changed, 309 insertions(+), 46 deletions(-) create mode 100755 deployability/modules/workflow_engine/examples/agent/aws/Jenkins-agents-test.yaml create mode 100644 deployability/modules/workflow_engine/examples/central_components/aws/Jenkins-central-component-test.yaml create mode 100644 deployability/modules/workflow_engine/examples/manager/aws/Jenkins-manager-test.yaml diff --git a/deployability/modules/testing/playbooks/setup.yml b/deployability/modules/testing/playbooks/setup.yml index cd55ffb5f4..4ebc86c59e 100644 --- a/deployability/modules/testing/playbooks/setup.yml +++ b/deployability/modules/testing/playbooks/setup.yml @@ -7,6 +7,5 @@ become_user: "{{ current_user }}" tasks: - name: Cleaning old key ssh-keygen registries - ansible.builtin.command: - cmd: "ssh-keygen -f /home/{{ current_user }}/.ssh/known_hosts -R '{{ item }}'" - loop: {{ hosts_ip }} + command: "ssh-keygen -f ~/.ssh/known_hosts -R '{{ item }}'" + loop: "{{ hosts_ip }}" diff --git a/deployability/modules/testing/testing.py b/deployability/modules/testing/testing.py index c0f7ee985e..7c869df355 100644 --- a/deployability/modules/testing/testing.py +++ b/deployability/modules/testing/testing.py @@ -95,11 +95,11 @@ def _run_tests(cls, test_list: list[str], ansible: Ansible, extra_vars: ExtraVar for test in test_list: rendering_var = {**extra_vars, 'test': test} template = str(cls._test_template) - playbook = ansible.render_playbook(template, rendering_var) - if not playbook: - logger.warning(f"Test {test} not found. Skipped.") - continue - ansible.run_playbook(playbook, extra_vars) + result = ansible.run_playbook(template, rendering_var) + if result.stats["failures"]: + for event in result.events: + if "fatal" in event['stdout']: + raise Exception(f"Test {test} failed with error: {event['stdout']}") @classmethod @@ -111,10 +111,12 @@ def _setup(cls, ansible: Ansible, extra_vars: ExtraVars) -> None: ansible (Ansible): The Ansible object to run the setup. extra_vars (str): The extra vars for the setup. """ - rendering_var = {**extra_vars} template = str(cls._setup_playbook) - playbook = ansible.render_playbook(template, rendering_var) - ansible.run_playbook(playbook, extra_vars) + result = ansible.run_playbook(template, extra_vars) + if result.stats["failures"]: + for event in result.events: + if "fatal" in event['stdout']: + raise Exception(f"Setup {template} failed with error: {event['stdout']}") @classmethod diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index d4bfc26ab3..b3c752d11f 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -38,11 +38,13 @@ def install_manager(inventory_path, node_name, wazuh_version, live) -> None: if os_name == 'debian': commands = [ - f"wget https://{s3_url}/{release}/wazuh-install.sh && bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" + f"wget https://{s3_url}/{release}/wazuh-install.sh", + f"bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" ] else: commands = [ - f"curl -sO https://{s3_url}/{release}/wazuh-install.sh && bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" + f"curl -sO https://{s3_url}/{release}/wazuh-install.sh", + f"bash wazuh-install.sh --wazuh-server {node_name} --ignore-check" ] logger.info(f'Installing Manager in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}') ConnectionManager.execute_commands(inventory_path, commands) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/Jenkins-agents-test.yaml b/deployability/modules/workflow_engine/examples/agent/aws/Jenkins-agents-test.yaml new file mode 100755 index 0000000000..510b6a3df2 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/aws/Jenkins-agents-test.yaml @@ -0,0 +1,172 @@ +version: 0.1 +description: This workflow is used to test agents deployment for DDT1 PoC +variables: + agent-os: + - linux-ubuntu-20.04-amd64 + - windows-server-2022-amd64 + + macos-agent-os: + - macos-sonoma-14-amd64 + + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + macos-infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique macOS agent allocate task + - task: "allocate-macos-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{macos-infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: macos-agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.4 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info" + - component: "agent" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: True + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + + # Generic macOS agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: True + foreach: + - variable: macos-agent-os + as: agent + depends-on: + - "allocate-macos-agent-{agent}" + - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml index 0ad7ae8b69..924fe4622b 100755 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml @@ -172,7 +172,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: agent-os as: agent @@ -196,7 +196,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: "True" + - live: False foreach: - variable: macos-agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/central_components/aws/Jenkins-central-component-test.yaml b/deployability/modules/workflow_engine/examples/central_components/aws/Jenkins-central-component-test.yaml new file mode 100644 index 0000000000..46b74eb69a --- /dev/null +++ b/deployability/modules/workflow_engine/examples/central_components/aws/Jenkins-central-component-test.yaml @@ -0,0 +1,59 @@ +version: 0.1 +description: This workflow is used to test the Wazuh manager deployment for DDT1 PoC +variables: + central_components-os: + - linux-ubuntu-20.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique central components allocate task + - task: "allocate-central_components-{central_components}" + description: "Allocate resources for the central_components." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{central_components}" + - inventory-output: "{working-dir}/central_components-{central_components}/inventory.yaml" + - track-output: "{working-dir}/central_components-{central_components}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: central_components-os + as: central_components + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/central_components-{central_components-os}/track.yaml" + + # Generic manager test task + - task: "run-central_components-{central_components}-tests" + description: "Run tests install for the central_components." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/central_components-{central_components}/inventory.yaml" + - tests: "install,restart,stop,uninstall" + - component: "central_components" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: False + on-error: "abort-all" + foreach: + - variable: central_components-os + as: central_components diff --git a/deployability/modules/workflow_engine/examples/manager/aws/Jenkins-manager-test.yaml b/deployability/modules/workflow_engine/examples/manager/aws/Jenkins-manager-test.yaml new file mode 100644 index 0000000000..fd690a8a89 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/manager/aws/Jenkins-manager-test.yaml @@ -0,0 +1,58 @@ +version: 0.1 +description: This workflow is used to test manager deployment for DDT1 PoC +variables: + manager-os: + - linux-ubuntu-20.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager}" + - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: manager-os + as: manager + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager}/track.yaml" + + # Generic manager test task + - task: "run-manager-tests" + description: "Run tests install for the manager." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - tests: "install,restart,stop,uninstall" + - component: "manager" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: True + depends-on: + - "allocate-manager-linux-ubuntu-20.04-amd64" + diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml index d7c11a4485..0dfc9a037c 100644 --- a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml @@ -3,16 +3,6 @@ description: This workflow is used to test manager deployment for DDT1 PoC variables: manager-os: - linux-ubuntu-20.04-amd64 - - linux-ubuntu-22.04-amd64 - - linux-amazon-2-amd64 - - linux-redhat-7-amd64 - - linux-redhat-8-amd64 - - linux-redhat-9-amd64 - - linux-centos-7-amd64 - - linux-centos-8-amd64 - - linux-debian-10-amd64 - - linux-debian-11-amd64 - - linux-debian-12-amd64 infra-provider: aws working-dir: /tmp/dtt1-poc @@ -57,17 +47,7 @@ tasks: args: - modules/testing/main.py - targets: - - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" - - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - - wazuh-3: "{working-dir}/manager-linux-ubuntu-22.04-amd64/inventory.yaml" - - wazuh-4: "{working-dir}/manager-linux-amazon-2-amd64/inventory.yaml" - - wazuh-5: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" - - wazuh-6: "{working-dir}/manager-linux-redhat-8-amd64/inventory.yaml" - - wazuh-7: "{working-dir}/manager-linux-redhat-9-amd64/inventory.yaml" - - wazuh-8: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" - - wazuh-9: "{working-dir}/manager-linux-debian-10-amd64/inventory.yaml" - - wazuh-10: "{working-dir}/manager-linux-debian-11-amd64/inventory.yaml" - - wazuh-11: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" + - wazuh-1: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" - tests: "install,restart,stop,uninstall" - component: "manager" - wazuh-version: "4.7.4" @@ -75,13 +55,4 @@ tasks: - live: False depends-on: - "allocate-manager-linux-ubuntu-20.04-amd64" - - "allocate-manager-linux-ubuntu-22.04-amd64" - - "allocate-manager-linux-amazon-2-amd64" - - "allocate-manager-linux-redhat-7-amd64" - - "allocate-manager-linux-redhat-8-amd64" - - "allocate-manager-linux-redhat-9-amd64" - - "allocate-manager-linux-centos-7-amd64" - - "allocate-manager-linux-centos-8-amd64" - - "allocate-manager-linux-debian-10-amd64" - - "allocate-manager-linux-debian-11-amd64" - - "allocate-manager-linux-debian-12-amd64" + From f40b808a38d82f8ebbf6b50d0adacccb67b71555 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Wed, 22 May 2024 17:45:04 -0300 Subject: [PATCH 171/195] Fix templates --- .../agent/aws/test-full-agent-with-live.yaml | 241 ++++++++++++++++++ .../manager/aws/dtt1-managers-poc-aws.yaml | 32 ++- 2 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 deployability/modules/workflow_engine/examples/agent/aws/test-full-agent-with-live.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-full-agent-with-live.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-full-agent-with-live.yaml new file mode 100644 index 0000000000..619a06faec --- /dev/null +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-full-agent-with-live.yaml @@ -0,0 +1,241 @@ +version: 0.1 +description: This workflow is used to test agents deployment for DDT1 PoC +variables: + agent-os: + - linux-redhat-8-amd64 + - linux-redhat-8-arm64 + - linux-centos-8-amd64 + - linux-centos-8-arm64 + - linux-debian-12-amd64 + - linux-debian-12-arm64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-20.04-arm64 + - linux-oracle-9-amd64 + - linux-amazon-2023-amd64 + - linux-amazon-2023-arm64 + + windows-agent-os: + - windows-server-2019-amd64 + - windows-server-2022-amd64 + + macos-agent-os: + - macos-ventura-13-amd64 + - macos-ventura-13-arm64 + + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + macos-infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique macOS agent allocate task + - task: "allocate-macos-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{macos-infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: macos-agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique Windows agent allocate task + - task: "allocate-windows-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: windows-agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.7.4 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,connection,basic_info,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: False + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + + # Generic macOS agent test task + - task: "run-macos-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: False + foreach: + - variable: macos-agent-os + as: agent + depends-on: + - "allocate-macos-agent-{agent}" + - "provision-manager-{manager-os}" + + # Generic Windows agent test task + - task: "run-windows-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.7.4" + - wazuh-revision: "40717" + - live: False + foreach: + - variable: windows-agent-os + as: agent + depends-on: + - "allocate-windows-agent-{agent}" + - "provision-manager-{manager-os}" \ No newline at end of file diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml index 0dfc9a037c..318bbbb8b9 100644 --- a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml +++ b/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml @@ -3,6 +3,16 @@ description: This workflow is used to test manager deployment for DDT1 PoC variables: manager-os: - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-amazon-2-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 infra-provider: aws working-dir: /tmp/dtt1-poc @@ -47,7 +57,17 @@ tasks: args: - modules/testing/main.py - targets: - - wazuh-1: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" + - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - wazuh-3: "{working-dir}/manager-linux-ubuntu-22.04-amd64/inventory.yaml" + - wazuh-4: "{working-dir}/manager-linux-amazon-2-amd64/inventory.yaml" + - wazuh-5: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" + - wazuh-6: "{working-dir}/manager-linux-redhat-8-amd64/inventory.yaml" + - wazuh-7: "{working-dir}/manager-linux-redhat-9-amd64/inventory.yaml" + - wazuh-8: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" + - wazuh-9: "{working-dir}/manager-linux-debian-10-amd64/inventory.yaml" + - wazuh-10: "{working-dir}/manager-linux-debian-11-amd64/inventory.yaml" + - wazuh-11: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" - tests: "install,restart,stop,uninstall" - component: "manager" - wazuh-version: "4.7.4" @@ -55,4 +75,14 @@ tasks: - live: False depends-on: - "allocate-manager-linux-ubuntu-20.04-amd64" + - "allocate-manager-linux-ubuntu-22.04-amd64" + - "allocate-manager-linux-amazon-2-amd64" + - "allocate-manager-linux-redhat-7-amd64" + - "allocate-manager-linux-redhat-8-amd64" + - "allocate-manager-linux-redhat-9-amd64" + - "allocate-manager-linux-centos-7-amd64" + - "allocate-manager-linux-centos-8-amd64" + - "allocate-manager-linux-debian-10-amd64" + - "allocate-manager-linux-debian-11-amd64" + - "allocate-manager-linux-debian-12-amd64" From a0e8589b109ec493cd57286a5b33c22eaeede3d1 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Thu, 23 May 2024 09:26:30 -0300 Subject: [PATCH 172/195] Fix templates --- .../examples/agent/aws/test-full-agent-with-live.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-full-agent-with-live.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-full-agent-with-live.yaml index 619a06faec..1c2e333840 100644 --- a/deployability/modules/workflow_engine/examples/agent/aws/test-full-agent-with-live.yaml +++ b/deployability/modules/workflow_engine/examples/agent/aws/test-full-agent-with-live.yaml @@ -238,4 +238,4 @@ tasks: as: agent depends-on: - "allocate-windows-agent-{agent}" - - "provision-manager-{manager-os}" \ No newline at end of file + - "provision-manager-{manager-os}" From 61c31c6f3ee0473bcffb3192df6bface092e39c4 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Thu, 23 May 2024 11:36:29 -0300 Subject: [PATCH 173/195] Updated Windows Sign AMI --- deployability/modules/allocation/static/specs/os.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 613176220d..bd7830cd71 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -421,7 +421,7 @@ aws: zone: us-east-1 user: wazuh-user windows-sign-10-amd64: - ami: ami-05fb40a2b48162ac3 + ami: ami-053a31c7736e82d06 zone: us-east-1 user: wazuh-user windows-server-2012r2-amd64: From df8daaebd59a8d5c37ec3d2700c03608242a9779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Correa=20Rodr=C3=ADguez?= Date: Thu, 23 May 2024 18:40:37 +0200 Subject: [PATCH 174/195] Added Ubuntu 24.04 AMD64 and ARM64 to the Allocator --- deployability/modules/allocation/aws/helpers/userData.sh | 8 +++++++- deployability/modules/allocation/static/specs/os.yml | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/deployability/modules/allocation/aws/helpers/userData.sh b/deployability/modules/allocation/aws/helpers/userData.sh index 07f556e6bd..046c58026b 100644 --- a/deployability/modules/allocation/aws/helpers/userData.sh +++ b/deployability/modules/allocation/aws/helpers/userData.sh @@ -162,7 +162,13 @@ fi if [ "$DIST_NAME" = "ubuntu" ] || [ "$DIST_NAME" = "debian" ]; then perl -pi -e "s/^#?Port 22$/Port ${SSH_PORT}/" /etc/ssh/sshd_config - service sshd restart || service ssh restart + if [ "$DIST_VER" = "24" ]; then + perl -pi -e "s/^#?ListenStream=22$/ListenStream=${SSH_PORT}/" /etc/systemd/system/sockets.target.wants/ssh.socket + systemctl daemon-reload + systemctl restart sshd || systemctl restart ssh + else + service sshd restart || service ssh restart + fi fi if [ "$DIST_NAME" = "darwin" ]; then diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 613176220d..a0ef03f355 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -209,6 +209,14 @@ vagrant: aws: # Ubuntu + linux-ubuntu-24.04-amd64: + ami: ami-04b70fa74e45c3917 + zone: us-east-1 + user: ubuntu + linux-ubuntu-24.04-arm64: + ami: ami-0eac975a54dfee8cb + zone: us-east-1 + user: ubuntu linux-ubuntu-22.04-amd64: ami: ami-053b0d53c279acc90 zone: us-east-1 From fa24f0a37145613f9c4d06f2c846e643ff4120ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Correa=20Rodr=C3=ADguez?= Date: Tue, 28 May 2024 16:19:58 +0200 Subject: [PATCH 175/195] Updated Rocky Linux 9 OSs in the Allocator module --- deployability/modules/allocation/static/specs/os.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 40b58cab33..89ce2d61b0 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -125,8 +125,8 @@ vagrant: box_version: 4.3.12 virtualizer: virtualbox linux-rocky-9-amd64: - box: generic/rocky9 - box_version: 4.3.12 + box: rockylinux/9 + box_version: 4.0.0 virtualizer: virtualbox # Macos macos-highsierra-10.13-amd64: @@ -391,11 +391,11 @@ aws: zone: us-east-1 user: rocky linux-rocky-9-amd64: - ami: ami-09c77dc92e45bc3ea + ami: ami-09eb79aa215c2a900 zone: us-east-1 user: rocky linux-rocky-9-arm64: - ami: ami-001f90eb122403b4c + ami: ami-0151988e287370acc zone: us-east-1 user: rocky # Macos From 67e6a0c8dba8031ce44138ecf4c8b0ba2e42a9d5 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Mon, 3 Jun 2024 12:27:17 -0300 Subject: [PATCH 176/195] Changed Windows 10 AMI --- deployability/modules/allocation/static/specs/os.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 89ce2d61b0..9d1acbff17 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -425,7 +425,7 @@ aws: user: ec2-user # Windows windows-desktop-10-amd64: - ami: ami-0a747df120215911a + ami: ami-03eb1fb8f55d01fde zone: us-east-1 user: wazuh-user windows-sign-10-amd64: From e23d6bf4b83e92afe2704b66b446dab203d2d79e Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 4 Jun 2024 15:09:08 -0300 Subject: [PATCH 177/195] Added support for AlmaLinux --- .../modules/allocation/aws/helpers/userData.sh | 8 +++++--- .../modules/allocation/static/specs/os.yml | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/deployability/modules/allocation/aws/helpers/userData.sh b/deployability/modules/allocation/aws/helpers/userData.sh index 046c58026b..cbb637486e 100644 --- a/deployability/modules/allocation/aws/helpers/userData.sh +++ b/deployability/modules/allocation/aws/helpers/userData.sh @@ -128,12 +128,14 @@ if [ ! -r "/etc/os-release" ] || [ "$DIST_NAME" = "centos" ]; then fi fi -if [ "$DIST_NAME" = "amzn" ] || [ "$DIST_NAME" = "sles" ] || [ "$DIST_NAME" = "opensuse-leap" ]; then +if [ "$DIST_NAME" = "amzn" ] || [ "$DIST_NAME" = "sles" ] \ + || [ "$DIST_NAME" = "opensuse-leap" ]; then sudo sed -i "s/#Port\s22/Port ${SSH_PORT}/" /etc/ssh/sshd_config sudo systemctl restart sshd.service fi -if [ "$DIST_NAME" = "rhel" ] || [ "$DIST_NAME" = "centos" ] || [ "$DIST_NAME" = "rocky" ] || [ "$DIST_NAME" = "fedora" ]; then +if [ "$DIST_NAME" = "rhel" ] || [ "$DIST_NAME" = "centos" ] || [ "$DIST_NAME" = "rocky" ] \ + || [ "$DIST_NAME" = "fedora" ] || [ "$DIST_NAME" = "almalinux" ]; then sudo sed -i "s/#Port\s22/Port ${SSH_PORT}/" /etc/ssh/sshd_config if [ "$DIST_NAME" = "centos" ] && [ "$DIST_VER" != "7" ]; then sudo yum -y install policycoreutils-python-utils @@ -165,7 +167,7 @@ if [ "$DIST_NAME" = "ubuntu" ] || [ "$DIST_NAME" = "debian" ]; then if [ "$DIST_VER" = "24" ]; then perl -pi -e "s/^#?ListenStream=22$/ListenStream=${SSH_PORT}/" /etc/systemd/system/sockets.target.wants/ssh.socket systemctl daemon-reload - systemctl restart sshd || systemctl restart ssh + systemctl restart sshd || systemctl restart ssh else service sshd restart || service ssh restart fi diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 9d1acbff17..54eef7d910 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -128,6 +128,11 @@ vagrant: box: rockylinux/9 box_version: 4.0.0 virtualizer: virtualbox + # Alma Linux + linux-alma-8-amd64: + box: almalinux/8 + box_version: 8.10.20240604 + virtualizer: virtualbox # Macos macos-highsierra-10.13-amd64: box: development/macos-high-sierra @@ -398,6 +403,15 @@ aws: ami: ami-0151988e287370acc zone: us-east-1 user: rocky + # Alma Linux + linux-alma-8-amd64: + ami: ami-059c62b4890c5498a + zone: us-east-1 + user: ec2-user + linux-alma-8-arm64: + ami: ami-01e3e647c7acbc64b + zone: us-east-1 + user: ec2-user # Macos macos-ventura-13-amd64: ami: ami-0e94a57427e4e0611 From 1c6a2d8a4143c4549a4b127d3c47dcca62f8d072 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Thu, 6 Jun 2024 11:19:26 -0300 Subject: [PATCH 178/195] Fixed Device name for AWS deployment --- deployability/modules/allocation/aws/provider.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py index e03a7a9cd7..2b9b7ab955 100644 --- a/deployability/modules/allocation/aws/provider.py +++ b/deployability/modules/allocation/aws/provider.py @@ -190,10 +190,14 @@ def __create_ec2_instance(config: AWSConfig) -> str: Returns: str: Identifier of the created instance. """ - client = boto3.resource('ec2') + client = boto3.client('ec2') + resource = boto3.resource('ec2') userData_file = Path(__file__).parent.parent / 'aws' / 'helpers' / 'userData.sh' windosUserData_file = Path(__file__).parent.parent / 'aws' / 'helpers' / 'windowsUserData.ps1' + # Describe the AMI to get the root device name + ami = client.describe_images(ImageIds=[config.ami]) + root_device_name = ami['Images'][0]['RootDeviceName'] if config.platform == 'windows': with open(windosUserData_file, 'r') as file: @@ -209,7 +213,7 @@ def __create_ec2_instance(config: AWSConfig) -> str: 'SecurityGroupIds': config.security_groups, 'BlockDeviceMappings': [ { - 'DeviceName': '/dev/sda1', + 'DeviceName': root_device_name, 'Ebs': { 'DeleteOnTermination': True, @@ -240,7 +244,7 @@ def __create_ec2_instance(config: AWSConfig) -> str: if config.issue: params['TagSpecifications'][0]['Tags'].append({'Key': 'issue', 'Value': config.issue}) - instance = client.create_instances(**params)[0] + instance = resource.create_instances(**params)[0] # Wait until the instance is running. instance.wait_until_running() return instance.instance_id From 6bb63bfde9e9a255bd0b63b591a776a9827d9fb5 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Thu, 6 Jun 2024 16:18:33 -0300 Subject: [PATCH 179/195] Updated Centos stream 8 AMIs with yum repositories --- deployability/modules/allocation/static/specs/os.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 54eef7d910..24b712cf6e 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -308,13 +308,13 @@ aws: zone: us-east-1 user: centos linux-centos-8-amd64: - ami: ami-05f2b469e504202f7 + ami: ami-0d8e399bc6283a29e zone: us-east-1 user: cloud-user linux-centos-8-arm64: - ami: ami-012947942cdc60db6 + ami: ami-063078a79d6ca4ca0 zone: us-east-1 - user: centos + user: cloud-user linux-centos-9-amd64: ami: ami-0259c451b16b72e24 zone: us-east-1 From 587f5672105c98a106deb56b5a49b296d18e4d57 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Fri, 7 Jun 2024 11:20:52 -0300 Subject: [PATCH 180/195] Fixed aws cli validation --- deployability/modules/allocation/aws/provider.py | 2 +- deployability/modules/allocation/vagrant/provider.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deployability/modules/allocation/aws/provider.py b/deployability/modules/allocation/aws/provider.py index 2b9b7ab955..7822889859 100644 --- a/deployability/modules/allocation/aws/provider.py +++ b/deployability/modules/allocation/aws/provider.py @@ -380,7 +380,7 @@ def validate_dependencies(): result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{dependency}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if result.returncode != 0: if dependency == 'awscli': - aws_binary = subprocess.run(['which', '/usr/local/bin/aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + aws_binary = subprocess.run(['which', 'aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if aws_binary.returncode != 0: missing_dependencies.append(dependency) else: diff --git a/deployability/modules/allocation/vagrant/provider.py b/deployability/modules/allocation/vagrant/provider.py index 18d14ee560..addc1314e2 100644 --- a/deployability/modules/allocation/vagrant/provider.py +++ b/deployability/modules/allocation/vagrant/provider.py @@ -464,7 +464,7 @@ def validate_dependencies(composite_name: str): result = subprocess.run(['bash', '-c', f"apt list --installed 2>/dev/null | grep -q -E ^{dependency}*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if result.returncode != 0: if dependency == 'awscli': - aws_binary = subprocess.run(['which', '/usr/local/bin/aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + aws_binary = subprocess.run(['which', 'aws'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if aws_binary.returncode != 0: missing_dependencies.append(dependency) else: From 5440f9f93dcbf43178069429be1afcbb054e5422 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Fri, 7 Jun 2024 17:28:48 -0300 Subject: [PATCH 181/195] 4.8.0 adaptation and fixes --- .../modules/testing/tests/helpers/agent.py | 2 +- .../modules/testing/tests/helpers/executor.py | 11 + .../modules/testing/tests/helpers/generic.py | 26 +- .../testing/tests/test_agent/test_install.py | 4 +- .../test_central_components/test_install.py | 2 +- .../tests/test_manager/test_install.py | 2 +- .../4.8.0/agent/Test-agents-complete.yaml | 267 ++++++++++++++++++ .../4.8.0/agent/Test-agents-one-each.yaml | 172 +++++++++++ .../4.8.0/agent/Test-agents-one-linux.yaml | 113 ++++++++ .../central_components/Test-CC-complete.yaml | 69 +++++ .../central_components/Test-CC-one-linux.yaml | 59 ++++ .../4.8.0/manager/Test-manager-complete.yaml | 88 ++++++ .../4.8.0/manager/Test-manager-one-linux.yaml | 58 ++++ 13 files changed, 858 insertions(+), 15 deletions(-) create mode 100755 deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-complete.yaml create mode 100755 deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-one-each.yaml create mode 100755 deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-one-linux.yaml create mode 100644 deployability/modules/workflow_engine/examples/4.8.0/central_components/Test-CC-complete.yaml create mode 100644 deployability/modules/workflow_engine/examples/4.8.0/central_components/Test-CC-one-linux.yaml create mode 100644 deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-complete.yaml create mode 100644 deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-one-linux.yaml diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index c0bf9b988a..5d1c2d3fb0 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -68,7 +68,7 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv commands.extend(system_commands) elif os_type == 'windows' : commands.extend([ - f"Invoke-WebRequest -Uri https://packages.wazuh.com/{release}/windows/wazuh-agent-{wazuh_version}-1.msi " + f"Invoke-WebRequest -Uri https://{s3_url}.wazuh.com/{release}/windows/wazuh-agent-{wazuh_version}-1.msi " "-OutFile $env:TEMP\wazuh-agent.msi" ]) commands.extend([ diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index 4f7b17d78b..a5ed915de4 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -49,15 +49,26 @@ def execute_commands(inventory_path, commands) -> dict: executor = ConnectionManager._get_executor(inventory_path) if isinstance(commands, str): try: + print("command" , "#"*10) + print(commands) + print("command" , "#"*10) result = executor._execute_command(ConectionInventory._get_inventory_data(inventory_path), commands) except Exception as e: raise Exception(f'Error executing command: {commands} with error: {e}') + print("result" , "#"*20) + print(result) return result else: results = {} for command in commands: + print("command" , "#"*10) + print(command) + print("command" , "#"*10) result = executor._execute_command(ConectionInventory._get_inventory_data(inventory_path), command) results[command] = result + + print("result" , "#"*20) + print(result) return results class WindowsExecutor(): diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 3b8d9a37bf..b8df53ae4b 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -260,7 +260,7 @@ def get_public_ip_from_aws_dns(dns_name) -> str: Args: dns_name (str): host's dns public dns name - + Returns: str: public ip """ @@ -292,7 +292,7 @@ def get_client_keys(inventory_path) -> list[dict]: client_key = ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WINDOWS_CLIENT_KEYS}"').get('output') elif os_type == 'macos': client_key = ConnectionManager.execute_commands(inventory_path, f'cat {MACOS_CLIENT_KEYS}').get('output') - + if client_key != None: lines = client_key.split('\n')[:-1] for line in lines: @@ -372,7 +372,7 @@ def _extract_hosts(paths, is_aws): return [Utils.extract_ansible_host(path) for path in paths] @staticmethod - def certs_create(wazuh_version, master_path, dashboard_path, indexer_paths=[], worker_paths=[]) -> None: + def certs_create(wazuh_version, master_path, dashboard_path, indexer_paths=[], worker_paths=[], live="") -> None: """ Creates wazuh certificates @@ -399,16 +399,22 @@ def certs_create(wazuh_version, master_path, dashboard_path, indexer_paths=[], w ##Basic commands to setup the config file, add the ip for the master & dashboard os_name = HostInformation.get_os_name_from_inventory(master_path) + + if live == "False": + s3_url = 'packages-dev.wazuh.com' + else: + s3_url = 'packages.wazuh.com' + if os_name == 'debian': commands = [ - f'wget https://packages.wazuh.com/{wazuh_version}/wazuh-install.sh', - f'wget https://packages.wazuh.com/{wazuh_version}/config.yml', + f'wget https://{s3_url}/{wazuh_version}/wazuh-install.sh', + f'wget https://{s3_url}/{wazuh_version}/config.yml', f"sed -i '/^\s*#/d' {current_directory}/config.yml" ] else: commands = [ - f'curl -sO https://packages.wazuh.com/{wazuh_version}/wazuh-install.sh', - f'curl -sO https://packages.wazuh.com/{wazuh_version}/config.yml', + f'curl -sO https://{s3_url}/{wazuh_version}/wazuh-install.sh', + f'curl -sO https://{s3_url}/{wazuh_version}/config.yml', f"sed -i '/^\s*#/d' {current_directory}/config.yml" ] @@ -866,11 +872,11 @@ def is_component_active(inventory_path, host_role) -> bool: return result.get('success') elif os_type == 'macos': - result = ConnectionManager.execute_commands(inventory_path, f'launchctl list | grep com.{host_role.replace("-", ".")}').get('output') - if result == None: + result = ConnectionManager.execute_commands(inventory_path, f'ps aux | grep {host_role} | grep -v grep') + if result.get('output') == None: return False else: - return f'com.{host_role.replace("-", ".")}' in result + return result.get('success') class Waits: diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index dd5247f8cf..f1328fac86 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -76,8 +76,8 @@ def test_installation(wazuh_params): logger.info(f'Manager is already installed in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])}') else: HostConfiguration.disable_firewall(manager_params) - HostConfiguration.certs_create(wazuh_params['wazuh_version'], wazuh_params['master'], wazuh_params['dashboard'], wazuh_params['indexers'], wazuh_params['workers']) - WazuhManager.install_manager(wazuh_params['master'], 'wazuh-1', wazuh_params['wazuh_version']) + HostConfiguration.certs_create(wazuh_params['wazuh_version'], wazuh_params['master'], wazuh_params['dashboard'], wazuh_params['indexers'], wazuh_params['workers'], wazuh_params['live']) + WazuhManager.install_manager(wazuh_params['master'], 'wazuh-1', wazuh_params['wazuh_version'], wazuh_params['live']) assert HostInformation.dir_exists(wazuh_params['master'], WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])}') # Agent installation diff --git a/deployability/modules/testing/tests/test_central_components/test_install.py b/deployability/modules/testing/tests/test_central_components/test_install.py index 78a780a276..87004a76d2 100644 --- a/deployability/modules/testing/tests/test_central_components/test_install.py +++ b/deployability/modules/testing/tests/test_central_components/test_install.py @@ -60,7 +60,7 @@ def test_installation(wazuh_params): HostConfiguration.disable_firewall(manager_params) # Certs create and scp from master to worker - HostConfiguration.certs_create(wazuh_params['wazuh_version'], wazuh_params['master'], wazuh_params['dashboard'], wazuh_params['indexers'], wazuh_params['workers']) + HostConfiguration.certs_create(wazuh_params['wazuh_version'], wazuh_params['master'], wazuh_params['dashboard'], wazuh_params['indexers'], wazuh_params['workers'], wazuh_params['live']) # Install central components and perform checkfile testing for _, manager_params in wazuh_params['managers'].items(): diff --git a/deployability/modules/testing/tests/test_manager/test_install.py b/deployability/modules/testing/tests/test_manager/test_install.py index 39279c672a..460b61d41e 100644 --- a/deployability/modules/testing/tests/test_manager/test_install.py +++ b/deployability/modules/testing/tests/test_manager/test_install.py @@ -56,7 +56,7 @@ def test_installation(wazuh_params): HostConfiguration.disable_firewall(manager_params) # Certs create and scp from master to worker - HostConfiguration.certs_create(wazuh_params['wazuh_version'], wazuh_params['master'], wazuh_params['dashboard'], wazuh_params['indexers'], wazuh_params['workers']) + HostConfiguration.certs_create(wazuh_params['wazuh_version'], wazuh_params['master'], wazuh_params['dashboard'], wazuh_params['indexers'], wazuh_params['workers'], wazuh_params['live']) for workers in wazuh_params['workers']: HostConfiguration.scp_to(wazuh_params['master'], workers, 'wazuh-install-files.tar') diff --git a/deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-complete.yaml b/deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-complete.yaml new file mode 100755 index 0000000000..d30f60a2a8 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-complete.yaml @@ -0,0 +1,267 @@ +version: 0.1 +description: This workflow is used to test agents deployment for DDT1 PoC +variables: + agent-os: + - linux-redhat-7-amd64 + - linux-redhat-7-arm64 + - linux-redhat-8-amd64 + - linux-redhat-8-arm64 + - linux-redhat-9-amd64 + - linux-redhat-9-arm64 + - linux-centos-7-amd64 + - linux-centos-7-arm64 + - linux-centos-8-amd64 + - linux-centos-8-arm64 + - linux-debian-10-amd64 + - linux-debian-10-arm64 + - linux-debian-11-amd64 + - linux-debian-11-arm64 + - linux-debian-12-amd64 + - linux-debian-12-arm64 + - linux-ubuntu-22.04-amd64 + - linux-ubuntu-22.04-arm64 + - linux-ubuntu-18.04-amd64 + - linux-ubuntu-18.04-arm64 + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-20.04-arm64 + - linux-oracle-9-amd64 + - linux-amazon-2-amd64 + - linux-amazon-2-arm64 + - linux-amazon-2023-amd64 + - linux-amazon-2023-arm64 + + windows-agent-os: + - windows-desktop-10-amd64 + - windows-server-2012r2-amd64 + - windows-server-2016-amd64 + - windows-server-2019-amd64 + - windows-server-2022-amd64 + + macos-agent-os: + - macos-ventura-13-amd64 + - macos-sonoma-14-amd64 + + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + macos-infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.8.0 + live: False + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,connection,basic_info,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.8.0" + - wazuh-revision: "40812" + - live: False + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + + + + # Unique agent allocate task + - task: "allocate-macos-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{macos-infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: macos-agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + + # Generic agent test task + - task: "run-macos-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,connection,basic_info,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.8.0" + - wazuh-revision: "40812" + - live: False + foreach: + - variable: macos-agent-os + as: agent + depends-on: + - "allocate-macos-agent-{agent}" + - "provision-manager-{manager-os}" + + + # Unique agent allocate task + - task: "allocate-windows-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: windows-agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + + # Generic agent test task + - task: "run-windows-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,connection,basic_info,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.8.0" + - wazuh-revision: "40812" + - live: False + foreach: + - variable: windows-agent-os + as: agent + depends-on: + - "allocate-windows-agent-{agent}" + - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-one-each.yaml b/deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-one-each.yaml new file mode 100755 index 0000000000..ac4e092af5 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-one-each.yaml @@ -0,0 +1,172 @@ +version: 0.1 +description: This workflow is used to test agents deployment for DDT1 PoC +variables: + agent-os: + - linux-ubuntu-20.04-amd64 + - windows-server-2022-amd64 + + macos-agent-os: + - macos-sonoma-14-amd64 + + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + macos-infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique macOS agent allocate task + - task: "allocate-macos-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{macos-infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: macos-agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.8.0 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info" + - component: "agent" + - wazuh-version: "4.8.0" + - wazuh-revision: "40812" + - live: True + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" + + # Generic macOS agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info,connection,restart,stop,uninstall" + - component: "agent" + - wazuh-version: "4.8.0" + - wazuh-revision: "40812" + - live: True + foreach: + - variable: macos-agent-os + as: agent + depends-on: + - "allocate-macos-agent-{agent}" + - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-one-linux.yaml b/deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-one-linux.yaml new file mode 100755 index 0000000000..fcad1c0d06 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-one-linux.yaml @@ -0,0 +1,113 @@ +version: 0.1 +description: This workflow is used to test agents deployment for DDT1 PoC +variables: + agent-os: + - linux-ubuntu-20.04-amd64 + + manager-os: linux-ubuntu-22.04-amd64 + infra-provider: aws + macos-infra-provider: vagrant + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager-os}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager-os}" + - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager-os}/track.yaml" + + # Unique agent allocate task + - task: "allocate-agent-{agent}" + description: "Allocate resources for the agent." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: small + - composite-name: "{agent}" + - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" + - track-output: "{working-dir}/agent-{agent}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + - label-issue: "https://github.com/wazuh/wazuh-qa/issues/5191" + on-error: "abort-all" + foreach: + - variable: agent-os + as: agent + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/agent-{agent}/track.yaml" + + # Unique manager provision task + - task: "provision-manager-{manager-os}" + description: "Provision the manager." + do: + this: process + with: + path: python3 + args: + - modules/provision/main.py + - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml" + - install: + - component: wazuh-manager + type: assistant + version: 4.8.0 + live: True + depends-on: + - "allocate-manager-{manager-os}" + on-error: "abort-all" + + # Generic agent test task + - task: "run-agent-{agent}-tests" + description: "Run tests install for the agent {agent}." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml" + - agent: "{working-dir}/agent-{agent}/inventory.yaml" + - tests: "install,registration,basic_info" + - component: "agent" + - wazuh-version: "4.8.0" + - wazuh-revision: "40812" + - live: True + foreach: + - variable: agent-os + as: agent + depends-on: + - "allocate-agent-{agent}" + - "provision-manager-{manager-os}" diff --git a/deployability/modules/workflow_engine/examples/4.8.0/central_components/Test-CC-complete.yaml b/deployability/modules/workflow_engine/examples/4.8.0/central_components/Test-CC-complete.yaml new file mode 100644 index 0000000000..08440105c0 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/4.8.0/central_components/Test-CC-complete.yaml @@ -0,0 +1,69 @@ +version: 0.1 +description: This workflow is used to test the Wazuh manager deployment for DDT1 PoC +variables: + central_components-os: + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-amazon-2-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique central components allocate task + - task: "allocate-central_components-{central_components}" + description: "Allocate resources for the central_components." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{central_components}" + - inventory-output: "{working-dir}/central_components-{central_components}/inventory.yaml" + - track-output: "{working-dir}/central_components-{central_components}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: central_components-os + as: central_components + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/central_components-{central_components-os}/track.yaml" + + # Generic manager test task + - task: "run-central_components-{central_components}-tests" + description: "Run tests install for the central_components." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/central_components-{central_components}/inventory.yaml" + - tests: "install,restart,stop,uninstall" + - component: "central_components" + - wazuh-version: "4.8.0" + - wazuh-revision: "40812" + - live: False + on-error: "abort-all" + foreach: + - variable: central_components-os + as: central_components diff --git a/deployability/modules/workflow_engine/examples/4.8.0/central_components/Test-CC-one-linux.yaml b/deployability/modules/workflow_engine/examples/4.8.0/central_components/Test-CC-one-linux.yaml new file mode 100644 index 0000000000..2d5e790b47 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/4.8.0/central_components/Test-CC-one-linux.yaml @@ -0,0 +1,59 @@ +version: 0.1 +description: This workflow is used to test the Wazuh manager deployment for DDT1 PoC +variables: + central_components-os: + - linux-ubuntu-20.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique central components allocate task + - task: "allocate-central_components-{central_components}" + description: "Allocate resources for the central_components." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{central_components}" + - inventory-output: "{working-dir}/central_components-{central_components}/inventory.yaml" + - track-output: "{working-dir}/central_components-{central_components}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: central_components-os + as: central_components + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/central_components-{central_components-os}/track.yaml" + + # Generic manager test task + - task: "run-central_components-{central_components}-tests" + description: "Run tests install for the central_components." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/central_components-{central_components}/inventory.yaml" + - tests: "install,restart,stop,uninstall" + - component: "central_components" + - wazuh-version: "4.8.0" + - wazuh-revision: "40812" + - live: False + on-error: "abort-all" + foreach: + - variable: central_components-os + as: central_components diff --git a/deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-complete.yaml b/deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-complete.yaml new file mode 100644 index 0000000000..67937d787a --- /dev/null +++ b/deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-complete.yaml @@ -0,0 +1,88 @@ +version: 0.1 +description: This workflow is used to test manager deployment for DDT1 PoC +variables: + manager-os: + - linux-ubuntu-20.04-amd64 + - linux-ubuntu-22.04-amd64 + - linux-amazon-2-amd64 + - linux-redhat-7-amd64 + - linux-redhat-8-amd64 + - linux-redhat-9-amd64 + - linux-centos-7-amd64 + - linux-centos-8-amd64 + - linux-debian-10-amd64 + - linux-debian-11-amd64 + - linux-debian-12-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager}" + - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: manager-os + as: manager + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager}/track.yaml" + + # Generic manager test task + - task: "run-manager-tests" + description: "Run tests install for the manager." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-centos-7-amd64/inventory.yaml" + - wazuh-2: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - wazuh-3: "{working-dir}/manager-linux-ubuntu-22.04-amd64/inventory.yaml" + - wazuh-4: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" + - wazuh-5: "{working-dir}/manager-linux-redhat-8-amd64/inventory.yaml" + - wazuh-6: "{working-dir}/manager-linux-redhat-9-amd64/inventory.yaml" + #- wazuh-7: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" + - wazuh-7: "{working-dir}/manager-linux-debian-10-amd64/inventory.yaml" + - wazuh-8: "{working-dir}/manager-linux-debian-11-amd64/inventory.yaml" + - wazuh-9: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" + #- wazuh-4: "{working-dir}/manager-linux-amazon-2-amd64/inventory.yaml" + - tests: "install,restart,stop,uninstall" + - component: "manager" + - wazuh-version: "4.8.0" + - wazuh-revision: "40812" + - live: False + depends-on: + - "allocate-manager-linux-ubuntu-20.04-amd64" + - "allocate-manager-linux-ubuntu-22.04-amd64" + #- "allocate-manager-linux-amazon-2-amd64" + - "allocate-manager-linux-redhat-7-amd64" + - "allocate-manager-linux-redhat-8-amd64" + - "allocate-manager-linux-redhat-9-amd64" + - "allocate-manager-linux-centos-7-amd64" + #- "allocate-manager-linux-centos-8-amd64" + - "allocate-manager-linux-debian-10-amd64" + - "allocate-manager-linux-debian-11-amd64" + - "allocate-manager-linux-debian-12-amd64" + diff --git a/deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-one-linux.yaml b/deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-one-linux.yaml new file mode 100644 index 0000000000..7626bb5192 --- /dev/null +++ b/deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-one-linux.yaml @@ -0,0 +1,58 @@ +version: 0.1 +description: This workflow is used to test manager deployment for DDT1 PoC +variables: + manager-os: + - linux-ubuntu-20.04-amd64 + infra-provider: aws + working-dir: /tmp/dtt1-poc + +tasks: + # Unique manager allocate task + - task: "allocate-manager-{manager}" + description: "Allocate resources for the manager." + do: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: create + - provider: "{infra-provider}" + - size: large + - composite-name: "{manager}" + - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" + - track-output: "{working-dir}/manager-{manager}/track.yaml" + - label-termination-date: "1d" + - label-team: "qa" + on-error: "abort-all" + foreach: + - variable: manager-os + as: manager + cleanup: + this: process + with: + path: python3 + args: + - modules/allocation/main.py + - action: delete + - track-output: "{working-dir}/manager-{manager}/track.yaml" + + # Generic manager test task + - task: "run-manager-tests" + description: "Run tests install for the manager." + do: + this: process + with: + path: python3 + args: + - modules/testing/main.py + - targets: + - wazuh-1: "{working-dir}/manager-linux-ubuntu-20.04-amd64/inventory.yaml" + - tests: "install,restart,stop,uninstall" + - component: "manager" + - wazuh-version: "4.8.0" + - wazuh-revision: "40812" + - live: False + depends-on: + - "allocate-manager-linux-ubuntu-20.04-amd64" + From 84272ed5bdbf8664f3974a93ec0c9321019b0ef8 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Fri, 7 Jun 2024 17:31:56 -0300 Subject: [PATCH 182/195] Remove prints used for test --- .../modules/testing/tests/helpers/executor.py | 10 ---------- .../4.8.0/manager/Test-manager-complete.yaml | 14 +++++++------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index a5ed915de4..a3280d6d14 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -49,26 +49,16 @@ def execute_commands(inventory_path, commands) -> dict: executor = ConnectionManager._get_executor(inventory_path) if isinstance(commands, str): try: - print("command" , "#"*10) - print(commands) - print("command" , "#"*10) result = executor._execute_command(ConectionInventory._get_inventory_data(inventory_path), commands) except Exception as e: raise Exception(f'Error executing command: {commands} with error: {e}') - print("result" , "#"*20) - print(result) return result else: results = {} for command in commands: - print("command" , "#"*10) - print(command) - print("command" , "#"*10) result = executor._execute_command(ConectionInventory._get_inventory_data(inventory_path), command) results[command] = result - print("result" , "#"*20) - print(result) return results class WindowsExecutor(): diff --git a/deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-complete.yaml b/deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-complete.yaml index 67937d787a..2c97f22147 100644 --- a/deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-complete.yaml +++ b/deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-complete.yaml @@ -63,11 +63,11 @@ tasks: - wazuh-4: "{working-dir}/manager-linux-redhat-7-amd64/inventory.yaml" - wazuh-5: "{working-dir}/manager-linux-redhat-8-amd64/inventory.yaml" - wazuh-6: "{working-dir}/manager-linux-redhat-9-amd64/inventory.yaml" - #- wazuh-7: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" - - wazuh-7: "{working-dir}/manager-linux-debian-10-amd64/inventory.yaml" - - wazuh-8: "{working-dir}/manager-linux-debian-11-amd64/inventory.yaml" - - wazuh-9: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" - #- wazuh-4: "{working-dir}/manager-linux-amazon-2-amd64/inventory.yaml" + - wazuh-7: "{working-dir}/manager-linux-centos-8-amd64/inventory.yaml" + - wazuh-8: "{working-dir}/manager-linux-debian-10-amd64/inventory.yaml" + - wazuh-9: "{working-dir}/manager-linux-debian-11-amd64/inventory.yaml" + - wazuh-10: "{working-dir}/manager-linux-debian-12-amd64/inventory.yaml" + - wazuh-11: "{working-dir}/manager-linux-amazon-2-amd64/inventory.yaml" - tests: "install,restart,stop,uninstall" - component: "manager" - wazuh-version: "4.8.0" @@ -76,12 +76,12 @@ tasks: depends-on: - "allocate-manager-linux-ubuntu-20.04-amd64" - "allocate-manager-linux-ubuntu-22.04-amd64" - #- "allocate-manager-linux-amazon-2-amd64" + - "allocate-manager-linux-amazon-2-amd64" - "allocate-manager-linux-redhat-7-amd64" - "allocate-manager-linux-redhat-8-amd64" - "allocate-manager-linux-redhat-9-amd64" - "allocate-manager-linux-centos-7-amd64" - #- "allocate-manager-linux-centos-8-amd64" + - "allocate-manager-linux-centos-8-amd64" - "allocate-manager-linux-debian-10-amd64" - "allocate-manager-linux-debian-11-amd64" - "allocate-manager-linux-debian-12-amd64" From dd50ba42ae3e7a92db0b5d594618708ad5d8f061 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Fri, 7 Jun 2024 17:32:27 -0300 Subject: [PATCH 183/195] Remove line --- deployability/modules/testing/tests/helpers/executor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index a3280d6d14..4f7b17d78b 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -58,7 +58,6 @@ def execute_commands(inventory_path, commands) -> dict: for command in commands: result = executor._execute_command(ConectionInventory._get_inventory_data(inventory_path), command) results[command] = result - return results class WindowsExecutor(): From 126fb6d4c169923cd594a53c51b1bdc9325ec795 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Tue, 11 Jun 2024 09:37:40 -0300 Subject: [PATCH 184/195] Add test_indexer_conexion --- .../modules/testing/tests/helpers/manager.py | 20 ++++++++++++++++++- .../test_central_components/test_install.py | 5 +++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index b3c752d11f..e182092fd5 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -5,7 +5,7 @@ import requests import time -from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT +from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT, WAZUH_LOG from .executor import WazuhAPI, ConnectionManager from .generic import HostInformation, CheckFiles from modules.testing.utils import logger @@ -438,3 +438,21 @@ def get_manager_logs(wazuh_api: WazuhAPI) -> list: except Exception as e: logger.error(f"Unexpected error: {e}") return f"Unexpected error: {e}" + + @staticmethod + def get_indexer_status(inventory_path) -> None: + """ + Returns if Wazuh indexer is connected to Wazuh manager + + Args: + inventory_path: host's inventory path + + Returns: + str: Agents status + """ + + indexerConnection = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_LOG} | grep "IndexerConnector initialized successfully" | tail -n1').get('output') + + if indexerConnection is not None and indexerConnection.strip(): + return True + return False diff --git a/deployability/modules/testing/tests/test_central_components/test_install.py b/deployability/modules/testing/tests/test_central_components/test_install.py index 87004a76d2..2a23a897fd 100644 --- a/deployability/modules/testing/tests/test_central_components/test_install.py +++ b/deployability/modules/testing/tests/test_central_components/test_install.py @@ -164,3 +164,8 @@ def test_indexer_port(wazuh_params): def test_filebeat_status(wazuh_params): assert 'active' in GeneralComponentActions.get_component_status(wazuh_params['master'], 'filebeat'), logger.error(f"The Filebeat in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params['master'])} is not active") + + +def test_indexer_conexion(wazuh_params): + for indexer_params in wazuh_params['indexers']: + assert WazuhManager.get_indexer_status(indexer_params), logger.error(f'IndexerConnector initialization failed {HostInformation.get_os_name_and_version_from_inventory(indexer_params)}') From b41369cfd4f3adb85fa3f3ded7fcb5ff509ecc90 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Tue, 11 Jun 2024 12:01:44 -0300 Subject: [PATCH 185/195] Simplified validation of indexer status --- deployability/modules/testing/tests/helpers/manager.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index e182092fd5..b14b189f43 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -453,6 +453,4 @@ def get_indexer_status(inventory_path) -> None: indexerConnection = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_LOG} | grep "IndexerConnector initialized successfully" | tail -n1').get('output') - if indexerConnection is not None and indexerConnection.strip(): - return True - return False + return indexerConnection is not None and indexerConnection.strip() From e4b3bfa2b6a76a1f90ea9da26631afa7de051e66 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 11 Jun 2024 12:15:18 -0300 Subject: [PATCH 186/195] The rollback method is improved in the case of not being able to obtain the status of the instance correctly --- .../modules/allocation/vagrant/instance.py | 50 ++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/deployability/modules/allocation/vagrant/instance.py b/deployability/modules/allocation/vagrant/instance.py index efffa23f1c..90385c7da4 100755 --- a/deployability/modules/allocation/vagrant/instance.py +++ b/deployability/modules/allocation/vagrant/instance.py @@ -103,13 +103,8 @@ def delete(self) -> None: Skipping deletion.") return self.__run_vagrant_command(['destroy', '-f']) - if str(self.host_identifier) == "macstadium": - logger.debug(f"Deleting remote directory {self.host_instance_dir}") - VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) - if self.virtualizer == 'parallels': - logger.debug(f"Killing remote process on port {self.ssh_port}") - proccess = VagrantUtils.remote_command(f"sudo lsof -Pi :{self.ssh_port} -sTCP:LISTEN -t", self.remote_host_parameters) - VagrantUtils.remote_command(f"sudo kill -9 {proccess}", self.remote_host_parameters) + if self.host_instance_dir: + self.__cleanup_remote_host() def status(self) -> str: """ @@ -120,18 +115,24 @@ def status(self) -> str: """ output = self.__run_vagrant_command('status') vagrant_status = self.__parse_vagrant_status(output) - if vagrant_status == None: - if VagrantUtils.remote_command(f"sudo ls {self.host_instance_dir} > /dev/null 2>&1", self.remote_host_parameters): - if VagrantUtils.remote_command(f"sudo /usr/local/bin/prlctl list -a | grep {self.identifier} > /dev/null 2>&1", self.remote_host_parameters): - logger.warning(f"The instance was found, it will be deleted. The creation of the instance must be restarted again.") - self.delete() - else: - VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) - raise ValueError(f"Instance {self.identifier} is not running, remote instance dir {self.host_instance_dir} was removed.") + if vagrant_status is None: + if self.remote_host_parameters['server_ip'] == None: + raise ValueError(f"Cannot obtain the status of the instance {self.identifier}, please remove the instance manually.") else: - raise ValueError(f"Instance {self.host_instance_dir} not found.") + output = VagrantUtils.remote_command(f"sudo test -d {self.host_instance_dir} && echo 'Directory exists' || echo 'Directory does not exist'", self.remote_host_parameters) + if 'Directory exists' in output: + if VagrantUtils.remote_command(f"sudo /usr/local/bin/prlctl list -a | grep {self.identifier}", self.remote_host_parameters): + logger.warning(f"The instance was found, it will be deleted. The creation of the instance must be restarted again.") + self.__run_vagrant_command(['destroy', '-f']) + self.__cleanup_remote_host() + raise ValueError(f"Cannot obtain the status of the instance {self.identifier}, the creation of the instance must be restarted again.") + else: + VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) + raise ValueError(f"Instance {self.identifier} is not running, remote instance dir {self.host_instance_dir} was removed.") + else: + raise ValueError(f"Instance {self.host_instance_dir} not found.") else: - return self.__parse_vagrant_status(output) + return vagrant_status def ssh_connection_info(self) -> ConnectionInfo: """ @@ -259,3 +260,18 @@ def __parse_vagrant_status(self, message: str) -> str: status = ' '.join(status_line.split()[1:]) status = status.split('(')[0].strip() return status + + def __cleanup_remote_host(self) -> None: + """ + Cleans up the remote host. + + Returns: + None + """ + if str(self.host_identifier) == "macstadium": + logger.debug(f"Deleting remote directory {self.host_instance_dir}") + VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) + if self.virtualizer == 'parallels': + logger.debug(f"Killing remote process on port {self.ssh_port}") + proccess = VagrantUtils.remote_command(f"sudo lsof -Pi :{self.ssh_port} -sTCP:LISTEN -t", self.remote_host_parameters) + VagrantUtils.remote_command(f"sudo kill -9 {proccess}", self.remote_host_parameters) From ee8ec856387d3659f73b3384ba21ddb4058753f8 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 11 Jun 2024 12:23:17 -0300 Subject: [PATCH 187/195] Changed the host identifier validation --- .../modules/allocation/vagrant/instance.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/deployability/modules/allocation/vagrant/instance.py b/deployability/modules/allocation/vagrant/instance.py index 90385c7da4..15c6d8bf1e 100755 --- a/deployability/modules/allocation/vagrant/instance.py +++ b/deployability/modules/allocation/vagrant/instance.py @@ -103,7 +103,7 @@ def delete(self) -> None: Skipping deletion.") return self.__run_vagrant_command(['destroy', '-f']) - if self.host_instance_dir: + if str(self.host_identifier) == "macstadium": self.__cleanup_remote_host() def status(self) -> str: @@ -268,10 +268,9 @@ def __cleanup_remote_host(self) -> None: Returns: None """ - if str(self.host_identifier) == "macstadium": - logger.debug(f"Deleting remote directory {self.host_instance_dir}") - VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) - if self.virtualizer == 'parallels': - logger.debug(f"Killing remote process on port {self.ssh_port}") - proccess = VagrantUtils.remote_command(f"sudo lsof -Pi :{self.ssh_port} -sTCP:LISTEN -t", self.remote_host_parameters) - VagrantUtils.remote_command(f"sudo kill -9 {proccess}", self.remote_host_parameters) + logger.debug(f"Deleting remote directory {self.host_instance_dir}") + VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) + if self.virtualizer == 'parallels': + logger.debug(f"Killing remote process on port {self.ssh_port}") + proccess = VagrantUtils.remote_command(f"sudo lsof -Pi :{self.ssh_port} -sTCP:LISTEN -t", self.remote_host_parameters) + VagrantUtils.remote_command(f"sudo kill -9 {proccess}", self.remote_host_parameters) From 7a2b06cf61eab0cc1798abdd997cb6ee684f9318 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 11 Jun 2024 13:29:54 -0300 Subject: [PATCH 188/195] Fixed rollback messages --- deployability/modules/allocation/vagrant/instance.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deployability/modules/allocation/vagrant/instance.py b/deployability/modules/allocation/vagrant/instance.py index 15c6d8bf1e..3bdd8227e1 100755 --- a/deployability/modules/allocation/vagrant/instance.py +++ b/deployability/modules/allocation/vagrant/instance.py @@ -122,15 +122,15 @@ def status(self) -> str: output = VagrantUtils.remote_command(f"sudo test -d {self.host_instance_dir} && echo 'Directory exists' || echo 'Directory does not exist'", self.remote_host_parameters) if 'Directory exists' in output: if VagrantUtils.remote_command(f"sudo /usr/local/bin/prlctl list -a | grep {self.identifier}", self.remote_host_parameters): - logger.warning(f"The instance was found, it will be deleted. The creation of the instance must be restarted again.") + logger.warning(f"The instance was found, it will be deleted. The creation of the instance must be retried..") self.__run_vagrant_command(['destroy', '-f']) self.__cleanup_remote_host() - raise ValueError(f"Cannot obtain the status of the instance {self.identifier}, the creation of the instance must be restarted again.") + raise ValueError(f"Cannot obtain the status of the instance {self.identifier}, the creation of the instance must be retried..") else: VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) - raise ValueError(f"Instance {self.identifier} is not running, remote instance dir {self.host_instance_dir} was removed.") + raise ValueError(f"Instance {self.identifier} is not running, remote instance dir {self.host_instance_dir} was removed. The creation of the instance must be retried.") else: - raise ValueError(f"Instance {self.host_instance_dir} not found.") + raise ValueError(f"Instance {self.host_instance_dir} not found. The creation of the instance must be retried.") else: return vagrant_status From 144039c3ee4ce68a65e28eabf91462127c7ea3b6 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Tue, 11 Jun 2024 13:30:52 -0300 Subject: [PATCH 189/195] Removed extra dot --- deployability/modules/allocation/vagrant/instance.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployability/modules/allocation/vagrant/instance.py b/deployability/modules/allocation/vagrant/instance.py index 3bdd8227e1..8b3c838931 100755 --- a/deployability/modules/allocation/vagrant/instance.py +++ b/deployability/modules/allocation/vagrant/instance.py @@ -122,10 +122,10 @@ def status(self) -> str: output = VagrantUtils.remote_command(f"sudo test -d {self.host_instance_dir} && echo 'Directory exists' || echo 'Directory does not exist'", self.remote_host_parameters) if 'Directory exists' in output: if VagrantUtils.remote_command(f"sudo /usr/local/bin/prlctl list -a | grep {self.identifier}", self.remote_host_parameters): - logger.warning(f"The instance was found, it will be deleted. The creation of the instance must be retried..") + logger.warning(f"The instance was found, it will be deleted. The creation of the instance must be retried.") self.__run_vagrant_command(['destroy', '-f']) self.__cleanup_remote_host() - raise ValueError(f"Cannot obtain the status of the instance {self.identifier}, the creation of the instance must be retried..") + raise ValueError(f"Cannot obtain the status of the instance {self.identifier}, the creation of the instance must be retried.") else: VagrantUtils.remote_command(f"sudo rm -rf {self.host_instance_dir}", self.remote_host_parameters) raise ValueError(f"Instance {self.identifier} is not running, remote instance dir {self.host_instance_dir} was removed. The creation of the instance must be retried.") From 5d9d78008add12f871d93b2bc6609e0c59cbc448 Mon Sep 17 00:00:00 2001 From: Marcelo Ariel Hamra Date: Thu, 13 Jun 2024 18:20:21 -0300 Subject: [PATCH 190/195] Rename workflow_engine to jobflow. --- .gitignore | 2 +- deployability/README.MD | 24 +-- deployability/modules/allocation/README.MD | 26 +-- .../modules/generic/logger/config.yaml | 2 +- .../{workflow_engine => jobflow}/README.MD | 36 ++-- .../{workflow_engine => jobflow}/__init__.py | 2 +- .../{workflow_engine => jobflow}/__main__.py | 10 +- .../agent/aws/Jenkins-agents-test.yaml | 0 .../agent/aws/test-agent-basic-info.yaml | 0 .../agent/aws/test-agent-complete-macOS.yaml | 0 .../agent/aws/test-agent-complete.yaml | 0 .../aws/test-agent-restart-ins-prov.yaml | 0 .../agent/aws/test-agent-stop-ins-prov.yaml | 0 .../examples/agent/aws/test-agent-suse.yaml | 0 .../examples/agent/aws/test-agent-time.yaml | 0 .../aws/test-agent-uninstall-ins-prov.yaml | 0 .../aws/test-agent-windows-complete.yaml | 0 .../agent/aws/test-full-agent-with-live.yaml | 0 .../test-agent-basic-info-vagrant.yaml | 0 .../agent/vagrant/test-agent-complete-1.yaml | 6 +- .../agent/vagrant/test-agent-complete-2.yaml | 0 .../vagrant/test-agent-complete-macOS.yaml | 0 .../test-agent-restart-ins-prov-1.yaml | 0 .../test-agent-restart-ins-prov-2.yaml | 0 .../vagrant/test-agent-stop-ins-prov-1.yaml | 0 .../vagrant/test-agent-stop-ins-prov-2.yaml | 0 .../test-agent-uninstall-ins-prov-1.yaml | 0 .../test-agent-uninstall-ins-prov-2.yaml | 0 .../vagrant/test-agent-windows-complete.yaml | 0 .../aws/Jenkins-central-component-test.yaml | 0 .../aws/dtt1-central_components-poc-aws.yaml | 0 .../dtt1-central_components-poc-vagrant.yaml | 0 .../manager/aws/Jenkins-manager-test.yaml | 0 .../manager/aws/dtt1-managers-poc-aws.yaml | 0 .../vagrant/dtt1-managers-poc-vagrant.yaml | 0 .../jobflow_processor.py} | 42 ++--- .../logger/__init__.py | 0 .../logger/config.yaml | 2 +- .../logger/filter.py | 0 .../logger/logger.py | 2 +- .../{workflow_engine => jobflow}/models.py | 2 +- .../requirements-dev.txt | 0 .../schema_validator.py | 4 +- .../schemas/schema_v1.json | 0 .../{workflow_engine => jobflow}/task.py | 2 +- .../modules/jobflow/tests/TESTING-README.md | 167 ++++++++++++++++++ .../tests/conftest.py | 26 +-- .../tests/data/wf-ko-no-path-on-cleanup.yaml | 0 .../tests/data/wf-ko-no-path-on-do.yaml | 0 .../tests/data/wf-ko-schema-error.yaml | 0 .../tests/data/wf-ok.yaml | 0 .../tests/test_dag.py | 8 +- .../tests/test_jobflow_file.py} | 132 +++++++------- .../tests/test_jobflow_processor.py} | 116 ++++++------ .../tests/test_schema_validator.py | 20 +-- .../tests/test_task.py | 8 +- deployability/modules/provision/README.MD | 32 ++-- .../modules/provision/tests/TESTING-README.md | 2 +- deployability/modules/setup.py | 12 +- deployability/modules/testing/README.MD | 30 ++-- .../workflow_engine/tests/TESTING-README.md | 167 ------------------ 61 files changed, 442 insertions(+), 440 deletions(-) rename deployability/modules/{workflow_engine => jobflow}/README.MD (85%) rename deployability/modules/{workflow_engine => jobflow}/__init__.py (77%) rename deployability/modules/{workflow_engine => jobflow}/__main__.py (81%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/aws/Jenkins-agents-test.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/aws/test-agent-basic-info.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/aws/test-agent-complete-macOS.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/aws/test-agent-complete.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/aws/test-agent-restart-ins-prov.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/aws/test-agent-stop-ins-prov.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/aws/test-agent-suse.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/aws/test-agent-time.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/aws/test-agent-uninstall-ins-prov.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/aws/test-agent-windows-complete.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/aws/test-full-agent-with-live.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/vagrant/test-agent-complete-1.yaml (96%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/vagrant/test-agent-complete-2.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/vagrant/test-agent-complete-macOS.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/agent/vagrant/test-agent-windows-complete.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/central_components/aws/Jenkins-central-component-test.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/central_components/aws/dtt1-central_components-poc-aws.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/manager/aws/Jenkins-manager-test.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/manager/aws/dtt1-managers-poc-aws.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml (100%) rename deployability/modules/{workflow_engine/workflow_processor.py => jobflow/jobflow_processor.py} (91%) rename deployability/modules/{workflow_engine => jobflow}/logger/__init__.py (100%) rename deployability/modules/{workflow_engine => jobflow}/logger/config.yaml (96%) rename deployability/modules/{workflow_engine => jobflow}/logger/filter.py (100%) rename deployability/modules/{workflow_engine => jobflow}/logger/logger.py (92%) rename deployability/modules/{workflow_engine => jobflow}/models.py (93%) rename deployability/modules/{workflow_engine => jobflow}/requirements-dev.txt (100%) rename deployability/modules/{workflow_engine => jobflow}/schema_validator.py (97%) rename deployability/modules/{workflow_engine => jobflow}/schemas/schema_v1.json (100%) rename deployability/modules/{workflow_engine => jobflow}/task.py (98%) create mode 100644 deployability/modules/jobflow/tests/TESTING-README.md rename deployability/modules/{workflow_engine => jobflow}/tests/conftest.py (69%) rename deployability/modules/{workflow_engine => jobflow}/tests/data/wf-ko-no-path-on-cleanup.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/tests/data/wf-ko-no-path-on-do.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/tests/data/wf-ko-schema-error.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/tests/data/wf-ok.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/tests/test_dag.py (97%) rename deployability/modules/{workflow_engine/tests/test_workflow_file.py => jobflow/tests/test_jobflow_file.py} (66%) rename deployability/modules/{workflow_engine/tests/test_workflow_processor.py => jobflow/tests/test_jobflow_processor.py} (79%) rename deployability/modules/{workflow_engine => jobflow}/tests/test_schema_validator.py (86%) rename deployability/modules/{workflow_engine => jobflow}/tests/test_task.py (95%) delete mode 100644 deployability/modules/workflow_engine/tests/TESTING-README.md diff --git a/.gitignore b/.gitignore index 4c6a71ac34..3f35aa49e9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ venv wazuh_testing.egg-info dist deployability/modules/build -deployability/modules/workflow_engine.egg-info +deployability/modules/jobflow.egg-info # Python bytecode files *.pyc diff --git a/deployability/README.MD b/deployability/README.MD index d605359bff..bbf390c634 100644 --- a/deployability/README.MD +++ b/deployability/README.MD @@ -35,27 +35,27 @@ git checkout {project-branch} pip3 install -r deployability/deps/requirements.txt ``` -4. Install the Workflow engine library and its launcher: +4. Install the JobFlow engine library and its launcher: While in wazuh-qa: ``` cd modules -pip3 uninstall -y workflow_engine && pip3 install . +pip3 uninstall -y jobflow && pip3 install . ``` 5. Test Fixture to Execute It will be necessary to create a fixture (yaml file) where the infrastructure, provisioning, and tests to be executed will be declared. -> Note: It is possible to find some fixture examples in deployability/modules/workflow_engine/examples/ +> Note: It is possible to find some fixture examples in deployability/modules/jobflow/examples/ Example: ``` version: 0.1 -description: This workflow is used to test agents' deployment for DDT1 PoC +description: This YAML is used to test agents' deployment for DDT1 PoC variables: agents-os: - linux-ubuntu-22.04-amd64 @@ -247,13 +247,13 @@ depends-on Execute the command by referencing the parameters required by the library (launcher). ``` -python3 -m workflow_engine {.yaml fixture path} +python3 -m jobflow {.yaml fixture path} ``` Example ``` -python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml +python3 -m jobflow modules/jobflow/examples/dtt1-agents-poc.yaml ``` @@ -263,11 +263,11 @@ python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml - Modules -The framework has 4 modules (`allocation`, `provision`, `testing`, `observability`) that must act consecutively, and a 5th module that orchestrates the previous modules (`Workflow`). +The framework has 4 modules (`allocation`, `provision`, `testing`, `observability`) that must act consecutively, and a 5th module that orchestrates the previous modules (`Jobflow`). |Module|Description |--|--| -|Workflow (Orchestrator)|Receives a YAML containing all the instructions to execute for the test development. It contains instructions for allocation, provision, and testing. +|JobFlow (Orchestrator)|Receives a YAML containing all the instructions to execute for the test development. It contains instructions for allocation, provision, and testing. |Allocation| Receives instructions for the desired architecture and creates the structures, generating IPs, and ports either in AWS or locally with Vagrant. |Provision| Installs applications on the structures created in allocation. |Testing| Executes tests on the previously defined structures and triggers actions depending on the test. @@ -285,7 +285,7 @@ wazuh-qa/ │ ├── generic │ ├── provision │ ├── testing - │ └── workflow_engine + │ └── jobflow └── plugins ``` Deployability contains the following directories: @@ -299,18 +299,18 @@ Deployability contains the following directories: #### Overview -![image](https://github.com/wazuh/wazuh-qa/assets/125690423/d09dfa61-67cb-410d-8677-bd904d5c4d51) +![image](https://github.com/wazuh/wazuh-qa/assets/2949519/29b600cf-44bd-412c-b02f-9b08353babde) #### Allocator, provision & test process -![image](https://github.com/wazuh/wazuh-qa/assets/125690423/b1e24a43-13d7-4035-a3e5-5ace4667ec10) +![image](https://github.com/wazuh/wazuh-qa/assets/2949519/9338ccc4-2ffa-47d4-b4cc-86c2a8aa5851) ---- -[draw-plot.zip](https://github.com/wazuh/wazuh-qa/files/14330528/plot.zip) +[draw-plot.zip](https://github.com/user-attachments/files/15792115/draw-plot.zip) diff --git a/deployability/modules/allocation/README.MD b/deployability/modules/allocation/README.MD index d90929fdc0..0491a3764c 100644 --- a/deployability/modules/allocation/README.MD +++ b/deployability/modules/allocation/README.MD @@ -6,7 +6,7 @@ The Allocation module allows you to create and destroy VMs both locally and in A #### Set the environment -The execution of the allocation is carried out through the Workflow engine library, or by executing them manually through commands. +The execution of the allocation is carried out through the JobFlow engine, or by executing them manually through commands. Execution can be done from any operating system. Initially, you have to install the required Python libraries. We recommend using virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. @@ -46,30 +46,30 @@ To use this module, you should use a Debian-based system, we recommend using Ubu These commands must be available to run on the Host Operating System. -#### Use the Allocation module through the Workflow engine +#### Use the Allocation module through the JobFlow engine Now, it is possible to use the Worklow engine library to launch the provision module by doing the following steps: -1. Install the Workflow engine library and its launcher: +1. Install the JobFlow engine and its launcher: While in wazuh-qa: ```bash cd modules - pip3 uninstall -y workflow_engine && pip3 install . + pip3 uninstall -y jobflow && pip3 install . ``` 2. Test Fixture to Execute: It will be necessary to create a fixture (yaml file) where the infrastructure, provisioning, and tests to be executed will be declared. - >Note: It is possible to find some fixture examples in [deployability/modules/workflow_engine/examples/](../workflow_engine/examples) + >Note: It is possible to find some fixture examples in [deployability/modules/jobflow/examples/](../jobflow/examples) Example: ```bash version: 0.1 - description: This workflow is used to test agents' deployment for DDT1 PoC + description: This YAML file is used to test agents' deployment for DDT1 PoC variables: agent-os: - linux-ubuntu-18.04-amd64 @@ -161,19 +161,19 @@ Now, it is possible to use the Worklow engine library to launch the provision mo Execute the command by referencing the parameters required by the library (launcher). ```bash - python3 -m workflow_engine {.yaml fixture path} + python3 -m jobflow {.yaml fixture path} ``` Example ```bash - python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml + python3 -m jobflow modules/jobflow/examples/dtt1-agents-poc.yaml ``` #### Manual execution of the Allocation module -If one wishes to execute the allocaation module without installing the Workflow engine, they can proceed by using the launcher ([module/allocation/main.py](main.py)): +If one wishes to execute the allocaation module without installing the JobFlow engine, they can proceed by using the launcher ([module/allocation/main.py](main.py)): 1. Create @@ -318,7 +318,7 @@ If one wishes to execute the allocaation module without installing the Workflow The allocation module allows creating infrastructure on both AWS and locally (using Vagrant). -Instructions can be initiated from the fixture and executed through the Workflow engine or executed using Python commands. +Instructions can be initiated from the fixture and executed through the JobFlow engine or executed using Python commands. In either case, the following information will be needed: @@ -350,7 +350,7 @@ python3 modules/allocation/main.py --action create --provider vagrant --size lar #### General-specific functions -- **Launcher** ([/wazuh-qa/deployability/modules/allocation/main.py](main.py)): The entry point for the workflow or the user who wishes to execute a test. +- **Launcher** ([/wazuh-qa/deployability/modules/allocation/main.py](main.py)): The entry point of the module. The command line or the JobFlow engine typically invokes this entry point. - **Module functions** ([/wazuh-qa/deployability/modules/allocation/allocation.py](allocation.py)): Module-specific functions responsible for triggering the allocation. @@ -373,10 +373,10 @@ python3 modules/allocation/main.py --action create --provider vagrant --size lar #### Diagram -![image](https://github.com/wazuh/wazuh-qa/assets/64099752/d302f96b-70c0-4fbc-869b-914973ee8186) +![image](https://github.com/wazuh/wazuh-qa/assets/2949519/c673b3ec-ac6a-4ca0-b625-9f98be422f20) -[Allocation.drawio.zip](https://github.com/wazuh/wazuh-qa/files/14868775/Allocation.drawio.zip) +[Allocation.drawio.zip](https://github.com/user-attachments/files/15792190/Allocation.drawio.zip) ### Known issues diff --git a/deployability/modules/generic/logger/config.yaml b/deployability/modules/generic/logger/config.yaml index dee20af028..f518ba9e7b 100644 --- a/deployability/modules/generic/logger/config.yaml +++ b/deployability/modules/generic/logger/config.yaml @@ -23,7 +23,7 @@ handlers: class: logging.FileHandler level: DEBUG formatter: simple - filename: /tmp/workflow.log + filename: /tmp/jobflow.log filters: [uppercase] loggers: allocator: diff --git a/deployability/modules/workflow_engine/README.MD b/deployability/modules/jobflow/README.MD similarity index 85% rename from deployability/modules/workflow_engine/README.MD rename to deployability/modules/jobflow/README.MD index c4430d97a4..c5c048be7c 100644 --- a/deployability/modules/workflow_engine/README.MD +++ b/deployability/modules/jobflow/README.MD @@ -1,8 +1,8 @@ -## Workflow engine +## JobFlow engine ### User documentation -The execution of the Workflow is done through the installation of its library. +The execution of the JobFlow engine is done through the installation of its library. Initially, Python libraries must be installed. It is recommended to use virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. @@ -29,26 +29,26 @@ To use this module, you should use a Debian-based system, we recommend using Ubu pip3 install -r deployability/deps/requirements.txt ``` -4. Install the Workflow engine library and its launcher: +4. Install the JobFlow engine library and its launcher: While in wazuh-qa: ```bash cd modules - pip3 uninstall -y workflow_engine && pip3 install . + pip3 uninstall -y jobflow && pip3 install . ``` 5. Test Fixture to Execute: - It will be necessary to create a fixture (yaml file) where the infrastructure, provisioning, and tests to be executed will be declared. + It will be necessary to create a fixture (YAML file) where the infrastructure, provisioning, and tests to be executed will be declared. - >Note: It is possible to find some fixture examples in deployability/modules/workflow_engine/examples/ + >Note: It is possible to find some fixture examples in deployability/modules/jobflow/examples/ Example: ```bash version: 0.1 - description: This workflow is used to test agents' deployment for DDT1 PoC + description: This YAML is used to test agents' deployment for DDT1 PoC variables: agents-os: - linux-ubuntu-22.04-amd64 @@ -240,13 +240,13 @@ To use this module, you should use a Debian-based system, we recommend using Ubu Execute the command by referencing the parameters required by the library (launcher). ```bash - python3 -m workflow_engine {.yaml fixture path} + python3 -m jobflow {.yaml fixture path} ``` Example ```bash - python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml + python3 -m jobflow modules/jobflow/examples/dtt1-agents-poc.yaml ``` > Note The command execution can also be mediated through Jenkins. @@ -255,12 +255,12 @@ To use this module, you should use a Debian-based system, we recommend using Ubu ### Technical documentation -`Workflow Engine` is the orchestrator of the deployability test architecture. +`JobFlow engine` is the orchestrator of the deployability test architecture. Its function is to allow the ordered and structured execution in steps of allocation, provision, and testing. -`The Workflow Engine` receives instructions through a `YAML document`, the structure of which can be exemplified in tests found in: -`wazuh-qa/deployability/modules/workflow_engine/examples` +`The JobFlow engine` receives instructions through a `YAML document`, the structure of which can be exemplified in tests found in: +`wazuh-qa/deployability/modules/jobflow/examples` **In these tests**: - Tasks: define the steps. @@ -303,19 +303,19 @@ tasks: as: agent ``` -These tasks are executed by the `Workflow Engine` launcher installed as workflow_engine library in your virtual environment. +These tasks are executed by the `JobFlow engine` launcher installed as jobflow library in your virtual environment. This launcher receives the parameters, sets up the test logs, and proceeds with the ordered execution. -The parameters sent from the launcher are processed by deployability/modules/workflow_engine/models.py, which checks the nature of the parameters sent and filters out incorrect parameters. +The parameters sent from the launcher are processed by deployability/modules/jobflow/models.py, which checks the nature of the parameters sent and filters out incorrect parameters. -![image](https://github.com/wazuh/wazuh-qa/assets/125690423/32aa77b7-f294-41ac-af93-db8a084dbad1) +![image](https://github.com/wazuh/wazuh-qa/assets/2949519/ee6231c2-9c97-4d9b-8c7d-af1cdce92d28) -These are then sent to `deployability/modules/workflow_engine/workflow_processor.py`, where using `deployability/modules/schemas`, instructions in YAML are received and the schema of the instructions is checked. +These are then sent to `deployability/modules/jobflow/jobflow_processor.py`, where using `deployability/modules/schemas`, instructions in YAML are received and the schema of the instructions is checked. -The commands are executed in the WorkflowProcessor of the same file, which also handles parallel executions and aborts failed executions. +The commands are executed in the JobFlowProcessor of the same file, which also handles parallel executions and aborts failed executions. -[WF.drawio.zip](https://github.com/wazuh/wazuh-qa/files/14167559/WF.drawio.zip) +[WF.drawio.zip](https://github.com/user-attachments/files/15792275/WF.drawio.zip) ### License diff --git a/deployability/modules/workflow_engine/__init__.py b/deployability/modules/jobflow/__init__.py similarity index 77% rename from deployability/modules/workflow_engine/__init__.py rename to deployability/modules/jobflow/__init__.py index 39cf4553b9..2b2bf8a414 100755 --- a/deployability/modules/workflow_engine/__init__.py +++ b/deployability/modules/jobflow/__init__.py @@ -1,4 +1,4 @@ # Copyright (C) 2015, Wazuh Inc. # Created by Wazuh, Inc. . # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 -from .workflow_processor import WorkflowProcessor +from .jobflow_processor import JobFlowProcessor diff --git a/deployability/modules/workflow_engine/__main__.py b/deployability/modules/jobflow/__main__.py similarity index 81% rename from deployability/modules/workflow_engine/__main__.py rename to deployability/modules/jobflow/__main__.py index 6a0a78fdc8..aa199a9b86 100755 --- a/deployability/modules/workflow_engine/__main__.py +++ b/deployability/modules/jobflow/__main__.py @@ -9,14 +9,14 @@ project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')) sys.path.append(project_root) -from workflow_engine.workflow_processor import WorkflowProcessor -from workflow_engine.models import InputPayload +from jobflow.jobflow_processor import JobFlowProcessor +from jobflow.models import InputPayload def parse_arguments() -> argparse.Namespace: """Parse command line arguments.""" - parser = argparse.ArgumentParser(description='Execute tasks in a workflow.') - parser.add_argument('workflow_file', type=str,help='Path to the workflow file (YAML format).') + parser = argparse.ArgumentParser(description='Execute tasks in a JobFlow.') + parser.add_argument('jobflow_file', type=str,help='Path to the workflow file (YAML format).') parser.add_argument('--threads', type=int, default=1, required=False, help='Number of threads to use for parallel execution.') parser.add_argument('--dry-run', action='store_true', required=False, help='Display the plan without executing tasks.') parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], default='INFO', @@ -28,7 +28,7 @@ def main() -> None: """Main entry point.""" try: args = parse_arguments() - processor = WorkflowProcessor(**dict(InputPayload(**vars(args)))) + processor = JobFlowProcessor(**dict(InputPayload(**vars(args)))) signal.signal(signal.SIGINT, processor.handle_interrupt) processor.run() except Exception as e: diff --git a/deployability/modules/workflow_engine/examples/agent/aws/Jenkins-agents-test.yaml b/deployability/modules/jobflow/examples/agent/aws/Jenkins-agents-test.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/Jenkins-agents-test.yaml rename to deployability/modules/jobflow/examples/agent/aws/Jenkins-agents-test.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml b/deployability/modules/jobflow/examples/agent/aws/test-agent-basic-info.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/test-agent-basic-info.yaml rename to deployability/modules/jobflow/examples/agent/aws/test-agent-basic-info.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml b/deployability/modules/jobflow/examples/agent/aws/test-agent-complete-macOS.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete-macOS.yaml rename to deployability/modules/jobflow/examples/agent/aws/test-agent-complete-macOS.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml b/deployability/modules/jobflow/examples/agent/aws/test-agent-complete.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/test-agent-complete.yaml rename to deployability/modules/jobflow/examples/agent/aws/test-agent-complete.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml b/deployability/modules/jobflow/examples/agent/aws/test-agent-restart-ins-prov.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml rename to deployability/modules/jobflow/examples/agent/aws/test-agent-restart-ins-prov.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml b/deployability/modules/jobflow/examples/agent/aws/test-agent-stop-ins-prov.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml rename to deployability/modules/jobflow/examples/agent/aws/test-agent-stop-ins-prov.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml b/deployability/modules/jobflow/examples/agent/aws/test-agent-suse.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/test-agent-suse.yaml rename to deployability/modules/jobflow/examples/agent/aws/test-agent-suse.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-time.yaml b/deployability/modules/jobflow/examples/agent/aws/test-agent-time.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/test-agent-time.yaml rename to deployability/modules/jobflow/examples/agent/aws/test-agent-time.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml b/deployability/modules/jobflow/examples/agent/aws/test-agent-uninstall-ins-prov.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml rename to deployability/modules/jobflow/examples/agent/aws/test-agent-uninstall-ins-prov.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml b/deployability/modules/jobflow/examples/agent/aws/test-agent-windows-complete.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml rename to deployability/modules/jobflow/examples/agent/aws/test-agent-windows-complete.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-full-agent-with-live.yaml b/deployability/modules/jobflow/examples/agent/aws/test-full-agent-with-live.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/aws/test-full-agent-with-live.yaml rename to deployability/modules/jobflow/examples/agent/aws/test-full-agent-with-live.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml rename to deployability/modules/jobflow/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-1.yaml similarity index 96% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml rename to deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-1.yaml index aa4392a5ac..093ab81aba 100755 --- a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-1.yaml +++ b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-1.yaml @@ -30,6 +30,7 @@ tasks: - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + - instance-name: "manager" on-error: "abort-all" cleanup: this: process @@ -57,6 +58,7 @@ tasks: - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" - label-team: "qa" + - instance-name: "agent-instance-{agent}" on-error: "abort-all" foreach: - variable: agent-os @@ -86,7 +88,7 @@ tasks: - component: wazuh-manager type: assistant version: 4.7.4 - live: False + live: True depends-on: - "allocate-manager-{manager-os}" on-error: "abort-all" @@ -108,7 +110,7 @@ tasks: - component: "agent" - wazuh-version: "4.7.4" - wazuh-revision: "40717" - - live: False + - live: True foreach: - variable: agent-os as: agent diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-2.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-2.yaml rename to deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-2.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-macOS.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-complete-macOS.yaml rename to deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-macOS.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml rename to deployability/modules/jobflow/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml rename to deployability/modules/jobflow/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml rename to deployability/modules/jobflow/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml rename to deployability/modules/jobflow/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml rename to deployability/modules/jobflow/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml rename to deployability/modules/jobflow/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml diff --git a/deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-windows-complete.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/agent/vagrant/test-agent-windows-complete.yaml rename to deployability/modules/jobflow/examples/agent/vagrant/test-agent-windows-complete.yaml diff --git a/deployability/modules/workflow_engine/examples/central_components/aws/Jenkins-central-component-test.yaml b/deployability/modules/jobflow/examples/central_components/aws/Jenkins-central-component-test.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/central_components/aws/Jenkins-central-component-test.yaml rename to deployability/modules/jobflow/examples/central_components/aws/Jenkins-central-component-test.yaml diff --git a/deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml b/deployability/modules/jobflow/examples/central_components/aws/dtt1-central_components-poc-aws.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/central_components/aws/dtt1-central_components-poc-aws.yaml rename to deployability/modules/jobflow/examples/central_components/aws/dtt1-central_components-poc-aws.yaml diff --git a/deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml b/deployability/modules/jobflow/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml rename to deployability/modules/jobflow/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml diff --git a/deployability/modules/workflow_engine/examples/manager/aws/Jenkins-manager-test.yaml b/deployability/modules/jobflow/examples/manager/aws/Jenkins-manager-test.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/manager/aws/Jenkins-manager-test.yaml rename to deployability/modules/jobflow/examples/manager/aws/Jenkins-manager-test.yaml diff --git a/deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml b/deployability/modules/jobflow/examples/manager/aws/dtt1-managers-poc-aws.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml rename to deployability/modules/jobflow/examples/manager/aws/dtt1-managers-poc-aws.yaml diff --git a/deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/jobflow/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml rename to deployability/modules/jobflow/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml diff --git a/deployability/modules/workflow_engine/workflow_processor.py b/deployability/modules/jobflow/jobflow_processor.py similarity index 91% rename from deployability/modules/workflow_engine/workflow_processor.py rename to deployability/modules/jobflow/jobflow_processor.py index 85635aa6c9..27941e8a23 100755 --- a/deployability/modules/workflow_engine/workflow_processor.py +++ b/deployability/modules/jobflow/jobflow_processor.py @@ -11,31 +11,31 @@ from pathlib import Path from itertools import product -from workflow_engine.logger.logger import logger -from workflow_engine.schema_validator import SchemaValidator -from workflow_engine.task import Task, TASKS_HANDLERS +from jobflow.logger.logger import logger +from jobflow.schema_validator import SchemaValidator +from jobflow.task import Task, TASKS_HANDLERS -class WorkflowFile: - """Class for loading and processing a workflow file.""" +class JobFlowFile: + """Class for loading and processing a JobFlow workflow file.""" schema_path = Path(__file__).parent / 'schemas' / 'schema_v1.json' - def __init__(self, workflow_file_path: Path | str, schema_path: Path | str = None) -> None: + def __init__(self, jobflow_file_path: Path | str, schema_path: Path | str = None) -> None: self.schema_path = schema_path or self.schema_path - self.__validate_schema(workflow_file_path) - self.workflow_raw_data = self.__load_workflow(workflow_file_path) + self.__validate_schema(jobflow_file_path) + self.jobflow_raw_data = self.__load_workflow(jobflow_file_path) self.task_collection = self.__process_workflow() self.__static_workflow_validation() - def __validate_schema(self, workflow_file: Path | str) -> None: + def __validate_schema(self, jobflow_file: Path | str) -> None: """ Validate the workflow file against the schema. Args: - workflow_file (Path | str): Path to the workflow file. + jobflow_file (Path | str): Path to the workflow file. """ try: - logger.debug(f"Validating input file: {workflow_file}") - validator = SchemaValidator(self.schema_path, workflow_file) + logger.debug(f"Validating input file: {jobflow_file}") + validator = SchemaValidator(self.schema_path, jobflow_file) validator.preprocess_data() validator.validateSchema() except Exception as e: @@ -50,7 +50,7 @@ def __load_workflow(self, file_path: str) -> dict: file_path (str): Path to the workflow file. Returns: - dict: Workflow data. + dict: workflow data. """ if not os.path.exists(file_path): @@ -65,8 +65,8 @@ def __process_workflow(self): """Process the workflow and return a list of tasks.""" logger.debug("Process workflow.") task_collection = [] - variables = self.workflow_raw_data.get('variables', {}) - for task in self.workflow_raw_data.get('tasks', []): + variables = self.jobflow_raw_data.get('variables', {}) + for task in self.jobflow_raw_data.get('tasks', []): task_collection.extend(self.__expand_task(task, variables)) if not task_collection: @@ -261,15 +261,15 @@ def get_subtask_plan(task_name: str, dependency_dict: dict, level: int = 0) -> d return execution_plan -class WorkflowProcessor: +class JobFlowProcessor: """Class for processing a workflow.""" - def __init__(self, workflow_file: str, dry_run: bool, threads: int, log_level: str = 'INFO', schema_file: Path | str = None): + def __init__(self, jobflow_file: str, dry_run: bool, threads: int, log_level: str = 'INFO', schema_file: Path | str = None): """ - Initialize WorkflowProcessor. + Initialize JobFlowProcessor. Args: - workflow_file (str): Path to the workflow file (YAML format). + jobflow_file (str): Path to the workflow file (YAML format). dry_run (bool): Display the plan without executing tasks. threads (int): Number of threads to use for parallel execution. log_level (str): Log level. @@ -277,7 +277,7 @@ def __init__(self, workflow_file: str, dry_run: bool, threads: int, log_level: s """ logger.setLevel(log_level) # Initialize the instance variables. - self.task_collection = WorkflowFile(workflow_file, schema_file).task_collection + self.task_collection = JobFlowFile(jobflow_file, schema_file).task_collection self.dry_run = dry_run self.threads = threads @@ -366,7 +366,7 @@ def run(self) -> None: except Exception as e: - logger.error("Error in Workflow: %s", e) + logger.error("Error in workflow: %s", e) def handle_interrupt(self, signum, frame): diff --git a/deployability/modules/workflow_engine/logger/__init__.py b/deployability/modules/jobflow/logger/__init__.py similarity index 100% rename from deployability/modules/workflow_engine/logger/__init__.py rename to deployability/modules/jobflow/logger/__init__.py diff --git a/deployability/modules/workflow_engine/logger/config.yaml b/deployability/modules/jobflow/logger/config.yaml similarity index 96% rename from deployability/modules/workflow_engine/logger/config.yaml rename to deployability/modules/jobflow/logger/config.yaml index e4c858047a..b000b1afbb 100644 --- a/deployability/modules/workflow_engine/logger/config.yaml +++ b/deployability/modules/jobflow/logger/config.yaml @@ -25,7 +25,7 @@ handlers: class: logging.FileHandler level: DEBUG formatter: simple - filename: /tmp/workflow.log + filename: /tmp/jobflow.log root: level: DEBUG handlers: [console, file] diff --git a/deployability/modules/workflow_engine/logger/filter.py b/deployability/modules/jobflow/logger/filter.py similarity index 100% rename from deployability/modules/workflow_engine/logger/filter.py rename to deployability/modules/jobflow/logger/filter.py diff --git a/deployability/modules/workflow_engine/logger/logger.py b/deployability/modules/jobflow/logger/logger.py similarity index 92% rename from deployability/modules/workflow_engine/logger/logger.py rename to deployability/modules/jobflow/logger/logger.py index 0a1b0b4809..eb083c3f63 100644 --- a/deployability/modules/workflow_engine/logger/logger.py +++ b/deployability/modules/jobflow/logger/logger.py @@ -21,4 +21,4 @@ def _load_config() -> None: _load_config() -logger = logging.getLogger("workflow_engine") +logger = logging.getLogger("jobflow") diff --git a/deployability/modules/workflow_engine/models.py b/deployability/modules/jobflow/models.py similarity index 93% rename from deployability/modules/workflow_engine/models.py rename to deployability/modules/jobflow/models.py index 5892028b67..655a1282dc 100644 --- a/deployability/modules/workflow_engine/models.py +++ b/deployability/modules/jobflow/models.py @@ -7,7 +7,7 @@ class InputPayload(BaseModel): - workflow_file: str | Path + jobflow_file: str | Path threads: int = 1 dry_run: bool = False log_level: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] = 'INFO' diff --git a/deployability/modules/workflow_engine/requirements-dev.txt b/deployability/modules/jobflow/requirements-dev.txt similarity index 100% rename from deployability/modules/workflow_engine/requirements-dev.txt rename to deployability/modules/jobflow/requirements-dev.txt diff --git a/deployability/modules/workflow_engine/schema_validator.py b/deployability/modules/jobflow/schema_validator.py similarity index 97% rename from deployability/modules/workflow_engine/schema_validator.py rename to deployability/modules/jobflow/schema_validator.py index 7ac2639fc9..cf0884f512 100755 --- a/deployability/modules/workflow_engine/schema_validator.py +++ b/deployability/modules/jobflow/schema_validator.py @@ -9,7 +9,7 @@ from pathlib import Path from ruamel.yaml import YAML -from workflow_engine.logger.logger import logger +from jobflow.logger.logger import logger class SchemaValidator: """ @@ -75,7 +75,7 @@ def preprocess_data(self) -> None: def validateSchema(self) -> None: """ - Validate the Workflow schema + Validate the JobFlow schema Raises: ValidationError: If the YAML data is not valid. diff --git a/deployability/modules/workflow_engine/schemas/schema_v1.json b/deployability/modules/jobflow/schemas/schema_v1.json similarity index 100% rename from deployability/modules/workflow_engine/schemas/schema_v1.json rename to deployability/modules/jobflow/schemas/schema_v1.json diff --git a/deployability/modules/workflow_engine/task.py b/deployability/modules/jobflow/task.py similarity index 98% rename from deployability/modules/workflow_engine/task.py rename to deployability/modules/jobflow/task.py index d076b90b53..bdf973bf53 100755 --- a/deployability/modules/workflow_engine/task.py +++ b/deployability/modules/jobflow/task.py @@ -6,7 +6,7 @@ import time from abc import ABC, abstractmethod -from workflow_engine.logger.logger import logger +from jobflow.logger.logger import logger class Task(ABC): """Abstract base class for tasks.""" diff --git a/deployability/modules/jobflow/tests/TESTING-README.md b/deployability/modules/jobflow/tests/TESTING-README.md new file mode 100644 index 0000000000..6ecb28a9c8 --- /dev/null +++ b/deployability/modules/jobflow/tests/TESTING-README.md @@ -0,0 +1,167 @@ +# JobFlow engine Unit Testing using Pytest + +The jobflow module includes pytest unit tests. + +## Requirements + +- Make sure you have Python installed on your system. You can download it from + [python.org](https://www.python.org/downloads/). +- Clone the wazuh-qa repository in your local environment. +- Install the necessary dependencies by running: +```bash +git clone https://github.com/wazuh/wazuh-qa.git -b [your-branch] +cd wazuh-qa +pip install -r deployability/modules/jobflow/requirements-dev.txt +``` +- Configure the `PYTHONPATH` variable with the full path to the directory `deployability/modules`, for example if you've +cloned the `wazuh-qa` repository into `/wazuh/wazuh-qa`, configure the `PYTHONPATH` in this way: +```bash +> pwd +/wazuh/wazuh-qa +> export PYTHONPATH=$PYTHONPATH:$PWD/deployability/modules +> echo $PYTHONPATH +/wazuh/wazuh-qa/deployability/modules +``` + +## Test Structure +The directory `deployability/modules/jobflow/tests/` contains the unit test files for the `jobflow` +module. + +## Running Tests +To run the tests, make sure that your system meets the requirements by executing the following command from the project +root: + +```bash +pytest -vv deployability/modules/jobflow +``` +This command will run all tests in the `tests/` directory. Using additional arguments, You can also run specific tests +or directories. The output of this command looks like this: +```bash +pytest -vv deployability/modules/jobflow +============================================================================================== test session starts ============================================================================================== +platform linux -- Python 3.10.13, pytest-7.1.2, pluggy-1.3.0 -- /usr/local/bin/python3 +cachedir: .pytest_cache +metadata: {'Python': '3.10.13', 'Platform': 'Linux-5.15.146.1-microsoft-standard-WSL2-x86_64-with-glibc2.31', 'Packages': {'pytest': '7.1.2', 'pluggy': '1.3.0'}, 'Plugins': {'anyio': '4.2.0', 'testinfra': '5.0.0', 'metadata': '3.0.0', 'html': '3.1.1'}} +rootdir: /home/marcelo/wazuh/wazuh-qa/deployability/modules +plugins: anyio-4.2.0, testinfra-5.0.0, metadata-3.0.0, html-3.1.1 +collected 92 items + +deployability/modules/jobflow/tests/test_dag.py::test_dag_constructor[True] PASSED [ 1%] +deployability/modules/jobflow/tests/test_dag.py::test_dag_constructor[False] PASSED [ 2%] +deployability/modules/jobflow/tests/test_dag.py::test_dag_is_active[True-dag0] PASSED [ 3%] +deployability/modules/jobflow/tests/test_dag.py::test_dag_is_active[True-dag1] PASSED [ 4%] +deployability/modules/jobflow/tests/test_dag.py::test_dag_is_active[False-dag0] PASSED [ 5%] +deployability/modules/jobflow/tests/test_dag.py::test_dag_is_active[False-dag1] PASSED [ 6%] +deployability/modules/jobflow/tests/test_dag.py::test_get_execution_plan[dag0] PASSED [ 7%] +deployability/modules/jobflow/tests/test_dag.py::test_set_status[task1-failed-dag0] PASSED [ 8%] +deployability/modules/jobflow/tests/test_dag.py::test_set_status[task1-canceled-dag0] PASSED [ 9%] +deployability/modules/jobflow/tests/test_dag.py::test_set_status[task1-successful-dag0] PASSED [ 10%] +deployability/modules/jobflow/tests/test_dag.py::test_set_status[task1-non_existing_status-dag0] FAILED [ 11%] +deployability/modules/jobflow/tests/test_dag.py::test_set_status[non_existing_task-successful-dag0] PASSED [ 13%] +deployability/modules/jobflow/tests/test_dag.py::test_set_status[non_existing_task-non_existing_status-dag0] FAILED [ 14%] +deployability/modules/jobflow/tests/test_dag.py::test_should_be_canceled[True-dag0] PASSED [ 15%] +deployability/modules/jobflow/tests/test_dag.py::test_should_be_canceled[False-dag0] PASSED [ 16%] +deployability/modules/jobflow/tests/test_dag.py::test_build_dag[dag0] PASSED [ 17%] +deployability/modules/jobflow/tests/test_dag.py::test_build_dag[dag1] PASSED [ 18%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-all-to_be_canceled0-dag0] PASSED [ 19%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-all-to_be_canceled0-dag1] PASSED [ 20%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-related-flows-to_be_canceled1-dag0] FAILED [ 21%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-related-flows-to_be_canceled1-dag1] FAILED [ 22%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task1-continue-to_be_canceled2-dag0] FAILED [ 23%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task1-continue-to_be_canceled2-dag1] FAILED [ 25%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-all-to_be_canceled3-dag0] FAILED [ 26%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-all-to_be_canceled3-dag1] FAILED [ 27%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-related-flows-to_be_canceled4-dag0] FAILED [ 28%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-related-flows-to_be_canceled4-dag1] FAILED [ 29%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task2-continue-to_be_canceled5-dag0] FAILED [ 30%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task2-continue-to_be_canceled5-dag1] FAILED [ 31%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-all-to_be_canceled6-dag0] PASSED [ 32%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-all-to_be_canceled6-dag1] PASSED [ 33%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-related-flows-to_be_canceled7-dag0] PASSED [ 34%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-related-flows-to_be_canceled7-dag1] PASSED [ 35%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task5-continue-to_be_canceled8-dag0] FAILED [ 36%] +deployability/modules/jobflow/tests/test_dag.py::test_cancel_dependant_tasks[task5-continue-to_be_canceled8-dag1] FAILED [ 38%] +deployability/modules/jobflow/tests/test_dag.py::test_create_execution_plan[dag0-exec_plan0] PASSED [ 39%] +deployability/modules/jobflow/tests/test_dag.py::test_create_execution_plan[dag1-exec_plan1] PASSED [ 40%] +deployability/modules/jobflow/tests/test_schema_validator.py::test_schema_validator_constructor[logger_mock0] PASSED [ 41%] +deployability/modules/jobflow/tests/test_schema_validator.py::test_schema_validator_constructor_ko PASSED [ 42%] +deployability/modules/jobflow/tests/test_schema_validator.py::test_preprocess_data PASSED [ 43%] +deployability/modules/jobflow/tests/test_schema_validator.py::test_preprocess_data_ko[wf-ko-no-path-on-do.yaml-Missing required properties in 'with' for task: {'task': 'run-agent-tests-{agent}'] PASSED [ 44%] +deployability/modules/jobflow/tests/test_schema_validator.py::test_preprocess_data_ko[wf-ko-no-path-on-cleanup.yaml-Missing required properties in 'with' for task: {'task': 'allocate-manager'] PASSED [ 45%] +deployability/modules/jobflow/tests/test_schema_validator.py::test_validate_schema PASSED [ 46%] +deployability/modules/jobflow/tests/test_schema_validator.py::test_validate_schema_ko[logger_mock0] PASSED [ 47%] +deployability/modules/jobflow/tests/test_task.py::test_process_task_constructor[task0] PASSED [ 48%] +deployability/modules/jobflow/tests/test_task.py::test_process_task_execute[task0] PASSED [ 50%] +deployability/modules/jobflow/tests/test_task.py::test_process_task_execute[task1] PASSED [ 51%] +deployability/modules/jobflow/tests/test_task.py::test_process_task_execute[task2] PASSED [ 52%] +deployability/modules/jobflow/tests/test_task.py::test_process_task_execute[task3] PASSED [ 53%] +deployability/modules/jobflow/tests/test_task.py::test_process_task_execute[task4] PASSED [ 54%] +deployability/modules/jobflow/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc0-1-task0] PASSED [ 55%] +deployability/modules/jobflow/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc0-0-task0] PASSED [ 56%] +deployability/modules/jobflow/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc1-1-task0] PASSED [ 57%] +deployability/modules/jobflow/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc1-0-task0] PASSED [ 58%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_constructor PASSED [ 59%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_validate_schema[logger_mock0] PASSED [ 60%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_validate_schema_ko[logger_mock0] PASSED [ 61%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_load_jobflow[logger_mock0] PASSED [ 63%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_load_jobflow_ko[logger_mock0] PASSED [ 64%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_process_jobflow[logger_mock0] PASSED [ 65%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_process_jobflow_ok[logger_mock0] PASSED [ 66%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_replace_placeholder[element0-values0-return_value0] PASSED [ 67%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_replace_placeholder[element1-values1-return_value1] PASSED [ 68%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_replace_placeholder[string_element {value}-values2-string_element value] PASSED [ 69%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_replace_placeholder[element3-None-return_value3] PASSED [ 70%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_expand_task[task0-return_value0-variables0] PASSED [ 71%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_expand_task[task1-return_value1-variables1] PASSED [ 72%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_static_jobflow_validation PASSED [ 73%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_static_jobflow_validation_ko[task_collection0-Duplicated task names: task 1] PASSED [ 75%] +deployability/modules/jobflow/tests/test_jobflow_file.py::test_jobflow_file_static_jobflow_validation_ko[task_collection1-Tasks do not exist: task 3, task 4] PASSED [ 76%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_jobflow_processor_constructor[jobflow.yaml-False-1-info-schema.yaml] PASSED [ 77%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_jobflow_processor_constructor[jobflow.yaml-True-1-debug-schema.yaml] PASSED [ 78%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_jobflow_processor_constructor[jobflow.yaml-True-1-debug-None] PASSED [ 79%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_execute_task[logger_mock0-w_processor0-dag0-custom_action-True] PASSED [ 80%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_execute_task[logger_mock1-w_processor1-dag1-custom_action-False] PASSED [ 81%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_execute_task_ko[logger_mock0-w_processor0-dag0-KeyboardInterrupt-None] PASSED [ 82%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_execute_task_ko[logger_mock0-w_processor0-dag0-KeyboardInterrupt-abort-all] PASSED [ 83%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_execute_task_ko[logger_mock1-w_processor1-dag1-Exception-None] PASSED [ 84%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_execute_task_ko[logger_mock1-w_processor1-dag1-Exception-abort-all] PASSED [ 85%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_create_task_object[w_processor0-process] PASSED [ 86%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_create_task_object[w_processor0-dummy] PASSED [ 88%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_create_task_object[w_processor0-dummy-random] PASSED [ 89%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_create_task_object_ko[w_processor0] PASSED [ 90%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_execute_tasks_parallel[logger_mock0-w_processor0-dag0-False] PASSED [ 91%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_execute_tasks_parallel[logger_mock0-w_processor0-dag0-True] PASSED [ 92%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_execute_tasks_parallel_ko[logger_mock0-w_processor0-dag0-False] PASSED [ 93%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_execute_tasks_parallel_ko[logger_mock0-w_processor0-dag0-True] PASSED [ 94%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_generate_futures[w_processor0] PASSED [ 95%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_generate_futures_reverse[w_processor0] PASSED [ 96%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_run[logger_mock0-w_processor0-False] PASSED [ 97%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_run[logger_mock0-w_processor0-True] PASSED [ 98%] +deployability/modules/jobflow/tests/test_jobflow_processor.py::test_handle_interrupt[logger_mock0-w_processor0] PASSED [100%] + +=================================================================================================== FAILURES ==================================================================================================== +``` + +The `.github/jobflow/jobflow-engine-unit-tests.yaml` automatically runs the unit tests in the GitHub environment. +The run results are in the `checks` tab or your GitHub pull request. + +## Relevant Files +- `tests/test_[test name].py`: all the unit test files start with a `test_` prefix. There is one unit test file for + each tested class. +- `tests/conftest.py`: contains the fixtures used throughout the unit tests. + +## Unit test development guidelines and recommendations +- Use Python coding style standards and recommendations to develop unit tests: snake case for all variable and function + names, maximum line length of 120 characters, two empty lines must separate each function, typing all your functions + and return values, create Docstring for all functions with numpy style. +- Develop unit tests for each function or method of the module. +- Error flows are usually created in a second unit test with the suffix `_ko`. For example, the + `test_process_task_execute` found in the `deployability/modules/jobflow/tests/test_jobflow_processor` is the + unit test normal flow for the `JobFlowProcessor.process_task_execute` method. The + `JobFlowProcessor.process_task_execute_ko` unit test implements the error flow. +- Use the pytest's decorator `@pytest.mark.parametrize` to implement test cases for the same unit test. +- Mock the object instance and functions used by your tested function using the `unitest.mock.patch` and + `unitest.mock.patch.object` functions or decorators. +- Try to factorize your testing code using `pytest.fixtures`. The shared fixtures are in the `conftest.py` file. In + many unit tests of this project, the fixtures implement a `request` object that receives parameters from the + `pytest.mark.parametrize`. diff --git a/deployability/modules/workflow_engine/tests/conftest.py b/deployability/modules/jobflow/tests/conftest.py similarity index 69% rename from deployability/modules/workflow_engine/tests/conftest.py rename to deployability/modules/jobflow/tests/conftest.py index 5de92b5222..6e1f1ac1b5 100644 --- a/deployability/modules/workflow_engine/tests/conftest.py +++ b/deployability/modules/jobflow/tests/conftest.py @@ -7,7 +7,7 @@ from unittest.mock import patch, MagicMock import pytest -from workflow_engine.workflow_processor import DAG, WorkflowProcessor +from jobflow.jobflow_processor import DAG, JobFlowProcessor DEFAULT_TASK_COLLECTION = [ {'task': 'task1', 'path': '/cmd1', 'args': [{"param1": "value1"}]}, @@ -19,7 +19,7 @@ @pytest.fixture def logger_mock(request) -> MagicMock: """Fixture to mock common logger methods.""" - logger_to_patch = request.param.get('logger_to_patch', "workflow_engine.workflow_processor.logger") + logger_to_patch = request.param.get('logger_to_patch', "jobflow.jobflow_processor.logger") with patch(logger_to_patch) as l_mock: patch.object(l_mock, 'warning') patch.object(l_mock, 'info') @@ -39,9 +39,9 @@ def dag(request) -> DAG: gl_dag = graphlib.TopologicalSorter() dep_dict = {'task1': 'task2'} with patch.object(gl_dag, 'prepare'), \ - patch('workflow_engine.workflow_processor.DAG._DAG__build_dag', + patch('jobflow.jobflow_processor.DAG._DAG__build_dag', return_value=(gl_dag, dep_dict)), \ - patch('workflow_engine.workflow_processor.DAG._DAG__create_execution_plan', + patch('jobflow.jobflow_processor.DAG._DAG__create_execution_plan', return_value=execution_plan_dict): ret_dag = DAG(task_collection=task_collection, reverse=reverse) else: @@ -54,22 +54,22 @@ def dag(request) -> DAG: @pytest.fixture -def w_processor(request) -> WorkflowProcessor: - """Create a mocked WorkflowProcessor instance.""" +def w_processor(request) -> JobFlowProcessor: + """Create a mocked JobFlowProcessor instance.""" - workflow_file = request.param.get('workflow_file', 'workflow.yaml') + jobflow_file = request.param.get('jobflow_file', 'jobflow.yaml') dry_run = request.param.get('dry_run', False) threads = request.param.get('threads', 1) log_level = request.param.get('log_level', 'info') schema_file = request.param.get('schema_file', 'schema.yaml') - with patch("workflow_engine.workflow_processor.WorkflowFile") as file_mock: - workflow_file_instance = file_mock.return_value - workflow_file_instance.task_collection = request.param.get('task_collection', DEFAULT_TASK_COLLECTION) + with patch("jobflow.jobflow_processor.JobFlowFile") as file_mock: + jobflow_file_instance = file_mock.return_value + jobflow_file_instance.task_collection = request.param.get('task_collection', DEFAULT_TASK_COLLECTION) if request.param.get('patch', True): - with patch('workflow_engine.workflow_processor.logger.setLevel'): - processor = WorkflowProcessor(workflow_file, dry_run, threads, + with patch('jobflow.jobflow_processor.logger.setLevel'): + processor = JobFlowProcessor(jobflow_file, dry_run, threads, log_level, schema_file) else: - processor = WorkflowProcessor(workflow_file, dry_run, + processor = JobFlowProcessor(jobflow_file, dry_run, threads, log_level, schema_file) return processor diff --git a/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml b/deployability/modules/jobflow/tests/data/wf-ko-no-path-on-cleanup.yaml similarity index 100% rename from deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml rename to deployability/modules/jobflow/tests/data/wf-ko-no-path-on-cleanup.yaml diff --git a/deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml b/deployability/modules/jobflow/tests/data/wf-ko-no-path-on-do.yaml similarity index 100% rename from deployability/modules/workflow_engine/tests/data/wf-ko-no-path-on-do.yaml rename to deployability/modules/jobflow/tests/data/wf-ko-no-path-on-do.yaml diff --git a/deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml b/deployability/modules/jobflow/tests/data/wf-ko-schema-error.yaml similarity index 100% rename from deployability/modules/workflow_engine/tests/data/wf-ko-schema-error.yaml rename to deployability/modules/jobflow/tests/data/wf-ko-schema-error.yaml diff --git a/deployability/modules/workflow_engine/tests/data/wf-ok.yaml b/deployability/modules/jobflow/tests/data/wf-ok.yaml similarity index 100% rename from deployability/modules/workflow_engine/tests/data/wf-ok.yaml rename to deployability/modules/jobflow/tests/data/wf-ok.yaml diff --git a/deployability/modules/workflow_engine/tests/test_dag.py b/deployability/modules/jobflow/tests/test_dag.py similarity index 97% rename from deployability/modules/workflow_engine/tests/test_dag.py rename to deployability/modules/jobflow/tests/test_dag.py index b3c6ec181e..f6b00ae317 100644 --- a/deployability/modules/workflow_engine/tests/test_dag.py +++ b/deployability/modules/jobflow/tests/test_dag.py @@ -6,12 +6,12 @@ from unittest.mock import patch, MagicMock, call import pytest -from workflow_engine.workflow_processor import DAG +from jobflow.jobflow_processor import DAG @pytest.mark.parametrize("reverse", [True, False]) -@patch("workflow_engine.workflow_processor.DAG._DAG__build_dag") -@patch("workflow_engine.workflow_processor.DAG._DAG__create_execution_plan") +@patch("jobflow.jobflow_processor.DAG._DAG__build_dag") +@patch("jobflow.jobflow_processor.DAG._DAG__create_execution_plan") def test_dag_constructor(create_exec_plan_mock: MagicMock, build_dag_mock: MagicMock, reverse: bool): """Test ProcessTask constructor Check all the dag object state after initialization and if the private dag methods are called during the instance @@ -169,7 +169,7 @@ def test_build_dag(dag: DAG): dag : DAG DAG fixture defined in conftest.py with task_collection parameterized. """ - with patch('workflow_engine.workflow_processor.graphlib.TopologicalSorter.add') as mock_add: + with patch('jobflow.jobflow_processor.graphlib.TopologicalSorter.add') as mock_add: res_dag, res_dependency_dict = dag._DAG__build_dag() assert isinstance(res_dag, graphlib.TopologicalSorter) call_list = [] diff --git a/deployability/modules/workflow_engine/tests/test_workflow_file.py b/deployability/modules/jobflow/tests/test_jobflow_file.py similarity index 66% rename from deployability/modules/workflow_engine/tests/test_workflow_file.py rename to deployability/modules/jobflow/tests/test_jobflow_file.py index a3b411899e..ca811e18eb 100644 --- a/deployability/modules/workflow_engine/tests/test_workflow_file.py +++ b/deployability/modules/jobflow/tests/test_jobflow_file.py @@ -1,35 +1,35 @@ # Copyright (C) 2015-2021, Wazuh Inc. # Created by Wazuh, Inc. . # This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 -"""WorkflowFile unit tests.""" +"""JobFlowFile unit tests.""" from typing import Any, List from unittest.mock import patch, MagicMock, call, mock_open import pytest -from workflow_engine.workflow_processor import WorkflowFile +from jobflow.jobflow_processor import JobFlowFile -def test_workflow_file_constructor(): - """Test WorkflowFile constructor. +def test_jobflow_file_constructor(): + """Test JobFlowFile constructor. Check the function calls and instance variables after object creation.""" - with patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__validate_schema") as validate_mock, \ - patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__load_workflow", + with patch("jobflow.jobflow_processor.JobFlowFile._JobFlowFile__validate_schema") as validate_mock, \ + patch("jobflow.jobflow_processor.JobFlowFile._JobFlowFile__load_workflow", return_value={'data': 'data'}) as load_mock, \ - patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__process_workflow") as process_mock, \ - patch("workflow_engine.workflow_processor.WorkflowFile._WorkflowFile__static_workflow_validation") \ + patch("jobflow.jobflow_processor.JobFlowFile._JobFlowFile__process_workflow") as process_mock, \ + patch("jobflow.jobflow_processor.JobFlowFile._JobFlowFile__static_workflow_validation") \ as static_validation_mock: - wf = WorkflowFile(workflow_file_path='my_file.yaml', schema_path='my_schema.yaml') + wf = JobFlowFile(jobflow_file_path='my_file.yaml', schema_path='my_schema.yaml') assert wf.schema_path == 'my_schema.yaml' validate_mock.assert_called_once_with('my_file.yaml') load_mock.assert_called_once_with('my_file.yaml') - assert wf.workflow_raw_data == {'data': 'data'} + assert wf.jobflow_raw_data == {'data': 'data'} process_mock.assert_called_once() static_validation_mock.assert_called_once() @pytest.mark.parametrize('logger_mock', [{}], indirect=True) -def test_workflow_file_validate_schema(logger_mock: MagicMock): - """Test WorkflowFile.__validate_schema. +def test_jobflow_file_validate_schema(logger_mock: MagicMock): + """Test JobFlowFile.__validate_schema. Check debug messages and function called by the method. Parameters @@ -39,23 +39,23 @@ def test_workflow_file_validate_schema(logger_mock: MagicMock): """ wf = MagicMock() wf.schema_path = 'my_schema_path.yaml' - workflow_file = 'my_file_path.yaml' + jobflow_file = 'my_file_path.yaml' schema_validator = MagicMock() - with patch('workflow_engine.workflow_processor.SchemaValidator', + with patch('jobflow.jobflow_processor.SchemaValidator', return_value=schema_validator) as schema_validator_mock: with patch.object(schema_validator, 'preprocess_data') as preprocess_mock, \ patch.object(schema_validator, 'validateSchema') as validate_schema_mock: - WorkflowFile._WorkflowFile__validate_schema(self=wf, workflow_file=workflow_file) + JobFlowFile._JobFlowFile__validate_schema(self=wf, jobflow_file=jobflow_file) - logger_mock.debug.assert_called_once_with(f"Validating input file: {workflow_file}") - schema_validator_mock.assert_called_once_with(wf.schema_path, workflow_file) + logger_mock.debug.assert_called_once_with(f"Validating input file: {jobflow_file}") + schema_validator_mock.assert_called_once_with(wf.schema_path, jobflow_file) preprocess_mock.assert_called_once() validate_schema_mock.assert_called_once() @pytest.mark.parametrize('logger_mock', [{}], indirect=True) -def test_workflow_file_validate_schema_ko(logger_mock: MagicMock): - """Test WorkflowFile.__validate_schema error flow. +def test_jobflow_file_validate_schema_ko(logger_mock: MagicMock): + """Test JobFlowFile.__validate_schema error flow. Check logged messages and function calls of the method. Parameters @@ -65,14 +65,14 @@ def test_workflow_file_validate_schema_ko(logger_mock: MagicMock): """ wf = MagicMock() wf.schema_path = 'my_schema_path.yaml' - workflow_file = 'my_file_path.yaml' + jobflow_file = 'my_file_path.yaml' file_exc = FileNotFoundError() - with patch('workflow_engine.workflow_processor.SchemaValidator', side_effect=file_exc) as schema_validator_mock, \ + with patch('jobflow.jobflow_processor.SchemaValidator', side_effect=file_exc) as schema_validator_mock, \ pytest.raises(FileNotFoundError): - WorkflowFile._WorkflowFile__validate_schema(self=wf, workflow_file=workflow_file) + JobFlowFile._JobFlowFile__validate_schema(self=wf, jobflow_file=jobflow_file) - logger_mock.debug.assert_called_once_with(f"Validating input file: {workflow_file}") - schema_validator_mock.assert_called_once_with(wf.schema_path, workflow_file) + logger_mock.debug.assert_called_once_with(f"Validating input file: {jobflow_file}") + schema_validator_mock.assert_called_once_with(wf.schema_path, jobflow_file) logger_mock.error.assert_called_once_with("Error while validating schema [%s] with error: %s", wf.schema_path, file_exc) @@ -80,8 +80,8 @@ def test_workflow_file_validate_schema_ko(logger_mock: MagicMock): @pytest.mark.parametrize('logger_mock', [{}], indirect=True) @patch('builtins.open', new_callable=mock_open, read_data='YAML content') -def test_workflow_file_load_workflow(mock_open: MagicMock, logger_mock: MagicMock): - """Test WorkflowFile.__load_workflow. +def test_jobflow_file_load_jobflow(mock_open: MagicMock, logger_mock: MagicMock): + """Test JobFlowFile.__load_workflow. Check logged messages and function calls of the method. Parameters @@ -93,22 +93,22 @@ def test_workflow_file_load_workflow(mock_open: MagicMock, logger_mock: MagicMoc """ wf = MagicMock() wf.schema_path = 'my_schema_path.yaml' - workflow_file = 'my_file_path.yaml' + jobflow_file = 'my_file_path.yaml' mock_open.return_value.__enter__.return_value = mock_open - with patch('workflow_engine.workflow_processor.os.path.exists', return_value=True) as path_exists_mock, \ - patch('workflow_engine.workflow_processor.yaml.safe_load') as safe_load_mock: - WorkflowFile._WorkflowFile__load_workflow(self=wf, file_path=workflow_file) + with patch('jobflow.jobflow_processor.os.path.exists', return_value=True) as path_exists_mock, \ + patch('jobflow.jobflow_processor.yaml.safe_load') as safe_load_mock: + JobFlowFile._JobFlowFile__load_workflow(self=wf, file_path=jobflow_file) - path_exists_mock.assert_called_once_with(workflow_file) - logger_mock.debug.assert_called_once_with(f"Loading workflow file: {workflow_file}") - mock_open.assert_called_once_with(workflow_file, 'r', encoding='utf-8') + path_exists_mock.assert_called_once_with(jobflow_file) + logger_mock.debug.assert_called_once_with(f"Loading workflow file: {jobflow_file}") + mock_open.assert_called_once_with(jobflow_file, 'r', encoding='utf-8') safe_load_mock.assert_called_once_with(mock_open) @pytest.mark.parametrize('logger_mock', [{}], indirect=True) @patch('builtins.open', new_callable=mock_open, read_data='YAML content') -def test_workflow_file_load_workflow_ko(mock_open: MagicMock, logger_mock: MagicMock): - """Test WorkflowFile.__load_workflow error flow. +def test_jobflow_file_load_jobflow_ko(mock_open: MagicMock, logger_mock: MagicMock): + """Test JobFlowFile.__load_workflow error flow. Check if the FileNotFoundError exception is raised by the method. Parameters @@ -120,16 +120,16 @@ def test_workflow_file_load_workflow_ko(mock_open: MagicMock, logger_mock: Magic """ wf = MagicMock() wf.schema_path = 'my_schema_path.yaml' - workflow_file = 'my_file_path.yaml' + jobflow_file = 'my_file_path.yaml' mock_open.return_value.__enter__.return_value = mock_open - with patch('workflow_engine.workflow_processor.os.path.exists', return_value=False) as path_exists_mock, \ - pytest.raises(FileNotFoundError, match=f'File "{workflow_file}" not found.') as file_exc: - WorkflowFile._WorkflowFile__load_workflow(self=wf, file_path=workflow_file) + with patch('jobflow.jobflow_processor.os.path.exists', return_value=False) as path_exists_mock, \ + pytest.raises(FileNotFoundError, match=f'File "{jobflow_file}" not found.') as file_exc: + JobFlowFile._JobFlowFile__load_workflow(self=wf, file_path=jobflow_file) @pytest.mark.parametrize('logger_mock', [{}], indirect=True) -def test_workflow_file_process_workflow(logger_mock: MagicMock): - """Test WorkflowFile.__process_workflow. +def test_jobflow_file_process_jobflow(logger_mock: MagicMock): + """Test JobFlowFile.__process_workflow. Check that the method calls the expand_task method of each task using a lambda as a side effect. Parameters @@ -141,22 +141,22 @@ def test_workflow_file_process_workflow(logger_mock: MagicMock): task_list = [{'task': 'task1'}, {'task': 'task2'}, {'task': 'task3'}] expanded_task_list = [{'task': 'task3_1'}, {'task': 'task3_2'}] wf = MagicMock() - wf.workflow_raw_data = {'tasks': task_list, 'variables': variable_list} - wf._WorkflowFile__expand_task.side_effect = lambda task, variables: [task] + \ + wf.jobflow_raw_data = {'tasks': task_list, 'variables': variable_list} + wf._JobFlowFile__expand_task.side_effect = lambda task, variables: [task] + \ (expanded_task_list if task['task'] == 'task3' else []) - tasks = WorkflowFile._WorkflowFile__process_workflow(wf) + tasks = JobFlowFile._JobFlowFile__process_workflow(wf) logger_mock.debug.assert_called_once_with("Process workflow.") calls = [call(task, variable_list) for task in task_list] - wf._WorkflowFile__expand_task.assert_has_calls(calls) + wf._JobFlowFile__expand_task.assert_has_calls(calls) task_list.extend(expanded_task_list) assert tasks == task_list @pytest.mark.parametrize('logger_mock', [{}], indirect=True) -def test_workflow_file_process_workflow_ok(logger_mock: MagicMock): - """Test WorkflowFile.__process_workflow error flow. - Check that a ValueError is raised when no task are found in the workflow. +def test_jobflow_file_process_jobflow_ok(logger_mock: MagicMock): + """Test JobFlowFile.__process_workflow error flow. + Check that a ValueError is raised when no task are found in the JobFlow. Parameters ---------- @@ -164,12 +164,12 @@ def test_workflow_file_process_workflow_ok(logger_mock: MagicMock): The logger fixture defined in conftest.py """ wf = MagicMock() - wf.workflow_row_data = { + wf.jobflow_row_data = { 'tasks': [] } wf.__expand_task.return_value = [] with pytest.raises(ValueError, match="No tasks found in the workflow."): - tasks = WorkflowFile._WorkflowFile__process_workflow(self=wf) + tasks = JobFlowFile._JobFlowFile__process_workflow(self=wf) logger_mock.debug.assert_called_once_with("Process workflow.") @@ -183,12 +183,12 @@ def test_workflow_file_process_workflow_ok(logger_mock: MagicMock): ['element_1 value_1', 'element_2 value_2', 'element_3 value_3']), ('string_element {value}', {'value': 'value'}, 'string_element value'), ({1, 2}, None, {1, 2})]) -def test_workflow_file_replace_placeholder(element: Any, values: dict, return_value: Any): - """Test WorkflowFile.__replace_placeholder.""" +def test_jobflow_file_replace_placeholder(element: Any, values: dict, return_value: Any): + """Test JobFlowFile.__replace_placeholder.""" wf = MagicMock() - wf._WorkflowFile__replace_placeholders.side_effect = \ - lambda s, e, v: WorkflowFile._WorkflowFile__replace_placeholders(wf, s, e, v) - result = WorkflowFile._WorkflowFile__replace_placeholders(self=wf, element=element, values=values) + wf._JobFlowFile__replace_placeholders.side_effect = \ + lambda s, e, v: JobFlowFile._JobFlowFile__replace_placeholders(wf, s, e, v) + result = JobFlowFile._JobFlowFile__replace_placeholders(self=wf, element=element, values=values) assert result == return_value @@ -214,8 +214,8 @@ def test_workflow_file_replace_placeholder(element: Any, values: dict, return_va [{'task': 'task1', 'placeholder': 'placeholder value_1'}], {'variable_1': 'value_1'}) ]) -def test_workflow_file_expand_task(task: dict, return_value: dict, variables: dict): - """Test WorkflowFile.___expand_task. +def test_jobflow_file_expand_task(task: dict, return_value: dict, variables: dict): + """Test JobFlowFile.___expand_task. Check the if the expand_task return dictionary is ok. Parameters @@ -228,22 +228,22 @@ def test_workflow_file_expand_task(task: dict, return_value: dict, variables: di The variables dictionary used as the input parameter for the expand_task method. """ def side_effect(s, e, v = None): - return WorkflowFile._WorkflowFile__replace_placeholders(wf, s, e, v) + return JobFlowFile._JobFlowFile__replace_placeholders(wf, s, e, v) wf = MagicMock() - wf._WorkflowFile__replace_placeholders.side_effect = side_effect + wf._JobFlowFile__replace_placeholders.side_effect = side_effect - tasks = WorkflowFile._WorkflowFile__expand_task(wf, task, variables) + tasks = JobFlowFile._JobFlowFile__expand_task(wf, task, variables) assert tasks == return_value -def test_workflow_file_static_workflow_validation(): - """Test WorkflowFile.__static_workflow_validation. +def test_jobflow_file_static_jobflow_validation(): + """Test JobFlowFile.__static_workflow_validation. Check if no exception is raised with a valid task_collection""" wf = MagicMock() wf.task_collection = [{"task": "task 1", "param": "1"}, {"task": "task 2", "param": "2", 'depends-on': ['task 1']} ] - WorkflowFile._WorkflowFile__static_workflow_validation(wf) + JobFlowFile._JobFlowFile__static_workflow_validation(wf) @pytest.mark.parametrize('task_collection, error_msg', [ @@ -254,8 +254,8 @@ def test_workflow_file_static_workflow_validation(): {"task": "task 2", "param": "2", 'depends-on': ['task 3']}], 'Tasks do not exist: task 3, task 4') ]) -def test_workflow_file_static_workflow_validation_ko(task_collection: List[dict], error_msg: str): - """Test WorkflowFile.__static_workflow_validation. +def test_jobflow_file_static_jobflow_validation_ko(task_collection: List[dict], error_msg: str): + """Test JobFlowFile.__static_workflow_validation. Check if the validation raises ValueError exceptions with invalid task collections. Parameters @@ -268,4 +268,4 @@ def test_workflow_file_static_workflow_validation_ko(task_collection: List[dict] wf = MagicMock() wf.task_collection = task_collection with pytest.raises(ValueError, match=error_msg): - WorkflowFile._WorkflowFile__static_workflow_validation(wf) + JobFlowFile._JobFlowFile__static_workflow_validation(wf) diff --git a/deployability/modules/workflow_engine/tests/test_workflow_processor.py b/deployability/modules/jobflow/tests/test_jobflow_processor.py similarity index 79% rename from deployability/modules/workflow_engine/tests/test_workflow_processor.py rename to deployability/modules/jobflow/tests/test_jobflow_processor.py index a0639c6ea0..b5dce36fb3 100644 --- a/deployability/modules/workflow_engine/tests/test_workflow_processor.py +++ b/deployability/modules/jobflow/tests/test_jobflow_processor.py @@ -1,40 +1,40 @@ # Copyright (C) 2015-2021, Wazuh Inc. # Created by Wazuh, Inc. . # This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 -"""WorkflowProcessor Unit tests""" +"""JobFlowProcessor Unit tests""" import time import json from concurrent.futures import Future from unittest.mock import patch, MagicMock, call import pytest -from workflow_engine.workflow_processor import WorkflowProcessor, DAG -from workflow_engine.task import ProcessTask, TASKS_HANDLERS +from jobflow.jobflow_processor import JobFlowProcessor, DAG +from jobflow.task import ProcessTask, TASKS_HANDLERS -@pytest.mark.parametrize('workflow_file, dry_run, threads, log_level, schema_file', - [('workflow.yaml', False, 1, 'info', 'schema.yaml'), - ('workflow.yaml', True, 1, 'debug', 'schema.yaml'), - ('workflow.yaml', True, 1, 'debug', None), +@pytest.mark.parametrize('jobflow_file, dry_run, threads, log_level, schema_file', + [('jobflow.yaml', False, 1, 'info', 'schema.yaml'), + ('jobflow.yaml', True, 1, 'debug', 'schema.yaml'), + ('jobflow.yaml', True, 1, 'debug', None), ]) -@patch("workflow_engine.workflow_processor.logger") -@patch("workflow_engine.workflow_processor.WorkflowFile") -def test_workflow_processor_constructor(file_mock: MagicMock, logger_mock: MagicMock, - workflow_file:str, dry_run: bool, threads: int, log_level: str, +@patch("jobflow.jobflow_processor.logger") +@patch("jobflow.jobflow_processor.JobFlowFile") +def test_jobflow_processor_constructor(file_mock: MagicMock, logger_mock: MagicMock, + jobflow_file:str, dry_run: bool, threads: int, log_level: str, schema_file:str): - """Test WorkflowProcessor constructor. - Check the workflowprocessor instance variables after construction. + """Test JobFlowProcessor constructor. + Check the JobFlowProcessor instance variables after construction. Parameters ---------- file_mock : MagicMock - Mock of a WorkflowFile Constructor. + Mock of a JobFlowFile Constructor. logger_mock : MagicMock The logger fixture defined in conftest.py. - workflow_file : str - Path to workflow yaml file. + jobflow_file : str + Path to jobflow yaml file. dry_run : bool - Define if the workflow will run or not + Define if the jobflow will run or not threads : int number of threads log_level : str @@ -47,12 +47,12 @@ def test_workflow_processor_constructor(file_mock: MagicMock, logger_mock: Magic {'task': 'task2', 'path': '/cmd2', 'args': [{"param1": "value1"}]}, {'task': 'task3', 'path': '/cmd3', 'args': [{"param1": "value1"}]}, ] - workflow_file_instance = file_mock.return_value - workflow_file_instance.task_collection = task_collection + jobflow_file_instance = file_mock.return_value + jobflow_file_instance.task_collection = task_collection with patch.object(logger_mock, 'setLevel') as set_level_mock: - processor = WorkflowProcessor(workflow_file, dry_run, threads, log_level, schema_file) + processor = JobFlowProcessor(jobflow_file, dry_run, threads, log_level, schema_file) set_level_mock.assert_called_once_with(log_level) - file_mock.assert_called_once_with(workflow_file, schema_file) + file_mock.assert_called_once_with(jobflow_file, schema_file) assert processor.task_collection == task_collection assert processor.dry_run == dry_run assert processor.threads == threads @@ -62,7 +62,7 @@ def test_workflow_processor_constructor(file_mock: MagicMock, logger_mock: Magic [({}, {}, {}, 'custom_action', True), ({}, {}, {}, 'custom_action', False),], indirect=["dag", "w_processor", "logger_mock"]) -def test_execute_task(logger_mock: MagicMock, w_processor: WorkflowProcessor, dag: DAG, action: str, +def test_execute_task(logger_mock: MagicMock, w_processor: JobFlowProcessor, dag: DAG, action: str, should_be_canceled: bool): """Test WorflowProcessor.execute_task function normal Check the execute_task method when log messages and function calls when the should_be_canceled return value @@ -72,8 +72,8 @@ def test_execute_task(logger_mock: MagicMock, w_processor: WorkflowProcessor, da ---------- logger_mock : MagicMock The logger fixture defined in conftest.py. - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. + w_processor : JobFlowProcessor + The JobFlow processor fixture defined in conftest.py. dag : DAG The dag fixture defined in conftest.py. action : str @@ -99,7 +99,7 @@ def time_side_effect(): patch.object(w_processor, 'create_task_object', return_value=p_task) as create_task_mock, \ patch.object(dag, 'set_status') as set_status_mock, \ patch.object(p_task, 'execute') as exec_mock, \ - patch('workflow_engine.workflow_processor.time') as time_mock: + patch('jobflow.jobflow_processor.time') as time_mock: time_mock.time = MagicMock(side_effect=time_side_effect) w_processor.execute_task(dag=dag, task=task, action=action) should_be_canceled_mock.assert_called_once_with(task['task']) @@ -123,7 +123,7 @@ def time_side_effect(): [({}, {}, {}, KeyboardInterrupt), ({}, {}, {}, Exception)], indirect=["dag", "w_processor", "logger_mock"]) -def test_execute_task_ko(logger_mock: MagicMock, w_processor: WorkflowProcessor, dag: DAG, exception, +def test_execute_task_ko(logger_mock: MagicMock, w_processor: JobFlowProcessor, dag: DAG, exception, on_error: str): """Test WorflowProcessor.execute_task function, error flows. Check logged messages, set_status call and cancel_dependant_tasks in the failure flow. @@ -132,8 +132,8 @@ def test_execute_task_ko(logger_mock: MagicMock, w_processor: WorkflowProcessor, ---------- logger_mock : MagicMock The logger fixture defined in conftest.py. - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. + w_processor : JobFlowProcessor + The JobFlow processor fixture defined in conftest.py. dag : DAG The dag fixture defined in conftest.py. exception : [type] @@ -149,7 +149,7 @@ def test_execute_task_ko(logger_mock: MagicMock, w_processor: WorkflowProcessor, patch.object(w_processor, 'create_task_object', return_value=p_task), \ patch.object(dag, 'set_status') as set_status_mock, \ patch.object(p_task, 'execute', side_effect=exc), \ - patch('workflow_engine.workflow_processor.time'), \ + patch('jobflow.jobflow_processor.time'), \ patch.object(dag, 'cancel_dependant_tasks') as cancel_mock, \ pytest.raises(expected_exception=exception): w_processor.execute_task(dag=dag, task=task, action='action') @@ -161,14 +161,14 @@ def test_execute_task_ko(logger_mock: MagicMock, w_processor: WorkflowProcessor, @pytest.mark.parametrize('task_type', ['process', 'dummy', 'dummy-random']) @pytest.mark.parametrize('w_processor', [{}], indirect=True) -def test_create_task_object(w_processor: WorkflowProcessor, task_type: str): +def test_create_task_object(w_processor: JobFlowProcessor, task_type: str): """Test WorkfowProcess.create_task_object function normal flow. Check the task type returned by the method. Parameters ---------- - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. + w_processor : JobFlowProcessor + The JobFlow processor fixture defined in conftest.py. task_type : str type of task """ @@ -178,14 +178,14 @@ def test_create_task_object(w_processor: WorkflowProcessor, task_type: str): @pytest.mark.parametrize('w_processor', [{}], indirect=True) -def test_create_task_object_ko(w_processor: WorkflowProcessor): +def test_create_task_object_ko(w_processor: JobFlowProcessor): """Test WorkfowProcess.create_task_object function error flow. Check that the create_task_object raise a ValueError exception for invalid types.} Parameters ---------- - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. + w_processor : JobFlowProcessor + The JobFlow processor fixture defined in conftest.py. """ task_type = 'unknown' task_dict = {'task': 'task1', 'action': {'this': task_type, 'with': {'param'}}} @@ -196,8 +196,8 @@ def test_create_task_object_ko(w_processor: WorkflowProcessor): @pytest.mark.parametrize('reverse', [False, True]) @pytest.mark.parametrize('logger_mock, w_processor, dag',[({}, {}, {})], indirect=["dag", "w_processor", "logger_mock"]) -@patch('workflow_engine.workflow_processor.concurrent.futures.ThreadPoolExecutor') -def test_execute_tasks_parallel(executor_mock: MagicMock, logger_mock: MagicMock, w_processor: WorkflowProcessor, +@patch('jobflow.jobflow_processor.concurrent.futures.ThreadPoolExecutor') +def test_execute_tasks_parallel(executor_mock: MagicMock, logger_mock: MagicMock, w_processor: JobFlowProcessor, dag: DAG, reverse: bool): """Test WorkfowProcess.execute_task_parallel function. Check if the logged messages and function calls of the method with reverse True and False cases. @@ -208,8 +208,8 @@ def test_execute_tasks_parallel(executor_mock: MagicMock, logger_mock: MagicMock Mock of the ThreadPoolExecutor. logger_mock : MagicMock The logger fixture defined in conftest.py. - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. + w_processor : JobFlowProcessor + The JobFlow processor fixture defined in conftest.py. dag : DAG The dag fixture defined in conftest.py. reverse : bool @@ -220,7 +220,7 @@ def test_execute_tasks_parallel(executor_mock: MagicMock, logger_mock: MagicMock y = MagicMock() y.__enter__ = MagicMock(return_value=y) executor_mock.return_value = y - with patch('workflow_engine.workflow_processor.concurrent.futures.wait') as wait_mock, \ + with patch('jobflow.jobflow_processor.concurrent.futures.wait') as wait_mock, \ patch.object(w_processor, 'generate_futures', return_value=futures) as gen_futures_mock: w_processor.execute_tasks_parallel(dag, reverse=reverse) logger_mock.info.assert_called_once_with("Executing tasks in parallel.") @@ -232,8 +232,8 @@ def test_execute_tasks_parallel(executor_mock: MagicMock, logger_mock: MagicMock @pytest.mark.parametrize('reverse', [False, True]) @pytest.mark.parametrize('logger_mock, w_processor, dag',[({}, {}, {})], indirect=["dag", "w_processor", "logger_mock"]) -@patch('workflow_engine.workflow_processor.concurrent.futures.ThreadPoolExecutor') -def test_execute_tasks_parallel_ko(executor_mock: MagicMock, logger_mock: MagicMock, w_processor: WorkflowProcessor, +@patch('jobflow.jobflow_processor.concurrent.futures.ThreadPoolExecutor') +def test_execute_tasks_parallel_ko(executor_mock: MagicMock, logger_mock: MagicMock, w_processor: JobFlowProcessor, dag: DAG, reverse: bool): """Test WorkfowProcess.execute_task_parallel function error flow. Check function call message loggin and calls when the KeyboardInterrupt is generated while waiting the subprocess @@ -245,8 +245,8 @@ def test_execute_tasks_parallel_ko(executor_mock: MagicMock, logger_mock: MagicM not used, just patched logger_mock : MagicMock The logger fixture defined in conftest.py. - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. + w_processor : JobFlowProcessor + The JobFlow processor fixture defined in conftest.py. dag : DAG The dag fixture defined in conftest.py. reverse : bool @@ -257,7 +257,7 @@ def patch_recursive_and_return_exception(_): w_processor.execute_tasks_parallel = execute_parallel_mock raise KeyboardInterrupt() - with patch('workflow_engine.workflow_processor.concurrent.futures.wait', + with patch('jobflow.jobflow_processor.concurrent.futures.wait', side_effect=patch_recursive_and_return_exception), \ patch.object(w_processor, 'generate_futures'): w_processor.execute_tasks_parallel(dag, reverse=reverse) @@ -275,14 +275,14 @@ def patch_recursive_and_return_exception(_): {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],}, ], indirect=True) -def test_generate_futures(w_processor: WorkflowProcessor): +def test_generate_futures(w_processor: JobFlowProcessor): """Test WorkfowProcess.generate_futures function without reverse. Check the futures returned by the method. Parameters ---------- - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. + w_processor : JobFlowProcessor + The JobFlow processor fixture defined in conftest.py. """ def submit_execute_task_side_effect(_, dag: DAG, task, __): dag.set_status(task['task'], 'successful') @@ -305,14 +305,14 @@ def submit_execute_task_side_effect(_, dag: DAG, task, __): {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],}, ], indirect=True) -def test_generate_futures_reverse(w_processor: WorkflowProcessor): +def test_generate_futures_reverse(w_processor: JobFlowProcessor): """Test WorkfowProcess.generate_futures function with reverse True. Check that set_status with successful is called for the tasks. Parameters ---------- - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. + w_processor : JobFlowProcessor + The JobFlow processor fixture defined in conftest.py. """ def set_status_side_effect(task, status): @@ -337,7 +337,7 @@ def set_status_side_effect(task, status): {'task': 'task4', 'depends-on': ['task1']}, {'task': 'task5', 'depends-on': ['task2', 'task3', 'task4']}],})], indirect=True) -def test_run(logger_mock: MagicMock, w_processor: WorkflowProcessor, dry_run: bool): +def test_run(logger_mock: MagicMock, w_processor: JobFlowProcessor, dry_run: bool): """Test WorkfowProcess.run function. Check log message and execute_tasks_parallel call. @@ -345,8 +345,8 @@ def test_run(logger_mock: MagicMock, w_processor: WorkflowProcessor, dry_run: bo ---------- logger_mock : MagicMock The logger fixture defined in conftest.py. - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. + w_processor : JobFlowProcessor + The JobFlow processor fixture defined in conftest.py. dry_run : bool Parameterized value to test the run method. """ @@ -357,7 +357,7 @@ def dag_constructor(_, reverse=False): dag = DAG(w_processor.task_collection) reverse_dag = DAG(w_processor.task_collection, reverse=True) with patch.object(w_processor, 'execute_tasks_parallel') as exec_tasks_mock, \ - patch('workflow_engine.workflow_processor.DAG', side_effect=dag_constructor) as dag_mock: + patch('jobflow.jobflow_processor.DAG', side_effect=dag_constructor) as dag_mock: w_processor.run() if dry_run: dag_mock.assert_called_once_with(w_processor.task_collection) @@ -369,7 +369,7 @@ def dag_constructor(_, reverse=False): @pytest.mark.parametrize('logger_mock, w_processor', [({}, {})], indirect=['logger_mock', 'w_processor']) -def test_handle_interrupt(logger_mock: MagicMock, w_processor: WorkflowProcessor): +def test_handle_interrupt(logger_mock: MagicMock, w_processor: JobFlowProcessor): """Test WorkfowProcess.handle_interrupt function. Check logging when the handle_interrupt is called. @@ -377,8 +377,8 @@ def test_handle_interrupt(logger_mock: MagicMock, w_processor: WorkflowProcessor ---------- logger_mock : MagicMock The logger fixture defined in conftest.py. - w_processor : WorkflowProcessor - The workflow processor fixture defined in conftest.py. + w_processor : JobFlowProcessor + The JobFlow processor fixture defined in conftest.py. """ with pytest.raises(KeyboardInterrupt, match="User interrupt detected. End process..."): w_processor.handle_interrupt(0, 0) diff --git a/deployability/modules/workflow_engine/tests/test_schema_validator.py b/deployability/modules/jobflow/tests/test_schema_validator.py similarity index 86% rename from deployability/modules/workflow_engine/tests/test_schema_validator.py rename to deployability/modules/jobflow/tests/test_schema_validator.py index 25a45db95a..6345f10a0a 100644 --- a/deployability/modules/workflow_engine/tests/test_schema_validator.py +++ b/deployability/modules/jobflow/tests/test_schema_validator.py @@ -11,10 +11,10 @@ import pytest from jsonschema.exceptions import ValidationError, UnknownType -from workflow_engine.schema_validator import SchemaValidator +from jobflow.schema_validator import SchemaValidator @pytest.mark.parametrize('logger_mock', - [{'logger_to_patch':'workflow_engine.schema_validator.logger'}], + [{'logger_to_patch':'jobflow.schema_validator.logger'}], indirect=True) def test_schema_validator_constructor(logger_mock: MagicMock): """Test SchemaValidator constructor normal flow. @@ -61,24 +61,24 @@ def test_preprocess_data(): validator.preprocess_data() -@pytest.mark.parametrize('workflow_file, error_msg', +@pytest.mark.parametrize('jobflow_file, error_msg', [('wf-ko-no-path-on-do.yaml', "Missing required properties in 'with' for task: {'task': 'run-agent-tests-{agent}'"), ('wf-ko-no-path-on-cleanup.yaml', "Missing required properties in 'with' for task: {'task': 'allocate-manager'"),]) -def test_preprocess_data_ko(workflow_file: str, error_msg: str): +def test_preprocess_data_ko(jobflow_file: str, error_msg: str): """Test SchemaValidator preprocess_data error flow. Check the ValidationError generated by invalid yml files. Parameters ---------- - workflow_file : str - workflow yml file name. + jobflow_file : str + jobflow yml file name. error_msg : str Error message to check """ schema_path = Path(__file__).parent.parent / 'schemas' / 'schema_v1.json' - wf_file_path = Path(__file__).parent / 'data' / workflow_file + wf_file_path = Path(__file__).parent / 'data' / jobflow_file validator = SchemaValidator(schema_path, wf_file_path) with pytest.raises(ValidationError, match=error_msg): validator.preprocess_data() @@ -93,11 +93,11 @@ def test_validate_schema(): @pytest.mark.parametrize('logger_mock', - [{'logger_to_patch':'workflow_engine.schema_validator.logger'}], + [{'logger_to_patch':'jobflow.schema_validator.logger'}], indirect=True) def test_validate_schema_ko(logger_mock: MagicMock): """Test SchemaValidator validate_schema error flows. - Check the messages sent to the log when an invalid workflow yml file is used. + Check the messages sent to the log when an invalid jobflow yml file is used. Parameters ---------- @@ -113,7 +113,7 @@ def test_validate_schema_ko(logger_mock: MagicMock): logger_mock.error.reset_mock() validator = SchemaValidator(schema_path, wf_file_path) - with patch('workflow_engine.schema_validator.jsonschema.validate', side_effect=UnknownType): + with patch('jobflow.schema_validator.jsonschema.validate', side_effect=UnknownType): validator.validateSchema() logger_mock.error.assert_called_once() assert 'Unexpected error at schema validation:' in logger_mock.error.call_args[0][0] diff --git a/deployability/modules/workflow_engine/tests/test_task.py b/deployability/modules/jobflow/tests/test_task.py similarity index 95% rename from deployability/modules/workflow_engine/tests/test_task.py rename to deployability/modules/jobflow/tests/test_task.py index 604c8e2926..92948e27cc 100644 --- a/deployability/modules/workflow_engine/tests/test_task.py +++ b/deployability/modules/jobflow/tests/test_task.py @@ -6,7 +6,7 @@ from unittest.mock import patch, MagicMock, call import pytest -from workflow_engine.task import ProcessTask +from jobflow.task import ProcessTask @pytest.fixture def task(request) -> ProcessTask: @@ -40,7 +40,7 @@ def test_process_task_constructor(task: ProcessTask): ('task5', {"path": "/mypath", "args": [{"param1": "value1"}, {"param2": "value2"}]}) ], indirect=True) -@patch("workflow_engine.task.logger") +@patch("jobflow.task.logger") def test_process_task_execute(logger_mock: MagicMock, task: ProcessTask): """Test ProcessTask.execute method normal flow. Check that ProcessTask.execute calls subprocess.run to run commands with the defined parameters. The @@ -66,7 +66,7 @@ def test_process_task_execute(logger_mock: MagicMock, task: ProcessTask): stderr="") debug_calls = [call(f'Running task "{task.task_name}" with arguments: ' f'{results[task.task_name]["parm_list"][1:]}')] - with patch("workflow_engine.task.subprocess.run", return_value=result) as proc_run_mock, \ + with patch("jobflow.task.subprocess.run", return_value=result) as proc_run_mock, \ patch.object(logger_mock, "debug") as logger_debug_mock: debug_calls.append(call(f'Finished task "{task.task_name}" execution ' f'with result:\n{str(result.stdout)}')) @@ -104,7 +104,7 @@ def test_process_task_execute_ko(subproc_retval: int, subproc_run_exc: List[Tupl result = CompletedProcess(args=["--param1=value1"], returncode=subproc_retval, stdout="command output", stderr=stderr) - with patch("workflow_engine.task.subprocess.run", return_value=result) as proc_run_mock: + with patch("jobflow.task.subprocess.run", return_value=result) as proc_run_mock: if raise_exc: proc_run_mock.side_effect = CalledProcessError(returncode=1, cmd=task.task_parameters['path'], diff --git a/deployability/modules/provision/README.MD b/deployability/modules/provision/README.MD index 11049eeafe..418261e31b 100644 --- a/deployability/modules/provision/README.MD +++ b/deployability/modules/provision/README.MD @@ -5,12 +5,12 @@ The Provision module installs libraries, dependencies and applications on the allocated infrastructure (From Allocation module) This module can be executed as follows: - A. Installing and using Workflow engine + A. Installing and using the JobFlow engine B. Direct execution -#### A. Installing and using Workflow engine +#### A. Installing and using the JobFlow engine -The execution of the workflow is done through the installation of its library. +The execution of the JobFlow engine is done through the installation of its library. Initially, Python libraries must be installed. we recommended the use of virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. @@ -44,13 +44,13 @@ To use this module, you should use a Debian-based system, we recommend using Ubu pip3 install -r deployability/deps/requirements.txt ``` -5. Install the Workflow engine library and its launcher: +5. Install the JobFlow engine and its launcher: While in wazuh-qa: ```bash cd modules - pip3 uninstall -y workflow_engine && pip3 install . + pip3 uninstall -y jobflow && pip3 install . ``` Run the module by doing the following steps: @@ -59,13 +59,13 @@ To use this module, you should use a Debian-based system, we recommend using Ubu It is required to create a fixture (YAML file) where the infrastructure, provisioning, and tests to be executed is declared. - >Note: You can find some fixture examples in '[deployability/modules/workflow_engine/examples/](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/workflow_engine/examples)' + >Note: You can find some fixture examples in '[deployability/modules/jobflow/examples/](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/jobflow/examples)' Example: ```bash version: 0.1 - description: This workflow is used to provision agent hosts for DDT1 PoC + description: This YAML is used to provision agent hosts for DDT1 PoC variables: agents-os: - linux-ubuntu-22.04-amd64 @@ -155,18 +155,18 @@ To use this module, you should use a Debian-based system, we recommend using Ubu Execute the command by referencing the parameters required by the library (launcher). ```bash - python3 -m workflow_engine {.yaml fixture path} + python3 -m jobflow {.yaml fixture path} ``` Example ```bash - python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml + python3 -m jobflow modules/jobflow/examples/dtt1-agents-poc.yaml ``` #### B. Direct execution -To execute the Allocation module without installing the Workflow engine, you can use the launcher ('[module/provision/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/main.py)'): +To execute the Allocation module without installing the JobFlow engine, you can use the launcher ('[module/provision/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/main.py)'): 1. Execution @@ -187,7 +187,7 @@ To execute the Allocation module without installing the Workflow engine, you can The `provision module` allows for the installation and uninstallation of libraries, dependencies, and applications in the infrastructure. -It can receive instructions either through the command line or be mediated through the Workflow Engine. +It can receive instructions either through the command line or be mediated through the JobFlow engine. In both cases, the following information is required: @@ -222,17 +222,17 @@ In both cases, the following information is required: as: agent ``` -The snippet obtained from a Workflow fixture highlights the parameters necessary for its operation. These include the inventory path, the dependency/library to install, and the installation type. +The snippet obtained from a JobFlow fixture highlights the parameters necessary for its operation. These include the inventory path, the dependency/library to install, and the installation type. It requires an infrastructure on which to perform its actions. The module is composed of: - Launcher ('[/wazuh-qa/deployability/modules/provision/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/main.py)') - Entry point for the workflow or the user who wishes to execute a provision. + Entry point for the jobflow or the user who wishes to execute a provision. - Parameter validator ('[/wazuh-qa/deployability/modules/provision/models.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/models.py)') - Validator for parameters entered by the Workflow engine or the user. + Validator for parameters entered by the JobFlow engine or the user. - Module functions ('[/wazuh-qa/deployability/modules/provision/provision.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/provision.py)') Module-specific functions responsible for triggering the provision. @@ -247,10 +247,10 @@ The module is composed of: Enables reading and changing the format of YAML files to executable formats. -![image](https://github.com/wazuh/wazuh-qa/assets/125690423/e06fb59c-6497-4396-b20c-a21a68a5e883) +![image](https://github.com/wazuh/wazuh-qa/assets/2949519/326c1198-79a9-4098-9a52-d35c7065d629) -[Provision.drawio.zip](https://github.com/wazuh/wazuh-qa/files/14241269/Provision.drawio.zip) +[Provision.drawio.zip](https://github.com/user-attachments/files/15788313/Provision.drawio.zip) ### License diff --git a/deployability/modules/provision/tests/TESTING-README.md b/deployability/modules/provision/tests/TESTING-README.md index 0fd2baeb84..717a43550c 100644 --- a/deployability/modules/provision/tests/TESTING-README.md +++ b/deployability/modules/provision/tests/TESTING-README.md @@ -108,7 +108,7 @@ deployability/modules/provision/models.py:64 ============================================================================= 51 passed, 2 warnings in 0.16s ============================================================================== ``` -The `.github/workflow/provision-unit-tests.yaml` automatically runs the unit tests in the GitHub environment. +The `.github/jobflow/provision-unit-tests.yaml` automatically runs the unit tests in the GitHub environment. The run results are shown in the `checks` tab or your GitHub pull request. ## Relevant Files diff --git a/deployability/modules/setup.py b/deployability/modules/setup.py index ea439929af..345df522b3 100644 --- a/deployability/modules/setup.py +++ b/deployability/modules/setup.py @@ -25,20 +25,20 @@ def get_version(): return version -package_data_list = get_files_from_directory("workflow_engine") -scripts_list = ['engine=workflow_engine.__main__:main'] +package_data_list = get_files_from_directory("jobflow") +scripts_list = ['engine=jobflow.__main__:main'] setup( - name='workflow_engine', + name='jobflow', version=get_version(), description='Wazuh testing utilities to help programmers automate deployment tests', url='https://github.com/wazuh', author='Wazuh', author_email='hello@wazuh.com', license='GPLv2', - packages=['workflow_engine'], - package_dir={'workflow_engine': 'workflow_engine'}, - package_data={'workflow_engine': package_data_list}, + packages=['jobflow'], + package_dir={'jobflow': 'jobflow'}, + package_data={'jobflow': package_data_list}, entry_points={'console_scripts': scripts_list}, include_package_data=True, zip_safe=False diff --git a/deployability/modules/testing/README.MD b/deployability/modules/testing/README.MD index f2b625e3f4..d7a4731920 100644 --- a/deployability/modules/testing/README.MD +++ b/deployability/modules/testing/README.MD @@ -7,13 +7,13 @@ It is designed so that you can perform installations, actions on components and This module must receive allocated and provisioned infrastructure. (From Allocation and Provision modules) This module can be executed as follows: - A. Installing and using Workflow engine + A. Installing and using the JobFlow engine B. Direct execution -#### A. Installing and using Workflow engine +#### A. Installing and using the JobFlow engine -The execution of the workflow is done through the installation of its library. +The execution of the JobFlow engine is done through the installation of its library. Initially, Python libraries must be installed, we recommended the use of virtual environments. Follow the technical documentation at https://docs.python.org/3/library/venv.html. @@ -48,13 +48,13 @@ To use this module, you should use a Debian-based system, we recommend using Ubu pip3 install -r deployability/deps/requirements.txt ``` -5. Install the Workflow engine library and its launcher: +5. Install the JobFlow engine and its launcher: While in wazuh-qa: ```bash cd modules - pip3 uninstall -y workflow_engine && pip3 install . + pip3 uninstall -y jobflow && pip3 install . ``` 6. The module may execute any of these commands: @@ -70,13 +70,13 @@ To use this module, you should use a Debian-based system, we recommend using Ubu It will be necessary to create a fixture (YAML file) where the infrastructure, provisioning, and tests to be executed will be declared. - >Note: It is possible to find some fixture examples in '[deployability/modules/workflow_engine/examples/](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/workflow_engine/examples)' + >Note: It is possible to find some fixture examples in '[deployability/modules/jobflow/examples/](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/jobflow/examples)' Example: ```bash version: 0.1 - description: This workflow is used to test agents' deployment for DDT1 PoC + description: This YAML is used to test agents' deployment for DDT1 PoC variables: agents-os: - linux-ubuntu-22.04-amd64 @@ -185,18 +185,18 @@ To use this module, you should use a Debian-based system, we recommend using Ubu Execute the command by referencing the parameters required by the library (launcher). ```bash - python3 -m workflow_engine {.yaml fixture path} + python3 -m jobflow {.yaml fixture path} ``` Example ```bash - python3 -m workflow_engine modules/workflow_engine/examples/dtt1-agents-poc.yaml + python3 -m jobflow modules/jobflow/examples/dtt1-agents-poc.yaml ``` #### B. Direct execution -To execute the testing module without installing the Workflow engine, it can be done by using the launcher ('[module/testing/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/main.py)'): +To execute the testing module without installing the JobFlow engine, it can be done by using the launcher ('[module/testing/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/main.py)'): 1. Execution @@ -229,7 +229,7 @@ To execute the testing module without installing the Workflow engine, it can be The testing module allows the execution of tests on agents and central components. -Instructions can be received from the fixture and executed through the Workflow Engine or run through commands on an already provisioned infrastructure. +Instructions can be received from the fixture and executed through the JobFlow engine or run through commands on an already provisioned infrastructure. In both cases, the following information will be required: @@ -264,9 +264,9 @@ For manual execution, an example command would be: The module is composed of: -- **Launcher** ('[/wazuh-qa/deployability/modules/testing/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/main.py)'): Entry point for the workflow or the user who wishes to execute a test. +- **Launcher** ('[/wazuh-qa/deployability/modules/testing/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/main.py)'): Entry point for the jobflow or the user who wishes to execute a test. -- **Parameter validator** ('[/wazuh-qa/deployability/modules/testing/models.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/models.py)'): Validator for parameters entered by the Workflow or the user. +- **Parameter validator** ('[/wazuh-qa/deployability/modules/testing/models.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/models.py)'): Validator for parameters entered by the JobFlow or the user. - **Module functions** ('[/wazuh-qa/deployability/modules/testing/testing.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/testing.py)'): Module-specific functions responsible for triggering the test. @@ -281,12 +281,12 @@ The module is composed of: - **Conftest** ('[/wazuh-qa/deployability/modules/tests/conftest.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/tests/conftest.py)'): Contains information that will be transferred from the parameter validator to the tests. -![image](https://github.com/wazuh/wazuh-qa/assets/125690423/58332378-489a-49da-a73a-4d87d6bed3bc) +![image](https://github.com/wazuh/wazuh-qa/assets/2949519/f2c08ca6-5505-40d7-a9eb-6ab37b3d1ba5) The testing module receives the infrastructure generated and provisioned by the allocation and provision modules. The module has the ability to execute actions on the hosts as well as perform the necessary validation. -[Testing.drawio.zip](https://github.com/wazuh/wazuh-qa/files/14372508/Testing.drawio.zip) +[Testing.drawio.zip](https://github.com/user-attachments/files/15792385/Testing.drawio.zip) The test module must recieve the infrastructure generated and provisioned by the allocation and provision modules. The module can execute actions on the hosts as well as perform the necessary validation. diff --git a/deployability/modules/workflow_engine/tests/TESTING-README.md b/deployability/modules/workflow_engine/tests/TESTING-README.md deleted file mode 100644 index 9144e08dc3..0000000000 --- a/deployability/modules/workflow_engine/tests/TESTING-README.md +++ /dev/null @@ -1,167 +0,0 @@ -# Workflow engine Unit Testing using Pytest - -The workflow_engine module includes pytest unit tests. - -## Requirements - -- Make sure you have Python installed on your system. You can download it from - [python.org](https://www.python.org/downloads/). -- Clone the wazuh-qa repository in your local environment. -- Install the necessary dependencies by running: -```bash -git clone https://github.com/wazuh/wazuh-qa.git -b [your-branch] -cd wazuh-qa -pip install -r deployability/modules/workflow_engine/requirements-dev.txt -``` -- Configure the `PYTHONPATH` variable with the full path to the directory `deployability/modules`, for example if you've -cloned the `wazuh-qa` repository into `/wazuh/wazuh-qa`, configure the `PYTHONPATH` in this way: -```bash -> pwd -/wazuh/wazuh-qa -> export PYTHONPATH=$PYTHONPATH:$PWD/deployability/modules -> echo $PYTHONPATH -/wazuh/wazuh-qa/deployability/modules -``` - -## Test Structure -The directory `deployability/modules/workflow_engine/tests/` contains the unit test files for the `workflow_engine` -module. - -## Running Tests -To run the tests, make sure that your system meets the requirements by executing the following command from the project -root: - -```bash -pytest -vv deployability/modules/workflow_engine -``` -This command will run all tests in the `tests/` directory. Using additional arguments, You can also run specific tests -or directories. The output of this command looks like this: -```bash -pytest -vv deployability/modules/workflow_engine -============================================================================================== test session starts ============================================================================================== -platform linux -- Python 3.10.13, pytest-7.1.2, pluggy-1.3.0 -- /usr/local/bin/python3 -cachedir: .pytest_cache -metadata: {'Python': '3.10.13', 'Platform': 'Linux-5.15.146.1-microsoft-standard-WSL2-x86_64-with-glibc2.31', 'Packages': {'pytest': '7.1.2', 'pluggy': '1.3.0'}, 'Plugins': {'anyio': '4.2.0', 'testinfra': '5.0.0', 'metadata': '3.0.0', 'html': '3.1.1'}} -rootdir: /home/marcelo/wazuh/wazuh-qa/deployability/modules -plugins: anyio-4.2.0, testinfra-5.0.0, metadata-3.0.0, html-3.1.1 -collected 92 items - -deployability/modules/workflow_engine/tests/test_dag.py::test_dag_constructor[True] PASSED [ 1%] -deployability/modules/workflow_engine/tests/test_dag.py::test_dag_constructor[False] PASSED [ 2%] -deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[True-dag0] PASSED [ 3%] -deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[True-dag1] PASSED [ 4%] -deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[False-dag0] PASSED [ 5%] -deployability/modules/workflow_engine/tests/test_dag.py::test_dag_is_active[False-dag1] PASSED [ 6%] -deployability/modules/workflow_engine/tests/test_dag.py::test_get_execution_plan[dag0] PASSED [ 7%] -deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-failed-dag0] PASSED [ 8%] -deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-canceled-dag0] PASSED [ 9%] -deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-successful-dag0] PASSED [ 10%] -deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[task1-non_existing_status-dag0] FAILED [ 11%] -deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[non_existing_task-successful-dag0] PASSED [ 13%] -deployability/modules/workflow_engine/tests/test_dag.py::test_set_status[non_existing_task-non_existing_status-dag0] FAILED [ 14%] -deployability/modules/workflow_engine/tests/test_dag.py::test_should_be_canceled[True-dag0] PASSED [ 15%] -deployability/modules/workflow_engine/tests/test_dag.py::test_should_be_canceled[False-dag0] PASSED [ 16%] -deployability/modules/workflow_engine/tests/test_dag.py::test_build_dag[dag0] PASSED [ 17%] -deployability/modules/workflow_engine/tests/test_dag.py::test_build_dag[dag1] PASSED [ 18%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-all-to_be_canceled0-dag0] PASSED [ 19%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-all-to_be_canceled0-dag1] PASSED [ 20%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-related-flows-to_be_canceled1-dag0] FAILED [ 21%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-abort-related-flows-to_be_canceled1-dag1] FAILED [ 22%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-continue-to_be_canceled2-dag0] FAILED [ 23%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task1-continue-to_be_canceled2-dag1] FAILED [ 25%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-all-to_be_canceled3-dag0] FAILED [ 26%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-all-to_be_canceled3-dag1] FAILED [ 27%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-related-flows-to_be_canceled4-dag0] FAILED [ 28%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-abort-related-flows-to_be_canceled4-dag1] FAILED [ 29%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-continue-to_be_canceled5-dag0] FAILED [ 30%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task2-continue-to_be_canceled5-dag1] FAILED [ 31%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-all-to_be_canceled6-dag0] PASSED [ 32%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-all-to_be_canceled6-dag1] PASSED [ 33%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-related-flows-to_be_canceled7-dag0] PASSED [ 34%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-abort-related-flows-to_be_canceled7-dag1] PASSED [ 35%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-continue-to_be_canceled8-dag0] FAILED [ 36%] -deployability/modules/workflow_engine/tests/test_dag.py::test_cancel_dependant_tasks[task5-continue-to_be_canceled8-dag1] FAILED [ 38%] -deployability/modules/workflow_engine/tests/test_dag.py::test_create_execution_plan[dag0-exec_plan0] PASSED [ 39%] -deployability/modules/workflow_engine/tests/test_dag.py::test_create_execution_plan[dag1-exec_plan1] PASSED [ 40%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_schema_validator_constructor[logger_mock0] PASSED [ 41%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_schema_validator_constructor_ko PASSED [ 42%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data PASSED [ 43%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data_ko[wf-ko-no-path-on-do.yaml-Missing required properties in 'with' for task: {'task': 'run-agent-tests-{agent}'] PASSED [ 44%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_preprocess_data_ko[wf-ko-no-path-on-cleanup.yaml-Missing required properties in 'with' for task: {'task': 'allocate-manager'] PASSED [ 45%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_validate_schema PASSED [ 46%] -deployability/modules/workflow_engine/tests/test_schema_validator.py::test_validate_schema_ko[logger_mock0] PASSED [ 47%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_constructor[task0] PASSED [ 48%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task0] PASSED [ 50%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task1] PASSED [ 51%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task2] PASSED [ 52%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task3] PASSED [ 53%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute[task4] PASSED [ 54%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc0-1-task0] PASSED [ 55%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc0-0-task0] PASSED [ 56%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc1-1-task0] PASSED [ 57%] -deployability/modules/workflow_engine/tests/test_task.py::test_process_task_execute_ko[subproc_run_exc1-0-task0] PASSED [ 58%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_constructor PASSED [ 59%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_validate_schema[logger_mock0] PASSED [ 60%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_validate_schema_ko[logger_mock0] PASSED [ 61%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_load_workflow[logger_mock0] PASSED [ 63%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_load_workflow_ko[logger_mock0] PASSED [ 64%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_process_workflow[logger_mock0] PASSED [ 65%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_process_workflow_ok[logger_mock0] PASSED [ 66%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element0-values0-return_value0] PASSED [ 67%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element1-values1-return_value1] PASSED [ 68%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[string_element {value}-values2-string_element value] PASSED [ 69%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_replace_placeholder[element3-None-return_value3] PASSED [ 70%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_expand_task[task0-return_value0-variables0] PASSED [ 71%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_expand_task[task1-return_value1-variables1] PASSED [ 72%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation PASSED [ 73%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation_ko[task_collection0-Duplicated task names: task 1] PASSED [ 75%] -deployability/modules/workflow_engine/tests/test_workflow_file.py::test_workflow_file_static_workflow_validation_ko[task_collection1-Tasks do not exist: task 3, task 4] PASSED [ 76%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-False-1-info-schema.yaml] PASSED [ 77%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-True-1-debug-schema.yaml] PASSED [ 78%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_workflow_processor_constructor[workflow.yaml-True-1-debug-None] PASSED [ 79%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task[logger_mock0-w_processor0-dag0-custom_action-True] PASSED [ 80%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task[logger_mock1-w_processor1-dag1-custom_action-False] PASSED [ 81%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock0-w_processor0-dag0-KeyboardInterrupt-None] PASSED [ 82%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock0-w_processor0-dag0-KeyboardInterrupt-abort-all] PASSED [ 83%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock1-w_processor1-dag1-Exception-None] PASSED [ 84%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_task_ko[logger_mock1-w_processor1-dag1-Exception-abort-all] PASSED [ 85%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-process] PASSED [ 86%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-dummy] PASSED [ 88%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object[w_processor0-dummy-random] PASSED [ 89%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_create_task_object_ko[w_processor0] PASSED [ 90%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel[logger_mock0-w_processor0-dag0-False] PASSED [ 91%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel[logger_mock0-w_processor0-dag0-True] PASSED [ 92%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel_ko[logger_mock0-w_processor0-dag0-False] PASSED [ 93%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_execute_tasks_parallel_ko[logger_mock0-w_processor0-dag0-True] PASSED [ 94%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_generate_futures[w_processor0] PASSED [ 95%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_generate_futures_reverse[w_processor0] PASSED [ 96%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_run[logger_mock0-w_processor0-False] PASSED [ 97%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_run[logger_mock0-w_processor0-True] PASSED [ 98%] -deployability/modules/workflow_engine/tests/test_workflow_processor.py::test_handle_interrupt[logger_mock0-w_processor0] PASSED [100%] - -=================================================================================================== FAILURES ==================================================================================================== -``` - -The `.github/workflow/workflow-engine-unit-tests.yaml` automatically runs the unit tests in the GitHub environment. -The run results are in the `checks` tab or your GitHub pull request. - -## Relevant Files -- `tests/test_[test name].py`: all the unit test files start with a `test_` prefix. There is one unit test file for - each tested class. -- `tests/conftest.py`: contains the fixtures used throughout the unit tests. - -## Unit test development guidelines and recommendations -- Use Python coding style standards and recommendations to develop unit tests: snake case for all variable and function - names, maximum line length of 120 characters, two empty lines must separate each function, typing all your functions - and return values, create Docstring for all functions with numpy style. -- Develop unit tests for each function or method of the module. -- Error flows are usually created in a second unit test with the suffix `_ko`. For example, the - `test_process_task_execute` found in the `deployability/modules/workflow_engine/tests/test_workflow_processor` is the - unit test normal flow for the `WorkflowProcessor.process_task_execute` method. The - `WorkflowProcessor.process_task_execute_ko` unit test implements the error flow. -- Use the pytest's decorator `@pytest.mark.parametrize` to implement test cases for the same unit test. -- Mock the object instance and functions used by your tested function using the `unitest.mock.patch` and - `unitest.mock.patch.object` functions or decorators. -- Try to factorize your testing code using `pytest.fixtures`. The shared fixtures are in the `conftest.py` file. In - many unit tests of this project, the fixtures implement a `request` object that receives parameters from the - `pytest.mark.parametrize`. From 8d16c07320e4812ca6be81a7d460e679fc60fc3d Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Fri, 14 Jun 2024 12:41:35 -0300 Subject: [PATCH 191/195] Some typos fixes --- .../jobflow/tests/test_schema_validator.py | 4 +- deployability/modules/provision/README.MD | 2 +- deployability/modules/testing/README.MD | 4 +- .../modules/workflow_engine.egg-info/PKG-INFO | 8 ++++ .../workflow_engine.egg-info/SOURCES.txt | 45 +++++++++++++++++++ .../dependency_links.txt | 1 + .../workflow_engine.egg-info/entry_points.txt | 2 + .../workflow_engine.egg-info/not-zip-safe | 1 + .../workflow_engine.egg-info/top_level.txt | 1 + 9 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 deployability/modules/workflow_engine.egg-info/PKG-INFO create mode 100644 deployability/modules/workflow_engine.egg-info/SOURCES.txt create mode 100644 deployability/modules/workflow_engine.egg-info/dependency_links.txt create mode 100644 deployability/modules/workflow_engine.egg-info/entry_points.txt create mode 100644 deployability/modules/workflow_engine.egg-info/not-zip-safe create mode 100644 deployability/modules/workflow_engine.egg-info/top_level.txt diff --git a/deployability/modules/jobflow/tests/test_schema_validator.py b/deployability/modules/jobflow/tests/test_schema_validator.py index 6345f10a0a..2c1c75f15f 100644 --- a/deployability/modules/jobflow/tests/test_schema_validator.py +++ b/deployability/modules/jobflow/tests/test_schema_validator.py @@ -37,7 +37,7 @@ def test_schema_validator_constructor(logger_mock: MagicMock): validator = SchemaValidator(schema_path, wf_file_path) assert validator.schema_data == schema_data assert validator.yaml_data == yaml_data - calls = [call(f"Loading schema file: {schema_path}"), + calls = [call(f"Loading schema file: {schema_path}"), call(f"Loading yaml file: {wf_file_path}")] logger_mock.debug.assert_has_calls(calls) @@ -97,7 +97,7 @@ def test_validate_schema(): indirect=True) def test_validate_schema_ko(logger_mock: MagicMock): """Test SchemaValidator validate_schema error flows. - Check the messages sent to the log when an invalid jobflow yml file is used. + Check the messages sent to the log when an invalid workflow yml file is used. Parameters ---------- diff --git a/deployability/modules/provision/README.MD b/deployability/modules/provision/README.MD index 418261e31b..71b06ce882 100644 --- a/deployability/modules/provision/README.MD +++ b/deployability/modules/provision/README.MD @@ -229,7 +229,7 @@ It requires an infrastructure on which to perform its actions. The module is composed of: - Launcher ('[/wazuh-qa/deployability/modules/provision/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/main.py)') - Entry point for the jobflow or the user who wishes to execute a provision. + Entry point for the workflow or the user who wishes to execute a provision. - Parameter validator ('[/wazuh-qa/deployability/modules/provision/models.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/provision/models.py)') Validator for parameters entered by the JobFlow engine or the user. diff --git a/deployability/modules/testing/README.MD b/deployability/modules/testing/README.MD index d7a4731920..8525531dab 100644 --- a/deployability/modules/testing/README.MD +++ b/deployability/modules/testing/README.MD @@ -61,7 +61,7 @@ To use this module, you should use a Debian-based system, we recommend using Ubu - ssh - scp - + These commands must be available to run on the Host Operating System. Run the module by doing the following steps: @@ -264,7 +264,7 @@ For manual execution, an example command would be: The module is composed of: -- **Launcher** ('[/wazuh-qa/deployability/modules/testing/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/main.py)'): Entry point for the jobflow or the user who wishes to execute a test. +- **Launcher** ('[/wazuh-qa/deployability/modules/testing/main.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/main.py)'): Entry point for the workflow or the user who wishes to execute a test. - **Parameter validator** ('[/wazuh-qa/deployability/modules/testing/models.py](https://github.com/wazuh/wazuh-qa/tree/master/deployability/modules/testing/models.py)'): Validator for parameters entered by the JobFlow or the user. diff --git a/deployability/modules/workflow_engine.egg-info/PKG-INFO b/deployability/modules/workflow_engine.egg-info/PKG-INFO new file mode 100644 index 0000000000..7bc805e400 --- /dev/null +++ b/deployability/modules/workflow_engine.egg-info/PKG-INFO @@ -0,0 +1,8 @@ +Metadata-Version: 2.1 +Name: workflow_engine +Version: 1.0 +Summary: Wazuh testing utilities to help programmers automate deployment tests +Home-page: https://github.com/wazuh +Author: Wazuh +Author-email: hello@wazuh.com +License: GPLv2 diff --git a/deployability/modules/workflow_engine.egg-info/SOURCES.txt b/deployability/modules/workflow_engine.egg-info/SOURCES.txt new file mode 100644 index 0000000000..0abdc1e135 --- /dev/null +++ b/deployability/modules/workflow_engine.egg-info/SOURCES.txt @@ -0,0 +1,45 @@ +setup.py +workflow_engine/README.MD +workflow_engine/__init__.py +workflow_engine/__main__.py +workflow_engine/models.py +workflow_engine/requirements-dev.txt +workflow_engine/schema_validator.py +workflow_engine/task.py +workflow_engine/workflow_processor.py +workflow_engine.egg-info/PKG-INFO +workflow_engine.egg-info/SOURCES.txt +workflow_engine.egg-info/dependency_links.txt +workflow_engine.egg-info/entry_points.txt +workflow_engine.egg-info/not-zip-safe +workflow_engine.egg-info/top_level.txt +workflow_engine/examples/agent/aws/test-agent-complete.yaml +workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml +workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml +workflow_engine/examples/agent/aws/test-agent-suse.yaml +workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml +workflow_engine/examples/agent/vagrant/test-agent-complete.yaml +workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov.yaml +workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml +workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov.yaml +workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov.yaml +workflow_engine/examples/manager/aws/dtt1-managers-poc-aws-all.yaml +workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml +workflow_engine/examples/manager/vagrant/dtt1-managers-poc-aws-all.yaml +workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +workflow_engine/logger/__init__.py +workflow_engine/logger/config.yaml +workflow_engine/logger/filter.py +workflow_engine/logger/logger.py +workflow_engine/schemas/schema_v1.json +workflow_engine/tests/TESTING-README.md +workflow_engine/tests/conftest.py +workflow_engine/tests/test_dag.py +workflow_engine/tests/test_schema_validator.py +workflow_engine/tests/test_task.py +workflow_engine/tests/test_workflow_file.py +workflow_engine/tests/test_workflow_processor.py +workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml +workflow_engine/tests/data/wf-ko-no-path-on-do.yaml +workflow_engine/tests/data/wf-ko-schema-error.yaml +workflow_engine/tests/data/wf-ok.yaml \ No newline at end of file diff --git a/deployability/modules/workflow_engine.egg-info/dependency_links.txt b/deployability/modules/workflow_engine.egg-info/dependency_links.txt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/deployability/modules/workflow_engine.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/deployability/modules/workflow_engine.egg-info/entry_points.txt b/deployability/modules/workflow_engine.egg-info/entry_points.txt new file mode 100644 index 0000000000..bd39e78207 --- /dev/null +++ b/deployability/modules/workflow_engine.egg-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +engine = workflow_engine.__main__:main diff --git a/deployability/modules/workflow_engine.egg-info/not-zip-safe b/deployability/modules/workflow_engine.egg-info/not-zip-safe new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/deployability/modules/workflow_engine.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/deployability/modules/workflow_engine.egg-info/top_level.txt b/deployability/modules/workflow_engine.egg-info/top_level.txt new file mode 100644 index 0000000000..16d139c485 --- /dev/null +++ b/deployability/modules/workflow_engine.egg-info/top_level.txt @@ -0,0 +1 @@ +workflow_engine From 528aa419c4dd84a45cd6b395ff8c951f2d2eaba5 Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Fri, 14 Jun 2024 12:44:43 -0300 Subject: [PATCH 192/195] Remove modules chache --- .../modules/workflow_engine.egg-info/PKG-INFO | 8 ---- .../workflow_engine.egg-info/SOURCES.txt | 45 ------------------- .../dependency_links.txt | 1 - .../workflow_engine.egg-info/entry_points.txt | 2 - .../workflow_engine.egg-info/not-zip-safe | 1 - .../workflow_engine.egg-info/top_level.txt | 1 - 6 files changed, 58 deletions(-) delete mode 100644 deployability/modules/workflow_engine.egg-info/PKG-INFO delete mode 100644 deployability/modules/workflow_engine.egg-info/SOURCES.txt delete mode 100644 deployability/modules/workflow_engine.egg-info/dependency_links.txt delete mode 100644 deployability/modules/workflow_engine.egg-info/entry_points.txt delete mode 100644 deployability/modules/workflow_engine.egg-info/not-zip-safe delete mode 100644 deployability/modules/workflow_engine.egg-info/top_level.txt diff --git a/deployability/modules/workflow_engine.egg-info/PKG-INFO b/deployability/modules/workflow_engine.egg-info/PKG-INFO deleted file mode 100644 index 7bc805e400..0000000000 --- a/deployability/modules/workflow_engine.egg-info/PKG-INFO +++ /dev/null @@ -1,8 +0,0 @@ -Metadata-Version: 2.1 -Name: workflow_engine -Version: 1.0 -Summary: Wazuh testing utilities to help programmers automate deployment tests -Home-page: https://github.com/wazuh -Author: Wazuh -Author-email: hello@wazuh.com -License: GPLv2 diff --git a/deployability/modules/workflow_engine.egg-info/SOURCES.txt b/deployability/modules/workflow_engine.egg-info/SOURCES.txt deleted file mode 100644 index 0abdc1e135..0000000000 --- a/deployability/modules/workflow_engine.egg-info/SOURCES.txt +++ /dev/null @@ -1,45 +0,0 @@ -setup.py -workflow_engine/README.MD -workflow_engine/__init__.py -workflow_engine/__main__.py -workflow_engine/models.py -workflow_engine/requirements-dev.txt -workflow_engine/schema_validator.py -workflow_engine/task.py -workflow_engine/workflow_processor.py -workflow_engine.egg-info/PKG-INFO -workflow_engine.egg-info/SOURCES.txt -workflow_engine.egg-info/dependency_links.txt -workflow_engine.egg-info/entry_points.txt -workflow_engine.egg-info/not-zip-safe -workflow_engine.egg-info/top_level.txt -workflow_engine/examples/agent/aws/test-agent-complete.yaml -workflow_engine/examples/agent/aws/test-agent-restart-ins-prov.yaml -workflow_engine/examples/agent/aws/test-agent-stop-ins-prov.yaml -workflow_engine/examples/agent/aws/test-agent-suse.yaml -workflow_engine/examples/agent/aws/test-agent-uninstall-ins-prov.yaml -workflow_engine/examples/agent/vagrant/test-agent-complete.yaml -workflow_engine/examples/agent/vagrant/test-agent-restart-ins-prov.yaml -workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml -workflow_engine/examples/agent/vagrant/test-agent-stop-ins-prov.yaml -workflow_engine/examples/agent/vagrant/test-agent-uninstall-ins-prov.yaml -workflow_engine/examples/manager/aws/dtt1-managers-poc-aws-all.yaml -workflow_engine/examples/manager/aws/dtt1-managers-poc-aws.yaml -workflow_engine/examples/manager/vagrant/dtt1-managers-poc-aws-all.yaml -workflow_engine/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml -workflow_engine/logger/__init__.py -workflow_engine/logger/config.yaml -workflow_engine/logger/filter.py -workflow_engine/logger/logger.py -workflow_engine/schemas/schema_v1.json -workflow_engine/tests/TESTING-README.md -workflow_engine/tests/conftest.py -workflow_engine/tests/test_dag.py -workflow_engine/tests/test_schema_validator.py -workflow_engine/tests/test_task.py -workflow_engine/tests/test_workflow_file.py -workflow_engine/tests/test_workflow_processor.py -workflow_engine/tests/data/wf-ko-no-path-on-cleanup.yaml -workflow_engine/tests/data/wf-ko-no-path-on-do.yaml -workflow_engine/tests/data/wf-ko-schema-error.yaml -workflow_engine/tests/data/wf-ok.yaml \ No newline at end of file diff --git a/deployability/modules/workflow_engine.egg-info/dependency_links.txt b/deployability/modules/workflow_engine.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789179..0000000000 --- a/deployability/modules/workflow_engine.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/deployability/modules/workflow_engine.egg-info/entry_points.txt b/deployability/modules/workflow_engine.egg-info/entry_points.txt deleted file mode 100644 index bd39e78207..0000000000 --- a/deployability/modules/workflow_engine.egg-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[console_scripts] -engine = workflow_engine.__main__:main diff --git a/deployability/modules/workflow_engine.egg-info/not-zip-safe b/deployability/modules/workflow_engine.egg-info/not-zip-safe deleted file mode 100644 index 8b13789179..0000000000 --- a/deployability/modules/workflow_engine.egg-info/not-zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/deployability/modules/workflow_engine.egg-info/top_level.txt b/deployability/modules/workflow_engine.egg-info/top_level.txt deleted file mode 100644 index 16d139c485..0000000000 --- a/deployability/modules/workflow_engine.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -workflow_engine From 31fdcb1c62176775bef74baa6db3568399b16dea Mon Sep 17 00:00:00 2001 From: fcaffieri Date: Fri, 14 Jun 2024 15:22:39 -0300 Subject: [PATCH 193/195] Fix examples and JobFlow bugs --- .../examples/4.8.0/agent/Test-agents-complete.yaml | 0 .../examples/4.8.0/agent/Test-agents-one-each.yaml | 0 .../examples/4.8.0/agent/Test-agents-one-linux.yaml | 0 .../4.8.0/central_components/Test-CC-complete.yaml | 0 .../4.8.0/central_components/Test-CC-one-linux.yaml | 0 .../examples/4.8.0/manager/Test-manager-complete.yaml | 0 .../examples/4.8.0/manager/Test-manager-one-linux.yaml | 0 .../agent/vagrant/test-agent-basic-info-vagrant.yaml | 3 +++ .../examples/agent/vagrant/test-agent-complete-1.yaml | 2 ++ .../examples/agent/vagrant/test-agent-complete-2.yaml | 2 ++ .../examples/agent/vagrant/test-agent-complete-macOS.yaml | 2 ++ .../agent/vagrant/test-agent-restart-ins-prov-1.yaml | 2 ++ .../agent/vagrant/test-agent-restart-ins-prov-2.yaml | 2 ++ .../agent/vagrant/test-agent-stop-ins-prov-1.yaml | 2 ++ .../agent/vagrant/test-agent-stop-ins-prov-2.yaml | 2 ++ .../agent/vagrant/test-agent-uninstall-ins-prov-1.yaml | 2 ++ .../agent/vagrant/test-agent-uninstall-ins-prov-2.yaml | 2 ++ .../agent/vagrant/test-agent-windows-complete.yaml | 8 +++----- .../vagrant/dtt1-central_components-poc-vagrant.yaml | 2 +- .../manager/vagrant/dtt1-managers-poc-vagrant.yaml | 2 +- deployability/modules/jobflow/jobflow_processor.py | 5 +++-- 21 files changed, 29 insertions(+), 9 deletions(-) rename deployability/modules/{workflow_engine => jobflow}/examples/4.8.0/agent/Test-agents-complete.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/4.8.0/agent/Test-agents-one-each.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/4.8.0/agent/Test-agents-one-linux.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/4.8.0/central_components/Test-CC-complete.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/4.8.0/central_components/Test-CC-one-linux.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/4.8.0/manager/Test-manager-complete.yaml (100%) rename deployability/modules/{workflow_engine => jobflow}/examples/4.8.0/manager/Test-manager-one-linux.yaml (100%) diff --git a/deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-complete.yaml b/deployability/modules/jobflow/examples/4.8.0/agent/Test-agents-complete.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-complete.yaml rename to deployability/modules/jobflow/examples/4.8.0/agent/Test-agents-complete.yaml diff --git a/deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-one-each.yaml b/deployability/modules/jobflow/examples/4.8.0/agent/Test-agents-one-each.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-one-each.yaml rename to deployability/modules/jobflow/examples/4.8.0/agent/Test-agents-one-each.yaml diff --git a/deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-one-linux.yaml b/deployability/modules/jobflow/examples/4.8.0/agent/Test-agents-one-linux.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/4.8.0/agent/Test-agents-one-linux.yaml rename to deployability/modules/jobflow/examples/4.8.0/agent/Test-agents-one-linux.yaml diff --git a/deployability/modules/workflow_engine/examples/4.8.0/central_components/Test-CC-complete.yaml b/deployability/modules/jobflow/examples/4.8.0/central_components/Test-CC-complete.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/4.8.0/central_components/Test-CC-complete.yaml rename to deployability/modules/jobflow/examples/4.8.0/central_components/Test-CC-complete.yaml diff --git a/deployability/modules/workflow_engine/examples/4.8.0/central_components/Test-CC-one-linux.yaml b/deployability/modules/jobflow/examples/4.8.0/central_components/Test-CC-one-linux.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/4.8.0/central_components/Test-CC-one-linux.yaml rename to deployability/modules/jobflow/examples/4.8.0/central_components/Test-CC-one-linux.yaml diff --git a/deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-complete.yaml b/deployability/modules/jobflow/examples/4.8.0/manager/Test-manager-complete.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-complete.yaml rename to deployability/modules/jobflow/examples/4.8.0/manager/Test-manager-complete.yaml diff --git a/deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-one-linux.yaml b/deployability/modules/jobflow/examples/4.8.0/manager/Test-manager-one-linux.yaml similarity index 100% rename from deployability/modules/workflow_engine/examples/4.8.0/manager/Test-manager-one-linux.yaml rename to deployability/modules/jobflow/examples/4.8.0/manager/Test-manager-one-linux.yaml diff --git a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml index 6b94fadd93..aae7686180 100755 --- a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml +++ b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-basic-info-vagrant.yaml @@ -58,6 +58,7 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{manager-os}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - label-termination-date: "1d" @@ -86,6 +87,7 @@ tasks: - provider: "{infra-provider}" - size: small - composite-name: "{agent}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" @@ -117,6 +119,7 @@ tasks: - provider: "{macos-infra-provider}" - size: small - composite-name: "{agent}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" diff --git a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-1.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-1.yaml index 093ab81aba..0075b0e1fc 100755 --- a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-1.yaml +++ b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-1.yaml @@ -26,6 +26,7 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{manager-os}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - label-termination-date: "1d" @@ -54,6 +55,7 @@ tasks: - provider: "{infra-provider}" - size: small - composite-name: "{agent}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" diff --git a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-2.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-2.yaml index 91bb485a25..3651ac1bf1 100755 --- a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-2.yaml +++ b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-2.yaml @@ -26,6 +26,7 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{manager-os}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - label-termination-date: "1d" @@ -53,6 +54,7 @@ tasks: - provider: "{infra-provider}" - size: small - composite-name: "{agent}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" diff --git a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-macOS.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-macOS.yaml index 918ed5bd70..a4b6175967 100644 --- a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-macOS.yaml +++ b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-complete-macOS.yaml @@ -23,6 +23,7 @@ tasks: - provider: "aws" - size: large - composite-name: "{manager-os}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - label-termination-date: "1d" @@ -50,6 +51,7 @@ tasks: - provider: "{infra-provider}" - size: small - composite-name: "{agent}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" diff --git a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml index 2c0215cfda..34b7fe2dc3 100755 --- a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml +++ b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-restart-ins-prov-1.yaml @@ -26,6 +26,7 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{manager-os}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" on-error: "abort-all" @@ -51,6 +52,7 @@ tasks: - provider: "{infra-provider}" - size: small - composite-name: "{agent}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" on-error: "abort-all" diff --git a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml index 9ef8f73540..ab955e189c 100755 --- a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml +++ b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-restart-ins-prov-2.yaml @@ -26,6 +26,7 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{manager-os}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" on-error: "abort-all" @@ -51,6 +52,7 @@ tasks: - provider: "{infra-provider}" - size: small - composite-name: "{agent}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" on-error: "abort-all" diff --git a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml index 7c776fb2aa..2360d34296 100755 --- a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml +++ b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-stop-ins-prov-1.yaml @@ -26,6 +26,7 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{manager-os}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" on-error: "abort-all" @@ -51,6 +52,7 @@ tasks: - provider: "{infra-provider}" - size: small - composite-name: "{agent}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" on-error: "abort-all" diff --git a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml index 7bf65cc472..0286f4f5fa 100755 --- a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml +++ b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-stop-ins-prov-2.yaml @@ -26,6 +26,7 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{manager-os}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" on-error: "abort-all" @@ -51,6 +52,7 @@ tasks: - provider: "{infra-provider}" - size: small - composite-name: "{agent}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" on-error: "abort-all" diff --git a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml index 3495ff25c1..4d943c7d2f 100755 --- a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml +++ b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-uninstall-ins-prov-1.yaml @@ -26,6 +26,7 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{manager-os}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" on-error: "abort-all" @@ -51,6 +52,7 @@ tasks: - provider: "{infra-provider}" - size: small - composite-name: "{agent}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" on-error: "abort-all" diff --git a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml index 0d875b00f4..ef861b4438 100755 --- a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml +++ b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-uninstall-ins-prov-2.yaml @@ -26,6 +26,7 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{manager-os}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" on-error: "abort-all" @@ -51,6 +52,7 @@ tasks: - provider: "{infra-provider}" - size: small - composite-name: "{agent}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" on-error: "abort-all" diff --git a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-windows-complete.yaml b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-windows-complete.yaml index 59f8204c84..d94e8707ae 100755 --- a/deployability/modules/jobflow/examples/agent/vagrant/test-agent-windows-complete.yaml +++ b/deployability/modules/jobflow/examples/agent/vagrant/test-agent-windows-complete.yaml @@ -1,13 +1,9 @@ + version: 0.1 description: This workflow is used to test agents deployment for DDT1 PoC variables: agent-os: - - linux-ubuntu-20.04-amd64 - - linux-debian-12-amd64 - - linux-oracle-9-amd64 - - linux-centos-8-amd64 - - linux-redhat-9-amd64 - windows-desktop-10-amd64 - windows-server-2012r2-amd64 - windows-server-2016-amd64 @@ -31,6 +27,7 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{manager-os}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml" - track-output: "{working-dir}/manager-{manager-os}/track.yaml" - label-termination-date: "1d" @@ -58,6 +55,7 @@ tasks: - provider: "{infra-provider}" - size: medium - composite-name: "{agent}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml" - track-output: "{working-dir}/agent-{agent}/track.yaml" - label-termination-date: "1d" diff --git a/deployability/modules/jobflow/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml b/deployability/modules/jobflow/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml index 8603b18cfb..e412873651 100644 --- a/deployability/modules/jobflow/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml +++ b/deployability/modules/jobflow/examples/central_components/vagrant/dtt1-central_components-poc-vagrant.yaml @@ -30,7 +30,7 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{central_components}" - - instance-name: "{central_components}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/central_components-{central_components}/inventory.yaml" - track-output: "{working-dir}/central_components-{central_components}/track.yaml" - label-termination-date: "1d" diff --git a/deployability/modules/jobflow/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml b/deployability/modules/jobflow/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml index d324d86285..fb1369e2dc 100644 --- a/deployability/modules/jobflow/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml +++ b/deployability/modules/jobflow/examples/manager/vagrant/dtt1-managers-poc-vagrant.yaml @@ -31,7 +31,7 @@ tasks: - provider: "{infra-provider}" - size: large - composite-name: "{manager}" - - instance-name: "{manager}" + - instance-name: "[QA]-Test-Example-Please-Edit-Me" - inventory-output: "{working-dir}/manager-{manager}/inventory.yaml" - track-output: "{working-dir}/manager-{manager}/track.yaml" - label-termination-date: "1d" diff --git a/deployability/modules/jobflow/jobflow_processor.py b/deployability/modules/jobflow/jobflow_processor.py index 27941e8a23..3adeff8dd0 100755 --- a/deployability/modules/jobflow/jobflow_processor.py +++ b/deployability/modules/jobflow/jobflow_processor.py @@ -297,12 +297,12 @@ def execute_task(self, dag: DAG, task: dict, action) -> None: logger.info("[%s] Finished task in %.2f seconds.", task_name, time.time() - start_time) dag.set_status(task_name, 'successful') except KeyboardInterrupt as e: - logger.error("[%s] Task failed with error: %s.", task_name, e) + logger.error("[%s] Task failed with error: %s", task_name, e) dag.set_status(task_name, 'failed') dag.cancel_dependant_tasks(task_name, task.get('on-error', 'abort-related-flows')) raise KeyboardInterrupt except Exception as e: - logger.error("[%s] Task failed with error: %s.", task_name, e) + logger.error("[%s] Task failed with error: %s", task_name, e) dag.set_status(task_name, 'failed') dag.cancel_dependant_tasks(task_name, task.get('on-error', 'abort-related-flows')) raise @@ -359,6 +359,7 @@ def run(self) -> None: logger.info("Executing Reverse DAG tasks.") reversed_dag = DAG(self.task_collection, reverse=True) + reversed_dag.to_be_canceled = dag.to_be_canceled - dag.finished_tasks_status["successful"] self.execute_tasks_parallel(reversed_dag, reverse=True) else: dag = DAG(self.task_collection) From 35584752b447ea9861c69b1d758418fd76a7a093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Correa=20Rodr=C3=ADguez?= Date: Tue, 18 Jun 2024 19:19:41 +0200 Subject: [PATCH 194/195] Added RHEL 8.10 to the Allocator module --- deployability/modules/allocation/static/specs/os.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/modules/allocation/static/specs/os.yml b/deployability/modules/allocation/static/specs/os.yml index 24b712cf6e..c280549ebc 100644 --- a/deployability/modules/allocation/static/specs/os.yml +++ b/deployability/modules/allocation/static/specs/os.yml @@ -329,7 +329,7 @@ aws: zone: us-east-1 user: ec2-user linux-redhat-8-amd64: - ami: ami-054333d53aa32850c + ami: ami-00d00bcb416b79a1a zone: us-east-1 user: ec2-user linux-redhat-8-arm64: From ef3c06a6c9fb8c4d1562e7c91a37c433cbae70d6 Mon Sep 17 00:00:00 2001 From: c-bordon Date: Wed, 19 Jun 2024 11:49:23 -0300 Subject: [PATCH 195/195] Updated pywinrm version --- deployability/deps/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployability/deps/requirements.txt b/deployability/deps/requirements.txt index b71cb57648..29226d44a1 100755 --- a/deployability/deps/requirements.txt +++ b/deployability/deps/requirements.txt @@ -12,4 +12,4 @@ pytest==7.4.4 paramiko==3.4.0 requests==2.31.0 chardet==5.2.0 -pywinrm==0.4.0 +pywinrm==0.4.3