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

potiuk 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 69b72e39c05 Multi-Team: Add teams inspect CLI command to display team 
resources (#72036)
69b72e39c05 is described below

commit 69b72e39c050587826eca524c036dda09c1e2bab
Author: SameerMesiah97 <[email protected]>
AuthorDate: Tue Sep 22 23:45:57 2026 +0100

    Multi-Team: Add teams inspect CLI command to display team resources (#72036)
    
    * Add teams inspect CLI command
    
    Adds a new  command to display the resources
    associated with a team. The command reports DAG bundle associations,
    pools, connections, and variables for the requested team, providing a
    single view of team-owned resources.
    
    Also adds unit tests covering teams with associated resources, empty
    teams, and nonexistent teams.
    
    * Use AirflowConsole for team inspect output to format results consistently 
across table, JSON, YAML, and plain output modes, add filtering coverage for 
resources belonging to other teams, and update empty-team output tests.
    
    * Remove mapper from console print and add multi-team check to teams 
inspect command. Add unit test for multi-team disabled scenario.
    
    ---------
    
    Co-authored-by: Sameer Mesiah <[email protected]>
---
 airflow-core/newsfragments/72036.feature.rst       |   1 +
 airflow-core/src/airflow/cli/cli_config.py         |   7 ++
 .../src/airflow/cli/commands/team_command.py       |  47 ++++++++
 .../tests/unit/cli/commands/test_team_command.py   | 123 +++++++++++++++++++++
 4 files changed, 178 insertions(+)

diff --git a/airflow-core/newsfragments/72036.feature.rst 
b/airflow-core/newsfragments/72036.feature.rst
new file mode 100644
index 00000000000..c28d6e32467
--- /dev/null
+++ b/airflow-core/newsfragments/72036.feature.rst
@@ -0,0 +1 @@
+Add an ``airflow teams inspect`` CLI command for displaying resources 
associated with a team, including Dag bundles, pools, connections, and 
variables.
diff --git a/airflow-core/src/airflow/cli/cli_config.py 
b/airflow-core/src/airflow/cli/cli_config.py
index 234a812ba8d..e6f1a01975f 100644
--- a/airflow-core/src/airflow/cli/cli_config.py
+++ b/airflow-core/src/airflow/cli/cli_config.py
@@ -1692,6 +1692,13 @@ TEAMS_COMMANDS = (
         
func=lazy_load_command("airflow.cli.commands.team_command.team_verify"),
         args=(ARG_VERBOSE,),
     ),
+    ActionCommand(
+        name="inspect",
+        help="Inspect a team",
+        description="Display resources associated with a team.\n",
+        
func=lazy_load_command("airflow.cli.commands.team_command.team_inspect"),
+        args=(ARG_TEAM_NAME, ARG_OUTPUT, ARG_VERBOSE),
+    ),
 )
 STATE_STORE_COMMANDS = (
     ActionCommand(
diff --git a/airflow-core/src/airflow/cli/commands/team_command.py 
b/airflow-core/src/airflow/cli/commands/team_command.py
index bc9bbeb8075..5f739e475ce 100644
--- a/airflow-core/src/airflow/cli/commands/team_command.py
+++ b/airflow-core/src/airflow/cli/commands/team_command.py
@@ -303,3 +303,50 @@ def team_verify(args, *, session=NEW_SESSION):
         raise SystemExit(1)
 
     print("Verification succeeded.")
+
+
+@cli_utils.action_cli
+@providers_configuration_loaded
+@provide_session
+def team_inspect(args, *, session=NEW_SESSION):
+    """Inspect resources belonging to a team."""
+    if not conf.getboolean("core", "multi_team"):
+        print("Multi-team is not enabled.")
+        return
+
+    team_name = _extract_team_name(args)
+
+    team = session.scalar(select(Team).where(Team.name == team_name))
+    if team is None:
+        raise SystemExit(f"Team '{team_name}' does not exist")
+
+    bundle_names = session.scalars(
+        select(dag_bundle_team_association_table.c.dag_bundle_name)
+        .where(dag_bundle_team_association_table.c.team_name == team_name)
+        .order_by(dag_bundle_team_association_table.c.dag_bundle_name)
+    ).all()
+
+    pool_names = session.scalars(
+        select(Pool.pool).where(Pool.team_name == 
team_name).order_by(Pool.pool)
+    ).all()
+
+    connection_ids = session.scalars(
+        select(Connection.conn_id).where(Connection.team_name == 
team_name).order_by(Connection.conn_id)
+    ).all()
+
+    variable_keys = session.scalars(
+        select(Variable.key).where(Variable.team_name == 
team_name).order_by(Variable.key)
+    ).all()
+
+    AirflowConsole().print_as(
+        data=[
+            {
+                "name": team.name,
+                "dag_bundles": bundle_names,
+                "pools": pool_names,
+                "connections": connection_ids,
+                "variables": variable_keys,
+            }
+        ],
+        output=args.output,
+    )
diff --git a/airflow-core/tests/unit/cli/commands/test_team_command.py 
b/airflow-core/tests/unit/cli/commands/test_team_command.py
index 90004fd9ae7..4f1889dd383 100644
--- a/airflow-core/tests/unit/cli/commands/test_team_command.py
+++ b/airflow-core/tests/unit/cli/commands/test_team_command.py
@@ -628,3 +628,126 @@ class TestCliTeams:
                     team_command.team_verify(self.parser.parse_args(["teams", 
"verify"]))
 
         assert "references unknown team 'missing-team'" in stdout.getvalue()
+
+    def test_team_inspect(self, stdout_capture):
+        """Test inspecting a team with associated resources."""
+        self.session.add_all([Team(name="team1"), Team(name="team2")])
+        self.session.commit()
+
+        self.session.add_all(
+            [
+                DagBundleModel(name="bundle1"),
+                DagBundleModel(name="bundle2"),
+            ]
+        )
+        self.session.commit()
+
+        self.session.execute(
+            dag_bundle_team_association_table.insert(),
+            [
+                {"dag_bundle_name": "bundle1", "team_name": "team1"},
+                {"dag_bundle_name": "bundle2", "team_name": "team2"},
+            ],
+        )
+
+        self.session.add_all(
+            [
+                Pool(
+                    pool=Pool.get_default_team_pool_name("team1"),
+                    slots=128,
+                    description="Default pool",
+                    include_deferred=False,
+                    team_name="team1",
+                ),
+                Connection(conn_id="conn1", conn_type="http", 
team_name="team1"),
+                Variable(key="var1", val="value", team_name="team1"),
+                Pool(
+                    pool="pool1",
+                    slots=5,
+                    description="Additional pool",
+                    include_deferred=False,
+                    team_name="team1",
+                ),
+                Pool(
+                    pool=Pool.get_default_team_pool_name("team2"),
+                    slots=128,
+                    description="Default pool",
+                    include_deferred=False,
+                    team_name="team2",
+                ),
+                Connection(conn_id="conn2", conn_type="http", 
team_name="team2"),
+                Variable(key="var2", val="value", team_name="team2"),
+                Pool(
+                    pool="pool2",
+                    slots=5,
+                    description="Additional pool",
+                    include_deferred=False,
+                    team_name="team2",
+                ),
+            ]
+        )
+        self.session.commit()
+
+        with conf_vars({("core", "multi_team"): "True"}):
+            with stdout_capture as stdout:
+                team_command.team_inspect(
+                    self.parser.parse_args(["teams", "inspect", "team1", 
"--output", "json"])
+                )
+
+        assert json.loads(stdout.getvalue()) == [
+            {
+                "name": "team1",
+                "dag_bundles": ["bundle1"],
+                "pools": [
+                    Pool.get_default_team_pool_name("team1"),
+                    "pool1",
+                ],
+                "connections": ["conn1"],
+                "variables": ["var1"],
+            }
+        ]
+
+    def test_team_inspect_empty_team(self, stdout_capture):
+        """Test inspecting a team with no associated resources."""
+        self.session.add(Team(name="team1"))
+        self.session.commit()
+
+        self.session.add(
+            Pool(
+                pool=Pool.get_default_team_pool_name("team1"),
+                slots=128,
+                description="Default pool",
+                include_deferred=False,
+                team_name="team1",
+            )
+        )
+
+        self.session.commit()
+        with conf_vars({("core", "multi_team"): "True"}):
+            with stdout_capture as stdout:
+                team_command.team_inspect(
+                    self.parser.parse_args(["teams", "inspect", "team1", 
"--output", "json"])
+                )
+
+        assert json.loads(stdout.getvalue()) == [
+            {
+                "name": "team1",
+                "dag_bundles": [],
+                "pools": [Pool.get_default_team_pool_name("team1")],
+                "connections": [],
+                "variables": [],
+            }
+        ]
+
+    def test_team_inspect_nonexistent_team(self):
+        """Test inspecting a team that does not exist."""  #
+        with conf_vars({("core", "multi_team"): "True"}):
+            with pytest.raises(SystemExit, match="Team 'team1' does not 
exist"):
+                team_command.team_inspect(self.parser.parse_args(["teams", 
"inspect", "team1"]))
+
+    def test_team_inspect_multi_team_disabled(self, stdout_capture):
+        with conf_vars({("core", "multi_team"): "False"}):
+            with stdout_capture as stdout:
+                team_command.team_inspect(self.parser.parse_args(["teams", 
"inspect", "team1"]))
+
+        assert "Multi-team is not enabled." in stdout.getvalue()

Reply via email to