From ea16f75cca29889213c5285351c4f848274b4075 Mon Sep 17 00:00:00 2001 From: Tibor Frank Date: Mon, 20 May 2024 07:59:30 +0000 Subject: [PATCH] C-Dash: Enable reading of constants from env variables + add switching on/off the apps Change-Id: I46e0cd2f214fd6a05ec0b87a433708bc4b6e2eb1 Signed-off-by: Tibor Frank --- csit.infra.dash/app/.ebextensions/env-flask.config | 16 ++- csit.infra.dash/app/cdash/__init__.py | 108 ++++++++--------- csit.infra.dash/app/cdash/routes.py | 28 +++-- csit.infra.dash/app/cdash/stats/layout.py | 6 +- .../app/cdash/templates/base_layout.jinja2 | 52 ++------- csit.infra.dash/app/cdash/utils/constants.py | 97 +++++++++++++-- csit.infra.dash/app/cdash/utils/utils.py | 130 +++++++++++---------- csit.infra.dash/docker-compose.yaml | 14 +++ 8 files changed, 268 insertions(+), 183 deletions(-) diff --git a/csit.infra.dash/app/.ebextensions/env-flask.config b/csit.infra.dash/app/.ebextensions/env-flask.config index f7ae7c500c..795aa82cdb 100644 --- a/csit.infra.dash/app/.ebextensions/env-flask.config +++ b/csit.infra.dash/app/.ebextensions/env-flask.config @@ -1,4 +1,18 @@ option_settings: aws:elasticbeanstalk:application:environment: FLASK_DEBUG: 0 - FLASK_ENV: production \ No newline at end of file + FLASK_ENV: production + CSIT_START_TRENDING: "True" + CSIT_START_REPORT: "True" + CSIT_START_COMPARISONS: "True" + CSIT_START_COVERAGE: "True" + CSIT_START_STATISTICS: "True" + CSIT_START_FAILURES: "True" + CSIT_START_SEARCH: "True" + CSIT_START_DOC: "True" + CSIT_TITLE: "FD.io CSIT" + CSIT_BRAND: "CSIT-Dash" + CSIT_URL_CICD: "https://jenkins.fd.io/job/" + CSIT_URL_LOGS: "https://logs.fd.io/vex-yul-rot-jenkins-1/" + CSIT_URL_DOC: "https://csit.fd.io/cdocs/" + CSIT_TIME_PERIOD: 250 \ No newline at end of file diff --git a/csit.infra.dash/app/cdash/__init__.py b/csit.infra.dash/app/cdash/__init__.py index 3d3f2001a3..bf761da9ee 100644 --- a/csit.infra.dash/app/cdash/__init__.py +++ b/csit.infra.dash/app/cdash/__init__.py @@ -27,6 +27,7 @@ from .data.data import Data def init_app(): """Construct core Flask application with embedded Dash app. """ + logging.basicConfig( format=C.LOG_FORMAT, datefmt=C.LOG_DATE_FORMAT, @@ -67,76 +68,59 @@ def init_app(): ).read_all_data(days=time_period) # Import Dash applications. + err_msg = "Application not loaded, no data available." logging.info("\n\nStarting the applications:\n" + "-" * 26 + "\n") - if data["statistics"].empty or data["trending"].empty: - logging.error( - f'"{C.NEWS_TITLE}" application not loaded, no data available.' - ) - logging.error( - f'"{C.STATS_TITLE}" application not loaded, no data available.' - ) - else: - logging.info(C.NEWS_TITLE) - from .news.news import init_news - app = init_news( - app, - data_stats=data["statistics"], - data_trending=data["trending"] - ) + if C.START_FAILURES: + logging.info(C.NEWS_TITLE) + if data["statistics"].empty or data["trending"].empty: + logging.error(err_msg) + else: + from .news.news import init_news + app = init_news(app, data["statistics"], data["trending"]) + if C.START_STATISTICS: logging.info(C.STATS_TITLE) - from .stats.stats import init_stats - app = init_stats( - app, - data_stats=data["statistics"], - data_trending=data["trending"] - ) - - if data["trending"].empty: - logging.error( - f'"{C.TREND_TITLE}" application not loaded, no data available.' - ) - else: + if data["statistics"].empty or data["trending"].empty: + logging.error(err_msg) + else: + from .stats.stats import init_stats + app = init_stats(app, data["statistics"], data["trending"]) + if C.START_TRENDING: logging.info(C.TREND_TITLE) - from .trending.trending import init_trending - app = init_trending(app, data_trending=data["trending"]) - - if data["iterative"].empty: - logging.error( - f'"{C.REPORT_TITLE}" application not loaded, no data available.' - ) - logging.error( - f'"{C.COMP_TITLE}" application not loaded, no data available.' - ) - else: + if data["trending"].empty: + logging.error(err_msg) + else: + from .trending.trending import init_trending + app = init_trending(app, data["trending"]) + if C.START_REPORT: logging.info(C.REPORT_TITLE) - from .report.report import init_report - app = init_report(app, data_iterative=data["iterative"]) - + if data["iterative"].empty: + logging.error(err_msg) + else: + from .report.report import init_report + app = init_report(app, data["iterative"]) + if C.START_COMPARISONS: logging.info(C.COMP_TITLE) - from .comparisons.comparisons import init_comparisons - app = init_comparisons(app, data_iterative=data["iterative"]) - - if data["coverage"].empty: - logging.error(( - f'"{C.COVERAGE_TITLE}" application not loaded, ' - 'no data available.' - )) - else: + if data["iterative"].empty: + logging.error(err_msg) + else: + from .comparisons.comparisons import init_comparisons + app = init_comparisons(app, data["iterative"]) + if C.START_COVERAGE: logging.info(C.COVERAGE_TITLE) - from .coverage.coverage import init_coverage - app = init_coverage(app, data_coverage=data["coverage"]) - - if all((data["trending"].empty, data["iterative"].empty, - data["coverage"].empty)): - logging.error(( - f'"{C.SEARCH_TITLE}" application not loaded, ' - 'no data available.' - )) - else: + if data["coverage"].empty: + logging.error(err_msg) + else: + from .coverage.coverage import init_coverage + app = init_coverage(app, data["coverage"]) + if C.START_SEARCH: logging.info(C.SEARCH_TITLE) - from .search.search import init_search - app = init_search(app, data) + if all((data["trending"].empty, data["iterative"].empty, + data["coverage"].empty)): + logging.error(err_msg) + else: + from .search.search import init_search + app = init_search(app, data) return app diff --git a/csit.infra.dash/app/cdash/routes.py b/csit.infra.dash/app/cdash/routes.py index ed29fffa12..422bd1ab2e 100644 --- a/csit.infra.dash/app/cdash/routes.py +++ b/csit.infra.dash/app/cdash/routes.py @@ -24,15 +24,29 @@ from .utils.constants import Constants as C def home(): """Landing page. """ + + menu_itms = list() + if C.START_TRENDING: + menu_itms.append({"path": "/trending/", "title": C.TREND_TITLE}) + if C.START_REPORT: + menu_itms.append({"path": "/report/", "title": C.REPORT_TITLE}) + if C.START_COMPARISONS: + menu_itms.append({"path": "/comparisons/", "title": C.COMP_TITLE}) + if C.START_COVERAGE: + menu_itms.append({"path": "/coverage/", "title": C.COVERAGE_TITLE}) + if C.START_STATISTICS: + menu_itms.append({"path": "/stats/", "title": C.STATS_TITLE}) + if C.START_FAILURES: + menu_itms.append({"path": "/news/", "title": C.NEWS_TITLE}) + if C.START_SEARCH: + menu_itms.append({"path": "/search/", "title": C.SEARCH_TITLE}) + if C.START_DOC: + menu_itms.append({"path": "/cdocs/", "title": C.DOC_TITLE}) + return render_template( C.MAIN_HTML_LAYOUT_FILE, title=C.TITLE, + brand=C.BRAND, description=C.DESCRIPTION, - trending_title=C.TREND_TITLE, - report_title=C.REPORT_TITLE, - comp_title=C.COMP_TITLE, - stats_title=C.STATS_TITLE, - news_title=C.NEWS_TITLE, - cov_title=C.COVERAGE_TITLE, - search_title=C.SEARCH_TITLE + menu_itms=menu_itms ) diff --git a/csit.infra.dash/app/cdash/stats/layout.py b/csit.infra.dash/app/cdash/stats/layout.py index 655c61c078..616a4028e6 100644 --- a/csit.infra.dash/app/cdash/stats/layout.py +++ b/csit.infra.dash/app/cdash/stats/layout.py @@ -196,7 +196,7 @@ class Layout: "dd-tbeds-value": self._default["tbed"], "al-job-children": html.A( self._default["job"], - href=f"{C.URL_JENKINS}{self._default['job']}", + href=f"{C.URL_CICD}{self._default['job']}", target="_blank" ) } @@ -631,7 +631,7 @@ class Layout: "al-job-children": html.A( self._default["job"], href=( - f"{C.URL_JENKINS}" + f"{C.URL_CICD}" f"{self._default['job']}" ), target="_blank" @@ -654,7 +654,7 @@ class Layout: { "al-job-children": html.A( job, - href=f"{C.URL_JENKINS}{job}", + href=f"{C.URL_CICD}{job}", target="_blank" ) } diff --git a/csit.infra.dash/app/cdash/templates/base_layout.jinja2 b/csit.infra.dash/app/cdash/templates/base_layout.jinja2 index 7b0dadc5a0..97a71e358f 100644 --- a/csit.infra.dash/app/cdash/templates/base_layout.jinja2 +++ b/csit.infra.dash/app/cdash/templates/base_layout.jinja2 @@ -6,7 +6,7 @@

