Skip to content

Commit eb10b9b

Browse files
authored
Merge pull request kubernetes#128107 from alaypatel07/kep-4017-integration-tests
[KEP-4017]: update e2e and integration test for PodIndexLabel
2 parents e79722d + 0aa065a commit eb10b9b

File tree

6 files changed

+126
-34
lines changed

6 files changed

+126
-34
lines changed

test/e2e/apps/job.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
"k8s.io/client-go/util/retry"
4343
"k8s.io/client-go/util/workqueue"
4444
batchinternal "k8s.io/kubernetes/pkg/apis/batch"
45+
"k8s.io/kubernetes/test/e2e/feature"
4546
"k8s.io/kubernetes/test/e2e/framework"
4647
e2ejob "k8s.io/kubernetes/test/e2e/framework/job"
4748
e2enode "k8s.io/kubernetes/test/e2e/framework/node"
@@ -468,6 +469,43 @@ done`}
468469
gomega.Expect(gotIndexes).To(gomega.Equal(wantIndexes), "expected completed indexes %s, but got %s", wantIndexes, gotIndexes)
469470
})
470471

472+
/*
473+
Release: v1.32
474+
Testname: Ensure Pods of an Indexed Job get a unique index for PodIndexLabel key.
475+
Description: Create an Indexed job. Job MUST complete successfully.
476+
Ensure that created pods have completion index label.
477+
*/
478+
// TODO: once this test is stable, squash the functionality into pre-existing conformance test called "should create
479+
// pods for an Indexed job with completion indexes and specified hostname" earlier in this file.
480+
framework.It("should create pods with completion indexes for an Indexed Job", feature.PodIndexLabel, func(ctx context.Context) {
481+
parallelism := int32(2)
482+
completions := int32(4)
483+
backoffLimit := int32(6) // default value
484+
485+
ginkgo.By("Creating Indexed job")
486+
job := e2ejob.NewTestJob("succeed", "indexed-job", v1.RestartPolicyNever, parallelism, completions, nil, backoffLimit)
487+
job.Spec.CompletionMode = ptr.To(batchv1.IndexedCompletion)
488+
job, err := e2ejob.CreateJob(ctx, f.ClientSet, f.Namespace.Name, job)
489+
framework.ExpectNoError(err, "failed to create indexed job in namespace %s", f.Namespace.Name)
490+
491+
ginkgo.By("Ensuring job reaches completions")
492+
err = e2ejob.WaitForJobComplete(ctx, f.ClientSet, f.Namespace.Name, job.Name, nil, completions)
493+
framework.ExpectNoError(err, "failed to ensure job completion in namespace: %s", f.Namespace.Name)
494+
495+
ginkgo.By("Ensuring all pods have the required index labels")
496+
pods, err := e2ejob.GetJobPods(ctx, f.ClientSet, f.Namespace.Name, job.Name)
497+
framework.ExpectNoError(err, "failed to get pod list for job in namespace: %s", f.Namespace.Name)
498+
succeededIndexes := sets.NewInt()
499+
for _, pod := range pods.Items {
500+
ix, err := strconv.Atoi(pod.Labels[batchv1.JobCompletionIndexAnnotation])
501+
framework.ExpectNoError(err, "failed obtaining completion index in namespace: %s for pod: %s", pod.Namespace, pod.Name)
502+
succeededIndexes.Insert(ix)
503+
}
504+
gotIndexes := succeededIndexes.List()
505+
wantIndexes := []int{0, 1, 2, 3}
506+
gomega.Expect(gotIndexes).To(gomega.Equal(wantIndexes), "expected completed indexes in namespace: %s for job: %s", job.Namespace, job.Name)
507+
})
508+
471509
/*
472510
Testcase: Ensure that job with successPolicy succeeded when all indexes succeeded
473511
Description: Create an indexed job with successPolicy.

test/e2e/apps/statefulset.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ var _ = SIGDescribe("StatefulSet", func() {
162162
ginkgo.By("Verifying statefulset set proper service name")
163163
framework.ExpectNoError(e2estatefulset.CheckServiceName(ss, headlessSvcName))
164164

165+
ginkgo.By("checking the index label and value of all pods")
166+
framework.ExpectNoError(e2estatefulset.CheckPodIndexLabel(ctx, c, ss))
167+
165168
cmd := "echo $(hostname) | dd of=/data/hostname conv=fsync"
166169
ginkgo.By("Running " + cmd + " in all stateful pods")
167170
framework.ExpectNoError(e2estatefulset.ExecInStatefulPods(ctx, c, ss, cmd))

test/e2e/feature/feature.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,8 @@ var (
334334
// TODO: document the feature (owning SIG, when to use this feature for a test)
335335
StatefulSet = framework.WithFeature(framework.ValidFeatures.Add("StatefulSet"))
336336

337+
PodIndexLabel = framework.WithFeature(framework.ValidFeatures.Add("PodIndexLabel"))
338+
337339
// TODO: document the feature (owning SIG, when to use this feature for a test)
338340
StatefulSetStartOrdinal = framework.WithFeature(framework.ValidFeatures.Add("StatefulSetStartOrdinal"))
339341

test/e2e/framework/statefulset/rest.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
"context"
2121
"fmt"
2222
"path/filepath"
23+
"reflect"
24+
"strconv"
2325
"strings"
2426
"time"
2527

@@ -233,6 +235,26 @@ func CheckServiceName(ss *appsv1.StatefulSet, expectedServiceName string) error
233235
return nil
234236
}
235237

238+
// CheckPodIndexLabel asserts that the pods for ss have expected index label and values.
239+
func CheckPodIndexLabel(ctx context.Context, c clientset.Interface, ss *appsv1.StatefulSet) error {
240+
pods := GetPodList(ctx, c, ss)
241+
labelIndices := sets.NewInt()
242+
for _, pod := range pods.Items {
243+
ix, err := strconv.Atoi(pod.Labels[appsv1.PodIndexLabel])
244+
if err != nil {
245+
return err
246+
}
247+
labelIndices.Insert(ix)
248+
}
249+
wantIndexes := []int{0, 1, 2}
250+
gotIndexes := labelIndices.List()
251+
252+
if !reflect.DeepEqual(gotIndexes, wantIndexes) {
253+
return fmt.Errorf("pod index labels are not as expected, got: %v, want: %v", gotIndexes, wantIndexes)
254+
}
255+
return nil
256+
}
257+
236258
// ExecInStatefulPods executes cmd in all Pods in ss. If a error occurs it is returned and cmd is not execute in any subsequent Pods.
237259
func ExecInStatefulPods(ctx context.Context, c clientset.Interface, ss *appsv1.StatefulSet, cmd string) error {
238260
podList := GetPodList(ctx, c, ss)

test/integration/job/job_test.go

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4127,13 +4127,20 @@ func validateIndexedJobPods(ctx context.Context, t *testing.T, clientSet clients
41274127
for _, pod := range pods.Items {
41284128
if metav1.IsControlledBy(&pod, jobObj) {
41294129
if pod.Status.Phase == v1.PodPending || pod.Status.Phase == v1.PodRunning {
4130-
ix, err := getCompletionIndex(&pod)
4130+
annotationIx, err := getCompletionIndex(pod.Annotations)
41314131
if err != nil {
4132-
t.Errorf("Failed getting completion index for pod %s: %v", pod.Name, err)
4133-
} else {
4134-
gotActive.Insert(ix)
4132+
t.Errorf("Failed getting completion index in annotations for pod %s: %v", pod.Name, err)
41354133
}
4136-
expectedName := fmt.Sprintf("%s-%d", jobObj.Name, ix)
4134+
labelIx, err := getCompletionIndex(pod.Labels)
4135+
if err != nil {
4136+
t.Errorf("Failed getting completion index in labels for pod %s: %v", pod.Name, err)
4137+
}
4138+
if annotationIx != labelIx {
4139+
t.Errorf("Mismatch in value of annotation index: %v and label index: %v", labelIx,
4140+
annotationIx)
4141+
}
4142+
gotActive.Insert(labelIx)
4143+
expectedName := fmt.Sprintf("%s-%d", jobObj.Name, labelIx)
41374144
if diff := cmp.Equal(expectedName, pod.Spec.Hostname); !diff {
41384145
t.Errorf("Got pod hostname %s, want %s", pod.Spec.Hostname, expectedName)
41394146
}
@@ -4298,7 +4305,7 @@ func setJobPhaseForIndex(ctx context.Context, clientSet clientset.Interface, job
42984305
if p := pod.Status.Phase; !metav1.IsControlledBy(&pod, jobObj) || p == v1.PodFailed || p == v1.PodSucceeded {
42994306
continue
43004307
}
4301-
if pix, err := getCompletionIndex(&pod); err == nil && pix == ix {
4308+
if pix, err := getCompletionIndex(pod.Annotations); err == nil && pix == ix {
43024309
pod.Status.Phase = phase
43034310
if phase == v1.PodFailed || phase == v1.PodSucceeded {
43044311
pod.Status.ContainerStatuses = []v1.ContainerStatus{
@@ -4352,20 +4359,17 @@ func getJobPodsForIndex(ctx context.Context, clientSet clientset.Interface, jobO
43524359
if !filter(&pod) {
43534360
continue
43544361
}
4355-
if pix, err := getCompletionIndex(&pod); err == nil && pix == ix {
4362+
if pix, err := getCompletionIndex(pod.Annotations); err == nil && pix == ix {
43564363
result = append(result, &pod)
43574364
}
43584365
}
43594366
return result, nil
43604367
}
43614368

4362-
func getCompletionIndex(p *v1.Pod) (int, error) {
4363-
if p.Annotations == nil {
4364-
return 0, errors.New("no annotations found")
4365-
}
4366-
v, ok := p.Annotations[batchv1.JobCompletionIndexAnnotation]
4369+
func getCompletionIndex(lookupMap map[string]string) (int, error) {
4370+
v, ok := lookupMap[batchv1.JobCompletionIndexAnnotation]
43674371
if !ok {
4368-
return 0, fmt.Errorf("annotation %s not found", batchv1.JobCompletionIndexAnnotation)
4372+
return 0, fmt.Errorf("key %s not found in lookup Map", batchv1.JobCompletionIndexAnnotation)
43694373
}
43704374
return strconv.Atoi(v)
43714375
}

test/integration/statefulset/statefulset_test.go

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package statefulset
1919
import (
2020
"context"
2121
"fmt"
22+
"strconv"
2223
"testing"
2324
"time"
2425

@@ -655,42 +656,42 @@ func TestDeletingPodForRollingUpdatePartition(t *testing.T) {
655656

656657
func TestStatefulSetStartOrdinal(t *testing.T) {
657658
tests := []struct {
658-
ordinals *appsv1.StatefulSetOrdinals
659-
name string
660-
namespace string
661-
replicas int
662-
expectedPodNames []string
659+
ordinals *appsv1.StatefulSetOrdinals
660+
name string
661+
namespace string
662+
replicas int
663+
expectedPodIndexes []int
663664
}{
664665
{
665-
name: "default start ordinal, no ordinals set",
666-
namespace: "no-ordinals",
667-
replicas: 3,
668-
expectedPodNames: []string{"sts-0", "sts-1", "sts-2"},
666+
name: "default start ordinal, no ordinals set",
667+
namespace: "no-ordinals",
668+
replicas: 3,
669+
expectedPodIndexes: []int{0, 1, 2},
669670
},
670671
{
671-
name: "default start ordinal",
672-
namespace: "no-start-ordinals",
673-
ordinals: &appsv1.StatefulSetOrdinals{},
674-
replicas: 3,
675-
expectedPodNames: []string{"sts-0", "sts-1", "sts-2"},
672+
name: "default start ordinal",
673+
namespace: "no-start-ordinals",
674+
ordinals: &appsv1.StatefulSetOrdinals{},
675+
replicas: 3,
676+
expectedPodIndexes: []int{0, 1, 2},
676677
},
677678
{
678679
name: "start ordinal 4",
679680
namespace: "start-ordinal-4",
680681
ordinals: &appsv1.StatefulSetOrdinals{
681682
Start: 4,
682683
},
683-
replicas: 4,
684-
expectedPodNames: []string{"sts-4", "sts-5", "sts-6", "sts-7"},
684+
replicas: 4,
685+
expectedPodIndexes: []int{4, 5, 6, 7},
685686
},
686687
{
687688
name: "start ordinal 5",
688689
namespace: "start-ordinal-5",
689690
ordinals: &appsv1.StatefulSetOrdinals{
690691
Start: 2,
691692
},
692-
replicas: 7,
693-
expectedPodNames: []string{"sts-2", "sts-3", "sts-4", "sts-5", "sts-6", "sts-7", "sts-8"},
693+
replicas: 7,
694+
expectedPodIndexes: []int{2, 3, 4, 5, 6, 7, 8},
694695
},
695696
}
696697

@@ -719,17 +720,39 @@ func TestStatefulSetStartOrdinal(t *testing.T) {
719720
}
720721

721722
var podNames []string
723+
var podLabelIndexes []int
722724
for _, pod := range pods.Items {
723725
podNames = append(podNames, pod.Name)
726+
if idx, ok := pod.Labels[appsv1.PodIndexLabel]; !ok {
727+
t.Errorf("Expected pod index label with key: %s", appsv1.PodIndexLabel)
728+
} else {
729+
idxInt, err := strconv.Atoi(idx)
730+
if err != nil {
731+
t.Errorf("Unable to convert pod index to int, unexpected pod index: %s", idx)
732+
}
733+
podLabelIndexes = append(podLabelIndexes, idxInt)
734+
}
724735
}
725736
ignoreOrder := cmpopts.SortSlices(func(a, b string) bool {
726737
return a < b
727738
})
739+
ignoreOrderForOrdinals := cmpopts.SortSlices(func(a, b int) bool {
740+
return a < b
741+
})
742+
743+
expectedNames := []string{}
744+
for _, ord := range test.expectedPodIndexes {
745+
expectedNames = append(expectedNames, fmt.Sprintf("sts-%d", ord))
746+
}
728747

729748
// Validate all the expected pods were created.
730-
if diff := cmp.Diff(test.expectedPodNames, podNames, ignoreOrder); diff != "" {
749+
if diff := cmp.Diff(expectedNames, podNames, ignoreOrder); diff != "" {
731750
t.Errorf("Unexpected pod names: (-want +got): %v", diff)
732751
}
752+
// Validate all the expected index labels were added.
753+
if diff := cmp.Diff(test.expectedPodIndexes, podLabelIndexes, ignoreOrderForOrdinals); diff != "" {
754+
t.Errorf("Unexpected pod indices: (-want +got): %v", diff)
755+
}
733756

734757
// Scale down to 1 pod and verify it matches the first pod.
735758
scaleSTS(t, c, sts, 1)
@@ -739,8 +762,8 @@ func TestStatefulSetStartOrdinal(t *testing.T) {
739762
if len(pods.Items) != 1 {
740763
t.Errorf("len(pods) = %v, want %v", len(pods.Items), 1)
741764
}
742-
if pods.Items[0].Name != test.expectedPodNames[0] {
743-
t.Errorf("Unexpected singleton pod name: got = %v, want %v", pods.Items[0].Name, test.expectedPodNames[0])
765+
if pods.Items[0].Name != expectedNames[0] {
766+
t.Errorf("Unexpected singleton pod name: got = %v, want %v", pods.Items[0].Name, expectedNames[0])
744767
}
745768
})
746769
}

0 commit comments

Comments
 (0)