TUN-4851: Component tests to sanity check that Proxy DNS and Tunnel are only run when expected

This commit is contained in:
Nuno Diegues 2021-07-30 17:44:11 +01:00
parent cd4af5696d
commit 1e8dea9112
6 changed files with 118 additions and 19 deletions

View File

@ -3,7 +3,7 @@ import copy
from dataclasses import dataclass, InitVar 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 # frozen=True raises exception when assigning to fields. This emulates immutability
@ -93,3 +93,12 @@ class ClassicTunnelConfig(ClassicTunnelBaseConfig):
def get_url(self): def get_url(self):
return "https://" + self.hostname return "https://" + self.hostname
@dataclass(frozen=True)
class ProxyDnsConfig(BaseConfig):
full_config = {
"port": PROXY_DNS_PORT,
"no-autoupdate": True,
}

View File

@ -1,14 +1,21 @@
import os import os
from enum import Enum, auto
from time import sleep
import pytest import pytest
import yaml import yaml
from time import sleep from config import NamedTunnelConfig, ClassicTunnelConfig, ProxyDnsConfig
from constants import BACKOFF_SECS, PROXY_DNS_PORT
from config import NamedTunnelConfig, ClassicTunnelConfig
from constants import BACKOFF_SECS
from util import LOGGER from util import LOGGER
class CfdModes(Enum):
NAMED = auto()
CLASSIC = auto()
PROXY_DNS = auto()
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def component_tests_config(): def component_tests_config():
config_file = os.getenv("COMPONENT_TESTS_CONFIG") config_file = os.getenv("COMPONENT_TESTS_CONFIG")
@ -19,22 +26,30 @@ def component_tests_config():
config = yaml.safe_load(stream) config = yaml.safe_load(stream)
LOGGER.info(f"component tests base config {config}") 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 if cfd_mode is CfdModes.NAMED:
additional_config["proxy-dns"] = True
additional_config["proxy-dns-port"] = 9053
if named_tunnel:
return NamedTunnelConfig(additional_config=additional_config, return NamedTunnelConfig(additional_config=additional_config,
cloudflared_binary=config['cloudflared_binary'], cloudflared_binary=config['cloudflared_binary'],
tunnel=config['tunnel'], tunnel=config['tunnel'],
credentials_file=config['credentials_file'], credentials_file=config['credentials_file'],
ingress=config['ingress']) ingress=config['ingress'])
elif cfd_mode is CfdModes.CLASSIC:
return ClassicTunnelConfig( return ClassicTunnelConfig(
additional_config=additional_config, cloudflared_binary=config['cloudflared_binary'], additional_config=additional_config, cloudflared_binary=config['cloudflared_binary'],
hostname=config['classic_hostname'], origincert=config['origincert']) 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 return _component_tests_config

View File

@ -1,3 +1,5 @@
METRICS_PORT = 51000 METRICS_PORT = 51000
MAX_RETRIES = 5 MAX_RETRIES = 5
BACKOFF_SECS = 7 BACKOFF_SECS = 7
PROXY_DNS_PORT = 9053

View File

@ -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."

View File

@ -6,6 +6,7 @@ from time import sleep
import pytest import pytest
from flaky import flaky from flaky import flaky
from conftest import CfdModes
from util import start_cloudflared, wait_tunnel_ready, check_tunnel_not_connected 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): def test_classic_reconnect(self, tmp_path, component_tests_config):
extra_config = copy.copy(self.extra_config) extra_config = copy.copy(self.extra_config)
extra_config["hello-world"] = True extra_config["hello-world"] = True
config = component_tests_config( config = component_tests_config(additional_config=extra_config, cfd_mode=CfdModes.CLASSIC)
additional_config=extra_config, named_tunnel=False)
with start_cloudflared(tmp_path, config, cfd_args=[], new_process=True, allow_input=True, capture_output=False) as cloudflared: 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) self.assert_reconnect(config, cloudflared, 1)

View File

@ -8,6 +8,7 @@ from pathlib import Path
import pytest import pytest
import test_logging import test_logging
from conftest import CfdModes
from util import start_cloudflared, wait_tunnel_ready from util import start_cloudflared, wait_tunnel_ready
@ -34,7 +35,7 @@ class TestServiceMode:
"hello-world": True, "hello-world": True,
"logfile": str(log_file), "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(): def assert_log_file():
test_logging.assert_log_in_file(log_file) test_logging.assert_log_in_file(log_file)
@ -52,7 +53,7 @@ class TestServiceMode:
"loglevel": "debug", "loglevel": "debug",
"log-directory": str(log_dir), "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(): def assert_rotating_log():
test_logging.assert_log_to_dir(config, log_dir) test_logging.assert_log_to_dir(config, log_dir)