TUN-4125: Change component tests to run in CI with its own dedicated resources
This commit is contained in:
parent
1cf6ae37eb
commit
9d3a7bd08e
|
@ -220,9 +220,11 @@ stretch: &stretch
|
||||||
pre-cache:
|
pre-cache:
|
||||||
- sudo pip3 install --upgrade -r component-tests/requirements.txt
|
- sudo pip3 install --upgrade -r component-tests/requirements.txt
|
||||||
post-cache:
|
post-cache:
|
||||||
# Constructs config file from env vars
|
# Creates and routes a Named Tunnel for this build. Also constructs config file from env vars.
|
||||||
- python3 component-tests/config.py
|
- python3 component-tests/setup.py --type create
|
||||||
- pytest component-tests
|
- pytest component-tests
|
||||||
|
# The Named Tunnel is deleted and its route unprovisioned here.
|
||||||
|
- python3 component-tests/setup.py --type cleanup
|
||||||
update-homebrew:
|
update-homebrew:
|
||||||
builddeps:
|
builddeps:
|
||||||
- openssh-client
|
- openssh-client
|
||||||
|
|
|
@ -1,15 +1,10 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import base64
|
|
||||||
import copy
|
import copy
|
||||||
import os
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from dataclasses import dataclass, InitVar
|
from dataclasses import dataclass, InitVar
|
||||||
|
|
||||||
from constants import METRICS_PORT
|
from constants import METRICS_PORT
|
||||||
|
|
||||||
from util import LOGGER
|
|
||||||
|
|
||||||
# frozen=True raises exception when assigning to fields. This emulates immutability
|
# frozen=True raises exception when assigning to fields. This emulates immutability
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,35 +93,3 @@ class ClassicTunnelConfig(ClassicTunnelBaseConfig):
|
||||||
|
|
||||||
def get_url(self):
|
def get_url(self):
|
||||||
return "https://" + self.hostname
|
return "https://" + self.hostname
|
||||||
|
|
||||||
|
|
||||||
def build_config_from_env():
|
|
||||||
config_path = get_env("COMPONENT_TESTS_CONFIG")
|
|
||||||
config_content = base64.b64decode(
|
|
||||||
get_env("COMPONENT_TESTS_CONFIG_CONTENT")).decode('utf-8')
|
|
||||||
config_yaml = yaml.safe_load(config_content)
|
|
||||||
|
|
||||||
credentials_file = get_env("COMPONENT_TESTS_CREDENTIALS_FILE")
|
|
||||||
write_file(credentials_file, config_yaml["credentials_file"])
|
|
||||||
|
|
||||||
origincert = get_env("COMPONENT_TESTS_ORIGINCERT")
|
|
||||||
write_file(origincert, config_yaml["origincert"])
|
|
||||||
|
|
||||||
write_file(config_content, config_path)
|
|
||||||
|
|
||||||
|
|
||||||
def write_file(content, path):
|
|
||||||
with open(path, 'w') as outfile:
|
|
||||||
outfile.write(content)
|
|
||||||
outfile.close
|
|
||||||
|
|
||||||
|
|
||||||
def get_env(env_name):
|
|
||||||
val = os.getenv(env_name)
|
|
||||||
if val is None:
|
|
||||||
raise Exception(f"{env_name} is not set")
|
|
||||||
return val
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
build_config_from_env()
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
cloudflare==2.8.15
|
||||||
flaky==3.7.0
|
flaky==3.7.0
|
||||||
pytest==6.2.2
|
pytest==6.2.2
|
||||||
pyyaml==5.4.1
|
pyyaml==5.4.1
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import argparse
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import CloudFlare
|
||||||
|
import yaml
|
||||||
|
from retrying import retry
|
||||||
|
|
||||||
|
from constants import MAX_RETRIES, BACKOFF_SECS
|
||||||
|
from util import LOGGER
|
||||||
|
|
||||||
|
|
||||||
|
def get_config_from_env():
|
||||||
|
config_content = base64.b64decode(get_env("COMPONENT_TESTS_CONFIG_CONTENT")).decode('utf-8')
|
||||||
|
return yaml.safe_load(config_content)
|
||||||
|
|
||||||
|
|
||||||
|
def get_config_from_file():
|
||||||
|
config_path = get_env("COMPONENT_TESTS_CONFIG")
|
||||||
|
with open(config_path, 'r') as infile:
|
||||||
|
return yaml.safe_load(infile)
|
||||||
|
|
||||||
|
|
||||||
|
def persist_config(config):
|
||||||
|
config_path = get_env("COMPONENT_TESTS_CONFIG")
|
||||||
|
with open(config_path, 'w') as outfile:
|
||||||
|
yaml.safe_dump(config, outfile)
|
||||||
|
|
||||||
|
|
||||||
|
def persist_origin_cert(config):
|
||||||
|
origincert = get_env("COMPONENT_TESTS_ORIGINCERT")
|
||||||
|
path = config["origincert"]
|
||||||
|
with open(path, 'w') as outfile:
|
||||||
|
outfile.write(origincert)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
@retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
|
||||||
|
def create_tunnel(config, origincert_path, random_uuid):
|
||||||
|
# Delete any previous existing credentials file. If the agent keeps files around (that's the case in Windows) then
|
||||||
|
# cloudflared tunnel create will refuse to create the tunnel because it does not want to overwrite credentials
|
||||||
|
# files.
|
||||||
|
credentials_path = config["credentials_file"]
|
||||||
|
try:
|
||||||
|
os.remove(credentials_path)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
tunnel_name = "cfd_component_test-" + random_uuid
|
||||||
|
create_cmd = [config["cloudflared_binary"], "tunnel", "--origincert", origincert_path, "create",
|
||||||
|
"--credentials-file", credentials_path, tunnel_name]
|
||||||
|
LOGGER.info(f"Creating tunnel with {create_cmd}")
|
||||||
|
subprocess.run(create_cmd, check=True)
|
||||||
|
|
||||||
|
list_cmd = [config["cloudflared_binary"], "tunnel", "--origincert", origincert_path, "list", "--name",
|
||||||
|
tunnel_name, "--output", "json"]
|
||||||
|
LOGGER.info(f"Listing tunnel with {list_cmd}")
|
||||||
|
cloudflared = subprocess.run(list_cmd, check=True, capture_output=True)
|
||||||
|
return json.loads(cloudflared.stdout)[0]["id"]
|
||||||
|
|
||||||
|
|
||||||
|
@retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
|
||||||
|
def delete_tunnel(config):
|
||||||
|
credentials_path = config["credentials_file"]
|
||||||
|
delete_cmd = [config["cloudflared_binary"], "tunnel", "--origincert", config["origincert"], "delete",
|
||||||
|
"--credentials-file", credentials_path, "-f", config["tunnel"]]
|
||||||
|
LOGGER.info(f"Deleting tunnel with {delete_cmd}")
|
||||||
|
subprocess.run(delete_cmd, check=True)
|
||||||
|
|
||||||
|
|
||||||
|
@retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
|
||||||
|
def create_dns(config, hostname, type, content):
|
||||||
|
cf = CloudFlare.CloudFlare(debug=True, token=get_env("DNS_API_TOKEN"))
|
||||||
|
cf.zones.dns_records.post(
|
||||||
|
config["zone_tag"],
|
||||||
|
data={'name': hostname, 'type': type, 'content': content, 'proxied': True}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_classic_dns(config, random_uuid):
|
||||||
|
classic_hostname = "classic-" + random_uuid + "." + config["zone_domain"]
|
||||||
|
create_dns(config, classic_hostname, "AAAA", "fd10:aec2:5dae::")
|
||||||
|
return classic_hostname
|
||||||
|
|
||||||
|
|
||||||
|
def create_named_dns(config, random_uuid):
|
||||||
|
hostname = "named-" + random_uuid + "." + config["zone_domain"]
|
||||||
|
create_dns(config, hostname, "CNAME", config["tunnel"] + ".cfargotunnel.com")
|
||||||
|
return hostname
|
||||||
|
|
||||||
|
|
||||||
|
@retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
|
||||||
|
def delete_dns(config, hostname):
|
||||||
|
cf = CloudFlare.CloudFlare(debug=True, token=get_env("DNS_API_TOKEN"))
|
||||||
|
zone_tag = config["zone_tag"]
|
||||||
|
dns_records = cf.zones.dns_records.get(zone_tag, params={'name': hostname})
|
||||||
|
if len(dns_records) > 0:
|
||||||
|
cf.zones.dns_records.delete(zone_tag, dns_records[0]['id'])
|
||||||
|
|
||||||
|
|
||||||
|
def write_file(content, path):
|
||||||
|
with open(path, 'w') as outfile:
|
||||||
|
outfile.write(content)
|
||||||
|
|
||||||
|
|
||||||
|
def get_env(env_name):
|
||||||
|
val = os.getenv(env_name)
|
||||||
|
if val is None:
|
||||||
|
raise Exception(f"{env_name} is not set")
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
def create():
|
||||||
|
"""
|
||||||
|
Creates the necessary resources for the components test to run.
|
||||||
|
- Creates a named tunnel with a random name.
|
||||||
|
- Creates a random CNAME DNS entry for that tunnel.
|
||||||
|
- Creates a random AAAA DNS entry for a classic tunnel.
|
||||||
|
|
||||||
|
Those created resources are added to the config (obtained from an environment variable).
|
||||||
|
The resulting configuration is persisted for the tests to use.
|
||||||
|
"""
|
||||||
|
config = get_config_from_env()
|
||||||
|
origincert_path = persist_origin_cert(config)
|
||||||
|
|
||||||
|
random_uuid = str(uuid.uuid4())
|
||||||
|
config["tunnel"] = create_tunnel(config, origincert_path, random_uuid)
|
||||||
|
config["classic_hostname"] = create_classic_dns(config, random_uuid)
|
||||||
|
config["ingress"] = [
|
||||||
|
{
|
||||||
|
"hostname": create_named_dns(config, random_uuid),
|
||||||
|
"service": "hello_world"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service": "http_status:404"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
persist_config(config)
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup():
|
||||||
|
"""
|
||||||
|
Reads the persisted configuration that was created previously.
|
||||||
|
Deletes the resources that were created there.
|
||||||
|
"""
|
||||||
|
config = get_config_from_file()
|
||||||
|
delete_tunnel(config)
|
||||||
|
delete_dns(config, config["classic_hostname"])
|
||||||
|
delete_dns(config, config["ingress"][0]["hostname"])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(description='setup component tests')
|
||||||
|
parser.add_argument('--type', choices=['create', 'cleanup'], default='create')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.type == 'create':
|
||||||
|
create()
|
||||||
|
else:
|
||||||
|
cleanup()
|
|
@ -1,7 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from util import start_cloudflared, wait_tunnel_ready, send_requests
|
from util import start_cloudflared, wait_tunnel_ready, send_requests
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue