Skip to content
This repository was archived by the owner on Jun 2, 2021. It is now read-only.

Commit 8a0668f

Browse files
authored
v3(services) Job state set to failed when LO responds with failure (cloudfoundry#1900)
* v3(services) Job state set to failed when LO responds with failure * fix failing tests * Fix formatting
1 parent c5373de commit 8a0668f

6 files changed

Lines changed: 113 additions & 22 deletions

File tree

app/actions/v3/service_binding_create.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
module VCAP::CloudController
44
module V3
5+
class LastOperationFailedState < StandardError
6+
end
7+
58
class ServiceBindingCreate
69
PollingStatus = Struct.new(:finished, :retry_after).freeze
710
PollingFinished = PollingStatus.new(true, nil).freeze
@@ -50,6 +53,10 @@ def poll(binding)
5053
}
5154
)
5255

56+
if details[:last_operation][:state] == 'failed'
57+
raise LastOperationFailedState.new(details[:last_operation][:description])
58+
end
59+
5360
if binding.reload.terminal_state?
5461
PollingFinished
5562
else

spec/request/service_route_bindings_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1069,7 +1069,7 @@
10691069
end
10701070
end
10711071

1072-
it_behaves_like 'binding last operation response handling', 'delete'
1072+
it_behaves_like 'unbinding last operation response handling', 'delete'
10731073
end
10741074

10751075
context 'when the broker returns a failure' do

spec/support/shared_examples/request/last_operation_response_handling.rb

Lines changed: 101 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
RSpec.shared_examples 'binding last operation response handling' do |type|
2-
context 'valid http codes' do
1+
RSpec.shared_examples 'binding last operation response handling' do |operation_type|
2+
context 'failure http codes' do
33
valid_responses = [
4-
{ code: 400, body: { description: 'helpful message' }, expected_description: 'helpful message' },
5-
{ code: 200, body: { state: 'failed', description: 'something went wrong' }, expected_description: 'something went wrong' }
4+
{ code: 400, body: { description: 'helpful message' } },
5+
{ code: 200, body: { state: 'failed', description: 'something went wrong' } }
66
]
77

88
valid_responses.each do |response|
@@ -11,21 +11,21 @@
1111
let(:last_operation_status_code) { response[:code] }
1212
let(:last_operation_body) { response[:body] }
1313

14-
it 'updates the binding and job' do
15-
execute_all_jobs(expected_successes: 1, expected_failures: 0)
14+
it 'updates the binding and job to failed' do
15+
execute_all_jobs(expected_successes: 0, expected_failures: 1)
1616

1717
binding.reload
18-
expect(binding.last_operation.type).to eq(type)
18+
expect(binding.last_operation.type).to eq(operation_type)
1919
expect(binding.last_operation.state).to eq('failed')
2020
expect(binding.last_operation.description).to eq(response[:body][:description])
2121

22-
expect(job.state).to eq(VCAP::CloudController::PollableJobModel::COMPLETE_STATE)
22+
expect(job.state).to eq(VCAP::CloudController::PollableJobModel::FAILED_STATE)
2323
end
2424
end
2525
end
2626
end
2727

28-
context 'invalid http codes' do
28+
context 'keep polling http codes' do
2929
[404, 500].each do |code|
3030
context "last operation response is #{code}" do
3131
let(:last_operation_status_code) { code }
@@ -35,7 +35,7 @@
3535
execute_all_jobs(expected_successes: 1, expected_failures: 0)
3636

3737
binding.reload
38-
expect(binding.last_operation.type).to eq(type)
38+
expect(binding.last_operation.type).to eq(operation_type)
3939
expect(binding.last_operation.state).to eq('in progress')
4040
expect(binding.last_operation.description).to include("Status Code: #{code}")
4141

@@ -57,7 +57,7 @@
5757
execute_all_jobs(expected_successes: 1, expected_failures: 0)
5858

5959
binding.reload
60-
expect(binding.last_operation.type).to eq(type)
60+
expect(binding.last_operation.type).to eq(operation_type)
6161
expect(binding.last_operation.state).to eq('in progress')
6262

6363
expect(job.state).to eq(VCAP::CloudController::PollableJobModel::POLLING_STATE)
@@ -77,7 +77,96 @@
7777
execute_all_jobs(expected_successes: 1, expected_failures: 0)
7878

7979
binding.reload
80-
expect(binding.last_operation.type).to eq(type)
80+
expect(binding.last_operation.type).to eq(operation_type)
81+
expect(binding.last_operation.state).to eq('in progress')
82+
83+
expect(job.state).to eq(VCAP::CloudController::PollableJobModel::POLLING_STATE)
84+
end
85+
end
86+
end
87+
end
88+
89+
# TODO: Merge these two shared examples into one in https://www.pivotaltracker.com/story/show/174748133
90+
RSpec.shared_examples 'unbinding last operation response handling' do |operation_type|
91+
context 'failure http codes' do
92+
valid_responses = [
93+
{ code: 400, body: { description: 'helpful message' } },
94+
{ code: 200, body: { state: 'failed', description: 'something went wrong' } }
95+
]
96+
97+
valid_responses.each do |response|
98+
context "last operation response is #{response[:code]}" do
99+
let(:state) { response[:state] }
100+
let(:last_operation_status_code) { response[:code] }
101+
let(:last_operation_body) { response[:body] }
102+
103+
it 'updates the binding and job to failed' do
104+
execute_all_jobs(expected_successes: 1, expected_failures: 0)
105+
106+
binding.reload
107+
expect(binding.last_operation.type).to eq(operation_type)
108+
expect(binding.last_operation.state).to eq('failed')
109+
expect(binding.last_operation.description).to eq(response[:body][:description])
110+
111+
expect(job.state).to eq(VCAP::CloudController::PollableJobModel::COMPLETE_STATE)
112+
end
113+
end
114+
end
115+
end
116+
117+
context 'keep polling http codes' do
118+
[404, 500].each do |code|
119+
context "last operation response is #{code}" do
120+
let(:last_operation_status_code) { code }
121+
let(:last_operation_body) { 'something awful' }
122+
123+
it 'continues polling' do
124+
execute_all_jobs(expected_successes: 1, expected_failures: 0)
125+
126+
binding.reload
127+
expect(binding.last_operation.type).to eq(operation_type)
128+
expect(binding.last_operation.state).to eq('in progress')
129+
expect(binding.last_operation.description).to include("Status Code: #{code}")
130+
131+
expect(job.state).to eq(VCAP::CloudController::PollableJobModel::POLLING_STATE)
132+
end
133+
end
134+
end
135+
end
136+
137+
context 'timeout' do
138+
before do
139+
stub_request(:get, broker_binding_last_operation_url).
140+
with(query: hash_including({
141+
operation: operation
142+
})).to_timeout
143+
end
144+
145+
it 'continues polling' do
146+
execute_all_jobs(expected_successes: 1, expected_failures: 0)
147+
148+
binding.reload
149+
expect(binding.last_operation.type).to eq(operation_type)
150+
expect(binding.last_operation.state).to eq('in progress')
151+
152+
expect(job.state).to eq(VCAP::CloudController::PollableJobModel::POLLING_STATE)
153+
end
154+
end
155+
156+
context 'connection errors' do
157+
[SocketError, Errno::ECONNREFUSED, RuntimeError].each do |error|
158+
before do
159+
stub_request(:get, broker_binding_last_operation_url).
160+
with(query: hash_including({
161+
operation: operation
162+
})).to_raise(error)
163+
end
164+
165+
it 'continues polling' do
166+
execute_all_jobs(expected_successes: 1, expected_failures: 0)
167+
168+
binding.reload
169+
expect(binding.last_operation.type).to eq(operation_type)
81170
expect(binding.last_operation.state).to eq('in progress')
82171

83172
expect(job.state).to eq(VCAP::CloudController::PollableJobModel::POLLING_STATE)

spec/support/shared_examples/v3_service_binding_create.rb

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -229,13 +229,8 @@ class BadError < StandardError; end
229229
context 'response says failed' do
230230
let(:state) { 'failed' }
231231

232-
it 'returns true' do
233-
polling_status = action.poll(binding)
234-
expect(polling_status[:finished]).to be_truthy
235-
end
236-
237232
it 'updates the last operation' do
238-
action.poll(binding)
233+
expect { action.poll(binding) }.to raise_error(VCAP::CloudController::V3::LastOperationFailedState)
239234

240235
binding.reload
241236
expect(binding.last_operation.state).to eq('failed')

spec/unit/actions/service_credential_binding_create_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,8 @@ module V3
332332

333333
context 'response says failed' do
334334
let(:state) { 'failed' }
335-
it 'does not notify diego or create an audit event' do
336-
action.poll(binding)
335+
it 'does not create an audit event' do
336+
expect { action.poll(binding) }.to raise_error(VCAP::CloudController::V3::LastOperationFailedState)
337337

338338
expect(@service_binding_event_repository).not_to have_received(:record_create)
339339
end

spec/unit/actions/service_route_binding_create_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ module V3
324324
context 'response says failed' do
325325
let(:state) { 'failed' }
326326
it 'does not notify diego or create an audit event' do
327-
action.poll(binding)
327+
expect { action.poll(binding) }.to raise_error(VCAP::CloudController::V3::LastOperationFailedState)
328328

329329
expect(messenger).not_to have_received(:send_desire_request)
330330
expect(event_repository).not_to have_received(:record_service_instance_event)

0 commit comments

Comments
 (0)