Commit 2c879de1 authored by Geoff Simmons's avatar Geoff Simmons

Update Ingress status.loadBalancer after successful sync.

The addresses for this array are taken from the public names and/or
IPs in the spec for Service(s) that expose the Ingress. These are
identified as:

- in the same namespace as the admin Service
- have the label viking.uplex.de/svc=public
- have the same selectors as the admin Service
- type is one of ClusterIP, NodePort or LoadBalancer

The label viking.uplex.de/svc is only required if the Ingress status
update is required. For example to use a tool like ArgoCD, or if
the cloud provider requires it.

Set the label in the Service template for the viking-service chart.
parent cd21126b
......@@ -11,6 +11,7 @@ metadata:
helm.sh/chart: {{ template "viking-service.chart" . }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/instance: {{ .Release.Name }}
viking.uplex.de/svc: public
name: {{ template "viking-service.fullname" . }}
spec:
{{- with .Values.vikingService.service.clusterIP }}
......
......@@ -33,6 +33,7 @@ package controller
import (
"encoding/base64"
"fmt"
"reflect"
"strconv"
vcr_v1alpha1 "code.uplex.de/uplex-varnish/k8s-ingress/pkg/apis/varnishingress/v1alpha1"
......@@ -59,6 +60,18 @@ const (
defACLfailStatus = uint16(403)
defDNSRetryDelay = "30s"
defMax2ndTTL = "5m"
vikingPubSvcKey = vikingLabelPfx + "svc"
vikingPubSvcVal = "public"
)
var (
vikingPubSvcSet = labels.Set(map[string]string{
vikingPubSvcKey: vikingPubSvcVal,
})
// Selector for use in List() operations to find Services that
// expose the public Ingress ports. To get address for use in
// the Ingress status.loadBalancer field.
vikingPubSvcSelector = labels.SelectorFromSet(vikingPubSvcSet)
)
func (worker *NamespaceWorker) filterVarnishIngSvcs(
......@@ -898,6 +911,102 @@ func (worker *NamespaceWorker) ings2OffloaderSpec(
return offldrSpec, nil
}
// Update the status.loadBalancer field of each suuccessfully synced
// Ingress, using network addresses of the Services that expose the
// http and https ports.
func (worker *NamespaceWorker) updateIngStatus(
admSvc *api_v1.Service, ings []*extensions.Ingress,
) update.Status {
svcs, err := worker.svc.List(vikingPubSvcSelector)
if err != nil {
if errors.IsNotFound(err) {
worker.log.Warnf("No Service found in namespace %s "+
"with label %s=%s, will not update "+
"status.loadBalancer for Ingress(es): %v",
worker.namespace, vikingPubSvcKey,
vikingPubSvcVal, ings)
return update.MakeSuccess("")
}
return update.MakeFatal(
"Error searching for Services with label %s=%s in "+
"namespace %s, cannot update "+
"status.loadBalancer for Ingress(es) %v: %v",
vikingPubSvcKey, vikingPubSvcVal, worker.namespace,
ings, err)
}
if len(svcs) == 0 {
worker.log.Warnf("No Service found in namespace %s "+
"with label %s=%s, will not update "+
"status.loadBalancer for Ingress(es): %v",
worker.namespace, vikingPubSvcKey, vikingPubSvcVal,
ings)
return update.MakeSuccess("")
}
ips := map[string]struct{}{}
hosts := map[string]struct{}{}
for _, svc := range svcs {
if !reflect.DeepEqual(svc.Spec.Selector, admSvc.Spec.Selector) {
continue
}
if svc.Spec.Type != api_v1.ServiceTypeClusterIP &&
svc.Spec.Type != api_v1.ServiceTypeNodePort &&
svc.Spec.Type != api_v1.ServiceTypeLoadBalancer {
worker.log.Warnf("Service %s/%s has label %s=%s but "+
"type %s, not adding to status.loadBalancer "+
"for Ingress(es): %v", svc.Namespace, svc.Name,
vikingPubSvcKey, vikingPubSvcVal, svc.Spec.Type,
ings)
continue
}
if svc.Spec.ExternalName != "" {
hosts[svc.Spec.ExternalName] = struct{}{}
}
if svc.Spec.LoadBalancerIP != "" {
ips[svc.Spec.LoadBalancerIP] = struct{}{}
}
for _, extIP := range svc.Spec.ExternalIPs {
ips[extIP] = struct{}{}
}
if svc.Spec.ClusterIP != "" {
ips[svc.Spec.ClusterIP] = struct{}{}
}
}
if len(ips)+len(hosts) == 0 {
worker.log.Warnf("No public addresses found for Services in "+
"namespace %s with label %s=%s, will not update "+
"status.loadBalancer for Ingress(es): %v",
worker.namespace, vikingPubSvcKey, vikingPubSvcVal,
ings)
return update.MakeSuccess("")
}
lb := make([]api_v1.LoadBalancerIngress, len(ips)+len(hosts))
n := 0
for host := range hosts {
lb[n].Hostname = host
n++
}
for ip := range ips {
lb[n].IP = ip
n++
}
for _, ing := range ings {
ing.Status.LoadBalancer.Ingress =
make([]api_v1.LoadBalancerIngress, len(ips)+len(hosts))
copy(ing.Status.LoadBalancer.Ingress, lb)
ingClient := worker.client.ExtensionsV1beta1().
Ingresses(ing.Namespace)
if _, err := ingClient.UpdateStatus(ing); err != nil {
return update.MakeFatal(
"Cannot update status for Ingress %s/%s: %v",
ing.Namespace, ing.Name, err)
}
}
return update.MakeSuccess("")
}
func (worker *NamespaceWorker) addOrUpdateIng(
ing *extensions.Ingress,
) update.Status {
......@@ -1087,7 +1196,7 @@ func (worker *NamespaceWorker) addOrUpdateIng(
"vcfgMetaData=%+v bcfgMetaData=%+v: %+v", svcKey,
ingsMeta, vcfgMeta, bcfgMeta, vclSpec)
}
return update.MakeSuccess("")
return worker.updateIngStatus(svc, ings)
}
// We only handle Ingresses with the class annotation with the value
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment