@@ -23,8 +23,10 @@ import (
23
23
24
24
"k8s.io/apimachinery/pkg/api/meta"
25
25
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
26
27
"k8s.io/apimachinery/pkg/runtime"
27
28
"k8s.io/apimachinery/pkg/runtime/schema"
29
+ "k8s.io/apimachinery/pkg/types"
28
30
"k8s.io/apimachinery/pkg/watch"
29
31
"k8s.io/client-go/dynamic"
30
32
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -51,6 +53,10 @@ type dynamicWatch struct {
51
53
client dynamic.Interface
52
54
restMapper meta.RESTMapper
53
55
events chan event.GenericEvent
56
+
57
+ // lastRV caches the last reported resource version.
58
+ // This helps us avoid sending duplicate events (e.g. on a rewatch)
59
+ lastRV map [types.NamespacedName ]string
54
60
}
55
61
56
62
func (dw * dynamicWatch ) newDynamicClient (gvk schema.GroupVersionKind , namespace string ) (dynamic.ResourceInterface , error ) {
@@ -73,6 +79,8 @@ func (dw *dynamicWatch) Add(trigger schema.GroupVersionKind, options metav1.List
73
79
return fmt .Errorf ("creating client for (%s): %v" , trigger .String (), err )
74
80
}
75
81
82
+ dw .lastRV = make (map [types.NamespacedName ]string )
83
+
76
84
go func () {
77
85
for {
78
86
dw .watchUntilClosed (client , trigger , options , filterNamespace , target )
@@ -116,11 +124,33 @@ func (dw *dynamicWatch) watchUntilClosed(client dynamic.ResourceInterface, trigg
116
124
defer events .Stop ()
117
125
118
126
for clientEvent := range events .ResultChan () {
119
- if clientEvent .Type == watch .Bookmark {
120
- // not an invalidation, we ignore it
127
+ switch clientEvent .Type {
128
+ case watch .Bookmark :
129
+ // not an object change, we ignore it
121
130
continue
131
+ case watch .Error :
132
+ log .Error (fmt .Errorf ("unexpected error from watch: %v" , clientEvent .Object ), "error during watch" )
133
+ return
122
134
}
123
- log .WithValues ("type" , clientEvent .Type ).WithValues ("kind" , trigger .String ()).Info ("broadcasting event" )
135
+
136
+ u := clientEvent .Object .(* unstructured.Unstructured )
137
+ key := types.NamespacedName {Namespace : u .GetNamespace (), Name : u .GetName ()}
138
+ rv := u .GetResourceVersion ()
139
+
140
+ switch clientEvent .Type {
141
+ case watch .Deleted :
142
+ // stop lastRV growing indefinitely
143
+ delete (dw .lastRV , key )
144
+ // We always send the delete notification
145
+ case watch .Added , watch .Modified :
146
+ if previousRV , found := dw .lastRV [key ]; found && previousRV == rv {
147
+ // Don't send spurious invalidations
148
+ continue
149
+ }
150
+ dw .lastRV [key ] = rv
151
+ }
152
+
153
+ log .WithValues ("type" , clientEvent .Type ).WithValues ("kind" , trigger .String ()).WithValues ("name" , key .Name , "namespace" , key .Namespace ).Info ("broadcasting event" )
124
154
dw .events <- event.GenericEvent {Object : clientObject {Object : clientEvent .Object , ObjectMeta : & target }}
125
155
}
126
156
0 commit comments