SitePeering status is not reconciled when no GatewayPools exist
- Dominant language
- Go
- Stars
- 28
- Forks
- 11
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 55
Description
## Bug
`SitePeering.status` is never reconciled when a cluster has no `GatewayPool` resources.
This affects direct private-L3 topologies that intentionally use:
```yaml
spec:
meshNodes: false
tunnelProtocol: None
```
## Version
- Unbounded: `v0.1.24-rc.10`
- Commit: `fa854d29b8a1861d5fa96e98b08c748835b19f52`
- Kubernetes: AKS `v1.35.6`
## Reproduction
1. Install the rc.10 operator and Unbounded-Net.
2. Create two promoted `unbounded-cloud.io/v1alpha3` Sites with one Ready node in each Site.
3. Create a direct-L3 SitePeering and no GatewayPools:
```yaml
apiVersion: net.unbounded-cloud.io/v1alpha1
kind: SitePeering
metadata:
name: cluster-flex-private-l3
spec:
sites:
- cluster
- flex
meshNodes: false
tunnelProtocol: None
```
4. Observe the resource:
```console
$ kubectl get sitepeering cluster-flex-private-l3 -o yaml
apiVersion: net.unbounded-cloud.io/v1alpha1
kind: SitePeering
metadata:
name: cluster-flex-private-l3
spec:
meshNodes: false
sites:
- cluster
- flex
tunnelProtocol: None
```
There is no `status` section, even after both Sites contain Ready nodes.
## Expected behavior
The controller should populate status independently of GatewayPool presence:
```yaml
status:
peeredSiteCount: 2
totalNodeCount: 2
```
The API type and CRD expose these fields as the `Sites` and `Nodes` printer columns.
## Actual behavior
`SitePeering.status` remains absent. Both Sites report `NetReady=True`, both nodes are Ready, and there are no controller errors.
RBAC is not the problem. The live controller service account is authorized:
```console
$ kubectl auth can-i patch sitepeerings.net.unbounded-cloud.io \
--subresource=status \
--as=system:serviceaccount:unbounded-system:unbounded-net-controller
yes
$ kubectl auth can-i update sitepeerings.net.unbounded-cloud.io \
--subresource=status \
--as=system:serviceaccount:unbounded-system:unbounded-net-controller
yes
```
The rc.10 release manifest and live ClusterRole both include `sitepeerings/status` with `get`, `patch`, and `update`.
## Root cause
`PeeringAggregationController` couples SitePeering status reconciliation to GatewayPool work items:
- SitePeering and Site events call `enqueueAllPools()`.
- `enqueueAllPools()` only enqueues names found in `gatewayPoolInformer`.
- A direct-L3 topology intentionally has zero GatewayPools, so no work item is enqueued.
- `reconcileSitePeeringStatuses()` is called only from `syncPool()`.
- Therefore status reconciliation never runs when there are no GatewayPools.
Relevant code:
- `internal/net/controller/peering_aggregation_controller.go`
- event handlers and `enqueueAllPools()`
- `syncPool()`
- `reconcileSitePeeringStatuses()`
## Suggested fix
Decouple SitePeering status reconciliation from per-GatewayPool synchronization. For example:
- Add a dedicated global SitePeering-status queue key or a separate controller.
- Enqueue it on SitePeering and Site add/update/delete events.
- Run it once after informer cache synchronization.
- Keep GatewayPool reachability reconciliation on per-pool keys.
The fix should also handle a transition from one GatewayPool to zero GatewayPools without leaving stale SitePeering status.
## Missing regression coverage
Add tests for:
1. Two Sites plus one SitePeering and zero GatewayPools produces `peeredSiteCount=2` and the summed node count.
2. A Site `status.nodeCount` update refreshes `totalNodeCount` when there are zero GatewayPools.
3. Site deletion refreshes both status counts.
4. Deleting the final GatewayPool does not stop or stale SitePeering status reconciliation.
5. No status patch is emitted when the desired status is unchanged.
The current `peering_aggregation_controller_test.go` covers reachability calculations but does not cover SitePeering status reconciliation or the zero-GatewayPool event path.
Contributor guide
Research direction
Read internal/net/controller/peering_aggregation_controller.go, focusing on the event handlers, enqueueAllPools(), syncPool(), and reconcileSitePeeringStatuses(). Then run the existing peering_aggregation_controller_test.go and add coverage for zero GatewayPools, Site status changes, Site deletion, final GatewayPool deletion, and unchanged status. Done means SitePeering status counts reconcile in each case without unnecessary patches.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, kubernetes
- Domain
- backend, infrastructure
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100