From d8f647cdef5aa526cbf3732aa47e03817b476f83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Anh=20B=C3=ACnh?= Date: Wed, 20 Mar 2024 21:42:26 +0700 Subject: [PATCH] [#38284] Update Confirmation Logic for Config Changes on Sensitive Environments Like Production (#38299) * - [#38284] Update Confirmation Logic for Dag status Changes on Sensitive Environments Like Production * - Update pause/unpause confirm messages * - Not version added yet * - Fix static check * - Ruff check * Revert "- Fix static check" This reverts commit cccfeb757ab1d6f8ac35a6730ae6cc1c7a9a2edc. * - Fix ruff check --- airflow/config_templates/config.yml | 9 +++++++++ airflow/www/app.py | 4 ++++ airflow/www/extensions/init_appbuilder.py | 13 +++++++++++++ airflow/www/static/js/dag.js | 10 ++++++++++ airflow/www/templates/airflow/dag.html | 12 +++++++++++- 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/airflow/config_templates/config.yml b/airflow/config_templates/config.yml index 2fc72cbb2d9f1..0a1b292fdc59f 100644 --- a/airflow/config_templates/config.yml +++ b/airflow/config_templates/config.yml @@ -1943,6 +1943,15 @@ webserver: type: float example: ~ default: "1.0" + require_confirmation_dag_change: + description: | + Require confirmation when changing a DAG in the web UI. This is to prevent accidental changes + to a DAG that may be running on sensitive environments like production. + When set to True, confirmation dialog will be shown when a user tries to Pause/Unpause, Trigger a DAG + version_added: ~ + type: boolean + example: ~ + default: "False" email: description: | Configuration email backend and whether to diff --git a/airflow/www/app.py b/airflow/www/app.py index 749efe8912c03..17a4c681ddd3c 100644 --- a/airflow/www/app.py +++ b/airflow/www/app.py @@ -86,6 +86,9 @@ def create_app(config=None, testing=False): flask_app.config["SQLALCHEMY_DATABASE_URI"] = conf.get("database", "SQL_ALCHEMY_CONN") instance_name = conf.get(section="webserver", key="instance_name", fallback="Airflow") + require_confirmation_dag_change = conf.getboolean( + section="webserver", key="require_confirmation_dag_change", fallback=False + ) instance_name_has_markup = conf.getboolean( section="webserver", key="instance_name_has_markup", fallback=False ) @@ -93,6 +96,7 @@ def create_app(config=None, testing=False): instance_name = Markup(instance_name).striptags() flask_app.config["APP_NAME"] = instance_name + flask_app.config["REQUIRE_CONFIRMATION_DAG_CHANGE"] = require_confirmation_dag_change url = make_url(flask_app.config["SQLALCHEMY_DATABASE_URI"]) if url.drivername == "sqlite" and url.database and not url.database.startswith("/"): diff --git a/airflow/www/extensions/init_appbuilder.py b/airflow/www/extensions/init_appbuilder.py index ed972af271510..7bb71ba9804fd 100644 --- a/airflow/www/extensions/init_appbuilder.py +++ b/airflow/www/extensions/init_appbuilder.py @@ -293,6 +293,19 @@ def app_name(self): """ return self.get_app.config["APP_NAME"] + @property + def require_confirmation_dag_change(self): + """Get the value of the require_confirmation_dag_change configuration. + + The logic is: + - return True, in page dag.html, when user trigger/pause the dag from UI. + Once confirmation box will be shown before triggering the dag. + - Default value is False. + + :return: Boolean + """ + return self.get_app.config["REQUIRE_CONFIRMATION_DAG_CHANGE"] + @property def app_theme(self): """ diff --git a/airflow/www/static/js/dag.js b/airflow/www/static/js/dag.js index 39a26f28e63f4..b70587a70cfe8 100644 --- a/airflow/www/static/js/dag.js +++ b/airflow/www/static/js/dag.js @@ -116,6 +116,16 @@ $("#pause_resume").on("change", function onChange() { const $input = $(this); const id = $input.data("dag-id"); const isPaused = $input.is(":checked"); + const requireConfirmation = $input.data("require-confirmation"); + if (requireConfirmation) { + const confirmation = window.confirm( + `Are you sure you want to ${isPaused ? "resume" : "pause"} this DAG?` + ); + if (!confirmation) { + $input.prop("checked", !isPaused); + return; + } + } const url = `${pausedUrl}?is_paused=${isPaused}&dag_id=${encodeURIComponent( id )}`; diff --git a/airflow/www/templates/airflow/dag.html b/airflow/www/templates/airflow/dag.html index 6f5a187f61f68..a09fa7ffa6494 100644 --- a/airflow/www/templates/airflow/dag.html +++ b/airflow/www/templates/airflow/dag.html @@ -123,6 +123,7 @@

{% endif %}