Skip to content

Commit 9e065cb

Browse files
authored
Handle rate limited UAA (#4171)
1 parent c26e925 commit 9e065cb

File tree

8 files changed

+89
-0
lines changed

8 files changed

+89
-0
lines changed

app/controllers/v3/roles_controller.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ def create
5858
render status: :created, json: Presenters::V3::RolePresenter.new(role)
5959
rescue RoleCreate::Error => e
6060
unprocessable!(e)
61+
rescue UaaRateLimited
62+
headers['Retry-After'] = rand(5..20).to_s
63+
raise CloudController::Errors::V3::ApiError.new_from_details('UaaRateLimited')
6164
rescue UaaUnavailable
6265
raise CloudController::Errors::ApiError.new_from_details('UaaUnavailable')
6366
end

app/controllers/v3/users_controller.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ def create
5454
else
5555
render status: :created, json: Presenters::V3::UserPresenter.new(user, uaa_users: User.uaa_users_info([user.guid]))
5656
end
57+
rescue UaaRateLimited
58+
headers['Retry-After'] = rand(5..20).to_s
59+
raise CloudController::Errors::V3::ApiError.new_from_details('UaaRateLimited')
5760
rescue VCAP::CloudController::UaaUnavailable
5861
raise CloudController::Errors::ApiError.new_from_details('UaaUnavailable')
5962
rescue UserCreate::Error => e

errors/v3.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,8 @@
1212
name: ServiceBrokerGone
1313
http_code: 422
1414
message: "The service broker was removed before the synchronization completed"
15+
16+
20008:
17+
name: UaaRateLimited
18+
http_code: 429
19+
message: "The UAA is currently rate limited. Please try again later"

lib/cloud_controller/uaa/errors.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,10 @@ def message
3030
'The UAA returned an unexpected error'
3131
end
3232
end
33+
34+
class UaaRateLimited < UaaError
35+
def message
36+
'The UAA is currently rate limited. Please try again later'
37+
end
38+
end
3339
end

lib/cloud_controller/uaa/uaa_client.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ def create_shadow_user(username, origin)
102102
raise e unless e.info['error'] == 'scim_resource_already_exists'
103103

104104
{ 'id' => e.info['user_id'] }
105+
rescue CF::UAA::BadResponse => e
106+
unless e.message == 'invalid status response: 429'
107+
logger.error("UAA request for creating a user failed: #{e.inspect}")
108+
raise UaaUnavailable
109+
end
110+
111+
logger.warn("UAA request for creating a user ran into rate limits: #{e.inspect}")
112+
raise UaaRateLimited
105113
rescue CF::UAA::UAAError => e
106114
logger.error("UAA request for creating a user failed: #{e.inspect}")
107115
raise UaaUnavailable

spec/request/roles_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,22 @@
10441044
expect(parsed_response).to match_json_response(expected_response)
10451045
expect(uaa_client).to have_received(:create_shadow_user)
10461046
end
1047+
1048+
context 'when UAA is rate limited' do
1049+
before do
1050+
allow(uaa_client).to receive(:create_shadow_user).and_raise(VCAP::CloudController::UaaRateLimited)
1051+
end
1052+
1053+
it 'raises a 429 with a helpful message and Retry-After header' do
1054+
post '/v3/roles', params.to_json, admin_header
1055+
1056+
expect(last_response).to have_status_code(429)
1057+
expect(last_response).to have_error_message(
1058+
'The UAA is currently rate limited. Please try again later'
1059+
)
1060+
expect(last_response.headers).to include('Retry-After')
1061+
end
1062+
end
10471063
end
10481064
end
10491065
end

spec/request/users_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,22 @@
968968
expect(uaa_client).not_to have_received(:users_for_ids)
969969
end
970970

971+
context 'when UAA is rate limited' do
972+
before do
973+
allow(uaa_client).to receive(:create_shadow_user).and_raise(VCAP::CloudController::UaaRateLimited)
974+
end
975+
976+
it 'raises a 429 with a helpful message and Retry-After header' do
977+
post '/v3/users', params.to_json, admin_header
978+
979+
expect(last_response).to have_status_code(429)
980+
expect(last_response).to have_error_message(
981+
'The UAA is currently rate limited. Please try again later'
982+
)
983+
expect(last_response.headers).to include('Retry-After')
984+
end
985+
end
986+
971987
context 'when user already exists in UAA' do
972988
before do
973989
allow(uaa_client).to receive(:ids_for_usernames_and_origins).and_return([user_guid])

spec/unit/lib/uaa/uaa_client_spec.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,38 @@ module VCAP::CloudController
522522
expect(mock_logger).to have_received(:error).with("UAA request for creating a user failed: #{uaa_error.inspect}")
523523
end
524524
end
525+
526+
context 'when uaa is rate limited' do
527+
let(:uaa_error) { CF::UAA::BadResponse.new('invalid status response: 429') }
528+
let(:mock_logger) { double(:steno_logger, warn: nil) }
529+
530+
before do
531+
scim = instance_double(CF::UAA::Scim)
532+
allow(scim).to receive(:add).and_raise(uaa_error)
533+
allow(uaa_client).to receive_messages(scim: scim, logger: mock_logger)
534+
end
535+
536+
it 'logs the error and raises UaaRateLimited' do
537+
expect { uaa_client.create_shadow_user('[email protected]', 'idp.local') }.to raise_error(UaaRateLimited)
538+
expect(mock_logger).to have_received(:warn).with("UAA request for creating a user ran into rate limits: #{uaa_error.inspect}")
539+
end
540+
end
541+
542+
context 'when a BadResponse is raised' do
543+
let(:uaa_error) { CF::UAA::BadResponse.new('invalid status response: 433') }
544+
let(:mock_logger) { double(:steno_logger, error: nil) }
545+
546+
before do
547+
scim = instance_double(CF::UAA::Scim)
548+
allow(scim).to receive(:add).and_raise(uaa_error)
549+
allow(uaa_client).to receive_messages(scim: scim, logger: mock_logger)
550+
end
551+
552+
it 'logs and raises the error' do
553+
expect { uaa_client.create_shadow_user('[email protected]', 'idp.local') }.to raise_error(UaaUnavailable)
554+
expect(mock_logger).to have_received(:error).with("UAA request for creating a user failed: #{uaa_error.inspect}")
555+
end
556+
end
525557
end
526558

527559
describe '#id_for_username' do

0 commit comments

Comments
 (0)