Skip to content

Commit 9327597

Browse files
committed
refactor into designed declarative approach
1 parent 3cda00c commit 9327597

File tree

1 file changed

+115
-145
lines changed

1 file changed

+115
-145
lines changed

providers/gce/gce_loadbalancer_external.go

Lines changed: 115 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -269,95 +269,150 @@ func (g *Cloud) ensureExternalLoadBalancer(clusterName string, clusterID string,
269269
}
270270

271271
// Forwarding rule logic
272-
// First, handle the old forwarding rule name for backward compatibility.
273-
fwd, err := g.GetRegionForwardingRule(loadBalancerName, g.region)
274-
if err != nil && !isNotFound(err) {
275-
return nil, err
272+
// We build the desired forwarding rules and then sync them with what exists.
273+
existingFRs, err := g.getExistingForwardingRules(loadBalancerName)
274+
if err != nil {
275+
return nil, fmt.Errorf("error getting existing forwarding rules for %s: %v", loadBalancerName, err)
276276
}
277-
if fwd != nil {
278-
// If the old forwarding rule exists, we need to check if it matches one of the protocols.
279-
// If so, we keep it. If not, we delete it.
280-
protocol, err := getProtocol(fwd.IPProtocol)
281-
if err != nil {
282-
return nil, err
283-
}
284-
if _, ok := groupedPorts[protocol]; !ok {
285-
// This forwarding rule's protocol is not in the service spec, so delete it.
286-
if err := g.DeleteRegionForwardingRule(loadBalancerName, g.region); err != nil && !isNotFound(err) {
277+
278+
// Get the old forwarding rule for backward compatibility.
279+
var oldFwdRule *compute.ForwardingRule
280+
if fwd, ok := existingFRs[loadBalancerName]; ok {
281+
oldFwdRule = fwd
282+
}
283+
284+
desiredFRs, err := g.buildDesiredForwardingRules(loadBalancerName, serviceName.String(), ipAddressToUse, g.targetPoolURL(loadBalancerName), apiService, netTier, oldFwdRule)
285+
if err != nil {
286+
return nil, fmt.Errorf("error building desired forwarding rules for %s: %v", loadBalancerName, err)
287+
}
288+
289+
// Delete unwanted forwarding rules.
290+
for _, fr := range existingFRs {
291+
if _, ok := desiredFRs[fr.Name]; !ok {
292+
klog.Infof("ensureExternalLoadBalancer(%s): Deleting orphaned forwarding rule %s.", lbRefStr, fr.Name)
293+
if err := g.DeleteRegionForwardingRule(fr.Name, g.region); err != nil && !isNotFound(err) {
287294
return nil, err
288295
}
289296
}
290297
}
291298

292-
// Then, iterate over the protocols and create/update forwarding rules.
293-
for protocol, protocolPorts := range groupedPorts {
294-
var frName string
295-
if g.AlphaFeatureGate.Enabled(AlphaFeatureMultiProtocolLB) {
296-
frName = g.getProtocolForwardingRuleName(loadBalancerName, protocol)
297-
// If the old forwarding rule matches this protocol, use its name.
298-
if fwd != nil && fwd.IPProtocol == string(protocol) {
299-
frName = loadBalancerName
300-
}
301-
} else {
302-
frName = loadBalancerName
303-
}
299+
// Create or update forwarding rules.
300+
for _, desiredFR := range desiredFRs {
301+
existingFR, exists := existingFRs[desiredFR.Name]
304302

305-
exists, needsUpdate, _, err := g.forwardingRuleNeedsUpdate(frName, g.region, ipAddressToUse, protocolPorts)
306-
if err != nil {
307-
return nil, err
303+
needsUpdate := false
304+
if exists {
305+
if !g.forwardingRulesEqual(existingFR, desiredFR) {
306+
needsUpdate = true
307+
}
308308
}
309309

310-
if needsUpdate || tpNeedsRecreation {
310+
if !exists || needsUpdate || tpNeedsRecreation {
311311
if exists {
312-
if err := g.DeleteRegionForwardingRule(frName, g.region); err != nil && !isNotFound(err) {
312+
klog.Infof("ensureExternalLoadBalancer(%s): Deleting forwarding rule %s to update.", lbRefStr, desiredFR.Name)
313+
if err := g.DeleteRegionForwardingRule(desiredFR.Name, g.region); err != nil && !isNotFound(err) {
313314
return nil, err
314315
}
315316
}
316-
klog.Infof("ensureExternalLoadBalancer(%s): Creating forwarding rule %s for protocol %s, IP %s (tier: %s).", lbRefStr, frName, protocol, ipAddressToUse, netTier)
317-
if err := createForwardingRule(g, frName, serviceName.String(), g.region, ipAddressToUse, g.targetPoolURL(loadBalancerName), protocolPorts, netTier, g.enableDiscretePortForwarding); err != nil {
317+
klog.Infof("ensureExternalLoadBalancer(%s): Creating forwarding rule %s for protocol %s, IP %s (tier: %s).", lbRefStr, desiredFR.Name, desiredFR.IPProtocol, ipAddressToUse, netTier)
318+
// The desiredFR is a complete spec, so we can just pass it to CreateRegionForwardingRule.
319+
if err := g.CreateRegionForwardingRule(desiredFR, g.region); err != nil && !isHTTPErrorCode(err, http.StatusConflict) {
318320
return nil, fmt.Errorf("failed to create forwarding rule for load balancer (%s): %v", lbRefStr, err)
319321
}
320-
klog.Infof("ensureExternalLoadBalancer(%s): Created forwarding rule %s.", lbRefStr, frName)
322+
klog.Infof("ensureExternalLoadBalancer(%s): Created forwarding rule %s.", lbRefStr, desiredFR.Name)
321323
}
322324
}
323325

324-
// Garbage collect old forwarding rules.
325-
if g.AlphaFeatureGate.Enabled(AlphaFeatureMultiProtocolLB) {
326-
activeFRNames := sets.NewString()
327-
for protocol := range groupedPorts {
328-
activeFRNames.Insert(g.getProtocolForwardingRuleName(loadBalancerName, protocol))
326+
isSafeToReleaseIP = true
327+
status := &v1.LoadBalancerStatus{}
328+
status.Ingress = []v1.LoadBalancerIngress{{IP: ipAddressToUse}}
329+
330+
return status, nil
331+
}
332+
333+
// getExistingForwardingRules returns a map of forwarding rules for the given load balancer.
334+
func (g *Cloud) getExistingForwardingRules(loadBalancerName string) (map[string]*compute.ForwardingRule, error) {
335+
frs, err := g.ListRegionForwardingRules(g.region)
336+
if err != nil {
337+
return nil, err
338+
}
339+
340+
existingFRs := make(map[string]*compute.ForwardingRule)
341+
for _, fr := range frs {
342+
if strings.HasPrefix(fr.Name, loadBalancerName) {
343+
existingFRs[fr.Name] = fr
329344
}
330-
// Check for the old forwarding rule name.
331-
if fwd != nil {
332-
protocol, err := getProtocol(fwd.IPProtocol)
333-
if err != nil {
334-
return nil, err
335-
}
336-
if _, ok := groupedPorts[protocol]; ok {
337-
activeFRNames.Insert(loadBalancerName)
345+
}
346+
return existingFRs, nil
347+
}
348+
349+
// buildDesiredForwardingRules builds the desired forwarding rules for the given load balancer.
350+
func (g *Cloud) buildDesiredForwardingRules(loadBalancerName, serviceName, ipAddress, targetPoolURL string, apiService *v1.Service, netTier cloud.NetworkTier, oldFwdRule *compute.ForwardingRule) (map[string]*compute.ForwardingRule, error) {
351+
desiredFRs := make(map[string]*compute.ForwardingRule)
352+
groupedPorts := groupPortsByProtocol(apiService.Spec.Ports)
353+
desc := makeServiceDescription(serviceName)
354+
355+
for protocol, protocolPorts := range groupedPorts {
356+
var frName string
357+
if g.AlphaFeatureGate.Enabled(AlphaFeatureMultiProtocolLB) {
358+
frName = g.getProtocolForwardingRuleName(loadBalancerName, protocol)
359+
// If the old forwarding rule matches this protocol, use its name for backward compatibility.
360+
if oldFwdRule != nil && oldFwdRule.IPProtocol == string(protocol) {
361+
frName = loadBalancerName
338362
}
363+
} else {
364+
frName = loadBalancerName
339365
}
340366

341-
// List all forwarding rules for this service and delete the ones that are not active.
342-
frs, err := g.ListRegionForwardingRules(g.region)
367+
frPorts := getPorts(protocolPorts)
368+
portRange, err := loadBalancerPortRange(protocolPorts)
343369
if err != nil {
344370
return nil, err
345371
}
346-
for _, fr := range frs {
347-
if strings.HasPrefix(fr.Name, loadBalancerName) && !activeFRNames.Has(fr.Name) {
348-
klog.Infof("ensureExternalLoadBalancer(%s): Deleting orphaned forwarding rule %s.", lbRefStr, fr.Name)
349-
if err := g.DeleteRegionForwardingRule(fr.Name, g.region); err != nil && !isNotFound(err) {
350-
return nil, err
351-
}
352-
}
372+
373+
rule := &compute.ForwardingRule{
374+
Name: frName,
375+
Description: desc,
376+
IPAddress: ipAddress,
377+
IPProtocol: string(protocol),
378+
PortRange: portRange,
379+
Target: targetPoolURL,
380+
NetworkTier: netTier.ToGCEValue(),
353381
}
382+
383+
if len(frPorts) <= maxForwardedPorts && g.enableDiscretePortForwarding {
384+
rule.Ports = frPorts
385+
rule.PortRange = ""
386+
}
387+
388+
desiredFRs[frName] = rule
354389
}
390+
return desiredFRs, nil
391+
}
355392

356-
isSafeToReleaseIP = true
357-
status := &v1.LoadBalancerStatus{}
358-
status.Ingress = []v1.LoadBalancerIngress{{IP: ipAddressToUse}}
393+
// forwardingRulesEqual checks if two forwarding rules are equal.
394+
func (g *Cloud) forwardingRulesEqual(existing, desired *compute.ForwardingRule) bool {
395+
if existing.IPAddress != desired.IPAddress {
396+
klog.V(3).Infof("Forwarding rule %s IP changed from %s to %s", existing.Name, existing.IPAddress, desired.IPAddress)
397+
return false
398+
}
399+
if existing.IPProtocol != desired.IPProtocol {
400+
klog.V(3).Infof("Forwarding rule %s protocol changed from %s to %s", existing.Name, existing.IPProtocol, desired.IPProtocol)
401+
return false
402+
}
359403

360-
return status, nil
404+
frEqualPorts := equalPorts(existing.Ports, desired.Ports, existing.PortRange, desired.PortRange, g.enableDiscretePortForwarding)
405+
if !frEqualPorts {
406+
klog.V(3).Infof("Forwarding rule %s ports changed from (range: %v, ports: %v) to (range: %v, ports: %v)", existing.Name, existing.PortRange, existing.Ports, desired.PortRange, desired.Ports)
407+
return false
408+
}
409+
410+
if existing.NetworkTier != desired.NetworkTier {
411+
klog.V(3).Infof("Forwarding rule %s network tier changed from %s to %s", existing.Name, existing.NetworkTier, desired.NetworkTier)
412+
return false
413+
}
414+
415+
return true
361416
}
362417

363418

@@ -853,58 +908,7 @@ func (g *Cloud) ensureHTTPHealthCheck(name, path string, port int32) (hc *comput
853908
return hc, nil
854909
}
855910

856-
// Passing nil for requested IP is perfectly fine - it just means that no specific
857-
// IP is being requested.
858-
// Returns whether the forwarding rule exists, whether it needs to be updated,
859-
// what its IP address is (if it exists), and any error we encountered.
860-
func (g *Cloud) forwardingRuleNeedsUpdate(name, region string, loadBalancerIP string, ports []v1.ServicePort) (exists bool, needsUpdate bool, ipAddress string, err error) {
861-
fwd, err := g.GetRegionForwardingRule(name, region)
862-
if err != nil {
863-
if isHTTPErrorCode(err, http.StatusNotFound) {
864-
return false, true, "", nil
865-
}
866-
// Err on the side of caution in case of errors. Caller should notice the error and retry.
867-
// We never want to end up recreating resources because g api flaked.
868-
return true, false, "", fmt.Errorf("error getting load balancer's forwarding rule: %v", err)
869-
}
870-
// If the user asks for a specific static ip through the Service spec,
871-
// check that we're actually using it.
872-
// TODO: we report loadbalancer IP through status, so we want to verify if
873-
// that matches the forwarding rule as well.
874-
if loadBalancerIP != "" && loadBalancerIP != fwd.IPAddress {
875-
klog.Infof("LoadBalancer ip for forwarding rule %v was expected to be %v, but was actually %v", fwd.Name, fwd.IPAddress, loadBalancerIP)
876-
return true, true, fwd.IPAddress, nil
877-
}
878911

879-
protocol, err := getProtocol(ports)
880-
if err != nil {
881-
return true, false, "", err
882-
}
883-
884-
newPortRange, err := loadBalancerPortRange(ports)
885-
if err != nil {
886-
// Err on the side of caution in case of errors. Caller should notice the error and retry.
887-
// We never want to end up recreating resources because g api flaked.
888-
return true, false, "", err
889-
}
890-
newPorts := []string{}
891-
if frPorts := getPorts(ports); len(frPorts) <= maxForwardedPorts && g.enableDiscretePortForwarding {
892-
newPorts = frPorts
893-
newPortRange = ""
894-
}
895-
frEqualPorts := equalPorts(fwd.Ports, newPorts, fwd.PortRange, newPortRange, g.enableDiscretePortForwarding)
896-
if !frEqualPorts {
897-
klog.Infof("Forwarding rule port range / ports are not equal, old (port range: %v, ports: %v), new (port range: %v, ports: %v)", fwd.PortRange, fwd.Ports, newPortRange, newPorts)
898-
return true, true, fwd.IPAddress, nil
899-
}
900-
901-
if string(protocol) != fwd.IPProtocol {
902-
klog.Infof("LoadBalancer protocol for forwarding rule %v was expected to be %v, but was actually %v", fwd.Name, fwd.IPProtocol, string(protocol))
903-
return true, true, fwd.IPAddress, nil
904-
}
905-
906-
return true, false, fwd.IPAddress, nil
907-
}
908912

909913
// Doesn't check whether the hosts have changed, since host updating is handled
910914
// separately.
@@ -1141,41 +1145,7 @@ func (g *Cloud) ensureHTTPHealthCheckFirewall(svc *v1.Service, serviceName, ipAd
11411145
return nil
11421146
}
11431147

1144-
func createForwardingRule(s CloudForwardingRuleService, name, serviceName, region, ipAddress, target string, ports []v1.ServicePort, netTier cloud.NetworkTier, enableDiscretePortForwarding bool) error {
1145-
frPorts := getPorts(ports)
1146-
protocol, err := getProtocol(ports)
1147-
if err != nil {
1148-
return err
1149-
}
1150-
portRange, err := loadBalancerPortRange(ports)
1151-
if err != nil {
1152-
return err
1153-
}
1154-
desc := makeServiceDescription(serviceName)
11551148

1156-
rule := &compute.ForwardingRule{
1157-
Name: name,
1158-
Description: desc,
1159-
IPAddress: ipAddress,
1160-
IPProtocol: string(protocol),
1161-
PortRange: portRange,
1162-
Target: target,
1163-
NetworkTier: netTier.ToGCEValue(),
1164-
}
1165-
1166-
if len(frPorts) <= maxForwardedPorts && enableDiscretePortForwarding {
1167-
rule.Ports = frPorts
1168-
rule.PortRange = ""
1169-
}
1170-
1171-
err = s.CreateRegionForwardingRule(rule, region)
1172-
1173-
if err != nil && !isHTTPErrorCode(err, http.StatusConflict) {
1174-
return err
1175-
}
1176-
1177-
return nil
1178-
}
11791149

11801150
func (g *Cloud) createFirewall(svc *v1.Service, name, desc, destinationIP string, sourceRanges utilnet.IPNetSet, ports []v1.ServicePort, hosts []*gceInstance) error {
11811151
firewall, err := g.firewallObject(name, desc, destinationIP, sourceRanges, ports, hosts)

0 commit comments

Comments
 (0)