Skip to content
This repository was archived by the owner on May 28, 2022. It is now read-only.

Commit 6106737

Browse files
committed
TUN-4082: Test logging when running as a service
1 parent 8250b67 commit 6106737

2 files changed

Lines changed: 141 additions & 75 deletions

File tree

component-tests/test_logging.py

Lines changed: 67 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,88 @@
11
#!/usr/bin/env python
22
import json
33
import os
4-
from util import start_cloudflared, wait_tunnel_ready, send_requests, LOGGER
4+
import subprocess
55

6+
from util import start_cloudflared, wait_tunnel_ready, send_requests
67

7-
class TestLogging:
8-
# TODO: Test logging when running as a service https://jira.cfops.it/browse/TUN-4082
9-
# Rolling logger rotate log files after 1 MB
10-
rotate_after_size = 1000 * 1000
11-
default_log_file = "cloudflared.log"
12-
expect_message = "Starting tunnel"
8+
# Rolling logger rotate log files after 1 MB
9+
rotate_after_size = 1000 * 1000
10+
default_log_file = "cloudflared.log"
11+
expect_message = "Starting tunnel"
12+
13+
14+
def assert_log_to_terminal(cloudflared):
15+
stderr = cloudflared.stderr.read(200)
16+
# cloudflared logs the following when it first starts
17+
# 2021-03-10T12:30:39Z INF Starting tunnel tunnelID=<tunnel ID>
18+
assert expect_message.encode() in stderr, f"{stderr} doesn't contain {expect_message}"
19+
20+
21+
def assert_log_in_file(file):
22+
with open(file, "r") as f:
23+
log = f.read(200)
24+
# cloudflared logs the following when it first starts
25+
# {"level":"info","tunnelID":"<tunnel ID>","time":"2021-03-10T12:21:13Z","message":"Starting tunnel"}
26+
assert expect_message in log, f"{log} doesn't contain {expect_message}"
27+
28+
29+
def assert_json_log(file):
30+
def assert_in_json(j, key):
31+
assert key in j, f"{key} is not in j"
32+
33+
with open(file, "r") as f:
34+
line = f.readline()
35+
json_log = json.loads(line)
36+
assert_in_json(json_log, "level")
37+
assert_in_json(json_log, "time")
38+
assert_in_json(json_log, "message")
1339

40+
41+
def assert_log_to_dir(config, log_dir):
42+
max_batches = 3
43+
batch_requests = 1000
44+
for _ in range(max_batches):
45+
send_requests(config.get_url(),
46+
batch_requests, require_ok=False)
47+
files = os.listdir(log_dir)
48+
if len(files) == 2:
49+
current_log_file_index = files.index(default_log_file)
50+
current_file = log_dir / files[current_log_file_index]
51+
stats = os.stat(current_file)
52+
assert stats.st_size > 0
53+
assert_json_log(current_file)
54+
55+
# One file is the current log file, the other is the rotated log file
56+
rotated_log_file_index = 0 if current_log_file_index == 1 else 1
57+
rotated_file = log_dir / files[rotated_log_file_index]
58+
stats = os.stat(rotated_file)
59+
assert stats.st_size > rotate_after_size
60+
assert_log_in_file(rotated_file)
61+
assert_json_log(current_file)
62+
return
63+
64+
raise Exception(
65+
f"Log file isn't rotated after sending {max_batches * batch_requests} requests")
66+
67+
68+
class TestLogging:
1469
def test_logging_to_terminal(self, tmp_path, component_tests_config):
1570
config = component_tests_config()
1671
with start_cloudflared(tmp_path, config, new_process=True) as cloudflared:
1772
wait_tunnel_ready(tunnel_url=config.get_url())
18-
self.assert_log_to_terminal(cloudflared)
73+
assert_log_to_terminal(cloudflared)
1974

2075
def test_logging_to_file(self, tmp_path, component_tests_config):
21-
log_file = tmp_path / self.default_log_file
76+
log_file = tmp_path / default_log_file
2277
extra_config = {
2378
# Convert from pathlib.Path to str
2479
"logfile": str(log_file),
2580
}
2681
config = component_tests_config(extra_config)
2782
with start_cloudflared(tmp_path, config, new_process=True, capture_output=False):
2883
wait_tunnel_ready(tunnel_url=config.get_url())
29-
self.assert_log_in_file(log_file)
30-
self.assert_json_log(log_file)
84+
assert_log_in_file(log_file)
85+
assert_json_log(log_file)
3186

