Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for keyring errors when initializing Flyte for_sandbox config client #2962

Open
wants to merge 43 commits into
base: master
Choose a base branch
from

Conversation

taieeuu
Copy link
Contributor

@taieeuu taieeuu commented Nov 27, 2024

Fix for keyring errors when initializing Flyte for_sandbox config client (needs verification)

Tracking issue

flyteorg/flyte#4354

What changes were proposed in this pull request?

First, I started by analyzing the issue reporter's code and modified the Config.for_sandbox() method to add a new value AuthType.No_Auth to auth_mode. Additionally, I updated the AuthUnaryInterceptor class to handle gRPC responses. If a 401 error (i.e., grpc.StatusCode.UNAUTHENTICATED) is encountered, it will trigger the creation of a PKCE authenticator.

How was this patch tested?

I am having difficulty replicating the issue reporter's environment, as it seems to require a public domain setup and GNOME keyring installed. However, I followed the suggestions from the discussion forum and implemented this fix.

Check all the applicable boxes

  • I updated the documentation accordingly.
  • All new and existing tests passed.
  • All commits are signed-off.

Summary by Bito

Implementation of keyring error handling for Flyte sandbox config client with lazy authentication initialization using factory pattern. The changes include gRPC health checking, improved authentication flow, and prevention of immediate keyring access during client initialization. The solution features service availability verification, exception handling for gRPC status codes, and enhanced authentication interceptor functionality for credential refresh and failure handling.

Unit tests added: False

Estimated effort to review (1-5, lower is better): 2

Copy link

welcome bot commented Nov 27, 2024

Thank you for opening this pull request! 🙌

These tips will help get your PR across the finish line:

  • Most of the repos have a PR template; if not, fill it out to the best of your knowledge.
  • Sign off your commits (Reference: DCO Guide).

Comment on lines 70 to 71
if e.code() == grpc.StatusCode.UNAUTHENTICATED or e.code() == grpc.StatusCode.UNKNOWN:
self._authenticator.refresh_credentials()
updated_call_details = self._call_details_with_auth_metadata(client_call_details)
return continuation(updated_call_details, request)
return self._handle_unauthenticated_error(fut, client_call_details, request)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add comments about response 401...

Copy link
Member

@Future-Outlier Future-Outlier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this log provided by @Tom-Newton can help.

tomnewton@ben-nevis:~/WayveCode/wayve/ai/nvs/services/workflow$ python /home/tomnewton/Documents/reproduce_key_vault_error.py
/usr/lib/python3/dist-packages/paramiko/transport.py:219: CryptographyDeprecationWarning: Blowfish has been deprecated
"class": algorithms.Blowfish,
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────── Traceback (most recent call last) ─────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ /home/tomnewton/Documents/reproduce_key_vault_error.py:6 in <module>                                                                                                                                                                                       │
│                                                                                                                                                                                                                                                            │
│ ❱ 6 remote.client                                                                                                                                                                                                                                          │
│                                                                                                                                                                                                                                                            │
│ /home/tomnewton/.local/lib/python3.8/site-packages/flytekit/remote/remote.py:205 in client                                                                                                                                                                 │
│                                                                                                                                                                                                                                                            │
│ ❱  205 │   │   │   self._client = SynchronousFlyteClient(self.config.platform, **self._kwargs)                                                                                                                                                             │
│                                                                                                                                                                                                                                                            │
│ /home/tomnewton/.local/lib/python3.8/site-packages/flytekit/clients/raw.py:44 in __init__                                                                                                                                                                  │
│                                                                                                                                                                                                                                                            │
│ ❱  44 │   │   self._channel = wrap_exceptions_channel(cfg, upgrade_channel_to_authenticated(cf                                                                                                                                                             │
│                                                                                                                                                                                                                                                            │
│ /home/tomnewton/.local/lib/python3.8/site-packages/flytekit/clients/auth_helper.py:111 in upgrade_channel_to_authenticated                                                                                                                                 │
│                                                                                                                                                                                                                                                            │
│ ❱ 111authenticator = get_authenticator(cfg, RemoteClientConfigStore(in_channel))                                                                                                                                                                      │
│                                                                                                                                                                                                                                                            │
│ /home/tomnewton/.local/lib/python3.8/site-packages/flytekit/clients/auth_helper.py:69 in get_authenticator                                                                                                                                                 │
│                                                                                                                                                                                                                                                            │
│ ❱  69 │   │   return PKCEAuthenticator(cfg.endpoint, cfg_store, verify=verify)                                                                                                                                                                             │
│                                                                                                                                                                                                                                                            │
│ /home/tomnewton/.local/lib/python3.8/site-packages/flytekit/clients/auth/authenticator.py:102 in __init__                                                                                                                                                  │
│                                                                                                                                                                                                                                                            │
│ ❱ 102 │   │   super().__init__(endpoint, header_key, KeyringStore.retrieve(endpoint), verify=v                                                                                                                                                             │
│                                                                                                                                                                                                                                                            │
│ /home/tomnewton/.local/lib/python3.8/site-packages/flytekit/clients/auth/keyring.py:49 in retrieve                                                                                                                                                         │
│                                                                                                                                                                                                                                                            │
│ ❱ 49 │   │   │   refresh_token = _keyring.get_password(for_endpoint, KeyringStore._refresh_to                                                                                                                                                              │
│                                                                                                                                                                                                                                                            │
│ /home/tomnewton/.local/lib/python3.8/site-packages/keyring/core.py:55 in get_password                                                                                                                                                                      │
│                                                                                                                                                                                                                                                            │
│ ❱  55return get_keyring().get_password(service_name, username)                                                                                                                                                                                        │
│                                                                                                                                                                                                                                                            │
│ /home/tomnewton/.local/lib/python3.8/site-packages/keyring/backends/chainer.py:49 in get_password                                                                                                                                                          │
│                                                                                                                                                                                                                                                            │
│ ❱ 49 │   │   │   password = keyring.get_password(service, username)                                                                                                                                                                                        │
│                                                                                                                                                                                                                                                            │
│ /home/tomnewton/.local/lib/python3.8/site-packages/keyring/backends/SecretService.py:78 in get_password                                                                                                                                                    │
│                                                                                                                                                                                                                                                            │
│ ❱  78 │   │   collection = self.get_preferred_collection()                                                                                                                                                                                                 │
│                                                                                                                                                                                                                                                            │
│ /home/tomnewton/.local/lib/python3.8/site-packages/keyring/backends/SecretService.py:67 in get_preferred_collection                                                                                                                                        │
│                                                                                                                                                                                                                                                            │
│ ❱  67 │   │   │   │   raise KeyringLocked("Failed to unlock the collection!")                                                                                                                                                                              │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
KeyringLocked: Failed to unlock the collection!

@Tom-Newton
Copy link
Contributor

I just tested a couple of scenarios where I previously had problems and it seems to be working now. Thanks for working on this 🙌.

Signed-off-by: taieeuu <[email protected]>
@taieeuu taieeuu changed the title Fix for keyring errors when initializing Flyte for_sandbox config client (needs verification) Fix for keyring errors when initializing Flyte for_sandbox config client Dec 5, 2024
Comment on lines 93 to 102
try:
if isinstance(self._authenticator, Authenticator) and not isinstance(
self._authenticator, PKCEAuthenticator
):
logging.info("Current authenticator is 'None', switching to PKCEAuthenticator")

from flytekit.clients.auth.authenticator import PKCEAuthenticator
from flytekit.clients.auth_helper import get_session

session = get_session(self._cfg)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we move import under try: ...?
why this work for Newton?
this shouldn't work.

@Future-Outlier Future-Outlier self-assigned this Dec 9, 2024
Signed-off-by: taieeuu <[email protected]>
Signed-off-by: taieeuu <[email protected]>
Signed-off-by: taieeuu <[email protected]>
Signed-off-by: taieeuu <[email protected]>
Signed-off-by: taieeuu <[email protected]>
Signed-off-by: taieeuu <[email protected]>
Signed-off-by: taieeuu <[email protected]>
Signed-off-by: taieeuu <[email protected]>
Signed-off-by: taieeuu <[email protected]>
Signed-off-by: taieeuu <[email protected]>
Signed-off-by: taieeuu <[email protected]>
Signed-off-by: taieeuu <[email protected]>
@flyte-bot
Copy link
Contributor

flyte-bot commented Dec 28, 2024

Code Review Agent Run #00ff28

Actionable Suggestions - 1
  • flytekit/clients/raw.py - 1
Additional Suggestions - 2
  • dev-requirements.in - 1
    • Consider consolidating gRPC dependency declarations · Line 64-66
  • flytekit/clients/raw.py - 1
    • Consider explicit health check returns · Line 89-92
Review Details
  • Files reviewed - 5 · Commit Range: 21ebec7..569558e
    • dev-requirements.in
    • dev-requirements.txt
    • flytekit/clients/raw.py
    • tests/flytekit/unit/clients/test_friendly.py
    • tests/flytekit/unit/clients/test_raw.py
  • Files skipped - 2
    • .github/workflows/monodocs_build.yml - Reason: Filter setting
    • .github/workflows/pythonbuild.yml - Reason: Filter setting
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

AI Code Review powered by Bito Logo

@flyte-bot
Copy link
Contributor

flyte-bot commented Dec 28, 2024

Changelist by Bito

This pull request implements the following key changes.

Key Change Files Impacted
Feature Improvement - Enhanced gRPC Authentication and Health Checking

auth_helper.py - Refactored authentication logic to use factory pattern for authenticator creation

auth_interceptor.py - Improved auth interceptor with lazy authenticator initialization and credential refresh

raw.py - Added gRPC health checking functionality with authentication support

dev-requirements.in - Added gRPC health checking dependencies

dev-requirements.txt - Updated dependencies with grpcio-health-checking package

Testing - Updated Test Cases for Authentication Changes

test_auth_helper.py - Updated auth helper tests to use new authenticator property

test_friendly.py - Added health check mocking to project-related tests

test_raw.py - Added health check verification tests for raw client

flytekit/clients/raw.py Outdated Show resolved Hide resolved
Signed-off-by: taieeuu <[email protected]>
Signed-off-by: taieeuu <[email protected]>
@flyte-bot
Copy link
Contributor

flyte-bot commented Dec 28, 2024

Code Review Agent Run #10536f

Actionable Suggestions - 0
Review Details
  • Files reviewed - 1 · Commit Range: 569558e..3d142c3
    • flytekit/clients/raw.py
  • Files skipped - 1
    • .github/workflows/pythonbuild.yml - Reason: Filter setting
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

AI Code Review powered by Bito Logo

@Future-Outlier Future-Outlier removed their assignment Jan 13, 2025
@flyte-bot
Copy link
Contributor

flyte-bot commented Jan 25, 2025

Code Review Agent Run #5371e4

Actionable Suggestions - 2
  • flytekit/clients/auth_helper.py - 2
Review Details
  • Files reviewed - 4 · Commit Range: 3d142c3..e207a64
    • flytekit/clients/auth_helper.py
    • flytekit/clients/grpc_utils/auth_interceptor.py
    • flytekit/clients/raw.py
    • tests/flytekit/unit/clients/test_auth_helper.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

AI Code Review powered by Bito Logo

Comment on lines +126 to +127
def authenticator_factory():
return get_proxy_authenticator(cfg)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider caching authenticator instance

Consider caching the authenticator instance instead of creating a new one on each call to authenticator_factory(). This could improve performance by avoiding unnecessary object creation.

Code suggestion
Check the AI-generated fix before applying
Suggested change
def authenticator_factory():
return get_proxy_authenticator(cfg)
_cached_authenticator = None
def authenticator_factory():
nonlocal _cached_authenticator
if _cached_authenticator is None:
_cached_authenticator = get_proxy_authenticator(cfg)
return _cached_authenticator

Code Review Run #5371e4


Is this a valid issue, or was it incorrectly flagged by the Agent?

  • it was incorrectly flagged

Comment on lines +144 to +145
def authenticator_factory():
return get_authenticator(cfg, RemoteClientConfigStore(in_channel))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider caching authenticator instance

Consider caching the authenticator instance instead of creating a new one on every call to authenticator_factory(). This could improve performance since authentication configuration is unlikely to change during runtime.

Code suggestion
Check the AI-generated fix before applying
Suggested change
def authenticator_factory():
return get_authenticator(cfg, RemoteClientConfigStore(in_channel))
authenticator = None
def authenticator_factory():
nonlocal authenticator
if authenticator is None:
authenticator = get_authenticator(cfg, RemoteClientConfigStore(in_channel))
return authenticator

Code Review Run #5371e4


Is this a valid issue, or was it incorrectly flagged by the Agent?

  • it was incorrectly flagged

Signed-off-by: taieeuu <[email protected]>
Signed-off-by: taieeuu <[email protected]>
@flyte-bot
Copy link
Contributor

flyte-bot commented Jan 25, 2025

Code Review Agent Run Status

  • Limitations and other issues: ❌ Failure - Bito Code Review Agent didn't review this pull request automatically because it exceeded the size limit. No action is needed if you didn't intend for the agent to review it. Otherwise, you can initiate the review by typing /review in a comment below.

Copy link
Contributor

@wild-endeavor wild-endeavor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks! could we add one more unit test though please 🙏 ? mock.patch("flytekit.clients.auth.authenticator.KeyringStore") and set retrieve function of the mock to raise an exception, and then call upgrade_channel_to_authenticated and make sure there's no exception. mock whatever else you need too.

@flyte-bot
Copy link
Contributor

flyte-bot commented Feb 4, 2025

Code Review Agent Run Status

  • Limitations and other issues: ❌ Failure - Bito Code Review Agent didn't review this pull request automatically because it exceeded the size limit. No action is needed if you didn't intend for the agent to review it. Otherwise, you can initiate the review by typing /review in a comment below.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: In review
Development

Successfully merging this pull request may close these issues.

5 participants