Feature gate initializers field

This commit is contained in:
Jordan Liggitt 2017-08-27 22:19:28 -04:00
parent 877ee91930
commit 658956f063
No known key found for this signature in database
GPG Key ID: 39928704103C7229
15 changed files with 129 additions and 7 deletions

View File

@ -22,6 +22,9 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apiserver/pkg/features:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
"//vendor/k8s.io/client-go/dynamic:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
],

View File

@ -33,6 +33,9 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
)
@ -150,6 +153,8 @@ func TestCRDShadowGroup(t *testing.T) {
}
func TestCRD(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.Initializers, true)()
config, tearDown := StartTestServerOrDie(t)
defer tearDown()

View File

@ -164,4 +164,5 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
StreamingProxyRedirects: {Default: true, PreRelease: utilfeature.Beta},
genericfeatures.AdvancedAuditing: {Default: false, PreRelease: utilfeature.Alpha},
TaintNodesByCondition: {Default: false, PreRelease: utilfeature.Alpha},
genericfeatures.Initializers: {Default: false, PreRelease: utilfeature.Alpha},
}

View File

@ -23,6 +23,8 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/features:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
],
)

View File

@ -33,6 +33,8 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubeapiserver/admission/configuration"
@ -71,6 +73,18 @@ func (i *initializer) Validate() error {
if i.config == nil {
return fmt.Errorf("the Initializer admission plugin requires a Kubernetes client to be provided")
}
if i.authorizer == nil {
return fmt.Errorf("the Initializer admission plugin requires an authorizer to be provided")
}
if !utilfeature.DefaultFeatureGate.Enabled(features.Initializers) {
if err := utilfeature.DefaultFeatureGate.Set(string(features.Initializers) + "=true"); err != nil {
glog.Errorf("error enabling Initializers feature as part of admission plugin setup: %v", err)
} else {
glog.Infof("enabled Initializers feature as part of admission plugin setup")
}
}
i.config.Run(wait.NeverStop)
return nil
}
@ -228,11 +242,6 @@ func (i *initializer) Admit(a admission.Attributes) (err error) {
}
func (i *initializer) canInitialize(a admission.Attributes, message string) error {
// if no authorizer is present, the initializer plugin allows modification of uninitialized resources
if i.authorizer == nil {
glog.V(4).Infof("No authorizer provided to initialization admission control, unable to check permissions")
return nil
}
// caller must have the ability to mutate un-initialized resources
authorized, reason, err := i.authorizer.Authorize(authorizer.AttributesRecord{
Name: a.GetName(),

View File

@ -47,6 +47,13 @@ const (
//
// Enables compression of REST responses (GET and LIST only)
APIResponseCompression utilfeature.Feature = "APIResponseCompression"
// owner: @smarterclayton
// alpha: v1.7
//
// Allow asynchronous coordination of object creation.
// Auto-enabled by the Initializers admission plugin.
Initializers utilfeature.Feature = "Initializers"
)
func init() {
@ -60,4 +67,5 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
StreamingProxyRedirects: {Default: true, PreRelease: utilfeature.Beta},
AdvancedAuditing: {Default: false, PreRelease: utilfeature.Alpha},
APIResponseCompression: {Default: false, PreRelease: utilfeature.Alpha},
Initializers: {Default: false, PreRelease: utilfeature.Alpha},
}

View File

@ -31,6 +31,7 @@ go_test(
"//vendor/k8s.io/apiserver/pkg/apis/example:go_default_library",
"//vendor/k8s.io/apiserver/pkg/apis/example/v1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/features:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage:go_default_library",
@ -39,6 +40,8 @@ go_test(
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend/factory:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/testing:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
],
)

View File

@ -44,6 +44,7 @@ import (
examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
"k8s.io/apiserver/pkg/endpoints/request"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage"
@ -52,6 +53,8 @@ import (
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/apiserver/pkg/storage/storagebackend/factory"
storagetesting "k8s.io/apiserver/pkg/storage/testing"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
)
var scheme = runtime.NewScheme()
@ -390,6 +393,8 @@ func isQualifiedResource(err error, kind, group string) bool {
}
func TestStoreCreateInitialized(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.Initializers, true)()
podA := &example.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo", Namespace: "test",
@ -493,6 +498,8 @@ func TestStoreCreateInitialized(t *testing.T) {
}
func TestStoreCreateInitializedFailed(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.Initializers, true)()
podA := &example.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo", Namespace: "test",
@ -991,6 +998,8 @@ func TestGracefulStoreHandleFinalizers(t *testing.T) {
}
func TestFailedInitializationStoreUpdate(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.Initializers, true)()
initialGeneration := int64(1)
podInitializing := &example.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo", Initializers: &metav1.Initializers{Pending: []metav1.Initializer{{Name: "Test"}}}, Generation: initialGeneration},

View File

@ -48,7 +48,9 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/features:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
)

View File

@ -26,7 +26,9 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature"
)
// RESTCreateStrategy defines the minimum validation, accepted input, and
@ -88,6 +90,11 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx genericapirequest.Context, ob
objectMeta.SetName(strategy.GenerateName(objectMeta.GetGenerateName()))
}
// Ensure Initializers are not set unless the feature is enabled
if !utilfeature.DefaultFeatureGate.Enabled(features.Initializers) {
objectMeta.SetInitializers(nil)
}
// ClusterName is ignored and should not be saved
objectMeta.SetClusterName("")

View File

@ -27,6 +27,8 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature"
)
// RESTUpdateStrategy defines the minimum validation, accepted input, and
@ -99,6 +101,12 @@ func BeforeUpdate(strategy RESTUpdateStrategy, ctx genericapirequest.Context, ob
}
objectMeta.SetGeneration(oldMeta.GetGeneration())
// Ensure Initializers are not set unless the feature is enabled
if !utilfeature.DefaultFeatureGate.Enabled(features.Initializers) {
oldMeta.SetInitializers(nil)
objectMeta.SetInitializers(nil)
}
strategy.PrepareForUpdate(ctx, obj, old)
// ClusterName is ignored and should not be saved

View File

@ -31,6 +31,9 @@ filegroup(
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
srcs = [
":package-srcs",
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:all-srcs",
],
tags = ["automanaged"],
)

View File

@ -0,0 +1,22 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["feature_gate_testing.go"],
visibility = ["//visibility:public"],
deps = ["//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,40 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testing
import (
"fmt"
"testing"
"k8s.io/apiserver/pkg/util/feature"
)
// SetFeatureGateDuringTest sets the specified gate to the specified value, and returns a function that restores the original value.
// Failures to set or restore cause the test to fail.
func SetFeatureGateDuringTest(t *testing.T, gate feature.FeatureGate, feature feature.Feature, value bool) func() {
originalValue := gate.Enabled(feature)
if err := gate.Set(fmt.Sprintf("%s=%v", feature, value)); err != nil {
t.Errorf("error setting %s=%v: %v", feature, value, err)
}
return func() {
if err := gate.Set(fmt.Sprintf("%s=%v", feature, originalValue)); err != nil {
t.Errorf("error restoring %s=%v: %v", feature, originalValue, err)
}
}
}

View File

@ -36,7 +36,7 @@ import (
"k8s.io/kubernetes/test/e2e/framework"
)
var _ = SIGDescribe("Initializers", func() {
var _ = SIGDescribe("Initializers [Feature:Initializers]", func() {
f := framework.NewDefaultFramework("initializers")
// TODO: Add failure traps once we have JustAfterEach