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
8e2cad79
Commit
8e2cad79
authored
Aug 18, 2015
by
Filip Grzadkowski
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #12721 from wojtek-t/fix_cacher_filtering
Store previous value in WatchCache for filtering
parents
19f52668
3a71eb1b
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
263 additions
and
22 deletions
+263
-22
watch_cache.go
pkg/client/cache/watch_cache.go
+37
-10
watch_cache_test.go
pkg/client/cache/watch_cache_test.go
+60
-0
cacher.go
pkg/storage/cacher.go
+24
-12
cacher_test.go
pkg/storage/cacher_test.go
+142
-0
No files found.
pkg/client/cache/watch_cache.go
View file @
8e2cad79
...
@@ -27,12 +27,27 @@ import (
...
@@ -27,12 +27,27 @@ import (
"k8s.io/kubernetes/pkg/watch"
"k8s.io/kubernetes/pkg/watch"
)
)
// TODO(wojtek-t): All structure in this file should be private to
// pkg/storage package. We should remove the reference to WatchCache
// from Reflector (by changing the Replace method signature in Store
// interface to take resource version too) and move it under pkg/storage.
// WatchCacheEvent is a single "watch event" that is send to users of
// WatchCache. Additionally to a typical "watch.Event" it contains
// the previous value of the object to enable proper filtering in the
// upper layers.
type
WatchCacheEvent
struct
{
Type
watch
.
EventType
Object
runtime
.
Object
PrevObject
runtime
.
Object
}
// watchCacheElement is a single "watch event" stored in a cache.
// watchCacheElement is a single "watch event" stored in a cache.
// It contains the resource version of the object and the object
// It contains the resource version of the object and the object
// itself.
// itself.
type
watchCacheElement
struct
{
type
watchCacheElement
struct
{
resourceVersion
uint64
resourceVersion
uint64
event
watch
.
Event
watchCacheEvent
WatchCache
Event
}
}
// WatchCache implements a Store interface.
// WatchCache implements a Store interface.
...
@@ -66,8 +81,9 @@ type WatchCache struct {
...
@@ -66,8 +81,9 @@ type WatchCache struct {
// This handler is run at the end of every successful Replace() method.
// This handler is run at the end of every successful Replace() method.
onReplace
func
()
onReplace
func
()
// This handler is run at the end of every Add/Update/Delete method.
// This handler is run at the end of every Add/Update/Delete method
onEvent
func
(
watch
.
Event
)
// and additionally gets the previous value of the object.
onEvent
func
(
WatchCacheEvent
)
}
}
func
NewWatchCache
(
capacity
int
)
*
WatchCache
{
func
NewWatchCache
(
capacity
int
)
*
WatchCache
{
...
@@ -140,16 +156,27 @@ func parseResourceVersion(resourceVersion string) (uint64, error) {
...
@@ -140,16 +156,27 @@ func parseResourceVersion(resourceVersion string) (uint64, error) {
func
(
w
*
WatchCache
)
processEvent
(
event
watch
.
Event
,
resourceVersion
uint64
,
updateFunc
func
(
runtime
.
Object
)
error
)
error
{
func
(
w
*
WatchCache
)
processEvent
(
event
watch
.
Event
,
resourceVersion
uint64
,
updateFunc
func
(
runtime
.
Object
)
error
)
error
{
w
.
Lock
()
w
.
Lock
()
defer
w
.
Unlock
()
defer
w
.
Unlock
()
previous
,
exists
,
err
:=
w
.
store
.
Get
(
event
.
Object
)
if
err
!=
nil
{
return
err
}
var
prevObject
runtime
.
Object
if
exists
{
prevObject
=
previous
.
(
runtime
.
Object
)
}
else
{
prevObject
=
nil
}
watchCacheEvent
:=
WatchCacheEvent
{
event
.
Type
,
event
.
Object
,
prevObject
}
if
w
.
onEvent
!=
nil
{
if
w
.
onEvent
!=
nil
{
w
.
onEvent
(
e
vent
)
w
.
onEvent
(
watchCacheE
vent
)
}
}
w
.
updateCache
(
resourceVersion
,
e
vent
)
w
.
updateCache
(
resourceVersion
,
watchCacheE
vent
)
w
.
resourceVersion
=
resourceVersion
w
.
resourceVersion
=
resourceVersion
return
updateFunc
(
event
.
Object
)
return
updateFunc
(
event
.
Object
)
}
}
// Assumes that lock is already held for write.
// Assumes that lock is already held for write.
func
(
w
*
WatchCache
)
updateCache
(
resourceVersion
uint64
,
event
watch
.
Event
)
{
func
(
w
*
WatchCache
)
updateCache
(
resourceVersion
uint64
,
event
WatchCache
Event
)
{
if
w
.
endIndex
==
w
.
startIndex
+
w
.
capacity
{
if
w
.
endIndex
==
w
.
startIndex
+
w
.
capacity
{
// Cache is full - remove the oldest element.
// Cache is full - remove the oldest element.
w
.
startIndex
++
w
.
startIndex
++
...
@@ -219,13 +246,13 @@ func (w *WatchCache) SetOnReplace(onReplace func()) {
...
@@ -219,13 +246,13 @@ func (w *WatchCache) SetOnReplace(onReplace func()) {
w
.
onReplace
=
onReplace
w
.
onReplace
=
onReplace
}
}
func
(
w
*
WatchCache
)
SetOnEvent
(
onEvent
func
(
watch
.
Event
))
{
func
(
w
*
WatchCache
)
SetOnEvent
(
onEvent
func
(
WatchCache
Event
))
{
w
.
Lock
()
w
.
Lock
()
defer
w
.
Unlock
()
defer
w
.
Unlock
()
w
.
onEvent
=
onEvent
w
.
onEvent
=
onEvent
}
}
func
(
w
*
WatchCache
)
GetAllEventsSince
(
resourceVersion
uint64
)
([]
watch
.
Event
,
error
)
{
func
(
w
*
WatchCache
)
GetAllEventsSince
(
resourceVersion
uint64
)
([]
WatchCache
Event
,
error
)
{
w
.
RLock
()
w
.
RLock
()
defer
w
.
RUnlock
()
defer
w
.
RUnlock
()
...
@@ -244,9 +271,9 @@ func (w *WatchCache) GetAllEventsSince(resourceVersion uint64) ([]watch.Event, e
...
@@ -244,9 +271,9 @@ func (w *WatchCache) GetAllEventsSince(resourceVersion uint64) ([]watch.Event, e
return
w
.
cache
[(
w
.
startIndex
+
i
)
%
w
.
capacity
]
.
resourceVersion
>=
resourceVersion
return
w
.
cache
[(
w
.
startIndex
+
i
)
%
w
.
capacity
]
.
resourceVersion
>=
resourceVersion
}
}
first
:=
sort
.
Search
(
size
,
f
)
first
:=
sort
.
Search
(
size
,
f
)
result
:=
make
([]
watch
.
Event
,
size
-
first
)
result
:=
make
([]
WatchCache
Event
,
size
-
first
)
for
i
:=
0
;
i
<
size
-
first
;
i
++
{
for
i
:=
0
;
i
<
size
-
first
;
i
++
{
result
[
i
]
=
w
.
cache
[(
w
.
startIndex
+
first
+
i
)
%
w
.
capacity
]
.
e
vent
result
[
i
]
=
w
.
cache
[(
w
.
startIndex
+
first
+
i
)
%
w
.
capacity
]
.
watchCacheE
vent
}
}
return
result
,
nil
return
result
,
nil
}
}
pkg/client/cache/watch_cache_test.go
View file @
8e2cad79
...
@@ -22,6 +22,7 @@ import (
...
@@ -22,6 +22,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/watch"
)
)
func
makeTestPod
(
name
string
,
resourceVersion
uint64
)
*
api
.
Pod
{
func
makeTestPod
(
name
string
,
resourceVersion
uint64
)
*
api
.
Pod
{
...
@@ -108,6 +109,34 @@ func TestEvents(t *testing.T) {
...
@@ -108,6 +109,34 @@ func TestEvents(t *testing.T) {
store
:=
NewWatchCache
(
5
)
store
:=
NewWatchCache
(
5
)
store
.
Add
(
makeTestPod
(
"pod"
,
2
))
store
.
Add
(
makeTestPod
(
"pod"
,
2
))
// Test for Added event.
{
_
,
err
:=
store
.
GetAllEventsSince
(
1
)
if
err
==
nil
{
t
.
Errorf
(
"expected error too old"
)
}
}
{
result
,
err
:=
store
.
GetAllEventsSince
(
2
)
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
if
len
(
result
)
!=
1
{
t
.
Fatalf
(
"unexpected events: %v"
,
result
)
}
if
result
[
0
]
.
Type
!=
watch
.
Added
{
t
.
Errorf
(
"unexpected event type: %v"
,
result
[
0
]
.
Type
)
}
pod
:=
makeTestPod
(
"pod"
,
uint64
(
2
))
if
!
api
.
Semantic
.
DeepEqual
(
pod
,
result
[
0
]
.
Object
)
{
t
.
Errorf
(
"unexpected item: %v, expected: %v"
,
result
[
0
]
.
Object
,
pod
)
}
if
result
[
0
]
.
PrevObject
!=
nil
{
t
.
Errorf
(
"unexpected item: %v"
,
result
[
0
]
.
PrevObject
)
}
}
store
.
Update
(
makeTestPod
(
"pod"
,
3
))
store
.
Update
(
makeTestPod
(
"pod"
,
3
))
store
.
Update
(
makeTestPod
(
"pod"
,
4
))
store
.
Update
(
makeTestPod
(
"pod"
,
4
))
...
@@ -127,10 +156,17 @@ func TestEvents(t *testing.T) {
...
@@ -127,10 +156,17 @@ func TestEvents(t *testing.T) {
t
.
Fatalf
(
"unexpected events: %v"
,
result
)
t
.
Fatalf
(
"unexpected events: %v"
,
result
)
}
}
for
i
:=
0
;
i
<
2
;
i
++
{
for
i
:=
0
;
i
<
2
;
i
++
{
if
result
[
i
]
.
Type
!=
watch
.
Modified
{
t
.
Errorf
(
"unexpected event type: %v"
,
result
[
i
]
.
Type
)
}
pod
:=
makeTestPod
(
"pod"
,
uint64
(
i
+
3
))
pod
:=
makeTestPod
(
"pod"
,
uint64
(
i
+
3
))
if
!
api
.
Semantic
.
DeepEqual
(
pod
,
result
[
i
]
.
Object
)
{
if
!
api
.
Semantic
.
DeepEqual
(
pod
,
result
[
i
]
.
Object
)
{
t
.
Errorf
(
"unexpected item: %v, expected: %v"
,
result
[
i
]
.
Object
,
pod
)
t
.
Errorf
(
"unexpected item: %v, expected: %v"
,
result
[
i
]
.
Object
,
pod
)
}
}
prevPod
:=
makeTestPod
(
"pod"
,
uint64
(
i
+
2
))
if
!
api
.
Semantic
.
DeepEqual
(
prevPod
,
result
[
i
]
.
PrevObject
)
{
t
.
Errorf
(
"unexpected item: %v, expected: %v"
,
result
[
i
]
.
PrevObject
,
prevPod
)
}
}
}
}
}
...
@@ -160,4 +196,28 @@ func TestEvents(t *testing.T) {
...
@@ -160,4 +196,28 @@ func TestEvents(t *testing.T) {
}
}
}
}
}
}
// Test for delete event.
store
.
Delete
(
makeTestPod
(
"pod"
,
uint64
(
9
)))
{
result
,
err
:=
store
.
GetAllEventsSince
(
9
)
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
if
len
(
result
)
!=
1
{
t
.
Fatalf
(
"unexpected events: %v"
,
result
)
}
if
result
[
0
]
.
Type
!=
watch
.
Deleted
{
t
.
Errorf
(
"unexpected event type: %v"
,
result
[
0
]
.
Type
)
}
pod
:=
makeTestPod
(
"pod"
,
uint64
(
9
))
if
!
api
.
Semantic
.
DeepEqual
(
pod
,
result
[
0
]
.
Object
)
{
t
.
Errorf
(
"unexpected item: %v, expected: %v"
,
result
[
0
]
.
Object
,
pod
)
}
prevPod
:=
makeTestPod
(
"pod"
,
uint64
(
8
))
if
!
api
.
Semantic
.
DeepEqual
(
prevPod
,
result
[
0
]
.
PrevObject
)
{
t
.
Errorf
(
"unexpected item: %v, expected: %v"
,
result
[
0
]
.
PrevObject
,
prevPod
)
}
}
}
}
pkg/storage/cacher.go
View file @
8e2cad79
...
@@ -184,7 +184,7 @@ func (c *Cacher) List(key string, listObj runtime.Object) error {
...
@@ -184,7 +184,7 @@ func (c *Cacher) List(key string, listObj runtime.Object) error {
return
nil
return
nil
}
}
func
(
c
*
Cacher
)
processEvent
(
event
watch
.
Event
)
{
func
(
c
*
Cacher
)
processEvent
(
event
cache
.
WatchCache
Event
)
{
c
.
Lock
()
c
.
Lock
()
defer
c
.
Unlock
()
defer
c
.
Unlock
()
for
_
,
watcher
:=
range
c
.
watchers
{
for
_
,
watcher
:=
range
c
.
watchers
{
...
@@ -271,16 +271,16 @@ func (lw *cacherListerWatcher) Watch(resourceVersion string) (watch.Interface, e
...
@@ -271,16 +271,16 @@ func (lw *cacherListerWatcher) Watch(resourceVersion string) (watch.Interface, e
// cacherWatch implements watch.Interface
// cacherWatch implements watch.Interface
type
cacheWatcher
struct
{
type
cacheWatcher
struct
{
sync
.
Mutex
sync
.
Mutex
input
chan
watch
.
Event
input
chan
cache
.
WatchCache
Event
result
chan
watch
.
Event
result
chan
watch
.
Event
filter
FilterFunc
filter
FilterFunc
stopped
bool
stopped
bool
forget
func
()
forget
func
()
}
}
func
newCacheWatcher
(
initEvents
[]
watch
.
Event
,
filter
FilterFunc
,
forget
func
())
*
cacheWatcher
{
func
newCacheWatcher
(
initEvents
[]
cache
.
WatchCache
Event
,
filter
FilterFunc
,
forget
func
())
*
cacheWatcher
{
watcher
:=
&
cacheWatcher
{
watcher
:=
&
cacheWatcher
{
input
:
make
(
chan
watch
.
Event
,
10
),
input
:
make
(
chan
cache
.
WatchCache
Event
,
10
),
result
:
make
(
chan
watch
.
Event
,
10
),
result
:
make
(
chan
watch
.
Event
,
10
),
filter
:
filter
,
filter
:
filter
,
stopped
:
false
,
stopped
:
false
,
...
@@ -310,15 +310,29 @@ func (c *cacheWatcher) stop() {
...
@@ -310,15 +310,29 @@ func (c *cacheWatcher) stop() {
}
}
}
}
func
(
c
*
cacheWatcher
)
add
(
event
watch
.
Event
)
{
func
(
c
*
cacheWatcher
)
add
(
event
cache
.
WatchCache
Event
)
{
c
.
input
<-
event
c
.
input
<-
event
}
}
func
(
c
*
cacheWatcher
)
process
(
initEvents
[]
watch
.
Event
)
{
func
(
c
*
cacheWatcher
)
sendWatchCacheEvent
(
event
cache
.
WatchCacheEvent
)
{
for
_
,
event
:=
range
initEvents
{
curObjPasses
:=
event
.
Type
!=
watch
.
Deleted
&&
c
.
filter
(
event
.
Object
)
if
c
.
filter
(
event
.
Object
)
{
oldObjPasses
:=
false
c
.
result
<-
event
if
event
.
PrevObject
!=
nil
{
oldObjPasses
=
c
.
filter
(
event
.
PrevObject
)
}
switch
{
case
curObjPasses
&&
!
oldObjPasses
:
c
.
result
<-
watch
.
Event
{
watch
.
Added
,
event
.
Object
}
case
curObjPasses
&&
oldObjPasses
:
c
.
result
<-
watch
.
Event
{
watch
.
Modified
,
event
.
Object
}
case
!
curObjPasses
&&
oldObjPasses
:
c
.
result
<-
watch
.
Event
{
watch
.
Deleted
,
event
.
Object
}
}
}
}
func
(
c
*
cacheWatcher
)
process
(
initEvents
[]
cache
.
WatchCacheEvent
)
{
for
_
,
event
:=
range
initEvents
{
c
.
sendWatchCacheEvent
(
event
)
}
}
defer
close
(
c
.
result
)
defer
close
(
c
.
result
)
defer
c
.
Stop
()
defer
c
.
Stop
()
...
@@ -327,8 +341,6 @@ func (c *cacheWatcher) process(initEvents []watch.Event) {
...
@@ -327,8 +341,6 @@ func (c *cacheWatcher) process(initEvents []watch.Event) {
if
!
ok
{
if
!
ok
{
return
return
}
}
if
c
.
filter
(
event
.
Object
)
{
c
.
sendWatchCacheEvent
(
event
)
c
.
result
<-
event
}
}
}
}
}
pkg/storage/cacher_test.go
View file @
8e2cad79
...
@@ -25,7 +25,9 @@ import (
...
@@ -25,7 +25,9 @@ import (
"github.com/coreos/go-etcd/etcd"
"github.com/coreos/go-etcd/etcd"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage"
"k8s.io/kubernetes/pkg/storage"
etcdstorage
"k8s.io/kubernetes/pkg/storage/etcd"
etcdstorage
"k8s.io/kubernetes/pkg/storage/etcd"
...
@@ -300,6 +302,146 @@ func TestWatch(t *testing.T) {
...
@@ -300,6 +302,146 @@ func TestWatch(t *testing.T) {
close
(
fakeClient
.
WatchResponse
)
close
(
fakeClient
.
WatchResponse
)
}
}
func
TestFiltering
(
t
*
testing
.
T
)
{
fakeClient
:=
tools
.
NewFakeEtcdClient
(
t
)
prefixedKey
:=
etcdtest
.
AddPrefix
(
"pods"
)
fakeClient
.
ExpectNotFoundGet
(
prefixedKey
)
cacher
:=
newTestCacher
(
fakeClient
)
fakeClient
.
WaitForWatchCompletion
()
podFoo
:=
makeTestPod
(
"foo"
)
podFoo
.
ObjectMeta
.
Labels
=
map
[
string
]
string
{
"filter"
:
"foo"
}
podFooFiltered
:=
makeTestPod
(
"foo"
)
testCases
:=
[]
struct
{
object
*
api
.
Pod
etcdResponse
*
etcd
.
Response
filtered
bool
event
watch
.
EventType
}{
{
object
:
podFoo
,
etcdResponse
:
&
etcd
.
Response
{
Action
:
"create"
,
Node
:
&
etcd
.
Node
{
Value
:
string
(
runtime
.
EncodeOrDie
(
testapi
.
Codec
(),
podFoo
)),
CreatedIndex
:
1
,
ModifiedIndex
:
1
,
},
},
filtered
:
true
,
event
:
watch
.
Added
,
},
{
object
:
podFooFiltered
,
etcdResponse
:
&
etcd
.
Response
{
Action
:
"set"
,
Node
:
&
etcd
.
Node
{
Value
:
string
(
runtime
.
EncodeOrDie
(
testapi
.
Codec
(),
podFooFiltered
)),
CreatedIndex
:
1
,
ModifiedIndex
:
2
,
},
PrevNode
:
&
etcd
.
Node
{
Value
:
string
(
runtime
.
EncodeOrDie
(
testapi
.
Codec
(),
podFoo
)),
CreatedIndex
:
1
,
ModifiedIndex
:
1
,
},
},
filtered
:
true
,
// Deleted, because the new object doesn't match filter.
event
:
watch
.
Deleted
,
},
{
object
:
podFoo
,
etcdResponse
:
&
etcd
.
Response
{
Action
:
"set"
,
Node
:
&
etcd
.
Node
{
Value
:
string
(
runtime
.
EncodeOrDie
(
testapi
.
Codec
(),
podFoo
)),
CreatedIndex
:
1
,
ModifiedIndex
:
3
,
},
PrevNode
:
&
etcd
.
Node
{
Value
:
string
(
runtime
.
EncodeOrDie
(
testapi
.
Codec
(),
podFooFiltered
)),
CreatedIndex
:
1
,
ModifiedIndex
:
2
,
},
},
filtered
:
true
,
// Added, because the previous object didn't match filter.
event
:
watch
.
Added
,
},
{
object
:
podFoo
,
etcdResponse
:
&
etcd
.
Response
{
Action
:
"set"
,
Node
:
&
etcd
.
Node
{
Value
:
string
(
runtime
.
EncodeOrDie
(
testapi
.
Codec
(),
podFoo
)),
CreatedIndex
:
1
,
ModifiedIndex
:
4
,
},
PrevNode
:
&
etcd
.
Node
{
Value
:
string
(
runtime
.
EncodeOrDie
(
testapi
.
Codec
(),
podFoo
)),
CreatedIndex
:
1
,
ModifiedIndex
:
3
,
},
},
filtered
:
true
,
event
:
watch
.
Modified
,
},
{
object
:
podFoo
,
etcdResponse
:
&
etcd
.
Response
{
Action
:
"delete"
,
Node
:
&
etcd
.
Node
{
CreatedIndex
:
1
,
ModifiedIndex
:
5
,
},
PrevNode
:
&
etcd
.
Node
{
Value
:
string
(
runtime
.
EncodeOrDie
(
testapi
.
Codec
(),
podFoo
)),
CreatedIndex
:
1
,
ModifiedIndex
:
4
,
},
},
filtered
:
true
,
event
:
watch
.
Deleted
,
},
}
// Set up Watch for object "podFoo" with label filter set.
selector
:=
labels
.
SelectorFromSet
(
labels
.
Set
{
"filter"
:
"foo"
})
filter
:=
func
(
obj
runtime
.
Object
)
bool
{
metadata
,
err
:=
meta
.
Accessor
(
obj
)
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
return
false
}
return
selector
.
Matches
(
labels
.
Set
(
metadata
.
Labels
()))
}
watcher
,
err
:=
cacher
.
Watch
(
"pods/ns/foo"
,
1
,
filter
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
for
_
,
test
:=
range
testCases
{
fakeClient
.
WatchResponse
<-
test
.
etcdResponse
if
test
.
filtered
{
event
:=
<-
watcher
.
ResultChan
()
if
e
,
a
:=
test
.
event
,
event
.
Type
;
e
!=
a
{
t
.
Errorf
(
"%v %v"
,
e
,
a
)
}
// unset fields that are set by the infrastructure
obj
:=
event
.
Object
.
(
*
api
.
Pod
)
obj
.
ObjectMeta
.
ResourceVersion
=
""
obj
.
ObjectMeta
.
CreationTimestamp
=
util
.
Time
{}
if
e
,
a
:=
test
.
object
,
obj
;
!
reflect
.
DeepEqual
(
e
,
a
)
{
t
.
Errorf
(
"expected: %#v, got: %#v"
,
e
,
a
)
}
}
}
close
(
fakeClient
.
WatchResponse
)
}
func
TestStorageError
(
t
*
testing
.
T
)
{
func
TestStorageError
(
t
*
testing
.
T
)
{
fakeClient
:=
tools
.
NewFakeEtcdClient
(
t
)
fakeClient
:=
tools
.
NewFakeEtcdClient
(
t
)
prefixedKey
:=
etcdtest
.
AddPrefix
(
"pods"
)
prefixedKey
:=
etcdtest
.
AddPrefix
(
"pods"
)
...
...
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