Skip to content

Commit 4c3e411

Browse files
committed
feat(gateway): configure DNS records for Gateway API resources
Add Gateway reconciler, route DNS sub-reconcilers, GC, and Gateway API scheme registration. Include controller-runtime and Gateway status mocks, gateway-related golangci config, and shared controller metrics constants.
1 parent 7dfcba8 commit 4c3e411

38 files changed

Lines changed: 7612 additions & 5 deletions

cmd/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ import (
2323
"sigs.k8s.io/controller-runtime/pkg/manager"
2424
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
2525
"sigs.k8s.io/controller-runtime/pkg/webhook"
26+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
2627

2728
"github.com/vmware-tanzu/nsx-operator/pkg/apis/legacy/v1alpha1"
2829
crdv1alpha1 "github.com/vmware-tanzu/nsx-operator/pkg/apis/vpc/v1alpha1"
2930
"github.com/vmware-tanzu/nsx-operator/pkg/config"
31+
"github.com/vmware-tanzu/nsx-operator/pkg/controllers/gateway"
3032
"github.com/vmware-tanzu/nsx-operator/pkg/controllers/inventory"
3133
"github.com/vmware-tanzu/nsx-operator/pkg/controllers/ipaddressallocation"
3234
namespacecontroller "github.com/vmware-tanzu/nsx-operator/pkg/controllers/namespace"
@@ -83,6 +85,7 @@ func init() {
8385
utilruntime.Must(crdv1alpha1.AddToScheme(scheme))
8486
utilruntime.Must(v1alpha1.AddToScheme(scheme))
8587
utilruntime.Must(vmv1alpha1.AddToScheme(scheme))
88+
utilruntime.Must(gatewayv1.Install(scheme))
8689
config.AddFlags()
8790

8891
cf, err = config.NewNSXOperatorConfigFromFile()
@@ -248,6 +251,7 @@ func startServiceController(mgr manager.Manager, nsxClient *nsx.Client) {
248251
pod.NewPodReconciler(mgr, subnetPortService, subnetService, vpcService, nodeService),
249252
networkpolicycontroller.NewNetworkPolicyReconciler(mgr, commonService, vpcService),
250253
service.NewServiceLbReconciler(mgr, commonService, dnsRecordService),
254+
gateway.NewGatewayReconciler(mgr, dnsRecordService),
251255
subnetbindingcontroller.NewReconciler(mgr, subnetService, subnetBindingService),
252256
subnetipreservationcontroller.NewReconciler(mgr, subnetIPReservationService, subnetService),
253257
)

pkg/controllers/common/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const (
2525
MetricResTypeNode = "node"
2626
MetricResTypeServiceLb = "servicelb"
2727
MetricResTypeStatefulSet = "statefulset"
28+
MetricResTypeGateway = "gateway"
2829
MaxConcurrentReconciles = 8
2930
NSXOperatorError = "nsx-op/error"
3031
//sync the error with NCP side
@@ -35,6 +36,9 @@ const (
3536

3637
LabelK8sMasterRole = "node-role.kubernetes.io/master"
3738
LabelK8sControlRole = "node-role.kubernetes.io/control-plane"
39+
40+
ManagedK8sGatewayClassIstio = "istio"
41+
ManagedK8sGatewayClassAviLB = "avi-lb"
3842
)
3943

4044
var (
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/* Copyright © 2026 Broadcom, Inc. All Rights Reserved.
2+
SPDX-License-Identifier: Apache-2.0 */
3+
4+
package gateway
5+
6+
import (
7+
"strings"
8+
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/apimachinery/pkg/types"
11+
"sigs.k8s.io/controller-runtime/pkg/client"
12+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
13+
14+
servicecommon "github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/common"
15+
"github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/dns"
16+
extdns "github.com/vmware-tanzu/nsx-operator/pkg/third_party/externaldns/endpoint"
17+
extdnssrc "github.com/vmware-tanzu/nsx-operator/pkg/third_party/externaldns/source"
18+
)
19+
20+
// Route DNS: hostnames + ipCache → extdns.Endpoint; Gateway annotation DNS → owner-scoped batches (no ListenerSet annotation DNS).
21+
func (r *GatewayReconciler) buildEndpointsFromAnnotations(obj client.Object, targets extdns.Targets, owner *dns.ResourceRef, gwNN types.NamespacedName) (*dns.AggregatedDNSEndpoints, map[string]string, error) {
22+
parseHostnamesFromAnnotation := func(rawAnno map[string]string) []string {
23+
if rawAnno == nil {
24+
return nil
25+
}
26+
hostnames := rawAnno[servicecommon.AnnotationDNSHostnameKey]
27+
if strings.TrimSpace(hostnames) == "" {
28+
return nil
29+
}
30+
anno := map[string]string{
31+
servicecommon.AnnotationDNSHostnameSourceKey: extdnssrc.GatewayHostnameSourceAnnotationOnly,
32+
servicecommon.AnnotationDNSHostnameKey: hostnames,
33+
}
34+
hosts := extdnssrc.RouteHostnames(&metav1.ObjectMeta{Annotations: anno}, nil,
35+
servicecommon.AnnotationDNSHostnameSourceKey, servicecommon.AnnotationDNSHostnameKey, false)
36+
return hosts
37+
}
38+
39+
hosts := parseHostnamesFromAnnotation(obj.GetAnnotations())
40+
if len(hosts) == 0 {
41+
return nil, nil, nil
42+
}
43+
var eps []*extdns.Endpoint
44+
buildEndpoints(&eps, hosts, targets, gwNN.String())
45+
if len(eps) == 0 {
46+
return nil, nil, nil
47+
}
48+
endpointRows, allowed, err := r.DNS.ValidateEndpointsByZone(obj.GetNamespace(), owner, eps)
49+
if err != nil {
50+
return nil, allowed, err
51+
}
52+
return dns.NewOwnerScopedAggregatedRouteDNS(owner, endpointRows), allowed, nil
53+
}
54+
55+
type parentGatewayMatch struct {
56+
nn types.NamespacedName
57+
filter string
58+
ips extdns.Targets
59+
}
60+
61+
func mergeTargetsUnion(a, b extdns.Targets) extdns.Targets {
62+
return extdns.NewTargets(append(append([]string(nil), a...), b...)...)
63+
}
64+
65+
// buildRouteDNSMergedEndpoints builds owner-scoped Route DNS rows (Gateway or ListenerSet→root Gateway, same rules as aggregation).
66+
func (a routeReconcilerAdapter[PT, T]) buildRouteDNSMergedEndpoints(route PT) (*dns.AggregatedDNSEndpoints, map[string]string, error) {
67+
owner := route.GetResourceRef()
68+
eps, allowed, err := a.reconciler.buildRouteDNSEndpointsForAggregation(route.GetNamespace(), owner, route.GetParentRefs(), route.GetRouteParentStatus(), route.GetObjectMeta(), route.GetSpecHostnames())
69+
if err != nil || len(eps) == 0 {
70+
return nil, allowed, err
71+
}
72+
return dns.NewOwnerScopedAggregatedRouteDNS(owner, eps), allowed, nil
73+
}
74+
75+
// buildEndpoints appends extdns endpoints per hostname; sets EndpointLabelParentGateway to gatewayLabel (comma-separated if merged).
76+
func buildEndpoints(out *[]*extdns.Endpoint, hostnames []string, targets extdns.Targets, parentGatewayLabel string) {
77+
ttl := extdns.TTL(0)
78+
for _, h := range hostnames {
79+
if h == "" {
80+
continue
81+
}
82+
for _, ep := range extdns.EndpointsForHostname(h, targets, ttl) {
83+
if ep == nil {
84+
continue
85+
}
86+
if parentGatewayLabel != "" {
87+
ep.WithLabel(dns.EndpointLabelParentGateway, parentGatewayLabel)
88+
}
89+
*out = append(*out, ep)
90+
}
91+
}
92+
}
93+
94+
// collectGatewayEndpointsByAnnotation builds Gateway owner DNS from hostname annotation + targets.
95+
func (r *GatewayReconciler) collectGatewayEndpointsByAnnotation(gw *gatewayv1.Gateway, targets extdns.Targets) (*dns.AggregatedDNSEndpoints, map[string]string, error) {
96+
owner := &dns.ResourceRef{Kind: dns.ResourceKindGateway, Object: gw.GetObjectMeta()}
97+
gwNN := types.NamespacedName{Namespace: gw.Namespace, Name: gw.Name}
98+
out, allowed, err := r.buildEndpointsFromAnnotations(gw, targets, owner, gwNN)
99+
if err != nil {
100+
log.Error(err, "Failed to build DNS Endpoints for Gateway annotations", "Gateway", gwNN.String())
101+
return nil, allowed, err
102+
}
103+
return out, allowed, nil
104+
}

0 commit comments

Comments
 (0)