3287
def test_logging_to_dir(self, tmp_path, component_tests_config):
3388
log_dir = tmp_path / "logs"
@@ -39,55 +94,4 @@ def test_logging_to_dir(self, tmp_path, component_tests_config):
3994
config = component_tests_config(extra_config)
4095
with start_cloudflared(tmp_path, config, new_process=True, capture_output=False):
4196
wait_tunnel_ready(tunnel_url=config.get_url())
42-
self.assert_log_to_dir(config, log_dir)
43-
44-
def assert_log_to_terminal(self, cloudflared):
45-
stderr = cloudflared.stderr.read(200)
46-
# cloudflared logs the following when it first starts
47-
# 2021-03-10T12:30:39Z INF Starting tunnel tunnelID=<tunnel ID>
48-
assert self.expect_message.encode(
49-
) in stderr, f"{stderr} doesn't contain {self.expect_message}"
50-
51-
def assert_log_in_file(self, file, expect_message=""):
52-
with open(file, "r") as f:
53-
log = f.read(200)
54-
# cloudflared logs the following when it first starts
55-
# {"level":"info","tunnelID":"<tunnel ID>","time":"2021-03-10T12:21:13Z","message":"Starting tunnel"}
56-
assert self.expect_message in log, f"{log} doesn't contain {self.expect_message}"
57-
58-
def assert_json_log(self, file):
59-
with open(file, "r") as f:
60-
line = f.readline()
61-
json_log = json.loads(line)
62-
self.assert_in_json(json_log, "level")
63-
self.assert_in_json(json_log, "time")
64-
self.assert_in_json(json_log, "message")
65-
66-
def assert_in_json(self, j, key):
67-
assert key in j, f"{key} is not in j"
68-
69-
def assert_log_to_dir(self, config, log_dir):
70-
max_batches = 3
71-
batch_requests = 1000
72-
for _ in range(max_batches):
73-
send_requests(config.get_url(),
74-
batch_requests, require_ok=False)
75-
files = os.listdir(log_dir)
76-
if len(files) == 2:
77-
current_log_file_index = files.index(self.default_log_file)
78-
current_file = log_dir / files[current_log_file_index]
79-
stats = os.stat(current_file)
80-
assert stats.st_size > 0
81-
self.assert_json_log(current_file)
82-
83-
# One file is the current log file, the other is the rotated log file
84-
rotated_log_file_index = 0 if current_log_file_index == 1 else 1
85-
rotated_file = log_dir / files[rotated_log_file_index]
86-
stats = os.stat(rotated_file)
87-
assert stats.st_size > self.rotate_after_size
88-
self.assert_log_in_file(rotated_file)
89-
self.assert_json_log(current_file)
90-
return
91-
92-
raise Exception(
93-
f"Log file isn't rotated after sending {max_batches * batch_requests} requests")
97+
assert_log_to_dir(config, log_dir)

component-tests/test_service.py

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
#!/usr/bin/env python
2-
from contextlib import contextmanager
32
import os
4-
from pathlib import Path
53
import platform
6-
import pytest
74
import subprocess
5+
from contextlib import contextmanager
6+
from pathlib import Path
7+
8+
import pytest
89

9-
from util import start_cloudflared, cloudflared_cmd, wait_tunnel_ready, LOGGER
10+
import test_logging
11+
from util import start_cloudflared, wait_tunnel_ready
1012

1113

1214
def select_platform(plat):
@@ -22,34 +24,94 @@ def default_config_file():
2224
return os.path.join(default_config_dir(), "config.yml")
2325

2426

