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

dheerajturaga pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 4679a4e584f Add airflowctl dags drain command (#73225)
4679a4e584f is described below

commit 4679a4e584f6323d2136132a87bf63849dbb9b68
Author: Dheeraj Turaga <[email protected]>
AuthorDate: Wed Sep 16 21:27:44 2026 -0500

    Add airflowctl dags drain command (#73225)
    
    * Add airflowctl dags drain command
    
    Dag draining already works through the REST API (scheduling_state) and
    the UI, but there was no way to trigger it from airflowctl. Per this
    repo's CLI policy, new commands achievable via the Public API belong in
    airflowctl rather than the airflow CLI, so this adds the CLI entry point
    there specifically.
    
    * Regenerate airflowctl help images for the drain command
    
    Keeps the docs/images SVG snapshot and its hash file in sync with the
    new `airflowctl dags drain` command added in the previous commit.
---
 airflow-ctl/docs/images/command_hashes.txt         |   2 +-
 airflow-ctl/docs/images/output_dags.svg            | 146 +++++++++++----------
 airflow-ctl/src/airflowctl/ctl/cli_config.py       |   9 ++
 .../src/airflowctl/ctl/commands/dag_command.py     |  25 +++-
 .../airflow_ctl/ctl/commands/test_dag_command.py   |  62 ++++++++-
 5 files changed, 168 insertions(+), 76 deletions(-)

diff --git a/airflow-ctl/docs/images/command_hashes.txt 
b/airflow-ctl/docs/images/command_hashes.txt
index b8994fb977b..bd58cb3c8cd 100644
--- a/airflow-ctl/docs/images/command_hashes.txt
+++ b/airflow-ctl/docs/images/command_hashes.txt
@@ -4,7 +4,7 @@ auth:d79e9c7d00c432bdbcbc2a86e2e32053
 backfill:74c8737b0a62a86ed3605fa9e6165874
 config:a3d936cb15fe3b547bf6c82cf93d923f
 connections:942f9f88cb908c28bf5c19159fc5065b
-dags:ea477b70a1c3bcf8ed603cdbd11e5ab6
+dags:21ac7c0005cac17bb15fd0dbf2af49fc
 dagrun:07035226eaaff0a557d3ad9aab4b41b5
 jobs:d4af478f28dae48ee18d43b998edc345
 pools:19efe105b9515ab1926ebcaf0e028d71
diff --git a/airflow-ctl/docs/images/output_dags.svg 
b/airflow-ctl/docs/images/output_dags.svg
index 78d61ab4506..47ff0b31ea8 100644
--- a/airflow-ctl/docs/images/output_dags.svg
+++ b/airflow-ctl/docs/images/output_dags.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 933 757.5999999999999" 
xmlns="http://www.w3.org/2000/svg";>
+<svg class="rich-terminal" viewBox="0 0 933 806.4" 
xmlns="http://www.w3.org/2000/svg";>
     <!-- Generated with Rich https://www.textualize.io -->
     <style>
 
@@ -19,154 +19,162 @@
         font-weight: 700;
     }
 
-    .terminal-4223325571-matrix {
+    .terminal-633085154-matrix {
         font-family: Fira Code, monospace;
         font-size: 20px;
         line-height: 24.4px;
         font-variant-east-asian: full-width;
     }
 
-    .terminal-4223325571-title {
+    .terminal-633085154-title {
         font-size: 18px;
         font-weight: bold;
         font-family: arial;
     }
 
-    .terminal-4223325571-r1 { fill: #ff8700 }
-.terminal-4223325571-r2 { fill: #c5c8c6 }
-.terminal-4223325571-r3 { fill: #808080 }
-.terminal-4223325571-r4 { fill: #68a0b3 }
+    .terminal-633085154-r1 { fill: #ff8700 }
+.terminal-633085154-r2 { fill: #c5c8c6 }
+.terminal-633085154-r3 { fill: #808080 }
+.terminal-633085154-r4 { fill: #68a0b3 }
     </style>
 
     <defs>
-    <clipPath id="terminal-4223325571-clip-terminal">
-      <rect x="0" y="0" width="914.0" height="706.5999999999999" />
+    <clipPath id="terminal-633085154-clip-terminal">
+      <rect x="0" y="0" width="914.0" height="755.4" />
     </clipPath>
-    <clipPath id="terminal-4223325571-line-0">
+    <clipPath id="terminal-633085154-line-0">
     <rect x="0" y="1.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-1">
+<clipPath id="terminal-633085154-line-1">
     <rect x="0" y="25.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-2">
+<clipPath id="terminal-633085154-line-2">
     <rect x="0" y="50.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-3">
+<clipPath id="terminal-633085154-line-3">
     <rect x="0" y="74.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-4">
+<clipPath id="terminal-633085154-line-4">
     <rect x="0" y="99.1" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-5">
+<clipPath id="terminal-633085154-line-5">
     <rect x="0" y="123.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-6">
+<clipPath id="terminal-633085154-line-6">
     <rect x="0" y="147.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-7">
+<clipPath id="terminal-633085154-line-7">
     <rect x="0" y="172.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-8">
+<clipPath id="terminal-633085154-line-8">
     <rect x="0" y="196.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-9">
+<clipPath id="terminal-633085154-line-9">
     <rect x="0" y="221.1" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-10">
+<clipPath id="terminal-633085154-line-10">
     <rect x="0" y="245.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-11">
+<clipPath id="terminal-633085154-line-11">
     <rect x="0" y="269.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-12">
+<clipPath id="terminal-633085154-line-12">
     <rect x="0" y="294.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-13">
+<clipPath id="terminal-633085154-line-13">
     <rect x="0" y="318.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-14">
+<clipPath id="terminal-633085154-line-14">
     <rect x="0" y="343.1" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-15">
+<clipPath id="terminal-633085154-line-15">
     <rect x="0" y="367.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-16">
+<clipPath id="terminal-633085154-line-16">
     <rect x="0" y="391.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-17">
+<clipPath id="terminal-633085154-line-17">
     <rect x="0" y="416.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-18">
+<clipPath id="terminal-633085154-line-18">
     <rect x="0" y="440.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-19">
+<clipPath id="terminal-633085154-line-19">
     <rect x="0" y="465.1" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-20">
+<clipPath id="terminal-633085154-line-20">
     <rect x="0" y="489.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-21">
+<clipPath id="terminal-633085154-line-21">
     <rect x="0" y="513.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-22">
+<clipPath id="terminal-633085154-line-22">
     <rect x="0" y="538.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-23">
+<clipPath id="terminal-633085154-line-23">
     <rect x="0" y="562.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-24">
+<clipPath id="terminal-633085154-line-24">
     <rect x="0" y="587.1" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-25">
+<clipPath id="terminal-633085154-line-25">
     <rect x="0" y="611.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-26">
+<clipPath id="terminal-633085154-line-26">
     <rect x="0" y="635.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4223325571-line-27">
+<clipPath id="terminal-633085154-line-27">
     <rect x="0" y="660.3" width="915" height="24.65"/>
             </clipPath>
+<clipPath id="terminal-633085154-line-28">
+    <rect x="0" y="684.7" width="915" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-633085154-line-29">
+    <rect x="0" y="709.1" width="915" height="24.65"/>
+            </clipPath>
     </defs>
 
-    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="931" height="755.6" rx="8"/>
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="931" height="804.4" rx="8"/>
             <g transform="translate(26,22)">
             <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
             <circle cx="22" cy="0" r="7" fill="#febc2e"/>
             <circle cx="44" cy="0" r="7" fill="#28c840"/>
             </g>
         
-    <g transform="translate(9, 41)" 
clip-path="url(#terminal-4223325571-clip-terminal)">
+    <g transform="translate(9, 41)" 
clip-path="url(#terminal-633085154-clip-terminal)">
     
-    <g class="terminal-4223325571-matrix">
-    <text class="terminal-4223325571-r1" x="0" y="20" textLength="73.2" 
clip-path="url(#terminal-4223325571-line-0)">Usage:</text><text 
class="terminal-4223325571-r3" x="85.4" y="20" textLength="183" 
clip-path="url(#terminal-4223325571-line-0)">airflowctl&#160;dags</text><text 
class="terminal-4223325571-r2" x="268.4" y="20" textLength="24.4" 
clip-path="url(#terminal-4223325571-line-0)">&#160;[</text><text 
class="terminal-4223325571-r4" x="292.8" y="20" textLength="24.4" 
clip-path="url(#t [...]
-</text><text class="terminal-4223325571-r2" x="915" y="44.4" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-1)">
-</text><text class="terminal-4223325571-r2" x="0" y="68.8" textLength="280.6" 
clip-path="url(#terminal-4223325571-line-2)">Perform&#160;Dags&#160;operations</text><text
 class="terminal-4223325571-r2" x="915" y="68.8" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-2)">
-</text><text class="terminal-4223325571-r2" x="915" y="93.2" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-3)">
-</text><text class="terminal-4223325571-r1" x="0" y="117.6" textLength="256.2" 
clip-path="url(#terminal-4223325571-line-4)">Positional&#160;Arguments:</text><text
 class="terminal-4223325571-r2" x="915" y="117.6" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-4)">
-</text><text class="terminal-4223325571-r4" x="24.4" y="142" textLength="85.4" 
clip-path="url(#terminal-4223325571-line-5)">COMMAND</text><text 
class="terminal-4223325571-r2" x="915" y="142" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-5)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="166.4" textLength="61" 
clip-path="url(#terminal-4223325571-line-6)">clear</text><text 
class="terminal-4223325571-r2" x="268.4" y="166.4" textLength="646.6" 
clip-path="url(#terminal-4223325571-line-6)">Clear&#160;task&#160;instances&#160;for&#160;Dag&#160;runs&#160;selected&#160;by&#160;run&#160;ID,</text><text
 class="terminal-4223325571-r2" x="915" y="166.4" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-6)">
-</text><text class="terminal-4223325571-r2" x="0" y="190.8" textLength="390.4" 
clip-path="url(#terminal-4223325571-line-7)">partition&#160;key,&#160;or&#160;partition&#160;date</text><text
 class="terminal-4223325571-r2" x="915" y="190.8" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-7)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="215.2" 
textLength="73.2" 
clip-path="url(#terminal-4223325571-line-8)">delete</text><text 
class="terminal-4223325571-r2" x="268.4" y="215.2" textLength="268.4" 
clip-path="url(#terminal-4223325571-line-8)">Delete&#160;a&#160;Dag&#160;by&#160;its&#160;ID</text><text
 class="terminal-4223325571-r2" x="915" y="215.2" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-8)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="239.6" 
textLength="36.6" clip-path="url(#terminal-4223325571-line-9)">get</text><text 
class="terminal-4223325571-r2" x="268.4" y="239.6" textLength="292.8" 
clip-path="url(#terminal-4223325571-line-9)">Retrieve&#160;a&#160;Dag&#160;by&#160;its&#160;ID</text><text
 class="terminal-4223325571-r2" x="915" y="239.6" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-9)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="264" 
textLength="134.2" 
clip-path="url(#terminal-4223325571-line-10)">get-details</text><text 
class="terminal-4223325571-r2" x="268.4" y="264" textLength="475.8" 
clip-path="url(#terminal-4223325571-line-10)">Retrieve&#160;detailed&#160;information&#160;for&#160;a&#160;Dag</text><text
 class="terminal-4223325571-r2" x="915" y="264" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-10)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="288.4" 
textLength="195.2" 
clip-path="url(#terminal-4223325571-line-11)">get-import-error</text><text 
class="terminal-4223325571-r2" x="268.4" y="288.4" textLength="451.4" 
clip-path="url(#terminal-4223325571-line-11)">Retrieve&#160;a&#160;Dag&#160;import&#160;error&#160;by&#160;its&#160;ID</text><text
 class="terminal-4223325571-r2" x="915" y="288.4" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-11)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="312.8" 
