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

Make IMDS unavailability easier to debug #19423

Merged
merged 1 commit into from
Jun 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions sdk/identity/azure-identity/azure/identity/_credentials/imds.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------
import logging
import os
from typing import TYPE_CHECKING

Expand All @@ -21,8 +20,6 @@
from typing import Any, Optional
from azure.core.credentials import AccessToken

_LOGGER = logging.getLogger(__name__)

IMDS_URL = "http://169.254.169.254/metadata/identity/oauth2/token"

PIPELINE_SETTINGS = {
Expand Down Expand Up @@ -51,6 +48,7 @@ def __init__(self, **kwargs):
self._endpoint_available = True # type: Optional[bool]
else:
self._endpoint_available = None
self._error_message = None # type: Optional[str]
self._user_assigned_identity = "client_id" in kwargs or "identity_config" in kwargs

def _acquire_token_silently(self, *scopes):
Expand All @@ -67,16 +65,18 @@ def _request_token(self, *scopes, **kwargs): # pylint:disable=unused-argument
self._client.request_token(*scopes, connection_timeout=0.3, retry_total=0)
self._endpoint_available = True
except HttpResponseError:
# received a response, choked on it
# IMDS responded
self._endpoint_available = True
except Exception: # pylint:disable=broad-except
except Exception as ex: # pylint:disable=broad-except
# if anything else was raised, assume the endpoint is unavailable
self._endpoint_available = False
_LOGGER.info("No response from the IMDS endpoint.")
self._error_message = (
"ManagedIdentityCredential authentication unavailable, no response from the IMDS endpoint."
)
six.raise_from(CredentialUnavailableError(self._error_message), ex)

if not self._endpoint_available:
message = "ManagedIdentityCredential authentication unavailable, no managed identity endpoint found."
raise CredentialUnavailableError(message=message)
raise CredentialUnavailableError(self._error_message)

try:
token = self._client.request_token(*scopes, headers={"Metadata": "true"})
Expand All @@ -85,13 +85,13 @@ def _request_token(self, *scopes, **kwargs): # pylint:disable=unused-argument
# or the identity with the specified client_id is not available
if ex.status_code == 400:
self._endpoint_available = False
message = "ManagedIdentityCredential authentication unavailable. "
self._error_message = "ManagedIdentityCredential authentication unavailable. "
if self._user_assigned_identity:
message += "The requested identity has not been assigned to this resource."
self._error_message += "The requested identity has not been assigned to this resource."
else:
message += "No identity has been assigned to this resource."
six.raise_from(CredentialUnavailableError(message=message), ex)
self._error_message += "No identity has been assigned to this resource."
six.raise_from(CredentialUnavailableError(message=self._error_message), ex)

# any other error is unexpected
six.raise_from(ClientAuthenticationError(message=ex.message, response=ex.response), None)
six.raise_from(ClientAuthenticationError(message=ex.message, response=ex.response), ex)
return token
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------
import logging
import os
from typing import TYPE_CHECKING

Expand All @@ -19,8 +18,6 @@
from typing import Any, Optional
from azure.core.credentials import AccessToken

_LOGGER = logging.getLogger(__name__)


class ImdsCredential(AsyncContextManager, GetTokenMixin):
def __init__(self, **kwargs: "Any") -> None:
Expand All @@ -31,6 +28,7 @@ def __init__(self, **kwargs: "Any") -> None:
self._endpoint_available = True # type: Optional[bool]
else:
self._endpoint_available = None
self._error_message = None # type: Optional[str]
self._user_assigned_identity = "client_id" in kwargs or "identity_config" in kwargs

async def close(self) -> None:
Expand All @@ -48,16 +46,18 @@ async def _request_token(self, *scopes, **kwargs: "Any") -> "AccessToken": # py
await self._client.request_token(*scopes, connection_timeout=0.3, retry_total=0)
self._endpoint_available = True
except HttpResponseError:
# received a response, choked on it
# IMDS responded
self._endpoint_available = True
except Exception: # pylint:disable=broad-except
except Exception as ex: # pylint:disable=broad-except
# if anything else was raised, assume the endpoint is unavailable
self._endpoint_available = False
_LOGGER.info("No response from the IMDS endpoint.")
self._error_message = (
"ManagedIdentityCredential authentication unavailable, no response from the IMDS endpoint."
)
raise CredentialUnavailableError(message=self._error_message) from ex

if not self._endpoint_available:
message = "ManagedIdentityCredential authentication unavailable, no managed identity endpoint found."
raise CredentialUnavailableError(message=message)
raise CredentialUnavailableError(message=self._error_message)

try:
token = await self._client.request_token(*scopes, headers={"Metadata": "true"})
Expand All @@ -66,13 +66,13 @@ async def _request_token(self, *scopes, **kwargs: "Any") -> "AccessToken": # py
# or the identity with the specified client_id is not available
if ex.status_code == 400:
self._endpoint_available = False
message = "ManagedIdentityCredential authentication unavailable. "
self._error_message = "ManagedIdentityCredential authentication unavailable. "
if self._user_assigned_identity:
message += "The requested identity has not been assigned to this resource."
self._error_message += "The requested identity has not been assigned to this resource."
else:
message += "No identity has been assigned to this resource."
raise CredentialUnavailableError(message=message) from ex
self._error_message += "No identity has been assigned to this resource."
raise CredentialUnavailableError(message=self._error_message) from ex

# any other error is unexpected
raise ClientAuthenticationError(message=ex.message, response=ex.response) from None
raise ClientAuthenticationError(message=ex.message, response=ex.response) from ex
return token