seanghaeli opened a new pull request, #75251:
URL: https://github.com/apache/airflow/pull/75251

   In multi-team mode, a team's KubernetesExecutor already reads its namespace, 
pod template and the other pod settings from the team's config, but it still 
connects with the global `[kubernetes_executor]` settings. So every team ends 
up on the same cluster. A team can set its own `client_factory`, but the 
factory is called with no arguments, so it can't tell which team it's building 
a client for.
   
   This has a second problem for subclasses that bring their own client, like 
the AwsEksExecutor in #74399. The only way for a subclass to install its 
factory is the `[kubernetes_executor] client_factory` setting. A plain 
KubernetesExecutor in the same scope (both global, or the same team) reads that 
same setting, so it ends up sending its pods to the subclass's cluster.
   
   This PR:
   
   - Reads `in_cluster`, `config_file`, `cluster_context`, `verify_ssl`, 
`ssl_ca_cert` and `api_client_retry_configuration` through the team's config, 
for the sync client, the async client and the pod watcher (which already gets 
the team). They don't fall back to the global section, same as all other team 
config. `enable_tcp_keepalive` and `tcp_keep_*` stay global, since they set 
socket options for the whole process.
   - Calls a `client_factory` / `async_client_factory` with `team_name` if it 
takes that argument. Factories with no arguments are still called with none, so 
the 10.24.0 contract still works.
   - Lets a subclass set `client_factory` and `async_client_factory` as class 
attributes (import paths, so the watcher process can resolve them). They take 
precedence over the config settings and are called with `team_name`, so a 
KubernetesExecutor running next to the subclass keeps its own client.
   
   One thing worth calling out: `verify_ssl = False` changes the process-wide 
default `Configuration`. Now that teams can set it differently, a team's client 
sets it on that client only. So one team turning off TLS verification doesn't 
turn it off for the other teams' clients in the same scheduler, and a team 
doesn't pick it up from a global executor that turned it off. The no-team path 
is unchanged.
   
   Behavior change for multi-team users: a team that relied on the global 
connection settings now needs to set them for the team. Multi-team is still 
experimental, and I added a note at the top of the changelog.
   
   closes: #74844
   
   ### Testing
   
   Unit tests in Breeze (`test_kube_client.py`, `test_client.py`, 
`executors/test_kubernetes_executor.py`): 287 passed, 1 skipped. I also 
reverted each of the 26 pieces of the change one at a time and checked that a 
test fails for each one (26/26), and with all the source reverted to main, the 
new tests fail (24 failed).
   
   E2E on two real EKS clusters (A and B) with a real scheduler, Dag processor 
and API server, and one Dag per team. Each task prints the OIDC issuer of the 
cluster its pod runs on (from its service account token).
   
   Two teams, both KubernetesExecutor, with team-scoped 
