setup.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. #!/usr/bin/env python
  2. import argparse
  3. import base64
  4. import json
  5. import os
  6. import subprocess
  7. import uuid
  8. import CloudFlare
  9. import yaml
  10. from retrying import retry
  11. from constants import MAX_RETRIES, BACKOFF_SECS
  12. from util import LOGGER
  13. def get_config_from_env():
  14. config_content = base64.b64decode(get_env("COMPONENT_TESTS_CONFIG_CONTENT")).decode('utf-8')
  15. return yaml.safe_load(config_content)
  16. def get_config_from_file():
  17. config_path = get_env("COMPONENT_TESTS_CONFIG")
  18. with open(config_path, 'r') as infile:
  19. return yaml.safe_load(infile)
  20. def persist_config(config):
  21. config_path = get_env("COMPONENT_TESTS_CONFIG")
  22. with open(config_path, 'w') as outfile:
  23. yaml.safe_dump(config, outfile)
  24. def persist_origin_cert(config):
  25. origincert = get_env("COMPONENT_TESTS_ORIGINCERT")
  26. path = config["origincert"]
  27. with open(path, 'w') as outfile:
  28. outfile.write(origincert)
  29. return path
  30. @retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
  31. def create_tunnel(config, origincert_path, random_uuid):
  32. # Delete any previous existing credentials file. If the agent keeps files around (that's the case in Windows) then
  33. # cloudflared tunnel create will refuse to create the tunnel because it does not want to overwrite credentials
  34. # files.
  35. credentials_path = config["credentials_file"]
  36. try:
  37. os.remove(credentials_path)
  38. except OSError:
  39. pass
  40. tunnel_name = "cfd_component_test-" + random_uuid
  41. create_cmd = [config["cloudflared_binary"], "tunnel", "--origincert", origincert_path, "create",
  42. "--credentials-file", credentials_path, tunnel_name]
  43. LOGGER.info(f"Creating tunnel with {create_cmd}")
  44. subprocess.run(create_cmd, check=True)
  45. list_cmd = [config["cloudflared_binary"], "tunnel", "--origincert", origincert_path, "list", "--name",
  46. tunnel_name, "--output", "json"]
  47. LOGGER.info(f"Listing tunnel with {list_cmd}")
  48. cloudflared = subprocess.run(list_cmd, check=True, capture_output=True)
  49. return json.loads(cloudflared.stdout)[0]["id"]
  50. @retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
  51. def delete_tunnel(config):
  52. credentials_path = config["credentials_file"]
  53. delete_cmd = [config["cloudflared_binary"], "tunnel", "--origincert", config["origincert"], "delete",
  54. "--credentials-file", credentials_path, "-f", config["tunnel"]]
  55. LOGGER.info(f"Deleting tunnel with {delete_cmd}")
  56. subprocess.run(delete_cmd, check=True)
  57. @retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
  58. def create_dns(config, hostname, type, content):
  59. cf = CloudFlare.CloudFlare(debug=False, token=get_env("DNS_API_TOKEN"))
  60. cf.zones.dns_records.post(
  61. config["zone_tag"],
  62. data={'name': hostname, 'type': type, 'content': content, 'proxied': True}
  63. )
  64. def create_named_dns(config, random_uuid):
  65. hostname = "named-" + random_uuid + "." + config["zone_domain"]
  66. create_dns(config, hostname, "CNAME", config["tunnel"] + ".cfargotunnel.com")
  67. return hostname
  68. @retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
  69. def delete_dns(config, hostname):
  70. cf = CloudFlare.CloudFlare(debug=False, token=get_env("DNS_API_TOKEN"))
  71. zone_tag = config["zone_tag"]
  72. dns_records = cf.zones.dns_records.get(zone_tag, params={'name': hostname})
  73. if len(dns_records) > 0:
  74. cf.zones.dns_records.delete(zone_tag, dns_records[0]['id'])
  75. def write_file(content, path):
  76. with open(path, 'w') as outfile:
  77. outfile.write(content)
  78. def get_env(env_name):
  79. val = os.getenv(env_name)
  80. if val is None:
  81. raise Exception(f"{env_name} is not set")
  82. return val
  83. def create():
  84. """
  85. Creates the necessary resources for the components test to run.
  86. - Creates a named tunnel with a random name.
  87. - Creates a random CNAME DNS entry for that tunnel.
  88. Those created resources are added to the config (obtained from an environment variable).
  89. The resulting configuration is persisted for the tests to use.
  90. """
  91. config = get_config_from_env()
  92. origincert_path = persist_origin_cert(config)
  93. random_uuid = str(uuid.uuid4())
  94. config["tunnel"] = create_tunnel(config, origincert_path, random_uuid)
  95. config["ingress"] = [
  96. {
  97. "hostname": create_named_dns(config, random_uuid),
  98. "service": "hello_world"
  99. },
  100. {
  101. "service": "http_status:404"
  102. }
  103. ]
  104. persist_config(config)
  105. def cleanup():
  106. """
  107. Reads the persisted configuration that was created previously.
  108. Deletes the resources that were created there.
  109. """
  110. config = get_config_from_file()
  111. delete_tunnel(config)
  112. delete_dns(config, config["ingress"][0]["hostname"])
  113. if __name__ == '__main__':
  114. parser = argparse.ArgumentParser(description='setup component tests')
  115. parser.add_argument('--type', choices=['create', 'cleanup'], default='create')
  116. args = parser.parse_args()
  117. if args.type == 'create':
  118. create()
  119. else:
  120. cleanup()