Skip to content

Commit 3dfb5bc

Browse files
committed
refactor: introduce loadBalancer sync operation
This clarifies the flow, laying the groundwork for multiple protocols.
1 parent b54f8b1 commit 3dfb5bc

File tree

7 files changed

+90
-38
lines changed

7 files changed

+90
-38
lines changed

providers/gce/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ go_library(
2626
"gce_loadbalancer_internal.go",
2727
"gce_loadbalancer_metrics.go",
2828
"gce_loadbalancer_naming.go",
29+
"gce_loadbalancer_sync.go",
2930
"gce_networkendpointgroup.go",
3031
"gce_networks.go",
3132
"gce_routes.go",

providers/gce/gce_loadbalancer.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,15 @@ func (g *Cloud) EnsureLoadBalancer(ctx context.Context, clusterName string, svc
169169

170170
klog.V(4).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v): ensure %v loadbalancer", clusterName, svc.Namespace, svc.Name, loadBalancerName, g.region, desiredScheme)
171171

172-
existingFwdRule, err := g.GetRegionForwardingRule(loadBalancerName, g.region)
172+
actualForwardingRule, err := g.GetRegionForwardingRule(loadBalancerName, g.region)
173173
if err != nil && !isNotFound(err) {
174174
return nil, err
175175
}
176+
op := &loadBalancerSync{}
177+
op.actualForwardingRule = actualForwardingRule
176178

177-
if existingFwdRule != nil {
178-
existingScheme := cloud.LbScheme(strings.ToUpper(existingFwdRule.LoadBalancingScheme))
179+
if op.actualForwardingRule != nil {
180+
existingScheme := cloud.LbScheme(strings.ToUpper(op.actualForwardingRule.LoadBalancingScheme))
179181

180182
// If the loadbalancer type changes between INTERNAL and EXTERNAL, the old load balancer should be deleted.
181183
if existingScheme != desiredScheme {
@@ -192,16 +194,16 @@ func (g *Cloud) EnsureLoadBalancer(ctx context.Context, clusterName string, svc
192194
}
193195

194196
// Assume the ensureDeleted function successfully deleted the forwarding rule.
195-
existingFwdRule = nil
197+
op.actualForwardingRule = nil
196198
}
197199
}
198200

199201
var status *v1.LoadBalancerStatus
200202
switch desiredScheme {
201203
case cloud.SchemeInternal:
202-
status, err = g.ensureInternalLoadBalancer(clusterName, clusterID, svc, existingFwdRule, nodes)
204+
status, err = g.ensureInternalLoadBalancer(clusterName, clusterID, svc, op, nodes)
203205
default:
204-
status, err = g.ensureExternalLoadBalancer(clusterName, clusterID, svc, existingFwdRule, nodes)
206+
status, err = g.ensureExternalLoadBalancer(clusterName, clusterID, svc, op, nodes)
205207
}
206208
if err != nil {
207209
klog.Errorf("Failed to EnsureLoadBalancer(%s, %s, %s, %s, %s), err: %v", clusterName, svc.Namespace, svc.Name, loadBalancerName, g.region, err)

providers/gce/gce_loadbalancer_external.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ const (
5555
// Due to an interesting series of design decisions, this handles both creating
5656
// new load balancers and updating existing load balancers, recognizing when
5757
// each is needed.
58-
func (g *Cloud) ensureExternalLoadBalancer(clusterName string, clusterID string, apiService *v1.Service, existingFwdRule *compute.ForwardingRule, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) {
58+
func (g *Cloud) ensureExternalLoadBalancer(clusterName string, clusterID string, apiService *v1.Service, op *loadBalancerSync, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) {
5959
// Process services with LoadBalancerClass "networking.gke.io/l4-regional-external-legacy" used for this controller.
6060
// LoadBalancerClass can't be updated so we know this controller should process the NetLB.
6161
// Skip service handling if it uses Regional Backend Services and handled by other controllers
62-
if !shouldProcessNetLB(apiService, existingFwdRule) {
62+
if !shouldProcessNetLB(apiService, op.actualForwardingRule) {
6363
return nil, cloudprovider.ImplementedElsewhere
6464
}
6565

providers/gce/gce_loadbalancer_external_test.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,7 +1055,10 @@ func TestEnsureExternalLoadBalancerExistingFwdRule(t *testing.T) {
10551055
svc, err = gce.client.CoreV1().Services(svc.Namespace).Create(context.TODO(), svc, metav1.CreateOptions{})
10561056
require.NoError(t, err)
10571057

1058-
_, err = gce.ensureExternalLoadBalancer(vals.ClusterName, vals.ClusterID, svc, tc.existingForwardingRule, nodes)
1058+
op := &loadBalancerSync{}
1059+
op.actualForwardingRule = tc.existingForwardingRule
1060+
1061+
_, err = gce.ensureExternalLoadBalancer(vals.ClusterName, vals.ClusterID, svc, op, nodes)
10591062
if tc.wantError != nil {
10601063
assert.EqualError(t, err, (*tc.wantError).Error())
10611064
} else {
@@ -2121,11 +2124,15 @@ func TestEnsureExternalLoadBalancerErrors(t *testing.T) {
21212124
if tc.injectMock != nil {
21222125
tc.injectMock(gce.c.(*cloud.MockGCE))
21232126
}
2127+
2128+
op := &loadBalancerSync{}
2129+
op.actualForwardingRule = params.existingFwdRule
2130+
21242131
status, err := gce.ensureExternalLoadBalancer(
21252132
params.clusterName,
21262133
params.clusterID,
21272134
params.service,
2128-
params.existingFwdRule,
2135+
op,
21292136
params.nodes,
21302137
)
21312138
assert.Error(t, err, "Should return an error when "+desc)

providers/gce/gce_loadbalancer_internal.go

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ const (
5454
labelGKESubnetworkName = "cloud.google.com/gke-node-pool-subnet"
5555
)
5656

57-
func (g *Cloud) ensureInternalLoadBalancer(clusterName, clusterID string, svc *v1.Service, existingFwdRule *compute.ForwardingRule, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) {
57+
func (g *Cloud) ensureInternalLoadBalancer(clusterName, clusterID string, svc *v1.Service, op *loadBalancerSync, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) {
5858
// Process services with LoadBalancerClass "networking.gke.io/l4-regional-internal-legacy" used for this controller.
5959
// LoadBalancerClass can't be updated so we know this controller should process the ILB.
60-
if existingFwdRule == nil && !hasFinalizer(svc, ILBFinalizerV1) && !hasLoadBalancerClass(svc, LegacyRegionalInternalLoadBalancerClass) {
60+
if op.actualForwardingRule == nil && !hasFinalizer(svc, ILBFinalizerV1) && !hasLoadBalancerClass(svc, LegacyRegionalInternalLoadBalancerClass) {
6161
// Neither the forwarding rule nor the V1 finalizer exists. This is most likely a new service.
6262
if svc.Spec.LoadBalancerClass != nil && !hasLoadBalancerClass(svc, LegacyRegionalInternalLoadBalancerClass) {
6363
klog.V(2).Infof("Skipped ensureInternalLoadBalancer for service %s/%s, as service contains %q loadBalancerClass.", svc.Namespace, svc.Name, *svc.Spec.LoadBalancerClass)
@@ -118,8 +118,8 @@ func (g *Cloud) ensureInternalLoadBalancer(clusterName, clusterID string, svc *v
118118

119119
// Get existing backend service (if exists)
120120
var existingBackendService *compute.BackendService
121-
if existingFwdRule != nil && existingFwdRule.BackendService != "" {
122-
existingBSName := getNameFromLink(existingFwdRule.BackendService)
121+
if op.actualForwardingRule != nil && op.actualForwardingRule.BackendService != "" {
122+
existingBSName := getNameFromLink(op.actualForwardingRule.BackendService)
123123
if existingBackendService, err = g.GetRegionBackendService(existingBSName, g.region); err != nil && !isNotFound(err) {
124124
return nil, err
125125
}
@@ -153,7 +153,7 @@ func (g *Cloud) ensureInternalLoadBalancer(clusterName, clusterID string, svc *v
153153
}
154154
// Determine IP which will be used for this LB. If no forwarding rule has been established
155155
// or specified in the Service spec, then requestedIP = "".
156-
ipToUse := ilbIPToUse(svc, existingFwdRule, subnetworkURL)
156+
ipToUse := ilbIPToUse(svc, op.actualForwardingRule, subnetworkURL)
157157

158158
klog.V(2).Infof("ensureInternalLoadBalancer(%v): Using subnet %s for LoadBalancer IP %s", loadBalancerName, options.SubnetName, ipToUse)
159159

@@ -199,20 +199,20 @@ func (g *Cloud) ensureInternalLoadBalancer(clusterName, clusterID string, svc *v
199199
newFwdRule.Ports = nil
200200
newFwdRule.AllPorts = true
201201
}
202+
op.desiredForwardingRule = newFwdRule
202203

203-
fwdRuleDeleted := false
204-
if existingFwdRule != nil && !forwardingRulesEqual(existingFwdRule, newFwdRule) {
204+
if op.actualForwardingRule != nil && !forwardingRulesEqual(op.actualForwardingRule, op.desiredForwardingRule) {
205205
// Delete existing forwarding rule before making changes to the backend service. For example - changing protocol
206206
// of backend service without first deleting forwarding rule will throw an error since the linked forwarding
207207
// rule would show the old protocol.
208208
if klogV := klog.V(2); klogV.Enabled() {
209-
frDiff := cmp.Diff(existingFwdRule, newFwdRule)
210-
klogV.Infof("ensureInternalLoadBalancer(%v): forwarding rule changed - Existing - %+v\n, New - %+v\n, Diff(-existing, +new) - %s\n. Deleting existing forwarding rule.", loadBalancerName, existingFwdRule, newFwdRule, frDiff)
209+
frDiff := cmp.Diff(op.actualForwardingRule, op.desiredForwardingRule)
210+
klogV.Infof("ensureInternalLoadBalancer(%v): forwarding rule changed - Existing - %+v\n, New - %+v\n, Diff(-existing, +new) - %s\n. Deleting existing forwarding rule.", loadBalancerName, op.actualForwardingRule, op.desiredForwardingRule, frDiff)
211211
}
212212
if err = ignoreNotFound(g.DeleteRegionForwardingRule(loadBalancerName, g.region)); err != nil {
213213
return nil, err
214214
}
215-
fwdRuleDeleted = true
215+
op.actualForwardingRule = nil
216216
}
217217

218218
bsDescription := makeBackendServiceDescription(nm, sharedBackend)
@@ -221,9 +221,9 @@ func (g *Cloud) ensureInternalLoadBalancer(clusterName, clusterID string, svc *v
221221
return nil, err
222222
}
223223

224-
if fwdRuleDeleted || existingFwdRule == nil {
225-
// existing rule has been deleted, pass in nil
226-
if err := g.ensureInternalForwardingRule(nil, newFwdRule); err != nil {
224+
if op.actualForwardingRule == nil {
225+
// existing rule has been deleted
226+
if err := g.ensureInternalForwardingRule(op); err != nil {
227227
return nil, err
228228
}
229229
}
@@ -1084,24 +1084,24 @@ func getFwdRuleAPIVersion(rule *compute.ForwardingRule) (meta.Version, error) {
10841084
return d.APIVersion, nil
10851085
}
10861086

1087-
func (g *Cloud) ensureInternalForwardingRule(existingFwdRule, newFwdRule *compute.ForwardingRule) (err error) {
1088-
if existingFwdRule != nil {
1089-
if forwardingRulesEqual(existingFwdRule, newFwdRule) {
1090-
klog.V(4).Infof("existingFwdRule == newFwdRule, no updates needed (existingFwdRule == %+v)", existingFwdRule)
1087+
func (g *Cloud) ensureInternalForwardingRule(op *loadBalancerSync) (err error) {
1088+
if op.actualForwardingRule != nil {
1089+
if forwardingRulesEqual(op.actualForwardingRule, op.desiredForwardingRule) {
1090+
klog.V(4).Infof("actualForwardingRule == desiredForwardingRule, no updates needed (actualForwardingRule == %+v)", op.actualForwardingRule)
10911091
return nil
10921092
}
1093-
klog.V(2).Infof("ensureInternalLoadBalancer(%v): deleting existing forwarding rule with IP address %v", existingFwdRule.Name, existingFwdRule.IPAddress)
1094-
if err = ignoreNotFound(g.DeleteRegionForwardingRule(existingFwdRule.Name, g.region)); err != nil {
1093+
klog.V(2).Infof("ensureInternalLoadBalancer(%v): deleting existing forwarding rule with IP address %v", op.actualForwardingRule.Name, op.actualForwardingRule.IPAddress)
1094+
if err = ignoreNotFound(g.DeleteRegionForwardingRule(op.actualForwardingRule.Name, g.region)); err != nil {
10951095
return err
10961096
}
10971097
}
10981098
// At this point, the existing rule has been deleted if required.
10991099
// Create the rule based on the api version determined
1100-
klog.V(2).Infof("ensureInternalLoadBalancer(%v): creating forwarding rule", newFwdRule.Name)
1101-
if err = g.CreateRegionForwardingRule(newFwdRule, g.region); err != nil {
1100+
klog.V(2).Infof("ensureInternalLoadBalancer(%v): creating forwarding rule", op.desiredForwardingRule.Name)
1101+
if err = g.CreateRegionForwardingRule(op.desiredForwardingRule, g.region); err != nil {
11021102
return err
11031103
}
1104-
klog.V(2).Infof("ensureInternalLoadBalancer(%v): created forwarding rule", newFwdRule.Name)
1104+
klog.V(2).Infof("ensureInternalLoadBalancer(%v): created forwarding rule", op.desiredForwardingRule.Name)
11051105
return nil
11061106
}
11071107

providers/gce/gce_loadbalancer_internal_test.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,14 @@ func createInternalLoadBalancer(gce *Cloud, svc *v1.Service, existingFwdRule *co
4848
return nil, err
4949
}
5050

51+
op := &loadBalancerSync{}
52+
op.actualForwardingRule = existingFwdRule
53+
5154
return gce.ensureInternalLoadBalancer(
5255
clusterName,
5356
clusterID,
5457
svc,
55-
existingFwdRule,
58+
op,
5659
nodes,
5760
)
5861
}
@@ -660,7 +663,10 @@ func TestUpdateInternalLoadBalancerNodes(t *testing.T) {
660663
nodes, err := createAndInsertNodes(gce, node1Name, vals.ZoneName)
661664
require.NoError(t, err)
662665

663-
_, err = gce.ensureInternalLoadBalancer(vals.ClusterName, vals.ClusterID, svc, nil, nodes)
666+
op := &loadBalancerSync{}
667+
op.actualForwardingRule = nil
668+
669+
_, err = gce.ensureInternalLoadBalancer(vals.ClusterName, vals.ClusterID, svc, op, nodes)
664670
assert.NoError(t, err)
665671

666672
// Replace the node in initial zone; add new node in a new zone.
@@ -729,7 +735,10 @@ func TestUpdateInternalLoadBalancerNodesWithEmptyZone(t *testing.T) {
729735
nodes, err := createAndInsertNodes(gce, node1Name, vals.ZoneName)
730736
require.NoError(t, err)
731737

732-
_, err = gce.ensureInternalLoadBalancer(vals.ClusterName, vals.ClusterID, svc, nil, nodes)
738+
op := &loadBalancerSync{}
739+
op.actualForwardingRule = nil
740+
741+
_, err = gce.ensureInternalLoadBalancer(vals.ClusterName, vals.ClusterID, svc, op, nodes)
733742
assert.NoError(t, err)
734743

735744
// Ensure Node has been added to instance group
@@ -747,7 +756,7 @@ func TestUpdateInternalLoadBalancerNodesWithEmptyZone(t *testing.T) {
747756
nodes[0].Labels[v1.LabelTopologyZone] = "" // empty zone
748757

749758
lbName := gce.GetLoadBalancerName(context.TODO(), "", svc)
750-
existingFwdRule := &compute.ForwardingRule{
759+
op.actualForwardingRule = &compute.ForwardingRule{
751760
Name: lbName,
752761
IPAddress: "",
753762
Ports: []string{"123"},
@@ -756,7 +765,7 @@ func TestUpdateInternalLoadBalancerNodesWithEmptyZone(t *testing.T) {
756765
Description: fmt.Sprintf(`{"kubernetes.io/service-name":"%s"}`, types.NamespacedName{Name: svc.Name, Namespace: svc.Namespace}.String()),
757766
}
758767

759-
_, err = gce.ensureInternalLoadBalancer(vals.ClusterName, vals.ClusterID, svc, existingFwdRule, nodes)
768+
_, err = gce.ensureInternalLoadBalancer(vals.ClusterName, vals.ClusterID, svc, op, nodes)
760769
assert.NoError(t, err)
761770

762771
// Expect load balancer to not have deleted node test-node-1
@@ -1232,11 +1241,15 @@ func TestEnsureInternalLoadBalancerErrors(t *testing.T) {
12321241
}
12331242
_, err = gce.client.CoreV1().Services(params.service.Namespace).Create(context.TODO(), params.service, metav1.CreateOptions{})
12341243
require.NoError(t, err)
1244+
1245+
op := &loadBalancerSync{}
1246+
op.actualForwardingRule = params.existingFwdRule
1247+
12351248
status, err := gce.ensureInternalLoadBalancer(
12361249
params.clusterName,
12371250
params.clusterID,
12381251
params.service,
1239-
params.existingFwdRule,
1252+
op,
12401253
params.nodes,
12411254
)
12421255
assert.Error(t, err, "Should return an error when "+desc)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//go:build !providerless
2+
// +build !providerless
3+
4+
/*
5+
Copyright 2025 The Kubernetes Authors.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
*/
19+
20+
package gce
21+
22+
import compute "google.golang.org/api/compute/v1"
23+
24+
// loadBalancerSync is a struct that tracks the desired and actual state of the GCP resources
25+
// that back the load balancer. It is used to drive the actual state to the desired state.
26+
type loadBalancerSync struct {
27+
desiredForwardingRule *compute.ForwardingRule
28+
actualForwardingRule *compute.ForwardingRule
29+
}

0 commit comments

Comments
 (0)