rebase: bump k8s.io/kubernetes in the k8s-dependencies group

Bumps the k8s-dependencies group with 1 update: [k8s.io/kubernetes](https://github.com/kubernetes/kubernetes).

Updates `k8s.io/kubernetes` from 1.32.3 to 1.33.0
- [Release notes](https://github.com/kubernetes/kubernetes/releases)
- [Commits](https://github.com/kubernetes/kubernetes/compare/v1.32.3...v1.33.0)

---
updated-dependencies:
- dependency-name: k8s.io/kubernetes
  dependency-version: 1.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: k8s-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
dependabot[bot]
2025-04-28 22:16:28 +00:00
committed by mergify[bot]
parent 4147d5d15a
commit 51895f8619
699 changed files with 51590 additions and 17096 deletions

View File

@ -16,4 +16,4 @@ limitations under the License.
// Package volume includes internal representations of external volume types
// as well as utility methods required to mount/unmount volumes to kubelets.
package volume // import "k8s.io/kubernetes/pkg/volume"
package volume

View File

@ -37,7 +37,7 @@ type metricsBlock struct {
device string
}
// NewMetricsStatfs creates a new metricsBlock with the device node of the
// NewMetricsBlock creates a new metricsBlock with the device node of the
// Volume.
func NewMetricsBlock(device string) MetricsProvider {
return &metricsBlock{device}

View File

@ -23,7 +23,11 @@ import (
const (
// ErrCodeNotSupported code for NotSupported Errors.
ErrCodeNotSupported int = iota + 1
// ErrCodeNoPathDefined code for NoPathDefined Errors.
ErrCodeNoPathDefined
// ErrCodeFsInfoFailed code for FsInfoFailed Errors.
ErrCodeFsInfoFailed
)

View File

@ -34,7 +34,7 @@ type metricsStatFS struct {
path string
}
// NewMetricsStatfs creates a new metricsStatFS with the Volume path.
// NewMetricsStatFS creates a new metricsStatFS with the Volume path.
func NewMetricsStatFS(path string) MetricsProvider {
return &metricsStatFS{path}
}

View File

@ -44,7 +44,10 @@ import (
"k8s.io/kubernetes/pkg/volume/util/subpath"
)
// ProbeOperation represents a type of operation for probing volume plugins.
type ProbeOperation uint32
// ProbeEvent represents an event triggered during a volume plugin probe operation.
type ProbeEvent struct {
Plugin VolumePlugin // VolumePlugin that was added/updated/removed. if ProbeEvent.Op is 'ProbeRemove', Plugin should be nil
PluginName string
@ -52,17 +55,22 @@ type ProbeEvent struct {
}
const (
// Common parameter which can be specified in StorageClass to specify the desired FSType
// VolumeParameterFSType is a common parameter which can be specified in StorageClass to specify the desired FSType
// Provisioners SHOULD implement support for this if they are block device based
// Must be a filesystem type supported by the host operating system.
// Ex. "ext4", "xfs", "ntfs". Default value depends on the provisioner
VolumeParameterFSType = "fstype"
// ProbeAddOrUpdate represents an operation where a probe is added or updated.
ProbeAddOrUpdate ProbeOperation = 1 << iota
// ProbeRemove represents an operation to remove a probe.
// This operation is used to indicate that a previously added probe should be removed.
ProbeRemove
)
var ErrNoPluiginMatched = errors.New("no volume plugin matched")
// ErrNoPluginMatched is used to return when no volume plugin matches the requested type.
var ErrNoPluginMatched = errors.New("no volume plugin matched")
// VolumeOptions contains option information about a volume.
type VolumeOptions struct {
@ -108,6 +116,7 @@ type NodeResizeOptions struct {
OldSize resource.Quantity
}
// DynamicPluginProber is an interface that defines methods for probing dynamic volume plugins.
type DynamicPluginProber interface {
Init() error
@ -225,6 +234,7 @@ type AttachableVolumePlugin interface {
NewDetacher() (Detacher, error)
// CanAttach tests if provided volume spec is attachable
CanAttach(spec *Spec) (bool, error)
VerifyExhaustedResource(spec *Spec, nodeName types.NodeName)
}
// DeviceMountableVolumePlugin is an extended interface of VolumePlugin and is used
@ -628,9 +638,8 @@ func (pm *VolumePluginMgr) initProbedPlugin(probedPlugin VolumePlugin) error {
// specification. If no plugins can support or more than one plugin can
// support it, return error.
func (pm *VolumePluginMgr) FindPluginBySpec(spec *Spec) (VolumePlugin, error) {
pm.mutex.RLock()
defer pm.mutex.RUnlock()
pm.mutex.Lock()
defer pm.mutex.Unlock()
if spec == nil {
return nil, fmt.Errorf("could not find plugin because volume spec is nil")
}
@ -643,8 +652,8 @@ func (pm *VolumePluginMgr) FindPluginBySpec(spec *Spec) (VolumePlugin, error) {
matchedPluginNames = append(matchedPluginNames, v.GetPluginName())
}
}
pm.refreshProbedPlugins()
for _, plugin := range pm.probedPlugins {
if plugin.CanSupport(spec) {
match = plugin
@ -653,7 +662,7 @@ func (pm *VolumePluginMgr) FindPluginBySpec(spec *Spec) (VolumePlugin, error) {
}
if len(matchedPluginNames) == 0 {
return nil, ErrNoPluiginMatched
return nil, ErrNoPluginMatched
}
if len(matchedPluginNames) > 1 {
return nil, fmt.Errorf("multiple volume plugins matched: %s", strings.Join(matchedPluginNames, ","))
@ -664,14 +673,13 @@ func (pm *VolumePluginMgr) FindPluginBySpec(spec *Spec) (VolumePlugin, error) {
// FindPluginByName fetches a plugin by name. If no plugin is found, returns error.
func (pm *VolumePluginMgr) FindPluginByName(name string) (VolumePlugin, error) {
pm.mutex.RLock()
defer pm.mutex.RUnlock()
pm.mutex.Lock()
defer pm.mutex.Unlock()
var match VolumePlugin
if v, found := pm.plugins[name]; found {
match = v
}
pm.refreshProbedPlugins()
if plugin, found := pm.probedPlugins[name]; found {
if match != nil {
@ -698,6 +706,7 @@ func (pm *VolumePluginMgr) refreshProbedPlugins() {
// because the probe function can return a list of valid plugins
// even when an error is present we still must add the plugins
// or they will be skipped because each event only fires once
for _, event := range events {
if event.Op == ProbeAddOrUpdate {
if err := pm.initProbedPlugin(event.Plugin); err != nil {
@ -730,7 +739,7 @@ func (pm *VolumePluginMgr) FindPersistentPluginBySpec(spec *Spec) (PersistentVol
return nil, fmt.Errorf("no persistent volume plugin matched")
}
// FindPersistentPluginByName fetches a persistent volume plugin by name. If
// FindPersistentPluginByName fetches a recyclable persistent volume plugin by name. If
// no plugin is found, returns error.
func (pm *VolumePluginMgr) FindPersistentPluginByName(name string) (PersistentVolumePlugin, error) {
volumePlugin, err := pm.FindPluginByName(name)
@ -743,7 +752,7 @@ func (pm *VolumePluginMgr) FindPersistentPluginByName(name string) (PersistentVo
return nil, fmt.Errorf("no persistent volume plugin matched")
}
// FindRecyclablePluginByName fetches a persistent volume plugin by name. If
// FindRecyclablePluginBySpec fetches a recyclable persistent volume plugin by spec. If
// no plugin is found, returns error.
func (pm *VolumePluginMgr) FindRecyclablePluginBySpec(spec *Spec) (RecyclableVolumePlugin, error) {
volumePlugin, err := pm.FindPluginBySpec(spec)
@ -756,7 +765,7 @@ func (pm *VolumePluginMgr) FindRecyclablePluginBySpec(spec *Spec) (RecyclableVol
return nil, fmt.Errorf("no recyclable volume plugin matched")
}
// FindProvisionablePluginByName fetches a persistent volume plugin by name. If
// FindProvisionablePluginByName fetches a provisionable persistent volume plugin by name. If
// no plugin is found, returns error.
func (pm *VolumePluginMgr) FindProvisionablePluginByName(name string) (ProvisionableVolumePlugin, error) {
volumePlugin, err := pm.FindPluginByName(name)
@ -769,7 +778,7 @@ func (pm *VolumePluginMgr) FindProvisionablePluginByName(name string) (Provision
return nil, fmt.Errorf("no provisionable volume plugin matched")
}
// FindDeletablePluginBySpec fetches a persistent volume plugin by spec. If
// FindDeletablePluginBySpec fetches a provisionable persistent volume plugin by spec. If
// no plugin is found, returns error.
func (pm *VolumePluginMgr) FindDeletablePluginBySpec(spec *Spec) (DeletableVolumePlugin, error) {
volumePlugin, err := pm.FindPluginBySpec(spec)
@ -782,7 +791,7 @@ func (pm *VolumePluginMgr) FindDeletablePluginBySpec(spec *Spec) (DeletableVolum
return nil, fmt.Errorf("no deletable volume plugin matched")
}
// FindDeletablePluginByName fetches a persistent volume plugin by name. If
// FindDeletablePluginByName fetches a deleteable persistent volume plugin by name. If
// no plugin is found, returns error.
func (pm *VolumePluginMgr) FindDeletablePluginByName(name string) (DeletableVolumePlugin, error) {
volumePlugin, err := pm.FindPluginByName(name)
@ -829,7 +838,7 @@ func (pm *VolumePluginMgr) FindAttachablePluginByName(name string) (AttachableVo
return nil, nil
}
// FindDeviceMountablePluginBySpec fetches a persistent volume plugin by spec.
// FindDeviceMountablePluginBySpec fetches a devicemountable persistent volume plugin by spec.
func (pm *VolumePluginMgr) FindDeviceMountablePluginBySpec(spec *Spec) (DeviceMountableVolumePlugin, error) {
volumePlugin, err := pm.FindPluginBySpec(spec)
if err != nil {
@ -845,7 +854,7 @@ func (pm *VolumePluginMgr) FindDeviceMountablePluginBySpec(spec *Spec) (DeviceMo
return nil, nil
}
// FindDeviceMountablePluginByName fetches a devicemountable volume plugin by name.
// FindDeviceMountablePluginByName fetches a devicemountable persistent volume plugin by name.
func (pm *VolumePluginMgr) FindDeviceMountablePluginByName(name string) (DeviceMountableVolumePlugin, error) {
volumePlugin, err := pm.FindPluginByName(name)
if err != nil {
@ -857,7 +866,7 @@ func (pm *VolumePluginMgr) FindDeviceMountablePluginByName(name string) (DeviceM
return nil, nil
}
// FindExpandablePluginBySpec fetches a persistent volume plugin by spec.
// FindExpandablePluginBySpec fetches an expandable persistent volume plugin by spec.
func (pm *VolumePluginMgr) FindExpandablePluginBySpec(spec *Spec) (ExpandableVolumePlugin, error) {
volumePlugin, err := pm.FindPluginBySpec(spec)
if err != nil {
@ -867,7 +876,7 @@ func (pm *VolumePluginMgr) FindExpandablePluginBySpec(spec *Spec) (ExpandableVol
klog.V(4).InfoS("FindExpandablePluginBySpec -> returning noopExpandableVolumePluginInstance", "specName", spec.Name())
return &noopExpandableVolumePluginInstance{spec}, nil
}
if errors.Is(err, ErrNoPluiginMatched) {
if errors.Is(err, ErrNoPluginMatched) {
return nil, nil
}
klog.V(4).InfoS("FindExpandablePluginBySpec -> err", "specName", spec.Name(), "err", err)
@ -880,7 +889,7 @@ func (pm *VolumePluginMgr) FindExpandablePluginBySpec(spec *Spec) (ExpandableVol
return nil, nil
}
// FindExpandablePluginBySpec fetches a persistent volume plugin by name.
// FindExpandablePluginByName fetches an expandable persistent volume plugin by name.
func (pm *VolumePluginMgr) FindExpandablePluginByName(name string) (ExpandableVolumePlugin, error) {
volumePlugin, err := pm.FindPluginByName(name)
if err != nil {
@ -919,7 +928,7 @@ func (pm *VolumePluginMgr) FindMapperPluginByName(name string) (BlockVolumePlugi
return nil, nil
}
// FindNodeExpandablePluginBySpec fetches a persistent volume plugin by spec
// FindNodeExpandablePluginBySpec fetches a node expandable persistent volume plugin by spec
func (pm *VolumePluginMgr) FindNodeExpandablePluginBySpec(spec *Spec) (NodeExpandableVolumePlugin, error) {
volumePlugin, err := pm.FindPluginBySpec(spec)
if err != nil {
@ -931,7 +940,7 @@ func (pm *VolumePluginMgr) FindNodeExpandablePluginBySpec(spec *Spec) (NodeExpan
return nil, nil
}
// FindNodeExpandablePluginByName fetches a persistent volume plugin by name
// FindNodeExpandablePluginByName fetches a node expandable persistent volume plugin by name
func (pm *VolumePluginMgr) FindNodeExpandablePluginByName(name string) (NodeExpandableVolumePlugin, error) {
volumePlugin, err := pm.FindPluginByName(name)
if err != nil {
@ -945,6 +954,9 @@ func (pm *VolumePluginMgr) FindNodeExpandablePluginByName(name string) (NodeExpa
return nil, nil
}
// Run starts the volume plugin manager, initializing and running the necessary
// tasks for managing volume plugins. This method is typically called to begin
// the plugin management lifecycle.
func (pm *VolumePluginMgr) Run(stopCh <-chan struct{}) {
kletHost, ok := pm.Host.(KubeletVolumeHost)
if ok {
@ -1007,7 +1019,7 @@ func NewPersistentVolumeRecyclerPodTemplate() *v1.Pod {
return pod
}
// Check validity of recycle pod template
// ValidateRecyclerPodTemplate checks validity of recycle pod template
// List of checks:
// - at least one volume is defined in the recycle pod template
// If successful, returns nil

View File

@ -85,6 +85,11 @@ func diskUsage(currPath string, info os.FileInfo) (int64, error) {
return size, nil
}
// go1.23 behavior change: https://github.com/golang/go/issues/63703#issuecomment-2535941458
if info.Mode()&os.ModeIrregular != 0 {
return size, nil
}
size += info.Size()
if !info.IsDir() {

View File

@ -19,8 +19,6 @@ package hostutil
import (
"fmt"
"os"
"k8s.io/mount-utils"
)
// FileType enumerates the known set of possible file types.
@ -52,10 +50,6 @@ type HostUtils interface {
DeviceOpened(pathname string) (bool, error)
// PathIsDevice determines if a path is a device.
PathIsDevice(pathname string) (bool, error)
// GetDeviceNameFromMount finds the device name by checking the mount path
// to get the global mount path within its plugin directory.
// TODO: Remove this method once the rbd and vsphere plugins are removed from in-tree.
GetDeviceNameFromMount(mounter mount.Interface, mountPath, pluginMountDir string) (string, error)
// MakeRShared checks that given path is on a mount with 'rshared' mount
// propagation. If not, it bind-mounts the path as rshared.
MakeRShared(path string) error

View File

@ -66,7 +66,7 @@ func (hu *HostUtil) PathIsDevice(pathname string) (bool, error) {
return isDevice, err
}
// ExclusiveOpenFailsOnDevice is shared with NsEnterMounter
// ExclusiveOpenFailsOnDevice checks if block device in use by calling Open with O_EXCL flag.
func ExclusiveOpenFailsOnDevice(pathname string) (bool, error) {
var isDevice bool
finfo, err := os.Stat(pathname)
@ -154,8 +154,6 @@ func (hu *HostUtil) PathExists(pathname string) (bool, error) {
}
// EvalHostSymlinks returns the path name after evaluating symlinks.
// TODO once the nsenter implementation is removed, this method can be removed
// from the interface and filepath.EvalSymlinks used directly
func (hu *HostUtil) EvalHostSymlinks(pathname string) (string, error) {
return filepath.EvalSymlinks(pathname)
}
@ -280,8 +278,8 @@ func (hu *HostUtil) GetMode(pathname string) (os.FileMode, error) {
return GetModeLinux(pathname)
}
// GetOwnerLinux is shared between Linux and NsEnterMounter
// pathname must already be evaluated for symlinks
// pathname must already be evaluated for symlinks.
// GetOwnerLinux returns the integer ID for the user and group of the given path.
func GetOwnerLinux(pathname string) (int64, int64, error) {
info, err := os.Stat(pathname)
if err != nil {
@ -291,7 +289,7 @@ func GetOwnerLinux(pathname string) (int64, int64, error) {
return int64(stat.Uid), int64(stat.Gid), nil
}
// GetModeLinux is shared between Linux and NsEnterMounter
// GetModeLinux returns permissions of the pathname.
func GetModeLinux(pathname string) (os.FileMode, error) {
info, err := os.Stat(pathname)
if err != nil {

View File

@ -29,7 +29,6 @@ import (
"golang.org/x/sys/windows"
"k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/util/filesystem"
"k8s.io/mount-utils"
utilpath "k8s.io/utils/path"
)
@ -103,14 +102,6 @@ func isSystemCannotAccessErr(err error) bool {
func (hu *(HostUtil)) GetFileType(pathname string) (FileType, error) {
filetype, err := getFileType(pathname)
// os.Stat will return a 1920 error (windows.ERROR_CANT_ACCESS_FILE) if we use it on a Unix Socket
// on Windows. In this case, we need to use a different method to check if it's a Unix Socket.
if err == errUnknownFileType || isSystemCannotAccessErr(err) {
if isSocket, errSocket := filesystem.IsUnixDomainSocket(pathname); errSocket == nil && isSocket {
return FileTypeSocket, nil
}
}
return filetype, err
}

View File

@ -78,7 +78,7 @@ func (sp *subpath) PrepareSafeSubpath(subPath Subpath) (newHostPath string, clea
return newHostPath, cleanupAction, err
}
// This implementation is shared between Linux and NsEnter
// safeOpenSubPath opens subpath and returns its fd.
func safeOpenSubPath(mounter mount.Interface, subpath Subpath) (int, error) {
if !mount.PathWithinBase(subpath.Path, subpath.VolumePath) {
return -1, fmt.Errorf("subpath %q not within volume path %q", subpath.Path, subpath.VolumePath)
@ -92,11 +92,6 @@ func safeOpenSubPath(mounter mount.Interface, subpath Subpath) (int, error) {
// prepareSubpathTarget creates target for bind-mount of subpath. It returns
// "true" when the target already exists and something is mounted there.
// Given Subpath must have all paths with already resolved symlinks and with
// paths relevant to kubelet (when it runs in a container).
// This function is called also by NsEnterMounter. It works because
// /var/lib/kubelet is mounted from the host into the container with Kubelet as
// /var/lib/kubelet too.
func prepareSubpathTarget(mounter mount.Interface, subpath Subpath) (bool, string, error) {
// Early check for already bind-mounted subpath.
bindPathTarget := getSubpathBindTarget(subpath)
@ -237,7 +232,7 @@ func doBindSubPath(mounter mount.Interface, subpath Subpath) (hostPath string, e
return bindPathTarget, nil
}
// This implementation is shared between Linux and NsEnter
// doCleanSubPaths tears down the subpath bind mounts for a pod
func doCleanSubPaths(mounter mount.Interface, podDir string, volumeName string) error {
// scan /var/lib/kubelet/pods/<uid>/volume-subpaths/<volume>/*
subPathDir := filepath.Join(podDir, containerSubPathDirectoryName, volumeName)
@ -372,9 +367,7 @@ func removeEmptyDirs(baseDir, endDir string) error {
return nil
}
// This implementation is shared between Linux and NsEnterMounter. Both pathname
// and base must be either already resolved symlinks or thet will be resolved in
// kubelet's mount namespace (in case it runs containerized).
// doSafeMakeDir creates a directory at pathname, but only if it is within base.
func doSafeMakeDir(pathname string, base string, perm os.FileMode) error {
klog.V(4).Infof("Creating directory %q within base %q", pathname, base)
@ -523,7 +516,6 @@ func findExistingPrefix(base, pathname string) (string, []string, error) {
return pathname, []string{}, nil
}
// This implementation is shared between Linux and NsEnterMounter
// Open path and return its fd.
// Symlinks are disallowed (pathname must already resolve symlinks),
// and the path must be within the base directory.

View File

@ -24,7 +24,6 @@ import (
"os"
"k8s.io/mount-utils"
"k8s.io/utils/nsenter"
)
type subpath struct{}
@ -36,12 +35,6 @@ func New(mount.Interface) Interface {
return &subpath{}
}
// NewNSEnter is to satisfy the compiler for having NewSubpathNSEnter exist for all
// OS choices. however, NSEnter is only valid on Linux
func NewNSEnter(mounter mount.Interface, ne *nsenter.Nsenter, rootDir string) Interface {
return nil
}
func (sp *subpath) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
return subPath.Path, nil, errUnsupported
}

View File

@ -29,7 +29,6 @@ import (
"k8s.io/klog/v2"
"k8s.io/mount-utils"
"k8s.io/utils/nsenter"
)
// MaxPathLength is the maximum length of Windows path. Normally, it is 260, but if long path is enable,
@ -43,12 +42,6 @@ func New(mount.Interface) Interface {
return &subpath{}
}
// NewNSEnter is to satisfy the compiler for having NewSubpathNSEnter exist for all
// OS choices. however, NSEnter is only valid on Linux
func NewNSEnter(mounter mount.Interface, ne *nsenter.Nsenter, rootDir string) Interface {
return nil
}
// isDriveLetterPath returns true if the given path is empty or it ends with ":" or ":\" or ":\\"
func isDriveLetterorEmptyPath(path string) bool {
if path == "" || strings.HasSuffix(path, ":\\\\") || strings.HasSuffix(path, ":") || strings.HasSuffix(path, ":\\") {
@ -208,6 +201,12 @@ func lockAndCheckSubPathWithoutSymlink(volumePath, subPath string) ([]uintptr, e
break
}
// go1.23 behavior change: https://github.com/golang/go/issues/63703#issuecomment-2535941458
if stat.Mode()&os.ModeIrregular != 0 {
errorResult = fmt.Errorf("subpath %q is an unexpected irregular file after EvalSymlinks", currentFullPath)
break
}
if !mount.PathWithinBase(currentFullPath, volumePath) {
errorResult = fmt.Errorf("SubPath %q not within volume path %q", currentFullPath, volumePath)
break
@ -342,6 +341,10 @@ func doSafeMakeDir(pathname string, base string, perm os.FileMode) error {
if stat.Mode()&os.ModeSymlink != 0 {
return fmt.Errorf("subpath %q is an unexpected symlink after Mkdir", currentPath)
}
// go1.23 behavior change: https://github.com/golang/go/issues/63703#issuecomment-2535941458
if stat.Mode()&os.ModeIrregular != 0 {
return fmt.Errorf("subpath %q is an unexpected irregular file after Mkdir", currentPath)
}
}
return nil

View File

@ -17,12 +17,15 @@ limitations under the License.
package volume
import (
"sync/atomic"
"time"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
)
// Volume represents a directory used by pods or hosts on a node. All method
@ -130,6 +133,20 @@ type MounterArgs struct {
FSGroupChangePolicy *v1.PodFSGroupChangePolicy
DesiredSize *resource.Quantity
SELinuxLabel string
Recorder record.EventRecorder
}
type VolumeOwnership struct {
mounter Mounter
dir string
fsGroup *int64
fsGroupChangePolicy *v1.PodFSGroupChangePolicy
completionCallback func(volumetypes.CompleteFuncParam)
// for monitoring progress of permission change operation
pod *v1.Pod
fileCounter atomic.Int64
recorder record.EventRecorder
}
// Mounter interface provides methods to set up/mount the volume.

View File

@ -20,14 +20,19 @@ limitations under the License.
package volume
import (
"context"
"fmt"
"path/filepath"
"strings"
"syscall"
"os"
"time"
v1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/record"
"k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/kubelet/events"
"k8s.io/kubernetes/pkg/volume/util/types"
)
@ -37,38 +42,110 @@ const (
execMask = os.FileMode(0110)
)
// SetVolumeOwnership modifies the given volume to be owned by
// fsGroup, and sets SetGid so that newly created files are owned by
// fsGroup. If fsGroup is nil nothing is done.
func SetVolumeOwnership(mounter Mounter, dir string, fsGroup *int64, fsGroupChangePolicy *v1.PodFSGroupChangePolicy, completeFunc func(types.CompleteFuncParam)) error {
if fsGroup == nil {
var (
// function that will be used for changing file permissions on linux
// mainly stored here as a variable so as it can replaced in tests
filePermissionChangeFunc = changeFilePermission
progressReportDuration = 60 * time.Second
firstEventReportDuration = 30 * time.Second
)
// NewVolumeOwnership returns an interface that can be used to recursively change volume permissions and ownership
func NewVolumeOwnership(mounter Mounter, dir string, fsGroup *int64, fsGroupChangePolicy *v1.PodFSGroupChangePolicy, completeFunc func(types.CompleteFuncParam)) *VolumeOwnership {
vo := &VolumeOwnership{
mounter: mounter,
dir: dir,
fsGroup: fsGroup,
fsGroupChangePolicy: fsGroupChangePolicy,
completionCallback: completeFunc,
}
vo.fileCounter.Store(0)
return vo
}
func (vo *VolumeOwnership) AddProgressNotifier(pod *v1.Pod, recorder record.EventRecorder) *VolumeOwnership {
vo.pod = pod
vo.recorder = recorder
return vo
}
func (vo *VolumeOwnership) ChangePermissions() error {
if vo.fsGroup == nil {
return nil
}
timer := time.AfterFunc(30*time.Second, func() {
klog.Warningf("Setting volume ownership for %s and fsGroup set. If the volume has a lot of files then setting volume ownership could be slow, see https://github.com/kubernetes/kubernetes/issues/69699", dir)
if skipPermissionChange(vo.mounter, vo.dir, vo.fsGroup, vo.fsGroupChangePolicy) {
klog.V(3).InfoS("Skipping permission and ownership change for volume", "path", vo.dir)
return nil
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
timer := time.AfterFunc(firstEventReportDuration, func() {
vo.initiateProgressMonitor(ctx)
})
defer timer.Stop()
if skipPermissionChange(mounter, dir, fsGroup, fsGroupChangePolicy) {
klog.V(3).InfoS("Skipping permission and ownership change for volume", "path", dir)
return nil
}
return vo.changePermissionsRecursively()
}
err := walkDeep(dir, func(path string, info os.FileInfo, err error) error {
func (vo *VolumeOwnership) initiateProgressMonitor(ctx context.Context) {
klog.Warningf("Setting volume ownership for %s and fsGroup set. If the volume has a lot of files then setting volume ownership could be slow, see https://github.com/kubernetes/kubernetes/issues/69699", vo.dir)
if vo.pod != nil {
go vo.monitorProgress(ctx)
}
}
func (vo *VolumeOwnership) changePermissionsRecursively() error {
err := walkDeep(vo.dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
return changeFilePermission(path, fsGroup, mounter.GetAttributes().ReadOnly, info)
vo.fileCounter.Add(1)
return filePermissionChangeFunc(path, vo.fsGroup, vo.mounter.GetAttributes().ReadOnly, info)
})
if completeFunc != nil {
completeFunc(types.CompleteFuncParam{
if vo.completionCallback != nil {
vo.completionCallback(types.CompleteFuncParam{
Err: &err,
})
}
return err
}
func (vo *VolumeOwnership) monitorProgress(ctx context.Context) {
dirName := getDirnameToReport(vo.dir, string(vo.pod.UID))
msg := fmt.Sprintf("Setting volume ownership for %s is taking longer than expected, consider using OnRootMismatch - https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods", dirName)
vo.recorder.Event(vo.pod, v1.EventTypeWarning, events.VolumePermissionChangeInProgress, msg)
ticker := time.NewTicker(progressReportDuration)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
vo.logWarning()
}
}
}
// report everything after podUID in dir string, including podUID
func getDirnameToReport(dir, podUID string) string {
podUIDIndex := strings.Index(dir, podUID)
if podUIDIndex == -1 {
return dir
}
return dir[podUIDIndex:]
}
func (vo *VolumeOwnership) logWarning() {
dirName := getDirnameToReport(vo.dir, string(vo.pod.UID))
msg := fmt.Sprintf("Setting volume ownership for %s, processed %d files.", dirName, vo.fileCounter.Load())
klog.Warning(msg)
vo.recorder.Event(vo.pod, v1.EventTypeWarning, events.VolumePermissionChangeInProgress, msg)
}
func changeFilePermission(filename string, fsGroup *int64, readonly bool, info os.FileInfo) error {
err := os.Lchown(filename, -1, int(*fsGroup))
if err != nil {

View File

@ -21,9 +21,19 @@ package volume
import (
v1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/volume/util/types"
)
func SetVolumeOwnership(mounter Mounter, dir string, fsGroup *int64, fsGroupChangePolicy *v1.PodFSGroupChangePolicy, completeFunc func(types.CompleteFuncParam)) error {
// NewVolumeOwnership returns an interface that can be used to recursively change volume permissions and ownership
func NewVolumeOwnership(mounter Mounter, dir string, fsGroup *int64, fsGroupChangePolicy *v1.PodFSGroupChangePolicy, completeFunc func(types.CompleteFuncParam)) *VolumeOwnership {
return nil
}
func (vo *VolumeOwnership) AddProgressNotifier(pod *v1.Pod, recorder record.EventRecorder) *VolumeOwnership {
return vo
}
func (vo *VolumeOwnership) ChangePermissions() error {
return nil
}