Skip to content

Commit 5e229a0

Browse files
authored
Merge pull request kubernetes#124185 from aojea/e2e_stress_remotecommand
e2e test to execute 1000 times in a container with concurrency 10
2 parents 69b648a + f0730ef commit 5e229a0

File tree

2 files changed

+104
-3
lines changed

2 files changed

+104
-3
lines changed

test/e2e/framework/pod/exec_util.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ type ExecOptions struct {
5151
// returning stdout, stderr and error. `options` allowed for
5252
// additional parameters to be passed.
5353
func ExecWithOptions(f *framework.Framework, options ExecOptions) (string, string, error) {
54+
return ExecWithOptionsContext(context.Background(), f, options)
55+
}
56+
57+
func ExecWithOptionsContext(ctx context.Context, f *framework.Framework, options ExecOptions) (string, string, error) {
5458
if !options.Quiet {
5559
framework.Logf("ExecWithOptions %+v", options)
5660
}
@@ -77,7 +81,8 @@ func ExecWithOptions(f *framework.Framework, options ExecOptions) (string, strin
7781

7882
var stdout, stderr bytes.Buffer
7983
framework.Logf("ExecWithOptions: execute(POST %s)", req.URL())
80-
err = execute("POST", req.URL(), config, options.Stdin, &stdout, &stderr, tty)
84+
err = execute(ctx, "POST", req.URL(), config, options.Stdin, &stdout, &stderr, tty)
85+
8186
if options.PreserveWhitespace {
8287
return stdout.String(), stderr.String(), err
8388
}
@@ -139,12 +144,12 @@ func ExecShellInPodWithFullOutput(ctx context.Context, f *framework.Framework, p
139144
return execCommandInPodWithFullOutput(ctx, f, podName, "/bin/sh", "-c", cmd)
140145
}
141146

142-
func execute(method string, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error {
147+
func execute(ctx context.Context, method string, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool) error {
143148
exec, err := remotecommand.NewSPDYExecutor(config, method, url)
144149
if err != nil {
145150
return err
146151
}
147-
return exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{
152+
return exec.StreamWithContext(ctx, remotecommand.StreamOptions{
148153
Stdin: stdin,
149154
Stdout: stdout,
150155
Stderr: stderr,

test/e2e/kubectl/exec.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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+
// OWNER = sig/cli
18+
19+
package kubectl
20+
21+
import (
22+
"context"
23+
"time"
24+
25+
"github.com/onsi/ginkgo/v2"
26+
v1 "k8s.io/api/core/v1"
27+
"k8s.io/kubernetes/test/e2e/framework"
28+
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
29+
admissionapi "k8s.io/pod-security-admission/api"
30+
)
31+
32+
func worker(f *framework.Framework, pod *v1.Pod, id int, jobs <-chan int, results chan<- error) {
33+
for j := range jobs {
34+
framework.Logf("Worker: %d Job: %d", id, j)
35+
func() {
36+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
37+
defer cancel()
38+
stdout, stderr, err := e2epod.ExecWithOptionsContext(ctx, f, e2epod.ExecOptions{
39+
Command: []string{"date"},
40+
Namespace: f.Namespace.Name,
41+
PodName: pod.Name,
42+
ContainerName: pod.Spec.Containers[0].Name,
43+
Stdin: nil,
44+
CaptureStdout: true,
45+
CaptureStderr: true,
46+
PreserveWhitespace: false,
47+
})
48+
if err != nil {
49+
framework.Logf("Try: %d Error: %v stdout: %s stderr: %s", j, err, stdout, stderr)
50+
}
51+
results <- err
52+
}()
53+
}
54+
}
55+
56+
var _ = SIGDescribe("Kubectl exec", func() {
57+
f := framework.NewDefaultFramework("exec")
58+
59+
f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
60+
f.It("should be able to execute 1000 times in a container", func(ctx context.Context) {
61+
const size = 1000
62+
ns := f.Namespace.Name
63+
podName := "test-exec-pod"
64+
jobs := make(chan int, size)
65+
results := make(chan error, size)
66+
67+
pod := e2epod.NewAgnhostPod(ns, podName, nil, nil, nil)
68+
pod = e2epod.NewPodClient(f).CreateSync(ctx, pod)
69+
70+
// 10 workers for 1000 executions
71+
ginkgo.By("Starting workers to exec on pod")
72+
for w := 0; w < 10; w++ {
73+
framework.Logf("Starting worker %d", w)
74+
go worker(f, pod, w, jobs, results)
75+
}
76+
for i := 0; i < size; i++ {
77+
framework.Logf("Sending job %d", i)
78+
jobs <- i
79+
}
80+
ginkgo.By("All jobs processed")
81+
close(jobs)
82+
83+
errors := []error{}
84+
for c := 0; c < size; c++ {
85+
framework.Logf("Getting results %d", c)
86+
err := <-results
87+
if err != nil {
88+
errors = append(errors, err)
89+
}
90+
}
91+
// Accept a 99% success rate to be able to handle infrastructure errors
92+
if len(errors) > (size*1)/100 {
93+
framework.Failf("Exec failed %d times with following errors : %v", len(errors), errors)
94+
}
95+
})
96+
})

0 commit comments

Comments
 (0)