@@ -13,6 +13,10 @@ import (
13
13
"k8s.io/client-go/dynamic"
14
14
)
15
15
16
+ type resourceVersionGetter interface {
17
+ GetResourceVersion () string
18
+ }
19
+
16
20
type WatchNotifyFunctions struct {
17
21
AddFunc func (obj * unstructured.Unstructured )
18
22
UpdateFunc func (obj * unstructured.Unstructured )
@@ -26,14 +30,15 @@ type WatcherInterface interface {
26
30
}
27
31
28
32
type Watcher struct {
29
- preList bool
30
- client dynamic.Interface
31
- watcher watch.Interface
32
- running bool
33
+ preList bool
34
+ client dynamic.Interface
35
+ watcher watch.Interface
36
+ lastResourceVersion string
37
+ running bool
33
38
}
34
39
35
40
func NewWatcher (k8sClient dynamic.Interface , preList bool ) WatcherInterface {
36
- return & Watcher {client : k8sClient , watcher : nil , running : false , preList : preList }
41
+ return & Watcher {client : k8sClient , watcher : nil , running : false , preList : preList , lastResourceVersion : "" }
37
42
}
38
43
39
44
func (w * Watcher ) Start (notifyF WatchNotifyFunctions , gvr schema.GroupVersionResource , listOptions metav1.ListOptions ) error {
@@ -55,7 +60,7 @@ func (w *Watcher) Start(notifyF WatchNotifyFunctions, gvr schema.GroupVersionRes
55
60
}
56
61
57
62
// List of current objects
58
- resourceVersion : = ""
63
+ w . lastResourceVersion = ""
59
64
60
65
if w .preList {
61
66
listOptions .Watch = false
@@ -65,9 +70,9 @@ func (w *Watcher) Start(notifyF WatchNotifyFunctions, gvr schema.GroupVersionRes
65
70
return err
66
71
}
67
72
for i , item := range list .Items {
68
- if isResourceVersionHigher (item .GetResourceVersion (), resourceVersion ) {
73
+ if isResourceVersionHigher (item .GetResourceVersion (), w . lastResourceVersion ) {
69
74
// Update the resourceVersion to the latest
70
- resourceVersion = item .GetResourceVersion ()
75
+ w . lastResourceVersion = item .GetResourceVersion ()
71
76
if w .preList {
72
77
notifyF .AddFunc (& item )
73
78
}
@@ -78,48 +83,45 @@ func (w *Watcher) Start(notifyF WatchNotifyFunctions, gvr schema.GroupVersionRes
78
83
list .Items = nil
79
84
list = nil
80
85
}
81
- } else {
82
- resourceVersion = "0"
83
86
}
84
87
85
88
// Start the watcher
86
- listOptions .ResourceVersion = resourceVersion
87
- watcher , err := w .client .Resource (gvr ).Namespace ("" ).Watch (context .Background (), listOptions )
89
+ listOptions .ResourceVersion = w .lastResourceVersion
90
+ listOptions .Watch = true
91
+ listOptions .AllowWatchBookmarks = true
92
+ w .watcher , err = w .client .Resource (gvr ).Namespace ("" ).Watch (context .TODO (), listOptions )
88
93
if err != nil {
89
94
return err
90
95
}
91
- w .watcher = watcher
92
96
w .running = true
93
- currentWatcherContext , cancelFunc := context .WithCancel (context .Background ())
94
-
95
- // Function to restart the watcher
96
- restartWatcher := func () {
97
- if currentWatcherContext != nil && cancelFunc != nil {
98
- cancelFunc ()
99
- }
100
- listOptions .ResourceVersion = resourceVersion
101
- currentWatcherContext , cancelFunc = context .WithCancel (context .Background ())
102
- w .watcher , err = w .client .Resource (gvr ).Namespace ("" ).Watch (currentWatcherContext , listOptions )
103
- if err != nil {
104
- log .Printf ("watcher restart error: %v, on object: %+v" , err , gvr )
105
- }
106
- watcher = w .watcher
107
- }
108
-
109
97
go func () {
110
98
// Watch for events
111
99
for {
112
- event , ok := <- watcher .ResultChan ()
100
+ event , ok := <- w . watcher .ResultChan ()
113
101
if ! ok {
114
102
if w .running {
115
- log .Printf ("Watcher channel closed on object %+v" , gvr )
116
- restartWatcher ()
103
+ log .Printf ("Restarting watcher on object %+v" , gvr )
104
+ w .watcher .Stop ()
105
+ w .watcher = nil
106
+ listOptions .ResourceVersion = w .lastResourceVersion
107
+ w .watcher , err = w .client .Resource (gvr ).Namespace ("" ).Watch (context .TODO (), listOptions )
108
+ if err != nil {
109
+ log .Printf ("watcher restart error: %v, on object: %+v" , err , gvr )
110
+ }
117
111
continue
118
112
} else {
119
113
// Stop the watcher
120
114
return
121
115
}
122
116
}
117
+
118
+ if metaObject , ok := event .Object .(resourceVersionGetter ); ok {
119
+ // Update the resourceVersion to the latest
120
+ if metaObject .GetResourceVersion () != "" && isResourceVersionHigher (metaObject .GetResourceVersion (), w .lastResourceVersion ) {
121
+ w .lastResourceVersion = metaObject .GetResourceVersion ()
122
+ }
123
+ }
124
+
123
125
switch event .Type {
124
126
case watch .Added :
125
127
// Convert the object to unstructured
@@ -128,8 +130,6 @@ func (w *Watcher) Start(notifyF WatchNotifyFunctions, gvr schema.GroupVersionRes
128
130
log .Printf ("watcher error: addedObject is nil" )
129
131
continue
130
132
}
131
- // Update the resourceVersion
132
- resourceVersion = addedObject .GetResourceVersion ()
133
133
notifyF .AddFunc (addedObject )
134
134
addedObject = nil // Make sure the item is scraped by the GC
135
135
case watch .Modified :
@@ -139,8 +139,6 @@ func (w *Watcher) Start(notifyF WatchNotifyFunctions, gvr schema.GroupVersionRes
139
139
log .Printf ("watcher error: modifiedObject is nil" )
140
140
continue
141
141
}
142
- // Update the resourceVersion
143
- resourceVersion = modifiedObject .GetResourceVersion ()
144
142
notifyF .UpdateFunc (modifiedObject )
145
143
modifiedObject = nil // Make sure the item is scraped by the GC
146
144
case watch .Deleted :
@@ -150,31 +148,19 @@ func (w *Watcher) Start(notifyF WatchNotifyFunctions, gvr schema.GroupVersionRes
150
148
log .Printf ("watcher error: deletedObject is nil" )
151
149
continue
152
150
}
153
- // Update the resourceVersion
154
- resourceVersion = deletedObject .GetResourceVersion ()
155
151
notifyF .DeleteFunc (deletedObject )
156
152
deletedObject = nil // Make sure the item is scraped by the GC
157
-
158
- case watch .Bookmark :
159
- // Update the resourceVersion
160
- bookmarkObject := event .Object .(* unstructured.Unstructured )
161
- if bookmarkObject == nil {
162
- log .Printf ("watcher error: bookmarkObject is nil" )
163
- continue
164
- }
165
- resourceVersion = bookmarkObject .GetResourceVersion ()
166
- bookmarkObject = nil // Make sure the item is scraped by the GC
167
-
168
153
case watch .Error :
169
154
// Convert the object to metav1.Status
170
155
watchError := event .Object .(* metav1.Status )
171
- // Check if the object reason is "Expired" or "Gone" and restart the watcher
172
- if watchError . Reason == "Expired" || watchError . Reason == "Gone" || watchError .Code == 410 {
173
- restartWatcher ()
174
- continue
175
- } else {
176
- log . Printf ( "watcher error: %v, on object %+v" , event . Object , gvr )
156
+ if watchError != nil {
157
+ if watchError .Code == 410 {
158
+ // If the resourceVersion is too old, reset the resourceVersion to the latest.
159
+ // https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions
160
+ w . lastResourceVersion = ""
161
+ }
177
162
}
163
+ continue
178
164
}
179
165
}
180
166
}()
0 commit comments