123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913 |
- /*
- 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"
- v1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/types"
- utilfeature "k8s.io/apiserver/pkg/util/feature"
- "k8s.io/klog"
- "k8s.io/kubernetes/pkg/features"
- "k8s.io/kubernetes/pkg/volume"
- "k8s.io/kubernetes/pkg/volume/util"
- "k8s.io/kubernetes/pkg/volume/util/operationexecutor"
- volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
- )
- // 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(operationexecutor.MarkVolumeOpts) 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)
- // SetDeviceMountState sets device mount state for the given volume. When deviceMountState is set to DeviceGloballyMounted
- // then device is mounted at a global mount point. When it is set to DeviceMountUncertain then also it means volume
- // MAY be globally mounted at a global mount point. In both cases - the volume must be unmounted from
- // global mount point prior to detach.
- // If a volume with the name volumeName does not exist in the list of
- // attached volumes, an error is returned.
- SetDeviceMountState(volumeName v1.UniqueVolumeName, deviceMountState operationexecutor.DeviceMountState, devicePath, deviceMountPath string) 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)
- // VolumeExistsWithSpecName returns true if the given volume specified with the
- // volume spec name (a.k.a., InnerVolumeSpecName) exists in the list of
- // volumes that should be attached to this node.
- // If a pod with the same name does not exist under the specified
- // volume, false is returned.
- VolumeExistsWithSpecName(podName volumetypes.UniquePodName, volumeSpecName string) bool
- // 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
- // GetAllMountedVolumes returns list of all possibly mounted volumes including
- // those that are in VolumeMounted state and VolumeMountUncertain state.
- GetAllMountedVolumes() []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
- // MarkFSResizeRequired marks each volume that is successfully attached and
- // mounted for the specified pod as requiring file system resize (if the plugin for the
- // volume indicates it requires file system resize).
- MarkFSResizeRequired(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName)
- // GetAttachedVolumes returns a list of volumes that is known to be attached
- // to the node. This list can be used to determine volumes that are either in-use
- // or have a mount/unmount operation pending.
- GetAttachedVolumes() []AttachedVolume
- }
- // 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
- // DeviceMountState indicates if device has been globally mounted or is not.
- DeviceMountState operationexecutor.DeviceMountState
- }
- // DeviceMayBeMounted returns true if device is mounted in global path or is in
- // uncertain state.
- func (av AttachedVolume) DeviceMayBeMounted() bool {
- return av.DeviceMountState == operationexecutor.DeviceGloballyMounted ||
- av.DeviceMountState == operationexecutor.DeviceMountUncertain
- }
- // 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
- // deviceMountState stores information that tells us if device is mounted
- // globally or not
- deviceMountState operationexecutor.DeviceMountState
- // devicePath contains the path on the node where the volume is attached for
- // attachable volumes
- devicePath string
- // deviceMountPath contains the path on the node where the device should
- // be mounted after it is attached.
- deviceMountPath 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
- // 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}/
- volumeSpec *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
- // 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
- // fsResizeRequired indicates the underlying volume has been successfully
- // mounted to this pod but its size has been expanded after that.
- fsResizeRequired bool
- // volumeMountStateForPod stores state of volume mount for the pod. if it is:
- // - VolumeMounted: means volume for pod has been successfully mounted
- // - VolumeMountUncertain: means volume for pod may not be mounted, but it must be unmounted
- volumeMountStateForPod operationexecutor.VolumeMountState
- }
- func (asw *actualStateOfWorld) MarkVolumeAsAttached(
- volumeName v1.UniqueVolumeName, volumeSpec *volume.Spec, _ types.NodeName, devicePath string) error {
- return asw.addVolume(volumeName, volumeSpec, devicePath)
- }
- func (asw *actualStateOfWorld) MarkVolumeAsUncertain(
- volumeName v1.UniqueVolumeName, volumeSpec *volume.Spec, _ types.NodeName) error {
- return nil
- }
- func (asw *actualStateOfWorld) MarkVolumeAsDetached(
- volumeName v1.UniqueVolumeName, nodeName types.NodeName) {
- asw.DeleteVolume(volumeName)
- }
- func (asw *actualStateOfWorld) MarkVolumeAsMounted(markVolumeOpts operationexecutor.MarkVolumeOpts) error {
- return asw.AddPodToVolume(markVolumeOpts)
- }
- 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, devicePath, deviceMountPath string) error {
- return asw.SetDeviceMountState(volumeName, operationexecutor.DeviceGloballyMounted, devicePath, deviceMountPath)
- }
- func (asw *actualStateOfWorld) MarkDeviceAsUncertain(
- volumeName v1.UniqueVolumeName, devicePath, deviceMountPath string) error {
- return asw.SetDeviceMountState(volumeName, operationexecutor.DeviceMountUncertain, devicePath, deviceMountPath)
- }
- func (asw *actualStateOfWorld) MarkVolumeMountAsUncertain(markVolumeOpts operationexecutor.MarkVolumeOpts) error {
- markVolumeOpts.VolumeMountState = operationexecutor.VolumeMountUncertain
- return asw.AddPodToVolume(markVolumeOpts)
- }
- func (asw *actualStateOfWorld) MarkDeviceAsUnmounted(
- volumeName v1.UniqueVolumeName) error {
- return asw.SetDeviceMountState(volumeName, operationexecutor.DeviceNotMounted, "", "")
- }
- func (asw *actualStateOfWorld) GetDeviceMountState(volumeName v1.UniqueVolumeName) operationexecutor.DeviceMountState {
- asw.RLock()
- defer asw.RUnlock()
- volumeObj, volumeExists := asw.attachedVolumes[volumeName]
- if !volumeExists {
- return operationexecutor.DeviceNotMounted
- }
- return volumeObj.deviceMountState
- }
- func (asw *actualStateOfWorld) GetVolumeMountState(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName) operationexecutor.VolumeMountState {
- asw.RLock()
- defer asw.RUnlock()
- volumeObj, volumeExists := asw.attachedVolumes[volumeName]
- if !volumeExists {
- return operationexecutor.VolumeNotMounted
- }
- podObj, podExists := volumeObj.mountedPods[podName]
- if !podExists {
- return operationexecutor.VolumeNotMounted
- }
- return podObj.volumeMountStateForPod
- }
- // 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 = util.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 attachablePlugin, err := asw.volumePluginMgr.FindAttachablePluginBySpec(volumeSpec); err == nil && attachablePlugin != nil {
- 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,
- deviceMountState: operationexecutor.DeviceNotMounted,
- devicePath: devicePath,
- }
- } else {
- // If volume object already exists, update the fields such as device path
- volumeObj.devicePath = devicePath
- klog.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(markVolumeOpts operationexecutor.MarkVolumeOpts) error {
- podName := markVolumeOpts.PodName
- podUID := markVolumeOpts.PodUID
- volumeName := markVolumeOpts.VolumeName
- mounter := markVolumeOpts.Mounter
- blockVolumeMapper := markVolumeOpts.BlockVolumeMapper
- outerVolumeSpecName := markVolumeOpts.OuterVolumeSpecName
- volumeGidValue := markVolumeOpts.VolumeGidVolume
- volumeSpec := markVolumeOpts.VolumeSpec
- 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,
- volumeSpec: volumeSpec,
- volumeMountStateForPod: markVolumeOpts.VolumeMountState,
- }
- }
- // If pod exists, reset remountRequired value
- podObj.remountRequired = false
- podObj.volumeMountStateForPod = markVolumeOpts.VolumeMountState
- asw.attachedVolumes[volumeName].mountedPods[podName] = podObj
- return nil
- }
- func (asw *actualStateOfWorld) MarkVolumeAsResized(
- 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)
- }
- podObj, podExists := volumeObj.mountedPods[podName]
- if !podExists {
- return fmt.Errorf(
- "no pod with the name %q exists in the mounted pods list of volume %s",
- podName,
- volumeName)
- }
- klog.V(5).Infof("Volume %s(OuterVolumeSpecName %s) of pod %s has been resized",
- volumeName, podObj.outerVolumeSpecName, podName)
- podObj.fsResizeRequired = 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 {
- if podObj, podExists := volumeObj.mountedPods[podName]; podExists {
- volumePlugin, err :=
- asw.volumePluginMgr.FindPluginBySpec(podObj.volumeSpec)
- if err != nil || volumePlugin == nil {
- // Log and continue processing
- klog.Errorf(
- "MarkRemountRequired failed to FindPluginBySpec for pod %q (podUid %q) volume: %q (volSpecName: %q)",
- podObj.podName,
- podObj.podUID,
- volumeObj.volumeName,
- podObj.volumeSpec.Name())
- continue
- }
- if volumePlugin.RequiresRemount() {
- podObj.remountRequired = true
- asw.attachedVolumes[volumeName].mountedPods[podName] = podObj
- }
- }
- }
- }
- func (asw *actualStateOfWorld) MarkFSResizeRequired(
- volumeName v1.UniqueVolumeName,
- podName volumetypes.UniquePodName) {
- asw.Lock()
- defer asw.Unlock()
- volumeObj, volumeExists := asw.attachedVolumes[volumeName]
- if !volumeExists {
- klog.Warningf("MarkFSResizeRequired for volume %s failed as volume not exist", volumeName)
- return
- }
- podObj, podExists := volumeObj.mountedPods[podName]
- if !podExists {
- klog.Warningf("MarkFSResizeRequired for volume %s failed "+
- "as pod(%s) not exist", volumeName, podName)
- return
- }
- volumePlugin, err :=
- asw.volumePluginMgr.FindNodeExpandablePluginBySpec(podObj.volumeSpec)
- if err != nil || volumePlugin == nil {
- // Log and continue processing
- klog.Errorf(
- "MarkFSResizeRequired failed to find expandable plugin for pod %q volume: %q (volSpecName: %q)",
- podObj.podName,
- volumeObj.volumeName,
- podObj.volumeSpec.Name())
- return
- }
- if volumePlugin.RequiresFSResize() {
- if !podObj.fsResizeRequired {
- klog.V(3).Infof("PVC volume %s(OuterVolumeSpecName %s) of pod %s requires file system resize",
- volumeName, podObj.outerVolumeSpecName, podName)
- podObj.fsResizeRequired = true
- }
- asw.attachedVolumes[volumeName].mountedPods[podName] = podObj
- }
- }
- func (asw *actualStateOfWorld) SetDeviceMountState(
- volumeName v1.UniqueVolumeName, deviceMountState operationexecutor.DeviceMountState, devicePath, deviceMountPath 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)
- }
- volumeObj.deviceMountState = deviceMountState
- volumeObj.deviceMountPath = deviceMountPath
- if devicePath != "" {
- volumeObj.devicePath = devicePath
- }
- 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 {
- // if volume mount was uncertain we should keep trying to mount the volume
- if podObj.volumeMountStateForPod == operationexecutor.VolumeMountUncertain {
- return false, volumeObj.devicePath, nil
- }
- if podObj.remountRequired {
- return true, volumeObj.devicePath, newRemountRequiredError(volumeObj.volumeName, podObj.podName)
- }
- if podObj.fsResizeRequired &&
- utilfeature.DefaultFeatureGate.Enabled(features.ExpandInUsePersistentVolumes) {
- return true, volumeObj.devicePath, newFsResizeRequiredError(volumeObj.volumeName, podObj.podName)
- }
- }
- return podExists, volumeObj.devicePath, nil
- }
- func (asw *actualStateOfWorld) VolumeExistsWithSpecName(podName volumetypes.UniquePodName, volumeSpecName string) bool {
- asw.RLock()
- defer asw.RUnlock()
- for _, volumeObj := range asw.attachedVolumes {
- if podObj, podExists := volumeObj.mountedPods[podName]; podExists {
- if podObj.volumeSpec.Name() == volumeSpecName {
- return true
- }
- }
- }
- return false
- }
- 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 {
- if podObj.volumeMountStateForPod == operationexecutor.VolumeMounted {
- mountedVolume = append(
- mountedVolume,
- getMountedVolume(&podObj, &volumeObj))
- }
- }
- }
- return mountedVolume
- }
- // GetAllMountedVolumes returns all volumes which could be locally mounted for a pod.
- func (asw *actualStateOfWorld) GetAllMountedVolumes() []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 {
- if podObj.volumeMountStateForPod == operationexecutor.VolumeMounted ||
- podObj.volumeMountStateForPod == operationexecutor.VolumeMountUncertain {
- 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 && podObj.volumeMountStateForPod == operationexecutor.VolumeMounted {
- 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.deviceMountState == operationexecutor.DeviceGloballyMounted {
- globallyMountedVolumes = append(
- globallyMountedVolumes,
- asw.newAttachedVolume(&volumeObj))
- }
- }
- return globallyMountedVolumes
- }
- func (asw *actualStateOfWorld) GetAttachedVolumes() []AttachedVolume {
- asw.RLock()
- defer asw.RUnlock()
- allAttachedVolumes := make(
- []AttachedVolume, 0 /* len */, len(asw.attachedVolumes) /* cap */)
- for _, volumeObj := range asw.attachedVolumes {
- allAttachedVolumes = append(
- allAttachedVolumes,
- asw.newAttachedVolume(&volumeObj))
- }
- return allAttachedVolumes
- }
- 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) newAttachedVolume(
- attachedVolume *attachedVolume) AttachedVolume {
- return AttachedVolume{
- AttachedVolume: operationexecutor.AttachedVolume{
- VolumeName: attachedVolume.volumeName,
- VolumeSpec: attachedVolume.spec,
- NodeName: asw.nodeName,
- PluginIsAttachable: attachedVolume.pluginIsAttachable,
- DevicePath: attachedVolume.devicePath,
- DeviceMountPath: attachedVolume.deviceMountPath,
- PluginName: attachedVolume.pluginName},
- DeviceMountState: attachedVolume.deviceMountState,
- }
- }
- // 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,
- }
- }
- // fsResizeRequiredError is an error returned when PodExistsInVolume() found
- // volume/pod attached/mounted but fsResizeRequired was true, indicating the
- // given volume receives an resize request after attached/mounted.
- type fsResizeRequiredError struct {
- volumeName v1.UniqueVolumeName
- podName volumetypes.UniquePodName
- }
- func (err fsResizeRequiredError) Error() string {
- return fmt.Sprintf(
- "volumeName %q mounted to %q needs to resize file system",
- err.volumeName, err.podName)
- }
- func newFsResizeRequiredError(
- volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName) error {
- return fsResizeRequiredError{
- volumeName: volumeName,
- podName: podName,
- }
- }
- // IsFSResizeRequiredError returns true if the specified error is a
- // fsResizeRequiredError.
- func IsFSResizeRequiredError(err error) bool {
- _, ok := err.(fsResizeRequiredError)
- return ok
- }
- // 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: mountedPod.volumeSpec.Name(),
- OuterVolumeSpecName: mountedPod.outerVolumeSpecName,
- PluginName: attachedVolume.pluginName,
- PodUID: mountedPod.podUID,
- Mounter: mountedPod.mounter,
- BlockVolumeMapper: mountedPod.blockVolumeMapper,
- VolumeGidValue: mountedPod.volumeGidValue,
- VolumeSpec: mountedPod.volumeSpec,
- DeviceMountPath: attachedVolume.deviceMountPath}}
- }
|