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
264a658a
Commit
264a658a
authored
Aug 20, 2015
by
Saad Ali
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #12869 from vishh/daemon-registry-client
Daemon registry client
parents
cd798a47
ec22c2dd
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
1441 additions
and
3 deletions
+1441
-3
listers.go
pkg/client/unversioned/cache/listers.go
+52
-0
listers_test.go
pkg/client/unversioned/cache/listers_test.go
+123
-1
client.go
pkg/client/unversioned/client.go
+5
-0
daemon.go
pkg/client/unversioned/daemon.go
+95
-0
daemon_test.go
pkg/client/unversioned/daemon_test.go
+159
-0
doc.go
pkg/client/unversioned/doc.go
+1
-1
helper.go
pkg/client/unversioned/helper.go
+1
-1
resource_quotas_test.go
pkg/client/unversioned/resource_quotas_test.go
+4
-0
fake_daemons.go
pkg/client/unversioned/testclient/fake_daemons.go
+74
-0
testclient.go
pkg/client/unversioned/testclient/testclient.go
+4
-0
doc.go
pkg/registry/daemon/doc.go
+19
-0
etcd.go
pkg/registry/daemon/etcd/etcd.go
+76
-0
etcd_test.go
pkg/registry/daemon/etcd/etcd_test.go
+707
-0
rest.go
pkg/registry/daemon/rest.go
+121
-0
No files found.
pkg/client/unversioned/cache/listers.go
View file @
264a658a
...
...
@@ -225,6 +225,58 @@ func (s *StoreToReplicationControllerLister) GetPodControllers(pod *api.Pod) (co
return
}
// StoreToDaemonLister gives a store List and Exists methods. The store must contain only Daemons.
type
StoreToDaemonLister
struct
{
Store
}
// Exists checks if the given dc exists in the store.
func
(
s
*
StoreToDaemonLister
)
Exists
(
daemon
*
api
.
Daemon
)
(
bool
,
error
)
{
_
,
exists
,
err
:=
s
.
Store
.
Get
(
daemon
)
if
err
!=
nil
{
return
false
,
err
}
return
exists
,
nil
}
// StoreToDaemonLister lists all daemons in the store.
// TODO: converge on the interface in pkg/client
func
(
s
*
StoreToDaemonLister
)
List
()
(
daemons
[]
api
.
Daemon
,
err
error
)
{
for
_
,
c
:=
range
s
.
Store
.
List
()
{
daemons
=
append
(
daemons
,
*
(
c
.
(
*
api
.
Daemon
)))
}
return
daemons
,
nil
}
// GetPodDaemons returns a list of daemons managing a pod. Returns an error iff no matching daemons are found.
func
(
s
*
StoreToDaemonLister
)
GetPodDaemons
(
pod
*
api
.
Pod
)
(
daemons
[]
api
.
Daemon
,
err
error
)
{
var
selector
labels
.
Selector
var
daemonController
api
.
Daemon
if
len
(
pod
.
Labels
)
==
0
{
err
=
fmt
.
Errorf
(
"No daemons found for pod %v because it has no labels"
,
pod
.
Name
)
return
}
for
_
,
m
:=
range
s
.
Store
.
List
()
{
daemonController
=
*
m
.
(
*
api
.
Daemon
)
if
daemonController
.
Namespace
!=
pod
.
Namespace
{
continue
}
selector
=
labels
.
Set
(
daemonController
.
Spec
.
Selector
)
.
AsSelector
()
// If a daemonController with a nil or empty selector creeps in, it should match nothing, not everything.
if
selector
.
Empty
()
||
!
selector
.
Matches
(
labels
.
Set
(
pod
.
Labels
))
{
continue
}
daemons
=
append
(
daemons
,
daemonController
)
}
if
len
(
daemons
)
==
0
{
err
=
fmt
.
Errorf
(
"Could not find daemons for pod %s in namespace %s with labels: %v"
,
pod
.
Name
,
pod
.
Namespace
,
pod
.
Labels
)
}
return
}
// StoreToServiceLister makes a Store that has the List method of the client.ServiceInterface
// The Store must contain (only) Services.
type
StoreToServiceLister
struct
{
...
...
pkg/client/unversioned/cache/listers_test.go
View file @
264a658a
...
...
@@ -64,7 +64,7 @@ func TestStoreToReplicationControllerLister(t *testing.T) {
},
outRCNames
:
util
.
NewStringSet
(
"basic"
),
},
// No pod lab
le
s
// No pod lab
el
s
{
inRCs
:
[]
*
api
.
ReplicationController
{
{
...
...
@@ -155,6 +155,128 @@ func TestStoreToReplicationControllerLister(t *testing.T) {
}
}
func
TestStoreToDaemonLister
(
t
*
testing
.
T
)
{
store
:=
NewStore
(
MetaNamespaceKeyFunc
)
lister
:=
StoreToDaemonLister
{
store
}
testCases
:=
[]
struct
{
inDCs
[]
*
api
.
Daemon
list
func
()
([]
api
.
Daemon
,
error
)
outDCNames
util
.
StringSet
expectErr
bool
}{
// Basic listing
{
inDCs
:
[]
*
api
.
Daemon
{
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"basic"
}},
},
list
:
func
()
([]
api
.
Daemon
,
error
)
{
return
lister
.
List
()
},
outDCNames
:
util
.
NewStringSet
(
"basic"
),
},
// Listing multiple controllers
{
inDCs
:
[]
*
api
.
Daemon
{
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"basic"
}},
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"complex"
}},
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"complex2"
}},
},
list
:
func
()
([]
api
.
Daemon
,
error
)
{
return
lister
.
List
()
},
outDCNames
:
util
.
NewStringSet
(
"basic"
,
"complex"
,
"complex2"
),
},
// No pod labels
{
inDCs
:
[]
*
api
.
Daemon
{
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"basic"
,
Namespace
:
"ns"
},
Spec
:
api
.
DaemonSpec
{
Selector
:
map
[
string
]
string
{
"foo"
:
"baz"
},
},
},
},
list
:
func
()
([]
api
.
Daemon
,
error
)
{
pod
:=
&
api
.
Pod
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"pod1"
,
Namespace
:
"ns"
},
}
return
lister
.
GetPodDaemons
(
pod
)
},
outDCNames
:
util
.
NewStringSet
(),
expectErr
:
true
,
},
// No RC selectors
{
inDCs
:
[]
*
api
.
Daemon
{
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"basic"
,
Namespace
:
"ns"
},
},
},
list
:
func
()
([]
api
.
Daemon
,
error
)
{
pod
:=
&
api
.
Pod
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"pod1"
,
Namespace
:
"ns"
,
Labels
:
map
[
string
]
string
{
"foo"
:
"bar"
},
},
}
return
lister
.
GetPodDaemons
(
pod
)
},
outDCNames
:
util
.
NewStringSet
(),
expectErr
:
true
,
},
// Matching labels to selectors and namespace
{
inDCs
:
[]
*
api
.
Daemon
{
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"foo"
},
Spec
:
api
.
DaemonSpec
{
Selector
:
map
[
string
]
string
{
"foo"
:
"bar"
},
},
},
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"bar"
,
Namespace
:
"ns"
},
Spec
:
api
.
DaemonSpec
{
Selector
:
map
[
string
]
string
{
"foo"
:
"bar"
},
},
},
},
list
:
func
()
([]
api
.
Daemon
,
error
)
{
pod
:=
&
api
.
Pod
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"pod1"
,
Labels
:
map
[
string
]
string
{
"foo"
:
"bar"
},
Namespace
:
"ns"
,
},
}
return
lister
.
GetPodDaemons
(
pod
)
},
outDCNames
:
util
.
NewStringSet
(
"bar"
),
},
}
for
_
,
c
:=
range
testCases
{
for
_
,
r
:=
range
c
.
inDCs
{
store
.
Add
(
r
)
}
gotControllers
,
err
:=
c
.
list
()
if
err
!=
nil
&&
c
.
expectErr
{
continue
}
else
if
c
.
expectErr
{
t
.
Fatalf
(
"Expected error, got none"
)
}
else
if
err
!=
nil
{
t
.
Fatalf
(
"Unexpected error %#v"
,
err
)
}
gotNames
:=
make
([]
string
,
len
(
gotControllers
))
for
ix
:=
range
gotControllers
{
gotNames
[
ix
]
=
gotControllers
[
ix
]
.
Name
}
if
!
c
.
outDCNames
.
HasAll
(
gotNames
...
)
||
len
(
gotNames
)
!=
len
(
c
.
outDCNames
)
{
t
.
Errorf
(
"Unexpected got controllers %+v expected %+v"
,
gotNames
,
c
.
outDCNames
)
}
}
}
func
TestStoreToPodLister
(
t
*
testing
.
T
)
{
store
:=
NewStore
(
MetaNamespaceKeyFunc
)
ids
:=
[]
string
{
"foo"
,
"bar"
,
"baz"
}
...
...
pkg/client/unversioned/client.go
View file @
264a658a
...
...
@@ -33,6 +33,7 @@ type Interface interface {
PodsNamespacer
PodTemplatesNamespacer
ReplicationControllersNamespacer
DaemonsNamespacer
ServicesNamespacer
EndpointsNamespacer
VersionInterface
...
...
@@ -52,6 +53,10 @@ func (c *Client) ReplicationControllers(namespace string) ReplicationControllerI
return
newReplicationControllers
(
c
,
namespace
)
}
func
(
c
*
Client
)
Daemons
(
namespace
string
)
DaemonInterface
{
return
newDaemons
(
c
,
namespace
)
}
func
(
c
*
Client
)
Nodes
()
NodeInterface
{
return
newNodes
(
c
)
}
...
...
pkg/client/unversioned/daemon.go
0 → 100644
View file @
264a658a
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
unversioned
import
(
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/watch"
)
// DaemonsNamespacer has methods to work with Daemon resources in a namespace
type
DaemonsNamespacer
interface
{
Daemons
(
namespace
string
)
DaemonInterface
}
type
DaemonInterface
interface
{
List
(
selector
labels
.
Selector
)
(
*
api
.
DaemonList
,
error
)
Get
(
name
string
)
(
*
api
.
Daemon
,
error
)
Create
(
ctrl
*
api
.
Daemon
)
(
*
api
.
Daemon
,
error
)
Update
(
ctrl
*
api
.
Daemon
)
(
*
api
.
Daemon
,
error
)
Delete
(
name
string
)
error
Watch
(
label
labels
.
Selector
,
field
fields
.
Selector
,
resourceVersion
string
)
(
watch
.
Interface
,
error
)
}
// daemons implements DaemonsNamespacer interface
type
daemons
struct
{
r
*
Client
ns
string
}
func
newDaemons
(
c
*
Client
,
namespace
string
)
*
daemons
{
return
&
daemons
{
c
,
namespace
}
}
// Ensure statically that daemons implements DaemonInterface.
var
_
DaemonInterface
=
&
daemons
{}
func
(
c
*
daemons
)
List
(
selector
labels
.
Selector
)
(
result
*
api
.
DaemonList
,
err
error
)
{
result
=
&
api
.
DaemonList
{}
err
=
c
.
r
.
Get
()
.
Namespace
(
c
.
ns
)
.
Resource
(
"daemons"
)
.
LabelsSelectorParam
(
selector
)
.
Do
()
.
Into
(
result
)
return
}
// Get returns information about a particular daemon.
func
(
c
*
daemons
)
Get
(
name
string
)
(
result
*
api
.
Daemon
,
err
error
)
{
result
=
&
api
.
Daemon
{}
err
=
c
.
r
.
Get
()
.
Namespace
(
c
.
ns
)
.
Resource
(
"daemons"
)
.
Name
(
name
)
.
Do
()
.
Into
(
result
)
return
}
// Create creates a new daemon.
func
(
c
*
daemons
)
Create
(
daemon
*
api
.
Daemon
)
(
result
*
api
.
Daemon
,
err
error
)
{
result
=
&
api
.
Daemon
{}
err
=
c
.
r
.
Post
()
.
Namespace
(
c
.
ns
)
.
Resource
(
"daemons"
)
.
Body
(
daemon
)
.
Do
()
.
Into
(
result
)
return
}
// Update updates an existing daemon.
func
(
c
*
daemons
)
Update
(
daemon
*
api
.
Daemon
)
(
result
*
api
.
Daemon
,
err
error
)
{
result
=
&
api
.
Daemon
{}
err
=
c
.
r
.
Put
()
.
Namespace
(
c
.
ns
)
.
Resource
(
"daemons"
)
.
Name
(
daemon
.
Name
)
.
Body
(
daemon
)
.
Do
()
.
Into
(
result
)
return
}
// Delete deletes an existing daemon.
func
(
c
*
daemons
)
Delete
(
name
string
)
error
{
return
c
.
r
.
Delete
()
.
Namespace
(
c
.
ns
)
.
Resource
(
"daemons"
)
.
Name
(
name
)
.
Do
()
.
Error
()
}
// Watch returns a watch.Interface that watches the requested daemons.
func
(
c
*
daemons
)
Watch
(
label
labels
.
Selector
,
field
fields
.
Selector
,
resourceVersion
string
)
(
watch
.
Interface
,
error
)
{
return
c
.
r
.
Get
()
.
Prefix
(
"watch"
)
.
Namespace
(
c
.
ns
)
.
Resource
(
"daemons"
)
.
Param
(
"resourceVersion"
,
resourceVersion
)
.
LabelsSelectorParam
(
label
)
.
FieldsSelectorParam
(
field
)
.
Watch
()
}
pkg/client/unversioned/daemon_test.go
0 → 100644
View file @
264a658a
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
unversioned
import
(
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/labels"
)
func
getDCResourceName
()
string
{
return
"daemons"
}
func
TestListDaemons
(
t
*
testing
.
T
)
{
ns
:=
api
.
NamespaceAll
c
:=
&
testClient
{
Request
:
testRequest
{
Method
:
"GET"
,
Path
:
testapi
.
ResourcePath
(
getDCResourceName
(),
ns
,
""
),
},
Response
:
Response
{
StatusCode
:
200
,
Body
:
&
api
.
DaemonList
{
Items
:
[]
api
.
Daemon
{
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"foo"
,
Labels
:
map
[
string
]
string
{
"foo"
:
"bar"
,
"name"
:
"baz"
,
},
},
Spec
:
api
.
DaemonSpec
{
Template
:
&
api
.
PodTemplateSpec
{},
},
},
},
},
},
}
receivedControllerList
,
err
:=
c
.
Setup
()
.
Daemons
(
ns
)
.
List
(
labels
.
Everything
())
c
.
Validate
(
t
,
receivedControllerList
,
err
)
}
func
TestGetDaemon
(
t
*
testing
.
T
)
{
ns
:=
api
.
NamespaceDefault
c
:=
&
testClient
{
Request
:
testRequest
{
Method
:
"GET"
,
Path
:
testapi
.
ResourcePath
(
getDCResourceName
(),
ns
,
"foo"
),
Query
:
buildQueryValues
(
nil
)},
Response
:
Response
{
StatusCode
:
200
,
Body
:
&
api
.
Daemon
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"foo"
,
Labels
:
map
[
string
]
string
{
"foo"
:
"bar"
,
"name"
:
"baz"
,
},
},
Spec
:
api
.
DaemonSpec
{
Template
:
&
api
.
PodTemplateSpec
{},
},
},
},
}
receivedController
,
err
:=
c
.
Setup
()
.
Daemons
(
ns
)
.
Get
(
"foo"
)
c
.
Validate
(
t
,
receivedController
,
err
)
}
func
TestGetDaemonWithNoName
(
t
*
testing
.
T
)
{
ns
:=
api
.
NamespaceDefault
c
:=
&
testClient
{
Error
:
true
}
receivedPod
,
err
:=
c
.
Setup
()
.
Daemons
(
ns
)
.
Get
(
""
)
if
(
err
!=
nil
)
&&
(
err
.
Error
()
!=
nameRequiredError
)
{
t
.
Errorf
(
"Expected error: %v, but got %v"
,
nameRequiredError
,
err
)
}
c
.
Validate
(
t
,
receivedPod
,
err
)
}
func
TestUpdateDaemon
(
t
*
testing
.
T
)
{
ns
:=
api
.
NamespaceDefault
requestController
:=
&
api
.
Daemon
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"foo"
,
ResourceVersion
:
"1"
},
}
c
:=
&
testClient
{
Request
:
testRequest
{
Method
:
"PUT"
,
Path
:
testapi
.
ResourcePath
(
getDCResourceName
(),
ns
,
"foo"
),
Query
:
buildQueryValues
(
nil
)},
Response
:
Response
{
StatusCode
:
200
,
Body
:
&
api
.
Daemon
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"foo"
,
Labels
:
map
[
string
]
string
{
"foo"
:
"bar"
,
"name"
:
"baz"
,
},
},
Spec
:
api
.
DaemonSpec
{
Template
:
&
api
.
PodTemplateSpec
{},
},
},
},
}
receivedController
,
err
:=
c
.
Setup
()
.
Daemons
(
ns
)
.
Update
(
requestController
)
c
.
Validate
(
t
,
receivedController
,
err
)
}
func
TestDeleteDaemon
(
t
*
testing
.
T
)
{
ns
:=
api
.
NamespaceDefault
c
:=
&
testClient
{
Request
:
testRequest
{
Method
:
"DELETE"
,
Path
:
testapi
.
ResourcePath
(
getDCResourceName
(),
ns
,
"foo"
),
Query
:
buildQueryValues
(
nil
)},
Response
:
Response
{
StatusCode
:
200
},
}
err
:=
c
.
Setup
()
.
Daemons
(
ns
)
.
Delete
(
"foo"
)
c
.
Validate
(
t
,
nil
,
err
)
}
func
TestCreateDaemon
(
t
*
testing
.
T
)
{
ns
:=
api
.
NamespaceDefault
requestController
:=
&
api
.
Daemon
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"foo"
},
}
c
:=
&
testClient
{
Request
:
testRequest
{
Method
:
"POST"
,
Path
:
testapi
.
ResourcePath
(
getDCResourceName
(),
ns
,
""
),
Body
:
requestController
,
Query
:
buildQueryValues
(
nil
)},
Response
:
Response
{
StatusCode
:
200
,
Body
:
&
api
.
Daemon
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"foo"
,
Labels
:
map
[
string
]
string
{
"foo"
:
"bar"
,
"name"
:
"baz"
,
},
},
Spec
:
api
.
DaemonSpec
{
Template
:
&
api
.
PodTemplateSpec
{},
},
},
},
}
receivedController
,
err
:=
c
.
Setup
()
.
Daemons
(
ns
)
.
Create
(
requestController
)
c
.
Validate
(
t
,
receivedController
,
err
)
}
pkg/client/unversioned/doc.go
View file @
264a658a
...
...
@@ -17,7 +17,7 @@ limitations under the License.
/*
Package client contains the implementation of the client side communication with the
Kubernetes master. The Client class provides methods for reading, creating, updating,
and deleting pods, replication controllers, services, and minions.
and deleting pods, replication controllers,
daemons,
services, and minions.
Most consumers should use the Config object to create a Client:
...
...
pkg/client/unversioned/helper.go
View file @
264a658a
...
...
@@ -127,7 +127,7 @@ type TLSClientConfig struct {
}
// New creates a Kubernetes client for the given config. This client works with pods,
// replication controllers and services. It allows operations such as list, get, update
// replication controllers
, daemons,
and services. It allows operations such as list, get, update
// and delete on these objects. An error is returned if the provided configuration
// is not valid.
func
New
(
c
*
Config
)
(
*
Client
,
error
)
{
...
...
pkg/client/unversioned/resource_quotas_test.go
View file @
264a658a
...
...
@@ -45,6 +45,7 @@ func TestResourceQuotaCreate(t *testing.T) {
api
.
ResourcePods
:
resource
.
MustParse
(
"10"
),
api
.
ResourceServices
:
resource
.
MustParse
(
"10"
),
api
.
ResourceReplicationControllers
:
resource
.
MustParse
(
"10"
),
api
.
ResourceDaemon
:
resource
.
MustParse
(
"10"
),
api
.
ResourceQuotas
:
resource
.
MustParse
(
"10"
),
},
},
...
...
@@ -77,6 +78,7 @@ func TestResourceQuotaGet(t *testing.T) {
api
.
ResourcePods
:
resource
.
MustParse
(
"10"
),
api
.
ResourceServices
:
resource
.
MustParse
(
"10"
),
api
.
ResourceReplicationControllers
:
resource
.
MustParse
(
"10"
),
api
.
ResourceDaemon
:
resource
.
MustParse
(
"10"
),
api
.
ResourceQuotas
:
resource
.
MustParse
(
"10"
),
},
},
...
...
@@ -133,6 +135,7 @@ func TestResourceQuotaUpdate(t *testing.T) {
api
.
ResourcePods
:
resource
.
MustParse
(
"10"
),
api
.
ResourceServices
:
resource
.
MustParse
(
"10"
),
api
.
ResourceReplicationControllers
:
resource
.
MustParse
(
"10"
),
api
.
ResourceDaemon
:
resource
.
MustParse
(
"10"
),
api
.
ResourceQuotas
:
resource
.
MustParse
(
"10"
),
},
},
...
...
@@ -160,6 +163,7 @@ func TestResourceQuotaStatusUpdate(t *testing.T) {
api
.
ResourcePods
:
resource
.
MustParse
(
"10"
),
api
.
ResourceServices
:
resource
.
MustParse
(
"10"
),
api
.
ResourceReplicationControllers
:
resource
.
MustParse
(
"10"
),
api
.
ResourceDaemon
:
resource
.
MustParse
(
"10"
),
api
.
ResourceQuotas
:
resource
.
MustParse
(
"10"
),
},
},
...
...
pkg/client/unversioned/testclient/fake_daemons.go
0 → 100644
View file @
264a658a
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
testclient
import
(
"k8s.io/kubernetes/pkg/api"
kClientLib
"k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/watch"
)
// FakeDaemons implements DaemonInterface. Meant to be embedded into a struct to get a default
// implementation. This makes faking out just the method you want to test easier.
type
FakeDaemons
struct
{
Fake
*
Fake
Namespace
string
}
const
(
GetDaemonAction
=
"get-daemon"
UpdateDaemonAction
=
"update-daemon"
WatchDaemonAction
=
"watch-daemon"
DeleteDaemonAction
=
"delete-daemon"
ListDaemonAction
=
"list-daemons"
CreateDaemonAction
=
"create-daemon"
)
// Ensure statically that FakeDaemons implements DaemonInterface.
var
_
kClientLib
.
DaemonInterface
=
&
FakeDaemons
{}
func
(
c
*
FakeDaemons
)
Get
(
name
string
)
(
*
api
.
Daemon
,
error
)
{
obj
,
err
:=
c
.
Fake
.
Invokes
(
NewGetAction
(
"daemons"
,
c
.
Namespace
,
name
),
&
api
.
Daemon
{})
return
obj
.
(
*
api
.
Daemon
),
err
}
func
(
c
*
FakeDaemons
)
List
(
label
labels
.
Selector
)
(
*
api
.
DaemonList
,
error
)
{
obj
,
err
:=
c
.
Fake
.
Invokes
(
NewListAction
(
"daemons"
,
c
.
Namespace
,
label
,
nil
),
&
api
.
DaemonList
{})
return
obj
.
(
*
api
.
DaemonList
),
err
}
func
(
c
*
FakeDaemons
)
Create
(
daemon
*
api
.
Daemon
)
(
*
api
.
Daemon
,
error
)
{
obj
,
err
:=
c
.
Fake
.
Invokes
(
NewCreateAction
(
"daemons"
,
c
.
Namespace
,
daemon
),
&
api
.
Daemon
{})
return
obj
.
(
*
api
.
Daemon
),
err
}
func
(
c
*
FakeDaemons
)
Update
(
daemon
*
api
.
Daemon
)
(
*
api
.
Daemon
,
error
)
{
obj
,
err
:=
c
.
Fake
.
Invokes
(
NewUpdateAction
(
"daemons"
,
c
.
Namespace
,
daemon
),
&
api
.
Daemon
{})
return
obj
.
(
*
api
.
Daemon
),
err
}
func
(
c
*
FakeDaemons
)
Delete
(
name
string
)
error
{
_
,
err
:=
c
.
Fake
.
Invokes
(
NewDeleteAction
(
"daemons"
,
c
.
Namespace
,
name
),
&
api
.
Daemon
{})
return
err
}
func
(
c
*
FakeDaemons
)
Watch
(
label
labels
.
Selector
,
field
fields
.
Selector
,
resourceVersion
string
)
(
watch
.
Interface
,
error
)
{
c
.
Fake
.
Invokes
(
NewWatchAction
(
"daemons"
,
c
.
Namespace
,
label
,
field
,
resourceVersion
),
nil
)
return
c
.
Fake
.
Watch
,
nil
}
pkg/client/unversioned/testclient/testclient.go
View file @
264a658a
...
...
@@ -114,6 +114,10 @@ func (c *Fake) ReplicationControllers(namespace string) client.ReplicationContro
return
&
FakeReplicationControllers
{
Fake
:
c
,
Namespace
:
namespace
}
}
func
(
c
*
Fake
)
Daemons
(
namespace
string
)
client
.
DaemonInterface
{
return
&
FakeDaemons
{
Fake
:
c
,
Namespace
:
namespace
}
}
func
(
c
*
Fake
)
Nodes
()
client
.
NodeInterface
{
return
&
FakeNodes
{
Fake
:
c
}
}
...
...
pkg/registry/daemon/doc.go
0 → 100644
View file @
264a658a
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package daemon provides Registry interface and its RESTStorage
// implementation for storing Daemon api objects.
package
daemon
pkg/registry/daemon/etcd/etcd.go
0 → 100644
View file @
264a658a
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
etcd
import
(
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/daemon"
"k8s.io/kubernetes/pkg/registry/generic"
etcdgeneric
"k8s.io/kubernetes/pkg/registry/generic/etcd"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage"
)
// rest implements a RESTStorage for daemons against etcd
type
REST
struct
{
*
etcdgeneric
.
Etcd
}
// daemonPrefix is the location for daemons in etcd, only exposed
// for testing
var
daemonPrefix
=
"/daemons"
// NewREST returns a RESTStorage object that will work against daemons.
func
NewREST
(
s
storage
.
Interface
)
*
REST
{
store
:=
&
etcdgeneric
.
Etcd
{
NewFunc
:
func
()
runtime
.
Object
{
return
&
api
.
Daemon
{}
},
// NewListFunc returns an object capable of storing results of an etcd list.
NewListFunc
:
func
()
runtime
.
Object
{
return
&
api
.
DaemonList
{}
},
// Produces a path that etcd understands, to the root of the resource
// by combining the namespace in the context with the given prefix
KeyRootFunc
:
func
(
ctx
api
.
Context
)
string
{
return
etcdgeneric
.
NamespaceKeyRootFunc
(
ctx
,
daemonPrefix
)
},
// Produces a path that etcd understands, to the resource by combining
// the namespace in the context with the given prefix
KeyFunc
:
func
(
ctx
api
.
Context
,
name
string
)
(
string
,
error
)
{
return
etcdgeneric
.
NamespaceKeyFunc
(
ctx
,
daemonPrefix
,
name
)
},
// Retrieve the name field of a daemon
ObjectNameFunc
:
func
(
obj
runtime
.
Object
)
(
string
,
error
)
{
return
obj
.
(
*
api
.
Daemon
)
.
Name
,
nil
},
// Used to match objects based on labels/fields for list and watch
PredicateFunc
:
func
(
label
labels
.
Selector
,
field
fields
.
Selector
)
generic
.
Matcher
{
return
daemon
.
MatchDaemon
(
label
,
field
)
},
EndpointName
:
"daemons"
,
// Used to validate daemon creation
CreateStrategy
:
daemon
.
Strategy
,
// Used to validate daemon updates
UpdateStrategy
:
daemon
.
Strategy
,
Storage
:
s
,
}
return
&
REST
{
store
}
}
pkg/registry/daemon/etcd/etcd_test.go
0 → 100755
View file @
264a658a
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
etcd
import
(
"strings"
"testing"
"time"
"github.com/coreos/go-etcd/etcd"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/latest"
"k8s.io/kubernetes/pkg/api/rest/resttest"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
etcdgeneric
"k8s.io/kubernetes/pkg/registry/generic/etcd"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage"
etcdstorage
"k8s.io/kubernetes/pkg/storage/etcd"
"k8s.io/kubernetes/pkg/tools"
"k8s.io/kubernetes/pkg/tools/etcdtest"
)
const
(
PASS
=
iota
FAIL
)
func
newEtcdStorage
(
t
*
testing
.
T
)
(
*
tools
.
FakeEtcdClient
,
storage
.
Interface
)
{
fakeEtcdClient
:=
tools
.
NewFakeEtcdClient
(
t
)
fakeEtcdClient
.
TestIndex
=
true
helper
:=
etcdstorage
.
NewEtcdStorage
(
fakeEtcdClient
,
latest
.
Codec
,
etcdtest
.
PathPrefix
())
return
fakeEtcdClient
,
helper
}
// newStorage creates a REST storage backed by etcd helpers
func
newStorage
(
t
*
testing
.
T
)
(
*
REST
,
*
tools
.
FakeEtcdClient
)
{
fakeEtcdClient
,
h
:=
newEtcdStorage
(
t
)
storage
:=
NewREST
(
h
)
return
storage
,
fakeEtcdClient
}
// createController is a helper function that returns a controller with the updated resource version.
func
createController
(
storage
*
REST
,
dc
api
.
Daemon
,
t
*
testing
.
T
)
(
api
.
Daemon
,
error
)
{
ctx
:=
api
.
WithNamespace
(
api
.
NewContext
(),
dc
.
Namespace
)
obj
,
err
:=
storage
.
Create
(
ctx
,
&
dc
)
if
err
!=
nil
{
t
.
Errorf
(
"Failed to create controller, %v"
,
err
)
}
newDc
:=
obj
.
(
*
api
.
Daemon
)
return
*
newDc
,
nil
}
var
validPodTemplate
=
api
.
PodTemplate
{
Template
:
api
.
PodTemplateSpec
{
ObjectMeta
:
api
.
ObjectMeta
{
Labels
:
map
[
string
]
string
{
"a"
:
"b"
},
},
Spec
:
api
.
PodSpec
{
Containers
:
[]
api
.
Container
{
{
Name
:
"test"
,
Image
:
"test_image"
,
ImagePullPolicy
:
api
.
PullIfNotPresent
,
},
},
RestartPolicy
:
api
.
RestartPolicyAlways
,
DNSPolicy
:
api
.
DNSClusterFirst
,
},
},
}
var
validControllerSpec
=
api
.
DaemonSpec
{
Selector
:
validPodTemplate
.
Template
.
Labels
,
Template
:
&
validPodTemplate
.
Template
,
}
var
validController
=
api
.
Daemon
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"foo"
,
Namespace
:
"default"
},
Spec
:
validControllerSpec
,
}
// makeControllerKey constructs etcd paths to controller items enforcing namespace rules.
func
makeControllerKey
(
ctx
api
.
Context
,
id
string
)
(
string
,
error
)
{
return
etcdgeneric
.
NamespaceKeyFunc
(
ctx
,
daemonPrefix
,
id
)
}
// makeControllerListKey constructs etcd paths to the root of the resource,
// not a specific controller resource
func
makeControllerListKey
(
ctx
api
.
Context
)
string
{
return
etcdgeneric
.
NamespaceKeyRootFunc
(
ctx
,
daemonPrefix
)
}
func
TestEtcdCreateController
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
fakeClient
:=
newStorage
(
t
)
_
,
err
:=
storage
.
Create
(
ctx
,
&
validController
)
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
key
,
_
:=
makeControllerKey
(
ctx
,
validController
.
Name
)
key
=
etcdtest
.
AddPrefix
(
key
)
resp
,
err
:=
fakeClient
.
Get
(
key
,
false
,
false
)
if
err
!=
nil
{
t
.
Fatalf
(
"Unexpected error %v"
,
err
)
}
var
ctrl
api
.
Daemon
err
=
latest
.
Codec
.
DecodeInto
([]
byte
(
resp
.
Node
.
Value
),
&
ctrl
)
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
if
ctrl
.
Name
!=
"foo"
{
t
.
Errorf
(
"Unexpected controller: %#v %s"
,
ctrl
,
resp
.
Node
.
Value
)
}
}
func
TestEtcdCreateControllerAlreadyExisting
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
fakeClient
:=
newStorage
(
t
)
key
,
_
:=
makeControllerKey
(
ctx
,
validController
.
Name
)
key
=
etcdtest
.
AddPrefix
(
key
)
fakeClient
.
Set
(
key
,
runtime
.
EncodeOrDie
(
latest
.
Codec
,
&
validController
),
0
)
_
,
err
:=
storage
.
Create
(
ctx
,
&
validController
)
if
!
errors
.
IsAlreadyExists
(
err
)
{
t
.
Errorf
(
"expected already exists err, got %#v"
,
err
)
}
}
func
TestEtcdCreateControllerValidates
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
_
:=
newStorage
(
t
)
emptyName
:=
validController
emptyName
.
Name
=
""
failureCases
:=
[]
api
.
Daemon
{
emptyName
}
for
_
,
failureCase
:=
range
failureCases
{
c
,
err
:=
storage
.
Create
(
ctx
,
&
failureCase
)
if
c
!=
nil
{
t
.
Errorf
(
"Expected nil channel"
)
}
if
!
errors
.
IsInvalid
(
err
)
{
t
.
Errorf
(
"Expected to get an invalid resource error, got %v"
,
err
)
}
}
}
func
TestCreateControllerWithGeneratedName
(
t
*
testing
.
T
)
{
storage
,
_
:=
newStorage
(
t
)
controller
:=
&
api
.
Daemon
{
ObjectMeta
:
api
.
ObjectMeta
{
Namespace
:
api
.
NamespaceDefault
,
GenerateName
:
"daemon-"
,
},
Spec
:
api
.
DaemonSpec
{
Selector
:
map
[
string
]
string
{
"a"
:
"b"
},
Template
:
&
validPodTemplate
.
Template
,
},
}
ctx
:=
api
.
NewDefaultContext
()
_
,
err
:=
storage
.
Create
(
ctx
,
controller
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
controller
.
Name
==
"daemon-"
||
!
strings
.
HasPrefix
(
controller
.
Name
,
"daemon-"
)
{
t
.
Errorf
(
"unexpected name: %#v"
,
controller
)
}
}
func
TestCreateControllerWithConflictingNamespace
(
t
*
testing
.
T
)
{
storage
,
_
:=
newStorage
(
t
)
controller
:=
&
api
.
Daemon
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"test"
,
Namespace
:
"not-default"
},
}
ctx
:=
api
.
NewDefaultContext
()
channel
,
err
:=
storage
.
Create
(
ctx
,
controller
)
if
channel
!=
nil
{
t
.
Error
(
"Expected a nil channel, but we got a value"
)
}
errSubString
:=
"namespace"
if
err
==
nil
{
t
.
Errorf
(
"Expected an error, but we didn't get one"
)
}
else
if
!
errors
.
IsBadRequest
(
err
)
||
strings
.
Index
(
err
.
Error
(),
errSubString
)
==
-
1
{
t
.
Errorf
(
"Expected a Bad Request error with the sub string '%s', got %v"
,
errSubString
,
err
)
}
}
func
TestEtcdGetController
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
fakeClient
:=
newStorage
(
t
)
key
,
_
:=
makeControllerKey
(
ctx
,
validController
.
Name
)
key
=
etcdtest
.
AddPrefix
(
key
)
fakeClient
.
Set
(
key
,
runtime
.
EncodeOrDie
(
latest
.
Codec
,
&
validController
),
0
)
ctrl
,
err
:=
storage
.
Get
(
ctx
,
validController
.
Name
)
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
controller
,
ok
:=
ctrl
.
(
*
api
.
Daemon
)
if
!
ok
{
t
.
Errorf
(
"Expected a controller, got %#v"
,
ctrl
)
}
if
controller
.
Name
!=
validController
.
Name
{
t
.
Errorf
(
"Unexpected controller: %#v"
,
controller
)
}
}
func
TestEtcdControllerValidatesUpdate
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
_
:=
newStorage
(
t
)
updateController
,
err
:=
createController
(
storage
,
validController
,
t
)
if
err
!=
nil
{
t
.
Errorf
(
"Failed to create controller, cannot proceed with test."
)
}
updaters
:=
[]
func
(
dc
api
.
Daemon
)
(
runtime
.
Object
,
bool
,
error
){
func
(
dc
api
.
Daemon
)
(
runtime
.
Object
,
bool
,
error
)
{
dc
.
UID
=
"newUID"
return
storage
.
Update
(
ctx
,
&
dc
)
},
func
(
dc
api
.
Daemon
)
(
runtime
.
Object
,
bool
,
error
)
{
dc
.
Name
=
""
return
storage
.
Update
(
ctx
,
&
dc
)
},
func
(
dc
api
.
Daemon
)
(
runtime
.
Object
,
bool
,
error
)
{
dc
.
Spec
.
Template
.
Spec
.
RestartPolicy
=
api
.
RestartPolicyOnFailure
return
storage
.
Update
(
ctx
,
&
dc
)
},
func
(
dc
api
.
Daemon
)
(
runtime
.
Object
,
bool
,
error
)
{
dc
.
Spec
.
Selector
=
map
[
string
]
string
{}
return
storage
.
Update
(
ctx
,
&
dc
)
},
}
for
_
,
u
:=
range
updaters
{
c
,
updated
,
err
:=
u
(
updateController
)
if
c
!=
nil
||
updated
{
t
.
Errorf
(
"Expected nil object and not created"
)
}
if
!
errors
.
IsInvalid
(
err
)
&&
!
errors
.
IsBadRequest
(
err
)
{
t
.
Errorf
(
"Expected invalid or bad request error, got %v of type %T"
,
err
,
err
)
}
}
}
func
TestEtcdControllerValidatesNamespaceOnUpdate
(
t
*
testing
.
T
)
{
storage
,
_
:=
newStorage
(
t
)
ns
:=
"newnamespace"
// The update should fail if the namespace on the controller is set to something
// other than the namespace on the given context, even if the namespace on the
// controller is valid.
updateController
,
err
:=
createController
(
storage
,
validController
,
t
)
newNamespaceController
:=
validController
newNamespaceController
.
Namespace
=
ns
_
,
err
=
createController
(
storage
,
newNamespaceController
,
t
)
c
,
updated
,
err
:=
storage
.
Update
(
api
.
WithNamespace
(
api
.
NewContext
(),
ns
),
&
updateController
)
if
c
!=
nil
||
updated
{
t
.
Errorf
(
"Expected nil object and not created"
)
}
// TODO: Be more paranoid about the type of error and make sure it has the substring
// "namespace" in it, once #5684 is fixed. Ideally this would be a NewBadRequest.
if
err
==
nil
{
t
.
Errorf
(
"Expected an error, but we didn't get one"
)
}
}
// TestEtcdGetControllerDifferentNamespace ensures same-name controllers in different namespaces do not clash
func
TestEtcdGetControllerDifferentNamespace
(
t
*
testing
.
T
)
{
storage
,
fakeClient
:=
newStorage
(
t
)
otherNs
:=
"other"
ctx1
:=
api
.
NewDefaultContext
()
ctx2
:=
api
.
WithNamespace
(
api
.
NewContext
(),
otherNs
)
key1
,
_
:=
makeControllerKey
(
ctx1
,
validController
.
Name
)
key2
,
_
:=
makeControllerKey
(
ctx2
,
validController
.
Name
)
key1
=
etcdtest
.
AddPrefix
(
key1
)
key2
=
etcdtest
.
AddPrefix
(
key2
)
fakeClient
.
Set
(
key1
,
runtime
.
EncodeOrDie
(
latest
.
Codec
,
&
validController
),
0
)
otherNsController
:=
validController
otherNsController
.
Namespace
=
otherNs
fakeClient
.
Set
(
key2
,
runtime
.
EncodeOrDie
(
latest
.
Codec
,
&
otherNsController
),
0
)
obj
,
err
:=
storage
.
Get
(
ctx1
,
validController
.
Name
)
ctrl1
,
_
:=
obj
.
(
*
api
.
Daemon
)
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
if
ctrl1
.
Name
!=
"foo"
{
t
.
Errorf
(
"Unexpected controller: %#v"
,
ctrl1
)
}
if
ctrl1
.
Namespace
!=
"default"
{
t
.
Errorf
(
"Unexpected controller: %#v"
,
ctrl1
)
}
obj
,
err
=
storage
.
Get
(
ctx2
,
validController
.
Name
)
ctrl2
,
_
:=
obj
.
(
*
api
.
Daemon
)
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
if
ctrl2
.
Name
!=
"foo"
{
t
.
Errorf
(
"Unexpected controller: %#v"
,
ctrl2
)
}
if
ctrl2
.
Namespace
!=
"other"
{
t
.
Errorf
(
"Unexpected controller: %#v"
,
ctrl2
)
}
}
func
TestEtcdGetControllerNotFound
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
fakeClient
:=
newStorage
(
t
)
key
,
_
:=
makeControllerKey
(
ctx
,
validController
.
Name
)
key
=
etcdtest
.
AddPrefix
(
key
)
fakeClient
.
Data
[
key
]
=
tools
.
EtcdResponseWithError
{
R
:
&
etcd
.
Response
{
Node
:
nil
,
},
E
:
tools
.
EtcdErrorNotFound
,
}
ctrl
,
err
:=
storage
.
Get
(
ctx
,
validController
.
Name
)
if
ctrl
!=
nil
{
t
.
Errorf
(
"Unexpected non-nil controller: %#v"
,
ctrl
)
}
if
!
errors
.
IsNotFound
(
err
)
{
t
.
Errorf
(
"Unexpected error returned: %#v"
,
err
)
}
}
func
TestEtcdDeleteController
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
fakeClient
:=
newStorage
(
t
)
key
,
_
:=
makeControllerKey
(
ctx
,
validController
.
Name
)
key
=
etcdtest
.
AddPrefix
(
key
)
fakeClient
.
Set
(
key
,
runtime
.
EncodeOrDie
(
latest
.
Codec
,
&
validController
),
0
)
obj
,
err
:=
storage
.
Delete
(
ctx
,
validController
.
Name
,
nil
)
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
if
status
,
ok
:=
obj
.
(
*
api
.
Status
);
!
ok
{
t
.
Errorf
(
"Expected status of delete, got %#v"
,
status
)
}
else
if
status
.
Status
!=
api
.
StatusSuccess
{
t
.
Errorf
(
"Expected success, got %#v"
,
status
.
Status
)
}
if
len
(
fakeClient
.
DeletedKeys
)
!=
1
{
t
.
Errorf
(
"Expected 1 delete, found %#v"
,
fakeClient
.
DeletedKeys
)
}
if
fakeClient
.
DeletedKeys
[
0
]
!=
key
{
t
.
Errorf
(
"Unexpected key: %s, expected %s"
,
fakeClient
.
DeletedKeys
[
0
],
key
)
}
}
func
TestEtcdListControllers
(
t
*
testing
.
T
)
{
storage
,
fakeClient
:=
newStorage
(
t
)
ctx
:=
api
.
NewDefaultContext
()
key
:=
makeControllerListKey
(
ctx
)
key
=
etcdtest
.
AddPrefix
(
key
)
controller
:=
validController
controller
.
Name
=
"bar"
fakeClient
.
Data
[
key
]
=
tools
.
EtcdResponseWithError
{
R
:
&
etcd
.
Response
{
Node
:
&
etcd
.
Node
{
Nodes
:
[]
*
etcd
.
Node
{
{
Value
:
runtime
.
EncodeOrDie
(
latest
.
Codec
,
&
validController
),
},
{
Value
:
runtime
.
EncodeOrDie
(
latest
.
Codec
,
&
controller
),
},
},
},
},
E
:
nil
,
}
objList
,
err
:=
storage
.
List
(
ctx
,
labels
.
Everything
(),
fields
.
Everything
())
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
controllers
,
_
:=
objList
.
(
*
api
.
DaemonList
)
if
len
(
controllers
.
Items
)
!=
2
||
controllers
.
Items
[
0
]
.
Name
!=
validController
.
Name
||
controllers
.
Items
[
1
]
.
Name
!=
controller
.
Name
{
t
.
Errorf
(
"Unexpected controller list: %#v"
,
controllers
)
}
}
func
TestEtcdListControllersNotFound
(
t
*
testing
.
T
)
{
storage
,
fakeClient
:=
newStorage
(
t
)
ctx
:=
api
.
NewDefaultContext
()
key
:=
makeControllerListKey
(
ctx
)
key
=
etcdtest
.
AddPrefix
(
key
)
fakeClient
.
Data
[
key
]
=
tools
.
EtcdResponseWithError
{
R
:
&
etcd
.
Response
{},
E
:
tools
.
EtcdErrorNotFound
,
}
objList
,
err
:=
storage
.
List
(
ctx
,
labels
.
Everything
(),
fields
.
Everything
())
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
controllers
,
_
:=
objList
.
(
*
api
.
DaemonList
)
if
len
(
controllers
.
Items
)
!=
0
{
t
.
Errorf
(
"Unexpected controller list: %#v"
,
controllers
)
}
}
func
TestEtcdListControllersLabelsMatch
(
t
*
testing
.
T
)
{
storage
,
fakeClient
:=
newStorage
(
t
)
ctx
:=
api
.
NewDefaultContext
()
key
:=
makeControllerListKey
(
ctx
)
key
=
etcdtest
.
AddPrefix
(
key
)
controller
:=
validController
controller
.
Labels
=
map
[
string
]
string
{
"k"
:
"v"
}
controller
.
Name
=
"bar"
fakeClient
.
Data
[
key
]
=
tools
.
EtcdResponseWithError
{
R
:
&
etcd
.
Response
{
Node
:
&
etcd
.
Node
{
Nodes
:
[]
*
etcd
.
Node
{
{
Value
:
runtime
.
EncodeOrDie
(
latest
.
Codec
,
&
validController
),
},
{
Value
:
runtime
.
EncodeOrDie
(
latest
.
Codec
,
&
controller
),
},
},
},
},
E
:
nil
,
}
testLabels
:=
labels
.
SelectorFromSet
(
labels
.
Set
(
controller
.
Labels
))
objList
,
err
:=
storage
.
List
(
ctx
,
testLabels
,
fields
.
Everything
())
if
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
controllers
,
_
:=
objList
.
(
*
api
.
DaemonList
)
if
len
(
controllers
.
Items
)
!=
1
||
controllers
.
Items
[
0
]
.
Name
!=
controller
.
Name
||
!
testLabels
.
Matches
(
labels
.
Set
(
controllers
.
Items
[
0
]
.
Labels
))
{
t
.
Errorf
(
"Unexpected controller list: %#v for query with labels %#v"
,
controllers
,
testLabels
)
}
}
func
TestEtcdWatchController
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
fakeClient
:=
newStorage
(
t
)
watching
,
err
:=
storage
.
Watch
(
ctx
,
labels
.
Everything
(),
fields
.
Everything
(),
"1"
,
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
fakeClient
.
WaitForWatchCompletion
()
select
{
case
_
,
ok
:=
<-
watching
.
ResultChan
()
:
if
!
ok
{
t
.
Errorf
(
"watching channel should be open"
)
}
default
:
}
fakeClient
.
WatchInjectError
<-
nil
if
_
,
ok
:=
<-
watching
.
ResultChan
();
ok
{
t
.
Errorf
(
"watching channel should be closed"
)
}
watching
.
Stop
()
}
// Tests that we can watch for the creation of daemon controllers with specified labels.
func
TestEtcdWatchControllersMatch
(
t
*
testing
.
T
)
{
ctx
:=
api
.
WithNamespace
(
api
.
NewDefaultContext
(),
validController
.
Namespace
)
storage
,
fakeClient
:=
newStorage
(
t
)
fakeClient
.
ExpectNotFoundGet
(
etcdgeneric
.
NamespaceKeyRootFunc
(
ctx
,
"/registry/pods"
))
watching
,
err
:=
storage
.
Watch
(
ctx
,
labels
.
SelectorFromSet
(
validController
.
Spec
.
Selector
),
fields
.
Everything
(),
"1"
,
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
fakeClient
.
WaitForWatchCompletion
()
// The watcher above is waiting for these Labels, on receiving them it should
// apply the ControllerStatus decorator, which lists pods, causing a query against
// the /registry/pods endpoint of the etcd client.
controller
:=
&
api
.
Daemon
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"foo"
,
Labels
:
validController
.
Spec
.
Selector
,
Namespace
:
"default"
,
},
}
controllerBytes
,
_
:=
latest
.
Codec
.
Encode
(
controller
)
fakeClient
.
WatchResponse
<-
&
etcd
.
Response
{
Action
:
"create"
,
Node
:
&
etcd
.
Node
{
Value
:
string
(
controllerBytes
),
},
}
select
{
case
_
,
ok
:=
<-
watching
.
ResultChan
()
:
if
!
ok
{
t
.
Errorf
(
"watching channel should be open"
)
}
case
<-
time
.
After
(
time
.
Millisecond
*
100
)
:
t
.
Error
(
"unexpected timeout from result channel"
)
}
watching
.
Stop
()
}
// Tests that we can watch for daemon controllers with specified fields.
func
TestEtcdWatchControllersFields
(
t
*
testing
.
T
)
{
ctx
:=
api
.
WithNamespace
(
api
.
NewDefaultContext
(),
validController
.
Namespace
)
storage
,
fakeClient
:=
newStorage
(
t
)
fakeClient
.
ExpectNotFoundGet
(
etcdgeneric
.
NamespaceKeyRootFunc
(
ctx
,
"/registry/pods"
))
testFieldMap
:=
map
[
int
][]
fields
.
Set
{
PASS
:
{
{
"metadata.name"
:
"foo"
},
},
FAIL
:
{
{
"metadata.name"
:
"bar"
},
{
"name"
:
"foo"
},
},
}
testEtcdActions
:=
[]
string
{
etcdstorage
.
EtcdCreate
,
etcdstorage
.
EtcdSet
,
etcdstorage
.
EtcdCAS
,
etcdstorage
.
EtcdDelete
}
controller
:=
&
api
.
Daemon
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"foo"
,
Labels
:
validController
.
Spec
.
Selector
,
Namespace
:
"default"
,
},
Status
:
api
.
DaemonStatus
{
CurrentNumberScheduled
:
2
,
NumberMisscheduled
:
1
,
DesiredNumberScheduled
:
4
,
},
}
controllerBytes
,
_
:=
latest
.
Codec
.
Encode
(
controller
)
for
expectedResult
,
fieldSet
:=
range
testFieldMap
{
for
_
,
field
:=
range
fieldSet
{
for
_
,
action
:=
range
testEtcdActions
{
watching
,
err
:=
storage
.
Watch
(
ctx
,
labels
.
Everything
(),
field
.
AsSelector
(),
"1"
,
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
var
prevNode
*
etcd
.
Node
=
nil
node
:=
&
etcd
.
Node
{
Value
:
string
(
controllerBytes
),
}
if
action
==
etcdstorage
.
EtcdDelete
{
prevNode
=
node
}
fakeClient
.
WaitForWatchCompletion
()
fakeClient
.
WatchResponse
<-
&
etcd
.
Response
{
Action
:
action
,
Node
:
node
,
PrevNode
:
prevNode
,
}
select
{
case
r
,
ok
:=
<-
watching
.
ResultChan
()
:
if
expectedResult
==
FAIL
{
t
.
Errorf
(
"Unexpected result from channel %#v"
,
r
)
}
if
!
ok
{
t
.
Errorf
(
"watching channel should be open"
)
}
case
<-
time
.
After
(
time
.
Millisecond
*
100
)
:
if
expectedResult
==
PASS
{
t
.
Error
(
"unexpected timeout from result channel"
)
}
}
watching
.
Stop
()
}
}
}
}
func
TestEtcdWatchControllersNotMatch
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
fakeClient
:=
newStorage
(
t
)
fakeClient
.
ExpectNotFoundGet
(
etcdgeneric
.
NamespaceKeyRootFunc
(
ctx
,
"/registry/pods"
))
watching
,
err
:=
storage
.
Watch
(
ctx
,
labels
.
SelectorFromSet
(
labels
.
Set
{
"name"
:
"foo"
}),
fields
.
Everything
(),
"1"
,
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
fakeClient
.
WaitForWatchCompletion
()
controller
:=
&
api
.
Daemon
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"bar"
,
Labels
:
map
[
string
]
string
{
"name"
:
"bar"
,
},
},
}
controllerBytes
,
_
:=
latest
.
Codec
.
Encode
(
controller
)
fakeClient
.
WatchResponse
<-
&
etcd
.
Response
{
Action
:
"create"
,
Node
:
&
etcd
.
Node
{
Value
:
string
(
controllerBytes
),
},
}
select
{
case
<-
watching
.
ResultChan
()
:
t
.
Error
(
"unexpected result from result channel"
)
case
<-
time
.
After
(
time
.
Millisecond
*
100
)
:
// expected case
}
}
func
TestCreate
(
t
*
testing
.
T
)
{
storage
,
fakeClient
:=
newStorage
(
t
)
test
:=
resttest
.
New
(
t
,
storage
,
fakeClient
.
SetError
)
test
.
TestCreate
(
// valid
&
api
.
Daemon
{
Spec
:
api
.
DaemonSpec
{
Selector
:
map
[
string
]
string
{
"a"
:
"b"
},
Template
:
&
validPodTemplate
.
Template
,
},
},
// invalid
&
api
.
Daemon
{
Spec
:
api
.
DaemonSpec
{
Selector
:
map
[
string
]
string
{},
Template
:
&
validPodTemplate
.
Template
,
},
},
)
}
func
TestDelete
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
fakeClient
:=
newStorage
(
t
)
test
:=
resttest
.
New
(
t
,
storage
,
fakeClient
.
SetError
)
key
,
_
:=
makeControllerKey
(
ctx
,
validController
.
Name
)
key
=
etcdtest
.
AddPrefix
(
key
)
createFn
:=
func
()
runtime
.
Object
{
dc
:=
validController
dc
.
ResourceVersion
=
"1"
fakeClient
.
Data
[
key
]
=
tools
.
EtcdResponseWithError
{
R
:
&
etcd
.
Response
{
Node
:
&
etcd
.
Node
{
Value
:
runtime
.
EncodeOrDie
(
latest
.
Codec
,
&
dc
),
ModifiedIndex
:
1
,
},
},
}
return
&
dc
}
gracefulSetFn
:=
func
()
bool
{
// If the controller is still around after trying to delete either the delete
// failed, or we're deleting it gracefully.
if
fakeClient
.
Data
[
key
]
.
R
.
Node
!=
nil
{
return
true
}
return
false
}
test
.
TestDelete
(
createFn
,
gracefulSetFn
)
}
pkg/registry/daemon/rest.go
0 → 100644
View file @
264a658a
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
daemon
import
(
"fmt"
"reflect"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/fielderrors"
)
// daemonStrategy implements verification logic for daemons.
type
daemonStrategy
struct
{
runtime
.
ObjectTyper
api
.
NameGenerator
}
// Strategy is the default logic that applies when creating and updating Daemon objects.
var
Strategy
=
daemonStrategy
{
api
.
Scheme
,
api
.
SimpleNameGenerator
}
// NamespaceScoped returns true because all Daemons need to be within a namespace.
func
(
daemonStrategy
)
NamespaceScoped
()
bool
{
return
true
}
// PrepareForCreate clears the status of a daemon before creation.
func
(
daemonStrategy
)
PrepareForCreate
(
obj
runtime
.
Object
)
{
daemon
:=
obj
.
(
*
api
.
Daemon
)
daemon
.
Status
=
api
.
DaemonStatus
{}
daemon
.
Generation
=
1
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func
(
daemonStrategy
)
PrepareForUpdate
(
obj
,
old
runtime
.
Object
)
{
newDaemon
:=
obj
.
(
*
api
.
Daemon
)
oldDaemon
:=
old
.
(
*
api
.
Daemon
)
// Any changes to the spec increment the generation number, any changes to the
// status should reflect the generation number of the corresponding object. We push
// the burden of managing the status onto the clients because we can't (in general)
// know here what version of spec the writer of the status has seen. It may seem like
// we can at first -- since obj contains spec -- but in the future we will probably make
// status its own object, and even if we don't, writes may be the result of a
// read-update-write loop, so the contents of spec may not actually be the spec that
// the controller has *seen*.
//
// TODO: Any changes to a part of the object that represents desired state (labels,
// annotations etc) should also increment the generation.
if
!
reflect
.
DeepEqual
(
oldDaemon
.
Spec
,
newDaemon
.
Spec
)
{
newDaemon
.
Generation
=
oldDaemon
.
Generation
+
1
}
}
// Validate validates a new daemon.
func
(
daemonStrategy
)
Validate
(
ctx
api
.
Context
,
obj
runtime
.
Object
)
fielderrors
.
ValidationErrorList
{
daemon
:=
obj
.
(
*
api
.
Daemon
)
return
validation
.
ValidateDaemon
(
daemon
)
}
// AllowCreateOnUpdate is false for daemon; this means a POST is
// needed to create one
func
(
daemonStrategy
)
AllowCreateOnUpdate
()
bool
{
return
false
}
// ValidateUpdate is the default update validation for an end user.
func
(
daemonStrategy
)
ValidateUpdate
(
ctx
api
.
Context
,
obj
,
old
runtime
.
Object
)
fielderrors
.
ValidationErrorList
{
validationErrorList
:=
validation
.
ValidateDaemon
(
obj
.
(
*
api
.
Daemon
))
updateErrorList
:=
validation
.
ValidateDaemonUpdate
(
old
.
(
*
api
.
Daemon
),
obj
.
(
*
api
.
Daemon
))
return
append
(
validationErrorList
,
updateErrorList
...
)
}
// AllowUnconditionalUpdate is the default update policy for daemon objects.
func
(
daemonStrategy
)
AllowUnconditionalUpdate
()
bool
{
return
true
}
// DaemonToSelectableFields returns a field set that represents the object.
func
DaemonToSelectableFields
(
daemon
*
api
.
Daemon
)
fields
.
Set
{
return
fields
.
Set
{
"metadata.name"
:
daemon
.
Name
,
}
}
// MatchDaemon is the filter used by the generic etcd backend to route
// watch events from etcd to clients of the apiserver only interested in specific
// labels/fields.
func
MatchDaemon
(
label
labels
.
Selector
,
field
fields
.
Selector
)
generic
.
Matcher
{
return
&
generic
.
SelectionPredicate
{
Label
:
label
,
Field
:
field
,
GetAttrs
:
func
(
obj
runtime
.
Object
)
(
labels
.
Set
,
fields
.
Set
,
error
)
{
daemon
,
ok
:=
obj
.
(
*
api
.
Daemon
)
if
!
ok
{
return
nil
,
nil
,
fmt
.
Errorf
(
"given object is not a daemon."
)
}
return
labels
.
Set
(
daemon
.
ObjectMeta
.
Labels
),
DaemonToSelectableFields
(
daemon
),
nil
},
}
}
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