Skip to content

Commit f19cae4

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 f19cae4

File tree

2 files changed

+212
-0
lines changed

2 files changed

+212
-0
lines changed

test/cases/workload/main_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
"time"
14+
15+
"sigs.k8s.io/e2e-framework/pkg/env"
16+
"sigs.k8s.io/e2e-framework/pkg/envconf"
17+
)
18+
19+
const (
20+
defaultWorkloadTestTimeout = 10 * time.Minute
21+
)
22+
23+
var (
24+
testenv env.Environment
25+
workloadTestCommand *string
26+
workloadTestImage *string
27+
workloadTestName *string
28+
workloadTestResources *string
29+
workloadTestTimeout *time.Duration
30+
)
31+
32+
func TestMain(m *testing.M) {
33+
workloadTestCommand = flag.String("workloadTestCommand", "", "command for workload test")
34+
workloadTestImage = flag.String("workloadTestImage", "", "image for workload test")
35+
workloadTestName = flag.String("workloadTestName", "workload-test", "name for workload test")
36+
workloadTestResources = flag.String("workloadTestResources", "", "JSON map of resources for workload test (e.g., '{\"nvidia.com/gpu\": \"1\"}')")
37+
workloadTestTimeout = flag.Duration("workloadTestTimeout", defaultWorkloadTestTimeout, fmt.Sprintf("timeout for workload test (default: %s)", defaultWorkloadTestTimeout))
38+
cfg, err := envconf.NewFromFlags()
39+
if err != nil {
40+
log.Fatalf("failed to initialize test environment: %v", err)
41+
}
42+
testenv = env.NewWithConfig(cfg)
43+
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
44+
defer cancel()
45+
testenv = testenv.WithContext(ctx)
46+
47+
testenv.Setup(func(ctx context.Context, config *envconf.Config) (context.Context, error) {
48+
log.Println("Starting workload test suite...")
49+
return ctx, nil
50+
})
51+
52+
os.Exit(testenv.Run(m))
53+
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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, timeout time.Duration) *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: ptr.Int64(int64(timeout.Seconds())),
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+
if err := json.Unmarshal([]byte(resourcesJSON), &resources); err != nil {
73+
return nil, err
74+
}
75+
for name, qty := range resources {
76+
if q, err := resource.ParseQuantity(qty); err != nil || q.IsZero() {
77+
delete(resources, name)
78+
}
79+
}
80+
return resources, nil
81+
}
82+
83+
func TestWorkload(t *testing.T) {
84+
name := ptr.ToString(workloadTestName)
85+
image := ptr.ToString(workloadTestImage)
86+
command := ptr.ToString(workloadTestCommand)
87+
timeout := ptr.ToDuration(workloadTestTimeout)
88+
89+
if name == "" {
90+
t.Fatal("workloadTestName must be set to run the test")
91+
}
92+
if image == "" {
93+
t.Fatal("workloadTestImage must be set to run the test")
94+
}
95+
96+
resources, err := parseResources(ptr.ToString(workloadTestResources))
97+
if err != nil {
98+
t.Fatalf("Failed to parse workloadTestResources: %v", err)
99+
}
100+
101+
feature := features.New(name).WithLabel("suite", "workload")
102+
if _, ok := resources["aws.amazon.com/neuron"]; ok {
103+
feature = feature.WithLabel("hardware", "neuron")
104+
}
105+
if _, ok := resources["nvidia.com/gpu"]; ok {
106+
feature = feature.WithLabel("hardware", "gpu")
107+
}
108+
109+
workload := feature.Setup(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
110+
job := createWorkloadJob(name, image, command, resources, timeout)
111+
if len(resources) > 0 {
112+
t.Logf("Creating %s job with resources: %v", name, resources)
113+
} else {
114+
t.Logf("Creating %s job", name)
115+
}
116+
if err := cfg.Client().Resources().Create(ctx, job); err != nil {
117+
t.Fatal(err)
118+
}
119+
t.Logf("%s job created successfully", name)
120+
return ctx
121+
}).
122+
Assess("Job succeeds", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
123+
job := &batchv1.Job{
124+
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: corev1.NamespaceDefault},
125+
}
126+
t.Logf("Waiting for %s job to complete", name)
127+
err := wait.For(fwext.NewConditionExtension(cfg.Client().Resources()).JobSucceeded(job),
128+
wait.WithContext(ctx),
129+
wait.WithTimeout(timeout),
130+
)
131+
if err != nil {
132+
t.Fatal(err)
133+
}
134+
return ctx
135+
}).
136+
Teardown(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
137+
log, err := fwext.GetJobLogs(cfg.Client().RESTConfig(), &batchv1.Job{
138+
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: corev1.NamespaceDefault},
139+
})
140+
if err != nil {
141+
t.Error(err)
142+
}
143+
t.Logf("Test log for %s:", name)
144+
t.Log(log)
145+
job := &batchv1.Job{
146+
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: corev1.NamespaceDefault},
147+
}
148+
if err := cfg.Client().Resources().Delete(ctx, job, func(do *metav1.DeleteOptions) {
149+
policy := metav1.DeletePropagationBackground
150+
do.PropagationPolicy = &policy
151+
}); err != nil {
152+
t.Error(err)
153+
}
154+
return ctx
155+
}).
156+
Feature()
157+
158+
testenv.Test(t, workload)
159+
}

0 commit comments

Comments
 (0)