Skip to content

Commit 81be2f9

Browse files
committed
Add benchmark workflows
1 parent 73130b2 commit 81be2f9

File tree

7 files changed

+355
-9
lines changed

7 files changed

+355
-9
lines changed

.github/workflows/benchmark.yaml

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Benchmark
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- pkg/**/*
7+
- cmd/**/*
8+
- test/**/*
9+
- hack/**/*
10+
- kustomize/**/*
11+
- go.mod
12+
- .github/workflows/benchmark.yaml
13+
- '!hack/releases-helm-chart.sh'
14+
push:
15+
paths:
16+
- pkg/**/*
17+
- cmd/**/*
18+
- test/**/*
19+
- hack/**/*
20+
- kustomize/**/*
21+
- go.mod
22+
- .github/workflows/benchmark.yaml
23+
- '!hack/releases-helm-chart.sh'
24+
25+
env:
26+
CGO_ENABLED: "0"
27+
GO_VERSION: "1.23.0"
28+
29+
jobs:
30+
benchmark:
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@v4
34+
- name: Set up Go
35+
uses: actions/setup-go@v5
36+
with:
37+
go-version: ${{ env.GO_VERSION }}
38+
39+
- name: Test Benchmark
40+
shell: bash
41+
run: |
42+
./hack/e2e-test.sh e2e/kwokctl/benchmark
43+
44+
- name: Test Benchmark Hack
45+
shell: bash
46+
run: |
47+
./hack/e2e-test.sh e2e/kwokctl/benchmark-hack
48+
49+
- name: Upload logs
50+
uses: actions/upload-artifact@v4
51+
if: failure()
52+
with:
53+
name: kwok-logs-benchmark
54+
path: ${{ github.workspace }}/logs

.github/workflows/test.yaml

-6
Original file line numberDiff line numberDiff line change
@@ -270,12 +270,6 @@ jobs:
270270
fi
271271
./hack/e2e-test.sh kwokctl/kwokctl_${{ matrix.kwokctl-runtime }}
272272
273-
- name: Test Benchmark
274-
if: ${{ matrix.os == 'ubuntu-latest' && matrix.kwokctl-runtime == 'binary' }}
275-
shell: bash
276-
run: |
277-
./hack/e2e-test.sh e2e/kwokctl/benchmark
278-
279273
- name: Test Auto Detect
280274
if: ${{ matrix.kwokctl-runtime == 'binary' }}
281275
shell: bash

