-
Notifications
You must be signed in to change notification settings - Fork 181
/
load_balancer_service.go
183 lines (160 loc) · 7.46 KB
/
load_balancer_service.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package ingress
import (
"context"
"fmt"
operatorv1 "github.com/openshift/api/operator/v1"
"github.com/openshift/cluster-ingress-operator/pkg/manifests"
"github.com/openshift/cluster-ingress-operator/pkg/operator/controller"
"github.com/openshift/cluster-ingress-operator/pkg/util/slice"
corev1 "k8s.io/api/core/v1"
configv1 "github.com/openshift/api/config/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
// awsLBProxyProtocolAnnotation is used to enable the PROXY protocol on any
// AWS load balancer services created.
//
// https://kubernetes.io/docs/concepts/services-networking/service/#proxy-protocol-support-on-aws
awsLBProxyProtocolAnnotation = "service.beta.kubernetes.io/aws-load-balancer-proxy-protocol"
iksLBProxyProtocolAnnotations = "service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type"
iksLBProxyRegionAnnotations = "service.kubernetes.io/ibm-load-balancer-cloud-provider-zone"
)
var (
// internalLBAnnotations maps platform to the annotation name and value
// that tell the cloud provider that is associated with the platform
// that the load balancer is internal.
//
// https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer
InternalLBAnnotations = map[configv1.PlatformType]map[string]string{
configv1.AWSPlatformType: {
"service.beta.kubernetes.io/aws-load-balancer-internal": "0.0.0.0/0",
},
configv1.AzurePlatformType: {
// Azure load balancers are not customizable and are set to (2 fail @ 5s interval, 2 healthy)
"service.beta.kubernetes.io/azure-load-balancer-internal": "true",
},
// There are no required annotations for this platform as of
// 2019-06-17 (but maybe MetalLB will have something for
// internal load-balancers in the future).
configv1.BareMetalPlatformType: nil,
configv1.GCPPlatformType: {
"cloud.google.com/load-balancer-type": "Internal",
},
// There are no required annotations for this platform.
configv1.LibvirtPlatformType: nil,
configv1.OpenStackPlatformType: {
"service.beta.kubernetes.io/openstack-internal-load-balancer": "true",
},
configv1.NonePlatformType: nil,
// vSphere does not support load balancers as of 2019-06-17.
configv1.VSpherePlatformType: nil,
configv1.IBMCloudPlatformType: {
iksLBProxyProtocolAnnotations: "private",
},
}
)
// ensureLoadBalancerService creates an LB service if one is desired but absent.
// Always returns the current LB service if one exists (whether it already
// existed or was created during the course of the function).
func (r *reconciler) ensureLoadBalancerService(ci *operatorv1.IngressController, deploymentRef metav1.OwnerReference, infraConfig *configv1.Infrastructure) (*corev1.Service, error) {
desiredLBService, err := desiredLoadBalancerService(ci, deploymentRef, infraConfig)
if err != nil {
return nil, err
}
currentLBService, err := r.currentLoadBalancerService(ci)
if err != nil {
return nil, err
}
if desiredLBService != nil && currentLBService == nil {
if err := r.client.Create(context.TODO(), desiredLBService); err != nil {
return nil, fmt.Errorf("failed to create load balancer service %s/%s: %v", desiredLBService.Namespace, desiredLBService.Name, err)
}
log.Info("created load balancer service", "namespace", desiredLBService.Namespace, "name", desiredLBService.Name)
return desiredLBService, nil
}
return currentLBService, nil
}
// desiredLoadBalancerService returns the desired LB service for a
// ingresscontroller, or nil if an LB service isn't desired. An LB service is
// desired if the high availability type is Cloud. An LB service will declare an
// owner reference to the given deployment.
func desiredLoadBalancerService(ci *operatorv1.IngressController, deploymentRef metav1.OwnerReference, infraConfig *configv1.Infrastructure) (*corev1.Service, error) {
if ci.Status.EndpointPublishingStrategy.Type != operatorv1.LoadBalancerServiceStrategyType {
return nil, nil
}
service := manifests.LoadBalancerService()
name := controller.LoadBalancerServiceName(ci)
service.Namespace = name.Namespace
service.Name = name.Name
if service.Labels == nil {
service.Labels = map[string]string{}
}
service.Labels["router"] = name.Name
service.Labels[manifests.OwningIngressControllerLabel] = ci.Name
service.Spec.Selector = controller.IngressControllerDeploymentPodSelector(ci).MatchLabels
isInternal := ci.Status.EndpointPublishingStrategy.LoadBalancer == nil || ci.Status.EndpointPublishingStrategy.LoadBalancer.Scope == operatorv1.InternalLoadBalancer
if service.Annotations == nil {
service.Annotations = map[string]string{}
}
if infraConfig.Status.Platform == configv1.AWSPlatformType {
service.Annotations[awsLBProxyProtocolAnnotation] = "*"
// Set the load balancer for AWS to be as aggressive as Azure (2 fail @ 5s interval, 2 healthy)
service.Annotations["service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval"] = "5"
service.Annotations["service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout"] = "4"
service.Annotations["service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold"] = "2"
service.Annotations["service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold"] = "2"
}
// Azure load balancers are not customizable and are set to (2 fail @ 5s interval, 2 healthy)
// GCP load balancers are not customizable and are set to (3 fail @ 8s interval, 1 healthy)
if infraConfig.Status.Platform == configv1.IBMCloudPlatformType {
service.Annotations[iksLBProxyProtocolAnnotations] = "public"
}
if isInternal {
annotation := InternalLBAnnotations[infraConfig.Status.Platform]
for name, value := range annotation {
service.Annotations[name] = value
}
}
service.SetOwnerReferences([]metav1.OwnerReference{deploymentRef})
service.Finalizers = []string{manifests.LoadBalancerServiceFinalizer}
return service, nil
}
// currentLoadBalancerService returns any existing LB service for the
// ingresscontroller.
func (r *reconciler) currentLoadBalancerService(ci *operatorv1.IngressController) (*corev1.Service, error) {
service := &corev1.Service{}
if err := r.client.Get(context.TODO(), controller.LoadBalancerServiceName(ci), service); err != nil {
if errors.IsNotFound(err) {
return nil, nil
}
return nil, err
}
return service, nil
}
// finalizeLoadBalancerService removes the "ingress.openshift.io/operator" finalizer
// from the load balancer service of ci. This was for helping with DNS cleanup, but
// that's no longer necessary. We just need to clear the finalizer which might exist
// on existing resources.
// TODO: How can we delete this code?
func (r *reconciler) finalizeLoadBalancerService(ci *operatorv1.IngressController) (bool, error) {
service, err := r.currentLoadBalancerService(ci)
if err != nil {
return false, err
}
if service == nil {
return false, nil
}
// Mutate a copy to avoid assuming we know where the current one came from
// (i.e. it could have been from a cache).
updated := service.DeepCopy()
if slice.ContainsString(updated.Finalizers, manifests.LoadBalancerServiceFinalizer) {
updated.Finalizers = slice.RemoveString(updated.Finalizers, manifests.LoadBalancerServiceFinalizer)
if err := r.client.Update(context.TODO(), updated); err != nil {
return true, fmt.Errorf("failed to remove finalizer from service %s/%s for ingress %s/%s: %v",
service.Namespace, service.Name, ci.Namespace, ci.Name, err)
}
}
log.Info("finalized load balancer service for ingress", "namespace", ci.Namespace, "name", ci.Name)
return true, nil
}