textLength="109.8" 
clip-path="url(#terminal-4223325571-line-12)">get-stats</text><text 
class="terminal-4223325571-r2" x="268.4" y="312.8" textLength="536.8" 
clip-path="url(#terminal-4223325571-line-12)">Retrieve&#160;run&#160;statistics&#160;for&#160;one&#160;or&#160;more&#160;Dags</text><text
 class="terminal-4223325571-r2" x="915" y="312.8" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-12)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="337.2" 
textLength="97.6" 
clip-path="url(#terminal-4223325571-line-13)">get-tags</text><text 
class="terminal-4223325571-r2" x="268.4" y="337.2" textLength="366" 
clip-path="url(#terminal-4223325571-line-13)">List&#160;all&#160;tags&#160;used&#160;across&#160;Dags</text><text
 class="terminal-4223325571-r2" x="915" y="337.2" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-13)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="361.6" 
textLength="134.2" 
clip-path="url(#terminal-4223325571-line-14)">get-version</text><text 
class="terminal-4223325571-r2" x="268.4" y="361.6" textLength="439.2" 
clip-path="url(#terminal-4223325571-line-14)">Retrieve&#160;a&#160;specific&#160;version&#160;of&#160;a&#160;Dag</text><text
 class="terminal-4223325571-r2" x="915" y="361.6" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-14)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="386" textLength="48.8" 
