diff --git a/brian_dashboard_manager/config.py b/brian_dashboard_manager/config.py index c5a828e0573c376726a1ad02e6d8655eb2d9dbbc..e167231f1208f3c27f304d4b1bb6fc1ca63bf721 100644 --- a/brian_dashboard_manager/config.py +++ b/brian_dashboard_manager/config.py @@ -30,6 +30,8 @@ If the value is a list, dashboard titles within the list should be excluded. import json import jsonschema +STATE_PATH = '/tmp/briandashboardmanager-state.json' + DEFAULT_ORGANIZATIONS = [ { "name": "GÉANT Staff", diff --git a/brian_dashboard_manager/grafana/provision.py b/brian_dashboard_manager/grafana/provision.py index b16f773d63e84518465a179471c7a96d2a3e6608..685be93971bbd9b49ea946a4ea1748331c4ee79a 100644 --- a/brian_dashboard_manager/grafana/provision.py +++ b/brian_dashboard_manager/grafana/provision.py @@ -4,8 +4,10 @@ entire provisioning lifecycle. """ import logging import time +import json +import datetime from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor -from brian_dashboard_manager.config import DEFAULT_ORGANIZATIONS +from brian_dashboard_manager.config import DEFAULT_ORGANIZATIONS, STATE_PATH from brian_dashboard_manager.grafana.utils.request import \ AdminRequest, \ TokenRequest @@ -94,6 +96,23 @@ def provision_aggregate(token_request, agg_type, aggregate_folder, create_dashboard(token_request, rendered, aggregate_folder['id']) +def provision_maybe(config): + with open(STATE_PATH, 'r+') as f: + def write_timestamp(timestamp, provisioning): + f.seek(0) + f.write(json.dumps( + {'timestamp': timestamp, 'provisioning': provisioning})) + f.truncate() + + try: + now = datetime.datetime.now() + write_timestamp(now.timestamp(), True) + provision(config) + finally: + now = datetime.datetime.now() + write_timestamp(now.timestamp(), False) + + def provision(config): request = AdminRequest(**config) diff --git a/brian_dashboard_manager/routes/update.py b/brian_dashboard_manager/routes/update.py index ed538e41ebe6f161dea5235e464537b0a8ac3908..780d40d6affb7aa7784273df23d6760e2830902f 100644 --- a/brian_dashboard_manager/routes/update.py +++ b/brian_dashboard_manager/routes/update.py @@ -1,8 +1,14 @@ +import json +import datetime +from flask import jsonify, Response from concurrent.futures import ThreadPoolExecutor +from json.decoder import JSONDecodeError from flask import Blueprint, current_app from brian_dashboard_manager.routes import common -from brian_dashboard_manager.grafana.provision import provision +from brian_dashboard_manager.grafana.provision import provision_maybe from brian_dashboard_manager import CONFIG_KEY +from brian_dashboard_manager.config import STATE_PATH + routes = Blueprint("update", __name__) @@ -22,6 +28,25 @@ def after_request(resp): return common.after_request(resp) +def should_provision(): + try: + with open(STATE_PATH, 'r+') as f: + try: + state = json.load(f) + except JSONDecodeError: + state = {} + + provisioning = state.get('provisioning', False) + timestamp = datetime.datetime.fromtimestamp( + state.get('timestamp', 1)) + + can_provision = not provisioning + return can_provision, timestamp + except FileNotFoundError: + with open(STATE_PATH, 'w') as f: + return True, None + + @routes.route('/', methods=['GET']) def update(): """ @@ -37,6 +62,11 @@ def update(): :return: json """ - executor = ThreadPoolExecutor(max_workers=1) - executor.submit(provision, current_app.config[CONFIG_KEY]) - return {'data': {'message': 'Provisioning dashboards!'}} + should, timestamp = should_provision() + if should: + executor = ThreadPoolExecutor(max_workers=1) + executor.submit(provision_maybe, current_app.config[CONFIG_KEY]) + return jsonify({'data': {'message': 'Provisioning dashboards!'}}) + else: + message = f'Provision already in progress since {timestamp}' + return Response(message, status=503)