4. Service Instances

You can configure the details of services, including applications to deploy, application deployment details, and backing services to create, in App Broker configuration properties. These properties are generally under spring.cloud.appbroker.services.spring-doc.cn

4.1. Configuring App Deployment

Deployment details for a backing application can be configured statically in the service broker’s application configuration and dynamically by using service instance parameters and customization implementations.spring-doc.cn

4.1.1. Static Customization

You can statically configure backing application deployment details in the application configuration for the service broker by using properties under spring.cloud.appbroker.spring-doc.cn

Properties Configuration

You can specify application deployment properties in configuration. These properties can have default values and service-specific values.spring-doc.cn

For Cloud Foundry, you can set default values for all services under spring.cloud.appbroker.deployer.cloudfoundry.*, as follows:spring-doc.cn

spring:
  cloud:
    appbroker:
      deployer:
        cloudfoundry:
          properties:
            memory: 1G
            health-check: http
            health-check-http-endpoint: /health
            health-check-timeout: 180
            api-polling-timeout: 300

The following table lists properties that can be set for all or for deployments:spring-doc.cn

Property Description Default

api-polling-timeoutspring-doc.cn

The timeout for polled async CF API calls, in seconds.spring-doc.cn

300spring-doc.cn

buildpackspring-doc.cn

The buildpack to use for deploying the application.spring-doc.cn

buildpacksspring-doc.cn

The list of buildpacks to use for deploying the application.spring-doc.cn

domainspring-doc.cn

The domain to use when mapping routes for applications.spring-doc.cn

domainsspring-doc.cn

The list of domain to use when mapping routes for applications.spring-doc.cn

health-checkspring-doc.cn

The type of health check to perform on deployed application, if not overridden per-app.spring-doc.cn

PORTspring-doc.cn

health-check-http-endpointspring-doc.cn

The path that the http health check will use.spring-doc.cn

/healthspring-doc.cn

health-check-timeoutspring-doc.cn

The timeout value for health checks in seconds.spring-doc.cn

120spring-doc.cn

javaOptsspring-doc.cn

The javaOpts to use for deploying the application.spring-doc.cn

memoryspring-doc.cn

The memory to use for deploying the application.spring-doc.cn

no-routespring-doc.cn

If the application does not need a routespring-doc.cn

falsespring-doc.cn

route-pathspring-doc.cn

The route-path to use for deploying the application.spring-doc.cn

routesspring-doc.cn

The routes to use for deploying the application.spring-doc.cn

stackspring-doc.cn

The stack to use for deploying the application.spring-doc.cn

You can set overriding values for a specific service in the service’s configuration under spring.cloud.appbroker.services.*, as follows:spring-doc.cn

spring:
  cloud:
    appbroker:
      services:
        - service-name: example
          plan-name: standard
          apps:
            - name: example-service-app1
              path: classpath:app1.jar
              properties:
                memory: 2G
                count: 2
                no-route: true

The following table lists properties that can be set for all or for specific application deployments:spring-doc.cn

Property Description Default

countspring-doc.cn

memoryspring-doc.cn

diskspring-doc.cn

hostspring-doc.cn

targetspring-doc.cn

domainspring-doc.cn

The domain to use when mapping routes for the deployed application. domain and host are mutually exclusive with routes.spring-doc.cn

routesspring-doc.cn

The routes to which to bind the deployed application.spring-doc.cn

health-checkspring-doc.cn

The type of health check to perform on the deployed application.spring-doc.cn

PORTspring-doc.cn

health-check-http-endpointspring-doc.cn

The path used by the HTTP health check.spring-doc.cn

/healthspring-doc.cn

health-check-timeoutspring-doc.cn

The timeout value used by the health check, in seconds.spring-doc.cn

120spring-doc.cn

api-timeoutspring-doc.cn

The timeout value used for blocking API calls, in seconds.spring-doc.cn

360spring-doc.cn

api-polling-timeoutspring-doc.cn

The timeout value used for polling asynchronous API endpoints (for example, CF create/update/delete service instance), in seconds.spring-doc.cn

