Merge pull request #124505 from jsafrane/clean-pvlabeler
Remove PersistentVolumeLabel admission plugin
This commit is contained in:
		@@ -56,7 +56,7 @@ function run_kube_apiserver() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  # Admission Controllers to invoke prior to persisting objects in cluster
 | 
					  # Admission Controllers to invoke prior to persisting objects in cluster
 | 
				
			||||||
  ENABLE_ADMISSION_PLUGINS="LimitRanger,ResourceQuota"
 | 
					  ENABLE_ADMISSION_PLUGINS="LimitRanger,ResourceQuota"
 | 
				
			||||||
  DISABLE_ADMISSION_PLUGINS="ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,StorageObjectInUseProtection"
 | 
					  DISABLE_ADMISSION_PLUGINS="ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,StorageObjectInUseProtection"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Include RBAC (to exercise bootstrapping), and AlwaysAllow to allow all actions
 | 
					  # Include RBAC (to exercise bootstrapping), and AlwaysAllow to allow all actions
 | 
				
			||||||
  AUTHORIZATION_MODE="RBAC,AlwaysAllow"
 | 
					  AUTHORIZATION_MODE="RBAC,AlwaysAllow"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,7 +49,6 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/plugin/pkg/admission/runtimeclass"
 | 
						"k8s.io/kubernetes/plugin/pkg/admission/runtimeclass"
 | 
				
			||||||
	"k8s.io/kubernetes/plugin/pkg/admission/security/podsecurity"
 | 
						"k8s.io/kubernetes/plugin/pkg/admission/security/podsecurity"
 | 
				
			||||||
	"k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
 | 
						"k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
 | 
				
			||||||
	"k8s.io/kubernetes/plugin/pkg/admission/storage/persistentvolume/label"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/plugin/pkg/admission/storage/persistentvolume/resize"
 | 
						"k8s.io/kubernetes/plugin/pkg/admission/storage/persistentvolume/resize"
 | 
				
			||||||
	"k8s.io/kubernetes/plugin/pkg/admission/storage/storageclass/setdefault"
 | 
						"k8s.io/kubernetes/plugin/pkg/admission/storage/storageclass/setdefault"
 | 
				
			||||||
	"k8s.io/kubernetes/plugin/pkg/admission/storage/storageobjectinuseprotection"
 | 
						"k8s.io/kubernetes/plugin/pkg/admission/storage/storageobjectinuseprotection"
 | 
				
			||||||
