Skip to content

Commit fff7c35

Browse files
committed
tests: add e2e test
1 parent c681431 commit fff7c35

File tree

1 file changed

+78
-8
lines changed

1 file changed

+78
-8
lines changed

operator/e2e/e2e_test.go

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,46 @@ func TestE2E(t *testing.T) {
7676
name: "random-yaml-nonroot",
7777
f: func(t *testing.T) {
7878
t.Parallel()
79-
79+
// 1. Test `WorkspaceReclaimPolicy` is unset.
8080
cmd := exec.Command("kubectl", "apply", "-f", "e2e/testdata/random-yaml-nonroot")
8181
require.NoError(t, run(cmd))
8282
dumpLogs(t, "random-yaml-nonroot", "pod/random-yaml-nonroot-workspace-0")
8383

84-
// _, err := waitFor[pulumiv1.Stack]("stacks/random-yaml-nonroot", "random-yaml-nonroot", "condition=Ready", 5*time.Minute)
85-
// assert.NoError(t, err) // Failing on CI
84+
_, err := waitFor[pulumiv1.Stack]("stacks/random-yaml-nonroot", "random-yaml-nonroot", 5*time.Minute, "condition=Ready")
85+
assert.NoError(t, err)
86+
87+
// Ensure that the workspace pod was not deleted after successful Stack reconciliation.
88+
found, err := foundEvent("Pod", "random-yaml-nonroot-workspace-0", "random-yaml-nonroot", "Killing")
89+
assert.NoError(t, err)
90+
assert.False(t, found)
91+
92+
// 2. Test `WorkspaceReclaimPolicy` is set to `Delete`.
93+
// Update the Stack spec to set the `WorkspaceReclaimPolicy` to `Delete`.
94+
cmd = exec.Command("kubectl", "patch", "stacks", "--namespace", "random-yaml-nonroot", "random-yaml-nonroot", "--type=merge", "-p", `{"spec":{"workspaceReclaimPolicy":"Delete"}}`)
95+
require.NoError(t, run(cmd))
96+
97+
// Wait for the Stack to be reconciled, and observedGeneration to be updated.
98+
_, err = waitFor[pulumiv1.Stack](
99+
"stacks/random-yaml-nonroot",
100+
"random-yaml-nonroot",
101+
5*time.Minute,
102+
"condition=Ready",
103+
"jsonpath={.status.observedGeneration}=3")
104+
assert.NoError(t, err)
105+
106+
// Ensure that the workspace pod is now deleted after successful Stack reconciliation.
107+
retryUntil(t, 30*time.Second, true, func() bool {
108+
found, err = foundEvent("Pod", "random-yaml-nonroot-workspace-0", "random-yaml-nonroot", "Killing")
109+
assert.NoError(t, err)
110+
return found
111+
})
112+
113+
if t.Failed() {
114+
cmd := exec.Command("kubectl", "get", "pods", "-A")
115+
out, err := cmd.CombinedOutput()
116+
assert.NoError(t, err)
117+
t.Log(string(out))
118+
}
86119
},
87120
},
88121
{
@@ -92,12 +125,11 @@ func TestE2E(t *testing.T) {
92125
if os.Getenv("PULUMI_BOT_TOKEN") == "" {
93126
t.Skip("missing PULUMI_BOT_TOKEN")
94127
}
95-
96128
cmd := exec.Command("bash", "-c", "envsubst < e2e/testdata/git-auth-nonroot/* | kubectl apply -f -")
97129
require.NoError(t, run(cmd))
98130
dumpLogs(t, "git-auth-nonroot", "pod/git-auth-nonroot-workspace-0")
99131

100-
stack, err := waitFor[pulumiv1.Stack]("stacks/git-auth-nonroot", "git-auth-nonroot", "condition=Ready", 5*time.Minute)
132+
stack, err := waitFor[pulumiv1.Stack]("stacks/git-auth-nonroot", "git-auth-nonroot", 5*time.Minute, "condition=Ready")
101133
assert.NoError(t, err)
102134

103135
assert.Equal(t, `"[secret]"`, string(stack.Status.Outputs["secretOutput"].Raw))
@@ -113,7 +145,7 @@ func TestE2E(t *testing.T) {
113145
require.NoError(t, run(cmd))
114146
dumpLogs(t, "targets", "pod/targets-workspace-0")
115147

116-
stack, err := waitFor[pulumiv1.Stack]("stacks/targets", "targets", "condition=Ready", 5*time.Minute)
148+
stack, err := waitFor[pulumiv1.Stack]("stacks/targets", "targets", 5*time.Minute, "condition=Ready")
117149
assert.NoError(t, err)
118150

119151
assert.Contains(t, stack.Status.Outputs, "targeted")
@@ -127,6 +159,19 @@ func TestE2E(t *testing.T) {
127159
}
128160
}
129161

162+
// retryUntil retries the provided function until it returns the required condition or the deadline is reached.
163+
func retryUntil(t *testing.T, d time.Duration, condition bool, f func() bool) {
164+
t.Helper()
165+
deadline := time.Now().Add(d)
166+
for time.Now().Before(deadline) {
167+
if f() == condition {
168+
return
169+
}
170+
time.Sleep(1 * time.Second)
171+
}
172+
t.Fatalf("timed out waiting for condition")
173+
}
174+
130175
// dumpLogs prints logs if the test fails.
131176
func dumpLogs(t *testing.T, namespace, name string) {
132177
t.Cleanup(func() {
@@ -141,13 +186,22 @@ func dumpLogs(t *testing.T, namespace, name string) {
141186
})
142187
}
143188

144-
func waitFor[T any](name, namespace, condition string, d time.Duration) (*T, error) {
189+
func waitFor[T any](name, namespace string, d time.Duration, conditions ...string) (*T, error) {
190+
if len(conditions) == 0 {
191+
return nil, fmt.Errorf("no conditions provided")
192+
}
193+
145194
cmd := exec.Command("kubectl", "wait", name,
146195
"-n", namespace,
147-
"--for", condition,
196+
"--for", conditions[0],
148197
fmt.Sprintf("--timeout=%ds", int(d.Seconds())),
149198
"--output=yaml",
150199
)
200+
// Add additional conditions if provided.
201+
for _, condition := range conditions[1:] {
202+
cmd.Args = append(cmd.Args, "--for", condition)
203+
}
204+
151205
err := run(cmd)
152206
if err != nil {
153207
return nil, err
@@ -163,6 +217,22 @@ func waitFor[T any](name, namespace, condition string, d time.Duration) (*T, err
163217
return &obj, nil
164218
}
165219

220+
// foundEvent checks if a Kubernetes event with the given kind, name, namespace, and reason exists.
221+
func foundEvent(kind, name, namespace, reason string) (bool, error) {
222+
cmd := exec.Command("kubectl", "get", "events",
223+
"-n", namespace,
224+
"--field-selector", fmt.Sprintf("involvedObject.kind=%s,involvedObject.name=%s,reason=%s", kind, name, reason),
225+
"--output=name",
226+
)
227+
err := run(cmd)
228+
if err != nil {
229+
return false, err
230+
}
231+
232+
buf, _ := cmd.Stdout.(*bytes.Buffer)
233+
return strings.Contains(buf.String(), "event/"+name), nil
234+
}
235+
166236
// run executes the provided command within this context
167237
func run(cmd *exec.Cmd) error {
168238
command := strings.Join(cmd.Args, " ")

0 commit comments

Comments
 (0)