Skip to content

Commit

Permalink
Enhance CATS-SB
Browse files Browse the repository at this point in the history
- The behaviour for a fetch service instance can now be controlled with `fetch_service_instance`
- The behaviour for a fetch service binding can now be controlled with `fetch_service_instance`
- The behaviour for a fetch service instance last operation can now be controlled with `fetch_service_instance_last_operation`
- The behaviour for a fetch service binding last operation can now be controlled with `fetch_service_binding_last_operation`

- Improved error responses in case a service instance or service binding does not exist but the resource or its last operation is being fetched
- Improved overriding the response json with configuration
  • Loading branch information
svkrieger committed Jan 11, 2024
1 parent e4d6058 commit 2f9ad80
Show file tree
Hide file tree
Showing 3 changed files with 875 additions and 38 deletions.
37 changes: 35 additions & 2 deletions assets/service_broker/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
"body": {}
}
},
"fetch": {
"fetch_service_instance_last_operation": {
"default": {
"in_progress": {
"sleep_seconds": 0,
Expand All @@ -139,6 +139,20 @@
}
}
},
"fetch_service_instance": {
"default": {
"sleep_seconds": 0,
"status": 200,
"body": {}
}
},
"fetch_service_binding": {
"default": {
"sleep_seconds": 0,
"status": 200,
"body": {}
}
},
"update": {
"fake-async-plan-guid": {
"sleep_seconds": 0,
Expand Down Expand Up @@ -198,9 +212,28 @@
"status": 200,
"body": {}
}
},
"fetch_service_binding_last_operation": {
"default": {
"in_progress": {
"sleep_seconds": 0,
"status": 200,
"body": {
"state": "in progress"
}
},
"finished": {
"sleep_seconds": 0,
"status": 200,
"body": {
"state": "succeeded"
}
}
}
}
},
"service_instances": {},
"service_bindings": {},
"max_fetch_service_instance_requests": 1
"max_fetch_service_instance_requests": 1,
"max_fetch_service_binding_requests": 1
}
161 changes: 131 additions & 30 deletions assets/service_broker/service_broker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,39 @@ def to_json(opts={})
end
end

class ServiceBinding
attr_reader :binding_data, :instance_id, :fetch_count, :deleted

def initialize(opts={})
@binding_data = opts.fetch(:binding_data)
@instance_id = opts.fetch(:instance_id)
@fetch_count = opts.fetch(:fetch_count, 0)
@deleted = opts.fetch(:deleted, false)
end

def plan_id
@binding_data['plan_id']
end

def delete!
@deleted = true
@fetch_count = 0
self
end

def increment_fetch_count
@fetch_count += 1
end

def to_json(opts={})
{
binding_data: binding_data,
fetch_count: fetch_count,
deleted: deleted
}.to_json(opts)
end
end

class DataSource
attr_reader :data

Expand All @@ -64,6 +97,10 @@ def max_fetch_service_instance_requests
@data['max_fetch_service_instance_requests'] || 1
end

def max_fetch_service_binding_requests
@data['max_fetch_service_binding_requests'] || 1
end

def service_instance_by_id(cc_id)
@data['service_instances'][cc_id]
end
Expand All @@ -78,15 +115,23 @@ def create_service_instance(cc_id, json_data)
service_instance
end

def service_binding_by_id(cc_id)
@data['service_bindings'][cc_id]
end

def create_service_binding(instance_id, binding_id, binding_data)
@data['service_instances'][binding_id] = {
'binding_data' => binding_data,
'instance_id' => instance_id,
}
service_binding = ServiceBinding.new(
binding_data: binding_data,
instance_id: instance_id
)

@data['service_bindings'][binding_id] = service_binding

service_binding
end

def delete_service_binding(binding_id)
@data['service_instances'].delete(binding_id)
@data['service_bindings'].delete(binding_id)
end

def merge!(data)
Expand Down Expand Up @@ -195,7 +240,7 @@ def respond_from_config(behavior)
end

