Skip to content

Commit 63fe663

Browse files
committed
feat: add workload test case for external tests
Signed-off-by: Patrick J.P. Culp <jpculp@amazon.com>
1 parent 78811ac commit 63fe663

File tree

2 files changed

+204
-0
lines changed

2 files changed

+204
-0
lines changed

test/cases/workload/main_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//go:build e2e
2+
3+
package workload
4+
5+
import (
6+
"context"
7+
"flag"
8+
"fmt"
9+
"log"
10+
"os"
11+
"os/signal"
12+
"testing"
13+
14+
"sigs.k8s.io/e2e-framework/pkg/env"
15+
"sigs.k8s.io/e2e-framework/pkg/envconf"
16+
)
17+
18+
const (
19+
defaultWorkloadTestTimeoutMinutes = 10
20+
)
21+
22+
var (
23+
testenv env.Environment
24+
workloadTestCommand *string
25+
workloadTestImage *string
26+
workloadTestName *string
27+
workloadTestResources *string
28+
workloadTestTimeout *int
29+
)
30+
31+
func TestMain(m *testing.M) {
32+
workloadTestCommand = flag.String("workloadTestCommand", "", "command for workload test")
33+
workloadTestImage = flag.String("workloadTestImage", "", "image for workload test")
34+
workloadTestName = flag.String("workloadTestName", "workload-test", "name for workload test")
35+
workloadTestResources = flag.String("workloadTestResources", "", "JSON map of resources for workload test (e.g., '{\"nvidia.com/gpu\": \"1\"}')")
36+
workloadTestTimeout = flag.Int("workloadTestTimeout", defaultWorkloadTestTimeoutMinutes, fmt.Sprintf("timeout in minutes for workload test (default: %d)", defaultWorkloadTestTimeoutMinutes))
37+
cfg, err := envconf.NewFromFlags()
38+
if err != nil {
39+
log.Fatalf("failed to initialize test environment: %v", err)
40+
}
41+
testenv = env.NewWithConfig(cfg)
42+
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
43+
defer cancel()
44+
testenv = testenv.WithContext(ctx)
45+
46+
testenv.Setup(func(ctx context.Context, config *envconf.Config) (context.Context, error) {
47+
log.Println("Starting workload test suite...")
48+
return ctx, nil
49+
})
50+
51+
os.Exit(testenv.Run(m))
52+
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
//go:build e2e
2+
3+
package workload
4+
5+
import (
6+
"context"
7+
"encoding/json"
8+
"strings"
9+
"testing"
10+
"time"
11+
12+
fwext "github.com/aws/aws-k8s-tester/internal/e2e"
13+
"github.com/aws/smithy-go/ptr"
14+
batchv1 "k8s.io/api/batch/v1"
15+
corev1 "k8s.io/api/core/v1"
16+
"k8s.io/apimachinery/pkg/api/resource"
17+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18+
"sigs.k8s.io/e2e-framework/klient/wait"
19+
"sigs.k8s.io/e2e-framework/pkg/envconf"
20+
"sigs.k8s.io/e2e-framework/pkg/features"
21+
)
22+
23+
func createWorkloadJob(name, image, command string, resources map[string]string, timeoutSeconds int64) *batchv1.Job {
24+
job := &batchv1.Job{
25+
ObjectMeta: metav1.ObjectMeta{
26+
Name: name,
27+
Namespace: corev1.NamespaceDefault,
28+
Labels: map[string]string{"app": name},
29+
},
30+
Spec: batchv1.JobSpec{
31+
BackoffLimit: ptr.Int32(4),
32+
Template: corev1.PodTemplateSpec{
33+
ObjectMeta: metav1.ObjectMeta{
34+
Labels: map[string]string{"app": name},
35+
},
36+
Spec: corev1.PodSpec{
37+
RestartPolicy: corev1.RestartPolicyNever,
38+
ActiveDeadlineSeconds: &timeoutSeconds,
39+
Containers: []corev1.Container{
40+
{
41+
Name: name,
42+
Image: image,
43+
Args: strings.Fields(command),
44+
ImagePullPolicy: corev1.PullAlways,
45+
Resources: buildResourceRequirements(resources),
46+
},
47+
},
48+
},
49+
},
50+
},
51+
}
52+
53+
return job
54+
}
55+
56+
func buildResourceRequirements(resources map[string]string) corev1.ResourceRequirements {
57+
if len(resources) == 0 {
58+
return corev1.ResourceRequirements{}
59+
}
60+
rl := make(corev1.ResourceList)
61+
for name, qty := range resources {
62+
rl[corev1.ResourceName(name)] = resource.MustParse(qty)
63+
}
64+
return corev1.ResourceRequirements{Limits: rl, Requests: rl}
65+
}
66+
67+
func parseResources(resourcesJSON string) (map[string]string, error) {
68+
if resourcesJSON == "" {
69+
return nil, nil
70+
}
71+
var resources map[string]string
72+
err := json.Unmarshal([]byte(resourcesJSON), &resources)
73+
return resources, err
74+
}
75+
76+
func TestWorkload(t *testing.T) {
77+
name := ptr.ToString(workloadTestName)
78+
image := ptr.ToString(workloadTestImage)
79+
command := ptr.ToString(workloadTestCommand)
80+
timeoutMinutes := ptr.ToInt(workloadTestTimeout)
81+
82+
if name == "" {
83+
t.Fatal("workloadTestName must be set to run the test")
84+
}
85+
if image == "" {
86+
t.Fatal("workloadTestImage must be set to run the test")
87+
}
88+
89+
resources, err := parseResources(ptr.ToString(workloadTestResources))
90+
if err != nil {
91+
t.Fatalf("Failed to parse workloadTestResources: %v", err)
92+
}
93+
94+
feature := features.New(name).WithLabel("suite", "workload")
95+
if _, ok := resources["aws.amazon.com/neuron"]; ok {
96+
feature = feature.WithLabel("hardware", "neuron")
97+
}
98+
if _, ok := resources["nvidia.com/gpu"]; ok {
99+
feature = feature.WithLabel("hardware", "gpu")
100+
}
101+
102+
workload := feature.Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
103+
job := createWorkloadJob(name, image, command, resources, int64(timeoutMinutes*60))
104+
if len(resources) > 0 {
105+
t.Logf("Creating %s job with resources: %v", name, resources)
106+
} else {
107+
t.Logf("Creating %s job", name)
108+
}
109+
if err := cfg.Client().Resources().Create(ctx, job); err != nil {
110+
t.Fatal(err)
111+
}
112+
t.Logf("%s job created successfully", name)
113+
return ctx
114+
}).
115+
Assess("Job succeeds", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
116+
job := &batchv1.Job{
117+
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: corev1.NamespaceDefault},
118+
}
119+
t.Logf("Waiting for %s job to complete", name)
120+
err := wait.For(fwext.NewConditionExtension(cfg.Client().Resources()).JobSucceeded(job),
121+
wait.WithContext(ctx),
122+
wait.WithTimeout(time.Duration(timeoutMinutes)*time.Minute),
123+
)
124+
if err != nil {
125+
t.Fatal(err)
126+
}
127+
return ctx
128+
}).
129+
Teardown(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
130+
log, err := fwext.GetJobLogs(cfg.Client().RESTConfig(), &batchv1.Job{
131+
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: corev1.NamespaceDefault},
132+
})
133+
if err != nil {
134+
t.Error(err)
135+
}
136+
t.Logf("Test log for %s:", name)
137+
t.Log(log)
138+
job := &batchv1.Job{
139+
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: corev1.NamespaceDefault},
140+
}
141+
if err := cfg.Client().Resources().Delete(ctx, job, func(do *metav1.DeleteOptions) {
142+
policy := metav1.DeletePropagationBackground
143+
do.PropagationPolicy = &policy
144+
}); err != nil {
145+
t.Error(err)
146+
}
147+
return ctx
148+
}).
149+
Feature()
150+
151+
testenv.Test(t, workload)
152+
}

0 commit comments

Comments
 (0)