Skip to content
This repository was archived by the owner on Apr 17, 2025. It is now read-only.

Commit 78037b5

Browse files
committed
Add integration tests for list resources extension
Add integration tests to list and watch resources under a tree. Add test utilities to enable killing a watch command gracefully/
1 parent 75fcae7 commit 78037b5

File tree

2 files changed

+129
-0
lines changed

2 files changed

+129
-0
lines changed

pkg/testutils/testutils.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package testutils
22

33
import (
4+
"context"
45
"fmt"
56
"io/ioutil"
67
"os"
@@ -129,6 +130,20 @@ func runShouldContainMultiple(offset int, substrs []string, seconds float64, cmd
129130
}, seconds).Should(beQuiet(), "Command: %s", cmdln)
130131
}
131132

133+
func RunShouldContainMultipleWithTimeout(substrs []string, seconds, timeout float64, cmdln ...string) {
134+
runShouldContainMultipleWithTimeout(1, substrs, seconds, timeout, cmdln...)
135+
}
136+
137+
func runShouldContainMultipleWithTimeout(offset int, substrs []string, seconds, timeout float64, cmdln ...string) {
138+
EventuallyWithOffset(offset+1, func() string {
139+
missing, err := tryRunShouldContainMultipleWithTimeout(substrs, timeout, cmdln...)
140+
if err != nil {
141+
return "failed: " + err.Error()
142+
}
143+
return missing
144+
}, seconds).Should(beQuiet(), "Command: %s", cmdln)
145+
}
146+
132147
func RunErrorShouldContain(substr string, seconds float64, cmdln ...string) {
133148
runErrorShouldContainMultiple(1, []string{substr}, seconds, cmdln...)
134149
}
@@ -153,6 +168,38 @@ func tryRunShouldContainMultiple(substrs []string, cmdln ...string) (string, err
153168
return missAny(substrs, stdout), err
154169
}
155170

171+
func tryRunShouldContainMultipleWithTimeout(substrs []string, timeout float64, cmdln ...string) (string, error) {
172+
stdout, err := runCommandWithTimeout(timeout, cmdln...)
173+
GinkgoT().Log("Output: ", stdout)
174+
return missAny(substrs, stdout), err
175+
}
176+
177+
// runCommandWithTimeout is like RunCommand, but uses a timeout context to kill the command after `timeout` seconds.
178+
// This is useful if the command is not expected to exit on its own, like for a watch command.
179+
func runCommandWithTimeout(timeout float64, cmdln ...string) (string, error) {
180+
var args []string
181+
for _, subcmdln := range cmdln {
182+
// Any arg that starts and ends in a double quote shouldn't be split further
183+
if len(subcmdln) > 2 && subcmdln[0] == '"' && subcmdln[len(subcmdln)-1] == '"' {
184+
args = append(args, subcmdln[1:len(subcmdln)-1])
185+
} else {
186+
args = append(args, strings.Split(subcmdln, " ")...)
187+
}
188+
}
189+
prefix := fmt.Sprintf("[%d] Running: ", time.Now().Unix())
190+
GinkgoT().Log(prefix, args)
191+
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
192+
defer cancel()
193+
cmd := exec.CommandContext(ctx, args[0], args[1:]...)
194+
// Work around https://github.com/kubernetes/kubectl/issues/1098#issuecomment-929743957:
195+
cmd.Env = append(os.Environ(), "KUBECTL_COMMAND_HEADERS=false")
196+
stdout, err := cmd.CombinedOutput()
197+
if err != nil && strings.Contains(err.Error(), "signal: killed") {
198+
return string(stdout), nil
199+
}
200+
return string(stdout), err
201+
}
202+
156203
// If any of the substrs are missing from teststring, returns a string of the form:
157204
// did not output the expected substring(s): <string1>, <string2>, ...
158205
// and instead output: teststring

test/e2e/list_resources_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package e2e
2+
3+
import (
4+
"time"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
8+
. "sigs.k8s.io/hierarchical-namespaces/pkg/testutils"
9+
)
10+
11+
var _ = Describe("List Resources", func() {
12+
const (
13+
parent1 = namspacePrefix + "parent1"
14+
parent2 = namspacePrefix + "parent2"
15+
child1a = namspacePrefix + "child1a"
16+
child1b = namspacePrefix + "child1b"
17+
child2a = namspacePrefix + "child2a"
18+
grandchild1ai = namspacePrefix + "grandchild1ai"
19+
)
20+
21+
BeforeEach(func() {
22+
CleanupTestNamespaces()
23+
CreateNamespace(parent1)
24+
CreateNamespace(parent2)
25+
CreateSubnamespace(child1a, parent1)
26+
CreateSubnamespace(child1b, parent1)
27+
CreateSubnamespace(child2a, parent2)
28+
CreateSubnamespace(grandchild1ai, child1a)
29+
// wait for all the descendents to be in the tree
30+
RunShouldContain(grandchild1ai, 5, "kubectl hns tree", parent1)
31+
})
32+
33+
AfterEach(func() {
34+
CleanupTestNamespaces()
35+
})
36+
37+
It("should list resources", func() {
38+
createEmptySecret(parent1, "s-parent1")
39+
createEmptySecret(child1a, "s-child1a")
40+
createEmptySecret(child1b, "s-child1b")
41+
createEmptySecret(grandchild1ai, "s-grandchild1ai")
42+
createEmptySecret(child2a, "s-child2a")
43+
44+
// verify
45+
// list resources under one tree
46+
RunShouldContainMultiple([]string{"s-parent1", "s-child1a", "s-child1b", "s-grandchild1ai"}, 5, "kubectl hns --namespace", parent1, "get secret")
47+
RunShouldNotContain("s-child2a", 5, "kubectl hns --namespace", parent1, "get secret")
48+
49+
// list resources under another tree
50+
RunShouldContain("s-child2a", 5, "kubectl hns --namespace", parent2, "get secret")
51+
RunShouldNotContainMultiple([]string{"s-parent1", "s-child1a", "s-child1b", "s-grandchild1ai"}, 5, "kubectl hns --namespace", parent2, "get secret")
52+
53+
// list all resources
54+
RunShouldContainMultiple([]string{"s-parent1", "s-child1a", "s-child1b", "s-grandchild1ai", "s-child2a"}, 5, "kubectl hns get secret --all-namespaces")
55+
})
56+
57+
It("should watch resources", func() {
58+
go func() {
59+
defer GinkgoRecover()
60+
interval := 1 * time.Second
61+
time.Sleep(interval)
62+
createEmptySecret(parent1, "s-parent1")
63+
time.Sleep(interval)
64+
createEmptySecret(child1a, "s-child1a")
65+
time.Sleep(interval)
66+
createEmptySecret(child1b, "s-child1b")
67+
time.Sleep(interval)
68+
createEmptySecret(grandchild1ai, "s-grandchild1ai")
69+
time.Sleep(interval)
70+
createEmptySecret(child2a, "s-child2a")
71+
}()
72+
73+
// verify
74+
cmd := []string{"kubectl hns --namespace", parent1, "get secret --watch"}
75+
wantStrings := []string{"s-parent1", "s-child1a", "s-child1b", "s-grandchild1ai"}
76+
RunShouldContainMultipleWithTimeout(wantStrings, 15, 15, cmd...)
77+
})
78+
})
79+
80+
func createEmptySecret(namespace, name string) {
81+
MustRun("kubectl --namespace", namespace, "create secret generic", name)
82+
}

0 commit comments

Comments
 (0)