Skip to content

Commit 4bdd407

Browse files
Merge pull request #776 from agentgateway/kkb-fault-injection
Added fault injection topics
2 parents d2e11df + f9cd0f7 commit 4bdd407

3 files changed

Lines changed: 365 additions & 0 deletions

File tree

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
Inject artificial latency into requests with an {{< reuse "agw-docs/snippets/policy.md" >}} to test how your clients and upstream services handle slow responses. Fault injection is useful for verifying that timeouts, retries, and client-side deadlines behave as expected. To learn more, you can use fault injection alongside [Timeouts]({{< link-hextra path="/resiliency/timeouts/" >}}) and [Retries]({{< link-hextra path="/resiliency/retry/" >}}).
2+
3+
The injected delay counts against the request timeout. If the delay is longer than the configured [request timeout]({{< link-hextra path="/resiliency/timeouts/" >}}), the request times out.
4+
5+
{{< reuse "agw-docs/snippets/agentgateway/prereq.md" >}}
6+
7+
## Inject a delay {#inject-a-delay}
8+
9+
1. Create an HTTPRoute for the httpbin app.
10+
```yaml {paths="delay-in-trafficpolicy"}
11+
kubectl apply -n httpbin -f- <<EOF
12+
apiVersion: gateway.networking.k8s.io/v1
13+
kind: HTTPRoute
14+
metadata:
15+
name: httpbin-delay
16+
namespace: httpbin
17+
spec:
18+
hostnames:
19+
- faultinjection.example
20+
parentRefs:
21+
- name: agentgateway-proxy
22+
namespace: {{< reuse "agw-docs/snippets/namespace.md" >}}
23+
rules:
24+
- backendRefs:
25+
- kind: Service
26+
name: httpbin
27+
port: 8000
28+
EOF
29+
```
30+
31+
2. Create an {{< reuse "agw-docs/snippets/policy.md" >}} that injects a fixed 2-second delay into the requests that the HTTPRoute handles.
32+
```yaml {paths="delay-in-trafficpolicy"}
33+
kubectl apply -f- <<EOF
34+
apiVersion: {{< reuse "agw-docs/snippets/api-version.md" >}}
35+
kind: {{< reuse "agw-docs/snippets/policy.md" >}}
36+
metadata:
37+
name: httpbin-delay
38+
namespace: httpbin
39+
spec:
40+
targetRefs:
41+
- group: gateway.networking.k8s.io
42+
kind: HTTPRoute
43+
name: httpbin-delay
44+
traffic:
45+
delay:
46+
duration: 2s
47+
EOF
48+
```
49+
50+
{{< reuse "agw-docs/snippets/review-table.md" >}}
51+
52+
| Field | Description |
53+
| -- | -- |
54+
| `targetRefs` | The route, gateway, or listener to apply the delay to. In this example, the policy targets the `httpbin-delay` HTTPRoute. |
55+
| `traffic.delay.duration` | The latency to inject before the request is forwarded to the backend. Set either a duration string, such as `2s` or `500ms`, or a CEL expression that is evaluated against the request. For more information, see [Inject a probabilistic or random delay](#inject-a-probabilistic-or-random-delay). |
56+
57+
3. Send a request along the route and verify that the response is delayed by about 2 seconds.
58+
{{< tabs >}}
59+
{{% tab name="Cloud Provider LoadBalancer" %}}
60+
```sh
61+
curl -s -o /dev/null -w "%{time_total}s\n" http://$INGRESS_GW_ADDRESS/get -H "host: faultinjection.example"
62+
```
63+
{{% /tab %}}
64+
{{% tab name="Port-forward for local testing" %}}
65+
```sh
66+
curl -s -o /dev/null -w "%{time_total}s\n" localhost:8080/get -H "host: faultinjection.example"
67+
```
68+
{{% /tab %}}
69+
{{< /tabs >}}
70+
71+
Example output:
72+
```console
73+
2.01s
74+
```
75+
76+
4. Optional: Verify that the delay policy is applied in the proxy configuration.
77+
78+
1. Port-forward the gateway proxy on port 15000.
79+
```sh
80+
kubectl port-forward deploy/agentgateway-proxy -n {{< reuse "agw-docs/snippets/namespace.md" >}} 15000
81+
```
82+
83+
2. Find the delay policy in the config dump.
84+
```sh
85+
curl -s http://localhost:15000/config_dump | jq '[.policies[] | select(.policy.traffic.delay?)] | .[0]'
86+
```
87+
88+
## Inject a probabilistic or random delay {#inject-a-probabilistic-or-random-delay}
89+
90+
Because `duration` accepts a CEL expression, you can inject latency into only a subset of requests, or add jitter. The expression is evaluated against each request and returns either a duration or a number that is interpreted as milliseconds. A non-positive result injects no delay.
91+
92+
| Expression | Effect |
93+
| -- | -- |
94+
| `duration("500ms")` | A fixed 500ms delay, expressed as a CEL duration. |
95+
| `random() < 0.1 ? 500 : 0` | A 500ms delay on approximately 10% of requests, and no delay otherwise. |
96+
| `int(random() * 500.0)` | A random delay between 0 and 500ms (jitter) on every request. |
97+
98+
For example, the following policy delays approximately 10% of requests by 500ms.
99+
100+
```yaml
101+
kubectl apply -f- <<EOF
102+
apiVersion: {{< reuse "agw-docs/snippets/api-version.md" >}}
103+
kind: {{< reuse "agw-docs/snippets/policy.md" >}}
104+
metadata:
105+
name: httpbin-delay
106+
namespace: httpbin
107+
spec:
108+
targetRefs:
109+
- group: gateway.networking.k8s.io
110+
kind: HTTPRoute
111+
name: httpbin-delay
112+
traffic:
113+
delay:
114+
duration: "random() < 0.1 ? 500 : 0"
115+
EOF
116+
```
117+
118+
{{< doc-test paths="delay-in-trafficpolicy" >}}
119+
# WHAT THIS TEST VALIDATES:
120+
# * The httpbin-delay HTTPRoute (host faultinjection.example) is Accepted.
121+
# * The httpbin-delay AgentgatewayPolicy (traffic.delay.duration: 2s) is Accepted.
122+
# * A request to /get with host faultinjection.example returns 200 (the injected
123+
# delay does not break the request).
124+
# * The delay policy is present in the proxy config dump.
125+
# WHAT THIS TEST DOES NOT VALIDATE (and why):
126+
# * That the response is actually delayed by ~2s — Different layer: YAMLTest has
127+
# no response-latency assertion, so timing is not measured.
128+
# * The probabilistic/random CEL delay example — Display-only block, not tagged.
129+
# Warm up the new faultinjection.example hostname before asserting (two-phase proxy warmup).
130+
for i in $(seq 1 60); do
131+
curl -s --max-time 5 -o /dev/null "http://${INGRESS_GW_ADDRESS}:80/get" -H "host: faultinjection.example" && break
132+
sleep 2
133+
done
134+
{{< /doc-test >}}
135+
136+
{{< doc-test paths="delay-in-trafficpolicy" >}}
137+
YAMLTest -f - <<'EOF'
138+
- name: wait for httpbin-delay HTTPRoute to be accepted
139+
wait:
140+
target:
141+
kind: HTTPRoute
142+
metadata:
143+
namespace: httpbin
144+
name: httpbin-delay
145+
jsonPath: "$.status.parents[0].conditions[?(@.type=='Accepted')].status"
146+
jsonPathExpectation:
147+
comparator: equals
148+
value: "True"
149+
polling:
150+
timeoutSeconds: 300
151+
intervalSeconds: 5
152+
- name: wait for httpbin-delay AgentgatewayPolicy to be accepted
153+
wait:
154+
target:
155+
kind: AgentgatewayPolicy
156+
metadata:
157+
namespace: httpbin
158+
name: httpbin-delay
159+
jsonPath: "$.status.ancestors[0].conditions[?(@.type=='Accepted')].status"
160+
jsonPathExpectation:
161+
comparator: equals
162+
value: "True"
163+
polling:
164+
timeoutSeconds: 300
165+
intervalSeconds: 5
166+
- name: verify request through the delayed route returns 200
167+
retries: 1
168+
http:
169+
url: "http://${INGRESS_GW_ADDRESS}:80"
170+
path: /get
171+
method: GET
172+
headers:
173+
host: "faultinjection.example"
174+
source:
175+
type: local
176+
expect:
177+
statusCode: 200
178+
- name: verify delay policy in config dump
179+
http:
180+
url: http://localhost:15000
181+
skipSslVerification: true
182+
method: GET
183+
path: /config_dump
184+
source:
185+
type: pod
186+
usePortForward: true
187+
selector:
188+
kind: Deployment
189+
metadata:
190+
namespace: agentgateway-system
191+
name: agentgateway-proxy
192+
expect:
193+
bodyContains:
194+
- '"duration"'
195+
EOF
196+
{{< /doc-test >}}
197+
198+
## Cleanup
199+
200+
{{< reuse "agw-docs/snippets/cleanup.md" >}} Run the following commands.
201+
202+
```sh
203+
kubectl delete httproute httpbin-delay -n httpbin
204+
kubectl delete {{< reuse "agw-docs/snippets/policy.md" >}} httpbin-delay -n httpbin
205+
```
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
title: Fault injection
3+
weight: 20
4+
description: Inject artificial latency into requests to test how your clients and services handle slow responses.
5+
test:
6+
delay-in-trafficpolicy:
7+
- file: ${versionRoot}/quickstart/install.md
8+
path: experimental
9+
- file: ${versionRoot}/setup/gateway.md
10+
path: all
11+
- file: ${versionRoot}/install/sample-app.md
12+
path: install-httpbin
13+
- path: delay-in-trafficpolicy
14+
---
15+
16+
{{< reuse "agw-docs/pages/resiliency/fault-injection.md" >}}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
title: Fault injection
3+
weight: 20
4+
description: Inject artificial latency into requests to test how your clients and services handle slow responses.
5+
# TODO: re-enable once the agentgateway:latest-dev image includes the standalone
6+
# `delay` policy (added in agentgateway#2613 / commit c844384d). Until then the
7+
# --validate-only doc-test fails with "unknown field `delay`".
8+
# test:
9+
# fault-injection:
10+
# - file: ${versionRoot}/configuration/resiliency/fault-injection.md
11+
# path: fault-injection
12+
---
13+
14+
Attaches to: {{< badge content="Route" path="/configuration/routes/">}}
15+
16+
{{< reuse "agw-docs/snippets/config-styles-note.md" >}}
17+
18+
{{< doc-test paths="fault-injection" >}}
19+
{{< reuse "agw-docs/snippets/install-agentgateway-binary.md" >}}
20+
{{< /doc-test >}}
21+
22+
Fault injection adds artificial latency to matching requests before agentgateway forwards them to the backend. Use it to test how your clients and upstream services behave when responses are slow, such as verifying that timeouts, retries, and client-side deadlines work as expected.
23+
24+
The injected delay counts against the request timeout. If the delay is longer than the configured [request timeout]({{< link-hextra path="/configuration/resiliency/timeouts/" >}}), the request times out.
25+
26+
## Inject a delay
27+
28+
Set `delay.duration` in the route policies. The `duration` can be either of the following values.
29+
30+
| Value | Description |
31+
| -- | -- |
32+
| A duration string | A fixed latency to inject, such as `2s` or `500ms`. |
33+
| A CEL expression | An expression that is evaluated against each request and returns a duration or a number of milliseconds. Use this option for conditional, probabilistic, or randomized delays. A non-positive result injects no delay. |
34+
35+
The following example injects a fixed 2-second delay before agentgateway forwards requests to the backend.
36+
37+
{{< tabs >}}
38+
{{< tab name="Simplified (MCP)" >}}
39+
```yaml
40+
# yaml-language-server: $schema=https://agentgateway.dev/schema/config
41+
mcp:
42+
port: 3000
43+
policies:
44+
delay:
45+
duration: 2s
46+
targets:
47+
- name: everything
48+
stdio:
49+
cmd: npx
50+
args: ["@modelcontextprotocol/server-everything"]
51+
```
52+
{{< /tab >}}
53+
{{< tab name="Routing-based" >}}
54+
```yaml
55+
# yaml-language-server: $schema=https://agentgateway.dev/schema/config
56+
binds:
57+
- port: 3000
58+
listeners:
59+
- routes:
60+
- policies:
61+
delay:
62+
duration: 2s
63+
backends:
64+
- host: localhost:8080
65+
```
66+
{{< /tab >}}
67+
{{< /tabs >}}
68+
69+
## Inject a probabilistic or random delay
70+
71+
Because `duration` accepts a CEL expression, you can inject latency into only a subset of requests, or add jitter. The expression returns either a duration or a number that is interpreted as milliseconds.
72+
73+
| Expression | Effect |
74+
| -- | -- |
75+
| `duration("500ms")` | A fixed 500ms delay, expressed as a CEL duration. |
76+
| `random() < 0.1 ? 500 : 0` | A 500ms delay on approximately 10% of requests, and no delay otherwise. |
77+
| `int(random() * 500.0)` | A random delay between 0 and 500ms (jitter) on every request. |
78+
79+
The following example delays approximately 10% of requests by 500ms.
80+
81+
```yaml
82+
# yaml-language-server: $schema=https://agentgateway.dev/schema/config
83+
binds:
84+
- port: 3000
85+
listeners:
86+
- routes:
87+
- policies:
88+
delay:
89+
duration: "random() < 0.1 ? 500 : 0"
90+
backends:
91+
- host: localhost:8080
92+
```
93+
94+
{{< doc-test paths="fault-injection" >}}
95+
# WHAT THIS TEST VALIDATES:
96+
# * The delay (fault injection) policy is accepted by agentgateway in the
97+
# routing-based, simplified MCP, and CEL-expression forms.
98+
# WHAT THIS TEST DOES NOT VALIDATE (and why):
99+
# * That the latency is actually injected at runtime — verifying that would
100+
# require timing a live request against a running backend, which this page
101+
# omits.
102+
cat <<'EOF' > config.yaml
103+
# yaml-language-server: $schema=https://agentgateway.dev/schema/config
104+
binds:
105+
- port: 3000
106+
listeners:
107+
- routes:
108+
- policies:
109+
delay:
110+
duration: 2s
111+
backends:
112+
- host: localhost:8080
113+
EOF
114+
agentgateway -f config.yaml --validate-only
115+
116+
cat <<'EOF' > config-mcp.yaml
117+
# yaml-language-server: $schema=https://agentgateway.dev/schema/config
118+
mcp:
119+
port: 3000
120+
policies:
121+
delay:
122+
duration: 2s
123+
targets:
124+
- name: everything
125+
stdio:
126+
cmd: npx
127+
args: ["@modelcontextprotocol/server-everything"]
128+
EOF
129+
agentgateway -f config-mcp.yaml --validate-only
130+
131+
cat <<'EOF' > config-cel.yaml
132+
# yaml-language-server: $schema=https://agentgateway.dev/schema/config
133+
binds:
134+
- port: 3000
135+
listeners:
136+
- routes:
137+
- policies:
138+
delay:
139+
duration: "random() < 0.1 ? 500 : 0"
140+
backends:
141+
- host: localhost:8080
142+
EOF
143+
agentgateway -f config-cel.yaml --validate-only
144+
{{< /doc-test >}}

0 commit comments

Comments
 (0)