@@ -5,25 +5,32 @@ package service
55
66import (
77 "context"
8+ "fmt"
89 "time"
910
1011 v1 "k8s.io/api/core/v1"
1112 apierrors "k8s.io/apimachinery/pkg/api/errors"
1213 apimachineryruntime "k8s.io/apimachinery/pkg/runtime"
14+ "k8s.io/apimachinery/pkg/types"
15+ "k8s.io/apimachinery/pkg/util/sets"
1316 "k8s.io/apimachinery/pkg/util/version"
1417 clientset "k8s.io/client-go/kubernetes"
1518 "k8s.io/client-go/rest"
1619 "k8s.io/client-go/tools/record"
1720 ctrl "sigs.k8s.io/controller-runtime"
21+ "sigs.k8s.io/controller-runtime/pkg/builder"
1822 "sigs.k8s.io/controller-runtime/pkg/client"
1923 "sigs.k8s.io/controller-runtime/pkg/controller"
24+ "sigs.k8s.io/controller-runtime/pkg/handler"
2025 "sigs.k8s.io/controller-runtime/pkg/webhook"
2126
27+ "github.com/vmware-tanzu/nsx-operator/pkg/apis/vpc/v1alpha1"
2228 "github.com/vmware-tanzu/nsx-operator/pkg/controllers/common"
2329 "github.com/vmware-tanzu/nsx-operator/pkg/logger"
2430 "github.com/vmware-tanzu/nsx-operator/pkg/metrics"
2531 _ "github.com/vmware-tanzu/nsx-operator/pkg/nsx/ratelimiter"
2632 servicecommon "github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/common"
33+ "github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/dns"
2734)
2835
2936var (
@@ -38,6 +45,7 @@ type ServiceLbReconciler struct {
3845 Client client.Client
3946 Scheme * apimachineryruntime.Scheme
4047 Service * servicecommon.Service
48+ DNS dns.DNSRecordProvider
4149 Recorder record.EventRecorder
4250}
4351
@@ -63,25 +71,42 @@ func (r *ServiceLbReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
6371 if err := r .Client .Get (ctx , req .NamespacedName , service ); err != nil {
6472 if apierrors .IsNotFound (err ) {
6573 log .Info ("Not found LB service" , "req" , req .NamespacedName )
66- return ResultNormal , client .IgnoreNotFound (err )
74+ if _ , delErr := r .DNS .DeleteRecordByOwnerNN (ctx , dns .ResourceKindService , req .Namespace , req .Name ); delErr != nil {
75+ log .Error (delErr , "Failed to delete DNS records for Service" , "Namespace" , req .Namespace , "Name" , req .Name )
76+ return common .ResultRequeueAfter10sec , delErr
77+ }
78+ return ResultNormal , nil
6779 }
6880 log .Error (err , "Failed to fetch LB service" , "req" , req .NamespacedName )
6981 return common .ResultRequeueAfter10sec , err
7082 }
7183
72- if service .Spec .Type == v1 .ServiceTypeLoadBalancer {
73- log .Info ("Reconciling LB service" , "LBService" , req .NamespacedName )
74- log .Debug ("Reconciling LB Service" , "name" , service .Name , "version" , service .ResourceVersion , "status" , service .Status )
75- metrics .CounterInc (r .Service .NSXConfig , metrics .ControllerSyncTotal , MetricResType )
76-
77- if service .ObjectMeta .DeletionTimestamp .IsZero () {
78- metrics .CounterInc (r .Service .NSXConfig , metrics .ControllerUpdateTotal , MetricResType )
79- err := updateSuccess (r , ctx , service )
80- if err != nil {
81- log .Error (err , "Failed to update LB service" , "Name" , service .Name , "Namespace" , service .Namespace )
82- return common .ResultRequeueAfter10sec , err
83- }
84+ if service .Spec .Type != v1 .ServiceTypeLoadBalancer || ! service .ObjectMeta .DeletionTimestamp .IsZero () {
85+ // Try to delete DNS records for Service when it is not a LoadBalancer or is marked for deletion
86+ if _ , err := r .DNS .DeleteRecordByOwnerNN (ctx , dns .ResourceKindService , service .Namespace , service .Name ); err != nil {
87+ log .Error (err , "Failed to delete DNS records for Service" , "Namespace" , service .Namespace , "Name" , service .Name )
88+ return common .ResultRequeueAfter10sec , err
89+ }
90+ if uerr := r .removeServiceDNSReadyCondition (ctx , req .NamespacedName ); uerr != nil {
91+ log .Error (uerr , "Failed to clear Service DNS Ready condition" , "Service" , req .NamespacedName .String ())
92+ return common .ResultRequeueAfter10sec , uerr
8493 }
94+ return ResultNormal , nil
95+ }
96+
97+ log .Info ("Reconciling LB service" , "LBService" , req .NamespacedName )
98+ log .Debug ("Reconciling LB Service" , "name" , service .Name , "version" , service .ResourceVersion , "status" , service .Status )
99+ metrics .CounterInc (r .Service .NSXConfig , metrics .ControllerSyncTotal , MetricResType )
100+
101+ if err := r .reconcileLoadBalancerServiceDNS (ctx , service ); err != nil {
102+ log .Error (err , "Failed to reconcile DNS for LoadBalancer Service" , "Name" , service .Name , "Namespace" , service .Namespace )
103+ return common .ResultRequeueAfter10sec , err
104+ }
105+
106+ metrics .CounterInc (r .Service .NSXConfig , metrics .ControllerUpdateTotal , MetricResType )
107+ if err := updateSuccess (r , ctx , service ); err != nil {
108+ log .Error (err , "Failed to update LB service" , "Name" , service .Name , "Namespace" , service .Namespace )
109+ return common .ResultRequeueAfter10sec , err
85110 }
86111
87112 return ResultNormal , nil
@@ -119,13 +144,18 @@ func (r *ServiceLbReconciler) setServiceLbStatus(ctx context.Context, lbService
119144}
120145
121146func (r * ServiceLbReconciler ) setupWithManager (mgr ctrl.Manager ) error {
122- return ctrl .NewControllerManagedBy (mgr ).
147+ b := ctrl .NewControllerManagedBy (mgr ).
123148 For (& v1.Service {}).
149+ Watches (
150+ & v1alpha1.NetworkInfo {},
151+ handler .EnqueueRequestsFromMapFunc (r .enqueueLBServiceRequestsFromNetworkInfo ),
152+ builder .WithPredicates (predicateNetworkInfoAllowedDNSDomainsChanged ()),
153+ ).
124154 WithOptions (
125155 controller.Options {
126156 MaxConcurrentReconciles : common .NumReconcile (),
127- }).
128- Complete (r )
157+ })
158+ return b . Complete (r )
129159}
130160
131161// Start setup manager
@@ -172,18 +202,73 @@ func (r *ServiceLbReconciler) StartController(mgr ctrl.Manager, _ webhook.Server
172202 log .Error (err , "Failed to create controller" , "controller" , "ServiceLb" )
173203 return err
174204 }
205+ go common .GenericGarbageCollector (make (chan bool ), servicecommon .GCInterval , r .CollectGarbage )
175206 return nil
176207}
177208
209+ // listLoadBalancerServicesWithDNSAnnotation returns Service NNs that should retain DNS rows (LB, not terminating, hostname annotation).
210+ func listLoadBalancerServicesWithDNSAnnotation (ctx context.Context , c client.Client ) (sets.Set [types.NamespacedName ], error ) {
211+ svcList := & v1.ServiceList {}
212+ if err := c .List (ctx , svcList ); err != nil {
213+ return nil , err
214+ }
215+ nnSet := sets .New [types.NamespacedName ]()
216+ for i := range svcList .Items {
217+ svc := & svcList .Items [i ]
218+ if svc .Spec .Type != v1 .ServiceTypeLoadBalancer || ! svc .ObjectMeta .DeletionTimestamp .IsZero () {
219+ continue
220+ }
221+ if len (parseDNSHostnamesFromServiceAnnotation (svc .GetAnnotations ())) == 0 {
222+ continue
223+ }
224+ nnSet .Insert (types.NamespacedName {Namespace : svc .Namespace , Name : svc .Name })
225+ }
226+ return nnSet , nil
227+ }
228+
178229func (r * ServiceLbReconciler ) CollectGarbage (ctx context.Context ) error {
230+ if r .DNS == nil {
231+ return nil
232+ }
233+ apiSet , err := listLoadBalancerServicesWithDNSAnnotation (ctx , r .Client )
234+ if err != nil {
235+ log .Error (err , "Service LB GC: failed to list Services" )
236+ return err
237+ }
238+ ownersByKind := r .DNS .ListRecordOwnerResource ()
239+ cachedServices := ownersByKind [dns .ResourceKindService ]
240+ var errs []error
241+ for nn := range cachedServices {
242+ if apiSet .Has (nn ) {
243+ continue
244+ }
245+ if _ , err := r .DNS .DeleteRecordByOwnerNN (ctx , dns .ResourceKindService , nn .Namespace , nn .Name ); err != nil {
246+ log .Error (err , "Service LB GC: failed to delete DNS records for Service owner missing from API or no longer eligible" ,
247+ "Namespace" , nn .Namespace , "Name" , nn .Name )
248+ errs = append (errs , err )
249+ continue
250+ }
251+ if err := r .removeServiceDNSReadyCondition (ctx , nn ); err != nil {
252+ log .Error (err , "Service LB GC: failed to clear Service DNS Ready condition" , "Namespace" , nn .Namespace , "Name" , nn .Name )
253+ errs = append (errs , err )
254+ }
255+ }
256+ if len (errs ) > 0 {
257+ return fmt .Errorf ("service LB garbage collection encountered %d error(s): %v" , len (errs ), errs )
258+ }
179259 return nil
180260}
181261
182- func NewServiceLbReconciler (mgr ctrl.Manager , commonService servicecommon.Service ) * ServiceLbReconciler {
262+ func NewServiceLbReconciler (mgr ctrl.Manager , commonService servicecommon.Service , dnsRecordService * dns. DNSRecordService ) * ServiceLbReconciler {
183263 if isServiceLbStatusIpModeSupported (mgr .GetConfig ()) {
264+ var dnsProv dns.DNSRecordProvider
265+ if dnsRecordService != nil {
266+ dnsProv = dnsRecordService
267+ }
184268 serviceLbReconciler := & ServiceLbReconciler {
185269 Client : mgr .GetClient (),
186270 Scheme : mgr .GetScheme (),
271+ DNS : dnsProv ,
187272 Recorder : mgr .GetEventRecorderFor ("serviceLb-controller" ),
188273 }
189274 serviceLbReconciler .Service = & commonService
0 commit comments