in cloudstack_loadbalancer.go [76:200]
func (cs *CSCloud) EnsureLoadBalancer(ctx context.Context, clusterName string, service *corev1.Service, nodes []*corev1.Node) (status *corev1.LoadBalancerStatus, err error) {
klog.V(4).Infof("EnsureLoadBalancer(%v, %v, %v, %v, %v, %v)", clusterName, service.Namespace, service.Name, service.Spec.LoadBalancerIP, service.Spec.Ports, nodes)
if len(service.Spec.Ports) == 0 {
return nil, fmt.Errorf("requested load balancer with no ports")
}
// Get the load balancer details and existing rules.
lb, err := cs.getLoadBalancer(service)
if err != nil {
return nil, err
}
// Set the load balancer algorithm.
switch service.Spec.SessionAffinity {
case corev1.ServiceAffinityNone:
lb.algorithm = "roundrobin"
case corev1.ServiceAffinityClientIP:
lb.algorithm = "source"
default:
return nil, fmt.Errorf("unsupported load balancer affinity: %v", service.Spec.SessionAffinity)
}
// Verify that all the hosts belong to the same network, and retrieve their ID's.
lb.hostIDs, lb.networkID, err = cs.verifyHosts(nodes)
if err != nil {
return nil, err
}
if !lb.hasLoadBalancerIP() {
// Create or retrieve the load balancer IP.
if err := lb.getLoadBalancerIP(service.Spec.LoadBalancerIP); err != nil {
return nil, err
}
if lb.ipAddr != "" && lb.ipAddr != service.Spec.LoadBalancerIP {
defer func(lb *loadBalancer) {
if err != nil {
if err := lb.releaseLoadBalancerIP(); err != nil {
klog.Errorf(err.Error())
}
}
}(lb)
}
}
klog.V(4).Infof("Load balancer %v is associated with IP %v", lb.name, lb.ipAddr)
for _, port := range service.Spec.Ports {
// Construct the protocol name first, we need it a few times
protocol := ProtocolFromServicePort(port, service.Annotations)
if protocol == LoadBalancerProtocolInvalid {
return nil, fmt.Errorf("unsupported load balancer protocol: %v", port.Protocol)
}
// All ports have their own load balancer rule, so add the port to lbName to keep the names unique.
lbRuleName := fmt.Sprintf("%s-%s-%d", lb.name, protocol, port.Port)
// If the load balancer rule exists and is up-to-date, we move on to the next rule.
lbRule, needsUpdate, err := lb.checkLoadBalancerRule(lbRuleName, port, protocol)
if err != nil {
return nil, err
}
if lbRule != nil {
if needsUpdate {
klog.V(4).Infof("Updating load balancer rule: %v", lbRuleName)
if err := lb.updateLoadBalancerRule(lbRuleName, protocol); err != nil {
return nil, err
}
// Delete the rule from the map, to prevent it being deleted.
delete(lb.rules, lbRuleName)
} else {
klog.V(4).Infof("Load balancer rule %v is up-to-date", lbRuleName)
// Delete the rule from the map, to prevent it being deleted.
delete(lb.rules, lbRuleName)
}
} else {
klog.V(4).Infof("Creating load balancer rule: %v", lbRuleName)
lbRule, err = lb.createLoadBalancerRule(lbRuleName, port, protocol)
if err != nil {
return nil, err
}
klog.V(4).Infof("Assigning hosts (%v) to load balancer rule: %v", lb.hostIDs, lbRuleName)
if err = lb.assignHostsToRule(lbRule, lb.hostIDs); err != nil {
return nil, err
}
}
if lbRule != nil {
klog.V(4).Infof("Creating firewall rules for load balancer rule: %v (%v:%v:%v)", lbRuleName, protocol, lbRule.Publicip, port.Port)
if _, err := lb.updateFirewallRule(lbRule.Publicipid, int(port.Port), protocol, service.Spec.LoadBalancerSourceRanges); err != nil {
return nil, err
}
}
}
// Cleanup any rules that are now still in the rules map, as they are no longer needed.
for _, lbRule := range lb.rules {
protocol := ProtocolFromLoadBalancer(lbRule.Protocol)
if protocol == LoadBalancerProtocolInvalid {
return nil, fmt.Errorf("Error parsing protocol %v: %v", lbRule.Protocol, err)
}
port, err := strconv.ParseInt(lbRule.Publicport, 10, 32)
if err != nil {
return nil, fmt.Errorf("Error parsing port %s: %v", lbRule.Publicport, err)
}
klog.V(4).Infof("Deleting firewall rules associated with load balancer rule: %v (%v:%v:%v)", lbRule.Name, protocol, lbRule.Publicip, port)
if _, err := lb.deleteFirewallRule(lbRule.Publicipid, int(port), protocol); err != nil {
return nil, err
}
klog.V(4).Infof("Deleting obsolete load balancer rule: %v", lbRule.Name)
if err := lb.deleteLoadBalancerRule(lbRule); err != nil {
return nil, err
}
}
status = &corev1.LoadBalancerStatus{}
status.Ingress = []corev1.LoadBalancerIngress{{IP: lb.ipAddr}}
return status, nil
}