Update NodeStatus use subresources.

This commit is contained in:
gmarek 2015-04-08 11:32:47 +02:00
parent b12d75d0ee
commit ccc56d3c3c
11 changed files with 80 additions and 17 deletions

View File

@ -35,6 +35,7 @@ type NodeInterface interface {
List(selector labels.Selector) (*api.NodeList, error)
Delete(name string) error
Update(*api.Node) (*api.Node, error)
UpdateStatus(*api.Node) (*api.Node, error)
Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
}
@ -94,6 +95,16 @@ func (c *nodes) Update(node *api.Node) (*api.Node, error) {
return result, err
}
func (c *nodes) UpdateStatus(node *api.Node) (*api.Node, error) {
result := &api.Node{}
if len(node.ResourceVersion) == 0 {
err := fmt.Errorf("invalid update object, missing resource version: %v", node)
return nil, err
}
err := c.r.Put().Resource(c.resourceName()).Name(node.Name).SubResource("status").Body(node).Do().Into(result)
return result, err
}
// Watch returns a watch.Interface that watches the requested nodes.
func (c *nodes) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
return c.r.Get().

View File

@ -54,6 +54,11 @@ func (c *FakeNodes) Update(minion *api.Node) (*api.Node, error) {
return obj.(*api.Node), err
}
func (c *FakeNodes) UpdateStatus(minion *api.Node) (*api.Node, error) {
obj, err := c.Fake.Invokes(FakeAction{Action: "update-status-node", Value: minion}, &api.Node{})
return obj.(*api.Node), err
}
func (c *FakeNodes) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "watch-nodes", Value: resourceVersion})
return c.Fake.Watch, c.Fake.Err

View File

@ -413,7 +413,7 @@ func (nc *NodeController) tryUpdateNodeStatus(node *api.Node) (time.Duration, ap
}
}
if !api.Semantic.DeepEqual(nc.getCondition(&node.Status, api.NodeReady), lastReadyCondition) {
if _, err = nc.kubeClient.Nodes().Update(node); err != nil {
if _, err = nc.kubeClient.Nodes().UpdateStatus(node); err != nil {
glog.Errorf("Error updating node %s: %v", node.Name, err)
return gracePeriod, lastReadyCondition, readyCondition, err
} else {

View File

@ -56,10 +56,11 @@ type FakeNodeHandler struct {
Existing []*api.Node
// Output
CreatedNodes []*api.Node
DeletedNodes []*api.Node
UpdatedNodes []*api.Node
RequestCount int
CreatedNodes []*api.Node
DeletedNodes []*api.Node
UpdatedNodes []*api.Node
UpdatedNodeStatuses []*api.Node
RequestCount int
}
func (c *FakeNodeHandler) Nodes() client.NodeInterface {
@ -124,6 +125,13 @@ func (m *FakeNodeHandler) Update(node *api.Node) (*api.Node, error) {
return node, nil
}
func (m *FakeNodeHandler) UpdateStatus(node *api.Node) (*api.Node, error) {
nodeCopy := *node
m.UpdatedNodeStatuses = append(m.UpdatedNodeStatuses, &nodeCopy)
m.RequestCount++
return node, nil
}
func (m *FakeNodeHandler) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
return nil, nil
}
@ -1053,7 +1061,7 @@ func TestMonitorNodeStatusUpdateStatus(t *testing.T) {
if item.expectedRequestCount != item.fakeNodeHandler.RequestCount {
t.Errorf("expected %v call, but got %v.", item.expectedRequestCount, item.fakeNodeHandler.RequestCount)
}
if !api.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodes) {
if len(item.fakeNodeHandler.UpdatedNodes) > 0 && !api.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodes) {
t.Errorf("expected nodes %+v, got %+v", item.expectedNodes[0],
item.fakeNodeHandler.UpdatedNodes[0])
}

View File

@ -1843,7 +1843,7 @@ func (kl *Kubelet) tryUpdateNodeStatus() error {
kl.recordNodeOnlineEvent()
}
_, err = kl.kubeClient.Nodes().Update(node)
_, err = kl.kubeClient.Nodes().UpdateStatus(node)
return err
}

View File

@ -3113,7 +3113,7 @@ func TestUpdateNewNodeStatus(t *testing.T) {
if err := kubelet.updateNodeStatus(); err != nil {
t.Errorf("unexpected error: %v", err)
}
if len(kubeClient.Actions) != 2 || kubeClient.Actions[1].Action != "update-node" {
if len(kubeClient.Actions) != 2 || kubeClient.Actions[1].Action != "update-status-node" {
t.Fatalf("unexpected actions: %v", kubeClient.Actions)
}
updatedNode, ok := kubeClient.Actions[1].Value.(*api.Node)

View File

@ -376,7 +376,7 @@ func (m *Master) init(c *Config) {
endpointsStorage := endpointsetcd.NewStorage(c.EtcdHelper)
m.endpointRegistry = endpoint.NewRegistry(endpointsStorage)
nodeStorage := nodeetcd.NewStorage(c.EtcdHelper, c.KubeletClient)
nodeStorage, nodeStatusStorage := nodeetcd.NewStorage(c.EtcdHelper, c.KubeletClient)
m.nodeRegistry = minion.NewRegistry(nodeStorage)
// TODO: split me up into distinct storage registries
@ -397,7 +397,9 @@ func (m *Master) init(c *Config) {
"services": service.NewStorage(m.serviceRegistry, c.Cloud, m.nodeRegistry, m.endpointRegistry, m.portalNet, c.ClusterName),
"endpoints": endpointsStorage,
"minions": nodeStorage,
"minions/status": nodeStatusStorage,
"nodes": nodeStorage,
"nodes/status": nodeStatusStorage,
"events": event.NewStorage(eventRegistry),
"limitRanges": limitrange.NewStorage(limitRangeRegistry),

View File

@ -34,8 +34,22 @@ type REST struct {
connection client.ConnectionInfoGetter
}
// StatusREST implements the REST endpoint for changing the status of a pod.
type StatusREST struct {
store *etcdgeneric.Etcd
}
func (r *StatusREST) New() runtime.Object {
return &api.Node{}
}
// Update alters the status subset of an object.
func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
return r.store.Update(ctx, obj)
}
// NewStorage returns a RESTStorage object that will work against nodes.
func NewStorage(h tools.EtcdHelper, connection client.ConnectionInfoGetter) *REST {
func NewStorage(h tools.EtcdHelper, connection client.ConnectionInfoGetter) (*REST, *StatusREST) {
prefix := "/registry/minions"
store := &etcdgeneric.Etcd{
NewFunc: func() runtime.Object { return &api.Node{} },
@ -58,7 +72,10 @@ func NewStorage(h tools.EtcdHelper, connection client.ConnectionInfoGetter) *RES
Helper: h,
}
return &REST{store, connection}
statusStore := *store
statusStore.UpdateStrategy = minion.StatusStrategy
return &REST{store, connection}, &StatusREST{store: &statusStore}
}
// Implement Redirector.

View File

@ -55,7 +55,7 @@ func newHelper(t *testing.T) (*tools.FakeEtcdClient, tools.EtcdHelper) {
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
fakeEtcdClient, h := newHelper(t)
storage := NewStorage(h, fakeConnectionInfoGetter{})
storage, _ := NewStorage(h, fakeConnectionInfoGetter{})
return storage, fakeEtcdClient
}

View File

@ -56,14 +56,14 @@ func (nodeStrategy) AllowCreateOnUpdate() bool {
// PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (nodeStrategy) PrepareForCreate(obj runtime.Object) {
_ = obj.(*api.Node)
// Nodes allow *all* fields, including status, to be set.
// Nodes allow *all* fields, including status, to be set on create.
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (nodeStrategy) PrepareForUpdate(obj, old runtime.Object) {
_ = obj.(*api.Node)
_ = old.(*api.Node)
// Nodes allow *all* fields, including status, to be set.
newNode := obj.(*api.Node)
oldNode := old.(*api.Node)
newNode.Status = oldNode.Status
}
// Validate validates a new node.
@ -77,6 +77,27 @@ func (nodeStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fie
return validation.ValidateMinionUpdate(old.(*api.Node), obj.(*api.Node))
}
type nodeStatusStrategy struct {
nodeStrategy
}
var StatusStrategy = nodeStatusStrategy{Strategy}
func (nodeStatusStrategy) PrepareForCreate(obj runtime.Object) {
_ = obj.(*api.Node)
// Nodes allow *all* fields, including status, to be set on create.
}
func (nodeStatusStrategy) PrepareForUpdate(obj, old runtime.Object) {
newNode := obj.(*api.Node)
oldNode := old.(*api.Node)
newNode.Spec = oldNode.Spec
}
func (nodeStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fielderrors.ValidationErrorList {
return validation.ValidateMinionUpdate(old.(*api.Node), obj.(*api.Node))
}
// ResourceGetter is an interface for retrieving resources by ResourceLocation.
type ResourceGetter interface {
Get(api.Context, string) (runtime.Object, error)

View File

@ -43,7 +43,6 @@ type podStrategy struct {
// Strategy is the default logic that applies when creating and updating Pod
// objects via the REST API.
// TODO: Create other strategies for updating status, bindings, etc
var Strategy = podStrategy{api.Scheme, api.SimpleNameGenerator}
// NamespaceScoped is true for pods.