2016-11-10 22:56:29 +00:00
/ *
Copyright 2015 The Kubernetes Authors .
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 controller
import (
"fmt"
2017-06-14 21:33:12 +00:00
"math/rand"
2017-09-24 20:36:59 +00:00
"net"
2016-11-10 22:56:29 +00:00
"reflect"
"sort"
"strconv"
"strings"
"sync"
2017-09-19 20:09:33 +00:00
"sync/atomic"
2016-11-10 22:56:29 +00:00
"time"
"github.com/golang/glog"
2017-09-17 18:42:31 +00:00
apiv1 "k8s.io/api/core/v1"
2017-07-16 19:19:59 +00:00
extensions "k8s.io/api/extensions/v1beta1"
2017-08-25 15:50:08 +00:00
"k8s.io/apimachinery/pkg/conversion"
2017-04-01 14:39:42 +00:00
"k8s.io/apimachinery/pkg/util/intstr"
2017-08-15 12:32:26 +00:00
"k8s.io/apimachinery/pkg/util/sets"
2017-09-30 23:42:42 +00:00
"k8s.io/apimachinery/pkg/util/wait"
2017-04-01 14:39:42 +00:00
clientset "k8s.io/client-go/kubernetes"
2017-05-24 18:02:51 +00:00
"k8s.io/client-go/kubernetes/scheme"
2017-09-17 18:42:31 +00:00
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
2017-04-01 14:39:42 +00:00
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/flowcontrol"
2017-10-06 20:33:32 +00:00
"k8s.io/kubernetes/pkg/kubelet/util/sliceutils"
"k8s.io/ingress-nginx/pkg/ingress"
"k8s.io/ingress-nginx/pkg/ingress/annotations/class"
"k8s.io/ingress-nginx/pkg/ingress/annotations/healthcheck"
"k8s.io/ingress-nginx/pkg/ingress/annotations/parser"
"k8s.io/ingress-nginx/pkg/ingress/annotations/proxy"
"k8s.io/ingress-nginx/pkg/ingress/defaults"
"k8s.io/ingress-nginx/pkg/ingress/resolver"
"k8s.io/ingress-nginx/pkg/ingress/status"
"k8s.io/ingress-nginx/pkg/k8s"
"k8s.io/ingress-nginx/pkg/net/ssl"
"k8s.io/ingress-nginx/pkg/task"
2016-11-10 22:56:29 +00:00
)
const (
2017-06-14 23:49:35 +00:00
defUpstreamName = "upstream-default-backend"
defServerName = "_"
rootLocation = "/"
2017-08-26 03:46:17 +00:00
fakeCertificate = "default-fake-certificate"
2016-11-10 22:56:29 +00:00
)
var (
// list of ports that cannot be used by TCP or UDP services
reservedPorts = [ ] string { "80" , "443" , "8181" , "18080" }
2017-08-25 15:50:08 +00:00
2017-08-26 03:46:17 +00:00
fakeCertificatePath = ""
fakeCertificateSHA = ""
2017-08-25 15:50:08 +00:00
cloner = conversion . NewCloner ( )
2016-11-10 22:56:29 +00:00
)
2016-11-11 23:43:35 +00:00
// GenericController holds the boilerplate code required to build an Ingress controlller.
2016-11-10 22:56:29 +00:00
type GenericController struct {
cfg * Configuration
2017-10-01 00:48:14 +00:00
listers * ingress . StoreLister
cacheController * cacheController
2016-11-10 22:56:29 +00:00
2016-12-29 20:02:06 +00:00
annotations annotationExtractor
2016-11-10 22:56:29 +00:00
recorder record . EventRecorder
syncQueue * task . Queue
syncStatus status . Sync
2016-11-23 23:22:29 +00:00
// local store of SSL certificates
// (only certificates used in ingress)
2016-11-10 22:56:29 +00:00
sslCertTracker * sslCertTracker
syncRateLimiter flowcontrol . RateLimiter
// 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
stopCh chan struct { }
2017-06-14 21:33:12 +00:00
2017-06-25 22:12:07 +00:00
// runningConfig contains the running configuration in the Backend
2017-06-14 21:33:12 +00:00
runningConfig * ingress . Configuration
2017-08-03 14:51:39 +00:00
2017-09-19 20:09:33 +00:00
forceReload int32
2016-11-10 22:56:29 +00:00
}
// Configuration contains all the settings required by an Ingress controller
type Configuration struct {
2017-03-24 02:17:39 +00:00
Client clientset . Interface
2016-11-10 22:56:29 +00:00
ResyncPeriod time . Duration
DefaultService string
IngressClass string
Namespace string
ConfigMapName string
2017-04-13 01:50:54 +00:00
ForceNamespaceIsolation bool
2017-09-01 22:26:12 +00:00
DisableNodeList bool
2017-04-13 01:50:54 +00:00
2016-11-10 22:56:29 +00:00
// optional
TCPConfigMapName string
// optional
UDPConfigMapName string
DefaultSSLCertificate string
DefaultHealthzURL string
2017-03-02 15:50:31 +00:00
DefaultIngressClass string
2016-11-10 22:56:29 +00:00
// optional
PublishService string
2016-11-11 23:43:35 +00:00
// Backend is the particular implementation to be used.
// (for instance NGINX)
2016-11-10 22:56:29 +00:00
Backend ingress . Controller
2017-01-20 22:01:37 +00:00
2017-06-20 01:22:08 +00:00
UpdateStatus bool
2017-10-08 17:29:19 +00:00
UseNodeInternalIP bool
2017-06-20 01:22:08 +00:00
ElectionID string
UpdateStatusOnShutdown bool
2017-10-08 17:29:19 +00:00
SortBackends bool
2016-11-10 22:56:29 +00:00
}
// newIngressController creates an Ingress controller
2016-11-11 23:43:35 +00:00
func newIngressController ( config * Configuration ) * GenericController {
2016-11-10 22:56:29 +00:00
eventBroadcaster := record . NewBroadcaster ( )
eventBroadcaster . StartLogging ( glog . Infof )
2017-09-17 18:42:31 +00:00
eventBroadcaster . StartRecordingToSink ( & v1core . EventSinkImpl {
2017-08-15 12:32:26 +00:00
Interface : config . Client . CoreV1 ( ) . Events ( config . Namespace ) ,
2016-11-10 22:56:29 +00:00
} )
ic := GenericController {
cfg : config ,
stopLock : & sync . Mutex { } ,
stopCh : make ( chan struct { } ) ,
2017-06-11 19:56:30 +00:00
syncRateLimiter : flowcontrol . NewTokenBucketRateLimiter ( 0.3 , 1 ) ,
2017-09-17 18:42:31 +00:00
recorder : eventBroadcaster . NewRecorder ( scheme . Scheme , apiv1 . EventSource {
2016-11-10 22:56:29 +00:00
Component : "ingress-controller" ,
} ) ,
sslCertTracker : newSSLCertTracker ( ) ,
}
2017-04-09 16:52:10 +00:00
ic . syncQueue = task . NewTaskQueue ( ic . syncIngress )
2016-11-10 22:56:29 +00:00
2017-10-01 00:48:14 +00:00
ic . listers , ic . cacheController = ic . createListers ( config . DisableNodeList )
2017-02-11 05:07:28 +00:00
2017-01-20 22:01:37 +00:00
if config . UpdateStatus {
ic . syncStatus = status . NewStatusSyncer ( status . Config {
2017-06-20 01:22:08 +00:00
Client : config . Client ,
PublishService : ic . cfg . PublishService ,
2017-09-18 23:53:26 +00:00
IngressLister : ic . listers . Ingress ,
2017-06-20 01:22:08 +00:00
ElectionID : config . ElectionID ,
IngressClass : config . IngressClass ,
DefaultIngressClass : config . DefaultIngressClass ,
UpdateStatusOnShutdown : config . UpdateStatusOnShutdown ,
2017-07-28 22:57:33 +00:00
CustomIngressStatus : ic . cfg . Backend . UpdateIngressStatus ,
2017-10-08 17:29:19 +00:00
UseNodeInternalIP : ic . cfg . UseNodeInternalIP ,
2017-01-20 22:01:37 +00:00
} )
} else {
glog . Warning ( "Update of ingress status is disabled (flag --update-status=false was specified)" )
}
2016-12-29 20:02:06 +00:00
ic . annotations = newAnnotationExtractor ( ic )
2017-09-18 23:53:26 +00:00
ic . cfg . Backend . SetListers ( ic . listers )
2017-02-07 18:13:08 +00:00
2017-08-25 15:50:08 +00:00
cloner . RegisterDeepCopyFunc ( ingress . GetGeneratedDeepCopyFuncs )
2016-11-11 23:43:35 +00:00
return & ic
2016-11-10 22:56:29 +00:00
}
// Info returns information about the backend
2016-11-16 18:24:26 +00:00
func ( ic GenericController ) Info ( ) * ingress . BackendInfo {
2016-11-10 22:56:29 +00:00
return ic . cfg . Backend . Info ( )
}
// IngressClass returns information about the backend
func ( ic GenericController ) IngressClass ( ) string {
return ic . cfg . IngressClass
}
2016-12-29 20:02:06 +00:00
// GetDefaultBackend returns the default backend
func ( ic GenericController ) GetDefaultBackend ( ) defaults . Backend {
return ic . cfg . Backend . BackendDefaults ( )
}
2017-09-17 16:34:29 +00:00
// GetPublishService returns the configured service used to set ingress status
2017-09-17 18:42:31 +00:00
func ( ic GenericController ) GetPublishService ( ) * apiv1 . Service {
2017-09-18 23:53:26 +00:00
s , err := ic . listers . Service . GetByName ( ic . cfg . PublishService )
2017-09-17 16:34:29 +00:00
if err != nil {
return nil
}
return s
}
2017-08-09 17:42:12 +00:00
// GetRecorder returns the event recorder
2017-08-25 15:50:08 +00:00
func ( ic GenericController ) GetRecorder ( ) record . EventRecorder {
2017-08-09 17:42:12 +00:00
return ic . recorder
}
2017-02-05 22:41:05 +00:00
// GetSecret searches for a secret in the local secrets Store
2017-09-17 18:42:31 +00:00
func ( ic GenericController ) GetSecret ( name string ) ( * apiv1 . Secret , error ) {
2017-09-18 23:53:26 +00:00
return ic . listers . Secret . GetByName ( name )
2016-11-10 22:56:29 +00:00
}
2017-08-25 15:50:08 +00:00
// GetService searches for a service in the local secrets Store
2017-09-17 18:42:31 +00:00
func ( ic GenericController ) GetService ( name string ) ( * apiv1 . Service , error ) {
2017-09-18 23:53:26 +00:00
return ic . listers . Service . GetByName ( name )
2016-11-10 22:56:29 +00:00
}
// sync collects all the pieces required to assemble the configuration file and
// then sends the content to the backend (OnUpdate) receiving the populated
// template as response reloading the backend if is required.
2017-09-28 00:33:23 +00:00
func ( ic * GenericController ) syncIngress ( item interface { } ) error {
2016-11-10 22:56:29 +00:00
ic . syncRateLimiter . Accept ( )
if ic . syncQueue . IsShuttingDown ( ) {
return nil
}
2017-09-28 00:33:23 +00:00
if element , ok := item . ( task . Element ) ; ok {
if name , ok := element . Key . ( string ) ; ok {
if obj , exists , _ := ic . listers . Ingress . GetByKey ( name ) ; exists {
ing := obj . ( * extensions . Ingress )
ic . readSecrets ( ing )
}
2017-07-27 17:46:33 +00:00
}
}
2017-09-25 19:40:55 +00:00
// Sort ingress rules using the ResourceVersion field
ings := ic . listers . Ingress . List ( )
sort . SliceStable ( ings , func ( i , j int ) bool {
2017-09-26 18:05:20 +00:00
ir := ings [ i ] . ( * extensions . Ingress ) . ResourceVersion
jr := ings [ j ] . ( * extensions . Ingress ) . ResourceVersion
2017-09-25 19:40:55 +00:00
return ir < jr
} )
// filter ingress rules
var ingresses [ ] * extensions . Ingress
for _ , ingIf := range ings {
ing := ingIf . ( * extensions . Ingress )
if ! class . IsValid ( ing , ic . cfg . IngressClass , ic . cfg . DefaultIngressClass ) {
continue
}
ingresses = append ( ingresses , ing )
}
upstreams , servers := ic . getBackendServers ( ingresses )
2016-11-11 23:43:35 +00:00
var passUpstreams [ ] * ingress . SSLPassthroughBackend
2017-06-12 12:45:08 +00:00
2016-11-10 22:56:29 +00:00
for _ , server := range servers {
if ! server . SSLPassthrough {
continue
}
for _ , loc := range server . Locations {
if loc . Path != rootLocation {
2017-04-02 02:32:22 +00:00
glog . Warningf ( "ignoring path %v of ssl passthrough host %v" , loc . Path , server . Hostname )
2016-11-10 22:56:29 +00:00
continue
}
2016-11-11 23:43:35 +00:00
passUpstreams = append ( passUpstreams , & ingress . SSLPassthroughBackend {
2016-11-16 18:24:26 +00:00
Backend : loc . Backend ,
Hostname : server . Hostname ,
2017-04-09 23:51:38 +00:00
Service : loc . Service ,
Port : loc . Port ,
2016-11-10 22:56:29 +00:00
} )
break
}
}
2017-09-13 19:15:47 +00:00
pcfg := ingress . Configuration {
2016-11-16 18:24:26 +00:00
Backends : upstreams ,
Servers : servers ,
2017-09-17 18:42:31 +00:00
TCPEndpoints : ic . getStreamServices ( ic . cfg . TCPConfigMapName , apiv1 . ProtocolTCP ) ,
UDPEndpoints : ic . getStreamServices ( ic . cfg . UDPConfigMapName , apiv1 . ProtocolUDP ) ,
2016-11-16 18:24:26 +00:00
PassthroughBackends : passUpstreams ,
2017-06-14 21:33:12 +00:00
}
2017-09-19 20:09:33 +00:00
if ! ic . isForceReload ( ) && ic . runningConfig != nil && ic . runningConfig . Equal ( & pcfg ) {
2017-06-14 23:40:25 +00:00
glog . V ( 3 ) . Infof ( "skipping backend reload (no changes detected)" )
2017-06-14 21:33:12 +00:00
return nil
}
glog . Infof ( "backend reload required" )
2016-11-10 22:56:29 +00:00
2017-09-13 19:15:47 +00:00
err := ic . cfg . Backend . OnUpdate ( pcfg )
2016-11-10 22:56:29 +00:00
if err != nil {
incReloadErrorCount ( )
2017-06-11 19:56:30 +00:00
glog . Errorf ( "unexpected failure restarting the backend: \n%v" , err )
2016-11-10 22:56:29 +00:00
return err
}
2017-06-11 19:56:30 +00:00
glog . Infof ( "ingress backend successfully reloaded..." )
incReloadCount ( )
2017-06-12 12:45:08 +00:00
setSSLExpireTime ( servers )
2017-06-11 19:56:30 +00:00
2017-09-13 19:15:47 +00:00
ic . runningConfig = & pcfg
2017-10-08 15:06:37 +00:00
ic . SetForceReload ( false )
2017-06-14 21:33:12 +00:00
2016-11-10 22:56:29 +00:00
return nil
}
2017-09-17 18:42:31 +00:00
func ( ic * GenericController ) getStreamServices ( configmapName string , proto apiv1 . Protocol ) [ ] ingress . L4Service {
2017-02-16 17:26:58 +00:00
glog . V ( 3 ) . Infof ( "obtaining information about stream services of type %v located in configmap %v" , proto , configmapName )
2017-02-02 22:41:02 +00:00
if configmapName == "" {
// no configmap configured
2017-02-24 21:46:39 +00:00
return [ ] ingress . L4Service { }
2016-11-10 22:56:29 +00:00
}
2017-09-18 23:53:26 +00:00
_ , _ , err := k8s . ParseNameNS ( configmapName )
2016-11-10 22:56:29 +00:00
if err != nil {
2017-09-18 23:53:26 +00:00
glog . Errorf ( "unexpected error reading configmap %v: %v" , configmapName , err )
2017-02-24 21:46:39 +00:00
return [ ] ingress . L4Service { }
2016-11-10 22:56:29 +00:00
}
2017-09-18 23:53:26 +00:00
configmap , err := ic . listers . ConfigMap . GetByName ( configmapName )
2016-11-10 22:56:29 +00:00
if err != nil {
2017-09-18 23:53:26 +00:00
glog . Errorf ( "unexpected error reading configmap %v: %v" , configmapName , err )
2017-02-24 21:46:39 +00:00
return [ ] ingress . L4Service { }
2016-11-10 22:56:29 +00:00
}
2017-02-24 21:46:39 +00:00
var svcs [ ] ingress . L4Service
2017-09-27 14:10:16 +00:00
var svcProxyProtocol ingress . ProxyProtocol
2016-11-10 22:56:29 +00:00
// k -> port to expose
// v -> <namespace>/<service name>:<port from service to be used>
2017-02-02 22:41:02 +00:00
for k , v := range configmap . Data {
2017-02-24 21:46:39 +00:00
externalPort , err := strconv . Atoi ( k )
2016-11-10 22:56:29 +00:00
if err != nil {
2017-02-02 22:41:02 +00:00
glog . Warningf ( "%v is not valid as a TCP/UDP port" , k )
2016-11-10 22:56:29 +00:00
continue
}
// this ports used by the backend
2017-10-06 20:33:32 +00:00
if sliceutils . StringInSlice ( k , reservedPorts ) {
2016-11-10 22:56:29 +00:00
glog . Warningf ( "port %v cannot be used for TCP or UDP services. It is reserved for the Ingress controller" , k )
continue
}
nsSvcPort := strings . Split ( v , ":" )
2017-07-02 20:46:15 +00:00
if len ( nsSvcPort ) < 2 {
2017-09-27 14:10:16 +00:00
glog . Warningf ( "invalid format (namespace/name:port:[PROXY]:[PROXY]) '%v'" , k )
2016-11-10 22:56:29 +00:00
continue
}
nsName := nsSvcPort [ 0 ]
svcPort := nsSvcPort [ 1 ]
2017-09-27 14:10:16 +00:00
svcProxyProtocol . Decode = false
svcProxyProtocol . Encode = false
2017-07-02 20:46:15 +00:00
// Proxy protocol is possible if the service is TCP
2017-09-27 14:10:16 +00:00
if len ( nsSvcPort ) >= 3 && proto == apiv1 . ProtocolTCP {
if len ( nsSvcPort ) >= 3 && strings . ToUpper ( nsSvcPort [ 2 ] ) == "PROXY" {
svcProxyProtocol . Decode = true
}
if len ( nsSvcPort ) == 4 && strings . ToUpper ( nsSvcPort [ 3 ] ) == "PROXY" {
svcProxyProtocol . Encode = true
2017-07-02 20:46:15 +00:00
}
}
2016-11-10 22:56:29 +00:00
svcNs , svcName , err := k8s . ParseNameNS ( nsName )
if err != nil {
glog . Warningf ( "%v" , err )
continue
}
2017-09-18 23:53:26 +00:00
svcObj , svcExists , err := ic . listers . Service . GetByKey ( nsName )
2016-11-10 22:56:29 +00:00
if err != nil {
glog . Warningf ( "error getting service %v: %v" , nsName , err )
continue
}
if ! svcExists {
glog . Warningf ( "service %v was not found" , nsName )
continue
}
2017-09-17 18:42:31 +00:00
svc := svcObj . ( * apiv1 . Service )
2016-11-10 22:56:29 +00:00
2016-11-11 23:43:35 +00:00
var endps [ ] ingress . Endpoint
2016-11-10 22:56:29 +00:00
targetPort , err := strconv . Atoi ( svcPort )
if err != nil {
2017-09-18 23:53:26 +00:00
glog . V ( 3 ) . Infof ( "searching service %v endpoints using the name '%v'" , svcNs , svcName , svcPort )
2016-11-10 22:56:29 +00:00
for _ , sp := range svc . Spec . Ports {
if sp . Name == svcPort {
2017-08-07 03:22:06 +00:00
if sp . Protocol == proto {
endps = ic . getEndpoints ( svc , & sp , proto , & healthcheck . Upstream { } )
break
}
2016-11-10 22:56:29 +00:00
}
}
} else {
// we need to use the TargetPort (where the endpoints are running)
2017-02-16 17:26:58 +00:00
glog . V ( 3 ) . Infof ( "searching service %v/%v endpoints using the target port '%v'" , svcNs , svcName , targetPort )
2016-11-10 22:56:29 +00:00
for _ , sp := range svc . Spec . Ports {
if sp . Port == int32 ( targetPort ) {
2017-08-07 03:22:06 +00:00
if sp . Protocol == proto {
endps = ic . getEndpoints ( svc , & sp , proto , & healthcheck . Upstream { } )
break
}
2016-11-10 22:56:29 +00:00
}
}
}
2017-02-16 17:26:58 +00:00
// stream services cannot contain empty upstreams and there is no
// default backend equivalent
2016-11-10 22:56:29 +00:00
if len ( endps ) == 0 {
2017-02-16 17:26:58 +00:00
glog . Warningf ( "service %v/%v does not have any active endpoints for port %v and protocol %v" , svcNs , svcName , svcPort , proto )
2016-11-10 22:56:29 +00:00
continue
}
2017-02-24 21:46:39 +00:00
svcs = append ( svcs , ingress . L4Service {
Port : externalPort ,
Backend : ingress . L4Backend {
2017-09-27 14:10:16 +00:00
Name : svcName ,
Namespace : svcNs ,
Port : intstr . FromString ( svcPort ) ,
Protocol : proto ,
ProxyProtocol : svcProxyProtocol ,
2017-02-24 21:46:39 +00:00
} ,
Endpoints : endps ,
2016-11-10 22:56:29 +00:00
} )
}
return svcs
}
// getDefaultUpstream returns an upstream associated with the
// default backend service. In case of error retrieving information
// configure the upstream to return http code 503.
2016-11-11 23:43:35 +00:00
func ( ic * GenericController ) getDefaultUpstream ( ) * ingress . Backend {
upstream := & ingress . Backend {
2016-11-10 22:56:29 +00:00
Name : defUpstreamName ,
}
svcKey := ic . cfg . DefaultService
2017-09-18 23:53:26 +00:00
svcObj , svcExists , err := ic . listers . Service . GetByKey ( svcKey )
2016-11-10 22:56:29 +00:00
if err != nil {
glog . Warningf ( "unexpected error searching the default backend %v: %v" , ic . cfg . DefaultService , err )
2017-08-24 13:33:26 +00:00
upstream . Endpoints = append ( upstream . Endpoints , ic . cfg . Backend . DefaultEndpoint ( ) )
2016-11-10 22:56:29 +00:00
return upstream
}
if ! svcExists {
2017-03-14 12:47:34 +00:00
glog . Warningf ( "service %v does not exist" , svcKey )
2017-08-24 13:33:26 +00:00
upstream . Endpoints = append ( upstream . Endpoints , ic . cfg . Backend . DefaultEndpoint ( ) )
2016-11-10 22:56:29 +00:00
return upstream
}
2017-09-17 18:42:31 +00:00
svc := svcObj . ( * apiv1 . Service )
endps := ic . getEndpoints ( svc , & svc . Spec . Ports [ 0 ] , apiv1 . ProtocolTCP , & healthcheck . Upstream { } )
2016-11-10 22:56:29 +00:00
if len ( endps ) == 0 {
glog . Warningf ( "service %v does not have any active endpoints" , svcKey )
2017-08-24 13:33:26 +00:00
endps = [ ] ingress . Endpoint { ic . cfg . Backend . DefaultEndpoint ( ) }
2016-11-10 22:56:29 +00:00
}
2017-08-11 02:51:32 +00:00
upstream . Service = svc
2016-11-11 23:43:35 +00:00
upstream . Endpoints = append ( upstream . Endpoints , endps ... )
2016-11-10 22:56:29 +00:00
return upstream
}
2016-11-11 23:43:35 +00:00
// getBackendServers returns a list of Upstream and Server to be used by the backend
2016-11-10 22:56:29 +00:00
// An upstream can be used in multiple servers if the namespace, service name and port are the same
2017-09-25 19:40:55 +00:00
func ( ic * GenericController ) getBackendServers ( ingresses [ ] * extensions . Ingress ) ( [ ] * ingress . Backend , [ ] * ingress . Server ) {
2017-09-18 23:53:26 +00:00
du := ic . getDefaultUpstream ( )
2017-09-25 19:40:55 +00:00
upstreams := ic . createUpstreams ( ingresses , du )
servers := ic . createServers ( ingresses , upstreams , du )
2016-11-10 22:56:29 +00:00
2017-09-25 19:40:55 +00:00
for _ , ing := range ingresses {
2017-06-26 01:30:30 +00:00
affinity := ic . annotations . SessionAffinity ( ing )
2016-12-29 20:02:06 +00:00
anns := ic . annotations . Extract ( ing )
2016-11-10 22:56:29 +00:00
for _ , rule := range ing . Spec . Rules {
host := rule . Host
if host == "" {
host = defServerName
}
server := servers [ host ]
if server == nil {
server = servers [ defServerName ]
}
if rule . HTTP == nil &&
host != defServerName {
2017-04-03 10:22:08 +00:00
glog . V ( 3 ) . Infof ( "ingress rule %v/%v does not contain HTTP rules, using default backend" , ing . Namespace , ing . Name )
2016-11-10 22:56:29 +00:00
continue
}
2017-08-22 20:16:59 +00:00
if server . CertificateAuth . CAFileName == "" {
ca := ic . annotations . CertificateAuth ( ing )
if ca != nil {
server . CertificateAuth = * ca
2017-09-27 03:46:22 +00:00
// It is possible that no CAFileName is found in the secret
if server . CertificateAuth . CAFileName == "" {
glog . V ( 3 ) . Infof ( "secret %v does not contain 'ca.crt', mutual authentication not enabled - ingress rule %v/%v." , server . CertificateAuth . Secret , ing . Namespace , ing . Name )
}
2017-08-22 20:16:59 +00:00
}
} else {
2017-09-27 03:46:22 +00:00
glog . V ( 3 ) . Infof ( "server %v already contains a mutual authentication configuration - ingress rule %v/%v" , server . Hostname , ing . Namespace , ing . Name )
2017-08-22 20:16:59 +00:00
}
2016-11-10 22:56:29 +00:00
for _ , path := range rule . HTTP . Paths {
upsName := fmt . Sprintf ( "%v-%v-%v" ,
ing . GetNamespace ( ) ,
path . Backend . ServiceName ,
path . Backend . ServicePort . String ( ) )
ups := upstreams [ upsName ]
// if there's no path defined we assume /
nginxPath := rootLocation
if path . Path != "" {
nginxPath = path . Path
}
addLoc := true
for _ , loc := range server . Locations {
if loc . Path == nginxPath {
addLoc = false
if ! loc . IsDefBackend {
2016-11-16 18:24:26 +00:00
glog . V ( 3 ) . Infof ( "avoiding replacement of ingress rule %v/%v location %v upstream %v (%v)" , ing . Namespace , ing . Name , loc . Path , ups . Name , loc . Backend )
2016-11-10 22:56:29 +00:00
break
}
2016-11-16 18:24:26 +00:00
glog . V ( 3 ) . Infof ( "replacing ingress rule %v/%v location %v upstream %v (%v)" , ing . Namespace , ing . Name , loc . Path , ups . Name , loc . Backend )
loc . Backend = ups . Name
2016-11-10 22:56:29 +00:00
loc . IsDefBackend = false
2016-11-16 18:24:26 +00:00
loc . Backend = ups . Name
2017-04-09 23:51:38 +00:00
loc . Port = ups . Port
loc . Service = ups . Service
2017-08-25 15:50:08 +00:00
loc . Ingress = ing
2016-12-29 20:02:06 +00:00
mergeLocationAnnotations ( loc , anns )
2017-08-19 21:13:02 +00:00
if loc . Redirect . FromToWWW {
server . RedirectFromToWWW = true
}
2016-11-10 22:56:29 +00:00
break
}
}
// is a new location
if addLoc {
2016-11-16 18:24:26 +00:00
glog . V ( 3 ) . Infof ( "adding location %v in ingress rule %v/%v upstream %v" , nginxPath , ing . Namespace , ing . Name , ups . Name )
2016-12-29 20:02:06 +00:00
loc := & ingress . Location {
2017-08-23 17:39:40 +00:00
Path : nginxPath ,
Backend : ups . Name ,
IsDefBackend : false ,
Service : ups . Service ,
Port : ups . Port ,
2017-08-25 15:50:08 +00:00
Ingress : ing ,
2016-12-29 20:02:06 +00:00
}
mergeLocationAnnotations ( loc , anns )
2017-08-19 21:13:02 +00:00
if loc . Redirect . FromToWWW {
server . RedirectFromToWWW = true
}
2016-12-29 20:02:06 +00:00
server . Locations = append ( server . Locations , loc )
2016-11-10 22:56:29 +00:00
}
2017-06-26 01:30:30 +00:00
if ups . SessionAffinity . AffinityType == "" {
ups . SessionAffinity . AffinityType = affinity . AffinityType
}
if affinity . AffinityType == "cookie" {
ups . SessionAffinity . CookieSessionAffinity . Name = affinity . CookieConfig . Name
ups . SessionAffinity . CookieSessionAffinity . Hash = affinity . CookieConfig . Hash
locs := ups . SessionAffinity . CookieSessionAffinity . Locations
if _ , ok := locs [ host ] ; ! ok {
locs [ host ] = [ ] string { }
}
locs [ host ] = append ( locs [ host ] , path . Path )
}
2016-11-10 22:56:29 +00:00
}
}
}
2017-08-25 15:50:08 +00:00
aUpstreams := make ( [ ] * ingress . Backend , 0 , len ( upstreams ) )
2017-04-02 02:32:22 +00:00
for _ , upstream := range upstreams {
isHTTPSfrom := [ ] * ingress . Server { }
for _ , server := range servers {
for _ , location := range server . Locations {
if upstream . Name == location . Backend {
2017-08-25 15:50:08 +00:00
if len ( upstream . Endpoints ) == 0 {
2017-10-09 12:16:09 +00:00
glog . V ( 3 ) . Infof ( "upstream %v does not have any active endpoints." , upstream . Name )
location . Backend = ""
2017-08-25 15:50:08 +00:00
// check if the location contains endpoints and a custom default backend
if location . DefaultBackend != nil {
sp := location . DefaultBackend . Spec . Ports [ 0 ]
2017-09-17 18:42:31 +00:00
endps := ic . getEndpoints ( location . DefaultBackend , & sp , apiv1 . ProtocolTCP , & healthcheck . Upstream { } )
2017-08-25 15:50:08 +00:00
if len ( endps ) > 0 {
glog . V ( 3 ) . Infof ( "using custom default backend in server %v location %v (service %v/%v)" ,
server . Hostname , location . Path , location . DefaultBackend . Namespace , location . DefaultBackend . Name )
b , err := cloner . DeepCopy ( upstream )
2017-08-25 23:49:44 +00:00
if err != nil {
glog . Errorf ( "unexpected error copying Upstream: %v" , err )
} else {
2017-08-25 15:50:08 +00:00
name := fmt . Sprintf ( "custom-default-backend-%v" , upstream . Name )
nb := b . ( * ingress . Backend )
nb . Name = name
nb . Endpoints = endps
aUpstreams = append ( aUpstreams , nb )
location . Backend = name
}
}
}
}
// Configure Backends[].SSLPassthrough
2017-04-02 02:32:22 +00:00
if server . SSLPassthrough {
if location . Path == rootLocation {
if location . Backend == defUpstreamName {
glog . Warningf ( "ignoring ssl passthrough of %v as it doesn't have a default backend (root context)" , server . Hostname )
2017-04-09 23:51:38 +00:00
continue
2017-04-02 02:32:22 +00:00
}
2017-04-09 23:51:38 +00:00
isHTTPSfrom = append ( isHTTPSfrom , server )
2017-04-02 02:32:22 +00:00
}
}
}
}
}
2017-08-25 15:50:08 +00:00
2017-04-02 02:32:22 +00:00
if len ( isHTTPSfrom ) > 0 {
2017-04-09 23:51:38 +00:00
upstream . SSLPassthrough = true
2017-04-02 02:32:22 +00:00
}
}
2017-08-25 15:50:08 +00:00
// create the list of upstreams and skip those without endpoints
for _ , upstream := range upstreams {
if len ( upstream . Endpoints ) == 0 {
continue
2016-11-10 22:56:29 +00:00
}
2017-08-25 15:50:08 +00:00
aUpstreams = append ( aUpstreams , upstream )
2016-11-10 22:56:29 +00:00
}
2017-08-25 15:50:08 +00:00
2017-07-13 00:35:36 +00:00
if ic . cfg . SortBackends {
2017-09-25 19:40:55 +00:00
sort . SliceStable ( aUpstreams , func ( a , b int ) bool {
2017-09-18 23:53:26 +00:00
return aUpstreams [ a ] . Name < aUpstreams [ b ] . Name
} )
2017-07-13 00:35:36 +00:00
}
2016-11-10 22:56:29 +00:00
aServers := make ( [ ] * ingress . Server , 0 , len ( servers ) )
for _ , value := range servers {
2017-09-25 19:40:55 +00:00
sort . SliceStable ( value . Locations , func ( i , j int ) bool {
2017-09-18 23:53:26 +00:00
return value . Locations [ i ] . Path > value . Locations [ j ] . Path
} )
2016-11-10 22:56:29 +00:00
aServers = append ( aServers , value )
}
2017-09-18 23:53:26 +00:00
2017-09-25 19:40:55 +00:00
sort . SliceStable ( aServers , func ( i , j int ) bool {
2017-09-18 23:53:26 +00:00
return aServers [ i ] . Hostname < aServers [ j ] . Hostname
} )
2016-11-10 22:56:29 +00:00
return aUpstreams , aServers
}
2017-09-27 03:46:22 +00:00
// GetAuthCertificate is used by the auth-tls annotations to get a cert from a secret
2017-10-01 00:48:14 +00:00
func ( ic GenericController ) GetAuthCertificate ( name string ) ( * resolver . AuthSSLCert , error ) {
if _ , exists := ic . sslCertTracker . Get ( name ) ; ! exists {
ic . syncSecret ( name )
2017-05-23 17:20:31 +00:00
}
2017-10-01 00:48:14 +00:00
_ , err := ic . listers . Secret . GetByName ( name )
2017-02-06 18:16:36 +00:00
if err != nil {
return & resolver . AuthSSLCert { } , fmt . Errorf ( "unexpected error: %v" , err )
}
2017-10-01 00:48:14 +00:00
bc , exists := ic . sslCertTracker . Get ( name )
2016-11-10 22:56:29 +00:00
if ! exists {
2017-10-01 00:48:14 +00:00
return & resolver . AuthSSLCert { } , fmt . Errorf ( "secret %v does not exist" , name )
2016-11-10 22:56:29 +00:00
}
cert := bc . ( * ingress . SSLCert )
2017-01-10 12:16:18 +00:00
return & resolver . AuthSSLCert {
2017-10-01 00:48:14 +00:00
Secret : name ,
2017-02-06 18:16:36 +00:00
CAFileName : cert . CAFileName ,
PemSHA : cert . PemSHA ,
2016-11-10 22:56:29 +00:00
} , nil
}
// createUpstreams creates the NGINX upstreams for each service referenced in
// Ingress rules. The servers inside the upstream are endpoints.
2017-09-25 19:40:55 +00:00
func ( ic * GenericController ) createUpstreams ( data [ ] * extensions . Ingress , du * ingress . Backend ) map [ string ] * ingress . Backend {
2016-11-11 23:43:35 +00:00
upstreams := make ( map [ string ] * ingress . Backend )
2017-09-18 23:53:26 +00:00
upstreams [ defUpstreamName ] = du
2016-11-10 22:56:29 +00:00
2017-09-25 19:40:55 +00:00
for _ , ing := range data {
2016-12-29 20:02:06 +00:00
secUpstream := ic . annotations . SecureUpstream ( ing )
hz := ic . annotations . HealthCheck ( ing )
2017-07-16 20:19:45 +00:00
serviceUpstream := ic . annotations . ServiceUpstream ( ing )
2017-09-30 21:29:16 +00:00
upstreamHashBy := ic . annotations . UpstreamHashBy ( ing )
2016-11-10 22:56:29 +00:00
var defBackend string
if ing . Spec . Backend != nil {
defBackend = fmt . Sprintf ( "%v-%v-%v" ,
ing . GetNamespace ( ) ,
ing . Spec . Backend . ServiceName ,
ing . Spec . Backend . ServicePort . String ( ) )
glog . V ( 3 ) . Infof ( "creating upstream %v" , defBackend )
upstreams [ defBackend ] = newUpstream ( defBackend )
svcKey := fmt . Sprintf ( "%v/%v" , ing . GetNamespace ( ) , ing . Spec . Backend . ServiceName )
2017-07-16 20:19:45 +00:00
2017-09-13 19:15:47 +00:00
// Add the service cluster endpoint as the upstream instead of individual endpoints
// if the serviceUpstream annotation is enabled
if serviceUpstream {
endpoint , err := ic . getServiceClusterEndpoint ( svcKey , ing . Spec . Backend )
if err != nil {
glog . Errorf ( "Failed to get service cluster endpoint for service %s: %v" , svcKey , err )
} else {
upstreams [ defBackend ] . Endpoints = [ ] ingress . Endpoint { endpoint }
}
}
if len ( upstreams [ defBackend ] . Endpoints ) == 0 {
endps , err := ic . serviceEndpoints ( svcKey , ing . Spec . Backend . ServicePort . String ( ) , hz )
upstreams [ defBackend ] . Endpoints = append ( upstreams [ defBackend ] . Endpoints , endps ... )
if err != nil {
glog . Warningf ( "error creating upstream %v: %v" , defBackend , err )
}
}
2016-11-10 22:56:29 +00:00
}
for _ , rule := range ing . Spec . Rules {
if rule . HTTP == nil {
continue
}
for _ , path := range rule . HTTP . Paths {
name := fmt . Sprintf ( "%v-%v-%v" ,
ing . GetNamespace ( ) ,
path . Backend . ServiceName ,
path . Backend . ServicePort . String ( ) )
2017-04-09 23:51:38 +00:00
if _ , ok := upstreams [ name ] ; ok {
continue
2016-11-10 22:56:29 +00:00
}
2017-04-09 23:51:38 +00:00
glog . V ( 3 ) . Infof ( "creating upstream %v" , name )
upstreams [ name ] = newUpstream ( name )
2017-08-11 02:51:32 +00:00
upstreams [ name ] . Port = path . Backend . ServicePort
2017-04-09 23:51:38 +00:00
if ! upstreams [ name ] . Secure {
2017-05-14 22:14:27 +00:00
upstreams [ name ] . Secure = secUpstream . Secure
}
2017-06-16 00:43:17 +00:00
2017-05-14 22:14:27 +00:00
if upstreams [ name ] . SecureCACert . Secret == "" {
upstreams [ name ] . SecureCACert = secUpstream . CACert
2016-11-16 18:24:26 +00:00
}
2017-06-16 00:43:17 +00:00
2017-09-30 21:29:16 +00:00
if upstreams [ name ] . UpstreamHashBy == "" {
upstreams [ name ] . UpstreamHashBy = upstreamHashBy
}
2017-04-09 23:51:38 +00:00
svcKey := fmt . Sprintf ( "%v/%v" , ing . GetNamespace ( ) , path . Backend . ServiceName )
2017-07-16 20:19:45 +00:00
2017-09-13 19:15:47 +00:00
// Add the service cluster endpoint as the upstream instead of individual endpoints
// if the serviceUpstream annotation is enabled
if serviceUpstream {
endpoint , err := ic . getServiceClusterEndpoint ( svcKey , & path . Backend )
if err != nil {
glog . Errorf ( "failed to get service cluster endpoint for service %s: %v" , svcKey , err )
} else {
upstreams [ name ] . Endpoints = [ ] ingress . Endpoint { endpoint }
}
}
if len ( upstreams [ name ] . Endpoints ) == 0 {
endp , err := ic . serviceEndpoints ( svcKey , path . Backend . ServicePort . String ( ) , hz )
if err != nil {
glog . Warningf ( "error obtaining service endpoints: %v" , err )
continue
}
upstreams [ name ] . Endpoints = endp
}
2017-04-09 23:51:38 +00:00
2017-09-18 23:53:26 +00:00
s , err := ic . listers . Service . GetByName ( svcKey )
2017-04-09 23:51:38 +00:00
if err != nil {
glog . Warningf ( "error obtaining service: %v" , err )
continue
}
2017-09-18 23:53:26 +00:00
upstreams [ name ] . Service = s
2016-11-10 22:56:29 +00:00
}
}
}
return upstreams
}
2017-07-16 20:19:45 +00:00
func ( ic * GenericController ) getServiceClusterEndpoint ( svcKey string , backend * extensions . IngressBackend ) ( endpoint ingress . Endpoint , err error ) {
2017-09-18 23:53:26 +00:00
svcObj , svcExists , err := ic . listers . Service . GetByKey ( svcKey )
2017-07-16 20:19:45 +00:00
if ! svcExists {
return endpoint , fmt . Errorf ( "service %v does not exist" , svcKey )
}
2017-09-17 18:42:31 +00:00
svc := svcObj . ( * apiv1 . Service )
2017-07-16 20:19:45 +00:00
if svc . Spec . ClusterIP == "" {
return endpoint , fmt . Errorf ( "No ClusterIP found for service %s" , svcKey )
}
endpoint . Address = svc . Spec . ClusterIP
endpoint . Port = backend . ServicePort . String ( )
return endpoint , err
}
2016-11-10 22:56:29 +00:00
// serviceEndpoints returns the upstream servers (endpoints) associated
// to a service.
2017-09-13 19:15:47 +00:00
func ( ic * GenericController ) serviceEndpoints ( svcKey , backendPort string ,
2016-11-11 23:43:35 +00:00
hz * healthcheck . Upstream ) ( [ ] ingress . Endpoint , error ) {
2017-09-18 23:53:26 +00:00
svc , err := ic . listers . Service . GetByName ( svcKey )
2017-09-13 19:15:47 +00:00
var upstreams [ ] ingress . Endpoint
2016-11-10 22:56:29 +00:00
if err != nil {
return upstreams , fmt . Errorf ( "error getting service %v from the cache: %v" , svcKey , err )
}
glog . V ( 3 ) . Infof ( "obtaining port information for service %v" , svcKey )
for _ , servicePort := range svc . Spec . Ports {
// targetPort could be a string, use the name or the port (int)
if strconv . Itoa ( int ( servicePort . Port ) ) == backendPort ||
servicePort . TargetPort . String ( ) == backendPort ||
servicePort . Name == backendPort {
2017-09-17 18:42:31 +00:00
endps := ic . getEndpoints ( svc , & servicePort , apiv1 . ProtocolTCP , hz )
2016-11-10 22:56:29 +00:00
if len ( endps ) == 0 {
glog . Warningf ( "service %v does not have any active endpoints" , svcKey )
}
2017-07-13 00:35:36 +00:00
if ic . cfg . SortBackends {
2017-09-25 19:40:55 +00:00
sort . SliceStable ( endps , func ( i , j int ) bool {
2017-09-18 23:53:26 +00:00
iName := endps [ i ] . Address
jName := endps [ j ] . Address
if iName != jName {
return iName < jName
}
return endps [ i ] . Port < endps [ j ] . Port
} )
2017-07-13 00:35:36 +00:00
}
2016-11-10 22:56:29 +00:00
upstreams = append ( upstreams , endps ... )
break
}
}
2017-07-13 00:35:36 +00:00
if ! ic . cfg . SortBackends {
rand . Seed ( time . Now ( ) . UnixNano ( ) )
for i := range upstreams {
j := rand . Intn ( i + 1 )
upstreams [ i ] , upstreams [ j ] = upstreams [ j ] , upstreams [ i ]
}
2017-06-14 21:33:12 +00:00
}
2016-11-10 22:56:29 +00:00
return upstreams , nil
}
2017-02-16 17:26:58 +00:00
// createServers initializes a map that contains information about the list of
// FDQN referenced by ingress rules and the common name field in the referenced
// SSL certificates. Each server is configured with location / using a default
// backend specified by the user or the one inside the ingress spec.
2017-09-25 19:40:55 +00:00
func ( ic * GenericController ) createServers ( data [ ] * extensions . Ingress ,
2017-09-18 23:53:26 +00:00
upstreams map [ string ] * ingress . Backend ,
du * ingress . Backend ) map [ string ] * ingress . Server {
servers := make ( map [ string ] * ingress . Server , len ( data ) )
// If a server has a hostname equivalent to a pre-existing alias, then we
// remove the alias to avoid conflicts.
aliases := make ( map [ string ] string , len ( data ) )
2017-09-13 19:15:47 +00:00
bdef := ic . GetDefaultBackend ( )
2017-10-06 03:28:21 +00:00
ngxProxy := proxy . Configuration {
2017-09-14 14:42:22 +00:00
BodySize : bdef . ProxyBodySize ,
ConnectTimeout : bdef . ProxyConnectTimeout ,
SendTimeout : bdef . ProxySendTimeout ,
ReadTimeout : bdef . ProxyReadTimeout ,
BufferSize : bdef . ProxyBufferSize ,
CookieDomain : bdef . ProxyCookieDomain ,
CookiePath : bdef . ProxyCookiePath ,
NextUpstream : bdef . ProxyNextUpstream ,
RequestBuffering : bdef . ProxyRequestBuffering ,
2016-12-29 20:02:06 +00:00
}
2017-09-27 03:46:22 +00:00
// generated on Start() with createDefaultSSLCertificate()
2017-08-26 03:46:17 +00:00
defaultPemFileName := fakeCertificatePath
defaultPemSHA := fakeCertificateSHA
2017-03-06 19:29:33 +00:00
2017-09-27 03:46:22 +00:00
// Tries to fetch the default Certificate from nginx configuration.
// If it does not exists, use the ones generated on Start()
2017-01-26 18:51:55 +00:00
defaultCertificate , err := ic . getPemCertificate ( ic . cfg . DefaultSSLCertificate )
2017-08-26 03:46:17 +00:00
if err == nil {
2017-01-26 18:51:55 +00:00
defaultPemFileName = defaultCertificate . PemFileName
defaultPemSHA = defaultCertificate . PemSHA
}
2017-02-16 17:26:58 +00:00
// initialize the default server
2016-11-10 22:56:29 +00:00
servers [ defServerName ] = & ingress . Server {
2017-01-26 18:51:55 +00:00
Hostname : defServerName ,
SSLCertificate : defaultPemFileName ,
SSLPemChecksum : defaultPemSHA ,
2016-11-10 22:56:29 +00:00
Locations : [ ] * ingress . Location {
{
Path : rootLocation ,
IsDefBackend : true ,
2017-08-11 02:51:32 +00:00
Backend : du . Name ,
2016-11-10 22:56:29 +00:00
Proxy : ngxProxy ,
2017-08-11 02:51:32 +00:00
Service : du . Service ,
2016-11-10 22:56:29 +00:00
} ,
2017-09-13 19:15:47 +00:00
} }
2016-11-10 22:56:29 +00:00
// initialize all the servers
2017-09-25 19:40:55 +00:00
for _ , ing := range data {
2017-02-14 14:02:23 +00:00
2016-11-10 22:56:29 +00:00
// check if ssl passthrough is configured
2016-12-29 20:02:06 +00:00
sslpt := ic . annotations . SSLPassthrough ( ing )
2017-09-17 14:54:00 +00:00
// default upstream server
2017-08-11 02:51:32 +00:00
un := du . Name
2017-09-17 14:54:00 +00:00
2017-02-16 17:26:58 +00:00
if ing . Spec . Backend != nil {
// replace default backend
2017-09-13 19:15:47 +00:00
defUpstream := fmt . Sprintf ( "%v-%v-%v" , ing . GetNamespace ( ) , ing . Spec . Backend . ServiceName , ing . Spec . Backend . ServicePort . String ( ) )
2017-02-16 17:26:58 +00:00
if backendUpstream , ok := upstreams [ defUpstream ] ; ok {
2017-08-11 02:51:32 +00:00
un = backendUpstream . Name
2017-09-17 14:54:00 +00:00
// Special case:
// ingress only with a backend and no rules
// this case defines a "catch all" server
defLoc := servers [ defServerName ] . Locations [ 0 ]
if defLoc . IsDefBackend && len ( ing . Spec . Rules ) == 0 {
defLoc . IsDefBackend = false
defLoc . Backend = backendUpstream . Name
defLoc . Service = backendUpstream . Service
}
2017-02-16 17:26:58 +00:00
}
}
2016-11-10 22:56:29 +00:00
for _ , rule := range ing . Spec . Rules {
host := rule . Host
if host == "" {
host = defServerName
}
if _ , ok := servers [ host ] ; ok {
// server already configured
continue
}
2017-04-09 23:51:38 +00:00
2016-11-10 22:56:29 +00:00
servers [ host ] = & ingress . Server {
2016-11-16 18:24:26 +00:00
Hostname : host ,
2016-11-10 22:56:29 +00:00
Locations : [ ] * ingress . Location {
{
2017-08-23 17:39:40 +00:00
Path : rootLocation ,
IsDefBackend : true ,
Backend : un ,
Proxy : ngxProxy ,
2017-09-17 18:42:31 +00:00
Service : & apiv1 . Service { } ,
2016-11-10 22:56:29 +00:00
} ,
2017-09-18 23:53:26 +00:00
} ,
SSLPassthrough : sslpt ,
}
2016-11-10 22:56:29 +00:00
}
}
2017-08-10 03:22:54 +00:00
// configure default location, alias, and SSL
2017-09-25 19:40:55 +00:00
for _ , ing := range data {
2017-08-15 06:23:19 +00:00
// setup server-alias based on annotations
2017-08-10 03:22:54 +00:00
aliasAnnotation := ic . annotations . Alias ( ing )
2017-09-27 06:59:10 +00:00
srvsnippet := ic . annotations . ServerSnippet ( ing )
2017-08-10 03:22:54 +00:00
2016-11-10 22:56:29 +00:00
for _ , rule := range ing . Spec . Rules {
host := rule . Host
if host == "" {
host = defServerName
}
2017-08-10 03:22:54 +00:00
// setup server aliases
2017-08-15 06:23:19 +00:00
servers [ host ] . Alias = aliasAnnotation
2017-09-18 23:53:26 +00:00
if aliasAnnotation != "" {
if _ , ok := aliases [ aliasAnnotation ] ; ! ok {
aliases [ aliasAnnotation ] = host
}
}
2017-08-10 03:22:54 +00:00
2017-09-27 06:59:10 +00:00
//notifying the user that it has already been configured.
if servers [ host ] . ServerSnippet != "" && srvsnippet != "" {
glog . Warningf ( "ingress %v/%v for host %v contains a Server Snippet section that it has already been configured." ,
ing . Namespace , ing . Name , host )
}
// only add a server snippet if the server does not have one previously configured
if servers [ host ] . ServerSnippet == "" && srvsnippet != "" {
servers [ host ] . ServerSnippet = srvsnippet
}
2016-11-24 00:14:14 +00:00
// only add a certificate if the server does not have one previously configured
2017-08-29 19:40:03 +00:00
if servers [ host ] . SSLCertificate != "" {
continue
}
if len ( ing . Spec . TLS ) == 0 {
glog . V ( 3 ) . Infof ( "ingress %v/%v for host %v does not contains a TLS section" , ing . Namespace , ing . Name , host )
2017-07-12 17:31:15 +00:00
continue
}
2016-12-25 19:48:10 +00:00
2017-07-12 17:31:15 +00:00
tlsSecretName := ""
found := false
for _ , tls := range ing . Spec . TLS {
if sets . NewString ( tls . Hosts ... ) . Has ( host ) {
tlsSecretName = tls . SecretName
found = true
break
2017-03-26 12:28:59 +00:00
}
2017-07-12 17:31:15 +00:00
}
2017-03-26 12:28:59 +00:00
2017-07-12 17:31:15 +00:00
if ! found {
2017-08-29 19:40:03 +00:00
glog . Warningf ( "ingress %v/%v for host %v contains a TLS section but none of the host match" ,
ing . Namespace , ing . Name , host )
2017-07-12 17:31:15 +00:00
continue
}
2017-03-16 15:07:07 +00:00
2017-07-12 17:31:15 +00:00
if tlsSecretName == "" {
2017-07-27 16:57:34 +00:00
glog . V ( 3 ) . Infof ( "host %v is listed on tls section but secretName is empty. Using default cert" , host )
2017-07-12 17:31:15 +00:00
servers [ host ] . SSLCertificate = defaultPemFileName
servers [ host ] . SSLPemChecksum = defaultPemSHA
continue
}
2017-06-12 12:45:08 +00:00
2017-07-12 17:31:15 +00:00
key := fmt . Sprintf ( "%v/%v" , ing . Namespace , tlsSecretName )
bc , exists := ic . sslCertTracker . Get ( key )
if ! exists {
2017-08-29 19:40:03 +00:00
glog . Warningf ( "ssl certificate \"%v\" does not exist in local store" , key )
2017-07-12 17:31:15 +00:00
continue
}
2017-05-12 00:50:43 +00:00
2017-07-12 17:31:15 +00:00
cert := bc . ( * ingress . SSLCert )
2017-08-10 03:27:57 +00:00
err = cert . Certificate . VerifyHostname ( host )
if err != nil {
glog . Warningf ( "ssl certificate %v does not contain a Common Name or Subject Alternative Name for host %v" , key , host )
2017-07-12 17:31:15 +00:00
continue
}
2017-05-12 00:50:43 +00:00
2017-07-12 17:31:15 +00:00
servers [ host ] . SSLCertificate = cert . PemFileName
2017-10-04 20:11:03 +00:00
servers [ host ] . SSLFullChainCertificate = cert . FullChainPemFileName
2017-07-12 17:31:15 +00:00
servers [ host ] . SSLPemChecksum = cert . PemSHA
servers [ host ] . SSLExpireTime = cert . ExpireTime
if cert . ExpireTime . Before ( time . Now ( ) . Add ( 240 * time . Hour ) ) {
glog . Warningf ( "ssl certificate for host %v is about to expire in 10 days" , host )
2016-11-10 22:56:29 +00:00
}
}
}
2017-09-18 23:53:26 +00:00
for alias , host := range aliases {
if _ , ok := servers [ alias ] ; ok {
glog . Warningf ( "There is a conflict with server hostname '%v' and alias '%v' (in server %v). Removing alias to avoid conflicts." , alias , host )
servers [ host ] . Alias = ""
}
}
2017-09-20 09:35:16 +00:00
2016-11-10 22:56:29 +00:00
return servers
}
// getEndpoints returns a list of <endpoint ip>:<port> for a given service/target port combination.
func ( ic * GenericController ) getEndpoints (
2017-09-17 18:42:31 +00:00
s * apiv1 . Service ,
servicePort * apiv1 . ServicePort ,
proto apiv1 . Protocol ,
2017-09-13 19:15:47 +00:00
hz * healthcheck . Upstream ) [ ] ingress . Endpoint {
2016-11-10 22:56:29 +00:00
2016-11-11 23:43:35 +00:00
upsServers := [ ] ingress . Endpoint { }
2016-11-10 22:56:29 +00:00
2017-03-16 11:20:52 +00:00
// avoid duplicated upstream servers when the service
// contains multiple port definitions sharing the same
// targetport.
2017-06-14 23:49:35 +00:00
adus := make ( map [ string ] bool )
2017-03-16 11:20:52 +00:00
2017-04-20 02:30:51 +00:00
// ExternalName services
2017-09-17 18:42:31 +00:00
if s . Spec . Type == apiv1 . ServiceTypeExternalName {
2017-06-13 03:02:28 +00:00
targetPort := servicePort . TargetPort . IntValue ( )
2017-04-20 02:30:51 +00:00
// check for invalid port value
if targetPort <= 0 {
return upsServers
}
2017-09-24 20:36:59 +00:00
if net . ParseIP ( s . Spec . ExternalName ) == nil {
_ , err := net . LookupHost ( s . Spec . ExternalName )
if err != nil {
glog . Errorf ( "unexpected error resolving host %v: %v" , s . Spec . ExternalName , err )
return upsServers
}
}
2017-04-20 02:30:51 +00:00
return append ( upsServers , ingress . Endpoint {
Address : s . Spec . ExternalName ,
Port : fmt . Sprintf ( "%v" , targetPort ) ,
MaxFails : hz . MaxFails ,
FailTimeout : hz . FailTimeout ,
} )
}
glog . V ( 3 ) . Infof ( "getting endpoints for service %v/%v and port %v" , s . Namespace , s . Name , servicePort . String ( ) )
2017-09-18 23:53:26 +00:00
ep , err := ic . listers . Endpoint . GetServiceEndpoints ( s )
2017-04-20 02:30:51 +00:00
if err != nil {
glog . Warningf ( "unexpected error obtaining service endpoints: %v" , err )
return upsServers
}
2016-11-10 22:56:29 +00:00
for _ , ss := range ep . Subsets {
for _ , epPort := range ss . Ports {
if ! reflect . DeepEqual ( epPort . Protocol , proto ) {
continue
}
var targetPort int32
2017-06-13 03:02:28 +00:00
if servicePort . Name == "" {
// ServicePort.Name is optional if there is only one port
targetPort = epPort . Port
} else if servicePort . Name == epPort . Name {
targetPort = epPort . Port
2016-11-10 22:56:29 +00:00
}
// check for invalid port value
2016-12-29 22:59:56 +00:00
if targetPort <= 0 {
2016-11-10 22:56:29 +00:00
continue
}
for _ , epAddress := range ss . Addresses {
2017-03-16 11:20:52 +00:00
ep := fmt . Sprintf ( "%v:%v" , epAddress . IP , targetPort )
if _ , exists := adus [ ep ] ; exists {
continue
}
2016-11-11 23:43:35 +00:00
ups := ingress . Endpoint {
2016-11-10 22:56:29 +00:00
Address : epAddress . IP ,
Port : fmt . Sprintf ( "%v" , targetPort ) ,
MaxFails : hz . MaxFails ,
FailTimeout : hz . FailTimeout ,
2017-07-29 13:27:56 +00:00
Target : epAddress . TargetRef ,
2016-11-10 22:56:29 +00:00
}
upsServers = append ( upsServers , ups )
2017-03-16 11:20:52 +00:00
adus [ ep ] = true
2016-11-10 22:56:29 +00:00
}
}
}
glog . V ( 3 ) . Infof ( "endpoints found: %v" , upsServers )
return upsServers
}
2017-07-27 17:46:33 +00:00
// readSecrets extracts information about secrets from an Ingress rule
func ( ic * GenericController ) readSecrets ( ing * extensions . Ingress ) {
2017-04-09 16:52:10 +00:00
for _ , tls := range ing . Spec . TLS {
2017-05-05 15:28:01 +00:00
if tls . SecretName == "" {
continue
}
2017-04-09 16:52:10 +00:00
key := fmt . Sprintf ( "%v/%v" , ing . Namespace , tls . SecretName )
2017-07-27 17:46:33 +00:00
ic . syncSecret ( key )
}
key , _ := parser . GetStringAnnotation ( "ingress.kubernetes.io/auth-tls-secret" , ing )
if key == "" {
return
2017-04-09 16:52:10 +00:00
}
2017-07-27 17:46:33 +00:00
ic . syncSecret ( key )
2017-04-09 16:52:10 +00:00
}
2016-11-10 22:56:29 +00:00
// Stop stops the loadbalancer controller.
func ( ic GenericController ) Stop ( ) error {
ic . stopLock . Lock ( )
defer ic . stopLock . Unlock ( )
// Only try draining the workqueue if we haven't already.
if ! ic . syncQueue . IsShuttingDown ( ) {
glog . Infof ( "shutting down controller queues" )
close ( ic . stopCh )
go ic . syncQueue . Shutdown ( )
2017-01-20 22:01:37 +00:00
if ic . syncStatus != nil {
ic . syncStatus . Shutdown ( )
}
2016-11-10 22:56:29 +00:00
return nil
}
return fmt . Errorf ( "shutdown already in progress" )
}
// Start starts the Ingress controller.
2017-09-19 20:09:33 +00:00
func ( ic * GenericController ) Start ( ) {
2016-11-10 22:56:29 +00:00
glog . Infof ( "starting Ingress controller" )
2017-10-01 00:48:14 +00:00
ic . cacheController . Run ( ic . stopCh )
2017-09-30 23:42:42 +00:00
2017-10-01 00:48:14 +00:00
createDefaultSSLCertificate ( )
2017-05-29 16:22:30 +00:00
2017-10-01 00:48:14 +00:00
time . Sleep ( 5 * time . Second )
2017-08-21 20:18:30 +00:00
// initial sync of secrets to avoid unnecessary reloads
2017-10-01 00:48:14 +00:00
glog . Info ( "running initial sync of secret" )
for _ , obj := range ic . listers . Ingress . List ( ) {
ing := obj . ( * extensions . Ingress )
2017-08-21 20:18:30 +00:00
2017-10-01 00:48:14 +00:00
if ! class . IsValid ( ing , ic . cfg . IngressClass , ic . cfg . DefaultIngressClass ) {
a , _ := parser . GetStringAnnotation ( class . IngressKey , ing )
glog . Infof ( "ignoring add for ingress %v based on annotation %v with value %v" , ing . Name , class . IngressKey , a )
continue
}
ic . readSecrets ( ing )
}
2017-08-26 03:46:17 +00:00
2017-08-04 18:22:06 +00:00
go ic . syncQueue . Run ( time . Second , ic . stopCh )
2016-11-10 22:56:29 +00:00
2017-01-20 22:01:37 +00:00
if ic . syncStatus != nil {
go ic . syncStatus . Run ( ic . stopCh )
}
2016-11-10 22:56:29 +00:00
2017-10-01 00:48:14 +00:00
go wait . Until ( ic . checkMissingSecrets , 30 * time . Second , ic . stopCh )
2017-09-18 23:53:26 +00:00
// force initial sync
ic . syncQueue . Enqueue ( & extensions . Ingress { } )
2016-11-10 22:56:29 +00:00
<- ic . stopCh
}
2017-08-26 03:46:17 +00:00
2017-09-19 20:09:33 +00:00
func ( ic * GenericController ) isForceReload ( ) bool {
return atomic . LoadInt32 ( & ic . forceReload ) != 0
}
2017-10-08 15:06:37 +00:00
func ( ic * GenericController ) SetForceReload ( shouldReload bool ) {
2017-09-19 20:09:33 +00:00
if shouldReload {
atomic . StoreInt32 ( & ic . forceReload , 1 )
2017-10-08 15:06:37 +00:00
ic . syncQueue . Enqueue ( & extensions . Ingress { } )
2017-09-19 20:09:33 +00:00
} else {
atomic . StoreInt32 ( & ic . forceReload , 0 )
}
}
2017-08-26 03:46:17 +00:00
func createDefaultSSLCertificate ( ) {
defCert , defKey := ssl . GetFakeSSLCert ( )
c , err := ssl . AddOrUpdateCertAndKey ( fakeCertificate , defCert , defKey , [ ] byte { } )
if err != nil {
glog . Fatalf ( "Error generating self signed certificate: %v" , err )
}
fakeCertificateSHA = c . PemSHA
fakeCertificatePath = c . PemFileName
}