300spring-doc.cn

status-timeoutspring-doc.cn

staging-timeoutspring-doc.cn

startup-timeoutspring-doc.cn

delete-routesspring-doc.cn

Whether to delete routes when un-deploying an application.spring-doc.cn

truespring-doc.cn

java-optsspring-doc.cn

use-spring-application-jsonspring-doc.cn

Decide to write environment variables into SPRING_APPLICATION_JSON or write them as raw environment variablesspring-doc.cn

truespring-doc.cn

Environment Configuration

You can provide environment variables to be set on a deployed application. Environment variables are set by using properties under environment for the deployed application, as follows:spring-doc.cn

spring:
  cloud:
    appbroker:
      services:
        - service-name: example
          plan-name: standard
          apps:
            - name: example-service-app1
              path: classpath:app1.jar
              environment:
                logging.level.spring.security: DEBUG
                spring.profiles.active: cloud
Service Configuration

You can configure services that should be bound to a deployed application. Services are configured by using properties under services for the deployed application, as follows:spring-doc.cn

spring:
  cloud:
    appbroker:
      services:
        - service-name: example
          plan-name: standard
          apps:
            - name: example-service-app1
              path: classpath:app1.jar
              services:
              - service-instance-name: example-db
          services:
          - service-instance-name: example-db
            name: mysql
            plan: small
            parameters:
            param-key: param-value

4.1.2. Dynamic Customization

To customize the backing application deployment by using information that is only available when performing a service broker operation or that must be generated per service instance, you can use the service broker application configuration to provide the names of customization implementations.spring-doc.cn

Backing Application Target

You can configure the target location for backing applications (in Cloud Foundry, an org and space) using a target specification, as in the following example:spring-doc.cn

spring:
  cloud:
    appbroker:
      services:
        - service-name: example
          plan-name: standard
          target:
          *  name: SpacePerServiceInstance*
        apps:
          apps:
            - name: example-service-app1
              path: classpath:app1.jar

By default (if you do not provide a target specification), all backing applications are deployed to the default target specified under spring.cloud.appbroker.deployer. For Cloud Foundry, this is the org named by spring.cloud.appbroker.deployer.cloudfoundry.default-org and the space named by spring.cloud.appbroker.deployer.cloudfoundry.default-space.spring-doc.cn

The SpacePerServiceInstance Target

If you use the SpacePerServiceInstance target, App Broker deploys backing applications to a unique target location that is named by using the service instance GUID provided by the platform at service instance create time. For Cloud Foundry, this target location is the org named by spring.cloud.appbroker.deployer.cloudfoundry.default-org, and a new space is created by using the service instance GUID as the space name.spring-doc.cn

The ServiceInstanceGuidSuffix Target

If you use the ServiceInstanceGuidSuffix target, App Broker deploys backing applications by using a unique name and hostname that incorporates the service instance GUID provided by the platform at service instance create time. For Cloud Foundry, the target location is the org named by spring.cloud.appbroker.deployer.cloudfoundry.default-org, the space named by spring.cloud.appbroker.deployer.cloudfoundry.default-space, and an application name as [APP-NAME]-[SI-GUID], where [APP-NAME] is the name listed for the application under spring.cloud.appbroker.services.apps and [SI-GUID] is the service instance GUID. The application also uses a hostname that incorporates the service instance GUID as a suffix, as [APP-NAME]-[SI-GUID].spring-doc.cn

Creating a Custom Target

If you want to create a custom Target, App Broker provides a flexible way to add new targets by creating a new Bean that extends from TargetFactory and implementing the create method, as follows:spring-doc.cn

public class CustomSpaceTarget extends TargetFactory<CustomSpaceTarget.Config> {

	public CustomSpaceTarget() {
		super(Config.class);
	}

	@Override
	public Target create(Config config) {
		return this::apply;
	}

	private ArtifactDetails apply(Map<String, String> properties, String name, String serviceInstanceId) {
		String space = "my-custom-space";
		properties.put(DeploymentProperties.TARGET_PROPERTY_KEY, space);

		return ArtifactDetails.builder()
			.name(name)
			.properties(properties)
			.build();
	}

