Sync Hostname and IP address from service to ingress status (#7464)
* Change statusSync.runningAddresses() return type Previously, this method returning a string slice containing the resolved IP addresses / FQDNs to sync onto the Ingress. It was then converted just before use into a slice of LoadBalancerIngresses. This commit changes this logic so that this method generates LoadBalancerIngress objects directly, and returns these. This has two main benefits: - Future work in syncing _both_ hostname and IP, or any other fields that may be used in future (eg Ports), is now supported. - There is less need to rely on net.ParseIP() to determine if a value is an IP address or Hostname, as this can be correctly assigned at generation time based on where each value came from. * Sync both IP and Hostname to Ingress Status Previously, if the IP address was set on a PublishService's LoadBalancerIngress entries, only that would be synced. Hostname was only synced as a fallback when the IP address was missing. Now, both fields are checked independantly and both are synced if present.
This commit is contained in:
parent
33061b8cdf
commit
3c86f838d4
2 changed files with 101 additions and 68 deletions
|
|
@ -26,7 +26,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
pool "gopkg.in/go-playground/pool.v3"
|
||||
|
|
@ -143,7 +142,7 @@ func (s *statusSync) sync(key interface{}) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.updateStatus(sliceToStatus(addrs))
|
||||
s.updateStatus(standardizeLoadBalancerIngresses(addrs))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -162,13 +161,25 @@ func NewStatusSyncer(config Config) Syncer {
|
|||
return st
|
||||
}
|
||||
|
||||
func nameOrIPToLoadBalancerIngress(nameOrIP string) apiv1.LoadBalancerIngress {
|
||||
if net.ParseIP(nameOrIP) != nil {
|
||||
return apiv1.LoadBalancerIngress{IP: nameOrIP}
|
||||
}
|
||||
|
||||
return apiv1.LoadBalancerIngress{Hostname: nameOrIP}
|
||||
}
|
||||
|
||||
// runningAddresses returns a list of IP addresses and/or FQDN where the
|
||||
// ingress controller is currently running
|
||||
func (s *statusSync) runningAddresses() ([]string, error) {
|
||||
func (s *statusSync) runningAddresses() ([]apiv1.LoadBalancerIngress, error) {
|
||||
if s.PublishStatusAddress != "" {
|
||||
re := regexp.MustCompile(`,\s*`)
|
||||
multipleAddrs := re.Split(s.PublishStatusAddress, -1)
|
||||
return multipleAddrs, nil
|
||||
addrs := make([]apiv1.LoadBalancerIngress, len(multipleAddrs))
|
||||
for i, addr := range multipleAddrs {
|
||||
addrs[i] = nameOrIPToLoadBalancerIngress(addr)
|
||||
}
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
if s.PublishService != "" {
|
||||
|
|
@ -183,7 +194,7 @@ func (s *statusSync) runningAddresses() ([]string, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
addrs := make([]string, 0)
|
||||
addrs := make([]apiv1.LoadBalancerIngress, 0)
|
||||
for i := range pods.Items {
|
||||
pod := pods.Items[i]
|
||||
// only Running pods are valid
|
||||
|
|
@ -206,8 +217,8 @@ func (s *statusSync) runningAddresses() ([]string, error) {
|
|||
}
|
||||
|
||||
name := k8s.GetNodeIPOrName(s.Client, pod.Spec.NodeName, s.UseNodeInternalIP)
|
||||
if !stringInSlice(name, addrs) {
|
||||
addrs = append(addrs, name)
|
||||
if !stringInIngresses(name, addrs) {
|
||||
addrs = append(addrs, nameOrIPToLoadBalancerIngress(name))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -225,17 +236,9 @@ func (s *statusSync) isRunningMultiplePods() bool {
|
|||
return len(pods.Items) > 1
|
||||
}
|
||||
|
||||
// sliceToStatus converts a slice of IP and/or hostnames to LoadBalancerIngress
|
||||
func sliceToStatus(endpoints []string) []apiv1.LoadBalancerIngress {
|
||||
lbi := []apiv1.LoadBalancerIngress{}
|
||||
for _, ep := range endpoints {
|
||||
if net.ParseIP(ep) == nil {
|
||||
lbi = append(lbi, apiv1.LoadBalancerIngress{Hostname: ep})
|
||||
} else {
|
||||
lbi = append(lbi, apiv1.LoadBalancerIngress{IP: ep})
|
||||
}
|
||||
}
|
||||
|
||||
// standardizeLoadBalancerIngresses sorts the list of loadbalancer by
|
||||
// IP
|
||||
func standardizeLoadBalancerIngresses(lbi []apiv1.LoadBalancerIngress) []apiv1.LoadBalancerIngress {
|
||||
sort.SliceStable(lbi, func(a, b int) bool {
|
||||
return lbi[a].IP < lbi[b].IP
|
||||
})
|
||||
|
|
@ -321,7 +324,7 @@ func ingressSliceEqual(lhs, rhs []apiv1.LoadBalancerIngress) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func statusAddressFromService(service string, kubeClient clientset.Interface) ([]string, error) {
|
||||
func statusAddressFromService(service string, kubeClient clientset.Interface) ([]apiv1.LoadBalancerIngress, error) {
|
||||
ns, name, _ := k8s.ParseNameNS(service)
|
||||
svc, err := kubeClient.CoreV1().Services(ns).Get(context.TODO(), name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
|
|
@ -330,39 +333,50 @@ func statusAddressFromService(service string, kubeClient clientset.Interface) ([
|
|||
|
||||
switch svc.Spec.Type {
|
||||
case apiv1.ServiceTypeExternalName:
|
||||
return []string{svc.Spec.ExternalName}, nil
|
||||
return []apiv1.LoadBalancerIngress{{
|
||||
Hostname: svc.Spec.ExternalName,
|
||||
}}, nil
|
||||
case apiv1.ServiceTypeClusterIP:
|
||||
return []string{svc.Spec.ClusterIP}, nil
|
||||
return []apiv1.LoadBalancerIngress{{
|
||||
IP: svc.Spec.ClusterIP,
|
||||
}}, nil
|
||||
case apiv1.ServiceTypeNodePort:
|
||||
addresses := sets.NewString()
|
||||
if svc.Spec.ExternalIPs != nil {
|
||||
addresses.Insert(svc.Spec.ExternalIPs...)
|
||||
} else {
|
||||
addresses.Insert(svc.Spec.ClusterIP)
|
||||
if svc.Spec.ExternalIPs == nil {
|
||||
return []apiv1.LoadBalancerIngress{{
|
||||
IP: svc.Spec.ClusterIP,
|
||||
}}, nil
|
||||
}
|
||||
return addresses.List(), nil
|
||||
addrs := make([]apiv1.LoadBalancerIngress, len(svc.Spec.ExternalIPs))
|
||||
for i, ip := range svc.Spec.ExternalIPs {
|
||||
addrs[i] = apiv1.LoadBalancerIngress{IP: ip}
|
||||
}
|
||||
return addrs, nil
|
||||
case apiv1.ServiceTypeLoadBalancer:
|
||||
addresses := sets.NewString()
|
||||
for _, ip := range svc.Status.LoadBalancer.Ingress {
|
||||
if ip.IP == "" {
|
||||
addresses.Insert(ip.Hostname)
|
||||
} else {
|
||||
addresses.Insert(ip.IP)
|
||||
addrs := make([]apiv1.LoadBalancerIngress, len(svc.Status.LoadBalancer.Ingress))
|
||||
for i, ingress := range svc.Status.LoadBalancer.Ingress {
|
||||
addrs[i] = apiv1.LoadBalancerIngress{}
|
||||
if ingress.Hostname != "" {
|
||||
addrs[i].Hostname = ingress.Hostname
|
||||
}
|
||||
if ingress.IP != "" {
|
||||
addrs[i].IP = ingress.IP
|
||||
}
|
||||
}
|
||||
|
||||
addresses.Insert(svc.Spec.ExternalIPs...)
|
||||
|
||||
return addresses.List(), nil
|
||||
for _, ip := range svc.Spec.ExternalIPs {
|
||||
if !stringInIngresses(ip, addrs) {
|
||||
addrs = append(addrs, apiv1.LoadBalancerIngress{IP: ip})
|
||||
}
|
||||
}
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unable to extract IP address/es from service %v", service)
|
||||
}
|
||||
|
||||
// stringInSlice returns true if s is in list
|
||||
func stringInSlice(s string, list []string) bool {
|
||||
func stringInIngresses(s string, list []apiv1.LoadBalancerIngress) bool {
|
||||
for _, v := range list {
|
||||
if v == s {
|
||||
if v.IP == s || v.Hostname == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue