Skip to content

airflowctl: action_on_existence flag returns empty success & errors in variables import #51310

Description

@jx2lee

Apache Airflow version

3.0.1

If "Other Airflow 2 version" selected, which one?

No response

What happened?

related: #50908
details: #50908 (comment)
cc. @bugraoz93 (comment)

What you think should happen instead?

With --action_on_existence fail, request should return 200 OK with right results.errors / results.success. But, empty

How to reproduce

When importing variables with --action_on_existence flag set to fail, overwrite, or skip,
the API responds 200 OK, but both results.success and results.errors are empty.
This makes it impossible to determine whether the existing variables were handled as expected.

Operating System

osx

Versions of Apache Airflow Providers

No response

Deployment

Other

Deployment details

No response

Anything else?

Reproduction Steps

  1. Pre-condition: Two variables already exist in Airflow
[
  {"key": "foo", "value": "1"},
  {"key": "bar", "value": "2"}
]
  1. Prepare import file (vars.json) containing the same keys:
[
  {"key": "foo", "value": "99"},
  {"key": "bar", "value": "88"}
]
  1. Run CLI:
uv run airflowctl variables import vars.json --action_on_existence fail

You'll found empty success/errors.

Are you willing to submit PR?

  • Yes I am willing to submit a PR!

Code of Conduct

Activity

  1. boring-cyborg commented on Jun 2, 2025

    @boring-cyborg

    Thanks for opening your first issue here! Be sure to follow the issue template! If you are willing to raise PR to address this issue please do so, no need to wait for approval.

  2. changed the title [-]airflowctl: action_on_existence flag returns empty success & errors arrays in variables import[/-] [+]airflow: action_on_existence flag returns empty success & errors in `variables import`[/+] on Jun 2, 2025
  3. changed the title [-]airflow: action_on_existence flag returns empty success & errors in `variables import`[/-] [+]airflowctl: action_on_existence flag returns empty success & errors in `variables import`[/+] on Jun 2, 2025
  4. added
    area:APIAirflow's REST/HTTP API
    and removed
    needs-triagelabel for new issues that we didn't triage yet
    on Jun 2, 2025
  5. bugraoz93 commented on Jun 2, 2025

    @bugraoz93
    Contributor

    I think testing with Postman and seeing if it is working for all bulk operations should be enough. Thanks for creating it! I assigned to you

  6. jx2lee commented on Jun 7, 2025

    @jx2lee
    ContributorAuthor

    @bugraoz93 I found potential problem!

    Airflow REST API documentation indicates that bulk variables endpoint returns a BulkResponse. However, bulk method in VariableOperation within airflowctl currently returns BulkActionResponse. This discrepancy could lead to inconsistencies in handling API responses.

    REST API response bulk variables.
    Image

    BulkResponse response scheme
    Image

    This diff might also exist in PoolOperation's bulk method.
    Should we consider updating these methods to return BulkResponse to align with the API spec? If you can provide guidance or direction, I'd be happy to create a PR to address this.

  7. jx2lee commented on Jun 7, 2025

    @jx2lee
    ContributorAuthor

    If we'll update methods to return BulkResponse, how about implementing it as follows?

    screencast.2025-06-07.21-53-43.mp4
  8. bugraoz93 commented on Jun 7, 2025

    @bugraoz93
    Contributor

    If we'll update methods to return BulkResponse, how about implementing it as follows?

    screencast.2025-06-07.21-53-43.mp4

    Sure, great catch @jx2lee! Thanks! Feel free to create a PR. We can indeed use the same model in airflowctl and fix this one. It would be easier to discuss over the code :) You did the change in operations right?

  9. jx2lee commented on Jun 7, 2025

    @jx2lee
    ContributorAuthor

    You did the change in operations right?

    @bugraoz93 Sure. I did change in operation, to BulkResponse. Once the output format is defined or guide is provided, I can quickly make it.
    Below pseudo code.

    diff --git a/airflow-ctl/src/airflowctl/api/operations.py b/airflow-ctl/src/airflowctl/api/operations.py
    index 00f7601348..485d157ad3 100644
    --- a/airflow-ctl/src/airflowctl/api/operations.py
    +++ b/airflow-ctl/src/airflowctl/api/operations.py
    @@ -33,6 +33,7 @@ from airflowctl.api.datamodels.generated import (
         BackfillCollectionResponse,
         BackfillPostBody,
         BackfillResponse,
    +    BulkResponse,
         BulkActionResponse,
         BulkBodyConnectionBody,
         BulkBodyPoolBody,
    @@ -676,7 +677,7 @@ class VariablesOperations(BaseOperations):
             """CRUD multiple variables."""
             try:
                 self.response = self.client.patch("variables", json=variables.model_dump())
    -            return BulkActionResponse.model_validate_json(self.response.content)
    +            return BulkResponse.model_validate_json(self.response.content)
             except ServerResponseError as e:
                 raise e
    
    diff --git a/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py b/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
    index 1c31828a37..b69c3ae0ab 100644
    --- a/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
    +++ b/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
    @@ -35,7 +35,8 @@ from airflowctl.api.datamodels.generated import (
     @provide_api_client(kind=ClientKind.CLI)
     def import_(args, api_client=NEW_API_CLIENT):
         """Import variables from a given file."""
    -    success_message = "[green]Import successful! success: {success}, errors: {errors}[/green]"
    +    success_message = "[green]Import successful! success: {success}[/green]"
    +    errors_message = "[red]But errors occured, errors: {errors}[/red]"
         if not os.path.exists(args.file):
             rich.print(f"[red]Missing variable file: {args.file}")
             sys.exit(1)
    @@ -71,8 +72,10 @@ def import_(args, api_client=NEW_API_CLIENT):
             ]
         )
         result = api_client.variables.bulk(variables=bulk_body)
    -    rich.print(success_message.format(success=result.success, errors=result.errors))
    -    return result.success, result.errors
    +    rich.print(success_message.format(success=result.create.success))
    +    if result.create.errors:
    +        rich.print(errors_message.format(errors=result.create.errors))
    +    return result.create.success, result.create.errors
    
  10. bugraoz93 commented on Jun 7, 2025

    @bugraoz93
    Contributor

    You did the change in operations right?

    @bugraoz93 Sure. I did change in operation, to BulkResponse. Once the output format is defined or guide is provided, I can quickly make it.
    Below pseudo code.

    diff --git a/airflow-ctl/src/airflowctl/api/operations.py b/airflow-ctl/src/airflowctl/api/operations.py
    index 00f7601348..485d157ad3 100644
    --- a/airflow-ctl/src/airflowctl/api/operations.py
    +++ b/airflow-ctl/src/airflowctl/api/operations.py
    @@ -33,6 +33,7 @@ from airflowctl.api.datamodels.generated import (
         BackfillCollectionResponse,
         BackfillPostBody,
         BackfillResponse,
    +    BulkResponse,
         BulkActionResponse,
         BulkBodyConnectionBody,
         BulkBodyPoolBody,
    @@ -676,7 +677,7 @@ class VariablesOperations(BaseOperations):
             """CRUD multiple variables."""
             try:
                 self.response = self.client.patch("variables", json=variables.model_dump())
    -            return BulkActionResponse.model_validate_json(self.response.content)
    +            return BulkResponse.model_validate_json(self.response.content)
             except ServerResponseError as e:
                 raise e
    
    diff --git a/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py b/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
    index 1c31828a37..b69c3ae0ab 100644
    --- a/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
    +++ b/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
    @@ -35,7 +35,8 @@ from airflowctl.api.datamodels.generated import (
     @provide_api_client(kind=ClientKind.CLI)
     def import_(args, api_client=NEW_API_CLIENT):
         """Import variables from a given file."""
    -    success_message = "[green]Import successful! success: {success}, errors: {errors}[/green]"
    +    success_message = "[green]Import successful! success: {success}[/green]"
    +    errors_message = "[red]But errors occured, errors: {errors}[/red]"
         if not os.path.exists(args.file):
             rich.print(f"[red]Missing variable file: {args.file}")
             sys.exit(1)
    @@ -71,8 +72,10 @@ def import_(args, api_client=NEW_API_CLIENT):
             ]
         )
         result = api_client.variables.bulk(variables=bulk_body)
    -    rich.print(success_message.format(success=result.success, errors=result.errors))
    -    return result.success, result.errors
    +    rich.print(success_message.format(success=result.create.success))
    +    if result.create.errors:
    +        rich.print(errors_message.format(errors=result.create.errors))
    +    return result.create.success, result.create.errors
    

    We always send create action, so it might be good to only print success and errors rather than for all actions. We can bring those functionalities in future releases such as in place updates. It doesn't need to be particularly in the current version

  11. jx2lee commented on Jun 8, 2025

    @jx2lee
    ContributorAuthor

    We always send create action, so it might be good to only print success and errors rather than for all actions. We can bring those functionalities in future releases such as in place updates. It doesn't need to be particularly in the current version

    Thanks for sharing your view! I agreed, make PR soon. 👍🏽

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions