mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Co-authored-by: Rajalakshmi-Girish <rajalakshmi.girish1@ibm.com> Co-authored-by: Abhishek Kr Srivastav <Abhishek.kr.srivastav@ibm.com>
		
			
				
	
	
		
			165 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			165 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2019 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 csi
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
 | 
						|
	"google.golang.org/grpc/codes"
 | 
						|
	"google.golang.org/grpc/status"
 | 
						|
	api "k8s.io/api/core/v1"
 | 
						|
	"k8s.io/klog/v2"
 | 
						|
	"k8s.io/kubernetes/pkg/volume"
 | 
						|
	"k8s.io/kubernetes/pkg/volume/util"
 | 
						|
	volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
 | 
						|
)
 | 
						|
 | 
						|
var _ volume.NodeExpandableVolumePlugin = &csiPlugin{}
 | 
						|
 | 
						|
func (c *csiPlugin) RequiresFSResize() bool {
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func (c *csiPlugin) NodeExpand(resizeOptions volume.NodeResizeOptions) (bool, error) {
 | 
						|
	klog.V(4).Info(log("Expander.NodeExpand(%s)", resizeOptions.DeviceMountPath))
 | 
						|
	csiSource, err := getCSISourceFromSpec(resizeOptions.VolumeSpec)
 | 
						|
	if err != nil {
 | 
						|
		return false, errors.New(log("Expander.NodeExpand failed to get CSI persistent source: %v", err))
 | 
						|
	}
 | 
						|
 | 
						|
	csClient, err := newCsiDriverClient(csiDriverName(csiSource.Driver))
 | 
						|
	if err != nil {
 | 
						|
		// Treat the absence of the CSI driver as a transient error
 | 
						|
		// See https://github.com/kubernetes/kubernetes/issues/120268
 | 
						|
		return false, volumetypes.NewTransientOperationFailure(err.Error())
 | 
						|
	}
 | 
						|
	fsVolume, err := util.CheckVolumeModeFilesystem(resizeOptions.VolumeSpec)
 | 
						|
	if err != nil {
 | 
						|
		return false, errors.New(log("Expander.NodeExpand failed to check VolumeMode of source: %v", err))
 | 
						|
	}
 | 
						|
 | 
						|
	return c.nodeExpandWithClient(resizeOptions, csiSource, csClient, fsVolume)
 | 
						|
}
 | 
						|
 | 
						|
func (c *csiPlugin) nodeExpandWithClient(
 | 
						|
	resizeOptions volume.NodeResizeOptions,
 | 
						|
	csiSource *api.CSIPersistentVolumeSource,
 | 
						|
	csClient csiClient,
 | 
						|
	fsVolume bool) (bool, error) {
 | 
						|
	driverName := csiSource.Driver
 | 
						|
 | 
						|
	ctx, cancel := createCSIOperationContext(resizeOptions.VolumeSpec, csiTimeout)
 | 
						|
	defer cancel()
 | 
						|
 | 
						|
	nodeExpandSet, err := csClient.NodeSupportsNodeExpand(ctx)
 | 
						|
	if err != nil {
 | 
						|
		return false, fmt.Errorf("Expander.NodeExpand failed to check if node supports expansion : %v", err)
 | 
						|
	}
 | 
						|
 | 
						|
	if !nodeExpandSet {
 | 
						|
		return false, volumetypes.NewOperationNotSupportedError(fmt.Sprintf("NodeExpand is not supported by the CSI driver %s", driverName))
 | 
						|
	}
 | 
						|
 | 
						|
	pv := resizeOptions.VolumeSpec.PersistentVolume
 | 
						|
	if pv == nil {
 | 
						|
		return false, fmt.Errorf("Expander.NodeExpand failed to find associated PersistentVolume for plugin %s", c.GetPluginName())
 | 
						|
	}
 | 
						|
	nodeExpandSecrets := map[string]string{}
 | 
						|
	expandClient := c.host.GetKubeClient()
 | 
						|
 | 
						|
	if csiSource.NodeExpandSecretRef != nil {
 | 
						|
		nodeExpandSecrets, err = getCredentialsFromSecret(expandClient, csiSource.NodeExpandSecretRef)
 | 
						|
		if err != nil {
 | 
						|
			return false, fmt.Errorf("expander.NodeExpand failed to get NodeExpandSecretRef %s/%s: %v",
 | 
						|
				csiSource.NodeExpandSecretRef.Namespace, csiSource.NodeExpandSecretRef.Name, err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	opts := csiResizeOptions{
 | 
						|
		volumePath:        resizeOptions.DeviceMountPath,
 | 
						|
		stagingTargetPath: resizeOptions.DeviceStagePath,
 | 
						|
		volumeID:          csiSource.VolumeHandle,
 | 
						|
		newSize:           resizeOptions.NewSize,
 | 
						|
		fsType:            csiSource.FSType,
 | 
						|
		accessMode:        api.ReadWriteOnce,
 | 
						|
		mountOptions:      pv.Spec.MountOptions,
 | 
						|
		secrets:           nodeExpandSecrets,
 | 
						|
	}
 | 
						|
 | 
						|
	if !fsVolume {
 | 
						|
		// for block volumes the volumePath in CSI NodeExpandvolumeRequest is
 | 
						|
		// basically same as DevicePath because block devices are not mounted and hence
 | 
						|
		// DeviceMountPath does not get populated in resizeOptions.DeviceMountPath
 | 
						|
		opts.volumePath = resizeOptions.DevicePath
 | 
						|
		opts.fsType = fsTypeBlockName
 | 
						|
	}
 | 
						|
 | 
						|
	if pv.Spec.AccessModes != nil {
 | 
						|
		opts.accessMode = pv.Spec.AccessModes[0]
 | 
						|
	}
 | 
						|
 | 
						|
	_, err = csClient.NodeExpandVolume(ctx, opts)
 | 
						|
	if err != nil {
 | 
						|
		if inUseError(err) {
 | 
						|
			failedConditionErr := fmt.Errorf("Expander.NodeExpand failed to expand the volume : %w", volumetypes.NewFailedPreconditionError(err.Error()))
 | 
						|
			return false, failedConditionErr
 | 
						|
		}
 | 
						|
 | 
						|
		if isInfeasibleError(err) {
 | 
						|
			infeasibleError := volumetypes.NewInfeasibleError(fmt.Sprintf("Expander.NodeExpand failed to expand the volume %s", err.Error()))
 | 
						|
			return false, infeasibleError
 | 
						|
		}
 | 
						|
		return false, fmt.Errorf("Expander.NodeExpand failed to expand the volume : %w", err)
 | 
						|
	}
 | 
						|
	return true, nil
 | 
						|
}
 | 
						|
 | 
						|
func inUseError(err error) bool {
 | 
						|
	st, ok := status.FromError(err)
 | 
						|
	if !ok {
 | 
						|
		// not a grpc error
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	// if this is a failed precondition error then that means driver does not support expansion
 | 
						|
	// of in-use volumes
 | 
						|
	// More info - https://github.com/container-storage-interface/spec/blob/master/spec.md#controllerexpandvolume-errors
 | 
						|
	return st.Code() == codes.FailedPrecondition
 | 
						|
}
 | 
						|
 | 
						|
// IsInfeasibleError returns true for grpc errors that are considered terminal in a way
 | 
						|
// that they indicate CSI operation as infeasible.
 | 
						|
// This function returns a subset of final errors. All infeasible errors are also final errors.
 | 
						|
func isInfeasibleError(err error) bool {
 | 
						|
	st, ok := status.FromError(err)
 | 
						|
	if !ok {
 | 
						|
		// This is not gRPC error. The operation must have failed before gRPC
 | 
						|
		// method was called, otherwise we would get gRPC error.
 | 
						|
		// We don't know if any previous volume operation is in progress, be on the safe side.
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	switch st.Code() {
 | 
						|
	case codes.InvalidArgument,
 | 
						|
		codes.OutOfRange,
 | 
						|
		codes.NotFound:
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	// All other errors mean that operation either did not
 | 
						|
	// even start or failed. It is for sure are not infeasible errors
 | 
						|
	return false
 | 
						|
}
 |