2727 - cron : " 30 2 * * *"
2828 workflow_dispatch :
2929 inputs :
30- profiles :
31- description : " Storage profiles (comma-separated): sds, cephrbd"
32- required : false
33- default : " sds,cephrbd"
3430 timeout :
3531 description : " Ginkgo timeout (e.g. 2h, 4h)"
3632 required : false
4339 E2E_K8S_URL : https://api.e2e.virtlab.flant.com
4440
4541jobs :
46- setup :
47- name : Setup Profiles
42+ # ============================================
43+ # 1. SETUP - Environment preparation
44+ # ============================================
45+ setup-nested-envs :
46+ name : Setup Nested Envs
4847 runs-on : ubuntu-latest
48+ concurrency :
49+ group : setup-nested-envs-${{ github.ref }}
50+ cancel-in-progress : true
51+ env :
52+ PROFILE : sds-replicated-volume
4953 outputs :
50- profiles : ${{ steps.profiles .outputs.profiles }}
54+ run_id : ${{ steps.prep .outputs.run_id }}
5155 steps :
5256 - uses : actions/checkout@v4
5357
54- - name : Load storage profiles
55- id : profiles
58+ - name : Load storage profile
59+ id : load
5660 run : |
57- # Single profile: sds with storage class sds-replicated-volume
58- echo 'profiles=["sds"]' >> "$GITHUB_OUTPUT"
61+ cd ci/dvp-e2e
62+ # Map sds-replicated-volume to sds profile from profiles.json
63+ PROFILE=$(jq -r '.[0].name' profiles.json)
64+ echo "profile=$PROFILE" >> "$GITHUB_OUTPUT"
65+ echo "Will test profile: $PROFILE (mapped from sds-replicated-volume)"
5966
60- - name : Print matrix
67+ - name : Prepare run context
68+ id : prep
6169 run : |
62- echo "Will test profiles: ${{ steps.profiles.outputs.profiles }}"
70+ RUN_ID="nightly-nested-e2e-sds-$(date +%H%M%S)"
71+ echo "run_id=$RUN_ID" >> "$GITHUB_OUTPUT"
72+ mkdir -p ./tmp/run-context
73+ echo "profile: ${PROFILE}" > ./tmp/run-context/config.yaml
74+ echo "run_id: ${RUN_ID}" >> ./tmp/run-context/config.yaml
75+ echo "timestamp: $(date -Iseconds)" >> ./tmp/run-context/config.yaml
6376
77+ - name : Upload run context
78+ uses : actions/upload-artifact@v4
79+ with :
80+ name : run-context-${{ steps.prep.outputs.run_id }}
81+ path : ./tmp/run-context
82+
83+ # ============================================
84+ # 2. PREPARE - Cluster preparation
85+ # ============================================
6486 prepare :
65- name : Matrix Setup (${{ matrix.profile }})
66- needs : [setup]
87+ name : Prepare Cluster
88+ needs : [setup-nested-envs ]
6789 runs-on : ubuntu-latest
6890 timeout-minutes : 300
6991 concurrency :
70- group : prepare-${{ github.ref }}-${{ matrix.profile }}
92+ group : prepare-${{ github.ref }}-sds-replicated-volume
7193 cancel-in-progress : true
72- strategy :
73- fail-fast : false
74- matrix :
75- profile : ${{ fromJson(needs.setup.outputs.profiles) }}
76-
7794 env :
95+ PROFILE : sds-replicated-volume
7896 GO_VERSION : " 1.24.6"
7997 TMP_ROOT : ${{ github.workspace }}/ci/dvp-e2e/tmp
80- STORAGE_CLASS : sds-replicated-volume
98+ LOOP_WEBHOOK : ${{ secrets.LOOP_WEBHOOK_URL || secrets.LOOP_WEBHOOK }}
99+ LOOP_CHANNEL : ${{ secrets.LOOP_CHANNEL || 'test-virtualization-loop-alerts' }} # TODO: replace with channel secret after successful run
81100
82101 outputs :
83102 run_id : ${{ steps.prep.outputs.run_id }}
84- storage_class : ${{ env.STORAGE_CLASS }}
103+ storage_class : ${{ steps.profile-config.outputs.storage_class }}
104+ image_storage_class : ${{ steps.profile-config.outputs.image_storage_class }}
105+ snapshot_storage_class : ${{ steps.profile-config.outputs.snapshot_storage_class }}
106+ attach_disk_size : ${{ steps.profile-config.outputs.attach_disk_size }}
85107
86108 steps :
87109 - uses : actions/checkout@v4
@@ -102,7 +124,7 @@ jobs:
102124 - name : Install kubectl
103125 uses : azure/setup-kubectl@v4
104126 with :
105- version : ' latest'
127+ version : " latest"
106128
107129 - name : Install Deckhouse CLI
108130 env :
@@ -123,13 +145,20 @@ jobs:
123145 curl -L -o /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.44.1/yq_linux_amd64
124146 chmod +x /usr/local/bin/yq
125147
148+ - name : Restore run context
149+ uses : actions/download-artifact@v4
150+ with :
151+ name : run-context-${{ needs.setup-nested-envs.outputs.run_id }}
152+ path : .
153+
126154 - name : Prepare environment
127155 id : prep
128156 run : |
129- RUN_ID="nightly-nested-e2e- ${{ matrix.profile }}-$(date +%H%M) "
157+ RUN_ID="${{ needs.setup-nested-envs.outputs.run_id }}"
130158 echo "run_id=$RUN_ID" >> "$GITHUB_OUTPUT"
131159 echo "RUN_ID=$RUN_ID" >> "$GITHUB_ENV"
132- echo "PROFILE=${{ matrix.profile }}" >> "$GITHUB_ENV"
160+ # Map sds-replicated-volume to sds for profile config
161+ echo "PROFILE=sds" >> "$GITHUB_ENV"
133162 echo "TMP_ROOT=${{ env.TMP_ROOT }}" >> "$GITHUB_ENV"
134163 mkdir -p "${{ env.TMP_ROOT }}/shared" "${{ env.TMP_ROOT }}/matrix-logs"
135164
@@ -169,12 +198,154 @@ jobs:
169198 RUN_DIR="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}"
170199 echo "VALUES_TEMPLATE_FILE=${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/values.yaml" >> $GITHUB_ENV
171200
201+ - name : Configure registry auth (REGISTRY_DOCKER_CFG)
202+ run : |
203+ prod_user="${{ secrets.PROD_READ_REGISTRY_USER }}"
204+ prod_pass="${{ secrets.PROD_READ_REGISTRY_PASSWORD }}"
205+ dev_user="${{ secrets.BOOTSTRAP_DEV_REGISTRY_LOGIN }}"
206+ dev_pass="${{ secrets.BOOTSTRAP_DEV_REGISTRY_PASSWORD }}"
207+ echo "::add-mask::$prod_user"
208+ echo "::add-mask::$prod_pass"
209+ echo "::add-mask::$dev_user"
210+ echo "::add-mask::$dev_pass"
211+ prod_auth_b64=$(printf '%s:%s' "$prod_user" "$prod_pass" | base64 | tr -d '\n')
212+ dev_auth_b64=$(printf '%s:%s' "$dev_user" "$dev_pass" | base64 | tr -d '\n')
213+ docker_cfg=$(printf '{"auths":{"registry.deckhouse.io":{"auth":"%s"},"dev-registry.deckhouse.io":{"auth":"%s"}}}' "$prod_auth_b64" "$dev_auth_b64")
214+ docker_cfg_b64=$(printf '%s' "$docker_cfg" | base64 | tr -d '\n')
215+ echo "::add-mask::$docker_cfg_b64"
216+ {
217+ echo "REGISTRY_DOCKER_CFG=$docker_cfg_b64"
218+ echo "DECKHOUSE_REGISTRY_USER=$prod_user"
219+ echo "DECKHOUSE_REGISTRY_PASSWORD=$prod_pass"
220+ } >> "$GITHUB_ENV"
221+
222+ - name : Inject REGISTRY_DOCKER_CFG into values.yaml
223+ working-directory : ci/dvp-e2e
224+ run : |
225+ yq eval --inplace '.deckhouse.registryDockerCfg = strenv(REGISTRY_DOCKER_CFG)' "${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/values.yaml"
226+
227+ - name : Docker login to Deckhouse registry
228+ uses : docker/login-action@v3
229+ with :
230+ registry : registry.deckhouse.io
231+ username : ${{ env.DECKHOUSE_REGISTRY_USER }}
232+ password : ${{ env.DECKHOUSE_REGISTRY_PASSWORD }}
233+
234+ - name : Docker login to dev-registry
235+ uses : docker/login-action@v3
236+ with :
237+ registry : ${{ vars.DEV_REGISTRY }}
238+ username : ${{ secrets.BOOTSTRAP_DEV_REGISTRY_LOGIN }}
239+ password : ${{ secrets.BOOTSTRAP_DEV_REGISTRY_PASSWORD }}
240+
172241 - name : Configure storage profile
173242 working-directory : ci/dvp-e2e
243+ id : profile-config
244+ env :
245+ PROFILE : sds
174246 run : |
175- # Set storage profile to sds with storage class sds-replicated-volume
247+ # Get storage class configuration from profiles.json
248+ PROFILE_CONFIG=$(./scripts/get_profile_config.sh "${PROFILE}")
249+
250+ # Parse the output more carefully
251+ STORAGE_CLASS=$(echo "$PROFILE_CONFIG" | grep "^STORAGE_CLASS=" | cut -d'=' -f2)
252+ IMAGE_STORAGE_CLASS=$(echo "$PROFILE_CONFIG" | grep "^IMAGE_STORAGE_CLASS=" | cut -d'=' -f2)
253+ SNAPSHOT_STORAGE_CLASS=$(echo "$PROFILE_CONFIG" | grep "^SNAPSHOT_STORAGE_CLASS=" | cut -d'=' -f2)
254+ ATTACH_DISK_SIZE=$(echo "$PROFILE_CONFIG" | grep "^ATTACH_DISK_SIZE=" | cut -d'=' -f2)
255+
256+ echo "Profile: ${PROFILE}"
257+ echo "Storage Class: ${STORAGE_CLASS}"
258+ echo "Image Storage Class: ${IMAGE_STORAGE_CLASS}"
259+ echo "Snapshot Storage Class: ${SNAPSHOT_STORAGE_CLASS}"
260+ echo "Attach Disk Size: ${ATTACH_DISK_SIZE}"
261+
262+ # Export variables safely
263+ echo "STORAGE_CLASS=${STORAGE_CLASS}" >> $GITHUB_ENV
264+ echo "IMAGE_STORAGE_CLASS=${IMAGE_STORAGE_CLASS}" >> $GITHUB_ENV
265+ echo "SNAPSHOT_STORAGE_CLASS=${SNAPSHOT_STORAGE_CLASS}" >> $GITHUB_ENV
266+ echo "ATTACH_DISK_SIZE=${ATTACH_DISK_SIZE}" >> $GITHUB_ENV
267+ echo "storage_class=$STORAGE_CLASS" >> $GITHUB_OUTPUT
268+ echo "image_storage_class=$IMAGE_STORAGE_CLASS" >> $GITHUB_OUTPUT
269+ echo "snapshot_storage_class=$SNAPSHOT_STORAGE_CLASS" >> $GITHUB_OUTPUT
270+ echo "attach_disk_size=$ATTACH_DISK_SIZE" >> $GITHUB_OUTPUT
271+ # Pass storage profile into run values for Helm templates
176272 PROFILE='sds' yq eval --inplace '.storageProfile = strenv(PROFILE)' "${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/values.yaml"
177- echo "Configured storage profile: sds with storage class: ${STORAGE_CLASS}"
273+ # Effective disk SC used for worker data disks (prefer image SC when set)
274+ EFF_DISK_SC=${IMAGE_STORAGE_CLASS:-$STORAGE_CLASS}
275+ echo "EFFECTIVE_DISK_SC=${EFF_DISK_SC}" >> $GITHUB_ENV
276+
277+ - name : Install infra (namespace/RBAC/ingress)
278+ working-directory : ci/dvp-e2e
279+ run : |
280+ USE_GH_SSH_KEYS=true SSH_FILE_NAME=id_ed task render-infra \
281+ TMP_DIR="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}" \
282+ VALUES_FILE="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/values.yaml" \
283+ PARENT_KUBECONFIG="${KUBECONFIG}" \
284+ SSH_FILE_NAME="id_ed"
285+ USE_GH_SSH_KEYS=true SSH_FILE_NAME=id_ed task infra-deploy \
286+ TMP_DIR="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}" \
287+ VALUES_FILE="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/values.yaml" \
288+ PARENT_KUBECONFIG="${KUBECONFIG}" \
289+ SSH_FILE_NAME="id_ed"
290+
291+ - name : Bootstrap nested cluster (via jump-host)
292+ working-directory : ci/dvp-e2e
293+ run : |
294+ echo "🚀 dhctl bootstrap (profile: sds-replicated-volume -> sds)"
295+ task dhctl-bootstrap \
296+ TMP_DIR="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}" \
297+ VALUES_FILE="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/values.yaml" \
298+ PARENT_KUBECONFIG="${KUBECONFIG}" \
299+ SSH_FILE_NAME="id_ed" \
300+ TARGET_STORAGE_CLASS="ceph-pool-r2-csi-rbd-immediate"
301+
302+ - name : Attach data disks to worker VMs using hotplug
303+ working-directory : ci/dvp-e2e
304+ run : |
305+ task infra:attach-storage-disks-hotplug \
306+ TMP_DIR="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}" \
307+ VALUES_FILE="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/values.yaml" \
308+ PARENT_KUBECONFIG="${KUBECONFIG}" \
309+ DISK_SIZE="${ATTACH_DISK_SIZE:-10Gi}" \
310+ STORAGE_CLASS="ceph-pool-r2-csi-rbd-immediate" \
311+ DISK_COUNT="2"
312+
313+ - name : Build nested kubeconfig
314+ working-directory : ci/dvp-e2e
315+ run : |
316+ task nested:kubeconfig \
317+ TMP_DIR="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}" \
318+ VALUES_FILE="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/values.yaml" \
319+ NAMESPACE="${{ env.RUN_ID }}" \
320+ SSH_DIR="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/ssh" \
321+ SSH_FILE_NAME="id_ed" \
322+ NESTED_DIR="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/nested" \
323+ NESTED_KUBECONFIG="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/nested/kubeconfig" \
324+ PARENT_KUBECONFIG="${KUBECONFIG}"
325+
326+ - name : Configure storage classes
327+ working-directory : ci/dvp-e2e
328+ run : |
329+ echo "💾 Configuring storage classes for profile: sds-replicated-volume -> sds"
330+ task nested:storage:configure \
331+ STORAGE_PROFILE="sds" \
332+ TARGET_STORAGE_CLASS="${{ steps.profile-config.outputs.storage_class }}" \
333+ TMP_DIR="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}" \
334+ VALUES_FILE="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/values.yaml" \
335+ GENERATED_VALUES_FILE="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/generated-values.yaml" \
336+ SSH_DIR="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/ssh" \
337+ SSH_FILE_NAME="id_ed" \
338+ PASSWORD_FILE="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/password.txt" \
339+ PASSWORD_HASH_FILE="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/password-hash.txt" \
340+ NAMESPACE="${{ env.RUN_ID }}" \
341+ DOMAIN="" \
342+ DEFAULT_USER="ubuntu" \
343+ NESTED_DIR="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/nested" \
344+ NESTED_KUBECONFIG="${{ env.TMP_ROOT }}/runs/${{ env.RUN_ID }}/nested/kubeconfig"
345+
346+ # Ingress smoke disabled: not required for storage config
347+
348+ # Ceph CSI smoke check removed per request
178349
179350 - name : Upload run context
180351 if : always()
@@ -185,35 +356,11 @@ jobs:
185356 ci/dvp-e2e/tmp/runs/${{ env.RUN_ID }}
186357 ci/dvp-e2e/tmp/shared
187358 if-no-files-found : warn
188-
189- run-e2e :
190- name : E2E (${{ matrix.profile }}) [skeleton]
191- needs : [setup, prepare]
192- runs-on : ubuntu-latest
193- concurrency :
194- group : e2e-${{ github.ref }}-${{ matrix.profile }}
195- cancel-in-progress : true
196- strategy :
197- fail-fast : false
198- matrix :
199- profile : ${{ fromJson(needs.setup.outputs.profiles) }}
200- steps :
201- - name : Echo run
202- run : |
203- echo "E2E stage for profile=${{ matrix.profile }} (skeleton - placeholder)"
204- report :
205- name : Report [skeleton]
206- needs : [setup, run-e2e]
207- if : always()
208- runs-on : ubuntu-latest
209- steps :
210- - name : Echo report
211- run : |
212- echo "Report stage (skeleton). Collecting results from matrix..."
359+ overwrite : true
213360
214361 cleanup :
215- name : Cleanup Resources
216- needs : report
362+ name : Cleanup [skeleton]
363+ needs : [setup-nested-envs, prepare]
217364 if : always()
218365 runs-on : ubuntu-latest
219366 steps :
@@ -225,7 +372,7 @@ jobs:
225372 - name : Install kubectl
226373 uses : azure/setup-kubectl@v4
227374 with :
228- version : ' latest'
375+ version : " latest"
229376
230377 - name : Build parent kubeconfig from secret (cleanup)
231378 shell : bash
@@ -257,25 +404,9 @@ jobs:
257404 - name : Cleanup test namespaces
258405 run : |
259406 set -euo pipefail
260- PREFIX="nightly-nested-e2e-"
261- echo "🧹 Cleaning up namespaces matching prefix '${PREFIX}'"
262- mapfile -t CANDIDATES < <(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | grep "^${PREFIX}" || true)
263- OURS=()
264- for ns in "${CANDIDATES[@]:-}"; do
265- [ -z "$ns" ] && continue
266- if kubectl -n "$ns" get deploy jump-host >/dev/null 2>&1; then
267- OURS+=("$ns")
268- fi
269- done
270- if [ "${#OURS[@]}" -eq 0 ]; then
271- echo "[INFO] No namespaces to delete."
272- else
273- echo "[INFO] Deleting namespaces:"
274- printf ' - %s\n' "${OURS[@]}"
275- for ns in "${OURS[@]}"; do
276- kubectl delete ns "$ns" --wait=false || true
277- done
278- fi
407+ echo "🧹 Cleaning up namespaces matching 'nightly-nested-e2e-*'"
408+ kubectl get ns -o name | grep "namespace/nightly-nested-e2e-" | cut -d/ -f2 | \
409+ xargs -r kubectl delete ns --wait=false || echo "[INFO] No namespaces to delete"
279410
280411 - name : Report cleanup results
281412 if : always()
0 commit comments