Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/run-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ runs:
uses: actions/checkout@v4
with:
repository: agynio/e2e
ref: main
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'agynio/e2e' && github.head_ref || 'main' }}
path: e2e

- name: Stage provider binary
Expand Down
2 changes: 1 addition & 1 deletion scripts/run-pipeline.sh
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ EOF
fi
done

for env_name in CODEX_INIT_IMAGE AGN_INIT_IMAGE CLAUDE_INIT_IMAGE AGN_EXPOSE_INIT_IMAGE; do
for env_name in CODEX_INIT_IMAGE AGN_INIT_IMAGE CLAUDE_INIT_IMAGE AGN_EXPOSE_INIT_IMAGE EXPOSE_DEBUG_TOKEN EXPOSE_DEBUG_TOKEN_SECRET EXPOSE_DEBUG_ENDPOINT; do
env_value=${!env_name:-}
if [ -n "$env_value" ]; then
exec_env+=("${env_name}=${env_value}")
Expand Down
239 changes: 85 additions & 154 deletions suites/go-core/tests/expose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package tests

import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io"
Expand All @@ -27,66 +26,56 @@ import (
)

const (
exposeTestTimeout = 8 * time.Minute
exposeCommandTimeout = 2 * time.Minute
exposeListTimeout = 30 * time.Second
exposeListEmptyTimeout = 60 * time.Second
exposeReachabilityTimeout = 90 * time.Second
exposeUnreachableTimeout = 90 * time.Second
exposeRequestTimeout = 15 * time.Second
exposeZitiRequestTimeout = 30 * time.Second
exposeDiagnosticsTimeout = 20 * time.Second
exposePort = 3000
exposeExpectedResponse = "Hi! How are you?"
exposeStatusActive = "active"
zitiMgmtEndpointEnvKey = "ZITI_MGMT_ENDPOINT"
zitiMgmtServiceName = "ziti-mgmt"
zitiMgmtPath = "/edge/management/v1"
zitiDiagnosticsSecretName = "ziti-management-diagnostics"
zitiDiagnosticsUserKey = "username"
zitiDiagnosticsPasswordKey = "password"
exposeTestTimeout = 8 * time.Minute
exposeCommandTimeout = 2 * time.Minute
exposeListTimeout = 30 * time.Second
exposeListEmptyTimeout = 60 * time.Second
exposeReachabilityTimeout = 90 * time.Second
exposeUnreachableTimeout = 90 * time.Second
exposeRequestTimeout = 15 * time.Second
exposeZitiRequestTimeout = 30 * time.Second
exposeDiagnosticsTimeout = 20 * time.Second
exposePort = 3000
exposeExpectedResponse = "Hi! How are you?"
exposeStatusActive = "active"
exposeDebugEndpointEnvKey = "EXPOSE_DEBUG_ENDPOINT"
exposeDebugTokenKey = "token"
exposeDebugTokenEnvKey = "EXPOSE_DEBUG_TOKEN"
exposeDebugTokenSecretKey = "EXPOSE_DEBUG_TOKEN_SECRET"
)

