Spring Cloud Open Service Broker is a framework for building Spring Boot applications that implement the Open Service Broker API.
Introduction
The Open Service Broker API defines an HTTP interface between the services marketplace of a platform and a service broker.
Service brokers are responsible for:
-
Advertising a catalog of their service offerings and plans
-
Provisioning (creating or updating) service instances
-
Creating bindings between a service instance and a client application
-
Deleting bindings between a service instance and a client application
-
Deprovisioning (deleting) service instances
The Spring Cloud Open Service Broker project provides the scaffolding for an Open Service Broker API-compliant service broker by implementing the required Spring web controllers, domain objects and configuration. Service broker authors can simply provide Spring beans that implement the appropriate interfaces.
Getting started
See the Spring Boot documentation for getting started building a Spring Boot application.
Most service broker applications will implement API or web UI endpoints beyond the Open Service Broker API endpoints. These additional endpoints might provide information about the application, provide a dashboard UI, or provide controls over application behavior. Developers can choose to implement these additional endpoints using Spring MVC or Spring WebFlux.
The choice of Spring web framework does not affect the behavior of the Open Service Broker API endpoints, but the auto-configuration of the project depends on which web framework the service broker application uses.
Two Spring Boot starter dependencies are provided, reflecting the choice of Spring web framework.
Advertising Services
The service broker catalog provides a set of metadata describing the available services
along with attributes such as cost and capabilities. The catalog is made available to the platform’s services
marketplace via the service broker /v2/catalog
endpoint.
The service broker can either provide a Spring bean of type Catalog or implement the service CatalogService.
Providing a Catalog Bean
The service broker catalog can be exposed by creating a Spring bean and contribute it to the Spring
application context. This can be done in a Spring @Configuration
class as in this example:
package com.example.servicebroker;
import org.springframework.cloud.servicebroker.model.catalog.Catalog;
import org.springframework.cloud.servicebroker.model.catalog.Plan;
import org.springframework.cloud.servicebroker.model.catalog.ServiceDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ExampleCatalogConfiguration {
@Bean
public Catalog catalog() {
Plan plan = Plan.builder()
.id("simple-plan")
.name("standard")
.description("A simple plan")
.free(true)
.build();
ServiceDefinition serviceDefinition = ServiceDefinition.builder()
.id("example-service")
.name("example")
.description("A simple example")
.bindable(true)
.tags("example", "tags")
.plans(plan)
.build();
return Catalog.builder()
.serviceDefinitions(serviceDefinition)
.build();
}
}
Providing a Catalog Using Properties
A catalog may be configured with Spring Boot externalized configuration within a Java properties file or YAML file. The catalog is parsed and made available as a Catalog
bean during auto-configuration.
# Example Spring Boot YAML configuration
spring:
cloud:
openservicebroker:
catalog:
services:
- id: example-service
name: example
description: A simple example
bindable: true
tags:
- example
- tags
plans:
- id: simple-plan
name: standard
description: A simple plan
# Example Spring Boot properties configuration
spring.cloud.openservicebroker.catalog.services[0].id=example-service
spring.cloud.openservicebroker.catalog.services[0].name=example
spring.cloud.openservicebroker.catalog.services[0].description=A simple example
spring.cloud.openservicebroker.catalog.services[0].bindable=true
spring.cloud.openservicebroker.catalog.services[0].tags[0]=example
spring.cloud.openservicebroker.catalog.services[0].tags[1]=tags
spring.cloud.openservicebroker.catalog.services[0].plans[0].id=simple-plan
spring.cloud.openservicebroker.catalog.services[0].plans[0].name=standard
spring.cloud.openservicebroker.catalog.services[0].plans[0].description=A simple plan
Implementing a Catalog Service
A service broker can take more control over the catalog by implementing the CatalogService
interface.
This might be required if some details of the catalog metadata need to be read from the environment or from an external data source.
package com.example.servicebroker;
import org.springframework.cloud.servicebroker.model.catalog.Catalog;
import org.springframework.cloud.servicebroker.model.catalog.Plan;
import org.springframework.cloud.servicebroker.model.catalog.ServiceDefinition;
import org.springframework.cloud.servicebroker.service.CatalogService;
import org.springframework.stereotype.Service;
@Service
public class ExampleCatalogService implements CatalogService {
@Override
public Catalog getCatalog() {
return Catalog.builder()
.serviceDefinitions(getServiceDefinition("example-service"))
.build();
}
@Override
public ServiceDefinition getServiceDefinition(String serviceId) {
return ServiceDefinition.builder()
.id(serviceId)
.name("example")
.description("A simple example")
.bindable(true)
.tags("example", "tags")
.plans(getPlan())
.build();
}
private Plan getPlan() {
return Plan.builder()
.id("simple-plan")
.name("standard")
.description("A simple plan")
.free(true)
.build();
}
}
Service Instances
Service brokers are responsible for provisioning the services advertised in their catalog and managing their lifecycle in the underlying cloud platform. The services created by the broker are referred to as service instances.
Service brokers must implement the ServiceInstanceService
interface and provide implementations of the required methods of that interface. Each method receives a single
Java object parameter containing all details of the request from the platform, and returns a Java
object value providing details of the operation to the platform.
The service instance create, update, and delete operations can be performed synchronously or asynchronously.
-
When a service broker creates, updates, or deletes a service instance synchronously, the appropriate interface method should block and only return a response to the platform when the operation completes successfully or when a failure occurs.
-
When performing an operation asynchronously, the service broker can return a response to the platform before the operation is complete and indicate in the response that the operation is in progress. The platform will poll the service broker to get the status of the operation when an asynchronous operation is indicated.
Service Instance Creation
An implementation of the createServiceInstance() method must be provided by the service broker.
Service brokers will typically provision a resource in the platform or in another system when creating a service instance. Service brokers are responsible for keeping track of any resources associated with a service instance for future retrieval, updating, or deletion.
Service Instance Updating
An implementation of the
updateServiceInstance()
method must be provided by the service broker if the plan_updateable
field is set to true
in the services
catalog. Otherwise, this method will never be called by the platform and the default implementation in the interface
can be used.
Services brokers can modify the configuration of an existing resource when updating a service instance, or deploying a new resource.
Service Instance Deletion
An implementation of the deleteServiceInstance() method must be provided by the service broker.
Any resources provisioned in the create operation should be deprovisioned by the delete operation.
Service Instance Operation Status Retrieval
An implementation of the getLastOperation() method must be provided by the service broker if any create, update, or delete operation can return an asynchronous "operation in progress" response to the platform. Otherwise, this method will never be called by the platform and the default implementation in the interface can be used.
The platform will poll this method of the service broker for a service instances that has an asynchronous operation in progress until the service broker indicates that the operation has completed successfully or a failure has occurred.
Service Instance Retrieval
An implementation of the
getServiceInstance()
method must be provided by the service broker if the instances_retrievable
field is set to true
in the
services catalog. Otherwise, this method will never be called by the platform and the default implementation
in the interface can be used.
Service brokers are responsible for maintaining any service instance state necessary to support the retrieval operation.
Example Implementation
package com.example.servicebroker;
import org.springframework.cloud.servicebroker.model.instance.CreateServiceInstanceRequest;
import org.springframework.cloud.servicebroker.model.instance.CreateServiceInstanceResponse;
import org.springframework.cloud.servicebroker.model.instance.DeleteServiceInstanceRequest;
import org.springframework.cloud.servicebroker.model.instance.DeleteServiceInstanceResponse;
import org.springframework.cloud.servicebroker.model.instance.GetLastServiceOperationRequest;
import org.springframework.cloud.servicebroker.model.instance.GetLastServiceOperationResponse;
import org.springframework.cloud.servicebroker.model.instance.GetServiceInstanceRequest;
import org.springframework.cloud.servicebroker.model.instance.GetServiceInstanceResponse;
import org.springframework.cloud.servicebroker.model.instance.OperationState;
import org.springframework.cloud.servicebroker.model.instance.UpdateServiceInstanceRequest;
import org.springframework.cloud.servicebroker.model.instance.UpdateServiceInstanceResponse;
import org.springframework.cloud.servicebroker.service.ServiceInstanceService;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class ExampleServiceInstanceService implements ServiceInstanceService {
@Override
public CreateServiceInstanceResponse createServiceInstance(CreateServiceInstanceRequest request) {
String serviceInstanceId = request.getServiceInstanceId();
String planId = request.getPlanId();
Map<String, Object> parameters = request.getParameters();
//
// perform the steps necessary to initiate the asynchronous
// provisioning of all necessary resources
//
String dashboardUrl = new String(/* construct a dashboard URL */);
return CreateServiceInstanceResponse.builder()
.dashboardUrl(dashboardUrl)
.async(true)
.build();
}
@Override
public UpdateServiceInstanceResponse updateServiceInstance(UpdateServiceInstanceRequest request) {
String serviceInstanceId = request.getServiceInstanceId();
String planId = request.getPlanId();
String previousPlan = request.getPreviousValues().getPlanId();
Map<String, Object> parameters = request.getParameters();
//
// perform the steps necessary to initiate the asynchronous
// updating of all necessary resources
//
return UpdateServiceInstanceResponse.builder()
.async(true)
.build();
}
@Override
public DeleteServiceInstanceResponse deleteServiceInstance(DeleteServiceInstanceRequest request) {
String serviceInstanceId = request.getServiceInstanceId();
String planId = request.getPlanId();
//
// perform the steps necessary to initiate the asynchronous
// deletion of all provisioned resources
//
return DeleteServiceInstanceResponse.builder()
.async(true)
.build();
}
@Override
public GetServiceInstanceResponse getServiceInstance(GetServiceInstanceRequest request) {
String serviceInstanceId = request.getServiceInstanceId();
//
// retrieve the details of the specified service instance
//
String dashboardUrl = new String(/* retrieve dashboard URL */);
return GetServiceInstanceResponse.builder()
.dashboardUrl(dashboardUrl)
.build();
}
@Override
public GetLastServiceOperationResponse getLastOperation(GetLastServiceOperationRequest request) {
String serviceInstanceId = request.getServiceInstanceId();
//
// determine the status of the operation in progress
//
OperationState state = OperationState.SUCCEEDED;
return GetLastServiceOperationResponse.builder()
.operationState(state)
.build();
}
}
Service Bindings
Service brokers can provide information to a consumer of a service instance via a service binding. Service bindings are often used to expose credentials for service instance resources to an application.
An implementation of the ServiceInstanceBindingService
interface must be provided by the service broker if the bindable
field is set to true
for any plan in the
service catalog. Otherwise, the binding methods of the service broker will not be called by the platform, and
a default implementation of this interface can be used. Each method receives a single Java object parameter
containing all details of the request from the platform, and returns a Java object value providing details of
the operation to the platform.
Service Binding Creation
An implementation of the createServiceInstanceBinding() method must be provided by the service broker.
Two types of bindings are supported:
-
App bindings can be used to provide credentials, log drains, and volume services to applications.
-
Route bindings can be used to provide routes for the platform to use when proxying requests.
The response from this method allows one of two Java object types to be returned, reflecting the two types of bindings supported.
Service brokers can generate one set of credentials for all binding requests, or provide unique credentials for each binding request.
Service Binding Deletion
An implementation of the deleteServiceInstanceBinding() method must be provided by the service broker.
Any credentials provisioned in the create operation should be deprovisioned by the delete operation.
Service Binding Retrieval
An implementation of the
getServiceInstanceBinding()
method must be provided by the service broker if the bindings_retrievable
field is set to true
in the
services catalog. Otherwise, this method will never be called by the platform and the default implementation
in the interface can be used.
Service brokers are responsible for maintaining any service binding state necessary to support the retrieval operation.
Example Implementation
package com.example.servicebroker;
import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceAppBindingResponse;
import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceBindingRequest;
import org.springframework.cloud.servicebroker.model.binding.CreateServiceInstanceBindingResponse;
import org.springframework.cloud.servicebroker.model.binding.DeleteServiceInstanceBindingRequest;
import org.springframework.cloud.servicebroker.model.binding.GetServiceInstanceAppBindingResponse;
import org.springframework.cloud.servicebroker.model.binding.GetServiceInstanceBindingRequest;
import org.springframework.cloud.servicebroker.model.binding.GetServiceInstanceBindingResponse;
import org.springframework.cloud.servicebroker.service.ServiceInstanceBindingService;
import org.springframework.stereotype.Service;
@Service
public class ExampleServiceBindingService implements ServiceInstanceBindingService {
@Override
public CreateServiceInstanceBindingResponse createServiceInstanceBinding(CreateServiceInstanceBindingRequest request) {
String serviceInstanceId = request.getServiceInstanceId();
String bindingId = request.getBindingId();
//
// create credentials and store for later retrieval
//
String url = new String(/* build a URL to access the service instance */);
String bindingUsername = new String(/* create a user */);
String bindingPassword = new String(/* create a password */);
return CreateServiceInstanceAppBindingResponse.builder()
.credentials("url", url)
.credentials("username", bindingUsername)
.credentials("password", bindingPassword)
.bindingExisted(false)
.build();
}
@Override
public void deleteServiceInstanceBinding(DeleteServiceInstanceBindingRequest request) {
String serviceInstanceId = request.getServiceInstanceId();
String bindingId = request.getBindingId();
//
// delete any binding-specific credentials
//
}
@Override
public GetServiceInstanceBindingResponse getServiceInstanceBinding(GetServiceInstanceBindingRequest request) {
String serviceInstanceId = request.getServiceInstanceId();
String bindingId = request.getBindingId();
//
// retrieve the details of the specified service binding
//
String url = new String(/* retrieved URL */);
String bindingUsername = new String(/* retrieved user */);
String bindingPassword = new String(/* retrieved password */);
return GetServiceInstanceAppBindingResponse.builder()
.credentials("username", bindingUsername)
.credentials("password", bindingPassword)
.credentials("url", url)
.build();
}
}
Service Broker Security
Authentication and authorization of service broker endpoints is not specified in the Open Service Broker API specification, but some platforms require or allow basic authentication or OAuth2 credentials to be provided when a service broker is registered to the platform.
The Spring Cloud Open Service Broker project does not implement any security configuration.
Service broker application endpoints can be secured using Spring Security
and Spring Boot security configuration
by applying security to application endpoints with the path-matching pattern /v2/**
.
Example Configuration
package com.example.servicebroker;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
@EnableWebSecurity
public class ExampleSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/v2/**").hasRole("ADMIN")
.and()
.httpBasic();
}
@Bean
public InMemoryUserDetailsManager userDetailsService() {
return new InMemoryUserDetailsManager(adminUser());
}
private UserDetails adminUser() {
return User
.withUsername("admin")
.password("supersecret")
.roles("ADMIN")
.build();
}
}
Example Service Broker Application
The Bookstore Service Broker project implements a simple service broker that adheres to the Open Service Broker API using the Spring Cloud Open Service Broker framework. It can be deployed to either Cloud Foundry or Kubernetes, and can be registered as a service broker to either platform. View the project README for more information.