You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by bb...@apache.org on 2022/04/09 18:57:49 UTC

[airflow] 01/02: Add Xcom button, hide map index actions, disabled run

This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a commit to branch mapped-instance-actions
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 85f44b97dcc3dd46188127d73b4b3f01d08afb4c
Author: Brent Bovenzi <br...@gmail.com>
AuthorDate: Sat Apr 9 13:27:38 2022 -0400

    Add Xcom button, hide map index actions, disabled run
---
 airflow/www/static/js/dag.js                       | 18 +++++++++++++++++
 .../content/taskInstance/MappedInstances.jsx       | 17 +++++++++-------
 .../js/tree/details/content/taskInstance/Nav.jsx   |  9 ++++++++-
 .../content/taskInstance/taskActions/Run.jsx       | 21 ++++++++++++++++----
 airflow/www/templates/airflow/dag.html             | 23 ++++++++++++++--------
 5 files changed, 68 insertions(+), 20 deletions(-)

diff --git a/airflow/www/static/js/dag.js b/airflow/www/static/js/dag.js
index 1606737017..145d1f2666 100644
--- a/airflow/www/static/js/dag.js
+++ b/airflow/www/static/js/dag.js
@@ -124,6 +124,13 @@ function updateModalUrls() {
     execution_date: executionDate,
     map_index: mapIndex,
   });
+
+  updateButtonUrl(buttons.xcom, {
+    dag_id: dagId,
+    task_id: taskId,
+    execution_date: executionDate,
+    map_index: mapIndex,
+  });
 }
 
 // Update modal urls on toggle
