diff --git a/README.md b/README.md index 0d37446..a8feeac 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,35 @@ In general, for safety, schema version upgrades should not delete data. Schema v ----- +## hub-and-spoke architecture + +![](readme/hub-and-spoke1.png) + +This diagram was created with https://app.diagrams.net/. +To edit it, download the diagram file and edit it with the https://app.diagrams.net/ web application, or you may run the application from [source](https://github.com/jgraph/drawio) if you wish. + +right now I have 2 types of operations, immediate mode and async. + +both types of operations do assignment synchronously. so if the system cant assign the operation to one or more hosts (spokes), +or whatever the operation requires, then it will fail. + +some operations tolerate partial failures, like, capacity_avaliable will succeed if at least one spoke succeeds +for immediate mode, assignment and completion of the operation (like `list`, `capacity_avaliable`, `destroy`) are the same thing + +for async ones, they can be assigned without knowing whether or not they succeeded (`create`) + +![](readme/hub-and-spoke2.png) + +This diagram was created with https://app.diagrams.net/. +To edit it, download the diagram file and edit it with the https://app.diagrams.net/ web application, or you may run the application from [source](https://github.com/jgraph/drawio) if you wish. + +if you issue a create, and it technically could go to any number of hosts, but only one host responds, it will succeed +but if you issue a create and somehow 2 hosts both think they own that task, it will fail and throw a big error. cuz it expects exactly 1 to own the create task + +currently its not set up to do any polling. its not really like a queue at all. It's all immediate for the most part + +----- + ## how to setup btcpay server Generate a private key and the accompanying bitpay SIN for the btcpay API client. diff --git a/capsulflask/hub_api.py b/capsulflask/hub_api.py index 592687a..f071353 100644 --- a/capsulflask/hub_api.py +++ b/capsulflask/hub_api.py @@ -6,7 +6,7 @@ from werkzeug.exceptions import abort from capsulflask.db import get_model, my_exec_info_message -bp = Blueprint("hosts", __name__, url_prefix="/hosts") +bp = Blueprint("hub", __name__, url_prefix="/hub") def authorized_for_host(id): auth_header_value = request.headers.get('Authorization').replace("Bearer ", "") @@ -17,6 +17,6 @@ def heartbeat(id): if authorized_for_host(id): get_model().host_heartbeat(id) else: - current_app.logger.info(f"/hosts/heartbeat/{id} returned 401: invalid token") + current_app.logger.info(f"/hub/heartbeat/{id} returned 401: invalid token") return abort(401, "invalid host id or token") diff --git a/capsulflask/spoke_api.py b/capsulflask/spoke_api.py index 592687a..3ae6f1b 100644 --- a/capsulflask/spoke_api.py +++ b/capsulflask/spoke_api.py @@ -1,4 +1,5 @@ +import aiohttp from flask import Blueprint from flask import current_app from flask import request @@ -6,17 +7,19 @@ from werkzeug.exceptions import abort from capsulflask.db import get_model, my_exec_info_message -bp = Blueprint("hosts", __name__, url_prefix="/hosts") +bp = Blueprint("spoke", __name__, url_prefix="/spoke") -def authorized_for_host(id): +def authorized_as_hub(id): auth_header_value = request.headers.get('Authorization').replace("Bearer ", "") - return get_model().authorized_for_host(id, auth_header_value) + return auth_header_value == current_app.config["HUB_TOKEN"] -@bp.route("/heartbeat/", methods=("POST")) -def heartbeat(id): - if authorized_for_host(id): - get_model().host_heartbeat(id) +@bp.route("/heartbeat", methods=("POST")) +def heartbeat(): + if authorized_as_hub(id): + # make request to hub-domain.com/hub/heartbeat/{current_app.config["SPOKE_HOST_ID"]} + # succeed or fail based on whether the request succeeds or fails. + pass else: - current_app.logger.info(f"/hosts/heartbeat/{id} returned 401: invalid token") - return abort(401, "invalid host id or token") + current_app.logger.info(f"/hosts/heartbeat returned 401: invalid token") + return abort(401, "invalid hub token") diff --git a/capsulflask/spoke_model.py b/capsulflask/spoke_model.py index 4c4b081..2cc6751 100644 --- a/capsulflask/spoke_model.py +++ b/capsulflask/spoke_model.py @@ -54,7 +54,6 @@ class MockSpoke(SpokeInterface): def destroy(self, email: str, id: str): current_app.logger.info(f"mock destroy: {id} for {email}") - class ShellScriptSpoke(SpokeInterface): def validate_completed_process(self, completedProcess, email=None): @@ -157,17 +156,12 @@ class ShellScriptSpoke(SpokeInterface): {completedProcess.stderr} """) - def destroy(self, email: str, id: str): + def destroy(self, email: str, id: str) -> str: validate_capsul_id(id) completedProcess = run([join(current_app.root_path, 'shell_scripts/destroy.sh'), id], capture_output=True) self.validate_completed_process(completedProcess, email) lines = completedProcess.stdout.splitlines() status = lines[len(lines)-1].decode("utf-8") + return status - if not status == "success": - raise ValueError(f"""failed to destroy vm "{id}" for {email}: - stdout: - {completedProcess.stdout} - stderr: - {completedProcess.stderr} - """) + diff --git a/readme/hub-and-spoke.xml b/readme/hub-and-spoke.xml new file mode 100644 index 0000000..a522cc1 --- /dev/null +++ b/readme/hub-and-spoke.xml @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/readme/hub-and-spoke1.png b/readme/hub-and-spoke1.png new file mode 100644 index 0000000..50b2196 Binary files /dev/null and b/readme/hub-and-spoke1.png differ diff --git a/readme/hub-and-spoke2.png b/readme/hub-and-spoke2.png new file mode 100644 index 0000000..06fd083 Binary files /dev/null and b/readme/hub-and-spoke2.png differ