Merge pull request #2522 from wazuh/merge-5.0.0-into-main

Merge 5.0.0 into main
This commit is contained in:
Carlos Bordon
2026-07-02 11:01:03 -03:00
committed by GitHub
8 changed files with 537 additions and 841 deletions
+47 -78
View File
@@ -12,6 +12,14 @@ on:
- 'wazuh-agent/**'
- '.github/**'
env:
ARTIFACTS_LOCAL_DIR: /home/runner/work/wazuh-docker/wazuh-docker/docker-images
ARTIFACT_NAMES: |
wazuh-manager.tar
wazuh-indexer.tar
wazuh-dashboard.tar
wazuh-agent.tar
jobs:
build-docker-images:
runs-on: codebuild-github-actions-codebuild-runner-devops-amd-${{ github.run_id }}-${{ github.run_attempt }}
@@ -41,33 +49,17 @@ jobs:
docker save wazuh/wazuh-dashboard:${{env.WAZUH_IMAGE_VERSION}} -o /home/runner/work/wazuh-docker/wazuh-docker/docker-images/wazuh-dashboard.tar
docker save wazuh/wazuh-agent:${{env.WAZUH_IMAGE_VERSION}} -o /home/runner/work/wazuh-docker/wazuh-docker/docker-images/wazuh-agent.tar
- name: Temporarily save Wazuh manager Docker image
uses: actions/upload-artifact@v7
with:
name: docker-artifact-manager
path: /home/runner/work/wazuh-docker/wazuh-docker/docker-images/wazuh-manager.tar
retention-days: 1
- name: Temporarily save Wazuh indexer Docker image
uses: actions/upload-artifact@v7
with:
name: docker-artifact-indexer
path: /home/runner/work/wazuh-docker/wazuh-docker/docker-images/wazuh-indexer.tar
retention-days: 1
- name: Temporarily save Wazuh dashboard Docker image
uses: actions/upload-artifact@v7
with:
name: docker-artifact-dashboard
path: /home/runner/work/wazuh-docker/wazuh-docker/docker-images/wazuh-dashboard.tar
retention-days: 1
- name: Temporarily save Wazuh agent Docker image
uses: actions/upload-artifact@v7
with:
name: docker-artifact-agent
path: /home/runner/work/wazuh-docker/wazuh-docker/docker-images/wazuh-agent.tar
retention-days: 1
- name: Temporarily save Wazuh Docker images
env:
S3_ARTIFACTS_PATH: ${{ secrets.CI_DEV_INTERNAL_S3_BUCKET }}/wazuh-docker/4_pr_check/${{ github.run_id }}
run: |
echo "Uploading Docker image artifacts to S3..."
while IFS= read -r artifact; do
[ -z "$artifact" ] && continue
echo " Uploading: $artifact"
aws s3 cp "${ARTIFACTS_LOCAL_DIR}/${artifact}" "${S3_ARTIFACTS_PATH}/${artifact}"
done <<< "$ARTIFACT_NAMES"
echo "All artifacts uploaded successfully."
check-single-node:
runs-on: codebuild-github-actions-codebuild-runner-devops-amd-${{ github.run_id }}-${{ github.run_attempt }}
@@ -86,32 +78,21 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Retrieve saved Wazuh indexer Docker image
uses: actions/download-artifact@v8
with:
name: docker-artifact-indexer
- name: Retrieve saved Wazuh manager Docker image
uses: actions/download-artifact@v8
with:
name: docker-artifact-manager
- name: Retrieve saved Wazuh dashboard Docker image
uses: actions/download-artifact@v8
with:
name: docker-artifact-dashboard
- name: Retrieve saved Wazuh agent Docker image
uses: actions/download-artifact@v8
with:
name: docker-artifact-agent
- name: Docker load
- name: Retrieve saved Wazuh Docker images and load them into Docker
env:
S3_ARTIFACTS_PATH: s3://${{ secrets.CI_DEV_INTERNAL_S3_BUCKET }}/wazuh-docker/4_pr_check/${{ github.run_id }}
ARTIFACTS_LOCAL_DIR: /home/runner/work/wazuh-docker/wazuh-docker/docker-images
run: |
docker load --input ./wazuh-indexer.tar
docker load --input ./wazuh-dashboard.tar
docker load --input ./wazuh-manager.tar
docker load --input ./wazuh-agent.tar
mkdir -p "${ARTIFACTS_LOCAL_DIR}"
echo "Downloading and loading Docker image artifacts from S3..."
while IFS= read -r artifact; do
[ -z "$artifact" ] && continue
echo " Downloading: $artifact"
aws s3 cp "${S3_ARTIFACTS_PATH}/${artifact}" "${ARTIFACTS_LOCAL_DIR}/${artifact}"
echo " Loading into Docker: $artifact"
docker load -i "${ARTIFACTS_LOCAL_DIR}/${artifact}"
done <<< "$ARTIFACT_NAMES"
echo "All artifacts downloaded and loaded successfully."
- name: Create single node certficates
run: docker compose -f single-node/generate-indexer-certs.yml run --rm generator
@@ -233,33 +214,21 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Retrieve saved Wazuh dashboard Docker image
uses: actions/download-artifact@v8
with:
name: docker-artifact-dashboard
- name: Retrieve saved Wazuh manager Docker image
uses: actions/download-artifact@v8
with:
name: docker-artifact-manager
- name: Retrieve saved Wazuh indexer Docker image
uses: actions/download-artifact@v8
with:
name: docker-artifact-indexer
- name: Retrieve saved Wazuh agent Docker image
uses: actions/download-artifact@v8
with:
name: docker-artifact-agent
- name: Docker load
- name: Retrieve saved Wazuh Docker images and load them into Docker
env:
S3_ARTIFACTS_PATH: s3://${{ secrets.CI_DEV_INTERNAL_S3_BUCKET }}/wazuh-docker/4_pr_check/${{ github.run_id }}
ARTIFACTS_LOCAL_DIR: /home/runner/work/wazuh-docker/wazuh-docker/docker-images
run: |
docker load --input ./wazuh-manager.tar
docker load --input ./wazuh-indexer.tar
docker load --input ./wazuh-dashboard.tar
docker load --input ./wazuh-agent.tar
rm -rf wazuh-manager.tar wazuh-indexer.tar wazuh-dashboard.tar wazuh-agent.tar
mkdir -p "${ARTIFACTS_LOCAL_DIR}"
echo "Downloading and loading Docker image artifacts from S3..."
while IFS= read -r artifact; do
[ -z "$artifact" ] && continue
echo " Downloading: $artifact"
aws s3 cp "${S3_ARTIFACTS_PATH}/${artifact}" "${ARTIFACTS_LOCAL_DIR}/${artifact}"
echo " Loading into Docker: $artifact"
docker load -i "${ARTIFACTS_LOCAL_DIR}/${artifact}"
done <<< "$ARTIFACT_NAMES"
echo "All artifacts downloaded and loaded successfully."
- name: Create multi node certficates
run: docker compose -f multi-node/generate-indexer-certs.yml run --rm generator
@@ -81,6 +81,8 @@ on:
permissions:
id-token: write
contents: read
env:
LOCAL_ARTIFACT_URLS_FILEPATH: /tmp/${{ vars.ARTIFACT_URL_FILE_NAME }}
jobs:
setup:
@@ -166,7 +168,6 @@ jobs:
WORKFLOW_VENV: "${{ github.workspace }}/workflow_venv"
GENERATE_PRESIGNED_URLS_SCRIPT_PATH: ${{ github.workspace }}/wazuh-automation/tools/sign_urls/generate_presigned_dev_urls.py
PRESIGNED_URLS_SCRIPT_PROCESS: "build_docker"
LOCAL_ARTIFACT_URLS_FILEPATH: /tmp/${{ vars.ARTIFACT_URL_FILE_NAME }}
COMMIT_LIST: ${{ inputs.commit_list }}
ASSISTANT_REVISION: ${{ inputs.assistant_revision }}
@@ -284,10 +285,9 @@ jobs:
- name: Save presigned URLs file to artifact
if: ${{ inputs.dev == true }}
uses: actions/upload-artifact@v4
with:
name: presigned-artifact-urls-${{ github.run_id }}
path: ${{ env.LOCAL_ARTIFACT_URLS_FILEPATH }}
run: |
echo "Uploading presigned URLs artifact..."
aws s3 cp "${{ env.LOCAL_ARTIFACT_URLS_FILEPATH }}" "s3://${{ secrets.CI_DEV_INTERNAL_S3_BUCKET }}/wazuh-docker/5_build_and_push_images/${{ github.run_id }}/${{ vars.ARTIFACT_URL_FILE_NAME }}"
build-and-push:
runs-on: codebuild-github-actions-codebuild-runner-devops-amd-${{ github.run_id }}-${{ github.run_attempt }}
@@ -337,10 +337,10 @@ jobs:
- name: Download artifact_urls.yaml (dev)
if: ${{ inputs.dev == true }}
uses: actions/download-artifact@v4
with:
name: presigned-artifact-urls-${{ github.run_id }}
path: ./build-docker-images
run: |
echo "Downloading presigned URLs artifact..."
aws s3 cp "s3://${{ secrets.CI_DEV_INTERNAL_S3_BUCKET }}/wazuh-docker/5_build_and_push_images/${{ github.run_id }}/${{ vars.ARTIFACT_URL_FILE_NAME }}" "${{ env.LOCAL_ARTIFACT_URLS_FILEPATH }}"
mv "${{ env.LOCAL_ARTIFACT_URLS_FILEPATH }}" ./build-docker-images/${{ vars.ARTIFACT_URL_FILE_NAME }}
- name: Compute component reference (dev)
if: ${{ inputs.dev == true }}
+30 -10
View File
@@ -37,6 +37,11 @@ on:
default: false
required: false
type: boolean
bump-issue-link:
description: 'Issue link used in the original bump (required for revert if different from issue-link)'
required: false
type: string
jobs:
bump:
name: Repository bumper 5.x
@@ -96,18 +101,24 @@ jobs:
version=${{ env.VERSION }}
stage=${{ env.STAGE }}
tag=${{ env.TAG }}
set_as_main=${{ inputs.set_as_main }}
if [[ "$set_as_main" == "true" ]]; then
script_params="--set-as-main"
if [[ -n "$version" && -n "$stage" && "$tag" != "true" ]]; then
script_params="--version ${version} --stage ${stage}"
elif [[ -n "$version" && -n "$stage" && "$tag" == "true" ]]; then
script_params="--version ${version} --stage ${stage} --tag"
elif [[ -z "$version" && -n "$stage" && "$tag" == "true" ]]; then
script_params="--stage ${stage} --tag"
elif [[ -z "$version" && -z "$stage" && "$tag" == "true" ]]; then
script_params="--tag"
fi
# Both version and stage provided
if [[ -n "$version" && -n "$stage" && "$tag" != "true" ]]; then
script_params+=" --version ${version} --stage ${stage}"
elif [[ -n "$version" && -n "$stage" && "$tag" == "true" ]]; then
script_params+=" --version ${version} --stage ${stage} --tag ${tag}"
if [[ "$set_as_main" == "true" ]]; then
if [[ -z "$version" || -z "$stage" ]]; then
echo "Error: set_as_main requires both version and stage inputs."
exit 1
fi
script_params="${script_params} --set-as-main"
fi
issue_number=$(echo "${{ inputs.issue-link }}" | awk -F'/' '{print $NF}')
@@ -146,9 +157,18 @@ jobs:
id: revert_step
if: inputs.revert == true
run: |
# 1. Get the current issue number (for the new revert branch/PR)
ISSUE_NUMBER=$(echo "${{ inputs.issue-link }}" | awk -F'/' '{print $NF}')
BUMP_BRANCH="enhancement/wqa${ISSUE_NUMBER}-bump-${{ github.ref_name }}"
# 2. Get the issue number from the original bump (if provided; otherwise, defaults to the current one)
if [ -n "${{ inputs.bump-issue-link }}" ]; then
BUMP_ISSUE_NUMBER=$(echo "${{ inputs.bump-issue-link }}" | awk -F'/' '{print $NF}')
else
BUMP_ISSUE_NUMBER=$ISSUE_NUMBER
fi
# 3. Search for the original bump branch using the obtained BUMP ISSUE number
BUMP_BRANCH="enhancement/wqa${BUMP_ISSUE_NUMBER}-bump-${{ github.ref_name }}"
PR_NUMBER=$(gh pr list --head "$BUMP_BRANCH" --base "${{ github.ref_name }}" --state merged --json number --jq '.[0].number')
@@ -167,7 +187,7 @@ jobs:
# Remove the files to prevent them from being included in the revert commit
git checkout HEAD -- VERSION.json 2>/dev/null || true
git checkout HEAD -- CHANGELOG.md 2>/dev/null || true
# Add any other repository-specific version files here
# [!] ADD ANY OTHER REPOSITORY-SPECIFIC VERSION FILES HERE [!]
if git diff --staged --quiet; then
echo "No references to revert. Skipping commit."
+33 -24
View File
@@ -53,6 +53,7 @@ env:
AUTOMATION_REFERENCE: ${{ inputs.automation_reference || 'main' }}
ALLOCATOR_PATH: /tmp/allocator_instance
REGION: us-east-1
LOGS_ARTIFACT_ZIP_FILE: "docker_logs_artifacts_${{ github.run_id }}.zip"
jobs:
# -------------------------------------------------------------------------
@@ -176,8 +177,8 @@ jobs:
echo "deployment_matrix=[\"${DEPLOY_TYPE}\"]" >> $GITHUB_OUTPUT
fi
else
echo "pr_head_ref=${{ needs.get_pr_info.outputs.pr_head_ref }}" >> $GITHUB_OUTPUT
echo "deployment_matrix=${{ needs.get_pr_info.outputs.deployment_matrix }}" >> $GITHUB_OUTPUT
echo 'pr_head_ref=${{ needs.get_pr_info.outputs.pr_head_ref }}' >> $GITHUB_OUTPUT
echo 'deployment_matrix=${{ needs.get_pr_info.outputs.deployment_matrix }}' >> $GITHUB_OUTPUT
fi
- name: Checkout wazuh-docker PR branch (VERSION.json only)
@@ -409,6 +410,7 @@ jobs:
pip install -r wazuh-automation/deployability/deps/requirements.txt
pip install -r wazuh-automation/integration-test-module/requirements.txt
pip install -e wazuh-automation/integration-test-module/
pip install pyyaml
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
@@ -417,6 +419,19 @@ jobs:
role-session-name: docker-test-${{ github.run_id }}-${{ matrix.deployment_type }}
aws-region: ${{ env.REGION }}
- name: Generate presigned cert tool URL
run: |
python wazuh-automation/tools/sign_urls/generate_presigned_dev_urls.py \
--process build_docker \
--wazuh-version "${{ env.DOCKER_VERSION }}" \
--aws-s3-bucket-dev "${{ vars.AWS_S3_BUCKET_DEV }}"
python3 -c "
import yaml
data = yaml.safe_load(open('/tmp/artifact_urls.yaml'))
print(f'wazuh_certs_tool={data[\"wazuh_certs_tool\"]}')
" >> "$GITHUB_ENV"
# -----------------------------------------------------------------------
# Provision: allocate VM and extract SSH credentials
# -----------------------------------------------------------------------
@@ -502,20 +517,10 @@ jobs:
- name: Prepare cert tool and config
run: |
DEPLOYMENT="${{ matrix.deployment_type }}"
VERSION="${{ env.WAZUH_VERSION }}"
STAGE="${{ env.WAZUH_STAGE }}"
MAJOR=$(echo "$VERSION" | cut -d. -f1)
echo "Cert tool: ${VERSION}-${STAGE} Docker image: ${{ env.DOCKER_TAG }}"
echo "Cert tool: ${{ env.wazuh_certs_tool }} Docker image: ${{ env.DOCKER_TAG }}"
# Download cert tool once on the runner
if [ -n "$STAGE" ]; then
CERT_TOOL_URL="https://packages-staging.xdrsiem.wazuh.info/pre-release/${MAJOR}.x/installation-assistant/wazuh-certs-tool-${VERSION}-${STAGE}.sh"
else
CERT_TOOL_URL="https://packages.wazuh.com/${MAJOR}.$(echo "$VERSION" | cut -d. -f2)/wazuh-certs-tool-${VERSION}-1.sh"
fi
echo "Downloading cert tool: $CERT_TOOL_URL"
curl -fsSL -o "wazuh-docker/${DEPLOYMENT}/wazuh-certs-tool.sh" "$CERT_TOOL_URL"
curl --output "wazuh-docker/${DEPLOYMENT}/wazuh-certs-tool.sh" "${{ env.wazuh_certs_tool }}"
chmod +x "wazuh-docker/${DEPLOYMENT}/wazuh-certs-tool.sh"
echo "Downloaded OK"
@@ -732,11 +737,10 @@ jobs:
- name: Upload Docker logs
if: failure() || steps.run_tests.outcome == 'failure'
uses: actions/upload-artifact@v4
with:
name: docker-logs-${{ matrix.deployment_type }}-${{ github.run_id }}
path: docker-logs-*.txt
retention-days: 7
run: |
echo "Uploading Docker logs artifact..."
zip "${{ env.LOGS_ARTIFACT_ZIP_FILE }}" docker-logs-*.txt
aws s3 cp "${{ env.LOGS_ARTIFACT_ZIP_FILE }}" "s3://${{ secrets.CI_DEV_INTERNAL_S3_BUCKET }}/wazuh-docker/5_check_integration_tools/${{ github.run_id }}/${{ env.LOGS_ARTIFACT_ZIP_FILE }}"
# -----------------------------------------------------------------------
# Reporting
@@ -810,11 +814,16 @@ jobs:
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-docker-${{ matrix.deployment_type }}-${{ github.run_id }}
path: test-results-docker-${{ matrix.deployment_type }}.github
retention-days: 7
env:
S3_ARTIFACTS_PATH: s3://${{ secrets.CI_DEV_INTERNAL_S3_BUCKET }}/wazuh-docker/5_check_integration_tools/${{ github.run_id }}
LOCAL_RESULTS_PATH: test-results-docker-${{ matrix.deployment_type }}.github
run: |
if [ -f "${LOCAL_RESULTS_PATH}" ]; then
echo "Uploading test results to S3..."
aws s3 cp "${LOCAL_RESULTS_PATH}" "${S3_ARTIFACTS_PATH}/test-results-docker-${{ matrix.deployment_type }}/"
else
echo "::warning::No test results file found - skipping upload (an earlier step likely failed before test_runner produced output)."
fi
# -----------------------------------------------------------------------
# Cleanup: always stop stack and deallocate VM
-664
View File
@@ -1,664 +0,0 @@
name: (5.x) Docker PR check
permissions:
contents: read
id-token: write
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
branches:
- 5.*
- main
paths:
- 'build-docker-images/**'
- 'multi-node/**'
- 'single-node/**'
- 'wazuh-agent/**'
- '.github/**'
workflow_dispatch:
inputs:
docker_reference:
description: 'Branch or tag to build from'
required: true
type: string
jobs:
prepare-variables:
if: ${{ !github.event.pull_request.draft }}
runs-on: codebuild-github-actions-codebuild-runner-devops-amd-${{ github.run_id }}-${{ github.run_attempt }}
outputs:
WAZUH_VERSION: ${{ steps.dotenv.outputs.WAZUH_VERSION }}
WAZUH_IMAGE_VERSION: ${{ steps.dotenv.outputs.WAZUH_IMAGE_VERSION }}
WAZUH_REGISTRY: ${{ vars.IMAGE_REGISTRY_DEV }}
IMAGE_TAG: ${{ steps.dotenv.outputs.IMAGE_TAG }}
WAZUH_MINOR_VERSION: ${{ steps.dotenv.outputs.WAZUH_MINOR_VERSION }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Export .env variables
id: dotenv
shell: bash
run: |
if [ ! -f .env ]; then echo "::error::.env missing"; exit 1; fi
grep -v '^#' .env | grep -v '^\s*$' >> "$GITHUB_OUTPUT"
FULL_VERSION=$(grep "^WAZUH_VERSION=" .env | cut -d'=' -f2)
MINOR_VERSION=$(echo "$FULL_VERSION" | cut -d'.' -f1,2)
echo "WAZUH_MINOR_VERSION=$MINOR_VERSION" >> "$GITHUB_OUTPUT"
build-images:
needs: prepare-variables
uses: ./.github/workflows/5_build_and_push_images.yml
secrets: inherit
with:
image_tag: ${{ needs.prepare-variables.outputs.WAZUH_IMAGE_VERSION }}
docker_reference: ${{ github.head_ref || inputs.docker_reference }}
wazuh_automation_reference: 'main'
commit_list: '["latest", "latest", "latest", "latest"]'
assistant_revision: 'latest'
id: ${{ github.run_id }}
dev: true
Execute-Goss-tests:
needs: [prepare-variables, build-images]
runs-on: codebuild-github-actions-codebuild-runner-devops-amd-${{ github.run_id }}-${{ github.run_attempt }}
env:
WAZUH_IMAGE_VERSION: ${{ needs.prepare-variables.outputs.WAZUH_IMAGE_VERSION }}
WAZUH_REGISTRY: ${{ needs.prepare-variables.outputs.WAZUH_REGISTRY }}
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Install Goss
uses: e1himself/goss-installation-action@v1.0.3
with:
version: 'v0.4.4'
- name: Configure aws credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_IAM_DOCKER_ROLE }}
aws-region: "${{ secrets.AWS_REGION }}"
- name: Log in to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2
- name: Execute Goss tests (wazuh-manager)
run: dgoss run ${{ env.WAZUH_REGISTRY }}/wazuh/wazuh-manager:${{ env.WAZUH_IMAGE_VERSION }}-latest
env:
GOSS_SLEEP: 30
GOSS_FILE: .github/.goss.yaml
check-single-node:
name: Check single node on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: ["codebuild-github-actions-codebuild-runner-devops-amd-${{ github.run_id }}-${{ github.run_attempt }}", "codebuild-github-actions-codebuild-runner-devops-arm-${{ github.run_id }}-${{ github.run_attempt }}"]
fail-fast: false
needs: [prepare-variables, Execute-Goss-tests]
env:
WAZUH_IMAGE_VERSION: ${{ needs.prepare-variables.outputs.WAZUH_IMAGE_VERSION }}
WAZUH_MINOR_VERSION: ${{ needs.prepare-variables.outputs.WAZUH_MINOR_VERSION }}
WAZUH_REGISTRY: ${{ needs.prepare-variables.outputs.WAZUH_REGISTRY }}
INDEXER_USERNAME: admin
INDEXER_PASSWORD: admin
MANAGER_NODES: "manager"
API_USERNAME: wazuh-wui
API_PASSWORD: wazuh-wui
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Configure aws credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_IAM_DOCKER_ROLE }}
aws-region: "${{ secrets.AWS_REGION }}"
- name: Log in to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2
- name: Download artifact_urls.yaml
uses: actions/download-artifact@v4
with:
name: presigned-artifact-urls-${{ github.run_id }}
path: ./single-node/
- name: Log in to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Add environment variables into GITHUB_ENV
run: |
# Export variables to the environment
awk -F':' '!/^#/ && NF>1 {name=$1; val=substr($0,length(name)+3); gsub(/[-.]/,"_",name); print name "=" val}' ${{ vars.ARTIFACT_URL_FILE_NAME }} >> "$GITHUB_ENV"
working-directory: ./single-node/
- name: Create single node certficates
run: |
curl --output ./wazuh-certs-tool.sh "${{ env.wazuh_certs_tool }}"
cat > config.yml <<EOF
nodes:
# Wazuh indexer server nodes
indexer:
- name: wazuh.indexer
dns: "wazuh.indexer"
# Wazuh manager nodes
# Use node_type only with more than one Wazuh manager
manager:
- name: wazuh.manager
dns: "wazuh.manager"
# Wazuh dashboard node
dashboard:
- name: wazuh.dashboard
dns: "wazuh.dashboard"
EOF
cat config.yml
sudo bash ../tools/utils/deployment/certificates-conf.sh --cert --copy --priv
sudo sysctl -w vm.max_map_count=262144
working-directory: ./single-node
- name: Edit single node docker-compose file
shell: bash
env:
WAZUH_REGISTRY: ${{ env.WAZUH_REGISTRY }}
run: |
TARGET_FILE="single-node/docker-compose.yml"
if [ -f "$TARGET_FILE" ]; then
echo "Updating registry in $TARGET_FILE to: ${{ env.WAZUH_REGISTRY }}"
sed -i "s|wazuh/wazuh-|${{ env.WAZUH_REGISTRY }}/wazuh/wazuh-|g" "$TARGET_FILE"
sed -i "s/\(.*wazuh\/wazuh-.*:\)${{ env.WAZUH_IMAGE_VERSION }}/\1${{ env.WAZUH_IMAGE_VERSION }}-latest/g" "$TARGET_FILE"
else
echo "File $TARGET_FILE not found"
exit 1
fi
- name: Start single node stack
id: start_single_node_stack
run: docker compose up -d
working-directory: ./single-node
- name: Check Wazuh indexer start
if: ${{ always() && steps.start_single_node_stack.outcome == 'success' }}
run: |
for i in {1..20}; do
echo "Checking Wazuh indexer health (Attempt $i/20)"
RESPONSE=$(curl -XGET "https://127.0.0.1:9200/_cluster/health?pretty" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s --retry 2 || true)
INDEXER_CONTAINERS=$(docker ps --format '{{.Names}}' | grep "indexer")
if echo "$RESPONSE" | grep -qE "green|yellow"; then
echo "Cluster Online"
echo "$RESPONSE"
exit 0
fi
echo "Waiting for cluster to be online"
for CONTAINER_NAME in $INDEXER_CONTAINERS; do
echo ""
echo "========================================================="
echo "Container logs for $CONTAINER_NAME"
echo "========================================================="
docker logs --tail 30 "$CONTAINER_NAME"
echo "---------------------------------------------------------"
done
[ $i -lt 20 ] && sleep 60
done
status_index="`curl -XGET "https://127.0.0.1:9200/_cat/indices" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s | wc -l`"
status_index_green="`curl -XGET "https://127.0.0.1:9200/_cat/indices" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s | grep -E "green|yellow" | wc -l`"
if [[ $status_index_green -eq $status_index ]]; then
curl -XGET "https://127.0.0.1:9200/_cat/indices" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s
else
curl -XGET "https://127.0.0.1:9200/_cat/indices" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s
exit 1
fi
- name: Check Wazuh indexer nodes
if: ${{ always() && steps.start_single_node_stack.outcome == 'success' }}
run: |
nodes="`curl -XGET "https://127.0.0.1:9200/_cat/nodes" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s | grep -E "indexer" | wc -l`"
echo "Wazuh indexer nodes: ${nodes}"
- name: Check Wazuh templates
if: ${{ always() && steps.start_single_node_stack.outcome == 'success' }}
run: |
qty_templates="`curl -XGET "https://127.0.0.1:9200/_cat/templates" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s | grep -P "wazuh|wazuh-agent|wazuh-statistics" | wc -l`"
templates="`curl -XGET "https://127.0.0.1:9200/_cat/templates" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s | grep -P "wazuh|wazuh-agent|wazuh-statistics"`"
if [[ $qty_templates -gt 3 ]]; then
echo "wazuh templates:"
echo "${templates}"
else
echo "wazuh templates:"
echo "${templates}"
exit 1
fi
- name: Check Wazuh manager start
if: ${{ always() && steps.start_single_node_stack.outcome == 'success' }}
run: |
for NODE in "${{ env.MANAGER_NODES }}"; do
ok=false
for i in {1..20}; do
TOKEN=$(curl -s -u ${{ env.API_USERNAME }}:${{ env.API_PASSWORD }} -k -X POST "https://127.0.0.1:55000/security/user/authenticate?raw=true")
services="`curl -k -s -X GET "https://127.0.0.1:55000/cluster/$NODE/status?pretty=true" -H "Authorization: Bearer ${TOKEN}" | jq -r .data.affected_items | grep running | wc -l`"
if [[ $services -gt 7 ]]; then
echo "Wazuh Manager $NODE Services: ${services}"
echo "OK"
ok=true
break
else
curl -k -X GET "https://127.0.0.1:55000/cluster/$NODE/status?pretty=true" -H "Authorization: Bearer ${TOKEN}" | jq -r .data.affected_items
echo "Wazuh Manager $NODE Services: ${services}. Retrying in 30s"
[ $i -lt 20 ] && sleep 30
fi
done
if [[ "$ok" != "true" ]]; then
echo "Error: Wazuh Manager $NODE did not reach expected running services threshold"
exit 1
fi
done
- name: Check Wazuh dashboard service URL
if: ${{ always() && steps.start_single_node_stack.outcome == 'success' }}
run: |
for i in {1..20}; do
echo "Checking Wazuh dashboard (Attempt $i/20)"
STATUS=$(curl -k -s -o /dev/null -w "%{http_code}" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} "https://127.0.0.1:443/app/status" || true)
echo "Current status: $STATUS"
if [[ "$STATUS" == "200" ]]; then
echo "Wazuh dashboard is UP"
exit 0
elif [[ "$STATUS" == "429" || "$STATUS" == "503" ]]; then
echo "Dashboard is busy or initializing (Status $STATUS). Retrying in 30s"
else
echo "Unexpected status $STATUS. Retrying in 30s"
fi
sleep 30
done
echo "Error: Dashboard did not reach 200 status in time."
exit 1
- name: Modify Docker endpoint into Wazuh agent docker-compose.yml file
if: ${{ always() && steps.start_single_node_stack.outcome == 'success' }}
run: sed -i "s/<WAZUH_MANAGER_IP>/$(ip addr show docker0 | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1)/g" wazuh-agent/docker-compose.yml
- name: Edit Wazuh agent docker-compose file
if: ${{ always() && steps.start_single_node_stack.outcome == 'success' }}
shell: bash
env:
WAZUH_REGISTRY: ${{ env.WAZUH_REGISTRY }}
run: |
TARGET_FILE="wazuh-agent/docker-compose.yml"
if [ -f "$TARGET_FILE" ]; then
echo "Updating registry in $TARGET_FILE to: ${{ env.WAZUH_REGISTRY }}"
sed -i "s|wazuh/wazuh-|${{ env.WAZUH_REGISTRY }}/wazuh/wazuh-|g" "$TARGET_FILE"
sed -i "s/\(.*wazuh\/wazuh-.*:\)${{ env.WAZUH_IMAGE_VERSION }}/\1${{ env.WAZUH_IMAGE_VERSION }}-latest/g" "$TARGET_FILE"
else
echo "File $TARGET_FILE not found"
exit 1
fi
- name: Start Wazuh agent
if: ${{ always() && steps.start_single_node_stack.outcome == 'success' }}
run: docker compose up -d
working-directory: ./wazuh-agent
- name: Check Wazuh agent enrollment
if: ${{ always() && steps.start_single_node_stack.outcome == 'success' }}
run: |
enrolled=false
for i in {1..5}; do
TOKEN=$(curl -s -u ${{ env.API_USERNAME }}:${{ env.API_PASSWORD }} -k -X POST "https://127.0.0.1:55000/security/user/authenticate?raw=true")
agents="`curl -k -s -X GET "https://127.0.0.1:55000/agents?pretty=true" -H "Authorization: Bearer ${TOKEN}" | jq -r .data.affected_items | grep active | wc -l`"
if [[ $agents -gt 0 ]]; then
echo "Wazuh agents: ${agents}"
echo "OK"
enrolled=true
break
else
curl -k -s -X GET "https://127.0.0.1:55000/agents?pretty=true" -H "Authorization: Bearer ${TOKEN}"
echo "Wazuh agents: ${agents}. Retrying in 10s"
[ $i -lt 5 ] && sleep 10
fi
done
if [[ "$enrolled" != "true" ]]; then
echo "Error: Wazuh agent enrollment did not reach expected active agents threshold"
exit 1
fi
- name: Check errors in wazuh-manager.log for Wazuh manager
if: ${{ always() && steps.start_single_node_stack.outcome == 'success' }}
run: ./.github/single-node-log-check.sh
- name: Check documents into wazuh-states index
if: ${{ always() && steps.start_single_node_stack.outcome == 'success' }}
run: |
for i in {1..20}; do
echo "Checking documents in wazuh-states (Attempt $i/20)..."
RESPONSE=$(curl -XGET "https://127.0.0.1:9200/wazuh-states*/_count" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s || echo "{}")
DOCS=$(echo "$RESPONSE" | jq -r '.count // 0')
if [[ "$DOCS" -gt 0 ]]; then
echo "wazuh-states index has documents: ${DOCS}"
exit 0
fi
echo "The index is empty or does not exist yet (Count: $DOCS). Waiting 60s"
[ $i -lt 20 ] && sleep 60
done
echo "Error: No documents found in wazuh-states after 20 attempts."
echo "Last response: $RESPONSE"
exit 1
- name: Docker logs
if: always()
continue-on-error: true
run: |
INDEXER_CONTAINERS=$(docker ps --format '{{.Names}}')
for CONTAINER_NAME in $INDEXER_CONTAINERS; do
echo ""
echo "========================================================="
echo "Container logs for $CONTAINER_NAME"
echo "========================================================="
docker logs "$CONTAINER_NAME"
echo "---------------------------------------------------------"
done
working-directory: ./single-node
check-multi-node:
name: Check multi node on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: ["codebuild-github-actions-codebuild-runner-devops-amd-${{ github.run_id }}-${{ github.run_attempt }}", "codebuild-github-actions-codebuild-runner-devops-arm-${{ github.run_id }}-${{ github.run_attempt }}"]
fail-fast: false
needs: [prepare-variables, Execute-Goss-tests]
env:
WAZUH_IMAGE_VERSION: ${{ needs.prepare-variables.outputs.WAZUH_IMAGE_VERSION }}
WAZUH_MINOR_VERSION: ${{ needs.prepare-variables.outputs.WAZUH_MINOR_VERSION }}
WAZUH_REGISTRY: ${{ needs.prepare-variables.outputs.WAZUH_REGISTRY }}
INDEXER_USERNAME: admin
INDEXER_PASSWORD: admin
MANAGER_NODES: "master,worker01"
API_USERNAME: wazuh-wui
API_PASSWORD: wazuh-wui
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Configure aws credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_IAM_DOCKER_ROLE }}
aws-region: "${{ secrets.AWS_REGION }}"
- name: Log in to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2
- name: Download artifact_urls.yaml
uses: actions/download-artifact@v4
with:
name: presigned-artifact-urls-${{ github.run_id }}
path: ./multi-node/
- name: Log in to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Add environment variables into GITHUB_ENV
run: |
# Export variables to the environment
awk -F':' '!/^#/ && NF>1 {name=$1; val=substr($0,length(name)+3); gsub(/[-.]/,"_",name); print name "=" val}' ${{ vars.ARTIFACT_URL_FILE_NAME }} >> "$GITHUB_ENV"
working-directory: ./multi-node/
- name: Create multi node certficates
run: |
curl --output ./wazuh-certs-tool.sh "${{ env.wazuh_certs_tool }}"
cat > config.yml <<EOF
nodes:
# Wazuh indexer server nodes
indexer:
- name: wazuh1.indexer
dns: "wazuh1.indexer"
- name: wazuh2.indexer
dns: "wazuh2.indexer"
- name: wazuh3.indexer
dns: "wazuh3.indexer"
# Wazuh manager nodes
# Use node_type only with more than one Wazuh manager
manager:
- name: wazuh.master
dns: "wazuh.master"
node_type: master
- name: wazuh.worker
dns: "wazuh.worker"
node_type: worker
# Wazuh dashboard node
dashboard:
- name: wazuh.dashboard
dns: "wazuh.dashboard"
EOF
cat config.yml
sudo bash ../tools/utils/deployment/certificates-conf.sh --cert --copy --priv
sudo sysctl -w vm.max_map_count=262144
working-directory: ./multi-node
- name: Edit multi node docker-compose file
shell: bash
run: |
TARGET_FILE="multi-node/docker-compose.yml"
if [ -f "$TARGET_FILE" ]; then
echo "Updating registry in $TARGET_FILE to: ${{ env.WAZUH_REGISTRY }}"
sed -i "s|wazuh/wazuh-|${{ env.WAZUH_REGISTRY }}/wazuh/wazuh-|g" "$TARGET_FILE"
sed -i "s/\(.*wazuh\/wazuh-.*:\)${{ env.WAZUH_IMAGE_VERSION }}/\1${{ env.WAZUH_IMAGE_VERSION }}-latest/g" "$TARGET_FILE"
else
echo "File $TARGET_FILE not found"
exit 1
fi
- name: Start multi node stack
id: start_multi_node_stack
run: docker compose up -d
working-directory: ./multi-node
- name: Check Wazuh indexer start
if: ${{ always() && steps.start_multi_node_stack.outcome == 'success' }}
run: |
for i in {1..20}; do
echo "Checking Wazuh indexer health (Attempt $i/20)"
RESPONSE=$(curl -XGET "https://127.0.0.1:9200/_cluster/health?pretty" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s --retry 2 || true)
INDEXER_CONTAINERS=$(docker ps --format '{{.Names}}' | grep "indexer")
if echo "$RESPONSE" | grep -qE "green|yellow"; then
echo "Cluster Online"
echo "$RESPONSE"
exit 0
fi
echo "Waiting for cluster to be online"
for CONTAINER_NAME in $INDEXER_CONTAINERS; do
echo ""
echo "========================================================="
echo "Container logs for $CONTAINER_NAME"
echo "========================================================="
docker logs --tail 30 "$CONTAINER_NAME"
echo "---------------------------------------------------------"
done
[ $i -lt 20 ] && sleep 60
done
status_index="`curl -XGET "https://127.0.0.1:9200/_cat/indices" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s | wc -l`"
status_index_green="`curl -XGET "https://127.0.0.1:9200/_cat/indices" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s | grep -E "green" | wc -l`"
if [[ $status_index_green -eq $status_index ]]; then
curl -XGET "https://127.0.0.1:9200/_cat/indices" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s
else
curl -XGET "https://127.0.0.1:9200/_cat/indices" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s
exit 1
fi
- name: Check Wazuh indexer nodes
if: ${{ always() && steps.start_multi_node_stack.outcome == 'success' }}
run: |
nodes="`curl -XGET "https://127.0.0.1:9200/_cat/nodes" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s | grep -E "indexer" | wc -l`"
if [[ $nodes -eq 3 ]]; then
echo "Wazuh indexer nodes: ${nodes}"
else
echo "Wazuh indexer nodes: ${nodes}"
exit 1
fi
- name: Check Wazuh templates
if: ${{ always() && steps.start_multi_node_stack.outcome == 'success' }}
run: |
qty_templates="`curl -XGET "https://127.0.0.1:9200/_cat/templates" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s | grep "wazuh" | wc -l`"
templates="`curl -XGET "https://127.0.0.1:9200/_cat/templates" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s | grep "wazuh"`"
if [[ $qty_templates -gt 3 ]]; then
echo "wazuh templates:"
echo "${templates}"
else
echo "wazuh templates:"
echo "${templates}"
exit 1
fi
- name: Check Wazuh manager start
if: ${{ always() && steps.start_multi_node_stack.outcome == 'success' }}
run: |
IFS=',' read -r -a NODES <<< "${{ env.MANAGER_NODES }}"
for NODE in "${NODES[@]}"; do
if [[ "$NODE" == "master" ]]; then
THRESHOLD=8
else
THRESHOLD=7
fi
ok=false
for i in {1..20}; do
TOKEN=$(curl -s -u ${{ env.API_USERNAME }}:${{ env.API_PASSWORD }} -k -X POST "https://127.0.0.1:55000/security/user/authenticate?raw=true")
services="`curl -k -s -X GET "https://127.0.0.1:55000/cluster/$NODE/status?pretty=true" -H "Authorization: Bearer ${TOKEN}" | jq -r .data.affected_items | grep running | wc -l`"
if [[ $services -ge $THRESHOLD ]]; then
echo "Wazuh Manager $NODE Services: ${services}"
echo "OK"
ok=true
break
else
curl -k -X GET "https://127.0.0.1:55000/cluster/$NODE/status?pretty=true" -H "Authorization: Bearer ${TOKEN}" | jq -r .data.affected_items
echo "Wazuh Manager $NODE Services: ${services}. Retrying in 30s"
[ $i -lt 20 ] && sleep 30
fi
done
if [[ "$ok" != "true" ]]; then
echo "Error: Wazuh Manager $NODE did not reach expected running services threshold"
exit 1
fi
done
- name: Check Wazuh dashboard service URL
if: ${{ always() && steps.start_multi_node_stack.outcome == 'success' }}
run: |
for i in {1..20}; do
echo "Checking Wazuh dashboard (Attempt $i/20)"
STATUS=$(curl -k -s -o /dev/null -w "%{http_code}" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} "https://127.0.0.1:443/app/status" || true)
echo "Current status: $STATUS"
if [[ "$STATUS" == "200" ]]; then
echo "Wazuh dashboard is UP"
exit 0
elif [[ "$STATUS" == "429" || "$STATUS" == "503" ]]; then
echo "Dashboard is busy or initializing (Status $STATUS). Retrying in 30s"
else
echo "Unexpected status $STATUS. Retrying in 30s"
fi
sleep 30
done
echo "Error: Dashboard did not reach 200 status in time."
exit 1
- name: Modify Docker endpoint into Wazuh agent docker-compose.yml file
if: ${{ always() && steps.start_multi_node_stack.outcome == 'success' }}
run: sed -i "s/<WAZUH_MANAGER_IP>/$(ip addr show docker0 | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1)/g" wazuh-agent/docker-compose.yml
- name: Edit Wazuh agent docker-compose file
if: ${{ always() && steps.start_multi_node_stack.outcome == 'success' }}
shell: bash
env:
WAZUH_REGISTRY: ${{ env.WAZUH_REGISTRY }}
run: |
TARGET_FILE="wazuh-agent/docker-compose.yml"
if [ -f "$TARGET_FILE" ]; then
echo "Updating registry in $TARGET_FILE to: ${{ env.WAZUH_REGISTRY }}"
sed -i "s|wazuh/wazuh-|${{ env.WAZUH_REGISTRY }}/wazuh/wazuh-|g" "$TARGET_FILE"
sed -i "s/\(.*wazuh\/wazuh-.*:\)${{ env.WAZUH_IMAGE_VERSION }}/\1${{ env.WAZUH_IMAGE_VERSION }}-latest/g" "$TARGET_FILE"
else
echo "File $TARGET_FILE not found"
exit 1
fi
- name: Start Wazuh agent
if: ${{ always() && steps.start_multi_node_stack.outcome == 'success' }}
run: docker compose -f wazuh-agent/docker-compose.yml up -d
- name: Check Wazuh agent enrollment
if: ${{ always() && steps.start_multi_node_stack.outcome == 'success' }}
run: |
enrolled=false
for i in {1..5}; do
TOKEN=$(curl -s -u ${{ env.API_USERNAME }}:${{ env.API_PASSWORD }} -k -X POST "https://127.0.0.1:55000/security/user/authenticate?raw=true")
agents="`curl -k -s -X GET "https://127.0.0.1:55000/agents?pretty=true" -H "Authorization: Bearer ${TOKEN}" | jq -r .data.affected_items | grep active | wc -l`"
if [[ $agents -gt 0 ]]; then
echo "Wazuh agents: ${agents}"
echo "OK"
enrolled=true
break
else
curl -k -s -X GET "https://127.0.0.1:55000/agents?pretty=true" -H "Authorization: Bearer ${TOKEN}"
echo "Wazuh agents: ${agents}. Retrying in 10s"
[ $i -lt 5 ] && sleep 10
fi
done
if [[ "$enrolled" != "true" ]]; then
echo "Error: Wazuh agent enrollment did not reach expected active agents threshold"
exit 1
fi
- name: Check errors in wazuh-manager.log for Wazuh manager
if: ${{ always() && steps.start_multi_node_stack.outcome == 'success' }}
run: ./.github/multi-node-log-check.sh
- name: Check documents into wazuh-states index
if: ${{ always() && steps.start_multi_node_stack.outcome == 'success' }}
run: |
for i in {1..20}; do
echo "Checking documents in wazuh-states (Attempt $i/20)..."
RESPONSE=$(curl -XGET "https://127.0.0.1:9200/wazuh-states*/_count" -u ${{ env.INDEXER_USERNAME }}:${{ env.INDEXER_PASSWORD }} -k -s || echo "{}")
DOCS=$(echo "$RESPONSE" | jq -r '.count // 0')
if [[ "$DOCS" -gt 0 ]]; then
echo "wazuh-states index has documents: ${DOCS}"
exit 0
fi
echo "The index is empty or does not exist yet (Count: $DOCS). Waiting 60s"
[ $i -lt 20 ] && sleep 60
done
echo "Error: No documents found in wazuh-states after 20 attempts."
echo "Last response: $RESPONSE"
exit 1
- name: Docker logs
if: always()
continue-on-error: true
run: |
INDEXER_CONTAINERS=$(docker ps --format '{{.Names}}')
for CONTAINER_NAME in $INDEXER_CONTAINERS; do
echo ""
echo "========================================================="
echo "Container logs for $CONTAINER_NAME"
echo "========================================================="
docker logs "$CONTAINER_NAME"
echo "---------------------------------------------------------"
done
working-directory: ./multi-node
+6
View File
@@ -30,3 +30,9 @@
- [Security](ref/security.md)
- [Performance](ref/performance.md)
- [Glossary](ref/glossary.md)
---
# Integration Tests
- [Docker Integration Tests](ref/integration_test/docker_integration_tests.md)
@@ -0,0 +1,332 @@
# Docker Integration Tests
Workflow file: `.github/workflows/5_check_integration_tools.yml`
This workflow optionally builds Docker images from the PR branch, provisions a dedicated AWS VM, deploys the Wazuh Docker stack (single-node or multi-node), and runs the integration test suite against it via SSH.
---
## Triggers
| Mode | Trigger | Who can trigger |
|---|---|---|
| PR comment | `issue_comment` on an open, non-draft PR | Any repo collaborator |
| Manual | `workflow_dispatch` | Anyone with repo write access |
---
## Execution Flows
### issue_comment flow
```mermaid
flowchart TD
A[PR comment posted] --> B{Recognized command\non open non-draft PR?}
B -- No --> Z[Ignored]
B -- Yes --> C[get_pr_info\nReact · Extract PR data\nParse command · Create Check Run]
C --> D[prepare\nResolve branch · Read VERSION.json]
D --> E[build_images\nBuild + push to ECR\nalways runs on PR comment]
E --> F{deployment_matrix}
F --> G[docker_test\nsingle-node]
F --> H[docker_test\nmulti-node]
G --> I[update_check]
H --> I
```
**Recognized commands:**
| Comment | Deployment matrix |
|---|---|
| `/test-docker` | `["single-node","multi-node"]` |
| `/test-docker-single` | `["single-node"]` |
| `/test-docker-multi` | `["multi-node"]` |
When triggered by PR comment, `build_images` **always** runs — images are always built from the PR branch and pushed to ECR.
### workflow_dispatch flow
```mermaid
flowchart TD
A[Manual trigger] --> D[prepare\nResolve branch · Read VERSION.json]
D --> E{Build needed?\nno version + no stage\n+ registry=ECR}
E -- Yes --> F[build_images\nBuild + push to ECR]
E -- No --> G{deployment_type input}
F --> G
G -- single-node --> H[docker_test\nsingle-node]
G -- multi-node --> I[docker_test\nmulti-node]
G -- both --> H & I
```
`build_images` is **skipped** when either `version` or `stage` is provided, or when `registry = DockerHub`.
---
## Parameters
### workflow_dispatch inputs
| Input | Required | Default | Description |
|---|---|---|---|
| `pr_head_ref` | Yes | — | Branch of `wazuh-docker` to test |
| `automation_reference` | No | `main` | Branch of `wazuh-automation` to use |
| `deployment_type` | Yes | — | `single-node`, `multi-node`, or `both` |
| `version` | No | — | Override image version (e.g. `5.0.1`). If empty, reads from `VERSION.json` |
| `stage` | No | — | Image stage suffix (e.g. `beta1`, `beta2-latest`). Required when `version` is set |
| `registry` | No | `ECR` | `ECR` (dev/built images) or `DockerHub` (released images) |
### issue_comment parameters
All parameters are derived automatically:
| Parameter | Source |
|---|---|
| `pr_head_ref` | PR head branch from GitHub API |
| `deployment_matrix` | Parsed from comment command |
| `version` / `stage` | Read from `VERSION.json` on the PR branch |
| `registry` | Always ECR (images are always built) |
| `automation_reference` | Always `main` |
---
## Image Resolution Scenarios
The workflow distinguishes five cases based on inputs:
| Case | `version` input | `stage` input | Registry | Action | Image tag |
|---|---|---|---|---|---|
| a.1 | empty | empty | ECR (or PR comment) | **BUILD** from PR → ECR | `{version}-{stage}-latest` |
| a.2 | empty | empty | DockerHub | Pull (no build) | `{version}-{stage}` |
| b.1 | set | empty | ECR | Pull (no build) | `{version}-latest` |
| b.2 | set | empty | DockerHub | Pull (no build) | `{version}` |
| c | set or empty | set | ECR or DockerHub | Pull (no build) | `{version}-{stage}` |
> When neither `version` nor `stage` is set, `version` and `stage` are read from `VERSION.json` on the target branch.
> Case a.1 always applies when triggered by PR comment, regardless of the `registry` input (which is not available in that trigger mode).
---
## Job Details
### Job 1 — `get_pr_info` (issue_comment only)
| Step | What it does |
|---|---|
| React to comment | Adds a 🚀 reaction to the triggering PR comment |
| Extract PR data | Calls GitHub API to get PR `head_ref` and `head_sha` |
| Parse command | Maps comment text → `deployment_matrix` JSON and `check_name` string |
| Create Check Run | Creates a GitHub Check Run in `in_progress` state on the PR head SHA |
### Job 2 — `prepare` (both triggers)
| Step | What it does |
|---|---|
| Resolve context | Reads inputs (workflow_dispatch) or `get_pr_info` outputs (issue_comment) |
| Checkout `VERSION.json` | Sparse-checks out only `VERSION.json` from the target branch |
| Read version info | Extracts `version` and `stage` from `VERSION.json` |
| Show test plan | Logs the resolved image case (a.1/a.2/b.1/b.2/c) and writes a summary table |
Outputs: `pr_head_ref`, `deployment_matrix`, `wazuh_version`, `wazuh_stage`.
### Job 3 — `build_images` (conditional)
Calls the reusable workflow `.github/workflows/5_build_and_push_images.yml`.
**Runs when:** `version == ''` AND `stage == ''` AND (`registry == 'ECR'` OR `github.event_name == 'issue_comment'`).
**Skipped when:** any explicit `version` or `stage` is provided, or `registry = DockerHub`.
| Parameter passed | Value |
|---|---|
| `image_tag` | `{wazuh_version}-{wazuh_stage}` |
| `docker_reference` | `pr_head_ref` |
| `wazuh_automation_reference` | `automation_reference` input |
| `products` | `wazuh-manager,wazuh-dashboard,wazuh-indexer,wazuh-agent` |
| `dev` | `true` |
| `id` | `docker-integration-{run_id}` |
### Job 4 — `docker_test` (matrix, both triggers)
Runs once per entry in `deployment_matrix`. Each instance provisions its own VM.
#### Setup
1. Checkout `wazuh-automation` at `automation_reference`
2. Checkout `wazuh-docker` at `pr_head_ref`
3. Resolve image configuration (see [Image Resolution Scenarios](#image-resolution-scenarios)) → sets `DOCKER_REGISTRY`, `DOCKER_TAG`, `DOCKER_VERSION`
4. Set up Python 3.12 and install `test_runner`
5. Configure AWS credentials via OIDC (`AWS_IAM_DOCKER_ROLE`)
#### Instance allocation
Provisions a dedicated AWS VM using the `deployability` allocator module:
```bash
python3 wazuh-automation/deployability/modules/allocation/main.py \
--action create \
--provider aws \
--size large \
--composite-name ubuntu-24-amd64 \
--instance-name gha_{run_id}_docker_{deployment_type} \
--label-team devops \
--label-termination-date 1d
```
The allocator writes `inventory.yml` with the SSH connection details (`ansible_host`, `ansible_port`, `ansible_user`, `ansible_ssh_private_key_file`). These are extracted and exported as `SSH_HOST`, `SSH_PORT`, `SSH_USER`, `SSH_KEY` environment variables.
#### VM configuration and Docker install
All subsequent steps run on the remote VM over SSH:
1. **Install Docker CE**: `curl -fsSL https://get.docker.com | sudo sh`
2. **Login to ECR** (when registry is ECR or trigger is issue_comment): authenticates the VM's Docker daemon to the dev registry
3. **Set `vm.max_map_count=262144`**: required for OpenSearch/Wazuh Indexer
#### Certificate generation and config
Runs on the **runner** (not the VM):
1. **Download `wazuh-certs-tool.sh`** directly from the packages URL:
- Pre-release: `packages-staging.xdrsiem.wazuh.info/pre-release/{major}.x/installation-assistant/wazuh-certs-tool-{version}-{stage}.sh`
- Release: `packages.wazuh.com/{major}.{minor}/wazuh-certs-tool-{version}-1.sh`
2. **Generate `config.yml`** inline based on deployment type:
**single-node:**
```yaml
nodes:
indexer: [{ name: wazuh.indexer, dns: wazuh.indexer }]
manager: [{ name: wazuh.manager, dns: wazuh.manager }]
dashboard:[{ name: wazuh.dashboard, dns: wazuh.dashboard }]
```
**multi-node:**
```yaml
nodes:
indexer:
- { name: wazuh1.indexer, dns: wazuh1.indexer }
- { name: wazuh2.indexer, dns: wazuh2.indexer }
- { name: wazuh3.indexer, dns: wazuh3.indexer }
manager:
- { name: wazuh.master, dns: wazuh.master, node_type: master }
- { name: wazuh.worker, dns: wazuh.worker, node_type: worker }
dashboard: [{ name: wazuh.dashboard, dns: wazuh.dashboard }]
```
3. **Copy `wazuh-docker/` to VM** via SCP: `scp -r wazuh-docker {remote}:/tmp/wazuh-docker`
4. **Generate certificates on VM**: runs `tools/utils/deployment/certificates-conf.sh --cert --copy` inside `/tmp/wazuh-docker/{deployment}/`
#### Deployment
```bash
# On the VM
cd /tmp/wazuh-docker/{deployment_type}
sudo docker compose up -d
```
Waits up to **15 minutes** polling every 10 seconds until all non-nginx containers report `healthy` status.
After containers are healthy, waits for steady state:
- `single-node`: 60 seconds
- `multi-node`: 90 seconds
#### Test execution
```bash
test_runner \
--test-type "docker-{deployment_type}" \
--deployment-type "docker-{deployment_type}" \
--ssh-host "{SSH_HOST}" \
--ssh-port "{SSH_PORT}" \
--ssh-key-path "{SSH_KEY}" \
--ssh-username "{SSH_USER}" \
--version "{DOCKER_VERSION}" \
--log-level INFO \
--output github \
--output-file "test-results-docker-{deployment_type}.github"
```
| Argument | Value | Notes |
|---|---|---|
| `--test-type` | `docker-single-node` or `docker-multi-node` | Selects the test module set |
| `--deployment-type` | `docker-single-node` or `docker-multi-node` | Selects the deployment profile |
| `--ssh-host/port/key/username` | From allocator inventory | Connects to the allocated VM |
| `--version` | Resolved `DOCKER_VERSION` | Used for version assertion tests |
| `--output github` | — | Emits GitHub Actions annotations |
For details on what `docker-single-node` and `docker-multi-node` test types validate, see the `Integration Test Module — Description` of the internal documentation.
#### Reporting
| Output | When | Content |
|---|---|---|
| Step summary | Always | Test results appended to `$GITHUB_STEP_SUMMARY` |
| PR comment | `issue_comment` trigger only | Posts or updates a comment (marker: `<!-- docker-integration-check-{deployment} -->`) with ✅/❌ and results |
| Artifact: `test-results-docker-{deployment}-{run_id}` | Always | Results file, retained 7 days |
| Artifact: `docker-logs-{deployment}-{run_id}` | On failure only | Full `docker compose logs` output, retained 7 days |
#### Cleanup (always runs, even on failure)
1. `docker compose down -v` on the VM (stops containers and removes volumes)
2. Deallocate the VM:
```bash
python3 wazuh-automation/deployability/modules/allocation/main.py \
--action delete \
--track-output {ALLOCATOR_PATH}/track.yml
```
### Job 5 — `update_check` (issue_comment only)
Updates the GitHub Check Run created in Job 1:
| `docker_test` result | Check conclusion |
|---|---|
| `success` | `success` — ✅ All Docker integration tests passed |
| `failure` | `failure` — ❌ One or more tests failed |
| `cancelled` | `cancelled` |
---
## Required Secrets and Variables
### Secrets
| Secret | Used by |
|---|---|
| `AWS_IAM_DOCKER_ROLE` | OIDC role for AWS operations (allocator + ECR) |
| `GH_CLONE_TOKEN` | Checkout `wazuh-automation` |
| `GITHUB_TOKEN` | PR comments and Check Run updates (built-in) |
### Repository variables
| Variable | Used by |
|---|---|
| `IMAGE_REGISTRY_PROD` | DockerHub registry URL |
| `IMAGE_REGISTRY_DEV` | ECR registry URL |
---
## Permissions
| Permission | Purpose |
|---|---|
| `id-token: write` | OIDC authentication to AWS |
| `contents: read` | Checkout repository |
| `pull-requests: write` | Post PR comments |
| `issues: write` | Post comments via issues API |
| `checks: write` | Create and update GitHub Check Runs |
---
## Instance Naming
Allocated VMs are named:
```
gha_{github.run_id}_docker_{deployment_type}
```
Example: `gha_12345678_docker_single-node`
VMs are tagged with `termination-date: 1d` — they are automatically terminated after 24 hours as a safety net, even if the cleanup step fails.
+80 -56
View File
@@ -9,6 +9,8 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
LOG_FILE="${DIR}/tools/repository_bumper_$(date +"%Y-%m-%d_%H-%M-%S-%3N").log"
VERSION=""
STAGE=""
TAG=""
REFERENCE=""
FILES_EDITED=()
FILES_EXCLUDED='--exclude="repository_bumper_*.log" --exclude="CHANGELOG.md" --exclude="repository_bumper.sh" --exclude="*_bumper_repository.yml" --exclude="mermaid-init.js" --exclude="mermaid.min.js"'
@@ -74,40 +76,44 @@ update_stage_in_files() {
FILES_EDITED+=("${file}")
fi
done
}
if [ $STAGE != "alpha0" ]; then
version_tag_string=": 'v${VERSION}'"
files_tag=( $(grep_command "${version_tag_string}" "${DIR}") )
for file in "${files_tag[@]}"; do
sed -i -E "s/(: )'v${VERSION}'/\1'v${VERSION}-${STAGE}'/g" "${file}"
if [[ $(git diff --name-only "${file}") ]]; then
FILES_EDITED+=("${file}")
fi
done
version_number_string=": '${VERSION}'"
files_version=( $(grep -RlE ": '[0-9]\.[0-9]+\.[0-9]+'" "${DIR}") )
for file in "${files_version[@]}"; do
sed -i -E "s/(: )'${VERSION}'/\1'v${VERSION}-${STAGE}'/g" "${file}"
if [[ $(git diff --name-only "${file}") ]]; then
FILES_EDITED+=("${file}")
fi
done
# Compute the value written into branch reference defaults ("<key>: '...'").
# Without --tag, references stay branch-like (e.g. 5.0.0).
# With --tag, references become tag-like (e.g. v5.0.0-beta3), or a plain release
# tag (e.g. v5.0.0) when no stage is provided.
build_reference() {
if [[ -n "$TAG" ]]; then
if [[ -z "$STAGE" ]]; then
REFERENCE="v${VERSION}"
else
REFERENCE="v${VERSION}-${STAGE}"
fi
else
REFERENCE="${VERSION}"
fi
}
# Tag mode only: normalize every reference to the current version
# (branch-like "5.0.0", "v5.0.0" or "v5.0.0-<stage>") into ${REFERENCE}.
# Matching is restricted to "<key>: '...'" entries so plain version strings
# elsewhere in the repository are left untouched.
update_tag_references() {
local V_ESC="${VERSION//./\\.}"
files=( $(grep_command "${VERSION}" "${DIR}") )
for file in "${files[@]}"; do
sed -Ei "s/(:[[:space:]]*')v?${V_ESC}(-[A-Za-z0-9]+)?(')/\1${REFERENCE}\3/g" "${file}"
if [[ $(git diff --name-only "${file}") ]]; then
FILES_EDITED+=("${file}")
fi
done
}
update_main_in_files() {
if [[ $STAGE == "alpha0" ]]; then
bump_value="${VERSION}"
else
bump_value="v${VERSION}"
fi
main_string=": 'main'"
local main_string=": 'main'"
files=( $(grep_command "${main_string}" "${DIR}") )
for file in "${files[@]}"; do
if [[ "$skip_urls" != "yes" ]]; then
sed -Ei "s/(:[[:space:]])'main'/\1'${bump_value}'/g" "${file}"
fi
sed -Ei "s/(:[[:space:]])'main'/\1'${REFERENCE}'/g" "${file}"
if [[ $(git diff --name-only "${file}") ]]; then
FILES_EDITED+=("${file}")
fi
@@ -141,8 +147,8 @@ main() {
shift 2
;;
--tag)
TAG="$2"
shift 2
TAG="yes"
shift 1
;;
--set-as-main)
set_as_main="yes"
@@ -155,15 +161,33 @@ main() {
esac
done
# Validate arguments
if [[ -z "${VERSION}" ]]; then
echo "Error: --version argument is required." | tee -a "${LOG_FILE}"
# --tag rewrites branch references into tag-like references (e.g. v5.0.0-beta3)
# and re-tags the Docker images accordingly. It is mutually exclusive with
# --set-as-main, which keeps references on main.
if [[ -n "$TAG" && -n "$set_as_main" ]]; then
echo "Error: --tag cannot be combined with --set-as-main." | tee -a "${LOG_FILE}"
exit 1
fi
if [[ -z "${STAGE}" ]]; then
echo "Error: --stage argument is required." | tee -a "${LOG_FILE}"
exit 1
# Read the current version/stage early: tag scenarios may omit --version and/or
# --stage and reuse the values already stored in VERSION.json.
get_old_version_and_stage
# Resolve and validate arguments depending on the mode
if [[ -n "$TAG" ]]; then
# Tag mode: version defaults to the current one; stage is optional
# (absent yields a release tag without a stage suffix).
[[ -z "$VERSION" ]] && VERSION="$OLD_VERSION"
else
# Branch mode: a full version + stage bump is required
if [[ -z "${VERSION}" ]]; then
echo "Error: --version argument is required." | tee -a "${LOG_FILE}"
exit 1
fi
if [[ -z "${STAGE}" ]]; then
echo "Error: --stage argument is required." | tee -a "${LOG_FILE}"
exit 1
fi
fi
# Validate if version is in the correct format
@@ -172,28 +196,25 @@ main() {
exit 1
fi
# Validate if stage is in the correct format
STAGE=$(echo "${STAGE}" | tr '[:upper:]' '[:lower:]')
if ! [[ "${STAGE}" =~ ^(alpha[0-9]*|beta[0-9]*|rc[0-9]*|stable)$ ]]; then
echo "Error: Stage must be one of the following examples: alpha1, beta1, rc1, stable." | tee -a "${LOG_FILE}"
exit 1
# Validate if stage is in the correct format (when provided)
if [[ -n "${STAGE}" ]]; then
STAGE=$(echo "${STAGE}" | tr '[:upper:]' '[:lower:]')
if ! [[ "${STAGE}" =~ ^(alpha[0-9]*|beta[0-9]*|rc[0-9]*|stable)$ ]]; then
echo "Error: Stage must be one of the following examples: alpha1, beta1, rc1, stable." | tee -a "${LOG_FILE}"
exit 1
fi
fi
# Set skip_urls variable based on set_as_main flag
# Compute the value written into branch reference defaults
build_reference
echo "Reference for branch defaults: ${REFERENCE}" | tee -a "${LOG_FILE}"
# Convert 'main' references unless they must keep pointing to main (set-as-main)
if [[ -z "$set_as_main" ]]; then
echo "Updating version from main to $VERSION" | tee -a "${LOG_FILE}"
update_main_in_files "$VERSION" "$STAGE"
echo "Updating 'main' references to ${REFERENCE}" | tee -a "${LOG_FILE}"
update_main_in_files
fi
# Validate if tag is true or false
if [[ -n "${TAG}" && ! "${TAG}" =~ ^(true|false)$ ]]; then
echo "Error: --tag must be either true or false." | tee -a "${LOG_FILE}"
exit 1
fi
# Get old version and stage
get_old_version_and_stage
if [[ "${OLD_VERSION}" != "${VERSION}" ]]; then
echo "Updating version from ${OLD_VERSION} to ${VERSION}" | tee -a "${LOG_FILE}"
update_version_in_files "${VERSION}"
@@ -203,10 +224,13 @@ main() {
update_stage_in_files "$VERSION" "$STAGE"
fi
# Update Docker images tag if tag is true
if [[ "${TAG}" == "true" ]]; then
echo "Updating Docker images tag to ${VERSION}-${STAGE}" | tee -a "${LOG_FILE}"
update_docker_images_tag "${VERSION}-${STAGE}"
# Tag mode: normalize remaining version references and re-tag the Docker images
# (image tags carry no leading 'v', e.g. 5.0.0-beta3).
if [[ -n "$TAG" ]]; then
echo "Updating version references to tag reference ${REFERENCE}" | tee -a "${LOG_FILE}"
update_tag_references
echo "Updating Docker images tag to ${REFERENCE#v}" | tee -a "${LOG_FILE}"
update_docker_images_tag "${REFERENCE#v}"
fi