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

shahar1 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 3dc7a60a063 Fix GKE provider 401 on kubernetes client 36.x (#69032)
3dc7a60a063 is described below

commit 3dc7a60a063c1a90558789d546bc524480a62311
Author: Shahar Epstein <[email protected]>
AuthorDate: Fri Jun 26 16:22:20 2026 +0300

    Fix GKE provider 401 on kubernetes client 36.x (#69032)
    
    kubernetes-client 36.x renamed the bearer-token auth key from 
'authorization'
    to 'BearerToken'. The GKE connection hand-builds a Configuration and 
registered
    the token/prefix only under 'authorization', so on 36.x the 'Bearer' prefix 
is
    dropped and requests go out with the raw token, which the cluster rejects 
with
    401. Register the token and prefix under both keys so authentication works 
on
    client 35.x and 36.x alike, which lets the provider support 36.x without
    downgrading the kubernetes client (and reintroducing the NO_PROXY issue 36.x
    fixed).
---
 .../google/cloud/hooks/kubernetes_engine.py        | 13 ++++++++---
 .../google/cloud/hooks/test_kubernetes_engine.py   | 27 ++++++++++++++++++++++
 2 files changed, 37 insertions(+), 3 deletions(-)

diff --git 
a/providers/google/src/airflow/providers/google/cloud/hooks/kubernetes_engine.py
 
b/providers/google/src/airflow/providers/google/cloud/hooks/kubernetes_engine.py
index 1a935438d10..a721578132e 100644
--- 
a/providers/google/src/airflow/providers/google/cloud/hooks/kubernetes_engine.py
+++ 
b/providers/google/src/airflow/providers/google/cloud/hooks/kubernetes_engine.py
@@ -80,13 +80,20 @@ class GKEClusterConnection:
         return client.ApiClient(configuration)
 
     def _refresh_api_key_hook(self, configuration: 
client.configuration.Configuration):
-        configuration.api_key = {"authorization": 
self._get_token(self._credentials)}
+        configuration.api_key = 
self._bearer_api_key(self._get_token(self._credentials))
+
+    @staticmethod
+    def _bearer_api_key(token: str) -> dict[str, str]:
+        # kubernetes-client 36.x renamed the bearer auth key 'authorization' 
-> 'BearerToken'
+        # (https://github.com/kubernetes-client/python/issues/2582). Register 
both so the 'Bearer'
+        # prefix is applied on client 35.x and 36.x alike; otherwise 36.x 
sends the raw token -> 401.
+        return {"authorization": token, "BearerToken": token}
 
     def _get_config(self) -> client.configuration.Configuration:
         configuration = client.Configuration(
             host=self._cluster_url,
-            api_key_prefix={"authorization": "Bearer"},
-            api_key={"authorization": self._get_token(self._credentials)},
+            api_key_prefix={"authorization": "Bearer", "BearerToken": 
"Bearer"},
+            api_key=self._bearer_api_key(self._get_token(self._credentials)),
         )
         if not self.use_dns_endpoint:
             configuration.ssl_ca_cert = FileOrData(
diff --git 
a/providers/google/tests/unit/google/cloud/hooks/test_kubernetes_engine.py 
b/providers/google/tests/unit/google/cloud/hooks/test_kubernetes_engine.py
index 3121ee1b7da..f3a1a416204 100644
--- a/providers/google/tests/unit/google/cloud/hooks/test_kubernetes_engine.py
+++ b/providers/google/tests/unit/google/cloud/hooks/test_kubernetes_engine.py
@@ -31,6 +31,7 @@ from google.cloud.container_v1.types import Cluster
 from airflow.providers.common.compat.sdk import AirflowException
 from airflow.providers.google.cloud.hooks.kubernetes_engine import (
     GKEAsyncHook,
+    GKEClusterConnection,
     GKEHook,
     GKEKubernetesAsyncHook,
     GKEKubernetesHook,
@@ -486,6 +487,32 @@ class TestGKEKubernetesHookDeployments:
         
mock_super.return_value.apply_from_yaml_file.assert_called_once_with(**expected_kwargs)
 
 
+class TestGKEClusterConnection:
+    @pytest.mark.parametrize("expired", [False, True])
+    def test_registers_bearer_token_for_client_35_and_36(self, expired):
+        # The bearer token and its "Bearer" prefix must be registered under 
both the client-35.x key
+        # ('authorization') and the client-36.x key ('BearerToken', renamed in
+        # https://github.com/kubernetes-client/python/issues/2582). Asserting 
the registration
+        # directly verifies both-version support regardless of which client CI 
installed; the
+        # auth_settings() check then confirms the installed client emits the 
prefixed header.
+        credentials = mock.MagicMock()
+        credentials.token = "the-token"
+        credentials.expired = expired
+        credentials.refresh = lambda request: setattr(credentials, "token", 
"the-token")
+        conn = GKEClusterConnection(
+            cluster_url="https://cluster";,
+            ssl_ca_cert=None,
+            credentials=credentials,
+            use_dns_endpoint=True,
+        )
+
+        config = conn.get_conn().configuration
+
+        assert config.api_key == {"authorization": "the-token", "BearerToken": 
"the-token"}
+        assert config.api_key_prefix == {"authorization": "Bearer", 
"BearerToken": "Bearer"}
+        assert config.auth_settings()["BearerToken"]["value"] == "Bearer 
the-token"
+
+
 class TestGKEKubernetesAsyncHook:
     @staticmethod
     def make_mock_awaitable(mock_obj, result=None):

Reply via email to