Graduate JobTrackingWithFinalizers to stable
Change-Id: Ifc749a85b1270c0155ac511b91d4681d53236820
This commit is contained in:
@@ -53,7 +53,7 @@ type orderedIntervals []interval
|
||||
// the indexes that succeeded since the last sync.
|
||||
func calculateSucceededIndexes(job *batch.Job, pods []*v1.Pod) (orderedIntervals, orderedIntervals) {
|
||||
var prevIntervals orderedIntervals
|
||||
withFinalizers := trackingUncountedPods(job)
|
||||
withFinalizers := hasJobTrackingAnnotation(job)
|
||||
if withFinalizers {
|
||||
prevIntervals = succeededIndexesFromJob(job)
|
||||
}
|
||||
|
@@ -22,10 +22,6 @@ import (
|
||||
"github.com/google/go-cmp/cmp"
|
||||
batch "k8s.io/api/batch/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
@@ -219,13 +215,7 @@ func TestCalculateSucceededIndexes(t *testing.T) {
|
||||
}
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobTrackingWithFinalizers, tc.trackingWithFinalizers)()
|
||||
job := &batch.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
batch.JobTrackingFinalizer: "",
|
||||
},
|
||||
},
|
||||
Status: batch.JobStatus{
|
||||
CompletedIndexes: tc.prevSucceeded,
|
||||
},
|
||||
@@ -233,6 +223,11 @@ func TestCalculateSucceededIndexes(t *testing.T) {
|
||||
Completions: pointer.Int32Ptr(tc.completions),
|
||||
},
|
||||
}
|
||||
if tc.trackingWithFinalizers {
|
||||
job.Annotations = map[string]string{
|
||||
batch.JobTrackingFinalizer: "",
|
||||
}
|
||||
}
|
||||
pods := hollowPodsWithIndexPhase(tc.pods)
|
||||
for _, p := range pods {
|
||||
p.Finalizers = append(p.Finalizers, batch.JobTrackingFinalizer)
|
||||
|
@@ -714,17 +714,13 @@ func (jm *Controller) syncJob(ctx context.Context, key string) (forget bool, rEr
|
||||
|
||||
var expectedRmFinalizers sets.String
|
||||
var uncounted *uncountedTerminatedPods
|
||||
if trackingUncountedPods(&job) {
|
||||
if hasJobTrackingAnnotation(&job) {
|
||||
klog.V(4).InfoS("Tracking uncounted Pods with pod finalizers", "job", klog.KObj(&job))
|
||||
if job.Status.UncountedTerminatedPods == nil {
|
||||
job.Status.UncountedTerminatedPods = &batch.UncountedTerminatedPods{}
|
||||
}
|
||||
uncounted = newUncountedTerminatedPods(*job.Status.UncountedTerminatedPods)
|
||||
expectedRmFinalizers = jm.finalizerExpectations.getExpectedUIDs(key)
|
||||
} else if patch := removeTrackingAnnotationPatch(&job); patch != nil {
|
||||
if err := jm.patchJobHandler(ctx, &job, patch); err != nil {
|
||||
return false, fmt.Errorf("removing tracking finalizer from job %s: %w", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check the expectations of the job before counting active pods, otherwise a new pod can sneak in
|
||||
@@ -1476,7 +1472,7 @@ func (jm *Controller) manageJob(ctx context.Context, job *batch.Job, activePods
|
||||
if isIndexedJob(job) {
|
||||
addCompletionIndexEnvVariables(podTemplate)
|
||||
}
|
||||
if trackingUncountedPods(job) {
|
||||
if hasJobTrackingAnnotation(job) {
|
||||
podTemplate.Finalizers = appendJobCompletionFinalizerIfNotFound(podTemplate.Finalizers)
|
||||
}
|
||||
|
||||
@@ -1635,10 +1631,6 @@ func getCompletionMode(job *batch.Job) string {
|
||||
return string(batch.NonIndexedCompletion)
|
||||
}
|
||||
|
||||
func trackingUncountedPods(job *batch.Job) bool {
|
||||
return feature.DefaultFeatureGate.Enabled(features.JobTrackingWithFinalizers) && hasJobTrackingAnnotation(job)
|
||||
}
|
||||
|
||||
func hasJobTrackingAnnotation(job *batch.Job) bool {
|
||||
if job.Annotations == nil {
|
||||
return false
|
||||
@@ -1669,21 +1661,6 @@ func removeTrackingFinalizerPatch(pod *v1.Pod) []byte {
|
||||
return patchBytes
|
||||
}
|
||||
|
||||
func removeTrackingAnnotationPatch(job *batch.Job) []byte {
|
||||
if !hasJobTrackingAnnotation(job) {
|
||||
return nil
|
||||
}
|
||||
patch := map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"annotations": map[string]interface{}{
|
||||
batch.JobTrackingFinalizer: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
patchBytes, _ := json.Marshal(patch)
|
||||
return patchBytes
|
||||
}
|
||||
|
||||
type uncountedTerminatedPods struct {
|
||||
succeeded sets.String
|
||||
failed sets.String
|
||||
|
@@ -135,7 +135,7 @@ func newPodList(count int, status v1.PodPhase, job *batch.Job) []*v1.Pod {
|
||||
for i := 0; i < count; i++ {
|
||||
newPod := newPod(fmt.Sprintf("pod-%v", rand.String(10)), job)
|
||||
newPod.Status = v1.PodStatus{Phase: status}
|
||||
if trackingUncountedPods(job) {
|
||||
if hasJobTrackingAnnotation(job) {
|
||||
newPod.Finalizers = append(newPod.Finalizers, batch.JobTrackingFinalizer)
|
||||
}
|
||||
pods = append(pods, newPod)
|
||||
@@ -178,7 +178,7 @@ func setPodsStatusesWithIndexes(podIndexer cache.Indexer, job *batch.Job, status
|
||||
}
|
||||
p.Spec.Hostname = fmt.Sprintf("%s-%s", job.Name, s.Index)
|
||||
}
|
||||
if trackingUncountedPods(job) {
|
||||
if hasJobTrackingAnnotation(job) {
|
||||
p.Finalizers = append(p.Finalizers, batch.JobTrackingFinalizer)
|
||||
}
|
||||
podIndexer.Add(p)
|
||||
@@ -343,15 +343,15 @@ func TestControllerSyncJob(t *testing.T) {
|
||||
parallelism: 2,
|
||||
completions: 5,
|
||||
backoffLimit: 6,
|
||||
podControllerError: fmt.Errorf("fake error"),
|
||||
jobKeyForget: true,
|
||||
activePods: 1,
|
||||
succeededPods: 1,
|
||||
failedPods: 1,
|
||||
expectedCreations: 1,
|
||||
expectedActive: 1,
|
||||
expectedActive: 2,
|
||||
expectedSucceeded: 1,
|
||||
expectedFailed: 1,
|
||||
expectedPodPatches: 2,
|
||||
},
|
||||
"new failed pod": {
|
||||
parallelism: 2,
|
||||
@@ -728,7 +728,6 @@ func TestControllerSyncJob(t *testing.T) {
|
||||
t.Skipf("Test is exclusive for wFinalizers=%t", *tc.wFinalizersExclusive)
|
||||
}
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobReadyPods, tc.jobReadyPodsEnabled)()
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobTrackingWithFinalizers, wFinalizers)()
|
||||
|
||||
// job manager setup
|
||||
clientSet := clientset.NewForConfigOrDie(&restclient.Config{Host: "", ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
@@ -918,13 +917,12 @@ func checkIndexedJobPods(t *testing.T, control *controller.FakePodControl, wantI
|
||||
}
|
||||
|
||||
// TestSyncJobLegacyTracking makes sure that a Job is only tracked with
|
||||
// finalizers only when the feature is enabled and the job has the finalizer.
|
||||
// finalizers when the job has the annotation.
|
||||
func TestSyncJobLegacyTracking(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
job batch.Job
|
||||
trackingWithFinalizersEnabled bool
|
||||
wantUncounted bool
|
||||
wantPatches int
|
||||
job batch.Job
|
||||
wantUncounted bool
|
||||
wantPatches int
|
||||
}{
|
||||
"no annotation": {
|
||||
job: batch.Job{
|
||||
@@ -937,19 +935,7 @@ func TestSyncJobLegacyTracking(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
"no annotation, feature enabled": {
|
||||
job: batch.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: batch.JobSpec{
|
||||
Parallelism: pointer.Int32Ptr(1),
|
||||
},
|
||||
},
|
||||
trackingWithFinalizersEnabled: true,
|
||||
},
|
||||
"tracking annotation, feature disabled": {
|
||||
"tracking annotation": {
|
||||
job: batch.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
@@ -962,26 +948,9 @@ func TestSyncJobLegacyTracking(t *testing.T) {
|
||||
Parallelism: pointer.Int32Ptr(1),
|
||||
},
|
||||
},
|
||||
// Finalizer removed.
|
||||
wantPatches: 1,
|
||||
wantUncounted: true,
|
||||
},
|
||||
"tracking annotation, feature enabled": {
|
||||
job: batch.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "ns",
|
||||
Annotations: map[string]string{
|
||||
batch.JobTrackingFinalizer: "",
|
||||
},
|
||||
},
|
||||
Spec: batch.JobSpec{
|
||||
Parallelism: pointer.Int32Ptr(1),
|
||||
},
|
||||
},
|
||||
trackingWithFinalizersEnabled: true,
|
||||
wantUncounted: true,
|
||||
},
|
||||
"different annotation, feature enabled": {
|
||||
"different annotation": {
|
||||
job: batch.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
@@ -994,13 +963,10 @@ func TestSyncJobLegacyTracking(t *testing.T) {
|
||||
Parallelism: pointer.Int32Ptr(1),
|
||||
},
|
||||
},
|
||||
trackingWithFinalizersEnabled: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobTrackingWithFinalizers, tc.trackingWithFinalizersEnabled)()
|
||||
|
||||
// Job manager setup.
|
||||
clientSet := clientset.NewForConfigOrDie(&restclient.Config{Host: "", ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
manager, sharedInformerFactory := newControllerFromClient(clientSet, controller.NoResyncPeriodFunc)
|
||||
@@ -2909,7 +2875,6 @@ func TestSyncJobWithJobPodFailurePolicy(t *testing.T) {
|
||||
for _, wFinalizers := range []bool{false, true} {
|
||||
for name, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("%s; finalizers=%t", name, wFinalizers), func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobTrackingWithFinalizers, wFinalizers)()
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobPodFailurePolicy, tc.enableJobPodFailurePolicy)()
|
||||
clientset := clientset.NewForConfigOrDie(&restclient.Config{Host: "", ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
manager, sharedInformerFactory := newControllerFromClient(clientset, controller.NoResyncPeriodFunc)
|
||||
@@ -2992,15 +2957,9 @@ func TestSyncJobUpdateRequeue(t *testing.T) {
|
||||
updateErr: apierrors.NewConflict(schema.GroupResource{}, "", nil),
|
||||
wantRequeue: true,
|
||||
},
|
||||
"conflict error, with finalizers": {
|
||||
withFinalizers: true,
|
||||
updateErr: apierrors.NewConflict(schema.GroupResource{}, "", nil),
|
||||
wantRequeue: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobTrackingWithFinalizers, tc.withFinalizers)()
|
||||
manager, sharedInformerFactory := newControllerFromClient(clientset, controller.NoResyncPeriodFunc)
|
||||
fakePodControl := controller.FakePodControl{}
|
||||
manager.podControl = &fakePodControl
|
||||
@@ -4204,7 +4163,6 @@ func TestEnsureJobConditions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFinalizersRemovedExpectations(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobTrackingWithFinalizers, true)()
|
||||
clientset := fake.NewSimpleClientset()
|
||||
sharedInformers := informers.NewSharedInformerFactory(clientset, controller.NoResyncPeriodFunc())
|
||||
manager := NewController(sharedInformers.Core().V1().Pods(), sharedInformers.Batch().V1().Jobs(), clientset)
|
||||
|
Reference in New Issue
Block a user