Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
AWSIdentityConfig,
AWSIdentityProperties,
)
from .container import ContainerCredentialResolver
from .container import ContainerCredentialsResolver
from .environment import EnvironmentCredentialsResolver
from .imds import IMDSCredentialsResolver
from .static import StaticCredentialsResolver
Expand All @@ -17,7 +17,7 @@
"AWSCredentialsIdentity",
"AWSCredentialsResolver",
"AWSIdentityProperties",
"ContainerCredentialResolver",
"ContainerCredentialsResolver",
"EnvironmentCredentialsResolver",
"IMDSCredentialsResolver",
"StaticCredentialsResolver",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@


@dataclass
class ContainerCredentialConfig:
class ContainerCredentialsConfig:
"""Configuration for container credential retrieval operations."""

timeout: int = _DEFAULT_TIMEOUT
Expand All @@ -43,7 +43,7 @@ class ContainerCredentialConfig:
class ContainerMetadataClient:
"""Client for remote credential retrieval in Container environments like ECS/EKS."""

def __init__(self, http_client: HTTPClient, config: ContainerCredentialConfig):
def __init__(self, http_client: HTTPClient, config: ContainerCredentialsConfig):
self._http_client = http_client
self._config = config

Expand Down Expand Up @@ -103,7 +103,7 @@ def _is_allowed_container_metadata_host(self, hostname: str) -> bool:
return hostname in _CONTAINER_METADATA_ALLOWED_HOSTS


