feat(e2e): add distributed E2E test framework with parametric traffic generation

Add run-e2e-test.sh with CLI parameters (--hits, --http-ratio, --dns, --tls,
--src-ips, --keep-analysis, --up) for configurable traffic generation. Traffic
runs from VM endpoints with multiple source IPs (alias IPs on eth0) to produce
distinct sessions for the ML pipeline. Fix curl TLS flags (--tlsv1.2 instead
of --tls-v1-2), skip redundant local verification in distributed mode, and
fix dashboard is_available() cache that never retried after ClickHouse recovery.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jacquin Antoine
2026-04-15 00:09:32 +02:00
parent 7894d39f1c
commit f88b739992
40 changed files with 2154 additions and 337 deletions

View File

@ -8,12 +8,13 @@ import (
// HTTP1Request représente une requête HTTP/1.x parsée depuis le flux déchiffré.
type HTTP1Request struct {
Method string // méthode HTTP (GET, POST, …)
Path string // chemin (sans query string)
Query string // query string (sans le '?')
Protocol string // "HTTP/1.0" ou "HTTP/1.1"
Headers []string // noms des en-têtes dans l'ordre exact d'arrivée
HeaderSig string // signature : noms joints par ";"
Method string // méthode HTTP (GET, POST, …)
Path string // chemin (sans query string)
Query string // query string (sans le '?')
Protocol string // "HTTP/1.0" ou "HTTP/1.1"
Headers []string // noms des en-têtes dans l'ordre exact d'arrivée
HeaderSig string // signature : noms joints par ";"
HeaderKV map[string]string // valeurs des en-têtes clés (Host, User-Agent, etc.)
}
// HTTP1Response représente le début d'une réponse HTTP/1.x (status line).
@ -27,6 +28,14 @@ var knownMethods = []string{
"OPTIONS", "PATCH", "CONNECT", "TRACE",
}
// capturedHeaders est la liste des en-têtes dont on capture la valeur.
var capturedHeaders = []string{
"Host", "User-Agent", "Accept", "Accept-Encoding", "Accept-Language",
"Content-Type", "X-Request-Id", "X-Trace-Id", "X-Forwarded-For",
"Sec-CH-UA", "Sec-CH-UA-Mobile", "Sec-CH-UA-Platform",
"Sec-Fetch-Dest", "Sec-Fetch-Mode", "Sec-Fetch-Site",
}
// IsHTTP1Request retourne true si les premiers octets ressemblent à une
// requête HTTP/1.x (commence par une méthode reconnue suivi d'un espace).
func IsHTTP1Request(data []byte) bool {
@ -91,8 +100,9 @@ func ParseHTTP1Request(data []byte) *HTTP1Request {
query = rawPath[idx+1:]
}
// Extraire les noms d'en-têtes dans l'ordre
// Extraire les noms d'en-têtes dans l'ordre + capturer les valeurs clés
headers := make([]string, 0, len(lines)-1)
headerKV := make(map[string]string, len(capturedHeaders))
for _, line := range lines[1:] {
if line == "" {
break
@ -101,6 +111,13 @@ func ParseHTTP1Request(data []byte) *HTTP1Request {
name := strings.TrimSpace(line[:colon])
if name != "" {
headers = append(headers, name)
// Capturer la valeur si c'est un header d'intérêt
for _, key := range capturedHeaders {
if strings.EqualFold(name, key) {
headerKV[key] = strings.TrimSpace(line[colon+1:])
break
}
}
}
}
}
@ -114,6 +131,7 @@ func ParseHTTP1Request(data []byte) *HTTP1Request {
Protocol: protocol,
Headers: headers,
HeaderSig: sig,
HeaderKV: headerKV,
}
}
@ -143,4 +161,4 @@ func ParseHTTP1Response(data []byte) *HTTP1Response {
return nil
}
return &HTTP1Response{StatusCode: code}
}
}