Skip to content

Commit 90ca254

Browse files
committed
test: minor refactor of TestRunChromeSandbox
This change includes some minor cleanup and refactor of the chrome sandbox test. - return error instead of calling Fatal in nested helper functions to help the control flow - use the testing logger so that logs are properly formatted - consolidate test utilities and remove unused functions
1 parent ac08914 commit 90ca254

File tree

3 files changed

+84
-137
lines changed

3 files changed

+84
-137
lines changed

test/e2e/chromesandbox_test.go

Lines changed: 67 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -20,98 +20,96 @@ import (
2020
"io"
2121
"net/http"
2222
"os"
23-
"os/exec"
24-
"strings"
2523
"testing"
2624
"time"
2725

26+
"github.com/stretchr/testify/require"
27+
corev1 "k8s.io/api/core/v1"
2828
"k8s.io/apimachinery/pkg/types"
29-
"k8s.io/klog/v2"
29+
sandboxv1alpha1 "sigs.k8s.io/agent-sandbox/api/v1alpha1"
3030
"sigs.k8s.io/agent-sandbox/test/e2e/framework"
31+
"sigs.k8s.io/agent-sandbox/test/e2e/framework/predicates"
3132
)
3233

34+
func chromeSandbox() *sandboxv1alpha1.Sandbox {
35+
sandbox := &sandboxv1alpha1.Sandbox{}
36+
sandbox.Name = "chrome-sandbox"
37+
sandbox.Spec.PodTemplate = sandboxv1alpha1.PodTemplate{
38+
Spec: corev1.PodSpec{
39+
Containers: []corev1.Container{
40+
{
41+
Name: "chrome-sandbox",
42+
// might be nice to remove the IMAGE_TAG env var so this is easier to run from IDE
43+
Image: fmt.Sprintf("kind.local/chrome-sandbox:%s", os.Getenv("IMAGE_TAG")),
44+
ImagePullPolicy: corev1.PullIfNotPresent,
45+
},
46+
},
47+
},
48+
}
49+
return sandbox
50+
}
51+
3352
// TestRunChromeSandbox tests that we can run Chrome inside a Sandbox,
3453
// it also measures how long it takes for Chrome to start serving the CDP protocol.
3554
func TestRunChromeSandbox(t *testing.T) {
36-
ctx := context.Background()
37-
ctx, cancel := context.WithCancel(ctx)
38-
defer cancel()
39-
40-
log := klog.FromContext(ctx)
41-
42-
h := framework.NewTestContext(t)
55+
tc := framework.NewTestContext(t)
4356

44-
ns := fmt.Sprintf("chrome-sandbox-test-%d", time.Now().UnixNano())
45-
h.CreateTempNamespace(ctx, ns)
57+
ns := &corev1.Namespace{}
58+
ns.Name = fmt.Sprintf("chrome-sandbox-test-%d", time.Now().UnixNano())
59+
require.NoError(t, tc.CreateWithCleanup(t.Context(), ns))
4660

4761
startTime := time.Now()
48-
49-
manifest := `
50-
kind: Sandbox
51-
apiVersion: agents.x-k8s.io/v1alpha1
52-
metadata:
53-
name: chrome-sandbox
54-
spec:
55-
podTemplate:
56-
spec:
57-
containers:
58-
- name: chrome-sandbox
59-
image: kind.local/chrome-sandbox:latest
60-
imagePullPolicy: IfNotPresent
61-
`
62-
63-
manifest = strings.ReplaceAll(manifest, ":latest", ":"+os.Getenv("IMAGE_TAG"))
64-
65-
h.Apply(ctx, ns, manifest)
66-
67-
sandboxID := types.NamespacedName{
68-
Namespace: ns,
69-
Name: "chrome-sandbox",
70-
}
71-
72-
h.WaitForSandboxReady(ctx, sandboxID)
62+
sandboxObj := chromeSandbox()
63+
sandboxObj.Namespace = ns.Name
64+
require.NoError(t, tc.CreateWithCleanup(t.Context(), sandboxObj))
65+
require.NoError(t, tc.WaitForObject(t.Context(), sandboxObj, predicates.ReadyConditionIsTrue))
7366

7467
podID := types.NamespacedName{
75-
Namespace: ns,
68+
Namespace: ns.Name,
7669
Name: "chrome-sandbox",
7770
}
78-
71+
podObj := &corev1.Pod{}
72+
podObj.Name = podID.Name
73+
podObj.Namespace = podID.Namespace
7974
// Wait for the pod to be ready
80-
{
81-
waitForPodReady := exec.CommandContext(ctx, "kubectl", "-n", ns, "wait", "pod/"+podID.Name, "--for=condition=Ready", "--timeout=60s")
82-
log.Info("waiting for pod to be ready", "command", waitForPodReady.String())
83-
waitForPodReady.Stdout = os.Stdout
84-
waitForPodReady.Stderr = os.Stderr
85-
if err := waitForPodReady.Run(); err != nil {
86-
t.Fatalf("failed to wait-for-pod-ready: %v", err)
87-
}
88-
}
75+
require.NoError(t, tc.WaitForObject(t.Context(), podObj, predicates.ReadyConditionIsTrue))
76+
// Wait for chrome to be ready
77+
require.NoError(t, waitForChromeReady(t.Context(), tc, podID))
78+
duration := time.Since(startTime)
79+
t.Logf("Test took %s", duration)
80+
}
8981

