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(2457): add retry logic to EcsCredentialProvider #2477

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ebb1c75
Add retry logic to EcsCredentialProvider
annuh Jun 23, 2022
d29b3cb
Format code
annuh Jun 23, 2022
d25ddaa
add changelog file
annuh Jun 23, 2022
ccc12f1
Fix return value
annuh Jun 25, 2022
d98c993
improve exception message
annuh Jun 30, 2022
59c8faa
Merge branch 'aws:master' into issue-2457-support-retries-ecs-credent…
annuh Jul 15, 2022
68b01f1
Add tests
annuh Jul 15, 2022
1084872
Fix PHP5.x syntax error
annuh Jul 18, 2022
c615b22
Merge branch 'aws:master' into issue-2457-support-retries-ecs-credent…
annuh Aug 9, 2022
3b8c5cd
Merge branch 'master' into issue-2457-support-retries-ecs-credentials…
annuh Sep 23, 2022
01ca5b7
Merge branch 'aws:master' into issue-2457-support-retries-ecs-credent…
annuh Dec 1, 2022
f373ac2
Merge branch 'aws:master' into issue-2457-support-retries-ecs-credent…
annuh Dec 23, 2022
f322607
Merge branch 'aws:master' into issue-2457-support-retries-ecs-credent…
annuh Jan 3, 2023
a91dd05
Merge branch 'aws:master' into issue-2457-support-retries-ecs-credent…
annuh Jan 11, 2023
d90dfaa
Merge branch 'aws:master' into issue-2457-support-retries-ecs-credent…
annuh Aug 8, 2023
bcce958
apply PR feedback
annuh Dec 5, 2023
97af9f8
apply PR feedback
annuh Dec 5, 2023
312925c
fix tests
annuh Dec 5, 2023
e11fa3f
revert unwanted changes
annuh Dec 14, 2023
bc40f5e
feat(EscCredentialProvider): Resetting attempts to `0` when the `__in…
wotta Apr 23, 2024
6ccd079
feat(EscCredentialProvider): Adding missing tests to verify our changes.
wotta Apr 23, 2024
bb6c101
feat(EscCredentialProvider): Adding additional tests to validate new …
wotta May 13, 2024
1913d81
Merge pull request #1 from wotta/fixes-for-issue-2457-support-retries…
annuh May 13, 2024
faf7294
feat(issue-2457): Resolve comments.
wotta May 21, 2024
34e3097
feat(issue-2457): Resolve comments.
wotta May 21, 2024
82eb516
Merge pull request #2 from wotta/issue-2457-support-retries-ecs-crede…
annuh May 22, 2024
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
@@ -0,0 +1,7 @@
[
{
"type": "feature",
"category": "EcsCredentialsProvider",
"description": "Add support for retries in the ECS credentials provider"
}
]
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ your [issues][] or [pull requests][pull-requests] through GitHub.

Jump To:

* [Bug Reports](_#Bug-Reports_)
* [Feature Requests](_#Feature-Requests_)
* [Code Contributions](_#Code-Contributions_)
* [Bug Reports](#Bug-Reports)
* [Feature Requests](#Feature-Requests)
* [Code Contributions](#Code-Contributions)
annuh marked this conversation as resolved.
Show resolved Hide resolved

## How to contribute

Expand Down
107 changes: 74 additions & 33 deletions src/Credentials/EcsCredentialProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
namespace Aws\Credentials;

use Aws\Exception\CredentialsException;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Promise;
use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\ResponseInterface;

Expand All @@ -21,29 +23,40 @@ class EcsCredentialProvider
const ENV_TIMEOUT = 'AWS_METADATA_SERVICE_TIMEOUT';
const EKS_SERVER_HOST_IPV4 = '169.254.170.23';
const EKS_SERVER_HOST_IPV6 = 'fd00:ec2::23';
const ENV_RETRIES = 'AWS_METADATA_SERVICE_NUM_ATTEMPTS';

const DEFAULT_ENV_TIMEOUT = 1.0;
const DEFAULT_ENV_RETRIES = 3;

/** @var callable */
private $client;

/** @var float|mixed */
private $timeout;

/** @var int */
private $retries;

/** @var int */
private $attempts;

/**
* The constructor accepts following options:
* - timeout: (optional) Connection timeout, in seconds, default 1.0
* - retries: Optional number of retries to be attempted, default 3.
* - client: An EcsClient to make request from
*
* @param array $config Configuration options
*/
public function __construct(array $config = [])
{
$timeout = getenv(self::ENV_TIMEOUT);
$this->timeout = (float) isset($config['timeout'])
? $config['timeout']
: (getenv(self::ENV_TIMEOUT) ?: self::DEFAULT_ENV_TIMEOUT);
$this->retries = (int) isset($config['retries'])
? $config['retries']
: ((int) getenv(self::ENV_RETRIES) ?: self::DEFAULT_ENV_RETRIES);

if (!$timeout) {
$timeout = $_SERVER[self::ENV_TIMEOUT] ?? ($config['timeout'] ?? 1.0);
}

$this->timeout = (float) $timeout;
$this->client = $config['client'] ?? \Aws\default_http_handler();
}

Expand All @@ -55,40 +68,68 @@ public function __construct(array $config = [])
*/
public function __invoke()
{
$client = $this->client;
stobrien89 marked this conversation as resolved.
Show resolved Hide resolved
$uri = self::getEcsUri();
$this->attempts = 0;

$uri = $this->getEcsUri();

if ($this->isCompatibleUri($uri)) {
$request = new Request('GET', $uri);

$headers = $this->getHeadersForAuthToken();
return $client(
$request,
[
'timeout' => $this->timeout,
'proxy' => '',
'headers' => $headers
]
)->then(function (ResponseInterface $response) {
$result = $this->decodeResult((string) $response->getBody());
return new Credentials(
$result['AccessKeyId'],
$result['SecretAccessKey'],
$result['Token'],
strtotime($result['Expiration'])
);
})->otherwise(function ($reason) {
$reason = is_array($reason) ? $reason['exception'] : $reason;
$msg = $reason->getMessage();
throw new CredentialsException(
"Error retrieving credentials from container metadata ($msg)"
);
return Promise\Coroutine::of(function () {
$client = $this->client;
$request = new Request('GET', $this->getEcsUri());

$headers = $this->getHeadersForAuthToken();

$credentials = null;

while ($credentials === null) {
$credentials = (yield $client(
$request,
[
'timeout' => $this->timeout,
'proxy' => '',
'headers' => $headers,
]
)->then(function (ResponseInterface $response) {
$result = $this->decodeResult((string)$response->getBody());
return new Credentials(
$result['AccessKeyId'],
$result['SecretAccessKey'],
$result['Token'],
strtotime($result['Expiration'])
);
})->otherwise(function ($reason) {
$reason = is_array($reason) ? $reason['exception'] : $reason;

$isRetryable = $reason instanceof ConnectException;
if ($isRetryable && ($this->attempts < $this->retries)) {
sleep((int)pow(1.2, $this->attempts));
} else {
$msg = $reason->getMessage();
throw new CredentialsException(
sprintf('Error retrieving credentials from container metadata after attempt %d/%d (%s)', $this->attempts, $this->retries, $msg)
);
}
}));
$this->attempts++;
}

yield $credentials;
});
}

throw new CredentialsException("Uri '{$uri}' contains an unsupported host.");
}

/**
* Returns the number of attempts that have been done.
*
* @return int
*/
public function getAttempts(): int
{
return $this->attempts;
}

/**
* Retrieves authorization token.
*
Expand Down Expand Up @@ -158,7 +199,7 @@ private function getEcsUri()
if (!empty($credFullUri))
return $credFullUri;
}

return self::SERVER_URI . $credsUri;
}

Expand Down
Loading