2222permissions :
2323 contents : read
2424
25+ env :
26+ E2E_K8S_URL : https://api.e2e.virtlab.flant.com
27+
2528jobs :
29+ # ============================================
30+ # 1. SETUP - Environment preparation
31+ # ============================================
32+ setup :
33+ name : Setup Environment
34+ runs-on : ubuntu-latest
35+ outputs :
36+ profiles : ${{ steps.load.outputs.profiles }}
37+ steps :
38+ - uses : actions/checkout@v4
39+
40+ - name : Load storage profiles
41+ id : load
42+ run : |
43+ # For now, use hardcoded profiles. Later this can be made dynamic
44+ PROFILES='["sds", "cephrbd"]'
45+ echo "profiles=$PROFILES" >> "$GITHUB_OUTPUT"
46+
47+ - name : Print matrix
48+ run : |
49+ echo "Will test profiles: ${{ steps.load.outputs.profiles }}"
50+
51+ # ============================================
52+ # 2. VALIDATE - Configuration validation
53+ # ============================================
54+ validate :
55+ name : Validate Configuration
56+ needs : setup
57+ runs-on : ubuntu-latest
58+ steps :
59+ - uses : actions/checkout@v4
60+
61+ - name : Validate YAML syntax
62+ run : |
63+ echo "✓ Checking YAML syntax..."
64+ python3 -c "import yaml; yaml.safe_load(open('ci/dvp-e2e/values.yaml')); print('✅ values.yaml YAML syntax is valid')"
65+ python3 -c "import yaml; yaml.safe_load(open('.github/workflows/e2e-matrix.yml')); print('✅ e2e-matrix.yml YAML syntax is valid')"
66+
67+ - name : Check required secrets
68+ run : |
69+ echo "✓ Checking required secrets..."
70+ if [ -z "${{ secrets.E2E_VIRTUALIZATION_SA_SECRET }}" ]; then
71+ echo "❌ E2E_VIRTUALIZATION_SA_SECRET secret is required"
72+ exit 1
73+ fi
74+ echo "✅ Required secrets are present"
75+ echo "✅ E2E_K8S_URL is set as workflow variable: ${{ vars.E2E_K8S_URL }}"
76+
77+ # ============================================
78+ # 3. E2E - Parallel test execution
79+ # ============================================
2680 e2e :
2781 name : E2E (${{ matrix.profile }})
82+ needs : [setup, validate]
2883 runs-on : ubuntu-latest
2984 timeout-minutes : 300
3085 concurrency :
@@ -33,11 +88,13 @@ jobs:
3388 strategy :
3489 fail-fast : false
3590 matrix :
36- profile : [sds, cephrbd]
37-
91+ profile : ${{ fromJson(needs.setup.outputs.profiles) }}
92+
3893 env :
3994 GO_VERSION : ' 1.24.6'
4095 TMP_ROOT : ${{ github.workspace }}/ci/dvp-e2e/tmp
96+ LOOP_WEBHOOK : ${{ secrets.LOOP_WEBHOOK_URL || secrets.LOOP_WEBHOOK }}
97+ LOOP_CHANNEL : ${{ secrets.LOOP_CHANNEL || 'test-virtualization-loop-alerts' }} # TODO: replace with channel secret after successful run
4198
4299 steps :
43100 - uses : actions/checkout@v4
@@ -47,48 +104,29 @@ jobs:
47104 with :
48105 go-version : ${{ env.GO_VERSION }}
49106
50- - name : Install deps (kubectl, helm, d8, task, utils)
51- run : |
52- set -euo pipefail
53- sudo apt-get update
54- sudo apt-get install -y jq apache2-utils curl bash ca-certificates
55- curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
56- sudo install -m 0755 kubectl /usr/local/bin/kubectl
57- curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
58- curl -fsSL -o d8-install.sh https://raw.githubusercontent.com/deckhouse/deckhouse-cli/main/d8-install.sh
59- bash d8-install.sh
60- curl -L -o yq_linux_amd64 https://github.com/mikefarah/yq/releases/download/v4.44.1/yq_linux_amd64
61- sudo install -m 0755 yq_linux_amd64 /usr/local/bin/yq
62- rm -f yq_linux_amd64
63- curl -fsSL https://taskfile.dev/install.sh | sh -s -- -d -b /usr/local/bin
64- task --version
65-
66- - name : Prepare env
107+ - uses : ./.github/actions/setup-e2e-tools
108+
109+ - name : Prepare environment
67110 id : prep
68111 run : |
69112 RUN_ID="nightly-${{ matrix.profile }}-${{ github.run_id }}"
70113 echo "run_id=$RUN_ID" >> "$GITHUB_OUTPUT"
71114 echo "RUN_ID=$RUN_ID" >> "$GITHUB_ENV"
72115 echo "PROFILE=${{ matrix.profile }}" >> "$GITHUB_ENV"
73- if [ "${{ matrix.profile }}" = "cephrbd" ]; then
74- echo "SC=ceph-pool-r2-csi-rbd" >> "$GITHUB_ENV"
75- else
76- echo "SC=linstor-thin-r2" >> "$GITHUB_ENV"
77- fi
78116 echo "TMP_ROOT=${{ env.TMP_ROOT }}" >> "$GITHUB_ENV"
79117 mkdir -p "${{ env.TMP_ROOT }}/shared" "${{ env.TMP_ROOT }}/matrix-logs"
80118
81- - name : Build parent kubeconfig from SA secret
119+ - name : Build parent kubeconfig
82120 shell : bash
83121 run : |
84- if [ -n "${{ secrets .E2E_K8S_URL }}" ] && [ -n "${{ secrets.E2E_VIRTUALIZATION_SA_SECRET }}" ]; then
122+ if [ -n "${{ vars .E2E_K8S_URL }}" ] && [ -n "${{ secrets.E2E_VIRTUALIZATION_SA_SECRET }}" ]; then
85123 KCFG='${{ env.TMP_ROOT }}/shared/parent-admin.conf'
86124 cat > "$KCFG" <<EOF
87125 apiVersion: v1
88126 kind: Config
89127 clusters:
90128 - cluster:
91- server: ${{ secrets .E2E_K8S_URL }}
129+ server: ${{ vars .E2E_K8S_URL }}
92130 insecure-skip-tls-verify: true
93131 name: parent
94132 contexts:
@@ -110,57 +148,72 @@ jobs:
110148 exit 1
111149 fi
112150
113- # d8 already installed in deps step
114-
115-
116-
117- - name : Run one E2E (Taskfile nested:test-run)
151+ - name : Run E2E tests
118152 working-directory : ci/dvp-e2e
119153 env :
120154 KUBECONFIG : ${{ env.PARENT_KUBECONFIG_FILE }}
121155 REGISTRY_DOCKER_CFG : ${{ secrets.REGISTRY_DOCKER_CFG }}
122- LOOP_WEBHOOK : ${{ secrets.LOOP_WEBHOOK_URL || secrets.LOOP_WEBHOOK }}
123- LOOP_CHANNEL : ${{ secrets.LOOP_CHANNEL || 'test-virtualization-loop-alerts' }}
124156 TMP_ROOT : ${{ env.TMP_ROOT }}
125157 PARENT_KUBECONFIG_FILE : ${{ env.PARENT_KUBECONFIG_FILE }}
126158 E2E_DIR : ${{ github.workspace }}/tests/e2e
127159 run : |
128- task nested:test-run \
129- RUN_ID= "${RUN_ID}" \
130- STORAGE_PROFILE= "${{ matrix.profile }}" \
131- TIMEOUT='${{ inputs.timeout || '4h' }}' \
132- JUNIT_PATH="../../artifacts/${RUN_ID}/junit.xml"
160+ ./bin/e2e-runner \
161+ --run-id "${RUN_ID}" \
162+ --timeout "${{ inputs.timeout || '4h' }}" \
163+ --junit "../../artifacts/${RUN_ID}/junit.xml" \
164+ ${{ matrix.profile }}
133165
134- - name : Upload matrix logs
166+ - name : Upload test logs
135167 if : always()
136168 uses : actions/upload-artifact@v4
137169 with :
138- name : dvp- matrix-logs -${{ steps.prep.outputs.run_id }}
170+ name : logs-${{ matrix.profile }} -${{ steps.prep.outputs.run_id }}
139171 path : ci/dvp-e2e/tmp/matrix-logs/*.log
140172 if-no-files-found : warn
141173
142- - name : Upload junit artifacts
174+ - name : Upload JUnit report
143175 if : always()
144176 uses : actions/upload-artifact@v4
145177 with :
146- name : junit-${{ steps.prep.outputs.run_id }}
178+ name : junit-${{ matrix.profile }}-${{ steps.prep.outputs.run_id }}
147179 path : ci/dvp-e2e/artifacts/**/junit.xml
148180 if-no-files-found : warn
149181
150- - name : Send JUnit to Loop (optional)
182+ # ============================================
183+ # 4. REPORT - Result aggregation
184+ # ============================================
185+ report :
186+ name : Report Results
187+ needs : e2e
188+ if : always()
189+ runs-on : ubuntu-latest
190+ steps :
191+ - uses : actions/checkout@v4
192+
193+ - name : Download all JUnit reports
194+ uses : actions/download-artifact@v4
195+ with :
196+ pattern : junit-*
197+ path : ./results
198+
199+ - name : Send individual reports to Loop
151200 if : ${{ always() && (secrets.LOOP_WEBHOOK_URL != '' || secrets.LOOP_WEBHOOK != '') }}
152201 working-directory : ci/dvp-e2e
153- env :
154- LOOP_WEBHOOK : ${{ secrets.LOOP_WEBHOOK_URL || secrets.LOOP_WEBHOOK }}
155- LOOP_CHANNEL : test-virtualization-loop-alerts # TODO: replace with channel secret after successful run
156202 run : |
157- task loop:junit:parse \
158- JUNIT_FILE="../../artifacts/${RUN_ID}/junit.xml" \
159- RUN_ID="${RUN_ID}" \
160- STORAGE_PROFILE="${{ matrix.profile }}" \
161- TEST_TIMEOUT="${{ inputs.timeout || '4h' }}"
162-
163- - name : Save matrix summary to cluster secret
203+ # Send individual reports for each profile
204+ for junit_file in ../../results/junit-*/junit.xml; do
205+ if [ -f "$junit_file" ]; then
206+ profile=$(echo "$junit_file" | sed 's/.*junit-\([^-]*\)-.*/\1/')
207+ run_id=$(echo "$junit_file" | sed 's/.*-\([^-]*\)\/junit.xml/\1/')
208+ task loop:junit:parse \
209+ JUNIT_FILE="$junit_file" \
210+ RUN_ID="$run_id" \
211+ STORAGE_PROFILE="$profile" \
212+ TEST_TIMEOUT="${{ inputs.timeout || '4h' }}"
213+ fi
214+ done
215+
216+ - name : Generate matrix summary
164217 if : always()
165218 working-directory : ci/dvp-e2e
166219 env :
@@ -173,7 +226,7 @@ jobs:
173226 --webhook-url "${{ secrets.LOOP_WEBHOOK_URL || secrets.LOOP_WEBHOOK }}" \
174227 --channel "${{ secrets.LOOP_CHANNEL || 'test-virtualization-loop-alerts' }}" > matrix_summary.md || true
175228 DATE=$(date +"%Y-%m-%d")
176- HASH=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32 | md5sum | awk '{print $1}' )
229+ HASH=$(head -c 16 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 8 )
177230 kubectl apply -f - <<EOF || true
178231 apiVersion: v1
179232 kind: Secret
@@ -185,5 +238,80 @@ jobs:
185238 type: Opaque
186239 stringData:
187240 summary: |
188- $( sed 's/^/ /' matrix_summary.md )
241+ $(cat matrix_summary.md | sed 's/^/ /' )
189242 EOF
243+
244+ - name : Create test summary
245+ if : always()
246+ run : |
247+ echo "### E2E Test Results Summary" >> $GITHUB_STEP_SUMMARY
248+ echo "" >> $GITHUB_STEP_SUMMARY
249+ echo "| Profile | Status |" >> $GITHUB_STEP_SUMMARY
250+ echo "|---------|--------|" >> $GITHUB_STEP_SUMMARY
251+
252+ # Check each profile result
253+ for profile in sds cephrbd; do
254+ if [ -f "./results/junit-${profile}-*/junit.xml" ]; then
255+ echo "| $profile | ✅ Completed |" >> $GITHUB_STEP_SUMMARY
256+ else
257+ echo "| $profile | ❌ Failed/Missing |" >> $GITHUB_STEP_SUMMARY
258+ fi
259+ done
260+
261+ # ============================================
262+ # 5. CLEANUP - Resource cleanup
263+ # ============================================
264+ cleanup :
265+ name : Cleanup Resources
266+ needs : e2e
267+ if : always()
268+ runs-on : ubuntu-latest
269+ steps :
270+ - uses : actions/checkout@v4
271+
272+ - uses : ./.github/actions/setup-e2e-tools
273+
274+ - name : Setup kubeconfig for cleanup
275+ run : |
276+ if [ -n "${{ vars.E2E_K8S_URL }}" ] && [ -n "${{ secrets.E2E_VIRTUALIZATION_SA_SECRET }}" ]; then
277+ mkdir -p ~/.kube
278+ cat > ~/.kube/config <<EOF
279+ apiVersion: v1
280+ kind: Config
281+ clusters:
282+ - cluster:
283+ server: ${{ vars.E2E_K8S_URL }}
284+ insecure-skip-tls-verify: true
285+ name: parent
286+ contexts:
287+ - context:
288+ cluster: parent
289+ user: sa
290+ name: parent
291+ current-context: parent
292+ users:
293+ - name: sa
294+ user:
295+ token: $(echo '${{ secrets.E2E_VIRTUALIZATION_SA_SECRET }}' | base64 -d 2>/dev/null || \
296+ echo '${{ secrets.E2E_VIRTUALIZATION_SA_SECRET }}')
297+ EOF
298+ chmod 600 ~/.kube/config
299+ else
300+ echo "⚠️ Cannot setup kubeconfig for cleanup - secrets not available"
301+ exit 0
302+ fi
303+
304+ - name : Cleanup test namespaces
305+ working-directory : ci/dvp-e2e
306+ run : |
307+ echo "🧹 Cleaning up test namespaces..."
308+ task cleanup:namespaces:safe \
309+ FILTER_PREFIX="nightly-" \
310+ CONFIRM=true || echo "⚠️ Cleanup completed with warnings"
311+
312+ - name : Report cleanup results
313+ if : always()
314+ run : |
315+ echo "### Cleanup Results" >> $GITHUB_STEP_SUMMARY
316+ echo "✅ Cleanup job completed" >> $GITHUB_STEP_SUMMARY
317+ echo "🧹 Attempted to clean up namespaces matching 'nightly-*'" >> $GITHUB_STEP_SUMMARY
0 commit comments