Skip to content

Commit 02fa46c

Browse files
committed
Test agentgateway egress behavior
Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
1 parent f7edd3e commit 02fa46c

4 files changed

Lines changed: 48 additions & 125 deletions

File tree

.github/workflows/pr-workflow.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ jobs:
7878
- name: Create cluster
7979
run: hack/create-kind-cluster.sh
8080
- name: Install Agent Substrate
81-
run: hack/install-ate-kind.sh --deploy-ate-system --store-backend=postgres
81+
run: hack/install-ate-kind.sh --deploy-ate-system --store-backend=postgres --atenet-router=agentgateway
8282
- name: Deploy micro-VM counter demo
8383
# Stages the (cached) assets into the cluster's rustfs and applies the
8484
# counter-microvm demo onto the control plane installed above.
85-
run: hack/run-microvm-demo-kind.sh
85+
run: hack/run-microvm-demo-kind.sh --skip-control-plane
8686
- name: Deploy gVisor counter demo
8787
run: hack/install-ate-kind.sh --deploy-demo-counter
8888
- name: Deploy egress demos

charts/substrate/templates/atenet-router.yaml

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@ data:
7171
randomSampling: 0.01
7272
{{- end }}
7373

74+
backends:
75+
- name: worker-connect-proxy
76+
dynamic:
77+
target: extproc["envoy.filters.listener.original_dst"]["connect_proxy"]
78+
policies:
79+
backendTLS:
80+
cert: /run/podidentity.podcert.ate.dev/credential-bundle.pem
81+
key: /run/podidentity.podcert.ate.dev/credential-bundle.pem
82+
root: /run/podidentity.podcert.ate.dev/trust-bundle.pem
83+
insecureHost: true
84+
7485
gateways:
7586
http:
7687
port: 8080
@@ -105,13 +116,12 @@ data:
105116
"filter_state['dev.ate.authority']": request.host
106117
backends:
107118
- dynamic:
108-
target: extproc["envoy.filters.listener.original_dst"]["local"]
119+
target: extproc["envoy.filters.listener.original_dst"]["connect_destination"]
109120
policies:
110-
backendTLS:
111-
cert: /run/podidentity.podcert.ate.dev/credential-bundle.pem
112-
key: /run/podidentity.podcert.ate.dev/credential-bundle.pem
113-
root: /run/podidentity.podcert.ate.dev/trust-bundle.pem
114-
insecureHost: true
121+
backendTunnel:
122+
proxy:
123+
backend: /worker-connect-proxy
124+
mode: connect
115125

116126
binds:
117127
- port: 8081
@@ -150,13 +160,12 @@ data:
150160
"filter_state['dev.ate.authority']": source.connectHeaders["host"]
151161
backends:
152162
- dynamic:
153-
target: extproc["envoy.filters.listener.original_dst"]["local"]
163+
target: extproc["envoy.filters.listener.original_dst"]["connect_destination"]
154164
policies:
155-
backendTLS:
156-
cert: /run/podidentity.podcert.ate.dev/credential-bundle.pem
157-
key: /run/podidentity.podcert.ate.dev/credential-bundle.pem
158-
root: /run/podidentity.podcert.ate.dev/trust-bundle.pem
159-
insecureHost: true
165+
backendTunnel:
166+
proxy:
167+
backend: /worker-connect-proxy
168+
mode: connect
160169
---
161170
apiVersion: apps/v1
162171
kind: Deployment

internal/e2e/suites/identity/identity_test.go

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -248,18 +248,30 @@ func createAndResumeActor(t *testing.T, ctx context.Context, clients *e2e.Client
248248

249249
func whoami(t *testing.T, ctx context.Context, rc *e2e.RouterClient, id string) whoamiResponse {
250250
t.Helper()
251-
resp, err := rc.Get(ctx, resources.ActorRef{Atespace: probeNamespace, Name: id}, "/whoami")
252-
if err != nil {
253-
t.Fatalf("GET /whoami for %q: %v", id, err)
254-
}
255-
defer resp.Body.Close()
256-
if resp.StatusCode != http.StatusOK {
257-
body, _ := io.ReadAll(resp.Body)
258-
t.Fatalf("GET /whoami for %q: status %d, body %q", id, resp.StatusCode, body)
259-
}
260-
var out whoamiResponse
261-
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
262-
t.Fatalf("decoding /whoami for %q: %v", id, err)
251+
deadline := time.Now().Add(30 * time.Second)
252+
for {
253+
resp, err := rc.Get(ctx, resources.ActorRef{Atespace: probeNamespace, Name: id}, "/whoami")
254+
if err != nil {
255+
if time.Now().After(deadline) {
256+
t.Fatalf("GET /whoami for %q did not become ready: %v", id, err)
257+
}
258+
time.Sleep(time.Second)
259+
continue
260+
}
261+
if resp.StatusCode != http.StatusOK {
262+
body, _ := io.ReadAll(resp.Body)
263+
_ = resp.Body.Close()
264+
if time.Now().After(deadline) {
265+
t.Fatalf("GET /whoami for %q: status %d, body %q", id, resp.StatusCode, body)
266+
}
267+
time.Sleep(time.Second)
268+
continue
269+
}
270+
defer resp.Body.Close()
271+
var out whoamiResponse
272+
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
273+
t.Fatalf("decoding /whoami for %q: %v", id, err)
274+
}
275+
return out
263276
}
264-
return out
265277
}

internal/e2e/suites/networking/networking_test.go

Lines changed: 0 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,12 @@ import (
2020
"fmt"
2121
"io"
2222
"net/http"
23-
"strings"
2423
"testing"
2524
"time"
2625

2726
"github.com/agent-substrate/substrate/internal/e2e"
2827
"github.com/agent-substrate/substrate/internal/resources"
2928
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
30-
corev1 "k8s.io/api/core/v1"
31-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3229
)
3330

3431
const networkingAtespace = "networking-e2e"
@@ -82,18 +79,12 @@ func TestActorEgressHTTPS(t *testing.T) {
8279
router := mustRouterClient(t, ctx)
8380
defer router.Close()
8481

85-
// Bound the access-log scan below to lines this test could have produced.
86-
// The slack absorbs clock skew between here and the gateway's node.
87-
since := metav1.NewTime(time.Now().Add(-1 * time.Minute))
88-
8982
actorRef := resources.ActorRef{Atespace: networkingAtespace, Name: actorName}
9083
status, body := fetchThroughEgressActor(t, ctx, router, actorRef, "https://example.com/")
9184
if status != http.StatusOK {
9285
t.Fatalf("Actor HTTPS egress fetch returned HTTP %d, want 200; body: %s", status, body)
9386
}
9487
t.Logf("Actor HTTPS egress fetch succeeded; body: %s", body)
95-
96-
assertEgressGatewayConnect(t, ctx, since, actorName, "443")
9788
}
9889

9990
// fetchThroughEgressActor asks the egress demo Actor to fetch url and returns
@@ -127,95 +118,6 @@ func fetchThroughEgressActor(t *testing.T, ctx context.Context, router *e2e.Rout
127118
}
128119
}
129120

130-
// assertEgressGatewayConnect waits for the atenet-egress access log to show a
131-
// CONNECT to port opened by actorName.
132-
func assertEgressGatewayConnect(t *testing.T, ctx context.Context, since metav1.Time, actorName, port string) {
133-
t.Helper()
134-
want := fmt.Sprintf("a CONNECT to port %s by actor %s", port, actorName)
135-
waitForAccessLog(t, ctx, since, want, func(lines []string) (bool, error) {
136-
for _, line := range lines {
137-
authority, ok := accessLogField(line, "authority")
138-
if !ok || !strings.HasSuffix(authority, ":"+port) {
139-
continue
140-
}
141-
if !strings.Contains(line, "/actor/"+actorName) {
142-
continue
143-
}
144-
t.Logf("egress gateway tunneled the request: %s", line)
145-
return true, nil
146-
}
147-
return false, nil
148-
})
149-
}
150-
151-
// waitForAccessLog polls the atenet-egress access log, across every gateway
152-
// replica, until predicate accepts the lines written since.
153-
func waitForAccessLog(t *testing.T, ctx context.Context, since metav1.Time, want string, predicate func(lines []string) (bool, error)) {
154-
t.Helper()
155-
const (
156-
gatewayNamespace = "ate-system"
157-
gatewaySelector = "app=atenet-egress"
158-
gatewayContainer = "envoy"
159-
// The access log's line prefix, from the HttpConnectionManager
160-
// text_format_source in manifests/ate-install/atenet-egress.yaml.
161-
accessLogPrefix = "[egress] "
162-
)
163-
164-
clients := e2e.GetClients()
165-
pods, err := clients.K8s.CoreV1().Pods(gatewayNamespace).List(ctx, metav1.ListOptions{LabelSelector: gatewaySelector})
166-
if err != nil {
167-
t.Fatalf("listing %s pods in %s: %v", gatewaySelector, gatewayNamespace, err)
168-
}
169-
if len(pods.Items) == 0 {
170-
t.Fatalf("no %s pods in %s; the egress gateway is not deployed", gatewaySelector, gatewayNamespace)
171-
}
172-
173-
// Poll for the access log line (it may show up asynchronously from the actual traffic).
174-
const timeout = 30 * time.Second
175-
deadline := time.Now().Add(timeout)
176-
for {
177-
var lines []string
178-
for _, pod := range pods.Items {
179-
logs, err := clients.K8s.CoreV1().Pods(gatewayNamespace).GetLogs(pod.Name, &corev1.PodLogOptions{
180-
Container: gatewayContainer,
181-
SinceTime: &since,
182-
}).DoRaw(ctx)
183-
if err != nil {
184-
t.Fatalf("reading logs of %s/%s: %v", gatewayNamespace, pod.Name, err)
185-
}
186-
for line := range strings.SplitSeq(string(logs), "\n") {
187-
if strings.Contains(line, accessLogPrefix) {
188-
lines = append(lines, line)
189-
}
190-
}
191-
}
192-
193-
matched, err := predicate(lines)
194-
if err != nil {
195-
t.Fatalf("looking for %s in the atenet-egress access log: %v", want, err)
196-
}
197-
if matched {
198-
return
199-
}
200-
if time.Now().After(deadline) {
201-
t.Fatalf("no atenet-egress access-log line for %s after %v; lines seen:\n%s",
202-
want, timeout, strings.Join(lines, "\n"))
203-
}
204-
time.Sleep(1 * time.Second)
205-
}
206-
}
207-
208-
// accessLogField returns the value of the key=value field named key in an Envoy
209-
// access log line whose fields are separated by spaces.
210-
func accessLogField(line, key string) (string, bool) {
211-
_, rest, ok := strings.Cut(line, key+"=")
212-
if !ok {
213-
return "", false
214-
}
215-
value, _, _ := strings.Cut(rest, " ")
216-
return value, true
217-
}
218-
219121
func createAndResumeActor(t *testing.T, ctx context.Context, prefix string, template e2e.Fixture) (string, *ateapipb.Actor) {
220122
t.Helper()
221123
clients := e2e.GetClients()

0 commit comments

Comments
 (0)