Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
d70edd3d
Commit
d70edd3d
authored
Mar 19, 2019
by
fansong.cfs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add watch bookmark
parent
701e36bd
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
383 additions
and
15 deletions
+383
-15
decorated_watcher.go
...server/pkg/registry/generic/registry/decorated_watcher.go
+1
-1
store.go
...c/k8s.io/apiserver/pkg/registry/generic/registry/store.go
+1
-0
BUILD
staging/src/k8s.io/apiserver/pkg/storage/cacher/BUILD
+3
-0
cacher.go
staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher.go
+0
-0
cacher_whitebox_test.go
...s.io/apiserver/pkg/storage/cacher/cacher_whitebox_test.go
+218
-5
watch_cache.go
...ng/src/k8s.io/apiserver/pkg/storage/cacher/watch_cache.go
+2
-0
selection_predicate.go
...g/src/k8s.io/apiserver/pkg/storage/selection_predicate.go
+7
-6
BUILD
staging/src/k8s.io/apiserver/pkg/storage/tests/BUILD
+3
-0
cacher_test.go
...ing/src/k8s.io/apiserver/pkg/storage/tests/cacher_test.go
+134
-0
decoder.go
staging/src/k8s.io/client-go/rest/watch/decoder.go
+1
-1
decoder_test.go
staging/src/k8s.io/client-go/rest/watch/decoder_test.go
+1
-1
encoder_test.go
staging/src/k8s.io/client-go/rest/watch/encoder_test.go
+4
-0
reflector.go
staging/src/k8s.io/client-go/tools/cache/reflector.go
+7
-0
retrywatcher.go
staging/src/k8s.io/client-go/tools/watch/retrywatcher.go
+1
-1
No files found.
staging/src/k8s.io/apiserver/pkg/registry/generic/registry/decorated_watcher.go
View file @
d70edd3d
...
@@ -55,7 +55,7 @@ func (d *decoratedWatcher) run(ctx context.Context) {
...
@@ -55,7 +55,7 @@ func (d *decoratedWatcher) run(ctx context.Context) {
return
return
}
}
switch
recv
.
Type
{
switch
recv
.
Type
{
case
watch
.
Added
,
watch
.
Modified
,
watch
.
Deleted
:
case
watch
.
Added
,
watch
.
Modified
,
watch
.
Deleted
,
watch
.
Bookmark
:
err
:=
d
.
decorator
(
recv
.
Object
)
err
:=
d
.
decorator
(
recv
.
Object
)
if
err
!=
nil
{
if
err
!=
nil
{
send
=
makeStatusErrorEvent
(
err
)
send
=
makeStatusErrorEvent
(
err
)
...
...
staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go
View file @
d70edd3d
...
@@ -1075,6 +1075,7 @@ func (e *Store) Watch(ctx context.Context, options *metainternalversion.ListOpti
...
@@ -1075,6 +1075,7 @@ func (e *Store) Watch(ctx context.Context, options *metainternalversion.ListOpti
resourceVersion
:=
""
resourceVersion
:=
""
if
options
!=
nil
{
if
options
!=
nil
{
resourceVersion
=
options
.
ResourceVersion
resourceVersion
=
options
.
ResourceVersion
predicate
.
AllowWatchBookmarks
=
options
.
AllowWatchBookmarks
}
}
return
e
.
WatchPredicate
(
ctx
,
predicate
,
resourceVersion
)
return
e
.
WatchPredicate
(
ctx
,
predicate
,
resourceVersion
)
}
}
...
...
staging/src/k8s.io/apiserver/pkg/storage/cacher/BUILD
View file @
d70edd3d
...
@@ -59,8 +59,11 @@ go_test(
...
@@ -59,8 +59,11 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/apis/example:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/apis/example:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/apis/example/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/apis/example/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/etcd:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/etcd:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
],
],
)
)
...
...
staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher.go
View file @
d70edd3d
This diff is collapsed.
Click to expand it.
staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_whitebox_test.go
View file @
d70edd3d
...
@@ -32,13 +32,17 @@ import (
...
@@ -32,13 +32,17 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/diff"
utilruntime
"k8s.io/apimachinery/pkg/util/runtime"
utilruntime
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apiserver/pkg/apis/example"
"k8s.io/apiserver/pkg/apis/example"
examplev1
"k8s.io/apiserver/pkg/apis/example/v1"
examplev1
"k8s.io/apiserver/pkg/apis/example/v1"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage"
utilfeature
"k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting
"k8s.io/apiserver/pkg/util/feature/testing"
)
)
// verifies the cacheWatcher.process goroutine is properly cleaned up even if
// verifies the cacheWatcher.process goroutine is properly cleaned up even if
...
@@ -63,7 +67,7 @@ func TestCacheWatcherCleanupNotBlockedByResult(t *testing.T) {
...
@@ -63,7 +67,7 @@ func TestCacheWatcherCleanupNotBlockedByResult(t *testing.T) {
}
}
// set the size of the buffer of w.result to 0, so that the writes to
// set the size of the buffer of w.result to 0, so that the writes to
// w.result is blocked.
// w.result is blocked.
w
=
newCacheWatcher
(
0
,
filter
,
forget
,
testVersioner
{})
w
=
newCacheWatcher
(
0
,
filter
,
forget
,
testVersioner
{}
,
time
.
Now
(),
false
)
go
w
.
process
(
context
.
Background
(),
initEvents
,
0
)
go
w
.
process
(
context
.
Background
(),
initEvents
,
0
)
w
.
Stop
()
w
.
Stop
()
if
err
:=
wait
.
PollImmediate
(
1
*
time
.
Second
,
5
*
time
.
Second
,
func
()
(
bool
,
error
)
{
if
err
:=
wait
.
PollImmediate
(
1
*
time
.
Second
,
5
*
time
.
Second
,
func
()
(
bool
,
error
)
{
...
@@ -183,8 +187,9 @@ TestCase:
...
@@ -183,8 +187,9 @@ TestCase:
testCase
.
events
[
j
]
.
ResourceVersion
=
uint64
(
j
)
+
1
testCase
.
events
[
j
]
.
ResourceVersion
=
uint64
(
j
)
+
1
}
}
w
:=
newCacheWatcher
(
0
,
filter
,
forget
,
testVersioner
{})
w
:=
newCacheWatcher
(
0
,
filter
,
forget
,
testVersioner
{}
,
time
.
Now
(),
false
)
go
w
.
process
(
context
.
Background
(),
testCase
.
events
,
0
)
go
w
.
process
(
context
.
Background
(),
testCase
.
events
,
0
)
ch
:=
w
.
ResultChan
()
ch
:=
w
.
ResultChan
()
for
j
,
event
:=
range
testCase
.
expected
{
for
j
,
event
:=
range
testCase
.
expected
{
e
:=
<-
ch
e
:=
<-
ch
...
@@ -461,7 +466,7 @@ func TestCacheWatcherStoppedInAnotherGoroutine(t *testing.T) {
...
@@ -461,7 +466,7 @@ func TestCacheWatcherStoppedInAnotherGoroutine(t *testing.T) {
// timeout to zero and run the Stop goroutine concurrently.
// timeout to zero and run the Stop goroutine concurrently.
// May sure that the watch will not be blocked on Stop.
// May sure that the watch will not be blocked on Stop.
for
i
:=
0
;
i
<
maxRetriesToProduceTheRaceCondition
;
i
++
{
for
i
:=
0
;
i
<
maxRetriesToProduceTheRaceCondition
;
i
++
{
w
=
newCacheWatcher
(
0
,
filter
,
forget
,
testVersioner
{})
w
=
newCacheWatcher
(
0
,
filter
,
forget
,
testVersioner
{}
,
time
.
Now
(),
false
)
go
w
.
Stop
()
go
w
.
Stop
()
select
{
select
{
case
<-
done
:
case
<-
done
:
...
@@ -470,11 +475,12 @@ func TestCacheWatcherStoppedInAnotherGoroutine(t *testing.T) {
...
@@ -470,11 +475,12 @@ func TestCacheWatcherStoppedInAnotherGoroutine(t *testing.T) {
}
}
}
}
deadline
:=
time
.
Now
()
.
Add
(
time
.
Hour
)
// After that, verifies the cacheWatcher.process goroutine works correctly.
// After that, verifies the cacheWatcher.process goroutine works correctly.
for
i
:=
0
;
i
<
maxRetriesToProduceTheRaceCondition
;
i
++
{
for
i
:=
0
;
i
<
maxRetriesToProduceTheRaceCondition
;
i
++
{
w
=
newCacheWatcher
(
2
,
filter
,
emptyFunc
,
testVersioner
{})
w
=
newCacheWatcher
(
2
,
filter
,
emptyFunc
,
testVersioner
{}
,
deadline
,
false
)
w
.
input
<-
&
watchCacheEvent
{
Object
:
&
v1
.
Pod
{},
ResourceVersion
:
uint64
(
i
+
1
)}
w
.
input
<-
&
watchCacheEvent
{
Object
:
&
v1
.
Pod
{},
ResourceVersion
:
uint64
(
i
+
1
)}
ctx
,
_
:=
context
.
With
Timeout
(
context
.
Background
(),
time
.
Hour
)
ctx
,
_
:=
context
.
With
Deadline
(
context
.
Background
(),
deadline
)
go
w
.
process
(
ctx
,
nil
,
0
)
go
w
.
process
(
ctx
,
nil
,
0
)
select
{
select
{
case
<-
w
.
ResultChan
()
:
case
<-
w
.
ResultChan
()
:
...
@@ -484,3 +490,210 @@ func TestCacheWatcherStoppedInAnotherGoroutine(t *testing.T) {
...
@@ -484,3 +490,210 @@ func TestCacheWatcherStoppedInAnotherGoroutine(t *testing.T) {
w
.
Stop
()
w
.
Stop
()
}
}
}
}
func
TestTimeBucketWatchersBasic
(
t
*
testing
.
T
)
{
filter
:=
func
(
_
string
,
_
labels
.
Set
,
_
fields
.
Set
)
bool
{
return
true
}
forget
:=
func
()
{}
newWatcher
:=
func
(
deadline
time
.
Time
)
*
cacheWatcher
{
return
newCacheWatcher
(
0
,
filter
,
forget
,
testVersioner
{},
deadline
,
true
)
}
clock
:=
clock
.
NewFakeClock
(
time
.
Now
())
watchers
:=
newTimeBucketWatchers
(
clock
)
now
:=
clock
.
Now
()
watchers
.
addWatcher
(
newWatcher
(
now
.
Add
(
10
*
time
.
Second
)))
watchers
.
addWatcher
(
newWatcher
(
now
.
Add
(
20
*
time
.
Second
)))
watchers
.
addWatcher
(
newWatcher
(
now
.
Add
(
20
*
time
.
Second
)))
if
len
(
watchers
.
watchersBuckets
)
!=
2
{
t
.
Errorf
(
"unexpected bucket size: %#v"
,
watchers
.
watchersBuckets
)
}
watchers0
:=
watchers
.
popExpiredWatchers
()
if
len
(
watchers0
)
!=
0
{
t
.
Errorf
(
"unexpected bucket size: %#v"
,
watchers0
)
}
clock
.
Step
(
10
*
time
.
Second
)
watchers1
:=
watchers
.
popExpiredWatchers
()
if
len
(
watchers1
)
!=
1
||
len
(
watchers1
[
0
])
!=
1
{
t
.
Errorf
(
"unexpected bucket size: %v"
,
watchers1
)
}
watchers1
=
watchers
.
popExpiredWatchers
()
if
len
(
watchers1
)
!=
0
{
t
.
Errorf
(
"unexpected bucket size: %#v"
,
watchers1
)
}
clock
.
Step
(
12
*
time
.
Second
)
watchers2
:=
watchers
.
popExpiredWatchers
()
if
len
(
watchers2
)
!=
1
||
len
(
watchers2
[
0
])
!=
2
{
t
.
Errorf
(
"unexpected bucket size: %#v"
,
watchers2
)
}
}
func
testCacherSendBookmarkEvents
(
t
*
testing
.
T
,
watchCacheEnabled
,
allowWatchBookmarks
,
expectedBookmarks
bool
)
{
defer
utilfeaturetesting
.
SetFeatureGateDuringTest
(
t
,
utilfeature
.
DefaultFeatureGate
,
features
.
WatchBookmark
,
watchCacheEnabled
)()
backingStorage
:=
&
dummyStorage
{}
cacher
,
_
:=
newTestCacher
(
backingStorage
,
1000
)
defer
cacher
.
Stop
()
// Wait until cacher is initialized.
cacher
.
ready
.
wait
()
pred
:=
storage
.
Everything
pred
.
AllowWatchBookmarks
=
allowWatchBookmarks
ctx
,
_
:=
context
.
WithTimeout
(
context
.
Background
(),
3
*
time
.
Second
)
w
,
err
:=
cacher
.
Watch
(
ctx
,
"pods/ns"
,
"0"
,
pred
)
if
err
!=
nil
{
t
.
Fatalf
(
"Failed to create watch: %v"
,
err
)
}
resourceVersion
:=
uint64
(
1000
)
go
func
()
{
deadline
:=
time
.
Now
()
.
Add
(
time
.
Second
)
for
i
:=
0
;
time
.
Now
()
.
Before
(
deadline
);
i
++
{
err
=
cacher
.
watchCache
.
Add
(
&
examplev1
.
Pod
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
fmt
.
Sprintf
(
"pod-%d"
,
i
),
Namespace
:
"ns"
,
ResourceVersion
:
fmt
.
Sprintf
(
"%v"
,
resourceVersion
+
uint64
(
i
)),
}})
if
err
!=
nil
{
t
.
Fatalf
(
"failed to add a pod: %v"
,
err
)
}
time
.
Sleep
(
100
*
time
.
Millisecond
)
}
}()
timeoutCh
:=
time
.
After
(
2
*
time
.
Second
)
lastObservedRV
:=
uint64
(
0
)
for
{
select
{
case
event
,
ok
:=
<-
w
.
ResultChan
()
:
if
!
ok
{
t
.
Fatal
(
"Unexpected closed"
)
}
rv
,
err
:=
cacher
.
versioner
.
ObjectResourceVersion
(
event
.
Object
)
if
err
!=
nil
{
t
.
Errorf
(
"failed to parse resource version from %#v"
,
event
.
Object
)
}
if
event
.
Type
==
watch
.
Bookmark
{
if
!
expectedBookmarks
{
t
.
Fatalf
(
"Unexpected bookmark events received"
)
}
if
rv
<
lastObservedRV
{
t
.
Errorf
(
"Unexpected bookmark event resource version %v (last %v)"
,
rv
,
lastObservedRV
)
}
return
}
lastObservedRV
=
rv
case
<-
timeoutCh
:
if
expectedBookmarks
{
t
.
Fatal
(
"Unexpected timeout to receive a bookmark event"
)
}
return
}
}
}
func
TestCacherSendBookmarkEvents
(
t
*
testing
.
T
)
{
testCases
:=
[]
struct
{
watchCacheEnabled
bool
allowWatchBookmarks
bool
expectedBookmarks
bool
}{
{
watchCacheEnabled
:
true
,
allowWatchBookmarks
:
true
,
expectedBookmarks
:
true
,
},
{
watchCacheEnabled
:
true
,
allowWatchBookmarks
:
false
,
expectedBookmarks
:
false
,
},
{
watchCacheEnabled
:
false
,
allowWatchBookmarks
:
true
,
expectedBookmarks
:
false
,
},
{
watchCacheEnabled
:
false
,
allowWatchBookmarks
:
false
,
expectedBookmarks
:
false
,
},
}
for
_
,
tc
:=
range
testCases
{
testCacherSendBookmarkEvents
(
t
,
tc
.
watchCacheEnabled
,
tc
.
allowWatchBookmarks
,
tc
.
expectedBookmarks
)
}
}
func
TestDispatchingBookmarkEventsWithConcurrentStop
(
t
*
testing
.
T
)
{
defer
utilfeaturetesting
.
SetFeatureGateDuringTest
(
t
,
utilfeature
.
DefaultFeatureGate
,
features
.
WatchBookmark
,
true
)()
backingStorage
:=
&
dummyStorage
{}
cacher
,
_
:=
newTestCacher
(
backingStorage
,
1000
)
defer
cacher
.
Stop
()
// Wait until cacher is initialized.
cacher
.
ready
.
wait
()
// Ensure there is some budget for slowing down processing.
cacher
.
dispatchTimeoutBudget
.
returnUnused
(
100
*
time
.
Millisecond
)
resourceVersion
:=
uint64
(
1000
)
err
:=
cacher
.
watchCache
.
Add
(
&
examplev1
.
Pod
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
fmt
.
Sprintf
(
"pod-0"
),
Namespace
:
"ns"
,
ResourceVersion
:
fmt
.
Sprintf
(
"%v"
,
resourceVersion
),
}})
if
err
!=
nil
{
t
.
Fatalf
(
"failed to add a pod: %v"
,
err
)
}
for
i
:=
0
;
i
<
1000
;
i
++
{
pred
:=
storage
.
Everything
pred
.
AllowWatchBookmarks
=
true
ctx
,
_
:=
context
.
WithTimeout
(
context
.
Background
(),
time
.
Second
)
w
,
err
:=
cacher
.
Watch
(
ctx
,
"pods/ns"
,
"999"
,
pred
)
if
err
!=
nil
{
t
.
Fatalf
(
"Failed to create watch: %v"
,
err
)
}
bookmark
:=
&
watchCacheEvent
{
Type
:
watch
.
Bookmark
,
ResourceVersion
:
uint64
(
i
),
Object
:
cacher
.
newFunc
(),
}
err
=
cacher
.
versioner
.
UpdateObject
(
bookmark
.
Object
,
bookmark
.
ResourceVersion
)
if
err
!=
nil
{
t
.
Fatalf
(
"failure to update version of object (%d) %#v"
,
bookmark
.
ResourceVersion
,
bookmark
.
Object
)
}
go
func
()
{
cacher
.
dispatchEvent
(
bookmark
)
}()
go
func
()
{
w
.
Stop
()
}()
done
:=
make
(
chan
struct
{})
go
func
()
{
for
range
w
.
ResultChan
()
{
}
close
(
done
)
}()
select
{
case
<-
done
:
break
case
<-
time
.
After
(
time
.
Second
)
:
t
.
Fatal
(
"receive result timeout"
)
}
w
.
Stop
()
}
}
staging/src/k8s.io/apiserver/pkg/storage/cacher/watch_cache.go
View file @
d70edd3d
...
@@ -30,6 +30,7 @@ import (
...
@@ -30,6 +30,7 @@ import (
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/cache"
"k8s.io/klog"
utiltrace
"k8s.io/utils/trace"
utiltrace
"k8s.io/utils/trace"
)
)
...
@@ -386,6 +387,7 @@ func (w *watchCache) Replace(objs []interface{}, resourceVersion string) error {
...
@@ -386,6 +387,7 @@ func (w *watchCache) Replace(objs []interface{}, resourceVersion string) error {
w
.
onReplace
()
w
.
onReplace
()
}
}
w
.
cond
.
Broadcast
()
w
.
cond
.
Broadcast
()
klog
.
V
(
3
)
.
Infof
(
"Replace watchCache (rev: %v) "
,
resourceVersion
)
return
nil
return
nil
}
}
...
...
staging/src/k8s.io/apiserver/pkg/storage/selection_predicate.go
View file @
d70edd3d
...
@@ -71,12 +71,13 @@ func (f AttrFunc) WithFieldMutation(fieldMutator FieldMutationFunc) AttrFunc {
...
@@ -71,12 +71,13 @@ func (f AttrFunc) WithFieldMutation(fieldMutator FieldMutationFunc) AttrFunc {
// SelectionPredicate is used to represent the way to select objects from api storage.
// SelectionPredicate is used to represent the way to select objects from api storage.
type
SelectionPredicate
struct
{
type
SelectionPredicate
struct
{
Label
labels
.
Selector
Label
labels
.
Selector
Field
fields
.
Selector
Field
fields
.
Selector
GetAttrs
AttrFunc
GetAttrs
AttrFunc
IndexFields
[]
string
IndexFields
[]
string
Limit
int64
Limit
int64
Continue
string
Continue
string
AllowWatchBookmarks
bool
}
}
// Matches returns true if the given object's labels and fields (as
// Matches returns true if the given object's labels and fields (as
...
...
staging/src/k8s.io/apiserver/pkg/storage/tests/BUILD
View file @
d70edd3d
...
@@ -26,6 +26,7 @@ go_test(
...
@@ -26,6 +26,7 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/apis/example:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/apis/example:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/apis/example/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/apis/example/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/cacher:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/cacher:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/etcd:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/etcd:go_default_library",
...
@@ -33,6 +34,8 @@ go_test(
...
@@ -33,6 +34,8 @@ go_test(
"//staging/src/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/etcd3:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/etcd3:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/value:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/value:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
],
],
)
)
...
...
staging/src/k8s.io/apiserver/pkg/storage/tests/cacher_test.go
View file @
d70edd3d
...
@@ -40,6 +40,7 @@ import (
...
@@ -40,6 +40,7 @@ import (
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apiserver/pkg/apis/example"
"k8s.io/apiserver/pkg/apis/example"
examplev1
"k8s.io/apiserver/pkg/apis/example/v1"
examplev1
"k8s.io/apiserver/pkg/apis/example/v1"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage"
cacherstorage
"k8s.io/apiserver/pkg/storage/cacher"
cacherstorage
"k8s.io/apiserver/pkg/storage/cacher"
etcdstorage
"k8s.io/apiserver/pkg/storage/etcd"
etcdstorage
"k8s.io/apiserver/pkg/storage/etcd"
...
@@ -47,6 +48,8 @@ import (
...
@@ -47,6 +48,8 @@ import (
etcdtesting
"k8s.io/apiserver/pkg/storage/etcd/testing"
etcdtesting
"k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/apiserver/pkg/storage/etcd3"
"k8s.io/apiserver/pkg/storage/etcd3"
"k8s.io/apiserver/pkg/storage/value"
"k8s.io/apiserver/pkg/storage/value"
utilfeature
"k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting
"k8s.io/apiserver/pkg/util/feature/testing"
)
)
var
(
var
(
...
@@ -121,6 +124,12 @@ func makeTestPod(name string) *example.Pod {
...
@@ -121,6 +124,12 @@ func makeTestPod(name string) *example.Pod {
}
}
}
}
func
createPod
(
s
storage
.
Interface
,
obj
*
example
.
Pod
)
error
{
key
:=
"pods/"
+
obj
.
Namespace
+
"/"
+
obj
.
Name
out
:=
&
example
.
Pod
{}
return
s
.
Create
(
context
.
TODO
(),
key
,
obj
,
out
,
0
)
}
func
updatePod
(
t
*
testing
.
T
,
s
storage
.
Interface
,
obj
,
old
*
example
.
Pod
)
*
example
.
Pod
{
func
updatePod
(
t
*
testing
.
T
,
s
storage
.
Interface
,
obj
,
old
*
example
.
Pod
)
*
example
.
Pod
{
updateFn
:=
func
(
input
runtime
.
Object
,
res
storage
.
ResponseMeta
)
(
runtime
.
Object
,
*
uint64
,
error
)
{
updateFn
:=
func
(
input
runtime
.
Object
,
res
storage
.
ResponseMeta
)
(
runtime
.
Object
,
*
uint64
,
error
)
{
return
obj
.
DeepCopyObject
(),
nil
,
nil
return
obj
.
DeepCopyObject
(),
nil
,
nil
...
@@ -773,4 +782,129 @@ func TestCacherListerWatcherPagination(t *testing.T) {
...
@@ -773,4 +782,129 @@ func TestCacherListerWatcherPagination(t *testing.T) {
if
limit2
.
Items
[
0
]
.
Name
!=
podFoo
.
Name
{
if
limit2
.
Items
[
0
]
.
Name
!=
podFoo
.
Name
{
t
.
Errorf
(
"Expected list2.Items[0] to be %s but got %s"
,
podFoo
.
Name
,
limit2
.
Items
[
0
]
.
Name
)
t
.
Errorf
(
"Expected list2.Items[0] to be %s but got %s"
,
podFoo
.
Name
,
limit2
.
Items
[
0
]
.
Name
)
}
}
}
func
TestWatchDispatchBookmarkEvents
(
t
*
testing
.
T
)
{
defer
utilfeaturetesting
.
SetFeatureGateDuringTest
(
t
,
utilfeature
.
DefaultFeatureGate
,
features
.
WatchBookmark
,
true
)()
server
,
etcdStorage
:=
newEtcdTestStorage
(
t
,
etcdtest
.
PathPrefix
())
defer
server
.
Terminate
(
t
)
cacher
,
v
:=
newTestCacher
(
etcdStorage
,
10
)
defer
cacher
.
Stop
()
fooCreated
:=
updatePod
(
t
,
etcdStorage
,
makeTestPod
(
"foo"
),
nil
)
rv
,
err
:=
v
.
ParseResourceVersion
(
fooCreated
.
ResourceVersion
)
if
err
!=
nil
{
t
.
Fatalf
(
"Unexpected error: %v"
,
err
)
}
startVersion
:=
strconv
.
Itoa
(
int
(
rv
))
tests
:=
[]
struct
{
timeout
time
.
Duration
expected
bool
allowWatchBookmark
bool
}{
{
// test old client won't get Bookmark event
timeout
:
2
*
time
.
Second
,
expected
:
false
,
allowWatchBookmark
:
false
,
},
{
timeout
:
2
*
time
.
Second
,
expected
:
true
,
allowWatchBookmark
:
true
,
},
}
for
i
,
c
:=
range
tests
{
pred
:=
storage
.
Everything
pred
.
AllowWatchBookmarks
=
c
.
allowWatchBookmark
ctx
,
_
:=
context
.
WithTimeout
(
context
.
Background
(),
c
.
timeout
)
watcher
,
err
:=
cacher
.
Watch
(
ctx
,
"pods/ns/foo"
,
startVersion
,
pred
)
if
err
!=
nil
{
t
.
Fatalf
(
"Unexpected error: %v"
,
err
)
}
// Create events of other pods
updatePod
(
t
,
etcdStorage
,
makeTestPod
(
fmt
.
Sprintf
(
"foo-whatever-%d"
,
i
)),
nil
)
// Now wait for Bookmark event
select
{
case
event
,
ok
:=
<-
watcher
.
ResultChan
()
:
if
!
ok
&&
c
.
expected
{
t
.
Errorf
(
"Unexpected object watched (no objects)"
)
}
if
c
.
expected
&&
event
.
Type
!=
watch
.
Bookmark
{
t
.
Errorf
(
"Unexpected object watched %#v"
,
event
)
}
case
<-
time
.
After
(
time
.
Second
*
3
)
:
if
c
.
expected
{
t
.
Errorf
(
"Unexpected object watched (timeout)"
)
}
}
watcher
.
Stop
()
}
}
func
TestWatchBookmarksWithCorrectResourceVersion
(
t
*
testing
.
T
)
{
defer
utilfeaturetesting
.
SetFeatureGateDuringTest
(
t
,
utilfeature
.
DefaultFeatureGate
,
features
.
WatchBookmark
,
true
)()
server
,
etcdStorage
:=
newEtcdTestStorage
(
t
,
etcdtest
.
PathPrefix
())
defer
server
.
Terminate
(
t
)
cacher
,
v
:=
newTestCacher
(
etcdStorage
,
10
)
defer
cacher
.
Stop
()
pred
:=
storage
.
Everything
pred
.
AllowWatchBookmarks
=
true
ctx
,
_
:=
context
.
WithTimeout
(
context
.
Background
(),
2
*
time
.
Second
)
watcher
,
err
:=
cacher
.
WatchList
(
ctx
,
"pods/ns"
,
"0"
,
pred
)
if
err
!=
nil
{
t
.
Fatalf
(
"Unexpected error: %v"
,
err
)
}
defer
watcher
.
Stop
()
done
:=
make
(
chan
struct
{})
defer
close
(
done
)
go
func
()
{
for
i
:=
0
;
i
<
100
;
i
++
{
select
{
case
<-
done
:
return
default
:
pod
:=
fmt
.
Sprintf
(
"foo-%d"
,
i
)
err
:=
createPod
(
etcdStorage
,
makeTestPod
(
pod
))
if
err
!=
nil
{
t
.
Fatalf
(
"failed to create pod %v"
,
pod
)
}
time
.
Sleep
(
time
.
Second
/
100
)
}
}
}()
bookmarkReceived
:=
false
lastObservedResourceVersion
:=
uint64
(
0
)
for
event
:=
range
watcher
.
ResultChan
()
{
rv
,
err
:=
v
.
ObjectResourceVersion
(
event
.
Object
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to parse resourceVersion from %#v"
,
event
)
}
if
event
.
Type
==
watch
.
Bookmark
{
bookmarkReceived
=
true
// bookmark event has a RV greater than or equal to the before one
if
rv
<
lastObservedResourceVersion
{
t
.
Fatalf
(
"Unexpected bookmark resourceVersion %v less than observed %v)"
,
rv
,
lastObservedResourceVersion
)
}
}
else
{
// non-bookmark event has a RV greater than anything before
if
rv
<=
lastObservedResourceVersion
{
t
.
Fatalf
(
"Unexpected event resourceVersion %v less than or equal to bookmark %v)"
,
rv
,
lastObservedResourceVersion
)
}
}
lastObservedResourceVersion
=
rv
}
// Make sure we have received a bookmark event
if
!
bookmarkReceived
{
t
.
Fatalf
(
"Unpexected error, we did not received a bookmark event"
)
}
}
}
staging/src/k8s.io/client-go/rest/watch/decoder.go
View file @
d70edd3d
...
@@ -54,7 +54,7 @@ func (d *Decoder) Decode() (watch.EventType, runtime.Object, error) {
...
@@ -54,7 +54,7 @@ func (d *Decoder) Decode() (watch.EventType, runtime.Object, error) {
return
""
,
nil
,
fmt
.
Errorf
(
"unable to decode to metav1.Event"
)
return
""
,
nil
,
fmt
.
Errorf
(
"unable to decode to metav1.Event"
)
}
}
switch
got
.
Type
{
switch
got
.
Type
{
case
string
(
watch
.
Added
),
string
(
watch
.
Modified
),
string
(
watch
.
Deleted
),
string
(
watch
.
Error
)
:
case
string
(
watch
.
Added
),
string
(
watch
.
Modified
),
string
(
watch
.
Deleted
),
string
(
watch
.
Error
)
,
string
(
watch
.
Bookmark
)
:
default
:
default
:
return
""
,
nil
,
fmt
.
Errorf
(
"got invalid watch event type: %v"
,
got
.
Type
)
return
""
,
nil
,
fmt
.
Errorf
(
"got invalid watch event type: %v"
,
got
.
Type
)
}
}
...
...
staging/src/k8s.io/client-go/rest/watch/decoder_test.go
View file @
d70edd3d
...
@@ -42,7 +42,7 @@ func getDecoder() runtime.Decoder {
...
@@ -42,7 +42,7 @@ func getDecoder() runtime.Decoder {
}
}
func
TestDecoder
(
t
*
testing
.
T
)
{
func
TestDecoder
(
t
*
testing
.
T
)
{
table
:=
[]
watch
.
EventType
{
watch
.
Added
,
watch
.
Deleted
,
watch
.
Modified
,
watch
.
Error
}
table
:=
[]
watch
.
EventType
{
watch
.
Added
,
watch
.
Deleted
,
watch
.
Modified
,
watch
.
Error
,
watch
.
Bookmark
}
for
_
,
eventType
:=
range
table
{
for
_
,
eventType
:=
range
table
{
out
,
in
:=
io
.
Pipe
()
out
,
in
:=
io
.
Pipe
()
...
...
staging/src/k8s.io/client-go/rest/watch/encoder_test.go
View file @
d70edd3d
...
@@ -56,6 +56,10 @@ func TestEncodeDecodeRoundTrip(t *testing.T) {
...
@@ -56,6 +56,10 @@ func TestEncodeDecodeRoundTrip(t *testing.T) {
watch
.
Deleted
,
watch
.
Deleted
,
&
v1
.
Pod
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
"foo"
}},
&
v1
.
Pod
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
"foo"
}},
},
},
{
watch
.
Bookmark
,
&
v1
.
Pod
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
"foo"
}},
},
}
}
for
i
,
testCase
:=
range
testCases
{
for
i
,
testCase
:=
range
testCases
{
buf
:=
&
bytes
.
Buffer
{}
buf
:=
&
bytes
.
Buffer
{}
...
...
staging/src/k8s.io/client-go/tools/cache/reflector.go
View file @
d70edd3d
...
@@ -271,6 +271,11 @@ func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
...
@@ -271,6 +271,11 @@ func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
// We want to avoid situations of hanging watchers. Stop any wachers that do not
// We want to avoid situations of hanging watchers. Stop any wachers that do not
// receive any events within the timeout window.
// receive any events within the timeout window.
TimeoutSeconds
:
&
timeoutSeconds
,
TimeoutSeconds
:
&
timeoutSeconds
,
// To reduce load on kube-apiserver on watch restarts, you may enable watch bookmarks.
// Reflector doesn't assume bookmarks are returned at all (if the server do not support
// watch bookmarks, it will ignore this field).
// Disabled in Alpha release of watch bookmarks feature.
AllowWatchBookmarks
:
false
,
}
}
w
,
err
:=
r
.
listerWatcher
.
Watch
(
options
)
w
,
err
:=
r
.
listerWatcher
.
Watch
(
options
)
...
@@ -368,6 +373,8 @@ loop:
...
@@ -368,6 +373,8 @@ loop:
if
err
!=
nil
{
if
err
!=
nil
{
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"%s: unable to delete watch event object (%#v) from store: %v"
,
r
.
name
,
event
.
Object
,
err
))
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"%s: unable to delete watch event object (%#v) from store: %v"
,
r
.
name
,
event
.
Object
,
err
))
}
}
case
watch
.
Bookmark
:
// A `Bookmark` means watch has synced here, just update the resourceVersion
default
:
default
:
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"%s: unable to understand watch event %#v"
,
r
.
name
,
event
))
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"%s: unable to understand watch event %#v"
,
r
.
name
,
event
))
}
}
...
...
staging/src/k8s.io/client-go/tools/watch/retrywatcher.go
View file @
d70edd3d
...
@@ -153,7 +153,7 @@ func (rw *RetryWatcher) doReceive() (bool, time.Duration) {
...
@@ -153,7 +153,7 @@ func (rw *RetryWatcher) doReceive() (bool, time.Duration) {
// We need to inspect the event and get ResourceVersion out of it
// We need to inspect the event and get ResourceVersion out of it
switch
event
.
Type
{
switch
event
.
Type
{
case
watch
.
Added
,
watch
.
Modified
,
watch
.
Deleted
:
case
watch
.
Added
,
watch
.
Modified
,
watch
.
Deleted
,
watch
.
Bookmark
:
metaObject
,
ok
:=
event
.
Object
.
(
resourceVersionGetter
)
metaObject
,
ok
:=
event
.
Object
.
(
resourceVersionGetter
)
if
!
ok
{
if
!
ok
{
_
=
rw
.
send
(
watch
.
Event
{
_
=
rw
.
send
(
watch
.
Event
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment