Skip to content

feat(service): LoadBalancer Service with DNS annotation#1426

Open
wenyingd wants to merge 3 commits into
vmware-tanzu:mainfrom
wenyingd:dns-networkinfo-service
Open

feat(service): LoadBalancer Service with DNS annotation#1426
wenyingd wants to merge 3 commits into
vmware-tanzu:mainfrom
wenyingd:dns-networkinfo-service

Conversation

@wenyingd
Copy link
Copy Markdown
Contributor

@wenyingd wenyingd commented May 6, 2026

Add Service LB DNS reconciliation using DNSRecordProvider, ExternalDNS hostname admission helpers, and gomock DNSRecordProvider stubs. Wire ServiceLbReconciler with shared DNS record service in cmd.

Test Done:

  1. Prepare a vSphere Namespace which is configured with DNS zone configured with domain name "example.com"
  2. nsx-operator has sync the DNS domain names of the zone to NetworkInfo CR
  3. Prepare a LB Service in the namespace
 kubectl get svc -n test1 httpbin -oyaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"httpbin","service":"httpbin"},"name":"httpbin","namespace":"test1"},"spec":{"ports":[{"name":"http","port":8000,"targetPort":8080}],"selector":{"app":"httpbin"}}}
  creationTimestamp: "2026-05-12T08:24:00Z"
  finalizers:
  - netoperator.vmware.com/service
  labels:
    app: httpbin
    service: httpbin
    service.route.lbapi.run.tanzu.vmware.com/gateway-name: httpbin
    service.route.lbapi.run.tanzu.vmware.com/gateway-namespace: test1
    service.route.lbapi.run.tanzu.vmware.com/type: direct
  name: httpbin
  namespace: test1
  resourceVersion: "4859390"
  uid: b3222d4a-4662-4212-beb8-1e397b510209
spec:
  allocateLoadBalancerNodePorts: true
  clusterIP: 172.24.54.251
  clusterIPs:
  - 172.24.54.251
  externalTrafficPolicy: Cluster
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - name: http
    nodePort: 32748
    port: 8000
    protocol: TCP
    targetPort: 8080
  selector:
    app: httpbin
  sessionAffinity: None
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.168.0.14
      ipMode: Proxy
  1. Annotate the LB Service with the desired hostname, check a Ready=True condition is added
root@421833fb973aade676c9a60ec986fda1 [ ~ ]#  kubectl -n test1 annotate svc httpbin nsx.vmware.com/hostname="my-svc.example.com"
service/httpbin annotated

root@421833fb973aade676c9a60ec986fda1 [ ~ ]# kubectl get svc httpbin -n test1 -oyaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    nsx.vmware.com/hostname: my-svc.example.com
....
  name: httpbin
  namespace: test1
  resourceVersion: "4869993"
  uid: b3222d4a-4662-4212-beb8-1e397b510209
spec:
...
  type: LoadBalancer
status:
  conditions:
  - lastTransitionTime: "2026-05-14T09:13:53Z"
    message: ""
    reason: DNSRecordConfigured
    status: "True"
    type: Ready
  loadBalancer:
    ingress:
    - ip: 192.168.0.14
      ipMode: Proxy
  1. Annotate an invalid the hostname in the Service which is not matched by the DNS zone, check a Ready=False condition is added
root@421833fb973aade676c9a60ec986fda1 [ ~ ]#  kubectl -n test1 annotate svc httpbin nsx.vmware.com/hostname="my-svc.example1.com" --overwrite
service/httpbin annotated

root@421833fb973aade676c9a60ec986fda1 [ ~ ]# kubectl get svc httpbin -n test1 -oyaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    nsx.vmware.com/hostname: my-svc.example1.com
...
  name: httpbin
  namespace: test1
  resourceVersion: "4909874"
  uid: b3222d4a-4662-4212-beb8-1e397b510209
spec:
 ...
  type: LoadBalancer
status:
  conditions:
  - lastTransitionTime: "2026-05-14T10:17:17Z"
    message: hostname "my-svc.example1.com" does not match any allowed DNS domain
      in the namespace
    reason: DNSRecordFailed
    status: "False"
    type: Ready
  loadBalancer:
    ingress:
    - ip: 192.168.0.14
      ipMode: Proxy
  1. Update the annotation with a valid hostname, the Ready condition is updated as true,