@@ -82,7 +81,6 @@ var AllOrderedPlugins = []string{
 | 
				
			|||||||
	podtolerationrestriction.PluginName,     // PodTolerationRestriction
 | 
						podtolerationrestriction.PluginName,     // PodTolerationRestriction
 | 
				
			||||||
	eventratelimit.PluginName,               // EventRateLimit
 | 
						eventratelimit.PluginName,               // EventRateLimit
 | 
				
			||||||
	extendedresourcetoleration.PluginName,   // ExtendedResourceToleration
 | 
						extendedresourcetoleration.PluginName,   // ExtendedResourceToleration
 | 
				
			||||||
	label.PluginName,                        // PersistentVolumeLabel
 | 
					 | 
				
			||||||
	setdefault.PluginName,                   // DefaultStorageClass
 | 
						setdefault.PluginName,                   // DefaultStorageClass
 | 
				
			||||||
	storageobjectinuseprotection.PluginName, // StorageObjectInUseProtection
 | 
						storageobjectinuseprotection.PluginName, // StorageObjectInUseProtection
 | 
				
			||||||
	gc.PluginName,                           // OwnerReferencesPermissionEnforcement
 | 
						gc.PluginName,                           // OwnerReferencesPermissionEnforcement
 | 
				
			||||||
@@ -126,7 +124,6 @@ func RegisterAllAdmissionPlugins(plugins *admission.Plugins) {
 | 
				
			|||||||
	exists.Register(plugins)
 | 
						exists.Register(plugins)
 | 
				
			||||||
	noderestriction.Register(plugins)
 | 
						noderestriction.Register(plugins)
 | 
				
			||||||
	nodetaint.Register(plugins)
 | 
						nodetaint.Register(plugins)
 | 
				
			||||||
	label.Register(plugins) // DEPRECATED, future PVs should not rely on labels for zone topology
 | 
					 | 
				
			||||||
	podnodeselector.Register(plugins)
 | 
						podnodeselector.Register(plugins)
 | 
				
			||||||
	podtolerationrestriction.Register(plugins)
 | 
						podtolerationrestriction.Register(plugins)
 | 
				
			||||||
	runtimeclass.Register(plugins)
 | 
						runtimeclass.Register(plugins)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +0,0 @@
 | 
				
			|||||||
# See the OWNERS docs at https://go.k8s.io/owners
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
reviewers:
 | 
					 | 
				
			||||||
  - andrewsykim
 | 
					 | 
				
			||||||
  - dims
 | 
					 | 
				
			||||||
  - msau42
 | 
					 | 
				
			||||||
approvers:
 | 
					 | 
				
			||||||
  - andrewsykim
 | 
					 | 
				
			||||||
  - dims
 | 
					 | 
				
			||||||
  - msau42
 | 
					 | 
				
			||||||
@@ -1,309 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2015 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 label
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"context"
 | 
					 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
					 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
					 | 
				
			||||||
	"k8s.io/apiserver/pkg/admission"
 | 
					 | 
				
			||||||
	cloudprovider "k8s.io/cloud-provider"
 | 
					 | 
				
			||||||
	cloudvolume "k8s.io/cloud-provider/volume"
 | 
					 | 
				
			||||||
	volumehelpers "k8s.io/cloud-provider/volume/helpers"
 | 
					 | 
				
			||||||
	persistentvolume "k8s.io/component-helpers/storage/volume"
 | 
					 | 
				
			||||||
	"k8s.io/klog/v2"
 | 
					 | 
				
			||||||
	api "k8s.io/kubernetes/pkg/apis/core"
 | 
					 | 
				
			||||||
	k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
 | 
					 | 
				
			||||||
	kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	// PluginName is the name of persistent volume label admission plugin
 | 
					 | 
				
			||||||
	PluginName = "PersistentVolumeLabel"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Register registers a plugin
 | 
					 | 
				
			||||||
func Register(plugins *admission.Plugins) {
 | 
					 | 
				
			||||||
	plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
 | 
					 | 
				
			||||||
		persistentVolumeLabelAdmission := newPersistentVolumeLabel()
 | 
					 | 
				
			||||||
		return persistentVolumeLabelAdmission, nil
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _ = admission.Interface(&persistentVolumeLabel{})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type persistentVolumeLabel struct {
 | 
					 | 
				
			||||||
	*admission.Handler
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mutex            sync.Mutex
 | 
					 | 
				
			||||||
	cloudConfig      []byte
 | 
					 | 
				
			||||||
	azurePVLabeler   cloudprovider.PVLabeler
 | 
					 | 
				
			||||||
	vspherePVLabeler cloudprovider.PVLabeler
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _ admission.MutationInterface = &persistentVolumeLabel{}
 | 
					 | 
				
			||||||
var _ kubeapiserveradmission.WantsCloudConfig = &persistentVolumeLabel{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// newPersistentVolumeLabel returns an admission.Interface implementation which adds labels to PersistentVolume CREATE requests,
 | 
					 | 
				
			||||||
// based on the labels provided by the underlying cloud provider.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// As a side effect, the cloud provider may block invalid or non-existent volumes.
 | 
					 | 
				
			||||||
func newPersistentVolumeLabel() *persistentVolumeLabel {
 | 
					 | 
				
			||||||
	// DEPRECATED: in a future release, we will use mutating admission webhooks to apply PV labels.
 | 
					 | 
				
			||||||
	// Once the mutating admission webhook is used for Azure, and GCE,
 | 
					 | 
				
			||||||
	// this admission controller will be removed.
 | 
					 | 
				
			||||||
	klog.Warning("PersistentVolumeLabel admission controller is deprecated. " +
 | 
					 | 
				
			||||||
		"Please remove this controller from your configuration files and scripts.")
 | 
					 | 
				
			||||||
	return &persistentVolumeLabel{
 | 
					 | 
				
			||||||
		Handler: admission.NewHandler(admission.Create),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l *persistentVolumeLabel) SetCloudConfig(cloudConfig []byte) {
 | 
					 | 
				
			||||||
	l.cloudConfig = cloudConfig
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func nodeSelectorRequirementKeysExistInNodeSelectorTerms(reqs []api.NodeSelectorRequirement, terms []api.NodeSelectorTerm) bool {
 | 
					 | 
				
			||||||
	for _, req := range reqs {
 | 
					 | 
				
			||||||
		for _, term := range terms {
 | 
					 | 
				
			||||||
			for _, r := range term.MatchExpressions {
 | 
					 | 
				
			||||||
				if r.Key == req.Key {
 | 
					 | 
				
			||||||
					return true
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l *persistentVolumeLabel) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error) {
 | 
					 | 
				
			||||||
	if a.GetResource().GroupResource() != api.Resource("persistentvolumes") {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	obj := a.GetObject()
 | 
					 | 
				
			||||||
	if obj == nil {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	volume, ok := obj.(*api.PersistentVolume)
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	volumeLabels, err := l.findVolumeLabels(volume)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return admission.NewForbidden(a, err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	requirements := make([]api.NodeSelectorRequirement, 0)
 | 
					 | 
				
			||||||
	if len(volumeLabels) != 0 {
 | 
					 | 
				
			||||||
		if volume.Labels == nil {
 | 
					 | 
				
			||||||
			volume.Labels = make(map[string]string)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		for k, v := range volumeLabels {
 | 
					 | 
				
			||||||
			// We (silently) replace labels if they are provided.
 | 
					 | 
				
			||||||
			// This should be OK because they are in the kubernetes.io namespace
 | 
					 | 
				
			||||||
			// i.e. we own them
 | 
					 | 
				
			||||||
			volume.Labels[k] = v
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Set NodeSelectorRequirements based on the labels
 | 
					 | 
				
			||||||
			var values []string
 | 
					 | 
				
			||||||
			if k == v1.LabelTopologyZone || k == v1.LabelFailureDomainBetaZone {
 | 
					 | 
				
			||||||
				zones, err := volumehelpers.LabelZonesToSet(v)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					return admission.NewForbidden(a, fmt.Errorf("failed to convert label string for Zone: %s to a Set", v))
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				// zone values here are sorted for better testability.
 | 
					 | 
				
			||||||
				values = zones.List()
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				values = []string{v}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			requirements = append(requirements, api.NodeSelectorRequirement{Key: k, Operator: api.NodeSelectorOpIn, Values: values})
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if volume.Spec.NodeAffinity == nil {
 | 
					 | 
				
			||||||
			volume.Spec.NodeAffinity = new(api.VolumeNodeAffinity)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if volume.Spec.NodeAffinity.Required == nil {
 | 
					 | 
				
			||||||
			volume.Spec.NodeAffinity.Required = new(api.NodeSelector)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if len(volume.Spec.NodeAffinity.Required.NodeSelectorTerms) == 0 {
 | 
					 | 
				
			||||||
			// Need at least one term pre-allocated whose MatchExpressions can be appended to
 | 
					 | 
				
			||||||
			volume.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]api.NodeSelectorTerm, 1)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if nodeSelectorRequirementKeysExistInNodeSelectorTerms(requirements, volume.Spec.NodeAffinity.Required.NodeSelectorTerms) {
 | 
					 | 
				
			||||||
			klog.V(4).Infof("NodeSelectorRequirements for cloud labels %v conflict with existing NodeAffinity %v. Skipping addition of NodeSelectorRequirements for cloud labels.",
 | 
					 | 
				
			||||||
				requirements, volume.Spec.NodeAffinity)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			for _, req := range requirements {
 | 
					 | 
				
			||||||
				for i := range volume.Spec.NodeAffinity.Required.NodeSelectorTerms {
 | 
					 | 
				
			||||||
					volume.Spec.NodeAffinity.Required.NodeSelectorTerms[i].MatchExpressions = append(volume.Spec.NodeAffinity.Required.NodeSelectorTerms[i].MatchExpressions, req)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l *persistentVolumeLabel) findVolumeLabels(volume *api.PersistentVolume) (map[string]string, error) {
 | 
					 | 
				
			||||||
	existingLabels := volume.Labels
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// All cloud providers set only these two labels.
 | 
					 | 
				
			||||||
	topologyLabelGA := true
 | 
					 | 
				
			||||||
	domain, domainOK := existingLabels[v1.LabelTopologyZone]
 | 
					 | 
				
			||||||
	region, regionOK := existingLabels[v1.LabelTopologyRegion]
 | 
					 | 
				
			||||||
	// If they don't have GA labels we should check for failuredomain beta labels
 | 
					 | 
				
			||||||
	// TODO: remove this once all the cloud provider change to GA topology labels
 | 
					 | 
				
			||||||
	if !domainOK || !regionOK {
 | 
					 | 
				
			||||||
		topologyLabelGA = false
 | 
					 | 
				
			||||||
		domain, domainOK = existingLabels[v1.LabelFailureDomainBetaZone]
 | 
					 | 
				
			||||||
		region, regionOK = existingLabels[v1.LabelFailureDomainBetaRegion]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	isDynamicallyProvisioned := metav1.HasAnnotation(volume.ObjectMeta, persistentvolume.AnnDynamicallyProvisioned)
 | 
					 | 
				
			||||||
	if isDynamicallyProvisioned && domainOK && regionOK {
 | 
					 | 
				
			||||||
		// PV already has all the labels and we can trust the dynamic provisioning that it provided correct values.
 | 
					 | 
				
			||||||
		if topologyLabelGA {
 | 
					 | 
				
			||||||
			return map[string]string{
 | 
					 | 
				
			||||||
				v1.LabelTopologyZone:   domain,
 | 
					 | 
				
			||||||
				v1.LabelTopologyRegion: region,
 | 
					 | 
				
			||||||
			}, nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return map[string]string{
 | 
					 | 
				
			||||||
			v1.LabelFailureDomainBetaZone:   domain,
 | 
					 | 
				
			||||||
			v1.LabelFailureDomainBetaRegion: region,
 | 
					 | 
				
			||||||
		}, nil
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Either missing labels or we don't trust the user provided correct values.
 | 
					 | 
				
			||||||
	switch {
 | 
					 | 
				
			||||||
	case volume.Spec.AzureDisk != nil:
 | 
					 | 
				
			||||||
		labels, err := l.findAzureDiskLabels(volume)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, fmt.Errorf("error querying AzureDisk volume %s: %v", volume.Spec.AzureDisk.DiskName, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return labels, nil
 | 
					 | 
				
			||||||
	case volume.Spec.VsphereVolume != nil:
 | 
					 | 
				
			||||||
		labels, err := l.findVsphereVolumeLabels(volume)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, fmt.Errorf("error querying vSphere Volume %s: %v", volume.Spec.VsphereVolume.VolumePath, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return labels, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// Unrecognized volume, do not add any labels
 | 
					 | 
				
			||||||
	return nil, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getAzurePVLabeler returns the Azure implementation of PVLabeler
 | 
					 | 
				
			||||||
func (l *persistentVolumeLabel) getAzurePVLabeler() (cloudprovider.PVLabeler, error) {
 | 
					 | 
				
			||||||
	l.mutex.Lock()
 | 
					 | 
				
			||||||
	defer l.mutex.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if l.azurePVLabeler == nil {
 | 
					 | 
				
			||||||
		var cloudConfigReader io.Reader
 | 
					 | 
				
			||||||
		if len(l.cloudConfig) > 0 {
 | 
					 | 
				
			||||||
			cloudConfigReader = bytes.NewReader(l.cloudConfig)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		cloudProvider, err := cloudprovider.GetCloudProvider("azure", cloudConfigReader)
 | 
					 | 
				
			||||||
		if err != nil || cloudProvider == nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		azurePVLabeler, ok := cloudProvider.(cloudprovider.PVLabeler)
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			return nil, errors.New("Azure cloud provider does not implement PV labeling")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		l.azurePVLabeler = azurePVLabeler
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return l.azurePVLabeler, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l *persistentVolumeLabel) findAzureDiskLabels(volume *api.PersistentVolume) (map[string]string, error) {
 | 
					 | 
				
			||||||
	// Ignore any volumes that are being provisioned
 | 
					 | 
				
			||||||
	if volume.Spec.AzureDisk.DiskName == cloudvolume.ProvisionedVolumeName {
 | 
					 | 
				
			||||||
		return nil, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pvlabler, err := l.getAzurePVLabeler()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if pvlabler == nil {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("unable to build Azure cloud provider for AzureDisk")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pv := &v1.PersistentVolume{}
 | 
					 | 
				
			||||||
	err = k8s_api_v1.Convert_core_PersistentVolume_To_v1_PersistentVolume(volume, pv, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("failed to convert PersistentVolume to core/v1: %q", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return pvlabler.GetLabelsForVolume(context.TODO(), pv)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l *persistentVolumeLabel) findVsphereVolumeLabels(volume *api.PersistentVolume) (map[string]string, error) {
 | 
					 | 
				
			||||||
	pvlabler, err := l.getVspherePVLabeler()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if pvlabler == nil {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("unable to build vSphere cloud provider")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pv := &v1.PersistentVolume{}
 | 
					 | 
				
			||||||
	err = k8s_api_v1.Convert_core_PersistentVolume_To_v1_PersistentVolume(volume, pv, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("failed to convert PersistentVolume to core/v1: %q", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	labels, err := pvlabler.GetLabelsForVolume(context.TODO(), pv)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return labels, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l *persistentVolumeLabel) getVspherePVLabeler() (cloudprovider.PVLabeler, error) {
 | 
					 | 
				
			||||||
	l.mutex.Lock()
 | 
					 | 
				
			||||||
	defer l.mutex.Unlock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if l.vspherePVLabeler == nil {
 | 
					 | 
				
			||||||
		var cloudConfigReader io.Reader
 | 
					 | 
				
			||||||
		if len(l.cloudConfig) > 0 {
 | 
					 | 
				
			||||||
			cloudConfigReader = bytes.NewReader(l.cloudConfig)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		cloudProvider, err := cloudprovider.GetCloudProvider("vsphere", cloudConfigReader)
 | 
					 | 
				
			||||||
		if err != nil || cloudProvider == nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		vspherePVLabeler, ok := cloudProvider.(cloudprovider.PVLabeler)
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			// GetCloudProvider failed
 | 
					 | 
				
			||||||
			return nil, errors.New("vSphere Cloud Provider does not implement PV labeling")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		l.vspherePVLabeler = vspherePVLabeler
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return l.vspherePVLabeler, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,666 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2015 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 label
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"context"
 | 
					 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
	"sort"
 | 
					 | 
				
			||||||
	"testing"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
					 | 
				
			||||||
	apierrors "k8s.io/apimachinery/pkg/api/errors"
 | 
					 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
					 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
					 | 
				
			||||||
	"k8s.io/apiserver/pkg/admission"
 | 
					 | 
				
			||||||
	admissiontesting "k8s.io/apiserver/pkg/admission/testing"
 | 
					 | 
				
			||||||
	cloudprovider "k8s.io/cloud-provider"
 | 
					 | 
				
			||||||
	persistentvolume "k8s.io/component-helpers/storage/volume"
 | 
					 | 
				
			||||||
	api "k8s.io/kubernetes/pkg/apis/core"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type mockVolumes struct {
 | 
					 | 
				
			||||||
	volumeLabels      map[string]string
 | 
					 | 
				
			||||||
	volumeLabelsError error
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _ cloudprovider.PVLabeler = &mockVolumes{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (v *mockVolumes) GetLabelsForVolume(ctx context.Context, pv *v1.PersistentVolume) (map[string]string, error) {
 | 
					 | 
				
			||||||
	return v.volumeLabels, v.volumeLabelsError
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func mockVolumeFailure(err error) *mockVolumes {
 | 
					 | 
				
			||||||
	return &mockVolumes{volumeLabelsError: err}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func mockVolumeLabels(labels map[string]string) *mockVolumes {
 | 
					 | 
				
			||||||
	return &mockVolumes{volumeLabels: labels}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func Test_PVLAdmission(t *testing.T) {
 | 
					 | 
				
			||||||
	testcases := []struct {
 | 
					 | 
				
			||||||
		name            string
 | 
					 | 
				
			||||||
		handler         *persistentVolumeLabel
 | 
					 | 
				
			||||||
		pvlabeler       cloudprovider.PVLabeler
 | 
					 | 
				
			||||||
		preAdmissionPV  *api.PersistentVolume
 | 
					 | 
				
			||||||
		postAdmissionPV *api.PersistentVolume
 | 
					 | 
				
			||||||
		err             error
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "non-cloud PV ignored",
 | 
					 | 
				
			||||||
			handler: newPersistentVolumeLabel(),
 | 
					 | 
				
			||||||
			pvlabeler: mockVolumeLabels(map[string]string{
 | 
					 | 
				
			||||||
				"a":                  "1",
 | 
					 | 
				
			||||||
				"b":                  "2",
 | 
					 | 
				
			||||||
				v1.LabelTopologyZone: "1__2__3",
 | 
					 | 
				
			||||||
			}),
 | 
					 | 
				
			||||||
			preAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "noncloud", Namespace: "myns"},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						HostPath: &api.HostPathVolumeSource{
 | 
					 | 
				
			||||||
							Path: "/",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			postAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "noncloud", Namespace: "myns"},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						HostPath: &api.HostPathVolumeSource{
 | 
					 | 
				
			||||||
							Path: "/",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			err: nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:      "cloud provider error blocks creation of volume",
 | 
					 | 
				
			||||||
			handler:   newPersistentVolumeLabel(),
 | 
					 | 
				
			||||||
			pvlabeler: mockVolumeFailure(errors.New("invalid volume")),
 | 
					 | 
				
			||||||
			preAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "vSpherePV", Namespace: "myns"},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						VsphereVolume: &api.VsphereVirtualDiskVolumeSource{
 | 
					 | 
				
			||||||
							VolumePath: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			postAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "vSpherePV", Namespace: "myns"},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						VsphereVolume: &api.VsphereVirtualDiskVolumeSource{
 | 
					 | 
				
			||||||
							VolumePath: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			err: apierrors.NewForbidden(schema.ParseGroupResource("persistentvolumes"), "vSpherePV", errors.New("error querying vSphere Volume 123: invalid volume")),
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:      "cloud provider returns no labels",
 | 
					 | 
				
			||||||
			handler:   newPersistentVolumeLabel(),
 | 
					 | 
				
			||||||
			pvlabeler: mockVolumeLabels(map[string]string{}),
 | 
					 | 
				
			||||||
			preAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "awsebs", Namespace: "myns"},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
 | 
					 | 
				
			||||||
							VolumeID: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			postAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "awsebs", Namespace: "myns"},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
 | 
					 | 
				
			||||||
							VolumeID: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			err: nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:      "cloud provider returns nil, nil",
 | 
					 | 
				
			||||||
			handler:   newPersistentVolumeLabel(),
 | 
					 | 
				
			||||||
			pvlabeler: mockVolumeFailure(nil),
 | 
					 | 
				
			||||||
			preAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "awsebs", Namespace: "myns"},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
 | 
					 | 
				
			||||||
							VolumeID: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			postAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{Name: "awsebs", Namespace: "myns"},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
 | 
					 | 
				
			||||||
							VolumeID: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			err: nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "existing Beta labels from dynamic provisioning are not changed",
 | 
					 | 
				
			||||||
			handler: newPersistentVolumeLabel(),
 | 
					 | 
				
			||||||
			pvlabeler: mockVolumeLabels(map[string]string{
 | 
					 | 
				
			||||||
				v1.LabelFailureDomainBetaZone:   "domain1",
 | 
					 | 
				
			||||||
				v1.LabelFailureDomainBetaRegion: "region1",
 | 
					 | 
				
			||||||
			}),
 | 
					 | 
				
			||||||
			preAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
					Name: "awsebs", Namespace: "myns",
 | 
					 | 
				
			||||||
					Labels: map[string]string{
 | 
					 | 
				
			||||||
						v1.LabelFailureDomainBetaZone:   "existingDomain",
 | 
					 | 
				
			||||||
						v1.LabelFailureDomainBetaRegion: "existingRegion",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					Annotations: map[string]string{
 | 
					 | 
				
			||||||
						persistentvolume.AnnDynamicallyProvisioned: "kubernetes.io/aws-ebs",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
 | 
					 | 
				
			||||||
							VolumeID: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			postAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
					Name:      "awsebs",
 | 
					 | 
				
			||||||
					Namespace: "myns",
 | 
					 | 
				
			||||||
					Labels: map[string]string{
 | 
					 | 
				
			||||||
						v1.LabelFailureDomainBetaZone:   "existingDomain",
 | 
					 | 
				
			||||||
						v1.LabelFailureDomainBetaRegion: "existingRegion",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					Annotations: map[string]string{
 | 
					 | 
				
			||||||
						persistentvolume.AnnDynamicallyProvisioned: "kubernetes.io/aws-ebs",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
 | 
					 | 
				
			||||||
							VolumeID: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					NodeAffinity: &api.VolumeNodeAffinity{
 | 
					 | 
				
			||||||
						Required: &api.NodeSelector{
 | 
					 | 
				
			||||||
							NodeSelectorTerms: []api.NodeSelectorTerm{
 | 
					 | 
				
			||||||
								{
 | 
					 | 
				
			||||||
									MatchExpressions: []api.NodeSelectorRequirement{
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      v1.LabelFailureDomainBetaRegion,
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"existingRegion"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      v1.LabelFailureDomainBetaZone,
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"existingDomain"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
									},
 | 
					 | 
				
			||||||
								},
 | 
					 | 
				
			||||||
							},
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			err: nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "existing GA labels from dynamic provisioning are not changed",
 | 
					 | 
				
			||||||
			handler: newPersistentVolumeLabel(),
 | 
					 | 
				
			||||||
			pvlabeler: mockVolumeLabels(map[string]string{
 | 
					 | 
				
			||||||
				v1.LabelTopologyZone:   "domain1",
 | 
					 | 
				
			||||||
				v1.LabelTopologyRegion: "region1",
 | 
					 | 
				
			||||||
			}),
 | 
					 | 
				
			||||||
			preAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
					Name: "awsebs", Namespace: "myns",
 | 
					 | 
				
			||||||
					Labels: map[string]string{
 | 
					 | 
				
			||||||
						v1.LabelTopologyZone:   "existingDomain",
 | 
					 | 
				
			||||||
						v1.LabelTopologyRegion: "existingRegion",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					Annotations: map[string]string{
 | 
					 | 
				
			||||||
						persistentvolume.AnnDynamicallyProvisioned: "kubernetes.io/aws-ebs",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
 | 
					 | 
				
			||||||
							VolumeID: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			postAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
					Name:      "awsebs",
 | 
					 | 
				
			||||||
					Namespace: "myns",
 | 
					 | 
				
			||||||
					Labels: map[string]string{
 | 
					 | 
				
			||||||
						v1.LabelTopologyZone:   "existingDomain",
 | 
					 | 
				
			||||||
						v1.LabelTopologyRegion: "existingRegion",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					Annotations: map[string]string{
 | 
					 | 
				
			||||||
						persistentvolume.AnnDynamicallyProvisioned: "kubernetes.io/aws-ebs",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						AWSElasticBlockStore: &api.AWSElasticBlockStoreVolumeSource{
 | 
					 | 
				
			||||||
							VolumeID: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					NodeAffinity: &api.VolumeNodeAffinity{
 | 
					 | 
				
			||||||
						Required: &api.NodeSelector{
 | 
					 | 
				
			||||||
							NodeSelectorTerms: []api.NodeSelectorTerm{
 | 
					 | 
				
			||||||
								{
 | 
					 | 
				
			||||||
									MatchExpressions: []api.NodeSelectorRequirement{
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      v1.LabelTopologyRegion,
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"existingRegion"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      v1.LabelTopologyZone,
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"existingDomain"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
									},
 | 
					 | 
				
			||||||
								},
 | 
					 | 
				
			||||||
							},
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			err: nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "existing labels from user are changed",
 | 
					 | 
				
			||||||
			handler: newPersistentVolumeLabel(),
 | 
					 | 
				
			||||||
			pvlabeler: mockVolumeLabels(map[string]string{
 | 
					 | 
				
			||||||
				v1.LabelTopologyZone:   "domain1",
 | 
					 | 
				
			||||||
				v1.LabelTopologyRegion: "region1",
 | 
					 | 
				
			||||||
			}),
 | 
					 | 
				
			||||||
			preAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
					Name: "vSpherePV", Namespace: "myns",
 | 
					 | 
				
			||||||
					Labels: map[string]string{
 | 
					 | 
				
			||||||
						v1.LabelTopologyZone:   "existingDomain",
 | 
					 | 
				
			||||||
						v1.LabelTopologyRegion: "existingRegion",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						VsphereVolume: &api.VsphereVirtualDiskVolumeSource{
 | 
					 | 
				
			||||||
							VolumePath: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			postAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
					Name:      "vSpherePV",
 | 
					 | 
				
			||||||
					Namespace: "myns",
 | 
					 | 
				
			||||||
					Labels: map[string]string{
 | 
					 | 
				
			||||||
						v1.LabelTopologyZone:   "domain1",
 | 
					 | 
				
			||||||
						v1.LabelTopologyRegion: "region1",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						VsphereVolume: &api.VsphereVirtualDiskVolumeSource{
 | 
					 | 
				
			||||||
							VolumePath: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					NodeAffinity: &api.VolumeNodeAffinity{
 | 
					 | 
				
			||||||
						Required: &api.NodeSelector{
 | 
					 | 
				
			||||||
							NodeSelectorTerms: []api.NodeSelectorTerm{
 | 
					 | 
				
			||||||
								{
 | 
					 | 
				
			||||||
									MatchExpressions: []api.NodeSelectorRequirement{
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      v1.LabelTopologyRegion,
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"region1"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      v1.LabelTopologyZone,
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"domain1"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
									},
 | 
					 | 
				
			||||||
								},
 | 
					 | 
				
			||||||
							},
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			err: nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "Azure Disk PV labeled correctly",
 | 
					 | 
				
			||||||
			handler: newPersistentVolumeLabel(),
 | 
					 | 
				
			||||||
			pvlabeler: mockVolumeLabels(map[string]string{
 | 
					 | 
				
			||||||
				"a":                           "1",
 | 
					 | 
				
			||||||
				"b":                           "2",
 | 
					 | 
				
			||||||
				v1.LabelFailureDomainBetaZone: "1__2__3",
 | 
					 | 
				
			||||||
			}),
 | 
					 | 
				
			||||||
			preAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
					Name:      "azurepd",
 | 
					 | 
				
			||||||
					Namespace: "myns",
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						AzureDisk: &api.AzureDiskVolumeSource{
 | 
					 | 
				
			||||||
							DiskName: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			postAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
					Name:      "azurepd",
 | 
					 | 
				
			||||||
					Namespace: "myns",
 | 
					 | 
				
			||||||
					Labels: map[string]string{
 | 
					 | 
				
			||||||
						"a":                           "1",
 | 
					 | 
				
			||||||
						"b":                           "2",
 | 
					 | 
				
			||||||
						v1.LabelFailureDomainBetaZone: "1__2__3",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						AzureDisk: &api.AzureDiskVolumeSource{
 | 
					 | 
				
			||||||
							DiskName: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					NodeAffinity: &api.VolumeNodeAffinity{
 | 
					 | 
				
			||||||
						Required: &api.NodeSelector{
 | 
					 | 
				
			||||||
							NodeSelectorTerms: []api.NodeSelectorTerm{
 | 
					 | 
				
			||||||
								{
 | 
					 | 
				
			||||||
									MatchExpressions: []api.NodeSelectorRequirement{
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      "a",
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"1"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      "b",
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"2"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      v1.LabelFailureDomainBetaZone,
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"1", "2", "3"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
									},
 | 
					 | 
				
			||||||
								},
 | 
					 | 
				
			||||||
							},
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			err: nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "vSphere PV non-conflicting affinity rules added",
 | 
					 | 
				
			||||||
			handler: newPersistentVolumeLabel(),
 | 
					 | 
				
			||||||
			pvlabeler: mockVolumeLabels(map[string]string{
 | 
					 | 
				
			||||||
				"d": "1",
 | 
					 | 
				
			||||||
				"e": "2",
 | 
					 | 
				
			||||||
				"f": "3",
 | 
					 | 
				
			||||||
			}),
 | 
					 | 
				
			||||||
			preAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
					Name:      "vSpherePV",
 | 
					 | 
				
			||||||
					Namespace: "myns",
 | 
					 | 
				
			||||||
					Labels: map[string]string{
 | 
					 | 
				
			||||||
						"a": "1",
 | 
					 | 
				
			||||||
						"b": "2",
 | 
					 | 
				
			||||||
						"c": "3",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						VsphereVolume: &api.VsphereVirtualDiskVolumeSource{
 | 
					 | 
				
			||||||
							VolumePath: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					NodeAffinity: &api.VolumeNodeAffinity{
 | 
					 | 
				
			||||||
						Required: &api.NodeSelector{
 | 
					 | 
				
			||||||
							NodeSelectorTerms: []api.NodeSelectorTerm{
 | 
					 | 
				
			||||||
								{
 | 
					 | 
				
			||||||
									MatchExpressions: []api.NodeSelectorRequirement{
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      "a",
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"1"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      "b",
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"2"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      "c",
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"3"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
									},
 | 
					 | 
				
			||||||
								},
 | 
					 | 
				
			||||||
							},
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			postAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
					Name:      "vSpherePV",
 | 
					 | 
				
			||||||
					Namespace: "myns",
 | 
					 | 
				
			||||||
					Labels: map[string]string{
 | 
					 | 
				
			||||||
						"a": "1",
 | 
					 | 
				
			||||||
						"b": "2",
 | 
					 | 
				
			||||||
						"c": "3",
 | 
					 | 
				
			||||||
						"d": "1",
 | 
					 | 
				
			||||||
						"e": "2",
 | 
					 | 
				
			||||||
						"f": "3",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						VsphereVolume: &api.VsphereVirtualDiskVolumeSource{
 | 
					 | 
				
			||||||
							VolumePath: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					NodeAffinity: &api.VolumeNodeAffinity{
 | 
					 | 
				
			||||||
						Required: &api.NodeSelector{
 | 
					 | 
				
			||||||
							NodeSelectorTerms: []api.NodeSelectorTerm{
 | 
					 | 
				
			||||||
								{
 | 
					 | 
				
			||||||
									MatchExpressions: []api.NodeSelectorRequirement{
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      "a",
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"1"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      "b",
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"2"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      "c",
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"3"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      "d",
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"1"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      "e",
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"2"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      "f",
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"3"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
									},
 | 
					 | 
				
			||||||
								},
 | 
					 | 
				
			||||||
							},
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			err: nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:    "vSphere PV labeled correctly",
 | 
					 | 
				
			||||||
			handler: newPersistentVolumeLabel(),
 | 
					 | 
				
			||||||
			pvlabeler: mockVolumeLabels(map[string]string{
 | 
					 | 
				
			||||||
				"a":                           "1",
 | 
					 | 
				
			||||||
				"b":                           "2",
 | 
					 | 
				
			||||||
				v1.LabelFailureDomainBetaZone: "1__2__3",
 | 
					 | 
				
			||||||
			}),
 | 
					 | 
				
			||||||
			preAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
					Name:      "vSpherePV",
 | 
					 | 
				
			||||||
					Namespace: "myns",
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						VsphereVolume: &api.VsphereVirtualDiskVolumeSource{
 | 
					 | 
				
			||||||
							VolumePath: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			postAdmissionPV: &api.PersistentVolume{
 | 
					 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
					 | 
				
			||||||
					Name:      "vSpherePV",
 | 
					 | 
				
			||||||
					Namespace: "myns",
 | 
					 | 
				
			||||||
					Labels: map[string]string{
 | 
					 | 
				
			||||||
						"a":                           "1",
 | 
					 | 
				
			||||||
						"b":                           "2",
 | 
					 | 
				
			||||||
						v1.LabelFailureDomainBetaZone: "1__2__3",
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Spec: api.PersistentVolumeSpec{
 | 
					 | 
				
			||||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
					 | 
				
			||||||
						VsphereVolume: &api.VsphereVirtualDiskVolumeSource{
 | 
					 | 
				
			||||||
							VolumePath: "123",
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					NodeAffinity: &api.VolumeNodeAffinity{
 | 
					 | 
				
			||||||
						Required: &api.NodeSelector{
 | 
					 | 
				
			||||||
							NodeSelectorTerms: []api.NodeSelectorTerm{
 | 
					 | 
				
			||||||
								{
 | 
					 | 
				
			||||||
									MatchExpressions: []api.NodeSelectorRequirement{
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      "a",
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"1"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      "b",
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"2"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
										{
 | 
					 | 
				
			||||||
											Key:      v1.LabelFailureDomainBetaZone,
 | 
					 | 
				
			||||||
											Operator: api.NodeSelectorOpIn,
 | 
					 | 
				
			||||||
											Values:   []string{"1", "2", "3"},
 | 
					 | 
				
			||||||
										},
 | 
					 | 
				
			||||||
									},
 | 
					 | 
				
			||||||
								},
 | 
					 | 
				
			||||||
							},
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			err: nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, testcase := range testcases {
 | 
					 | 
				
			||||||
		t.Run(testcase.name, func(t *testing.T) {
 | 
					 | 
				
			||||||
			setPVLabeler(testcase.handler, testcase.pvlabeler)
 | 
					 | 
				
			||||||
			handler := admissiontesting.WithReinvocationTesting(t, admission.NewChainHandler(testcase.handler))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			err := handler.Admit(context.TODO(), admission.NewAttributesRecord(testcase.preAdmissionPV, nil, api.Kind("PersistentVolume").WithVersion("version"), testcase.preAdmissionPV.Namespace, testcase.preAdmissionPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
 | 
					 | 
				
			||||||
			if !reflect.DeepEqual(err, testcase.err) {
 | 
					 | 
				
			||||||
				t.Logf("expected error: %q", testcase.err)
 | 
					 | 
				
			||||||
				t.Logf("actual error: %q", err)
 | 
					 | 
				
			||||||
				t.Error("unexpected error when admitting PV")
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// sort node selector match expression by key because they are added out of order in the admission controller
 | 
					 | 
				
			||||||
			sortMatchExpressions(testcase.preAdmissionPV)
 | 
					 | 
				
			||||||
			if !reflect.DeepEqual(testcase.preAdmissionPV, testcase.postAdmissionPV) {
 | 
					 | 
				
			||||||
				t.Logf("expected PV: %+v", testcase.postAdmissionPV)
 | 
					 | 
				
			||||||
				t.Logf("actual PV: %+v", testcase.preAdmissionPV)
 | 
					 | 
				
			||||||
				t.Error("unexpected PV")
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// setPVLabler applies the given mock pvlabeler to implement PV labeling for all cloud providers.
 | 
					 | 
				
			||||||
// Given we mock out the values of the labels anyways, assigning the same mock labeler for every
 | 
					 | 
				
			||||||
// provider does not reduce test coverage but it does simplify/clean up the tests here because
 | 
					 | 
				
			||||||
// the provider is then decided based on the type of PV (EBS, GCEPD, Azure Disk, etc)
 | 
					 | 
				
			||||||
func setPVLabeler(handler *persistentVolumeLabel, pvlabeler cloudprovider.PVLabeler) {
 | 
					 | 
				
			||||||
	handler.azurePVLabeler = pvlabeler
 | 
					 | 
				
			||||||
	handler.vspherePVLabeler = pvlabeler
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// sortMatchExpressions sorts a PV's node selector match expressions by key name if it is not nil
 | 
					 | 
				
			||||||
func sortMatchExpressions(pv *api.PersistentVolume) {
 | 
					 | 
				
			||||||
	if pv.Spec.NodeAffinity == nil ||
 | 
					 | 
				
			||||||
		pv.Spec.NodeAffinity.Required == nil ||
 | 
					 | 
				
			||||||
		pv.Spec.NodeAffinity.Required.NodeSelectorTerms == nil {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	match := pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions
 | 
					 | 
				
			||||||
	sort.Slice(match, func(i, j int) bool {
 | 
					 | 
				
			||||||
		return match[i].Key < match[j].Key
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions = match
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,19 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 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 label created persistent volumes with zone information
 | 
					 | 
				
			||||||
// as provided by the cloud provider
 | 
					 | 
				
			||||||
package label // import "k8s.io/kubernetes/plugin/pkg/admission/storage/persistentvolume/label"
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user