Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the agent_files_deletion test #2296

Merged
merged 9 commits into from
Jan 11, 2022
9 changes: 9 additions & 0 deletions tests/system/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,15 @@ in `system/provisioning/<specified_cluster_environment>` path:
ansible-playbook -i inventory.yml playbook.yml
```

If you want to specify a particular branch of the Wazuh repository, you will need to include:
```shell script
ansible-playbook -i inventory.yml playbook.yml --extra-vars='{"wazuh_branch":"v4.3.0-rc1"}'
```
In the **basic cluster**, you also have to specify a branch from the Wazuh QA repository.
```shell script
ansible-playbook -i inventory.yml playbook.yml --extra-vars='{"wazuh_branch":"v4.3.0-rc1", "wazuh_qa_branch":"master"}'
```

We use [pytest](https://docs.pytest.org/en/latest/contents.html) to run our cluster system tests. Pytest will
recursively look for the closest `conftest` to import all the variables and fixtures needed for every test. If something
is lacking from the closest one, it will look for the next one (if possible) until reaching the current directory. This
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@
dest: /wazuh
version: "{{ wazuh_branch }}"

- name: "Clone wazuh-qa repository"
git:
repo: "https://github.com/wazuh/wazuh-qa"
dest: /wazuh-qa
version: "{{ wazuh_qa_branch }}"
depth: '1'

- name: Install master
args:
chdir: /wazuh
Expand Down Expand Up @@ -85,6 +92,7 @@
authd.debug=2
wazuh_clusterd.debug=2
wazuh_db.debug=2
wazuh_modules.debug=2

- name: Register agents
blockinfile:
Expand All @@ -100,3 +108,6 @@

- name: Start Wazuh
command: /var/ossec/bin/wazuh-control restart

- name: "Install necessary dependencies"
command: /var/ossec/framework/python/bin/python3.9 -m pip install lockfile filetype certifi testinfra
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@
dest: /wazuh
version: "{{ wazuh_branch }}"

- name: "Clone wazuh-qa repository"
git:
repo: "https://github.com/wazuh/wazuh-qa"
dest: /wazuh-qa
version: "{{ wazuh_qa_branch }}"
depth: '1'

- name: Install worker
args:
chdir: /wazuh
Expand Down Expand Up @@ -85,6 +92,10 @@
authd.debug=2
wazuh_clusterd.debug=2
wazuh_db.debug=2
wazuh_modules.debug=2

- name: Restart Wazuh
command: /var/ossec/bin/wazuh-control restart

- name: "Install necessary dependencies"
command: /var/ossec/framework/python/bin/python3.9 -m pip install lockfile filetype certifi testinfra
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (C) 2015-2021, Wazuh Inc.
# Created by Wazuh, Inc. <info@wazuh.com>.
# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2
import sys
sys.path.append('/wazuh-qa/deps/wazuh_testing')
from wazuh_testing import wazuh_db

result = wazuh_db.query_wdb(sys.argv[1])
if result:
print(result)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
wazuh-worker2:
- regex: '.*Received.*shared files to update from .*'
path: "/var/ossec/logs/cluster.log"
timeout: 60
- regex: '.*Worker wazuh-worker2.*Integrity sync.*Finished in.*'
path: "/var/ossec/logs/cluster.log"
timeout: 60
- regex: '.*Worker wazuh-worker2.*Integrity sync.*Starting.*'
path: "/var/ossec/logs/cluster.log"
timeout: 60
- regex: '.*Worker wazuh-worker2.*Integrity sync.*Finished in.*'
path: "/var/ossec/logs/cluster.log"
timeout: 60

wazuh-master:
- regex: '.*Worker wazuh-worker2.*Integrity check.*Finished in.*'
path: "/var/ossec/logs/cluster.log"
timeout: 60
Original file line number Diff line number Diff line change
@@ -1,46 +1,81 @@
# Copyright (C) 2015-2021, Wazuh Inc.
# Created by Wazuh, Inc. <info@wazuh.com>.
# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2

import os
import re
from os.path import join, dirname, abspath
from time import sleep
from time import time, sleep

import pytest

from wazuh_testing.tools import WAZUH_PATH
from wazuh_testing.tools import WAZUH_PATH, WAZUH_LOGS_PATH
from wazuh_testing.tools.monitoring import HostMonitor
from wazuh_testing.tools.system import HostManager

master_host = 'wazuh-master'
worker_host = 'wazuh-worker2'
agent_host = 'wazuh-agent3'
local_path = os.path.dirname(os.path.abspath(__file__))
messages_path = os.path.join(local_path, 'data/messages.yml')
script_path = os.path.join(re.sub(r'^.*?wazuh-qa', '/wazuh-qa', local_path), 'data/get_wdb_agent.py')

tmp_path = os.path.join(local_path, 'tmp')
managers_hosts = [master_host, worker_host]
inventory_path = join(dirname(dirname(dirname(abspath(__file__)))), 'provisioning', 'basic_cluster', 'inventory.yml')
host_manager = HostManager(inventory_path)
time_to_sync = 60
while_time = 5
time_to_sync = 20
time_to_agent_reconnect = 180

# Each file should exist in all hosts specified in 'hosts'.
files = [{'path': join(WAZUH_PATH, 'queue', 'rids', '{id}'), 'hosts': managers_hosts},
{'path': join(WAZUH_PATH, 'queue', 'agent-groups', '{id}'), 'hosts': managers_hosts},
{'path': join(WAZUH_PATH, 'queue', 'diff', '{name}'), 'hosts': [worker_host]},
{'path': join(WAZUH_PATH, 'queue', 'db', '{id}.db'), 'hosts': [worker_host]}]
db_queries = ["select * from agent where id={id}",
"select * from belongs where id_agent={id}"]
yanazaeva marked this conversation as resolved.
Show resolved Hide resolved

queries = ['global sql select * from agent where id={id}',
'global sql select * from belongs where id_agent={id}']


@pytest.fixture(scope='function')
def register_agent():
"""Restart the removed agent to trigger auto-enrollment."""
yield
host_manager.get_host(agent_host).ansible('command', f'service wazuh-agent restart', check=False)
def agent_healthcheck(master_token):
"""Check if the agent is active and reporting."""
timeout = time() + time_to_agent_reconnect
healthy = False

while not healthy:
response = host_manager.make_api_call(host=master_host, method='GET', token=master_token,
endpoint='/agents?status=active')

def test_agent_files_deletion(register_agent):
assert response['status'] == 200, 'Failed when trying to get the active agents'
if int(response['json']['data']['total_affected_items']) == 4:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check that the expected agent is active, not that there are 4 total_affected_items

for item in response['json']['data']['affected_items']:
if item['name'] == agent_host and item['manager'] == worker_host:
healthy = True
elif time() > timeout:
print("The agent 'wazuh-agent3' is not 'Active' yet.")
Comment on lines +53 to +54
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not exiting the while, so when we can't find the wazuh-agent3 active, the test will keep printing The agent 'wazuh-agent3' is not 'Active' yet. forever.

break
sleep(while_time)
sleep(time_to_sync)


def test_agent_files_deletion():
"""Check that when an agent is deleted, all its related files in managers are also removed."""
# Get the current ID and name of the agent that is reporting to worker_host.
# Clean ossec.log and cluster.log
for hosts in managers_hosts:
host_manager.clear_file(host=hosts, file_path=os.path.join(WAZUH_LOGS_PATH, 'ossec.log'))
host_manager.clear_file(host=hosts, file_path=os.path.join(WAZUH_LOGS_PATH, 'cluster.log'))
host_manager.control_service(host=hosts, service='wazuh', state='restarted')

# Get the token
master_token = host_manager.get_api_token(master_host)

# Check if the agent is connected and reporting
agent_healthcheck(master_token)

# Get the current ID and name of the agent that is reporting to worker_host.
response = host_manager.make_api_call(host=master_host, method='GET', token=master_token,
endpoint=f'/agents?select=id,name&q=manager={worker_host}')
assert response['status'] == 200, f'Failed when trying to obtain agent ID: {response}'
endpoint=f"/agents?select=id,name&q=manager={worker_host}")

assert response['status'] == 200, f"Failed when trying to obtain agent ID: {response}"
try:
agent_id = response['json']['data']['affected_items'][0]['id']
agent_name = response['json']['data']['affected_items'][0]['name']
Expand All @@ -51,43 +86,44 @@ def test_agent_files_deletion(register_agent):
for file in files:
for host in file['hosts']:
result = host_manager.run_shell(
host, f'test -e {file["path"].format(id=agent_id, name=agent_name)} && echo "exists"'
host, f"test -e {file['path'].format(id=agent_id, name=agent_name)} && echo 'exists'"
)
assert result, f'This file should exist in {host} but could not be found: ' \
f'{file["path"].format(id=agent_id, name=agent_name)}'
assert result, f"This file should exist in {host} but could not be found: " \
f"{file['path'].format(id=agent_id, name=agent_name)}"

# Check that agent information exists in global.db
# Check that agent information is in the wdb socket
for host in managers_hosts:
for query in db_queries:
result = host_manager.run_command(
host,
f'sqlite3 {join(WAZUH_PATH, "queue", "db", "global.db")} "{query.format(id=agent_id, name=agent_name)}"'
)
assert result, f'This db query should have returned something in {host}, but it did not: ' \
f'{query.format(id=agent_id, name=agent_name)}'
for query in queries:
result = host_manager.run_command(host,
f"{WAZUH_PATH}/framework/python/bin/python3.9 "
f"{script_path} '{query.format(id=agent_id)}'")
assert result, f"This db query should have returned something in {host}, but it did not: {result}"

# Remove the agent
response = host_manager.make_api_call(host=master_host, method='DELETE', token=master_token,
endpoint=f'/agents?agents_list={agent_id}&status=all&older_than=0s')
assert response['status'] == 200, f'Failed when trying to remove agent {agent_id}: {response}'
endpoint=f"/agents?agents_list={agent_id}&status=all&older_than=0s")
assert response['status'] == 200, f"Failed when trying to remove agent {agent_id}: {response}"

# Wait until information is synced to all workers
HostMonitor(inventory_path=inventory_path, messages_path=messages_path, tmp_path=tmp_path).run()
sleep(time_to_sync)

# Check that agent-related files where removed from each node.
for file in files:
for host in file['hosts']:
result = host_manager.run_shell(
host, f'test -e {file["path"].format(id=agent_id, name=agent_name)} && echo "exists"'
host, f"test -e {file['path'].format(id=agent_id, name=agent_name)} && echo 'exists'"
)
assert not result, f'This file should not exist in {host} but it was found: ' \
f'{file["path"].format(id=agent_id, name=agent_name)}'
assert not result, f"This file should not exist in {host} but it was found: " \
f"{file['path'].format(id=agent_id, name=agent_name)}"

# Check that agent information does not exist anymore in global.db
# Check that agent information is not in the wdb socket
for host in managers_hosts:
for query in db_queries:
result = host_manager.run_command(
host,
f'sqlite3 {join(WAZUH_PATH, "queue", "db", "global.db")} "{query.format(id=agent_id, name=agent_name)}"'
)
assert not result, f'This db query should have not returned anything in {host}, but it did: ' \
f'{query.format(id=agent_id, name=agent_name)} -> {result}'
for query in queries:
result = host_manager.run_command(host,
f"{WAZUH_PATH}/framework/python/bin/python3.9 "
f"{script_path} '{query.format(id=agent_id)}'")
assert not result, f"This db query should have not returned anything in {host}, but it did: {result}"

host_manager.control_service(host=agent_host, service='wazuh', state='restarted')
agent_healthcheck(master_token)