From c1a7fb45d42852b64413d4dd9cbe577b390cf59b Mon Sep 17 00:00:00 2001 From: Niels Bantilan Date: Fri, 14 Aug 2026 10:28:39 -0400 Subject: [PATCH] fix: expose ca_cert_file_path and insecure_skip_verify in init_from_api_key Users behind TLS-intercepting corporate proxies hit `SSL: CERTIFICATE_VERIFY_FAILED` on the OAuth token refresh when authenticating with `flyte.init_from_api_key()`, because the function hardcoded `insecure_skip_verify=False` and offered no way to supply a custom CA bundle. `flyte.init()` already supports both options, so thread them through from `init_from_api_key` as well. Co-Authored-By: Claude Fable 5 --- src/flyte/_initialize.py | 10 +++++++- tests/flyte/test_init_from_api_key.py | 36 +++++++++++++++++++++++++++ tests/user_api/test_init_api_key.py | 32 ++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/flyte/_initialize.py b/src/flyte/_initialize.py index 8e31e8892..70d66c198 100644 --- a/src/flyte/_initialize.py +++ b/src/flyte/_initialize.py @@ -470,6 +470,8 @@ async def init_from_api_key( image_builder: ImageBuildEngine.ImageBuilderType = "local", images: typing.Dict[str, str] | None = None, sync_local_sys_paths: bool = True, + insecure_skip_verify: bool = False, + ca_cert_file_path: str | None = None, ) -> None: """ Initialize the Flyte system using an API key for authentication. This is a convenience @@ -495,6 +497,11 @@ async def init_from_api_key( images: Optional dict of images that can be used by referencing the image name sync_local_sys_paths: Whether to include and synchronize local sys.path entries under the root directory into the remote container (default: True) + insecure_skip_verify: Whether to skip SSL certificate verification + ca_cert_file_path: Optional path to a CA certificate bundle used to verify the server certificate. + Useful behind TLS-intercepting corporate proxies that re-sign traffic with a private CA. + Note that if insecure_skip_verify is also set, it takes precedence and certificate + verification against this bundle is skipped. Returns: None @@ -531,7 +538,8 @@ async def init_from_api_key( log_level=log_level, log_format=log_format, insecure=False, - insecure_skip_verify=False, + insecure_skip_verify=insecure_skip_verify, + ca_cert_file_path=ca_cert_file_path, storage=storage, batch_size=batch_size, image_builder=image_builder, diff --git a/tests/flyte/test_init_from_api_key.py b/tests/flyte/test_init_from_api_key.py index c8ed2432a..eb728934f 100644 --- a/tests/flyte/test_init_from_api_key.py +++ b/tests/flyte/test_init_from_api_key.py @@ -125,6 +125,42 @@ def test_init_from_api_key_with_none_org(): assert call_kwargs["org"] is None +def test_init_from_api_key_tls_defaults(): + """Test that TLS options default to insecure_skip_verify=False and ca_cert_file_path=None.""" + endpoint = "test.flyte.example.com" + encoded_api_key = create_encoded_api_key(endpoint, "test_client_id", "test_client_secret", "test-org") + + # Mock the init.aio function + with mock.patch("flyte._initialize.init.aio", new_callable=mock.AsyncMock) as mock_init: + init_from_api_key(api_key=encoded_api_key, project="test-project", domain="test-domain") + + call_kwargs = mock_init.call_args.kwargs + assert call_kwargs["insecure"] is False + assert call_kwargs["insecure_skip_verify"] is False + assert call_kwargs["ca_cert_file_path"] is None + + +def test_init_from_api_key_tls_options_forwarded(): + """Test that insecure_skip_verify and ca_cert_file_path are forwarded to init.aio.""" + endpoint = "test.flyte.example.com" + encoded_api_key = create_encoded_api_key(endpoint, "test_client_id", "test_client_secret", "test-org") + + # Mock the init.aio function + with mock.patch("flyte._initialize.init.aio", new_callable=mock.AsyncMock) as mock_init: + init_from_api_key( + api_key=encoded_api_key, + project="test-project", + domain="test-domain", + insecure_skip_verify=True, + ca_cert_file_path="/path/ca.pem", + ) + + call_kwargs = mock_init.call_args.kwargs + assert call_kwargs["insecure"] is False + assert call_kwargs["insecure_skip_verify"] is True + assert call_kwargs["ca_cert_file_path"] == "/path/ca.pem" + + def test_init_from_api_key_parameter_override(): """Test that init_from_api_key uses provided parameters correctly.""" endpoint = "test.flyte.example.com" diff --git a/tests/user_api/test_init_api_key.py b/tests/user_api/test_init_api_key.py index 99e9045a8..a6406231f 100644 --- a/tests/user_api/test_init_api_key.py +++ b/tests/user_api/test_init_api_key.py @@ -52,6 +52,38 @@ async def test_init_from_api_key_reads_env(self, mock_sanitize, mock_decode, moc mock_decode.assert_called_once_with("env-key") + @patch("flyte._initialize.init") + @patch("flyte.remote._client.auth._auth_utils.decode_api_key") + @patch("flyte._utils.sanitize_endpoint") + @pytest.mark.asyncio + async def test_init_from_api_key_tls_defaults(self, mock_sanitize, mock_decode, mock_init): + mock_decode.return_value = ("test.endpoint.com", "client-id", "client-secret", "my-org") + mock_sanitize.return_value = "https://test.endpoint.com" + mock_init.aio = AsyncMock() + + await init_from_api_key.aio(api_key="encoded-key") + + call_kwargs = mock_init.aio.call_args[1] + assert call_kwargs["insecure"] is False + assert call_kwargs["insecure_skip_verify"] is False + assert call_kwargs["ca_cert_file_path"] is None + + @patch("flyte._initialize.init") + @patch("flyte.remote._client.auth._auth_utils.decode_api_key") + @patch("flyte._utils.sanitize_endpoint") + @pytest.mark.asyncio + async def test_init_from_api_key_forwards_tls_options(self, mock_sanitize, mock_decode, mock_init): + mock_decode.return_value = ("test.endpoint.com", "client-id", "client-secret", "my-org") + mock_sanitize.return_value = "https://test.endpoint.com" + mock_init.aio = AsyncMock() + + await init_from_api_key.aio(api_key="encoded-key", insecure_skip_verify=True, ca_cert_file_path="/path/ca.pem") + + call_kwargs = mock_init.aio.call_args[1] + assert call_kwargs["insecure"] is False + assert call_kwargs["insecure_skip_verify"] is True + assert call_kwargs["ca_cert_file_path"] == "/path/ca.pem" + class TestInitPassthrough: @pytest.fixture(autouse=True)