5. Service Bindings

Service brokers can provide information to a consumer of a service instance through a service binding. Service bindings are often used to expose credentials for service instance resources to an application.spring-doc.cn

If the bindable field is set to true for any plan in the service catalog, the service broker must provide an implementation of the ServiceInstanceBindingService interface. Otherwise, the binding methods of the service broker are not called by the platform, and a default implementation of this interface can be used. Each method receives a single Java object parameter that contains all the details of the request from the platform and returns a Java object value that provides details of the operation to the platform.spring-doc.cn

The service binding create and delete operations can be performed synchronously or asynchronously.spring-doc.cn

  • When a service broker creates or deletes a service binding synchronously, the appropriate interface method should block and return a response to the platform only when the operation completes successfully or when a failure occurs.spring-doc.cn

  • 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 polls the service broker to get the status of the operation when an asynchronous operation is indicated.spring-doc.cn

5.1. Service Binding Creation

The service broker must provide an implementation of the createServiceInstanceBinding() method.spring-doc.cn

Two types of bindings are supported:spring-doc.cn

  • App bindings can be used to provide credentials, log drains, and volume services to applications.spring-doc.cn

  • Route bindings can be used to provide routes for the platform to use when proxying requests.spring-doc.cn

The response from this method lets one of two Java object types be returned, reflecting the two types of supported bindings.spring-doc.cn

Service brokers can generate one set of credentials for all binding requests or provide unique credentials for each binding request.spring-doc.cn

5.1.1. Event Registry

You can use events to further customize service binding creation. To do so:spring-doc.cn

  1. Autowire the CreateServiceInstanceBindingEventFlowRegistry bean.spring-doc.cn

  2. Use one of the addInitializationFlow(), addCompletionFlow(), or addErrorFlow() methods to register custom reactive flows to run during the various stages of creating a service binding.spring-doc.cn

5.2. Service Binding Deletion

The service broker must provide an implementation of the deleteServiceInstanceBinding() method.spring-doc.cn

Any credentials provisioned in the create operation should be de-provisioned by the delete operation.spring-doc.cn

5.2.1. Event Registry

You can use events to further customize service binding deletion. To do so: . Autowire the DeleteServiceInstanceBindingEventFlowRegistry bean.spring-doc.cn

  1. Use one of the addInitializationFlow(), addCompletionFlow(), or addErrorFlow() methods to register custom reactive flows to run during the various stages of deleting a service binding.spring-doc.cn

5.3. Service Binding Operation Status Retrieval

If any create or delete operation can return an asynchronous “operation in progress” response to the platform, the service broker must provide an implementation of the getLastOperation() method. Otherwise, this method is never called by the platform, and the default implementation in the interface can be used.spring-doc.cn

The platform polls this method of the service broker for a service instance that has an asynchronous operation in progress until the service broker indicates that the operation has completed successfully or a failure has occurred.spring-doc.cn

5.3.1. Event Registry

You can use events to further customize service binding last operation requests. To do so: . Autowire the AsyncOperationServiceInstanceBindingEventFlowRegistry bean.spring-doc.cn

  1. Use one of the addInitializationFlow(), addCompletionFlow() or addErrorFlow() methods to register custom reactive flows to run during the various stages of last operation retrieval.spring-doc.cn

5.4. Service Binding Retrieval

If the bindings_retrievable field is set to true in the services catalog, the service catalog must provide an implementation of the getServiceInstanceBinding() method. Otherwise, this method is never called by the platform, and the default implementation in the interface can be used.spring-doc.cn

Service brokers are responsible for maintaining any service binding state necessary to support the retrieval operation.spring-doc.cn

5.5. Example Implementation

The following example implements a service binding:spring-doc.cn

package com.example.servicebroker;