`config_file`/`cluster_context` pointing at cluster A and cluster B. The global 
section points at cluster A.
   
   Before (this change reverted):
   ```
   01:23:19 RUN ii_tc_team_a state=success t=44s
   01:23:26 RUN ii_tc_team_b state=success t=51s
   --- kubectl get pods -n tc-e2e (cluster a: eks-systest-lane)
   NAME                                             STATUS     NODE             
                              RUN_ID
   tc-team-a-where-am-i-r851z2sy                    Completed  
ip-192-168-69-92.us-west-2.compute.internal    ii_tc_team_a
   tc-team-b-where-am-i-xoyo4rf2                    Completed  
ip-192-168-55-77.us-west-2.compute.internal    ii_tc_team_b
     pod/tc-team-a-where-am-i-r851z2sy: TC_E2E dag=tc_team_a 
pod=tc-team-a-where-am-i-r851z2sy namespace=tc-e2e 
issuer=.../id/FCCA5D040C7E38D31ACD37CD4CC74F77
     pod/tc-team-b-where-am-i-xoyo4rf2: TC_E2E dag=tc_team_b 
pod=tc-team-b-where-am-i-xoyo4rf2 namespace=tc-e2e 
issuer=.../id/FCCA5D040C7E38D31ACD37CD4CC74F77
   --- kubectl get pods -n tc-e2e (cluster b: eks-team-b-lane)
   No resources found in tc-e2e namespace.
   ```
   
   After:
   ```
   01:14:12 RUN ii_tc_team_a state=success t=45s
   01:14:18 RUN ii_tc_team_b state=success t=51s
   --- kubectl get pods -n tc-e2e (cluster a: eks-systest-lane)
   NAME                                             STATUS     NODE             
                              RUN_ID
   tc-team-a-where-am-i-bjz65ezy                    Completed  
ip-192-168-69-92.us-west-2.compute.internal    ii_tc_team_a
     pod/tc-team-a-where-am-i-bjz65ezy: TC_E2E dag=tc_team_a 
pod=tc-team-a-where-am-i-bjz65ezy namespace=tc-e2e 
issuer=.../id/FCCA5D040C7E38D31ACD37CD4CC74F77
   --- kubectl get pods -n tc-e2e (cluster b: eks-team-b-lane)
   NAME                                             STATUS     NODE             
                              RUN_ID
   tc-team-b-where-am-i-zwc4tcfy                    Completed  
ip-192-168-66-143.us-west-2.compute.internal   ii_tc_team_b
     pod/tc-team-b-where-am-i-zwc4tcfy: TC_E2E dag=tc_team_b 
pod=tc-team-b-where-am-i-zwc4tcfy namespace=tc-e2e 
issuer=.../id/CDA69AD28888CDCE3DE924E826AFBCDD
   ```
   
   The pod watcher builds its client from the team's settings too. The 
kubeconfig exec plugin logs which process asked for a token for which cluster: 
the scheduler and one watcher process per team, plus the async client team_b 
uses for pod creation:
   ```
   01:13:38 EXEC cluster=eks-systest-lane ppid=514 parent='airflow scheduler'
   01:13:40 EXEC cluster=eks-systest-lane ppid=596 parent='airflow scheduler'
   01:13:40 EXEC cluster=eks-team-b-lane ppid=514 parent='airflow scheduler'
   01:13:42 EXEC cluster=eks-team-b-lane ppid=656 parent='airflow scheduler'
   01:14:02 EXEC cluster=eks-team-b-lane ppid=514 parent='airflow scheduler'
   ```
   
   A team's `client_factory` that takes `team_name` is told its team (this one 
picks cluster B for team_a), and a no-argument factory (the 10.24.0 contract) 
still works. The scheduler and the watcher process (its child) each call the 
factory:
   ```
   01:20:42 RUN v3_tc_team_a state=success t=48s
   01:20:02 FACTORY build_client_for_team(team_name='team_a') called pid=1089 
ppid=1079
   01:20:04 FACTORY build_client_for_team(team_name='team_a') called pid=1169 
ppid=1089
     pod/tc-team-a-where-am-i-sm9o2230: TC_E2E dag=tc_team_a 
pod=tc-team-a-where-am-i-sm9o2230 namespace=tc-e2e 
issuer=.../id/CDA69AD28888CDCE3DE924E826AFBCDD
   
   01:19:29 RUN v2_tc_side_by_side state=success t=39s
   01:18:57 FACTORY build_client_no_args() called pid=790 ppid=780
   01:19:00 FACTORY build_client_no_args() called pid=834 ppid=790
     pod/tc-side-by-side-on-kubernetesexecutor-ywul9ns4: TC_E2E 
dag=tc_side_by_side pod=tc-side-by-side-on-kubernetesexecutor-ywul9ns4 
namespace=tc-e2e issuer=.../id/CDA69AD28888CDCE3DE924E826AFBCDD
   ```
   
   ---
   
   ##### Was generative AI tooling used to co-author this PR?
   
   - [X] Yes (please specify the tool below)
   
   Generated-by: Claude Code (Opus 5.5) following [the 
guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to