Skip to content

Commit 036c8e2

Browse files
author
Menno van der Sman
committed
Add a configurable number of retries to the EC2 credential provider
In cases where the EC2 provider is configured as the only valid provider of credentials, intermittent failures are to be expected and can be retried safely in order to avoid a credential error.
1 parent d1d76b3 commit 036c8e2

File tree

2 files changed

+48
-6
lines changed

2 files changed

+48
-6
lines changed

lib/aws/core/credential_providers.rb

+18-2
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,8 @@ class FailedRequestError < StandardError; end
354354
# @param [Hash] options
355355
# @option options [String] :ip_address ('169.254.169.254')
356356
# @option options [Integer] :port (80)
357+
# @option options [Integer] :retries (0) Number of times to
358+
# retry retrieving credentials.
357359
# @option options [Float] :http_open_timeout (1)
358360
# @option options [Float] :http_read_timeout (1)
359361
# @option options [Object] :http_debug_output (nil) HTTP wire
@@ -362,6 +364,7 @@ class FailedRequestError < StandardError; end
362364
def initialize options = {}
363365
@ip_address = options[:ip_address] || '169.254.169.254'
364366
@port = options[:port] || 80
367+
@retries = options[:retries] || 0
365368
@http_open_timeout = options[:http_open_timeout] || 1
366369
@http_read_timeout = options[:http_read_timeout] || 1
367370
@http_debug_output = options[:http_debug_output]
@@ -373,6 +376,9 @@ def initialize options = {}
373376
# @return [Integer] Defaults to port 80.
374377
attr_accessor :port
375378

379+
# @return [Integer] Defaults to 0
380+
attr_accessor :retries
381+
376382
# @return [Float]
377383
attr_accessor :http_open_timeout
378384

@@ -404,6 +410,8 @@ def credentials
404410

405411
# (see Provider#get_credentials)
406412
def get_credentials
413+
retries_left = retries
414+
407415
begin
408416

409417
http = Net::HTTP.new(ip_address, port, nil)
@@ -432,7 +440,15 @@ def get_credentials
432440
credentials
433441

434442
rescue *FAILURES => e
435-
{}
443+
if retries_left > 0
444+
sleep_time = 2 ** (retries - retries_left)
445+
Kernel.sleep(sleep_time)
446+
447+
retries_left -= 1
448+
retry
449+
else
450+
{}
451+
end
436452
end
437453
end
438454

@@ -566,7 +582,7 @@ class AssumeRoleProvider
566582

567583
include Provider
568584

569-
# @option options [AWS::STS] :sts (STS.new) An instance of {AWS::STS}.
585+
# @option options [AWS::STS] :sts (STS.new) An instance of {AWS::STS}.
570586
# This is used to make the API call to assume role.
571587
# @option options [required, String] :role_arn
572588
# @option options [required, String] :role_session_name

spec/aws/core/credential_providers_spec.rb

+30-4
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ module CredentialProviders
473473

474474
context 'timeouts' do
475475

476-
it 'has a short default timeouts' do
476+
it 'has short default timeouts' do
477477
provider.http_open_timeout.should == 1
478478
provider.http_read_timeout.should == 1
479479
end
@@ -487,7 +487,7 @@ module CredentialProviders
487487
provider.http_read_timeout.should == 1.5
488488
end
489489

490-
it 'uses the timeouts when request credentials' do
490+
it 'uses the timeouts when requesting credentials' do
491491

492492
profiles = "profile-name\n"
493493
creds = <<-CREDS.strip
@@ -602,18 +602,44 @@ module CredentialProviders
602602
provider.credentials.should == creds2
603603
end
604604

605-
it 'is set when credentails is valid' do
605+
it 'is set when credentials are valid' do
606606
provider.set?.should be_true
607607
end
608608

609-
it 'is not set when key_id or access_key is missing' do
609+
it 'is not set when the request fails' do
610610
http = double('http').as_null_object
611611
http.should_receive(:start).and_raise(Errno::ECONNREFUSED)
612612
Net::HTTP.stub(:new).and_return(http)
613613

614614
provider.set?.should be_false
615615
end
616616

617+
context 'retries' do
618+
619+
it 'does not retry by default' do
620+
provider.retries.should == 0
621+
end
622+
623+
it 'accepts a value for the number of retries' do
624+
provider = EC2Provider.new({:retries => 2})
625+
provider.retries.should == 2
626+
end
627+
628+
it 'retries the request for the set amount of times' do
629+
http = double('http').as_null_object
630+
http.should_receive(:start).exactly(3).times.and_raise(Errno::ECONNREFUSED)
631+
Net::HTTP.stub(:new).and_return(http)
632+
633+
Kernel.should_receive(:sleep).with(1)
634+
Kernel.should_receive(:sleep).with(2)
635+
636+
provider = EC2Provider.new({:retries => 2})
637+
provider.set?.should be_false
638+
provider.retries.should == 2
639+
end
640+
641+
end
642+
617643
end
618644

619645
describe SessionProvider do

0 commit comments

Comments
 (0)