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 d4a0861867d Add queue parameter to edge list-workers subcommand 
(#70436)
d4a0861867d is described below

commit d4a0861867d9cab0289f17d82e79f94cc9044d8d
Author: Shubham Raj <[email protected]>
AuthorDate: Sun Jul 26 22:15:01 2026 +0530

    Add queue parameter to edge list-workers subcommand (#70436)
    
    * add queue paarmeter to list-workers
    
    * refactor
---
 providers/edge3/docs/deployment.rst                |  3 ++-
 .../src/airflow/providers/edge3/cli/definition.py  |  1 +
 .../airflow/providers/edge3/cli/edge_command.py    |  4 +++-
 .../airflow/providers/edge3/models/edge_worker.py  | 20 ++++++++++++++---
 .../edge3/tests/unit/edge3/cli/test_definition.py  |  6 ++++++
 .../edge3/tests/unit/edge3/cli/test_worker.py      | 20 ++++++++++++++++-
 .../tests/unit/edge3/models/test_edge_worker.py    | 25 ++++++++++++++++++++--
 7 files changed, 71 insertions(+), 8 deletions(-)

diff --git a/providers/edge3/docs/deployment.rst 
b/providers/edge3/docs/deployment.rst
index ebbdef2e5fb..a48fea1bd53 100644
--- a/providers/edge3/docs/deployment.rst
+++ b/providers/edge3/docs/deployment.rst
@@ -250,7 +250,8 @@ instance. The commands are:
 
 - ``airflow edge list-workers``: List all workers in the cluster. Accepts an 
optional
   ``--worker-name-pattern`` glob (e.g. ``'prod-*'``) to filter workers by name,
-  and ``-s``/``--state`` to filter by worker state.
+  ``-s``/``--state`` to filter by worker state, and ``-q``/``--queues`` (comma
+  delimited) to list only workers serving any of the given queues.
 - ``airflow edge remote-edge-worker-request-maintenance``: Request a remote 
edge worker to enter maintenance mode
 - ``airflow edge remote-edge-worker-update-maintenance-comment``: Updates the 
maintenance comment for a remote edge worker
 - ``airflow edge remote-edge-worker-exit-maintenance``: Request a remote edge 
worker to exit maintenance mode
diff --git a/providers/edge3/src/airflow/providers/edge3/cli/definition.py 
b/providers/edge3/src/airflow/providers/edge3/cli/definition.py
index 97a609ad1a9..b1b89bbb469 100644
--- a/providers/edge3/src/airflow/providers/edge3/cli/definition.py
+++ b/providers/edge3/src/airflow/providers/edge3/cli/definition.py
@@ -189,6 +189,7 @@ EDGE_COMMANDS: list[ActionCommand] = [
             ARG_OUTPUT,
             ARG_STATE,
             ARG_WORKER_NAME_PATTERN,
+            ARG_QUEUES,
         ),
     ),
     ActionCommand(
diff --git a/providers/edge3/src/airflow/providers/edge3/cli/edge_command.py 
b/providers/edge3/src/airflow/providers/edge3/cli/edge_command.py
index 7a9186f2f57..2088bd3bb75 100644
--- a/providers/edge3/src/airflow/providers/edge3/cli/edge_command.py
+++ b/providers/edge3/src/airflow/providers/edge3/cli/edge_command.py
@@ -261,7 +261,9 @@ def list_edge_workers(args) -> None:
     from airflow.providers.edge3.models.edge_worker import 
get_registered_edge_hosts
 
     all_hosts_iter = get_registered_edge_hosts(
-        states=args.state, worker_name_pattern=args.worker_name_pattern
+        states=args.state,
+        worker_name_pattern=args.worker_name_pattern,
+        queues=args.queues.split(",") if args.queues else None,
     )
     # Format and print worker info on the screen
     fields = [
diff --git a/providers/edge3/src/airflow/providers/edge3/models/edge_worker.py 
b/providers/edge3/src/airflow/providers/edge3/models/edge_worker.py
index e20ca7db058..442b1545932 100644
--- a/providers/edge3/src/airflow/providers/edge3/models/edge_worker.py
+++ b/providers/edge3/src/airflow/providers/edge3/models/edge_worker.py
@@ -256,6 +256,7 @@ def _fetch_edge_hosts_from_db(
     hostname: str | None = None,
     states: list | None = None,
     worker_name_pattern: str | None = None,
+    queues: list[str] | None = None,
     *,
     session: Session = NEW_SESSION,
 ) -> Sequence[EdgeWorkerModel]:
@@ -269,15 +270,28 @@ def _fetch_edge_hosts_from_db(
             
EdgeWorkerModel.worker_name.like(_glob_to_like_pattern(worker_name_pattern), 
escape="\\")
         )
     query = query.order_by(EdgeWorkerModel.worker_name)
-    return session.scalars(query).all()
+    workers = session.scalars(query).all()
+    if queues:
+        # Queues are stored as a repr-encoded list in a single column, so exact
+        # membership is filtered in Python to avoid substring false positives. 
A
+        # worker matches if it serves any of the requested queues.
+        wanted = set(queues)
+        workers = [worker for worker in workers if worker.queues and 
wanted.intersection(worker.queues)]
+    return workers
 
 
 @providers_configuration_loaded
 @provide_session
 def get_registered_edge_hosts(
-    *, states: list | None = None, worker_name_pattern: str | None = None, 
session: Session = NEW_SESSION
+    *,
+    states: list | None = None,
+    worker_name_pattern: str | None = None,
+    queues: list[str] | None = None,
+    session: Session = NEW_SESSION,
 ):
-    return _fetch_edge_hosts_from_db(states=states, 
worker_name_pattern=worker_name_pattern, session=session)
+    return _fetch_edge_hosts_from_db(
+        states=states, worker_name_pattern=worker_name_pattern, queues=queues, 
session=session
+    )
 
 
 @provide_session
diff --git a/providers/edge3/tests/unit/edge3/cli/test_definition.py 
b/providers/edge3/tests/unit/edge3/cli/test_definition.py
index cb225e89ffd..cf6bb9e18ea 100644
--- a/providers/edge3/tests/unit/edge3/cli/test_definition.py
+++ b/providers/edge3/tests/unit/edge3/cli/test_definition.py
@@ -160,6 +160,12 @@ class TestEdgeCliDefinition:
         assert args.state == ["running", "maintenance"]
         assert args.worker_name_pattern == "prod-*"
 
+    def test_list_workers_command_queues_arg(self):
+        """Test list-workers command with the queues filter."""
+        params = ["edge", "list-workers", "--queues", "gpu,default"]
+        args = self.arg_parser.parse_args(params)
+        assert args.queues == "gpu,default"
+
     def test_remote_edge_worker_request_maintenance_args(self):
         """Test remote-edge-worker-request-maintenance command with required 
arguments."""
         params = [
diff --git a/providers/edge3/tests/unit/edge3/cli/test_worker.py 
b/providers/edge3/tests/unit/edge3/cli/test_worker.py
index f959237e5ad..2a7c2675bb7 100644
--- a/providers/edge3/tests/unit/edge3/cli/test_worker.py
+++ b/providers/edge3/tests/unit/edge3/cli/test_worker.py
@@ -1130,7 +1130,25 @@ class TestEdgeWorker:
                 ) as mock_get_hosts,
             ):
                 edge_command.list_edge_workers(args)
-        mock_get_hosts.assert_called_once_with(states=None, 
worker_name_pattern="prod-*")
+        mock_get_hosts.assert_called_once_with(states=None, 
worker_name_pattern="prod-*", queues=None)
+
+    @pytest.mark.db_test
+    def test_list_edge_workers_passes_queues(self, mock_edgeworker: 
EdgeWorkerModel):
+        args = self.parser.parse_args(["edge", "list-workers", "--output", 
"json", "--queues", "gpu,default"])
+        with contextlib.redirect_stdout(StringIO()):
+            with (
+                patch(
+                    
"airflow.providers.edge3.cli.edge_command._check_valid_db_connection",
+                ),
+                patch(
+                    
"airflow.providers.edge3.models.edge_worker.get_registered_edge_hosts",
+                    return_value=[mock_edgeworker],
+                ) as mock_get_hosts,
+            ):
+                edge_command.list_edge_workers(args)
+        mock_get_hosts.assert_called_once_with(
+            states=None, worker_name_pattern=None, queues=["gpu", "default"]
+        )
 
 
 class TestSignalHandling:
diff --git a/providers/edge3/tests/unit/edge3/models/test_edge_worker.py 
b/providers/edge3/tests/unit/edge3/models/test_edge_worker.py
index 85ddefdb02e..ab7c0550e79 100644
--- a/providers/edge3/tests/unit/edge3/models/test_edge_worker.py
+++ b/providers/edge3/tests/unit/edge3/models/test_edge_worker.py
@@ -97,8 +97,13 @@ class TestGetRegisteredEdgeHosts:
     @pytest.fixture(autouse=True)
     def setup_test_cases(self, session: Session):
         session.execute(delete(EdgeWorkerModel))
-        for name in ("prod-worker-1", "prod-worker-2", "dev-worker-1"):
-            session.add(EdgeWorkerModel(worker_name=name, queues=["default"], 
state=EdgeWorkerState.RUNNING))
+        queues_by_name = {
+            "prod-worker-1": ["default", "gpu"],
+            "prod-worker-2": ["default"],
+            "dev-worker-1": ["gpu"],
+        }
+        for name, queues in queues_by_name.items():
+            session.add(EdgeWorkerModel(worker_name=name, queues=queues, 
state=EdgeWorkerState.RUNNING))
         session.commit()
 
     def test_no_pattern_returns_all(self, session: Session):
@@ -120,3 +125,19 @@ class TestGetRegisteredEdgeHosts:
     def test_no_match_returns_empty(self, session: Session):
         hosts = get_registered_edge_hosts(worker_name_pattern="nonexistent-*", 
session=session)
         assert list(hosts) == []
+
+    def test_queues_filters_by_exact_membership(self, session: Session):
+        hosts = get_registered_edge_hosts(queues=["gpu"], session=session)
+        assert {h.worker_name for h in hosts} == {"prod-worker-1", 
"dev-worker-1"}
+
+    def test_queues_matches_any_of_multiple(self, session: Session):
+        hosts = get_registered_edge_hosts(queues=["gpu", "default"], 
session=session)
+        assert {h.worker_name for h in hosts} == {"prod-worker-1", 
"prod-worker-2", "dev-worker-1"}
+
+    def test_queues_no_match_returns_empty(self, session: Session):
+        hosts = get_registered_edge_hosts(queues=["nonexistent"], 
session=session)
+        assert list(hosts) == []
+
+    def test_queues_combined_with_name_pattern(self, session: Session):
+        hosts = get_registered_edge_hosts(worker_name_pattern="prod-*", 
queues=["gpu"], session=session)
+        assert {h.worker_name for h in hosts} == {"prod-worker-1"}

Reply via email to