Skip to content

Commit b951ac4

Browse files
authored
Multiple service bindings (#4586)
* Introduce max_service_credential_bindings_per_app_service_instance Config property allows more than one binding per app and service instance (default remains 1). * Introduce strategy parameter for binding creation Add optional strategy parameter (single | multiple) to the binding creation API. Allow multiple bindings when strategy = "multiple" and max_service_credential_bindings_per_app_service_instance is greater than 1.
1 parent e4dfb3f commit b951ac4

File tree

13 files changed

+48
-25
lines changed

13 files changed

+48
-25
lines changed

app/actions/service_credential_binding_app_create.rb

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def precursor(service_instance, message:, app: nil, volume_mount_services_enable
4848
num_valid_bindings += 1
4949
end
5050

51-
validate_number_of_bindings!(num_valid_bindings)
51+
validate_number_of_bindings!(num_valid_bindings, strategy: message.strategy)
5252
validate_app_guid_name_uniqueness!(app.guid, message.name, service_instance.guid)
5353

5454
new_binding.save_with_attributes_and_new_operation(
@@ -82,8 +82,12 @@ def validate_binding!(binding, desired_binding_name:)
8282
name_cannot_be_changed! if binding.name != desired_binding_name
8383
end
8484

85-
def validate_number_of_bindings!(number_of_bindings)
86-
too_many_bindings! if number_of_bindings >= max_bindings_per_app_service_instance
85+
def validate_number_of_bindings!(number_of_bindings, strategy:)
86+
if strategy == 'multiple'
87+
too_many_bindings! if number_of_bindings >= max_bindings_per_app_service_instance
88+
elsif number_of_bindings >= 1
89+
already_bound!
90+
end
8791
end
8892

8993
def validate_app_guid_name_uniqueness!(target_app_guid, desired_binding_name, target_service_instance_guid)
@@ -109,12 +113,7 @@ def event_repository
109113
end
110114

111115
def max_bindings_per_app_service_instance
112-
1
113-
# NOTE: This is hard-coded to 1 for now to preserve the old uniqueness behavior.
114-
# TODO: Once the DB migration that drops the unique constraints for service bindings has been released,
115-
# this should be switched to read from config:
116-
# VCAP::CloudController::Config.config.get(:max_service_credential_bindings_per_app_service_instance)
117-
# TODO: Also remove skips in related specs.
116+
VCAP::CloudController::Config.config.get(:max_service_credential_bindings_per_app_service_instance)
118117
end
119118

120119
def app_is_required!

app/messages/service_credential_app_binding_create_message.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
module VCAP::CloudController
22
class ServiceCredentialAppBindingCreateMessage < ServiceCredentialBindingCreateMessage
3+
validates :strategy, allow_blank: false, allow_nil: true, inclusion: {
4+
in: %w[single multiple],
5+
message: "must be 'single' or 'multiple'"
6+
}
7+
38
def relationships_message
49
@relationships_message ||= Relationships.new(relationships&.deep_symbolize_keys)
510
end

app/messages/service_credential_binding_create_message.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
require 'messages/metadata_base_message'
22
module VCAP::CloudController
33
class ServiceCredentialBindingCreateMessage < MetadataBaseMessage
4-
register_allowed_keys %i[type name relationships parameters]
4+
register_allowed_keys %i[type strategy name relationships parameters]
55
validates_with NoAdditionalKeysValidator, RelationshipValidator
66
validates :parameters, hash: true, allow_nil: true
77
validates :type, allow_blank: false, inclusion: {

config/cloud_controller.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ directories:
322322
stacks_file: config/stacks.yml
323323
newrelic_enabled: false
324324

325+
max_service_credential_bindings_per_app_service_instance: 1
325326
max_annotations_per_resource: 200
326327
max_labels_per_resource: 50
327328
max_migration_duration_in_minutes: 45

docs/v3/source/includes/resources/service_credential_bindings/_create.md.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ Name | Type | Description
116116
Name | Type | Description |
117117
---- | ---- | ----------- |
118118
**relationships.app** | [_to-one relationship_](#to-one-relationships) | The app to be bound. Required when type is `app`
119+
**strategy** | string | Strategy for creating the service credential binding. Valid values are `single` (default) and `multiple` (experimental). Only valid when type is `app`.
119120
**parameters** | _object_ | A JSON object that is passed to the service broker
120121
**metadata.labels** | [_label object_](#labels) | Labels applied to the service credential binding
121122
**metadata.annotations** | [_annotation object_](#annotations) | Annotations applied to the service credential binding

docs/v3/source/includes/resources/service_credential_bindings/_header.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ Service credential bindings are used to make the details of the connection to a
44

55
Service credential bindings can be of type `app` or `key`.
66

7-
A service credential binding is of type `app` when it is a binding between a [service instance](#service-instances) and an [application](#apps)
7+
A service credential binding is of type `app` when it is a binding between a [service instance](#service-instances) and an [application](#apps).
88
Not all services support this binding, as some services deliver value to users directly without integration with an application.
99
Field `broker_catalog.features.bindable` from [service plan](#the-service-plan-object) of the service instance can be used to determine if it is bindable.
1010

1111
A service credential binding is of type `key` when it only retrieves the details of the service instance and makes them available to the developer.
12-

lib/cloud_controller/config_schemas/api_schema.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ class ApiSchema < VCAP::Config
410410
optional(:query_raise_on_mismatch) => bool
411411
},
412412

413+
max_service_credential_bindings_per_app_service_instance: Integer,
413414
max_labels_per_resource: Integer,
414415
max_annotations_per_resource: Integer,
415416

lib/cloud_controller/config_schemas/worker_schema.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ class WorkerSchema < VCAP::Config
221221

222222
max_manifest_service_binding_poll_duration_in_seconds: Integer,
223223

224+
max_service_credential_bindings_per_app_service_instance: Integer,
224225
max_labels_per_resource: Integer,
225226
max_annotations_per_resource: Integer,
226227
custom_metric_tag_prefix_list: Array,

spec/unit/actions/service_credential_binding_app_create_spec.rb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,13 +190,11 @@ module V3
190190
end
191191

192192
context 'when multiple bindings are allowed' do
193+
let(:message) { VCAP::CloudController::ServiceCredentialAppBindingCreateMessage.new({ name: name, strategy: 'multiple' }) }
193194
let(:binding_1) { ServiceBinding.make(service_instance:, app:, name:) }
194195
let(:binding_2) { ServiceBinding.make(service_instance:, app:, name:) }
195196

196197
before do
197-
# TODO: Remove skip when the service bindings unique constraints are removed
198-
skip 'this test can be enabled when the service bindings unique constraints are removed and max_bindings_per_app_service_instance can be configured'
199-
200198
TestConfig.override(max_service_credential_bindings_per_app_service_instance: 3)
201199
binding_1.save_with_attributes_and_new_operation({}, { type: 'create', state: 'succeeded' })
202200
binding_2.save_with_attributes_and_new_operation({}, { type: 'create', state: 'succeeded' })
@@ -279,6 +277,16 @@ module V3
279277
'The app has too many bindings to this service instance (limit: 2). Consider deleting existing/orphaned bindings.')
280278
end
281279
end
280+
281+
context "when the strategy = 'multiple' parameter is omitted" do
282+
let(:message) { VCAP::CloudController::ServiceCredentialAppBindingCreateMessage.new({ name: }) }
283+
284+
it 'raises an error' do
285+
expect do
286+
action.precursor(service_instance, app:, message:)
287+
end.to raise_error(ServiceCredentialBindingAppCreate::UnprocessableCreate, 'The app is already bound to the service instance')
288+
end
289+
end
282290
end
283291
end
284292

spec/unit/lib/cloud_controller/diego/service_binding_files_builder_spec.rb

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@ module VCAP::CloudController::Diego
1818
end
1919

2020
context 'when there are multiple bindings with the same name for the same app and service instance' do
21-
before do
22-
# TODO: Remove skip when the service bindings unique constraints are removed
23-
skip 'this test can be enabled when the service bindings unique constraints are removed and max_bindings_per_app_service_instance can be configured'
24-
end
25-
2621
let(:newer_binding_created_at) { Time.now.utc - 2.minutes }
2722

2823
let!(:newer_binding) do

0 commit comments

Comments
 (0)