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

Commit 72cd316

Browse files
FelisiaMDerik EvangelistaDerik Evangelista
authored
V3(services): refactor binding jobs and actions
[174988751](https://www.pivotaltracker.com/story/show/174988751) * WIP - route and cred bindings controller are calling the same job * Extracted testing of create binding job to shared example Shared example can be used to test route and credential type of bindings. Next step in the refactoring is merging the action in one single version. Co-Authored-By: Derik Evangelista <ederik@vmware.com> * WIP: Started extracting binding action into one super class Co-Authored-By: Derik Evangelista <ederik@vmware.com> * WIP: poll service binding creation can be used by credential binding and route binding * WIP: Poll testing Co-Authored-By: Derik Evangelista <ederik@vmware.com> * wip: add unit test for polling * wip: fix failing test Co-authored-by: Derik Evangelista <devangelista@pivotal.io> Co-authored-by: Derik Evangelista <ederik@vmware.com>
1 parent 416c1f8 commit 72cd316

23 files changed

Lines changed: 1234 additions & 961 deletions

app/actions/service_credential_binding_create.rb

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
require 'repositories/service_binding_event_repository'
22
require 'services/service_brokers/service_client_provider'
3+
require 'actions/v3/service_binding_create'
34

45
module VCAP::CloudController
56
module V3
6-
class ServiceCredentialBindingCreate
7+
class ServiceCredentialBindingCreate < V3::ServiceBindingCreate
78
class UnprocessableCreate < StandardError
89
end
910

1011
class Unimplemented < StandardError
1112
end
1213

1314
def initialize(user_audit_info, audit_hash)
15+
super()
1416
@user_audit_info = user_audit_info
1517
@audit_hash = audit_hash
1618
end
@@ -39,37 +41,18 @@ def precursor(service_instance, app: nil, name: nil, volume_mount_services_enabl
3941
raise UnprocessableCreate.new(e.full_message)
4042
end
4143

42-
def bind(binding, parameters: {}, accepts_incomplete: false)
43-
client = VCAP::Services::ServiceClientProvider.provide(instance: binding.service_instance)
44-
details = client.bind(binding, arbitrary_parameters: parameters, accepts_incomplete: accepts_incomplete)
45-
46-
if details[:async]
47-
save_incomplete_binding(binding, details[:operation])
48-
else
49-
binding.save_with_new_operation(operation_succeeded, attributes: details[:binding])
50-
event_repository.record_create(binding, @user_audit_info, @audit_hash, manifest_triggered: false)
51-
end
52-
rescue => e
53-
binding.save_with_new_operation({
54-
type: 'create',
55-
state: 'failed',
56-
description: e.message,
57-
})
58-
raise e
59-
end
60-
6144
private
6245

63-
def operation_succeeded
64-
{ type: 'create', state: 'succeeded' }
65-
end
66-
67-
def save_incomplete_binding(binding, operation)
68-
binding.save_with_new_operation({
69-
type: 'create',
70-
state: 'in progress',
71-
broker_provided_operation: operation
72-
})
46+
def complete_binding_and_save(binding, binding_details, last_operation)
47+
binding.save_with_attributes_and_new_operation(
48+
binding_details,
49+
{
50+
type: 'create',
51+
state: last_operation[:state],
52+
description: last_operation[:description]
53+
}
54+
)
55+
event_repository.record_create(binding, @user_audit_info, @audit_hash, manifest_triggered: false)
7356
end
7457

7558
def validate!(service_instance, app, volume_mount_services_enabled)
Lines changed: 10 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
require 'services/service_brokers/service_client_provider'
2+
require 'actions/v3/service_binding_create'
23

34
module VCAP::CloudController
45
module V3
5-
class ServiceRouteBindingCreate
6+
class ServiceRouteBindingCreate < V3::ServiceBindingCreate
67
def initialize(service_event_repository)
8+
super()
79
@service_event_repository = service_event_repository
810
end
911

@@ -22,77 +24,10 @@ def precursor(service_instance, route)
2224
)
2325
end
2426

25-
def bind(precursor, parameters: {}, accepts_incomplete: false)
26-
client = VCAP::Services::ServiceClientProvider.provide(instance: precursor.service_instance)
27-
details = client.bind(precursor, arbitrary_parameters: parameters, accepts_incomplete: accepts_incomplete)
28-
29-
if details[:async]
30-
not_retrievable! unless bindings_retrievable?(precursor)
31-
save_incomplete_binding(precursor, details[:operation])
32-
else
33-
complete_binding_and_save(precursor, details[:binding][:route_service_url])
34-
end
35-
rescue => e
36-
precursor.save_with_new_operation({}, {
37-
type: 'create',
38-
state: 'failed',
39-
description: e.message,
40-
})
41-
42-
raise e
43-
end
44-
45-
def poll(binding)
46-
client = VCAP::Services::ServiceClientProvider.provide(instance: binding.service_instance)
47-
48-
details = fetch_last_operation(client, binding)
49-
return PollingNotComplete.new unless details
50-
51-
attributes = {}
52-
53-
complete = details[:last_operation][:state] == 'succeeded'
54-
if complete
55-
params = client.fetch_service_binding(binding)
56-
attributes[:route_service_url] = params[:route_service_url]
57-
end
58-
59-
binding.save_with_new_operation(
60-
attributes,
61-
{
62-
type: 'create',
63-
state: details[:last_operation][:state],
64-
description: details[:last_operation][:description],
65-
}
66-
)
67-
68-
if complete
69-
binding.notify_diego
70-
record_audit_event(binding)
71-
end
72-
73-
if binding.reload.terminal_state?
74-
PollingComplete.new
75-
else
76-
PollingNotComplete.new(details[:retry_after])
77-
end
78-
rescue => e
79-
binding.save_with_new_operation({}, {
80-
type: 'create',
81-
state: 'failed',
82-
description: e.message,
83-
})
84-
return PollingComplete.new
85-
end
86-
87-
PollingComplete = Class.new.freeze
88-
PollingNotComplete = Struct.new(:retry_after).freeze
89-
9027
class UnprocessableCreate < StandardError; end
9128

9229
class RouteBindingAlreadyExists < StandardError; end
9330

94-
class BindingNotRetrievable < StandardError; end
95-
9631
private
9732

9833
def validate!(service_instance, route)
@@ -107,46 +42,19 @@ def validate!(service_instance, route)
10742

10843
attr_reader :service_event_repository
10944

110-
def fetch_last_operation(client, binding)
111-
client.fetch_service_binding_last_operation(binding)
112-
rescue VCAP::Services::ServiceBrokers::V2::Errors::ServiceBrokerBadResponse,
113-
VCAP::Services::ServiceBrokers::V2::Errors::ServiceBrokerRequestRejected,
114-
HttpRequestError => e
115-
binding.save_with_new_operation({}, {
116-
type: 'create',
117-
state: 'in progress',
118-
description: e.message,
119-
})
120-
121-
return nil
122-
end
123-
124-
def save_incomplete_binding(precursor, operation)
125-
precursor.save_with_new_operation({},
45+
def complete_binding_and_save(binding, binding_details, last_operation)
46+
binding.save_with_attributes_and_new_operation(
12647
{
127-
type: 'create',
128-
state: 'in progress',
129-
broker_provided_operation: operation
130-
}
131-
)
132-
end
133-
134-
def complete_binding_and_save(precursor, route_service_url)
135-
save_with_route_service_url(precursor, route_service_url)
136-
precursor.notify_diego
137-
record_audit_event(precursor)
138-
end
139-
140-
def save_with_route_service_url(precursor, route_service_url)
141-
precursor.save_with_new_operation(
142-
{
143-
route_service_url: route_service_url
48+
route_service_url: binding_details[:route_service_url]
14449
},
14550
{
14651
type: 'create',
147-
state: 'succeeded',
52+
state: last_operation[:state],
53+
description: last_operation[:description],
14854
}
14955
)
56+
binding.notify_diego
57+
record_audit_event(binding)
15058
end
15159

15260
def record_audit_event(precursor)
@@ -157,10 +65,6 @@ def record_audit_event(precursor)
15765
)
15866
end
15967

160-
def bindings_retrievable?(binding)
161-
binding.service_instance.service.bindings_retrievable
162-
end
163-
16468
def operation_in_progress!
16569
raise UnprocessableCreate.new('There is an operation in progress for the service instance')
16670
end
@@ -188,10 +92,6 @@ def not_bindable!
18892
def already_exists!
18993
raise RouteBindingAlreadyExists.new('The route and service instance are already bound')
19094
end
191-
192-
def not_retrievable!
193-
raise BindingNotRetrievable.new('The broker responded asynchronously but does not support fetching binding data')
194-
end
19595
end
19696
end
19797
end
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
require 'services/service_brokers/service_client_provider'
2+
3+
module VCAP::CloudController
4+
module V3
5+
class ServiceBindingCreate
6+
PollingStatus = Struct.new(:finished, :retry_after).freeze
7+
PollingFinished = PollingStatus.new(true, nil).freeze
8+
ContinuePolling = ->(retry_after) { PollingStatus.new(false, retry_after) }
9+
10+
def bind(binding, parameters: {}, accepts_incomplete: false)
11+
client = VCAP::Services::ServiceClientProvider.provide(instance: binding.service_instance)
12+
details = client.bind(binding, arbitrary_parameters: parameters, accepts_incomplete: accepts_incomplete)
13+
14+
if details[:async]
15+
not_retrievable! unless bindings_retrievable?(binding)
16+
save_incomplete_binding(binding, details[:operation])
17+
else
18+
complete_binding_and_save(binding, details[:binding], { state: 'succeeded' })
19+
end
20+
rescue => e
21+
binding.save_with_attributes_and_new_operation(
22+
{},
23+
{
24+
type: 'create',
25+
state: 'failed',
26+
description: e.message,
27+
}
28+
)
29+
30+
raise e
31+
end
32+
33+
def poll(binding)
34+
client = VCAP::Services::ServiceClientProvider.provide(instance: binding.service_instance)
35+
details = fetch_last_operation(client, binding)
36+
return ContinuePolling.call(nil) unless details
37+
38+
if details[:last_operation][:state] == 'succeeded'
39+
params = client.fetch_service_binding(binding)
40+
complete_binding_and_save(binding, params, details[:last_operation])
41+
return PollingFinished
42+
end
43+
44+
binding.save_with_attributes_and_new_operation(
45+
{},
46+
{
47+
type: 'create',
48+
state: details[:last_operation][:state],
49+
description: details[:last_operation][:description],
50+
}
51+
)
52+
53+
if binding.reload.terminal_state?
54+
PollingFinished
55+
else
56+
ContinuePolling.call(details[:retry_after])
57+
end
58+
rescue => e
59+
binding.save_with_attributes_and_new_operation(
60+
{},
61+
{
62+
type: 'create',
63+
state: 'failed',
64+
description: e.message,
65+
}
66+
)
67+
PollingFinished
68+
end
69+
70+
class BindingNotRetrievable < StandardError; end
71+
72+
private
73+
74+
def save_incomplete_binding(precursor, operation)
75+
precursor.save_with_attributes_and_new_operation(
76+
{},
77+
{
78+
type: 'create',
79+
state: 'in progress',
80+
broker_provided_operation: operation
81+
}
82+
)
83+
end
84+
85+
def bindings_retrievable?(binding)
86+
binding.service_instance.service.bindings_retrievable
87+
end
88+
89+
def not_retrievable!
90+
raise BindingNotRetrievable.new('The broker responded asynchronously but does not support fetching binding data')
91+
end
92+
93+
def fetch_last_operation(client, binding)
94+
client.fetch_service_binding_last_operation(binding)
95+
rescue VCAP::Services::ServiceBrokers::V2::Errors::ServiceBrokerBadResponse,
96+
VCAP::Services::ServiceBrokers::V2::Errors::ServiceBrokerRequestRejected,
97+
HttpRequestError => e
98+
binding.save_with_attributes_and_new_operation(
99+
{},
100+
{
101+
type: 'create',
102+
state: 'in progress',
103+
description: e.message,
104+
})
105+
106+
return nil
107+
end
108+
end
109+
end
110+
end

app/controllers/v3/service_credential_bindings_controller.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
require 'messages/service_credential_binding_create_message'
1010
require 'decorators/include_binding_app_decorator'
1111
require 'decorators/include_binding_service_instance_decorator'
12-
require 'jobs/v3/create_service_credential_binding_job'
12+
require 'jobs/v3/create_service_credential_binding_job_actor'
1313

1414
class ServiceCredentialBindingsController < ApplicationController
1515
def index
@@ -112,7 +112,8 @@ def parameters
112112
private
113113

114114
def enqueue_bind_job(binding_guid, message)
115-
bind_job = VCAP::CloudController::V3::CreateServiceCredentialBindingJob.new(
115+
bind_job = VCAP::CloudController::V3::CreateBindingAsyncJob.new(
116+
:credential,
116117
binding_guid,
117118
user_audit_info: user_audit_info,
118119
audit_hash: message.audit_hash,

app/controllers/v3/service_route_bindings_controller.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
require 'messages/service_route_bindings_list_message'
44
require 'actions/service_route_binding_create'
55
require 'actions/service_route_binding_delete'
6-
require 'jobs/v3/create_route_binding_job'
6+
require 'jobs/v3/create_binding_async_job'
77
require 'jobs/v3/delete_route_binding_job'
88
require 'presenters/v3/paginated_list_presenter'
99
require 'presenters/v3/service_route_binding_presenter'
@@ -102,9 +102,11 @@ def valid_message(message_type:)
102102
end
103103

104104
def enqueue_bind_job(binding_guid, parameters)
105-
bind_job = VCAP::CloudController::V3::CreateRouteBindingJob.new(
105+
bind_job = VCAP::CloudController::V3::CreateBindingAsyncJob.new(
106+
:route,
106107
binding_guid,
107108
user_audit_info: user_audit_info,
109+
audit_hash: {},
108110
parameters: parameters,
109111
)
110112
pollable_job = Jobs::Enqueuer.new(bind_job, queue: Jobs::Queues.generic).enqueue_pollable

0 commit comments

Comments
 (0)