@@ -170,9 +177,16 @@ export function callModal({
   if (mi >= 0) {
     $('#modal_map_index').show();
     $('#modal_map_index .value').text(mi);
+    // Marking state and clear are not yet supported for mapped instances
+    $('#success_action').hide();
+    $('#failed_action').hide();
+    $('#clear_action').hide();
   } else {
     $('#modal_map_index').hide();
     $('#modal_map_index .value').text('');
+    $('#success_action').show();
+    $('#failed_action').show();
+    $('#clear_action').show();
   }
   if (isSubDag) {
     $('#div_btn_subdag').show();
@@ -197,8 +211,12 @@ export function callModal({
     $('#btn_mapped').show();
     $('#mapped_dropdown').css('display', 'inline-block');
     $('#btn_rendered').hide();
+    $('#btn_xcom').hide();
+    $('#btn_log').hide();
   } else {
     $('#btn_rendered').show();
+    $('#btn_xcom').show();
+    $('#btn_log').show();
     $('#btn_mapped').hide();
     $('#mapped_dropdown').hide();
   }
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/MappedInstances.jsx b/airflow/www/static/js/tree/details/content/taskInstance/MappedInstances.jsx
index b815f0987f..42bbdca66f 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/MappedInstances.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/MappedInstances.jsx
@@ -26,9 +26,9 @@ import {
   IconButton,
 } from '@chakra-ui/react';
 import { snakeCase } from 'lodash';
-import { FaMicroscope } from 'react-icons/fa';
-import { GiLog } from 'react-icons/gi';
-import { HiTemplate } from 'react-icons/hi';
+import {
+  MdDetails, MdCode, MdSyncAlt, MdReorder,
+} from 'react-icons/md';
 
 import { getMetaValue } from '../../../../utils';
 import { formatDateTime, formatDuration } from '../../../../datetime_utils';
@@ -39,9 +39,10 @@ import Table from '../../../Table';
 const renderedTemplatesUrl = getMetaValue('rendered_templates_url');
 const logUrl = getMetaValue('log_url');
 const taskUrl = getMetaValue('task_url');
+const xcomUrl = getMetaValue('xcom_url');
 
 const IconLink = (props) => (
-  <IconButton as={Link} variant="outline" colorScheme="blue" {...props} />
+  <IconButton as={Link} variant="ghost" colorScheme="blue" fontSize="3xl" {...props} />
 );
 
 const MappedInstances = ({
@@ -73,6 +74,7 @@ const MappedInstances = ({
       const detailsLink = `${taskUrl}&${params}`;
       const renderedLink = `${renderedTemplatesUrl}&${params}`;
       const logLink = `${logUrl}&${params}`;
+      const xcomLink = `${xcomUrl}&${params}`;
       return {
         ...mi,
         state: (
@@ -86,9 +88,10 @@ const MappedInstances = ({
         endDate: mi.endDate && formatDateTime(mi.endDate),
         links: (
           <Flex alignItems="center">
-            <IconLink mr={1} title="Rendered Templates" aria-label="Rendered Templates" icon={<HiTemplate />} href={renderedLink} />
-            <IconLink mr={1} title="Log" aria-label="Log" icon={<GiLog />} href={logLink} />
-            <IconLink title="Details" aria-label="Details" icon={<FaMicroscope />} href={detailsLink} />
+            <IconLink mr={1} title="Details" aria-label="Details" icon={<MdDetails />} href={detailsLink} />
+            <IconLink mr={1} title="Rendered Templates" aria-label="Rendered Templates" icon={<MdCode />} href={renderedLink} />
+            <IconLink mr={1} title="Log" aria-label="Log" icon={<MdReorder />} href={logLink} />
+            <IconLink title="XCom" fontWeight="bold" aria-label="XCom" icon={<MdSyncAlt />} href={xcomLink} />
           </Flex>
         ),
       };
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/Nav.jsx b/airflow/www/static/js/tree/details/content/taskInstance/Nav.jsx
index 7550bdb842..1b7062f898 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/Nav.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/Nav.jsx
@@ -34,12 +34,17 @@ const baseDate = getMetaValue('base_date');
 const taskInstancesUrl = getMetaValue('task_instances_list_url');
 const renderedK8sUrl = getMetaValue('rendered_k8s_url');
 const renderedTemplatesUrl = getMetaValue('rendered_templates_url');
+const xcomUrl = getMetaValue('xcom_url');
 const logUrl = getMetaValue('log_url');
 const taskUrl = getMetaValue('task_url');
 const gridUrl = getMetaValue('grid_url');
 const gridUrlNoRoot = getMetaValue('grid_url_no_root');
 
-const LinkButton = ({ children, ...rest }) => (<Button as={Link} variant="ghost" colorScheme="blue" {...rest}>{children}</Button>);
+const LinkButton = ({ children, ...rest }) => (
+  <Button as={Link} aria-label={children} variant="ghost" colorScheme="blue" {...rest}>
+    {children}
+  </Button>
+);
 
 const Nav = ({
   taskId, executionDate, operator, isMapped,
@@ -51,6 +56,7 @@ const Nav = ({
   const detailsLink = `${taskUrl}&${params}`;
   const renderedLink = `${renderedTemplatesUrl}&${params}`;
   const logLink = `${logUrl}&${params}`;
+  const xcomLink = `${xcomUrl}&${params}`;
   const k8sLink = `${renderedK8sUrl}&${params}`;
   const listParams = new URLSearchParams({
     _flt_3_dag_id: dagId,
@@ -89,6 +95,7 @@ const Nav = ({
           <LinkButton href={subDagLink}>Zoom into SubDag</LinkButton>
           )}
           <LinkButton href={logLink}>Log</LinkButton>
+          <LinkButton href={xcomLink}>XCom</LinkButton>
         </>
         )}
         <LinkButton href={allInstancesLink} title="View all instances across all DAG runs">All Instances</LinkButton>
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Run.jsx b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Run.jsx
index 204cec44c2..d77c3e947d 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Run.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Run.jsx
@@ -22,15 +22,21 @@ import {
   Button,
   Flex,
   ButtonGroup,
+  Tooltip,
 } from '@chakra-ui/react';
 
 import { useRunTask } from '../../../../api';
+import { getMetaValue } from '../../../../../utils';
+import { useContainerRef } from '../../../../context/containerRef';
+
+const canRun = getMetaValue('k8s_or_k8scelery_executor') === 'True';
 
 const Run = ({
   dagId,
   runId,
   taskId,
 }) => {
+  const containerRef = useContainerRef();
   const [ignoreAllDeps, setIgnoreAllDeps] = useState(false);
   const onToggleAllDeps = () => setIgnoreAllDeps(!ignoreAllDeps);
 
@@ -52,7 +58,7 @@ const Run = ({
 
   return (
     <Flex justifyContent="space-between" width="100%">
-      <ButtonGroup isAttached variant="outline">
+      <ButtonGroup isAttached variant="outline" disabled={!canRun}>
         <Button
           bg={ignoreAllDeps && 'gray.100'}
           onClick={onToggleAllDeps}
@@ -75,9 +81,16 @@ const Run = ({
           Ignore Task Deps
         </Button>
       </ButtonGroup>
-      <Button colorScheme="blue" onClick={onClick} isLoading={isLoading}>
-        Run
-      </Button>
+      <Tooltip
+        label="Only works with the Celery, CeleryKubernetes or Kubernetes executors"
+        shouldWrapChildren // Will show the tooltip even if the button is disabled
+        disabled={canRun}
+        portalProps={{ containerRef }}
+      >
+        <Button colorScheme="blue" onClick={onClick} isLoading={isLoading} disabled={!canRun}>
+          Run
+        </Button>
+      </Tooltip>
     </Flex>
   );
 };
diff --git a/airflow/www/templates/airflow/dag.html b/airflow/www/templates/airflow/dag.html
index 16c0d27cb5..277cd53979 100644
--- a/airflow/www/templates/airflow/dag.html
+++ b/airflow/www/templates/airflow/dag.html
@@ -62,6 +62,7 @@
   <meta name="dagrun_details_url" content="{{ url_for('Airflow.dagrun_details', redirect_url=request.base_url, dag_id=dag.dag_id) }}">
   <meta name="task_url" content="{{ url_for('Airflow.task', dag_id=dag.dag_id) }}">
   <meta name="log_url" content="{{ url_for('Airflow.log', dag_id=dag.dag_id) }}">
+  <meta name="xcom_url" content="{{ url_for('Airflow.xcom', dag_id=dag.dag_id) }}">
   <meta name="rendered_templates_url" content="{{ url_for('Airflow.rendered_templates', dag_id=dag.dag_id) }}">
   <meta name="rendered_k8s_url" content="{{ url_for('Airflow.rendered_k8s', dag_id=dag.dag_id) }}">
   <meta name="task_instances_list_url" content="{{ url_for('TaskInstanceModelView.list') }}">
@@ -234,6 +235,9 @@
           <a id="btn_log" class="btn btn-sm" data-base-url="{{ url_for('Airflow.log') }}">
             Log
           </a>
+          <a id="btn_xcom" class="btn btn-sm" data-base-url="{{ url_for('Airflow.xcom') }}">
+            XCom
+          </a>
           <a id="btn_ti" class="btn btn-sm" data-base-url="{{ url_for('TaskInstanceModelView.list') }}" title="View all instances across all DAG runs">
             All Instances
           </a>
@@ -273,7 +277,7 @@
             </div>
           {% endif %}
           <h4>Task Actions</h4>
-          <form method="POST" data-action="{{ url_for('Airflow.run') }}">
+          <form method="POST" data-action="{{ url_for('Airflow.run') }}" id="run_action">
             <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
             <input type="hidden" name="dag_id" value="{{ dag.dag_id }}">
             <input type="hidden" name="task_id">
@@ -281,6 +285,9 @@
             <input type="hidden" name="map_index">
             <input type="hidden" name="origin" value="{{ request.base_url }}">
             <div class="row">
+              <span class="col-xs-12 col-sm-9 text-danger" style="font-size: 12px">
+                {{ "Only works with the Celery, CeleryKubernetes or Kubernetes executors, sorry" if not k8s_or_k8scelery_executor else "" }}
+            </span>
               <span class="btn-group col-xs-12 col-sm-9 task-instance-modal-column" data-toggle="buttons">
                 <label
                   class="btn btn-default"
@@ -299,14 +306,14 @@
                 </label>
               </span>
               <span class="col-xs-12 col-sm-3 task-instance-modal-column">
-                <button type="submit" id="btn_run" class="btn btn-primary btn-block" title="Runs a single task instance">
+                <button type="submit" id="btn_run" class="btn btn-primary btn-block" title="Runs a single task instance" {{ " disabled" if not k8s_or_k8scelery_executor else "" }}>
                   Run
                 </button>
               </span>
             </div>
+            <hr style="margin-bottom: 8px;">
           </form>
-          <hr style="margin-bottom: 8px;">
-          <form method="POST" data-action="{{ url_for('Airflow.clear') }}">
+          <form method="POST" data-action="{{ url_for('Airflow.clear') }}" id="clear_action">
             <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
             <input type="hidden" name="dag_id" value="{{ dag.dag_id }}">
             <input type="hidden" name="task_id">
@@ -346,9 +353,9 @@
                 </button>
               </span>
             </div>
+            <hr style="margin-bottom: 8px;">
           </form>
-          <hr style="margin-bottom: 8px;">
-          <form method="GET" data-action="{{ url_for('Airflow.confirm') }}">
+          <form method="GET" data-action="{{ url_for('Airflow.confirm') }}" id="failed_action">
             <input type="hidden" name="dag_id" value="{{ dag.dag_id }}">
             <input type="hidden" name="task_id">
             <input type="hidden" name="dag_run_id">
@@ -379,9 +386,9 @@
                 </button>
               </span>
             </div>
+            <hr style="margin-bottom: 8px;">
           </form>
-          <hr style="margin-bottom: 8px;">
-          <form method="GET" data-action="{{ url_for('Airflow.confirm') }}">
+          <form method="GET" data-action="{{ url_for('Airflow.confirm') }}" id="success_action">
             <input type="hidden" name="dag_id" value="{{ dag.dag_id }}">
             <input type="hidden" name="task_id">
             <input type="hidden" name="dag_run_id">