test/e2e/benchmark.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ func scaleCreateNode(ctx context.Context, t *testing.T, kwokctlPath string, name
120120
func CaseBenchmark(kwokctlPath, clusterName string) *features.FeatureBuilder {
121121
return features.New("Benchmark").
122122
Assess("Create nodes", func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context {
123-
ctx0, cancel := context.WithTimeout(ctx, 120*time.Second)
123+
ctx0, cancel := context.WithTimeout(ctx, 180*time.Second)
124124
defer cancel()
125125

126-
err := scaleCreateNode(ctx0, t, kwokctlPath, clusterName, 2000)
126+
err := scaleCreateNode(ctx0, t, kwokctlPath, clusterName, 5000)
127127
if err != nil {
128128
t.Fatal(err)
129129
}
@@ -133,7 +133,7 @@ func CaseBenchmark(kwokctlPath, clusterName string) *features.FeatureBuilder {
133133
ctx0, cancel := context.WithTimeout(ctx, 240*time.Second)
134134
defer cancel()
135135

136-
err := scaleCreatePod(ctx0, t, kwokctlPath, clusterName, 5000)
136+
err := scaleCreatePod(ctx0, t, kwokctlPath, clusterName, 10000)
137137
if err != nil {
138138
t.Fatal(err)
139139
}

test/e2e/benchmark_hack.go

+196
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package e2e
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"io"
23+
"os/exec"
24+
"testing"
25+
"time"
26+
27+
"sigs.k8s.io/e2e-framework/pkg/envconf"
28+
"sigs.k8s.io/e2e-framework/pkg/features"
29+
)
30+
31+
func readerPodYaml(size int) io.Reader {
32+
r, w := io.Pipe()
33+
go func() {
34+
defer w.Close()
35+
for i := 0; i < size; i++ {
36+
_, _ = fmt.Fprintf(w, podYaml, i, i)
37+
}
38+
}()
39+
return r
40+
}
41+
42+
var podYaml = `
43+
apiVersion: v1
44+
kind: Pod
45+
metadata:
46+
name: pod-%d
47+
namespace: default
48+
uid: 00000000-0000-0000-0001-%012d
49+
spec:
50+
containers:
51+
- image: busybox
52+
name: container-0
53+
nodeName: node-0
54+
---
55+
`
56+
57+
func scaleCreatePodWithHack(ctx context.Context, t *testing.T, kwokctlPath string, name string, size int) error {
58+
scaleCmd := exec.CommandContext(ctx, kwokctlPath, "--name", name, "hack", "put", "--path", "-")
59+
scaleCmd.Stdin = readerPodYaml(size)
60+
if err := scaleCmd.Start(); err != nil {
61+
return fmt.Errorf("failed to start scale command: %w", err)
62+
}
63+
64+
if err := waitResource(ctx, t, kwokctlPath, name, "Pod", "Running", size, 5, 10); err != nil {
65+
return fmt.Errorf("failed to wait for resource: %w", err)
66+
}
67+
return nil
68+
}
69+
70+
func readerPodDeleteYaml(size int) io.Reader {
71+
r, w := io.Pipe()
72+
go func() {
73+
now := time.Now().UTC().Format(time.RFC3339)
74+
defer w.Close()
75+
for i := 0; i < size; i++ {
76+
_, _ = fmt.Fprintf(w, podDeleteYaml, i, i, now)
77+
}
78+
}()
79+
return r
80+
}
81+
82+
var podDeleteYaml = `
83+
apiVersion: v1
84+
kind: Pod
85+
metadata:
86+
name: pod-%d
87+
namespace: default
88+
uid: 00000000-0000-0000-0001-%012d
89+
deletionTimestamp: %s
90+
spec:
91+
containers:
92+
- image: busybox
93+
name: container-0
94+
nodeName: node-0
95+
---
96+
`
97+
98+
func scaleDeletePodWithHack(ctx context.Context, t *testing.T, kwokctlPath string, name string, size int) error {
99+
scaleCmd := exec.CommandContext(ctx, kwokctlPath, "--name", name, "hack", "put", "--path", "-")
100+
scaleCmd.Stdin = readerPodDeleteYaml(size)
101+
if err := scaleCmd.Start(); err != nil {
102+
return fmt.Errorf("failed to start scale command: %w", err)
103+
}
104+
105+
if err := waitResource(ctx, t, kwokctlPath, name, "Pod", "Running", 0, 5, 10); err != nil {
106+
return fmt.Errorf("failed to wait for resource: %w", err)
107+
}
108+
return nil
109+
}
110+
111+
func readerNodeYaml(size int) io.Reader {
112+
r, w := io.Pipe()
113+
go func() {
114+
defer w.Close()
115+
for i := 0; i < size; i++ {
116+
_, _ = fmt.Fprintf(w, nodeYaml, i, i)
117+
}
118+
}()
119+
return r
120+
}
121+
122+
var nodeYaml = `
123+
apiVersion: v1
124+
kind: Node
125+
metadata:
126+
annotations:
127+
kwok.x-k8s.io/node: fake
128+
node.alpha.kubernetes.io/ttl: "0"
129+
labels:
130+
beta.kubernetes.io/arch: amd64
131+
beta.kubernetes.io/os: linux
132+
kubernetes.io/arch: amd64
133+
kubernetes.io/os: linux
134+
kubernetes.io/role: agent
135+
node-role.kubernetes.io/agent: ""
136+
type: kwok
137+
name: node-%d
138+
uid: 00000000-0000-0000-0000-%012d
139+
status:
140+
allocatable:
141+
cpu: "32"
142+
memory: 256Gi
143+
pods: "110"
144+
capacity:
145+
cpu: "32"
146+
memory: 256Gi
147+
pods: "110"
148+
---
149+
`
150+
151+
func scaleCreateNodeWithHack(ctx context.Context, t *testing.T, kwokctlPath string, name string, size int) error {
152+
scaleCmd := exec.CommandContext(ctx, kwokctlPath, "--name", name, "hack", "put", "--path", "-")
153+
scaleCmd.Stdin = readerNodeYaml(size)
154+
if err := scaleCmd.Start(); err != nil {
155+
return fmt.Errorf("failed to start scale command: %w", err)
156+
}
157+
158+
if err := waitResource(ctx, t, kwokctlPath, name, "Node", "Ready", size, 10, 10); err != nil {
159+
return fmt.Errorf("failed to wait for resource: %w", err)
160+
}
161+
return nil
162+
}
163+
164+
func CaseBenchmarkWithHack(kwokctlPath, clusterName string) *features.FeatureBuilder {
165+
return features.New("Benchmark").
166+
Assess("Create nodes", func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context {
167+
ctx0, cancel := context.WithTimeout(ctx, 180*time.Second)
168+
defer cancel()
169+
170+
err := scaleCreateNodeWithHack(ctx0, t, kwokctlPath, clusterName, 5000)
171+
if err != nil {
172+
t.Fatal(err)
173+
}
174+
return ctx
175+
}).
176+
Assess("Create pods", func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context {
177+
ctx0, cancel := context.WithTimeout(ctx, 240*time.Second)
178+
defer cancel()
179+
180+
err := scaleCreatePodWithHack(ctx0, t, kwokctlPath, clusterName, 10000)
181+
if err != nil {
182+
t.Fatal(err)
183+
}
184+
return ctx
185+
}).
186+
Assess("Delete pods", func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context {
187+
ctx0, cancel := context.WithTimeout(ctx, 240*time.Second)
188+
defer cancel()
189+
190+
err := scaleDeletePodWithHack(ctx0, t, kwokctlPath, clusterName, 10000)
191+
if err != nil {
192+
t.Fatal(err)
193+
}
194+
return ctx
195+
})
196+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package benchmark_hack_test
18+
19+
import (
20+
"testing"
21+
22+
"sigs.k8s.io/kwok/test/e2e"
23+
)
24+
25+
func TestBenchmarkWithHack(t *testing.T) {
26+
f0 := e2e.CaseBenchmarkWithHack(kwokctlPath, clusterName).
27+
Feature()
28+
testEnv.Test(t, f0)
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package benchmark_hack_test is a test benchmarking environment for kwok.
18+
package benchmark_hack_test
19+
20+
import (
21+
"os"
22+
"runtime"
23+
"testing"
24+
25+
"sigs.k8s.io/e2e-framework/pkg/env"
26+
"sigs.k8s.io/e2e-framework/pkg/envconf"
27+
"sigs.k8s.io/e2e-framework/support/kwok"
28+
29+
"sigs.k8s.io/kwok/pkg/consts"
30+
"sigs.k8s.io/kwok/pkg/utils/path"
31+
"sigs.k8s.io/kwok/test/e2e/helper"
32+
)
33+
34+
var (
35+
runtimeEnv = consts.RuntimeTypeBinary
36+
testEnv env.Environment
37+
pwd = os.Getenv("PWD")
38+
rootDir = path.Join(pwd, "../../../..")
39+
logsDir = path.Join(rootDir, "logs")
40+
clusterName = envconf.RandomName("kwok-e2e-benchmark-hack", 24)
41+
kwokPath = path.Join(rootDir, "bin", runtime.GOOS, runtime.GOARCH, "kwok"+helper.BinSuffix)
42+
kwokctlPath = path.Join(rootDir, "bin", runtime.GOOS, runtime.GOARCH, "kwokctl"+helper.BinSuffix)
43+
baseArgs = []string{
44+
"--kwok-controller-binary=" + kwokPath,
45+
"--runtime=" + runtimeEnv,
46+
"--wait=15m",
47+
"--disable-kube-scheduler",
48+
"--disable-qps-limits",
49+
}
50+
)
51+
52+
func init() {
53+
_ = os.Setenv("KWOK_WORKDIR", path.Join(rootDir, "workdir"))
54+
}
55+
56+
func TestMain(m *testing.M) {
57+
testEnv = helper.Environment()
58+
59+
k := kwok.NewProvider().
60+
WithName(clusterName).
61+
WithPath(kwokctlPath)
62+
testEnv.Setup(
63+
helper.BuildKwokBinary(rootDir),
64+
helper.BuildKwokctlBinary(rootDir),
65+
helper.CreateCluster(k, baseArgs...),
66+
)
67+
testEnv.Finish(
68+
helper.ExportLogs(k, logsDir),
69+
helper.DestroyCluster(k),
70+
)
71+
os.Exit(testEnv.Run(m))
72+
}

test/e2e/kwokctl/benchmark/main_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ var (
4545
"--runtime=" + runtimeEnv,
4646
"--wait=15m",
4747
"--disable-kube-scheduler",
48+
"--disable-qps-limits",
4849
}
4950
)
5051

0 commit comments

Comments
 (0)