import reactor.core.publisher.Mono;

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.DeleteServiceInstanceBindingResponse;
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 Mono<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 */);

		CreateServiceInstanceBindingResponse response = CreateServiceInstanceAppBindingResponse.builder()
				.credentials("url", url)
				.credentials("username", bindingUsername)
				.credentials("password", bindingPassword)
				.bindingExisted(false)
				.async(true)
				.build();

		return Mono.just(response);
	}

	@Override
	public Mono<DeleteServiceInstanceBindingResponse> deleteServiceInstanceBinding(DeleteServiceInstanceBindingRequest request) {
		String serviceInstanceId = request.getServiceInstanceId();
		String bindingId = request.getBindingId();

		//
		// delete any binding-specific credentials
		//

		return Mono.just(DeleteServiceInstanceBindingResponse.builder()
				.async(true)
				.build());
	}

	@Override
	public Mono<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 */);

		GetServiceInstanceBindingResponse response = GetServiceInstanceAppBindingResponse.builder()
				.credentials("username", bindingUsername)
				.credentials("password", bindingPassword)
				.credentials("url", url)
				.build();

		return Mono.just(response);
	}
}

5.6. Example Event Flow Configuration

There are multiple ways to configure service binding event flows. One option is to autowire one or more registries and interact with the registry directly. Another option is to define beans for specific flows. These beans are automatically identified and added to the appropriate registry. A final option is to declare a new registry bean. However, be aware that defining a new registry bean overrides the provided auto-configuration.spring-doc.cn

5.6.1. Option 1: Autowire Registries

The following example configures service binding event flows:spring-doc.cn

package com.example.servicebroker;

import reactor.core.publisher.Mono;

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.DeleteServiceInstanceBindingResponse;
import org.springframework.cloud.servicebroker.model.binding.GetLastServiceBindingOperationRequest;
import org.springframework.cloud.servicebroker.model.binding.GetLastServiceBindingOperationResponse;
import org.springframework.cloud.servicebroker.service.events.AsyncOperationServiceInstanceBindingEventFlowRegistry;
import org.springframework.cloud.servicebroker.service.events.CreateServiceInstanceBindingEventFlowRegistry;
import org.springframework.cloud.servicebroker.service.events.DeleteServiceInstanceBindingEventFlowRegistry;
import org.springframework.cloud.servicebroker.service.events.flows.AsyncOperationServiceInstanceBindingCompletionFlow;
import org.springframework.cloud.servicebroker.service.events.flows.AsyncOperationServiceInstanceBindingErrorFlow;
import org.springframework.cloud.servicebroker.service.events.flows.AsyncOperationServiceInstanceBindingInitializationFlow;
import org.springframework.cloud.servicebroker.service.events.flows.CreateServiceInstanceBindingCompletionFlow;
import org.springframework.cloud.servicebroker.service.events.flows.CreateServiceInstanceBindingErrorFlow;
import org.springframework.cloud.servicebroker.service.events.flows.CreateServiceInstanceBindingInitializationFlow;
import org.springframework.cloud.servicebroker.service.events.flows.DeleteServiceInstanceBindingCompletionFlow;
import org.springframework.cloud.servicebroker.service.events.flows.DeleteServiceInstanceBindingErrorFlow;
import org.springframework.cloud.servicebroker.service.events.flows.DeleteServiceInstanceBindingInitializationFlow;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ExampleServiceBindingEventFlowsConfiguration {

	private final CreateServiceInstanceBindingEventFlowRegistry createRegistry;

	private final DeleteServiceInstanceBindingEventFlowRegistry deleteRegistry;

	private final AsyncOperationServiceInstanceBindingEventFlowRegistry asyncRegistry;

	public ExampleServiceBindingEventFlowsConfiguration(
			CreateServiceInstanceBindingEventFlowRegistry createRegistry,
			DeleteServiceInstanceBindingEventFlowRegistry deleteRegistry,
			AsyncOperationServiceInstanceBindingEventFlowRegistry asyncRegistry) {
		this.createRegistry = createRegistry;
		this.deleteRegistry = deleteRegistry;
		this.asyncRegistry = asyncRegistry;

		prepareCreateEventFlows()
				.then(prepareDeleteEventFlows())
				.then(prepareLastOperationEventFlows())
				.subscribe();
	}

	private Mono<Void> prepareCreateEventFlows() {
		return Mono.just(createRegistry)
				.map(registry -> registry.addInitializationFlow(new CreateServiceInstanceBindingInitializationFlow() {
					@Override
					public Mono<Void> initialize(CreateServiceInstanceBindingRequest request) {
						//
						// do something before the instance is created
						//
						return Mono.empty();
					}
				})
						.then(registry.addCompletionFlow(new CreateServiceInstanceBindingCompletionFlow() {
							@Override
							public Mono<Void> complete(CreateServiceInstanceBindingRequest request,
													   CreateServiceInstanceBindingResponse response) {
								//
								// do something after the instance is created
								//
								return Mono.empty();
							}
						}))
						.then(registry.addErrorFlow(new CreateServiceInstanceBindingErrorFlow() {
							@Override
							public Mono<Void> error(CreateServiceInstanceBindingRequest request, Throwable t) {
								//
								// do something if an error occurs while creating an instance
								//
								return Mono.empty();
							}
						})))
				.then();
	}

	private Mono<Void> prepareDeleteEventFlows() {
		return Mono.just(deleteRegistry)
				.map(registry -> registry.addInitializationFlow(new DeleteServiceInstanceBindingInitializationFlow() {
					@Override
					public Mono<Void> initialize(DeleteServiceInstanceBindingRequest request) {
						//
						// do something before the instance is deleted
						//
						return Mono.empty();
					}
				})
						.then(registry.addCompletionFlow(new DeleteServiceInstanceBindingCompletionFlow() {
							@Override
							public Mono<Void> complete(DeleteServiceInstanceBindingRequest request,
													   DeleteServiceInstanceBindingResponse response) {
								//
								// do something after the instance is deleted
								//
								return Mono.empty();
							}
						}))
						.then(registry.addErrorFlow(new DeleteServiceInstanceBindingErrorFlow() {
							@Override
							public Mono<Void> error(DeleteServiceInstanceBindingRequest request, Throwable t) {
								//
								// do something if an error occurs while deleting an instance
								//
								return Mono.empty();
							}
						})))
				.then();
	}

	private Mono<Void> prepareLastOperationEventFlows() {
		return Mono.just(asyncRegistry)
				.map(registry -> registry.addInitializationFlow(new AsyncOperationServiceInstanceBindingInitializationFlow() {
					@Override
					public Mono<Void> initialize(GetLastServiceBindingOperationRequest request) {
						//
						// do something before the instance is deleted
						//
						return Mono.empty();
					}
				})
						.then(registry.addCompletionFlow(new AsyncOperationServiceInstanceBindingCompletionFlow() {
							@Override
							public Mono<Void> complete(GetLastServiceBindingOperationRequest request,
									GetLastServiceBindingOperationResponse response) {
								//
								// do something after the instance is deleted
								//
								return Mono.empty();
							}
						}))
						.then(registry.addErrorFlow(new AsyncOperationServiceInstanceBindingErrorFlow() {
							public Mono<Void> error(GetLastServiceBindingOperationRequest request, Throwable t) {
								//
								// do something if an error occurs while deleting an instance
								//
								return Mono.empty();
							}
						})))
				.then();
	}

}