clip-path="url(#terminal-4223325571-line-15)">list</text><text 
class="terminal-4223325571-r2" x="268.4" y="386" textLength="158.6" 
clip-path="url(#terminal-4223325571-line-15)">List&#160;all&#160;Dags</text><text
 class="terminal-4223325571-r2" x="915" y="386" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-15)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="410.4" 
textLength="219.6" 
clip-path="url(#terminal-4223325571-line-16)">list-import-errors</text><text 
class="terminal-4223325571-r2" x="915" y="410.4" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-16)">
-</text><text class="terminal-4223325571-r2" x="268.4" y="434.8" 
textLength="317.2" 
clip-path="url(#terminal-4223325571-line-17)">List&#160;all&#160;Dag&#160;import&#160;errors</text><text
 class="terminal-4223325571-r2" x="915" y="434.8" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-17)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="459.2" 
textLength="146.4" 
clip-path="url(#terminal-4223325571-line-18)">list-version</text><text 
class="terminal-4223325571-r2" x="268.4" y="459.2" textLength="317.2" 
clip-path="url(#terminal-4223325571-line-18)">List&#160;all&#160;versions&#160;of&#160;a&#160;Dag</text><text
 class="terminal-4223325571-r2" x="915" y="459.2" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-18)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="483.6" 
