github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/k8s/handlers.go (about)

     1  package k8s
     2  
     3  import (
     4  	"reflect"
     5  	"sort"
     6  
     7  	"github.com/golang/glog"
     8  	"github.com/nginxinc/kubernetes-ingress/internal/k8s/secrets"
     9  	v1 "k8s.io/api/core/v1"
    10  	networking "k8s.io/api/networking/v1beta1"
    11  	"k8s.io/client-go/tools/cache"
    12  
    13  	"fmt"
    14  
    15  	conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1"
    16  	conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1"
    17  
    18  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    19  )
    20  
    21  // createConfigMapHandlers builds the handler funcs for config maps
    22  func createConfigMapHandlers(lbc *LoadBalancerController, name string) cache.ResourceEventHandlerFuncs {
    23  	return cache.ResourceEventHandlerFuncs{
    24  		AddFunc: func(obj interface{}) {
    25  			configMap := obj.(*v1.ConfigMap)
    26  			if configMap.Name == name {
    27  				glog.V(3).Infof("Adding ConfigMap: %v", configMap.Name)
    28  				lbc.AddSyncQueue(obj)
    29  			}
    30  		},
    31  		DeleteFunc: func(obj interface{}) {
    32  			configMap, isConfigMap := obj.(*v1.ConfigMap)
    33  			if !isConfigMap {
    34  				deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
    35  				if !ok {
    36  					glog.V(3).Infof("Error received unexpected object: %v", obj)
    37  					return
    38  				}
    39  				configMap, ok = deletedState.Obj.(*v1.ConfigMap)
    40  				if !ok {
    41  					glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-ConfigMap object: %v", deletedState.Obj)
    42  					return
    43  				}
    44  			}
    45  			if configMap.Name == name {
    46  				glog.V(3).Infof("Removing ConfigMap: %v", configMap.Name)
    47  				lbc.AddSyncQueue(obj)
    48  			}
    49  		},
    50  		UpdateFunc: func(old, cur interface{}) {
    51  			if !reflect.DeepEqual(old, cur) {
    52  				configMap := cur.(*v1.ConfigMap)
    53  				if configMap.Name == name {
    54  					glog.V(3).Infof("ConfigMap %v changed, syncing", cur.(*v1.ConfigMap).Name)
    55  					lbc.AddSyncQueue(cur)
    56  				}
    57  			}
    58  		},
    59  	}
    60  }
    61  
    62  // createEndpointHandlers builds the handler funcs for endpoints
    63  func createEndpointHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs {
    64  	return cache.ResourceEventHandlerFuncs{
    65  		AddFunc: func(obj interface{}) {
    66  			endpoint := obj.(*v1.Endpoints)
    67  			glog.V(3).Infof("Adding endpoints: %v", endpoint.Name)
    68  			lbc.AddSyncQueue(obj)
    69  		},
    70  		DeleteFunc: func(obj interface{}) {
    71  			endpoint, isEndpoint := obj.(*v1.Endpoints)
    72  			if !isEndpoint {
    73  				deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
    74  				if !ok {
    75  					glog.V(3).Infof("Error received unexpected object: %v", obj)
    76  					return
    77  				}
    78  				endpoint, ok = deletedState.Obj.(*v1.Endpoints)
    79  				if !ok {
    80  					glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Endpoints object: %v", deletedState.Obj)
    81  					return
    82  				}
    83  			}
    84  			glog.V(3).Infof("Removing endpoints: %v", endpoint.Name)
    85  			lbc.AddSyncQueue(obj)
    86  		},
    87  		UpdateFunc: func(old, cur interface{}) {
    88  			if !reflect.DeepEqual(old, cur) {
    89  				glog.V(3).Infof("Endpoints %v changed, syncing", cur.(*v1.Endpoints).Name)
    90  				lbc.AddSyncQueue(cur)
    91  			}
    92  		},
    93  	}
    94  }
    95  
    96  // createIngressHandlers builds the handler funcs for ingresses
    97  func createIngressHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs {
    98  	return cache.ResourceEventHandlerFuncs{
    99  		AddFunc: func(obj interface{}) {
   100  			ingress := obj.(*networking.Ingress)
   101  			glog.V(3).Infof("Adding Ingress: %v", ingress.Name)
   102  			lbc.AddSyncQueue(obj)
   103  		},
   104  		DeleteFunc: func(obj interface{}) {
   105  			ingress, isIng := obj.(*networking.Ingress)
   106  			if !isIng {
   107  				deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
   108  				if !ok {
   109  					glog.V(3).Infof("Error received unexpected object: %v", obj)
   110  					return
   111  				}
   112  				ingress, ok = deletedState.Obj.(*networking.Ingress)
   113  				if !ok {
   114  					glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Ingress object: %v", deletedState.Obj)
   115  					return
   116  				}
   117  			}
   118  			glog.V(3).Infof("Removing Ingress: %v", ingress.Name)
   119  			lbc.AddSyncQueue(obj)
   120  		},
   121  		UpdateFunc: func(old, current interface{}) {
   122  			c := current.(*networking.Ingress)
   123  			o := old.(*networking.Ingress)
   124  			if hasChanges(o, c) {
   125  				glog.V(3).Infof("Ingress %v changed, syncing", c.Name)
   126  				lbc.AddSyncQueue(c)
   127  			}
   128  		},
   129  	}
   130  }
   131  
   132  // createSecretHandlers builds the handler funcs for secrets
   133  func createSecretHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs {
   134  	return cache.ResourceEventHandlerFuncs{
   135  		AddFunc: func(obj interface{}) {
   136  			secret := obj.(*v1.Secret)
   137  			if !secrets.IsSupportedSecretType(secret.Type) {
   138  				glog.V(3).Infof("Ignoring Secret %v of unsupported type %v", secret.Name, secret.Type)
   139  				return
   140  			}
   141  			glog.V(3).Infof("Adding Secret: %v", secret.Name)
   142  			lbc.AddSyncQueue(obj)
   143  		},
   144  		DeleteFunc: func(obj interface{}) {
   145  			secret, isSecr := obj.(*v1.Secret)
   146  			if !isSecr {
   147  				deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
   148  				if !ok {
   149  					glog.V(3).Infof("Error received unexpected object: %v", obj)
   150  					return
   151  				}
   152  				secret, ok = deletedState.Obj.(*v1.Secret)
   153  				if !ok {
   154  					glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Secret object: %v", deletedState.Obj)
   155  					return
   156  				}
   157  			}
   158  			if !secrets.IsSupportedSecretType(secret.Type) {
   159  				glog.V(3).Infof("Ignoring Secret %v of unsupported type %v", secret.Name, secret.Type)
   160  				return
   161  			}
   162  
   163  			glog.V(3).Infof("Removing Secret: %v", secret.Name)
   164  			lbc.AddSyncQueue(obj)
   165  		},
   166  		UpdateFunc: func(old, cur interface{}) {
   167  			// A secret cannot change its type. That's why we only need to check the type of the current secret.
   168  			curSecret := cur.(*v1.Secret)
   169  			if !secrets.IsSupportedSecretType(curSecret.Type) {
   170  				glog.V(3).Infof("Ignoring Secret %v of unsupported type %v", curSecret.Name, curSecret.Type)
   171  				return
   172  			}
   173  
   174  			if !reflect.DeepEqual(old, cur) {
   175  				glog.V(3).Infof("Secret %v changed, syncing", cur.(*v1.Secret).Name)
   176  				lbc.AddSyncQueue(cur)
   177  			}
   178  		},
   179  	}
   180  }
   181  
   182  // createServiceHandlers builds the handler funcs for services.
   183  //
   184  // In the update handlers below we catch two cases:
   185  // (1) the service is the external service
   186  // (2) the service had a change like a change of the port field of a service port (for such a change Kubernetes doesn't
   187  // update the corresponding endpoints resource, that we monitor as well)
   188  // or a change of the externalName field of an ExternalName service.
   189  //
   190  // In both cases we enqueue the service to be processed by syncService
   191  func createServiceHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs {
   192  	return cache.ResourceEventHandlerFuncs{
   193  		AddFunc: func(obj interface{}) {
   194  			svc := obj.(*v1.Service)
   195  
   196  			glog.V(3).Infof("Adding service: %v", svc.Name)
   197  			lbc.AddSyncQueue(svc)
   198  		},
   199  		DeleteFunc: func(obj interface{}) {
   200  			svc, isSvc := obj.(*v1.Service)
   201  			if !isSvc {
   202  				deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
   203  				if !ok {
   204  					glog.V(3).Infof("Error received unexpected object: %v", obj)
   205  					return
   206  				}
   207  				svc, ok = deletedState.Obj.(*v1.Service)
   208  				if !ok {
   209  					glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Service object: %v", deletedState.Obj)
   210  					return
   211  				}
   212  			}
   213  
   214  			glog.V(3).Infof("Removing service: %v", svc.Name)
   215  			lbc.AddSyncQueue(svc)
   216  		},
   217  		UpdateFunc: func(old, cur interface{}) {
   218  			if !reflect.DeepEqual(old, cur) {
   219  				curSvc := cur.(*v1.Service)
   220  				if lbc.IsExternalServiceForStatus(curSvc) {
   221  					lbc.AddSyncQueue(curSvc)
   222  					return
   223  				}
   224  				oldSvc := old.(*v1.Service)
   225  				if hasServiceChanges(oldSvc, curSvc) {
   226  					glog.V(3).Infof("Service %v changed, syncing", curSvc.Name)
   227  					lbc.AddSyncQueue(curSvc)
   228  				}
   229  			}
   230  		},
   231  	}
   232  }
   233  
   234  type portSort []v1.ServicePort
   235  
   236  func (a portSort) Len() int {
   237  	return len(a)
   238  }
   239  
   240  func (a portSort) Swap(i, j int) {
   241  	a[i], a[j] = a[j], a[i]
   242  }
   243  
   244  func (a portSort) Less(i, j int) bool {
   245  	if a[i].Name == a[j].Name {
   246  		return a[i].Port < a[j].Port
   247  	}
   248  	return a[i].Name < a[j].Name
   249  }
   250  
   251  // hasServicedChanged checks if the service has changed based on custom rules we define (eg. port).
   252  func hasServiceChanges(oldSvc, curSvc *v1.Service) bool {
   253  	if hasServicePortChanges(oldSvc.Spec.Ports, curSvc.Spec.Ports) {
   254  		return true
   255  	}
   256  	if hasServiceExternalNameChanges(oldSvc, curSvc) {
   257  		return true
   258  	}
   259  	return false
   260  }
   261  
   262  // hasServiceExternalNameChanges only compares Service.Spec.Externalname for Type ExternalName services.
   263  func hasServiceExternalNameChanges(oldSvc, curSvc *v1.Service) bool {
   264  	return curSvc.Spec.Type == v1.ServiceTypeExternalName && oldSvc.Spec.ExternalName != curSvc.Spec.ExternalName
   265  }
   266  
   267  // hasServicePortChanges only compares ServicePort.Name and .Port.
   268  func hasServicePortChanges(oldServicePorts []v1.ServicePort, curServicePorts []v1.ServicePort) bool {
   269  	if len(oldServicePorts) != len(curServicePorts) {
   270  		return true
   271  	}
   272  
   273  	sort.Sort(portSort(oldServicePorts))
   274  	sort.Sort(portSort(curServicePorts))
   275  
   276  	for i := range oldServicePorts {
   277  		if oldServicePorts[i].Port != curServicePorts[i].Port ||
   278  			oldServicePorts[i].Name != curServicePorts[i].Name {
   279  			return true
   280  		}
   281  	}
   282  	return false
   283  }
   284  
   285  func createVirtualServerHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs {
   286  	return cache.ResourceEventHandlerFuncs{
   287  		AddFunc: func(obj interface{}) {
   288  			vs := obj.(*conf_v1.VirtualServer)
   289  			glog.V(3).Infof("Adding VirtualServer: %v", vs.Name)
   290  			lbc.AddSyncQueue(vs)
   291  		},
   292  		DeleteFunc: func(obj interface{}) {
   293  			vs, isVs := obj.(*conf_v1.VirtualServer)
   294  			if !isVs {
   295  				deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
   296  				if !ok {
   297  					glog.V(3).Infof("Error received unexpected object: %v", obj)
   298  					return
   299  				}
   300  				vs, ok = deletedState.Obj.(*conf_v1.VirtualServer)
   301  				if !ok {
   302  					glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-VirtualServer object: %v", deletedState.Obj)
   303  					return
   304  				}
   305  			}
   306  			glog.V(3).Infof("Removing VirtualServer: %v", vs.Name)
   307  			lbc.AddSyncQueue(vs)
   308  		},
   309  		UpdateFunc: func(old, cur interface{}) {
   310  			curVs := cur.(*conf_v1.VirtualServer)
   311  			oldVs := old.(*conf_v1.VirtualServer)
   312  			if !reflect.DeepEqual(oldVs.Spec, curVs.Spec) {
   313  				glog.V(3).Infof("VirtualServer %v changed, syncing", curVs.Name)
   314  				lbc.AddSyncQueue(curVs)
   315  			}
   316  		},
   317  	}
   318  }
   319  
   320  func createVirtualServerRouteHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs {
   321  	return cache.ResourceEventHandlerFuncs{
   322  		AddFunc: func(obj interface{}) {
   323  			vsr := obj.(*conf_v1.VirtualServerRoute)
   324  			glog.V(3).Infof("Adding VirtualServerRoute: %v", vsr.Name)
   325  			lbc.AddSyncQueue(vsr)
   326  		},
   327  		DeleteFunc: func(obj interface{}) {
   328  			vsr, isVsr := obj.(*conf_v1.VirtualServerRoute)
   329  			if !isVsr {
   330  				deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
   331  				if !ok {
   332  					glog.V(3).Infof("Error received unexpected object: %v", obj)
   333  					return
   334  				}
   335  				vsr, ok = deletedState.Obj.(*conf_v1.VirtualServerRoute)
   336  				if !ok {
   337  					glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-VirtualServerRoute object: %v", deletedState.Obj)
   338  					return
   339  				}
   340  			}
   341  			glog.V(3).Infof("Removing VirtualServerRoute: %v", vsr.Name)
   342  			lbc.AddSyncQueue(vsr)
   343  		},
   344  		UpdateFunc: func(old, cur interface{}) {
   345  			curVsr := cur.(*conf_v1.VirtualServerRoute)
   346  			oldVsr := old.(*conf_v1.VirtualServerRoute)
   347  			if !reflect.DeepEqual(oldVsr.Spec, curVsr.Spec) {
   348  				glog.V(3).Infof("VirtualServerRoute %v changed, syncing", curVsr.Name)
   349  				lbc.AddSyncQueue(curVsr)
   350  			}
   351  		},
   352  	}
   353  }
   354  
   355  func createGlobalConfigurationHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs {
   356  	return cache.ResourceEventHandlerFuncs{
   357  		AddFunc: func(obj interface{}) {
   358  			gc := obj.(*conf_v1alpha1.GlobalConfiguration)
   359  			glog.V(3).Infof("Adding GlobalConfiguration: %v", gc.Name)
   360  			lbc.AddSyncQueue(gc)
   361  		},
   362  		DeleteFunc: func(obj interface{}) {
   363  			gc, isGc := obj.(*conf_v1alpha1.GlobalConfiguration)
   364  			if !isGc {
   365  				deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
   366  				if !ok {
   367  					glog.V(3).Infof("Error received unexpected object: %v", obj)
   368  					return
   369  				}
   370  				gc, ok = deletedState.Obj.(*conf_v1alpha1.GlobalConfiguration)
   371  				if !ok {
   372  					glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-GlobalConfiguration object: %v", deletedState.Obj)
   373  					return
   374  				}
   375  			}
   376  			glog.V(3).Infof("Removing GlobalConfiguration: %v", gc.Name)
   377  			lbc.AddSyncQueue(gc)
   378  		},
   379  		UpdateFunc: func(old, cur interface{}) {
   380  			curGc := cur.(*conf_v1alpha1.GlobalConfiguration)
   381  			if !reflect.DeepEqual(old, cur) {
   382  				glog.V(3).Infof("GlobalConfiguration %v changed, syncing", curGc.Name)
   383  				lbc.AddSyncQueue(curGc)
   384  			}
   385  		},
   386  	}
   387  }
   388  
   389  func createTransportServerHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs {
   390  	return cache.ResourceEventHandlerFuncs{
   391  		AddFunc: func(obj interface{}) {
   392  			ts := obj.(*conf_v1alpha1.TransportServer)
   393  			glog.V(3).Infof("Adding TransportServer: %v", ts.Name)
   394  			lbc.AddSyncQueue(ts)
   395  		},
   396  		DeleteFunc: func(obj interface{}) {
   397  			ts, isTs := obj.(*conf_v1alpha1.TransportServer)
   398  			if !isTs {
   399  				deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
   400  				if !ok {
   401  					glog.V(3).Infof("Error received unexpected object: %v", obj)
   402  					return
   403  				}
   404  				ts, ok = deletedState.Obj.(*conf_v1alpha1.TransportServer)
   405  				if !ok {
   406  					glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-TransportServer object: %v", deletedState.Obj)
   407  					return
   408  				}
   409  			}
   410  			glog.V(3).Infof("Removing TransportServer: %v", ts.Name)
   411  			lbc.AddSyncQueue(ts)
   412  		},
   413  		UpdateFunc: func(old, cur interface{}) {
   414  			curTs := cur.(*conf_v1alpha1.TransportServer)
   415  			if !reflect.DeepEqual(old, cur) {
   416  				glog.V(3).Infof("TransportServer %v changed, syncing", curTs.Name)
   417  				lbc.AddSyncQueue(curTs)
   418  			}
   419  		},
   420  	}
   421  }
   422  
   423  func createPolicyHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs {
   424  	return cache.ResourceEventHandlerFuncs{
   425  		AddFunc: func(obj interface{}) {
   426  			pol := obj.(*conf_v1.Policy)
   427  			glog.V(3).Infof("Adding Policy: %v", pol.Name)
   428  			lbc.AddSyncQueue(pol)
   429  		},
   430  		DeleteFunc: func(obj interface{}) {
   431  			pol, isPol := obj.(*conf_v1.Policy)
   432  			if !isPol {
   433  				deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
   434  				if !ok {
   435  					glog.V(3).Infof("Error received unexpected object: %v", obj)
   436  					return
   437  				}
   438  				pol, ok = deletedState.Obj.(*conf_v1.Policy)
   439  				if !ok {
   440  					glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Policy object: %v", deletedState.Obj)
   441  					return
   442  				}
   443  			}
   444  			glog.V(3).Infof("Removing Policy: %v", pol.Name)
   445  			lbc.AddSyncQueue(pol)
   446  		},
   447  		UpdateFunc: func(old, cur interface{}) {
   448  			curPol := cur.(*conf_v1.Policy)
   449  			if !reflect.DeepEqual(old, cur) {
   450  				glog.V(3).Infof("Policy %v changed, syncing", curPol.Name)
   451  				lbc.AddSyncQueue(curPol)
   452  			}
   453  		},
   454  	}
   455  }
   456  
   457  func createIngressLinkHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs {
   458  	return cache.ResourceEventHandlerFuncs{
   459  		AddFunc: func(obj interface{}) {
   460  			link := obj.(*unstructured.Unstructured)
   461  			glog.V(3).Infof("Adding IngressLink: %v", link.GetName())
   462  			lbc.AddSyncQueue(link)
   463  		},
   464  		DeleteFunc: func(obj interface{}) {
   465  			link, isUnstructured := obj.(*unstructured.Unstructured)
   466  
   467  			if !isUnstructured {
   468  				deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
   469  				if !ok {
   470  					glog.V(3).Infof("Error received unexpected object: %v", obj)
   471  					return
   472  				}
   473  				link, ok = deletedState.Obj.(*unstructured.Unstructured)
   474  				if !ok {
   475  					glog.V(3).Infof("Error DeletedFinalStateUnknown contained non-Unstructured object: %v", deletedState.Obj)
   476  					return
   477  				}
   478  			}
   479  
   480  			glog.V(3).Infof("Removing IngressLink: %v", link.GetName())
   481  			lbc.AddSyncQueue(link)
   482  		},
   483  		UpdateFunc: func(old, cur interface{}) {
   484  			oldLink := old.(*unstructured.Unstructured)
   485  			curLink := cur.(*unstructured.Unstructured)
   486  			different, err := areResourcesDifferent(oldLink, curLink)
   487  			if err != nil {
   488  				glog.V(3).Infof("Error when comparing IngressLinks: %v", err)
   489  				lbc.AddSyncQueue(curLink)
   490  			}
   491  			if different {
   492  				glog.V(3).Infof("IngressLink %v changed, syncing", oldLink.GetName())
   493  				lbc.AddSyncQueue(curLink)
   494  			}
   495  		},
   496  	}
   497  }
   498  
   499  func createAppProtectPolicyHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs {
   500  	handlers := cache.ResourceEventHandlerFuncs{
   501  		AddFunc: func(obj interface{}) {
   502  			pol := obj.(*unstructured.Unstructured)
   503  			glog.V(3).Infof("Adding AppProtectPolicy: %v", pol.GetName())
   504  			lbc.AddSyncQueue(pol)
   505  		},
   506  		UpdateFunc: func(oldObj, obj interface{}) {
   507  			oldPol := oldObj.(*unstructured.Unstructured)
   508  			newPol := obj.(*unstructured.Unstructured)
   509  			different, err := areResourcesDifferent(oldPol, newPol)
   510  			if err != nil {
   511  				glog.V(3).Infof("Error when comparing policy %v", err)
   512  				lbc.AddSyncQueue(newPol)
   513  			}
   514  			if different {
   515  				glog.V(3).Infof("ApPolicy %v changed, syncing", oldPol.GetName())
   516  				lbc.AddSyncQueue(newPol)
   517  			}
   518  		},
   519  		DeleteFunc: func(obj interface{}) {
   520  			lbc.AddSyncQueue(obj)
   521  		},
   522  	}
   523  	return handlers
   524  }
   525  
   526  // areResourcesDifferent returns true if the resources are different based on their spec.
   527  func areResourcesDifferent(oldresource, resource *unstructured.Unstructured) (bool, error) {
   528  	oldSpec, found, err := unstructured.NestedMap(oldresource.Object, "spec")
   529  	if !found {
   530  		glog.V(3).Infof("Warning, oldspec has unexpected format")
   531  	}
   532  	if err != nil {
   533  		return false, err
   534  	}
   535  	spec, found, err := unstructured.NestedMap(resource.Object, "spec")
   536  	if !found {
   537  		return false, fmt.Errorf("Error, spec has unexpected format")
   538  	}
   539  	if err != nil {
   540  		return false, err
   541  	}
   542  	eq := reflect.DeepEqual(oldSpec, spec)
   543  	if eq {
   544  		glog.V(3).Infof("New spec of %v same as old spec", oldresource.GetName())
   545  	}
   546  	return !eq, nil
   547  }
   548  
   549  func createAppProtectLogConfHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs {
   550  	handlers := cache.ResourceEventHandlerFuncs{
   551  		AddFunc: func(obj interface{}) {
   552  			conf := obj.(*unstructured.Unstructured)
   553  			glog.V(3).Infof("Adding AppProtectLogConf: %v", conf.GetName())
   554  			lbc.AddSyncQueue(conf)
   555  		},
   556  		UpdateFunc: func(oldObj, obj interface{}) {
   557  			oldConf := oldObj.(*unstructured.Unstructured)
   558  			newConf := obj.(*unstructured.Unstructured)
   559  			different, err := areResourcesDifferent(oldConf, newConf)
   560  			if err != nil {
   561  				glog.V(3).Infof("Error when comparing LogConfs %v", err)
   562  				lbc.AddSyncQueue(newConf)
   563  			}
   564  			if different {
   565  				glog.V(3).Infof("ApLogConf %v changed, syncing", oldConf.GetName())
   566  				lbc.AddSyncQueue(newConf)
   567  			}
   568  		},
   569  		DeleteFunc: func(obj interface{}) {
   570  			lbc.AddSyncQueue(obj)
   571  		},
   572  	}
   573  	return handlers
   574  }
   575  
   576  func createAppProtectUserSigHandlers(lbc *LoadBalancerController) cache.ResourceEventHandlerFuncs {
   577  	handlers := cache.ResourceEventHandlerFuncs{
   578  		AddFunc: func(obj interface{}) {
   579  			sig := obj.(*unstructured.Unstructured)
   580  			glog.V(3).Infof("Adding AppProtectUserSig: %v", sig.GetName())
   581  			lbc.AddSyncQueue(sig)
   582  		},
   583  		UpdateFunc: func(oldObj, obj interface{}) {
   584  			oldSig := oldObj.(*unstructured.Unstructured)
   585  			newSig := obj.(*unstructured.Unstructured)
   586  			different, err := areResourcesDifferent(oldSig, newSig)
   587  			if err != nil {
   588  				glog.V(3).Infof("Error when comparing UserSigs %v", err)
   589  				lbc.AddSyncQueue(newSig)
   590  			}
   591  			if different {
   592  				glog.V(3).Infof("ApUserSig %v changed, syncing", oldSig.GetName())
   593  				lbc.AddSyncQueue(newSig)
   594  			}
   595  		},
   596  		DeleteFunc: func(obj interface{}) {
   597  			lbc.AddSyncQueue(obj)
   598  		},
   599  	}
   600  	return handlers
   601  }