diff --git a/pkg/cloudprovider/providers/azure/azure_loadbalancer.go b/pkg/cloudprovider/providers/azure/azure_loadbalancer.go index 0da751c322d..1384cb7bf5a 100644 --- a/pkg/cloudprovider/providers/azure/azure_loadbalancer.go +++ b/pkg/cloudprovider/providers/azure/azure_loadbalancer.go @@ -32,37 +32,57 @@ import ( "k8s.io/apimachinery/pkg/types" ) +// ServiceAnnotationLoadBalancerInternal is the annotation used on the service +const ServiceAnnotationLoadBalancerInternal = "service.beta.kubernetes.io/azure-load-balancer-internal" + // GetLoadBalancer returns whether the specified load balancer exists, and // if so, what its status is. func (az *Cloud) GetLoadBalancer(clusterName string, service *v1.Service) (status *v1.LoadBalancerStatus, exists bool, err error) { - lbName := getLoadBalancerName(clusterName) + isInternal := isLoadBalancerInternal(service) + lbName := getLoadBalancerName(clusterName, isInternal) serviceName := getServiceName(service) - pipName, err := az.getPublicIPName(clusterName, service) - if err != nil { - return nil, false, err - } - - _, existsLb, err := az.getAzureLoadBalancer(lbName) + lb, existsLb, err := az.getAzureLoadBalancer(lbName) if err != nil { return nil, false, err } if !existsLb { - glog.V(5).Infof("get(%s): lb(%s) - doesn't exist", serviceName, pipName) + glog.V(5).Infof("get(%s): lb(%s) - doesn't exist", serviceName, lbName) return nil, false, nil } - pip, existsPip, err := az.getPublicIPAddress(pipName) - if err != nil { - return nil, false, err + var lbIP *string + + if isInternal { + lbFrontendIPConfigName := getFrontendIPConfigName(service) + for _, ipConfiguration := range *lb.FrontendIPConfigurations { + if lbFrontendIPConfigName == *ipConfiguration.Name { + lbIP = ipConfiguration.PrivateIPAddress + break + } + } + } else { + // TODO: Consider also read address from lb's FrontendIPConfigurations + pipName, err := az.getPublicIPName(clusterName, service) + if err != nil { + return nil, false, err + } + pip, existsPip, err := az.getPublicIPAddress(pipName) + if err != nil { + return nil, false, err + } + if existsPip { + lbIP = pip.IPAddress + } } - if !existsPip { - glog.V(5).Infof("get(%s): pip(%s) - doesn't exist", serviceName, pipName) + + if lbIP == nil { + glog.V(5).Infof("get(%s): lb(%s) - IP doesn't exist", serviceName, lbName) return nil, false, nil } return &v1.LoadBalancerStatus{ - Ingress: []v1.LoadBalancerIngress{{IP: *pip.IPAddress}}, + Ingress: []v1.LoadBalancerIngress{{IP: *lbIP}}, }, true, nil } @@ -93,10 +113,11 @@ func (az *Cloud) getPublicIPName(clusterName string, service *v1.Service) (strin // EnsureLoadBalancer creates a new load balancer 'name', or updates the existing one. Returns the status of the balancer func (az *Cloud) EnsureLoadBalancer(clusterName string, service *v1.Service, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) { - lbName := getLoadBalancerName(clusterName) + isInternal := isLoadBalancerInternal(service) + lbName := getLoadBalancerName(clusterName, isInternal) pipName, err := az.getPublicIPName(clusterName, service) if err != nil { - return nil, err + return nil, false, err } serviceName := getServiceName(service) glog.V(2).Infof("ensure(%s): START clusterName=%q lbName=%q", serviceName, clusterName, lbName) @@ -190,7 +211,8 @@ func (az *Cloud) UpdateLoadBalancer(clusterName string, service *v1.Service, nod // have multiple underlying components, meaning a Get could say that the LB // doesn't exist even if some part of it is still laying around. func (az *Cloud) EnsureLoadBalancerDeleted(clusterName string, service *v1.Service) error { - lbName := getLoadBalancerName(clusterName) + isInternal := isLoadBalancerInternal(service) + lbName := getLoadBalancerName(clusterName, isInternal) serviceName := getServiceName(service) pipName, err := az.getPublicIPName(clusterName, service) @@ -307,7 +329,8 @@ func (az *Cloud) ensurePublicIPDeleted(serviceName, pipName string) error { // This also reconciles the Service's Ports with the LoadBalancer config. // This entails adding rules/probes for expected Ports and removing stale rules/ports. func (az *Cloud) reconcileLoadBalancer(lb network.LoadBalancer, pip *network.PublicIPAddress, clusterName string, service *v1.Service, nodes []*v1.Node) (network.LoadBalancer, bool, error) { - lbName := getLoadBalancerName(clusterName) + isInternal := isLoadBalancerInternal(service) + lbName := getLoadBalancerName(clusterName, isInternal) serviceName := getServiceName(service) lbFrontendIPConfigName := getFrontendIPConfigName(service) lbFrontendIPConfigID := az.getFrontendIPConfigID(lbName, lbFrontendIPConfigName) @@ -713,3 +736,12 @@ func (az *Cloud) ensureHostInPool(serviceName string, nodeName types.NodeName, b } return nil } + +// Check if service requests an internal load balancer. +func isLoadBalancerInternal(service *v1.Service) bool { + if l, ok := service.Annotations[ServiceAnnotationLoadBalancerInternal]; ok { + return l == "true" + } + + return false +} diff --git a/pkg/cloudprovider/providers/azure/azure_util.go b/pkg/cloudprovider/providers/azure/azure_util.go index d134bf9169b..0054e74d23f 100644 --- a/pkg/cloudprovider/providers/azure/azure_util.go +++ b/pkg/cloudprovider/providers/azure/azure_util.go @@ -160,7 +160,14 @@ func getPrimaryIPConfig(nic network.Interface) (*network.InterfaceIPConfiguratio return nil, fmt.Errorf("failed to determine the determine primary ipconfig. nicname=%q", *nic.Name) } -func getLoadBalancerName(clusterName string) string { +// For a load balancer, all frontend ip should reference either a subnet or publicIpAddress. +// Thus Azure do not allow mixed type (public and internal) load balancer. +// So we'd have a separate name for internal load balancer. +func getLoadBalancerName(clusterName string, isInternal bool) string { + if isInternal { + return fmt.Sprintf("%s-internal", clusterName) + } + return clusterName }