2016-02-22 00:13:08 +00:00
|
|
|
/*
|
|
|
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2016-04-14 23:42:37 +00:00
|
|
|
"encoding/json"
|
2016-02-22 00:13:08 +00:00
|
|
|
"fmt"
|
2016-03-22 18:01:04 +00:00
|
|
|
"reflect"
|
2016-03-15 02:29:13 +00:00
|
|
|
"sort"
|
2016-03-19 23:29:29 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2016-02-22 00:13:08 +00:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/golang/glog"
|
|
|
|
|
|
|
|
"k8s.io/kubernetes/pkg/api"
|
2016-04-14 23:42:37 +00:00
|
|
|
podutil "k8s.io/kubernetes/pkg/api/pod"
|
2016-02-22 00:13:08 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
|
|
|
"k8s.io/kubernetes/pkg/client/cache"
|
2016-03-30 23:12:37 +00:00
|
|
|
"k8s.io/kubernetes/pkg/client/record"
|
2016-02-22 00:13:08 +00:00
|
|
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
|
|
|
"k8s.io/kubernetes/pkg/controller/framework"
|
2016-04-14 23:42:37 +00:00
|
|
|
"k8s.io/kubernetes/pkg/labels"
|
2016-02-22 00:13:08 +00:00
|
|
|
"k8s.io/kubernetes/pkg/runtime"
|
2016-03-15 15:31:39 +00:00
|
|
|
"k8s.io/kubernetes/pkg/util/intstr"
|
2016-02-22 00:13:08 +00:00
|
|
|
"k8s.io/kubernetes/pkg/watch"
|
|
|
|
|
2016-03-28 01:12:15 +00:00
|
|
|
"k8s.io/contrib/ingress/controllers/nginx/nginx"
|
2016-02-22 00:13:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2016-04-16 22:36:45 +00:00
|
|
|
defUpstreamName = "upstream-default-backend"
|
|
|
|
defServerName = "_"
|
|
|
|
namedPortAnnotation = "kubernetes.io/ingress-named-ports"
|
|
|
|
podStoreSyncedPollPeriod = 1 * time.Second
|
2016-04-28 23:04:41 +00:00
|
|
|
rootLocation = "/"
|
2016-02-22 00:13:08 +00:00
|
|
|
)
|
|
|
|
|
2016-03-22 18:01:04 +00:00
|
|
|
var (
|
|
|
|
keyFunc = framework.DeletionHandlingMetaNamespaceKeyFunc
|
|
|
|
)
|
|
|
|
|
2016-04-14 23:42:37 +00:00
|
|
|
type namedPortMapping map[string]string
|
|
|
|
|
2016-05-02 02:34:00 +00:00
|
|
|
// getPort returns the port defined in a named port
|
2016-04-14 23:42:37 +00:00
|
|
|
func (npm namedPortMapping) getPort(name string) (string, bool) {
|
2016-05-02 02:34:00 +00:00
|
|
|
val, ok := npm.getPortMappings()[name]
|
2016-04-14 23:42:37 +00:00
|
|
|
return val, ok
|
|
|
|
}
|
|
|
|
|
2016-05-02 02:34:00 +00:00
|
|
|
// getPortMappings returns the map containing the
|
|
|
|
// mapping of named port names and the port number
|
|
|
|
func (npm namedPortMapping) getPortMappings() map[string]string {
|
2016-04-14 23:42:37 +00:00
|
|
|
data := npm[namedPortAnnotation]
|
|
|
|
var mapping map[string]string
|
|
|
|
if data == "" {
|
|
|
|
return mapping
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal([]byte(data), &mapping); err != nil {
|
|
|
|
glog.Errorf("unexpected error reading annotations: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return mapping
|
|
|
|
}
|
|
|
|
|
2016-02-22 00:13:08 +00:00
|
|
|
// loadBalancerController watches the kubernetes api and adds/removes services
|
|
|
|
// from the loadbalancer
|
|
|
|
type loadBalancerController struct {
|
2016-03-19 23:29:29 +00:00
|
|
|
client *client.Client
|
|
|
|
ingController *framework.Controller
|
|
|
|
endpController *framework.Controller
|
|
|
|
svcController *framework.Controller
|
|
|
|
ingLister StoreToIngressLister
|
|
|
|
svcLister cache.StoreToServiceLister
|
|
|
|
endpLister cache.StoreToEndpointsLister
|
2016-03-22 16:51:50 +00:00
|
|
|
nginx *nginx.Manager
|
2016-04-06 14:46:06 +00:00
|
|
|
podInfo *podInfo
|
2016-03-19 23:29:29 +00:00
|
|
|
defaultSvc string
|
|
|
|
nxgConfigMap string
|
|
|
|
tcpConfigMap string
|
2016-03-29 23:30:44 +00:00
|
|
|
udpConfigMap string
|
2016-03-19 20:17:58 +00:00
|
|
|
|
2016-03-30 23:12:37 +00:00
|
|
|
recorder record.EventRecorder
|
|
|
|
|
2016-03-22 18:01:04 +00:00
|
|
|
syncQueue *taskQueue
|
2016-04-01 00:29:36 +00:00
|
|
|
|
|
|
|
// taskQueue used to update the status of the Ingress rules.
|
|
|
|
// this avoids a sync execution in the ResourceEventHandlerFuncs
|
|
|
|
ingQueue *taskQueue
|
2016-03-22 18:01:04 +00:00
|
|
|
|
2016-02-22 00:13:08 +00:00
|
|
|
// stopLock is used to enforce only a single call to Stop is active.
|
|
|
|
// Needed because we allow stopping through an http endpoint and
|
|
|
|
// allowing concurrent stoppers leads to stack traces.
|
|
|
|
stopLock sync.Mutex
|
|
|
|
shutdown bool
|
2016-03-19 23:29:29 +00:00
|
|
|
stopCh chan struct{}
|
2016-02-22 00:13:08 +00:00
|
|
|
}
|
|
|
|
|
2016-03-15 02:29:13 +00:00
|
|
|
// newLoadBalancerController creates a controller for nginx loadbalancer
|
2016-03-19 23:29:29 +00:00
|
|
|
func newLoadBalancerController(kubeClient *client.Client, resyncPeriod time.Duration, defaultSvc,
|
2016-04-06 14:46:06 +00:00
|
|
|
namespace, nxgConfigMapName, tcpConfigMapName, udpConfigMapName string, runtimeInfo *podInfo) (*loadBalancerController, error) {
|
2016-03-30 23:12:37 +00:00
|
|
|
|
|
|
|
eventBroadcaster := record.NewBroadcaster()
|
|
|
|
eventBroadcaster.StartLogging(glog.Infof)
|
2016-04-15 14:59:02 +00:00
|
|
|
eventBroadcaster.StartRecordingToSink(kubeClient.Events(namespace))
|
2016-03-30 23:12:37 +00:00
|
|
|
|
2016-02-22 00:13:08 +00:00
|
|
|
lbc := loadBalancerController{
|
2016-03-19 20:17:58 +00:00
|
|
|
client: kubeClient,
|
|
|
|
stopCh: make(chan struct{}),
|
2016-04-06 14:46:06 +00:00
|
|
|
podInfo: runtimeInfo,
|
2016-03-19 23:29:29 +00:00
|
|
|
nginx: nginx.NewManager(kubeClient),
|
2016-03-19 20:17:58 +00:00
|
|
|
nxgConfigMap: nxgConfigMapName,
|
|
|
|
tcpConfigMap: tcpConfigMapName,
|
2016-03-29 23:30:44 +00:00
|
|
|
udpConfigMap: udpConfigMapName,
|
2016-03-19 23:29:29 +00:00
|
|
|
defaultSvc: defaultSvc,
|
2016-04-16 22:36:45 +00:00
|
|
|
recorder: eventBroadcaster.NewRecorder(api.EventSource{
|
|
|
|
Component: "nginx-ingress-controller",
|
|
|
|
}),
|
2016-02-22 00:13:08 +00:00
|
|
|
}
|
|
|
|
|
2016-03-22 18:01:04 +00:00
|
|
|
lbc.syncQueue = NewTaskQueue(lbc.sync)
|
2016-04-01 00:29:36 +00:00
|
|
|
lbc.ingQueue = NewTaskQueue(lbc.updateIngressStatus)
|
2016-03-22 18:01:04 +00:00
|
|
|
|
2016-03-30 23:12:37 +00:00
|
|
|
ingEventHandler := framework.ResourceEventHandlerFuncs{
|
|
|
|
AddFunc: func(obj interface{}) {
|
|
|
|
addIng := obj.(*extensions.Ingress)
|
|
|
|
lbc.recorder.Eventf(addIng, api.EventTypeNormal, "CREATE", fmt.Sprintf("%s/%s", addIng.Namespace, addIng.Name))
|
2016-03-31 17:59:28 +00:00
|
|
|
lbc.ingQueue.enqueue(obj)
|
2016-03-30 23:12:37 +00:00
|
|
|
lbc.syncQueue.enqueue(obj)
|
|
|
|
},
|
|
|
|
DeleteFunc: func(obj interface{}) {
|
|
|
|
upIng := obj.(*extensions.Ingress)
|
|
|
|
lbc.recorder.Eventf(upIng, api.EventTypeNormal, "DELETE", fmt.Sprintf("%s/%s", upIng.Namespace, upIng.Name))
|
|
|
|
lbc.syncQueue.enqueue(obj)
|
|
|
|
},
|
|
|
|
UpdateFunc: func(old, cur interface{}) {
|
|
|
|
if !reflect.DeepEqual(old, cur) {
|
|
|
|
upIng := cur.(*extensions.Ingress)
|
|
|
|
lbc.recorder.Eventf(upIng, api.EventTypeNormal, "UPDATE", fmt.Sprintf("%s/%s", upIng.Namespace, upIng.Name))
|
2016-03-31 17:59:28 +00:00
|
|
|
lbc.ingQueue.enqueue(cur)
|
2016-03-30 23:12:37 +00:00
|
|
|
lbc.syncQueue.enqueue(cur)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2016-03-22 18:01:04 +00:00
|
|
|
eventHandler := framework.ResourceEventHandlerFuncs{
|
|
|
|
AddFunc: func(obj interface{}) {
|
|
|
|
lbc.syncQueue.enqueue(obj)
|
|
|
|
},
|
|
|
|
DeleteFunc: func(obj interface{}) {
|
|
|
|
lbc.syncQueue.enqueue(obj)
|
|
|
|
},
|
|
|
|
UpdateFunc: func(old, cur interface{}) {
|
|
|
|
if !reflect.DeepEqual(old, cur) {
|
|
|
|
lbc.syncQueue.enqueue(cur)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2016-02-22 00:13:08 +00:00
|
|
|
lbc.ingLister.Store, lbc.ingController = framework.NewInformer(
|
|
|
|
&cache.ListWatch{
|
|
|
|
ListFunc: ingressListFunc(lbc.client, namespace),
|
|
|
|
WatchFunc: ingressWatchFunc(lbc.client, namespace),
|
|
|
|
},
|
2016-03-30 23:12:37 +00:00
|
|
|
&extensions.Ingress{}, resyncPeriod, ingEventHandler)
|
2016-02-22 00:13:08 +00:00
|
|
|
|
2016-03-15 02:29:13 +00:00
|
|
|
lbc.endpLister.Store, lbc.endpController = framework.NewInformer(
|
|
|
|
&cache.ListWatch{
|
2016-03-19 23:29:29 +00:00
|
|
|
ListFunc: endpointsListFunc(lbc.client, namespace),
|
|
|
|
WatchFunc: endpointsWatchFunc(lbc.client, namespace),
|
2016-02-22 00:13:08 +00:00
|
|
|
},
|
2016-03-22 18:01:04 +00:00
|
|
|
&api.Endpoints{}, resyncPeriod, eventHandler)
|
2016-02-22 00:13:08 +00:00
|
|
|
|
2016-03-15 02:29:13 +00:00
|
|
|
lbc.svcLister.Store, lbc.svcController = framework.NewInformer(
|
2016-02-22 00:13:08 +00:00
|
|
|
&cache.ListWatch{
|
2016-03-19 23:29:29 +00:00
|
|
|
ListFunc: serviceListFunc(lbc.client, namespace),
|
|
|
|
WatchFunc: serviceWatchFunc(lbc.client, namespace),
|
2016-02-22 00:13:08 +00:00
|
|
|
},
|
2016-04-15 14:59:02 +00:00
|
|
|
&api.Service{}, resyncPeriod, framework.ResourceEventHandlerFuncs{})
|
2016-02-22 00:13:08 +00:00
|
|
|
|
|
|
|
return &lbc, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ingressListFunc(c *client.Client, ns string) func(api.ListOptions) (runtime.Object, error) {
|
|
|
|
return func(opts api.ListOptions) (runtime.Object, error) {
|
|
|
|
return c.Extensions().Ingress(ns).List(opts)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ingressWatchFunc(c *client.Client, ns string) func(options api.ListOptions) (watch.Interface, error) {
|
|
|
|
return func(options api.ListOptions) (watch.Interface, error) {
|
|
|
|
return c.Extensions().Ingress(ns).Watch(options)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-15 02:29:13 +00:00
|
|
|
func serviceListFunc(c *client.Client, ns string) func(api.ListOptions) (runtime.Object, error) {
|
|
|
|
return func(opts api.ListOptions) (runtime.Object, error) {
|
|
|
|
return c.Services(ns).List(opts)
|
2016-02-22 00:13:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-15 02:29:13 +00:00
|
|
|
func serviceWatchFunc(c *client.Client, ns string) func(options api.ListOptions) (watch.Interface, error) {
|
|
|
|
return func(options api.ListOptions) (watch.Interface, error) {
|
|
|
|
return c.Services(ns).Watch(options)
|
2016-02-22 00:13:08 +00:00
|
|
|
}
|
2016-03-15 02:29:13 +00:00
|
|
|
}
|
2016-02-22 00:13:08 +00:00
|
|
|
|
2016-03-15 02:29:13 +00:00
|
|
|
func endpointsListFunc(c *client.Client, ns string) func(api.ListOptions) (runtime.Object, error) {
|
|
|
|
return func(opts api.ListOptions) (runtime.Object, error) {
|
|
|
|
return c.Endpoints(ns).List(opts)
|
2016-02-22 00:13:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-15 02:29:13 +00:00
|
|
|
func endpointsWatchFunc(c *client.Client, ns string) func(options api.ListOptions) (watch.Interface, error) {
|
|
|
|
return func(options api.ListOptions) (watch.Interface, error) {
|
|
|
|
return c.Endpoints(ns).Watch(options)
|
2016-02-22 00:13:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-22 18:01:04 +00:00
|
|
|
func (lbc *loadBalancerController) controllersInSync() bool {
|
|
|
|
return lbc.ingController.HasSynced() && lbc.svcController.HasSynced() && lbc.endpController.HasSynced()
|
|
|
|
}
|
|
|
|
|
2016-03-19 23:29:29 +00:00
|
|
|
func (lbc *loadBalancerController) getConfigMap(ns, name string) (*api.ConfigMap, error) {
|
|
|
|
return lbc.client.ConfigMaps(ns).Get(name)
|
2016-03-19 20:17:58 +00:00
|
|
|
}
|
|
|
|
|
2016-03-19 23:29:29 +00:00
|
|
|
func (lbc *loadBalancerController) getTCPConfigMap(ns, name string) (*api.ConfigMap, error) {
|
|
|
|
return lbc.client.ConfigMaps(ns).Get(name)
|
2016-03-19 20:17:58 +00:00
|
|
|
}
|
|
|
|
|
2016-03-29 23:30:44 +00:00
|
|
|
func (lbc *loadBalancerController) getUDPConfigMap(ns, name string) (*api.ConfigMap, error) {
|
|
|
|
return lbc.client.ConfigMaps(ns).Get(name)
|
|
|
|
}
|
|
|
|
|
2016-04-15 15:29:12 +00:00
|
|
|
// checkSvcForUpdate verifies if one of the running pods for a service contains
|
|
|
|
// named port. If the annotation in the service does not exists or is not equals
|
|
|
|
// to the port mapping obtained from the pod the service must be updated to reflect
|
|
|
|
// the current state
|
2016-05-02 02:34:00 +00:00
|
|
|
func (lbc *loadBalancerController) checkSvcForUpdate(svc *api.Service) (map[string]string, error) {
|
2016-04-15 15:29:12 +00:00
|
|
|
// get the pods associated with the service
|
2016-04-16 22:36:45 +00:00
|
|
|
// TODO: switch this to a watch
|
2016-04-14 23:42:37 +00:00
|
|
|
pods, err := lbc.client.Pods(svc.Namespace).List(api.ListOptions{
|
|
|
|
LabelSelector: labels.Set(svc.Spec.Selector).AsSelector(),
|
|
|
|
})
|
2016-05-02 02:34:00 +00:00
|
|
|
|
|
|
|
namedPorts := map[string]string{}
|
2016-04-14 23:42:37 +00:00
|
|
|
if err != nil {
|
2016-05-02 02:34:00 +00:00
|
|
|
return namedPorts, fmt.Errorf("error searching service pods %v/%v: %v", svc.Namespace, svc.Name, err)
|
2016-04-14 23:42:37 +00:00
|
|
|
}
|
|
|
|
|
2016-04-15 15:29:12 +00:00
|
|
|
if len(pods.Items) == 0 {
|
2016-05-02 02:34:00 +00:00
|
|
|
return namedPorts, nil
|
2016-04-15 15:29:12 +00:00
|
|
|
}
|
2016-04-14 23:42:37 +00:00
|
|
|
|
2016-04-15 15:29:12 +00:00
|
|
|
// we need to check only one pod searching for named ports
|
|
|
|
pod := &pods.Items[0]
|
|
|
|
glog.V(4).Infof("checking pod %v/%v for named port information", pod.Namespace, pod.Name)
|
|
|
|
for i := range svc.Spec.Ports {
|
|
|
|
servicePort := &svc.Spec.Ports[i]
|
2016-04-14 23:42:37 +00:00
|
|
|
|
2016-04-15 15:29:12 +00:00
|
|
|
_, err := strconv.Atoi(servicePort.TargetPort.StrVal)
|
|
|
|
if err != nil {
|
|
|
|
portNum, err := podutil.FindPort(pod, servicePort)
|
2016-04-14 23:42:37 +00:00
|
|
|
if err != nil {
|
2016-04-15 15:29:12 +00:00
|
|
|
glog.V(4).Infof("failed to find port for service %s/%s: %v", svc.Namespace, svc.Name, err)
|
|
|
|
continue
|
|
|
|
}
|
2016-04-14 23:42:37 +00:00
|
|
|
|
2016-04-15 15:29:12 +00:00
|
|
|
if servicePort.TargetPort.StrVal == "" {
|
|
|
|
continue
|
2016-04-14 23:42:37 +00:00
|
|
|
}
|
2016-04-15 15:29:12 +00:00
|
|
|
|
|
|
|
namedPorts[servicePort.TargetPort.StrVal] = fmt.Sprintf("%v", portNum)
|
2016-04-14 23:42:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-15 14:59:02 +00:00
|
|
|
if svc.ObjectMeta.Annotations == nil {
|
|
|
|
svc.ObjectMeta.Annotations = map[string]string{}
|
|
|
|
}
|
|
|
|
|
|
|
|
curNamedPort := svc.ObjectMeta.Annotations[namedPortAnnotation]
|
2016-04-16 22:36:45 +00:00
|
|
|
if len(namedPorts) > 0 && !reflect.DeepEqual(curNamedPort, namedPorts) {
|
2016-04-14 23:42:37 +00:00
|
|
|
data, _ := json.Marshal(namedPorts)
|
2016-04-15 15:29:12 +00:00
|
|
|
|
|
|
|
newSvc, err := lbc.client.Services(svc.Namespace).Get(svc.Name)
|
|
|
|
if err != nil {
|
2016-05-02 02:34:00 +00:00
|
|
|
return namedPorts, fmt.Errorf("error getting service %v/%v: %v", svc.Namespace, svc.Name, err)
|
2016-04-15 15:29:12 +00:00
|
|
|
}
|
|
|
|
|
2016-04-16 22:36:45 +00:00
|
|
|
if newSvc.ObjectMeta.Annotations == nil {
|
|
|
|
newSvc.ObjectMeta.Annotations = map[string]string{}
|
2016-04-15 15:29:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
newSvc.ObjectMeta.Annotations[namedPortAnnotation] = string(data)
|
2016-04-14 23:42:37 +00:00
|
|
|
glog.Infof("updating service %v with new named port mappings", svc.Name)
|
2016-05-02 02:34:00 +00:00
|
|
|
_, err = lbc.client.Services(svc.Namespace).Update(newSvc)
|
2016-04-14 23:42:37 +00:00
|
|
|
if err != nil {
|
2016-05-02 02:34:00 +00:00
|
|
|
return namedPorts, fmt.Errorf("error syncing service %v/%v: %v", svc.Namespace, svc.Name, err)
|
2016-04-14 23:42:37 +00:00
|
|
|
}
|
2016-05-02 02:34:00 +00:00
|
|
|
|
|
|
|
return newSvc.ObjectMeta.Annotations, nil
|
2016-04-14 23:42:37 +00:00
|
|
|
}
|
2016-04-15 15:29:12 +00:00
|
|
|
|
2016-05-02 02:34:00 +00:00
|
|
|
return namedPorts, nil
|
2016-04-14 23:42:37 +00:00
|
|
|
}
|
|
|
|
|
2016-03-22 18:01:04 +00:00
|
|
|
func (lbc *loadBalancerController) sync(key string) {
|
|
|
|
if !lbc.controllersInSync() {
|
2016-04-16 22:36:45 +00:00
|
|
|
time.Sleep(podStoreSyncedPollPeriod)
|
2016-03-22 18:01:04 +00:00
|
|
|
lbc.syncQueue.requeue(key, fmt.Errorf("deferring sync till endpoints controller has synced"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-03-15 02:29:13 +00:00
|
|
|
ings := lbc.ingLister.Store.List()
|
2016-03-15 15:31:39 +00:00
|
|
|
upstreams, servers := lbc.getUpstreamServers(ings)
|
|
|
|
|
2016-03-19 23:29:29 +00:00
|
|
|
var cfg *api.ConfigMap
|
|
|
|
|
|
|
|
ns, name, _ := parseNsName(lbc.nxgConfigMap)
|
|
|
|
cfg, err := lbc.getConfigMap(ns, name)
|
|
|
|
if err != nil {
|
|
|
|
cfg = &api.ConfigMap{}
|
|
|
|
}
|
2016-03-19 20:17:58 +00:00
|
|
|
|
2016-03-22 18:01:04 +00:00
|
|
|
ngxConfig := lbc.nginx.ReadConfig(cfg)
|
2016-03-19 23:29:29 +00:00
|
|
|
lbc.nginx.CheckAndReload(ngxConfig, nginx.IngressConfig{
|
|
|
|
Upstreams: upstreams,
|
|
|
|
Servers: servers,
|
2016-03-30 03:47:20 +00:00
|
|
|
TCPUpstreams: lbc.getTCPServices(),
|
|
|
|
UDPUpstreams: lbc.getUDPServices(),
|
2016-03-19 23:29:29 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-04-01 00:29:36 +00:00
|
|
|
func (lbc *loadBalancerController) updateIngressStatus(key string) {
|
2016-03-31 17:59:28 +00:00
|
|
|
if !lbc.controllersInSync() {
|
2016-04-16 22:36:45 +00:00
|
|
|
time.Sleep(podStoreSyncedPollPeriod)
|
2016-03-31 17:59:28 +00:00
|
|
|
lbc.ingQueue.requeue(key, fmt.Errorf("deferring sync till endpoints controller has synced"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
obj, ingExists, err := lbc.ingLister.Store.GetByKey(key)
|
2016-03-30 23:12:37 +00:00
|
|
|
if err != nil {
|
2016-03-31 17:59:28 +00:00
|
|
|
lbc.ingQueue.requeue(key, err)
|
2016-03-30 23:12:37 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-04-01 00:29:36 +00:00
|
|
|
if !ingExists {
|
|
|
|
return
|
|
|
|
}
|
2016-03-31 17:59:28 +00:00
|
|
|
|
2016-04-01 00:29:36 +00:00
|
|
|
ing := obj.(*extensions.Ingress)
|
2016-03-31 17:59:28 +00:00
|
|
|
|
2016-04-01 00:29:36 +00:00
|
|
|
ingClient := lbc.client.Extensions().Ingress(ing.Namespace)
|
2016-03-30 23:12:37 +00:00
|
|
|
|
2016-04-01 00:29:36 +00:00
|
|
|
currIng, err := ingClient.Get(ing.Name)
|
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("unexpected error searching Ingress %v/%v: %v", ing.Namespace, ing.Name, err)
|
|
|
|
return
|
|
|
|
}
|
2016-03-31 17:59:28 +00:00
|
|
|
|
2016-04-01 00:29:36 +00:00
|
|
|
lbIPs := ing.Status.LoadBalancer.Ingress
|
|
|
|
if !lbc.isStatusIPDefined(lbIPs) {
|
2016-04-06 14:46:06 +00:00
|
|
|
glog.Infof("Updating loadbalancer %v/%v with IP %v", ing.Namespace, ing.Name, lbc.podInfo.NodeIP)
|
2016-04-01 00:29:36 +00:00
|
|
|
currIng.Status.LoadBalancer.Ingress = append(currIng.Status.LoadBalancer.Ingress, api.LoadBalancerIngress{
|
2016-04-06 14:46:06 +00:00
|
|
|
IP: lbc.podInfo.NodeIP,
|
2016-04-01 00:29:36 +00:00
|
|
|
})
|
|
|
|
if _, err := ingClient.UpdateStatus(currIng); err != nil {
|
|
|
|
lbc.recorder.Eventf(currIng, api.EventTypeWarning, "UPDATE", "error: %v", err)
|
|
|
|
return
|
2016-03-31 17:59:28 +00:00
|
|
|
}
|
2016-04-01 00:29:36 +00:00
|
|
|
|
2016-04-06 14:46:06 +00:00
|
|
|
lbc.recorder.Eventf(currIng, api.EventTypeNormal, "CREATE", "ip: %v", lbc.podInfo.NodeIP)
|
2016-03-30 23:12:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (lbc *loadBalancerController) isStatusIPDefined(lbings []api.LoadBalancerIngress) bool {
|
|
|
|
for _, lbing := range lbings {
|
2016-04-06 14:46:06 +00:00
|
|
|
if lbing.IP == lbc.podInfo.NodeIP {
|
2016-03-30 23:12:37 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-03-19 23:29:29 +00:00
|
|
|
func (lbc *loadBalancerController) getTCPServices() []*nginx.Location {
|
|
|
|
if lbc.tcpConfigMap == "" {
|
|
|
|
// no configmap for TCP services
|
|
|
|
return []*nginx.Location{}
|
|
|
|
}
|
|
|
|
|
|
|
|
ns, name, err := parseNsName(lbc.tcpConfigMap)
|
|
|
|
if err != nil {
|
|
|
|
glog.Warningf("%v", err)
|
|
|
|
return []*nginx.Location{}
|
|
|
|
}
|
|
|
|
tcpMap, err := lbc.getTCPConfigMap(ns, name)
|
|
|
|
if err != nil {
|
|
|
|
glog.V(3).Infof("no configured tcp services found: %v", err)
|
|
|
|
return []*nginx.Location{}
|
|
|
|
}
|
|
|
|
|
2016-04-16 22:36:45 +00:00
|
|
|
return lbc.getStreamServices(tcpMap.Data, api.ProtocolTCP)
|
2016-03-29 23:30:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (lbc *loadBalancerController) getUDPServices() []*nginx.Location {
|
|
|
|
if lbc.udpConfigMap == "" {
|
|
|
|
// no configmap for TCP services
|
|
|
|
return []*nginx.Location{}
|
|
|
|
}
|
|
|
|
|
|
|
|
ns, name, err := parseNsName(lbc.udpConfigMap)
|
|
|
|
if err != nil {
|
|
|
|
glog.Warningf("%v", err)
|
|
|
|
return []*nginx.Location{}
|
|
|
|
}
|
|
|
|
tcpMap, err := lbc.getUDPConfigMap(ns, name)
|
|
|
|
if err != nil {
|
|
|
|
glog.V(3).Infof("no configured tcp services found: %v", err)
|
|
|
|
return []*nginx.Location{}
|
|
|
|
}
|
|
|
|
|
2016-04-16 22:36:45 +00:00
|
|
|
return lbc.getStreamServices(tcpMap.Data, api.ProtocolUDP)
|
2016-03-29 23:30:44 +00:00
|
|
|
}
|
|
|
|
|
2016-04-16 22:36:45 +00:00
|
|
|
func (lbc *loadBalancerController) getStreamServices(data map[string]string, proto api.Protocol) []*nginx.Location {
|
2016-03-29 23:30:44 +00:00
|
|
|
var svcs []*nginx.Location
|
2016-03-19 23:29:29 +00:00
|
|
|
// k -> port to expose in nginx
|
|
|
|
// v -> <namespace>/<service name>:<port from service to be used>
|
2016-03-29 23:30:44 +00:00
|
|
|
for k, v := range data {
|
2016-03-19 23:29:29 +00:00
|
|
|
port, err := strconv.Atoi(k)
|
|
|
|
if err != nil {
|
|
|
|
glog.Warningf("%v is not valid as a TCP port", k)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-04-16 22:36:45 +00:00
|
|
|
// this ports are required for NGINX
|
|
|
|
if k == "80" || k == "443" || k == "8181" {
|
|
|
|
glog.Warningf("port %v cannot be used for TCP or UDP services. Is reserved for NGINX", k)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
nsSvcPort := strings.Split(v, ":")
|
|
|
|
if len(nsSvcPort) != 2 {
|
2016-03-19 23:29:29 +00:00
|
|
|
glog.Warningf("invalid format (namespace/name:port) '%v'", k)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-04-16 22:36:45 +00:00
|
|
|
nsName := nsSvcPort[0]
|
|
|
|
svcPort := nsSvcPort[1]
|
|
|
|
|
|
|
|
svcNs, svcName, err := parseNsName(nsName)
|
2016-03-19 23:29:29 +00:00
|
|
|
if err != nil {
|
|
|
|
glog.Warningf("%v", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-04-16 22:36:45 +00:00
|
|
|
svcObj, svcExists, err := lbc.svcLister.Store.GetByKey(nsName)
|
2016-03-19 23:29:29 +00:00
|
|
|
if err != nil {
|
2016-04-16 22:36:45 +00:00
|
|
|
glog.Warningf("error getting service %v: %v", nsName, err)
|
2016-03-19 23:29:29 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if !svcExists {
|
2016-04-16 22:36:45 +00:00
|
|
|
glog.Warningf("service %v was not found", nsName)
|
2016-03-19 23:29:29 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
svc := svcObj.(*api.Service)
|
|
|
|
|
|
|
|
var endps []nginx.UpstreamServer
|
2016-04-16 22:36:45 +00:00
|
|
|
targetPort, err := strconv.Atoi(svcPort)
|
2016-03-19 23:29:29 +00:00
|
|
|
if err != nil {
|
2016-04-14 23:42:37 +00:00
|
|
|
for _, sp := range svc.Spec.Ports {
|
2016-04-16 22:36:45 +00:00
|
|
|
if sp.Name == svcPort {
|
2016-04-14 23:42:37 +00:00
|
|
|
endps = lbc.getEndpoints(svc, sp.TargetPort, proto)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2016-03-19 23:29:29 +00:00
|
|
|
} else {
|
|
|
|
// we need to use the TargetPort (where the endpoints are running)
|
|
|
|
for _, sp := range svc.Spec.Ports {
|
2016-05-09 19:06:09 +00:00
|
|
|
if sp.Port == int32(targetPort) {
|
2016-03-30 03:47:20 +00:00
|
|
|
endps = lbc.getEndpoints(svc, sp.TargetPort, proto)
|
2016-03-19 23:29:29 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-22 18:01:04 +00:00
|
|
|
// tcp upstreams cannot contain empty upstreams and there is no
|
|
|
|
// default backend equivalent for TCP
|
|
|
|
if len(endps) == 0 {
|
|
|
|
glog.Warningf("service %v/%v does no have any active endpoints", svcNs, svcName)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-03-29 23:30:44 +00:00
|
|
|
svcs = append(svcs, &nginx.Location{
|
2016-03-19 23:29:29 +00:00
|
|
|
Path: k,
|
|
|
|
Upstream: nginx.Upstream{
|
|
|
|
Name: fmt.Sprintf("%v-%v-%v", svcNs, svcName, port),
|
|
|
|
Backends: endps,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-03-29 23:30:44 +00:00
|
|
|
return svcs
|
2016-03-19 23:29:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (lbc *loadBalancerController) getDefaultUpstream() *nginx.Upstream {
|
|
|
|
upstream := &nginx.Upstream{
|
|
|
|
Name: defUpstreamName,
|
|
|
|
}
|
|
|
|
svcKey := lbc.defaultSvc
|
|
|
|
svcObj, svcExists, err := lbc.svcLister.Store.GetByKey(svcKey)
|
|
|
|
if err != nil {
|
|
|
|
glog.Warningf("unexpected error searching the default backend %v: %v", lbc.defaultSvc, err)
|
|
|
|
upstream.Backends = append(upstream.Backends, nginx.NewDefaultServer())
|
|
|
|
return upstream
|
|
|
|
}
|
|
|
|
|
|
|
|
if !svcExists {
|
|
|
|
glog.Warningf("service %v does no exists", svcKey)
|
|
|
|
upstream.Backends = append(upstream.Backends, nginx.NewDefaultServer())
|
|
|
|
return upstream
|
|
|
|
}
|
|
|
|
|
|
|
|
svc := svcObj.(*api.Service)
|
|
|
|
|
2016-03-30 03:47:20 +00:00
|
|
|
endps := lbc.getEndpoints(svc, svc.Spec.Ports[0].TargetPort, api.ProtocolTCP)
|
2016-03-19 23:29:29 +00:00
|
|
|
if len(endps) == 0 {
|
|
|
|
glog.Warningf("service %v does no have any active endpoints", svcKey)
|
|
|
|
upstream.Backends = append(upstream.Backends, nginx.NewDefaultServer())
|
|
|
|
} else {
|
|
|
|
upstream.Backends = append(upstream.Backends, endps...)
|
|
|
|
}
|
|
|
|
|
|
|
|
return upstream
|
2016-03-15 02:29:13 +00:00
|
|
|
}
|
|
|
|
|
2016-03-16 18:57:36 +00:00
|
|
|
func (lbc *loadBalancerController) getUpstreamServers(data []interface{}) ([]*nginx.Upstream, []*nginx.Server) {
|
|
|
|
upstreams := lbc.createUpstreams(data)
|
2016-03-19 23:29:29 +00:00
|
|
|
upstreams[defUpstreamName] = lbc.getDefaultUpstream()
|
2016-03-19 20:17:58 +00:00
|
|
|
|
2016-04-02 20:41:41 +00:00
|
|
|
servers := lbc.createServers(data)
|
2016-05-01 22:07:31 +00:00
|
|
|
if _, ok := servers[defServerName]; !ok {
|
|
|
|
// default server - no servername.
|
|
|
|
// there is no rule with default backend
|
|
|
|
servers[defServerName] = &nginx.Server{
|
|
|
|
Name: defServerName,
|
|
|
|
Locations: []*nginx.Location{{
|
|
|
|
Path: rootLocation,
|
|
|
|
IsDefBackend: true,
|
|
|
|
Upstream: *lbc.getDefaultUpstream(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2016-04-02 20:41:41 +00:00
|
|
|
}
|
|
|
|
|
2016-03-15 02:29:13 +00:00
|
|
|
for _, ingIf := range data {
|
2016-03-15 15:31:39 +00:00
|
|
|
ing := ingIf.(*extensions.Ingress)
|
2016-03-15 02:29:13 +00:00
|
|
|
|
|
|
|
for _, rule := range ing.Spec.Rules {
|
|
|
|
if rule.IngressRuleValue.HTTP == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-05-01 22:07:31 +00:00
|
|
|
host := rule.Host
|
|
|
|
if host == "" {
|
|
|
|
host = defServerName
|
|
|
|
}
|
|
|
|
server := servers[host]
|
|
|
|
if server == nil {
|
|
|
|
server = servers["_"]
|
|
|
|
}
|
2016-03-15 02:29:13 +00:00
|
|
|
|
2016-03-16 18:57:36 +00:00
|
|
|
for _, path := range rule.HTTP.Paths {
|
2016-04-16 22:36:45 +00:00
|
|
|
upsName := fmt.Sprintf("%v-%v-%v", ing.GetNamespace(), path.Backend.ServiceName, path.Backend.ServicePort.String())
|
2016-03-16 18:57:36 +00:00
|
|
|
ups := upstreams[upsName]
|
2016-03-15 02:29:13 +00:00
|
|
|
|
2016-04-16 22:36:45 +00:00
|
|
|
nginxPath := path.Path
|
|
|
|
// if there's no path defined we assume /
|
|
|
|
if nginxPath == "" {
|
|
|
|
lbc.recorder.Eventf(ing, api.EventTypeWarning, "MAPPING",
|
|
|
|
"Ingress rule '%v/%v' contains no path definition. Assuming /", ing.GetNamespace(), ing.GetName())
|
2016-04-28 23:04:41 +00:00
|
|
|
nginxPath = rootLocation
|
2016-03-15 02:29:13 +00:00
|
|
|
}
|
|
|
|
|
2016-04-28 23:04:41 +00:00
|
|
|
// Validate that there is no another previuous
|
|
|
|
// rule for the same host and path.
|
2016-04-16 22:36:45 +00:00
|
|
|
addLoc := true
|
2016-04-13 02:19:08 +00:00
|
|
|
for _, loc := range server.Locations {
|
2016-04-28 23:04:41 +00:00
|
|
|
if loc.Path == rootLocation && nginxPath == rootLocation && loc.IsDefBackend {
|
|
|
|
loc.Upstream = *ups
|
|
|
|
addLoc = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-04-16 22:36:45 +00:00
|
|
|
if loc.Path == nginxPath {
|
|
|
|
lbc.recorder.Eventf(ing, api.EventTypeWarning, "MAPPING",
|
|
|
|
"Path '%v' already defined in another Ingress rule", nginxPath)
|
|
|
|
addLoc = false
|
2016-03-16 18:57:36 +00:00
|
|
|
break
|
2016-03-15 02:29:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-16 22:36:45 +00:00
|
|
|
if addLoc {
|
2016-04-13 02:19:08 +00:00
|
|
|
server.Locations = append(server.Locations, &nginx.Location{
|
2016-04-16 22:36:45 +00:00
|
|
|
Path: nginxPath,
|
2016-04-13 02:19:08 +00:00
|
|
|
Upstream: *ups,
|
|
|
|
})
|
|
|
|
}
|
2016-03-19 23:29:29 +00:00
|
|
|
}
|
2016-03-15 02:29:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-16 18:57:36 +00:00
|
|
|
// TODO: find a way to make this more readable
|
|
|
|
// The structs must be ordered to always generate the same file
|
|
|
|
// if the content does not change.
|
|
|
|
aUpstreams := make([]*nginx.Upstream, 0, len(upstreams))
|
2016-03-15 02:29:13 +00:00
|
|
|
for _, value := range upstreams {
|
2016-03-15 15:31:39 +00:00
|
|
|
if len(value.Backends) == 0 {
|
2016-04-28 23:04:41 +00:00
|
|
|
glog.Warningf("upstream %v does no have any active endpoints. Using default backend", value.Name)
|
2016-03-15 15:31:39 +00:00
|
|
|
value.Backends = append(value.Backends, nginx.NewDefaultServer())
|
|
|
|
}
|
2016-03-15 02:29:13 +00:00
|
|
|
sort.Sort(nginx.UpstreamServerByAddrPort(value.Backends))
|
2016-03-15 15:31:39 +00:00
|
|
|
aUpstreams = append(aUpstreams, value)
|
2016-03-15 02:29:13 +00:00
|
|
|
}
|
2016-03-15 15:31:39 +00:00
|
|
|
sort.Sort(nginx.UpstreamByNameServers(aUpstreams))
|
2016-03-15 02:29:13 +00:00
|
|
|
|
2016-03-16 18:57:36 +00:00
|
|
|
aServers := make([]*nginx.Server, 0, len(servers))
|
2016-03-15 15:31:39 +00:00
|
|
|
for _, value := range servers {
|
|
|
|
sort.Sort(nginx.LocationByPath(value.Locations))
|
|
|
|
aServers = append(aServers, value)
|
|
|
|
}
|
|
|
|
sort.Sort(nginx.ServerByName(aServers))
|
2016-03-15 02:29:13 +00:00
|
|
|
|
2016-03-15 15:31:39 +00:00
|
|
|
return aUpstreams, aServers
|
2016-03-15 02:29:13 +00:00
|
|
|
}
|
|
|
|
|
2016-04-28 04:03:59 +00:00
|
|
|
// createUpstreams creates the NGINX upstreams for each service referenced in
|
|
|
|
// Ingress rules. The servers inside the upstream are endpoints.
|
2016-03-16 18:57:36 +00:00
|
|
|
func (lbc *loadBalancerController) createUpstreams(data []interface{}) map[string]*nginx.Upstream {
|
|
|
|
upstreams := make(map[string]*nginx.Upstream)
|
|
|
|
|
|
|
|
for _, ingIf := range data {
|
|
|
|
ing := ingIf.(*extensions.Ingress)
|
|
|
|
|
|
|
|
for _, rule := range ing.Spec.Rules {
|
|
|
|
if rule.IngressRuleValue.HTTP == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, path := range rule.HTTP.Paths {
|
2016-04-16 22:36:45 +00:00
|
|
|
name := fmt.Sprintf("%v-%v-%v", ing.GetNamespace(), path.Backend.ServiceName, path.Backend.ServicePort.String())
|
2016-04-28 23:04:41 +00:00
|
|
|
if _, ok := upstreams[name]; ok {
|
|
|
|
continue
|
|
|
|
}
|
2016-04-16 22:36:45 +00:00
|
|
|
|
2016-04-28 23:04:41 +00:00
|
|
|
glog.V(3).Infof("creating upstream %v", name)
|
|
|
|
upstreams[name] = nginx.NewUpstream(name)
|
2016-04-16 22:36:45 +00:00
|
|
|
|
2016-04-28 23:04:41 +00:00
|
|
|
svcKey := fmt.Sprintf("%v/%v", ing.GetNamespace(), path.Backend.ServiceName)
|
|
|
|
svcObj, svcExists, err := lbc.svcLister.Store.GetByKey(svcKey)
|
|
|
|
if err != nil {
|
|
|
|
glog.Infof("error getting service %v from the cache: %v", svcKey, err)
|
|
|
|
continue
|
|
|
|
}
|
2016-04-16 22:36:45 +00:00
|
|
|
|
2016-04-28 23:04:41 +00:00
|
|
|
if !svcExists {
|
|
|
|
glog.Warningf("service %v does no exists", svcKey)
|
|
|
|
continue
|
|
|
|
}
|
2016-04-16 22:36:45 +00:00
|
|
|
|
2016-04-28 23:04:41 +00:00
|
|
|
svc := svcObj.(*api.Service)
|
|
|
|
glog.V(3).Infof("obtaining port information for service %v", svcKey)
|
|
|
|
bp := path.Backend.ServicePort.String()
|
|
|
|
for _, servicePort := range svc.Spec.Ports {
|
|
|
|
// targetPort could be a string, use the name or the port (int)
|
2016-05-09 19:06:09 +00:00
|
|
|
if strconv.Itoa(int(servicePort.Port)) == bp || servicePort.TargetPort.String() == bp || servicePort.Name == bp {
|
2016-04-28 23:04:41 +00:00
|
|
|
endps := lbc.getEndpoints(svc, servicePort.TargetPort, api.ProtocolTCP)
|
|
|
|
if len(endps) == 0 {
|
|
|
|
glog.Warningf("service %v does no have any active endpoints", svcKey)
|
2016-04-16 22:36:45 +00:00
|
|
|
}
|
2016-04-28 23:04:41 +00:00
|
|
|
|
|
|
|
upstreams[name].Backends = append(upstreams[name].Backends, endps...)
|
|
|
|
break
|
2016-04-16 22:36:45 +00:00
|
|
|
}
|
2016-03-16 18:57:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return upstreams
|
|
|
|
}
|
|
|
|
|
|
|
|
func (lbc *loadBalancerController) createServers(data []interface{}) map[string]*nginx.Server {
|
|
|
|
servers := make(map[string]*nginx.Server)
|
|
|
|
|
|
|
|
pems := lbc.getPemsFromIngress(data)
|
|
|
|
|
|
|
|
for _, ingIf := range data {
|
|
|
|
ing := ingIf.(*extensions.Ingress)
|
|
|
|
|
|
|
|
for _, rule := range ing.Spec.Rules {
|
2016-05-01 22:07:31 +00:00
|
|
|
host := rule.Host
|
|
|
|
if host == "" {
|
|
|
|
host = defServerName
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := servers[host]; !ok {
|
2016-04-28 23:04:41 +00:00
|
|
|
locs := []*nginx.Location{}
|
|
|
|
locs = append(locs, &nginx.Location{
|
|
|
|
Path: rootLocation,
|
|
|
|
IsDefBackend: true,
|
|
|
|
Upstream: *lbc.getDefaultUpstream(),
|
|
|
|
})
|
2016-05-01 22:07:31 +00:00
|
|
|
servers[host] = &nginx.Server{Name: host, Locations: locs}
|
2016-03-16 18:57:36 +00:00
|
|
|
}
|
|
|
|
|
2016-05-01 22:07:31 +00:00
|
|
|
if pemFile, ok := pems[host]; ok {
|
|
|
|
server := servers[host]
|
2016-03-16 18:57:36 +00:00
|
|
|
server.SSL = true
|
|
|
|
server.SSLCertificate = pemFile
|
|
|
|
server.SSLCertificateKey = pemFile
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return servers
|
|
|
|
}
|
|
|
|
|
2016-03-16 14:12:45 +00:00
|
|
|
func (lbc *loadBalancerController) getPemsFromIngress(data []interface{}) map[string]string {
|
|
|
|
pems := make(map[string]string)
|
|
|
|
|
|
|
|
for _, ingIf := range data {
|
|
|
|
ing := ingIf.(*extensions.Ingress)
|
|
|
|
|
|
|
|
for _, tls := range ing.Spec.TLS {
|
|
|
|
secretName := tls.SecretName
|
|
|
|
secret, err := lbc.client.Secrets(ing.Namespace).Get(secretName)
|
|
|
|
if err != nil {
|
|
|
|
glog.Warningf("Error retriveing secret %v for ing %v: %v", secretName, ing.Name, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
cert, ok := secret.Data[api.TLSCertKey]
|
|
|
|
if !ok {
|
|
|
|
glog.Warningf("Secret %v has no private key", secretName)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
key, ok := secret.Data[api.TLSPrivateKeyKey]
|
|
|
|
if !ok {
|
|
|
|
glog.Warningf("Secret %v has no cert", secretName)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-04-01 00:29:36 +00:00
|
|
|
pemFileName, err := lbc.nginx.AddOrUpdateCertAndKey(fmt.Sprintf("%v-%v", ing.Namespace, secretName), string(cert), string(key))
|
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("No valid SSL certificate found in secret %v: %v", secretName, err)
|
|
|
|
continue
|
|
|
|
}
|
2016-03-22 18:01:04 +00:00
|
|
|
cn, err := lbc.nginx.CheckSSLCertificate(pemFileName)
|
2016-03-19 00:41:31 +00:00
|
|
|
if err != nil {
|
2016-04-01 00:29:36 +00:00
|
|
|
glog.Errorf("No valid SSL certificate found in secret %v: %v", secretName, err)
|
2016-03-19 00:41:31 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-04-02 20:41:41 +00:00
|
|
|
if len(tls.Hosts) == 0 {
|
|
|
|
if _, ok := pems["_"]; ok {
|
|
|
|
glog.Warningf("It is not possible to use %v secret for default SSL certificate because there is one already defined", secretName)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
pems["_"] = pemFileName
|
|
|
|
glog.Infof("Using the secret %v as source for the default SSL certificate", secretName)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-03-16 14:12:45 +00:00
|
|
|
for _, host := range tls.Hosts {
|
2016-03-19 00:41:31 +00:00
|
|
|
if isHostValid(host, cn) {
|
|
|
|
pems[host] = pemFileName
|
|
|
|
} else {
|
|
|
|
glog.Warningf("SSL Certificate stored in secret %v is not valid for the host %v defined in the Ingress rule %v", secretName, host, ing.Name)
|
|
|
|
}
|
2016-03-16 14:12:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pems
|
|
|
|
}
|
|
|
|
|
2016-03-15 15:31:39 +00:00
|
|
|
// getEndpoints returns a list of <endpoint ip>:<port> for a given service/target port combination.
|
2016-03-30 03:47:20 +00:00
|
|
|
func (lbc *loadBalancerController) getEndpoints(s *api.Service, servicePort intstr.IntOrString, proto api.Protocol) []nginx.UpstreamServer {
|
2016-03-19 23:29:29 +00:00
|
|
|
glog.V(3).Infof("getting endpoints for service %v/%v and port %v", s.Namespace, s.Name, servicePort.String())
|
2016-03-15 15:31:39 +00:00
|
|
|
ep, err := lbc.endpLister.GetServiceEndpoints(s)
|
|
|
|
if err != nil {
|
|
|
|
glog.Warningf("unexpected error obtaining service endpoints: %v", err)
|
|
|
|
return []nginx.UpstreamServer{}
|
|
|
|
}
|
|
|
|
|
2016-03-19 23:29:29 +00:00
|
|
|
upsServers := []nginx.UpstreamServer{}
|
2016-03-15 15:31:39 +00:00
|
|
|
|
|
|
|
for _, ss := range ep.Subsets {
|
|
|
|
for _, epPort := range ss.Ports {
|
2016-03-30 03:47:20 +00:00
|
|
|
|
|
|
|
if !reflect.DeepEqual(epPort.Protocol, proto) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-05-09 19:06:09 +00:00
|
|
|
var targetPort int32
|
2016-03-15 15:31:39 +00:00
|
|
|
switch servicePort.Type {
|
|
|
|
case intstr.Int:
|
2016-05-09 19:06:09 +00:00
|
|
|
if int(epPort.Port) == servicePort.IntValue() {
|
2016-03-15 15:31:39 +00:00
|
|
|
targetPort = epPort.Port
|
|
|
|
}
|
|
|
|
case intstr.String:
|
2016-05-02 02:34:00 +00:00
|
|
|
namedPorts := s.ObjectMeta.Annotations
|
|
|
|
val, ok := namedPortMapping(namedPorts).getPort(servicePort.StrVal)
|
|
|
|
if ok {
|
2016-04-14 23:42:37 +00:00
|
|
|
port, err := strconv.Atoi(val)
|
|
|
|
if err != nil {
|
|
|
|
glog.Warningf("%v is not valid as a port", val)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-05-09 19:06:09 +00:00
|
|
|
targetPort = int32(port)
|
2016-05-02 02:34:00 +00:00
|
|
|
} else {
|
|
|
|
newnp, err := lbc.checkSvcForUpdate(s)
|
|
|
|
if err != nil {
|
|
|
|
glog.Warningf("error mapping service ports: %v", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
val, ok := namedPortMapping(newnp).getPort(servicePort.StrVal)
|
|
|
|
if ok {
|
|
|
|
port, err := strconv.Atoi(val)
|
|
|
|
if err != nil {
|
|
|
|
glog.Warningf("%v is not valid as a port", val)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-05-09 19:06:09 +00:00
|
|
|
targetPort = int32(port)
|
2016-04-14 23:42:37 +00:00
|
|
|
}
|
2016-03-15 02:29:13 +00:00
|
|
|
}
|
2016-03-15 15:31:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if targetPort == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, epAddress := range ss.Addresses {
|
|
|
|
ups := nginx.UpstreamServer{Address: epAddress.IP, Port: fmt.Sprintf("%v", targetPort)}
|
|
|
|
upsServers = append(upsServers, ups)
|
2016-03-15 02:29:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-19 23:29:29 +00:00
|
|
|
glog.V(3).Infof("endpoints found: %v", upsServers)
|
2016-03-15 02:29:13 +00:00
|
|
|
return upsServers
|
|
|
|
}
|
|
|
|
|
2016-02-22 00:13:08 +00:00
|
|
|
// Stop stops the loadbalancer controller.
|
2016-03-30 23:12:37 +00:00
|
|
|
func (lbc *loadBalancerController) Stop() error {
|
2016-02-22 00:13:08 +00:00
|
|
|
// Stop is invoked from the http endpoint.
|
|
|
|
lbc.stopLock.Lock()
|
|
|
|
defer lbc.stopLock.Unlock()
|
|
|
|
|
|
|
|
// Only try draining the workqueue if we haven't already.
|
|
|
|
if !lbc.shutdown {
|
2016-03-30 23:12:37 +00:00
|
|
|
|
|
|
|
lbc.removeFromIngress()
|
|
|
|
|
2016-02-22 00:13:08 +00:00
|
|
|
close(lbc.stopCh)
|
2016-03-15 15:31:39 +00:00
|
|
|
glog.Infof("shutting down controller queues")
|
2016-02-22 00:13:08 +00:00
|
|
|
lbc.shutdown = true
|
2016-03-22 18:01:04 +00:00
|
|
|
lbc.syncQueue.shutdown()
|
2016-03-30 23:12:37 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Errorf("shutdown already in progress")
|
|
|
|
}
|
|
|
|
|
2016-04-16 22:36:45 +00:00
|
|
|
// removeFromIngress removes the IP address of the node where the Ingres
|
|
|
|
// controller is running before shutdown to avoid incorrect status
|
|
|
|
// information in Ingress rules
|
2016-03-30 23:12:37 +00:00
|
|
|
func (lbc *loadBalancerController) removeFromIngress() {
|
|
|
|
ings := lbc.ingLister.Store.List()
|
|
|
|
glog.Infof("updating %v Ingress rule/s", len(ings))
|
|
|
|
for _, cur := range ings {
|
|
|
|
ing := cur.(*extensions.Ingress)
|
|
|
|
|
|
|
|
ingClient := lbc.client.Extensions().Ingress(ing.Namespace)
|
|
|
|
currIng, err := ingClient.Get(ing.Name)
|
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("unexpected error searching Ingress %v/%v: %v", ing.Namespace, ing.Name, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
lbIPs := ing.Status.LoadBalancer.Ingress
|
|
|
|
if len(lbIPs) > 0 && lbc.isStatusIPDefined(lbIPs) {
|
2016-04-06 14:46:06 +00:00
|
|
|
glog.Infof("Updating loadbalancer %v/%v. Removing IP %v", ing.Namespace, ing.Name, lbc.podInfo.NodeIP)
|
2016-03-30 23:12:37 +00:00
|
|
|
|
|
|
|
for idx, lbStatus := range currIng.Status.LoadBalancer.Ingress {
|
2016-04-06 14:46:06 +00:00
|
|
|
if lbStatus.IP == lbc.podInfo.NodeIP {
|
2016-03-30 23:12:37 +00:00
|
|
|
currIng.Status.LoadBalancer.Ingress = append(currIng.Status.LoadBalancer.Ingress[:idx],
|
|
|
|
currIng.Status.LoadBalancer.Ingress[idx+1:]...)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := ingClient.UpdateStatus(currIng); err != nil {
|
|
|
|
lbc.recorder.Eventf(currIng, api.EventTypeWarning, "UPDATE", "error: %v", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-04-06 14:46:06 +00:00
|
|
|
lbc.recorder.Eventf(currIng, api.EventTypeNormal, "DELETE", "ip: %v", lbc.podInfo.NodeIP)
|
2016-03-30 23:12:37 +00:00
|
|
|
}
|
2016-02-22 00:13:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run starts the loadbalancer controller.
|
|
|
|
func (lbc *loadBalancerController) Run() {
|
2016-03-15 15:31:39 +00:00
|
|
|
glog.Infof("starting NGINX loadbalancer controller")
|
2016-03-15 02:29:13 +00:00
|
|
|
go lbc.nginx.Start()
|
2016-02-22 00:13:08 +00:00
|
|
|
|
|
|
|
go lbc.ingController.Run(lbc.stopCh)
|
2016-03-15 15:31:39 +00:00
|
|
|
go lbc.endpController.Run(lbc.stopCh)
|
|
|
|
go lbc.svcController.Run(lbc.stopCh)
|
2016-03-15 02:29:13 +00:00
|
|
|
|
2016-03-22 18:01:04 +00:00
|
|
|
go lbc.syncQueue.run(time.Second, lbc.stopCh)
|
2016-03-31 17:59:28 +00:00
|
|
|
go lbc.ingQueue.run(time.Second, lbc.stopCh)
|
2016-03-15 15:31:39 +00:00
|
|
|
|
2016-02-22 00:13:08 +00:00
|
|
|
<-lbc.stopCh
|
2016-03-15 15:31:39 +00:00
|
|
|
glog.Infof("shutting down NGINX loadbalancer controller")
|
2016-02-22 00:13:08 +00:00
|
|
|
}
|