diff --git a/.changes/28e51fe7-a4d8-4c99-a257-886ad89b7d02.json b/.changes/28e51fe7-a4d8-4c99-a257-886ad89b7d02.json new file mode 100644 index 00000000000..1a68bb39f3e --- /dev/null +++ b/.changes/28e51fe7-a4d8-4c99-a257-886ad89b7d02.json @@ -0,0 +1,8 @@ +{ + "id": "28e51fe7-a4d8-4c99-a257-886ad89b7d02", + "type": "bugfix", + "description": "improve retry policies for ECS and IMDS credentials to handle common network errors", + "issues": [ + "awslabs/aws-sdk-kotlin#1626" + ] +} \ No newline at end of file diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt index 06afaf043c5..40e707968ac 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt @@ -25,9 +25,6 @@ import aws.smithy.kotlin.runtime.io.closeIfCloseable import aws.smithy.kotlin.runtime.net.* import aws.smithy.kotlin.runtime.net.url.Url import aws.smithy.kotlin.runtime.operation.ExecutionContext -import aws.smithy.kotlin.runtime.retries.policy.RetryDirective -import aws.smithy.kotlin.runtime.retries.policy.RetryErrorType -import aws.smithy.kotlin.runtime.retries.policy.RetryPolicy import aws.smithy.kotlin.runtime.serde.json.JsonDeserializer import aws.smithy.kotlin.runtime.telemetry.logging.logger import aws.smithy.kotlin.runtime.time.TimestampFormat @@ -97,8 +94,6 @@ public class EcsCredentialsProvider( execution.endpointResolver = EndpointResolver { Endpoint(url) } } - op.execution.retryPolicy = EcsCredentialsRetryPolicy() - logger.debug { "retrieving container credentials" } val client = SdkHttpClient(httpClient) val creds = try { @@ -260,22 +255,6 @@ private class EcsCredentialsSerializer( } } -internal class EcsCredentialsRetryPolicy : RetryPolicy { - override fun evaluate(result: Result): RetryDirective = when { - result.isSuccess -> RetryDirective.TerminateAndSucceed - else -> evaluate(result.exceptionOrNull()!!) - } - - private fun evaluate(throwable: Throwable): RetryDirective = when (throwable) { - is CredentialsProviderException -> when { - throwable.sdkErrorMetadata.isThrottling -> RetryDirective.RetryError(RetryErrorType.Throttling) - throwable.sdkErrorMetadata.isRetryable -> RetryDirective.RetryError(RetryErrorType.ServerSide) - else -> RetryDirective.TerminateAndFail - } - else -> RetryDirective.TerminateAndFail - } -} - private val ecsV4Addr = IpV4Addr(169u, 254u, 170u, 2u) private val eksV4Addr = IpV4Addr(169u, 254u, 170u, 23u) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/config/imds/ImdsRetryPolicy.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/config/imds/ImdsRetryPolicy.kt index aaca35a59c9..8f8dec92b9e 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/config/imds/ImdsRetryPolicy.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/config/imds/ImdsRetryPolicy.kt @@ -9,32 +9,25 @@ import aws.smithy.kotlin.runtime.http.HttpStatusCode import aws.smithy.kotlin.runtime.http.category import aws.smithy.kotlin.runtime.retries.policy.RetryDirective import aws.smithy.kotlin.runtime.retries.policy.RetryErrorType -import aws.smithy.kotlin.runtime.retries.policy.RetryPolicy +import aws.smithy.kotlin.runtime.retries.policy.StandardRetryPolicy import aws.smithy.kotlin.runtime.telemetry.logging.logger import kotlin.coroutines.CoroutineContext -internal class ImdsRetryPolicy( - private val callContext: CoroutineContext, -) : RetryPolicy { - override fun evaluate(result: Result): RetryDirective = when { - result.isSuccess -> RetryDirective.TerminateAndSucceed - else -> evaluate(result.exceptionOrNull()!!) - } - - private fun evaluate(throwable: Throwable): RetryDirective = when (throwable) { +internal class ImdsRetryPolicy(private val callContext: CoroutineContext) : StandardRetryPolicy() { + override fun evaluateSpecificExceptions(ex: Throwable) = when (ex) { is EC2MetadataError -> { - val status = throwable.status + val status = ex.status when { status.category() == HttpStatusCode.Category.SERVER_ERROR -> RetryDirective.RetryError(RetryErrorType.ServerSide) // 401 indicates the token has expired, this is retryable status == HttpStatusCode.Unauthorized -> RetryDirective.RetryError(RetryErrorType.ServerSide) else -> { val logger = callContext.logger() - logger.debug { "Non retryable IMDS error: statusCode=$status; ${throwable.message}" } - RetryDirective.TerminateAndFail + logger.debug { "IMDS error: statusCode=$status; ${ex.message}" } + null } } } - else -> RetryDirective.TerminateAndFail + else -> null } }