Skip to content

test: fix tests aftersuite #3889

test: fix tests aftersuite

test: fix tests aftersuite #3889

Workflow file for this run

# Copyright 2025 Flant JSC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: E2E Matrix Tests (nested clusters)
on:
workflow_dispatch:
pull_request:
branches:
- main
- test/fix/tests
# schedule:
# - cron: "40 4 * * *"
concurrency:
group: "${{ github.workflow }}-${{ github.event.number || github.ref }}"
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
cleanup-nested-clusters:
if: github.event_name != 'pull_request'
name: Cleanup nested clusters
runs-on: ubuntu-latest
steps:
- name: Configure kubectl via azure/k8s-set-context@v4
uses: azure/k8s-set-context@v4
with:
method: kubeconfig
context: e2e-cluster-nightly-e2e-virt-sa
kubeconfig: ${{ secrets.VIRT_E2E_NIGHTLY_SA_TOKEN }}
- name: Delete nested clusters
run: |
current_date_seconds="$(date -u +%s)"
FORMAT="%-63s %22s\n"
collect_items_json() {
local resource="$1"
kubectl get "${resource}" -l test=nightly-e2e -o json \
| jq -c '.items[] | {name: .metadata.name, created_at: .metadata.creationTimestamp}'
}
should_keep() {
local created_at="$1"
local recourse_created_at age_days weekday_of_day
recourse_created_at_seconds="$(date -d "${created_at}" -u +%s)"
age_days="$(( (current_date_seconds - recourse_created_at_seconds) / 86400 ))"
weekday_of_day="$(date -d "${created_at}" -u +%u)"
if [ "${age_days}" -lt 2 ]; then
echo "keep"
return 0
fi
if [ "${weekday_of_day}" -eq 5 ] && [ "${age_days}" -lt 4 ]; then
echo "keep"
return 0
fi
echo "delete"
return 0
}
cleanup_kind() {
local kind="$1"
local item name created_at decision parsed
echo "[INFO] Process ${kind} with label test=nightly-e2e"
collect_items_json "${kind}" | while read -r item; do
name=$(echo $item | jq -r '.name')
created_at=$(echo $item | jq -r '.created_at')
[ -z "${name}" ] && continue
decision="$(should_keep "${created_at}")"
if [ "${decision}" = "keep" ]; then
printf "$FORMAT" "[INFO] Keep ${kind}/${name}:" "created_at ${created_at}"
continue
fi
printf "$FORMAT" "[INFO] Delete ${kind}/${name}:" "created_at ${created_at}"
kubectl delete "${kind}" "${name}" --timeout=300s || true
done || true
}
cleanup_kind "namespaces"
echo " "
cleanup_kind "vmclass"
set-vars:
name: Set vars
runs-on: ubuntu-latest
outputs:
date_start: ${{ steps.vars.outputs.date-start }}
randuuid4c: ${{ steps.vars.outputs.randuuid4c }}
steps:
- name: Set vars
id: vars
run: |
echo "date-start=$(date +%Y%m%d-%H%M%S)" >> $GITHUB_OUTPUT
echo "randuuid4c=$(openssl rand -hex 2)" >> $GITHUB_OUTPUT
e2e-ceph:
name: E2E Pipeline (Ceph)
needs:
# - cleanup-nested-clusters
- set-vars
uses: ./.github/workflows/e2e-reusable-pipeline.yml
with:
storage_type: ceph
nested_storageclass_name: nested-ceph-pool-r2-csi-rbd
branch: main
virtualization_tag: main
deckhouse_channel: alpha
default_user: cloud
go_version: "1.24.13"
e2e_timeout: "3.5h"
date_start: ${{ needs.set-vars.outputs.date_start }}
randuuid4c: ${{ needs.set-vars.outputs.randuuid4c }}
cluster_config_workers_memory: "8Gi"
secrets:
DEV_REGISTRY_DOCKER_CFG: ${{ secrets.DEV_REGISTRY_DOCKER_CFG }}
VIRT_E2E_NIGHTLY_SA_TOKEN: ${{ secrets.VIRT_E2E_NIGHTLY_SA_TOKEN }}
PROD_IO_REGISTRY_DOCKER_CFG: ${{ secrets.PROD_IO_REGISTRY_DOCKER_CFG }}
BOOTSTRAP_DEV_PROXY: ${{ secrets.BOOTSTRAP_DEV_PROXY }}
e2e-replicated:
if: github.event_name != 'pull_request'
name: E2E Pipeline (Replicated)
needs:
# - cleanup-nested-clusters
- set-vars
uses: ./.github/workflows/e2e-reusable-pipeline.yml
with:
storage_type: replicated
nested_storageclass_name: nested-thin-r1
branch: main
virtualization_tag: main
deckhouse_channel: alpha
default_user: cloud
go_version: "1.24.13"
e2e_timeout: "3.5h"
date_start: ${{ needs.set-vars.outputs.date_start }}
randuuid4c: ${{ needs.set-vars.outputs.randuuid4c }}
cluster_config_workers_memory: "9Gi"
secrets:
DEV_REGISTRY_DOCKER_CFG: ${{ secrets.DEV_REGISTRY_DOCKER_CFG }}
VIRT_E2E_NIGHTLY_SA_TOKEN: ${{ secrets.VIRT_E2E_NIGHTLY_SA_TOKEN }}
PROD_IO_REGISTRY_DOCKER_CFG: ${{ secrets.PROD_IO_REGISTRY_DOCKER_CFG }}
BOOTSTRAP_DEV_PROXY: ${{ secrets.BOOTSTRAP_DEV_PROXY }}
report-to-channel:
runs-on: ubuntu-latest
name: End-to-End tests report
needs:
- e2e-ceph
# - e2e-replicated
if: ${{ always()}}
env:
STORAGE_TYPES: '["ceph", "replicated"]'
steps:
- uses: actions/checkout@v4
- name: Download E2E report artifacts
uses: actions/download-artifact@v5
continue-on-error: true
id: download-artifacts-pattern
with:
pattern: "e2e-report-*"
path: downloaded-artifacts/
merge-multiple: false
- name: Send results to channel
run: |
# Map storage types to CSI names
get_csi_name() {
local storage_type=$1
case "$storage_type" in
"ceph")
echo "rbd.csi.ceph.com"
;;
"replicated")
echo "replicated.csi.storage.deckhouse.io"
;;
*)
echo "$storage_type"
;;
esac
}
# Function to load and parse report from artifact
# Outputs: file content to stdout, debug messages to stderr
# Works with pattern-based artifact download (e2e-report-*)
# Artifacts are organized as: downloaded-artifacts/e2e-report-<storage_type>-<run_id>/e2e_report_<storage_type>.json
load_report_from_artifact() {
local storage_type=$1
local base_path="downloaded-artifacts/"
echo "[INFO] Searching for report for storage type: $storage_type" >&2
echo "[DEBUG] Base path: $base_path" >&2
if [ ! -d "$base_path" ]; then
echo "[WARN] Base path does not exist: $base_path" >&2
return 1
fi
local report_file=""
# First, search in artifact directories matching pattern: e2e-report-<storage_type>-*
# Pattern downloads create subdirectories named after the artifact
# e.g., downloaded-artifacts/e2e-report-ceph-<run_id>/e2e_report_ceph.json
echo "[DEBUG] Searching in artifact directories matching pattern: e2e-report-${storage_type}-*" >&2
local artifact_dir=$(find "$base_path" -type d -name "e2e-report-${storage_type}-*" 2>/dev/null | head -1)
if [ -n "$artifact_dir" ]; then
echo "[DEBUG] Found artifact dir: $artifact_dir" >&2
report_file=$(find "$artifact_dir" -name "e2e_report_*.json" -type f 2>/dev/null | head -1)
if [ -n "$report_file" ] && [ -f "$report_file" ]; then
echo "[INFO] Found report file in artifact dir: $report_file" >&2
cat "$report_file"
return 0
fi
fi
# Fallback: search for file by name pattern anywhere in base_path
echo "[DEBUG] Searching for file: e2e_report_${storage_type}.json" >&2
report_file=$(find "$base_path" -type f -name "e2e_report_${storage_type}.json" 2>/dev/null | head -1)
if [ -n "$report_file" ] && [ -f "$report_file" ]; then
echo "[INFO] Found report file by name: $report_file" >&2
cat "$report_file"
return 0
fi
echo "[WARN] Could not load report artifact for $storage_type" >&2
return 1
}
# Function to create failure summary JSON (fallback)
create_failure_summary() {
local storage_type=$1
local stage=$2
local run_id=$3
local csi=$(get_csi_name "$storage_type")
local date=$(date +"%Y-%m-%d")
local time=$(date +"%H:%M:%S")
local branch="${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}"
local link="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${run_id:-${GITHUB_RUN_ID}}"
# Map stage to status message
local status_msg
case "$stage" in
"bootstrap")
status_msg=":x: BOOTSTRAP CLUSTER FAILED"
;;
"storage-setup")
status_msg=":x: STORAGE SETUP FAILED"
;;
"virtualization-setup")
status_msg=":x: VIRTUALIZATION SETUP FAILED"
;;
"e2e-test")
status_msg=":x: E2E TEST FAILED"
;;
*)
status_msg=":question: UNKNOWN"
;;
esac
jq -n \
--arg csi "$csi" \
--arg date "$date" \
--arg time "$time" \
--arg branch "$branch" \
--arg status "$status_msg" \
--arg link "$link" \
'{CSI: $csi, Date: $date, StartTime: $time, Branch: $branch, Status: $status, Passed: 0, Failed: 0, Pending: 0, Skipped: 0, Link: $link}'
}
# Parse summary JSON and add to table
parse_summary() {
local summary_json=$1
local storage_type=$2
if [ -z "$summary_json" ] || [ "$summary_json" == "null" ] || [ "$summary_json" == "" ]; then
echo "Warning: Empty summary for $storage_type"
return
fi
# Try to parse as JSON (handle both JSON string and already parsed JSON)
if ! echo "$summary_json" | jq empty 2>/dev/null; then
echo "Warning: Invalid JSON for $storage_type: $summary_json"
echo "[DEBUG] json: $summary_json"
return
fi
# Parse JSON fields
csi_raw=$(echo "$summary_json" | jq -r '.CSI // empty' 2>/dev/null)
if [ -z "$csi_raw" ] || [ "$csi_raw" == "null" ] || [ "$csi_raw" == "" ]; then
csi=$(get_csi_name "$storage_type")
else
csi="$csi_raw"
fi
date=$(echo "$summary_json" | jq -r '.Date // ""' 2>/dev/null)
time=$(echo "$summary_json" | jq -r '.StartTime // ""' 2>/dev/null)
branch=$(echo "$summary_json" | jq -r '.Branch // ""' 2>/dev/null)
status=$(echo "$summary_json" | jq -r '.Status // ":question: UNKNOWN"' 2>/dev/null)
passed=$(echo "$summary_json" | jq -r '.Passed // 0' 2>/dev/null)
failed=$(echo "$summary_json" | jq -r '.Failed // 0' 2>/dev/null)
pending=$(echo "$summary_json" | jq -r '.Pending // 0' 2>/dev/null)
skipped=$(echo "$summary_json" | jq -r '.Skipped // 0' 2>/dev/null)
link=$(echo "$summary_json" | jq -r '.Link // ""' 2>/dev/null)
# Set defaults if empty
[ -z "$passed" ] && passed=0
[ -z "$failed" ] && failed=0
[ -z "$pending" ] && pending=0
[ -z "$skipped" ] && skipped=0
[ -z "$status" ] && status=":question: UNKNOWN"
# Format link - use CSI name as fallback if link is empty
if [ -z "$link" ] || [ "$link" == "" ]; then
link_text="$csi"
else
link_text="[:link: $csi]($link)"
fi
# Add row to table
markdown_table+="| $link_text | $status | $passed | $failed | $pending | $skipped | $date | $time | $branch |\n"
}
# Initialize markdown table
echo "[INFO] Generate markdown table"
markdown_table=""
header="| CSI | Status | Passed | Failed | Pending | Skipped | Date | Time | Branch|\n"
separator="|---|---|---|---|---|---|---|---|---|\n"
markdown_table+="$header"
markdown_table+="$separator"
# Get current date for header
DATE=$(date +"%Y-%m-%d")
COMBINED_SUMMARY="## :dvp: **DVP | E2E on a nested cluster | $DATE**\n\n"
echo "[INFO] Get storage types"
readarray -t storage_types < <(echo "$STORAGE_TYPES" | jq -r '.[]')
echo "[INFO] Storage types: " "${storage_types[@]}"
echo "[INFO] Generate summary for each storage type"
for storage in "${storage_types[@]}"; do
echo "[INFO] Processing $storage"
# Try to load report from artifact
# Debug messages go to stderr (visible in logs), JSON content goes to stdout
echo "[INFO] Attempting to load report for $storage"
structured_report=$(load_report_from_artifact "$storage" || true)
if [ -n "$structured_report" ]; then
# Check if it's valid JSON
if echo "$structured_report" | jq empty 2>/dev/null; then
echo "[INFO] Report is valid JSON for $storage"
else
echo "[WARN] Report is not valid JSON for $storage"
echo "[DEBUG] Raw report content (first 200 chars):"
echo "$structured_report" | head -c 200
echo ""
structured_report=""
fi
fi
if [ -n "$structured_report" ] && echo "$structured_report" | jq empty 2>/dev/null; then
# Extract report data from structured file
report_json=$(echo "$structured_report" | jq -c '.report // empty')
failed_stage=$(echo "$structured_report" | jq -r '.failed_stage // empty')
workflow_run_id=$(echo "$structured_report" | jq -r '.workflow_run_id // empty')
echo "[INFO] Loaded report for $storage (failed_stage: ${failed_stage}, run_id: ${workflow_run_id})"
# Validate and parse report
if [ -n "$report_json" ] && [ "$report_json" != "" ] && [ "$report_json" != "null" ]; then
if echo "$report_json" | jq empty 2>/dev/null; then
echo "[INFO] Found valid report for $storage"
parse_summary "$report_json" "$storage"
else
echo "[WARN] Invalid report JSON for $storage, using failed stage info"
# Fallback to failed stage
if [ -n "$failed_stage" ] && [ "$failed_stage" != "" ] && [ "$failed_stage" != "success" ]; then
failed_summary=$(create_failure_summary "$storage" "$failed_stage" "$workflow_run_id")
parse_summary "$failed_summary" "$storage"
else
csi=$(get_csi_name "$storage")
markdown_table+="| $csi | :warning: INVALID REPORT | 0 | 0 | 0 | 0 | — | — | — |\n"
fi
fi
else
# No report in structured file, use failed stage
if [ -n "$failed_stage" ] && [ "$failed_stage" != "" ] && [ "$failed_stage" != "success" ]; then
echo "[INFO] Stage '$failed_stage' failed for $storage"
failed_summary=$(create_failure_summary "$storage" "$failed_stage" "$workflow_run_id")
parse_summary "$failed_summary" "$storage"
else
csi=$(get_csi_name "$storage")
markdown_table+="| $csi | :warning: NO REPORT | 0 | 0 | 0 | 0 | — | — | — |\n"
fi
fi
else
# Artifact not found or invalid, show warning
echo "[WARN] Could not load report artifact for $storage"
csi=$(get_csi_name "$storage")
markdown_table+="| $csi | :warning: ARTIFACT NOT FOUND | 0 | 0 | 0 | 0 | — | — | — |\n"
fi
done
echo "[INFO] Combined summary"
COMBINED_SUMMARY+="${markdown_table}\n"
echo -e "$COMBINED_SUMMARY"
# Send to channel if webhook is configured
echo "[INFO] Send to webhook"
if [ -n "$LOOP_WEBHOOK_URL" ]; then
curl --request POST --header 'Content-Type: application/json' --data "{\"text\": \"${COMBINED_SUMMARY}\"}" "$LOOP_WEBHOOK_URL"
fi
env:
LOOP_WEBHOOK_URL: ${{ secrets.LOOP_TEST_CHANNEL }}