diff --git a/component-tests/config.py b/component-tests/config.py index 7a3248cc..dc438149 100644 --- a/component-tests/config.py +++ b/component-tests/config.py @@ -3,7 +3,7 @@ import copy from dataclasses import dataclass, InitVar -from constants import METRICS_PORT +from constants import METRICS_PORT, PROXY_DNS_PORT # frozen=True raises exception when assigning to fields. This emulates immutability @@ -93,3 +93,12 @@ class ClassicTunnelConfig(ClassicTunnelBaseConfig): def get_url(self): return "https://" + self.hostname + + +@dataclass(frozen=True) +class ProxyDnsConfig(BaseConfig): + full_config = { + "port": PROXY_DNS_PORT, + "no-autoupdate": True, + } + diff --git a/component-tests/conftest.py b/component-tests/conftest.py index 386fc1dc..709ab39a 100644 --- a/component-tests/conftest.py +++ b/component-tests/conftest.py @@ -1,14 +1,21 @@ import os +from enum import Enum, auto +from time import sleep + import pytest import yaml -from time import sleep - -from config import NamedTunnelConfig, ClassicTunnelConfig -from constants import BACKOFF_SECS +from config import NamedTunnelConfig, ClassicTunnelConfig, ProxyDnsConfig +from constants import BACKOFF_SECS, PROXY_DNS_PORT from util import LOGGER +class CfdModes(Enum): + NAMED = auto() + CLASSIC = auto() + PROXY_DNS = auto() + + @pytest.fixture(scope="session") def component_tests_config(): config_file = os.getenv("COMPONENT_TESTS_CONFIG") @@ -19,22 +26,30 @@ def component_tests_config(): config = yaml.safe_load(stream) LOGGER.info(f"component tests base config {config}") - def _component_tests_config(additional_config={}, named_tunnel=True): + def _component_tests_config(additional_config={}, cfd_mode=CfdModes.NAMED, run_proxy_dns=True): + if run_proxy_dns: + # Regression test for TUN-4177, running with proxy-dns should not prevent tunnels from running. + # So we run all tests with it. + additional_config["proxy-dns"] = True + additional_config["proxy-dns-port"] = PROXY_DNS_PORT + else: + additional_config.pop("proxy-dns", None) + additional_config.pop("proxy-dns-port", None) - # Regression test for TUN-4177, running with proxy-dns should not prevent tunnels from running - additional_config["proxy-dns"] = True - additional_config["proxy-dns-port"] = 9053 - - if named_tunnel: + if cfd_mode is CfdModes.NAMED: return NamedTunnelConfig(additional_config=additional_config, cloudflared_binary=config['cloudflared_binary'], tunnel=config['tunnel'], credentials_file=config['credentials_file'], ingress=config['ingress']) - - return ClassicTunnelConfig( - additional_config=additional_config, cloudflared_binary=config['cloudflared_binary'], - hostname=config['classic_hostname'], origincert=config['origincert']) + elif cfd_mode is CfdModes.CLASSIC: + return ClassicTunnelConfig( + additional_config=additional_config, cloudflared_binary=config['cloudflared_binary'], + hostname=config['classic_hostname'], origincert=config['origincert']) + elif cfd_mode is CfdModes.PROXY_DNS: + return ProxyDnsConfig(cloudflared_binary=config['cloudflared_binary']) + else: + raise Exception(f"Unknown cloudflared mode {cfd_mode}") return _component_tests_config diff --git a/component-tests/constants.py b/component-tests/constants.py index 256bc197..0212017c 100644 --- a/component-tests/constants.py +++ b/component-tests/constants.py @@ -1,3 +1,5 @@ METRICS_PORT = 51000 MAX_RETRIES = 5 BACKOFF_SECS = 7 + +PROXY_DNS_PORT = 9053 diff --git a/component-tests/test_proxy_dns.py b/component-tests/test_proxy_dns.py new file mode 100644 index 00000000..d498b9e7 --- /dev/null +++ b/component-tests/test_proxy_dns.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +import socket +from time import sleep + +import constants +from conftest import CfdModes +from util import start_cloudflared, wait_tunnel_ready, check_tunnel_not_connected + + +# Sanity checks that test that we only run Proxy DNS and Tunnel when we really expect them to be there. +class TestProxyDns: + def test_proxy_dns_with_named_tunnel(self, tmp_path, component_tests_config): + run_test_scenario(tmp_path, component_tests_config, CfdModes.NAMED, run_proxy_dns=True) + + def test_proxy_dns_with_classic_tunnel(self, tmp_path, component_tests_config): + run_test_scenario(tmp_path, component_tests_config, CfdModes.CLASSIC, run_proxy_dns=True) + + def test_proxy_dns_alone(self, tmp_path, component_tests_config): + run_test_scenario(tmp_path, component_tests_config, CfdModes.PROXY_DNS, run_proxy_dns=True) + + def test_named_tunnel_alone(self, tmp_path, component_tests_config): + run_test_scenario(tmp_path, component_tests_config, CfdModes.NAMED, run_proxy_dns=False) + + def test_classic_tunnel_alone(self, tmp_path, component_tests_config): + run_test_scenario(tmp_path, component_tests_config, CfdModes.CLASSIC, run_proxy_dns=False) + + +def run_test_scenario(tmp_path, component_tests_config, cfd_mode, run_proxy_dns): + expect_proxy_dns = run_proxy_dns + expect_tunnel = False + + if cfd_mode == CfdModes.NAMED: + expect_tunnel = True + pre_args = ["tunnel"] + args = ["run"] + elif cfd_mode == CfdModes.CLASSIC: + expect_tunnel = True + pre_args = [] + args = [] + elif cfd_mode == CfdModes.PROXY_DNS: + expect_proxy_dns = True + pre_args = [] + args = ["proxy-dns", "--port", str(constants.PROXY_DNS_PORT)] + else: + assert False, f"Unknown cfd_mode {cfd_mode}" + + config = component_tests_config(cfd_mode=cfd_mode, run_proxy_dns=run_proxy_dns) + with start_cloudflared(tmp_path, config, cfd_pre_args=pre_args, cfd_args=args, new_process=True, capture_output=False): + if expect_tunnel: + wait_tunnel_ready() + else: + check_tunnel_not_connected() + verify_proxy_dns(expect_proxy_dns) + + +def verify_proxy_dns(should_be_running): + # Wait for the Proxy DNS listener to come up. + sleep(constants.BACKOFF_SECS) + had_failure = False + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.connect(('localhost', constants.PROXY_DNS_PORT)) + sock.send(b"anything") + except: + if should_be_running: + assert False, "Expected Proxy DNS to be running, but it was not." + had_failure = True + finally: + sock.close() + + if not should_be_running and not had_failure: + assert False, "Proxy DNS should not have been running, but it was." diff --git a/component-tests/test_reconnect.py b/component-tests/test_reconnect.py index 15af88ca..02ad7ba2 100644 --- a/component-tests/test_reconnect.py +++ b/component-tests/test_reconnect.py @@ -6,6 +6,7 @@ from time import sleep import pytest from flaky import flaky +from conftest import CfdModes from util import start_cloudflared, wait_tunnel_ready, check_tunnel_not_connected @@ -27,8 +28,7 @@ class TestReconnect: def test_classic_reconnect(self, tmp_path, component_tests_config): extra_config = copy.copy(self.extra_config) extra_config["hello-world"] = True - config = component_tests_config( - additional_config=extra_config, named_tunnel=False) + config = component_tests_config(additional_config=extra_config, cfd_mode=CfdModes.CLASSIC) with start_cloudflared(tmp_path, config, cfd_args=[], new_process=True, allow_input=True, capture_output=False) as cloudflared: self.assert_reconnect(config, cloudflared, 1) diff --git a/component-tests/test_service.py b/component-tests/test_service.py index c4b27dca..efc3e242 100644 --- a/component-tests/test_service.py +++ b/component-tests/test_service.py @@ -8,6 +8,7 @@ from pathlib import Path import pytest import test_logging +from conftest import CfdModes from util import start_cloudflared, wait_tunnel_ready @@ -34,7 +35,7 @@ class TestServiceMode: "hello-world": True, "logfile": str(log_file), } - config = component_tests_config(additional_config=additional_config, named_tunnel=False) + config = component_tests_config(additional_config=additional_config, cfd_mode=CfdModes.CLASSIC) def assert_log_file(): test_logging.assert_log_in_file(log_file) @@ -52,7 +53,7 @@ class TestServiceMode: "loglevel": "debug", "log-directory": str(log_dir), } - config = component_tests_config(additional_config=additional_config, named_tunnel=False) + config = component_tests_config(additional_config=additional_config, cfd_mode=CfdModes.CLASSIC) def assert_rotating_log(): test_logging.assert_log_to_dir(config, log_dir)