textLength="146.4" 
clip-path="url(#terminal-4223325571-line-19)">list-warning</text><text 
class="terminal-4223325571-r2" x="268.4" y="483.6" textLength="256.2" 
clip-path="url(#terminal-4223325571-line-19)">List&#160;all&#160;Dag&#160;warnings</text><text
 class="terminal-4223325571-r2" x="915" y="483.6" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-19)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="508" 
textLength="170.8" 
clip-path="url(#terminal-4223325571-line-20)">next-execution</text><text 
class="terminal-4223325571-r2" x="268.4" y="508" textLength="585.6" 
clip-path="url(#terminal-4223325571-line-20)">Show&#160;the&#160;next&#160;scheduled&#160;execution&#160;time&#160;for&#160;a&#160;Dag</text><text
 class="terminal-4223325571-r2" x="915" y="508" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-20)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="532.4" textLength="61" 
clip-path="url(#terminal-4223325571-line-21)">pause</text><text 
class="terminal-4223325571-r2" x="268.4" y="532.4" textLength="134.2" 
clip-path="url(#terminal-4223325571-line-21)">Pause&#160;a&#160;Dag</text><text 
class="terminal-4223325571-r2" x="915" y="532.4" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-21)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="556.8" textLength="61" 
clip-path="url(#terminal-4223325571-line-22)">state</text><text 
class="terminal-4223325571-r2" x="268.4" y="556.8" textLength="329.4" 
clip-path="url(#terminal-4223325571-line-22)">Get&#160;the&#160;status&#160;of&#160;a&#160;Dag&#160;run</text><text
 class="terminal-4223325571-r2" x="915" y="556.8" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-22)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="581.2" 
textLength="85.4" 
clip-path="url(#terminal-4223325571-line-23)">trigger</text><text 
class="terminal-4223325571-r2" x="268.4" y="581.2" textLength="256.2" 
clip-path="url(#terminal-4223325571-line-23)">Trigger&#160;a&#160;new&#160;Dag&#160;run</text><text
 class="terminal-4223325571-r2" x="915" y="581.2" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-23)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="605.6" 
textLength="85.4" 
clip-path="url(#terminal-4223325571-line-24)">unpause</text><text 
class="terminal-4223325571-r2" x="268.4" y="605.6" textLength="158.6" 
clip-path="url(#terminal-4223325571-line-24)">Unpause&#160;a&#160;Dag</text><text
 class="terminal-4223325571-r2" x="915" y="605.6" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-24)">
-</text><text class="terminal-4223325571-r4" x="48.8" y="630" textLength="73.2" 
clip-path="url(#terminal-4223325571-line-25)">update</text><text 
class="terminal-4223325571-r2" x="268.4" y="630" textLength="317.2" 
clip-path="url(#terminal-4223325571-line-25)">Update&#160;properties&#160;of&#160;a&#160;Dag</text><text
 class="terminal-4223325571-r2" x="915" y="630" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-25)">
-</text><text class="terminal-4223325571-r2" x="915" y="654.4" 
textLength="12.2" clip-path="url(#terminal-4223325571-line-26)">
-</text><text class="terminal-4223325571-r1" x="0" y="678.8" textLength="97.6" 
clip-path="url(#terminal-4223325571-line-27)">Options:</text><text 
class="terminal-4223325571-r2" x="915" y="678.8" textLength="12.2" 
clip-path="url(#terminal-4223325571-line-27)">
-</text><text class="terminal-4223325571-r4" x="24.4" y="703.2" 
textLength="24.4" clip-path="url(#terminal-4223325571-line-28)">-h</text><text 
class="terminal-4223325571-r2" x="48.8" y="703.2" textLength="24.4" 
clip-path="url(#terminal-4223325571-line-28)">,&#160;</text><text 
class="terminal-4223325571-r4" x="73.2" y="703.2" textLength="73.2" 
clip-path="url(#terminal-4223325571-line-28)">--help</text><text 
class="terminal-4223325571-r2" x="268.4" y="703.2" textLength="378.2" 
clip-path="ur [...]
+    <g class="terminal-633085154-matrix">
+    <text class="terminal-633085154-r1" x="0" y="20" textLength="73.2" 
clip-path="url(#terminal-633085154-line-0)">Usage:</text><text 
class="terminal-633085154-r3" x="85.4" y="20" textLength="183" 
clip-path="url(#terminal-633085154-line-0)">airflowctl&#160;dags</text><text 
class="terminal-633085154-r2" x="268.4" y="20" textLength="24.4" 
clip-path="url(#terminal-633085154-line-0)">&#160;[</text><text 
class="terminal-633085154-r4" x="292.8" y="20" textLength="24.4" 
clip-path="url(#terminal [...]
+</text><text class="terminal-633085154-r2" x="915" y="44.4" textLength="12.2" 
clip-path="url(#terminal-633085154-line-1)">
+</text><text class="terminal-633085154-r2" x="0" y="68.8" textLength="280.6" 
clip-path="url(#terminal-633085154-line-2)">Perform&#160;Dags&#160;operations</text><text
 class="terminal-633085154-r2" x="915" y="68.8" textLength="12.2" 
clip-path="url(#terminal-633085154-line-2)">
+</text><text class="terminal-633085154-r2" x="915" y="93.2" textLength="12.2" 
clip-path="url(#terminal-633085154-line-3)">
+</text><text class="terminal-633085154-r1" x="0" y="117.6" textLength="256.2" 
clip-path="url(#terminal-633085154-line-4)">Positional&#160;Arguments:</text><text
 class="terminal-633085154-r2" x="915" y="117.6" textLength="12.2" 
clip-path="url(#terminal-633085154-line-4)">
+</text><text class="terminal-633085154-r4" x="24.4" y="142" textLength="85.4" 
clip-path="url(#terminal-633085154-line-5)">COMMAND</text><text 
class="terminal-633085154-r2" x="915" y="142" textLength="12.2" 
clip-path="url(#terminal-633085154-line-5)">
+</text><text class="terminal-633085154-r4" x="48.8" y="166.4" textLength="61" 
clip-path="url(#terminal-633085154-line-6)">clear</text><text 
class="terminal-633085154-r2" x="268.4" y="166.4" textLength="646.6" 
clip-path="url(#terminal-633085154-line-6)">Clear&#160;task&#160;instances&#160;for&#160;Dag&#160;runs&#160;selected&#160;by&#160;run&#160;ID,</text><text
 class="terminal-633085154-r2" x="915" y="166.4" textLength="12.2" 
clip-path="url(#terminal-633085154-line-6)">
+</text><text class="terminal-633085154-r2" x="0" y="190.8" textLength="390.4" 
clip-path="url(#terminal-633085154-line-7)">partition&#160;key,&#160;or&#160;partition&#160;date</text><text
 class="terminal-633085154-r2" x="915" y="190.8" textLength="12.2" 
clip-path="url(#terminal-633085154-line-7)">
+</text><text class="terminal-633085154-r4" x="48.8" y="215.2" 
textLength="73.2" 
clip-path="url(#terminal-633085154-line-8)">delete</text><text 
class="terminal-633085154-r2" x="268.4" y="215.2" textLength="268.4" 
clip-path="url(#terminal-633085154-line-8)">Delete&#160;a&#160;Dag&#160;by&#160;its&#160;ID</text><text
 class="terminal-633085154-r2" x="915" y="215.2" textLength="12.2" 
clip-path="url(#terminal-633085154-line-8)">
+</text><text class="terminal-633085154-r4" x="48.8" y="239.6" textLength="61" 
clip-path="url(#terminal-633085154-line-9)">drain</text><text 
class="terminal-633085154-r2" x="268.4" y="239.6" textLength="134.2" 
clip-path="url(#terminal-633085154-line-9)">Drain&#160;a&#160;Dag</text><text 
class="terminal-633085154-r2" x="915" y="239.6" textLength="12.2" 
clip-path="url(#terminal-633085154-line-9)">
+</text><text class="terminal-633085154-r4" x="48.8" y="264" textLength="36.6" 
clip-path="url(#terminal-633085154-line-10)">get</text><text 
class="terminal-633085154-r2" x="268.4" y="264" textLength="292.8" 
clip-path="url(#terminal-633085154-line-10)">Retrieve&#160;a&#160;Dag&#160;by&#160;its&#160;ID</text><text
 class="terminal-633085154-r2" x="915" y="264" textLength="12.2" 
clip-path="url(#terminal-633085154-line-10)">
+</text><text class="terminal-633085154-r4" x="48.8" y="288.4" 
textLength="134.2" 
clip-path="url(#terminal-633085154-line-11)">get-details</text><text 
class="terminal-633085154-r2" x="268.4" y="288.4" textLength="475.8" 
clip-path="url(#terminal-633085154-line-11)">Retrieve&#160;detailed&#160;information&#160;for&#160;a&#160;Dag</text><text
 class="terminal-633085154-r2" x="915" y="288.4" textLength="12.2" 
clip-path="url(#terminal-633085154-line-11)">
+</text><text class="terminal-633085154-r4" x="48.8" y="312.8" 
textLength="195.2" 
clip-path="url(#terminal-633085154-line-12)">get-import-error</text><text 
class="terminal-633085154-r2" x="268.4" y="312.8" textLength="451.4" 
clip-path="url(#terminal-633085154-line-12)">Retrieve&#160;a&#160;Dag&#160;import&#160;error&#160;by&#160;its&#160;ID</text><text
 class="terminal-633085154-r2" x="915" y="312.8" textLength="12.2" 
clip-path="url(#terminal-633085154-line-12)">
+</text><text class="terminal-633085154-r4" x="48.8" y="337.2" 
textLength="109.8" 
clip-path="url(#terminal-633085154-line-13)">get-stats</text><text 
class="terminal-633085154-r2" x="268.4" y="337.2" textLength="536.8" 
clip-path="url(#terminal-633085154-line-13)">Retrieve&#160;run&#160;statistics&#160;for&#160;one&#160;or&#160;more&#160;Dags</text><text
 class="terminal-633085154-r2" x="915" y="337.2" textLength="12.2" 
clip-path="url(#terminal-633085154-line-13)">
+</text><text class="terminal-633085154-r4" x="48.8" y="361.6" 
textLength="97.6" 
clip-path="url(#terminal-633085154-line-14)">get-tags</text><text 
class="terminal-633085154-r2" x="268.4" y="361.6" textLength="366" 
clip-path="url(#terminal-633085154-line-14)">List&#160;all&#160;tags&#160;used&#160;across&#160;Dags</text><text
 class="terminal-633085154-r2" x="915" y="361.6" textLength="12.2" 
clip-path="url(#terminal-633085154-line-14)">
+</text><text class="terminal-633085154-r4" x="48.8" y="386" textLength="134.2" 
clip-path="url(#terminal-633085154-line-15)">get-version</text><text 
class="terminal-633085154-r2" x="268.4" y="386" textLength="439.2" 
clip-path="url(#terminal-633085154-line-15)">Retrieve&#160;a&#160;specific&#160;version&#160;of&#160;a&#160;Dag</text><text
 class="terminal-633085154-r2" x="915" y="386" textLength="12.2" 
clip-path="url(#terminal-633085154-line-15)">
+</text><text class="terminal-633085154-r4" x="48.8" y="410.4" 
textLength="48.8" clip-path="url(#terminal-633085154-line-16)">list</text><text 
class="terminal-633085154-r2" x="268.4" y="410.4" textLength="158.6" 
clip-path="url(#terminal-633085154-line-16)">List&#160;all&#160;Dags</text><text
 class="terminal-633085154-r2" x="915" y="410.4" textLength="12.2" 
clip-path="url(#terminal-633085154-line-16)">
+</text><text class="terminal-633085154-r4" x="48.8" y="434.8" 
textLength="219.6" 
clip-path="url(#terminal-633085154-line-17)">list-import-errors</text><text 
class="terminal-633085154-r2" x="915" y="434.8" textLength="12.2" 
clip-path="url(#terminal-633085154-line-17)">
+</text><text class="terminal-633085154-r2" x="268.4" y="459.2" 
textLength="317.2" 
clip-path="url(#terminal-633085154-line-18)">List&#160;all&#160;Dag&#160;import&#160;errors</text><text
 class="terminal-633085154-r2" x="915" y="459.2" textLength="12.2" 
clip-path="url(#terminal-633085154-line-18)">
+</text><text class="terminal-633085154-r4" x="48.8" y="483.6" 
textLength="146.4" 
clip-path="url(#terminal-633085154-line-19)">list-version</text><text 
class="terminal-633085154-r2" x="268.4" y="483.6" textLength="317.2" 
clip-path="url(#terminal-633085154-line-19)">List&#160;all&#160;versions&#160;of&#160;a&#160;Dag</text><text
 class="terminal-633085154-r2" x="915" y="483.6" textLength="12.2" 
clip-path="url(#terminal-633085154-line-19)">
+</text><text class="terminal-633085154-r4" x="48.8" y="508" textLength="146.4" 
clip-path="url(#terminal-633085154-line-20)">list-warning</text><text 
class="terminal-633085154-r2" x="268.4" y="508" textLength="256.2" 
clip-path="url(#terminal-633085154-line-20)">List&#160;all&#160;Dag&#160;warnings</text><text
 class="terminal-633085154-r2" x="915" y="508" textLength="12.2" 
clip-path="url(#terminal-633085154-line-20)">
+</text><text class="terminal-633085154-r4" x="48.8" y="532.4" 
textLength="170.8" 
clip-path="url(#terminal-633085154-line-21)">next-execution</text><text 
class="terminal-633085154-r2" x="268.4" y="532.4" textLength="585.6" 
clip-path="url(#terminal-633085154-line-21)">Show&#160;the&#160;next&#160;scheduled&#160;execution&#160;time&#160;for&#160;a&#160;Dag</text><text
 class="terminal-633085154-r2" x="915" y="532.4" textLength="12.2" 
clip-path="url(#terminal-633085154-line-21)">
+</text><text class="terminal-633085154-r4" x="48.8" y="556.8" textLength="61" 
clip-path="url(#terminal-633085154-line-22)">pause</text><text 
class="terminal-633085154-r2" x="268.4" y="556.8" textLength="134.2" 
clip-path="url(#terminal-633085154-line-22)">Pause&#160;a&#160;Dag</text><text 
class="terminal-633085154-r2" x="915" y="556.8" textLength="12.2" 
clip-path="url(#terminal-633085154-line-22)">
+</text><text class="terminal-633085154-r4" x="48.8" y="581.2" textLength="61" 
clip-path="url(#terminal-633085154-line-23)">state</text><text 
class="terminal-633085154-r2" x="268.4" y="581.2" textLength="329.4" 
clip-path="url(#terminal-633085154-line-23)">Get&#160;the&#160;status&#160;of&#160;a&#160;Dag&#160;run</text><text
 class="terminal-633085154-r2" x="915" y="581.2" textLength="12.2" 
clip-path="url(#terminal-633085154-line-23)">
+</text><text class="terminal-633085154-r4" x="48.8" y="605.6" 
textLength="85.4" 
clip-path="url(#terminal-633085154-line-24)">trigger</text><text 
class="terminal-633085154-r2" x="268.4" y="605.6" textLength="256.2" 
clip-path="url(#terminal-633085154-line-24)">Trigger&#160;a&#160;new&#160;Dag&#160;run</text><text
 class="terminal-633085154-r2" x="915" y="605.6" textLength="12.2" 
clip-path="url(#terminal-633085154-line-24)">
+</text><text class="terminal-633085154-r4" x="48.8" y="630" textLength="85.4" 
clip-path="url(#terminal-633085154-line-25)">unpause</text><text 
class="terminal-633085154-r2" x="268.4" y="630" textLength="158.6" 
clip-path="url(#terminal-633085154-line-25)">Unpause&#160;a&#160;Dag</text><text
 class="terminal-633085154-r2" x="915" y="630" textLength="12.2" 
clip-path="url(#terminal-633085154-line-25)">
+</text><text class="terminal-633085154-r4" x="48.8" y="654.4" 
textLength="73.2" 
clip-path="url(#terminal-633085154-line-26)">update</text><text 
class="terminal-633085154-r2" x="268.4" y="654.4" textLength="317.2" 
clip-path="url(#terminal-633085154-line-26)">Update&#160;properties&#160;of&#160;a&#160;Dag</text><text
 class="terminal-633085154-r2" x="915" y="654.4" textLength="12.2" 
clip-path="url(#terminal-633085154-line-26)">
+</text><text class="terminal-633085154-r2" x="915" y="678.8" textLength="12.2" 
clip-path="url(#terminal-633085154-line-27)">
+</text><text class="terminal-633085154-r1" x="0" y="703.2" textLength="97.6" 
clip-path="url(#terminal-633085154-line-28)">Options:</text><text 
class="terminal-633085154-r2" x="915" y="703.2" textLength="12.2" 
clip-path="url(#terminal-633085154-line-28)">
+</text><text class="terminal-633085154-r4" x="24.4" y="727.6" 
textLength="24.4" clip-path="url(#terminal-633085154-line-29)">-h</text><text 
class="terminal-633085154-r2" x="48.8" y="727.6" textLength="24.4" 
clip-path="url(#terminal-633085154-line-29)">,&#160;</text><text 
class="terminal-633085154-r4" x="73.2" y="727.6" textLength="73.2" 
clip-path="url(#terminal-633085154-line-29)">--help</text><text 
class="terminal-633085154-r2" x="268.4" y="727.6" textLength="378.2" 
clip-path="url(#term [...]
+</text><text class="terminal-633085154-r2" x="915" y="752" textLength="12.2" 
clip-path="url(#terminal-633085154-line-30)">
 </text>
     </g>
     </g>
diff --git a/airflow-ctl/src/airflowctl/ctl/cli_config.py 
b/airflow-ctl/src/airflowctl/ctl/cli_config.py
index 3603e0cc71d..a273784127e 100755
--- a/airflow-ctl/src/airflowctl/ctl/cli_config.py
+++ b/airflow-ctl/src/airflowctl/ctl/cli_config.py
@@ -1131,6 +1131,15 @@ DAG_COMMANDS = (
             ARG_DAG_CLEAR_YES,
         ),
     ),
+    ActionCommand(
+        name="drain",
+        help="Drain a Dag",
+        func=lazy_load_command("airflowctl.ctl.commands.dag_command.drain"),
+        args=(
+            ARG_DAG_ID,
+            ARG_OUTPUT,
+        ),
+    ),
     ActionCommand(
         name="next-execution",
         help="Show the next scheduled execution time for a Dag",
diff --git a/airflow-ctl/src/airflowctl/ctl/commands/dag_command.py 
b/airflow-ctl/src/airflowctl/ctl/commands/dag_command.py
index 15307085f36..12ba2ccc873 100644
--- a/airflow-ctl/src/airflowctl/ctl/commands/dag_command.py
+++ b/airflow-ctl/src/airflowctl/ctl/commands/dag_command.py
@@ -36,21 +36,25 @@ from airflowctl.api.datamodels.generated import (
     ClearTaskInstancesBody,
     DAGPatchBody,
     DAGRunResponse,
+    DagSchedulingState,
 )
 from airflowctl.ctl.console_formatting import AirflowConsole
 
 
 def update_dag_state(
     dag_id: str,
-    operation: Literal["pause", "unpause"],
+    operation: Literal["pause", "unpause", "drain"],
     api_client,
     output: str,
 ):
-    """Update Dag state (pause/unpause)."""
+    """Update Dag state (pause/unpause/drain)."""
+    dag_body = (
+        DAGPatchBody(scheduling_state=DagSchedulingState.DRAINING)
+        if operation == "drain"
+        else DAGPatchBody(is_paused=operation == "pause")
+    )
     try:
-        response = api_client.dags.update(
-            dag_id=dag_id, dag_body=DAGPatchBody(is_paused=operation == 
"pause")
-        )
+        response = api_client.dags.update(dag_id=dag_id, dag_body=dag_body)
     except ServerResponseError as e:
         rich.print(f"[red]Error while trying to {operation} Dag {dag_id}: 
{e}[/red]")
         sys.exit(1)
@@ -87,6 +91,17 @@ def unpause(args, api_client=NEW_API_CLIENT) -> None:
     )
 
 
+@provide_api_client(kind=ClientKind.CLI)
+def drain(args, api_client=NEW_API_CLIENT) -> None:
+    """Drain a Dag."""
+    return update_dag_state(
+        dag_id=args.dag_id,
+        operation="drain",
+        api_client=api_client,
+        output=args.output,
+    )
+
+
 _NEXT_EXECUTION_FIELDS = (
     "next_dagrun_logical_date",
     "next_dagrun_data_interval_start",
diff --git a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_dag_command.py 
b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_dag_command.py
index 1ee613b85f8..a481b776762 100644
--- a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_dag_command.py
+++ b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_dag_command.py
@@ -25,7 +25,7 @@ import httpx
 import pytest
 
 from airflowctl.api.client import ClientKind
-from airflowctl.api.datamodels.generated import ClearTaskInstancesBody, 
DAGResponse
+from airflowctl.api.datamodels.generated import ClearTaskInstancesBody, 
DAGResponse, DagSchedulingState
 from airflowctl.api.operations import DagRunOperations, ServerResponseError, 
TasksOperations
 from airflowctl.ctl import cli_parser
 from airflowctl.ctl.commands import dag_command
@@ -111,6 +111,40 @@ class TestDagCommands:
         allowed_run_types=None,
     )
 
+    dag_response_draining = DAGResponse(
+        dag_id=dag_id,
+        dag_display_name=dag_display_name,
+        is_paused=False,
+        scheduling_state=DagSchedulingState.DRAINING,
+        last_parsed_time=datetime.datetime(2024, 12, 31, 23, 59, 59),
+        last_expired=datetime.datetime(2025, 1, 1, 0, 0, 0),
+        fileloc="fileloc",
+        relative_fileloc="relative_fileloc",
+        description="description",
+        timetable_summary="timetable_summary",
+        timetable_description="timetable_description",
+        timetable_partitioned=False,
+        timetable_periodic=True,
+        tags=[],
+        max_active_tasks=1,
+        max_active_runs=1,
+        max_consecutive_failed_dag_runs=1,
+        has_task_concurrency_limits=True,
+        has_import_errors=True,
+        next_dagrun_logical_date=datetime.datetime(2025, 1, 1, 0, 0, 0),
+        next_dagrun_data_interval_start=datetime.datetime(2025, 1, 1, 0, 0, 0),
+        next_dagrun_data_interval_end=datetime.datetime(2025, 1, 1, 0, 0, 0),
+        next_dagrun_run_after=datetime.datetime(2025, 1, 1, 0, 0, 0),
+        owners=["apache-airflow"],
+        is_backfillable=True,
+        file_token="file_token",
+        bundle_name="bundle_name",
+        is_stale=False,
+        last_parse_duration=None,
+        bundle_version=None,
+        allowed_run_types=None,
+    )
+
     dag_response_no_schedule = DAGResponse(
         dag_id=dag_id,
         dag_display_name=dag_display_name,
@@ -220,6 +254,32 @@ class TestDagCommands:
                 api_client=api_client,
             )
 
+    def test_drain_dag(self, api_client_maker, monkeypatch):
+        api_client = api_client_maker(
+            path=f"/api/v2/dags/{self.dag_id}",
+            response_json=self.dag_response_draining.model_dump(mode="json"),
+            expected_http_status_code=200,
+            kind=ClientKind.CLI,
+        )
+        dag_response_dict = dag_command.drain(
+            self.parser.parse_args(["dags", "drain", self.dag_id]),
+            api_client=api_client,
+        )
+        assert dag_response_dict["scheduling_state"] == 
DagSchedulingState.DRAINING
+
+    def test_drain_fail(self, api_client_maker, monkeypatch):
+        api_client = api_client_maker(
+            path=f"/api/v2/dags/{self.dag_id}",
+            response_json={"detail": "DAG not found"},
+            expected_http_status_code=404,
+            kind=ClientKind.CLI,
+        )
+        with pytest.raises(SystemExit):
+            dag_command.drain(
+                self.parser.parse_args(["dags", "drain", self.dag_id]),
+                api_client=api_client,
+            )
+
     def test_next_execution(self, api_client_maker):
         api_client = api_client_maker(
             path=f"/api/v2/dags/{self.dag_id}",

Reply via email to