class ContainerCredentialResolver(
class ContainerCredentialsResolver(
IdentityResolver[AWSCredentialsIdentity, AWSIdentityProperties]
):
"""Resolves AWS Credentials from container credential sources."""
Expand All @@ -116,10 +116,10 @@ class ContainerCredentialResolver(
def __init__(
self,
http_client: HTTPClient,
config: ContainerCredentialConfig | None = None,
config: ContainerCredentialsConfig | None = None,
):
self._http_client = http_client
self._config = config or ContainerCredentialConfig()
self._config = config or ContainerCredentialsConfig()
self._client = ContainerMetadataClient(http_client, self._config)
self._credentials = None

Expand Down
68 changes: 36 additions & 32 deletions packages/smithy-aws-core/tests/unit/identity/test_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import pytest
from smithy_aws_core.identity import AWSCredentialsIdentity
from smithy_aws_core.identity.container import (
ContainerCredentialConfig,
ContainerCredentialResolver,
ContainerCredentialsConfig,
ContainerCredentialsResolver,
ContainerMetadataClient,
)
from smithy_core import URI
Expand All @@ -32,7 +32,7 @@


def test_config_custom_values():
config = ContainerCredentialConfig(timeout=10, retries=5)
config = ContainerCredentialsConfig(timeout=10, retries=5)
assert config.timeout == 10
assert config.retries == 5

Expand Down Expand Up @@ -62,7 +62,7 @@ def _assert_expected_credentials(
async def test_metadata_client_valid_host(host: str):
resp_body = json.dumps(DEFAULT_RESPONSE_DATA)
http_client = mock_http_client_response(200, resp_body.encode("utf-8"))
config = ContainerCredentialConfig()
config = ContainerCredentialsConfig()
client = ContainerMetadataClient(http_client, config)

# Valid Host
Expand All @@ -76,7 +76,7 @@ async def test_metadata_client_valid_host(host: str):
async def test_metadata_client_https_host():
resp_body = json.dumps(DEFAULT_RESPONSE_DATA)
http_client = mock_http_client_response(200, resp_body.encode("utf-8"))
config = ContainerCredentialConfig()
config = ContainerCredentialsConfig()
client = ContainerMetadataClient(http_client, config)

# Valid HTTPS Host
Expand All @@ -90,7 +90,7 @@ async def test_metadata_client_https_host():
async def test_metadata_client_invalid_host():
resp_body = json.dumps(DEFAULT_RESPONSE_DATA)
http_client = mock_http_client_response(200, resp_body.encode("utf-8"))
config = ContainerCredentialConfig(retries=0)
config = ContainerCredentialsConfig(retries=0)
client = ContainerMetadataClient(http_client, config)

# Invalid Host
Expand All @@ -103,7 +103,7 @@ async def test_metadata_client_invalid_host():
@pytest.mark.asyncio
async def test_metadata_client_non_200_response():
http_client = mock_http_client_response(404, b"not found")
config = ContainerCredentialConfig(retries=1)
config = ContainerCredentialsConfig(retries=1)
client = ContainerMetadataClient(http_client, config)

uri = URI(scheme="http", host="169.254.170.2")
Expand All @@ -120,7 +120,7 @@ async def test_metadata_client_invalid_json():
http_client = mock_http_client_response(
200, b"<!DOCTYPE html><head><title>proxy</title>"
)
config = ContainerCredentialConfig(retries=1)
config = ContainerCredentialsConfig(retries=1)
client = ContainerMetadataClient(http_client, config)

uri = URI(scheme="http", host="169.254.170.2")
Expand All @@ -137,7 +137,7 @@ def _assert_expected_identity(identity: AWSCredentialsIdentity) -> None:
@pytest.mark.asyncio
async def test_metadata_client_retries():
http_client = AsyncMock()
config = ContainerCredentialConfig(retries=2)
config = ContainerCredentialsConfig(retries=2)
client = ContainerMetadataClient(http_client, config)
uri = URI(scheme="http", host="169.254.170.2", path="/task")
http_client.send.side_effect = Exception()
Expand All @@ -153,9 +153,9 @@ async def test_resolver_env_relative():
http_client = mock_http_client_response(200, resp_body.encode("utf-8"))

with patch.dict(
os.environ, {ContainerCredentialResolver.ENV_VAR: "/test"}, clear=True
os.environ, {ContainerCredentialsResolver.ENV_VAR: "/test"}, clear=True
):
resolver = ContainerCredentialResolver(http_client)
resolver = ContainerCredentialsResolver(http_client)
identity = await resolver.get_identity(properties={})

# Ensure we derive the correct destination
Expand All @@ -177,10 +177,10 @@ async def test_resolver_env_full():

with patch.dict(
os.environ,
{ContainerCredentialResolver.ENV_VAR_FULL: "http://169.254.170.23/full"},
{ContainerCredentialsResolver.ENV_VAR_FULL: "http://169.254.170.23/full"},
clear=True,
):
resolver = ContainerCredentialResolver(http_client)
resolver = ContainerCredentialsResolver(http_client)
identity = await resolver.get_identity(properties={})

# Ensure we derive the correct destination
Expand All @@ -203,12 +203,12 @@ async def test_resolver_env_token():
with patch.dict(
os.environ,
{
ContainerCredentialResolver.ENV_VAR_FULL: "http://169.254.170.23/full",
ContainerCredentialResolver.ENV_VAR_AUTH_TOKEN: "Bearer foobar",
ContainerCredentialsResolver.ENV_VAR_FULL: "http://169.254.170.23/full",
ContainerCredentialsResolver.ENV_VAR_AUTH_TOKEN: "Bearer foobar",
},
clear=True,
):
resolver = ContainerCredentialResolver(http_client)
resolver = ContainerCredentialsResolver(http_client)
identity = await resolver.get_identity(properties={})

# Ensure we derive the correct destination and fields
Expand Down Expand Up @@ -238,12 +238,12 @@ async def test_resolver_env_token_file(tmp_path: pathlib.Path):
with patch.dict(
os.environ,
{
ContainerCredentialResolver.ENV_VAR_FULL: "http://169.254.170.23/full",
ContainerCredentialResolver.ENV_VAR_AUTH_TOKEN_FILE: str(token_file),
ContainerCredentialsResolver.ENV_VAR_FULL: "http://169.254.170.23/full",
ContainerCredentialsResolver.ENV_VAR_AUTH_TOKEN_FILE: str(token_file),
},
clear=True,
):
resolver = ContainerCredentialResolver(http_client)
resolver = ContainerCredentialsResolver(http_client)
identity = await resolver.get_identity(properties={})

# Ensure we derive the correct destination and fields
Expand Down Expand Up @@ -273,12 +273,12 @@ async def test_resolver_env_token_file_invalid_bytes(tmp_path: pathlib.Path):
with patch.dict(
os.environ,
{
ContainerCredentialResolver.ENV_VAR_FULL: "http://169.254.170.23/full",
ContainerCredentialResolver.ENV_VAR_AUTH_TOKEN_FILE: str(token_file),
ContainerCredentialsResolver.ENV_VAR_FULL: "http://169.254.170.23/full",
ContainerCredentialsResolver.ENV_VAR_AUTH_TOKEN_FILE: str(token_file),
},
clear=True,
):
resolver = ContainerCredentialResolver(http_client)
resolver = ContainerCredentialsResolver(http_client)
with pytest.raises(SmithyIdentityError) as e:
await resolver.get_identity(properties={})
assert "Unable to read valid utf-8 bytes from " in str(e.value)
Expand All @@ -296,13 +296,13 @@ async def test_resolver_env_token_file_precedence(tmp_path: pathlib.Path):
with patch.dict(
os.environ,
{
ContainerCredentialResolver.ENV_VAR_FULL: "http://169.254.170.23/full",
ContainerCredentialResolver.ENV_VAR_AUTH_TOKEN_FILE: str(token_file),
ContainerCredentialResolver.ENV_VAR_AUTH_TOKEN: "Bearer foobar",
ContainerCredentialsResolver.ENV_VAR_FULL: "http://169.254.170.23/full",
ContainerCredentialsResolver.ENV_VAR_AUTH_TOKEN_FILE: str(token_file),
ContainerCredentialsResolver.ENV_VAR_AUTH_TOKEN: "Bearer foobar",
},
clear=True,
):
resolver = ContainerCredentialResolver(http_client)
resolver = ContainerCredentialsResolver(http_client)
identity = await resolver.get_identity(properties={})

# Ensure we derive the correct destination and fields
Expand Down Expand Up @@ -331,9 +331,9 @@ async def test_resolver_valid_credentials_reused():
http_client = mock_http_client_response(200, resp_body.encode("utf-8"))

with patch.dict(
os.environ, {ContainerCredentialResolver.ENV_VAR: "/test"}, clear=True
os.environ, {ContainerCredentialsResolver.ENV_VAR: "/test"}, clear=True
):
resolver = ContainerCredentialResolver(http_client)
resolver = ContainerCredentialsResolver(http_client)
identity_one = await resolver.get_identity(properties={})
identity_two = await resolver.get_identity(properties={})

Expand All @@ -352,9 +352,9 @@ async def test_resolver_expired_credentials_refreshed():
http_client = mock_http_client_response(200, resp_body.encode("utf-8"))

with patch.dict(
os.environ, {ContainerCredentialResolver.ENV_VAR: "/test"}, clear=True
os.environ, {ContainerCredentialsResolver.ENV_VAR: "/test"}, clear=True
):
resolver = ContainerCredentialResolver(http_client)
resolver = ContainerCredentialsResolver(http_client)
identity_one = await resolver.get_identity(properties={})
identity_two = await resolver.get_identity(properties={})

Expand All @@ -372,7 +372,11 @@ async def test_resolver_missing_env():
resp_body = json.dumps(DEFAULT_RESPONSE_DATA)
http_client = mock_http_client_response(200, resp_body.encode("utf-8"))

with patch.dict(os.environ, {}, clear=True):
resolver = ContainerCredentialResolver(http_client)
with patch.dict(
os.environ,
{},
clear=True,
):
resolver = ContainerCredentialsResolver(http_client)
with pytest.raises(SmithyIdentityError):
await resolver.get_identity(properties={})
Loading