Stop checking VolumeScheduling feature gate

This commit is contained in:
Jordan Liggitt 2018-12-27 17:45:04 -05:00
parent ffc675163d
commit 73dcfe12da
42 changed files with 213 additions and 537 deletions

View File

@ -14,7 +14,6 @@ go_library(
"//cmd/kube-scheduler/app/options:go_default_library", "//cmd/kube-scheduler/app/options:go_default_library",
"//pkg/api/legacyscheme:go_default_library", "//pkg/api/legacyscheme:go_default_library",
"//pkg/controller:go_default_library", "//pkg/controller:go_default_library",
"//pkg/features:go_default_library",
"//pkg/scheduler:go_default_library", "//pkg/scheduler:go_default_library",
"//pkg/scheduler/algorithmprovider:go_default_library", "//pkg/scheduler/algorithmprovider:go_default_library",
"//pkg/scheduler/api:go_default_library", "//pkg/scheduler/api:go_default_library",
@ -38,10 +37,8 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/server/healthz:go_default_library", "//staging/src/k8s.io/apiserver/pkg/server/healthz:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/server/mux:go_default_library", "//staging/src/k8s.io/apiserver/pkg/server/mux:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/server/routes:go_default_library", "//staging/src/k8s.io/apiserver/pkg/server/routes:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/flag:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/flag:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/globalflag:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/globalflag:go_default_library",
"//staging/src/k8s.io/client-go/informers/storage/v1:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/tools/leaderelection:go_default_library", "//staging/src/k8s.io/client-go/tools/leaderelection:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library", "//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",

View File

@ -38,17 +38,14 @@ import (
"k8s.io/apiserver/pkg/server/healthz" "k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/mux" "k8s.io/apiserver/pkg/server/mux"
"k8s.io/apiserver/pkg/server/routes" "k8s.io/apiserver/pkg/server/routes"
utilfeature "k8s.io/apiserver/pkg/util/feature"
apiserverflag "k8s.io/apiserver/pkg/util/flag" apiserverflag "k8s.io/apiserver/pkg/util/flag"
"k8s.io/apiserver/pkg/util/globalflag" "k8s.io/apiserver/pkg/util/globalflag"
storageinformers "k8s.io/client-go/informers/storage/v1"
v1core "k8s.io/client-go/kubernetes/typed/core/v1" v1core "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/leaderelection" "k8s.io/client-go/tools/leaderelection"
schedulerserverconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" schedulerserverconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config"
"k8s.io/kubernetes/cmd/kube-scheduler/app/options" "k8s.io/kubernetes/cmd/kube-scheduler/app/options"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/scheduler" "k8s.io/kubernetes/pkg/scheduler"
"k8s.io/kubernetes/pkg/scheduler/algorithmprovider" "k8s.io/kubernetes/pkg/scheduler/algorithmprovider"
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api" schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
@ -165,11 +162,6 @@ func runCommand(cmd *cobra.Command, args []string, opts *options.Options) error
// Run executes the scheduler based on the given configuration. It only return on error or when stopCh is closed. // Run executes the scheduler based on the given configuration. It only return on error or when stopCh is closed.
func Run(cc schedulerserverconfig.CompletedConfig, stopCh <-chan struct{}) error { func Run(cc schedulerserverconfig.CompletedConfig, stopCh <-chan struct{}) error {
var storageClassInformer storageinformers.StorageClassInformer
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) {
storageClassInformer = cc.InformerFactory.Storage().V1().StorageClasses()
}
// Create the scheduler. // Create the scheduler.
sched, err := scheduler.New(cc.Client, sched, err := scheduler.New(cc.Client,
cc.InformerFactory.Core().V1().Nodes(), cc.InformerFactory.Core().V1().Nodes(),
@ -181,7 +173,7 @@ func Run(cc schedulerserverconfig.CompletedConfig, stopCh <-chan struct{}) error
cc.InformerFactory.Apps().V1().StatefulSets(), cc.InformerFactory.Apps().V1().StatefulSets(),
cc.InformerFactory.Core().V1().Services(), cc.InformerFactory.Core().V1().Services(),
cc.InformerFactory.Policy().V1beta1().PodDisruptionBudgets(), cc.InformerFactory.Policy().V1beta1().PodDisruptionBudgets(),
storageClassInformer, cc.InformerFactory.Storage().V1().StorageClasses(),
cc.Recorder, cc.Recorder,
cc.ComponentConfig.AlgorithmSource, cc.ComponentConfig.AlgorithmSource,
stopCh, stopCh,
@ -335,11 +327,6 @@ func newHealthzHandler(config *kubeschedulerconfig.KubeSchedulerConfiguration, s
// NewSchedulerConfig creates the scheduler configuration. This is exposed for use by tests. // NewSchedulerConfig creates the scheduler configuration. This is exposed for use by tests.
func NewSchedulerConfig(s schedulerserverconfig.CompletedConfig) (*factory.Config, error) { func NewSchedulerConfig(s schedulerserverconfig.CompletedConfig) (*factory.Config, error) {
var storageClassInformer storageinformers.StorageClassInformer
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) {
storageClassInformer = s.InformerFactory.Storage().V1().StorageClasses()
}
// Set up the configurator which can create schedulers from configs. // Set up the configurator which can create schedulers from configs.
configurator := factory.NewConfigFactory(&factory.ConfigFactoryArgs{ configurator := factory.NewConfigFactory(&factory.ConfigFactoryArgs{
SchedulerName: s.ComponentConfig.SchedulerName, SchedulerName: s.ComponentConfig.SchedulerName,
@ -353,7 +340,7 @@ func NewSchedulerConfig(s schedulerserverconfig.CompletedConfig) (*factory.Confi
StatefulSetInformer: s.InformerFactory.Apps().V1().StatefulSets(), StatefulSetInformer: s.InformerFactory.Apps().V1().StatefulSets(),
ServiceInformer: s.InformerFactory.Core().V1().Services(), ServiceInformer: s.InformerFactory.Core().V1().Services(),
PdbInformer: s.InformerFactory.Policy().V1beta1().PodDisruptionBudgets(), PdbInformer: s.InformerFactory.Policy().V1beta1().PodDisruptionBudgets(),
StorageClassInformer: storageClassInformer, StorageClassInformer: s.InformerFactory.Storage().V1().StorageClasses(),
HardPodAffinitySymmetricWeight: s.ComponentConfig.HardPodAffinitySymmetricWeight, HardPodAffinitySymmetricWeight: s.ComponentConfig.HardPodAffinitySymmetricWeight,
DisablePreemption: s.ComponentConfig.DisablePreemption, DisablePreemption: s.ComponentConfig.DisablePreemption,
PercentageOfNodesToScore: s.ComponentConfig.PercentageOfNodesToScore, PercentageOfNodesToScore: s.ComponentConfig.PercentageOfNodesToScore,

View File

@ -1783,11 +1783,9 @@ func ValidatePersistentVolumeUpdate(newPv, oldPv *core.PersistentVolume) field.E
allErrs = append(allErrs, ValidateImmutableField(newPv.Spec.VolumeMode, oldPv.Spec.VolumeMode, field.NewPath("volumeMode"))...) allErrs = append(allErrs, ValidateImmutableField(newPv.Spec.VolumeMode, oldPv.Spec.VolumeMode, field.NewPath("volumeMode"))...)
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { // Allow setting NodeAffinity if oldPv NodeAffinity was not set
// Allow setting NodeAffinity if oldPv NodeAffinity was not set if oldPv.Spec.NodeAffinity != nil {
if oldPv.Spec.NodeAffinity != nil { allErrs = append(allErrs, ValidateImmutableField(newPv.Spec.NodeAffinity, oldPv.Spec.NodeAffinity, field.NewPath("nodeAffinity"))...)
allErrs = append(allErrs, ValidateImmutableField(newPv.Spec.NodeAffinity, oldPv.Spec.NodeAffinity, field.NewPath("nodeAffinity"))...)
}
} }
return allErrs return allErrs
@ -3168,22 +3166,17 @@ func ValidateTopologySelectorTerm(term core.TopologySelectorTerm, fldPath *field
exprMap := make(map[string]sets.String) exprMap := make(map[string]sets.String)
exprPath := fldPath.Child("matchLabelExpressions") exprPath := fldPath.Child("matchLabelExpressions")
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { // Allow empty MatchLabelExpressions, in case this field becomes optional in the future.
// Allow empty MatchLabelExpressions, in case this field becomes optional in the future. for i, req := range term.MatchLabelExpressions {
idxPath := exprPath.Index(i)
valueSet, exprErrs := validateTopologySelectorLabelRequirement(req, idxPath)
allErrs = append(allErrs, exprErrs...)
for i, req := range term.MatchLabelExpressions { // Validate no duplicate keys exist.
idxPath := exprPath.Index(i) if _, exists := exprMap[req.Key]; exists {
valueSet, exprErrs := validateTopologySelectorLabelRequirement(req, idxPath) allErrs = append(allErrs, field.Duplicate(idxPath.Child("key"), req.Key))
allErrs = append(allErrs, exprErrs...)
// Validate no duplicate keys exist.
if _, exists := exprMap[req.Key]; exists {
allErrs = append(allErrs, field.Duplicate(idxPath.Child("key"), req.Key))
}
exprMap[req.Key] = valueSet
} }
} else if len(term.MatchLabelExpressions) != 0 { exprMap[req.Key] = valueSet
allErrs = append(allErrs, field.Forbidden(fldPath, "field is disabled by feature-gate VolumeScheduling"))
} }
return exprMap, allErrs return exprMap, allErrs
@ -5336,10 +5329,6 @@ func validateVolumeNodeAffinity(nodeAffinity *core.VolumeNodeAffinity, fldPath *
return false, allErrs return false, allErrs
} }
if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) {
allErrs = append(allErrs, field.Forbidden(fldPath, "Volume node affinity is disabled by feature-gate"))
}
if nodeAffinity.Required != nil { if nodeAffinity.Required != nil {
allErrs = append(allErrs, ValidateNodeSelector(nodeAffinity.Required, fldPath.Child("required"))...) allErrs = append(allErrs, ValidateNodeSelector(nodeAffinity.Required, fldPath.Child("required"))...)
} else { } else {

View File

@ -577,19 +577,6 @@ func TestValidateLocalVolumesDisabled(t *testing.T) {
} }
}) })
} }
for name, scenario := range scenarios {
t.Run(name+" VolumeScheduling disabled", func(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, false)()
errs := ValidatePersistentVolume(scenario.volume)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
if len(errs) > 0 && !scenario.isExpectedFailure {
t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
}
})
}
} }
func testVolumeWithNodeAffinity(affinity *core.VolumeNodeAffinity) *core.PersistentVolume { func testVolumeWithNodeAffinity(affinity *core.VolumeNodeAffinity) *core.PersistentVolume {

View File

@ -39,7 +39,6 @@ go_test(
srcs = ["util_test.go"], srcs = ["util_test.go"],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/storage:go_default_library", "//pkg/apis/storage:go_default_library",
"//pkg/features:go_default_library", "//pkg/features:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",

View File

@ -24,14 +24,6 @@ import (
// DropDisabledFields removes disabled fields from the StorageClass object. // DropDisabledFields removes disabled fields from the StorageClass object.
func DropDisabledFields(class, oldClass *storage.StorageClass) { func DropDisabledFields(class, oldClass *storage.StorageClass) {
if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) {
class.VolumeBindingMode = nil
class.AllowedTopologies = nil
if oldClass != nil {
oldClass.VolumeBindingMode = nil
oldClass.AllowedTopologies = nil
}
}
if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) && !allowVolumeExpansionInUse(oldClass) { if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) && !allowVolumeExpansionInUse(oldClass) {
class.AllowVolumeExpansion = nil class.AllowVolumeExpansion = nil
} }

View File

@ -24,53 +24,10 @@ import (
"k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/diff"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing" utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/apis/storage"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
) )
func TestDropAlphaFields(t *testing.T) {
bindingMode := storage.VolumeBindingWaitForFirstConsumer
allowedTopologies := []api.TopologySelectorTerm{
{
MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
{
Key: "kubernetes.io/hostname",
Values: []string{"node1"},
},
},
},
}
// Test that field gets dropped when feature gate is not set
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, false)()
class := &storage.StorageClass{
VolumeBindingMode: &bindingMode,
AllowedTopologies: allowedTopologies,
}
DropDisabledFields(class, nil)
if class.VolumeBindingMode != nil {
t.Errorf("VolumeBindingMode field didn't get dropped: %+v", class.VolumeBindingMode)
}
if class.AllowedTopologies != nil {
t.Errorf("AllowedTopologies field didn't get dropped: %+v", class.AllowedTopologies)
}
// Test that field does not get dropped when feature gate is set
class = &storage.StorageClass{
VolumeBindingMode: &bindingMode,
AllowedTopologies: allowedTopologies,
}
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
DropDisabledFields(class, nil)
if class.VolumeBindingMode != &bindingMode {
t.Errorf("VolumeBindingMode field got unexpectantly modified: %+v", class.VolumeBindingMode)
}
if !reflect.DeepEqual(class.AllowedTopologies, allowedTopologies) {
t.Errorf("AllowedTopologies field got unexpectantly modified: %+v", class.AllowedTopologies)
}
}
func TestDropAllowVolumeExpansion(t *testing.T) { func TestDropAllowVolumeExpansion(t *testing.T) {
allowVolumeExpansion := false allowVolumeExpansion := false
scWithoutAllowVolumeExpansion := func() *storage.StorageClass { scWithoutAllowVolumeExpansion := func() *storage.StorageClass {

View File

@ -19,13 +19,11 @@ go_library(
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/storage:go_default_library", "//pkg/apis/storage:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/api/storage/v1:go_default_library", "//staging/src/k8s.io/api/storage/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
], ],
) )
@ -52,10 +50,7 @@ go_test(
deps = [ deps = [
"//pkg/api/legacyscheme:go_default_library", "//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/storage/install:go_default_library", "//pkg/apis/storage/install:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/api/storage/v1:go_default_library", "//staging/src/k8s.io/api/storage/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
], ],
) )

View File

@ -20,8 +20,6 @@ import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
storagev1 "k8s.io/api/storage/v1" storagev1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/features"
) )
func addDefaultingFuncs(scheme *runtime.Scheme) error { func addDefaultingFuncs(scheme *runtime.Scheme) error {
@ -34,7 +32,7 @@ func SetDefaults_StorageClass(obj *storagev1.StorageClass) {
*obj.ReclaimPolicy = v1.PersistentVolumeReclaimDelete *obj.ReclaimPolicy = v1.PersistentVolumeReclaimDelete
} }
if obj.VolumeBindingMode == nil && utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { if obj.VolumeBindingMode == nil {
obj.VolumeBindingMode = new(storagev1.VolumeBindingMode) obj.VolumeBindingMode = new(storagev1.VolumeBindingMode)
*obj.VolumeBindingMode = storagev1.VolumeBindingImmediate *obj.VolumeBindingMode = storagev1.VolumeBindingImmediate
} }

View File

@ -22,11 +22,8 @@ import (
storagev1 "k8s.io/api/storage/v1" storagev1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
_ "k8s.io/kubernetes/pkg/apis/storage/install" _ "k8s.io/kubernetes/pkg/apis/storage/install"
"k8s.io/kubernetes/pkg/features"
) )
func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
@ -53,7 +50,7 @@ func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
func TestSetDefaultVolumeBindingMode(t *testing.T) { func TestSetDefaultVolumeBindingMode(t *testing.T) {
class := &storagev1.StorageClass{} class := &storagev1.StorageClass{}
// When feature gate is enabled, field should be defaulted // field should be defaulted
defaultMode := storagev1.VolumeBindingImmediate defaultMode := storagev1.VolumeBindingImmediate
output := roundTrip(t, runtime.Object(class)).(*storagev1.StorageClass) output := roundTrip(t, runtime.Object(class)).(*storagev1.StorageClass)
outMode := output.VolumeBindingMode outMode := output.VolumeBindingMode
@ -62,13 +59,4 @@ func TestSetDefaultVolumeBindingMode(t *testing.T) {
} else if *outMode != defaultMode { } else if *outMode != defaultMode {
t.Errorf("Expected VolumeBindingMode to be defaulted to: %+v, got: %+v", defaultMode, outMode) t.Errorf("Expected VolumeBindingMode to be defaulted to: %+v, got: %+v", defaultMode, outMode)
} }
class = &storagev1.StorageClass{}
// When feature gate is disabled, field should not be defaulted
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, false)()
output = roundTrip(t, runtime.Object(class)).(*storagev1.StorageClass)
if output.VolumeBindingMode != nil {
t.Errorf("Expected VolumeBindingMode to not be defaulted, got: %+v", output.VolumeBindingMode)
}
} }