def cf_respond_with_api_info_location(cf_api_info_location)
if cf_api_info_location.empty?
if cf_api_info_location.nil?
status 503
log_response(status, JSON.pretty_generate({
error: true,
Expand Down Expand Up @@ -225,7 +270,7 @@ def cf_respond_with_api_info_location(cf_api_info_location)
respond_with_behavior($datasource.behavior_for_type(:provision, service_instance.plan_id), params['accepts_incomplete'])
end

# fetch service instance
# fetch service instance last operation
get '/v2/service_instances/:id/last_operation/?' do |id|
service_instance = $datasource.service_instance_by_id(id)
if service_instance
Expand All @@ -237,7 +282,7 @@ def cf_respond_with_api_info_location(cf_api_info_location)
state = 'in_progress'
end

behavior = $datasource.behavior_for_type('fetch', plan_id)[state]
behavior = $datasource.behavior_for_type('fetch_service_instance_last_operation', plan_id)[state]
sleep behavior['sleep_seconds']
status behavior['status']

Expand All @@ -246,22 +291,42 @@ def cf_respond_with_api_info_location(cf_api_info_location)
else
log_response(status, behavior['raw_body'])
end
else
status 200
log_response(status, {
state: 'failed',
description: "Broker could not find service instance by the given id #{id}",
}.to_json)
else # service instance does not exist
status 410
log_response(status, "Broker could not find service instance by the given id #{id}")
end
end

# fetch service binding
# fetch service binding last operation
get '/v2/service_instances/:instance_id/service_bindings/:binding_id/last_operation/?' do |instance_id, binding_id|
status 200
log_response(status, {
state: 'succeeded',
description: '100%',
}.to_json)
service_binding = $datasource.service_binding_by_id(binding_id)
if service_binding
if service_binding.instance_id == instance_id
plan_id = service_binding.plan_id

if service_binding.increment_fetch_count > $datasource.max_fetch_service_binding_requests
state = 'finished'
else
state = 'in_progress'
end

behavior = $datasource.behavior_for_type('fetch_service_binding_last_operation', plan_id)[state]
sleep behavior['sleep_seconds']
status behavior['status']

if behavior['body']
log_response(status, behavior['body'].to_json)
else
log_response(status, behavior['raw_body'])
end
else # service binding is not associated with the given instance_id
status 410
log_response(status, "Broker could not find the service binding `#{binding_id}` for service instance `#{instance_id}`")
end
else # service binding does not exist
status 410
log_response(status, "Broker could not find the service binding `#{binding_id}` for service instance `#{instance_id}`")
end
end

# update service instance
Expand Down Expand Up @@ -294,32 +359,68 @@ def cf_respond_with_api_info_location(cf_api_info_location)
content_type :json
json_body = JSON.parse(request.body.read)

service_binding = $datasource.create_service_binding(instance_id, binding_id, json_body)
respond_with_behavior($datasource.behavior_for_type(:bind, service_binding['binding_data']['plan_id']), params[:accepts_incomplete])
service_instance = $datasource.service_instance_by_id(instance_id)
if service_instance
service_binding = $datasource.create_service_binding(instance_id, binding_id, json_body)
respond_with_behavior($datasource.behavior_for_type(:bind, service_binding.plan_id), params[:accepts_incomplete])
else
status 400
log_response(status, "Broker could not find service instance by the given id #{instance_id}")
end
end

# delete service binding
delete '/v2/service_instances/:instance_id/service_bindings/:id' do |instance_id, binding_id|
content_type :json

service_binding = $datasource.delete_service_binding(binding_id)
service_binding = $datasource.service_binding_by_id(binding_id)
if service_binding
respond_with_behavior($datasource.behavior_for_type(:unbind, service_binding['binding_data']['plan_id']), params[:accepts_incomplete])
service_binding.delete!
respond_with_behavior($datasource.behavior_for_type(:unbind, service_binding.plan_id), params[:accepts_incomplete])
else
respond_with_behavior($datasource.behavior_for_type(:unbind, nil), params[:accepts_incomplete])
end
end

# fetch service instance
get '/v2/service_instances/:instance_id' do |instance_id|
status 200
log_response(status, JSON.pretty_generate($datasource.data['service_instances'][instance_id].provision_data))
service_instance = $datasource.service_instance_by_id(instance_id)
if service_instance
behaviour = $datasource.behavior_for_type(:fetch_service_instance, service_instance.plan_id).clone

provision_data = service_instance.provision_data.clone
if behaviour["body"]
behaviour["body"] = provision_data.merge!(behaviour["body"])
end

respond_with_behavior(behaviour)
else # service instance does not exist
status 404
log_response(status, "Broker could not find service instance by the given id #{instance_id}")
end
end

# fetch service binding
get '/v2/service_instances/:instance_id/service_bindings/:id' do |instance_id, binding_id|
binding_data = $datasource.data['service_instances'][binding_id]['binding_data']
response_body = $datasource.behavior_for_type(:fetch_service_binding, binding_data['plan_id'])
response_body['body'].merge!(binding_data)
respond_with_behavior(response_body)
service_binding = $datasource.service_binding_by_id(binding_id)
if service_binding
if service_binding.instance_id == instance_id
behaviour = $datasource.behavior_for_type(:fetch_service_binding, service_binding.plan_id).clone

binding_data = service_binding.binding_data.clone
if behaviour["body"]
behaviour["body"] = binding_data.merge!(behaviour["body"])
end

respond_with_behavior(behaviour)
else # service binding is not associated with the given instance_id
status 404
log_response(status, "Broker could not find the service binding `#{binding_id}` for service instance `#{instance_id}`")
end
else # service binding does not exist
status 404
log_response(status, "Broker could not find the service binding `#{binding_id}` for service instance `#{instance_id}`")
end
end

get '/config/all/?' do
Expand Down
Loading

0 comments on commit 2f9ad80

Please sign in to comment.