Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/actions/v3/service_instance_update_managed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ def complete_instance_and_save(instance, broker_response)
u[:maintenance_info] = maintenance_info if maintenance_info_updated?
end
updates[:dashboard_url] = broker_response[:dashboard_url] if broker_response.key?(:dashboard_url)
updates[:broker_provided_metadata] = broker_response[:broker_provided_metadata] if broker_response.key?(:broker_provided_metadata)

ManagedServiceInstance.db.transaction do
service_instance.save_with_new_operation(
Expand All @@ -174,6 +175,7 @@ def complete_instance_and_save(instance, broker_response)
def save_incomplete_instance(instance, broker_response)
attributes_to_update = {}
attributes_to_update[:dashboard_url] = broker_response[:dashboard_url] if broker_response.key?(:dashboard_url)
attributes_to_update[:broker_provided_metadata] = broker_response[:broker_provided_metadata] if broker_response.key?(:broker_provided_metadata)

instance.save_with_new_operation(
attributes_to_update,
Expand Down
2 changes: 1 addition & 1 deletion app/models/services/managed_service_instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class ManagedServiceInstance < ServiceInstance

plugin :after_initialize

serialize_attributes :json, :maintenance_info
serialize_attributes :json, :maintenance_info, :broker_provided_metadata

def validation_policies
if space
Expand Down
6 changes: 5 additions & 1 deletion app/presenters/v3/service_instance_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def hash_common
end

def hash_additions_managed
{
base_hash = {
type: 'managed',
maintenance_info: maintenance_info,
upgrade_available: upgrade_available,
Expand All @@ -101,6 +101,10 @@ def hash_additions_managed
}
}
}

base_hash[:broker_provided_metadata] = service_instance.broker_provided_metadata if service_instance.broker_provided_metadata.present?

base_hash
end

def hash_additions_user_provided
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Sequel.migration do
change do
# rubocop:disable Migration/IncludeStringSize
add_column :service_instances, :broker_provided_metadata, String, size: 4096, text: true, null: true
# rubocop:enable Migration/IncludeStringSize
end
end
16 changes: 16 additions & 0 deletions docs/v3/source/includes/api_resources/_service_instances.erb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@
},
"upgrade_available": false,
"dashboard_url": "https://service-broker.example.org/dashboard",
"broker_provided_metadata": {
"labels": {
"service_engine_version": "16.6"
},
"attributes": {
"max_connections": "100"
}
},
"last_operation": {
"type": "create",
"state": "succeeded",
Expand Down Expand Up @@ -152,6 +160,14 @@
},
"upgrade_available": false,
"dashboard_url": "https://service-broker.example.org/dashboard",
"broker_provided_metadata": {
"labels": {
"service_engine_version": "16.6"
},
"attributes": {
"max_connections": "100"
}
},
"last_operation": {
"type": <%= metadata.fetch(:operation, "create").to_json %>,
"state": "succeeded",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Name | Type | Description
**maintenance_info** | _[maintenance_info object](#the-maintenance-info-object-for-service-instances)_ | Information about the version of this service instance; only shown when type is `managed`
**upgrade_available** | _bool_ | Whether or not an upgrade of this service instance is available on the current Service Plan; details are available in the maintenance_info object; Only shown when type is `managed`
**dashboard_url** | _string_ | The URL to the service instance dashboard (or null if there is none); only shown when type is `managed`
**broker_provided_metadata** | _[broker provided metadata object](#the-broker-provided-metadata-object-for-service-instances)_ | Metadata provided by the service broker about this service instance; only shown when type is `managed`
**last_operation** | _[last operation object](#the-last-operation-object-for-service-instances)_ | The last operation of this service instance
**relationships.service_plan** | [_to-one relationship_](#to-one-relationships) | The service plan the service instance relates to; only shown when type is `managed`
**relationships.space** | [_to-one relationship_](#to-one-relationships) | The space the service instance is contained in
Expand All @@ -43,6 +44,14 @@ Name | Type | Description
**description** | _string_ | A textual explanation associated with this version


#### The broker provided metadata object for service instances

Name | Type | Description
---- | ---- | -----------
**labels** | _object_ | Broker-specified key-value pairs specifying attributes of Service Instances that do not directly imply behavior changes
**attributes** | _object_ | Broker-specific key-value pairs generated by the Broker that MAY imply behavior changes by the Platform


#### The last operation object for service instances

Name | Type | Description
Expand Down
12 changes: 11 additions & 1 deletion lib/services/service_brokers/v2/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ def provision(instance, arbitrary_parameters: {}, accepts_incomplete: false, mai
return_values = {
instance: {
credentials: {},
dashboard_url: parsed_response['dashboard_url']
dashboard_url: parsed_response['dashboard_url'],
broker_provided_metadata: extract_broker_provided_metadata(parsed_response)
},
last_operation: {
type: 'create',
Expand Down Expand Up @@ -204,6 +205,9 @@ def update(instance, plan, accepts_incomplete: false, arbitrary_parameters: nil,

attributes[:dashboard_url] = dashboard_url if dashboard_url

# Add broker_provided_metadata extraction
attributes[:broker_provided_metadata] = extract_broker_provided_metadata(parsed_response)

if state == 'in progress'
proposed_changes = { service_plan_guid: plan.guid }
proposed_changes[:maintenance_info] = maintenance_info if maintenance_info
Expand Down Expand Up @@ -417,5 +421,11 @@ def hashified_public_annotations(annotations)
end
hashified_annotations(public_annotations)
end

def extract_broker_provided_metadata(parsed_response)
return nil unless parsed_response['metadata'].present? && parsed_response['metadata'].is_a?(Hash)

parsed_response['metadata']
end
end
end
33 changes: 33 additions & 0 deletions lib/services/service_brokers/v2/response_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,17 @@ def provision_response_schema
'operation' => {
'type' => 'string',
'maxLength' => 10_000
},
'metadata' => {
'type' => 'object',
'properties' => {
'labels' => {
'type' => 'object'
},
'attributes' => {
'type' => 'object'
}
}
}
}
}]
Expand Down Expand Up @@ -292,6 +303,17 @@ def update_response_schema
'operation' => {
'type' => 'string',
'maxLength' => 10_000
},
'metadata' => {
'type' => 'object',
'properties' => {
'labels' => {
'type' => 'object'
},
'attributes' => {
'type' => 'object'
}
}
}
}
}]
Expand All @@ -313,6 +335,17 @@ def fetch_service_instance_response_schema
},
'parameters' => {
'type' => 'object'
},
'metadata' => {
'type' => 'object',
'properties' => {
'labels' => {
'type' => 'object'
},
'attributes' => {
'type' => 'object'
}
}
}
}
}]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Migration test for adding broker_provided_metadata column to service_instances table
#
# This test verifies that the migration correctly adds the broker_provided_metadata
# column to the service_instances table with the correct properties (text type, nullable).