func TestZitiManagementEndpointDefaultUsesIngress(t *testing.T) {
t.Setenv(zitiMgmtEndpointEnvKey, "")
t.Setenv("E2E_DOMAIN", "e2e.agyn.dev")
t.Setenv("E2E_INGRESS_PORT", "30443")
func TestExposeDebugEndpointDefaultUsesService(t *testing.T) {
t.Setenv(exposeDebugEndpointEnvKey, "")
t.Setenv("E2E_NAMESPACE", "platform")

got := zitiManagementEndpoint()
want := "https://ziti-mgmt.e2e.agyn.dev:30443/edge/management/v1"
got := exposeDebugEndpoint()
want := "http://expose.platform.svc.cluster.local:8080"
if got != want {
t.Fatalf("ziti management endpoint mismatch: got %q want %q", got, want)
t.Fatalf("expose debug endpoint mismatch: got %q want %q", got, want)
}
}

func TestZitiManagementEndpointExplicitOverride(t *testing.T) {
t.Setenv(zitiMgmtEndpointEnvKey, "https://custom.example.test:9443/edge/management/v1/")
func TestExposeDebugEndpointExplicitOverride(t *testing.T) {
t.Setenv(exposeDebugEndpointEnvKey, "http://custom.example.test:8080/")

got := zitiManagementEndpoint()
want := "https://custom.example.test:9443/edge/management/v1"
got := exposeDebugEndpoint()
want := "http://custom.example.test:8080"
if got != want {
t.Fatalf("ziti management endpoint mismatch: got %q want %q", got, want)
t.Fatalf("expose debug endpoint mismatch: got %q want %q", got, want)
}
}

func TestZitiDiagnosticsSecretUsesPlatformNamespace(t *testing.T) {
secretNamespace, secretName := zitiDiagnosticsSecretRef()
if secretNamespace != "platform" {
t.Fatalf("ziti diagnostics secret namespace mismatch: got %q want %q", secretNamespace, "platform")
func TestExposeDebugTokenSecretExplicitRef(t *testing.T) {
t.Setenv(exposeDebugTokenSecretKey, "custom-platform/custom-secret")
secretNamespace, secretName, ok := exposeDebugTokenSecretRef()
if !ok {
t.Fatal("expected explicit secret ref")
}
if secretName != zitiDiagnosticsSecretName {
t.Fatalf("ziti diagnostics secret name mismatch: got %q want %q", secretName, zitiDiagnosticsSecretName)
}
}

func TestZitiDiagnosticsSecretUsesDevspaceNamespace(t *testing.T) {
t.Setenv("DEVSPACE_NAMESPACE", "custom-platform")
secretNamespace, secretName := zitiDiagnosticsSecretRef()
if secretNamespace != "custom-platform" {
t.Fatalf("ziti diagnostics secret namespace mismatch: got %q want %q", secretNamespace, "custom-platform")
t.Fatalf("expose debug token secret namespace mismatch: got %q want %q", secretNamespace, "custom-platform")
}
if secretName != zitiDiagnosticsSecretName {
t.Fatalf("ziti diagnostics secret name mismatch: got %q want %q", secretName, zitiDiagnosticsSecretName)
if secretName != "custom-secret" {
t.Fatalf("expose debug token secret name mismatch: got %q want %q", secretName, "custom-secret")
}
}

Expand Down Expand Up @@ -652,18 +641,6 @@ func createZitiHTTPClient(t *testing.T) *http.Client {
return httpClient
}

type zitiManagementSession struct {
endpoint string
token string
client *http.Client
}

type zitiAuthenticationResponse struct {
Data struct {
Token string `json:"token"`
} `json:"data"`
}

func logExposeTimeoutDiagnostics(t *testing.T, fixture exposeWorkloadFixture, exposure exposeEntry, exposedURL string) {
t.Helper()
t.Log("diagnostics: exposed service reachability timed out")
Expand All @@ -673,7 +650,7 @@ func logExposeTimeoutDiagnostics(t *testing.T, fixture exposeWorkloadFixture, ex

serviceName := fmt.Sprintf("exposed-%s", exposure.ID)
logExposureDetails(t, exposure, serviceName, exposedURL)
logZitiExposureDiagnostics(t, diagnosticsCtx, serviceName, exposure)
logExposeDebugDiagnostics(t, diagnosticsCtx, exposure)
logExposeWorkloadSidecarLogs(t, diagnosticsCtx, fixture)
}

Expand Down Expand Up @@ -701,130 +678,84 @@ func logExposureDetails(t *testing.T, exposure exposeEntry, serviceName, dialURL
)
}

func logZitiExposureDiagnostics(t *testing.T, ctx context.Context, serviceName string, exposure exposeEntry) {
func logExposeDebugDiagnostics(t *testing.T, ctx context.Context, exposure exposeEntry) {
t.Helper()
session, err := createZitiManagementSession(t, ctx)
token, err := exposeDebugToken(t, ctx)
if err != nil {
t.Logf("diagnostics: ziti management unavailable: %v", err)
t.Logf("diagnostics: expose debug token unavailable: %v", err)
return
}

logZitiResource(t, ctx, session, "service", zitiFilterPath("/services", "name", serviceName))
logZitiResource(t, ctx, session, "terminators", zitiFilterPath("/terminators", "service.name", serviceName))
logZitiResource(t, ctx, session, "bind-policy", zitiFilterPath("/service-policies", "name", serviceName+"-bind"))
logZitiResource(t, ctx, session, "dial-policy", zitiFilterPath("/service-policies", "name", serviceName+"-dial"))

if exposure.OpenZitiServiceID != "" {
logZitiResource(t, ctx, session, "service-by-id", "/services/"+url.PathEscape(exposure.OpenZitiServiceID))
}
if exposure.OpenZitiBindPolicyID != "" {
logZitiResource(t, ctx, session, "bind-policy-by-id", "/service-policies/"+url.PathEscape(exposure.OpenZitiBindPolicyID))
}
if exposure.OpenZitiDialPolicyID != "" {
logZitiResource(t, ctx, session, "dial-policy-by-id", "/service-policies/"+url.PathEscape(exposure.OpenZitiDialPolicyID))
}
}

func zitiFilterPath(resourcePath, field, value string) string {
return fmt.Sprintf("%s?filter=%s%%3D%%22%s%%22", resourcePath, url.QueryEscape(field), url.QueryEscape(value))
}

func createZitiManagementSession(t *testing.T, ctx context.Context) (zitiManagementSession, error) {
t.Helper()
username, password, err := zitiDiagnosticsCredentials(t, ctx)
if err != nil {
return zitiManagementSession{}, err
}

endpoint := zitiManagementEndpoint()
client := &http.Client{
Timeout: exposeRequestTimeout,
Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}},
}
payload := fmt.Sprintf(`{"username":%q,"password":%q}`, username, password)
request, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint+"/authenticate?method=password", strings.NewReader(payload))
endpoint := exposeDebugEndpoint()
requestURL := fmt.Sprintf("%s/debug/ziti/exposures/%s", endpoint, url.PathEscape(exposure.ID))
request, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
if err != nil {
return zitiManagementSession{}, fmt.Errorf("build authenticate request: %w", err)
t.Logf("diagnostics: expose debug request error: %v", err)
return
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("X-Expose-Debug-Token", token)

client := &http.Client{Timeout: exposeRequestTimeout}
response, err := client.Do(request)
if err != nil {
return zitiManagementSession{}, fmt.Errorf("authenticate: %w", err)
t.Logf("diagnostics: expose debug query error: %v", err)
return
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
return zitiManagementSession{}, fmt.Errorf("read authenticate response: %w", err)
}
if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices {
return zitiManagementSession{}, fmt.Errorf("authenticate status %d body=%s", response.StatusCode, truncateLogLine(strings.TrimSpace(string(body))))
}

var auth zitiAuthenticationResponse
if err := json.Unmarshal(body, &auth); err != nil {
return zitiManagementSession{}, fmt.Errorf("parse authenticate response: %w", err)
t.Logf("diagnostics: expose debug read error: %v", err)
return
}
token := strings.TrimSpace(auth.Data.Token)
if token == "" {
return zitiManagementSession{}, fmt.Errorf("authenticate response missing token")
trimmedBody := strings.TrimSpace(string(body))
if trimmedBody == "" {
trimmedBody = "{}"
}
return zitiManagementSession{endpoint: endpoint, token: token, client: client}, nil
t.Logf("diagnostics: expose debug status=%d body=%s", response.StatusCode, truncateLogLine(trimmedBody))
}

func zitiDiagnosticsCredentials(t *testing.T, ctx context.Context) (string, string, error) {
func exposeDebugToken(t *testing.T, ctx context.Context) (string, error) {
t.Helper()
secretNamespace, secretName := zitiDiagnosticsSecretRef()
if token := strings.TrimSpace(envOrDefault(exposeDebugTokenEnvKey, "")); token != "" {
return token, nil
}
secretNamespace, secretName, ok := exposeDebugTokenSecretRef()
if !ok {
return "", fmt.Errorf("%s or %s must be set for expose debug diagnostics", exposeDebugTokenEnvKey, exposeDebugTokenSecretKey)
}
secret, err := kubeClientset(t).CoreV1().Secrets(secretNamespace).Get(ctx, secretName, metav1.GetOptions{})
if err != nil {
return "", "", fmt.Errorf("get %s/%s: %w", secretNamespace, secretName, err)
return "", fmt.Errorf("get %s/%s: %w", secretNamespace, secretName, err)
}
username := strings.TrimSpace(string(secret.Data[zitiDiagnosticsUserKey]))
password := strings.TrimSpace(string(secret.Data[zitiDiagnosticsPasswordKey]))
if username == "" || password == "" {
return "", "", fmt.Errorf("%s/%s missing diagnostics credentials", secretNamespace, secretName)
token := strings.TrimSpace(string(secret.Data[exposeDebugTokenKey]))
if token == "" {
return "", fmt.Errorf("%s/%s missing expose debug token", secretNamespace, secretName)
}
return username, password, nil
}

func zitiDiagnosticsSecretRef() (string, string) {
return envOrDefault("E2E_NAMESPACE", envOrDefault("DEVSPACE_NAMESPACE", "platform")), zitiDiagnosticsSecretName
return token, nil
}

func zitiManagementEndpoint() string {
if explicitEndpoint := strings.TrimSpace(envOrDefault(zitiMgmtEndpointEnvKey, "")); explicitEndpoint != "" {
return strings.TrimRight(explicitEndpoint, "/")
func exposeDebugTokenSecretRef() (string, string, bool) {
ref := strings.TrimSpace(envOrDefault(exposeDebugTokenSecretKey, ""))
if ref == "" {
return "", "", false
}
domain := envOrDefault("E2E_DOMAIN", envOrDefault("DOMAIN", "agyn.dev"))
port := envOrDefault("E2E_INGRESS_PORT", envOrDefault("INGRESS_PORT", envOrDefault("PORT", "2496")))
return strings.TrimRight(fmt.Sprintf("https://%s.%s:%s%s", zitiMgmtServiceName, domain, port, zitiMgmtPath), "/")
}

func logZitiResource(t *testing.T, ctx context.Context, session zitiManagementSession, label, path string) {
t.Helper()
request, err := http.NewRequestWithContext(ctx, http.MethodGet, session.endpoint+path, nil)
if err != nil {
t.Logf("diagnostics: ziti %s request error: %v", label, err)
return
parts := strings.Split(ref, "/")
if len(parts) == 1 {
namespace := envOrDefault("E2E_NAMESPACE", envOrDefault("DEVSPACE_NAMESPACE", "platform"))
return namespace, parts[0], true
}
request.Header.Set("zt-session", session.token)

response, err := session.client.Do(request)
if err != nil {
t.Logf("diagnostics: ziti %s query error: %v", label, err)
return
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
t.Logf("diagnostics: ziti %s read error: %v", label, err)
return
if len(parts) == 2 && strings.TrimSpace(parts[0]) != "" && strings.TrimSpace(parts[1]) != "" {
return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), true
}
trimmedBody := strings.TrimSpace(string(body))
if trimmedBody == "" {
trimmedBody = "{}"
return "", "", false
}

func exposeDebugEndpoint() string {
if explicitEndpoint := strings.TrimSpace(envOrDefault(exposeDebugEndpointEnvKey, "")); explicitEndpoint != "" {
return strings.TrimRight(explicitEndpoint, "/")
}
t.Logf("diagnostics: ziti %s status=%d body=%s", label, response.StatusCode, truncateLogLine(trimmedBody))
namespace := envOrDefault("E2E_NAMESPACE", envOrDefault("DEVSPACE_NAMESPACE", "platform"))
return fmt.Sprintf("http://expose.%s.svc.cluster.local:8080", namespace)
}

func logExposeWorkloadSidecarLogs(t *testing.T, ctx context.Context, fixture exposeWorkloadFixture) {
Expand Down
9 changes: 8 additions & 1 deletion suites/playwright-chat-app/suite.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ run: |
export E2E_BASE_URL="${E2E_BASE_URL:-https://chat.${E2E_DOMAIN}}"

npm ci --no-fund --no-audit
npx buf generate
mkdir -p .bin
curl -fsSL --retry 3 --retry-delay 5 \
-o .bin/buf \
"https://github.com/bufbuild/buf/releases/download/v1.68.4/buf-$(uname -s)-$(uname -m)"
chmod +x .bin/buf
export PATH="$PWD/.bin:$PWD/node_modules/.bin:$PATH"
buf --version
buf generate

grep_tags=""
if [ -n "${TAGS:-}" ]; then
Expand Down
9 changes: 8 additions & 1 deletion suites/playwright-tracing-app/suite.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ run: |
cd /opt/app/data

npm ci --no-fund --no-audit
npx buf generate
mkdir -p .bin
curl -fsSL --retry 3 --retry-delay 5 \
-o .bin/buf \
"https://github.com/bufbuild/buf/releases/download/v1.68.4/buf-$(uname -s)-$(uname -m)"
chmod +x .bin/buf
export PATH="$PWD/.bin:$PWD/node_modules/.bin:$PATH"
buf --version
buf generate
export E2E_DOMAIN="${E2E_DOMAIN:-agyn.dev}"
export E2E_BASE_URL="${E2E_BASE_URL:-https://tracing.${E2E_DOMAIN}}"

Expand Down
9 changes: 8 additions & 1 deletion suites/playwright/suite.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,14 @@ run: |
export E2E_BASE_URL="${E2E_BASE_URL:-https://console.${E2E_DOMAIN}}"

npm ci --no-fund --no-audit
npx buf generate
mkdir -p .bin
curl -fsSL --retry 3 --retry-delay 5 \
-o .bin/buf \
"https://github.com/bufbuild/buf/releases/download/v1.68.4/buf-$(uname -s)-$(uname -m)"
chmod +x .bin/buf
export PATH="$PWD/.bin:$PWD/node_modules/.bin:$PATH"
buf --version
buf generate

grep_tags=""
if [ -n "${TAGS:-}" ]; then
Expand Down
Loading