2016-06-16 06:48:04 +00:00
|
|
|
/*
|
2016-06-03 00:25:58 +00:00
|
|
|
Copyright 2016 The Kubernetes Authors.
|
2016-06-16 06:48:04 +00:00
|
|
|
|
|
|
|
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 statusupdater implements interfaces that enable updating the status
|
|
|
|
// of API objects.
|
|
|
|
package statusupdater
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/golang/glog"
|
|
|
|
|
|
|
|
"k8s.io/kubernetes/pkg/api"
|
2016-09-14 18:35:38 +00:00
|
|
|
kcache "k8s.io/kubernetes/pkg/client/cache"
|
2016-06-16 06:48:04 +00:00
|
|
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
2016-07-02 01:50:25 +00:00
|
|
|
"k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache"
|
2016-06-16 06:48:04 +00:00
|
|
|
"k8s.io/kubernetes/pkg/util/strategicpatch"
|
|
|
|
)
|
|
|
|
|
|
|
|
// NodeStatusUpdater defines a set of operations for updating the
|
|
|
|
// VolumesAttached field in the Node Status.
|
|
|
|
type NodeStatusUpdater interface {
|
|
|
|
// Gets a list of node statuses that should be updated from the actual state
|
|
|
|
// of the world and updates them.
|
|
|
|
UpdateNodeStatuses() error
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewNodeStatusUpdater returns a new instance of NodeStatusUpdater.
|
|
|
|
func NewNodeStatusUpdater(
|
|
|
|
kubeClient internalclientset.Interface,
|
2016-09-14 18:35:38 +00:00
|
|
|
nodeInformer kcache.SharedInformer,
|
2016-06-16 06:48:04 +00:00
|
|
|
actualStateOfWorld cache.ActualStateOfWorld) NodeStatusUpdater {
|
|
|
|
return &nodeStatusUpdater{
|
|
|
|
actualStateOfWorld: actualStateOfWorld,
|
|
|
|
nodeInformer: nodeInformer,
|
|
|
|
kubeClient: kubeClient,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type nodeStatusUpdater struct {
|
|
|
|
kubeClient internalclientset.Interface
|
2016-09-14 18:35:38 +00:00
|
|
|
nodeInformer kcache.SharedInformer
|
2016-06-16 06:48:04 +00:00
|
|
|
actualStateOfWorld cache.ActualStateOfWorld
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nsu *nodeStatusUpdater) UpdateNodeStatuses() error {
|
|
|
|
nodesToUpdate := nsu.actualStateOfWorld.GetVolumesToReportAttached()
|
|
|
|
for nodeName, attachedVolumes := range nodesToUpdate {
|
|
|
|
nodeObj, exists, err := nsu.nodeInformer.GetStore().GetByKey(nodeName)
|
|
|
|
if nodeObj == nil || !exists || err != nil {
|
2016-07-23 00:07:47 +00:00
|
|
|
// If node does not exist, its status cannot be updated, log error and move on.
|
2016-08-18 17:18:56 +00:00
|
|
|
glog.V(5).Infof(
|
2016-07-23 00:07:47 +00:00
|
|
|
"Could not update node status. Failed to find node %q in NodeInformer cache. %v",
|
2016-06-16 06:48:04 +00:00
|
|
|
nodeName,
|
|
|
|
err)
|
2016-08-17 03:33:06 +00:00
|
|
|
continue
|
2016-06-16 06:48:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
node, ok := nodeObj.(*api.Node)
|
|
|
|
if !ok || node == nil {
|
|
|
|
return fmt.Errorf(
|
|
|
|
"failed to cast %q object %#v to Node",
|
|
|
|
nodeName,
|
|
|
|
nodeObj)
|
|
|
|
}
|
|
|
|
|
|
|
|
oldData, err := json.Marshal(node)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf(
|
|
|
|
"failed to Marshal oldData for node %q. %v",
|
|
|
|
nodeName,
|
|
|
|
err)
|
|
|
|
}
|
|
|
|
|
|
|
|
node.Status.VolumesAttached = attachedVolumes
|
|
|
|
|
|
|
|
newData, err := json.Marshal(node)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf(
|
|
|
|
"failed to Marshal newData for node %q. %v",
|
|
|
|
nodeName,
|
|
|
|
err)
|
|
|
|
}
|
|
|
|
|
|
|
|
patchBytes, err :=
|
|
|
|
strategicpatch.CreateStrategicMergePatch(oldData, newData, node)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf(
|
|
|
|
"failed to CreateStrategicMergePatch for node %q. %v",
|
|
|
|
nodeName,
|
|
|
|
err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = nsu.kubeClient.Core().Nodes().PatchStatus(nodeName, patchBytes)
|
|
|
|
if err != nil {
|
Fix race conditino in setting node statusUpdateNeeded flag
This PR fixes the race condition in setting node statusUpdateNeeded flag
in master's attachdetach controller. This flag is used to indicate
whether a node status has been updated by the node_status_updater or
not. When updater finishes update a node status, it is set to false.
When the node status is changed such as volume is detached or new volume
is attached to the node, the flag is set to true so that updater can
update the status again. The previous workflow has a race condition as
follows
1. updater gets the currently attached volume list from the node which needs to be
updated.
2. A new volume A is attached to the same node right after 1 and set the
flag to TRUE
3. updater updates the node attached volume list (which does not include volume A) and then set the flag to FALSE.
The result is that volume A will be never added to the attached volume
list so at node side, this volume is never attached.
So in this PR, the flag is set to FALSE when updater tries to get the
attached volume list (as in an atomic operation). So in the above
example, after step 2, the flag will be TRUE again, in step 3, updater
does not set the flag if updates is sucessful. So after that, flag is
still TRUE and in next round of update, the node status will be updated.
This PR also changes a unit test due to the workflow changes
2016-09-15 18:01:11 +00:00
|
|
|
// If update node status fails, reset flag statusUpdateNeeded back to true
|
|
|
|
// to indicate this node status needs to be udpated again
|
|
|
|
nsu.actualStateOfWorld.SetNodeStatusUpdateNeeded(nodeName)
|
2016-06-16 06:48:04 +00:00
|
|
|
return fmt.Errorf(
|
|
|
|
"failed to kubeClient.Core().Nodes().Patch for node %q. %v",
|
|
|
|
nodeName,
|
|
|
|
err)
|
|
|
|
}
|
|
|
|
|
|
|
|
glog.V(3).Infof(
|
|
|
|
"Updating status for node %q succeeded. patchBytes: %q",
|
2016-06-22 19:56:58 +00:00
|
|
|
nodeName,
|
2016-06-16 06:48:04 +00:00
|
|
|
string(patchBytes))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|