mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 18:53:35 +00:00
vendor files
This commit is contained in:
56
vendor/k8s.io/kubernetes/pkg/kubelet/volumemanager/cache/BUILD
generated
vendored
Normal file
56
vendor/k8s.io/kubernetes/pkg/kubelet/volumemanager/cache/BUILD
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"actual_state_of_world.go",
|
||||
"desired_state_of_world.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/volumemanager/cache",
|
||||
deps = [
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/util/operationexecutor:go_default_library",
|
||||
"//pkg/volume/util/types:go_default_library",
|
||||
"//pkg/volume/util/volumehelper:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"actual_state_of_world_test.go",
|
||||
"desired_state_of_world_test.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/volumemanager/cache",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/testing:go_default_library",
|
||||
"//pkg/volume/util/types:go_default_library",
|
||||
"//pkg/volume/util/volumehelper:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
695
vendor/k8s.io/kubernetes/pkg/kubelet/volumemanager/cache/actual_state_of_world.go
generated
vendored
Normal file
695
vendor/k8s.io/kubernetes/pkg/kubelet/volumemanager/cache/actual_state_of_world.go
generated
vendored
Normal file
@ -0,0 +1,695 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 cache implements data structures used by the kubelet volume manager to
|
||||
keep track of attached volumes and the pods that mounted them.
|
||||
*/
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
||||
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
|
||||
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
|
||||
)
|
||||
|
||||
// ActualStateOfWorld defines a set of thread-safe operations for the kubelet
|
||||
// volume manager's actual state of the world cache.
|
||||
// This cache contains volumes->pods i.e. a set of all volumes attached to this
|
||||
// node and the pods that the manager believes have successfully mounted the
|
||||
// volume.
|
||||
// Note: This is distinct from the ActualStateOfWorld implemented by the
|
||||
// attach/detach controller. They both keep track of different objects. This
|
||||
// contains kubelet volume manager specific state.
|
||||
type ActualStateOfWorld interface {
|
||||
// ActualStateOfWorld must implement the methods required to allow
|
||||
// operationexecutor to interact with it.
|
||||
operationexecutor.ActualStateOfWorldMounterUpdater
|
||||
|
||||
// ActualStateOfWorld must implement the methods required to allow
|
||||
// operationexecutor to interact with it.
|
||||
operationexecutor.ActualStateOfWorldAttacherUpdater
|
||||
|
||||
// AddPodToVolume adds the given pod to the given volume in the cache
|
||||
// indicating the specified volume has been successfully mounted to the
|
||||
// specified pod.
|
||||
// If a pod with the same unique name already exists under the specified
|
||||
// volume, reset the pod's remountRequired value.
|
||||
// If a volume with the name volumeName does not exist in the list of
|
||||
// attached volumes, an error is returned.
|
||||
AddPodToVolume(podName volumetypes.UniquePodName, podUID types.UID, volumeName v1.UniqueVolumeName, mounter volume.Mounter, blockVolumeMapper volume.BlockVolumeMapper, outerVolumeSpecName string, volumeGidValue string) error
|
||||
|
||||
// MarkRemountRequired marks each volume that is successfully attached and
|
||||
// mounted for the specified pod as requiring remount (if the plugin for the
|
||||
// volume indicates it requires remounting on pod updates). Atomically
|
||||
// updating volumes depend on this to update the contents of the volume on
|
||||
// pod update.
|
||||
MarkRemountRequired(podName volumetypes.UniquePodName)
|
||||
|
||||
// SetVolumeGloballyMounted sets the GloballyMounted value for the given
|
||||
// volume. When set to true this value indicates that the volume is mounted
|
||||
// to the underlying device at a global mount point. This global mount point
|
||||
// must unmounted prior to detach.
|
||||
// If a volume with the name volumeName does not exist in the list of
|
||||
// attached volumes, an error is returned.
|
||||
SetVolumeGloballyMounted(volumeName v1.UniqueVolumeName, globallyMounted bool) error
|
||||
|
||||
// DeletePodFromVolume removes the given pod from the given volume in the
|
||||
// cache indicating the volume has been successfully unmounted from the pod.
|
||||
// If a pod with the same unique name does not exist under the specified
|
||||
// volume, this is a no-op.
|
||||
// If a volume with the name volumeName does not exist in the list of
|
||||
// attached volumes, an error is returned.
|
||||
DeletePodFromVolume(podName volumetypes.UniquePodName, volumeName v1.UniqueVolumeName) error
|
||||
|
||||
// DeleteVolume removes the given volume from the list of attached volumes
|
||||
// in the cache indicating the volume has been successfully detached from
|
||||
// this node.
|
||||
// If a volume with the name volumeName does not exist in the list of
|
||||
// attached volumes, this is a no-op.
|
||||
// If a volume with the name volumeName exists and its list of mountedPods
|
||||
// is not empty, an error is returned.
|
||||
DeleteVolume(volumeName v1.UniqueVolumeName) error
|
||||
|
||||
// PodExistsInVolume returns true if the given pod exists in the list of
|
||||
// mountedPods for the given volume in the cache, indicating that the volume
|
||||
// is attached to this node and the pod has successfully mounted it.
|
||||
// If a pod with the same unique name does not exist under the specified
|
||||
// volume, false is returned.
|
||||
// If a volume with the name volumeName does not exist in the list of
|
||||
// attached volumes, a volumeNotAttachedError is returned indicating the
|
||||
// given volume is not yet attached.
|
||||
// If the given volumeName/podName combo exists but the value of
|
||||
// remountRequired is true, a remountRequiredError is returned indicating
|
||||
// the given volume has been successfully mounted to this pod but should be
|
||||
// remounted to reflect changes in the referencing pod. Atomically updating
|
||||
// volumes, depend on this to update the contents of the volume.
|
||||
// All volume mounting calls should be idempotent so a second mount call for
|
||||
// volumes that do not need to update contents should not fail.
|
||||
PodExistsInVolume(podName volumetypes.UniquePodName, volumeName v1.UniqueVolumeName) (bool, string, error)
|
||||
|
||||
// VolumeExists returns true if the given volume exists in the list of
|
||||
// attached volumes in the cache, indicating the volume is attached to this
|
||||
// node.
|
||||
VolumeExists(volumeName v1.UniqueVolumeName) bool
|
||||
|
||||
// GetMountedVolumes generates and returns a list of volumes and the pods
|
||||
// they are successfully attached and mounted for based on the current
|
||||
// actual state of the world.
|
||||
GetMountedVolumes() []MountedVolume
|
||||
|
||||
// GetMountedVolumesForPod generates and returns a list of volumes that are
|
||||
// successfully attached and mounted for the specified pod based on the
|
||||
// current actual state of the world.
|
||||
GetMountedVolumesForPod(podName volumetypes.UniquePodName) []MountedVolume
|
||||
|
||||
// GetGloballyMountedVolumes generates and returns a list of all attached
|
||||
// volumes that are globally mounted. This list can be used to determine
|
||||
// which volumes should be reported as "in use" in the node's VolumesInUse
|
||||
// status field. Globally mounted here refers to the shared plugin mount
|
||||
// point for the attachable volume from which the pod specific mount points
|
||||
// are created (via bind mount).
|
||||
GetGloballyMountedVolumes() []AttachedVolume
|
||||
|
||||
// GetUnmountedVolumes generates and returns a list of attached volumes that
|
||||
// have no mountedPods. This list can be used to determine which volumes are
|
||||
// no longer referenced and may be globally unmounted and detached.
|
||||
GetUnmountedVolumes() []AttachedVolume
|
||||
|
||||
// GetPods generates and returns a map of pods in which map is indexed
|
||||
// with pod's unique name. This map can be used to determine which pod is currently
|
||||
// in actual state of world.
|
||||
GetPods() map[volumetypes.UniquePodName]bool
|
||||
}
|
||||
|
||||
// MountedVolume represents a volume that has successfully been mounted to a pod.
|
||||
type MountedVolume struct {
|
||||
operationexecutor.MountedVolume
|
||||
}
|
||||
|
||||
// AttachedVolume represents a volume that is attached to a node.
|
||||
type AttachedVolume struct {
|
||||
operationexecutor.AttachedVolume
|
||||
|
||||
// GloballyMounted indicates that the volume is mounted to the underlying
|
||||
// device at a global mount point. This global mount point must unmounted
|
||||
// prior to detach.
|
||||
GloballyMounted bool
|
||||
}
|
||||
|
||||
// NewActualStateOfWorld returns a new instance of ActualStateOfWorld.
|
||||
func NewActualStateOfWorld(
|
||||
nodeName types.NodeName,
|
||||
volumePluginMgr *volume.VolumePluginMgr) ActualStateOfWorld {
|
||||
return &actualStateOfWorld{
|
||||
nodeName: nodeName,
|
||||
attachedVolumes: make(map[v1.UniqueVolumeName]attachedVolume),
|
||||
volumePluginMgr: volumePluginMgr,
|
||||
}
|
||||
}
|
||||
|
||||
// IsVolumeNotAttachedError returns true if the specified error is a
|
||||
// volumeNotAttachedError.
|
||||
func IsVolumeNotAttachedError(err error) bool {
|
||||
_, ok := err.(volumeNotAttachedError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsRemountRequiredError returns true if the specified error is a
|
||||
// remountRequiredError.
|
||||
func IsRemountRequiredError(err error) bool {
|
||||
_, ok := err.(remountRequiredError)
|
||||
return ok
|
||||
}
|
||||
|
||||
type actualStateOfWorld struct {
|
||||
// nodeName is the name of this node. This value is passed to Attach/Detach
|
||||
nodeName types.NodeName
|
||||
|
||||
// attachedVolumes is a map containing the set of volumes the kubelet volume
|
||||
// manager believes to be successfully attached to this node. Volume types
|
||||
// that do not implement an attacher interface are assumed to be in this
|
||||
// state by default.
|
||||
// The key in this map is the name of the volume and the value is an object
|
||||
// containing more information about the attached volume.
|
||||
attachedVolumes map[v1.UniqueVolumeName]attachedVolume
|
||||
|
||||
// volumePluginMgr is the volume plugin manager used to create volume
|
||||
// plugin objects.
|
||||
volumePluginMgr *volume.VolumePluginMgr
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// attachedVolume represents a volume the kubelet volume manager believes to be
|
||||
// successfully attached to a node it is managing. Volume types that do not
|
||||
// implement an attacher are assumed to be in this state.
|
||||
type attachedVolume struct {
|
||||
// volumeName contains the unique identifier for this volume.
|
||||
volumeName v1.UniqueVolumeName
|
||||
|
||||
// mountedPods is a map containing the set of pods that this volume has been
|
||||
// successfully mounted to. The key in this map is the name of the pod and
|
||||
// the value is a mountedPod object containing more information about the
|
||||
// pod.
|
||||
mountedPods map[volumetypes.UniquePodName]mountedPod
|
||||
|
||||
// spec is the volume spec containing the specification for this volume.
|
||||
// Used to generate the volume plugin object, and passed to plugin methods.
|
||||
// In particular, the Unmount method uses spec.Name() as the volumeSpecName
|
||||
// in the mount path:
|
||||
// /var/lib/kubelet/pods/{podUID}/volumes/{escapeQualifiedPluginName}/{volumeSpecName}/
|
||||
spec *volume.Spec
|
||||
|
||||
// pluginName is the Unescaped Qualified name of the volume plugin used to
|
||||
// attach and mount this volume. It is stored separately in case the full
|
||||
// volume spec (everything except the name) can not be reconstructed for a
|
||||
// volume that should be unmounted (which would be the case for a mount path
|
||||
// read from disk without a full volume spec).
|
||||
pluginName string
|
||||
|
||||
// pluginIsAttachable indicates the volume plugin used to attach and mount
|
||||
// this volume implements the volume.Attacher interface
|
||||
pluginIsAttachable bool
|
||||
|
||||
// globallyMounted indicates that the volume is mounted to the underlying
|
||||
// device at a global mount point. This global mount point must be unmounted
|
||||
// prior to detach.
|
||||
globallyMounted bool
|
||||
|
||||
// devicePath contains the path on the node where the volume is attached for
|
||||
// attachable volumes
|
||||
devicePath string
|
||||
}
|
||||
|
||||
// The mountedPod object represents a pod for which the kubelet volume manager
|
||||
// believes the underlying volume has been successfully been mounted.
|
||||
type mountedPod struct {
|
||||
// the name of the pod
|
||||
podName volumetypes.UniquePodName
|
||||
|
||||
// the UID of the pod
|
||||
podUID types.UID
|
||||
|
||||
// mounter used to mount
|
||||
mounter volume.Mounter
|
||||
|
||||
// mapper used to block volumes support
|
||||
blockVolumeMapper volume.BlockVolumeMapper
|
||||
|
||||
// outerVolumeSpecName is the volume.Spec.Name() of the volume as referenced
|
||||
// directly in the pod. If the volume was referenced through a persistent
|
||||
// volume claim, this contains the volume.Spec.Name() of the persistent
|
||||
// volume claim
|
||||
outerVolumeSpecName string
|
||||
|
||||
// remountRequired indicates the underlying volume has been successfully
|
||||
// mounted to this pod but it should be remounted to reflect changes in the
|
||||
// referencing pod.
|
||||
// Atomically updating volumes depend on this to update the contents of the
|
||||
// volume. All volume mounting calls should be idempotent so a second mount
|
||||
// call for volumes that do not need to update contents should not fail.
|
||||
remountRequired bool
|
||||
|
||||
// volumeGidValue contains the value of the GID annotation, if present.
|
||||
volumeGidValue string
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) MarkVolumeAsAttached(
|
||||
volumeName v1.UniqueVolumeName, volumeSpec *volume.Spec, _ types.NodeName, devicePath string) error {
|
||||
return asw.addVolume(volumeName, volumeSpec, devicePath)
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) MarkVolumeAsDetached(
|
||||
volumeName v1.UniqueVolumeName, nodeName types.NodeName) {
|
||||
asw.DeleteVolume(volumeName)
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) MarkVolumeAsMounted(
|
||||
podName volumetypes.UniquePodName,
|
||||
podUID types.UID,
|
||||
volumeName v1.UniqueVolumeName,
|
||||
mounter volume.Mounter,
|
||||
blockVolumeMapper volume.BlockVolumeMapper,
|
||||
outerVolumeSpecName string,
|
||||
volumeGidValue string) error {
|
||||
return asw.AddPodToVolume(
|
||||
podName,
|
||||
podUID,
|
||||
volumeName,
|
||||
mounter,
|
||||
blockVolumeMapper,
|
||||
outerVolumeSpecName,
|
||||
volumeGidValue)
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) AddVolumeToReportAsAttached(volumeName v1.UniqueVolumeName, nodeName types.NodeName) {
|
||||
// no operation for kubelet side
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) RemoveVolumeFromReportAsAttached(volumeName v1.UniqueVolumeName, nodeName types.NodeName) error {
|
||||
// no operation for kubelet side
|
||||
return nil
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) MarkVolumeAsUnmounted(
|
||||
podName volumetypes.UniquePodName, volumeName v1.UniqueVolumeName) error {
|
||||
return asw.DeletePodFromVolume(podName, volumeName)
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) MarkDeviceAsMounted(
|
||||
volumeName v1.UniqueVolumeName) error {
|
||||
return asw.SetVolumeGloballyMounted(volumeName, true /* globallyMounted */)
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) MarkDeviceAsUnmounted(
|
||||
volumeName v1.UniqueVolumeName) error {
|
||||
return asw.SetVolumeGloballyMounted(volumeName, false /* globallyMounted */)
|
||||
}
|
||||
|
||||
// addVolume adds the given volume to the cache indicating the specified
|
||||
// volume is attached to this node. If no volume name is supplied, a unique
|
||||
// volume name is generated from the volumeSpec and returned on success. If a
|
||||
// volume with the same generated name already exists, this is a noop. If no
|
||||
// volume plugin can support the given volumeSpec or more than one plugin can
|
||||
// support it, an error is returned.
|
||||
func (asw *actualStateOfWorld) addVolume(
|
||||
volumeName v1.UniqueVolumeName, volumeSpec *volume.Spec, devicePath string) error {
|
||||
asw.Lock()
|
||||
defer asw.Unlock()
|
||||
|
||||
volumePlugin, err := asw.volumePluginMgr.FindPluginBySpec(volumeSpec)
|
||||
if err != nil || volumePlugin == nil {
|
||||
return fmt.Errorf(
|
||||
"failed to get Plugin from volumeSpec for volume %q err=%v",
|
||||
volumeSpec.Name(),
|
||||
err)
|
||||
}
|
||||
|
||||
if len(volumeName) == 0 {
|
||||
volumeName, err = volumehelper.GetUniqueVolumeNameFromSpec(volumePlugin, volumeSpec)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"failed to GetUniqueVolumeNameFromSpec for volumeSpec %q using volume plugin %q err=%v",
|
||||
volumeSpec.Name(),
|
||||
volumePlugin.GetPluginName(),
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
pluginIsAttachable := false
|
||||
if _, ok := volumePlugin.(volume.AttachableVolumePlugin); ok {
|
||||
pluginIsAttachable = true
|
||||
}
|
||||
|
||||
volumeObj, volumeExists := asw.attachedVolumes[volumeName]
|
||||
if !volumeExists {
|
||||
volumeObj = attachedVolume{
|
||||
volumeName: volumeName,
|
||||
spec: volumeSpec,
|
||||
mountedPods: make(map[volumetypes.UniquePodName]mountedPod),
|
||||
pluginName: volumePlugin.GetPluginName(),
|
||||
pluginIsAttachable: pluginIsAttachable,
|
||||
globallyMounted: false,
|
||||
devicePath: devicePath,
|
||||
}
|
||||
} else {
|
||||
// If volume object already exists, update the fields such as device path
|
||||
volumeObj.devicePath = devicePath
|
||||
glog.V(2).Infof("Volume %q is already added to attachedVolume list, update device path %q",
|
||||
volumeName,
|
||||
devicePath)
|
||||
}
|
||||
asw.attachedVolumes[volumeName] = volumeObj
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) AddPodToVolume(
|
||||
podName volumetypes.UniquePodName,
|
||||
podUID types.UID,
|
||||
volumeName v1.UniqueVolumeName,
|
||||
mounter volume.Mounter,
|
||||
blockVolumeMapper volume.BlockVolumeMapper,
|
||||
outerVolumeSpecName string,
|
||||
volumeGidValue string) error {
|
||||
asw.Lock()
|
||||
defer asw.Unlock()
|
||||
|
||||
volumeObj, volumeExists := asw.attachedVolumes[volumeName]
|
||||
if !volumeExists {
|
||||
return fmt.Errorf(
|
||||
"no volume with the name %q exists in the list of attached volumes",
|
||||
volumeName)
|
||||
}
|
||||
|
||||
podObj, podExists := volumeObj.mountedPods[podName]
|
||||
if !podExists {
|
||||
podObj = mountedPod{
|
||||
podName: podName,
|
||||
podUID: podUID,
|
||||
mounter: mounter,
|
||||
blockVolumeMapper: blockVolumeMapper,
|
||||
outerVolumeSpecName: outerVolumeSpecName,
|
||||
volumeGidValue: volumeGidValue,
|
||||
}
|
||||
}
|
||||
|
||||
// If pod exists, reset remountRequired value
|
||||
podObj.remountRequired = false
|
||||
asw.attachedVolumes[volumeName].mountedPods[podName] = podObj
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) MarkRemountRequired(
|
||||
podName volumetypes.UniquePodName) {
|
||||
asw.Lock()
|
||||
defer asw.Unlock()
|
||||
for volumeName, volumeObj := range asw.attachedVolumes {
|
||||
for mountedPodName, podObj := range volumeObj.mountedPods {
|
||||
if mountedPodName != podName {
|
||||
continue
|
||||
}
|
||||
|
||||
volumePlugin, err :=
|
||||
asw.volumePluginMgr.FindPluginBySpec(volumeObj.spec)
|
||||
if err != nil || volumePlugin == nil {
|
||||
// Log and continue processing
|
||||
glog.Errorf(
|
||||
"MarkRemountRequired failed to FindPluginBySpec for pod %q (podUid %q) volume: %q (volSpecName: %q)",
|
||||
podObj.podName,
|
||||
podObj.podUID,
|
||||
volumeObj.volumeName,
|
||||
volumeObj.spec.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
if volumePlugin.RequiresRemount() {
|
||||
podObj.remountRequired = true
|
||||
asw.attachedVolumes[volumeName].mountedPods[podName] = podObj
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) SetVolumeGloballyMounted(
|
||||
volumeName v1.UniqueVolumeName, globallyMounted bool) error {
|
||||
asw.Lock()
|
||||
defer asw.Unlock()
|
||||
|
||||
volumeObj, volumeExists := asw.attachedVolumes[volumeName]
|
||||
if !volumeExists {
|
||||
return fmt.Errorf(
|
||||
"no volume with the name %q exists in the list of attached volumes",
|
||||
volumeName)
|
||||
}
|
||||
|
||||
volumeObj.globallyMounted = globallyMounted
|
||||
asw.attachedVolumes[volumeName] = volumeObj
|
||||
return nil
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) DeletePodFromVolume(
|
||||
podName volumetypes.UniquePodName, volumeName v1.UniqueVolumeName) error {
|
||||
asw.Lock()
|
||||
defer asw.Unlock()
|
||||
|
||||
volumeObj, volumeExists := asw.attachedVolumes[volumeName]
|
||||
if !volumeExists {
|
||||
return fmt.Errorf(
|
||||
"no volume with the name %q exists in the list of attached volumes",
|
||||
volumeName)
|
||||
}
|
||||
|
||||
_, podExists := volumeObj.mountedPods[podName]
|
||||
if podExists {
|
||||
delete(asw.attachedVolumes[volumeName].mountedPods, podName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) DeleteVolume(volumeName v1.UniqueVolumeName) error {
|
||||
asw.Lock()
|
||||
defer asw.Unlock()
|
||||
|
||||
volumeObj, volumeExists := asw.attachedVolumes[volumeName]
|
||||
if !volumeExists {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(volumeObj.mountedPods) != 0 {
|
||||
return fmt.Errorf(
|
||||
"failed to DeleteVolume %q, it still has %v mountedPods",
|
||||
volumeName,
|
||||
len(volumeObj.mountedPods))
|
||||
}
|
||||
|
||||
delete(asw.attachedVolumes, volumeName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) PodExistsInVolume(
|
||||
podName volumetypes.UniquePodName,
|
||||
volumeName v1.UniqueVolumeName) (bool, string, error) {
|
||||
asw.RLock()
|
||||
defer asw.RUnlock()
|
||||
|
||||
volumeObj, volumeExists := asw.attachedVolumes[volumeName]
|
||||
if !volumeExists {
|
||||
return false, "", newVolumeNotAttachedError(volumeName)
|
||||
}
|
||||
|
||||
podObj, podExists := volumeObj.mountedPods[podName]
|
||||
if podExists && podObj.remountRequired {
|
||||
return true, volumeObj.devicePath, newRemountRequiredError(volumeObj.volumeName, podObj.podName)
|
||||
}
|
||||
|
||||
return podExists, volumeObj.devicePath, nil
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) VolumeExists(
|
||||
volumeName v1.UniqueVolumeName) bool {
|
||||
asw.RLock()
|
||||
defer asw.RUnlock()
|
||||
|
||||
_, volumeExists := asw.attachedVolumes[volumeName]
|
||||
return volumeExists
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) GetMountedVolumes() []MountedVolume {
|
||||
asw.RLock()
|
||||
defer asw.RUnlock()
|
||||
mountedVolume := make([]MountedVolume, 0 /* len */, len(asw.attachedVolumes) /* cap */)
|
||||
for _, volumeObj := range asw.attachedVolumes {
|
||||
for _, podObj := range volumeObj.mountedPods {
|
||||
mountedVolume = append(
|
||||
mountedVolume,
|
||||
getMountedVolume(&podObj, &volumeObj))
|
||||
}
|
||||
}
|
||||
|
||||
return mountedVolume
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) GetMountedVolumesForPod(
|
||||
podName volumetypes.UniquePodName) []MountedVolume {
|
||||
asw.RLock()
|
||||
defer asw.RUnlock()
|
||||
mountedVolume := make([]MountedVolume, 0 /* len */, len(asw.attachedVolumes) /* cap */)
|
||||
for _, volumeObj := range asw.attachedVolumes {
|
||||
for mountedPodName, podObj := range volumeObj.mountedPods {
|
||||
if mountedPodName == podName {
|
||||
mountedVolume = append(
|
||||
mountedVolume,
|
||||
getMountedVolume(&podObj, &volumeObj))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mountedVolume
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) GetGloballyMountedVolumes() []AttachedVolume {
|
||||
asw.RLock()
|
||||
defer asw.RUnlock()
|
||||
globallyMountedVolumes := make(
|
||||
[]AttachedVolume, 0 /* len */, len(asw.attachedVolumes) /* cap */)
|
||||
for _, volumeObj := range asw.attachedVolumes {
|
||||
if volumeObj.globallyMounted {
|
||||
globallyMountedVolumes = append(
|
||||
globallyMountedVolumes,
|
||||
asw.newAttachedVolume(&volumeObj))
|
||||
}
|
||||
}
|
||||
|
||||
return globallyMountedVolumes
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) GetUnmountedVolumes() []AttachedVolume {
|
||||
asw.RLock()
|
||||
defer asw.RUnlock()
|
||||
unmountedVolumes := make([]AttachedVolume, 0 /* len */, len(asw.attachedVolumes) /* cap */)
|
||||
for _, volumeObj := range asw.attachedVolumes {
|
||||
if len(volumeObj.mountedPods) == 0 {
|
||||
unmountedVolumes = append(
|
||||
unmountedVolumes,
|
||||
asw.newAttachedVolume(&volumeObj))
|
||||
}
|
||||
}
|
||||
|
||||
return unmountedVolumes
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) GetPods() map[volumetypes.UniquePodName]bool {
|
||||
asw.RLock()
|
||||
defer asw.RUnlock()
|
||||
|
||||
podList := make(map[volumetypes.UniquePodName]bool)
|
||||
for _, volumeObj := range asw.attachedVolumes {
|
||||
for podName := range volumeObj.mountedPods {
|
||||
if !podList[podName] {
|
||||
podList[podName] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return podList
|
||||
}
|
||||
|
||||
func (asw *actualStateOfWorld) newAttachedVolume(
|
||||
attachedVolume *attachedVolume) AttachedVolume {
|
||||
return AttachedVolume{
|
||||
AttachedVolume: operationexecutor.AttachedVolume{
|
||||
VolumeName: attachedVolume.volumeName,
|
||||
VolumeSpec: attachedVolume.spec,
|
||||
NodeName: asw.nodeName,
|
||||
PluginIsAttachable: attachedVolume.pluginIsAttachable,
|
||||
DevicePath: attachedVolume.devicePath},
|
||||
GloballyMounted: attachedVolume.globallyMounted}
|
||||
}
|
||||
|
||||
// Compile-time check to ensure volumeNotAttachedError implements the error interface
|
||||
var _ error = volumeNotAttachedError{}
|
||||
|
||||
// volumeNotAttachedError is an error returned when PodExistsInVolume() fails to
|
||||
// find specified volume in the list of attached volumes.
|
||||
type volumeNotAttachedError struct {
|
||||
volumeName v1.UniqueVolumeName
|
||||
}
|
||||
|
||||
func (err volumeNotAttachedError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"volumeName %q does not exist in the list of attached volumes",
|
||||
err.volumeName)
|
||||
}
|
||||
|
||||
func newVolumeNotAttachedError(volumeName v1.UniqueVolumeName) error {
|
||||
return volumeNotAttachedError{
|
||||
volumeName: volumeName,
|
||||
}
|
||||
}
|
||||
|
||||
// Compile-time check to ensure remountRequiredError implements the error interface
|
||||
var _ error = remountRequiredError{}
|
||||
|
||||
// remountRequiredError is an error returned when PodExistsInVolume() found
|
||||
// volume/pod attached/mounted but remountRequired was true, indicating the
|
||||
// given volume should be remounted to the pod to reflect changes in the
|
||||
// referencing pod.
|
||||
type remountRequiredError struct {
|
||||
volumeName v1.UniqueVolumeName
|
||||
podName volumetypes.UniquePodName
|
||||
}
|
||||
|
||||
func (err remountRequiredError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"volumeName %q is mounted to %q but should be remounted",
|
||||
err.volumeName, err.podName)
|
||||
}
|
||||
|
||||
func newRemountRequiredError(
|
||||
volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName) error {
|
||||
return remountRequiredError{
|
||||
volumeName: volumeName,
|
||||
podName: podName,
|
||||
}
|
||||
}
|
||||
|
||||
// getMountedVolume constructs and returns a MountedVolume object from the given
|
||||
// mountedPod and attachedVolume objects.
|
||||
func getMountedVolume(
|
||||
mountedPod *mountedPod, attachedVolume *attachedVolume) MountedVolume {
|
||||
return MountedVolume{
|
||||
MountedVolume: operationexecutor.MountedVolume{
|
||||
PodName: mountedPod.podName,
|
||||
VolumeName: attachedVolume.volumeName,
|
||||
InnerVolumeSpecName: attachedVolume.spec.Name(),
|
||||
OuterVolumeSpecName: mountedPod.outerVolumeSpecName,
|
||||
PluginName: attachedVolume.pluginName,
|
||||
PodUID: mountedPod.podUID,
|
||||
Mounter: mountedPod.mounter,
|
||||
BlockVolumeMapper: mountedPod.blockVolumeMapper,
|
||||
VolumeGidValue: mountedPod.volumeGidValue,
|
||||
VolumeSpec: attachedVolume.spec}}
|
||||
}
|
548
vendor/k8s.io/kubernetes/pkg/kubelet/volumemanager/cache/actual_state_of_world_test.go
generated
vendored
Normal file
548
vendor/k8s.io/kubernetes/pkg/kubelet/volumemanager/cache/actual_state_of_world_test.go
generated
vendored
Normal file
@ -0,0 +1,548 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volumetesting "k8s.io/kubernetes/pkg/volume/testing"
|
||||
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
|
||||
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
|
||||
)
|
||||
|
||||
var emptyVolumeName = v1.UniqueVolumeName("")
|
||||
|
||||
// Calls MarkVolumeAsAttached() once to add volume
|
||||
// Verifies newly added volume exists in GetUnmountedVolumes()
|
||||
// Verifies newly added volume doesn't exist in GetGloballyMountedVolumes()
|
||||
func Test_MarkVolumeAsAttached_Positive_NewVolume(t *testing.T) {
|
||||
// Arrange
|
||||
volumePluginMgr, plugin := volumetesting.GetTestVolumePluginMgr(t)
|
||||
asw := NewActualStateOfWorld("mynode" /* nodeName */, volumePluginMgr)
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
UID: "pod1uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "volume-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]}
|
||||
devicePath := "fake/device/path"
|
||||
generatedVolumeName, _ := volumehelper.GetUniqueVolumeNameFromSpec(plugin, volumeSpec)
|
||||
|
||||
// Act
|
||||
err := asw.MarkVolumeAsAttached(emptyVolumeName, volumeSpec, "" /* nodeName */, devicePath)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("MarkVolumeAsAttached failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
verifyVolumeExistsAsw(t, generatedVolumeName, true /* shouldExist */, asw)
|
||||
verifyVolumeExistsInUnmountedVolumes(t, generatedVolumeName, asw)
|
||||
verifyVolumeDoesntExistInGloballyMountedVolumes(t, generatedVolumeName, asw)
|
||||
}
|
||||
|
||||
// Calls MarkVolumeAsAttached() once to add volume, specifying a name --
|
||||
// establishes that the supplied volume name is used to register the volume
|
||||
// rather than the generated one.
|
||||
// Verifies newly added volume exists in GetUnmountedVolumes()
|
||||
// Verifies newly added volume doesn't exist in GetGloballyMountedVolumes()
|
||||
func Test_MarkVolumeAsAttached_SuppliedVolumeName_Positive_NewVolume(t *testing.T) {
|
||||
// Arrange
|
||||
volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t)
|
||||
asw := NewActualStateOfWorld("mynode" /* nodeName */, volumePluginMgr)
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
UID: "pod1uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "volume-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]}
|
||||
devicePath := "fake/device/path"
|
||||
volumeName := v1.UniqueVolumeName("this-would-never-be-a-volume-name")
|
||||
|
||||
// Act
|
||||
err := asw.MarkVolumeAsAttached(volumeName, volumeSpec, "" /* nodeName */, devicePath)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("MarkVolumeAsAttached failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
verifyVolumeExistsAsw(t, volumeName, true /* shouldExist */, asw)
|
||||
verifyVolumeExistsInUnmountedVolumes(t, volumeName, asw)
|
||||
verifyVolumeDoesntExistInGloballyMountedVolumes(t, volumeName, asw)
|
||||
}
|
||||
|
||||
// Calls MarkVolumeAsAttached() twice to add the same volume
|
||||
// Verifies second call doesn't fail
|
||||
// Verifies newly added volume exists in GetUnmountedVolumes()
|
||||
// Verifies newly added volume doesn't exist in GetGloballyMountedVolumes()
|
||||
func Test_MarkVolumeAsAttached_Positive_ExistingVolume(t *testing.T) {
|
||||
// Arrange
|
||||
volumePluginMgr, plugin := volumetesting.GetTestVolumePluginMgr(t)
|
||||
devicePath := "fake/device/path"
|
||||
asw := NewActualStateOfWorld("mynode" /* nodeName */, volumePluginMgr)
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
UID: "pod1uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "volume-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]}
|
||||
generatedVolumeName, _ := volumehelper.GetUniqueVolumeNameFromSpec(plugin, volumeSpec)
|
||||
|
||||
err := asw.MarkVolumeAsAttached(emptyVolumeName, volumeSpec, "" /* nodeName */, devicePath)
|
||||
if err != nil {
|
||||
t.Fatalf("MarkVolumeAsAttached failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
// Act
|
||||
err = asw.MarkVolumeAsAttached(emptyVolumeName, volumeSpec, "" /* nodeName */, devicePath)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("MarkVolumeAsAttached failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
verifyVolumeExistsAsw(t, generatedVolumeName, true /* shouldExist */, asw)
|
||||
verifyVolumeExistsInUnmountedVolumes(t, generatedVolumeName, asw)
|
||||
verifyVolumeDoesntExistInGloballyMountedVolumes(t, generatedVolumeName, asw)
|
||||
}
|
||||
|
||||
// Populates data struct with a volume
|
||||
// Calls AddPodToVolume() to add a pod to the volume
|
||||
// Verifies volume/pod combo exist using PodExistsInVolume()
|
||||
func Test_AddPodToVolume_Positive_ExistingVolumeNewNode(t *testing.T) {
|
||||
// Arrange
|
||||
volumePluginMgr, plugin := volumetesting.GetTestVolumePluginMgr(t)
|
||||
asw := NewActualStateOfWorld("mynode" /* nodeName */, volumePluginMgr)
|
||||
devicePath := "fake/device/path"
|
||||
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
UID: "pod1uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "volume-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]}
|
||||
generatedVolumeName, err := volumehelper.GetUniqueVolumeNameFromSpec(plugin, volumeSpec)
|
||||
|
||||
err = asw.MarkVolumeAsAttached(emptyVolumeName, volumeSpec, "" /* nodeName */, devicePath)
|
||||
if err != nil {
|
||||
t.Fatalf("MarkVolumeAsAttached failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
podName := volumehelper.GetUniquePodName(pod)
|
||||
|
||||
mounter, err := plugin.NewMounter(volumeSpec, pod, volume.VolumeOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("NewMounter failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
mapper, err := plugin.NewBlockVolumeMapper(volumeSpec, pod, volume.VolumeOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("NewBlockVolumeMapper failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
// Act
|
||||
err = asw.AddPodToVolume(
|
||||
podName, pod.UID, generatedVolumeName, mounter, mapper, volumeSpec.Name(), "" /* volumeGidValue */)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("AddPodToVolume failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
verifyVolumeExistsAsw(t, generatedVolumeName, true /* shouldExist */, asw)
|
||||
verifyVolumeDoesntExistInUnmountedVolumes(t, generatedVolumeName, asw)
|
||||
verifyVolumeDoesntExistInGloballyMountedVolumes(t, generatedVolumeName, asw)
|
||||
verifyPodExistsInVolumeAsw(t, podName, generatedVolumeName, "fake/device/path" /* expectedDevicePath */, asw)
|
||||
}
|
||||
|
||||
// Populates data struct with a volume
|
||||
// Calls AddPodToVolume() twice to add the same pod to the volume
|
||||
// Verifies volume/pod combo exist using PodExistsInVolume() and the second call
|
||||
// did not fail.
|
||||
func Test_AddPodToVolume_Positive_ExistingVolumeExistingNode(t *testing.T) {
|
||||
// Arrange
|
||||
volumePluginMgr, plugin := volumetesting.GetTestVolumePluginMgr(t)
|
||||
asw := NewActualStateOfWorld("mynode" /* nodeName */, volumePluginMgr)
|
||||
devicePath := "fake/device/path"
|
||||
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
UID: "pod1uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "volume-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]}
|
||||
generatedVolumeName, err := volumehelper.GetUniqueVolumeNameFromSpec(
|
||||
plugin, volumeSpec)
|
||||
|
||||
err = asw.MarkVolumeAsAttached(emptyVolumeName, volumeSpec, "" /* nodeName */, devicePath)
|
||||
if err != nil {
|
||||
t.Fatalf("MarkVolumeAsAttached failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
podName := volumehelper.GetUniquePodName(pod)
|
||||
|
||||
mounter, err := plugin.NewMounter(volumeSpec, pod, volume.VolumeOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("NewMounter failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
mapper, err := plugin.NewBlockVolumeMapper(volumeSpec, pod, volume.VolumeOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("NewBlockVolumeMapper failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
err = asw.AddPodToVolume(
|
||||
podName, pod.UID, generatedVolumeName, mounter, mapper, volumeSpec.Name(), "" /* volumeGidValue */)
|
||||
if err != nil {
|
||||
t.Fatalf("AddPodToVolume failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
// Act
|
||||
err = asw.AddPodToVolume(
|
||||
podName, pod.UID, generatedVolumeName, mounter, mapper, volumeSpec.Name(), "" /* volumeGidValue */)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("AddPodToVolume failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
verifyVolumeExistsAsw(t, generatedVolumeName, true /* shouldExist */, asw)
|
||||
verifyVolumeDoesntExistInUnmountedVolumes(t, generatedVolumeName, asw)
|
||||
verifyVolumeDoesntExistInGloballyMountedVolumes(t, generatedVolumeName, asw)
|
||||
verifyPodExistsInVolumeAsw(t, podName, generatedVolumeName, "fake/device/path" /* expectedDevicePath */, asw)
|
||||
}
|
||||
|
||||
// Calls AddPodToVolume() to add pod to empty data stuct
|
||||
// Verifies call fails with "volume does not exist" error.
|
||||
func Test_AddPodToVolume_Negative_VolumeDoesntExist(t *testing.T) {
|
||||
// Arrange
|
||||
volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t)
|
||||
asw := NewActualStateOfWorld("mynode" /* nodeName */, volumePluginMgr)
|
||||
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
UID: "pod1uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "volume-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]}
|
||||
plugin, err := volumePluginMgr.FindPluginBySpec(volumeSpec)
|
||||
if err != nil {
|
||||
t.Fatalf(
|
||||
"volumePluginMgr.FindPluginBySpec failed to find volume plugin for %#v with: %v",
|
||||
volumeSpec,
|
||||
err)
|
||||
}
|
||||
|
||||
blockplugin, err := volumePluginMgr.FindMapperPluginBySpec(volumeSpec)
|
||||
if err != nil {
|
||||
t.Fatalf(
|
||||
"volumePluginMgr.FindMapperPluginBySpec failed to find volume plugin for %#v with: %v",
|
||||
volumeSpec,
|
||||
err)
|
||||
}
|
||||
|
||||
volumeName, err := volumehelper.GetUniqueVolumeNameFromSpec(
|
||||
plugin, volumeSpec)
|
||||
|
||||
podName := volumehelper.GetUniquePodName(pod)
|
||||
|
||||
mounter, err := plugin.NewMounter(volumeSpec, pod, volume.VolumeOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("NewMounter failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
mapper, err := blockplugin.NewBlockVolumeMapper(volumeSpec, pod, volume.VolumeOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("NewBlockVolumeMapper failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
// Act
|
||||
err = asw.AddPodToVolume(
|
||||
podName, pod.UID, volumeName, mounter, mapper, volumeSpec.Name(), "" /* volumeGidValue */)
|
||||
|
||||
// Assert
|
||||
if err == nil {
|
||||
t.Fatalf("AddPodToVolume did not fail. Expected: <\"no volume with the name ... exists in the list of attached volumes\"> Actual: <no error>")
|
||||
}
|
||||
|
||||
verifyVolumeExistsAsw(t, volumeName, false /* shouldExist */, asw)
|
||||
verifyVolumeDoesntExistInUnmountedVolumes(t, volumeName, asw)
|
||||
verifyVolumeDoesntExistInGloballyMountedVolumes(t, volumeName, asw)
|
||||
verifyPodDoesntExistInVolumeAsw(
|
||||
t,
|
||||
podName,
|
||||
volumeName,
|
||||
false, /* expectVolumeToExist */
|
||||
asw)
|
||||
}
|
||||
|
||||
// Calls MarkVolumeAsAttached() once to add volume
|
||||
// Calls MarkDeviceAsMounted() to mark volume as globally mounted.
|
||||
// Verifies newly added volume exists in GetUnmountedVolumes()
|
||||
// Verifies newly added volume exists in GetGloballyMountedVolumes()
|
||||
func Test_MarkDeviceAsMounted_Positive_NewVolume(t *testing.T) {
|
||||
// Arrange
|
||||
volumePluginMgr, plugin := volumetesting.GetTestVolumePluginMgr(t)
|
||||
asw := NewActualStateOfWorld("mynode" /* nodeName */, volumePluginMgr)
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
UID: "pod1uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "volume-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]}
|
||||
devicePath := "fake/device/path"
|
||||
generatedVolumeName, err := volumehelper.GetUniqueVolumeNameFromSpec(plugin, volumeSpec)
|
||||
|
||||
err = asw.MarkVolumeAsAttached(emptyVolumeName, volumeSpec, "" /* nodeName */, devicePath)
|
||||
if err != nil {
|
||||
t.Fatalf("MarkVolumeAsAttached failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
// Act
|
||||
err = asw.MarkDeviceAsMounted(generatedVolumeName)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("MarkDeviceAsMounted failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
verifyVolumeExistsAsw(t, generatedVolumeName, true /* shouldExist */, asw)
|
||||
verifyVolumeExistsInUnmountedVolumes(t, generatedVolumeName, asw)
|
||||
verifyVolumeExistsInGloballyMountedVolumes(t, generatedVolumeName, asw)
|
||||
}
|
||||
|
||||
func verifyVolumeExistsInGloballyMountedVolumes(
|
||||
t *testing.T, expectedVolumeName v1.UniqueVolumeName, asw ActualStateOfWorld) {
|
||||
globallyMountedVolumes := asw.GetGloballyMountedVolumes()
|
||||
for _, volume := range globallyMountedVolumes {
|
||||
if volume.VolumeName == expectedVolumeName {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
t.Fatalf(
|
||||
"Could not find volume %v in the list of GloballyMountedVolumes for actual state of world %+v",
|
||||
expectedVolumeName,
|
||||
globallyMountedVolumes)
|
||||
}
|
||||
|
||||
func verifyVolumeDoesntExistInGloballyMountedVolumes(
|
||||
t *testing.T, volumeToCheck v1.UniqueVolumeName, asw ActualStateOfWorld) {
|
||||
globallyMountedVolumes := asw.GetGloballyMountedVolumes()
|
||||
for _, volume := range globallyMountedVolumes {
|
||||
if volume.VolumeName == volumeToCheck {
|
||||
t.Fatalf(
|
||||
"Found volume %v in the list of GloballyMountedVolumes. Expected it not to exist.",
|
||||
volumeToCheck)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyVolumeExistsAsw(
|
||||
t *testing.T,
|
||||
expectedVolumeName v1.UniqueVolumeName,
|
||||
shouldExist bool,
|
||||
asw ActualStateOfWorld) {
|
||||
volumeExists := asw.VolumeExists(expectedVolumeName)
|
||||
if shouldExist != volumeExists {
|
||||
t.Fatalf(
|
||||
"VolumeExists(%q) response incorrect. Expected: <%v> Actual: <%v>",
|
||||
expectedVolumeName,
|
||||
shouldExist,
|
||||
volumeExists)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyVolumeExistsInUnmountedVolumes(
|
||||
t *testing.T, expectedVolumeName v1.UniqueVolumeName, asw ActualStateOfWorld) {
|
||||
unmountedVolumes := asw.GetUnmountedVolumes()
|
||||
for _, volume := range unmountedVolumes {
|
||||
if volume.VolumeName == expectedVolumeName {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
t.Fatalf(
|
||||
"Could not find volume %v in the list of UnmountedVolumes for actual state of world %+v",
|
||||
expectedVolumeName,
|
||||
unmountedVolumes)
|
||||
}
|
||||
|
||||
func verifyVolumeDoesntExistInUnmountedVolumes(
|
||||
t *testing.T, volumeToCheck v1.UniqueVolumeName, asw ActualStateOfWorld) {
|
||||
unmountedVolumes := asw.GetUnmountedVolumes()
|
||||
for _, volume := range unmountedVolumes {
|
||||
if volume.VolumeName == volumeToCheck {
|
||||
t.Fatalf(
|
||||
"Found volume %v in the list of UnmountedVolumes. Expected it not to exist.",
|
||||
volumeToCheck)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyPodExistsInVolumeAsw(
|
||||
t *testing.T,
|
||||
expectedPodName volumetypes.UniquePodName,
|
||||
expectedVolumeName v1.UniqueVolumeName,
|
||||
expectedDevicePath string,
|
||||
asw ActualStateOfWorld) {
|
||||
podExistsInVolume, devicePath, err :=
|
||||
asw.PodExistsInVolume(expectedPodName, expectedVolumeName)
|
||||
if err != nil {
|
||||
t.Fatalf(
|
||||
"ASW PodExistsInVolume failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
if !podExistsInVolume {
|
||||
t.Fatalf(
|
||||
"ASW PodExistsInVolume result invalid. Expected: <true> Actual: <%v>",
|
||||
podExistsInVolume)
|
||||
}
|
||||
|
||||
if devicePath != expectedDevicePath {
|
||||
t.Fatalf(
|
||||
"Invalid devicePath. Expected: <%q> Actual: <%q> ",
|
||||
expectedDevicePath,
|
||||
devicePath)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyPodDoesntExistInVolumeAsw(
|
||||
t *testing.T,
|
||||
podToCheck volumetypes.UniquePodName,
|
||||
volumeToCheck v1.UniqueVolumeName,
|
||||
expectVolumeToExist bool,
|
||||
asw ActualStateOfWorld) {
|
||||
podExistsInVolume, devicePath, err :=
|
||||
asw.PodExistsInVolume(podToCheck, volumeToCheck)
|
||||
if !expectVolumeToExist && err == nil {
|
||||
t.Fatalf(
|
||||
"ASW PodExistsInVolume did not return error. Expected: <error indicating volume does not exist> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
if expectVolumeToExist && err != nil {
|
||||
t.Fatalf(
|
||||
"ASW PodExistsInVolume failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
if podExistsInVolume {
|
||||
t.Fatalf(
|
||||
"ASW PodExistsInVolume result invalid. Expected: <false> Actual: <%v>",
|
||||
podExistsInVolume)
|
||||
}
|
||||
|
||||
if devicePath != "" {
|
||||
t.Fatalf(
|
||||
"Invalid devicePath. Expected: <\"\"> Actual: <%q> ",
|
||||
devicePath)
|
||||
}
|
||||
}
|
356
vendor/k8s.io/kubernetes/pkg/kubelet/volumemanager/cache/desired_state_of_world.go
generated
vendored
Normal file
356
vendor/k8s.io/kubernetes/pkg/kubelet/volumemanager/cache/desired_state_of_world.go
generated
vendored
Normal file
@ -0,0 +1,356 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 cache implements data structures used by the kubelet volume manager to
|
||||
keep track of attached volumes and the pods that mounted them.
|
||||
*/
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
||||
"k8s.io/kubernetes/pkg/volume/util/types"
|
||||
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
|
||||
)
|
||||
|
||||
// DesiredStateOfWorld defines a set of thread-safe operations for the kubelet
|
||||
// volume manager's desired state of the world cache.
|
||||
// This cache contains volumes->pods i.e. a set of all volumes that should be
|
||||
// attached to this node and the pods that reference them and should mount the
|
||||
// volume.
|
||||
// Note: This is distinct from the DesiredStateOfWorld implemented by the
|
||||
// attach/detach controller. They both keep track of different objects. This
|
||||
// contains kubelet volume manager specific state.
|
||||
type DesiredStateOfWorld interface {
|
||||
// AddPodToVolume adds the given pod to the given volume in the cache
|
||||
// indicating the specified pod should mount the specified volume.
|
||||
// A unique volumeName is generated from the volumeSpec and returned on
|
||||
// success.
|
||||
// If no volume plugin can support the given volumeSpec or more than one
|
||||
// plugin can support it, an error is returned.
|
||||
// If a volume with the name volumeName does not exist in the list of
|
||||
// volumes that should be attached to this node, the volume is implicitly
|
||||
// added.
|
||||
// If a pod with the same unique name already exists under the specified
|
||||
// volume, this is a no-op.
|
||||
AddPodToVolume(podName types.UniquePodName, pod *v1.Pod, volumeSpec *volume.Spec, outerVolumeSpecName string, volumeGidValue string) (v1.UniqueVolumeName, error)
|
||||
|
||||
// MarkVolumesReportedInUse sets the ReportedInUse value to true for the
|
||||
// reportedVolumes. For volumes not in the reportedVolumes list, the
|
||||
// ReportedInUse value is reset to false. The default ReportedInUse value
|
||||
// for a newly created volume is false.
|
||||
// When set to true this value indicates that the volume was successfully
|
||||
// added to the VolumesInUse field in the node's status. Mount operation needs
|
||||
// to check this value before issuing the operation.
|
||||
// If a volume in the reportedVolumes list does not exist in the list of
|
||||
// volumes that should be attached to this node, it is skipped without error.
|
||||
MarkVolumesReportedInUse(reportedVolumes []v1.UniqueVolumeName)
|
||||
|
||||
// DeletePodFromVolume removes the given pod from the given volume in the
|
||||
// cache indicating the specified pod no longer requires the specified
|
||||
// volume.
|
||||
// If a pod with the same unique name does not exist under the specified
|
||||
// volume, this is a no-op.
|
||||
// If a volume with the name volumeName does not exist in the list of
|
||||
// attached volumes, this is a no-op.
|
||||
// If after deleting the pod, the specified volume contains no other child
|
||||
// pods, the volume is also deleted.
|
||||
DeletePodFromVolume(podName types.UniquePodName, volumeName v1.UniqueVolumeName)
|
||||
|
||||
// VolumeExists returns true if the given volume exists in the list of
|
||||
// volumes that should be attached to this node.
|
||||
// If a pod with the same unique name does not exist under the specified
|
||||
// volume, false is returned.
|
||||
VolumeExists(volumeName v1.UniqueVolumeName) bool
|
||||
|
||||
// PodExistsInVolume returns true if the given pod exists in the list of
|
||||
// podsToMount for the given volume in the cache.
|
||||
// If a pod with the same unique name does not exist under the specified
|
||||
// volume, false is returned.
|
||||
// If a volume with the name volumeName does not exist in the list of
|
||||
// attached volumes, false is returned.
|
||||
PodExistsInVolume(podName types.UniquePodName, volumeName v1.UniqueVolumeName) bool
|
||||
|
||||
// GetVolumesToMount generates and returns a list of volumes that should be
|
||||
// attached to this node and the pods they should be mounted to based on the
|
||||
// current desired state of the world.
|
||||
GetVolumesToMount() []VolumeToMount
|
||||
|
||||
// GetPods generates and returns a map of pods in which map is indexed
|
||||
// with pod's unique name. This map can be used to determine which pod is currently
|
||||
// in desired state of world.
|
||||
GetPods() map[types.UniquePodName]bool
|
||||
}
|
||||
|
||||
// VolumeToMount represents a volume that is attached to this node and needs to
|
||||
// be mounted to PodName.
|
||||
type VolumeToMount struct {
|
||||
operationexecutor.VolumeToMount
|
||||
}
|
||||
|
||||
// NewDesiredStateOfWorld returns a new instance of DesiredStateOfWorld.
|
||||
func NewDesiredStateOfWorld(volumePluginMgr *volume.VolumePluginMgr) DesiredStateOfWorld {
|
||||
return &desiredStateOfWorld{
|
||||
volumesToMount: make(map[v1.UniqueVolumeName]volumeToMount),
|
||||
volumePluginMgr: volumePluginMgr,
|
||||
}
|
||||
}
|
||||
|
||||
type desiredStateOfWorld struct {
|
||||
// volumesToMount is a map containing the set of volumes that should be
|
||||
// attached to this node and mounted to the pods referencing it. The key in
|
||||
// the map is the name of the volume and the value is a volume object
|
||||
// containing more information about the volume.
|
||||
volumesToMount map[v1.UniqueVolumeName]volumeToMount
|
||||
// volumePluginMgr is the volume plugin manager used to create volume
|
||||
// plugin objects.
|
||||
volumePluginMgr *volume.VolumePluginMgr
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// The volume object represents a volume that should be attached to this node,
|
||||
// and mounted to podsToMount.
|
||||
type volumeToMount struct {
|
||||
// volumeName contains the unique identifier for this volume.
|
||||
volumeName v1.UniqueVolumeName
|
||||
|
||||
// podsToMount is a map containing the set of pods that reference this
|
||||
// volume and should mount it once it is attached. The key in the map is
|
||||
// the name of the pod and the value is a pod object containing more
|
||||
// information about the pod.
|
||||
podsToMount map[types.UniquePodName]podToMount
|
||||
|
||||
// pluginIsAttachable indicates that the plugin for this volume implements
|
||||
// the volume.Attacher interface
|
||||
pluginIsAttachable bool
|
||||
|
||||
// volumeGidValue contains the value of the GID annotation, if present.
|
||||
volumeGidValue string
|
||||
|
||||
// reportedInUse indicates that the volume was successfully added to the
|
||||
// VolumesInUse field in the node's status.
|
||||
reportedInUse bool
|
||||
}
|
||||
|
||||
// The pod object represents a pod that references the underlying volume and
|
||||
// should mount it once it is attached.
|
||||
type podToMount struct {
|
||||
// podName contains the name of this pod.
|
||||
podName types.UniquePodName
|
||||
|
||||
// Pod to mount the volume to. Used to create NewMounter.
|
||||
pod *v1.Pod
|
||||
|
||||
// volume spec containing the specification for this volume. Used to
|
||||
// generate the volume plugin object, and passed to plugin methods.
|
||||
// For non-PVC volumes this is the same as defined in the pod object. For
|
||||
// PVC volumes it is from the dereferenced PV object.
|
||||
spec *volume.Spec
|
||||
|
||||
// outerVolumeSpecName is the volume.Spec.Name() of the volume as referenced
|
||||
// directly in the pod. If the volume was referenced through a persistent
|
||||
// volume claim, this contains the volume.Spec.Name() of the persistent
|
||||
// volume claim
|
||||
outerVolumeSpecName string
|
||||
}
|
||||
|
||||
func (dsw *desiredStateOfWorld) AddPodToVolume(
|
||||
podName types.UniquePodName,
|
||||
pod *v1.Pod,
|
||||
volumeSpec *volume.Spec,
|
||||
outerVolumeSpecName string,
|
||||
volumeGidValue string) (v1.UniqueVolumeName, error) {
|
||||
dsw.Lock()
|
||||
defer dsw.Unlock()
|
||||
|
||||
volumePlugin, err := dsw.volumePluginMgr.FindPluginBySpec(volumeSpec)
|
||||
if err != nil || volumePlugin == nil {
|
||||
return "", fmt.Errorf(
|
||||
"failed to get Plugin from volumeSpec for volume %q err=%v",
|
||||
volumeSpec.Name(),
|
||||
err)
|
||||
}
|
||||
|
||||
var volumeName v1.UniqueVolumeName
|
||||
|
||||
// The unique volume name used depends on whether the volume is attachable
|
||||
// or not.
|
||||
attachable := dsw.isAttachableVolume(volumeSpec)
|
||||
if attachable {
|
||||
// For attachable volumes, use the unique volume name as reported by
|
||||
// the plugin.
|
||||
volumeName, err =
|
||||
volumehelper.GetUniqueVolumeNameFromSpec(volumePlugin, volumeSpec)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf(
|
||||
"failed to GetUniqueVolumeNameFromSpec for volumeSpec %q using volume plugin %q err=%v",
|
||||
volumeSpec.Name(),
|
||||
volumePlugin.GetPluginName(),
|
||||
err)
|
||||
}
|
||||
} else {
|
||||
// For non-attachable volumes, generate a unique name based on the pod
|
||||
// namespace and name and the name of the volume within the pod.
|
||||
volumeName = volumehelper.GetUniqueVolumeNameForNonAttachableVolume(podName, volumePlugin, volumeSpec)
|
||||
}
|
||||
|
||||
volumeObj, volumeExists := dsw.volumesToMount[volumeName]
|
||||
if !volumeExists {
|
||||
volumeObj = volumeToMount{
|
||||
volumeName: volumeName,
|
||||
podsToMount: make(map[types.UniquePodName]podToMount),
|
||||
pluginIsAttachable: attachable,
|
||||
volumeGidValue: volumeGidValue,
|
||||
reportedInUse: false,
|
||||
}
|
||||
dsw.volumesToMount[volumeName] = volumeObj
|
||||
}
|
||||
|
||||
// Create new podToMount object. If it already exists, it is refreshed with
|
||||
// updated values (this is required for volumes that require remounting on
|
||||
// pod update, like Downward API volumes).
|
||||
dsw.volumesToMount[volumeName].podsToMount[podName] = podToMount{
|
||||
podName: podName,
|
||||
pod: pod,
|
||||
spec: volumeSpec,
|
||||
outerVolumeSpecName: outerVolumeSpecName,
|
||||
}
|
||||
|
||||
return volumeName, nil
|
||||
}
|
||||
|
||||
func (dsw *desiredStateOfWorld) MarkVolumesReportedInUse(
|
||||
reportedVolumes []v1.UniqueVolumeName) {
|
||||
dsw.Lock()
|
||||
defer dsw.Unlock()
|
||||
|
||||
reportedVolumesMap := make(
|
||||
map[v1.UniqueVolumeName]bool, len(reportedVolumes) /* capacity */)
|
||||
|
||||
for _, reportedVolume := range reportedVolumes {
|
||||
reportedVolumesMap[reportedVolume] = true
|
||||
}
|
||||
|
||||
for volumeName, volumeObj := range dsw.volumesToMount {
|
||||
_, volumeReported := reportedVolumesMap[volumeName]
|
||||
volumeObj.reportedInUse = volumeReported
|
||||
dsw.volumesToMount[volumeName] = volumeObj
|
||||
}
|
||||
}
|
||||
|
||||
func (dsw *desiredStateOfWorld) DeletePodFromVolume(
|
||||
podName types.UniquePodName, volumeName v1.UniqueVolumeName) {
|
||||
dsw.Lock()
|
||||
defer dsw.Unlock()
|
||||
|
||||
volumeObj, volumeExists := dsw.volumesToMount[volumeName]
|
||||
if !volumeExists {
|
||||
return
|
||||
}
|
||||
|
||||
if _, podExists := volumeObj.podsToMount[podName]; !podExists {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete pod if it exists
|
||||
delete(dsw.volumesToMount[volumeName].podsToMount, podName)
|
||||
|
||||
if len(dsw.volumesToMount[volumeName].podsToMount) == 0 {
|
||||
// Delete volume if no child pods left
|
||||
delete(dsw.volumesToMount, volumeName)
|
||||
}
|
||||
}
|
||||
|
||||
func (dsw *desiredStateOfWorld) VolumeExists(
|
||||
volumeName v1.UniqueVolumeName) bool {
|
||||
dsw.RLock()
|
||||
defer dsw.RUnlock()
|
||||
|
||||
_, volumeExists := dsw.volumesToMount[volumeName]
|
||||
return volumeExists
|
||||
}
|
||||
|
||||
func (dsw *desiredStateOfWorld) PodExistsInVolume(
|
||||
podName types.UniquePodName, volumeName v1.UniqueVolumeName) bool {
|
||||
dsw.RLock()
|
||||
defer dsw.RUnlock()
|
||||
|
||||
volumeObj, volumeExists := dsw.volumesToMount[volumeName]
|
||||
if !volumeExists {
|
||||
return false
|
||||
}
|
||||
|
||||
_, podExists := volumeObj.podsToMount[podName]
|
||||
return podExists
|
||||
}
|
||||
|
||||
func (dsw *desiredStateOfWorld) GetPods() map[types.UniquePodName]bool {
|
||||
dsw.RLock()
|
||||
defer dsw.RUnlock()
|
||||
|
||||
podList := make(map[types.UniquePodName]bool)
|
||||
for _, volumeObj := range dsw.volumesToMount {
|
||||
for podName := range volumeObj.podsToMount {
|
||||
if !podList[podName] {
|
||||
podList[podName] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return podList
|
||||
}
|
||||
|
||||
func (dsw *desiredStateOfWorld) GetVolumesToMount() []VolumeToMount {
|
||||
dsw.RLock()
|
||||
defer dsw.RUnlock()
|
||||
|
||||
volumesToMount := make([]VolumeToMount, 0 /* len */, len(dsw.volumesToMount) /* cap */)
|
||||
for volumeName, volumeObj := range dsw.volumesToMount {
|
||||
for podName, podObj := range volumeObj.podsToMount {
|
||||
volumesToMount = append(
|
||||
volumesToMount,
|
||||
VolumeToMount{
|
||||
VolumeToMount: operationexecutor.VolumeToMount{
|
||||
VolumeName: volumeName,
|
||||
PodName: podName,
|
||||
Pod: podObj.pod,
|
||||
VolumeSpec: podObj.spec,
|
||||
PluginIsAttachable: volumeObj.pluginIsAttachable,
|
||||
OuterVolumeSpecName: podObj.outerVolumeSpecName,
|
||||
VolumeGidValue: volumeObj.volumeGidValue,
|
||||
ReportedInUse: volumeObj.reportedInUse}})
|
||||
}
|
||||
}
|
||||
return volumesToMount
|
||||
}
|
||||
|
||||
func (dsw *desiredStateOfWorld) isAttachableVolume(volumeSpec *volume.Spec) bool {
|
||||
attachableVolumePlugin, _ :=
|
||||
dsw.volumePluginMgr.FindAttachablePluginBySpec(volumeSpec)
|
||||
if attachableVolumePlugin != nil {
|
||||
volumeAttacher, err := attachableVolumePlugin.NewAttacher()
|
||||
if err == nil && volumeAttacher != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
382
vendor/k8s.io/kubernetes/pkg/kubelet/volumemanager/cache/desired_state_of_world_test.go
generated
vendored
Normal file
382
vendor/k8s.io/kubernetes/pkg/kubelet/volumemanager/cache/desired_state_of_world_test.go
generated
vendored
Normal file
@ -0,0 +1,382 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
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 cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volumetesting "k8s.io/kubernetes/pkg/volume/testing"
|
||||
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
|
||||
"k8s.io/kubernetes/pkg/volume/util/volumehelper"
|
||||
)
|
||||
|
||||
// Calls AddPodToVolume() to add new pod to new volume
|
||||
// Verifies newly added pod/volume exists via
|
||||
// PodExistsInVolume() VolumeExists() and GetVolumesToMount()
|
||||
func Test_AddPodToVolume_Positive_NewPodNewVolume(t *testing.T) {
|
||||
// Arrange
|
||||
volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t)
|
||||
dsw := NewDesiredStateOfWorld(volumePluginMgr)
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod3",
|
||||
UID: "pod3uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "volume-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]}
|
||||
podName := volumehelper.GetUniquePodName(pod)
|
||||
|
||||
// Act
|
||||
generatedVolumeName, err := dsw.AddPodToVolume(
|
||||
podName, pod, volumeSpec, volumeSpec.Name(), "" /* volumeGidValue */)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("AddPodToVolume failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
verifyVolumeExistsDsw(t, generatedVolumeName, dsw)
|
||||
verifyVolumeExistsInVolumesToMount(
|
||||
t, generatedVolumeName, false /* expectReportedInUse */, dsw)
|
||||
verifyPodExistsInVolumeDsw(t, podName, generatedVolumeName, dsw)
|
||||
}
|
||||
|
||||
// Calls AddPodToVolume() twice to add the same pod to the same volume
|
||||
// Verifies newly added pod/volume exists via
|
||||
// PodExistsInVolume() VolumeExists() and GetVolumesToMount() and no errors.
|
||||
func Test_AddPodToVolume_Positive_ExistingPodExistingVolume(t *testing.T) {
|
||||
// Arrange
|
||||
volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t)
|
||||
dsw := NewDesiredStateOfWorld(volumePluginMgr)
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod3",
|
||||
UID: "pod3uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "volume-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]}
|
||||
podName := volumehelper.GetUniquePodName(pod)
|
||||
|
||||
// Act
|
||||
generatedVolumeName, err := dsw.AddPodToVolume(
|
||||
podName, pod, volumeSpec, volumeSpec.Name(), "" /* volumeGidValue */)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("AddPodToVolume failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
verifyVolumeExistsDsw(t, generatedVolumeName, dsw)
|
||||
verifyVolumeExistsInVolumesToMount(
|
||||
t, generatedVolumeName, false /* expectReportedInUse */, dsw)
|
||||
verifyPodExistsInVolumeDsw(t, podName, generatedVolumeName, dsw)
|
||||
}
|
||||
|
||||
// Populates data struct with a new volume/pod
|
||||
// Calls DeletePodFromVolume() to removes the pod
|
||||
// Verifies newly added pod/volume are deleted
|
||||
func Test_DeletePodFromVolume_Positive_PodExistsVolumeExists(t *testing.T) {
|
||||
// Arrange
|
||||
volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t)
|
||||
dsw := NewDesiredStateOfWorld(volumePluginMgr)
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod3",
|
||||
UID: "pod3uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "volume-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
volumeSpec := &volume.Spec{Volume: &pod.Spec.Volumes[0]}
|
||||
podName := volumehelper.GetUniquePodName(pod)
|
||||
generatedVolumeName, err := dsw.AddPodToVolume(
|
||||
podName, pod, volumeSpec, volumeSpec.Name(), "" /* volumeGidValue */)
|
||||
if err != nil {
|
||||
t.Fatalf("AddPodToVolume failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
verifyVolumeExistsDsw(t, generatedVolumeName, dsw)
|
||||
verifyVolumeExistsInVolumesToMount(
|
||||
t, generatedVolumeName, false /* expectReportedInUse */, dsw)
|
||||
verifyPodExistsInVolumeDsw(t, podName, generatedVolumeName, dsw)
|
||||
|
||||
// Act
|
||||
dsw.DeletePodFromVolume(podName, generatedVolumeName)
|
||||
|
||||
// Assert
|
||||
verifyVolumeDoesntExist(t, generatedVolumeName, dsw)
|
||||
verifyVolumeDoesntExistInVolumesToMount(t, generatedVolumeName, dsw)
|
||||
verifyPodDoesntExistInVolumeDsw(t, podName, generatedVolumeName, dsw)
|
||||
}
|
||||
|
||||
// Calls AddPodToVolume() to add three new volumes to data struct
|
||||
// Verifies newly added pod/volume exists via PodExistsInVolume()
|
||||
// VolumeExists() and GetVolumesToMount()
|
||||
// Marks only second volume as reported in use.
|
||||
// Verifies only that volume is marked reported in use
|
||||
// Marks only first volume as reported in use.
|
||||
// Verifies only that volume is marked reported in use
|
||||
func Test_MarkVolumesReportedInUse_Positive_NewPodNewVolume(t *testing.T) {
|
||||
// Arrange
|
||||
volumePluginMgr, _ := volumetesting.GetTestVolumePluginMgr(t)
|
||||
dsw := NewDesiredStateOfWorld(volumePluginMgr)
|
||||
|
||||
pod1 := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
UID: "pod1uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "volume1-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
volume1Spec := &volume.Spec{Volume: &pod1.Spec.Volumes[0]}
|
||||
pod1Name := volumehelper.GetUniquePodName(pod1)
|
||||
|
||||
pod2 := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod2",
|
||||
UID: "pod2uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "volume2-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
volume2Spec := &volume.Spec{Volume: &pod2.Spec.Volumes[0]}
|
||||
pod2Name := volumehelper.GetUniquePodName(pod2)
|
||||
|
||||
pod3 := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod3",
|
||||
UID: "pod3uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "volume3-name",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
volume3Spec := &volume.Spec{Volume: &pod3.Spec.Volumes[0]}
|
||||
pod3Name := volumehelper.GetUniquePodName(pod3)
|
||||
|
||||
generatedVolume1Name, err := dsw.AddPodToVolume(
|
||||
pod1Name, pod1, volume1Spec, volume1Spec.Name(), "" /* volumeGidValue */)
|
||||
if err != nil {
|
||||
t.Fatalf("AddPodToVolume failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
generatedVolume2Name, err := dsw.AddPodToVolume(
|
||||
pod2Name, pod2, volume2Spec, volume2Spec.Name(), "" /* volumeGidValue */)
|
||||
if err != nil {
|
||||
t.Fatalf("AddPodToVolume failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
generatedVolume3Name, err := dsw.AddPodToVolume(
|
||||
pod3Name, pod3, volume3Spec, volume3Spec.Name(), "" /* volumeGidValue */)
|
||||
if err != nil {
|
||||
t.Fatalf("AddPodToVolume failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
// Act
|
||||
volumesReportedInUse := []v1.UniqueVolumeName{generatedVolume2Name}
|
||||
dsw.MarkVolumesReportedInUse(volumesReportedInUse)
|
||||
|
||||
// Assert
|
||||
verifyVolumeExistsDsw(t, generatedVolume1Name, dsw)
|
||||
verifyVolumeExistsInVolumesToMount(
|
||||
t, generatedVolume1Name, false /* expectReportedInUse */, dsw)
|
||||
verifyPodExistsInVolumeDsw(t, pod1Name, generatedVolume1Name, dsw)
|
||||
verifyVolumeExistsDsw(t, generatedVolume2Name, dsw)
|
||||
verifyVolumeExistsInVolumesToMount(
|
||||
t, generatedVolume2Name, true /* expectReportedInUse */, dsw)
|
||||
verifyPodExistsInVolumeDsw(t, pod2Name, generatedVolume2Name, dsw)
|
||||
verifyVolumeExistsDsw(t, generatedVolume3Name, dsw)
|
||||
verifyVolumeExistsInVolumesToMount(
|
||||
t, generatedVolume3Name, false /* expectReportedInUse */, dsw)
|
||||
verifyPodExistsInVolumeDsw(t, pod3Name, generatedVolume3Name, dsw)
|
||||
|
||||
// Act
|
||||
volumesReportedInUse = []v1.UniqueVolumeName{generatedVolume3Name}
|
||||
dsw.MarkVolumesReportedInUse(volumesReportedInUse)
|
||||
|
||||
// Assert
|
||||
verifyVolumeExistsDsw(t, generatedVolume1Name, dsw)
|
||||
verifyVolumeExistsInVolumesToMount(
|
||||
t, generatedVolume1Name, false /* expectReportedInUse */, dsw)
|
||||
verifyPodExistsInVolumeDsw(t, pod1Name, generatedVolume1Name, dsw)
|
||||
verifyVolumeExistsDsw(t, generatedVolume2Name, dsw)
|
||||
verifyVolumeExistsInVolumesToMount(
|
||||
t, generatedVolume2Name, false /* expectReportedInUse */, dsw)
|
||||
verifyPodExistsInVolumeDsw(t, pod2Name, generatedVolume2Name, dsw)
|
||||
verifyVolumeExistsDsw(t, generatedVolume3Name, dsw)
|
||||
verifyVolumeExistsInVolumesToMount(
|
||||
t, generatedVolume3Name, true /* expectReportedInUse */, dsw)
|
||||
verifyPodExistsInVolumeDsw(t, pod3Name, generatedVolume3Name, dsw)
|
||||
}
|
||||
|
||||
func verifyVolumeExistsDsw(
|
||||
t *testing.T, expectedVolumeName v1.UniqueVolumeName, dsw DesiredStateOfWorld) {
|
||||
volumeExists := dsw.VolumeExists(expectedVolumeName)
|
||||
if !volumeExists {
|
||||
t.Fatalf(
|
||||
"VolumeExists(%q) failed. Expected: <true> Actual: <%v>",
|
||||
expectedVolumeName,
|
||||
volumeExists)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyVolumeDoesntExist(
|
||||
t *testing.T, expectedVolumeName v1.UniqueVolumeName, dsw DesiredStateOfWorld) {
|
||||
volumeExists := dsw.VolumeExists(expectedVolumeName)
|
||||
if volumeExists {
|
||||
t.Fatalf(
|
||||
"VolumeExists(%q) returned incorrect value. Expected: <false> Actual: <%v>",
|
||||
expectedVolumeName,
|
||||
volumeExists)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyVolumeExistsInVolumesToMount(
|
||||
t *testing.T,
|
||||
expectedVolumeName v1.UniqueVolumeName,
|
||||
expectReportedInUse bool,
|
||||
dsw DesiredStateOfWorld) {
|
||||
volumesToMount := dsw.GetVolumesToMount()
|
||||
for _, volume := range volumesToMount {
|
||||
if volume.VolumeName == expectedVolumeName {
|
||||
if volume.ReportedInUse != expectReportedInUse {
|
||||
t.Fatalf(
|
||||
"Found volume %v in the list of VolumesToMount, but ReportedInUse incorrect. Expected: <%v> Actual: <%v>",
|
||||
expectedVolumeName,
|
||||
expectReportedInUse,
|
||||
volume.ReportedInUse)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
t.Fatalf(
|
||||
"Could not find volume %v in the list of desired state of world volumes to mount %+v",
|
||||
expectedVolumeName,
|
||||
volumesToMount)
|
||||
}
|
||||
|
||||
func verifyVolumeDoesntExistInVolumesToMount(
|
||||
t *testing.T, volumeToCheck v1.UniqueVolumeName, dsw DesiredStateOfWorld) {
|
||||
volumesToMount := dsw.GetVolumesToMount()
|
||||
for _, volume := range volumesToMount {
|
||||
if volume.VolumeName == volumeToCheck {
|
||||
t.Fatalf(
|
||||
"Found volume %v in the list of desired state of world volumes to mount. Expected it not to exist.",
|
||||
volumeToCheck)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyPodExistsInVolumeDsw(
|
||||
t *testing.T,
|
||||
expectedPodName volumetypes.UniquePodName,
|
||||
expectedVolumeName v1.UniqueVolumeName,
|
||||
dsw DesiredStateOfWorld) {
|
||||
if podExistsInVolume := dsw.PodExistsInVolume(
|
||||
expectedPodName, expectedVolumeName); !podExistsInVolume {
|
||||
t.Fatalf(
|
||||
"DSW PodExistsInVolume returned incorrect value. Expected: <true> Actual: <%v>",
|
||||
podExistsInVolume)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyPodDoesntExistInVolumeDsw(
|
||||
t *testing.T,
|
||||
expectedPodName volumetypes.UniquePodName,
|
||||
expectedVolumeName v1.UniqueVolumeName,
|
||||
dsw DesiredStateOfWorld) {
|
||||
if podExistsInVolume := dsw.PodExistsInVolume(
|
||||
expectedPodName, expectedVolumeName); podExistsInVolume {
|
||||
t.Fatalf(
|
||||
"DSW PodExistsInVolume returned incorrect value. Expected: <true> Actual: <%v>",
|
||||
podExistsInVolume)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user