require 'spec_helper'
require 'migrations/helpers/migration_shared_context'

RSpec.describe 'migration to add broker_provided_metadata column to service_instances table', isolation: :truncation, type: :migration do
include_context 'migration' do
let(:migration_filename) { '20251121174647_add_broker_provided_metadata_to_service_instances.rb' }
end

describe 'service_instances table' do
subject(:run_migration) { Sequel::Migrator.run(db, migrations_path, target: current_migration_index, allow_missing_migration_files: true) }

let(:space) { VCAP::CloudController::Space.make }

it 'adds a column `broker_provided_metadata`' do
expect(db[:service_instances].columns).not_to include(:broker_provided_metadata)
run_migration
expect(db[:service_instances].columns).to include(:broker_provided_metadata)
end

it 'allows null values for broker_provided_metadata' do
run_migration
# Insert a service instance without broker_provided_metadata
db[:service_instances].insert(
guid: 'test-service-instance-guid',
name: 'test-instance',
space_id: space.id,
broker_provided_metadata: nil
)
# Verify the insert succeeded and the column is null
instance = db[:service_instances].first(guid: 'test-service-instance-guid')
expect(instance[:broker_provided_metadata]).to be_nil
end

it 'accepts text values for broker_provided_metadata' do
run_migration
# Insert a service instance with broker_provided_metadata
metadata_json = '{"labels": {"version": "1.0"}, "attributes": {"engine": "postgresql"}}'
db[:service_instances].insert(
guid: 'test-service-instance-with-metadata',
name: 'test-instance-with-metadata',
space_id: space.id,
broker_provided_metadata: metadata_json
)
# Verify the metadata was stored correctly
instance = db[:service_instances].first(guid: 'test-service-instance-with-metadata')
expect(instance[:broker_provided_metadata]).to eq(metadata_json)
end

it 'preserves existing service instances after migration' do
# Insert a service instance before migration
db[:service_instances].insert(
guid: 'existing-service-instance-guid',
name: 'existing-instance',
space_id: space.id
)
run_migration
# Verify the existing instance still exists and has null metadata
instance = db[:service_instances].first(guid: 'existing-service-instance-guid')
expect(instance).not_to be_nil
expect(instance[:broker_provided_metadata]).to be_nil
end
end
end
72 changes: 72 additions & 0 deletions spec/unit/actions/v3/service_instance_update_managed_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,48 @@ module V3
{ prefix: nil, key_name: 'release', value: 'stable' })
end

context 'when broker returns broker_provided_metadata' do
let(:broker_metadata) do
{
'labels' => {
'service_engine_version' => 'postgresql 16.6'
},
'attributes' => {
'attr_key' => 'attr_value'
}
}
end

let(:update_response) do
{
dashboard_url: updated_dashboard_url,
broker_provided_metadata: broker_metadata,
last_operation: {
type: 'update',
state: 'succeeded'
}
}
end

it 'saves the broker_provided_metadata to the instance' do
action.update(accepts_incomplete: true)

instance = original_instance.reload
expect(instance.broker_provided_metadata).to eq(broker_metadata)
end
end

context 'when broker does not return broker_provided_metadata' do
it 'does not modify existing broker_provided_metadata' do
original_instance.update(broker_provided_metadata: { 'old' => 'metadata' })

action.update(accepts_incomplete: true)

instance = original_instance.reload
expect(instance.broker_provided_metadata).to eq({ 'old' => 'metadata' })
end
end

it 'logs an audit event' do
action.update(accepts_incomplete: true)

Expand Down Expand Up @@ -1219,6 +1261,36 @@ module V3
)
end

context 'when broker returns broker_provided_metadata during async update' do
let(:broker_metadata) do
{
'labels' => {
'version' => '17.2'
}
}
end

let(:update_response) do
{
dashboard_url: updated_dashboard_url,
broker_provided_metadata: broker_metadata,
last_operation: {
type: 'update',
state: 'in progress',
description: 'some description',
broker_provided_operation: broker_provided_operation
}
}
end

it 'saves the broker_provided_metadata to the instance' do
action.update(accepts_incomplete: true)

instance = original_instance.reload
expect(instance.broker_provided_metadata).to eq(broker_metadata)
end
end

context 'when update does not return dashboard_url' do
let(:update_response) do
{
Expand Down
Loading