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
241ab692
Commit
241ab692
authored
Jul 31, 2014
by
Clayton Coleman
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #549 from lavalamp/regWatch
Make replication controllers use new watch interface
parents
6d1a3a13
928092e7
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
172 additions
and
316 deletions
+172
-316
integration.go
cmd/integration/integration.go
+10
-2
interfaces.go
pkg/apiserver/interfaces.go
+1
-0
replication_controller.go
pkg/controller/replication_controller.go
+38
-56
replication_controller_test.go
pkg/controller/replication_controller_test.go
+8
-163
controller_registry.go
pkg/registry/controller_registry.go
+14
-0
controller_registry_test.go
pkg/registry/controller_registry_test.go
+8
-4
etcd_registry.go
pkg/registry/etcd_registry.go
+7
-0
interfaces.go
pkg/registry/interfaces.go
+2
-0
memory_registry.go
pkg/registry/memory_registry.go
+7
-0
decoder.go
pkg/tools/decoder.go
+9
-1
etcd_tools.go
pkg/tools/etcd_tools.go
+35
-37
etcd_tools_test.go
pkg/tools/etcd_tools_test.go
+30
-51
fake_etcd_client.go
pkg/tools/fake_etcd_client.go
+3
-2
No files found.
cmd/integration/integration.go
View file @
241ab692
...
@@ -104,7 +104,10 @@ func startComponents(manifestURL string) (apiServerURL string) {
...
@@ -104,7 +104,10 @@ func startComponents(manifestURL string) (apiServerURL string) {
handler
.
delegate
=
m
.
ConstructHandler
(
"/api/v1beta1"
)
handler
.
delegate
=
m
.
ConstructHandler
(
"/api/v1beta1"
)
controllerManager
:=
controller
.
MakeReplicationManager
(
etcdClient
,
cl
)
controllerManager
:=
controller
.
MakeReplicationManager
(
etcdClient
,
cl
)
controllerManager
.
Run
(
1
*
time
.
Second
)
// Prove that controllerManager's watch works by making it not sync until after this
// test is over. (Hopefully we don't take 10 minutes!)
controllerManager
.
Run
(
10
*
time
.
Minute
)
// Kubelet (localhost)
// Kubelet (localhost)
cfg1
:=
config
.
NewPodConfig
(
config
.
PodConfigNotificationSnapshotAndUpdates
)
cfg1
:=
config
.
NewPodConfig
(
config
.
PodConfigNotificationSnapshotAndUpdates
)
...
@@ -192,7 +195,12 @@ func runAtomicPutTest(c *client.Client) {
...
@@ -192,7 +195,12 @@ func runAtomicPutTest(c *client.Client) {
for
{
for
{
glog
.
Infof
(
"Starting to update (%s, %s)"
,
l
,
v
)
glog
.
Infof
(
"Starting to update (%s, %s)"
,
l
,
v
)
var
tmpSvc
api
.
Service
var
tmpSvc
api
.
Service
err
:=
c
.
Get
()
.
Path
(
"services"
)
.
Path
(
svc
.
ID
)
.
Do
()
.
Into
(
&
tmpSvc
)
err
:=
c
.
Get
()
.
Path
(
"services"
)
.
Path
(
svc
.
ID
)
.
PollPeriod
(
100
*
time
.
Millisecond
)
.
Do
()
.
Into
(
&
tmpSvc
)
if
err
!=
nil
{
if
err
!=
nil
{
glog
.
Errorf
(
"Error getting atomicService: %v"
,
err
)
glog
.
Errorf
(
"Error getting atomicService: %v"
,
err
)
continue
continue
...
...
pkg/apiserver/interfaces.go
View file @
241ab692
...
@@ -43,6 +43,7 @@ type RESTStorage interface {
...
@@ -43,6 +43,7 @@ type RESTStorage interface {
// ResourceWatcher should be implemented by all RESTStorage objects that
// ResourceWatcher should be implemented by all RESTStorage objects that
// want to offer the ability to watch for changes through the watch api.
// want to offer the ability to watch for changes through the watch api.
type
ResourceWatcher
interface
{
type
ResourceWatcher
interface
{
// TODO: take a query, like List, to filter out unwanted events.
WatchAll
()
(
watch
.
Interface
,
error
)
WatchAll
()
(
watch
.
Interface
,
error
)
WatchSingle
(
id
string
)
(
watch
.
Interface
,
error
)
WatchSingle
(
id
string
)
(
watch
.
Interface
,
error
)
}
}
pkg/controller/replication_controller.go
View file @
241ab692
...
@@ -17,8 +17,6 @@ limitations under the License.
...
@@ -17,8 +17,6 @@ limitations under the License.
package
controller
package
controller
import
(
import
(
"encoding/json"
"fmt"
"sync"
"sync"
"time"
"time"
...
@@ -27,13 +25,14 @@ import (
...
@@ -27,13 +25,14 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/
coreos/go-etcd/etcd
"
"github.com/
GoogleCloudPlatform/kubernetes/pkg/watch
"
"github.com/golang/glog"
"github.com/golang/glog"
)
)
// ReplicationManager is responsible for synchronizing ReplicationController objects stored in etcd
// ReplicationManager is responsible for synchronizing ReplicationController objects stored in etcd
// with actual running pods.
// with actual running pods.
// TODO: Remove the etcd dependency and re-factor in terms of a generic watch interface
// TODO: Allow choice of switching between etcd/apiserver watching, or remove etcd references
// from this file completely.
type
ReplicationManager
struct
{
type
ReplicationManager
struct
{
etcdClient
tools
.
EtcdClient
etcdClient
tools
.
EtcdClient
kubeClient
client
.
Interface
kubeClient
client
.
Interface
...
@@ -42,6 +41,9 @@ type ReplicationManager struct {
...
@@ -42,6 +41,9 @@ type ReplicationManager struct {
// To allow injection of syncReplicationController for testing.
// To allow injection of syncReplicationController for testing.
syncHandler
func
(
controllerSpec
api
.
ReplicationController
)
error
syncHandler
func
(
controllerSpec
api
.
ReplicationController
)
error
// To allow injection of watch creation.
watchMaker
func
()
(
watch
.
Interface
,
error
)
}
}
// PodControlInterface is an interface that knows how to add or delete pods
// PodControlInterface is an interface that knows how to add or delete pods
...
@@ -60,6 +62,7 @@ type RealPodControl struct {
...
@@ -60,6 +62,7 @@ type RealPodControl struct {
func
(
r
RealPodControl
)
createReplica
(
controllerSpec
api
.
ReplicationController
)
{
func
(
r
RealPodControl
)
createReplica
(
controllerSpec
api
.
ReplicationController
)
{
labels
:=
controllerSpec
.
DesiredState
.
PodTemplate
.
Labels
labels
:=
controllerSpec
.
DesiredState
.
PodTemplate
.
Labels
// TODO: don't fail to set this label just because the map isn't created.
if
labels
!=
nil
{
if
labels
!=
nil
{
labels
[
"replicationController"
]
=
controllerSpec
.
ID
labels
[
"replicationController"
]
=
controllerSpec
.
ID
}
}
...
@@ -86,9 +89,8 @@ func MakeReplicationManager(etcdClient tools.EtcdClient, kubeClient client.Inter
...
@@ -86,9 +89,8 @@ func MakeReplicationManager(etcdClient tools.EtcdClient, kubeClient client.Inter
kubeClient
:
kubeClient
,
kubeClient
:
kubeClient
,
},
},
}
}
rm
.
syncHandler
=
func
(
controllerSpec
api
.
ReplicationController
)
error
{
rm
.
syncHandler
=
rm
.
syncReplicationController
return
rm
.
syncReplicationController
(
controllerSpec
)
rm
.
watchMaker
=
rm
.
makeAPIWatch
}
return
rm
return
rm
}
}
...
@@ -98,69 +100,49 @@ func (rm *ReplicationManager) Run(period time.Duration) {
...
@@ -98,69 +100,49 @@ func (rm *ReplicationManager) Run(period time.Duration) {
go
util
.
Forever
(
func
()
{
rm
.
watchControllers
()
},
period
)
go
util
.
Forever
(
func
()
{
rm
.
watchControllers
()
},
period
)
}
}
// makeEtcdWatch starts watching via etcd.
func
(
rm
*
ReplicationManager
)
makeEtcdWatch
()
(
watch
.
Interface
,
error
)
{
helper
:=
tools
.
EtcdHelper
{
rm
.
etcdClient
}
return
helper
.
WatchList
(
"/registry/controllers"
,
tools
.
Everything
)
}
// makeAPIWatch starts watching via the apiserver.
func
(
rm
*
ReplicationManager
)
makeAPIWatch
()
(
watch
.
Interface
,
error
)
{
// TODO: Fix this ugly type assertion.
return
rm
.
kubeClient
.
(
*
client
.
Client
)
.
Get
()
.
Path
(
"watch"
)
.
Path
(
"replicationControllers"
)
.
Watch
()
}
func
(
rm
*
ReplicationManager
)
watchControllers
()
{
func
(
rm
*
ReplicationManager
)
watchControllers
()
{
watchChannel
:=
make
(
chan
*
etcd
.
Response
)
watching
,
err
:=
rm
.
watchMaker
()
stop
:=
make
(
chan
bool
)
if
err
!=
nil
{
// Ensure that the call to watch ends.
glog
.
Errorf
(
"Unexpected failure to watch: %v"
,
err
)
defer
close
(
stop
)
time
.
Sleep
(
5
*
time
.
Second
)
return
go
func
()
{
}
defer
util
.
HandleCrash
()
_
,
err
:=
rm
.
etcdClient
.
Watch
(
"/registry/controllers"
,
0
,
true
,
watchChannel
,
stop
)
if
err
==
etcd
.
ErrWatchStoppedByUser
{
close
(
watchChannel
)
}
else
{
glog
.
Errorf
(
"etcd.Watch stopped unexpectedly: %v (%#v)"
,
err
,
err
)
}
}()
for
{
for
{
select
{
select
{
case
<-
rm
.
syncTime
:
case
<-
rm
.
syncTime
:
rm
.
synchronize
()
rm
.
synchronize
()
case
watchResponse
,
open
:=
<-
watchChannel
:
case
event
,
open
:=
<-
watching
.
ResultChan
()
:
if
!
open
||
watchResponse
==
nil
{
if
!
open
{
// watchChannel has been closed, or something else went
// watchChannel has been closed, or something else went
// wrong with our etcd watch call. Let the util.Forever()
// wrong with our etcd watch call. Let the util.Forever()
// that called us call us again.
// that called us call us again.
return
return
}
}
glog
.
Infof
(
"Got watch: %#v"
,
watchResponse
)
glog
.
Infof
(
"Got watch: %#v"
,
event
)
controller
,
err
:=
rm
.
handleWatchResponse
(
watchResponse
)
if
rc
,
ok
:=
event
.
Object
.
(
*
api
.
ReplicationController
);
!
ok
{
if
err
!=
nil
{
glog
.
Errorf
(
"unexpected object: %#v"
,
event
.
Object
)
glog
.
Errorf
(
"Error handling data: %#v, %#v"
,
err
,
watchResponse
)
}
else
{
continue
rm
.
syncHandler
(
*
rc
)
}
}
rm
.
syncHandler
(
*
controller
)
}
}
}
func
(
rm
*
ReplicationManager
)
handleWatchResponse
(
response
*
etcd
.
Response
)
(
*
api
.
ReplicationController
,
error
)
{
switch
response
.
Action
{
case
"set"
:
if
response
.
Node
==
nil
{
return
nil
,
fmt
.
Errorf
(
"response node is null %#v"
,
response
)
}
var
controllerSpec
api
.
ReplicationController
if
err
:=
json
.
Unmarshal
([]
byte
(
response
.
Node
.
Value
),
&
controllerSpec
);
err
!=
nil
{
return
nil
,
err
}
return
&
controllerSpec
,
nil
case
"delete"
:
// Ensure that the final state of a replication controller is applied before it is deleted.
// Otherwise, a replication controller could be modified and then deleted (for example, from 3 to 0
// replicas), and it would be non-deterministic which of its pods continued to exist.
if
response
.
PrevNode
==
nil
{
return
nil
,
fmt
.
Errorf
(
"previous node is null %#v"
,
response
)
}
var
controllerSpec
api
.
ReplicationController
if
err
:=
json
.
Unmarshal
([]
byte
(
response
.
PrevNode
.
Value
),
&
controllerSpec
);
err
!=
nil
{
return
nil
,
err
}
}
return
&
controllerSpec
,
nil
}
}
return
nil
,
nil
}
}
func
(
rm
*
ReplicationManager
)
filterActivePods
(
pods
[]
api
.
Pod
)
[]
api
.
Pod
{
func
(
rm
*
ReplicationManager
)
filterActivePods
(
pods
[]
api
.
Pod
)
[]
api
.
Pod
{
...
...
pkg/controller/replication_controller_test.go
View file @
241ab692
...
@@ -29,6 +29,7 @@ import (
...
@@ -29,6 +29,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/coreos/go-etcd/etcd"
"github.com/coreos/go-etcd/etcd"
)
)
...
@@ -221,142 +222,6 @@ func TestCreateReplica(t *testing.T) {
...
@@ -221,142 +222,6 @@ func TestCreateReplica(t *testing.T) {
}
}
}
}
func
TestHandleWatchResponseNotSet
(
t
*
testing
.
T
)
{
body
,
_
:=
api
.
Encode
(
makePodList
(
2
))
fakeHandler
:=
util
.
FakeHandler
{
StatusCode
:
200
,
ResponseBody
:
string
(
body
),
}
testServer
:=
httptest
.
NewTLSServer
(
&
fakeHandler
)
client
:=
client
.
New
(
testServer
.
URL
,
nil
)
fakePodControl
:=
FakePodControl
{}
manager
:=
MakeReplicationManager
(
nil
,
client
)
manager
.
podControl
=
&
fakePodControl
_
,
err
:=
manager
.
handleWatchResponse
(
&
etcd
.
Response
{
Action
:
"update"
,
})
if
err
!=
nil
{
t
.
Errorf
(
"Unexpected error: %v"
,
err
)
}
}
func
TestHandleWatchResponseNoNode
(
t
*
testing
.
T
)
{
body
,
_
:=
api
.
Encode
(
makePodList
(
2
))
fakeHandler
:=
util
.
FakeHandler
{
StatusCode
:
200
,
ResponseBody
:
string
(
body
),
}
testServer
:=
httptest
.
NewTLSServer
(
&
fakeHandler
)
client
:=
client
.
New
(
testServer
.
URL
,
nil
)
fakePodControl
:=
FakePodControl
{}
manager
:=
MakeReplicationManager
(
nil
,
client
)
manager
.
podControl
=
&
fakePodControl
_
,
err
:=
manager
.
handleWatchResponse
(
&
etcd
.
Response
{
Action
:
"set"
,
})
if
err
==
nil
{
t
.
Error
(
"Unexpected non-error"
)
}
}
func
TestHandleWatchResponseBadData
(
t
*
testing
.
T
)
{
body
,
_
:=
api
.
Encode
(
makePodList
(
2
))
fakeHandler
:=
util
.
FakeHandler
{
StatusCode
:
200
,
ResponseBody
:
string
(
body
),
}
testServer
:=
httptest
.
NewTLSServer
(
&
fakeHandler
)
client
:=
client
.
New
(
testServer
.
URL
,
nil
)
fakePodControl
:=
FakePodControl
{}
manager
:=
MakeReplicationManager
(
nil
,
client
)
manager
.
podControl
=
&
fakePodControl
_
,
err
:=
manager
.
handleWatchResponse
(
&
etcd
.
Response
{
Action
:
"set"
,
Node
:
&
etcd
.
Node
{
Value
:
"foobar"
,
},
})
if
err
==
nil
{
t
.
Error
(
"Unexpected non-error"
)
}
}
func
TestHandleWatchResponse
(
t
*
testing
.
T
)
{
body
,
_
:=
api
.
Encode
(
makePodList
(
2
))
fakeHandler
:=
util
.
FakeHandler
{
StatusCode
:
200
,
ResponseBody
:
string
(
body
),
}
testServer
:=
httptest
.
NewTLSServer
(
&
fakeHandler
)
client
:=
client
.
New
(
testServer
.
URL
,
nil
)
fakePodControl
:=
FakePodControl
{}
manager
:=
MakeReplicationManager
(
nil
,
client
)
manager
.
podControl
=
&
fakePodControl
controller
:=
makeReplicationController
(
2
)
// TODO: fixme when etcd uses Encode/Decode
data
,
err
:=
json
.
Marshal
(
controller
)
if
err
!=
nil
{
t
.
Errorf
(
"Unexpected error: %v"
,
err
)
}
controllerOut
,
err
:=
manager
.
handleWatchResponse
(
&
etcd
.
Response
{
Action
:
"set"
,
Node
:
&
etcd
.
Node
{
Value
:
string
(
data
),
},
})
if
err
!=
nil
{
t
.
Errorf
(
"Unexpected error: %#v"
,
err
)
}
if
!
reflect
.
DeepEqual
(
controller
,
*
controllerOut
)
{
t
.
Errorf
(
"Unexpected mismatch. Expected %#v, Saw: %#v"
,
controller
,
controllerOut
)
}
}
func
TestHandleWatchResponseDelete
(
t
*
testing
.
T
)
{
body
,
_
:=
api
.
Encode
(
makePodList
(
2
))
fakeHandler
:=
util
.
FakeHandler
{
StatusCode
:
200
,
ResponseBody
:
string
(
body
),
}
testServer
:=
httptest
.
NewTLSServer
(
&
fakeHandler
)
client
:=
client
.
New
(
testServer
.
URL
,
nil
)
fakePodControl
:=
FakePodControl
{}
manager
:=
MakeReplicationManager
(
nil
,
client
)
manager
.
podControl
=
&
fakePodControl
controller
:=
makeReplicationController
(
2
)
// TODO: fixme when etcd writing uses api.Encode
data
,
err
:=
json
.
Marshal
(
controller
)
if
err
!=
nil
{
t
.
Errorf
(
"Unexpected error: %v"
,
err
)
}
controllerOut
,
err
:=
manager
.
handleWatchResponse
(
&
etcd
.
Response
{
Action
:
"delete"
,
PrevNode
:
&
etcd
.
Node
{
Value
:
string
(
data
),
},
})
if
err
!=
nil
{
t
.
Errorf
(
"Unexpected error: %#v"
,
err
)
}
if
!
reflect
.
DeepEqual
(
controller
,
*
controllerOut
)
{
t
.
Errorf
(
"Unexpected mismatch. Expected %#v, Saw: %#v"
,
controller
,
controllerOut
)
}
}
func
TestSyncronize
(
t
*
testing
.
T
)
{
func
TestSyncronize
(
t
*
testing
.
T
)
{
controllerSpec1
:=
api
.
ReplicationController
{
controllerSpec1
:=
api
.
ReplicationController
{
JSONBase
:
api
.
JSONBase
{
APIVersion
:
"v1beta1"
},
JSONBase
:
api
.
JSONBase
{
APIVersion
:
"v1beta1"
},
...
@@ -435,9 +300,14 @@ func TestSyncronize(t *testing.T) {
...
@@ -435,9 +300,14 @@ func TestSyncronize(t *testing.T) {
func
TestWatchControllers
(
t
*
testing
.
T
)
{
func
TestWatchControllers
(
t
*
testing
.
T
)
{
fakeEtcd
:=
tools
.
MakeFakeEtcdClient
(
t
)
fakeEtcd
:=
tools
.
MakeFakeEtcdClient
(
t
)
fakeWatcher
:=
watch
.
NewFake
()
manager
:=
MakeReplicationManager
(
fakeEtcd
,
nil
)
manager
:=
MakeReplicationManager
(
fakeEtcd
,
nil
)
manager
.
watchMaker
=
func
()
(
watch
.
Interface
,
error
)
{
return
fakeWatcher
,
nil
}
var
testControllerSpec
api
.
ReplicationController
var
testControllerSpec
api
.
ReplicationController
received
:=
make
(
chan
bool
)
received
:=
make
(
chan
struct
{}
)
manager
.
syncHandler
=
func
(
controllerSpec
api
.
ReplicationController
)
error
{
manager
.
syncHandler
=
func
(
controllerSpec
api
.
ReplicationController
)
error
{
if
!
reflect
.
DeepEqual
(
controllerSpec
,
testControllerSpec
)
{
if
!
reflect
.
DeepEqual
(
controllerSpec
,
testControllerSpec
)
{
t
.
Errorf
(
"Expected %#v, but got %#v"
,
testControllerSpec
,
controllerSpec
)
t
.
Errorf
(
"Expected %#v, but got %#v"
,
testControllerSpec
,
controllerSpec
)
...
@@ -448,38 +318,13 @@ func TestWatchControllers(t *testing.T) {
...
@@ -448,38 +318,13 @@ func TestWatchControllers(t *testing.T) {
go
manager
.
watchControllers
()
go
manager
.
watchControllers
()
fakeEtcd
.
WaitForWatchCompletion
()
// Test normal case
// Test normal case
testControllerSpec
.
ID
=
"foo"
testControllerSpec
.
ID
=
"foo"
fakeEtcd
.
WatchResponse
<-
&
etcd
.
Response
{
fakeWatcher
.
Add
(
&
testControllerSpec
)
Action
:
"set"
,
Node
:
&
etcd
.
Node
{
Value
:
util
.
MakeJSONString
(
testControllerSpec
),
},
}
select
{
select
{
case
<-
received
:
case
<-
received
:
case
<-
time
.
After
(
10
*
time
.
Millisecond
)
:
case
<-
time
.
After
(
10
*
time
.
Millisecond
)
:
t
.
Errorf
(
"Expected 1 call but got 0"
)
t
.
Errorf
(
"Expected 1 call but got 0"
)
}
}
// Test error case
fakeEtcd
.
WatchInjectError
<-
fmt
.
Errorf
(
"Injected error"
)
// Did everything shut down?
if
_
,
open
:=
<-
fakeEtcd
.
WatchResponse
;
open
{
t
.
Errorf
(
"An injected error did not cause a graceful shutdown"
)
}
// Test purposeful shutdown
go
manager
.
watchControllers
()
fakeEtcd
.
WaitForWatchCompletion
()
fakeEtcd
.
WatchStop
<-
true
// Did everything shut down?
if
_
,
open
:=
<-
fakeEtcd
.
WatchResponse
;
open
{
t
.
Errorf
(
"A stop did not cause a graceful shutdown"
)
}
}
}
pkg/registry/controller_registry.go
View file @
241ab692
...
@@ -17,6 +17,7 @@ limitations under the License.
...
@@ -17,6 +17,7 @@ limitations under the License.
package
registry
package
registry
import
(
import
(
"errors"
"fmt"
"fmt"
"time"
"time"
...
@@ -24,6 +25,7 @@ import (
...
@@ -24,6 +25,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
)
// ControllerRegistryStorage is an implementation of RESTStorage for the api server.
// ControllerRegistryStorage is an implementation of RESTStorage for the api server.
...
@@ -135,3 +137,15 @@ func (storage *ControllerRegistryStorage) waitForController(ctrl api.Replication
...
@@ -135,3 +137,15 @@ func (storage *ControllerRegistryStorage) waitForController(ctrl api.Replication
}
}
return
ctrl
,
nil
return
ctrl
,
nil
}
}
// WatchAll returns ReplicationController events via a watch.Interface, implementing
// apiserver.ResourceWatcher.
func
(
storage
*
ControllerRegistryStorage
)
WatchAll
()
(
watch
.
Interface
,
error
)
{
return
storage
.
registry
.
WatchControllers
()
}
// WatchSingle returns events for a single ReplicationController via a watch.Interface,
// implementing apiserver.ResourceWatcher.
func
(
storage
*
ControllerRegistryStorage
)
WatchSingle
(
id
string
)
(
watch
.
Interface
,
error
)
{
return
nil
,
errors
.
New
(
"unimplemented"
)
}
pkg/registry/controller_registry_test.go
View file @
241ab692
...
@@ -26,6 +26,7 @@ import (
...
@@ -26,6 +26,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
)
type
MockControllerRegistry
struct
{
type
MockControllerRegistry
struct
{
...
@@ -51,6 +52,9 @@ func (registry *MockControllerRegistry) UpdateController(controller api.Replicat
...
@@ -51,6 +52,9 @@ func (registry *MockControllerRegistry) UpdateController(controller api.Replicat
func
(
registry
*
MockControllerRegistry
)
DeleteController
(
ID
string
)
error
{
func
(
registry
*
MockControllerRegistry
)
DeleteController
(
ID
string
)
error
{
return
registry
.
err
return
registry
.
err
}
}
func
(
registry
*
MockControllerRegistry
)
WatchControllers
()
(
watch
.
Interface
,
error
)
{
return
nil
,
registry
.
err
}
func
TestListControllersError
(
t
*
testing
.
T
)
{
func
TestListControllersError
(
t
*
testing
.
T
)
{
mockRegistry
:=
MockControllerRegistry
{
mockRegistry
:=
MockControllerRegistry
{
...
@@ -267,13 +271,13 @@ func TestControllerStorageValidatesCreate(t *testing.T) {
...
@@ -267,13 +271,13 @@ func TestControllerStorageValidatesCreate(t *testing.T) {
}
}
failureCases
:=
map
[
string
]
api
.
ReplicationController
{
failureCases
:=
map
[
string
]
api
.
ReplicationController
{
"empty ID"
:
api
.
ReplicationController
{
"empty ID"
:
{
JSONBase
:
api
.
JSONBase
{
ID
:
""
},
JSONBase
:
api
.
JSONBase
{
ID
:
""
},
DesiredState
:
api
.
ReplicationControllerState
{
DesiredState
:
api
.
ReplicationControllerState
{
ReplicaSelector
:
map
[
string
]
string
{
"bar"
:
"baz"
},
ReplicaSelector
:
map
[
string
]
string
{
"bar"
:
"baz"
},
},
},
},
},
"empty selector"
:
api
.
ReplicationController
{
"empty selector"
:
{
JSONBase
:
api
.
JSONBase
{
ID
:
"abc"
},
JSONBase
:
api
.
JSONBase
{
ID
:
"abc"
},
DesiredState
:
api
.
ReplicationControllerState
{},
DesiredState
:
api
.
ReplicationControllerState
{},
},
},
...
@@ -298,13 +302,13 @@ func TestControllerStorageValidatesUpdate(t *testing.T) {
...
@@ -298,13 +302,13 @@ func TestControllerStorageValidatesUpdate(t *testing.T) {
}
}
failureCases
:=
map
[
string
]
api
.
ReplicationController
{
failureCases
:=
map
[
string
]
api
.
ReplicationController
{
"empty ID"
:
api
.
ReplicationController
{
"empty ID"
:
{
JSONBase
:
api
.
JSONBase
{
ID
:
""
},
JSONBase
:
api
.
JSONBase
{
ID
:
""
},
DesiredState
:
api
.
ReplicationControllerState
{
DesiredState
:
api
.
ReplicationControllerState
{
ReplicaSelector
:
map
[
string
]
string
{
"bar"
:
"baz"
},
ReplicaSelector
:
map
[
string
]
string
{
"bar"
:
"baz"
},
},
},
},
},
"empty selector"
:
api
.
ReplicationController
{
"empty selector"
:
{
JSONBase
:
api
.
JSONBase
{
ID
:
"abc"
},
JSONBase
:
api
.
JSONBase
{
ID
:
"abc"
},
DesiredState
:
api
.
ReplicationControllerState
{},
DesiredState
:
api
.
ReplicationControllerState
{},
},
},
...
...
pkg/registry/etcd_registry.go
View file @
241ab692
...
@@ -23,6 +23,7 @@ import (
...
@@ -23,6 +23,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/golang/glog"
"github.com/golang/glog"
)
)
...
@@ -207,6 +208,12 @@ func (registry *EtcdRegistry) ListControllers() ([]api.ReplicationController, er
...
@@ -207,6 +208,12 @@ func (registry *EtcdRegistry) ListControllers() ([]api.ReplicationController, er
return
controllers
,
err
return
controllers
,
err
}
}
// WatchControllers begins watching for new, changed, or deleted controllers.
// TODO: Add id/selector parameters?
func
(
registry
*
EtcdRegistry
)
WatchControllers
()
(
watch
.
Interface
,
error
)
{
return
registry
.
helper
()
.
WatchList
(
"/registry/controllers"
,
tools
.
Everything
)
}
func
makeControllerKey
(
id
string
)
string
{
func
makeControllerKey
(
id
string
)
string
{
return
"/registry/controllers/"
+
id
return
"/registry/controllers/"
+
id
}
}
...
...
pkg/registry/interfaces.go
View file @
241ab692
...
@@ -19,6 +19,7 @@ package registry
...
@@ -19,6 +19,7 @@ package registry
import
(
import
(
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
)
// PodRegistry is an interface implemented by things that know how to store Pod objects.
// PodRegistry is an interface implemented by things that know how to store Pod objects.
...
@@ -38,6 +39,7 @@ type PodRegistry interface {
...
@@ -38,6 +39,7 @@ type PodRegistry interface {
// ControllerRegistry is an interface for things that know how to store ReplicationControllers.
// ControllerRegistry is an interface for things that know how to store ReplicationControllers.
type
ControllerRegistry
interface
{
type
ControllerRegistry
interface
{
ListControllers
()
([]
api
.
ReplicationController
,
error
)
ListControllers
()
([]
api
.
ReplicationController
,
error
)
WatchControllers
()
(
watch
.
Interface
,
error
)
GetController
(
controllerID
string
)
(
*
api
.
ReplicationController
,
error
)
GetController
(
controllerID
string
)
(
*
api
.
ReplicationController
,
error
)
CreateController
(
controller
api
.
ReplicationController
)
error
CreateController
(
controller
api
.
ReplicationController
)
error
UpdateController
(
controller
api
.
ReplicationController
)
error
UpdateController
(
controller
api
.
ReplicationController
)
error
...
...
pkg/registry/memory_registry.go
View file @
241ab692
...
@@ -17,9 +17,12 @@ limitations under the License.
...
@@ -17,9 +17,12 @@ limitations under the License.
package
registry
package
registry
import
(
import
(
"errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
)
// An implementation of PodRegistry and ControllerRegistry that is backed by memory
// An implementation of PodRegistry and ControllerRegistry that is backed by memory
...
@@ -86,6 +89,10 @@ func (registry *MemoryRegistry) ListControllers() ([]api.ReplicationController,
...
@@ -86,6 +89,10 @@ func (registry *MemoryRegistry) ListControllers() ([]api.ReplicationController,
return
result
,
nil
return
result
,
nil
}
}
func
(
registry
*
MemoryRegistry
)
WatchControllers
()
(
watch
.
Interface
,
error
)
{
return
nil
,
errors
.
New
(
"unimplemented"
)
}
func
(
registry
*
MemoryRegistry
)
GetController
(
controllerID
string
)
(
*
api
.
ReplicationController
,
error
)
{
func
(
registry
*
MemoryRegistry
)
GetController
(
controllerID
string
)
(
*
api
.
ReplicationController
,
error
)
{
controller
,
found
:=
registry
.
controllerData
[
controllerID
]
controller
,
found
:=
registry
.
controllerData
[
controllerID
]
if
found
{
if
found
{
...
...
pkg/tools/decoder.go
View file @
241ab692
...
@@ -18,6 +18,7 @@ package tools
...
@@ -18,6 +18,7 @@ package tools
import
(
import
(
"encoding/json"
"encoding/json"
"fmt"
"io"
"io"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
...
@@ -44,7 +45,14 @@ func NewAPIEventDecoder(stream io.ReadCloser) *APIEventDecoder {
...
@@ -44,7 +45,14 @@ func NewAPIEventDecoder(stream io.ReadCloser) *APIEventDecoder {
func
(
d
*
APIEventDecoder
)
Decode
()
(
action
watch
.
EventType
,
object
interface
{},
err
error
)
{
func
(
d
*
APIEventDecoder
)
Decode
()
(
action
watch
.
EventType
,
object
interface
{},
err
error
)
{
var
got
api
.
WatchEvent
var
got
api
.
WatchEvent
err
=
d
.
decoder
.
Decode
(
&
got
)
err
=
d
.
decoder
.
Decode
(
&
got
)
return
got
.
Type
,
got
.
Object
.
Object
,
err
if
err
!=
nil
{
return
action
,
nil
,
err
}
switch
got
.
Type
{
case
watch
.
Added
,
watch
.
Modified
,
watch
.
Deleted
:
return
got
.
Type
,
got
.
Object
.
Object
,
err
}
return
action
,
nil
,
fmt
.
Errorf
(
"got invalid watch event type: %v"
,
got
.
Type
)
}
}
// Close closes the underlying stream.
// Close closes the underlying stream.
...
...
pkg/tools/etcd_tools.go
View file @
241ab692
...
@@ -19,6 +19,7 @@ package tools
...
@@ -19,6 +19,7 @@ package tools
import
(
import
(
"fmt"
"fmt"
"reflect"
"reflect"
"sync"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
...
@@ -267,23 +268,31 @@ type etcdWatcher struct {
...
@@ -267,23 +268,31 @@ type etcdWatcher struct {
list
bool
// If we're doing a recursive watch, should be true.
list
bool
// If we're doing a recursive watch, should be true.
filter
FilterFunc
filter
FilterFunc
etcdIncoming
chan
*
etcd
.
Response
etcdIncoming
chan
*
etcd
.
Response
etcdStop
chan
bool
etcdStop
chan
bool
etcdCallEnded
chan
struct
{}
outgoing
chan
watch
.
Event
outgoing
chan
watch
.
Event
userStop
chan
struct
{}
userStop
chan
struct
{}
stopped
bool
stopLock
sync
.
Mutex
// Injectable for testing. Send the event down the outgoing channel.
emit
func
(
watch
.
Event
)
}
}
// Returns a new etcdWatcher; if list is true, watch sub-nodes.
// Returns a new etcdWatcher; if list is true, watch sub-nodes.
func
newEtcdWatcher
(
list
bool
,
filter
FilterFunc
)
*
etcdWatcher
{
func
newEtcdWatcher
(
list
bool
,
filter
FilterFunc
)
*
etcdWatcher
{
w
:=
&
etcdWatcher
{
w
:=
&
etcdWatcher
{
list
:
list
,
list
:
list
,
filter
:
filter
,
filter
:
filter
,
etcdIncoming
:
make
(
chan
*
etcd
.
Response
),
etcdIncoming
:
make
(
chan
*
etcd
.
Response
),
etcdStop
:
make
(
chan
bool
),
etcdStop
:
make
(
chan
bool
),
outgoing
:
make
(
chan
watch
.
Event
),
etcdCallEnded
:
make
(
chan
struct
{}),
userStop
:
make
(
chan
struct
{}),
outgoing
:
make
(
chan
watch
.
Event
),
userStop
:
make
(
chan
struct
{}),
}
}
w
.
emit
=
func
(
e
watch
.
Event
)
{
w
.
outgoing
<-
e
}
go
w
.
translate
()
go
w
.
translate
()
return
w
return
w
}
}
...
@@ -292,11 +301,9 @@ func newEtcdWatcher(list bool, filter FilterFunc) *etcdWatcher {
...
@@ -292,11 +301,9 @@ func newEtcdWatcher(list bool, filter FilterFunc) *etcdWatcher {
// as a goroutine.
// as a goroutine.
func
(
w
*
etcdWatcher
)
etcdWatch
(
client
EtcdGetSet
,
key
string
)
{
func
(
w
*
etcdWatcher
)
etcdWatch
(
client
EtcdGetSet
,
key
string
)
{
defer
util
.
HandleCrash
()
defer
util
.
HandleCrash
()
defer
close
(
w
.
etcdCallEnded
)
_
,
err
:=
client
.
Watch
(
key
,
0
,
w
.
list
,
w
.
etcdIncoming
,
w
.
etcdStop
)
_
,
err
:=
client
.
Watch
(
key
,
0
,
w
.
list
,
w
.
etcdIncoming
,
w
.
etcdStop
)
if
err
==
etcd
.
ErrWatchStoppedByUser
{
if
err
!=
etcd
.
ErrWatchStoppedByUser
{
// etcd doesn't close the channel in this case.
close
(
w
.
etcdIncoming
)
}
else
{
glog
.
Errorf
(
"etcd.Watch stopped unexpectedly: %v (%#v)"
,
err
,
err
)
glog
.
Errorf
(
"etcd.Watch stopped unexpectedly: %v (%#v)"
,
err
,
err
)
}
}
}
}
...
@@ -309,6 +316,8 @@ func (w *etcdWatcher) translate() {
...
@@ -309,6 +316,8 @@ func (w *etcdWatcher) translate() {
for
{
for
{
select
{
select
{
case
<-
w
.
etcdCallEnded
:
return
case
<-
w
.
userStop
:
case
<-
w
.
userStop
:
w
.
etcdStop
<-
true
w
.
etcdStop
<-
true
return
return
...
@@ -324,7 +333,6 @@ func (w *etcdWatcher) translate() {
...
@@ -324,7 +333,6 @@ func (w *etcdWatcher) translate() {
func
(
w
*
etcdWatcher
)
sendResult
(
res
*
etcd
.
Response
)
{
func
(
w
*
etcdWatcher
)
sendResult
(
res
*
etcd
.
Response
)
{
var
action
watch
.
EventType
var
action
watch
.
EventType
var
data
[]
byte
var
data
[]
byte
var
nodes
etcd
.
Nodes
switch
res
.
Action
{
switch
res
.
Action
{
case
"set"
:
case
"set"
:
if
res
.
Node
==
nil
{
if
res
.
Node
==
nil
{
...
@@ -332,7 +340,6 @@ func (w *etcdWatcher) sendResult(res *etcd.Response) {
...
@@ -332,7 +340,6 @@ func (w *etcdWatcher) sendResult(res *etcd.Response) {
return
return
}
}
data
=
[]
byte
(
res
.
Node
.
Value
)
data
=
[]
byte
(
res
.
Node
.
Value
)
nodes
=
res
.
Node
.
Nodes
// TODO: Is this conditional correct?
// TODO: Is this conditional correct?
if
res
.
EtcdIndex
>
0
{
if
res
.
EtcdIndex
>
0
{
action
=
watch
.
Modified
action
=
watch
.
Modified
...
@@ -345,38 +352,23 @@ func (w *etcdWatcher) sendResult(res *etcd.Response) {
...
@@ -345,38 +352,23 @@ func (w *etcdWatcher) sendResult(res *etcd.Response) {
return
return
}
}
data
=
[]
byte
(
res
.
PrevNode
.
Value
)
data
=
[]
byte
(
res
.
PrevNode
.
Value
)
nodes
=
res
.
PrevNode
.
Nodes
action
=
watch
.
Deleted
action
=
watch
.
Deleted
}
default
:
glog
.
Errorf
(
"unknown action: %v"
,
res
.
Action
)
// If listing, we're interested in sub-nodes.
if
w
.
list
{
for
_
,
n
:=
range
nodes
{
obj
,
err
:=
api
.
Decode
([]
byte
(
n
.
Value
))
if
err
!=
nil
{
glog
.
Errorf
(
"failure to decode api object: %#v"
,
res
)
continue
}
if
w
.
filter
!=
nil
&&
!
w
.
filter
(
obj
)
{
continue
}
w
.
outgoing
<-
watch
.
Event
{
Type
:
action
,
Object
:
obj
,
}
}
return
return
}
}
obj
,
err
:=
api
.
Decode
(
data
)
obj
,
err
:=
api
.
Decode
(
data
)
if
err
!=
nil
{
if
err
!=
nil
{
glog
.
Errorf
(
"failure to decode api object: %#v"
,
res
)
glog
.
Errorf
(
"failure to decode api object: '%v' from %#v"
,
string
(
data
),
res
)
// TODO: expose an error through watch.Interface?
w
.
Stop
()
return
return
}
}
w
.
outgoing
<-
watch
.
Event
{
w
.
emit
(
watch
.
Event
{
Type
:
action
,
Type
:
action
,
Object
:
obj
,
Object
:
obj
,
}
}
)
}
}
// ResultChannel implements watch.Interface.
// ResultChannel implements watch.Interface.
...
@@ -386,5 +378,11 @@ func (w *etcdWatcher) ResultChan() <-chan watch.Event {
...
@@ -386,5 +378,11 @@ func (w *etcdWatcher) ResultChan() <-chan watch.Event {
// Stop implements watch.Interface.
// Stop implements watch.Interface.
func
(
w
*
etcdWatcher
)
Stop
()
{
func
(
w
*
etcdWatcher
)
Stop
()
{
close
(
w
.
userStop
)
w
.
stopLock
.
Lock
()
defer
w
.
stopLock
.
Unlock
()
// Prevent double channel closes.
if
!
w
.
stopped
{
w
.
stopped
=
true
close
(
w
.
userStop
)
}
}
}
pkg/tools/etcd_tools_test.go
View file @
241ab692
...
@@ -220,9 +220,8 @@ func TestAtomicUpdate(t *testing.T) {
...
@@ -220,9 +220,8 @@ func TestAtomicUpdate(t *testing.T) {
}
}
func
TestWatchInterpretation_ListAdd
(
t
*
testing
.
T
)
{
func
TestWatchInterpretation_ListAdd
(
t
*
testing
.
T
)
{
called
:=
false
w
:=
newEtcdWatcher
(
true
,
func
(
interface
{})
bool
{
w
:=
newEtcdWatcher
(
true
,
func
(
interface
{})
bool
{
called
=
true
t
.
Errorf
(
"unexpected filter call"
)
return
true
return
true
})
})
pod
:=
&
api
.
Pod
{
JSONBase
:
api
.
JSONBase
{
ID
:
"foo"
}}
pod
:=
&
api
.
Pod
{
JSONBase
:
api
.
JSONBase
{
ID
:
"foo"
}}
...
@@ -231,11 +230,7 @@ func TestWatchInterpretation_ListAdd(t *testing.T) {
...
@@ -231,11 +230,7 @@ func TestWatchInterpretation_ListAdd(t *testing.T) {
go
w
.
sendResult
(
&
etcd
.
Response
{
go
w
.
sendResult
(
&
etcd
.
Response
{
Action
:
"set"
,
Action
:
"set"
,
Node
:
&
etcd
.
Node
{
Node
:
&
etcd
.
Node
{
Nodes
:
etcd
.
Nodes
{
Value
:
string
(
podBytes
),
{
Value
:
string
(
podBytes
),
},
},
},
},
})
})
...
@@ -246,15 +241,11 @@ func TestWatchInterpretation_ListAdd(t *testing.T) {
...
@@ -246,15 +241,11 @@ func TestWatchInterpretation_ListAdd(t *testing.T) {
if
e
,
a
:=
pod
,
got
.
Object
;
!
reflect
.
DeepEqual
(
e
,
a
)
{
if
e
,
a
:=
pod
,
got
.
Object
;
!
reflect
.
DeepEqual
(
e
,
a
)
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
}
if
!
called
{
t
.
Errorf
(
"filter never called"
)
}
}
}
func
TestWatchInterpretation_ListDelete
(
t
*
testing
.
T
)
{
func
TestWatchInterpretation_Delete
(
t
*
testing
.
T
)
{
called
:=
false
w
:=
newEtcdWatcher
(
true
,
func
(
interface
{})
bool
{
w
:=
newEtcdWatcher
(
true
,
func
(
interface
{})
bool
{
called
=
true
t
.
Errorf
(
"unexpected filter call"
)
return
true
return
true
})
})
pod
:=
&
api
.
Pod
{
JSONBase
:
api
.
JSONBase
{
ID
:
"foo"
}}
pod
:=
&
api
.
Pod
{
JSONBase
:
api
.
JSONBase
{
ID
:
"foo"
}}
...
@@ -263,11 +254,7 @@ func TestWatchInterpretation_ListDelete(t *testing.T) {
...
@@ -263,11 +254,7 @@ func TestWatchInterpretation_ListDelete(t *testing.T) {
go
w
.
sendResult
(
&
etcd
.
Response
{
go
w
.
sendResult
(
&
etcd
.
Response
{
Action
:
"delete"
,
Action
:
"delete"
,
PrevNode
:
&
etcd
.
Node
{
PrevNode
:
&
etcd
.
Node
{
Nodes
:
etcd
.
Nodes
{
Value
:
string
(
podBytes
),
{
Value
:
string
(
podBytes
),
},
},
},
},
})
})
...
@@ -278,57 +265,49 @@ func TestWatchInterpretation_ListDelete(t *testing.T) {
...
@@ -278,57 +265,49 @@ func TestWatchInterpretation_ListDelete(t *testing.T) {
if
e
,
a
:=
pod
,
got
.
Object
;
!
reflect
.
DeepEqual
(
e
,
a
)
{
if
e
,
a
:=
pod
,
got
.
Object
;
!
reflect
.
DeepEqual
(
e
,
a
)
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
}
if
!
called
{
t
.
Errorf
(
"filter never called"
)
}
}
}
func
TestWatchInterpretation_
SingleAdd
(
t
*
testing
.
T
)
{
func
TestWatchInterpretation_
ResponseNotSet
(
t
*
testing
.
T
)
{
w
:=
newEtcdWatcher
(
false
,
func
(
interface
{})
bool
{
w
:=
newEtcdWatcher
(
false
,
func
(
interface
{})
bool
{
t
.
Errorf
(
"unexpected filter call"
)
t
.
Errorf
(
"unexpected filter call"
)
return
true
return
true
})
})
pod
:=
&
api
.
Pod
{
JSONBase
:
api
.
JSONBase
{
ID
:
"foo"
}}
w
.
emit
=
func
(
e
watch
.
Event
)
{
podBytes
,
_
:=
api
.
Encode
(
pod
)
t
.
Errorf
(
"Unexpected emit: %v"
,
e
)
}
go
w
.
sendResult
(
&
etcd
.
Response
{
w
.
sendResult
(
&
etcd
.
Response
{
Action
:
"set"
,
Action
:
"update"
,
Node
:
&
etcd
.
Node
{
Value
:
string
(
podBytes
),
},
})
})
}
got
:=
<-
w
.
outgoing
func
TestWatchInterpretation_ResponseNoNode
(
t
*
testing
.
T
)
{
if
e
,
a
:=
watch
.
Added
,
got
.
Type
;
e
!=
a
{
w
:=
newEtcdWatcher
(
false
,
func
(
interface
{})
bool
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
t
.
Errorf
(
"unexpected filter call"
)
}
return
true
if
e
,
a
:=
pod
,
got
.
Object
;
!
reflect
.
DeepEqual
(
e
,
a
)
{
})
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
w
.
emit
=
func
(
e
watch
.
Event
)
{
t
.
Errorf
(
"Unexpected emit: %v"
,
e
)
}
}
w
.
sendResult
(
&
etcd
.
Response
{
Action
:
"set"
,
})
}
}
func
TestWatchInterpretation_
SingleDelete
(
t
*
testing
.
T
)
{
func
TestWatchInterpretation_
ResponseBadData
(
t
*
testing
.
T
)
{
w
:=
newEtcdWatcher
(
false
,
func
(
interface
{})
bool
{
w
:=
newEtcdWatcher
(
false
,
func
(
interface
{})
bool
{
t
.
Errorf
(
"unexpected filter call"
)
t
.
Errorf
(
"unexpected filter call"
)
return
true
return
true
})
})
pod
:=
&
api
.
Pod
{
JSONBase
:
api
.
JSONBase
{
ID
:
"foo"
}}
w
.
emit
=
func
(
e
watch
.
Event
)
{
podBytes
,
_
:=
api
.
Encode
(
pod
)
t
.
Errorf
(
"Unexpected emit: %v"
,
e
)
}
go
w
.
sendResult
(
&
etcd
.
Response
{
w
.
sendResult
(
&
etcd
.
Response
{
Action
:
"
delete
"
,
Action
:
"
set
"
,
Prev
Node
:
&
etcd
.
Node
{
Node
:
&
etcd
.
Node
{
Value
:
string
(
podBytes
)
,
Value
:
"foobar"
,
},
},
})
})
got
:=
<-
w
.
outgoing
if
e
,
a
:=
watch
.
Deleted
,
got
.
Type
;
e
!=
a
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
if
e
,
a
:=
pod
,
got
.
Object
;
!
reflect
.
DeepEqual
(
e
,
a
)
{
t
.
Errorf
(
"Expected %v, got %v"
,
e
,
a
)
}
}
}
func
TestWatch
(
t
*
testing
.
T
)
{
func
TestWatch
(
t
*
testing
.
T
)
{
...
...
pkg/tools/fake_etcd_client.go
View file @
241ab692
...
@@ -215,6 +215,9 @@ func (f *FakeEtcdClient) Watch(prefix string, waitIndex uint64, recursive bool,
...
@@ -215,6 +215,9 @@ func (f *FakeEtcdClient) Watch(prefix string, waitIndex uint64, recursive bool,
if
receiver
==
nil
{
if
receiver
==
nil
{
return
f
.
Get
(
prefix
,
false
,
recursive
)
return
f
.
Get
(
prefix
,
false
,
recursive
)
}
else
{
// Emulate etcd's behavior. (I think.)
defer
close
(
receiver
)
}
}
f
.
watchCompletedChan
<-
true
f
.
watchCompletedChan
<-
true
...
@@ -222,8 +225,6 @@ func (f *FakeEtcdClient) Watch(prefix string, waitIndex uint64, recursive bool,
...
@@ -222,8 +225,6 @@ func (f *FakeEtcdClient) Watch(prefix string, waitIndex uint64, recursive bool,
case
<-
stop
:
case
<-
stop
:
return
nil
,
etcd
.
ErrWatchStoppedByUser
return
nil
,
etcd
.
ErrWatchStoppedByUser
case
err
:=
<-
injectedError
:
case
err
:=
<-
injectedError
:
// Emulate etcd's behavior.
close
(
receiver
)
return
nil
,
err
return
nil
,
err
}
}
// Never get here.
// Never get here.
...
...
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