Skip to content

Commit 5319473

Browse files
hdurand0710Gopher Bot
authored andcommitted
TEST/MEDIUM: collect HUG debug artifacts on conformance MR failures
Add a conformance-collect-debug-mr task that retrieves pod logs, haproxy.cfg, and map files from a running HUG pod after the conformance run, storing them as CI artifacts to ease post-mortem analysis of failures (e.g. HTTPRouteHTTPSListener flakiness). Collection is opt-in via HUG_LABEL_COLLECT_LOGS, a pod label selector (e.g. "run=hug-4ff08ccae6d535c8", where the sha is derived from the gateway namespace/name). When the variable is empty (the default) the task exits immediately and nothing is collected. The deployer intentionally does not delete the matching pod when its gateway is removed: it skips the Deployment and Service deletion for that one pod and excludes it from WaitForCleanup, so the pod stays alive between conformance test teardown and cluster deletion, giving this task a window to pull the artifacts.
1 parent 9811d29 commit 5319473

4 files changed

Lines changed: 70 additions & 0 deletions

File tree

.aspell.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,4 @@ allowed:
138138
- runnable
139139
- statx
140140
- patref
141+
- ccae

.gitlab/conformance-tests.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ conformance-MR-GW-API-1_5_0:
6767
CONFORMANCE_JUNIT_HTML_OUTPUT: "${CI_PROJECT_DIR}/conformance-junit-1.5.0.html"
6868
CONFORMANCE_RUN_TEST: ""
6969
HUG_SERVICE_TYPE: "ClusterIP"
70+
# Pod label selector for conformance-collect-debug-mr.
71+
# When set, that task collects logs, haproxy.cfg and map files from the
72+
# matching HUG pod after the conformance run. The deployer intentionally
73+
# does NOT delete the pod when its gateway is removed, keeping it alive
74+
# so artifacts can be retrieved here before the cluster is torn down.
75+
# Leave empty (default) to skip collection entirely.
76+
# Example: "run=hug-4ff08ccae6d535c8" (sha derived from gateway ns/name)
77+
HUG_LABEL_COLLECT_LOGS: ""
7078
CONFORMANCE_SKIP_TESTS: "\
7179
HTTPRouteHeaderMatching,\
7280
HTTPRouteInvalidBackendRefUnknownKind,\
@@ -95,12 +103,14 @@ conformance-MR-GW-API-1_5_0:
95103
- task conformance-build-test-image
96104
- task conformance-run-job
97105
after_script:
106+
- task conformance-collect-debug-mr || true
98107
- task kind-delete
99108
artifacts:
100109
when: always
101110
paths:
102111
- conformance-report-1.5.0.yaml
103112
- conformance-junit-1.5.0.xml
104113
- conformance-junit-1.5.0.html
114+
- debug-hug/
105115
reports:
106116
junit: conformance-junit-1.5.0.xml

taskfile/conformance.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,53 @@ tasks:
185185
186186
exit ${JOB_STATUS}
187187
188+
conformance-collect-debug-mr:
189+
desc: "collect HUG pod logs and HAProxy config/maps for post-mortem debugging (MR CI job)"
190+
deps:
191+
- task: check-kubectl
192+
vars:
193+
kubectl: "{{.TMP}}/kubectl/{{.KUBECTL_VERSION}}/kubectl"
194+
DEBUG_DIR: '{{default "debug-hug" .DEBUG_DIR}}'
195+
HUG_NS: "haproxy-unified-gateway"
196+
# HUG_LABEL_COLLECT_LOGS: pod label selector for debug artifact collection.
197+
# When set, collects logs, haproxy.cfg and map files from the matching HUG pod.
198+
# The deployer intentionally does NOT delete the pod when its gateway is removed,
199+
# keeping it alive so artifacts can be retrieved here before cluster teardown.
200+
# Leave empty (default) to skip collection entirely.
201+
# Example: "run=hug-4ff08ccae6d535c8" (sha derived from gateway ns/name)
202+
HUG_LABEL_COLLECT_LOGS: '{{default "" .HUG_LABEL_COLLECT_LOGS}}'
203+
cmds:
204+
- |
205+
if [ -z "{{.HUG_LABEL_COLLECT_LOGS}}" ]; then
206+
echo "HUG_LABEL_COLLECT_LOGS is not set — skipping debug collection"
207+
exit 0
208+
fi
209+
- mkdir -p "{{.DEBUG_DIR}}"
210+
- |
211+
POD=$(/{{.kubectl}} get pod -n {{.HUG_NS}} -l {{.HUG_LABEL_COLLECT_LOGS}} \
212+
-o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)
213+
if [ -z "$POD" ]; then
214+
echo "No pod with label {{.HUG_LABEL_COLLECT_LOGS}} found — skipping debug collection"
215+
exit 0
216+
fi
217+
echo "Collecting debug artifacts from pod: $POD (label: {{.HUG_LABEL_COLLECT_LOGS}})"
218+
/{{.kubectl}} logs -n {{.HUG_NS}} "$POD" > "{{.DEBUG_DIR}}/hug-pod.log" 2>&1 || true
219+
/{{.kubectl}} exec -n {{.HUG_NS}} "$POD" -- cat /usr/local/hug/haproxy.cfg \
220+
> "{{.DEBUG_DIR}}/haproxy.cfg" 2>/dev/null || true
221+
MAP_DIR=/usr/local/hug/maps/hug_https_443
222+
for MAP in \
223+
listener_exact_match.map \
224+
listener_wildcard_match.map \
225+
listener_route_exact_match.map \
226+
listener_route_wildcard_match.map \
227+
path_exact.map \
228+
path_prefix.map \
229+
path_regex.map; do
230+
/{{.kubectl}} exec -n {{.HUG_NS}} "$POD" -- cat "${MAP_DIR}/${MAP}" \
231+
> "{{.DEBUG_DIR}}/${MAP}" 2>/dev/null || true
232+
done
233+
echo "Debug artifacts written to {{.DEBUG_DIR}}"
234+
188235
### -----------------
189236
### Tasks used for local tests
190237
### Conformance tester runs outside the cluster

test/conformance/deployer/deployer.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ const (
5050

5151
defaultControllerNS = "haproxy-unified-gateway"
5252
defaultControllerLabel = "haproxy-unified-gateway"
53+
54+
// debugPreservedResource is the resource name of the HUG pod kept alive after
55+
// its gateway is deleted so that the CI debug-collection task can retrieve
56+
// haproxy.cfg and map files from it before the cluster is torn down.
57+
// Corresponds to gateway-conformance-infra/same-namespace-with-https-listener.
58+
debugPreservedResource = "hug-4ff08ccae6d535c8"
5359
)
5460

5561
// Config holds the parameters for the Gateway deployer.
@@ -243,6 +249,10 @@ func (r *GatewayReconciler) reconcileService(ctx context.Context, gw *gatewayv1.
243249
// deleteResources removes the Deployment and Service created for a Gateway.
244250
func (r *GatewayReconciler) deleteResources(ctx context.Context, gwNS, gwName string) error {
245251
name := resourceName(gwNS, gwName)
252+
if name == debugPreservedResource {
253+
_, _ = fmt.Fprintf(os.Stderr, "deployer: preserving %s/%s for post-mortem debug collection\n", r.Config.DeployerNs, name)
254+
return nil
255+
}
246256
ns := r.Config.DeployerNs
247257

248258
deploy := &appsv1.Deployment{}
@@ -451,6 +461,8 @@ func WaitForCleanup(ctx context.Context, restCfg *rest.Config, deployerNs, gatew
451461
); err != nil {
452462
return err
453463
}
464+
deploys.Items = slices.DeleteFunc(deploys.Items, func(d appsv1.Deployment) bool { return d.Name == debugPreservedResource })
465+
svcs.Items = slices.DeleteFunc(svcs.Items, func(s corev1.Service) bool { return s.Name == debugPreservedResource })
454466
if len(deploys.Items) == 0 && len(svcs.Items) == 0 {
455467
return nil
456468
}

0 commit comments

Comments
 (0)