82+
func waitForChromeReady(ctx context.Context, tc *framework.TestContext, podID types.NamespacedName) error {
83+
tc.Helper()
9084
// Loop until we can query chrome for its version via the debug port
85+
pollDuration := 100 * time.Millisecond
9186
for {
92-
if ctx.Err() != nil {
93-
t.Fatalf("context cancelled")
87+
select {
88+
case <-ctx.Done():
89+
return fmt.Errorf("context cancelled")
90+
default:
91+
// We have to port-forward in the loop because port-forward exits when it sees an error
92+
// https://github.com/kubernetes/kubectl/issues/1249
93+
portForwardCtx, portForwardCancel := context.WithCancel(ctx)
94+
if err := tc.PortForward(portForwardCtx, podID, 9222, 9222); err != nil {
95+
tc.Errorf("failed to port forward: %s", err)
96+
portForwardCancel()
97+
time.Sleep(pollDuration)
98+
continue
99+
}
100+
101+
u := "http://localhost:9222/json/version"
102+
info, err := getChromeInfo(ctx, u)
103+
portForwardCancel()
104+
if err != nil {
105+
tc.Errorf("failed to get chrome info: %s", err)
106+
time.Sleep(pollDuration)
107+
continue
108+
}
109+
tc.Logf("Chrome is ready (%s). Response: %s", u, info)
110+
return nil
94111
}
95-
96-
// We have to port-forward in the loop because port-forward exits when it sees an error
97-
// https://github.com/kubernetes/kubectl/issues/1249
98-
portForwardCtx, portForwardCancel := context.WithCancel(ctx)
99-
h.PortForward(portForwardCtx, podID, 9222, 9222)
100-
101-
u := "http://localhost:9222/json/version"
102-
info, err := getChromeInfo(ctx, u)
103-
portForwardCancel()
104-
if err != nil {
105-
log.Error(err, "failed to get Chrome info")
106-
time.Sleep(100 * time.Millisecond)
107-
continue
108-
}
109-
log.Info("Chrome is ready", "url", u, "response", info)
110-
break
111112
}
112-
113-
duration := time.Since(startTime)
114-
log.Info("Test completed successfully", "duration", duration)
115113
}
116114

117115
// getChromeInfo connects to the Chrome Debug Port and retrieves the version information.

test/e2e/framework/client.go

Lines changed: 15 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"io"
2222
"os"
2323
"os/exec"
24-
"path/filepath"
2524
"strings"
2625
"testing"
2726
"time"
@@ -30,20 +29,15 @@ import (
3029
corev1 "k8s.io/api/core/v1"
3130
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
3231
k8serrors "k8s.io/apimachinery/pkg/api/errors"
33-
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
34-
"k8s.io/apimachinery/pkg/runtime/schema"
3532
"k8s.io/apimachinery/pkg/types"
36-
"k8s.io/client-go/rest"
37-
"k8s.io/klog/v2"
3833
"sigs.k8s.io/agent-sandbox/test/e2e/framework/predicates"
3934
"sigs.k8s.io/controller-runtime/pkg/client"
4035
)
4136

4237
// ClusterClient is an abstraction layer for test cases to interact with the cluster.
4338
type ClusterClient struct {
4439
*testing.T
45-
client client.Client
46-
restConfig *rest.Config
40+
client client.Client
4741
}
4842

4943
// Update an object that already exists on the cluster.
@@ -205,53 +199,18 @@ func (cl *ClusterClient) validateAgentSandboxInstallation(ctx context.Context) e
205199
return nil
206200
}
207201

208-
func (cl *ClusterClient) Apply(ctx context.Context, namespace string, manifest string) {
209-
tempDir := cl.T.TempDir()
210-
manifestPath := filepath.Join(tempDir, "manifest.yaml")
211-
if err := os.WriteFile(manifestPath, []byte(manifest), 0644); err != nil {
212-
cl.T.Fatalf("failed to write manifest file %q: %v", manifestPath, err)
213-
}
214-
cmd := exec.CommandContext(ctx, "kubectl", "apply", "-f", manifestPath, "-n", namespace)
215-
cmd.Stdout = os.Stdout
216-
cmd.Stderr = os.Stderr
217-
if err := cmd.Run(); err != nil {
218-
cl.T.Fatalf("failed to apply manifest %q: %v", manifestPath, err)
219-
}
220-
}
221-
222-
var sandboxGVK = schema.GroupVersionKind{
223-
Group: "agents.x-k8s.io",
224-
Version: "v1alpha1",
225-
Kind: "Sandbox",
226-
}
227-
228-
func (cl *ClusterClient) RESTConfig() *rest.Config {
229-
return cl.restConfig
230-
}
231-
232-
func (cl *ClusterClient) CreateTempNamespace(ctx context.Context, name string) {
233-
ns := &unstructured.Unstructured{}
234-
ns.SetAPIVersion("v1")
235-
ns.SetKind("Namespace")
236-
ns.SetName(name)
237-
238-
if err := cl.CreateWithCleanup(ctx, ns); err != nil {
239-
cl.T.Fatalf("failed to create namespace %q: %v", name, err)
240-
}
241-
}
242-
243-
func (cl *ClusterClient) PortForward(ctx context.Context, pod types.NamespacedName, localPort, remotePort int) {
244-
log := klog.FromContext(ctx)
245-
202+
func (cl *ClusterClient) PortForward(ctx context.Context, pod types.NamespacedName, localPort, remotePort int) error {
203+
cl.Helper()
246204
// Set up a port-forward to the Chrome Debug Port
247-
portForward := exec.CommandContext(ctx, "kubectl", "-n", pod.Namespace, "port-forward", "pod/"+pod.Name, fmt.Sprintf("%d:%d", localPort, remotePort))
248-
log.Info("starting port-forward", "command", portForward.String())
205+
portForward := exec.CommandContext(ctx, "kubectl", "-n", pod.Namespace,
206+
"port-forward", "pod/"+pod.Name, fmt.Sprintf("%d:%d", localPort, remotePort))
207+
cl.Logf("starting port-forward: %s", portForward.String())
249208
var stdout bytes.Buffer
250209
var stderr bytes.Buffer
251210
portForward.Stdout = io.MultiWriter(os.Stdout, &stdout)
252211
portForward.Stderr = io.MultiWriter(os.Stderr, &stderr)
253212
if err := portForward.Start(); err != nil {
254-
cl.T.Fatalf("failed to start port-forward: %v", err)
213+
return fmt.Errorf("failed to start port-forward: %w", err)
255214
}
256215

257216
stopProcess := func() {
@@ -260,18 +219,19 @@ func (cl *ClusterClient) PortForward(ctx context.Context, pod types.NamespacedNa
260219
return
261220
}
262221
}
263-
log.Info("killing port-forward")
222+
cl.Log("killing port-forward")
264223
if err := portForward.Process.Kill(); err != nil {
265-
log.Error(err, "failed to kill port-forward")
224+
cl.Errorf("failed to kill port-forward: %s", err)
266225
}
267226
}
268227
cl.T.Cleanup(stopProcess)
269228

270229
go func() {
230+
cl.Helper()
271231
if err := portForward.Wait(); err != nil {
272-
log.Error(err, "port-forward exited with error")
232+
cl.Logf("port-forward exited with error: %s", err)
273233
} else {
274-
log.Info("port-forward exited")
234+
cl.Log("port-forward exited")
275235
}
276236
}()
277237

@@ -280,26 +240,16 @@ func (cl *ClusterClient) PortForward(ctx context.Context, pod types.NamespacedNa
280240
for {
281241
if portForward.ProcessState != nil {
282242
if portForward.ProcessState.Exited() {
283-
cl.T.Fatalf("port-forward process exited unexpectedly: stdout=%q stderr=%q", stdout.String(), stderr.String())
243+
return fmt.Errorf("port-forward process exited unexpectedly: stdout=%q stderr=%q", stdout.String(), stderr.String())
284244
}
285245
}
286246

287247
// Check stdout for the "Forwarding from" message
288248
if strings.Contains(stdout.String(), "Forwarding from") {
289-
log.Info("port-forward is ready", "stdout", stdout.String(), "stderr", stderr.String())
249+
cl.Logf("port-forward is ready\nstdout: %s\nstderr: %s", stdout.String(), stderr.String())
290250
break
291251
}
292252
time.Sleep(5 * time.Millisecond)
293253
}
294-
}
295-
296-
func (cl *ClusterClient) WaitForSandboxReady(ctx context.Context, sandboxID types.NamespacedName) {
297-
sandbox := &unstructured.Unstructured{}
298-
sandbox.SetGroupVersionKind(sandboxGVK)
299-
sandbox.SetName(sandboxID.Name)
300-
sandbox.SetNamespace(sandboxID.Namespace)
301-
302-
if err := cl.WaitForObject(ctx, sandbox, predicates.ReadyConditionIsTrue); err != nil {
303-
cl.T.Fatalf("waiting for sandbox to be ready: %v", err)
304-
}
254+
return nil
305255
}

test/e2e/framework/context.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,8 @@ func NewTestContext(t *testing.T) *TestContext {
6666
t.Fatal(err)
6767
}
6868
th.ClusterClient = ClusterClient{
69-
T: t,
70-
client: cl,
71-
restConfig: restCfg,
69+
T: t,
70+
client: cl,
7271
}
7372
t.Cleanup(func() {
7473
t.Helper()

0 commit comments

Comments
 (0)