root@421833fb973aade676c9a60ec986fda1 [ ~ ]# kubectl -n test1 annotate svc httpbin nsx.vmware.com/hostname="my-svc.example.com" --overwrite
service/httpbin annotated
root@421833fb973aade676c9a60ec986fda1 [ ~ ]# kubectl get svc httpbin -n test1 -oyaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    nsx.vmware.com/hostname: my-svc.example.com
...
  name: httpbin
  namespace: test1
  resourceVersion: "4912750"
  uid: b3222d4a-4662-4212-beb8-1e397b510209
spec:
...
  type: LoadBalancer
status:
  conditions:
  - lastTransitionTime: "2026-05-14T10:21:25Z"
    message: ""
    reason: DNSRecordConfigured
    status: "True"
    type: Ready
  loadBalancer:
    ingress:
    - ip: 192.168.0.14
      ipMode: Proxy
  1. Remove the annotation, the Ready condition is deleted
root@421833fb973aade676c9a60ec986fda1 [ ~ ]# kubectl -n test1 annotate svc httpbin nsx.vmware.com/hostname-
service/httpbin annotated
root@421833fb973aade676c9a60ec986fda1 [ ~ ]# kubectl get svc httpbin -n test1 -oyaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"httpbin","service":"httpbin"},"name":"httpbin","namespace":"test1"},"spec":{"ports":[{"name":"http","port":8000,"targetPort":8080}],"selector":{"app":"httpbin"}}}
  creationTimestamp: "2026-05-12T08:24:00Z"
...
  name: httpbin
  namespace: test1
  resourceVersion: "4914102"
  uid: b3222d4a-4662-4212-beb8-1e397b510209
spec:
...
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.168.0.14
      ipMode: Proxy
  1. Update the annotation with a FQDN already taken by other resources, a Ready=False condition is added
root@421833fb973aade676c9a60ec986fda1 [ ~ ]# kubectl -n test1 annotate svc httpbin nsx.vmware.com/hostname="httpbin.example.com" --overwrite
service/httpbin annotated
root@421833fb973aade676c9a60ec986fda1 [ ~ ]# kubectl get svc httpbin -n test1 -oyaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    nsx.vmware.com/hostname: httpbin.example.com
...
  name: httpbin
  namespace: test1
  resourceVersion: "4914784"
  uid: b3222d4a-4662-4212-beb8-1e397b510209
spec:
...
  type: LoadBalancer
status:
  conditions:
  - lastTransitionTime: "2026-05-14T10:24:28Z"
    message: 'DNS endpoint validation failed for DNS zone policy: FQDN httpbin.example.com
      is configured with different values in DNS zone /orgs/default/projects/project-quality/dns-services/default/zones/zone-1'
    reason: DNSRecordFailed
    status: "False"
    type: Ready
  loadBalancer:
    ingress:
    - ip: 192.168.0.14
      ipMode: Proxy
  1. Update the annotation with one valid hostname and one invalid hostname, Ready=False condition is configured.
root@421833fb973aade676c9a60ec986fda1 [ ~ ]# kubectl get svc httpbin -n test1 -oyaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    nsx.vmware.com/hostname: my-svc.example.com,public-svc.example1.com
...
  name: httpbin
  namespace: test1
  resourceVersion: "4917172"
  uid: b3222d4a-4662-4212-beb8-1e397b510209
spec:
...
  type: LoadBalancer
status:
  conditions:
  - lastTransitionTime: "2026-05-14T10:28:04Z"
    message: hostname "public-svc.example1.com" does not match any allowed DNS domain
      in the namespace
    reason: DNSRecordFailed
    status: "False"
    type: Ready
  loadBalancer:
    ingress:
    - ip: 192.168.0.14
      ipMode: Proxy
  1. Check the valid hostname can be requested by other services after the current transaction is failed
root@421833fb973aade676c9a60ec986fda1 [ ~ ]# kubectl -n test1 annotate svc curl nsx.vmware.com/hostname="my-svc.example.com" --overwrite
service/curl annotated
root@421833fb973aade676c9a60ec986fda1 [ ~ ]# kubectl get svc -n test1 curl -oyaml
apiVersion: v1
kind: Service
metadata:
  annotations:
    nsx.vmware.com/hostname: my-svc.example.com
...
  name: curl
  namespace: test1
  resourceVersion: "4922354"
  uid: 91c4ba6c-abeb-4985-91cf-df9542254ed1
spec:
...
  type: LoadBalancer
status:
  conditions:
  - lastTransitionTime: "2026-05-14T10:36:09Z"
    message: ""
    reason: DNSRecordConfigured
    status: "True"
    type: Ready
  loadBalancer:
    ingress:
    - ip: 192.168.0.15
      ipMode: Proxy

@wenyingd wenyingd force-pushed the dns-networkinfo-service branch from 4764fcf to b3bbbe8 Compare May 6, 2026 08:58
@wenyingd wenyingd changed the title feat(service): LoadBalancer Service DNS with VPC hostname validation feat(service): LoadBalancer Service DNS with DNS annotation May 6, 2026
@wenyingd wenyingd changed the title feat(service): LoadBalancer Service DNS with DNS annotation feat(service): LoadBalancer Service with DNS annotation May 6, 2026
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 6, 2026

Codecov Report

❌ Patch coverage is 81.57895% with 259 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.29%. Comparing base (6ae0434) to head (4551f66).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/controllers/service/service_lb_dns.go 65.62% 39 Missing and 16 partials ⚠️
pkg/nsx/services/dns/recordservice.go 79.62% 34 Missing and 20 partials ⚠️
pkg/controllers/service/service_lb_controller.go 39.28% 36 Missing and 15 partials ⚠️
.../controllers/networkinfo/networkinfo_controller.go 35.00% 12 Missing and 1 partial ⚠️
pkg/nsx/services/dns/store.go 94.19% 7 Missing and 6 partials ⚠️
pkg/nsx/services/dns/compare.go 84.00% 9 Missing and 3 partials ⚠️
pkg/nsx/services/dns/zones.go 88.77% 6 Missing and 5 partials ⚠️
.../third_party/externaldns/source/route_hostnames.go 81.39% 4 Missing and 4 partials ⚠️
cmd/main.go 0.00% 6 Missing ⚠️
...g/third_party/externaldns/annotations/hostnames.go 45.45% 3 Missing and 3 partials ⚠️
... and 9 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1426      +/-   ##
==========================================
+ Coverage   77.03%   77.29%   +0.26%     
==========================================
  Files         155      171      +16     
  Lines       22043    23415    +1372     
==========================================
+ Hits        16980    18099    +1119     
- Misses       3858     4026     +168     
- Partials     1205     1290      +85     
Flag Coverage Δ
unit-tests 77.29% <81.57%> (+0.26%) ⬆️
Files with missing lines Coverage Δ
pkg/nsx/client.go 93.42% <100.00%> (+0.11%) ⬆️
pkg/nsx/services/common/policy_tree.go 86.48% <100.00%> (+0.24%) ⬆️
pkg/nsx/services/common/types.go 100.00% <ø> (ø)
pkg/nsx/services/dns/errors.go 100.00% <100.00%> (ø)
pkg/nsx/services/dns/types.go 100.00% <100.00%> (ø)
pkg/third_party/externaldns/endpoint/endpoint.go 100.00% <100.00%> (ø)
pkg/util/utils.go 87.12% <100.00%> (+0.12%) ⬆️
pkg/clean/clean.go 86.88% <71.42%> (-0.95%) ⬇️
pkg/third_party/externaldns/endpoint/utils.go 90.47% <90.47%> (ø)
pkg/third_party/externaldns/provider/zonefinder.go 90.90% <90.90%> (ø)
... and 16 more

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wenyingd wenyingd force-pushed the dns-networkinfo-service branch 15 times, most recently from 4551f66 to 4fcb4bf Compare May 15, 2026 07:25
wenyingd and others added 3 commits May 15, 2026 15:29
- Implement DNSRecordService for NSX ProjectDnsRecord CRUD operations
- Validate hostnames against VPCNetworkConfiguration allowed DNS zones
- Wrap hostname-mismatch error as DNSZoneValidationError for accurate
  DNSRecordReady condition reporting

Co-authored-by: Cursor <cursoragent@cursor.com>
Wire NetworkInfo reconciler to DNSRecordService for per-namespace allowed
DNS domains derived from VPC DNS zone configuration. Register DNS record
service initialization in cmd when VPC networking is enabled.
- Reconcile DNS records based on the hostname annotation on LoadBalancer
  Services using VPCNetworkConfiguration allowed DNS zones
- Report DNSRecordReady condition for DNS zone validation errors and
  generic DNS build errors

Co-authored-by: Cursor <cursoragent@cursor.com>
@wenyingd wenyingd force-pushed the dns-networkinfo-service branch from 4fcb4bf to 4cfcb3a Compare May 15, 2026 07:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants