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 dags</text><text
class="terminal-4223325571-r2" x="268.4" y="20" textLength="24.4"
clip-path="url(#terminal-4223325571-line-0)"> [</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 Dags 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 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 task instances for Dag runs selected by run 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 key, or partition 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 a Dag by its 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 a Dag by its 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 detailed information for a 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 a Dag import error by its 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 run statistics for one or more 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 all tags used across 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 a specific version of a 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 all 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 all Dag import 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 all versions of a 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 all Dag 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 the next scheduled execution time for a 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 a 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 the status of a Dag 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 a new Dag 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 a 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 properties of a 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)">, </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 dags</text><text
class="terminal-633085154-r2" x="268.4" y="20" textLength="24.4"
clip-path="url(#terminal-633085154-line-0)"> [</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 Dags 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 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 task instances for Dag runs selected by run 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 key, or partition 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 a Dag by its 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 a 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 a Dag by its 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 detailed information for a 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 a Dag import error by its 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 run statistics for one or more 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 all tags used across 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 a specific version of a 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 all 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 all Dag import 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 all versions of a 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 all Dag 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 the next scheduled execution time for a 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 a 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 the status of a Dag 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 a new Dag 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 a 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 properties of a 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)">, </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}",