@@ -5,25 +5,33 @@ 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"
13+ "k8s.io/apimachinery/pkg/types"
1214 apimachineryruntime "k8s.io/apimachinery/pkg/runtime"
1315 "k8s.io/apimachinery/pkg/util/version"
1416 clientset "k8s.io/client-go/kubernetes"
1517 "k8s.io/client-go/rest"
1618 "k8s.io/client-go/tools/record"
19+ "k8s.io/client-go/util/retry"
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"
25+ "sigs.k8s.io/controller-runtime/pkg/manager"
2026 "sigs.k8s.io/controller-runtime/pkg/webhook"
2127
28+ "github.com/vmware-tanzu/nsx-operator/pkg/apis/vpc/v1alpha1"
2229 "github.com/vmware-tanzu/nsx-operator/pkg/controllers/common"
2330 "github.com/vmware-tanzu/nsx-operator/pkg/logger"
2431 "github.com/vmware-tanzu/nsx-operator/pkg/metrics"
2532 _ "github.com/vmware-tanzu/nsx-operator/pkg/nsx/ratelimiter"
2633 servicecommon "github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/common"
34+ "github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/dns"
2735)
2836
2937var (
@@ -38,6 +46,7 @@ type ServiceLbReconciler struct {
3846 Client client.Client
3947 Scheme * apimachineryruntime.Scheme
4048 Service * servicecommon.Service
49+ DNS dns.DNSRecordProvider
4150 Recorder record.EventRecorder
4251}
4352
@@ -53,6 +62,14 @@ func updateSuccess(r *ServiceLbReconciler, c context.Context, lbService *v1.Serv
5362 return err
5463}
5564
65+ func (r * ServiceLbReconciler ) deleteDNSForService (ctx context.Context , namespace , name string , op string ) error {
66+ if _ , err := r .DNS .DeleteRecordByOwnerNN (ctx , dns .ResourceKindService , namespace , name ); err != nil {
67+ log .Error (err , "Failed to delete DNS records for Service" , "Namespace" , namespace , "Name" , name , "Operation" , op )
68+ return fmt .Errorf ("deleting DNS records for %s: %w" , op , err )
69+ }
70+ return nil
71+ }
72+
5673func (r * ServiceLbReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
5774 service := & v1.Service {}
5875 startTime := time .Now ()
@@ -63,33 +80,43 @@ func (r *ServiceLbReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
6380 if err := r .Client .Get (ctx , req .NamespacedName , service ); err != nil {
6481 if apierrors .IsNotFound (err ) {
6582 log .Info ("Not found LB service" , "req" , req .NamespacedName )
66- return ResultNormal , client .IgnoreNotFound (err )
83+ if err := r .deleteDNSForService (ctx , req .Namespace , req .Name , "deleted Service" ); err != nil {
84+ return common .ResultRequeueAfter10sec , err
85+ }
86+ return ResultNormal , nil
6787 }
6888 log .Error (err , "Failed to fetch LB service" , "req" , req .NamespacedName )
6989 return common .ResultRequeueAfter10sec , err
7090 }
7191
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- }
92+ if service .Spec .Type != v1 .ServiceTypeLoadBalancer || ! service .ObjectMeta .DeletionTimestamp .IsZero () {
93+ // Try to delete DNS records for Service when it is not a LoadBalancer or is marked for deletion
94+ if err := r .clearDNSAndConditionForService (ctx , req .NamespacedName , "non-LB or terminating Service" ); err != nil {
95+ return common .ResultRequeueAfter10sec , err
8496 }
97+ return ResultNormal , nil
98+ }
99+
100+ log .Info ("Reconciling LB service" , "LBService" , req .NamespacedName )
101+ log .Debug ("Reconciling LB Service" , "name" , service .Name , "version" , service .ResourceVersion , "status" , service .Status )
102+ metrics .CounterInc (r .Service .NSXConfig , metrics .ControllerSyncTotal , MetricResType )
103+
104+ if err := r .reconcileLoadBalancerServiceDNS (ctx , service ); err != nil {
105+ log .Error (err , "Failed to reconcile DNS for LoadBalancer Service" , "Name" , service .Name , "Namespace" , service .Namespace )
106+ return common .ResultRequeueAfter10sec , fmt .Errorf ("reconciling DNS: %w" , err )
107+ }
108+
109+ metrics .CounterInc (r .Service .NSXConfig , metrics .ControllerUpdateTotal , MetricResType )
110+ if err := updateSuccess (r , ctx , service ); err != nil {
111+ log .Error (err , "Failed to update LB service" , "Name" , service .Name , "Namespace" , service .Namespace )
112+ return common .ResultRequeueAfter10sec , fmt .Errorf ("updating LB service: %w" , err )
85113 }
86114
87115 return ResultNormal , nil
88116}
89117
90118func (r * ServiceLbReconciler ) setServiceLbStatus (ctx context.Context , lbService * v1.Service ) error {
91119 ipMode := v1 .LoadBalancerIPModeProxy
92- statusUpdated := false
93120 // If nsx.vmware.com/ingress-ip-mode label with values proxy or vip,
94121 // the LoadBalancer service ipMode status would be set to whatever the label is set to,
95122 // Otherwise, it's set to Proxy by default when unset or other invalid values.
@@ -98,34 +125,48 @@ func (r *ServiceLbReconciler) setServiceLbStatus(ctx context.Context, lbService
98125 ipMode = v1 .LoadBalancerIPModeVIP
99126 }
100127 }
101- for i , ing := range lbService .Status .LoadBalancer .Ingress {
102- if ing .IP != "" {
103- if ing .IPMode == nil || * (ing .IPMode ) != ipMode {
104- lbService .Status .LoadBalancer .Ingress [i ].IPMode = & ipMode
105- statusUpdated = true
128+
129+ return retry .RetryOnConflict (retry .DefaultRetry , func () error {
130+ svc := & v1.Service {}
131+ if err := r .Client .Get (ctx , types.NamespacedName {Name : lbService .Name , Namespace : lbService .Namespace }, svc ); err != nil {
132+ return client .IgnoreNotFound (err )
133+ }
134+
135+ statusUpdated := false
136+ for i , ing := range svc .Status .LoadBalancer .Ingress {
137+ if ing .IP != "" {
138+ if ing .IPMode == nil || * (ing .IPMode ) != ipMode {
139+ svc .Status .LoadBalancer .Ingress [i ].IPMode = & ipMode
140+ statusUpdated = true
141+ }
106142 }
107143 }
108- }
109144
110- if statusUpdated {
111- err := r .Client .Status ().Update (ctx , lbService )
112- if err != nil {
113- log .Error (err , "Failed to update LB service status ipMode" , "Name" , lbService .Name , "Namespace" , lbService .Namespace , "ipMode" , ipMode )
114- return err
145+ if statusUpdated {
146+ err := r .Client .Status ().Update (ctx , svc )
147+ if err != nil {
148+ log .Error (err , "Failed to update LB service status ipMode" , "Name" , svc .Name , "Namespace" , svc .Namespace , "ipMode" , ipMode )
149+ return err
150+ }
151+ log .Info ("Updated LB service status ipMode" , "Name" , svc .Name , "Namespace" , svc .Namespace , "ipMode" , ipMode )
115152 }
116- log .Info ("Updated LB service status ipMode" , "Name" , lbService .Name , "Namespace" , lbService .Namespace , "ipMode" , ipMode )
117- }
118- return nil
153+ return nil
154+ })
119155}
120156
121157func (r * ServiceLbReconciler ) setupWithManager (mgr ctrl.Manager ) error {
122- return ctrl .NewControllerManagedBy (mgr ).
158+ b := ctrl .NewControllerManagedBy (mgr ).
123159 For (& v1.Service {}).
160+ Watches (
161+ & v1alpha1.NetworkInfo {},
162+ handler .EnqueueRequestsFromMapFunc (r .enqueueLBServiceRequestsFromNetworkInfo ),
163+ builder .WithPredicates (predicateNetworkInfoAllowedDNSDomainsChanged ()),
164+ ).
124165 WithOptions (
125166 controller.Options {
126167 MaxConcurrentReconciles : common .NumReconcile (),
127- }).
128- Complete (r )
168+ })
169+ return b . Complete (r )
129170}
130171
131172// Start setup manager
@@ -172,18 +213,36 @@ func (r *ServiceLbReconciler) StartController(mgr ctrl.Manager, _ webhook.Server
172213 log .Error (err , "Failed to create controller" , "controller" , "ServiceLb" )
173214 return err
174215 }
216+ err := mgr .Add (manager .RunnableFunc (func (ctx context.Context ) error {
217+ stop := make (chan bool )
218+ go func () {
219+ <- ctx .Done ()
220+ close (stop )
221+ }()
222+ common .GenericGarbageCollector (stop , servicecommon .GCInterval , r .CollectGarbage )
223+ return nil
224+ }))
225+ if err != nil {
226+ log .Error (err , "Failed to add LB GC to manager" )
227+ return err
228+ }
175229 return nil
176230}
177231
178232func (r * ServiceLbReconciler ) CollectGarbage (ctx context.Context ) error {
179- return nil
233+ return r . collectDNSGarbage ( ctx )
180234}
181235
182- func NewServiceLbReconciler (mgr ctrl.Manager , commonService servicecommon.Service ) * ServiceLbReconciler {
236+ func NewServiceLbReconciler (mgr ctrl.Manager , commonService servicecommon.Service , dnsRecordService * dns. DNSRecordService ) * ServiceLbReconciler {
183237 if isServiceLbStatusIpModeSupported (mgr .GetConfig ()) {
238+ var dnsProv dns.DNSRecordProvider
239+ if dnsRecordService != nil {
240+ dnsProv = dnsRecordService
241+ }
184242 serviceLbReconciler := & ServiceLbReconciler {
185243 Client : mgr .GetClient (),
186244 Scheme : mgr .GetScheme (),
245+ DNS : dnsProv ,
187246 Recorder : mgr .GetEventRecorderFor ("serviceLb-controller" ), //nolint:staticcheck // record.EventRecorder; StatusUpdater not on events.EventRecorder yet
188247 }
189248 serviceLbReconciler .Service = & commonService
0 commit comments