Skip to content

Commit f59639f

Browse files
committed
feat: support async provisioning (202) and admin-only instances panel
- Accept HTTP 202 from challenge manager API when instance is provisioning - Skip caching instance data while it is still deploying (locked=True) - Restrict the instances panel route to admins only - Switch from register_user_page_menu_bar to register_admin_plugin_menu_bar - Show tofu version on the admin settings page
1 parent ec6d91b commit f59639f

2 files changed

Lines changed: 20 additions & 10 deletions

File tree

__init__.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
from flask import Blueprint, redirect, render_template, request, url_for
1010

1111
from CTFd.plugins import (
12+
register_admin_plugin_menu_bar,
1213
register_plugin_assets_directory,
1314
register_plugin_script,
14-
register_user_page_menu_bar,
1515
)
1616
from CTFd.plugins.challenges import CHALLENGE_CLASSES
1717
from CTFd.plugins.migrations import upgrade
@@ -85,21 +85,30 @@ def load(app): # pylint: disable=too-many-statements
8585
def admin_settings(): # pylint: disable=unused-variable
8686
logger.debug("Accessing admin settings page.")
8787

88+
api_url = get_config("chall-manager:chall-manager_api_url")
89+
8890
try:
8991
logger.debug("getting connection status with chall-manager")
90-
health_url = (
91-
f'{get_config("chall-manager:chall-manager_api_url")}/healthcheck'
92-
)
93-
requests.get(health_url, timeout=5).raise_for_status()
92+
requests.get(f"{api_url}/healthcheck", timeout=5).raise_for_status()
9493
except Exception as e: # pylint: disable=broad-exception-caught
9594
logger.warning("cannot communicate with CM provided got %s", e)
9695
cm_api_reachable = False
9796
else:
9897
logger.info("communication with CM configured successfully")
9998
cm_api_reachable = True
10099

100+
tofu_version = {"data": "unavailable"}
101+
try:
102+
resp = requests.get(f"{api_url}/version", timeout=5)
103+
resp.raise_for_status()
104+
tofu_version = resp.json()
105+
except Exception as e: # pylint: disable=broad-exception-caught
106+
logger.warning("cannot retrieve tofu version from CM: %s", e)
107+
101108
return render_template(
102-
"chall_manager_config.html", cm_api_reachable=cm_api_reachable
109+
"chall_manager_config.html",
110+
cm_api_reachable=cm_api_reachable,
111+
tofu_version=tofu_version,
103112
)
104113

105114
# Route to monitor & manage running instances
@@ -206,7 +215,7 @@ def admin_panel(): # pylint: disable=unused-variable
206215

207216
# Route to monitor & manage running instances
208217
@page_blueprint.route("/instances")
209-
@authed_only
218+
@admins_only
210219
def instances(): # pylint: disable=unused-variable
211220
mana_total = int(get_config("chall-manager:chall-manager_mana_total"))
212221
mana_enabled = mana_total > 0
@@ -303,6 +312,6 @@ def instances(): # pylint: disable=unused-variable
303312
!= "true"
304313
)
305314
if instances_panel_enabled:
306-
register_user_page_menu_bar(
315+
register_admin_plugin_menu_bar(
307316
"Instances", "/plugins/ctfd-chall-manager/instances"
308317
)

utils/instance_manager.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,16 @@ def create_instance(
5151
message="an exception occurred while communicating with CM"
5252
) from e
5353

54-
if r.status_code != 200:
54+
if r.status_code not in (200, 202):
5555
error_data = r.json()
5656
message = error_data.get("message", "Unknown error")
5757
logger.error("chall-manager returned an error: %s", message)
5858
raise ChallManagerException(message=message)
5959

6060
result = r.json()
6161
instance_data = result.get("data", result)
62-
62+
63+
# Never cache a deploying instance — the frontend must poll for fresh state
6364
if not instance_data.get("locked"):
6465
cache.set(cache_key, instance_data, timeout=60)
6566
logger.debug("cached instance data")

0 commit comments

Comments
 (0)