Validate Job .spec.ttlSecondsAfterFinished; clear it when feature disabled

1. If TTLAfterFinished feature is enabled, the value should be non-negative.
2. If TTLAfterFinished feature is disabled, the field value should not
be kept.
This commit is contained in:
Janet Kuo
2018-08-03 15:05:56 -07:00
parent 5186807587
commit 47d06c446d
6 changed files with 123 additions and 15 deletions

View File

@@ -18,6 +18,7 @@ go_library(
"//pkg/api/pod:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/batch/validation: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/fields:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
@@ -27,6 +28,7 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
)
@@ -39,10 +41,12 @@ go_test(
"//pkg/api/testing:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/core: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/types:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
)

View File

@@ -30,10 +30,12 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/batch/validation"
"k8s.io/kubernetes/pkg/features"
)
// jobStrategy implements verification logic for Replication Controllers.
@@ -61,6 +63,10 @@ func (jobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
job := obj.(*batch.Job)
job.Status = batch.JobStatus{}
if !utilfeature.DefaultFeatureGate.Enabled(features.TTLAfterFinished) {
job.Spec.TTLSecondsAfterFinished = nil
}
pod.DropDisabledAlphaFields(&job.Spec.Template.Spec)
}
@@ -70,6 +76,11 @@ func (jobStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object
oldJob := old.(*batch.Job)
newJob.Status = oldJob.Status
if !utilfeature.DefaultFeatureGate.Enabled(features.TTLAfterFinished) {
newJob.Spec.TTLSecondsAfterFinished = nil
oldJob.Spec.TTLSecondsAfterFinished = nil
}
pod.DropDisabledAlphaFields(&newJob.Spec.Template.Spec)
pod.DropDisabledAlphaFields(&oldJob.Spec.Template.Spec)
}

View File

@@ -24,19 +24,24 @@ import (
"k8s.io/apimachinery/pkg/types"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
)
func newBool(a bool) *bool {
r := new(bool)
*r = a
return r
return &a
}
func newInt32(i int32) *int32 {
return &i
}
func TestJobStrategy(t *testing.T) {
ttlEnabled := utilfeature.DefaultFeatureGate.Enabled(features.TTLAfterFinished)
ctx := genericapirequest.NewDefaultContext()
if !Strategy.NamespaceScoped() {
t.Errorf("Job must be namespace scoped")
@@ -64,9 +69,10 @@ func TestJobStrategy(t *testing.T) {
Namespace: metav1.NamespaceDefault,
},
Spec: batch.JobSpec{
Selector: validSelector,
Template: validPodTemplateSpec,
ManualSelector: newBool(true),
Selector: validSelector,
Template: validPodTemplateSpec,
TTLSecondsAfterFinished: newInt32(0), // Set TTL
ManualSelector: newBool(true),
},
Status: batch.JobStatus{
Active: 11,
@@ -81,11 +87,21 @@ func TestJobStrategy(t *testing.T) {
if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs)
}
if ttlEnabled && job.Spec.TTLSecondsAfterFinished == nil {
// When the TTL feature is enabled, the TTL field can be set
t.Errorf("Job should allow setting .spec.ttlSecondsAfterFinished when %v feature is enabled", features.TTLAfterFinished)
}
if !ttlEnabled && job.Spec.TTLSecondsAfterFinished != nil {
// When the TTL feature is disabled, the TTL field cannot be set
t.Errorf("Job should not allow setting .spec.ttlSecondsAfterFinished when %v feature is disabled", features.TTLAfterFinished)
}
parallelism := int32(10)
updatedJob := &batch.Job{
ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "4"},
Spec: batch.JobSpec{
Parallelism: &parallelism,
Parallelism: &parallelism,
TTLSecondsAfterFinished: newInt32(1), // Update TTL
},
Status: batch.JobStatus{
Active: 11,
@@ -101,6 +117,9 @@ func TestJobStrategy(t *testing.T) {
if len(errs) == 0 {
t.Errorf("Expected a validation error")
}
if ttlEnabled != (job.Spec.TTLSecondsAfterFinished != nil || updatedJob.Spec.TTLSecondsAfterFinished != nil) {
t.Errorf("Job should only allow updating .spec.ttlSecondsAfterFinished when %v feature is enabled", features.TTLAfterFinished)
}
// Make sure we correctly implement the interface.
// Otherwise a typo could silently change the default.