in pkg/openstack/loadbalancer.go [1025:1135]
func (lbaas *LbaasV2) getServiceAddress(clusterName string, service *corev1.Service, lb *loadbalancers.LoadBalancer, svcConf *serviceConfig) (string, error) {
if svcConf.internal {
return lb.VipAddress, nil
}
var floatIP *floatingips.FloatingIP
serviceName := fmt.Sprintf("%s/%s", service.Namespace, service.Name)
// first attempt: fetch floating IP attached to load balancer's VIP port
portID := lb.VipPortID
floatIP, err := openstackutil.GetFloatingIPByPortID(lbaas.network, portID)
if err != nil {
return "", fmt.Errorf("failed when getting floating IP for port %s: %v", portID, err)
}
klog.V(4).Infof("Found floating ip %v by loadbalancer port id %q", floatIP, portID)
// second attempt: fetch floating IP specified in service Spec.LoadBalancerIP
// if found, associate floating IP with loadbalancer's VIP port
loadBalancerIP := service.Spec.LoadBalancerIP
if floatIP == nil && loadBalancerIP != "" {
opts := floatingips.ListOpts{
FloatingIP: loadBalancerIP,
}
existingIPs, err := openstackutil.GetFloatingIPs(lbaas.network, opts)
if err != nil {
return "", fmt.Errorf("failed when trying to get existing floating IP %s, error: %v", loadBalancerIP, err)
}
klog.V(4).Infof("Found floating ips %v by loadbalancer ip %q", existingIPs, loadBalancerIP)
if len(existingIPs) > 0 {
floatingip := existingIPs[0]
if len(floatingip.PortID) == 0 {
floatUpdateOpts := floatingips.UpdateOpts{
PortID: &portID,
}
klog.V(4).Infof("Attaching floating ip %q to loadbalancer port %q", floatingip.FloatingIP, portID)
mc := metrics.NewMetricContext("floating_ip", "update")
floatIP, err = floatingips.Update(lbaas.network, floatingip.ID, floatUpdateOpts).Extract()
if mc.ObserveRequest(err) != nil {
return "", fmt.Errorf("error updating LB floatingip %+v: %v", floatUpdateOpts, err)
}
} else {
return "", fmt.Errorf("floating IP %s is not available", loadBalancerIP)
}
}
}
// third attempt: create a new floating IP
if floatIP == nil {
if svcConf.lbPublicNetworkID != "" {
klog.V(2).Infof("Creating floating IP %s for loadbalancer %s", loadBalancerIP, lb.ID)
floatIPOpts := floatingips.CreateOpts{
FloatingNetworkID: svcConf.lbPublicNetworkID,
PortID: portID,
Description: fmt.Sprintf("Floating IP for Kubernetes external service %s from cluster %s", serviceName, clusterName),
}
if loadBalancerIP == "" && svcConf.lbPublicSubnetSpec.MatcherConfigured() {
var foundSubnet subnets.Subnet
// tweak list options for tags
foundSubnets, err := svcConf.lbPublicSubnetSpec.ListSubnetsForNetwork(lbaas, svcConf.lbPublicNetworkID)
if err != nil {
return "", err
}
if len(foundSubnets) == 0 {
return "", fmt.Errorf("no subnet matching %s found for network %s",
svcConf.lbPublicSubnetSpec, svcConf.lbPublicNetworkID)
}
// try to create floating IP in matching subnets (tags already filtered by list options)
klog.V(4).Infof("found %d subnets matching %s for network %s", len(foundSubnets),
svcConf.lbPublicSubnetSpec, svcConf.lbPublicNetworkID)
for _, subnet := range foundSubnets {
floatIPOpts.SubnetID = subnet.ID
floatIP, err = lbaas.createFloatingIP(fmt.Sprintf("Trying subnet %s for creating", subnet.Name), floatIPOpts)
if err == nil {
foundSubnet = subnet
break
}
klog.V(2).Infof("cannot use subnet %s: %s", subnet.Name, err)
}
if err != nil {
return "", fmt.Errorf("no free subnet matching %q found for network %s (last error %s)",
svcConf.lbPublicSubnetSpec, svcConf.lbPublicNetworkID, err)
} else {
klog.V(2).Infof("Successfully created floating IP %s for loadbalancer %s on subnet %s(%s)", floatIP.FloatingIP, lb.ID, foundSubnet.Name, foundSubnet.ID)
}
} else {
if svcConf.lbPublicSubnetSpec != nil {
floatIPOpts.SubnetID = svcConf.lbPublicSubnetSpec.subnetID
}
floatIPOpts.FloatingIP = loadBalancerIP
floatIP, err = lbaas.createFloatingIP("Creating", floatIPOpts)
if err != nil {
return "", err
}
klog.V(2).Infof("Successfully created floating IP %s for loadbalancer %s", floatIP.FloatingIP, lb.ID)
}
} else {
klog.Warningf("Floating network configuration not provided for Service %s, forcing to ensure an internal load balancer service", serviceName)
}
}
if floatIP != nil {
return floatIP.FloatingIP, nil
}
return lb.VipAddress, nil
}