Skip to content

Commit b56b8ff

Browse files
authored
🌱 Less noise for unit-test (#1732)
1 parent d94224a commit b56b8ff

File tree

4 files changed

+180
-33
lines changed

4 files changed

+180
-33
lines changed

docs/caph/04-developers/01-development-guide.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,25 @@ With `make test`, all unit tests are triggered. If they fail out of nowhere, the
8080

8181
With `make generate`, new CRDs are generated. This is necessary if you change the API.
8282

83+
## Running unit-tests locally
84+
85+
Developing unit-tests (which do not need a running cluster) are much faster. We recommend to do
86+
that.
87+
88+
A common way to run one particular unit-test is like this:
89+
90+
```console
91+
reset; ginkgo run --focus "foo" ./controllers/... | ./hack/filter-caph-controller-manager-logs.py -
92+
```
93+
94+
Explanation:
95+
96+
- `reset`: Reset the terminal so you can scroll back to the first line of output easily.
97+
- `ginkgo run --focus "foo" ./controllers/...`: Run tests in the controllers directory, but only those
98+
whose `It("...")` contains "foo".
99+
- `./hack/filter-caph-controller-manager-logs.py -`: Filter the output to avoid being overwhelmed.
100+
You can configure the script to exclude lines or individual key/value pairs of the JSON log lines.
101+
83102
## Running local e2e test
84103

85104
If you are interested in running the E2E tests locally, then you can use the following commands:

hack/filter-caph-controller-manager-logs.py

Lines changed: 158 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,47 @@
1818
import sys
1919
import json
2020

21-
keys_to_skip = ['controller', 'controllerGroup', 'controllerKind', 'reconcileID',
22-
'HetznerCluster', 'Cluster',
23-
'namespace', 'name', 'Machine', 'stack', 'stacktrace',
24-
'logger',
25-
]
21+
keys_to_skip = [
22+
"controller",
23+
"controllerGroup",
24+
"controllerKind",
25+
"reconcileID",
26+
"HetznerCluster",
27+
"Cluster",
28+
"namespace",
29+
"name",
30+
"Machine",
31+
"stack",
32+
"stacktrace",
33+
"logger",
34+
]
35+
36+
# Only show the name. Not a nested struct.
37+
# in: 'HCloudMachine': {'name': 'myname', 'namespace': '...'}
38+
# out:'HCloudMachine': 'myname'
39+
keys_to_name = [
40+
"HCloudMachine",
41+
"HCloudMachineTemplate",
42+
"HCloudRemediation",
43+
"HCloudRemediationTemplate",
44+
"HetznerBareMetalHost",
45+
"HetznerBareMetalMachine",
46+
"HetznerBareMetalMachineTemplate",
47+
"HetznerBareMetalRemediation",
48+
"HetznerBareMetalRemediationTemplate",
49+
"HetznerCluster",
50+
"HetznerClusterTemplate",
51+
]
52+
53+
rows_to_skip_regex = [
54+
r"maxprocs: Leaving GOMAXPROCS=\d+: CPU quota undefined",
55+
r"^Random Seed: \d+",
56+
]
57+
58+
rows_to_trigger_test_filter = ["Running Suite:"]
2659

60+
# List of fixed-strings.
2761
rows_to_skip = [
28-
'controller-runtime.webhook', 'certwatcher/certwatcher', 'Registering a validating webhook',
29-
'Registering a mutating webhook', 'Starting EventSource',
30-
'Starting Controller',
3162
'"Starting workers" controller/controller',
3263
'"starting manager"',
3364
'"Starting metrics server"',
@@ -40,8 +71,6 @@
4071
'"Serving metrics server"',
4172
'"Starting webhook server"',
4273
'"Serving webhook server"',
43-
'attempting to acquire leader lease caph-system/hetzner.cluster.x-k8s.io.',
44-
'Update to resource only changes insignificant fields',
4574
'"approved csr"',
4675
'"Registering webhook"',
4776
'"Stopping and waiting for',
@@ -50,16 +79,58 @@
5079
"'statusCode': 200, 'method': 'GET', 'url': 'https://robot-ws.your-server.de",
5180
]
5281

82+
rows_to_skip_for_tests = [
83+
"attempting to acquire leader lease caph-system/hetzner.cluster.x-k8s.io.",
84+
"Update to resource only changes insignificant fields",
85+
'"Wait completed, proceeding to shutdown the manager"',
86+
'"starting control plane" logger="controller-runtime.test-env"',
87+
'"installing CRDs" logger="controller-runtime.test-env"',
88+
'"reading CRDs from path"',
89+
'"read CRDs from file"',
90+
'"installing CRD"',
91+
'"adding API in waitlist" logger="controller-runtime.test-env"',
92+
'"installing webhooks" logger="controller-runtime.test-env"',
93+
'"installing mutating webhook" logger="controller-runtime.test-env"',
94+
'"installing validating webhook" logger="controller-runtime.test-env"',
95+
'cluster.x-k8s.io\\": prefer a domain-qualified finalizer',
96+
'"Update to resource changes significant fields, will enqueue event"',
97+
'"Wait for update being in local cache"',
98+
'"Created load balancer"',
99+
'"Created network with opts',
100+
'"Starting workers" controller="',
101+
'"controller-runtime.certwatcher"',
102+
"controller-runtime.webhook",
103+
"certwatcher/certwatcher",
104+
"Registering a validating webhook",
105+
"Registering a mutating webhook",
106+
"Starting EventSource",
107+
"Starting Controller",
108+
"Wait completed, proceeding to shutdown the manager",
109+
"unable to decode an event from the watch stream: context canceled",
110+
"client rate limiter Wait returned an error: context canceled",
111+
'os-ssh-secret": context canceled',
112+
"http: TLS handshake error from 127.0.0.1:",
113+
'"Cluster infrastructure did not become ready, blocking further processing"',
114+
'predicate="IgnoreInsignificant',
115+
'"HetznerCluster is not available yet"',
116+
'"Unable to write event (broadcaster is shut down)"',
117+
'"Unable to write event (may retry after sleeping)"',
118+
]
119+
120+
53121
def main():
54122

55-
if len(sys.argv) == 1 or sys.argv[1] in ['-h', '--help']:
56-
print('''%s [file|-]
123+
if len(sys.argv) == 1 or sys.argv[1] in ["-h", "--help"]:
124+
print(
125+
"""%s [file|-]
57126
filter the logs of caph-controller-manager.
58127
Used for debugging.
59-
''' % sys.argv[0])
128+
"""
129+
% sys.argv[0]
130+
)
60131
sys.exit(1)
61132

62-
if sys.argv[1] == '-':
133+
if sys.argv[1] == "-":
63134
fd = sys.stdin
64135
else:
65136
fd = open(sys.argv[1])
@@ -68,17 +139,68 @@ def main():
68139

69140
def read_logs(fd):
70141
for line in fd:
71-
handle_line(line)
142+
try:
143+
handle_line(line)
144+
except BrokenPipeError:
145+
return
146+
147+
148+
ansi_pattern = re.compile(r"\x1B\[[0-9;]*[A-Za-z]")
149+
150+
filtering_test_data = False
151+
152+
153+
def write_line(line):
154+
ascii_line = ansi_pattern.sub("", line)
155+
for r in rows_to_trigger_test_filter:
156+
if r in ascii_line:
157+
global filtering_test_data
158+
filtering_test_data = True
159+
rows_to_skip.extend(rows_to_skip_for_tests)
160+
161+
for r in rows_to_skip:
162+
if r in ascii_line:
163+
return
164+
for r in rows_to_skip_regex:
165+
if re.search(r, ascii_line):
166+
return
167+
sys.stdout.write(line)
168+
72169

73170
def handle_line(line):
74-
if not line.startswith('{'):
75-
sys.stdout.write(line)
171+
# Extract leading dots (•) and print them on a separate line
172+
leading_dots = ""
173+
while line.startswith("•"):
174+
leading_dots += "•"
175+
line = line[1:]
176+
if leading_dots:
177+
print(leading_dots)
178+
if not (line.startswith("{") and line.rstrip().endswith("}")):
179+
write_line(line)
180+
return
181+
try:
182+
data = json.loads(line)
183+
except json.decoder.JSONDecodeError as err:
184+
write_line("\n")
185+
write_line(str(err))
186+
write_line(line)
187+
write_line("\n")
76188
return
77-
data = json.loads(line)
78189
for key in keys_to_skip:
79190
data.pop(key, None)
80-
t = data.pop('time', '')
81-
t = re.sub(r'^.*T(.+)*\..+$', r'\1', t) # '2023-04-17T12:12:53.423Z
191+
192+
# in: 'HCloudMachine': {'name': 'myname', 'namespace': '...'}
193+
# out:'HCloudMachine': 'myname'
194+
for key in keys_to_name:
195+
value = data.get(key)
196+
if not value:
197+
continue
198+
name = value.get("name")
199+
if name:
200+
data[key] = name
201+
202+
t = data.pop("time", "")
203+
t = re.sub(r"^.*T(.+)*\..+$", r"\1", t) # '2023-04-17T12:12:53.423Z
82204

83205
# skip too long entries
84206
for key, value in list(data.items()):
@@ -87,20 +209,24 @@ def handle_line(line):
87209
if len(value) > 1_000:
88210
data[key] = value[:1_000] + "...cut..."
89211

90-
level = data.pop('level', '').ljust(5)
91-
file = data.pop('file', '')
92-
message = data.pop('message', '')
93-
94-
if not data:
95-
data=''
212+
level = data.pop("level", "").ljust(5)
213+
file = data.pop("file", "")
214+
message = data.pop("message", "")
215+
216+
if data:
217+
values = []
218+
for key, value in data.items():
219+
value = str(value)
220+
if any(c.isspace() for c in value):
221+
value = f"<{value}>"
222+
values.append(f"{key}={value}")
223+
data = " ".join(values)
224+
else:
225+
data = ""
96226

97227
new_line = f'{t} {level} "{message}" {file} {data}\n'
98-
for r in rows_to_skip:
99-
if r in new_line:
100-
return
101-
102-
sys.stdout.write(new_line)
228+
write_line(new_line)
103229

104230

105-
if __name__ == '__main__':
231+
if __name__ == "__main__":
106232
main()

hack/test-unit.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@ hack/tools/bin/gotestsum \
3737
--junitfile=.coverage/junit.xml \
3838
--format testname -- \
3939
-covermode=atomic -coverprofile=.coverage/cover.out -p=1 -timeout 5m \
40-
./controllers/... ./pkg/... ./api/...
40+
./controllers/... ./pkg/... ./api/... | ./hack/filter-caph-controller-manager-logs.py -

test/helpers/envtest.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import (
6060
hcloudclient "github.com/syself/cluster-api-provider-hetzner/pkg/services/hcloud/client"
6161
fakeclient "github.com/syself/cluster-api-provider-hetzner/pkg/services/hcloud/client/fake"
6262
"github.com/syself/cluster-api-provider-hetzner/pkg/services/hcloud/mockedsshclient"
63+
"github.com/syself/cluster-api-provider-hetzner/pkg/utils"
6364
)
6465

6566
func init() {
@@ -150,6 +151,7 @@ func NewTestEnvironment() *TestEnvironment {
150151
// Disable MetricsServer, so that two tests processes can run concurrently
151152
BindAddress: "0",
152153
},
154+
Logger: utils.GetDefaultLogger("info"),
153155
})
154156
if err != nil {
155157
klog.Fatalf("unable to create manager: %s", err)

0 commit comments

Comments
 (0)