This is an automated email from the ASF dual-hosted git repository.
shahar1 pushed a commit to branch airflow-ctl/v0-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/airflow-ctl/v0-1-test by this
push:
new 8fc5499c3a1 [airflow-ctl/v0-1-test] Report invalid variables in
airflowctl variables import as readable errors (#76174) (#76196)
8fc5499c3a1 is described below
commit 8fc5499c3a199bfa6db6803da830d68c881c5280
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sun Nov 1 10:41:46 2026 +0200
[airflow-ctl/v0-1-test] Report invalid variables in airflowctl variables
import as readable errors (#76174) (#76196)
* [airflow-ctl/v0-1-test] Report invalid variables in airflowctl variables
import as readable errors (#76174)
A variable with a bad value, such as a non-string description or a blank
or over-long key, crashed `airflowctl variables import` with a raw
pydantic traceback that did not say which variable was wrong. Report the
variable key, the file and the reason instead, as
`airflowctl connections import` already does.
(cherry picked from commit 122f7b43fd73dea356212462ae7738c75342e22f)
Co-authored-by: Rishabh <[email protected]>
* Drop the blank-key import test case from the v0-1-test backport
The blank-key case relies on the pattern constraint that #74896 added to
VariableBody.key. That change is not on airflow-ctl/v0-1-test: the branch
models follow the 3.2 spec, and Airflow 3.2 and 3.3 servers accept a
whitespace-only key.
---------
Co-authored-by: Rishabh <[email protected]>
Co-authored-by: Shahar Epstein <[email protected]>
---
.../airflowctl/ctl/commands/variable_command.py | 17 ++++++----
.../ctl/commands/test_variable_command.py | 39 ++++++++++++++++++++++
2 files changed, 50 insertions(+), 6 deletions(-)
diff --git a/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
b/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
index 19321002e44..750b94eb77f 100644
--- a/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
+++ b/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
@@ -21,7 +21,9 @@ import os
import sys
import rich
+from pydantic import ValidationError
from rich.console import Console
+from rich.markup import escape
from airflowctl.api.client import NEW_API_CLIENT, ClientKind,
provide_api_client
from airflowctl.api.datamodels.generated import (
@@ -30,6 +32,7 @@ from airflowctl.api.datamodels.generated import (
BulkCreateActionVariableBody,
VariableBody,
)
+from airflowctl.ctl.utils.validation import split_validation_errors
def _print_file_error(message: str, file_path: str) -> None:
@@ -63,13 +66,15 @@ def import_(args, api_client=NEW_API_CLIENT) -> list[str]:
if isinstance(v, dict) and "value" in v:
value, description = v["value"], v.get("description")
- vars_to_update.append(
- VariableBody(
- key=k,
- value=value,
- description=description,
+ try:
+ vars_to_update.append(VariableBody(key=k, value=value,
description=description))
+ except ValidationError as e:
+ _, invalid = split_validation_errors(VariableBody, e, str)
+ Console().print(
+ f"[red]Invalid variable {escape(repr(k))} in
{escape(args.file)}: {escape('; '.join(invalid))}",
+ soft_wrap=True,
)
- )
+ sys.exit(1)
bulk_body = BulkBodyVariableBody(
actions=[
diff --git
a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_variable_command.py
b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_variable_command.py
index f573585935f..bbc51738e8a 100644
--- a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_variable_command.py
+++ b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_variable_command.py
@@ -136,6 +136,45 @@ class TestCliVariableCommands:
assert "Invalid variable file:" in output
assert expected_json_path.as_posix() in output
+ @pytest.mark.parametrize(
+ ("variable_file", "expected_key", "expected_reason"),
+ [
+ (
+ {"my_key": {"value": "v", "description": 5}},
+ "'my_key'",
+ "description: Input should be a valid string",
+ ),
+ ({"k" * 251: "v"}, f"'{'k' * 251}'", "key: String should have at
most 250 characters"),
+ ],
+ ids=["non-string-description", "too-long-key"],
+ )
+ def test_import_reports_invalid_variable(
+ self, tmp_path, monkeypatch, capsys, variable_file, expected_key,
expected_reason
+ ):
+ monkeypatch.chdir(tmp_path)
+ expected_json_path = tmp_path / self.export_file_name
+ expected_json_path.write_text(json.dumps(variable_file))
+ bulk_called = False
+
+ def bulk(variables):
+ nonlocal bulk_called
+ bulk_called = True
+
+ api_client = SimpleNamespace(variables=SimpleNamespace(bulk=bulk))
+
+ with pytest.raises(SystemExit) as exit_info:
+ variable_command.import_(
+ self.parser.parse_args(["variables", "import",
expected_json_path.as_posix()]),
+ api_client=api_client,
+ )
+
+ assert exit_info.value.code == 1
+ output = " ".join(capsys.readouterr().out.split())
+ assert (
+ f"Invalid variable {expected_key} in
{expected_json_path.as_posix()}: {expected_reason}" in output
+ )
+ assert not bulk_called
+
def test_import_error(self, api_client_maker, tmp_path, monkeypatch):
api_client = api_client_maker(
path="/api/v2/variables",