	public static class Config {
	}

}

Once configured, we can specify in our service the new custom Target, as follows:spring-doc.cn

spring:
  cloud:
    appbroker:
      services:
        - service-name: example
          plan-name: standard
          target:
            name: CustomSpaceTarget
Service Instance Parameters

When a user provides parameters while creating or updating a service instance, App Broker can transform these parameters into the details of the backing app deployment by using parameters transformers. You can configure parameters transformers by using properties under parameters-transformers, as follows:spring-doc.cn

spring:
  cloud:
    appbroker:
      services:
        - service-name: example
          plan-name: standard
          apps:
            - name: example-service-app1
              path: classpath:app1.jar
              parameters-transformers:
                - name: EnvironmentMapping
                  args:
                    - include: parameter1,parameter2
                - name: PropertyMapping
                  args:
                    - include: count,memory

The named parameters-transformers refer to Java objects that have been contributed to the Spring application context. A parameters transformer can accept one or more arguments that configure its behavior and can modify any aspect of the backing application deployment (properties, environment variables, services, and so on).spring-doc.cn

The EnvironmentMapping Parameters Transformer

The EnvironmentMapping parameters transformer populates environment variables on the backing application from parameters provided when a service instance is created or updated. It supports a single argument, include, which specifies the names of parameters that are mapped to environment variables.spring-doc.cn

The PropertyMapping Parameters Transformer

The PropertyMapping parameters transformer sets deployment properties of the backing application from parameters provided when a service instance is created or updated. It supports a single argument, include, which specifies the names of deployment properties that should be recognized.spring-doc.cn

4.2. Creating a Service Instance

Spring Cloud App Broker provides the AppDeploymentCreateServiceInstanceWorkflow workflow, which handles deploying the configured backing applications and services, as illustrated in the previous sections. The service broker application can implement the CreateServiceInstanceWorkflow interface to further modify the deployment. Multiple workflows can be annotated with @Order so as to process the workflows in a specific order. Alternatively, the service broker application can implement the ServiceInstanceService interface provided by Spring Cloud Open Service Broker. See Service Instances in the Spring Cloud Open Service Broker documentation.spring-doc.cn

4.3. Updating a Service Instance

Spring Cloud App Broker provides the AppDeploymentUpdateServiceInstanceWorkflow workflow, which handles updating the configured backing applications and services, as illustrated in the previous sections. If the list of backing services is updated, the default behavior is to create and bind the new backing service instances and to unbind and delete the existing backing service instances that are no longer listed in the configuration.spring-doc.cn

The service broker application can implement the UpdateServiceInstanceWorkflow interface to further modify the deployment. Multiple workflows can be annotated with @Order so as to process the workflows in a specific order. Alternatively, the service broker application can implement the ServiceInstanceService interface provided by Spring Cloud Open Service Broker. See Service Instances in the Spring Cloud Open Service Broker documentation.spring-doc.cn

Modifying certain properties, such as disk and memory, when updating an application, may result in downtime.

4.4. Deleting a Service Instance

Spring Cloud App Broker provides the AppDeploymentDeleteServiceInstanceWorkflow workflow, which handles deleting the configured backing applications and services, as illustrated in the previous sections. The service broker application can implement the DeleteServiceInstanceWorkflow interface to further modify the deployment. Multiple workflows can be annotated with @Order so as to process the workflows in a specific order. Alternatively, the service broker application can implement the ServiceInstanceService interface provided by Spring Cloud Open Service Broker. See Service Instances in the Spring Cloud Open Service Broker documentation.spring-doc.cn

4.5. Persisting Service Instance State

Spring Cloud App Broker provides the ServiceInstanceStateRepository interface for persisting service instance state. The default implementation is InMemoryServiceInstanceStateRepository, which uses an in memory Map to save state and offers an easy getting-started experience. To use a proper database for persisting state, you can implement ServiceInstanceStateRepository in your application.spring-doc.cn

The InMemoryServiceInstanceStateRepository is provided for demonstration and testing purposes only. It is not suitable for production applications!