5.6.2. Option 2: Event Flow Beans

Optionally, you can configure beans for the individual flows, as follows:spring-doc.cn

package com.example.servicebroker;

import reactor.core.publisher.Mono;

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.DeleteServiceInstanceBindingResponse;
import org.springframework.cloud.servicebroker.model.binding.GetLastServiceBindingOperationRequest;
import org.springframework.cloud.servicebroker.model.binding.GetLastServiceBindingOperationResponse;
import org.springframework.cloud.servicebroker.service.events.flows.AsyncOperationServiceInstanceBindingCompletionFlow;
import org.springframework.cloud.servicebroker.service.events.flows.AsyncOperationServiceInstanceBindingErrorFlow;
import org.springframework.cloud.servicebroker.service.events.flows.AsyncOperationServiceInstanceBindingInitializationFlow;
import org.springframework.cloud.servicebroker.service.events.flows.CreateServiceInstanceBindingCompletionFlow;
import org.springframework.cloud.servicebroker.service.events.flows.CreateServiceInstanceBindingErrorFlow;
import org.springframework.cloud.servicebroker.service.events.flows.CreateServiceInstanceBindingInitializationFlow;
import org.springframework.cloud.servicebroker.service.events.flows.DeleteServiceInstanceBindingCompletionFlow;
import org.springframework.cloud.servicebroker.service.events.flows.DeleteServiceInstanceBindingErrorFlow;
import org.springframework.cloud.servicebroker.service.events.flows.DeleteServiceInstanceBindingInitializationFlow;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ExampleServiceBindingEventFlowsConfiguration2 {

	//
	// Create Service Instance Binding flows
	//

	@Bean
	public CreateServiceInstanceBindingInitializationFlow createServiceInstanceBindingInitializationFlow() {
		return new CreateServiceInstanceBindingInitializationFlow() {
			@Override
			public Mono<Void> initialize(CreateServiceInstanceBindingRequest request) {
				//
				// do something before the service instance binding completes
				//
				return Mono.empty();
			}
		};
	}

	@Bean
	public CreateServiceInstanceBindingCompletionFlow createServiceInstanceBindingCompletionFlow() {
		return new CreateServiceInstanceBindingCompletionFlow() {
			@Override
			public Mono<Void> complete(CreateServiceInstanceBindingRequest request,
					CreateServiceInstanceBindingResponse response) {
				//
				// do something after the service instance binding completes
				//
				return Mono.empty();
			}
		};
	}

	@Bean
	public CreateServiceInstanceBindingErrorFlow createServiceInstanceBindingErrorFlow() {
		return new CreateServiceInstanceBindingErrorFlow() {
			@Override
			public Mono<Void> error(CreateServiceInstanceBindingRequest request, Throwable t) {
				//
				// do something if an error occurs while creating a service instance binding
				//
				return Mono.empty();
			}
		};
	}

	//
	// Delete Service Instance Binding flows
	//

	@Bean
	public DeleteServiceInstanceBindingInitializationFlow deleteServiceInstanceBindingInitializationFlow() {
		return new DeleteServiceInstanceBindingInitializationFlow() {
			@Override
			public Mono<Void> initialize(DeleteServiceInstanceBindingRequest request) {
				//
				// do something before the service instance binding is deleted
				//
				return Mono.empty();
			}
		};
	}

	@Bean
	public DeleteServiceInstanceBindingCompletionFlow deleteServiceInstanceBindingCompletionFlow() {
		return new DeleteServiceInstanceBindingCompletionFlow() {
			@Override
			public Mono<Void> complete(DeleteServiceInstanceBindingRequest request,
					DeleteServiceInstanceBindingResponse response) {
				//
				// do something after the service instance binding is deleted
				//
				return Mono.empty();
			}
		};
	}

	@Bean
	public DeleteServiceInstanceBindingErrorFlow deleteServiceInstanceBindingErrorFlow() {
		return new DeleteServiceInstanceBindingErrorFlow() {
			@Override
			public Mono<Void> error(DeleteServiceInstanceBindingRequest request, Throwable t) {
				//
				// do something if an error occurs while deleting a service instance binding
				//
				return Mono.empty();
			}
		};
	}

	//
	// Get Last Service Instance Binding Operation flows
	//

	@Bean
	public AsyncOperationServiceInstanceBindingInitializationFlow getLastOperationServiceInstanceBindingInitializationFlow() {
		return new AsyncOperationServiceInstanceBindingInitializationFlow() {
			@Override
			public Mono<Void> initialize(GetLastServiceBindingOperationRequest request) {
				//
				// do something before getting the last operation
				//
				return Mono.empty();
			}
		};
	}

	@Bean
	public AsyncOperationServiceInstanceBindingCompletionFlow getLastOperationServiceInstanceBindingCompletionFlow() {
		return new AsyncOperationServiceInstanceBindingCompletionFlow() {
			@Override
			public Mono<Void> complete(GetLastServiceBindingOperationRequest request,
					GetLastServiceBindingOperationResponse response) {
				//
				// do something after getting the last operation
				//
				return Mono.empty();
			}
		};
	}

	@Bean
	public AsyncOperationServiceInstanceBindingErrorFlow getLastOperationServiceInstanceBindingErrorFlow() {
		return new AsyncOperationServiceInstanceBindingErrorFlow() {
			@Override
			public Mono<Void> error(GetLastServiceBindingOperationRequest request, Throwable t) {
				//
				// do something if an error occurs while getting the last operation
				//
				return Mono.empty();
			}
		};
	}
}