mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			231 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			231 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2017 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 persistentvolumeclaim
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 
 | |
| 	"k8s.io/apimachinery/pkg/util/validation/field"
 | |
| 	utilfeature "k8s.io/apiserver/pkg/util/feature"
 | |
| 	"k8s.io/kubernetes/pkg/apis/core"
 | |
| 	"k8s.io/kubernetes/pkg/apis/core/helper"
 | |
| 	"k8s.io/kubernetes/pkg/features"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	pvc                                  string = "PersistentVolumeClaim"
 | |
| 	volumeSnapshot                       string = "VolumeSnapshot"
 | |
| 	deprecatedStorageClassAnnotationsMsg        = `deprecated since v1.8; use "storageClassName" attribute instead`
 | |
| )
 | |
| 
 | |
| // DropDisabledFields removes disabled fields from the pvc spec.
 | |
| // This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a pvc spec.
 | |
| func DropDisabledFields(pvcSpec, oldPVCSpec *core.PersistentVolumeClaimSpec) {
 | |
| 	// Drop the contents of the volumeAttributesClassName if the VolumeAttributesClass
 | |
| 	// feature gate is disabled.
 | |
| 	if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeAttributesClass) {
 | |
| 		if oldPVCSpec == nil || oldPVCSpec.VolumeAttributesClassName == nil {
 | |
| 			pvcSpec.VolumeAttributesClassName = nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Drop the contents of the dataSourceRef field if the AnyVolumeDataSource
 | |
| 	// feature gate is disabled.
 | |
| 	if !utilfeature.DefaultFeatureGate.Enabled(features.AnyVolumeDataSource) {
 | |
| 		if !dataSourceRefInUse(oldPVCSpec) {
 | |
| 			pvcSpec.DataSourceRef = nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Drop the contents of the dataSourceRef field if the CrossNamespaceVolumeDataSource
 | |
| 	// feature gate is disabled and dataSourceRef.Namespace is specified.
 | |
| 	if !utilfeature.DefaultFeatureGate.Enabled(features.CrossNamespaceVolumeDataSource) &&
 | |
| 		pvcSpec.DataSourceRef != nil && pvcSpec.DataSourceRef.Namespace != nil && len(*pvcSpec.DataSourceRef.Namespace) != 0 {
 | |
| 		if !dataSourceRefInUse(oldPVCSpec) {
 | |
| 			pvcSpec.DataSourceRef = nil
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // EnforceDataSourceBackwardsCompatibility drops the data source field under certain conditions
 | |
| // to maintain backwards compatibility with old behavior.
 | |
| // See KEP 1495 for details.
 | |
| // Specifically, if this is an update of a PVC with no data source, or a creation of a new PVC,
 | |
| // and the dataSourceRef field is not filled in, then we will drop "invalid" data sources
 | |
| // (anything other than a PVC or a VolumeSnapshot) from this request as if an empty PVC had
 | |
| // been requested.
 | |
| // This should be called after DropDisabledFields so that if the AnyVolumeDataSource feature
 | |
| // gate is disabled, dataSourceRef will be forced to empty, ensuring pre-1.22 behavior.
 | |
| // This should be called before NormalizeDataSources, so that data sources other than PVCs
 | |
| // and VolumeSnapshots can only be set through the dataSourceRef field and not the dataSource
 | |
| // field.
 | |
| func EnforceDataSourceBackwardsCompatibility(pvcSpec, oldPVCSpec *core.PersistentVolumeClaimSpec) {
 | |
| 	// Check if the old PVC has a data source here is so that on updates from old clients
 | |
| 	// that omit dataSourceRef, we preserve the data source, even if it would have been
 | |
| 	// invalid to specify it at using the dataSource field at create.
 | |
| 	if dataSourceInUse(oldPVCSpec) {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Check if dataSourceRef is empty is because if it's not empty, then there is
 | |
| 	// definitely a newer client and it definitely either wants to create a non-empty
 | |
| 	// volume, or it wants to update a PVC that has a data source. Whether the
 | |
| 	// specified data source is valid or satisfiable is a matter for validation and
 | |
| 	// the volume populator code, but we can say with certainty that the client is
 | |
| 	// not expecting the legacy behavior of ignoring invalid data sources.
 | |
| 	if pvcSpec.DataSourceRef != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Historically, we only allow PVCs and VolumeSnapshots in the dataSource field.
 | |
| 	// All other values are silently dropped.
 | |
| 	if !dataSourceIsPvcOrSnapshot(pvcSpec.DataSource) {
 | |
| 		pvcSpec.DataSource = nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func DropDisabledFieldsFromStatus(pvc, oldPVC *core.PersistentVolumeClaim) {
 | |
| 	if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeAttributesClass) {
 | |
| 		if oldPVC == nil || oldPVC.Status.CurrentVolumeAttributesClassName == nil {
 | |
| 			pvc.Status.CurrentVolumeAttributesClassName = nil
 | |
| 		}
 | |
| 		if oldPVC == nil || oldPVC.Status.ModifyVolumeStatus == nil {
 | |
| 			pvc.Status.ModifyVolumeStatus = nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if !utilfeature.DefaultFeatureGate.Enabled(features.RecoverVolumeExpansionFailure) {
 | |
| 		if !helper.ClaimContainsAllocatedResources(oldPVC) {
 | |
| 			pvc.Status.AllocatedResources = nil
 | |
| 		}
 | |
| 		if !helper.ClaimContainsAllocatedResourceStatus(oldPVC) {
 | |
| 			pvc.Status.AllocatedResourceStatuses = nil
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func dataSourceInUse(oldPVCSpec *core.PersistentVolumeClaimSpec) bool {
 | |
| 	if oldPVCSpec == nil {
 | |
| 		return false
 | |
| 	}
 | |
| 	if oldPVCSpec.DataSource != nil || oldPVCSpec.DataSourceRef != nil {
 | |
| 		return true
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func dataSourceIsPvcOrSnapshot(dataSource *core.TypedLocalObjectReference) bool {
 | |
| 	if dataSource != nil {
 | |
| 		apiGroup := ""
 | |
| 		if dataSource.APIGroup != nil {
 | |
| 			apiGroup = *dataSource.APIGroup
 | |
| 		}
 | |
| 		if dataSource.Kind == pvc &&
 | |
| 			apiGroup == "" {
 | |
| 			return true
 | |
| 		}
 | |
| 
 | |
| 		if dataSource.Kind == volumeSnapshot && apiGroup == "snapshot.storage.k8s.io" {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func dataSourceRefInUse(oldPVCSpec *core.PersistentVolumeClaimSpec) bool {
 | |
| 	if oldPVCSpec == nil {
 | |
| 		return false
 | |
| 	}
 | |
| 	if oldPVCSpec.DataSourceRef != nil {
 | |
| 		return true
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // NormalizeDataSources ensures that DataSource and DataSourceRef have the same contents
 | |
| // as long as both are not explicitly set.
 | |
| // This should be used by creates/gets of PVCs, but not updates
 | |
| func NormalizeDataSources(pvcSpec *core.PersistentVolumeClaimSpec) {
 | |
| 	// Don't enable this behavior if the feature gate is not on
 | |
| 	if !utilfeature.DefaultFeatureGate.Enabled(features.AnyVolumeDataSource) {
 | |
| 		return
 | |
| 	}
 | |
| 	if pvcSpec.DataSource != nil && pvcSpec.DataSourceRef == nil {
 | |
| 		// Using the old way of setting a data source
 | |
| 		pvcSpec.DataSourceRef = &core.TypedObjectReference{
 | |
| 			Kind: pvcSpec.DataSource.Kind,
 | |
| 			Name: pvcSpec.DataSource.Name,
 | |
| 		}
 | |
| 		if pvcSpec.DataSource.APIGroup != nil {
 | |
| 			apiGroup := *pvcSpec.DataSource.APIGroup
 | |
| 			pvcSpec.DataSourceRef.APIGroup = &apiGroup
 | |
| 		}
 | |
| 	} else if pvcSpec.DataSourceRef != nil && pvcSpec.DataSource == nil {
 | |
| 		if pvcSpec.DataSourceRef.Namespace == nil || len(*pvcSpec.DataSourceRef.Namespace) == 0 {
 | |
| 			// Using the new way of setting a data source
 | |
| 			pvcSpec.DataSource = &core.TypedLocalObjectReference{
 | |
| 				Kind: pvcSpec.DataSourceRef.Kind,
 | |
| 				Name: pvcSpec.DataSourceRef.Name,
 | |
| 			}
 | |
| 			if pvcSpec.DataSourceRef.APIGroup != nil {
 | |
| 				apiGroup := *pvcSpec.DataSourceRef.APIGroup
 | |
| 				pvcSpec.DataSource.APIGroup = &apiGroup
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func GetWarningsForPersistentVolumeClaim(pv *core.PersistentVolumeClaim) []string {
 | |
| 	var warnings []string
 | |
| 
 | |
| 	if pv == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if _, ok := pv.ObjectMeta.Annotations[core.BetaStorageClassAnnotation]; ok {
 | |
| 		warnings = append(warnings,
 | |
| 			fmt.Sprintf(
 | |
| 				"%s: %s",
 | |
| 				field.NewPath("metadata", "annotations").Key(core.BetaStorageClassAnnotation),
 | |
| 				deprecatedStorageClassAnnotationsMsg,
 | |
| 			),
 | |
| 		)
 | |
| 	}
 | |
| 
 | |
| 	warnings = append(warnings, GetWarningsForPersistentVolumeClaimSpec(field.NewPath("spec"), pv.Spec)...)
 | |
| 
 | |
| 	return warnings
 | |
| }
 | |
| 
 | |
| func GetWarningsForPersistentVolumeClaimSpec(fieldPath *field.Path, pvSpec core.PersistentVolumeClaimSpec) []string {
 | |
| 
 | |
| 	var warnings []string
 | |
| 	requestValue := pvSpec.Resources.Requests[core.ResourceStorage]
 | |
| 	if requestValue.MilliValue()%int64(1000) != int64(0) {
 | |
| 		warnings = append(warnings, fmt.Sprintf(
 | |
| 			"%s: fractional byte value %q is invalid, must be an integer",
 | |
| 			fieldPath.Child("resources").Child("requests").Key(core.ResourceStorage.String()), requestValue.String()))
 | |
| 	}
 | |
| 	limitValue := pvSpec.Resources.Limits[core.ResourceStorage]
 | |
| 	if limitValue.MilliValue()%int64(1000) != int64(0) {
 | |
| 		warnings = append(warnings, fmt.Sprintf(
 | |
| 			"%s: fractional byte value %q is invalid, must be an integer",
 | |
| 			fieldPath.Child("resources").Child("limits").Key(core.ResourceStorage.String()), limitValue.String()))
 | |
| 	}
 | |
| 	return warnings
 | |
| }
 |