4.5.1. Example Implementation

The following example shows a service instance state repository implementation:spring-doc.cn

package com.example.appbroker;

import reactor.core.publisher.Mono;

import org.springframework.cloud.appbroker.state.ServiceInstanceState;
import org.springframework.cloud.appbroker.state.ServiceInstanceStateRepository;
import org.springframework.cloud.servicebroker.model.instance.OperationState;

class ExampleServiceInstanceStateRepository implements ServiceInstanceStateRepository {

	private final ServiceInstanceStateCrudRepository serviceInstanceStateCrudRepository;

	ExampleServiceInstanceStateRepository(ServiceInstanceStateCrudRepository serviceInstanceStateCrudRepository) {
		this.serviceInstanceStateCrudRepository = serviceInstanceStateCrudRepository;
	}

	@Override
	public Mono<ServiceInstanceState> saveState(String serviceInstanceId, OperationState state, String description) {
		return serviceInstanceStateCrudRepository.findByServiceInstanceId(serviceInstanceId)
				.switchIfEmpty(Mono.just(new ServiceInstance()))
				.flatMap(serviceInstance -> {
					serviceInstance.setServiceInstanceId(serviceInstanceId);
					serviceInstance.setOperationState(state);
					serviceInstance.setDescription(description);
					return Mono.just(serviceInstance);
				})
				.flatMap(serviceInstanceStateCrudRepository::save)
				.map(ExampleServiceInstanceStateRepository::toServiceInstanceState);
	}

	@Override
	public Mono<ServiceInstanceState> getState(String serviceInstanceId) {
		return serviceInstanceStateCrudRepository.findByServiceInstanceId(serviceInstanceId)
				.switchIfEmpty(Mono.error(new IllegalArgumentException("Unknown service instance ID " + serviceInstanceId)))
				.map(ExampleServiceInstanceStateRepository::toServiceInstanceState);
	}

	@Override
	public Mono<ServiceInstanceState> removeState(String serviceInstanceId) {
		return getState(serviceInstanceId)
				.doOnNext(serviceInstanceState -> serviceInstanceStateCrudRepository.deleteByServiceInstanceId(serviceInstanceId));
	}

	private static ServiceInstanceState toServiceInstanceState(ServiceInstance serviceInstance) {
		return new ServiceInstanceState(serviceInstance.getOperationState(), serviceInstance.getDescription(), null);
	}

}

One option for persisting service instance state is to use a Spring Data CrudRepository. The following example shows a ReactiveCrudRepository implementation:spring-doc.cn

package com.example.appbroker;

import reactor.core.publisher.Mono;

import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;

interface ServiceInstanceStateCrudRepository extends ReactiveCrudRepository<ServiceInstance, Long> {

	@Query("select * from service_instance where service_instance_id = :service_instance_id")
	Mono<ServiceInstance> findByServiceInstanceId(@Param("service_instance_id") String serviceInstanceId);

	@Query("delete from service_instance where service_instance_id = :service_instance_id")
	Mono<Void> deleteByServiceInstanceId(@Param("service_instance_id") String serviceInstanceId);

}

A model object is necessary for persisting data with a CrudRepository. The following example shows a ServiceInstance model:spring-doc.cn

package com.example.appbroker;

import org.springframework.cloud.servicebroker.model.instance.OperationState;
import org.springframework.data.annotation.Id;

class ServiceInstance {

	@Id
	private Long id;

	private String serviceInstanceId;

	private String description;

	private OperationState operationState;

	public ServiceInstance() {

	}

	public ServiceInstance(String serviceInstanceId, String description, OperationState operationState) {
		this.serviceInstanceId = serviceInstanceId;
		this.description = description;
		this.operationState = operationState;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getServiceInstanceId() {
		return serviceInstanceId;
	}

	public void setServiceInstanceId(String serviceInstanceId) {
		this.serviceInstanceId = serviceInstanceId;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public OperationState getOperationState() {
		return operationState;
	}

	public void setOperationState(OperationState operationState) {
		this.operationState = operationState;
	}

}