- CSIT-Dash + {{ brand }}

@@ -20,52 +20,18 @@ {{ description }}

-

- - {{ trending_title }} - -

-

- - {{ report_title }} - -

-

- - {{ comp_title }} - -

-

-

- - {{ cov_title }} - -

-

- - {{ stats_title }} - -

-

- - {{ news_title }} - -

-

- - {{ search_title }} - -

-

- - Documentation - -

+ {% for itm in menu_itms %} +

+ + {{ itm['title'] }} + +

+ {% endfor %}

{% endblock %} diff --git a/csit.infra.dash/app/cdash/utils/constants.py b/csit.infra.dash/app/cdash/utils/constants.py index 3db0d50f4f..bafa7b7f42 100644 --- a/csit.infra.dash/app/cdash/utils/constants.py +++ b/csit.infra.dash/app/cdash/utils/constants.py @@ -17,11 +17,75 @@ does not need to be hard coded here, but can be read from environment variables. """ +import os import logging from dash import html +def get_str_from_env(env_var_name: str, default_value: str) -> str: + """Attempt to read string from environment variable, return that or default. + + The environment variable must start with perfix "CSIT_". + + If environment variable exists, but is empty (and default is not), + empty string is returned. + + :param env_var_name: Base name of environment variable to attempt to read. + :param default_value: Value to return if the env var does not exist. + :type env_var_names: str + :type default_value: str + :returns: The value read, or default value. + :rtype: str + """ + prefix = "CSIT_" + env_str = os.environ.get(prefix + env_var_name, None) + if env_str is not None: + return env_str + return default_value + + +def get_int_from_env(env_var_name: str, default_value: int) -> int: + """Attempt to read int from environment variable, return that or default. + + The environment variable must start with perfix "CSIT_". + + String value is read, default is returned also if conversion fails. + + :param env_var_name: Base name of environment variable to attempt to read. + :param default_value: Value to return if read or conversion fails. + :type env_var_names: str + :type default_value: int + :returns: The value read, or default value. + :rtype: int + """ + try: + return int(get_str_from_env(env_var_name, str())) + except ValueError: + return default_value + + +def get_bool_from_env(env_var_name: str, default_value: bool) -> bool: + """Attempt to read bool from environment variable, return that or default. + + The environment variable must start with perfix "CSIT_". + + :param env_var_name: Base name of environment variable to attempt to read. + :param default_value: Value to return if read or conversion fails. + :type env_var_names: str + :type default_value: bool + :returns: The value read, or default value. + :rtype: bool + """ + env_str = get_str_from_env(env_var_name, str()).lower() + if env_str in ("true", "yes", "y", "1"): + return True + elif env_str in ("false", "no", "n", "0"): + return False + else: + return default_value + + class Constants: """Constants used in CDash. """ @@ -29,14 +93,24 @@ class Constants: ############################################################################ # General, application wide constants. + # Select applications to start. + START_TRENDING = get_bool_from_env("START_TRENDING", True) + START_REPORT = get_bool_from_env("START_REPORT", True) + START_COMPARISONS = get_bool_from_env("START_COMPARISONS", True) + START_COVERAGE = get_bool_from_env("START_COVERAGE", True) + START_STATISTICS = get_bool_from_env("START_STATISTICS", True) + START_FAILURES = get_bool_from_env("START_FAILURES", True) + START_SEARCH = get_bool_from_env("START_SEARCH", True) + START_DOC = get_bool_from_env("START_DOC", True) + # Logging settings. LOG_LEVEL = logging.INFO LOG_FORMAT = "%(asctime)s: %(levelname)s: %(message)s" LOG_DATE_FORMAT = "%Y/%m/%d %H:%M:%S" # The application title. - TITLE = "FD.io CSIT" - BRAND = "CSIT-Dash" + TITLE = get_str_from_env("TITLE", "FD.io CSIT") + BRAND = get_str_from_env("BRAND", "CSIT-Dash") # The application description. DESCRIPTION = "Performance Dashboard" @@ -45,14 +119,17 @@ class Constants: EXTERNAL_STYLESHEETS = ["/static/dist/css/bootstrap.css", ] # URL to Jenkins - URL_JENKINS = "https://jenkins.fd.io/job/" + URL_CICD = get_str_from_env("URL_CICD", "https://jenkins.fd.io/job/") # URL to logs - URL_LOGS = "https://logs.fd.io/vex-yul-rot-jenkins-1/" + URL_LOGS = get_str_from_env( + "URL_LOGS", "https://logs.fd.io/vex-yul-rot-jenkins-1/" + ) # URL to the documentation - URL_DOC_TRENDING = "https://csit.fd.io/cdocs/methodology/trending/analysis/" - URL_DOC_REL_NOTES = "https://csit.fd.io/cdocs/release_notes/current/" + URL_DOC = get_str_from_env("URL_DOC", "https://csit.fd.io/cdocs/") + URL_DOC_TRENDING = URL_DOC + "methodology/trending/analysis/" + URL_DOC_REL_NOTES = URL_DOC + "release_notes/current/" # Path and name of the file specifying the HTML layout of the dash # application. @@ -82,7 +159,7 @@ class Constants: # now back to the past. # TIME_PERIOD = None - means all data (max MAX_TIME_PERIOD days) is read. # TIME_PERIOD = MAX_TIME_PERIOD - is the default value - TIME_PERIOD = MAX_TIME_PERIOD # [days] + TIME_PERIOD = get_int_from_env("TIME_PERIOD", MAX_TIME_PERIOD) # [days] ############################################################################ # General, application wide, layout affecting constants. @@ -467,3 +544,9 @@ class Constants: SEARCH_DOWNLOAD_FILE_NAME = "search_data.csv" ############################################################################ + # Documentation. + + # The title. + DOC_TITLE = "Documentation" + + ############################################################################ diff --git a/csit.infra.dash/app/cdash/utils/utils.py b/csit.infra.dash/app/cdash/utils/utils.py index 306b4f60d1..e203dfbccd 100644 --- a/csit.infra.dash/app/cdash/utils/utils.py +++ b/csit.infra.dash/app/cdash/utils/utils.py @@ -480,37 +480,42 @@ def navbar_trending(active: tuple): :returns: Navigation bar. :rtype: dbc.NavbarSimple """ + children = list() + if C.START_TRENDING: + children.append(dbc.NavItem(dbc.NavLink( + C.TREND_TITLE, + active=active[0], + external_link=True, + href="/trending" + ))) + if C.START_FAILURES: + children.append(dbc.NavItem(dbc.NavLink( + C.NEWS_TITLE, + active=active[1], + external_link=True, + href="/news" + ))) + if C.START_STATISTICS: + children.append(dbc.NavItem(dbc.NavLink( + C.STATS_TITLE, + active=active[2], + external_link=True, + href="/stats" + ))) + if C.START_SEARCH: + children.append(dbc.NavItem(dbc.NavLink( + C.SEARCH_TITLE, + active=active[3], + external_link=True, + href="/search" + ))) + if C.START_DOC: + children.append(dbc.NavItem(dbc.NavLink( + "Documentation", + id="btn-documentation", + ))) return dbc.NavbarSimple( - children=[ - dbc.NavItem(dbc.NavLink( - C.TREND_TITLE, - active=active[0], - external_link=True, - href="/trending" - )), - dbc.NavItem(dbc.NavLink( - C.NEWS_TITLE, - active=active[1], - external_link=True, - href="/news" - )), - dbc.NavItem(dbc.NavLink( - C.STATS_TITLE, - active=active[2], - external_link=True, - href="/stats" - )), - dbc.NavItem(dbc.NavLink( - C.SEARCH_TITLE, - active=active[3], - external_link=True, - href="/search" - )), - dbc.NavItem(dbc.NavLink( - "Documentation", - id="btn-documentation", - )) - ], + children=children, id="navbarsimple-main", brand=C.BRAND, brand_href="/", @@ -529,38 +534,43 @@ def navbar_report(active: tuple): :returns: Navigation bar. :rtype: dbc.NavbarSimple """ + children = list() + if C.START_REPORT: + children.append(dbc.NavItem(dbc.NavLink( + C.REPORT_TITLE, + active=active[0], + external_link=True, + href="/report" + ))) + if C.START_COMPARISONS: + children.append(dbc.NavItem(dbc.NavLink( + "Comparisons", + active=active[1], + external_link=True, + href="/comparisons" + ))) + if C.START_COVERAGE: + children.append(dbc.NavItem(dbc.NavLink( + "Coverage Data", + active=active[2], + external_link=True, + href="/coverage" + ))) + if C.START_SEARCH: + children.append(dbc.NavItem(dbc.NavLink( + C.SEARCH_TITLE, + active=active[3], + external_link=True, + href="/search" + ))) + if C.START_DOC: + children.append(dbc.NavItem(dbc.NavLink( + "Documentation", + id="btn-documentation", + ))) return dbc.NavbarSimple( + children=children, id="navbarsimple-main", - children=[ - dbc.NavItem(dbc.NavLink( - C.REPORT_TITLE, - active=active[0], - external_link=True, - href="/report" - )), - dbc.NavItem(dbc.NavLink( - "Comparisons", - active=active[1], - external_link=True, - href="/comparisons" - )), - dbc.NavItem(dbc.NavLink( - "Coverage Data", - active=active[2], - external_link=True, - href="/coverage" - )), - dbc.NavItem(dbc.NavLink( - C.SEARCH_TITLE, - active=active[3], - external_link=True, - href="/search" - )), - dbc.NavItem(dbc.NavLink( - "Documentation", - id="btn-documentation", - )) - ], brand=C.BRAND, brand_href="/", brand_external_link=True, diff --git a/csit.infra.dash/docker-compose.yaml b/csit.infra.dash/docker-compose.yaml index 5ed71345fb..a3b5e1a683 100644 --- a/csit.infra.dash/docker-compose.yaml +++ b/csit.infra.dash/docker-compose.yaml @@ -6,6 +6,20 @@ services: environment: FLASK_DEBUG: 1 FLASK_ENV: "development" + CSIT_START_TRENDING: "True" + CSIT_START_REPORT: "True" + CSIT_START_COMPARISONS: "True" + CSIT_START_COVERAGE: "True" + CSIT_START_STATISTICS: "True" + CSIT_START_FAILURES: "True" + CSIT_START_SEARCH: "True" + CSIT_START_DOC: "True" + CSIT_TITLE: "FD.io CSIT" + CSIT_BRAND: "CSIT-Dash" + CSIT_URL_CICD: "https://jenkins.fd.io/job/" + CSIT_URL_LOGS: "https://logs.fd.io/vex-yul-rot-jenkins-1/" + CSIT_URL_DOC: "https://csit.fd.io/cdocs/" + CSIT_TIME_PERIOD: 250 mem_limit: "16g" ports: - "5000:5000" -- 2.16.6