2018-01-18 19:14:42 +00:00
|
|
|
/*
|
|
|
|
Copyright 2017 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 store
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"reflect"
|
2018-12-19 13:58:42 +00:00
|
|
|
"sort"
|
2018-01-18 19:14:42 +00:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2019-06-09 22:49:59 +00:00
|
|
|
"k8s.io/klog"
|
2018-01-18 19:14:42 +00:00
|
|
|
|
2019-06-09 22:49:59 +00:00
|
|
|
"github.com/eapache/channels"
|
2018-04-12 22:26:10 +00:00
|
|
|
corev1 "k8s.io/api/core/v1"
|
2019-06-09 22:49:59 +00:00
|
|
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
|
|
|
networkingv1beta1 "k8s.io/api/networking/v1beta1"
|
2018-03-29 12:35:01 +00:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2018-11-20 20:29:20 +00:00
|
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
|
|
k8sruntime "k8s.io/apimachinery/pkg/runtime"
|
2018-01-18 19:14:42 +00:00
|
|
|
"k8s.io/apimachinery/pkg/util/runtime"
|
2018-11-20 20:29:20 +00:00
|
|
|
"k8s.io/apimachinery/pkg/watch"
|
2018-03-29 12:35:01 +00:00
|
|
|
"k8s.io/client-go/informers"
|
2018-01-18 19:14:42 +00:00
|
|
|
clientset "k8s.io/client-go/kubernetes"
|
|
|
|
"k8s.io/client-go/kubernetes/scheme"
|
2018-04-12 22:26:10 +00:00
|
|
|
clientcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
2018-01-18 19:14:42 +00:00
|
|
|
"k8s.io/client-go/tools/cache"
|
|
|
|
"k8s.io/client-go/tools/record"
|
|
|
|
|
|
|
|
"k8s.io/ingress-nginx/internal/file"
|
|
|
|
"k8s.io/ingress-nginx/internal/ingress"
|
|
|
|
"k8s.io/ingress-nginx/internal/ingress/annotations"
|
|
|
|
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
|
|
|
|
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
|
|
|
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
|
|
|
|
ngx_template "k8s.io/ingress-nginx/internal/ingress/controller/template"
|
|
|
|
"k8s.io/ingress-nginx/internal/ingress/defaults"
|
2018-04-20 14:00:33 +00:00
|
|
|
"k8s.io/ingress-nginx/internal/ingress/errors"
|
2018-01-18 19:14:42 +00:00
|
|
|
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
|
|
|
"k8s.io/ingress-nginx/internal/k8s"
|
|
|
|
)
|
|
|
|
|
2019-06-25 03:47:22 +00:00
|
|
|
// IngressFilterFunc decides if an Ingress should be omitted or not
|
2019-05-18 10:08:05 +00:00
|
|
|
type IngressFilterFunc func(*ingress.Ingress) bool
|
|
|
|
|
2018-01-18 19:14:42 +00:00
|
|
|
// Storer is the interface that wraps the required methods to gather information
|
|
|
|
// about ingresses, services, secrets and ingress annotations.
|
|
|
|
type Storer interface {
|
|
|
|
// GetBackendConfiguration returns the nginx configuration stored in a configmap
|
|
|
|
GetBackendConfiguration() ngx_config.Configuration
|
|
|
|
|
2018-07-02 20:59:54 +00:00
|
|
|
// GetConfigMap returns the ConfigMap matching key.
|
2018-04-12 22:26:10 +00:00
|
|
|
GetConfigMap(key string) (*corev1.ConfigMap, error)
|
2018-01-18 19:14:42 +00:00
|
|
|
|
2018-07-02 20:59:54 +00:00
|
|
|
// GetSecret returns the Secret matching key.
|
2018-04-12 22:26:10 +00:00
|
|
|
GetSecret(key string) (*corev1.Secret, error)
|
2018-01-18 19:14:42 +00:00
|
|
|
|
2018-07-02 20:59:54 +00:00
|
|
|
// GetService returns the Service matching key.
|
2018-04-12 22:26:10 +00:00
|
|
|
GetService(key string) (*corev1.Service, error)
|
2018-01-18 19:14:42 +00:00
|
|
|
|
2018-07-02 20:59:54 +00:00
|
|
|
// GetServiceEndpoints returns the Endpoints of a Service matching key.
|
|
|
|
GetServiceEndpoints(key string) (*corev1.Endpoints, error)
|
2018-01-18 19:14:42 +00:00
|
|
|
|
2018-07-02 20:59:54 +00:00
|
|
|
// ListIngresses returns a list of all Ingresses in the store.
|
2019-05-18 10:08:05 +00:00
|
|
|
ListIngresses(IngressFilterFunc) []*ingress.Ingress
|
2018-01-18 19:14:42 +00:00
|
|
|
|
2018-11-27 14:53:51 +00:00
|
|
|
// GetRunningControllerPodsCount returns the number of Running ingress-nginx controller Pods.
|
|
|
|
GetRunningControllerPodsCount() int
|
2018-11-20 20:29:20 +00:00
|
|
|
|
2018-04-12 22:26:10 +00:00
|
|
|
// GetLocalSSLCert returns the local copy of a SSLCert
|
|
|
|
GetLocalSSLCert(name string) (*ingress.SSLCert, error)
|
2018-01-18 19:14:42 +00:00
|
|
|
|
2018-04-12 22:26:10 +00:00
|
|
|
// ListLocalSSLCerts returns the list of local SSLCerts
|
|
|
|
ListLocalSSLCerts() []*ingress.SSLCert
|
2018-01-18 19:14:42 +00:00
|
|
|
|
|
|
|
// GetAuthCertificate resolves a given secret name into an SSL certificate.
|
|
|
|
// The secret must contain 3 keys named:
|
|
|
|
// ca.crt: contains the certificate chain used for authentication
|
|
|
|
GetAuthCertificate(string) (*resolver.AuthSSLCert, error)
|
|
|
|
|
|
|
|
// GetDefaultBackend returns the default backend configuration
|
|
|
|
GetDefaultBackend() defaults.Backend
|
|
|
|
|
|
|
|
// Run initiates the synchronization of the controllers
|
|
|
|
Run(stopCh chan struct{})
|
|
|
|
}
|
|
|
|
|
|
|
|
// EventType type of event associated with an informer
|
|
|
|
type EventType string
|
|
|
|
|
|
|
|
const (
|
|
|
|
// CreateEvent event associated with new objects in an informer
|
|
|
|
CreateEvent EventType = "CREATE"
|
|
|
|
// UpdateEvent event associated with an object update in an informer
|
|
|
|
UpdateEvent EventType = "UPDATE"
|
|
|
|
// DeleteEvent event associated when an object is removed from an informer
|
|
|
|
DeleteEvent EventType = "DELETE"
|
2018-04-12 22:26:10 +00:00
|
|
|
// ConfigurationEvent event associated when a controller configuration object is created or updated
|
2018-01-18 23:04:40 +00:00
|
|
|
ConfigurationEvent EventType = "CONFIGURATION"
|
2018-01-18 19:14:42 +00:00
|
|
|
)
|
|
|
|
|
2018-07-02 20:59:54 +00:00
|
|
|
// Event holds the context of an event.
|
2018-01-18 19:14:42 +00:00
|
|
|
type Event struct {
|
|
|
|
Type EventType
|
|
|
|
Obj interface{}
|
|
|
|
}
|
|
|
|
|
2018-03-29 12:35:01 +00:00
|
|
|
// Informer defines the required SharedIndexInformers that interact with the API server.
|
|
|
|
type Informer struct {
|
|
|
|
Ingress cache.SharedIndexInformer
|
|
|
|
Endpoint cache.SharedIndexInformer
|
|
|
|
Service cache.SharedIndexInformer
|
|
|
|
Secret cache.SharedIndexInformer
|
|
|
|
ConfigMap cache.SharedIndexInformer
|
2018-11-20 20:29:20 +00:00
|
|
|
Pod cache.SharedIndexInformer
|
2018-03-29 12:35:01 +00:00
|
|
|
}
|
|
|
|
|
2018-07-02 20:59:54 +00:00
|
|
|
// Lister contains object listers (stores).
|
2018-01-18 19:14:42 +00:00
|
|
|
type Lister struct {
|
2018-11-30 22:56:11 +00:00
|
|
|
Ingress IngressLister
|
|
|
|
Service ServiceLister
|
|
|
|
Endpoint EndpointLister
|
|
|
|
Secret SecretLister
|
|
|
|
ConfigMap ConfigMapLister
|
|
|
|
IngressWithAnnotation IngressWithAnnotationsLister
|
|
|
|
Pod PodLister
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
|
|
|
|
2018-07-02 20:59:54 +00:00
|
|
|
// NotExistsError is returned when an object does not exist in a local store.
|
|
|
|
type NotExistsError string
|
|
|
|
|
|
|
|
// Error implements the error interface.
|
|
|
|
func (e NotExistsError) Error() string {
|
|
|
|
return fmt.Sprintf("no object matching key %q in local store", string(e))
|
|
|
|
}
|
|
|
|
|
2018-03-29 12:35:01 +00:00
|
|
|
// Run initiates the synchronization of the informers against the API server.
|
|
|
|
func (i *Informer) Run(stopCh chan struct{}) {
|
|
|
|
go i.Endpoint.Run(stopCh)
|
|
|
|
go i.Service.Run(stopCh)
|
|
|
|
go i.Secret.Run(stopCh)
|
|
|
|
go i.ConfigMap.Run(stopCh)
|
2018-11-20 20:29:20 +00:00
|
|
|
go i.Pod.Run(stopCh)
|
2018-01-18 19:14:42 +00:00
|
|
|
|
2018-03-29 12:35:01 +00:00
|
|
|
// wait for all involved caches to be synced before processing items
|
|
|
|
// from the queue
|
2018-01-18 19:14:42 +00:00
|
|
|
if !cache.WaitForCacheSync(stopCh,
|
2018-03-29 12:35:01 +00:00
|
|
|
i.Endpoint.HasSynced,
|
|
|
|
i.Service.HasSynced,
|
|
|
|
i.Secret.HasSynced,
|
|
|
|
i.ConfigMap.HasSynced,
|
2018-01-18 19:14:42 +00:00
|
|
|
) {
|
2019-07-08 20:10:38 +00:00
|
|
|
runtime.HandleError(fmt.Errorf("timed out waiting for caches to sync"))
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
|
|
|
|
2018-03-29 12:35:01 +00:00
|
|
|
// in big clusters, deltas can keep arriving even after HasSynced
|
|
|
|
// functions have returned 'true'
|
2018-01-18 19:14:42 +00:00
|
|
|
time.Sleep(1 * time.Second)
|
2018-03-29 12:35:01 +00:00
|
|
|
|
|
|
|
// we can start syncing ingress objects only after other caches are
|
|
|
|
// ready, because ingress rules require content from other listers, and
|
|
|
|
// 'add' events get triggered in the handlers during caches population.
|
|
|
|
go i.Ingress.Run(stopCh)
|
2018-01-18 19:14:42 +00:00
|
|
|
if !cache.WaitForCacheSync(stopCh,
|
2018-03-29 12:35:01 +00:00
|
|
|
i.Ingress.HasSynced,
|
2018-01-18 19:14:42 +00:00
|
|
|
) {
|
2019-07-08 20:10:38 +00:00
|
|
|
runtime.HandleError(fmt.Errorf("timed out waiting for caches to sync"))
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// k8sStore internal Storer implementation using informers and thread safe stores
|
|
|
|
type k8sStore struct {
|
2018-01-19 18:44:31 +00:00
|
|
|
// backendConfig contains the running configuration from the configmap
|
|
|
|
// this is required because this rarely changes but is a very expensive
|
|
|
|
// operation to execute in each OnUpdate invocation
|
2018-01-18 19:14:42 +00:00
|
|
|
backendConfig ngx_config.Configuration
|
|
|
|
|
2018-03-29 12:35:01 +00:00
|
|
|
// informer contains the cache Informers
|
|
|
|
informers *Informer
|
2018-01-19 18:44:31 +00:00
|
|
|
|
2018-03-29 12:35:01 +00:00
|
|
|
// listers contains the cache.Store interfaces used in the ingress controller
|
2018-01-18 19:14:42 +00:00
|
|
|
listers *Lister
|
|
|
|
|
|
|
|
// sslStore local store of SSL certificates (certificates used in ingress)
|
|
|
|
// this is required because the certificates must be present in the
|
|
|
|
// container filesystem
|
|
|
|
sslStore *SSLCertTracker
|
|
|
|
|
|
|
|
annotations annotations.Extractor
|
|
|
|
|
2018-01-22 22:07:31 +00:00
|
|
|
// secretIngressMap contains information about which ingress references a
|
|
|
|
// secret in the annotations.
|
2018-04-12 22:26:10 +00:00
|
|
|
secretIngressMap ObjectRefMap
|
2018-01-22 22:07:31 +00:00
|
|
|
|
2018-01-18 19:14:42 +00:00
|
|
|
filesystem file.Filesystem
|
|
|
|
|
2018-01-19 18:44:31 +00:00
|
|
|
// updateCh
|
2018-02-14 01:46:18 +00:00
|
|
|
updateCh *channels.RingChannel
|
2018-01-18 19:14:42 +00:00
|
|
|
|
2018-12-06 13:24:41 +00:00
|
|
|
// syncSecretMu protects against simultaneous invocations of syncSecret
|
|
|
|
syncSecretMu *sync.Mutex
|
|
|
|
|
|
|
|
// backendConfigMu protects against simultaneous read/write of backendConfig
|
|
|
|
backendConfigMu *sync.RWMutex
|
2018-01-25 13:46:20 +00:00
|
|
|
|
|
|
|
defaultSSLCertificate string
|
2018-06-04 21:48:30 +00:00
|
|
|
|
2018-11-20 20:29:20 +00:00
|
|
|
pod *k8s.PodInfo
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// New creates a new object store to be used in the ingress controller
|
2019-07-04 22:06:55 +00:00
|
|
|
func New(
|
2018-11-16 16:48:47 +00:00
|
|
|
namespace, configmap, tcp, udp, defaultSSLCertificate string,
|
2018-01-18 19:14:42 +00:00
|
|
|
resyncPeriod time.Duration,
|
|
|
|
client clientset.Interface,
|
|
|
|
fs file.Filesystem,
|
2018-06-04 21:48:30 +00:00
|
|
|
updateCh *channels.RingChannel,
|
2018-12-11 21:57:46 +00:00
|
|
|
pod *k8s.PodInfo,
|
|
|
|
disableCatchAll bool) Storer {
|
2018-01-18 19:14:42 +00:00
|
|
|
|
|
|
|
store := &k8sStore{
|
2019-07-04 22:06:55 +00:00
|
|
|
informers: &Informer{},
|
|
|
|
listers: &Lister{},
|
|
|
|
sslStore: NewSSLCertTracker(),
|
|
|
|
filesystem: fs,
|
|
|
|
updateCh: updateCh,
|
|
|
|
backendConfig: ngx_config.NewDefault(),
|
|
|
|
syncSecretMu: &sync.Mutex{},
|
|
|
|
backendConfigMu: &sync.RWMutex{},
|
|
|
|
secretIngressMap: NewObjectRefMap(),
|
|
|
|
defaultSSLCertificate: defaultSSLCertificate,
|
|
|
|
pod: pod,
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
eventBroadcaster := record.NewBroadcaster()
|
2018-12-05 16:27:55 +00:00
|
|
|
eventBroadcaster.StartLogging(klog.Infof)
|
2018-04-12 22:26:10 +00:00
|
|
|
eventBroadcaster.StartRecordingToSink(&clientcorev1.EventSinkImpl{
|
2018-01-18 19:14:42 +00:00
|
|
|
Interface: client.CoreV1().Events(namespace),
|
|
|
|
})
|
2018-04-12 22:26:10 +00:00
|
|
|
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{
|
2018-01-18 19:14:42 +00:00
|
|
|
Component: "nginx-ingress-controller",
|
|
|
|
})
|
|
|
|
|
2018-08-11 12:26:14 +00:00
|
|
|
// k8sStore fulfills resolver.Resolver interface
|
2018-01-18 19:14:42 +00:00
|
|
|
store.annotations = annotations.NewAnnotationExtractor(store)
|
|
|
|
|
2018-11-30 22:56:11 +00:00
|
|
|
store.listers.IngressWithAnnotation.Store = cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc)
|
2018-03-29 12:35:01 +00:00
|
|
|
|
|
|
|
// create informers factory, enable and assign required informers
|
2018-09-10 17:42:10 +00:00
|
|
|
infFactory := informers.NewSharedInformerFactoryWithOptions(client, resyncPeriod,
|
|
|
|
informers.WithNamespace(namespace),
|
|
|
|
informers.WithTweakListOptions(func(*metav1.ListOptions) {}))
|
2018-03-29 12:35:01 +00:00
|
|
|
|
2019-06-09 22:49:59 +00:00
|
|
|
if k8s.IsNetworkingIngressAvailable {
|
|
|
|
store.informers.Ingress = infFactory.Networking().V1beta1().Ingresses().Informer()
|
|
|
|
} else {
|
|
|
|
store.informers.Ingress = infFactory.Extensions().V1beta1().Ingresses().Informer()
|
|
|
|
}
|
|
|
|
|
2018-03-29 12:35:01 +00:00
|
|
|
store.listers.Ingress.Store = store.informers.Ingress.GetStore()
|
|
|
|
|
|
|
|
store.informers.Endpoint = infFactory.Core().V1().Endpoints().Informer()
|
|
|
|
store.listers.Endpoint.Store = store.informers.Endpoint.GetStore()
|
|
|
|
|
|
|
|
store.informers.Secret = infFactory.Core().V1().Secrets().Informer()
|
|
|
|
store.listers.Secret.Store = store.informers.Secret.GetStore()
|
|
|
|
|
|
|
|
store.informers.ConfigMap = infFactory.Core().V1().ConfigMaps().Informer()
|
|
|
|
store.listers.ConfigMap.Store = store.informers.ConfigMap.GetStore()
|
|
|
|
|
|
|
|
store.informers.Service = infFactory.Core().V1().Services().Informer()
|
|
|
|
store.listers.Service.Store = store.informers.Service.GetStore()
|
|
|
|
|
2018-11-20 20:29:20 +00:00
|
|
|
labelSelector := labels.SelectorFromSet(store.pod.Labels)
|
|
|
|
store.informers.Pod = cache.NewSharedIndexInformer(
|
|
|
|
&cache.ListWatch{
|
|
|
|
ListFunc: func(options metav1.ListOptions) (k8sruntime.Object, error) {
|
|
|
|
options.LabelSelector = labelSelector.String()
|
|
|
|
return client.CoreV1().Pods(store.pod.Namespace).List(options)
|
|
|
|
},
|
|
|
|
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
|
|
|
|
options.LabelSelector = labelSelector.String()
|
|
|
|
return client.CoreV1().Pods(store.pod.Namespace).Watch(options)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&corev1.Pod{},
|
|
|
|
resyncPeriod,
|
|
|
|
cache.Indexers{},
|
|
|
|
)
|
|
|
|
store.listers.Pod.Store = store.informers.Pod.GetStore()
|
|
|
|
|
2019-01-07 20:29:18 +00:00
|
|
|
ingDeleteHandler := func(obj interface{}) {
|
2019-06-09 22:49:59 +00:00
|
|
|
ing, ok := toIngress(obj)
|
2019-01-07 20:29:18 +00:00
|
|
|
if !ok {
|
|
|
|
// If we reached here it means the ingress was deleted but its final state is unrecorded.
|
|
|
|
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
|
|
|
|
if !ok {
|
|
|
|
klog.Errorf("couldn't get object from tombstone %#v", obj)
|
|
|
|
return
|
|
|
|
}
|
2019-06-09 22:49:59 +00:00
|
|
|
ing, ok = tombstone.Obj.(*networkingv1beta1.Ingress)
|
2019-01-07 20:29:18 +00:00
|
|
|
if !ok {
|
|
|
|
klog.Errorf("Tombstone contained object that is not an Ingress: %#v", obj)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2019-06-09 22:49:59 +00:00
|
|
|
|
2019-01-07 20:29:18 +00:00
|
|
|
if !class.IsValid(ing) {
|
|
|
|
klog.Infof("ignoring delete for ingress %v based on annotation %v", ing.Name, class.IngressKey)
|
|
|
|
return
|
|
|
|
}
|
2019-01-25 19:53:55 +00:00
|
|
|
if isCatchAllIngress(ing.Spec) && disableCatchAll {
|
2019-01-07 20:29:18 +00:00
|
|
|
klog.Infof("ignoring delete for catch-all ingress %v/%v because of --disable-catch-all", ing.Namespace, ing.Name)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
recorder.Eventf(ing, corev1.EventTypeNormal, "DELETE", fmt.Sprintf("Ingress %s/%s", ing.Namespace, ing.Name))
|
|
|
|
|
|
|
|
store.listers.IngressWithAnnotation.Delete(ing)
|
|
|
|
|
|
|
|
key := k8s.MetaNamespaceKey(ing)
|
|
|
|
store.secretIngressMap.Delete(key)
|
|
|
|
|
|
|
|
updateCh.In() <- Event{
|
|
|
|
Type: DeleteEvent,
|
|
|
|
Obj: obj,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-18 19:14:42 +00:00
|
|
|
ingEventHandler := cache.ResourceEventHandlerFuncs{
|
|
|
|
AddFunc: func(obj interface{}) {
|
2019-06-09 22:49:59 +00:00
|
|
|
ing, _ := toIngress(obj)
|
2018-04-12 22:26:10 +00:00
|
|
|
if !class.IsValid(ing) {
|
|
|
|
a, _ := parser.GetStringAnnotation(class.IngressKey, ing)
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Infof("ignoring add for ingress %v based on annotation %v with value %v", ing.Name, class.IngressKey, a)
|
2018-01-18 19:14:42 +00:00
|
|
|
return
|
|
|
|
}
|
2019-01-25 19:53:55 +00:00
|
|
|
if isCatchAllIngress(ing.Spec) && disableCatchAll {
|
2018-12-11 21:57:46 +00:00
|
|
|
klog.Infof("ignoring add for catch-all ingress %v/%v because of --disable-catch-all", ing.Namespace, ing.Name)
|
|
|
|
return
|
|
|
|
}
|
2018-04-13 13:34:42 +00:00
|
|
|
recorder.Eventf(ing, corev1.EventTypeNormal, "CREATE", fmt.Sprintf("Ingress %s/%s", ing.Namespace, ing.Name))
|
2018-01-18 19:14:42 +00:00
|
|
|
|
2018-12-02 18:35:12 +00:00
|
|
|
store.syncIngress(ing)
|
2018-04-12 22:26:10 +00:00
|
|
|
store.updateSecretIngressMap(ing)
|
|
|
|
store.syncSecrets(ing)
|
|
|
|
|
2018-02-14 01:46:18 +00:00
|
|
|
updateCh.In() <- Event{
|
2018-01-18 19:14:42 +00:00
|
|
|
Type: CreateEvent,
|
|
|
|
Obj: obj,
|
|
|
|
}
|
|
|
|
},
|
2019-01-07 20:29:18 +00:00
|
|
|
DeleteFunc: ingDeleteHandler,
|
2018-01-18 19:14:42 +00:00
|
|
|
UpdateFunc: func(old, cur interface{}) {
|
2019-06-09 22:49:59 +00:00
|
|
|
oldIng, _ := toIngress(old)
|
|
|
|
curIng, _ := toIngress(cur)
|
|
|
|
|
2018-01-18 19:14:42 +00:00
|
|
|
validOld := class.IsValid(oldIng)
|
|
|
|
validCur := class.IsValid(curIng)
|
|
|
|
if !validOld && validCur {
|
2019-01-25 19:53:55 +00:00
|
|
|
if isCatchAllIngress(curIng.Spec) && disableCatchAll {
|
2018-12-11 21:57:46 +00:00
|
|
|
klog.Infof("ignoring update for catch-all ingress %v/%v because of --disable-catch-all", curIng.Namespace, curIng.Name)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Infof("creating ingress %v based on annotation %v", curIng.Name, class.IngressKey)
|
2018-04-12 22:26:10 +00:00
|
|
|
recorder.Eventf(curIng, corev1.EventTypeNormal, "CREATE", fmt.Sprintf("Ingress %s/%s", curIng.Namespace, curIng.Name))
|
2018-01-18 19:14:42 +00:00
|
|
|
} else if validOld && !validCur {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Infof("removing ingress %v based on annotation %v", curIng.Name, class.IngressKey)
|
2019-01-07 20:29:18 +00:00
|
|
|
ingDeleteHandler(old)
|
|
|
|
return
|
2018-01-18 19:14:42 +00:00
|
|
|
} else if validCur && !reflect.DeepEqual(old, cur) {
|
2019-01-25 19:53:55 +00:00
|
|
|
if isCatchAllIngress(curIng.Spec) && disableCatchAll {
|
2019-01-07 20:29:18 +00:00
|
|
|
klog.Infof("ignoring update for catch-all ingress %v/%v and delete old one because of --disable-catch-all", curIng.Namespace, curIng.Name)
|
|
|
|
ingDeleteHandler(old)
|
|
|
|
return
|
2018-12-11 21:57:46 +00:00
|
|
|
}
|
2019-01-07 20:29:18 +00:00
|
|
|
|
|
|
|
recorder.Eventf(curIng, corev1.EventTypeNormal, "UPDATE", fmt.Sprintf("Ingress %s/%s", curIng.Namespace, curIng.Name))
|
2018-12-06 13:20:00 +00:00
|
|
|
} else {
|
2019-02-16 23:56:18 +00:00
|
|
|
klog.V(3).Infof("No changes on ingress %v/%v. Skipping update", curIng.Namespace, curIng.Name)
|
2018-12-06 13:20:00 +00:00
|
|
|
return
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
|
|
|
|
2018-12-02 18:35:12 +00:00
|
|
|
store.syncIngress(curIng)
|
2018-04-12 22:26:10 +00:00
|
|
|
store.updateSecretIngressMap(curIng)
|
|
|
|
store.syncSecrets(curIng)
|
|
|
|
|
2018-02-14 01:46:18 +00:00
|
|
|
updateCh.In() <- Event{
|
2018-01-18 19:14:42 +00:00
|
|
|
Type: UpdateEvent,
|
|
|
|
Obj: cur,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
secrEventHandler := cache.ResourceEventHandlerFuncs{
|
2018-04-12 22:26:10 +00:00
|
|
|
AddFunc: func(obj interface{}) {
|
|
|
|
sec := obj.(*corev1.Secret)
|
|
|
|
key := k8s.MetaNamespaceKey(sec)
|
|
|
|
|
|
|
|
if store.defaultSSLCertificate == key {
|
|
|
|
store.syncSecret(store.defaultSSLCertificate)
|
|
|
|
}
|
|
|
|
|
|
|
|
// find references in ingresses and update local ssl certs
|
|
|
|
if ings := store.secretIngressMap.Reference(key); len(ings) > 0 {
|
2018-12-05 16:28:28 +00:00
|
|
|
klog.Infof("secret %v was added and it is used in ingress annotations. Parsing...", key)
|
2018-04-12 22:26:10 +00:00
|
|
|
for _, ingKey := range ings {
|
2018-11-30 22:56:11 +00:00
|
|
|
ing, err := store.getIngress(ingKey)
|
2018-04-12 22:26:10 +00:00
|
|
|
if err != nil {
|
2018-12-05 16:28:28 +00:00
|
|
|
klog.Errorf("could not find Ingress %v in local store", ingKey)
|
2018-04-12 22:26:10 +00:00
|
|
|
continue
|
|
|
|
}
|
2018-12-02 18:35:12 +00:00
|
|
|
store.syncIngress(ing)
|
2018-04-12 22:26:10 +00:00
|
|
|
store.syncSecrets(ing)
|
|
|
|
}
|
|
|
|
updateCh.In() <- Event{
|
2018-04-13 13:34:42 +00:00
|
|
|
Type: CreateEvent,
|
2018-04-12 22:26:10 +00:00
|
|
|
Obj: obj,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2018-01-18 19:14:42 +00:00
|
|
|
UpdateFunc: func(old, cur interface{}) {
|
|
|
|
if !reflect.DeepEqual(old, cur) {
|
2018-04-12 22:26:10 +00:00
|
|
|
sec := cur.(*corev1.Secret)
|
|
|
|
key := k8s.MetaNamespaceKey(sec)
|
|
|
|
|
|
|
|
if store.defaultSSLCertificate == key {
|
|
|
|
store.syncSecret(store.defaultSSLCertificate)
|
|
|
|
}
|
2018-01-24 17:46:43 +00:00
|
|
|
|
2018-04-12 22:26:10 +00:00
|
|
|
// find references in ingresses and update local ssl certs
|
|
|
|
if ings := store.secretIngressMap.Reference(key); len(ings) > 0 {
|
2018-12-05 16:28:28 +00:00
|
|
|
klog.Infof("secret %v was updated and it is used in ingress annotations. Parsing...", key)
|
2018-04-12 22:26:10 +00:00
|
|
|
for _, ingKey := range ings {
|
2018-11-30 22:56:11 +00:00
|
|
|
ing, err := store.getIngress(ingKey)
|
2018-04-12 22:26:10 +00:00
|
|
|
if err != nil {
|
2018-12-05 16:28:28 +00:00
|
|
|
klog.Errorf("could not find Ingress %v in local store", ingKey)
|
2018-04-12 22:26:10 +00:00
|
|
|
continue
|
2018-04-06 20:44:41 +00:00
|
|
|
}
|
2018-12-02 18:35:12 +00:00
|
|
|
store.syncIngress(ing)
|
2018-04-12 22:26:10 +00:00
|
|
|
store.syncSecrets(ing)
|
2018-01-22 22:07:31 +00:00
|
|
|
}
|
2018-02-14 01:46:18 +00:00
|
|
|
updateCh.In() <- Event{
|
2018-04-12 22:26:10 +00:00
|
|
|
Type: UpdateEvent,
|
2018-01-22 22:07:31 +00:00
|
|
|
Obj: cur,
|
|
|
|
}
|
|
|
|
}
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
DeleteFunc: func(obj interface{}) {
|
2018-04-12 22:26:10 +00:00
|
|
|
sec, ok := obj.(*corev1.Secret)
|
2018-01-18 19:14:42 +00:00
|
|
|
if !ok {
|
|
|
|
// If we reached here it means the secret was deleted but its final state is unrecorded.
|
|
|
|
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
|
|
|
|
if !ok {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Errorf("couldn't get object from tombstone %#v", obj)
|
2018-01-18 19:14:42 +00:00
|
|
|
return
|
|
|
|
}
|
2018-04-12 22:26:10 +00:00
|
|
|
sec, ok = tombstone.Obj.(*corev1.Secret)
|
2018-01-18 19:14:42 +00:00
|
|
|
if !ok {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Errorf("Tombstone contained object that is not a Secret: %#v", obj)
|
2018-01-18 19:14:42 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2018-04-12 22:26:10 +00:00
|
|
|
|
2018-01-18 19:14:42 +00:00
|
|
|
store.sslStore.Delete(k8s.MetaNamespaceKey(sec))
|
2018-01-22 22:07:31 +00:00
|
|
|
|
2018-04-12 22:26:10 +00:00
|
|
|
key := k8s.MetaNamespaceKey(sec)
|
|
|
|
|
|
|
|
// find references in ingresses
|
|
|
|
if ings := store.secretIngressMap.Reference(key); len(ings) > 0 {
|
2018-12-05 16:28:28 +00:00
|
|
|
klog.Infof("secret %v was deleted and it is used in ingress annotations. Parsing...", key)
|
2018-04-12 22:26:10 +00:00
|
|
|
for _, ingKey := range ings {
|
2018-11-30 22:56:11 +00:00
|
|
|
ing, err := store.getIngress(ingKey)
|
2018-04-12 22:26:10 +00:00
|
|
|
if err != nil {
|
2018-12-05 16:28:28 +00:00
|
|
|
klog.Errorf("could not find Ingress %v in local store", ingKey)
|
2018-04-12 22:26:10 +00:00
|
|
|
continue
|
2018-01-23 23:38:27 +00:00
|
|
|
}
|
2018-12-02 18:35:12 +00:00
|
|
|
store.syncIngress(ing)
|
2018-01-22 22:07:31 +00:00
|
|
|
}
|
2018-02-14 01:46:18 +00:00
|
|
|
updateCh.In() <- Event{
|
2018-04-12 22:26:10 +00:00
|
|
|
Type: DeleteEvent,
|
|
|
|
Obj: obj,
|
2018-01-22 22:07:31 +00:00
|
|
|
}
|
|
|
|
}
|
2018-01-18 19:14:42 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-03-29 12:35:01 +00:00
|
|
|
epEventHandler := cache.ResourceEventHandlerFuncs{
|
2018-01-18 19:14:42 +00:00
|
|
|
AddFunc: func(obj interface{}) {
|
2018-02-14 01:46:18 +00:00
|
|
|
updateCh.In() <- Event{
|
2018-01-18 19:14:42 +00:00
|
|
|
Type: CreateEvent,
|
|
|
|
Obj: obj,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
DeleteFunc: func(obj interface{}) {
|
2018-02-14 01:46:18 +00:00
|
|
|
updateCh.In() <- Event{
|
2018-01-18 19:14:42 +00:00
|
|
|
Type: DeleteEvent,
|
|
|
|
Obj: obj,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
UpdateFunc: func(old, cur interface{}) {
|
2018-04-12 22:26:10 +00:00
|
|
|
oep := old.(*corev1.Endpoints)
|
|
|
|
cep := cur.(*corev1.Endpoints)
|
|
|
|
if !reflect.DeepEqual(cep.Subsets, oep.Subsets) {
|
2018-02-14 01:46:18 +00:00
|
|
|
updateCh.In() <- Event{
|
2018-01-18 19:14:42 +00:00
|
|
|
Type: UpdateEvent,
|
|
|
|
Obj: cur,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-04-12 22:26:10 +00:00
|
|
|
cmEventHandler := cache.ResourceEventHandlerFuncs{
|
2018-01-18 19:14:42 +00:00
|
|
|
AddFunc: func(obj interface{}) {
|
2018-04-12 22:26:10 +00:00
|
|
|
cm := obj.(*corev1.ConfigMap)
|
|
|
|
key := k8s.MetaNamespaceKey(cm)
|
|
|
|
// updates to configuration configmaps can trigger an update
|
2018-11-16 16:48:47 +00:00
|
|
|
if key == configmap || key == tcp || key == udp {
|
2018-04-12 22:26:10 +00:00
|
|
|
recorder.Eventf(cm, corev1.EventTypeNormal, "CREATE", fmt.Sprintf("ConfigMap %v", key))
|
|
|
|
if key == configmap {
|
|
|
|
store.setConfig(cm)
|
|
|
|
}
|
2018-02-14 01:46:18 +00:00
|
|
|
updateCh.In() <- Event{
|
2018-01-23 23:38:27 +00:00
|
|
|
Type: ConfigurationEvent,
|
2018-01-18 19:14:42 +00:00
|
|
|
Obj: obj,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
UpdateFunc: func(old, cur interface{}) {
|
|
|
|
if !reflect.DeepEqual(old, cur) {
|
2018-04-12 22:26:10 +00:00
|
|
|
cm := cur.(*corev1.ConfigMap)
|
|
|
|
key := k8s.MetaNamespaceKey(cm)
|
2018-01-18 19:14:42 +00:00
|
|
|
// updates to configuration configmaps can trigger an update
|
2018-11-16 16:48:47 +00:00
|
|
|
if key == configmap || key == tcp || key == udp {
|
2018-04-12 22:26:10 +00:00
|
|
|
recorder.Eventf(cm, corev1.EventTypeNormal, "UPDATE", fmt.Sprintf("ConfigMap %v", key))
|
|
|
|
if key == configmap {
|
|
|
|
store.setConfig(cm)
|
|
|
|
}
|
2018-06-21 12:47:06 +00:00
|
|
|
|
2018-11-30 22:56:11 +00:00
|
|
|
ings := store.listers.IngressWithAnnotation.List()
|
2018-06-21 12:47:06 +00:00
|
|
|
for _, ingKey := range ings {
|
|
|
|
key := k8s.MetaNamespaceKey(ingKey)
|
2018-11-30 22:56:11 +00:00
|
|
|
ing, err := store.getIngress(key)
|
2018-06-21 12:47:06 +00:00
|
|
|
if err != nil {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Errorf("could not find Ingress %v in local store: %v", key, err)
|
2018-06-21 12:47:06 +00:00
|
|
|
continue
|
|
|
|
}
|
2018-12-02 18:35:12 +00:00
|
|
|
store.syncIngress(ing)
|
2018-06-21 12:47:06 +00:00
|
|
|
}
|
|
|
|
|
2018-02-14 01:46:18 +00:00
|
|
|
updateCh.In() <- Event{
|
2018-01-18 23:04:40 +00:00
|
|
|
Type: ConfigurationEvent,
|
2018-01-18 19:14:42 +00:00
|
|
|
Obj: cur,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-11-20 20:29:20 +00:00
|
|
|
podEventHandler := cache.ResourceEventHandlerFuncs{
|
|
|
|
AddFunc: func(obj interface{}) {
|
|
|
|
updateCh.In() <- Event{
|
|
|
|
Type: CreateEvent,
|
|
|
|
Obj: obj,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
UpdateFunc: func(old, cur interface{}) {
|
|
|
|
oldPod := old.(*corev1.Pod)
|
|
|
|
curPod := cur.(*corev1.Pod)
|
|
|
|
|
|
|
|
if oldPod.Status.Phase == curPod.Status.Phase {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
updateCh.In() <- Event{
|
|
|
|
Type: UpdateEvent,
|
|
|
|
Obj: cur,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
DeleteFunc: func(obj interface{}) {
|
|
|
|
updateCh.In() <- Event{
|
|
|
|
Type: DeleteEvent,
|
|
|
|
Obj: obj,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-03-29 12:35:01 +00:00
|
|
|
store.informers.Ingress.AddEventHandler(ingEventHandler)
|
|
|
|
store.informers.Endpoint.AddEventHandler(epEventHandler)
|
|
|
|
store.informers.Secret.AddEventHandler(secrEventHandler)
|
2018-04-12 22:26:10 +00:00
|
|
|
store.informers.ConfigMap.AddEventHandler(cmEventHandler)
|
2018-03-29 12:35:01 +00:00
|
|
|
store.informers.Service.AddEventHandler(cache.ResourceEventHandlerFuncs{})
|
2018-11-20 20:29:20 +00:00
|
|
|
store.informers.Pod.AddEventHandler(podEventHandler)
|
2018-01-18 19:14:42 +00:00
|
|
|
|
2018-06-17 16:27:12 +00:00
|
|
|
// do not wait for informers to read the configmap configuration
|
2018-06-23 13:08:48 +00:00
|
|
|
ns, name, _ := k8s.ParseNameNS(configmap)
|
|
|
|
cm, err := client.CoreV1().ConfigMaps(ns).Get(name, metav1.GetOptions{})
|
2018-06-17 16:27:12 +00:00
|
|
|
if err != nil {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Warningf("Unexpected error reading configuration configmap: %v", err)
|
2018-06-17 16:27:12 +00:00
|
|
|
}
|
|
|
|
|
2018-07-15 22:24:16 +00:00
|
|
|
store.setConfig(cm)
|
2018-01-18 19:14:42 +00:00
|
|
|
return store
|
|
|
|
}
|
|
|
|
|
2019-01-25 19:53:55 +00:00
|
|
|
// isCatchAllIngress returns whether or not an ingress produces a
|
|
|
|
// catch-all server, and so should be ignored when --disable-catch-all is set
|
2019-06-09 22:49:59 +00:00
|
|
|
func isCatchAllIngress(spec networkingv1beta1.IngressSpec) bool {
|
2019-01-25 19:53:55 +00:00
|
|
|
return spec.Backend != nil && len(spec.Rules) == 0
|
|
|
|
}
|
|
|
|
|
2018-12-02 18:35:12 +00:00
|
|
|
// syncIngress parses ingress annotations converting the value of the
|
|
|
|
// annotation to a go struct
|
2019-06-09 22:49:59 +00:00
|
|
|
func (s *k8sStore) syncIngress(ing *networkingv1beta1.Ingress) {
|
2018-04-12 22:26:10 +00:00
|
|
|
key := k8s.MetaNamespaceKey(ing)
|
2018-12-05 16:28:28 +00:00
|
|
|
klog.V(3).Infof("updating annotations information for ingress %v", key)
|
2018-01-22 22:07:31 +00:00
|
|
|
|
2019-06-09 22:49:59 +00:00
|
|
|
copyIng := &networkingv1beta1.Ingress{}
|
2018-11-30 23:22:12 +00:00
|
|
|
ing.ObjectMeta.DeepCopyInto(©Ing.ObjectMeta)
|
|
|
|
ing.Spec.DeepCopyInto(©Ing.Spec)
|
2018-12-17 12:10:53 +00:00
|
|
|
ing.Status.DeepCopyInto(©Ing.Status)
|
2018-11-30 22:56:11 +00:00
|
|
|
|
|
|
|
for ri, rule := range copyIng.Spec.Rules {
|
|
|
|
if rule.HTTP == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for pi, path := range rule.HTTP.Paths {
|
|
|
|
if path.Path == "" {
|
|
|
|
copyIng.Spec.Rules[ri].HTTP.Paths[pi].Path = "/"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-22 22:07:31 +00:00
|
|
|
|
2018-11-30 22:56:11 +00:00
|
|
|
err := s.listers.IngressWithAnnotation.Update(&ingress.Ingress{
|
|
|
|
Ingress: *copyIng,
|
2018-11-30 23:22:12 +00:00
|
|
|
ParsedAnnotations: s.annotations.Extract(ing),
|
2018-11-30 22:56:11 +00:00
|
|
|
})
|
2018-04-12 22:26:10 +00:00
|
|
|
if err != nil {
|
2018-12-05 16:28:28 +00:00
|
|
|
klog.Error(err)
|
2018-01-22 22:07:31 +00:00
|
|
|
}
|
2018-04-12 22:26:10 +00:00
|
|
|
}
|
2018-01-22 22:07:31 +00:00
|
|
|
|
2018-04-12 22:26:10 +00:00
|
|
|
// updateSecretIngressMap takes an Ingress and updates all Secret objects it
|
|
|
|
// references in secretIngressMap.
|
2019-06-09 22:49:59 +00:00
|
|
|
func (s *k8sStore) updateSecretIngressMap(ing *networkingv1beta1.Ingress) {
|
2018-04-12 22:26:10 +00:00
|
|
|
key := k8s.MetaNamespaceKey(ing)
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.V(3).Infof("updating references to secrets for ingress %v", key)
|
2018-04-12 22:26:10 +00:00
|
|
|
|
|
|
|
// delete all existing references first
|
|
|
|
s.secretIngressMap.Delete(key)
|
|
|
|
|
|
|
|
var refSecrets []string
|
|
|
|
|
|
|
|
for _, tls := range ing.Spec.TLS {
|
|
|
|
secrName := tls.SecretName
|
|
|
|
if secrName != "" {
|
|
|
|
secrKey := fmt.Sprintf("%v/%v", ing.Namespace, secrName)
|
|
|
|
refSecrets = append(refSecrets, secrKey)
|
2018-01-22 22:07:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-13 08:44:34 +00:00
|
|
|
// We can not rely on cached ingress annotations because these are
|
|
|
|
// discarded when the referenced secret does not exist in the local
|
|
|
|
// store. As a result, adding a secret *after* the ingress(es) which
|
|
|
|
// references it would not trigger a resync of that secret.
|
2018-04-12 22:26:10 +00:00
|
|
|
secretAnnotations := []string{
|
2018-04-13 08:44:34 +00:00
|
|
|
"auth-secret",
|
|
|
|
"auth-tls-secret",
|
2018-04-12 22:26:10 +00:00
|
|
|
}
|
2018-04-13 08:44:34 +00:00
|
|
|
for _, ann := range secretAnnotations {
|
2018-04-19 22:05:54 +00:00
|
|
|
secrKey, err := objectRefAnnotationNsKey(ann, ing)
|
2018-04-20 14:00:33 +00:00
|
|
|
if err != nil && !errors.IsMissingAnnotations(err) {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Errorf("error reading secret reference in annotation %q: %s", ann, err)
|
2018-04-13 08:44:34 +00:00
|
|
|
continue
|
|
|
|
}
|
2018-04-19 22:05:54 +00:00
|
|
|
if secrKey != "" {
|
2018-04-12 22:26:10 +00:00
|
|
|
refSecrets = append(refSecrets, secrKey)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// populate map with all secret references
|
|
|
|
s.secretIngressMap.Insert(key, refSecrets...)
|
|
|
|
}
|
|
|
|
|
2018-04-19 22:05:54 +00:00
|
|
|
// objectRefAnnotationNsKey returns an object reference formatted as a
|
|
|
|
// 'namespace/name' key from the given annotation name.
|
2019-06-09 22:49:59 +00:00
|
|
|
func objectRefAnnotationNsKey(ann string, ing *networkingv1beta1.Ingress) (string, error) {
|
2018-04-19 22:05:54 +00:00
|
|
|
annValue, err := parser.GetStringAnnotation(ann, ing)
|
2018-12-02 18:35:12 +00:00
|
|
|
if err != nil {
|
2018-04-19 22:05:54 +00:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
secrNs, secrName, err := cache.SplitMetaNamespaceKey(annValue)
|
|
|
|
if secrName == "" {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if secrNs == "" {
|
|
|
|
return fmt.Sprintf("%v/%v", ing.Namespace, secrName), nil
|
|
|
|
}
|
|
|
|
return annValue, nil
|
|
|
|
}
|
|
|
|
|
2018-04-12 22:26:10 +00:00
|
|
|
// syncSecrets synchronizes data from all Secrets referenced by the given
|
|
|
|
// Ingress with the local store and file system.
|
2019-06-09 22:49:59 +00:00
|
|
|
func (s *k8sStore) syncSecrets(ing *networkingv1beta1.Ingress) {
|
2018-04-12 22:26:10 +00:00
|
|
|
key := k8s.MetaNamespaceKey(ing)
|
|
|
|
for _, secrKey := range s.secretIngressMap.ReferencedBy(key) {
|
|
|
|
s.syncSecret(secrKey)
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-02 20:59:54 +00:00
|
|
|
// GetSecret returns the Secret matching key.
|
2018-12-06 13:41:21 +00:00
|
|
|
func (s *k8sStore) GetSecret(key string) (*corev1.Secret, error) {
|
2018-01-18 19:14:42 +00:00
|
|
|
return s.listers.Secret.ByKey(key)
|
|
|
|
}
|
|
|
|
|
2018-04-12 22:26:10 +00:00
|
|
|
// ListLocalSSLCerts returns the list of local SSLCerts
|
2018-12-06 13:41:21 +00:00
|
|
|
func (s *k8sStore) ListLocalSSLCerts() []*ingress.SSLCert {
|
2018-01-18 19:14:42 +00:00
|
|
|
var certs []*ingress.SSLCert
|
|
|
|
for _, item := range s.sslStore.List() {
|
|
|
|
if s, ok := item.(*ingress.SSLCert); ok {
|
|
|
|
certs = append(certs, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return certs
|
|
|
|
}
|
|
|
|
|
2018-07-02 20:59:54 +00:00
|
|
|
// GetService returns the Service matching key.
|
2018-12-06 13:41:21 +00:00
|
|
|
func (s *k8sStore) GetService(key string) (*corev1.Service, error) {
|
2018-01-18 19:14:42 +00:00
|
|
|
return s.listers.Service.ByKey(key)
|
|
|
|
}
|
|
|
|
|
2018-11-30 22:56:11 +00:00
|
|
|
// getIngress returns the Ingress matching key.
|
2019-06-09 22:49:59 +00:00
|
|
|
func (s *k8sStore) getIngress(key string) (*networkingv1beta1.Ingress, error) {
|
2018-11-30 22:56:11 +00:00
|
|
|
ing, err := s.listers.IngressWithAnnotation.ByKey(key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ing.Ingress, nil
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ListIngresses returns the list of Ingresses
|
2019-05-18 10:08:05 +00:00
|
|
|
func (s *k8sStore) ListIngresses(filter IngressFilterFunc) []*ingress.Ingress {
|
2018-01-18 19:14:42 +00:00
|
|
|
// filter ingress rules
|
2018-11-30 22:56:11 +00:00
|
|
|
ingresses := make([]*ingress.Ingress, 0)
|
|
|
|
for _, item := range s.listers.IngressWithAnnotation.List() {
|
|
|
|
ing := item.(*ingress.Ingress)
|
2019-05-18 10:08:05 +00:00
|
|
|
|
|
|
|
if filter != nil && filter(ing) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-11-30 22:56:11 +00:00
|
|
|
ingresses = append(ingresses, ing)
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
|
|
|
|
2018-12-19 13:58:42 +00:00
|
|
|
// sort Ingresses using the CreationTimestamp field
|
|
|
|
sort.SliceStable(ingresses, func(i, j int) bool {
|
|
|
|
ir := ingresses[i].CreationTimestamp
|
|
|
|
jr := ingresses[j].CreationTimestamp
|
|
|
|
return ir.Before(&jr)
|
|
|
|
})
|
|
|
|
|
2018-01-18 19:14:42 +00:00
|
|
|
return ingresses
|
|
|
|
}
|
|
|
|
|
2018-04-12 22:26:10 +00:00
|
|
|
// GetLocalSSLCert returns the local copy of a SSLCert
|
2018-12-06 13:41:21 +00:00
|
|
|
func (s *k8sStore) GetLocalSSLCert(key string) (*ingress.SSLCert, error) {
|
2018-01-18 19:14:42 +00:00
|
|
|
return s.sslStore.ByKey(key)
|
|
|
|
}
|
|
|
|
|
2018-07-02 20:59:54 +00:00
|
|
|
// GetConfigMap returns the ConfigMap matching key.
|
2018-12-06 13:41:21 +00:00
|
|
|
func (s *k8sStore) GetConfigMap(key string) (*corev1.ConfigMap, error) {
|
2018-01-18 19:14:42 +00:00
|
|
|
return s.listers.ConfigMap.ByKey(key)
|
|
|
|
}
|
|
|
|
|
2018-07-02 20:59:54 +00:00
|
|
|
// GetServiceEndpoints returns the Endpoints of a Service matching key.
|
2018-12-06 13:41:21 +00:00
|
|
|
func (s *k8sStore) GetServiceEndpoints(key string) (*corev1.Endpoints, error) {
|
2018-07-02 20:59:54 +00:00
|
|
|
return s.listers.Endpoint.ByKey(key)
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetAuthCertificate is used by the auth-tls annotations to get a cert from a secret
|
2018-12-06 13:41:21 +00:00
|
|
|
func (s *k8sStore) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) {
|
2018-04-12 22:26:10 +00:00
|
|
|
if _, err := s.GetLocalSSLCert(name); err != nil {
|
2018-01-18 19:14:42 +00:00
|
|
|
s.syncSecret(name)
|
|
|
|
}
|
|
|
|
|
2018-04-12 22:26:10 +00:00
|
|
|
cert, err := s.GetLocalSSLCert(name)
|
2018-01-18 19:14:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &resolver.AuthSSLCert{
|
|
|
|
Secret: name,
|
|
|
|
CAFileName: cert.CAFileName,
|
|
|
|
PemSHA: cert.PemSHA,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2018-12-06 13:41:21 +00:00
|
|
|
func (s *k8sStore) writeSSLSessionTicketKey(cmap *corev1.ConfigMap, fileName string) {
|
2018-07-15 22:24:16 +00:00
|
|
|
ticketString := ngx_template.ReadConfig(cmap.Data).SSLSessionTicketKey
|
|
|
|
s.backendConfig.SSLSessionTicketKey = ""
|
|
|
|
|
|
|
|
if ticketString != "" {
|
|
|
|
ticketBytes := base64.StdEncoding.WithPadding(base64.StdPadding).DecodedLen(len(ticketString))
|
|
|
|
|
|
|
|
// 81 used instead of 80 because of padding
|
|
|
|
if !(ticketBytes == 48 || ticketBytes == 81) {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Warningf("ssl-session-ticket-key must contain either 48 or 80 bytes")
|
2018-07-15 22:24:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
decodedTicket, err := base64.StdEncoding.DecodeString(ticketString)
|
|
|
|
if err != nil {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Errorf("unexpected error decoding ssl-session-ticket-key: %v", err)
|
2018-07-15 22:24:16 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = ioutil.WriteFile(fileName, decodedTicket, file.ReadWriteByUser)
|
|
|
|
if err != nil {
|
2018-12-05 16:27:55 +00:00
|
|
|
klog.Errorf("unexpected error writing ssl-session-ticket-key to %s: %v", fileName, err)
|
2018-07-15 22:24:16 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
s.backendConfig.SSLSessionTicketKey = ticketString
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-18 19:14:42 +00:00
|
|
|
// GetDefaultBackend returns the default backend
|
2018-12-06 13:24:41 +00:00
|
|
|
func (s *k8sStore) GetDefaultBackend() defaults.Backend {
|
|
|
|
return s.GetBackendConfiguration().Backend
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
|
|
|
|
2018-12-06 13:24:41 +00:00
|
|
|
func (s *k8sStore) GetBackendConfiguration() ngx_config.Configuration {
|
|
|
|
s.backendConfigMu.RLock()
|
|
|
|
defer s.backendConfigMu.RUnlock()
|
|
|
|
|
2018-01-18 19:14:42 +00:00
|
|
|
return s.backendConfig
|
|
|
|
}
|
|
|
|
|
2018-04-12 22:26:10 +00:00
|
|
|
func (s *k8sStore) setConfig(cmap *corev1.ConfigMap) {
|
2018-12-06 13:24:41 +00:00
|
|
|
s.backendConfigMu.Lock()
|
|
|
|
defer s.backendConfigMu.Unlock()
|
|
|
|
|
2018-01-18 19:14:42 +00:00
|
|
|
s.backendConfig = ngx_template.ReadConfig(cmap.Data)
|
2018-07-15 22:24:16 +00:00
|
|
|
s.writeSSLSessionTicketKey(cmap, "/etc/nginx/tickets.key")
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
|
|
|
|
2018-03-29 12:35:01 +00:00
|
|
|
// Run initiates the synchronization of the informers and the initial
|
|
|
|
// synchronization of the secrets.
|
2018-12-06 13:41:21 +00:00
|
|
|
func (s *k8sStore) Run(stopCh chan struct{}) {
|
2018-03-29 12:35:01 +00:00
|
|
|
// start informers
|
|
|
|
s.informers.Run(stopCh)
|
2018-01-18 19:14:42 +00:00
|
|
|
}
|
2018-11-20 20:29:20 +00:00
|
|
|
|
2018-11-27 14:53:51 +00:00
|
|
|
// GetRunningControllerPodsCount returns the number of Running ingress-nginx controller Pods
|
|
|
|
func (s k8sStore) GetRunningControllerPodsCount() int {
|
|
|
|
count := 0
|
2018-11-20 20:29:20 +00:00
|
|
|
|
|
|
|
for _, i := range s.listers.Pod.List() {
|
|
|
|
pod := i.(*corev1.Pod)
|
|
|
|
|
|
|
|
if pod.Status.Phase != corev1.PodRunning {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-11-27 14:53:51 +00:00
|
|
|
count++
|
2018-11-20 20:29:20 +00:00
|
|
|
}
|
|
|
|
|
2018-11-27 14:53:51 +00:00
|
|
|
return count
|
2018-11-20 20:29:20 +00:00
|
|
|
}
|
2019-06-09 22:49:59 +00:00
|
|
|
|
|
|
|
var runtimeScheme = k8sruntime.NewScheme()
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
extensionsv1beta1.AddToScheme(runtimeScheme)
|
|
|
|
networkingv1beta1.AddToScheme(runtimeScheme)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fromExtensions(old *extensionsv1beta1.Ingress) (*networkingv1beta1.Ingress, error) {
|
|
|
|
networkingIngress := &networkingv1beta1.Ingress{}
|
|
|
|
|
|
|
|
err := runtimeScheme.Convert(old, networkingIngress, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return networkingIngress, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func toIngress(obj interface{}) (*networkingv1beta1.Ingress, bool) {
|
|
|
|
oldVersion, inExtension := obj.(*extensionsv1beta1.Ingress)
|
|
|
|
if inExtension {
|
|
|
|
ing, err := fromExtensions(oldVersion)
|
|
|
|
if err != nil {
|
|
|
|
klog.Errorf("unexpected error converting Ingress from extensions package: %v", err)
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
return ing, true
|
|
|
|
}
|
|
|
|
|
|
|
|
if ing, ok := obj.(*networkingv1beta1.Ingress); ok {
|
|
|
|
return ing, true
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, false
|
|
|
|
}
|