ingress-nginx-helm/pkg/ingress/controller/controller.go

1320 lines
38 KiB
Go
Raw Normal View History

/*
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"
"math/rand"
2017-09-24 20:36:59 +00:00
"net"
"reflect"
"sort"
"strconv"
"strings"
"sync"
"sync/atomic"
"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"
"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"
)
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"
)
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-11 23:43:35 +00:00
// GenericController holds the boilerplate code required to build an Ingress controlller.
type GenericController struct {
cfg *Configuration
2017-10-01 00:48:14 +00:00
listers *ingress.StoreLister
cacheController *cacheController
annotations annotationExtractor
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)
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{}
// runningConfig contains the running configuration in the Backend
runningConfig *ingress.Configuration
forceReload int32
}
// Configuration contains all the settings required by an Ingress controller
type Configuration struct {
Client clientset.Interface
ResyncPeriod time.Duration
DefaultService string
IngressClass string
Namespace string
ConfigMapName string
2017-04-13 01:50:54 +00:00
ForceNamespaceIsolation bool
DisableNodeList bool
2017-04-13 01:50:54 +00:00
// optional
TCPConfigMapName string
// optional
UDPConfigMapName string
DefaultSSLCertificate string
DefaultHealthzURL string
2017-03-02 15:50:31 +00:00
DefaultIngressClass string
// optional
PublishService string
2016-11-11 23:43:35 +00:00
// Backend is the particular implementation to be used.
// (for instance NGINX)
Backend ingress.Controller
UpdateStatus bool
UseNodeInternalIP bool
ElectionID string
UpdateStatusOnShutdown bool
SortBackends bool
}
// newIngressController creates an Ingress controller
2016-11-11 23:43:35 +00:00
func newIngressController(config *Configuration) *GenericController {
eventBroadcaster := record.NewBroadcaster()
eventBroadcaster.StartLogging(glog.Infof)
2017-09-17 18:42:31 +00:00
eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{
Interface: config.Client.CoreV1().Events(config.Namespace),
})
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{
Component: "ingress-controller",
}),
sslCertTracker: newSSLCertTracker(),
}
2017-04-09 16:52:10 +00:00
ic.syncQueue = task.NewTaskQueue(ic.syncIngress)
2017-10-01 00:48:14 +00:00
ic.listers, ic.cacheController = ic.createListers(config.DisableNodeList)
if config.UpdateStatus {
ic.syncStatus = status.NewStatusSyncer(status.Config{
Client: config.Client,
PublishService: ic.cfg.PublishService,
IngressLister: ic.listers.Ingress,
ElectionID: config.ElectionID,
IngressClass: config.IngressClass,
DefaultIngressClass: config.DefaultIngressClass,
UpdateStatusOnShutdown: config.UpdateStatusOnShutdown,
CustomIngressStatus: ic.cfg.Backend.UpdateIngressStatus,
UseNodeInternalIP: ic.cfg.UseNodeInternalIP,
})
} else {
glog.Warning("Update of ingress status is disabled (flag --update-status=false was specified)")
}
ic.annotations = newAnnotationExtractor(ic)
ic.cfg.Backend.SetListers(ic.listers)
2017-08-25 15:50:08 +00:00
cloner.RegisterDeepCopyFunc(ingress.GetGeneratedDeepCopyFuncs)
2016-11-11 23:43:35 +00:00
return &ic
}
// Info returns information about the backend
2016-11-16 18:24:26 +00:00
func (ic GenericController) Info() *ingress.BackendInfo {
return ic.cfg.Backend.Info()
}
// IngressClass returns information about the backend
func (ic GenericController) IngressClass() string {
return ic.cfg.IngressClass
}
// 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 {
s, err := ic.listers.Service.GetByName(ic.cfg.PublishService)
2017-09-17 16:34:29 +00:00
if err != nil {
return nil
}
return s
}
// GetRecorder returns the event recorder
2017-08-25 15:50:08 +00:00
func (ic GenericController) GetRecorder() record.EventRecorder {
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) {
return ic.listers.Secret.GetByName(name)
}
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) {
return ic.listers.Service.GetByName(name)
}
// 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 {
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
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)
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,
Service: loc.Service,
Port: loc.Port,
})
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,
}
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)")
return nil
}
glog.Infof("backend reload required")
2017-09-13 19:15:47 +00:00
err := ic.cfg.Backend.OnUpdate(pcfg)
if err != nil {
incReloadErrorCount()
2017-06-11 19:56:30 +00:00
glog.Errorf("unexpected failure restarting the backend: \n%v", err)
return err
}
2017-06-11 19:56:30 +00:00
glog.Infof("ingress backend successfully reloaded...")
incReloadCount()
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)
return nil
}
2017-09-17 18:42:31 +00:00
func (ic *GenericController) getStreamServices(configmapName string, proto apiv1.Protocol) []ingress.L4Service {
glog.V(3).Infof("obtaining information about stream services of type %v located in configmap %v", proto, configmapName)
if configmapName == "" {
// no configmap configured
2017-02-24 21:46:39 +00:00
return []ingress.L4Service{}
}
_, _, err := k8s.ParseNameNS(configmapName)
if err != nil {
glog.Errorf("unexpected error reading configmap %v: %v", configmapName, err)
2017-02-24 21:46:39 +00:00
return []ingress.L4Service{}
}
configmap, err := ic.listers.ConfigMap.GetByName(configmapName)
if err != nil {
glog.Errorf("unexpected error reading configmap %v: %v", configmapName, err)
2017-02-24 21:46:39 +00:00
return []ingress.L4Service{}
}
2017-02-24 21:46:39 +00:00
var svcs []ingress.L4Service
var svcProxyProtocol ingress.ProxyProtocol
// k -> port to expose
// v -> <namespace>/<service name>:<port from service to be used>
for k, v := range configmap.Data {
2017-02-24 21:46:39 +00:00
externalPort, err := strconv.Atoi(k)
if err != nil {
glog.Warningf("%v is not valid as a TCP/UDP port", k)
continue
}
// this ports used by the backend
2017-10-06 20:33:32 +00:00
if sliceutils.StringInSlice(k, reservedPorts) {
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, ":")
if len(nsSvcPort) < 2 {
glog.Warningf("invalid format (namespace/name:port:[PROXY]:[PROXY]) '%v'", k)
continue
}
nsName := nsSvcPort[0]
svcPort := nsSvcPort[1]
svcProxyProtocol.Decode = false
svcProxyProtocol.Encode = false
// Proxy protocol is possible if the service is TCP
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
}
}
svcNs, svcName, err := k8s.ParseNameNS(nsName)
if err != nil {
glog.Warningf("%v", err)
continue
}
svcObj, svcExists, err := ic.listers.Service.GetByKey(nsName)
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-11 23:43:35 +00:00
var endps []ingress.Endpoint
targetPort, err := strconv.Atoi(svcPort)
if err != nil {
glog.V(3).Infof("searching service %v endpoints using the name '%v'", svcNs, svcName, svcPort)
for _, sp := range svc.Spec.Ports {
if sp.Name == svcPort {
if sp.Protocol == proto {
endps = ic.getEndpoints(svc, &sp, proto, &healthcheck.Upstream{})
break
}
}
}
} else {
// we need to use the TargetPort (where the endpoints are running)
glog.V(3).Infof("searching service %v/%v endpoints using the target port '%v'", svcNs, svcName, targetPort)
for _, sp := range svc.Spec.Ports {
if sp.Port == int32(targetPort) {
if sp.Protocol == proto {
endps = ic.getEndpoints(svc, &sp, proto, &healthcheck.Upstream{})
break
}
}
}
}
// stream services cannot contain empty upstreams and there is no
// default backend equivalent
if len(endps) == 0 {
glog.Warningf("service %v/%v does not have any active endpoints for port %v and protocol %v", svcNs, svcName, svcPort, proto)
continue
}
2017-02-24 21:46:39 +00:00
svcs = append(svcs, ingress.L4Service{
Port: externalPort,
Backend: ingress.L4Backend{
Name: svcName,
Namespace: svcNs,
Port: intstr.FromString(svcPort),
Protocol: proto,
ProxyProtocol: svcProxyProtocol,
2017-02-24 21:46:39 +00:00
},
Endpoints: endps,
})
}
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{
Name: defUpstreamName,
}
svcKey := ic.cfg.DefaultService
svcObj, svcExists, err := ic.listers.Service.GetByKey(svcKey)
if err != nil {
glog.Warningf("unexpected error searching the default backend %v: %v", ic.cfg.DefaultService, err)
upstream.Endpoints = append(upstream.Endpoints, ic.cfg.Backend.DefaultEndpoint())
return upstream
}
if !svcExists {
2017-03-14 12:47:34 +00:00
glog.Warningf("service %v does not exist", svcKey)
upstream.Endpoints = append(upstream.Endpoints, ic.cfg.Backend.DefaultEndpoint())
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{})
if len(endps) == 0 {
glog.Warningf("service %v does not have any active endpoints", svcKey)
endps = []ingress.Endpoint{ic.cfg.Backend.DefaultEndpoint()}
}
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...)
return upstream
}
2016-11-11 23:43:35 +00:00
// getBackendServers returns a list of Upstream and Server to be used by the backend
// 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) {
du := ic.getDefaultUpstream()
2017-09-25 19:40:55 +00:00
upstreams := ic.createUpstreams(ingresses, du)
servers := ic.createServers(ingresses, upstreams, du)
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)
anns := ic.annotations.Extract(ing)
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 {
glog.V(3).Infof("ingress rule %v/%v does not contain HTTP rules, using default backend", ing.Namespace, ing.Name)
continue
}
if server.CertificateAuth.CAFileName == "" {
ca := ic.annotations.CertificateAuth(ing)
if ca != nil {
server.CertificateAuth = *ca
// 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)
}
}
} else {
glog.V(3).Infof("server %v already contains a mutual authentication configuration - ingress rule %v/%v", server.Hostname, ing.Namespace, ing.Name)
}
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)
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
loc.IsDefBackend = false
2016-11-16 18:24:26 +00:00
loc.Backend = ups.Name
loc.Port = ups.Port
loc.Service = ups.Service
2017-08-25 15:50:08 +00:00
loc.Ingress = ing
mergeLocationAnnotations(loc, anns)
if loc.Redirect.FromToWWW {
server.RedirectFromToWWW = true
}
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)
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,
}
mergeLocationAnnotations(loc, anns)
if loc.Redirect.FromToWWW {
server.RedirectFromToWWW = true
}
server.Locations = append(server.Locations, loc)
}
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)
}
}
}
}
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 {
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)
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)
continue
2017-04-02 02:32:22 +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 {
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
}
2017-08-25 15:50:08 +00:00
aUpstreams = append(aUpstreams, upstream)
}
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 {
return aUpstreams[a].Name < aUpstreams[b].Name
})
2017-07-13 00:35:36 +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 {
return value.Locations[i].Path > value.Locations[j].Path
})
aServers = append(aServers, value)
}
2017-09-25 19:40:55 +00:00
sort.SliceStable(aServers, func(i, j int) bool {
return aServers[i].Hostname < aServers[j].Hostname
})
return aUpstreams, aServers
}
// 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-10-01 00:48:14 +00:00
_, err := ic.listers.Secret.GetByName(name)
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)
if !exists {
2017-10-01 00:48:14 +00:00
return &resolver.AuthSSLCert{}, fmt.Errorf("secret %v does not exist", name)
}
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,
CAFileName: cert.CAFileName,
PemSHA: cert.PemSHA,
}, 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)
upstreams[defUpstreamName] = du
2017-09-25 19:40:55 +00:00
for _, ing := range data {
secUpstream := ic.annotations.SecureUpstream(ing)
hz := ic.annotations.HealthCheck(ing)
serviceUpstream := ic.annotations.ServiceUpstream(ing)
upstreamHashBy := ic.annotations.UpstreamHashBy(ing)
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-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)
}
}
}
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())
if _, ok := upstreams[name]; ok {
continue
}
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
if !upstreams[name].Secure {
2017-05-14 22:14:27 +00:00
upstreams[name].Secure = secUpstream.Secure
}
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
}
if upstreams[name].UpstreamHashBy == "" {
upstreams[name].UpstreamHashBy = upstreamHashBy
}
svcKey := fmt.Sprintf("%v/%v", ing.GetNamespace(), path.Backend.ServiceName)
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
}
s, err := ic.listers.Service.GetByName(svcKey)
if err != nil {
glog.Warningf("error obtaining service: %v", err)
continue
}
upstreams[name].Service = s
}
}
}
return upstreams
}
func (ic *GenericController) getServiceClusterEndpoint(svcKey string, backend *extensions.IngressBackend) (endpoint ingress.Endpoint, err error) {
svcObj, svcExists, err := ic.listers.Service.GetByKey(svcKey)
if !svcExists {
return endpoint, fmt.Errorf("service %v does not exist", svcKey)
}
2017-09-17 18:42:31 +00:00
svc := svcObj.(*apiv1.Service)
if svc.Spec.ClusterIP == "" || svc.Spec.ClusterIP == "None" {
return endpoint, fmt.Errorf("No ClusterIP found for service %s", svcKey)
}
endpoint.Address = svc.Spec.ClusterIP
// If the service port in the ingress uses a name, lookup
// the actual port in the service spec
if backend.ServicePort.Type == intstr.String {
var port int32 = -1
for _, svcPort := range svc.Spec.Ports {
if svcPort.Name == backend.ServicePort.String() {
port = svcPort.Port
break
}
}
if port == -1 {
return endpoint, fmt.Errorf("no port mapped for service %s and port name %s", svc.Name, backend.ServicePort.String())
}
endpoint.Port = fmt.Sprintf("%d", port)
} else {
endpoint.Port = backend.ServicePort.String()
}
return endpoint, err
}
// 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) {
svc, err := ic.listers.Service.GetByName(svcKey)
2017-09-13 19:15:47 +00:00
var upstreams []ingress.Endpoint
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)
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 {
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
}
upstreams = append(upstreams, endps...)
break
}
}
2017-10-26 22:17:18 +00:00
// Ingress with an ExternalName service and no port defined in the service.
if len(svc.Spec.Ports) == 0 && svc.Spec.Type == apiv1.ServiceTypeExternalName {
externalPort, err := strconv.Atoi(backendPort)
if err != nil {
glog.Warningf("only numeric ports are allowed in ExternalName services: %v is not valid as a TCP/UDP port", backendPort)
return upstreams, nil
}
servicePort := apiv1.ServicePort{
Protocol: "TCP",
Port: int32(externalPort),
TargetPort: intstr.FromString(backendPort),
}
endps := ic.getEndpoints(svc, &servicePort, apiv1.ProtocolTCP, hz)
if len(endps) == 0 {
glog.Warningf("service %v does not have any active endpoints", svcKey)
return upstreams, nil
}
upstreams = append(upstreams, endps...)
return upstreams, nil
}
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]
}
}
return upstreams, nil
}
// 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,
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{
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,
}
// generated on Start() with createDefaultSSLCertificate()
2017-08-26 03:46:17 +00:00
defaultPemFileName := fakeCertificatePath
defaultPemSHA := fakeCertificateSHA
// 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
}
// initialize the default server
servers[defServerName] = &ingress.Server{
2017-01-26 18:51:55 +00:00
Hostname: defServerName,
SSLCertificate: defaultPemFileName,
SSLPemChecksum: defaultPemSHA,
Locations: []*ingress.Location{
{
Path: rootLocation,
IsDefBackend: true,
2017-08-11 02:51:32 +00:00
Backend: du.Name,
Proxy: ngxProxy,
2017-08-11 02:51:32 +00:00
Service: du.Service,
},
2017-09-13 19:15:47 +00:00
}}
// initialize all the servers
2017-09-25 19:40:55 +00:00
for _, ing := range data {
// check if ssl passthrough is configured
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
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())
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
}
}
}
for _, rule := range ing.Spec.Rules {
host := rule.Host
if host == "" {
host = defServerName
}
if _, ok := servers[host]; ok {
// server already configured
continue
}
servers[host] = &ingress.Server{
2016-11-16 18:24:26 +00:00
Hostname: host,
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{},
},
},
SSLPassthrough: sslpt,
}
}
}
// configure default location, alias, and SSL
2017-09-25 19:40:55 +00:00
for _, ing := range data {
// setup server-alias based on annotations
aliasAnnotation := ic.annotations.Alias(ing)
2017-09-27 06:59:10 +00:00
srvsnippet := ic.annotations.ServerSnippet(ing)
for _, rule := range ing.Spec.Rules {
host := rule.Host
if host == "" {
host = defServerName
}
// setup server aliases
if aliasAnnotation != "" {
2017-10-27 11:46:32 +00:00
if servers[host].Alias == "" {
servers[host].Alias = aliasAnnotation
if _, ok := aliases[aliasAnnotation]; !ok {
aliases[aliasAnnotation] = host
}
} else {
glog.Warningf("ingress %v/%v for host %v contains an Alias but one has already been configured.",
ing.Namespace, ing.Name, host)
}
}
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
}
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-07-12 17:31:15 +00:00
if tlsSecretName == "" {
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-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-07-12 17:31:15 +00:00
cert := bc.(*ingress.SSLCert)
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-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)
}
}
}
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 = ""
}
}
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-11 23:43:35 +00:00
upsServers := []ingress.Endpoint{}
// 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)
// ExternalName services
2017-09-17 18:42:31 +00:00
if s.Spec.Type == apiv1.ServiceTypeExternalName {
2017-10-26 22:17:18 +00:00
glog.V(3).Info("Ingress using a service %v of type=ExternalName : %v", s.Name)
targetPort := servicePort.TargetPort.IntValue()
// check for invalid port value
if targetPort <= 0 {
2017-10-26 22:17:18 +00:00
glog.Errorf("ExternalName service with an invalid port: %v", targetPort)
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
}
}
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())
ep, err := ic.listers.Endpoint.GetServiceEndpoints(s)
if err != nil {
glog.Warningf("unexpected error obtaining service endpoints: %v", err)
return upsServers
}
for _, ss := range ep.Subsets {
for _, epPort := range ss.Ports {
if !reflect.DeepEqual(epPort.Protocol, proto) {
continue
}
var targetPort int32
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
}
// check for invalid port value
2016-12-29 22:59:56 +00:00
if targetPort <= 0 {
continue
}
for _, epAddress := range ss.Addresses {
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{
Address: epAddress.IP,
Port: fmt.Sprintf("%v", targetPort),
MaxFails: hz.MaxFails,
FailTimeout: hz.FailTimeout,
Target: epAddress.TargetRef,
}
upsServers = append(upsServers, ups)
adus[ep] = true
}
}
}
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 {
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
}
// 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()
if ic.syncStatus != nil {
ic.syncStatus.Shutdown()
}
return nil
}
return fmt.Errorf("shutdown already in progress")
}
// Start starts the Ingress controller.
func (ic *GenericController) Start() {
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-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-17 15:24:03 +00:00
glog.Info("running initial sync of secrets")
2017-10-01 00:48:14 +00:00
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)
if ic.syncStatus != nil {
go ic.syncStatus.Run(ic.stopCh)
}
2017-10-01 00:48:14 +00:00
go wait.Until(ic.checkMissingSecrets, 30*time.Second, ic.stopCh)
// force initial sync
ic.syncQueue.Enqueue(&extensions.Ingress{})
<-ic.stopCh
}
2017-08-26 03:46:17 +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) {
if shouldReload {
atomic.StoreInt32(&ic.forceReload, 1)
2017-10-08 15:06:37 +00:00
ic.syncQueue.Enqueue(&extensions.Ingress{})
} 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
}