View File

@ -19,13 +19,11 @@ go_library(
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/storage:go_default_library", "//pkg/apis/storage:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/api/storage/v1beta1:go_default_library", "//staging/src/k8s.io/api/storage/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
], ],
) )
@ -52,10 +50,7 @@ go_test(
deps = [ deps = [
"//pkg/api/legacyscheme:go_default_library", "//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/storage/install:go_default_library", "//pkg/apis/storage/install:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/api/storage/v1beta1:go_default_library", "//staging/src/k8s.io/api/storage/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
], ],
) )

View File

@ -20,8 +20,6 @@ import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
storagev1beta1 "k8s.io/api/storage/v1beta1" storagev1beta1 "k8s.io/api/storage/v1beta1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/features"
) )
func addDefaultingFuncs(scheme *runtime.Scheme) error { func addDefaultingFuncs(scheme *runtime.Scheme) error {
@ -34,7 +32,7 @@ func SetDefaults_StorageClass(obj *storagev1beta1.StorageClass) {
*obj.ReclaimPolicy = v1.PersistentVolumeReclaimDelete *obj.ReclaimPolicy = v1.PersistentVolumeReclaimDelete
} }
if obj.VolumeBindingMode == nil && utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { if obj.VolumeBindingMode == nil {
obj.VolumeBindingMode = new(storagev1beta1.VolumeBindingMode) obj.VolumeBindingMode = new(storagev1beta1.VolumeBindingMode)
*obj.VolumeBindingMode = storagev1beta1.VolumeBindingImmediate *obj.VolumeBindingMode = storagev1beta1.VolumeBindingImmediate
} }

View File

@ -22,11 +22,8 @@ import (
storagev1beta1 "k8s.io/api/storage/v1beta1" storagev1beta1 "k8s.io/api/storage/v1beta1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
_ "k8s.io/kubernetes/pkg/apis/storage/install" _ "k8s.io/kubernetes/pkg/apis/storage/install"
"k8s.io/kubernetes/pkg/features"
) )
func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
@ -53,7 +50,7 @@ func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
func TestSetDefaultVolumeBindingMode(t *testing.T) { func TestSetDefaultVolumeBindingMode(t *testing.T) {
class := &storagev1beta1.StorageClass{} class := &storagev1beta1.StorageClass{}
// When feature gate is enabled, field should be defaulted // field should be defaulted
defaultMode := storagev1beta1.VolumeBindingImmediate defaultMode := storagev1beta1.VolumeBindingImmediate
output := roundTrip(t, runtime.Object(class)).(*storagev1beta1.StorageClass) output := roundTrip(t, runtime.Object(class)).(*storagev1beta1.StorageClass)
outMode := output.VolumeBindingMode outMode := output.VolumeBindingMode
@ -62,14 +59,4 @@ func TestSetDefaultVolumeBindingMode(t *testing.T) {
} else if *outMode != defaultMode { } else if *outMode != defaultMode {
t.Errorf("Expected VolumeBindingMode to be defaulted to: %+v, got: %+v", defaultMode, outMode) t.Errorf("Expected VolumeBindingMode to be defaulted to: %+v, got: %+v", defaultMode, outMode)
} }
class = &storagev1beta1.StorageClass{}
// When feature gate is disabled, field should not be defaulted
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, false)()
output = roundTrip(t, runtime.Object(class)).(*storagev1beta1.StorageClass)
if output.VolumeBindingMode != nil {
t.Errorf("Expected VolumeBindingMode to not be defaulted, got: %+v", output.VolumeBindingMode)
}
} }

View File

@ -15,12 +15,10 @@ go_library(
"//pkg/apis/core/helper:go_default_library", "//pkg/apis/core/helper:go_default_library",
"//pkg/apis/core/validation:go_default_library", "//pkg/apis/core/validation:go_default_library",
"//pkg/apis/storage:go_default_library", "//pkg/apis/storage:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
], ],
) )
@ -31,10 +29,7 @@ go_test(
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/storage:go_default_library", "//pkg/apis/storage:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
], ],
) )

View File

@ -24,12 +24,10 @@ import (
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper" "k8s.io/kubernetes/pkg/apis/core/helper"
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/apis/storage"
"k8s.io/kubernetes/pkg/features"
) )
const ( const (
@ -233,14 +231,10 @@ var supportedVolumeBindingModes = sets.NewString(string(storage.VolumeBindingImm
// validateVolumeBindingMode tests that VolumeBindingMode specifies valid values. // validateVolumeBindingMode tests that VolumeBindingMode specifies valid values.
func validateVolumeBindingMode(mode *storage.VolumeBindingMode, fldPath *field.Path) field.ErrorList { func validateVolumeBindingMode(mode *storage.VolumeBindingMode, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { if mode == nil {
if mode == nil { allErrs = append(allErrs, field.Required(fldPath, ""))
allErrs = append(allErrs, field.Required(fldPath, "")) } else if !supportedVolumeBindingModes.Has(string(*mode)) {
} else if !supportedVolumeBindingModes.Has(string(*mode)) { allErrs = append(allErrs, field.NotSupported(fldPath, mode, supportedVolumeBindingModes.List()))
allErrs = append(allErrs, field.NotSupported(fldPath, mode, supportedVolumeBindingModes.List()))
}
} else if mode != nil {
allErrs = append(allErrs, field.Forbidden(fldPath, "field is disabled by feature-gate VolumeScheduling"))
} }
return allErrs return allErrs
@ -254,10 +248,6 @@ func validateAllowedTopologies(topologies []api.TopologySelectorTerm, fldPath *f
return allErrs return allErrs
} }
if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) {
allErrs = append(allErrs, field.Forbidden(fldPath, "field is disabled by feature-gate VolumeScheduling"))
}
rawTopologies := make([]map[string]sets.String, len(topologies)) rawTopologies := make([]map[string]sets.String, len(topologies))
for i, term := range topologies { for i, term := range topologies {
idxPath := fldPath.Index(i) idxPath := fldPath.Index(i)

View File

@ -22,11 +22,8 @@ import (
"testing" "testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/apis/storage"
"k8s.io/kubernetes/pkg/features"
) )
var ( var (
@ -480,22 +477,6 @@ func makeClass(mode *storage.VolumeBindingMode, topologies []api.TopologySelecto
} }
} }
// TODO: Remove these tests once feature gate is not required
func TestValidateVolumeBindingModeAlphaDisabled(t *testing.T) {
errorCases := map[string]*storage.StorageClass{
"immediate mode": makeClass(&immediateMode1, nil),
"waiting mode": makeClass(&waitingMode, nil),
"invalid mode": makeClass(&invalidMode, nil),
}
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, false)()
for testName, storageClass := range errorCases {
if errs := ValidateStorageClass(storageClass); len(errs) == 0 {
t.Errorf("Expected failure for test: %v", testName)
}
}
}
type bindingTest struct { type bindingTest struct {
class *storage.StorageClass class *storage.StorageClass
shouldSucceed bool shouldSucceed bool
@ -521,8 +502,6 @@ func TestValidateVolumeBindingMode(t *testing.T) {
}, },
} }
// TODO: remove when feature gate not required
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
for testName, testCase := range cases { for testName, testCase := range cases {
errs := ValidateStorageClass(testCase.class) errs := ValidateStorageClass(testCase.class)
if testCase.shouldSucceed && len(errs) != 0 { if testCase.shouldSucceed && len(errs) != 0 {
@ -579,8 +558,6 @@ func TestValidateUpdateVolumeBindingMode(t *testing.T) {
}, },
} }
// TODO: remove when feature gate not required
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
for testName, testCase := range cases { for testName, testCase := range cases {
errs := ValidateStorageClassUpdate(testCase.newClass, testCase.oldClass) errs := ValidateStorageClassUpdate(testCase.newClass, testCase.oldClass)
if testCase.shouldSucceed && len(errs) != 0 { if testCase.shouldSucceed && len(errs) != 0 {
@ -883,7 +860,6 @@ func TestValidateAllowedTopologies(t *testing.T) {
}, },
} }
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
for testName, testCase := range cases { for testName, testCase := range cases {
errs := ValidateStorageClass(testCase.class) errs := ValidateStorageClass(testCase.class)
if testCase.shouldSucceed && len(errs) != 0 { if testCase.shouldSucceed && len(errs) != 0 {
@ -893,12 +869,4 @@ func TestValidateAllowedTopologies(t *testing.T) {
t.Errorf("Expected failure for test %q, got success", testName) t.Errorf("Expected failure for test %q, got success", testName)
} }
} }
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, false)()
for testName, testCase := range cases {
errs := ValidateStorageClass(testCase.class)
if len(errs) == 0 && testCase.class.AllowedTopologies != nil {
t.Errorf("Expected failure for test %q, got success", testName)
}
}
} }

View File

@ -18,7 +18,6 @@ go_library(
"//pkg/api/v1/node:go_default_library", "//pkg/api/v1/node:go_default_library",
"//pkg/apis/core/v1/helper:go_default_library", "//pkg/apis/core/v1/helper:go_default_library",
"//pkg/controller:go_default_library", "//pkg/controller:go_default_library",
"//pkg/features:go_default_library",
"//pkg/kubelet/apis:go_default_library", "//pkg/kubelet/apis:go_default_library",
"//pkg/scheduler/api:go_default_library", "//pkg/scheduler/api:go_default_library",
"//pkg/util/node:go_default_library", "//pkg/util/node:go_default_library",
@ -33,7 +32,6 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/client-go/informers/core/v1:go_default_library", "//staging/src/k8s.io/client-go/informers/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
@ -60,7 +58,6 @@ go_test(
"//pkg/cloudprovider/providers/fake:go_default_library", "//pkg/cloudprovider/providers/fake:go_default_library",
"//pkg/controller:go_default_library", "//pkg/controller:go_default_library",
"//pkg/controller/testutil:go_default_library", "//pkg/controller/testutil:go_default_library",
"//pkg/features:go_default_library",
"//pkg/kubelet/apis:go_default_library", "//pkg/kubelet/apis:go_default_library",
"//pkg/scheduler/api:go_default_library", "//pkg/scheduler/api:go_default_library",
"//pkg/volume/util:go_default_library", "//pkg/volume/util:go_default_library",
@ -69,8 +66,6 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library", "//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/informers/core/v1:go_default_library", "//staging/src/k8s.io/client-go/informers/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",

View File

@ -33,7 +33,6 @@ import (
"k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
corelisters "k8s.io/client-go/listers/core/v1" corelisters "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
@ -41,7 +40,6 @@ import (
cloudprovider "k8s.io/cloud-provider" cloudprovider "k8s.io/cloud-provider"
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/features"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
volumeutil "k8s.io/kubernetes/pkg/volume/util" volumeutil "k8s.io/kubernetes/pkg/volume/util"
) )
@ -204,7 +202,7 @@ func (pvlc *PersistentVolumeLabelController) addLabelsAndAffinityToVolume(vol *v
func (pvlc *PersistentVolumeLabelController) createPatch(vol *v1.PersistentVolume, volLabels map[string]string) ([]byte, error) { func (pvlc *PersistentVolumeLabelController) createPatch(vol *v1.PersistentVolume, volLabels map[string]string) ([]byte, error) {
volName := vol.Name volName := vol.Name
newVolume := vol.DeepCopyObject().(*v1.PersistentVolume) newVolume := vol.DeepCopyObject().(*v1.PersistentVolume)
populateAffinity := utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) && len(volLabels) != 0 populateAffinity := len(volLabels) != 0
if newVolume.Labels == nil { if newVolume.Labels == nil {
newVolume.Labels = make(map[string]string) newVolume.Labels = make(map[string]string)

View File

@ -27,11 +27,8 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
sets "k8s.io/apimachinery/pkg/util/sets" sets "k8s.io/apimachinery/pkg/util/sets"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing" core "k8s.io/client-go/testing"
"k8s.io/kubernetes/pkg/features"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
volumeutil "k8s.io/kubernetes/pkg/volume/util" volumeutil "k8s.io/kubernetes/pkg/volume/util"
@ -451,7 +448,6 @@ func TestCreatePatch(t *testing.T) {
}, },
} }
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
for d, tc := range testCases { for d, tc := range testCases {
cloud := &fakecloud.FakeCloud{} cloud := &fakecloud.FakeCloud{}
client := fake.NewSimpleClientset() client := fake.NewSimpleClientset()
@ -520,8 +516,6 @@ func TestAddLabelsToVolume(t *testing.T) {
}, },
} }
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
for d, tc := range testCases { for d, tc := range testCases {
labeledCh := make(chan bool, 1) labeledCh := make(chan bool, 1)
client := fake.NewSimpleClientset() client := fake.NewSimpleClientset()

View File

@ -286,10 +286,6 @@ func checkVolumeSatisfyClaim(volume *v1.PersistentVolume, claim *v1.PersistentVo
} }
func (ctrl *PersistentVolumeController) shouldDelayBinding(claim *v1.PersistentVolumeClaim) (bool, error) { func (ctrl *PersistentVolumeController) shouldDelayBinding(claim *v1.PersistentVolumeClaim) (bool, error) {
if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) {
return false, nil
}
// When feature VolumeScheduling enabled, // When feature VolumeScheduling enabled,
// Scheduler signal to the PV controller to start dynamic // Scheduler signal to the PV controller to start dynamic
// provisioning by setting the "annSelectedNode" annotation // provisioning by setting the "annSelectedNode" annotation

View File

@ -24,15 +24,12 @@ import (
storagev1 "k8s.io/api/storage/v1" storagev1 "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing" core "k8s.io/client-go/testing"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/klog" "k8s.io/klog"
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/features"
) )
var ( var (
@ -336,24 +333,3 @@ func TestDelayBinding(t *testing.T) {
} }
} }
} }
func TestDelayBindingDisabled(t *testing.T) {
// When volumeScheduling feature gate is disabled, should always be immediate
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, false)()
client := &fake.Clientset{}
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
classInformer := informerFactory.Storage().V1().StorageClasses()
ctrl := &PersistentVolumeController{
classLister: classInformer.Lister(),
}
name := "volumeScheduling-feature-disabled"
shouldDelay, err := ctrl.shouldDelayBinding(makePVCClass(&classWaitMode, false))
if err != nil {
t.Errorf("Test %q returned error: %v", name, err)
}
if shouldDelay {
t.Errorf("Test %q returned true, expected false", name)
}
}

View File

@ -413,7 +413,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
PodShareProcessNamespace: {Default: true, PreRelease: utilfeature.Beta}, PodShareProcessNamespace: {Default: true, PreRelease: utilfeature.Beta},
PodPriority: {Default: true, PreRelease: utilfeature.Beta}, PodPriority: {Default: true, PreRelease: utilfeature.Beta},
TaintNodesByCondition: {Default: true, PreRelease: utilfeature.Beta}, TaintNodesByCondition: {Default: true, PreRelease: utilfeature.Beta},
MountPropagation: {Default: true, PreRelease: utilfeature.GA}, MountPropagation: {Default: true, PreRelease: utilfeature.GA, LockToDefault: true}, // remove in 1.14
QOSReserved: {Default: false, PreRelease: utilfeature.Alpha}, QOSReserved: {Default: false, PreRelease: utilfeature.Alpha},
ExpandPersistentVolumes: {Default: true, PreRelease: utilfeature.Beta}, ExpandPersistentVolumes: {Default: true, PreRelease: utilfeature.Beta},
ExpandInUsePersistentVolumes: {Default: false, PreRelease: utilfeature.Alpha}, ExpandInUsePersistentVolumes: {Default: false, PreRelease: utilfeature.Alpha},
@ -422,7 +422,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
CPUCFSQuotaPeriod: {Default: false, PreRelease: utilfeature.Alpha}, CPUCFSQuotaPeriod: {Default: false, PreRelease: utilfeature.Alpha},
ServiceNodeExclusion: {Default: false, PreRelease: utilfeature.Alpha}, ServiceNodeExclusion: {Default: false, PreRelease: utilfeature.Alpha},
MountContainers: {Default: false, PreRelease: utilfeature.Alpha}, MountContainers: {Default: false, PreRelease: utilfeature.Alpha},
VolumeScheduling: {Default: true, PreRelease: utilfeature.GA}, VolumeScheduling: {Default: true, PreRelease: utilfeature.GA, LockToDefault: true}, // remove in 1.16
CSIPersistentVolume: {Default: true, PreRelease: utilfeature.GA}, CSIPersistentVolume: {Default: true, PreRelease: utilfeature.GA},
CSIDriverRegistry: {Default: false, PreRelease: utilfeature.Alpha}, CSIDriverRegistry: {Default: false, PreRelease: utilfeature.Alpha},
CSINodeInfo: {Default: false, PreRelease: utilfeature.Alpha}, CSINodeInfo: {Default: false, PreRelease: utilfeature.Alpha},

View File

@ -9,7 +9,6 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/scheduler", importpath = "k8s.io/kubernetes/pkg/scheduler",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//pkg/features:go_default_library",
"//pkg/scheduler/algorithm:go_default_library", "//pkg/scheduler/algorithm:go_default_library",
"//pkg/scheduler/algorithm/predicates:go_default_library", "//pkg/scheduler/algorithm/predicates:go_default_library",
"//pkg/scheduler/api:go_default_library", "//pkg/scheduler/api:go_default_library",
@ -27,7 +26,6 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/client-go/informers/apps/v1:go_default_library", "//staging/src/k8s.io/client-go/informers/apps/v1:go_default_library",
"//staging/src/k8s.io/client-go/informers/core/v1:go_default_library", "//staging/src/k8s.io/client-go/informers/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/informers/policy/v1beta1:go_default_library", "//staging/src/k8s.io/client-go/informers/policy/v1beta1:go_default_library",
@ -46,7 +44,6 @@ go_test(
deps = [ deps = [
"//pkg/api/legacyscheme:go_default_library", "//pkg/api/legacyscheme:go_default_library",
"//pkg/controller/volume/persistentvolume:go_default_library", "//pkg/controller/volume/persistentvolume:go_default_library",
"//pkg/features:go_default_library",
"//pkg/scheduler/algorithm:go_default_library", "//pkg/scheduler/algorithm:go_default_library",
"//pkg/scheduler/algorithm/predicates:go_default_library", "//pkg/scheduler/algorithm/predicates:go_default_library",
"//pkg/scheduler/api:go_default_library", "//pkg/scheduler/api:go_default_library",
@ -66,8 +63,6 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library", "//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library", "//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",

View File

@ -635,18 +635,16 @@ func (c *VolumeZoneChecker) predicate(pod *v1.Pod, meta PredicateMetadata, nodeI
pvName := pvc.Spec.VolumeName pvName := pvc.Spec.VolumeName
if pvName == "" { if pvName == "" {
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { scName := v1helper.GetPersistentVolumeClaimClass(pvc)
scName := v1helper.GetPersistentVolumeClaimClass(pvc) if len(scName) > 0 {
if len(scName) > 0 { class, _ := c.classInfo.GetStorageClassInfo(scName)
class, _ := c.classInfo.GetStorageClassInfo(scName) if class != nil {
if class != nil { if class.VolumeBindingMode == nil {
if class.VolumeBindingMode == nil { return false, nil, fmt.Errorf("VolumeBindingMode not set for StorageClass %q", scName)
return false, nil, fmt.Errorf("VolumeBindingMode not set for StorageClass %q", scName) }
} if *class.VolumeBindingMode == storagev1.VolumeBindingWaitForFirstConsumer {
if *class.VolumeBindingMode == storagev1.VolumeBindingWaitForFirstConsumer { // Skip unbound volumes
// Skip unbound volumes continue
continue
}
} }
} }
} }
@ -1618,10 +1616,6 @@ func NewVolumeBindingPredicate(binder *volumebinder.VolumeBinder) FitPredicate {
} }
func (c *VolumeBindingChecker) predicate(pod *v1.Pod, meta PredicateMetadata, nodeInfo *schedulernodeinfo.NodeInfo) (bool, []PredicateFailureReason, error) { func (c *VolumeBindingChecker) predicate(pod *v1.Pod, meta PredicateMetadata, nodeInfo *schedulernodeinfo.NodeInfo) (bool, []PredicateFailureReason, error) {
if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) {
return true, nil, nil
}
node := nodeInfo.Node() node := nodeInfo.Node()
if node == nil { if node == nil {
return false, nil, fmt.Errorf("node not found") return false, nil, fmt.Errorf("node not found")

View File

@ -29,10 +29,7 @@ import (
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
"k8s.io/kubernetes/pkg/features"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api" schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo" schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
@ -4884,8 +4881,6 @@ func TestVolumeZonePredicateWithVolumeBinding(t *testing.T) {
}, },
} }
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
fit := NewVolumeZonePredicate(pvInfo, pvcInfo, classInfo) fit := NewVolumeZonePredicate(pvInfo, pvcInfo, classInfo)

View File

@ -12,7 +12,6 @@ go_library(
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//pkg/api/v1/pod:go_default_library", "//pkg/api/v1/pod:go_default_library",
"//pkg/features:go_default_library",
"//pkg/kubelet/apis:go_default_library", "//pkg/kubelet/apis:go_default_library",
"//pkg/scheduler/algorithm:go_default_library", "//pkg/scheduler/algorithm:go_default_library",
"//pkg/scheduler/algorithm/predicates:go_default_library", "//pkg/scheduler/algorithm/predicates:go_default_library",
@ -38,7 +37,6 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/client-go/informers/apps/v1:go_default_library", "//staging/src/k8s.io/client-go/informers/apps/v1:go_default_library",
"//staging/src/k8s.io/client-go/informers/core/v1:go_default_library", "//staging/src/k8s.io/client-go/informers/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/informers/policy/v1beta1:go_default_library", "//staging/src/k8s.io/client-go/informers/policy/v1beta1:go_default_library",

View File

@ -38,7 +38,6 @@ import (
"k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
appsinformers "k8s.io/client-go/informers/apps/v1" appsinformers "k8s.io/client-go/informers/apps/v1"
coreinformers "k8s.io/client-go/informers/core/v1" coreinformers "k8s.io/client-go/informers/core/v1"
policyinformers "k8s.io/client-go/informers/policy/v1beta1" policyinformers "k8s.io/client-go/informers/policy/v1beta1"
@ -51,7 +50,6 @@ import (
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
podutil "k8s.io/kubernetes/pkg/api/v1/pod" podutil "k8s.io/kubernetes/pkg/api/v1/pod"
"k8s.io/kubernetes/pkg/features"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
"k8s.io/kubernetes/pkg/scheduler/algorithm" "k8s.io/kubernetes/pkg/scheduler/algorithm"
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates" "k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
@ -378,16 +376,14 @@ func NewConfigFactory(args *ConfigFactoryArgs) Configurator {
}, },
) )
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { // Setup volume binder
// Setup volume binder c.volumeBinder = volumebinder.NewVolumeBinder(args.Client, args.PvcInformer, args.PvInformer, args.StorageClassInformer, time.Duration(args.BindTimeoutSeconds)*time.Second)
c.volumeBinder = volumebinder.NewVolumeBinder(args.Client, args.PvcInformer, args.PvInformer, args.StorageClassInformer, time.Duration(args.BindTimeoutSeconds)*time.Second)
args.StorageClassInformer.Informer().AddEventHandler( args.StorageClassInformer.Informer().AddEventHandler(
cache.ResourceEventHandlerFuncs{ cache.ResourceEventHandlerFuncs{
AddFunc: c.onStorageClassAdd, AddFunc: c.onStorageClassAdd,
}, },
) )
}
// Setup cache comparer // Setup cache comparer
debugger := cachedebugger.New( debugger := cachedebugger.New(
@ -491,9 +487,6 @@ func (c *configFactory) onPvcAdd(obj interface{}) {
} }
func (c *configFactory) onPvcUpdate(old, new interface{}) { func (c *configFactory) onPvcUpdate(old, new interface{}) {
if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) {
return
}
c.podQueue.MoveAllToActiveQueue() c.podQueue.MoveAllToActiveQueue()
} }

View File

@ -29,14 +29,12 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
appsinformers "k8s.io/client-go/informers/apps/v1" appsinformers "k8s.io/client-go/informers/apps/v1"
coreinformers "k8s.io/client-go/informers/core/v1" coreinformers "k8s.io/client-go/informers/core/v1"
policyinformers "k8s.io/client-go/informers/policy/v1beta1" policyinformers "k8s.io/client-go/informers/policy/v1beta1"
storageinformers "k8s.io/client-go/informers/storage/v1" storageinformers "k8s.io/client-go/informers/storage/v1"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/features"
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api" schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
latestschedulerapi "k8s.io/kubernetes/pkg/scheduler/api/latest" latestschedulerapi "k8s.io/kubernetes/pkg/scheduler/api/latest"
kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
@ -352,12 +350,10 @@ func (sched *Scheduler) preempt(preemptor *v1.Pod, scheduleErr error) (string, e
// //
// This function modifies assumed if volume binding is required. // This function modifies assumed if volume binding is required.
func (sched *Scheduler) assumeVolumes(assumed *v1.Pod, host string) (allBound bool, err error) { func (sched *Scheduler) assumeVolumes(assumed *v1.Pod, host string) (allBound bool, err error) {
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { allBound, err = sched.config.VolumeBinder.Binder.AssumePodVolumes(assumed, host)
allBound, err = sched.config.VolumeBinder.Binder.AssumePodVolumes(assumed, host) if err != nil {
if err != nil { sched.recordSchedulingFailure(assumed, err, SchedulerError,
sched.recordSchedulingFailure(assumed, err, SchedulerError, fmt.Sprintf("AssumePodVolumes failed: %v", err))
fmt.Sprintf("AssumePodVolumes failed: %v", err))
}
} }
return return
} }

View File

@ -32,8 +32,6 @@ import (
"k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
clientsetfake "k8s.io/client-go/kubernetes/fake" clientsetfake "k8s.io/client-go/kubernetes/fake"
corelister "k8s.io/client-go/listers/core/v1" corelister "k8s.io/client-go/listers/core/v1"
@ -41,7 +39,6 @@ import (
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/controller/volume/persistentvolume" "k8s.io/kubernetes/pkg/controller/volume/persistentvolume"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/scheduler/algorithm" "k8s.io/kubernetes/pkg/scheduler/algorithm"
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates" "k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
"k8s.io/kubernetes/pkg/scheduler/api" "k8s.io/kubernetes/pkg/scheduler/api"
@ -779,8 +776,6 @@ func TestSchedulerWithVolumeBinding(t *testing.T) {
// This can be small because we wait for pod to finish scheduling first // This can be small because we wait for pod to finish scheduling first
chanTimeout := 2 * time.Second chanTimeout := 2 * time.Second
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
table := []struct { table := []struct {
name string name string
expectError error expectError error

View File

@ -576,12 +576,10 @@ func (c *awsElasticBlockStoreProvisioner) Provision(selectedNode *v1.Node, allow
} }
} }
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity)
pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity) pv.Spec.NodeAffinity.Required = new(v1.NodeSelector)
pv.Spec.NodeAffinity.Required = new(v1.NodeSelector) pv.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]v1.NodeSelectorTerm, 1)
pv.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]v1.NodeSelectorTerm, 1) pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions = requirements
pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions = requirements
}
return pv, nil return pv, nil
} }

View File

@ -311,51 +311,49 @@ func (p *azureDiskProvisioner) Provision(selectedNode *v1.Node, allowedTopologie
}, },
} }
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { nodeSelectorTerms := make([]v1.NodeSelectorTerm, 0)
nodeSelectorTerms := make([]v1.NodeSelectorTerm, 0)
if zoned { if zoned {
// Set node affinity labels based on availability zone labels. // Set node affinity labels based on availability zone labels.
if len(labels) > 0 { if len(labels) > 0 {
requirements := make([]v1.NodeSelectorRequirement, 0) requirements := make([]v1.NodeSelectorRequirement, 0)
for k, v := range labels { for k, v := range labels {
requirements = append(requirements, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}}) requirements = append(requirements, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}})
} }
nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{ nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
MatchExpressions: requirements, MatchExpressions: requirements,
}) })
}
} else {
// Set node affinity labels based on fault domains.
// This is required because unzoned AzureDisk can't be attached to zoned nodes.
// There are at most 3 fault domains available in each region.
// Refer https://docs.microsoft.com/en-us/azure/virtual-machines/windows/manage-availability.
for i := 0; i < 3; i++ {
requirements := []v1.NodeSelectorRequirement{
{
Key: kubeletapis.LabelZoneRegion,
Operator: v1.NodeSelectorOpIn,
Values: []string{diskController.GetLocation()},
},
{
Key: kubeletapis.LabelZoneFailureDomain,
Operator: v1.NodeSelectorOpIn,
Values: []string{strconv.Itoa(i)},
},
}
nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
MatchExpressions: requirements,
})
}
} }
} else {
if len(nodeSelectorTerms) > 0 { // Set node affinity labels based on fault domains.
pv.Spec.NodeAffinity = &v1.VolumeNodeAffinity{ // This is required because unzoned AzureDisk can't be attached to zoned nodes.
Required: &v1.NodeSelector{ // There are at most 3 fault domains available in each region.
NodeSelectorTerms: nodeSelectorTerms, // Refer https://docs.microsoft.com/en-us/azure/virtual-machines/windows/manage-availability.
for i := 0; i < 3; i++ {
requirements := []v1.NodeSelectorRequirement{
{
Key: kubeletapis.LabelZoneRegion,
Operator: v1.NodeSelectorOpIn,
Values: []string{diskController.GetLocation()},
},
{
Key: kubeletapis.LabelZoneFailureDomain,
Operator: v1.NodeSelectorOpIn,
Values: []string{strconv.Itoa(i)},
}, },
} }
nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
MatchExpressions: requirements,
})
}
}
if len(nodeSelectorTerms) > 0 {
pv.Spec.NodeAffinity = &v1.VolumeNodeAffinity{
Required: &v1.NodeSelector{
NodeSelectorTerms: nodeSelectorTerms,
},
} }
} }

View File

@ -561,20 +561,18 @@ func (c *cinderVolumeProvisioner) Provision(selectedNode *v1.Node, allowedTopolo
pv.Spec.AccessModes = c.plugin.GetAccessModes() pv.Spec.AccessModes = c.plugin.GetAccessModes()
} }
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { requirements := make([]v1.NodeSelectorRequirement, 0)
requirements := make([]v1.NodeSelectorRequirement, 0) for k, v := range labels {
for k, v := range labels { if v != "" {
if v != "" { requirements = append(requirements, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}})
requirements = append(requirements, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}})
}
}
if len(requirements) > 0 {
pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity)
pv.Spec.NodeAffinity.Required = new(v1.NodeSelector)
pv.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]v1.NodeSelectorTerm, 1)
pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions = requirements
} }
} }
if len(requirements) > 0 {
pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity)
pv.Spec.NodeAffinity.Required = new(v1.NodeSelector)
pv.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]v1.NodeSelectorTerm, 1)
pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions = requirements
}
return pv, nil return pv, nil
} }

View File

@ -551,7 +551,7 @@ func (c *gcePersistentDiskProvisioner) Provision(selectedNode *v1.Node, allowedT
} }
} }
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) && len(requirements) > 0 { if len(requirements) > 0 {
pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity) pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity)
pv.Spec.NodeAffinity.Required = new(v1.NodeSelector) pv.Spec.NodeAffinity.Required = new(v1.NodeSelector)
pv.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]v1.NodeSelectorTerm, 1) pv.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]v1.NodeSelectorTerm, 1)

View File

@ -60,7 +60,6 @@ go_test(
deps = [ deps = [
"//pkg/apis/core/install:go_default_library", "//pkg/apis/core/install:go_default_library",
"//pkg/apis/core/v1/helper:go_default_library", "//pkg/apis/core/v1/helper:go_default_library",
"//pkg/features:go_default_library",
"//pkg/kubelet/apis:go_default_library", "//pkg/kubelet/apis:go_default_library",
"//pkg/util/mount:go_default_library", "//pkg/util/mount:go_default_library",
"//pkg/util/slice:go_default_library", "//pkg/util/slice:go_default_library",
@ -70,8 +69,6 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
"//staging/src/k8s.io/client-go/util/testing:go_default_library", "//staging/src/k8s.io/client-go/util/testing:go_default_library",
], ],
) )

View File

@ -24,7 +24,6 @@ import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
utiltesting "k8s.io/client-go/util/testing" utiltesting "k8s.io/client-go/util/testing"
// util.go uses api.Codecs.LegacyCodec so import this package to do some // util.go uses api.Codecs.LegacyCodec so import this package to do some
@ -32,7 +31,6 @@ import (
"hash/fnv" "hash/fnv"
_ "k8s.io/kubernetes/pkg/apis/core/install" _ "k8s.io/kubernetes/pkg/apis/core/install"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/util/mount" "k8s.io/kubernetes/pkg/util/mount"
"reflect" "reflect"
@ -40,7 +38,6 @@ import (
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
"k8s.io/kubernetes/pkg/util/slice" "k8s.io/kubernetes/pkg/util/slice"
@ -1065,7 +1062,6 @@ func TestSelectZoneForVolume(t *testing.T) {
ZonesWithNodes string ZonesWithNodes string
Node *v1.Node Node *v1.Node
AllowedTopologies []v1.TopologySelectorTerm AllowedTopologies []v1.TopologySelectorTerm
VolumeScheduling bool
// Expectations around returned zone from SelectZoneForVolume // Expectations around returned zone from SelectZoneForVolume
Reject bool // expect error due to validation failing Reject bool // expect error due to validation failing
ExpectSpecificZone bool // expect returned zone to specifically match a single zone (rather than one from a set) ExpectSpecificZone bool // expect returned zone to specifically match a single zone (rather than one from a set)
@ -1078,7 +1074,6 @@ func TestSelectZoneForVolume(t *testing.T) {
// [1] Node irrelevant // [1] Node irrelevant
// [2] Zone and Zones parameters presents // [2] Zone and Zones parameters presents
// [3] AllowedTopologies irrelevant // [3] AllowedTopologies irrelevant
// [4] VolumeScheduling irrelevant
{ {
Name: "Nil_Node_with_Zone_Zones_parameters_present", Name: "Nil_Node_with_Zone_Zones_parameters_present",
ZonePresent: true, ZonePresent: true,
@ -1092,53 +1087,45 @@ func TestSelectZoneForVolume(t *testing.T) {
// [1] Node with no zone labels // [1] Node with no zone labels
// [2] Zone/Zones parameter irrelevant // [2] Zone/Zones parameter irrelevant
// [3] AllowedTopologies irrelevant // [3] AllowedTopologies irrelevant
// [4] VolumeScheduling enabled
{ {
Name: "Node_with_no_Zone_labels", Name: "Node_with_no_Zone_labels",
Node: nodeWithNoLabels, Node: nodeWithNoLabels,
VolumeScheduling: true, Reject: true,
Reject: true,
}, },
// Node with Zone labels as well as Zone parameter specified [Fail] // Node with Zone labels as well as Zone parameter specified [Fail]
// [1] Node with zone labels // [1] Node with zone labels
// [2] Zone parameter specified // [2] Zone parameter specified
// [3] AllowedTopologies irrelevant // [3] AllowedTopologies irrelevant
// [4] VolumeScheduling enabled
{ {
Name: "Node_with_Zone_labels_and_Zone_parameter_present", Name: "Node_with_Zone_labels_and_Zone_parameter_present",
Node: nodeWithZoneLabels, Node: nodeWithZoneLabels,
ZonePresent: true, ZonePresent: true,
Zone: "zoneX", Zone: "zoneX",
VolumeScheduling: true, Reject: true,
Reject: true,
}, },
// Node with Zone labels as well as Zones parameter specified [Fail] // Node with Zone labels as well as Zones parameter specified [Fail]
// [1] Node with zone labels // [1] Node with zone labels
// [2] Zones parameter specified // [2] Zones parameter specified
// [3] AllowedTopologies irrelevant // [3] AllowedTopologies irrelevant
// [4] VolumeScheduling enabled
{ {
Name: "Node_with_Zone_labels_and_Zones_parameter_present", Name: "Node_with_Zone_labels_and_Zones_parameter_present",
Node: nodeWithZoneLabels, Node: nodeWithZoneLabels,
ZonesPresent: true, ZonesPresent: true,
Zones: "zoneX,zoneY", Zones: "zoneX,zoneY",
VolumeScheduling: true, Reject: true,
Reject: true,
}, },
// Zone parameter as well as AllowedTopologies specified [Fail] // Zone parameter as well as AllowedTopologies specified [Fail]
// [1] nil Node // [1] nil Node
// [2] Zone parameter specified // [2] Zone parameter specified
// [3] AllowedTopologies specified // [3] AllowedTopologies specified
// [4] VolumeScheduling enabled
{ {
Name: "Nil_Node_and_Zone_parameter_and_Allowed_Topology_term", Name: "Nil_Node_and_Zone_parameter_and_Allowed_Topology_term",
Node: nil, Node: nil,
ZonePresent: true, ZonePresent: true,
Zone: "zoneX", Zone: "zoneX",
VolumeScheduling: true,
AllowedTopologies: []v1.TopologySelectorTerm{ AllowedTopologies: []v1.TopologySelectorTerm{
{ {
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{ MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
@ -1156,13 +1143,11 @@ func TestSelectZoneForVolume(t *testing.T) {
// [1] nil Node // [1] nil Node
// [2] Zones parameter specified // [2] Zones parameter specified
// [3] AllowedTopologies specified // [3] AllowedTopologies specified
// [4] VolumeScheduling enabled
{ {
Name: "Nil_Node_and_Zones_parameter_and_Allowed_Topology_term", Name: "Nil_Node_and_Zones_parameter_and_Allowed_Topology_term",
Node: nil, Node: nil,
ZonesPresent: true, ZonesPresent: true,
Zones: "zoneX,zoneY", Zones: "zoneX,zoneY",
VolumeScheduling: true,
AllowedTopologies: []v1.TopologySelectorTerm{ AllowedTopologies: []v1.TopologySelectorTerm{
{ {
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{ MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
@ -1180,11 +1165,9 @@ func TestSelectZoneForVolume(t *testing.T) {
// [1] nil Node // [1] nil Node
// [2] no Zone/Zones parameter // [2] no Zone/Zones parameter
// [3] AllowedTopologies with invalid key specified // [3] AllowedTopologies with invalid key specified
// [4] VolumeScheduling enabled
{ {
Name: "Nil_Node_and_Invalid_Allowed_Topology_Key", Name: "Nil_Node_and_Invalid_Allowed_Topology_Key",
Node: nil, Node: nil,
VolumeScheduling: true,
AllowedTopologies: []v1.TopologySelectorTerm{ AllowedTopologies: []v1.TopologySelectorTerm{
{ {
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{ MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
@ -1206,11 +1189,9 @@ func TestSelectZoneForVolume(t *testing.T) {
// [1] nil Node // [1] nil Node
// [2] no Zone/Zones parameter // [2] no Zone/Zones parameter
// [3] Invalid AllowedTopologies // [3] Invalid AllowedTopologies
// [4] VolumeScheduling enabled
{ {
Name: "Nil_Node_and_Invalid_AllowedTopologies", Name: "Nil_Node_and_Invalid_AllowedTopologies",
Node: nil, Node: nil,
VolumeScheduling: true,
AllowedTopologies: []v1.TopologySelectorTerm{ AllowedTopologies: []v1.TopologySelectorTerm{
{ {
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{}, MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{},
@ -1219,77 +1200,29 @@ func TestSelectZoneForVolume(t *testing.T) {
Reject: true, Reject: true,
}, },
// POSITIVE TESTS WITH VolumeScheduling DISABLED
// Select zone from active zones [Pass]
// [1] nil Node (Node irrelevant)
// [2] no Zone parameter
// [3] no AllowedTopologies
// [4] VolumeScheduling disabled
{
Name: "No_Zone_Zones_parameter_and_VolumeScheduling_disabled",
ZonesWithNodes: "zoneX,zoneY",
VolumeScheduling: false,
Reject: false,
ExpectedZones: "zoneX,zoneY",
},
// Select zone from single zone parameter [Pass]
// [1] nil Node (Node irrelevant)
// [2] Zone parameter specified
// [3] no AllowedTopologies
// [4] VolumeScheduling disabled
{
Name: "Zone_parameter_present_and_VolumeScheduling_disabled",
ZonePresent: true,
Zone: "zoneX",
VolumeScheduling: false,
Reject: false,
ExpectSpecificZone: true,
ExpectedZone: "zoneX",
},
// Select zone from zones parameter [Pass]
// [1] nil Node (Node irrelevant)
// [2] Zones parameter specified
// [3] no AllowedTopologies
// [4] VolumeScheduling disabled
{
Name: "Zones_parameter_present_and_VolumeScheduling_disabled",
ZonesPresent: true,
Zones: "zoneX,zoneY",
VolumeScheduling: false,
Reject: false,
ExpectedZones: "zoneX,zoneY",
},
// POSITIVE TESTS WITH VolumeScheduling ENABLED // POSITIVE TESTS WITH VolumeScheduling ENABLED
// Select zone from active zones [Pass] // Select zone from active zones [Pass]
// [1] nil Node // [1] nil Node
// [2] no Zone parameter specified // [2] no Zone parameter specified
// [3] no AllowedTopologies // [3] no AllowedTopologies
// [4] VolumeScheduling enabled
{ {
Name: "Nil_Node_and_No_Zone_Zones_parameter_and_no_Allowed_topologies_and_VolumeScheduling_enabled", Name: "Nil_Node_and_No_Zone_Zones_parameter_and_no_Allowed_topologies_and_VolumeScheduling_enabled",
Node: nil, Node: nil,
ZonesWithNodes: "zoneX,zoneY", ZonesWithNodes: "zoneX,zoneY",
VolumeScheduling: true, Reject: false,
Reject: false, ExpectedZones: "zoneX,zoneY",
ExpectedZones: "zoneX,zoneY",
}, },
// Select zone from single zone parameter [Pass] // Select zone from single zone parameter [Pass]
// [1] nil Node // [1] nil Node
// [2] Zone parameter specified // [2] Zone parameter specified
// [3] no AllowedTopology specified // [3] no AllowedTopology specified
// [4] VolumeScheduling enabled
{ {
Name: "Nil_Node_and_Zone_parameter_present_and_VolumeScheduling_enabled", Name: "Nil_Node_and_Zone_parameter_present_and_VolumeScheduling_enabled",
ZonePresent: true, ZonePresent: true,
Zone: "zoneX", Zone: "zoneX",
Node: nil, Node: nil,
VolumeScheduling: true,
Reject: false, Reject: false,
ExpectSpecificZone: true, ExpectSpecificZone: true,
ExpectedZone: "zoneX", ExpectedZone: "zoneX",
@ -1299,26 +1232,22 @@ func TestSelectZoneForVolume(t *testing.T) {
// [1] nil Node // [1] nil Node
// [2] Zones parameter specified // [2] Zones parameter specified
// [3] no AllowedTopology // [3] no AllowedTopology
// [4] VolumeScheduling enabled
{ {
Name: "Nil_Node_and_Zones_parameter_present_and_VolumeScheduling_enabled", Name: "Nil_Node_and_Zones_parameter_present_and_VolumeScheduling_enabled",
ZonesPresent: true, ZonesPresent: true,
Zones: "zoneX,zoneY", Zones: "zoneX,zoneY",
Node: nil, Node: nil,
VolumeScheduling: true, Reject: false,
Reject: false, ExpectedZones: "zoneX,zoneY",
ExpectedZones: "zoneX,zoneY",
}, },
// Select zone from node label [Pass] // Select zone from node label [Pass]
// [1] Node with zone labels // [1] Node with zone labels
// [2] no zone/zones parameters // [2] no zone/zones parameters
// [3] no AllowedTopology // [3] no AllowedTopology
// [4] VolumeScheduling enabled
{ {
Name: "Node_with_Zone_labels_and_VolumeScheduling_enabled", Name: "Node_with_Zone_labels_and_VolumeScheduling_enabled",
Node: nodeWithZoneLabels, Node: nodeWithZoneLabels,
VolumeScheduling: true,
Reject: false, Reject: false,
ExpectSpecificZone: true, ExpectSpecificZone: true,
ExpectedZone: "zoneX", ExpectedZone: "zoneX",
@ -1328,11 +1257,9 @@ func TestSelectZoneForVolume(t *testing.T) {
// [1] Node with zone labels // [1] Node with zone labels
// [2] no Zone/Zones parameters // [2] no Zone/Zones parameters
// [3] AllowedTopology with single term with multiple values specified (ignored) // [3] AllowedTopology with single term with multiple values specified (ignored)
// [4] VolumeScheduling enabled
{ {
Name: "Node_with_Zone_labels_and_Multiple_Allowed_Topology_values_and_VolumeScheduling_enabled", Name: "Node_with_Zone_labels_and_Multiple_Allowed_Topology_values_and_VolumeScheduling_enabled",
Node: nodeWithZoneLabels, Node: nodeWithZoneLabels,
VolumeScheduling: true,
AllowedTopologies: []v1.TopologySelectorTerm{ AllowedTopologies: []v1.TopologySelectorTerm{
{ {
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{ MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
@ -1352,11 +1279,9 @@ func TestSelectZoneForVolume(t *testing.T) {
// [1] nil Node // [1] nil Node
// [2] no Zone/Zones parametes specified // [2] no Zone/Zones parametes specified
// [3] AllowedTopologies with single term with multiple values specified // [3] AllowedTopologies with single term with multiple values specified
// [4] VolumeScheduling enabled
{ {
Name: "Nil_Node_with_Multiple_Allowed_Topology_values_and_VolumeScheduling_enabled", Name: "Nil_Node_with_Multiple_Allowed_Topology_values_and_VolumeScheduling_enabled",
Node: nil, Node: nil,
VolumeScheduling: true,
AllowedTopologies: []v1.TopologySelectorTerm{ AllowedTopologies: []v1.TopologySelectorTerm{
{ {
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{ MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
@ -1375,11 +1300,9 @@ func TestSelectZoneForVolume(t *testing.T) {
// [1] nil Node // [1] nil Node
// [2] no Zone/Zones parametes specified // [2] no Zone/Zones parametes specified
// [3] AllowedTopologies with multiple terms specified // [3] AllowedTopologies with multiple terms specified
// [4] VolumeScheduling enabled
{ {
Name: "Nil_Node_and_Multiple_Allowed_Topology_terms_and_VolumeScheduling_enabled", Name: "Nil_Node_and_Multiple_Allowed_Topology_terms_and_VolumeScheduling_enabled",
Node: nil, Node: nil,
VolumeScheduling: true,
AllowedTopologies: []v1.TopologySelectorTerm{ AllowedTopologies: []v1.TopologySelectorTerm{
{ {
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{ MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
@ -1407,11 +1330,9 @@ func TestSelectZoneForVolume(t *testing.T) {
// [1] nil Node // [1] nil Node
// [2] no Zone/Zones parametes specified // [2] no Zone/Zones parametes specified
// [3] AllowedTopologies with single term and value specified // [3] AllowedTopologies with single term and value specified
// [4] VolumeScheduling enabled
{ {
Name: "Nil_Node_and_Single_Allowed_Topology_term_value_and_VolumeScheduling_enabled", Name: "Nil_Node_and_Single_Allowed_Topology_term_value_and_VolumeScheduling_enabled",
Node: nil, Node: nil,
VolumeScheduling: true,
AllowedTopologies: []v1.TopologySelectorTerm{ AllowedTopologies: []v1.TopologySelectorTerm{
{ {
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{ MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
@ -1430,8 +1351,6 @@ func TestSelectZoneForVolume(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.Name, func(t *testing.T) { t.Run(test.Name, func(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, test.VolumeScheduling)()
var zonesParameter, zonesWithNodes sets.String var zonesParameter, zonesWithNodes sets.String
var err error var err error

View File

@ -18,13 +18,11 @@ go_library(
"//pkg/cloudprovider/providers/aws:go_default_library", "//pkg/cloudprovider/providers/aws:go_default_library",
"//pkg/cloudprovider/providers/azure:go_default_library", "//pkg/cloudprovider/providers/azure:go_default_library",
"//pkg/cloudprovider/providers/gce:go_default_library", "//pkg/cloudprovider/providers/gce:go_default_library",
"//pkg/features:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library", "//pkg/kubeapiserver/admission:go_default_library",
"//pkg/kubelet/apis:go_default_library", "//pkg/kubelet/apis:go_default_library",
"//pkg/volume:go_default_library", "//pkg/volume:go_default_library",
"//pkg/volume/util:go_default_library", "//pkg/volume/util:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/cloud-provider:go_default_library", "//staging/src/k8s.io/cloud-provider:go_default_library",
"//vendor/k8s.io/klog:go_default_library", "//vendor/k8s.io/klog:go_default_library",
], ],
@ -37,15 +35,12 @@ go_test(
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/cloudprovider/providers/aws:go_default_library", "//pkg/cloudprovider/providers/aws:go_default_library",
"//pkg/features:go_default_library",
"//pkg/kubelet/apis:go_default_library", "//pkg/kubelet/apis:go_default_library",
"//pkg/volume/util:go_default_library", "//pkg/volume/util:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
], ],
) )

View File

@ -23,14 +23,12 @@ import (
"sync" "sync"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
utilfeature "k8s.io/apiserver/pkg/util/feature"
cloudprovider "k8s.io/cloud-provider" cloudprovider "k8s.io/cloud-provider"
"k8s.io/klog" "k8s.io/klog"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/cloudprovider/providers/aws" "k8s.io/kubernetes/pkg/cloudprovider/providers/aws"
"k8s.io/kubernetes/pkg/cloudprovider/providers/azure" "k8s.io/kubernetes/pkg/cloudprovider/providers/azure"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce" "k8s.io/kubernetes/pkg/cloudprovider/providers/gce"
"k8s.io/kubernetes/pkg/features"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission" kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
vol "k8s.io/kubernetes/pkg/volume" vol "k8s.io/kubernetes/pkg/volume"
@ -158,25 +156,23 @@ func (l *persistentVolumeLabel) Admit(a admission.Attributes) (err error) {
requirements = append(requirements, api.NodeSelectorRequirement{Key: k, Operator: api.NodeSelectorOpIn, Values: values}) requirements = append(requirements, api.NodeSelectorRequirement{Key: k, Operator: api.NodeSelectorOpIn, Values: values})
} }
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) { if volume.Spec.NodeAffinity == nil {
if volume.Spec.NodeAffinity == nil { volume.Spec.NodeAffinity = new(api.VolumeNodeAffinity)
volume.Spec.NodeAffinity = new(api.VolumeNodeAffinity) }
} if volume.Spec.NodeAffinity.Required == nil {
if volume.Spec.NodeAffinity.Required == nil { volume.Spec.NodeAffinity.Required = new(api.NodeSelector)
volume.Spec.NodeAffinity.Required = new(api.NodeSelector) }
} if len(volume.Spec.NodeAffinity.Required.NodeSelectorTerms) == 0 {
if len(volume.Spec.NodeAffinity.Required.NodeSelectorTerms) == 0 { // Need at least one term pre-allocated whose MatchExpressions can be appended to
// Need at least one term pre-allocated whose MatchExpressions can be appended to volume.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]api.NodeSelectorTerm, 1)
volume.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]api.NodeSelectorTerm, 1) }
} if nodeSelectorRequirementKeysExistInNodeSelectorTerms(requirements, volume.Spec.NodeAffinity.Required.NodeSelectorTerms) {
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.",
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)
requirements, volume.Spec.NodeAffinity) } else {
} else { for _, req := range requirements {
for _, req := range requirements { for i := range volume.Spec.NodeAffinity.Required.NodeSelectorTerms {
for i := range volume.Spec.NodeAffinity.Required.NodeSelectorTerms { volume.Spec.NodeAffinity.Required.NodeSelectorTerms[i].MatchExpressions = append(volume.Spec.NodeAffinity.Required.NodeSelectorTerms[i].MatchExpressions, req)
volume.Spec.NodeAffinity.Required.NodeSelectorTerms[i].MatchExpressions = append(volume.Spec.NodeAffinity.Required.NodeSelectorTerms[i].MatchExpressions, req)
}
} }
} }
} }

View File

@ -27,11 +27,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/cloudprovider/providers/aws" "k8s.io/kubernetes/pkg/cloudprovider/providers/aws"
"k8s.io/kubernetes/pkg/features"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
volumeutil "k8s.io/kubernetes/pkg/volume/util" volumeutil "k8s.io/kubernetes/pkg/volume/util"
) )
@ -125,8 +122,6 @@ func TestAdmission(t *testing.T) {
}, },
} }
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
// Non-cloud PVs are ignored // Non-cloud PVs are ignored
err := handler.Admit(admission.NewAttributesRecord(&ignoredPV, nil, api.Kind("PersistentVolume").WithVersion("version"), ignoredPV.Namespace, ignoredPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, false, nil)) err := handler.Admit(admission.NewAttributesRecord(&ignoredPV, nil, api.Kind("PersistentVolume").WithVersion("version"), ignoredPV.Namespace, ignoredPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, false, nil))
if err != nil { if err != nil {

View File

@ -489,17 +489,14 @@ func ClusterRoles() []rbacv1.ClusterRole {
rbacv1helpers.NewRule("create").Groups(certificatesGroup).Resources("certificatesigningrequests/selfnodeclient").RuleOrDie(), rbacv1helpers.NewRule("create").Groups(certificatesGroup).Resources("certificatesigningrequests/selfnodeclient").RuleOrDie(),
}, },
}, },
} {
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) {
roles = append(roles, rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{Name: "system:volume-scheduler"}, ObjectMeta: metav1.ObjectMeta{Name: "system:volume-scheduler"},
Rules: []rbacv1.PolicyRule{ Rules: []rbacv1.PolicyRule{
rbacv1helpers.NewRule(ReadUpdate...).Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(), rbacv1helpers.NewRule(ReadUpdate...).Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(),
rbacv1helpers.NewRule(Read...).Groups(storageGroup).Resources("storageclasses").RuleOrDie(), rbacv1helpers.NewRule(Read...).Groups(storageGroup).Resources("storageclasses").RuleOrDie(),
rbacv1helpers.NewRule(ReadUpdate...).Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(), rbacv1helpers.NewRule(ReadUpdate...).Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(),
}, },
}) },
} }
externalProvisionerRules := []rbacv1.PolicyRule{ externalProvisionerRules := []rbacv1.PolicyRule{
@ -535,6 +532,7 @@ func ClusterRoleBindings() []rbacv1.ClusterRoleBinding {
rbacv1helpers.NewClusterBinding("system:kube-dns").SAs("kube-system", "kube-dns").BindingOrDie(), rbacv1helpers.NewClusterBinding("system:kube-dns").SAs("kube-system", "kube-dns").BindingOrDie(),
rbacv1helpers.NewClusterBinding("system:kube-scheduler").Users(user.KubeScheduler).BindingOrDie(), rbacv1helpers.NewClusterBinding("system:kube-scheduler").Users(user.KubeScheduler).BindingOrDie(),
rbacv1helpers.NewClusterBinding("system:aws-cloud-provider").SAs("kube-system", "aws-cloud-provider").BindingOrDie(), rbacv1helpers.NewClusterBinding("system:aws-cloud-provider").SAs("kube-system", "aws-cloud-provider").BindingOrDie(),
rbacv1helpers.NewClusterBinding("system:volume-scheduler").Users(user.KubeScheduler).BindingOrDie(),
// This default binding of the system:node role to the system:nodes group is deprecated in 1.7 with the availability of the Node authorizer. // This default binding of the system:node role to the system:nodes group is deprecated in 1.7 with the availability of the Node authorizer.
// This leaves the binding, but with an empty set of subjects, so that tightening reconciliation can remove the subject. // This leaves the binding, but with an empty set of subjects, so that tightening reconciliation can remove the subject.
@ -544,10 +542,6 @@ func ClusterRoleBindings() []rbacv1.ClusterRoleBinding {
}, },
} }
if utilfeature.DefaultFeatureGate.Enabled(features.VolumeScheduling) {
rolebindings = append(rolebindings, rbacv1helpers.NewClusterBinding("system:volume-scheduler").Users(user.KubeScheduler).BindingOrDie())
}
addClusterRoleBindingLabel(rolebindings) addClusterRoleBindingLabel(rolebindings)
return rolebindings return rolebindings

View File

@ -63,7 +63,11 @@ var (
) )
type FeatureSpec struct { type FeatureSpec struct {
Default bool // Default is the default enablement state for the feature
Default bool
// LockToDefault indicates that the feature is locked to its default and cannot be changed
LockToDefault bool
// PreRelease indicates the maturity level of the feature
PreRelease prerelease PreRelease prerelease
} }
@ -199,6 +203,9 @@ func (f *featureGate) SetFromMap(m map[string]bool) error {
if !ok { if !ok {
return fmt.Errorf("unrecognized feature gate: %s", k) return fmt.Errorf("unrecognized feature gate: %s", k)
} }
if featureSpec.LockToDefault && featureSpec.Default != v {
return fmt.Errorf("cannot set feature gate %v to %v, feature is locked to %v", k, v, featureSpec.Default)
}
enabled[k] = v enabled[k] = v
// Handle "special" features like "all alpha gates" // Handle "special" features like "all alpha gates"
if fn, found := f.special[k]; found { if fn, found := f.special[k]; found {

View File

@ -221,6 +221,8 @@ func TestFeatureGateSetFromMap(t *testing.T) {
// gates for testing // gates for testing
const testAlphaGate Feature = "TestAlpha" const testAlphaGate Feature = "TestAlpha"
const testBetaGate Feature = "TestBeta" const testBetaGate Feature = "TestBeta"
const testLockedTrueGate Feature = "TestLockedTrue"
const testLockedFalseGate Feature = "TestLockedFalse"
tests := []struct { tests := []struct {
name string name string
@ -270,17 +272,54 @@ func TestFeatureGateSetFromMap(t *testing.T) {
}, },
setmapError: "unrecognized feature gate:", setmapError: "unrecognized feature gate:",
}, },
{
name: "set locked gates",
setmap: map[string]bool{
"TestLockedTrue": true,
"TestLockedFalse": false,
},
expect: map[Feature]bool{
testAlphaGate: false,
testBetaGate: false,
},
},
{
name: "set locked gates",
setmap: map[string]bool{
"TestLockedTrue": false,
},
expect: map[Feature]bool{
testAlphaGate: false,
testBetaGate: false,
},
setmapError: "cannot set feature gate TestLockedTrue to false, feature is locked to true",
},
{
name: "set locked gates",
setmap: map[string]bool{
"TestLockedFalse": true,
},
expect: map[Feature]bool{
testAlphaGate: false,
testBetaGate: false,
},
setmapError: "cannot set feature gate TestLockedFalse to true, feature is locked to false",
},
} }
for i, test := range tests { for i, test := range tests {
t.Run(fmt.Sprintf("SetFromMap %s", test.name), func(t *testing.T) { t.Run(fmt.Sprintf("SetFromMap %s", test.name), func(t *testing.T) {
f := NewFeatureGate() f := NewFeatureGate()
f.Add(map[Feature]FeatureSpec{ f.Add(map[Feature]FeatureSpec{
testAlphaGate: {Default: false, PreRelease: Alpha}, testAlphaGate: {Default: false, PreRelease: Alpha},
testBetaGate: {Default: false, PreRelease: Beta}, testBetaGate: {Default: false, PreRelease: Beta},
testLockedTrueGate: {Default: true, PreRelease: GA, LockToDefault: true},
testLockedFalseGate: {Default: false, PreRelease: GA, LockToDefault: true},
}) })
err := f.SetFromMap(test.setmap) err := f.SetFromMap(test.setmap)
if test.setmapError != "" { if test.setmapError != "" {
if !strings.Contains(err.Error(), test.setmapError) { if err == nil {
t.Errorf("expected error, got none")
} else if !strings.Contains(err.Error(), test.setmapError) {
t.Errorf("%d: SetFromMap(%#v) Expected err:%v, Got err:%v", i, test.setmap, test.setmapError, err) t.Errorf("%d: SetFromMap(%#v) Expected err:%v, Got err:%v", i, test.setmap, test.setmapError, err)
} }
} else if err != nil { } else if err != nil {

View File

@ -97,7 +97,6 @@ type testPVC struct {
} }
func TestVolumeBinding(t *testing.T) { func TestVolumeBinding(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentLocalVolumes, true)() defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentLocalVolumes, true)()
config := setupCluster(t, "volume-scheduling-", 2, 0, 0) config := setupCluster(t, "volume-scheduling-", 2, 0, 0)
defer config.teardown() defer config.teardown()
@ -268,7 +267,6 @@ func TestVolumeBinding(t *testing.T) {
// TestVolumeBindingRescheduling tests scheduler will retry scheduling when needed. // TestVolumeBindingRescheduling tests scheduler will retry scheduling when needed.
func TestVolumeBindingRescheduling(t *testing.T) { func TestVolumeBindingRescheduling(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentLocalVolumes, true)() defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentLocalVolumes, true)()
config := setupCluster(t, "volume-scheduling-", 2, 0, 0) config := setupCluster(t, "volume-scheduling-", 2, 0, 0)
defer config.teardown() defer config.teardown()
@ -412,7 +410,6 @@ func TestVolumeBindingDynamicStressSlow(t *testing.T) {
} }
func testVolumeBindingStress(t *testing.T, schedulerResyncPeriod time.Duration, dynamic bool, provisionDelaySeconds int) { func testVolumeBindingStress(t *testing.T, schedulerResyncPeriod time.Duration, dynamic bool, provisionDelaySeconds int) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentLocalVolumes, true)() defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentLocalVolumes, true)()
config := setupCluster(t, "volume-binding-stress-", 1, schedulerResyncPeriod, provisionDelaySeconds) config := setupCluster(t, "volume-binding-stress-", 1, schedulerResyncPeriod, provisionDelaySeconds)
defer config.teardown() defer config.teardown()
@ -502,7 +499,6 @@ func testVolumeBindingStress(t *testing.T, schedulerResyncPeriod time.Duration,
} }
func testVolumeBindingWithAffinity(t *testing.T, anti bool, numNodes, numPods, numPVsFirstNode int) { func testVolumeBindingWithAffinity(t *testing.T, anti bool, numNodes, numPods, numPVsFirstNode int) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentLocalVolumes, true)() defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentLocalVolumes, true)()
config := setupCluster(t, "volume-pod-affinity-", numNodes, 0, 0) config := setupCluster(t, "volume-pod-affinity-", numNodes, 0, 0)
defer config.teardown() defer config.teardown()
@ -629,7 +625,6 @@ func TestVolumeBindingWithAffinity(t *testing.T) {
} }
func TestPVAffinityConflict(t *testing.T) { func TestPVAffinityConflict(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentLocalVolumes, true)() defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentLocalVolumes, true)()
config := setupCluster(t, "volume-scheduling-", 3, 0, 0) config := setupCluster(t, "volume-scheduling-", 3, 0, 0)
defer config.teardown() defer config.teardown()
@ -690,7 +685,6 @@ func TestPVAffinityConflict(t *testing.T) {
} }
func TestVolumeProvision(t *testing.T) { func TestVolumeProvision(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentLocalVolumes, true)() defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentLocalVolumes, true)()
config := setupCluster(t, "volume-scheduling", 1, 0, 0) config := setupCluster(t, "volume-scheduling", 1, 0, 0)
defer config.teardown() defer config.teardown()
@ -830,7 +824,6 @@ func TestVolumeProvision(t *testing.T) {
// on provision failure. // on provision failure.
func TestRescheduleProvisioning(t *testing.T) { func TestRescheduleProvisioning(t *testing.T) {
// Set feature gates // Set feature gates
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeScheduling, true)()
controllerCh := make(chan struct{}) controllerCh := make(chan struct{})
context := initTestMaster(t, "reschedule-volume-provision", nil) context := initTestMaster(t, "reschedule-volume-provision", nil)