Skip to content

Commit 104e945

Browse files
committed
config: add ignored fields
Specify fields to skip sending object update. Will be applied to all objects. If after removal of these fields from k8s object all remaining fields will be equal, handler won't trigger sending update. Removing array elements is not supported. For example, ```yaml ignorefields: status: metadata: resourceVersion: managedFields: ``` will remove ".status", ".metadata.resourceVersion" and ".metadata.managedFields" from k8s object before comparing old & new k8s objects.
1 parent a97d5dd commit 104e945

File tree

3 files changed

+79
-22
lines changed

3 files changed

+79
-22
lines changed

config/config.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,19 @@ type Config struct {
9494
// For watching specific namespace, leave it empty for watching all.
9595
// this config is ignored when watching namespaces
9696
Namespace string `json:"namespace,omitempty"`
97+
98+
// Specify fields to skip sending object update. Will be applied to all objects.
99+
// If after removal of these fields from k8s object all remaining fields will be equal,
100+
// handler won't trigger sending update. Removing array elements is not supported.
101+
// For example,
102+
// ignorefields:
103+
// status:
104+
// metadata:
105+
// resourceVersion:
106+
// managedFields:
107+
// will remove ".status", ".metadata.resourceVersion" and ".metadata.managedFields"
108+
// from k8s object before comparing old & new k8s objects.
109+
IgnoredFields map[string]interface{} `json:"ignoredfields,omitempty"`
97110
}
98111

99112
// Slack contains slack configuration

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.14
44

55
require (
66
github.com/fatih/structtag v1.2.0
7+
github.com/google/go-cmp v0.6.0
78
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect
89
github.com/hashicorp/hcl v0.0.0-20171017181929-23c074d0eceb // indirect
910
github.com/inconshreveable/mousetrap v1.0.0 // indirect

pkg/controller/controller.go

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ import (
5454

5555
"github.com/prometheus/client_golang/prometheus"
5656
"github.com/prometheus/client_golang/prometheus/promauto"
57+
58+
"github.com/google/go-cmp/cmp"
5759
)
5860

5961
const maxRetries = 5
@@ -131,7 +133,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
131133
cache.Indexers{},
132134
)
133135

134-
allCoreEventsController := newResourceController(kubeClient, eventHandler, allCoreEventsInformer, objName(api_v1.Event{}), V1, kubewatchEventsMetrics)
136+
allCoreEventsController := newResourceController(kubeClient, eventHandler, allCoreEventsInformer, objName(api_v1.Event{}), V1, kubewatchEventsMetrics, conf.IgnoredFields)
135137
stopAllCoreEventsCh := make(chan struct{})
136138
defer close(stopAllCoreEventsCh)
137139

@@ -155,7 +157,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
155157
cache.Indexers{},
156158
)
157159

158-
allEventsController := newResourceController(kubeClient, eventHandler, allEventsInformer, objName(events_v1.Event{}), EVENTS_V1, kubewatchEventsMetrics)
160+
allEventsController := newResourceController(kubeClient, eventHandler, allEventsInformer, objName(events_v1.Event{}), EVENTS_V1, kubewatchEventsMetrics, conf.IgnoredFields)
159161
stopAllEventsCh := make(chan struct{})
160162
defer close(stopAllEventsCh)
161163

@@ -177,7 +179,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
177179
cache.Indexers{},
178180
)
179181

180-
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.Pod{}), V1, kubewatchEventsMetrics)
182+
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.Pod{}), V1, kubewatchEventsMetrics, conf.IgnoredFields)
181183
stopCh := make(chan struct{})
182184
defer close(stopCh)
183185

@@ -199,7 +201,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
199201
cache.Indexers{},
200202
)
201203

202-
c := newResourceController(kubeClient, eventHandler, informer, objName(autoscaling_v1.HorizontalPodAutoscaler{}), AUTOSCALING_V1, kubewatchEventsMetrics)
204+
c := newResourceController(kubeClient, eventHandler, informer, objName(autoscaling_v1.HorizontalPodAutoscaler{}), AUTOSCALING_V1, kubewatchEventsMetrics, conf.IgnoredFields)
203205
stopCh := make(chan struct{})
204206
defer close(stopCh)
205207

@@ -222,7 +224,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
222224
cache.Indexers{},
223225
)
224226

225-
c := newResourceController(kubeClient, eventHandler, informer, objName(apps_v1.DaemonSet{}), APPS_V1, kubewatchEventsMetrics)
227+
c := newResourceController(kubeClient, eventHandler, informer, objName(apps_v1.DaemonSet{}), APPS_V1, kubewatchEventsMetrics, conf.IgnoredFields)
226228
stopCh := make(chan struct{})
227229
defer close(stopCh)
228230

@@ -244,7 +246,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
244246
cache.Indexers{},
245247
)
246248

247-
c := newResourceController(kubeClient, eventHandler, informer, objName(apps_v1.StatefulSet{}), APPS_V1, kubewatchEventsMetrics)
249+
c := newResourceController(kubeClient, eventHandler, informer, objName(apps_v1.StatefulSet{}), APPS_V1, kubewatchEventsMetrics, conf.IgnoredFields)
248250
stopCh := make(chan struct{})
249251
defer close(stopCh)
250252

@@ -266,7 +268,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
266268
cache.Indexers{},
267269
)
268270

269-
c := newResourceController(kubeClient, eventHandler, informer, objName(apps_v1.ReplicaSet{}), APPS_V1, kubewatchEventsMetrics)
271+
c := newResourceController(kubeClient, eventHandler, informer, objName(apps_v1.ReplicaSet{}), APPS_V1, kubewatchEventsMetrics, conf.IgnoredFields)
270272
stopCh := make(chan struct{})
271273
defer close(stopCh)
272274

@@ -288,7 +290,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
288290
cache.Indexers{},
289291
)
290292

291-
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.Service{}), V1, kubewatchEventsMetrics)
293+
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.Service{}), V1, kubewatchEventsMetrics, conf.IgnoredFields)
292294
stopCh := make(chan struct{})
293295
defer close(stopCh)
294296

@@ -310,7 +312,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
310312
cache.Indexers{},
311313
)
312314

313-
c := newResourceController(kubeClient, eventHandler, informer, objName(apps_v1.Deployment{}), APPS_V1, kubewatchEventsMetrics)
315+
c := newResourceController(kubeClient, eventHandler, informer, objName(apps_v1.Deployment{}), APPS_V1, kubewatchEventsMetrics, conf.IgnoredFields)
314316
stopCh := make(chan struct{})
315317
defer close(stopCh)
316318

@@ -332,7 +334,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
332334
cache.Indexers{},
333335
)
334336

335-
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.Namespace{}), V1, kubewatchEventsMetrics)
337+
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.Namespace{}), V1, kubewatchEventsMetrics, conf.IgnoredFields)
336338
stopCh := make(chan struct{})
337339
defer close(stopCh)
338340

@@ -354,7 +356,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
354356
cache.Indexers{},
355357
)
356358

357-
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.ReplicationController{}), V1, kubewatchEventsMetrics)
359+
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.ReplicationController{}), V1, kubewatchEventsMetrics, conf.IgnoredFields)
358360
stopCh := make(chan struct{})
359361
defer close(stopCh)
360362

@@ -376,7 +378,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
376378
cache.Indexers{},
377379
)
378380

379-
c := newResourceController(kubeClient, eventHandler, informer, objName(batch_v1.Job{}), BATCH_V1, kubewatchEventsMetrics)
381+
c := newResourceController(kubeClient, eventHandler, informer, objName(batch_v1.Job{}), BATCH_V1, kubewatchEventsMetrics, conf.IgnoredFields)
380382
stopCh := make(chan struct{})
381383
defer close(stopCh)
382384

@@ -398,7 +400,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
398400
cache.Indexers{},
399401
)
400402

401-
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.Node{}), V1, kubewatchEventsMetrics)
403+
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.Node{}), V1, kubewatchEventsMetrics, conf.IgnoredFields)
402404
stopCh := make(chan struct{})
403405
defer close(stopCh)
404406

@@ -420,7 +422,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
420422
cache.Indexers{},
421423
)
422424

423-
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.ServiceAccount{}), V1, kubewatchEventsMetrics)
425+
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.ServiceAccount{}), V1, kubewatchEventsMetrics, conf.IgnoredFields)
424426
stopCh := make(chan struct{})
425427
defer close(stopCh)
426428

@@ -442,7 +444,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
442444
cache.Indexers{},
443445
)
444446

445-
c := newResourceController(kubeClient, eventHandler, informer, objName(rbac_v1.ClusterRole{}), RBAC_V1, kubewatchEventsMetrics)
447+
c := newResourceController(kubeClient, eventHandler, informer, objName(rbac_v1.ClusterRole{}), RBAC_V1, kubewatchEventsMetrics, conf.IgnoredFields)
446448
stopCh := make(chan struct{})
447449
defer close(stopCh)
448450

@@ -464,7 +466,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
464466
cache.Indexers{},
465467
)
466468

467-
c := newResourceController(kubeClient, eventHandler, informer, objName(rbac_v1.ClusterRoleBinding{}), RBAC_V1, kubewatchEventsMetrics)
469+
c := newResourceController(kubeClient, eventHandler, informer, objName(rbac_v1.ClusterRoleBinding{}), RBAC_V1, kubewatchEventsMetrics, conf.IgnoredFields)
468470
stopCh := make(chan struct{})
469471
defer close(stopCh)
470472

@@ -486,7 +488,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
486488
cache.Indexers{},
487489
)
488490

489-
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.PersistentVolume{}), V1, kubewatchEventsMetrics)
491+
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.PersistentVolume{}), V1, kubewatchEventsMetrics, conf.IgnoredFields)
490492
stopCh := make(chan struct{})
491493
defer close(stopCh)
492494

@@ -508,7 +510,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
508510
cache.Indexers{},
509511
)
510512

