@@ -17,12 +17,14 @@ import (
1717 servicehelper "k8s.io/cloud-provider/service/helpers"
1818 "k8s.io/klog/v2"
1919
20+ ipamv1alpha1ac "github.com/ironcore-dev/ironcore/client-go/applyconfigurations/ipam/v1alpha1"
21+ networkingv1alpha1ac "github.com/ironcore-dev/ironcore/client-go/applyconfigurations/networking/v1alpha1"
2022 "sigs.k8s.io/controller-runtime/pkg/client"
21- "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
2223
23- commonv1alpha1 "github.com/ironcore-dev/ironcore/api/common/v1alpha1"
24+ metav1ac "k8s.io/client-go/applyconfigurations/meta/v1"
25+
26+ "github.com/ironcore-dev/ironcore/api/common/v1alpha1"
2427 computev1alpha1 "github.com/ironcore-dev/ironcore/api/compute/v1alpha1"
25- ipamv1alpha1 "github.com/ironcore-dev/ironcore/api/ipam/v1alpha1"
2628 networkingv1alpha1 "github.com/ironcore-dev/ironcore/api/networking/v1alpha1"
2729)
2830
@@ -104,110 +106,54 @@ func (o *ironcoreLoadBalancer) GetLoadBalancerName(ctx context.Context, clusterN
104106func (o * ironcoreLoadBalancer ) EnsureLoadBalancer (ctx context.Context , clusterName string , service * v1.Service , nodes []* v1.Node ) (* v1.LoadBalancerStatus , error ) {
105107 klog .V (2 ).InfoS ("EnsureLoadBalancer for Service" , "Cluster" , clusterName , "Service" , client .ObjectKeyFromObject (service ))
106108
107- // decide load balancer type based on service annotation for internal load balancer
108- var desiredLoadBalancerType networkingv1alpha1.LoadBalancerType
109- if value , ok := service .Annotations [InternalLoadBalancerAnnotation ]; ok && value == "true" {
110- desiredLoadBalancerType = networkingv1alpha1 .LoadBalancerTypeInternal
111- } else {
112- desiredLoadBalancerType = networkingv1alpha1 .LoadBalancerTypePublic
113- }
109+ desiredLoadBalancerType := o .getDesiredLoadBalancerType (service )
114110
115111 loadBalancerName , err := o .getLoadBalancerName (ctx , clusterName , service )
116112 if err != nil {
117113 return nil , fmt .Errorf ("failed to get LoadBalancer name for Service %s: %w" , client .ObjectKeyFromObject (service ), err )
118114 }
119115
120- // get existing load balancer type
121- existingLoadBalancer := & networkingv1alpha1.LoadBalancer {}
122- var existingLoadBalancerType networkingv1alpha1.LoadBalancerType
123- if err := o .ironcoreClient .Get (ctx , client.ObjectKey {Namespace : o .ironcoreNamespace , Name : loadBalancerName }, existingLoadBalancer ); err == nil {
124- existingLoadBalancerType = existingLoadBalancer .Spec .Type
125- if existingLoadBalancerType != desiredLoadBalancerType {
126- if err = o .EnsureLoadBalancerDeleted (ctx , clusterName , service ); err != nil {
127- return nil , fmt .Errorf ("failed deleting existing loadbalancer %s: %w" , loadBalancerName , err )
128- }
129- }
116+ existingLoadBalancerType , err := o .ensureLoadBalancerType (ctx , clusterName , service , loadBalancerName , desiredLoadBalancerType )
117+ if err != nil {
118+ return nil , err
130119 }
131120
132- klog .V (2 ).InfoS ("Getting LoadBalancer ports from Service" , "Service" , client .ObjectKeyFromObject (service ))
133- var lbPorts []networkingv1alpha1.LoadBalancerPort
134- for _ , svcPort := range service .Spec .Ports {
135- protocol := svcPort .Protocol
136- lbPorts = append (lbPorts , networkingv1alpha1.LoadBalancerPort {
137- Protocol : & protocol ,
138- Port : svcPort .Port ,
139- })
121+ loadBalancerApplyConfig , err := o .buildLoadBalancerApplyConfig (clusterName , service , loadBalancerName , desiredLoadBalancerType )
122+ if err != nil {
123+ return nil , err
140124 }
141125
142- loadBalancer := & networkingv1alpha1.LoadBalancer {
143- TypeMeta : metav1.TypeMeta {
144- Kind : "LoadBalancer" ,
145- APIVersion : networkingv1alpha1 .SchemeGroupVersion .String (),
146- },
147- ObjectMeta : metav1.ObjectMeta {
148- Name : loadBalancerName ,
149- Namespace : o .ironcoreNamespace ,
150- Annotations : map [string ]string {
151- AnnotationKeyClusterName : clusterName ,
152- AnnotationKeyServiceName : service .Name ,
153- AnnotationKeyServiceNamespace : service .Namespace ,
154- AnnotationKeyServiceUID : string (service .UID ),
155- },
156- },
157- Spec : networkingv1alpha1.LoadBalancerSpec {
158- Type : desiredLoadBalancerType ,
159- IPFamilies : service .Spec .IPFamilies ,
160- NetworkRef : v1.LocalObjectReference {
161- Name : o .cloudConfig .NetworkName ,
162- },
163- Ports : lbPorts ,
164- },
165- }
126+ klog .V (2 ).InfoS ("Applying LoadBalancer for Service" , "LoadBalancer" , client.ObjectKey {Namespace : o .ironcoreNamespace , Name : loadBalancerName }, "Service" , client .ObjectKeyFromObject (service ))
166127
167- // if load balancer type is Internal then update IPSource with valid prefix template
168- if desiredLoadBalancerType == networkingv1alpha1 .LoadBalancerTypeInternal {
169- if o .cloudConfig .PrefixName == "" {
170- return nil , fmt .Errorf ("prefixName is not defined in config" )
171- }
172- loadBalancer .Spec .IPs = []networkingv1alpha1.IPSource {
173- {
174- Ephemeral : & networkingv1alpha1.EphemeralPrefixSource {
175- PrefixTemplate : & ipamv1alpha1.PrefixTemplateSpec {
176- Spec : ipamv1alpha1.PrefixSpec {
177- // TODO: for now we only support IPv4 until Gardener has support for IPv6 based Shoots
178- IPFamily : v1 .IPv4Protocol ,
179- ParentRef : & v1.LocalObjectReference {
180- Name : o .cloudConfig .PrefixName ,
181- },
182- },
183- },
184- },
185- },
186- }
187- }
188- // If useNicSelector is set to true then add NetworkInterfaceSelector to loadbalancer.SPec
189- if o .useNicSelector {
190- loadBalancer .Spec .NetworkInterfaceSelector = & metav1.LabelSelector {
191- MatchLabels : map [string ]string {
192- LabelKeyClusterName : clusterName ,
193- },
194- }
128+ if err := o .ironcoreClient .Apply (ctx , loadBalancerApplyConfig , client .FieldOwner (loadBalancerFieldOwner ), client .ForceOwnership ); err != nil {
129+ return nil , fmt .Errorf ("failed to apply LoadBalancer %s for Service %s: %w" , client.ObjectKey {Namespace : o .ironcoreNamespace , Name : loadBalancerName }, client .ObjectKeyFromObject (service ), err )
195130 }
196131
197- klog .V (2 ).InfoS ("Applying LoadBalancer for Service" , "LoadBalancer" , client .ObjectKeyFromObject (loadBalancer ), "Service" , client .ObjectKeyFromObject (service ))
198- if err := o .ironcoreClient .Patch (ctx , loadBalancer , client .Apply , loadBalancerFieldOwner , client .ForceOwnership ); err != nil {
199- return nil , fmt .Errorf ("failed to apply LoadBalancer %s for Service %s: %w" , client .ObjectKeyFromObject (loadBalancer ), client .ObjectKeyFromObject (service ), err )
200- }
201- klog .V (2 ).InfoS ("Applied LoadBalancer for Service" , "LoadBalancer" , client .ObjectKeyFromObject (loadBalancer ), "Service" , client .ObjectKeyFromObject (service ))
132+ klog .V (2 ).InfoS ("Applied LoadBalancer for Service" , "LoadBalancer" , client.ObjectKey {Namespace : o .ironcoreNamespace , Name : loadBalancerName }, "Service" , client .ObjectKeyFromObject (service ))
202133
203134 // if useNicSelector is set to false then cloudProvider has to create the loadbalancerRouting
204135 // if useNicSelector is set to true then Ironcore will take care of creating the loadbalancerRouting
205136 if ! o .useNicSelector {
206- klog .V (2 ).InfoS ("Applying LoadBalancerRouting for LoadBalancer" , "LoadBalancer" , client .ObjectKeyFromObject (loadBalancer ))
207- if err := o .applyLoadBalancerRoutingForLoadBalancer (ctx , loadBalancer , nodes ); err != nil {
137+ loadBalancerToRoute := & networkingv1alpha1.LoadBalancer {}
138+ loadBalancerKey := client.ObjectKey {Namespace : o .ironcoreNamespace , Name : loadBalancerName }
139+ if err := o .ironcoreClient .Get (ctx , loadBalancerKey , loadBalancerToRoute ); err != nil {
140+ return nil , fmt .Errorf ("failed to get LoadBalancer %s for routing: %w" , loadBalancerKey , err )
141+ }
142+
143+ klog .V (2 ).InfoS ("Applying LoadBalancerRouting for LoadBalancer" , "LoadBalancer" , client .ObjectKeyFromObject (loadBalancerToRoute ))
144+
145+ if err := o .applyLoadBalancerRoutingForLoadBalancer (ctx , loadBalancerToRoute , nodes ); err != nil {
208146 return nil , err
209147 }
210- klog .V (2 ).InfoS ("Applied LoadBalancerRouting for LoadBalancer" , "LoadBalancer" , client .ObjectKeyFromObject (loadBalancer ))
148+
149+ klog .V (2 ).InfoS ("Applied LoadBalancerRouting for LoadBalancer" , "LoadBalancer" , client .ObjectKeyFromObject (loadBalancerToRoute ))
150+ }
151+
152+ loadBalancer := & networkingv1alpha1.LoadBalancer {
153+ ObjectMeta : metav1.ObjectMeta {
154+ Name : loadBalancerName ,
155+ Namespace : o .ironcoreNamespace ,
156+ },
211157 }
212158
213159 lbStatus , err := waitLoadBalancerActive (ctx , o .ironcoreClient , existingLoadBalancerType , service , loadBalancer )
@@ -217,6 +163,103 @@ func (o *ironcoreLoadBalancer) EnsureLoadBalancer(ctx context.Context, clusterNa
217163 return & lbStatus , nil
218164}
219165
166+ func (o * ironcoreLoadBalancer ) getDesiredLoadBalancerType (service * v1.Service ) networkingv1alpha1.LoadBalancerType {
167+ if value , ok := service .Annotations [InternalLoadBalancerAnnotation ]; ok && value == "true" {
168+ return networkingv1alpha1 .LoadBalancerTypeInternal
169+ }
170+
171+ return networkingv1alpha1 .LoadBalancerTypePublic
172+ }
173+
174+ func (o * ironcoreLoadBalancer ) ensureLoadBalancerType (ctx context.Context , clusterName string , service * v1.Service , loadBalancerName string ,
175+ desiredType networkingv1alpha1.LoadBalancerType ,
176+ ) (networkingv1alpha1.LoadBalancerType , error ) {
177+ existingLoadBalancer := & networkingv1alpha1.LoadBalancer {}
178+
179+ if err := o .ironcoreClient .Get (ctx , client.ObjectKey {Namespace : o .ironcoreNamespace , Name : loadBalancerName }, existingLoadBalancer ); err != nil {
180+ if apierrors .IsNotFound (err ) {
181+ return "" , nil
182+ }
183+
184+ return "" , err
185+ }
186+
187+ existingType := existingLoadBalancer .Spec .Type
188+
189+ if existingType != desiredType {
190+ if err := o .EnsureLoadBalancerDeleted (ctx , clusterName , service ); err != nil {
191+ return "" , fmt .Errorf ("failed deleting existing loadbalancer %s: %w" , loadBalancerName , err )
192+ }
193+ }
194+
195+ return existingType , nil
196+ }
197+
198+ func (o * ironcoreLoadBalancer ) buildLoadBalancerApplyConfig (clusterName string , service * v1.Service , loadBalancerName string ,
199+ loadBalancerType networkingv1alpha1.LoadBalancerType ,
200+ ) (* networkingv1alpha1ac.LoadBalancerApplyConfiguration , error ) {
201+ lbPorts := make ([]* networkingv1alpha1ac.LoadBalancerPortApplyConfiguration , 0 , len (service .Spec .Ports ))
202+
203+ for _ , svcPort := range service .Spec .Ports {
204+ protocol := svcPort .Protocol
205+
206+ lbPorts = append (lbPorts , networkingv1alpha1ac .LoadBalancerPort ().
207+ WithProtocol (protocol ).
208+ WithPort (svcPort .Port ),
209+ )
210+ }
211+
212+ spec := networkingv1alpha1ac .LoadBalancerSpec ().
213+ WithType (loadBalancerType ).
214+ WithIPFamilies (service .Spec .IPFamilies ... ).
215+ WithNetworkRef (v1.LocalObjectReference {Name : o .cloudConfig .NetworkName }).
216+ WithPorts (lbPorts ... )
217+
218+ if loadBalancerType == networkingv1alpha1 .LoadBalancerTypeInternal {
219+ if o .cloudConfig .PrefixName == "" {
220+ return nil , fmt .Errorf ("prefixName is not defined in config" )
221+ }
222+
223+ spec .WithIPs (
224+ networkingv1alpha1ac .IPSource ().
225+ WithEphemeral (
226+ networkingv1alpha1ac .EphemeralPrefixSource ().
227+ WithPrefixTemplate (
228+ ipamv1alpha1ac .PrefixTemplateSpec ().
229+ WithSpec (
230+ ipamv1alpha1ac .PrefixSpec ().
231+ WithIPFamily (v1 .IPv4Protocol ).
232+ WithParentRef (
233+ v1.LocalObjectReference {
234+ Name : o .cloudConfig .PrefixName ,
235+ }),
236+ ),
237+ ),
238+ ),
239+ )
240+ }
241+
242+ if o .useNicSelector {
243+ spec .WithNetworkInterfaceSelector (
244+ metav1ac .LabelSelector ().
245+ WithMatchLabels (map [string ]string {
246+ LabelKeyClusterName : clusterName ,
247+ }),
248+ )
249+ }
250+
251+ return networkingv1alpha1ac .LoadBalancer (
252+ loadBalancerName ,
253+ o .ironcoreNamespace ,
254+ ).
255+ WithAnnotations (map [string ]string {
256+ AnnotationKeyClusterName : clusterName ,
257+ AnnotationKeyServiceName : service .Name ,
258+ AnnotationKeyServiceNamespace : service .Namespace ,
259+ AnnotationKeyServiceUID : string (service .UID ),
260+ }).
261+ WithSpec (spec ), nil
262+ }
220263func waitLoadBalancerActive (ctx context.Context , ironcoreClient client.Client , existingLoadBalancerType networkingv1alpha1.LoadBalancerType ,
221264 service * v1.Service , loadBalancer * networkingv1alpha1.LoadBalancer ) (v1.LoadBalancerStatus , error ) {
222265 klog .V (2 ).InfoS ("Waiting for LoadBalancer instance to become ready" , "LoadBalancer" , client .ObjectKeyFromObject (loadBalancer ))
@@ -253,43 +296,72 @@ func waitLoadBalancerActive(ctx context.Context, ironcoreClient client.Client, e
253296}
254297
255298func (o * ironcoreLoadBalancer ) applyLoadBalancerRoutingForLoadBalancer (ctx context.Context , loadBalancer * networkingv1alpha1.LoadBalancer , nodes []* v1.Node ) error {
256- loadBalacerDestinations , err := o .getLoadBalancerDestinationsForNodes (ctx , nodes , loadBalancer .Spec .NetworkRef .Name )
299+ loadBalancerDestinations , err := o .getLoadBalancerDestinationsForNodes (ctx , nodes , loadBalancer .Spec .NetworkRef .Name )
257300 if err != nil {
258301 return fmt .Errorf ("failed to get NetworkInterfaces for Nodes: %w" , err )
259302 }
260303
261304 network := & networkingv1alpha1.Network {}
262- networkKey := client.ObjectKey {Namespace : o .ironcoreNamespace , Name : loadBalancer .Spec .NetworkRef .Name }
305+ networkKey := client.ObjectKey {
306+ Namespace : o .ironcoreNamespace ,
307+ Name : loadBalancer .Spec .NetworkRef .Name ,
308+ }
309+
263310 if err := o .ironcoreClient .Get (ctx , networkKey , network ); err != nil {
264311 return fmt .Errorf ("failed to get Network %s: %w" , o .cloudConfig .NetworkName , err )
265312 }
266313
267- loadBalancerRouting := & networkingv1alpha1.LoadBalancerRouting {
268- TypeMeta : metav1.TypeMeta {
269- Kind : "LoadBalancerRouting" ,
270- APIVersion : networkingv1alpha1 .SchemeGroupVersion .String (),
271- },
272- ObjectMeta : metav1.ObjectMeta {
273- Name : loadBalancer .Name ,
274- Namespace : o .ironcoreNamespace ,
275- },
276- NetworkRef : commonv1alpha1.LocalUIDReference {
277- Name : network .Name ,
278- UID : network .UID ,
279- },
280- Destinations : loadBalacerDestinations ,
281- }
314+ loadBalancerRoutingApplyConfig := o .buildLoadBalancerRoutingApplyConfig (loadBalancer , network , loadBalancerDestinations )
282315
283- if err := controllerutil . SetOwnerReference ( loadBalancer , loadBalancerRouting , o . ironcoreClient . Scheme () ); err != nil {
284- return fmt .Errorf ("failed to set owner reference for load balancer routing %s: %w" , client .ObjectKeyFromObject (loadBalancerRouting ), err )
316+ if err := o . ironcoreClient . Apply ( ctx , loadBalancerRoutingApplyConfig , client . FieldOwner ( loadBalancerFieldOwner ), client . ForceOwnership ); err != nil {
317+ return fmt .Errorf ("failed to apply LoadBalancerRouting %s for LoadBalancer %s: %w" , client.ObjectKey { Namespace : o . ironcoreNamespace , Name : loadBalancer . Name }, client . ObjectKeyFromObject (loadBalancer ), err )
285318 }
286319
287- if err := o .ironcoreClient .Patch (ctx , loadBalancerRouting , client .Apply , loadBalancerFieldOwner , client .ForceOwnership ); err != nil {
288- return fmt .Errorf ("failed to apply LoadBalancerRouting %s for LoadBalancer %s: %w" , client .ObjectKeyFromObject (loadBalancerRouting ), client .ObjectKeyFromObject (loadBalancer ), err )
289- }
290320 return nil
291321}
292322
323+ func (o * ironcoreLoadBalancer ) buildLoadBalancerRoutingApplyConfig (
324+ loadBalancer * networkingv1alpha1.LoadBalancer ,
325+ network * networkingv1alpha1.Network ,
326+ destinations []networkingv1alpha1.LoadBalancerDestination ,
327+ ) * networkingv1alpha1ac.LoadBalancerRoutingApplyConfiguration {
328+ destinationApplyConfigs := make ([]* networkingv1alpha1ac.LoadBalancerDestinationApplyConfiguration , 0 , len (destinations ))
329+
330+ for _ , destination := range destinations {
331+ targetRef := networkingv1alpha1ac .LoadBalancerTargetRef ().
332+ WithName (destination .TargetRef .Name ).
333+ WithUID (destination .TargetRef .UID )
334+ if destination .TargetRef .ProviderID != "" {
335+ targetRef = targetRef .WithProviderID (destination .TargetRef .ProviderID )
336+ }
337+
338+ destinationApplyConfigs = append (
339+ destinationApplyConfigs ,
340+ networkingv1alpha1ac .LoadBalancerDestination ().
341+ WithIP (destination .IP ).
342+ WithTargetRef (targetRef ),
343+ )
344+ }
345+
346+ return networkingv1alpha1ac .LoadBalancerRouting (
347+ loadBalancer .Name ,
348+ o .ironcoreNamespace ,
349+ ).
350+ WithOwnerReferences (
351+ metav1ac .OwnerReference ().
352+ WithAPIVersion (networkingv1alpha1 .SchemeGroupVersion .String ()).
353+ WithKind ("LoadBalancer" ).
354+ WithName (loadBalancer .Name ).
355+ WithUID (loadBalancer .UID ),
356+ ).
357+ WithNetworkRef (
358+ v1alpha1.LocalUIDReference {
359+ Name : network .Name ,
360+ UID : network .UID ,
361+ }).
362+ WithDestinations (destinationApplyConfigs ... )
363+ }
364+
293365func (o * ironcoreLoadBalancer ) getLoadBalancerDestinationsForNodes (ctx context.Context , nodes []* v1.Node , networkName string ) ([]networkingv1alpha1.LoadBalancerDestination , error ) {
294366 var loadbalancerDestinations []networkingv1alpha1.LoadBalancerDestination
295367 for _ , node := range nodes {
0 commit comments