25-
class TestServiceMode():
27+
class TestServiceMode:
28+
@select_platform("Darwin")
29+
@pytest.mark.skipif(os.path.exists(default_config_file()), reason=f"There is already a config file in default path")
30+
def test_launchd_service_log_to_file(self, tmp_path, component_tests_config):
31+
log_file = tmp_path / test_logging.default_log_file
32+
additional_config = {
33+
# On Darwin cloudflared service defaults to run classic tunnel command
34+
"hello-world": True,
35+
"logfile": str(log_file),
36+
}
37+
config = component_tests_config(additional_config=additional_config, named_tunnel=False)
38+
39+
def assert_log_file():
40+
test_logging.assert_log_in_file(log_file)
41+
test_logging.assert_json_log(log_file)
42+
43+
self.launchd_service_scenario(config, assert_log_file)
44+
2645
@select_platform("Darwin")
2746
@pytest.mark.skipif(os.path.exists(default_config_file()), reason=f"There is already a config file in default path")
28-
def test_launchd_service(self, component_tests_config):
29-
# On Darwin cloudflared service defaults to run classic tunnel command
47+
def test_launchd_service_rotating_log(self, tmp_path, component_tests_config):
48+
log_dir = tmp_path / "logs"
3049
additional_config = {
50+
# On Darwin cloudflared service defaults to run classic tunnel command
3151
"hello-world": True,
52+
"loglevel": "debug",
53+
"log-directory": str(log_dir),
3254
}
33-
config = component_tests_config(
34-
additional_config=additional_config, named_tunnel=False)
55+
config = component_tests_config(additional_config=additional_config, named_tunnel=False)
56+
57+
def assert_rotating_log():
58+
test_logging.assert_log_to_dir(config, log_dir)
59+
60+
self.launchd_service_scenario(config, assert_rotating_log)
61+
62+
def launchd_service_scenario(self, config, extra_assertions):
3563
with self.run_service(Path(default_config_dir()), config):
3664
self.launchctl_cmd("list")
3765
self.launchctl_cmd("start")
3866
wait_tunnel_ready(tunnel_url=config.get_url())
67+
extra_assertions()
3968
self.launchctl_cmd("stop")
4069

4170
os.remove(default_config_file())
4271
self.launchctl_cmd("list", success=False)
4372

4473
@select_platform("Linux")
45-
@pytest.mark.skipif(os.path.exists("/etc/cloudflared/config.yml"), reason=f"There is already a config file in default path")
46-
def test_sysv_service(self, tmp_path, component_tests_config):
47-
config = component_tests_config()
74+
@pytest.mark.skipif(os.path.exists("/etc/cloudflared/config.yml"),
75+
reason=f"There is already a config file in default path")
76+
def test_sysv_service_log_to_file(self, tmp_path, component_tests_config):
77+
log_file = tmp_path / test_logging.default_log_file
78+
additional_config = {
79+
"logfile": str(log_file),
80+
}
81+
config = component_tests_config(additional_config=additional_config)
82+
83+
def assert_log_file():
84+
test_logging.assert_log_in_file(log_file)
85+
test_logging.assert_json_log(log_file)
86+
87+
self.sysv_service_scenario(config, tmp_path, assert_log_file)
88+
89+
@select_platform("Linux")
90+
@pytest.mark.skipif(os.path.exists("/etc/cloudflared/config.yml"),
91+
reason=f"There is already a config file in default path")
92+
def test_sysv_service_rotating_log(self, tmp_path, component_tests_config):
93+
log_dir = tmp_path / "logs"
94+
additional_config = {
95+
"loglevel": "debug",
96+
"log-directory": str(log_dir),
97+
}
98+
config = component_tests_config(additional_config=additional_config)
99+
100+
def assert_rotating_log():
101+
# We need the folder to have executable permissions for the "stat" command in the assertions to work.
102+
subprocess.check_call(['sudo', 'chmod', 'o+x', log_dir])
103+
test_logging.assert_log_to_dir(config, log_dir)
104+
105+
self.sysv_service_scenario(config, tmp_path, assert_rotating_log)
106+
107+
def sysv_service_scenario(self, config, tmp_path, extra_assertions):
48108
with self.run_service(tmp_path, config, root=True):
49109
self.sysv_cmd("start")
50110
self.sysv_cmd("status")
51111
wait_tunnel_ready(tunnel_url=config.get_url())
112+
extra_assertions()
52113
self.sysv_cmd("stop")
114+
53115
# Service install copies config file to /etc/cloudflared/config.yml
54116
subprocess.run(["sudo", "rm", "/etc/cloudflared/config.yml"])
55117
self.sysv_cmd("status", success=False)

0 commit comments

Comments
 (0)