Repository: incubator-airflow
Updated Branches:
  refs/heads/master a504a8fe1 -> 702a57ec5


[AIRFLOW-2657] Add ability to delete dag from web UI

Closes #3531 from Noremac201/master


Project: http://git-wip-us.apache.org/repos/asf/incubator-airflow/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-airflow/commit/702a57ec
Tree: http://git-wip-us.apache.org/repos/asf/incubator-airflow/tree/702a57ec
Diff: http://git-wip-us.apache.org/repos/asf/incubator-airflow/diff/702a57ec

Branch: refs/heads/master
Commit: 702a57ec5a96d159105c4f5ca76ddd2229eb2f44
Parents: a504a8f
Author: Cameron Moberg <[email protected]>
Authored: Fri Jun 22 23:18:36 2018 +0100
Committer: Kaxil Naik <[email protected]>
Committed: Fri Jun 22 23:18:36 2018 +0100

----------------------------------------------------------------------
 airflow/www/templates/airflow/dag.html       | 13 ++++++++++
 airflow/www/templates/airflow/dags.html      | 12 +++++++++
 airflow/www/views.py                         | 30 +++++++++++++++++++++++
 airflow/www_rbac/templates/airflow/dag.html  | 13 ++++++++++
 airflow/www_rbac/templates/airflow/dags.html | 11 +++++++++
 airflow/www_rbac/views.py                    | 30 +++++++++++++++++++++++
 6 files changed, 109 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-airflow/blob/702a57ec/airflow/www/templates/airflow/dag.html
----------------------------------------------------------------------
diff --git a/airflow/www/templates/airflow/dag.html 
b/airflow/www/templates/airflow/dag.html
index adb2d38..9ef4cce 100644
--- a/airflow/www/templates/airflow/dag.html
+++ b/airflow/www/templates/airflow/dag.html
@@ -100,6 +100,13 @@
           Refresh
         </a>
       </li>
+      <li>
+        <a href="{{ url_for("airflow.delete", dag_id=dag.dag_id, root=root) }}"
+        onclick="return confirmDeleteDag('{{ dag.safe_dag_id }}')">
+        <span class="glyphicon glyphicon-remove-circle" style="color:red" 
aria-hidden="true"></span>
+        Delete
+        </a>
+      </li>
     </ul>
   </div>
   <hr>
@@ -302,6 +309,12 @@ function updateQueryStringParameter(uri, key, value) {
       $("#dagModal").css("margin-top","0px");
     }
 