511-
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.Secret{}), V1, kubewatchEventsMetrics)
513+
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.Secret{}), V1, kubewatchEventsMetrics, conf.IgnoredFields)
512514
stopCh := make(chan struct{})
513515
defer close(stopCh)
514516

@@ -530,7 +532,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
530532
cache.Indexers{},
531533
)
532534

533-
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.ConfigMap{}), V1, kubewatchEventsMetrics)
535+
c := newResourceController(kubeClient, eventHandler, informer, objName(api_v1.ConfigMap{}), V1, kubewatchEventsMetrics, conf.IgnoredFields)
534536
stopCh := make(chan struct{})
535537
defer close(stopCh)
536538

@@ -552,7 +554,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
552554
cache.Indexers{},
553555
)
554556

555-
c := newResourceController(kubeClient, eventHandler, informer, objName(networking_v1.Ingress{}), NETWORKING_V1, kubewatchEventsMetrics)
557+
c := newResourceController(kubeClient, eventHandler, informer, objName(networking_v1.Ingress{}), NETWORKING_V1, kubewatchEventsMetrics, conf.IgnoredFields)
556558
stopCh := make(chan struct{})
557559
defer close(stopCh)
558560

@@ -583,7 +585,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
583585
cache.Indexers{},
584586
)
585587

586-
c := newResourceController(kubeClient, eventHandler, informer, crd.Resource, fmt.Sprintf("%s/%s", crd.Group, crd.Version), kubewatchEventsMetrics)
588+
c := newResourceController(kubeClient, eventHandler, informer, crd.Resource, fmt.Sprintf("%s/%s", crd.Group, crd.Version), kubewatchEventsMetrics, conf.IgnoredFields)
587589
stopCh := make(chan struct{})
588590
defer close(stopCh)
589591

@@ -596,7 +598,7 @@ func Start(conf *config.Config, eventHandler handlers.Handler) {
596598
<-sigterm
597599
}
598600

599-
func newResourceController(client kubernetes.Interface, eventHandler handlers.Handler, informer cache.SharedIndexInformer, resourceType string, apiVersion string, kubewatchEventsMetrics *prometheus.CounterVec) *Controller {
601+
func newResourceController(client kubernetes.Interface, eventHandler handlers.Handler, informer cache.SharedIndexInformer, resourceType string, apiVersion string, kubewatchEventsMetrics *prometheus.CounterVec, ignoredFields map[string]interface{}) *Controller {
600602
queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
601603
var newEvent Event
602604
var err error
@@ -634,6 +636,15 @@ func newResourceController(client kubernetes.Interface, eventHandler handlers.Ha
634636
if !ok {
635637
logrus.WithField("pkg", "kubewatch-"+resourceType).Errorf("cannot convert old to runtime.Object for update on %v", old)
636638
}
639+
if len(ignoredFields) > 0 {
640+
diff, errDiff := diffObjects(old, new, ignoredFields)
641+
if errDiff != nil {
642+
logrus.WithField("pkg", "kubewatch-"+resourceType).Errorf("cannot diff old & new objects %v and %v: %v", old, new, errDiff)
643+
} else if len(diff) == 0 {
644+
logrus.WithField("pkg", "kubewatch-"+resourceType).Infof("Ignoring update to %v: %s", resourceType, newEvent.key)
645+
return
646+
}
647+
}
637648
logrus.WithField("pkg", "kubewatch-"+resourceType).Infof("Processing update to %v: %s", resourceType, newEvent.key)
638649
if err == nil {
639650
queue.Add(newEvent)
@@ -670,6 +681,38 @@ func newResourceController(client kubernetes.Interface, eventHandler handlers.Ha
670681
}
671682
}
672683

684+
func diffObjects(old, new interface{}, ignoredFields map[string]interface{}) (string, error) {
685+
oldContent, err := runtime.DefaultUnstructuredConverter.ToUnstructured(old)
686+
if err != nil {
687+
return "", err
688+
}
689+
newContent, err := runtime.DefaultUnstructuredConverter.ToUnstructured(new)
690+
if err != nil {
691+
return "", err
692+
}
693+
recursiveDelete(oldContent, ignoredFields)
694+
recursiveDelete(newContent, ignoredFields)
695+
return cmp.Diff(oldContent, newContent), nil
696+
}
697+
698+
// recursiveDelete recursively removes key from object
699+
// value of key should be either nil or nested map[string]interface{}
700+
// value of object to delete from should be nested map[string]interface{}
701+
func recursiveDelete(object map[string]interface{}, key map[string]interface{}) {
702+
for k, v := range key {
703+
if v == nil {
704+
delete(object, k)
705+
continue
706+
}
707+
if recursiveKey, ok := v.(map[string]interface{}); ok {
708+
if recursiveObj, ok := object[k].(map[string]interface{}); ok {
709+
recursiveDelete(recursiveObj, recursiveKey)
710+
}
711+
}
712+
}
713+
return
714+
}
715+
673716
// Run starts the kubewatch controller
674717
func (c *Controller) Run(stopCh <-chan struct{}) {
675718
defer utilruntime.HandleCrash()

0 commit comments

Comments
 (0)