envoyproxy / envoyproxy/gateway
BackendTrafficPolicy status becomes stale when targetSelectors change from matching routes to matching no routes
- Dominant language
- Go
- Stars
- 3k
- Forks
- 864
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 140
Description
### Description
A `BackendTrafficPolicy` using `targetSelectors` can become stale in status when the selector is changed from matching an existing route to matching no routes.
After the selector update:
- the policy `metadata.generation` increments
- status still shows the old `Accepted=True` condition
- `observedGeneration` remains stale
- no condition indicates that the selector now matches zero resources
This makes the policy appear attached/valid even after it no longer selects any targets.
### Environment
- Envoy Gateway repo: `main`
- Reproduced at commit: `69b1dde32fdb44cca7eb3ee880064e36f90492f4`
### Reproducer
1. Install Envoy Gateway from current `main`
2. Apply the quickstart example
3. Label the quickstart `HTTPRoute`
4. Create a `BackendTrafficPolicy` using `targetSelectors` that matches that route
5. Update the same policy so the selector matches no routes
### Route setup
Label the quickstart route:
```bash
kubectl -n default label httproute backend app=selector-demo --overwrite
```
### Initial policy that matches
```yaml
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
name: quickstart-btp-selector-flip
namespace: default
spec:
targetSelectors:
- group: gateway.networking.k8s.io
kind: HTTPRoute
matchLabels:
app: selector-demo
retry:
numRetries: 3
perRetry:
backOff:
baseInterval: 100ms
maxInterval: 1s
timeout: 250ms
retryOn:
triggers:
- 5xx
- gateway-error
- connect-failure
```
Observed status after create:
```yaml
status:
ancestors:
- ancestorRef:
group: gateway.networking.k8s.io
kind: Gateway
name: eg
namespace: default
conditions:
- type: Accepted
status: "True"
reason: Accepted
message: Policy has been accepted.
observedGeneration: 1
```
### Updated policy that matches nothing
```yaml
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
name: quickstart-btp-selector-flip
namespace: default
spec:
targetSelectors:
- group: gateway.networking.k8s.io
kind: HTTPRoute
matchLabels:
app: selector-demo-no-match
retry:
numRetries: 3
perRetry:
backOff:
baseInterval: 100ms
maxInterval: 1s
timeout: 250ms
retryOn:
triggers:
- 5xx
- gateway-error
- connect-failure
```
The route labels remain:
```bash
kubectl -n default get httproute backend --show-labels
# NAME HOSTNAMES AGE LABELS
# backend ["www.example.com"] ... app=selector-demo
```
So the updated selector no longer matches any route.
### Observed behavior
After the update:
- `metadata.generation` increments from `1` to `2`
- status still shows:
- `Accepted=True`
- `observedGeneration=1`
- no condition indicates:
- zero selected resources
- detachment / unattached policy
- selector resolution result for the new generation
Example:
```yaml
metadata:
generation: 2
status:
ancestors:
- ancestorRef:
group: gateway.networking.k8s.io
kind: Gateway
name: eg
namespace: default
conditions:
- type: Accepted
status: "True"
reason: Accepted
message: Policy has been accepted.
observedGeneration: 1
```
### Expected behavior
When a selector-based policy changes from matching resources to matching none, I’d expect status to reflect the new state, for example by:
- updating `observedGeneration` to the latest generation
- surfacing a condition that zero resources are selected / attached
- or otherwise making it clear that the policy is no longer effectively attached
### Why this matters
This is misleading operationally:
- the initial status looks correct when the selector matches
- after the update, the policy still appears accepted and attached
- but it no longer selects any resources
- stale `observedGeneration` makes it unclear whether reconciliation completed
This makes selector-based policy debugging harder and can hide unintended detachments.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.