diff --git a/config_generation/nginx.py b/config_generation/nginx.py index e33a4fc..67619ae 100644 --- a/config_generation/nginx.py +++ b/config_generation/nginx.py @@ -319,6 +319,14 @@ def info_and_stub_status_server(timestamp): ) +def edgemanage_health_check_server(): + return nginx.Server( + nginx.Key('listen', "80"), + nginx.Key('server_name', "edgemanage_health_check"), + nginx.Location('/', nginx.Key('return', "200 hello")), + ) + + def banjax_server(): return nginx.Server( nginx.Key('listen', "80"), @@ -405,6 +413,9 @@ def http_block(dconf, timestamp): # /info and /stub_status http.add(info_and_stub_status_server(timestamp)) + # /edgemanage_health_check + http.add(edgemanage_health_check_server()) + # exposing a few banjax endpoints http.add(banjax_server()) diff --git a/input/config-example/edgemanage.yml b/input/config-example/edgemanage.yml new file mode 100644 index 0000000..653c0be --- /dev/null +++ b/input/config-example/edgemanage.yml @@ -0,0 +1,55 @@ +testobject: + # http Host: header value + host: edgemanage_health_check + port: 80 + proto: http + # Path to the object to be retrieved + uri: /edgemanage_test_object.bin + # Local copy of the object that we'll be fetching. + local: ./input/edgemanage_test_object.bin + # Verify SSL certificates? + verify: False + +# XXX these names shouldn't be here. +dns: + ns_records: + - dns1.example.com. + soa_nameserver: dns0.example.com. + soa_mailbox: zone.example.com. + # A list of labels to generate A records for with the balanced edge + # lists. This list automatically includes @ by default. + rotate_zones: + - www + +# XXX the persisted path shouldn't be hardcoded here +# edgemanage input +zonetemplate_dir: ./persisted/edgemanage/unfinished-zones/ + +# edgemanage output +named_dir: ./persisted/edgemanage/finished-zones/ + +healthdata_store: ./persisted/edgemanage/ + +# This setting defines the maximum number of substitutions that can be +# performed in a 10 minute period +dnschange_maxfreq: 10 + +# Number of connections to make in parallel to the edges and canaries +workers: 10 + +# Number of retries when fetching the object from an edge +retry: 3 + +# A value, in seconds, that is used to determine edge health - one of +# the core elements of edgemanage. If the fetch time, the fetch time +# slice average, or the overall average is under this value, there is +# a chance that an edge will be used. See the README for more +# explanation of how this value is used. +goodenough: 0.700 + +# XXX bug that you need this even when it's overridden by dnet_edge_count below +edge_count: 1 + +# Number of edges to keep as @ per-dnet +dnet_edge_count: + dnet_a: 1 diff --git a/input/edgemanage_test_object.bin b/input/edgemanage_test_object.bin new file mode 100644 index 0000000..b6fc4c6 --- /dev/null +++ b/input/edgemanage_test_object.bin @@ -0,0 +1 @@ +hello \ No newline at end of file diff --git a/main.py b/main.py index 94efcb3..745c501 100644 --- a/main.py +++ b/main.py @@ -31,7 +31,13 @@ from orchestration.hosts import docker_client_for_host, run_local_or_remote_noraise, host_to_role import logging -from util.helpers import get_logger, get_config_yml_path, path_to_output +from util.helpers import ( + get_logger, + get_config_yml_path, + get_edgemanage_config_yml_path, + path_to_output, +) + logger = get_logger(__name__, logging_level=logging.DEBUG) @@ -96,6 +102,7 @@ def gen_config(config, all_sites, timestamp): "get-banjax-rate-limit-states", "get-nginx-and-banjax-config-versions", "check-cert-expiry", + "edgemanage", ], help="what to do to the hosts" ) @@ -233,3 +240,50 @@ def gen_config(config, all_sites, timestamp): cert_bytes = f.read() cert = x509.load_pem_x509_certificate(cert_bytes, default_backend()) logger.info(f"subject: {cert.subject}, issuer: {cert.issuer}, expires: {cert.not_valid_after}") + + # XXX this is just a sketch, not integrated with the rest of the config-gen code. + # it would require changes to edgemanage to make it work + # (and substantial changes to make it work well), but a better approach might + # be rewriting edgemanage from scratch (it should not be nearly as complicated + # or involved as the existing code suggests) or taking a fundamentally different + # approach (eg., non-rotating IPs with scalable resources behind them). + elif args.action == "edgemanage": + import edgemanage + import json + + em_config = parse_config(get_edgemanage_config_yml_path()) + logger = logging.getLogger("root") + logger.setLevel(logging.DEBUG) + + # XXX + for dnet in ["dnet_a"]: + statefile_name = f"persisted/edgemanage/state_{dnet}.json" + statefile = None + if os.path.isfile(statefile_name): + with open(statefile_name, "r") as f: + d = json.loads(f.read()) + statefile = edgemanage.StateFile(d) + else: + statefile = edgemanage.StateFile() + + em = edgemanage.EdgeManage(dnet, em_config, statefile) + + for edge in config['edges']: + if edge['dnet'] != dnet: + continue + # edgemanage expects the FQDN, but our hostnames are not always FQDNs. + # so I'm passing the IP instead. + em.add_edge_state(edge['ip'], em_config["healthdata_store"]) + + statefile.verification_failures = em.do_edge_tests() + em.make_edges_live(True) # XXX force update doesn't actually work + + live_edges = em.edgelist_obj.get_live_edges() + if set(live_edges) != set(statefile.last_live): + statefile.add_rotation(100) # keep up to 100 + + statefile.last_live = live_edges + + statefile.set_last_run() + with open(statefile_name, "w") as f: + f.write(statefile.to_json()) diff --git a/persisted/edgemanage/.gitkeep b/persisted/edgemanage/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt index f13a681..9babe96 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,5 @@ python-gnupg docker pyaml-env==1.1.1 paramiko==2.7.2 -jinja2==3.0.1 \ No newline at end of file +jinja2==3.0.1 +git+git://github.com/equalitie/edgemanage@feature/python3-migration#egg=edgemanage diff --git a/util/helpers.py b/util/helpers.py index 7054197..ca8a157 100644 --- a/util/helpers.py +++ b/util/helpers.py @@ -82,6 +82,9 @@ def get_persisted_config_yml_path(): def get_banjax_config_yml_path(): return os.path.join(path_to_input(), 'config/banjax_config.yml') +def get_edgemanage_config_yml_path(): + return os.path.join(path_to_input(), 'config/edgemanage.yml') + def get_kibana_saved_objects_path(): return os.path.join(path_to_input(), 'kibana-saved-objects.ndjson')