From 5431e0ca126a3fbe0aad835f7fbbb6d9feb14456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Oliveirinha?= Date: Wed, 2 Mar 2022 15:56:32 +0000 Subject: [PATCH] TUN-5680: Adapt component tests for new service install based on token --- component-tests/README.md | 2 +- component-tests/config.py | 19 +++++++++++ component-tests/test_service.py | 60 +++++++++++++++++++++++++++------ component-tests/util.py | 14 ++++++-- 4 files changed, 81 insertions(+), 14 deletions(-) diff --git a/component-tests/README.md b/component-tests/README.md index 27c34783..8a76eeda 100644 --- a/component-tests/README.md +++ b/component-tests/README.md @@ -14,7 +14,7 @@ classic_hostname: "classic-tunnel-component-tests.example.com" origincert: "/Users/tunnel/.cloudflared/cert.pem" ingress: - hostname: named-tunnel-component-tests.example.com - service: http_status:200 + service: hello_world - service: http_status:404 ``` diff --git a/component-tests/config.py b/component-tests/config.py index dc438149..f53732f2 100644 --- a/component-tests/config.py +++ b/component-tests/config.py @@ -1,5 +1,7 @@ #!/usr/bin/env python import copy +import json +import base64 from dataclasses import dataclass, InitVar @@ -61,6 +63,23 @@ class NamedTunnelConfig(NamedTunnelBaseConfig): def get_url(self): return "https://" + self.ingress[0]['hostname'] + def base_config(self): + config = self.full_config.copy() + + # removes the tunnel reference + del(config["tunnel"]) + del(config["credentials-file"]) + + return config + + def get_token(self): + with open(self.credentials_file) as json_file: + creds = json.load(json_file) + token_dict = {"a": creds["AccountTag"], "t": creds["TunnelID"], "s": creds["TunnelSecret"]} + token_json_str = json.dumps(token_dict) + + return base64.b64encode(token_json_str.encode('utf-8')) + @dataclass(frozen=True) class ClassicTunnelBaseConfig(BaseConfig): diff --git a/component-tests/test_service.py b/component-tests/test_service.py index efc3e242..2e02db6b 100644 --- a/component-tests/test_service.py +++ b/component-tests/test_service.py @@ -1,5 +1,6 @@ #!/usr/bin/env python import os +import pathlib import platform import subprocess from contextlib import contextmanager @@ -9,7 +10,7 @@ import pytest import test_logging from conftest import CfdModes -from util import start_cloudflared, wait_tunnel_ready +from util import start_cloudflared, wait_tunnel_ready, write_config def select_platform(plat): @@ -43,6 +44,21 @@ class TestServiceMode: self.launchd_service_scenario(config, assert_log_file) + @select_platform("Darwin") + @pytest.mark.skipif(os.path.exists(default_config_file()), reason=f"There is already a config file in default path") + def test_launchd_service_with_token(self, tmp_path, component_tests_config): + log_file = tmp_path / test_logging.default_log_file + additional_config = { + "logfile": str(log_file), + } + config = component_tests_config(additional_config=additional_config) + + # service install doesn't install the config file but in this case we want to use some default settings + # so we write the base config without the tunnel credentials and ID + write_config(pathlib.Path(default_config_dir()), config.base_config()) + + self.launchd_service_scenario(config, use_token=True) + @select_platform("Darwin") @pytest.mark.skipif(os.path.exists(default_config_file()), reason=f"There is already a config file in default path") def test_launchd_service_rotating_log(self, tmp_path, component_tests_config): @@ -60,12 +76,13 @@ class TestServiceMode: self.launchd_service_scenario(config, assert_rotating_log) - def launchd_service_scenario(self, config, extra_assertions): - with self.run_service(Path(default_config_dir()), config): + def launchd_service_scenario(self, config, extra_assertions=None, use_token=False): + with self.run_service(Path(default_config_dir()), config, use_token=use_token): self.launchctl_cmd("list") self.launchctl_cmd("start") wait_tunnel_ready(tunnel_url=config.get_url()) - extra_assertions() + if extra_assertions is not None: + extra_assertions() self.launchctl_cmd("stop") os.remove(default_config_file()) @@ -105,12 +122,30 @@ class TestServiceMode: self.sysv_service_scenario(config, tmp_path, assert_rotating_log) - def sysv_service_scenario(self, config, tmp_path, extra_assertions): - with self.run_service(tmp_path, config, root=True): + @select_platform("Linux") + @pytest.mark.skipif(os.path.exists("/etc/cloudflared/config.yml"), + reason=f"There is already a config file in default path") + def test_sysv_service_with_token(self, tmp_path, component_tests_config): + additional_config = { + "loglevel": "debug", + } + + config = component_tests_config(additional_config=additional_config) + + # service install doesn't install the config file but in this case we want to use some default settings + # so we write the base config without the tunnel credentials and ID + config_path = write_config(tmp_path, config.base_config()) + subprocess.run(["sudo", "cp", config_path, "/etc/cloudflared/config.yml"], check=True) + + self.sysv_service_scenario(config, tmp_path, use_token=True) + + def sysv_service_scenario(self, config, tmp_path, extra_assertions=None, use_token=False): + with self.run_service(tmp_path, config, root=True, use_token=use_token): self.sysv_cmd("start") self.sysv_cmd("status") wait_tunnel_ready(tunnel_url=config.get_url()) - extra_assertions() + if extra_assertions is not None: + extra_assertions() self.sysv_cmd("stop") # Service install copies config file to /etc/cloudflared/config.yml @@ -118,14 +153,19 @@ class TestServiceMode: self.sysv_cmd("status", success=False) @contextmanager - def run_service(self, tmp_path, config, root=False): + def run_service(self, tmp_path, config, root=False, use_token=False): + args = ["service", "install"] + + if use_token: + args.append(config.get_token()) + try: service = start_cloudflared( - tmp_path, config, cfd_args=["service", "install"], cfd_pre_args=[], capture_output=False, root=root) + tmp_path, config, cfd_args=args, cfd_pre_args=[], capture_output=False, root=root, skip_config_flag=use_token) yield service finally: start_cloudflared( - tmp_path, config, cfd_args=["service", "uninstall"], cfd_pre_args=[], capture_output=False, root=root) + tmp_path, config, cfd_args=["service", "uninstall"], cfd_pre_args=[], capture_output=False, root=root, skip_config_flag=use_token) def launchctl_cmd(self, action, success=True): cmd = subprocess.run( diff --git a/component-tests/util.py b/component-tests/util.py index 3c42d2a7..db8f925d 100644 --- a/component-tests/util.py +++ b/component-tests/util.py @@ -21,9 +21,14 @@ def write_config(directory, config): def start_cloudflared(directory, config, cfd_args=["run"], cfd_pre_args=["tunnel"], new_process=False, - allow_input=False, capture_output=True, root=False): - config_path = write_config(directory, config.full_config) + allow_input=False, capture_output=True, root=False, skip_config_flag=False): + + config_path = None + if not skip_config_flag: + config_path = write_config(directory, config.full_config) + cmd = cloudflared_cmd(config, config_path, cfd_args, cfd_pre_args, root) + if new_process: return run_cloudflared_background(cmd, allow_input, capture_output) # By setting check=True, it will raise an exception if the process exits with non-zero exit code @@ -36,7 +41,10 @@ def cloudflared_cmd(config, config_path, cfd_args, cfd_pre_args, root): cmd += ["sudo"] cmd += [config.cloudflared_binary] cmd += cfd_pre_args - cmd += ["--config", str(config_path)] + + if config_path is not None: + cmd += ["--config", str(config_path)] + cmd += cfd_args LOGGER.info(f"Run cmd {cmd} with config {config}") return cmd