Skip to content

Commit 90be97f

Browse files
ivanmatmatiGopher Bot
authored andcommitted
DOC/MINOR: document attaching a Backend CR to a backend
Add documentation/CustomResources/backend.md, alongside the existing Defaults and Global CustomResource docs, describing how a Backend CR is merged into the backend HUG generates. It covers the two attachment mechanisms and how they interact: - route-level, via an ExtensionRef filter on an HTTPRoute backendRef, including the MergeType Append/Override directive for chaining several CRs (HTTPRoute only, as TLSRoute rules carry no filters); - Service-level, via the gate.v3.haproxy.org/backend-cr annotation, which applies to every backend built for that Service and works for both HTTPRoutes and TLSRoutes, with name or namespace/name values and a ReferenceGrant for cross-namespace references. It also documents precedence (Service-level wins, merged last), the backend identity rule (route-level CRs are part of the backend name hash, the Service-level annotation is not) and what re-reconciles the generated configuration.
1 parent 371cbc4 commit 90be97f

1 file changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
# Backend CustomResource
2+
3+
A `Backend` Custom Resource lets you tune the HAProxy `backend` section that HUG
4+
generates for a route's `backendRef`. The CR carries HAProxy backend settings
5+
(timeouts, balance algorithm, `http-request` rules, …) that are **merged** into
6+
the backend HUG builds from the Gateway API resources.
7+
8+
The CRD schema can be found here: [Backend CRD](../../api/definition/gate.v3.haproxy.org_backends.yaml)
9+
10+
The `spec` accepts all fields of the HAProxy `backend` section (via the embedded
11+
`models.Backend` from client-native).
12+
13+
---
14+
15+
## Two ways to attach a Backend CR
16+
17+
A `Backend` CR is never rendered on its own — it only takes effect once it is
18+
**attached** to a backend. There are two independent mechanisms:
19+
20+
| Mechanism | Declared on | Applies to | Selects the backend by |
21+
|---|---|---|---|
22+
| **Route-level** | an `HTTPRoute` `backendRef` (`ExtensionRef` filter) | HTTPRoute **only** | the specific `backendRef` (its filter hash) |
23+
| **Service-level** | a `Service` annotation (`gate.v3.haproxy.org/backend-cr`) | HTTPRoute **and** TLSRoute | every backend built for that Service |
24+
25+
> **TLSRoute note:** TLSRoute rules have no filters, so the route-level mechanism
26+
> is not available for TLSRoutes. A TLSRoute can only receive a Backend CR through
27+
> the Service-level annotation.
28+
29+
Both mechanisms can be combined; when they collide the **Service-level CR wins**
30+
(see [Precedence](#precedence)).
31+
32+
---
33+
34+
## The Backend CR
35+
36+
```yaml
37+
apiVersion: gate.v3.haproxy.org/v3
38+
kind: Backend
39+
metadata:
40+
name: backend-tuning
41+
namespace: example
42+
spec:
43+
server_timeout: 70000
44+
balance:
45+
algorithm: roundrobin
46+
http_request_rule_list:
47+
- cond: unless
48+
cond_test: '{ src 192.168.1.0/16 }'
49+
hdr_format: '%T'
50+
hdr_name: X-Haproxy-Current-Date
51+
type: add-header
52+
```
53+
54+
`spec.name` is irrelevant for the merge: HUG blanks it before merging so the CR
55+
cannot rename the generated backend. The backend name is always derived by HUG
56+
(see [Backend identity](#backend-identity)).
57+
58+
---
59+
60+
## Route-level attachment (HTTPRoute)
61+
62+
Attach a Backend CR to a single `backendRef` with an `ExtensionRef` filter of
63+
kind `Backend`:
64+
65+
```yaml
66+
apiVersion: gateway.networking.k8s.io/v1
67+
kind: HTTPRoute
68+
metadata:
69+
name: route-echo
70+
spec:
71+
parentRefs:
72+
- name: gateway
73+
sectionName: http
74+
rules:
75+
- matches:
76+
- path:
77+
type: PathPrefix
78+
value: /
79+
backendRefs:
80+
- name: http-echo
81+
port: 80
82+
filters:
83+
- type: ExtensionRef
84+
extensionRef:
85+
group: gate.v3.haproxy.org
86+
kind: Backend
87+
name: backend-tuning
88+
```
89+
90+
The CR is looked up **in the HTTPRoute's own namespace** (the `ExtensionRef` has
91+
no namespace field).
92+
93+
### Multiple CRs and merge order — `MergeType`
94+
95+
Several `Backend` CRs can be listed on the same `backendRef`; they are merged in
96+
declaration order. A special `ExtensionRef` of kind **`MergeType`** (name
97+
`Override` or `Append`) switches the merge behaviour of the CRs that follow it:
98+
99+
```yaml
100+
filters:
101+
- type: ExtensionRef
102+
extensionRef:
103+
group: gate.v3.haproxy.org
104+
kind: Backend
105+
name: backend-cr-1
106+
- type: ExtensionRef
107+
extensionRef:
108+
group: gate.v3.haproxy.org
109+
kind: MergeType
110+
name: Override # switch the following CRs to "override"
111+
- type: ExtensionRef
112+
extensionRef:
113+
group: gate.v3.haproxy.org
114+
kind: Backend
115+
name: backend-cr-2
116+
```
117+
118+
`MergeType` is not a real resource — it is only a directive:
119+
120+
| `MergeType` | Effect on list fields (e.g. `http_request_rule_list`) |
121+
|---|---|
122+
| `Append` *(default)* | CR list entries are **appended** to what is already there |
123+
| `Override` | CR list entries **replace** the existing list |
124+
125+
Scalar fields always follow the same rule regardless of `MergeType`: a non-zero
126+
value in the CR replaces the current one.
127+
128+
---
129+
130+
## Service-level attachment (HTTPRoute and TLSRoute)
131+
132+
Annotate the target `Service` with `gate.v3.haproxy.org/backend-cr`. The CR is
133+
then merged into **every** backend HUG builds for that Service, no matter which
134+
route (HTTPRoute or TLSRoute) references it:
135+
136+
```yaml
137+
apiVersion: v1
138+
kind: Service
139+
metadata:
140+
name: http-echo
141+
namespace: example
142+
annotations:
143+
gate.v3.haproxy.org/backend-cr: backend-tuning
144+
spec:
145+
ports:
146+
- name: http
147+
port: 80
148+
targetPort: http
149+
```
150+
151+
The annotation value is either:
152+
153+
- `name` — a Backend CR in the **Service's own namespace**, or
154+
- `namespace/name` — a Backend CR in another namespace (see
155+
[Cross-namespace references](#cross-namespace-references)).
156+
157+
The Service-level CR is merged with **override** semantics: scalar values and
158+
**whole list fields** in the CR replace what was there. Route-level results are
159+
never appended to by the Service-level CR.
160+
161+
A missing/unresolved annotation target, or a cross-namespace reference without a
162+
`ReferenceGrant`, is **ignored** (the backend is still built) and an error is
163+
logged — one bad annotation cannot break the backend build.
164+
165+
---
166+
167+
## Cross-namespace references
168+
169+
A `namespace/name` annotation that points outside the Service's namespace must be
170+
authorised by a `ReferenceGrant` living in the **target** namespace (the Backend
171+
CR's namespace), granting `Service` → `Backend` access. Same-namespace references
172+
never need a grant.
173+
174+
```yaml
175+
apiVersion: v1
176+
kind: Service
177+
metadata:
178+
name: http-echo
179+
namespace: team-a
180+
annotations:
181+
gate.v3.haproxy.org/backend-cr: infra/backend-tuning # CR lives in "infra"
182+
---
183+
apiVersion: gateway.networking.k8s.io/v1beta1
184+
kind: ReferenceGrant
185+
metadata:
186+
name: allow-team-a-to-backend-tuning
187+
namespace: infra # target namespace
188+
spec:
189+
from:
190+
- group: ""
191+
kind: Service
192+
namespace: team-a
193+
to:
194+
- group: gate.v3.haproxy.org
195+
kind: Backend
196+
name: backend-tuning # omit "name" to allow every Backend CR here
197+
```
198+
199+
Adding, changing or removing the grant re-reconciles the affected routes.
200+
201+
---
202+
203+
## Precedence
204+
205+
When a `backendRef` carries a route-level Backend CR **and** its target Service
206+
is annotated, both are applied to the same backend, in this order:
207+
208+
```
209+
HUG baseline backend
210+
──► route-level Backend CR(s) (HTTPRoute ExtensionRef, Append/Override)
211+
──► Service-level Backend CR (annotation, override — wins)
212+
```
213+
214+
The Service-level CR is always merged **last**, so for any field it sets, the
215+
**Service wins over the route-level CR**.
216+
217+
---
218+
219+
## Backend identity
220+
221+
HUG names each backend from `service + port + filter-hash`. The **route-level**
222+
`ExtensionRef` Backend CRs are part of that filter hash, so two `backendRef`s to
223+
the same service/port that reference different route-level CRs produce **two
224+
distinct backends**.
225+
226+
The **Service-level** annotation is **not** part of the hash: it applies uniformly
227+
to every backend of the Service and never changes a backend's name.
228+
229+
Consequence: two routes that resolve to the same service/port with identical
230+
filters share **one** backend. If they also carry conflicting route-level CRs the
231+
backend is built once from whichever `backendRef` is processed — there is no
232+
conflict detection. Keep per-route tuning consistent, or move shared tuning to the
233+
Service-level annotation.
234+
235+
---
236+
237+
## Re-reconciliation
238+
239+
Changes propagate to already-generated configuration automatically:
240+
241+
- editing a `Backend` CR re-reconciles every HTTPRoute referencing it via
242+
`ExtensionRef` and every HTTPRoute/TLSRoute whose target Service is annotated to
243+
it;
244+
- editing a Service annotation re-reconciles the routes reaching that Service;
245+
- adding/removing a `ReferenceGrant` re-reconciles the routes whose Service
246+
references a Backend CR across namespaces.

0 commit comments

Comments
 (0)