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 (
2020-03-24 13:44:13 +00:00
"context"
2018-01-18 19:14:42 +00:00
"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
"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
networkingv1beta1 "k8s.io/api/networking/v1beta1"
2018-03-29 12:35:01 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2020-10-13 14:37:45 +00:00
"k8s.io/apimachinery/pkg/fields"
2018-11-20 20:29:20 +00:00
k8sruntime "k8s.io/apimachinery/pkg/runtime"
2018-01-18 19:14:42 +00:00
"k8s.io/apimachinery/pkg/util/runtime"
2020-04-22 16:24:57 +00:00
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
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"
2020-08-08 23:31:02 +00:00
"k8s.io/klog/v2"
2018-01-18 19:14:42 +00:00
"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"
2020-01-08 22:46:43 +00:00
"k8s.io/ingress-nginx/internal/nginx"
2018-01-18 19:14:42 +00:00
)
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.
2020-09-25 21:45:13 +00:00
ListIngresses ( ) [ ] * ingress . Ingress
2018-01-18 19:14:42 +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-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
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 { } ) {
2019-08-13 21:14:55 +00:00
go i . Secret . Run ( stopCh )
2018-03-29 12:35:01 +00:00
go i . Endpoint . Run ( stopCh )
go i . Service . Run ( stopCh )
go i . ConfigMap . 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-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-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 ,
2018-06-04 21:48:30 +00:00
updateCh * channels . RingChannel ,
2018-12-11 21:57:46 +00:00
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 ( ) ,
updateCh : updateCh ,
backendConfig : ngx_config . NewDefault ( ) ,
syncSecretMu : & sync . Mutex { } ,
backendConfigMu : & sync . RWMutex { } ,
secretIngressMap : NewObjectRefMap ( ) ,
defaultSSLCertificate : defaultSSLCertificate ,
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
2020-06-11 17:18:49 +00:00
// As we currently do not filter out kubernetes objects we list, we can
// retrieve a huge amount of data from the API server.
// In a cluster using HELM < v3 configmaps are used to store binary data.
// If you happen to have a lot of HELM releases in the cluster it will make
// the memory consumption of nginx-ingress-controller explode.
// In order to avoid that we filter out labels OWNER=TILLER.
2020-10-13 14:37:45 +00:00
labelsTweakListOptionsFunc := func ( options * metav1 . ListOptions ) {
2020-06-11 17:18:49 +00:00
if len ( options . LabelSelector ) > 0 {
options . LabelSelector += ",OWNER!=TILLER"
} else {
options . LabelSelector = "OWNER!=TILLER"
}
}
2020-10-13 14:37:45 +00:00
// As of HELM >= v3 helm releases are stored using Secrets instead of ConfigMaps.
// In order to avoid listing those secrets we discard type "helm.sh/release.v1"
secretsTweakListOptionsFunc := func ( options * metav1 . ListOptions ) {
helmAntiSelector := fields . OneTermNotEqualSelector ( "type" , "helm.sh/release.v1" )
baseSelector , err := fields . ParseSelector ( options . FieldSelector )
if err != nil {
options . FieldSelector = helmAntiSelector . String ( )
} else {
options . FieldSelector = fields . AndSelectors ( baseSelector , helmAntiSelector ) . String ( )
}
}
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 ) ,
2020-10-13 14:37:45 +00:00
)
// create informers factory for configmaps
infFactoryConfigmaps := informers . NewSharedInformerFactoryWithOptions ( client , resyncPeriod ,
informers . WithNamespace ( namespace ) ,
informers . WithTweakListOptions ( labelsTweakListOptionsFunc ) ,
)
// create informers factory for secrets
infFactorySecrets := informers . NewSharedInformerFactoryWithOptions ( client , resyncPeriod ,
informers . WithNamespace ( namespace ) ,
informers . WithTweakListOptions ( secretsTweakListOptionsFunc ) ,
)
2018-03-29 12:35:01 +00:00
2020-09-02 13:08:53 +00:00
store . informers . Ingress = infFactory . Networking ( ) . 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 ( )
2020-10-13 14:37:45 +00:00
store . informers . Secret = infFactorySecrets . Core ( ) . V1 ( ) . Secrets ( ) . Informer ( )
2018-03-29 12:35:01 +00:00
store . listers . Secret . Store = store . informers . Secret . GetStore ( )
2020-10-13 14:37:45 +00:00
store . informers . ConfigMap = infFactoryConfigmaps . Core ( ) . V1 ( ) . ConfigMaps ( ) . Informer ( )
2018-03-29 12:35:01 +00:00
store . listers . ConfigMap . Store = store . informers . ConfigMap . GetStore ( )
store . informers . Service = infFactory . Core ( ) . V1 ( ) . Services ( ) . Informer ( )
store . listers . Service . Store = store . informers . Service . 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 {
2020-09-27 20:32:40 +00:00
klog . ErrorS ( nil , "Error obtaining object from tombstone" , "key" , obj )
2019-01-07 20:29:18 +00:00
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 ) {
return
}
2020-09-27 20:32:40 +00:00
2019-01-25 19:53:55 +00:00
if isCatchAllIngress ( ing . Spec ) && disableCatchAll {
2020-09-27 20:32:40 +00:00
klog . InfoS ( "Ignoring delete for catch-all because of --disable-catch-all" , "namespace" , ing . Namespace , "ingress" , ing . Name )
2019-01-07 20:29:18 +00:00
return
}
2020-09-27 20:32:40 +00:00
2019-01-07 20:29:18 +00:00
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 )
2020-09-27 20:32:40 +00:00
klog . InfoS ( "Ignoring add for ingress based on annotation" , "namespace" , ing . Namespace , "ingress" , ing . Name , "annotation" , a )
2018-01-18 19:14:42 +00:00
return
}
2019-01-25 19:53:55 +00:00
if isCatchAllIngress ( ing . Spec ) && disableCatchAll {
2020-09-27 20:32:40 +00:00
klog . InfoS ( "Ignoring add for catch-all ingress because of --disable-catch-all" , "namespace" , ing . Namespace , "ingress" , ing . Name )
2018-12-11 21:57:46 +00:00
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 {
2020-09-27 20:32:40 +00:00
klog . InfoS ( "ignoring update for catch-all ingress because of --disable-catch-all" , "namespace" , curIng . Namespace , "ingress" , curIng . Name )
2018-12-11 21:57:46 +00:00
return
}
2020-09-27 20:32:40 +00:00
klog . InfoS ( "creating ingress" , "namespace" , curIng . Namespace , "ingress" , curIng . Name , "class" , 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 {
2020-09-27 20:32:40 +00:00
klog . InfoS ( "removing ingress" , "namespace" , curIng . Namespace , "ingress" , curIng . Name , "class" , 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 {
2020-09-27 20:32:40 +00:00
klog . InfoS ( "ignoring update for catch-all ingress and delete old one because of --disable-catch-all" , "namespace" , curIng . Namespace , "ingress" , curIng . Name )
2019-01-07 20:29:18 +00:00
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 {
2020-09-27 20:32:40 +00:00
klog . V ( 3 ) . InfoS ( "No changes on ingress. Skipping update" , "namespace" , curIng . Namespace , "ingress" , 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 {
2020-09-27 20:32:40 +00:00
klog . InfoS ( "Secret was added and it is used in ingress annotations. Parsing" , "secret" , 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 {
2020-09-27 20:32:40 +00:00
klog . InfoS ( "secret was updated and it is used in ingress annotations. Parsing" , "secret" , 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 {
2020-09-27 20:32:40 +00:00
klog . ErrorS ( err , "could not find Ingress in local store" , "ingress" , 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 {
return
}
2020-09-27 20:32:40 +00:00
2018-04-12 22:26:10 +00:00
sec , ok = tombstone . Obj . ( * corev1 . Secret )
2018-01-18 19:14:42 +00:00
if ! ok {
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 {
2020-09-27 20:32:40 +00:00
klog . InfoS ( "secret was deleted and it is used in ingress annotations. Parsing" , "secret" , 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
}
2019-08-13 21:14:55 +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 ,
}
}
} ,
}
2019-09-23 00:11:15 +00:00
// TODO: add e2e test to verify that changes to one or more configmap trigger an update
changeTriggerUpdate := func ( name string ) bool {
return name == configmap || name == tcp || name == udp
}
2020-02-16 14:58:37 +00:00
handleCfgMapEvent := func ( key string , cfgMap * corev1 . ConfigMap , eventName string ) {
// updates to configuration configmaps can trigger an update
triggerUpdate := false
if changeTriggerUpdate ( key ) {
triggerUpdate = true
recorder . Eventf ( cfgMap , corev1 . EventTypeNormal , eventName , fmt . Sprintf ( "ConfigMap %v" , key ) )
if key == configmap {
store . setConfig ( cfgMap )
2019-09-23 00:11:15 +00:00
}
2020-02-16 14:58:37 +00:00
}
2019-09-23 00:11:15 +00:00
2020-02-16 14:58:37 +00:00
ings := store . listers . IngressWithAnnotation . List ( )
for _ , ingKey := range ings {
key := k8s . MetaNamespaceKey ( ingKey )
ing , err := store . getIngress ( key )
if err != nil {
klog . Errorf ( "could not find Ingress %v in local store: %v" , key , err )
continue
}
2020-02-11 10:00:48 +00:00
2020-02-16 14:58:37 +00:00
if parser . AnnotationsReferencesConfigmap ( ing ) {
store . syncIngress ( ing )
continue
2020-02-11 10:00:48 +00:00
}
if triggerUpdate {
2020-02-16 14:58:37 +00:00
store . syncIngress ( ing )
}
}
if triggerUpdate {
updateCh . In ( ) <- Event {
Type : ConfigurationEvent ,
Obj : cfgMap ,
2018-01-18 19:14:42 +00:00
}
2020-02-16 14:58:37 +00:00
}
}
cmEventHandler := cache . ResourceEventHandlerFuncs {
AddFunc : func ( obj interface { } ) {
cfgMap := obj . ( * corev1 . ConfigMap )
key := k8s . MetaNamespaceKey ( cfgMap )
handleCfgMapEvent ( key , cfgMap , "CREATE" )
2018-01-18 19:14:42 +00:00
} ,
UpdateFunc : func ( old , cur interface { } ) {
2019-09-23 00:11:15 +00:00
if reflect . DeepEqual ( old , cur ) {
return
}
2018-06-21 12:47:06 +00:00
2020-02-16 14:58:37 +00:00
cfgMap := cur . ( * corev1 . ConfigMap )
key := k8s . MetaNamespaceKey ( cfgMap )
handleCfgMapEvent ( key , cfgMap , "UPDATE" )
2018-01-18 19:14:42 +00:00
} ,
}
2020-04-29 15:30:50 +00:00
serviceHandler := cache . ResourceEventHandlerFuncs {
UpdateFunc : func ( old , cur interface { } ) {
oldSvc := old . ( * corev1 . Service )
curSvc := cur . ( * corev1 . Service )
if reflect . DeepEqual ( oldSvc , curSvc ) {
return
}
updateCh . In ( ) <- Event {
Type : UpdateEvent ,
Obj : cur ,
}
} ,
}
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 )
2020-04-29 15:30:50 +00:00
store . informers . Service . AddEventHandler ( serviceHandler )
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 )
2020-03-24 13:44:13 +00:00
cm , err := client . CoreV1 ( ) . ConfigMaps ( ns ) . Get ( context . TODO ( ) , 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
}
2020-04-22 16:24:57 +00:00
// Default path type is Prefix to not break existing definitions
var defaultPathType = networkingv1beta1 . PathTypePrefix
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 ( & copyIng . ObjectMeta )
ing . Spec . DeepCopyInto ( & copyIng . Spec )
2018-12-17 12:10:53 +00:00
ing . Status . DeepCopyInto ( & copyIng . 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
2020-04-28 15:14:27 +00:00
k8s . SetDefaultNGINXPathType ( copyIng )
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" ,
2019-07-17 00:23:32 +00:00
"proxy-ssl-secret" ,
2019-09-23 00:11:15 +00:00
"secure-verify-ca-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
}
2020-09-25 21:45:13 +00:00
func sortIngressSlice ( ingresses [ ] * ingress . Ingress ) {
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
2019-08-08 02:06:11 +00:00
if ir . Equal ( & jr ) {
in := fmt . Sprintf ( "%v/%v" , ingresses [ i ] . Namespace , ingresses [ i ] . Name )
jn := fmt . Sprintf ( "%v/%v" , ingresses [ j ] . Namespace , ingresses [ j ] . Name )
2019-09-18 14:59:03 +00:00
klog . V ( 3 ) . Infof ( "Ingress %v and %v have identical CreationTimestamp" , in , jn )
2019-08-08 02:06:11 +00:00
return in > jn
}
2018-12-19 13:58:42 +00:00
return ir . Before ( & jr )
} )
2020-09-25 21:45:13 +00:00
}
// ListIngresses returns the list of Ingresses
func ( s * k8sStore ) ListIngresses ( ) [ ] * ingress . Ingress {
// filter ingress rules
ingresses := make ( [ ] * ingress . Ingress , 0 )
for _ , item := range s . listers . IngressWithAnnotation . List ( ) {
ing := item . ( * ingress . Ingress )
ingresses = append ( ingresses , ing )
}
sortIngressSlice ( ingresses )
2018-12-19 13:58:42 +00:00
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 {
2019-09-03 20:47:28 +00:00
Secret : name ,
CAFileName : cert . CAFileName ,
CASHA : cert . CASHA ,
CRLFileName : cert . CRLFileName ,
CRLSHA : cert . CRLSHA ,
2019-10-17 07:23:42 +00:00
PemFileName : cert . PemFileName ,
2018-01-18 19:14:42 +00:00
} , 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 ( )
2019-12-11 01:45:02 +00:00
if cmap == nil {
return
}
2018-01-18 19:14:42 +00:00
s . backendConfig = ngx_template . ReadConfig ( cmap . Data )
2020-01-08 22:46:43 +00:00
if s . backendConfig . UseGeoIP2 && ! nginx . GeoLite2DBExists ( ) {
2020-09-27 20:32:40 +00:00
klog . Warning ( "The GeoIP2 feature is enabled but the databases are missing. Disabling" )
2020-01-08 22:46:43 +00:00
s . backendConfig . UseGeoIP2 = false
}
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
2019-06-09 22:49:59 +00:00
var runtimeScheme = k8sruntime . NewScheme ( )
func init ( ) {
2020-04-22 16:24:57 +00:00
utilruntime . Must ( networkingv1beta1 . AddToScheme ( runtimeScheme ) )
2019-06-09 22:49:59 +00:00
}
func toIngress ( obj interface { } ) ( * networkingv1beta1 . Ingress , bool ) {
if ing , ok := obj . ( * networkingv1beta1 . Ingress ) ; ok {
2020-04-28 15:14:27 +00:00
k8s . SetDefaultNGINXPathType ( ing )
2019-06-09 22:49:59 +00:00
return ing , true
}
return nil , false
}