feat(yarn_client): auth config for YARN REST (none/simple/basic/kerberos)

Add per-Connection authentication so CDH 5 / Kerberos / HTTP Basic
clusters can be queried.

- auth_type: none / simple / basic / kerberos
- auth_user / auth_password for HTTP Basic
- auth_principal / auth_keytab stored for audit/display; actual SPNEGO
  handled by httpx-kerberos using the system Kerberos credential cache
- YarnClientConfig.auth_for_httpx() returns the right httpx.Auth object
- _request passes auth= through to httpx.request alongside verify=

New dependency: httpx-kerberos.

Tests cover none/simple (no auth object), Basic auth header,
Kerberos auth object, missing basic user, invalid auth_type, and
tool-layer config propagation.
This commit is contained in:
Claude
2026-06-26 13:57:57 +08:00
parent ad557b5984
commit 10f075ab7f
9 changed files with 256 additions and 8 deletions
+47
View File
@@ -1,4 +1,6 @@
# coding=utf-8
import base64
from unittest.mock import patch
import httpx
@@ -323,3 +325,48 @@ def test_ssl_verify_false_without_ca_bundle_uses_global_default():
with patch("spark_executor.core.yarn_client.httpx.request", return_value=fake) as m:
get_application_status("application_1", cfg)
assert m.call_args.kwargs["verify"] is False
# --- authentication configuration ---
def test_status_no_auth_for_none():
fake = _resp(200, json_data={"app": {"state": "RUNNING"}})
with patch("spark_executor.core.yarn_client.httpx.request", return_value=fake) as m:
get_application_status("application_1", YarnClientConfig(yarn_rm_url=RM, auth_type="none"))
assert m.call_args.kwargs["auth"] is None
def test_status_basic_auth_header():
fake = _resp(200, json_data={"app": {"state": "RUNNING"}})
cfg = YarnClientConfig(
yarn_rm_url=RM,
auth_type="basic",
auth_user="alice",
auth_password="pw",
)
with patch("spark_executor.core.yarn_client.httpx.request", return_value=fake) as m:
get_application_status("application_1", cfg)
auth = m.call_args.kwargs["auth"]
assert isinstance(auth, httpx.BasicAuth)
encoded = auth._auth_header.split(" ", 1)[1]
assert base64.b64decode(encoded).decode() == "alice:pw"
def test_status_no_auth_for_simple():
fake = _resp(200, json_data={"app": {"state": "RUNNING"}})
with patch("spark_executor.core.yarn_client.httpx.request", return_value=fake) as m:
get_application_status("application_1", YarnClientConfig(yarn_rm_url=RM, auth_type="simple"))
assert m.call_args.kwargs["auth"] is None
def test_status_kerberos_auth():
fake = _resp(200, json_data={"app": {"state": "RUNNING"}})
cfg = YarnClientConfig(yarn_rm_url=RM, auth_type="kerberos")
with patch("spark_executor.core.yarn_client.httpx.request", return_value=fake) as m:
get_application_status("application_1", cfg)
auth = m.call_args.kwargs["auth"]
assert auth is not None
assert "Kerberos" in type(auth).__name__
def test_basic_auth_missing_user_raises():
cfg = YarnClientConfig(yarn_rm_url=RM, auth_type="basic")
with pytest.raises(YarnConfigError, match="auth_type='basic' requires auth_user"):
get_application_status("application_1", cfg)