Skip to content

Commit 8e75fbe

Browse files
committed
fixup! controller support for mixed protocol LB services
1 parent 72ed8d1 commit 8e75fbe

File tree

3 files changed

+58
-167
lines changed

3 files changed

+58
-167
lines changed

providers/gce/gce_loadbalancer.go

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -145,28 +145,28 @@ func (g *Cloud) EnsureLoadBalancer(ctx context.Context, clusterName string, svc
145145
return nil, err
146146
}
147147

148-
// Services with multiples protocols are not supported by this controller, warn the users and sets
149-
// the corresponding Service Status Condition.
148+
// Services with multiples protocols are not supported by this controller (without AlphaFeatureMultiProtocolLB),
149+
// warn the users and set the corresponding Service Status Condition.
150150
// https://github.com/kubernetes/enhancements/tree/master/keps/sig-network/1435-mixed-protocol-lb
151-
if !g.AlphaFeatureGate.Enabled(AlphaFeatureMultiProtocolLB) {
152-
if err := checkMixedProtocol(svc.Spec.Ports); err != nil {
153-
if hasLoadBalancerPortsError(svc) {
154-
return nil, err
155-
}
156-
klog.Warningf("Ignoring service %s/%s using different ports protocols", svc.Namespace, svc.Name)
157-
g.eventRecorder.Event(svc, v1.EventTypeWarning, v1.LoadBalancerPortsErrorReason, "LoadBalancers with multiple protocols are not supported.")
158-
svcApplyStatus := corev1apply.ServiceStatus().WithConditions(
159-
metav1apply.Condition().
160-
WithType(v1.LoadBalancerPortsError).
161-
WithStatus(metav1.ConditionTrue).
162-
WithReason(v1.LoadBalancerPortsErrorReason).
163-
WithMessage("LoadBalancer with multiple protocols are not supported"))
164-
svcApply := corev1apply.Service(svc.Name, svc.Namespace).WithStatus(svcApplyStatus)
165-
if _, errApply := g.client.CoreV1().Services(svc.Namespace).ApplyStatus(ctx, svcApply, metav1.ApplyOptions{FieldManager: "gce-cloud-controller", Force: true}); errApply != nil {
166-
return nil, errApply
167-
}
151+
if g.AlphaFeatureGate.Enabled(AlphaFeatureMultiProtocolLB) {
152+
klog.Infof("AlphaFeatureMultiProtocolLB feature gate is enabled")
153+
} else if err := checkMixedProtocol(svc.Spec.Ports); err != nil {
154+
if hasLoadBalancerPortsError(svc) {
168155
return nil, err
169156
}
157+
klog.Warningf("Ignoring service %s/%s using different ports protocols", svc.Namespace, svc.Name)
158+
g.eventRecorder.Event(svc, v1.EventTypeWarning, v1.LoadBalancerPortsErrorReason, "LoadBalancers with multiple protocols are not supported.")
159+
svcApplyStatus := corev1apply.ServiceStatus().WithConditions(
160+
metav1apply.Condition().
161+
WithType(v1.LoadBalancerPortsError).
162+
WithStatus(metav1.ConditionTrue).
163+
WithReason(v1.LoadBalancerPortsErrorReason).
164+
WithMessage("LoadBalancer with multiple protocols are not supported"))
165+
svcApply := corev1apply.Service(svc.Name, svc.Namespace).WithStatus(svcApplyStatus)
166+
if _, errApply := g.client.CoreV1().Services(svc.Namespace).ApplyStatus(ctx, svcApply, metav1.ApplyOptions{FieldManager: "gce-cloud-controller", Force: true}); errApply != nil {
167+
return nil, errApply
168+
}
169+
return nil, err
170170
}
171171

172172
klog.V(4).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v): ensure %v loadbalancer", clusterName, svc.Namespace, svc.Name, loadBalancerName, g.region, desiredScheme)
@@ -230,24 +230,24 @@ func (g *Cloud) UpdateLoadBalancer(ctx context.Context, clusterName string, svc
230230
return err
231231
}
232232

233-
// Services with multiples protocols are not supported by this controller, warn the users and sets
233+
// Services with multiples protocols are not supported by this controller (without AlphaFeatureMultiProtocolLB), warn the users and sets
234234
// the corresponding Service Status Condition, but keep processing the Update to not break upgrades.
235235
// https://github.com/kubernetes/enhancements/tree/master/keps/sig-network/1435-mixed-protocol-lb
236-
if !g.AlphaFeatureGate.Enabled(AlphaFeatureMultiProtocolLB) {
237-
if err := checkMixedProtocol(svc.Spec.Ports); err != nil && !hasLoadBalancerPortsError(svc) {
238-
klog.Warningf("Ignoring update for service %s/%s using different ports protocols", svc.Namespace, svc.Name)
239-
g.eventRecorder.Event(svc, v1.EventTypeWarning, v1.LoadBalancerPortsErrorReason, "LoadBalancer with multiple protocols are not supported.")
240-
svcApplyStatus := corev1apply.ServiceStatus().WithConditions(
241-
metav1apply.Condition().
242-
WithType(v1.LoadBalancerPortsError).
243-
WithStatus(metav1.ConditionTrue).
244-
WithReason(v1.LoadBalancerPortsErrorReason).
245-
WithMessage("LoadBalancer with multiple protocols are not supported"))
246-
svcApply := corev1apply.Service(svc.Name, svc.Namespace).WithStatus(svcApplyStatus)
247-
if _, errApply := g.client.CoreV1().Services(svc.Namespace).ApplyStatus(ctx, svcApply, metav1.ApplyOptions{FieldManager: "gce-cloud-controller", Force: true}); errApply != nil {
248-
// the error is retried by the controller loop
249-
return errApply
250-
}
236+
if g.AlphaFeatureGate.Enabled(AlphaFeatureMultiProtocolLB) {
237+
klog.Infof("AlphaFeatureMultiProtocolLB feature gate is enabled")
238+
} else if err := checkMixedProtocol(svc.Spec.Ports); err != nil && !hasLoadBalancerPortsError(svc) {
239+
klog.Warningf("Ignoring update for service %s/%s using different ports protocols", svc.Namespace, svc.Name)
240+
g.eventRecorder.Event(svc, v1.EventTypeWarning, v1.LoadBalancerPortsErrorReason, "LoadBalancer with multiple protocols are not supported.")
241+
svcApplyStatus := corev1apply.ServiceStatus().WithConditions(
242+
metav1apply.Condition().
243+
WithType(v1.LoadBalancerPortsError).
244+
WithStatus(metav1.ConditionTrue).
245+
WithReason(v1.LoadBalancerPortsErrorReason).
246+
WithMessage("LoadBalancer with multiple protocols are not supported"))
247+
svcApply := corev1apply.Service(svc.Name, svc.Namespace).WithStatus(svcApplyStatus)
248+
if _, errApply := g.client.CoreV1().Services(svc.Namespace).ApplyStatus(ctx, svcApply, metav1.ApplyOptions{FieldManager: "gce-cloud-controller", Force: true}); errApply != nil {
249+
// the error is retried by the controller loop
250+
return errApply
251251
}
252252
}
253253

providers/gce/gce_loadbalancer_external.go

Lines changed: 22 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ const (
5252
// IP address, a firewall rule, a target pool, and a forwarding rule. This
5353
// function has to manage all of them.
5454
//
55-
// Due to an interesting series of design decisions, this handles both creating
56-
// new load balancers and updating existing load balancers, recognizing when
57-
// each is needed.
55+
// This function handles both creating new load balancers and updating existing load balancers,
56+
// recognizing when each is needed.
57+
// This approach is resilient, for example if we are interrupted part-way during creation.
5858
func (g *Cloud) ensureExternalLoadBalancer(clusterName string, clusterID string, apiService *v1.Service, existingFwdRule *compute.ForwardingRule, 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.
@@ -112,134 +112,23 @@ func (g *Cloud) ensureExternalLoadBalancer(clusterName string, clusterID string,
112112
g.deleteWrongNetworkTieredResources(loadBalancerName, lbRefStr, netTier)
113113
}
114114

115-
// If the feature gate is not enabled, we check for multiple protocols and error out.
116-
if !g.AlphaFeatureGate.Enabled(AlphaFeatureMultiProtocolLB) {
117-
fwdRuleExists, fwdRuleNeedsUpdate, fwdRuleIP, err := g.forwardingRuleNeedsUpdate(loadBalancerName, g.region, requestedIP, ports)
118-
if err != nil {
119-
return nil, err
120-
}
121-
if !fwdRuleExists {
122-
klog.V(2).Infof("ensureExternalLoadBalancer(%s): Forwarding rule %v doesn't exist.", lbRefStr, loadBalancerName)
123-
}
124-
// Single-protocol logic
125-
ipAddressToUse, isSafeToReleaseIP, err := g.ensureIPAddress(loadBalancerName, lbRefStr, apiService.Spec.LoadBalancerIP, fwdRuleIP, netTier)
126-
if err != nil {
127-
return nil, err
128-
}
129-
defer func() {
130-
if isSafeToReleaseIP {
131-
if err := g.DeleteRegionAddress(loadBalancerName, g.region); err != nil && !isNotFound(err) {
132-
klog.Errorf("ensureExternalLoadBalancer(%s): Failed to release static IP %s in region %v: %v.", lbRefStr, ipAddressToUse, g.region, err)
133-
} else if isNotFound(err) {
134-
klog.V(2).Infof("ensureExternalLoadBalancer(%s): IP address %s is not reserved.", lbRefStr, ipAddressToUse)
135-
} else {
136-
klog.Infof("ensureExternalLoadBalancer(%s): Released static IP %s.", lbRefStr, ipAddressToUse)
137-
}
138-
} else {
139-
klog.Warningf("ensureExternalLoadBalancer(%s): Orphaning static IP %s in region %v: %v.", lbRefStr, ipAddressToUse, g.region, err)
140-
}
141-
}()
142-
143-
sourceRanges, err := servicehelpers.GetLoadBalancerSourceRanges(apiService)
144-
if err != nil {
145-
return nil, err
146-
}
147-
148-
firewallExists, firewallNeedsUpdate, err := g.firewallNeedsUpdate(loadBalancerName, serviceName.String(), ipAddressToUse, ports, sourceRanges)
149-
if err != nil {
150-
return nil, err
151-
}
152-
153-
if firewallNeedsUpdate {
154-
desc := makeFirewallDescription(serviceName.String(), ipAddressToUse)
155-
if firewallExists {
156-
klog.Infof("ensureExternalLoadBalancer(%s): Updating firewall.", lbRefStr)
157-
if err := g.updateFirewall(apiService, MakeFirewallName(loadBalancerName), desc, ipAddressToUse, sourceRanges, ports, hosts); err != nil {
158-
return nil, err
159-
}
160-
klog.Infof("ensureExternalLoadBalancer(%s): Updated firewall.", lbRefStr)
161-
} else {
162-
klog.Infof("ensureExternalLoadBalancer(%s): Creating firewall.", lbRefStr)
163-
if err := g.createFirewall(apiService, MakeFirewallName(loadBalancerName), desc, ipAddressToUse, sourceRanges, ports, hosts); err != nil {
164-
return nil, err
165-
}
166-
klog.Infof("ensureExternalLoadBalancer(%s): Created firewall.", lbRefStr)
167-
}
168-
}
169-
170-
tpExists, tpNeedsRecreation, err := g.targetPoolNeedsRecreation(loadBalancerName, g.region, apiService.Spec.SessionAffinity)
171-
if err != nil {
172-
return nil, err
173-
}
174-
if !tpExists {
175-
klog.Infof("ensureExternalLoadBalancer(%s): Target pool for service doesn't exist.", lbRefStr)
176-
}
177-
178-
var hcToCreate, hcToDelete *compute.HttpHealthCheck
179-
hcLocalTrafficExisting, err := g.GetHTTPHealthCheck(loadBalancerName)
180-
if err != nil && !isHTTPErrorCode(err, http.StatusNotFound) {
181-
return nil, fmt.Errorf("error checking HTTP health check for load balancer (%s): %v", lbRefStr, err)
182-
}
183-
if path, healthCheckNodePort := servicehelpers.GetServiceHealthCheckPathPort(apiService); path != "" {
184-
klog.V(4).Infof("ensureExternalLoadBalancer(%s): Service needs local traffic health checks on: %d%s.", lbRefStr, healthCheckNodePort, path)
185-
if hcLocalTrafficExisting == nil {
186-
klog.V(2).Infof("ensureExternalLoadBalancer(%s): Updating from nodes health checks to local traffic health checks.", lbRefStr)
187-
hcToDelete = makeHTTPHealthCheck(MakeNodesHealthCheckName(clusterID), GetNodesHealthCheckPath(), GetNodesHealthCheckPort())
188-
tpNeedsRecreation = true
189-
}
190-
hcToCreate = makeHTTPHealthCheck(loadBalancerName, path, healthCheckNodePort)
191-
} else {
192-
klog.V(4).Infof("ensureExternalLoadBalancer(%s): Service needs nodes health checks.", lbRefStr)
193-
if hcLocalTrafficExisting != nil {
194-
klog.V(2).Infof("ensureExternalLoadBalancer(%s): Updating from local traffic health checks to nodes health checks.", lbRefStr)
195-
hcToDelete = hcLocalTrafficExisting
196-
tpNeedsRecreation = true
197-
}
198-
hcToCreate = makeHTTPHealthCheck(MakeNodesHealthCheckName(clusterID), GetNodesHealthCheckPath(), GetNodesHealthCheckPort())
199-
}
200-
201-
if fwdRuleExists && (fwdRuleNeedsUpdate || tpNeedsRecreation) {
202-
isSafeToReleaseIP = false
203-
if err := g.DeleteRegionForwardingRule(loadBalancerName, g.region); err != nil && !isNotFound(err) {
204-
return nil, fmt.Errorf("failed to delete existing forwarding rule for load balancer (%s) update: %v", lbRefStr, err)
205-
}
206-
klog.Infof("ensureExternalLoadBalancer(%s): Deleted forwarding rule.", lbRefStr)
207-
}
208-
209-
if err := g.ensureTargetPoolAndHealthCheck(tpExists, tpNeedsRecreation, apiService, loadBalancerName, clusterID, ipAddressToUse, hosts, hcToCreate, hcToDelete); err != nil {
210-
return nil, err
211-
}
212-
213-
if tpNeedsRecreation || fwdRuleNeedsUpdate {
214-
klog.Infof("ensureExternalLoadBalancer(%s): Creating forwarding rule, IP %s (tier: %s).", lbRefStr, ipAddressToUse, netTier)
215-
if err := createForwardingRule(g, loadBalancerName, serviceName.String(), g.region, ipAddressToUse, g.targetPoolURL(loadBalancerName), ports, netTier, g.enableDiscretePortForwarding); err != nil {
216-
return nil, fmt.Errorf("failed to create forwarding rule for load balancer (%s): %v", lbRefStr, err)
217-
}
218-
isSafeToReleaseIP = true
219-
klog.Infof("ensureExternalLoadBalancer(%s): Created forwarding rule, IP %s.", lbRefStr, ipAddressToUse)
220-
}
221-
222-
status := &v1.LoadBalancerStatus{}
223-
status.Ingress = []v1.LoadBalancerIngress{{IP: ipAddressToUse}}
224-
225-
return status, nil
226-
}
227-
228115
// Multi-protocol logic starts here
229116
groupedPorts := groupPortsByProtocol(ports)
230117
var fwdRuleIP string
231118

232119
// We need to determine the IP address for all forwarding rules.
233120
// We check if any of the forwarding rules already exist and use their IP.
234-
for protocol := range groupedPorts {
235-
frName := g.getProtocolForwardingRuleName(loadBalancerName, protocol)
236-
fwd, err := g.GetRegionForwardingRule(frName, g.region)
237-
if err != nil && !isNotFound(err) {
238-
return nil, err
239-
}
240-
if fwd != nil {
241-
fwdRuleIP = fwd.IPAddress
242-
break
121+
if !g.AlphaFeatureGate.Enabled(AlphaFeatureMultiProtocolLB) {
122+
for protocol := range groupedPorts {
123+
frName := g.getProtocolForwardingRuleName(loadBalancerName, protocol)
124+
fwd, err := g.GetRegionForwardingRule(frName, g.region)
125+
if err != nil && !isNotFound(err) {
126+
return nil, err
127+
}
128+
if fwd != nil {
129+
fwdRuleIP = fwd.IPAddress
130+
break
131+
}
243132
}
244133
}
245134
// If no forwarding rule exists, check for the old one.
@@ -456,7 +345,6 @@ func (g *Cloud) ensureIPAddress(loadBalancerName, lbRefStr, requestedIP, fwdRule
456345
return ipAddressToUse, isSafeToReleaseIP, nil
457346
}
458347

459-
460348
// updateExternalLoadBalancer is the external implementation of LoadBalancer.UpdateLoadBalancer.
461349
func (g *Cloud) updateExternalLoadBalancer(clusterName string, service *v1.Service, nodes []*v1.Node) error {
462350
// Process services with LoadBalancerClass "networking.gke.io/l4-regional-external-legacy" used for this controller.
@@ -534,8 +422,13 @@ func (g *Cloud) ensureExternalLoadBalancerDeleted(clusterName, clusterID string,
534422
},
535423
func() error {
536424
klog.Infof("ensureExternalLoadBalancerDeleted(%s): Deleting forwarding rules.", lbRefStr)
537-
// The forwarding rule must be deleted before either the target pool can,
538-
// unfortunately, so we have to do these two serially.
425+
// The forwarding rule must be deleted before the target pool can be deleted,
426+
// unfortunately, so we have to delete forwarding rules then target pools serially.
427+
if err := ignoreNotFound(g.DeleteRegionForwardingRule(loadBalancerName, g.region)); err != nil {
428+
return err
429+
}
430+
431+
// TODO: Always or just with alpha feature flag?
539432
frs, err := g.ListRegionForwardingRules(g.region)
540433
if err != nil {
541434
return err
@@ -1125,8 +1018,7 @@ func equalPorts(existingPorts, newPorts []string, existingPortRange, newPortRang
11251018

11261019
func groupPortsByProtocol(ports []v1.ServicePort) map[v1.Protocol][]v1.ServicePort {
11271020
grouped := make(map[v1.Protocol][]v1.ServicePort)
1128-
for _, p := range ports {
1129-
port := p
1021+
for _, port := range ports {
11301022
grouped[port.Protocol] = append(grouped[port.Protocol], port)
11311023
}
11321024
return grouped
@@ -1195,7 +1087,6 @@ func (g *Cloud) firewallNeedsUpdate(name, serviceName, ipAddress string, ports [
11951087
return true, false, nil
11961088
}
11971089

1198-
11991090
func (g *Cloud) ensureHTTPHealthCheckFirewall(svc *v1.Service, serviceName, ipAddress, region, clusterID string, hosts []*gceInstance, hcName string, hcPort int32, isNodesHealthCheck bool) error {
12001091
// Prepare the firewall params for creating / checking.
12011092
desc := fmt.Sprintf(`{"kubernetes.io/cluster-id":"%s"}`, clusterID)

providers/gce/gce_loadbalancer_naming.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
"strings"
2727

2828
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud"
29-
"k8s.io/api/core/v1"
29+
v1 "k8s.io/api/core/v1"
3030
"k8s.io/apimachinery/pkg/types"
3131
)
3232

0 commit comments

Comments
 (0)