+    function confirmDeleteDag(dag_id){
+        return confirm("Are you sure you want to delete '"+dag_id+"' now?\n\
+          This option will delete ALL metadata, DAG runs, etc.\n\
+          This cannot be undone.");
+    }
+
     $("#btn_rendered").click(function(){
       url = "{{ url_for('airflow.rendered') }}" +
         "?task_id=" + encodeURIComponent(task_id) +

http://git-wip-us.apache.org/repos/asf/incubator-airflow/blob/702a57ec/airflow/www/templates/airflow/dags.html
----------------------------------------------------------------------
diff --git a/airflow/www/templates/airflow/dags.html 
b/airflow/www/templates/airflow/dags.html
index 2397890..8a86948 100644
--- a/airflow/www/templates/airflow/dags.html
+++ b/airflow/www/templates/airflow/dags.html
@@ -190,6 +190,12 @@
                   <span class="glyphicon glyphicon-refresh" aria-hidden="true" 
data-original-title="Refresh"></span>
                 </a>
 
+                <!-- Delete -->
+                <a href="{{ url_for('airflow.delete', dag_id=dag.dag_id) }}"
+                  onclick="return confirmDeleteDag('{{ dag.safe_dag_id }}')">
+                   <span class="glyphicon glyphicon-remove-circle" 
style="color:red" aria-hidden="true" data-original-title="Delete Dag"></span>
+                </a>
+
                 </td>
             </tr>
         {% endfor %}
@@ -244,6 +250,12 @@
       function confirmTriggerDag(dag_id){
           return confirm("Are you sure you want to run '"+dag_id+"' now?");
       }
+
+      function confirmDeleteDag(dag_id){
+          return confirm("Are you sure you want to delete '"+dag_id+"' now?\n\
+          This option will delete ALL metadata, DAG runs, etc.\n\
+          This cannot be undone.");
+      }
       all_dags = $("[id^=toggle]");
       $.each(all_dags, function(i,v) {
         $(v).change (function() {

http://git-wip-us.apache.org/repos/asf/incubator-airflow/blob/702a57ec/airflow/www/views.py
----------------------------------------------------------------------
diff --git a/airflow/www/views.py b/airflow/www/views.py
index c8baa5c..5c0c973 100644
--- a/airflow/www/views.py
+++ b/airflow/www/views.py
@@ -1044,6 +1044,32 @@ class Airflow(BaseView):
             "it should start any moment now.".format(ti))
         return redirect(origin)
 
+    @expose('/delete')
+    @login_required
+    @wwwutils.action_logging
+    @wwwutils.notify_owner
+    def delete(self):
+        from airflow.api.common.experimental import delete_dag
+        from airflow.exceptions import DagNotFound, DagFileExists
+
+        dag_id = request.args.get('dag_id')
+        origin = request.args.get('origin') or "/admin/"
+
+        try:
+            delete_dag.delete_dag(dag_id)
+        except DagNotFound:
+            flash("DAG with id {} not found. Cannot delete".format(dag_id))
+            return redirect(request.referrer)
+        except DagFileExists:
+            flash("Dag id {} is still in DagBag. "
+                  "Remove the DAG file first.".format(dag_id))
+            return redirect(request.referrer)
+
+        flash("Deleting DAG with id {}. May take a couple minutes to fully"
+              " disappear.".format(dag_id))
+        # Upon successful delete return to origin
+        return redirect(origin)
+
     @expose('/trigger')
     @login_required
     @wwwutils.action_logging
@@ -1283,6 +1309,10 @@ class Airflow(BaseView):
         dag_id = request.args.get('dag_id')
         blur = conf.getboolean('webserver', 'demo_mode')
         dag = dagbag.get_dag(dag_id)
+        if dag_id not in dagbag.dags:
+            flash('DAG "{0}" seems to be missing.'.format(dag_id), "error")
+            return redirect('/admin/')
+
         root = request.args.get('root')
         if root:
             dag = dag.sub_dag(

http://git-wip-us.apache.org/repos/asf/incubator-airflow/blob/702a57ec/airflow/www_rbac/templates/airflow/dag.html
----------------------------------------------------------------------
diff --git a/airflow/www_rbac/templates/airflow/dag.html 
b/airflow/www_rbac/templates/airflow/dag.html
index eb44908..e6495fe 100644
--- a/airflow/www_rbac/templates/airflow/dag.html
+++ b/airflow/www_rbac/templates/airflow/dag.html
@@ -99,6 +99,13 @@
           Refresh
         </a>
       </li>
+      <li>
+        <a href="{{ url_for('Airflow.delete', dag_id=dag.dag_id, root=root) }}"
+        onclick="return confirmDeleteDag('{{ dag.safe_dag_id }}')">
+        <span class="glyphicon glyphicon-remove-circle" style="color:red" 
aria-hidden="true"></span>
+        Delete
+        </a>
+      </li>
     </ul>
   </div>
   <hr>
@@ -300,6 +307,12 @@ function updateQueryStringParameter(uri, key, value) {
       $("#dagModal").css("margin-top","0px");
     }
 
+    function confirmDeleteDag(dag_id){
+        return confirm("Are you sure you want to delete '"+dag_id+"' now?\n\
+          This option will delete ALL metadata, DAG runs, etc.\n\
+          This cannot be undone.");
+    }
+
     $("#btn_rendered").click(function(){
       url = "{{ url_for('Airflow.rendered') }}" +
         "?task_id=" + encodeURIComponent(task_id) +

http://git-wip-us.apache.org/repos/asf/incubator-airflow/blob/702a57ec/airflow/www_rbac/templates/airflow/dags.html
----------------------------------------------------------------------
diff --git a/airflow/www_rbac/templates/airflow/dags.html 
b/airflow/www_rbac/templates/airflow/dags.html
index a712e5a..ed11a56 100644
--- a/airflow/www_rbac/templates/airflow/dags.html
+++ b/airflow/www_rbac/templates/airflow/dags.html
@@ -191,6 +191,11 @@
                   <span class="glyphicon glyphicon-refresh" aria-hidden="true" 
data-original-title="Refresh"></span>
                 </a>
 
+                <!-- Delete -->
+                <a href="{{ url_for('Airflow.delete', dag_id=dag.dag_id) }}"
+                   onclick="return confirmDeleteDag('{{ dag.safe_dag_id }}')">
+                  <span class="glyphicon glyphicon-remove-circle" 
style="color:red" aria-hidden="true" data-original-title="Delete Dag"></span>
+                </a>
                 </td>
             </tr>
         {% endfor %}
@@ -242,6 +247,12 @@
         window.location = DAGS_INDEX + "?page_size=" + p_size;
       });
 
+      function confirmDeleteDag(dag_id){
+          return confirm("Are you sure you want to delete '"+dag_id+"' now?\n\
+          This option will delete ALL metadata, DAG runs, etc.\n\
+          This cannot be undone.");
+      }
+
       function confirmTriggerDag(dag_id){
           return confirm("Are you sure you want to run '"+dag_id+"' now?");
       }

http://git-wip-us.apache.org/repos/asf/incubator-airflow/blob/702a57ec/airflow/www_rbac/views.py
----------------------------------------------------------------------
diff --git a/airflow/www_rbac/views.py b/airflow/www_rbac/views.py
index 91bd177..78f9799 100644
--- a/airflow/www_rbac/views.py
+++ b/airflow/www_rbac/views.py
@@ -728,6 +728,32 @@ class Airflow(AirflowBaseView):
             "it should start any moment now.".format(ti))
         return redirect(origin)
 
+    @expose('/delete')
+    @action_logging
+    @has_access
+    def delete(self):
+        from airflow.api.common.experimental import delete_dag
+        from airflow.exceptions import DagNotFound, DagFileExists
+
+        dag_id = request.args.get('dag_id')
+        origin = request.args.get('origin') or "/"
+
+        try:
+            delete_dag.delete_dag(dag_id)
+        except DagNotFound:
+            flash("DAG with id {} not found. Cannot delete".format(dag_id))
+            return redirect(request.referrer)
+        except DagFileExists:
+            flash("Dag id {} is still in DagBag. "
+                  "Remove the DAG file first.".format(dag_id))
+            return redirect(request.referrer)
+
+        flash("Deleting DAG with id {}. May take a couple minutes to fully"
+              " disappear.".format(dag_id))
+
+        # Upon success return to origin.
+        return redirect(origin)
+
     @expose('/trigger')
     @has_access
     @action_logging
@@ -961,6 +987,10 @@ class Airflow(AirflowBaseView):
         dag_id = request.args.get('dag_id')
         blur = conf.getboolean('webserver', 'demo_mode')
         dag = dagbag.get_dag(dag_id)
+        if dag_id not in dagbag.dags:
+            flash('DAG "{0}" seems to be missing.'.format(dag_id), "error")
+            return redirect('/')
+
         root = request.args.get('root')
         if root:
             dag = dag.sub_dag(

Reply via email to