Skip to content

Commit

Permalink
Provide default implementation of ServiceInstanceBindingService for u…
Browse files Browse the repository at this point in the history
…se with service brokers that do not provide bindable service offerings.
  • Loading branch information
scottfrederick committed Feb 20, 2016
1 parent 013c259 commit bd9ad52
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 29 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ This will trigger the inclusion of the default configuration.

The Cloud Foundry service broker API has three main endpoint groupings: catalog management, service instance provisioning/deprovisioning, and service instance binding/unbinding. The broker will need to provide one Spring bean to provide the necessary functionality for each endpoint grouping.

For catalog management, the framework provides a default implementation that requires the broker to just provide an implementation of a [`Catalog` bean](src/main/java/org/springframework/cloud/servicebroker/model/Catalog.java). There is an example of this approach in the [MongoDB sample broker](https://github.com/spgreenberg/spring-boot-cf-service-broker-mongo/blob/master/src/main/java/org/springframework/cloud/servicebroker/mongodb/config/CatalogConfig.java). To override this default, provide your own bean that implements the [`CatalogService`](src/main/java/org/springframework/cloud/servicebroker/service/CatalogService.java) interface.
For catalog management, the framework provides a default implementation that requires the broker to just provide an implementation of a [`Catalog` bean](src/main/java/org/springframework/cloud/servicebroker/model/Catalog.java). There is an example of this approach in the [MongoDB sample broker](https://github.com/spring-cloud-samples/cloudfoundry-service-broker/blob/master/src/main/java/org/springframework/cloud/servicebroker/mongodb/config/CatalogConfig.java). To override this default, provide your own bean that implements the [`CatalogService`](src/main/java/org/springframework/cloud/servicebroker/service/CatalogService.java) interface.

For service instance provisioning/deprovisioning, provide a Spring bean that implements the [`ServiceInstanceService`](src/main/java/org/springframework/cloud/servicebroker/service/ServiceInstanceService.java) interface. There is no default implementation provided.

For service instance binding/unbinding, provide a Spring bean that implements the [`ServiceInstanceBindingService`](src/main/java/org/springframework/cloud/servicebroker/service/ServiceInstanceBindingService.java) interface. There is no default implementation provided.
For service instance binding/unbinding, provide a Spring bean that implements the [`ServiceInstanceBindingService`](src/main/java/org/springframework/cloud/servicebroker/service/ServiceInstanceBindingService.java) interface. If the service broker does not provide any bindable services, this bean can be omitted and a default implementation will be provided.

## Security

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.cloud.servicebroker.service.NonBindableServiceInstanceBindingService;
import org.springframework.cloud.servicebroker.service.ServiceInstanceBindingService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
Expand All @@ -30,4 +32,9 @@ public CatalogService beanCatalogService(Catalog catalog) {
return new BeanCatalogService(catalog);
}

@Bean
@ConditionalOnMissingBean(ServiceInstanceBindingService.class)
public ServiceInstanceBindingService nonBindableServiceInstanceBindingService() {
return new NonBindableServiceInstanceBindingService();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.springframework.cloud.servicebroker.service;

import org.springframework.cloud.servicebroker.model.CreateServiceInstanceBindingRequest;
import org.springframework.cloud.servicebroker.model.CreateServiceInstanceBindingResponse;
import org.springframework.cloud.servicebroker.model.DeleteServiceInstanceBindingRequest;

/**
* Default implementation of ServiceInstanceBindingService for service brokers that do not support bindable services.
*
* See http://docs.cloudfoundry.org/services/api.html#binding
*
* @author Scott Frederick
*/
public class NonBindableServiceInstanceBindingService implements ServiceInstanceBindingService {
@Override
public CreateServiceInstanceBindingResponse createServiceInstanceBinding(CreateServiceInstanceBindingRequest request) {
throw nonBindableException();
}

@Override
public void deleteServiceInstanceBinding(DeleteServiceInstanceBindingRequest request) {
throw nonBindableException();
}

private UnsupportedOperationException nonBindableException() {
return new UnsupportedOperationException("This service broker does not support bindable services. " +
"The service broker should set 'bindable: false' in the service catalog for all service offerings, " +
"or provide an implementation of this service.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.springframework.cloud.servicebroker.controller;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.cloud.servicebroker.model.fixture.DataFixture;
import org.springframework.cloud.servicebroker.service.NonBindableServiceInstanceBindingService;
import org.springframework.cloud.servicebroker.service.ServiceInstanceBindingService;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(MockitoJUnitRunner.class)
public class NonBindableServiceInstanceBindingControllerIntegrationTest extends ServiceInstanceBindingIntegrationTest {

private MockMvc mockMvc;

@Before
public void setup() {
ServiceInstanceBindingService serviceInstanceBindingService = new NonBindableServiceInstanceBindingService();
ServiceInstanceBindingController controller =
new ServiceInstanceBindingController(catalogService, serviceInstanceBindingService);

this.mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setMessageConverters(new MappingJackson2HttpMessageConverter()).build();
}

@Test
public void createBindingToAppFails() throws Exception {
setupCatalogService(createRequest.getServiceDefinitionId());

mockMvc.perform(put(buildUrl(createRequest))
.content(DataFixture.toJson(createRequest))
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isInternalServerError());
}

@Test
public void deleteBindingFails() throws Exception {
setupCatalogService(deleteRequest.getServiceDefinitionId());

mockMvc.perform(delete(buildUrl(deleteRequest))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isInternalServerError());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import org.springframework.cloud.servicebroker.model.CreateServiceInstanceBindingRequest;
import org.springframework.cloud.servicebroker.model.CreateServiceInstanceAppBindingResponse;
import org.springframework.cloud.servicebroker.model.CreateServiceInstanceRouteBindingResponse;
import org.springframework.cloud.servicebroker.model.DeleteServiceInstanceBindingRequest;
import org.springframework.cloud.servicebroker.service.ServiceInstanceBindingService;
import org.junit.Before;
import org.junit.Test;
Expand All @@ -21,7 +20,6 @@
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.util.UriComponentsBuilder;

import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
Expand All @@ -36,7 +34,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(MockitoJUnitRunner.class)
public class ServiceInstanceBindingControllerIntegrationTest extends ControllerIntegrationTest {
public class ServiceInstanceBindingControllerIntegrationTest extends ServiceInstanceBindingIntegrationTest {

private MockMvc mockMvc;

Expand All @@ -45,24 +43,11 @@ public class ServiceInstanceBindingControllerIntegrationTest extends ControllerI

@Mock
private ServiceInstanceBindingService serviceInstanceBindingService;

private UriComponentsBuilder uriBuilder;

private CreateServiceInstanceBindingRequest createRequest;

private DeleteServiceInstanceBindingRequest deleteRequest;

@Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setMessageConverters(new MappingJackson2HttpMessageConverter()).build();

uriBuilder = UriComponentsBuilder.fromPath("/v2/service_instances/")
.pathSegment("service-instance-one-id", "service_bindings");

createRequest = ServiceInstanceBindingFixture.buildCreateAppBindingRequest();

deleteRequest = ServiceInstanceBindingFixture.buildDeleteServiceInstanceBindingRequest();
}

@Test
Expand Down Expand Up @@ -245,15 +230,4 @@ public void deleteBindingWithUnknownServiceDefinitionIdSucceeds() throws Excepti
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}

private String buildUrl(CreateServiceInstanceBindingRequest request) {
return uriBuilder.path(request.getBindingId()).toUriString();
}

private String buildUrl(DeleteServiceInstanceBindingRequest request) {
return uriBuilder.path(request.getBindingId())
.queryParam("service_id", request.getServiceDefinitionId())
.queryParam("plan_id", request.getPlanId())
.toUriString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.springframework.cloud.servicebroker.controller;

import org.junit.Before;
import org.springframework.cloud.servicebroker.model.CreateServiceInstanceBindingRequest;
import org.springframework.cloud.servicebroker.model.DeleteServiceInstanceBindingRequest;
import org.springframework.cloud.servicebroker.model.fixture.ServiceInstanceBindingFixture;
import org.springframework.web.util.UriComponentsBuilder;

public abstract class ServiceInstanceBindingIntegrationTest extends ControllerIntegrationTest {
protected UriComponentsBuilder uriBuilder;

protected CreateServiceInstanceBindingRequest createRequest;
protected DeleteServiceInstanceBindingRequest deleteRequest;

@Before
public void setupBase() {
uriBuilder = UriComponentsBuilder.fromPath("/v2/service_instances/")
.pathSegment("service-instance-one-id", "service_bindings");

createRequest = ServiceInstanceBindingFixture.buildCreateAppBindingRequest();

deleteRequest = ServiceInstanceBindingFixture.buildDeleteServiceInstanceBindingRequest();
}

protected String buildUrl(CreateServiceInstanceBindingRequest request) {
return uriBuilder.path(request.getBindingId()).toUriString();
}

protected String buildUrl(DeleteServiceInstanceBindingRequest request) {
return uriBuilder.path(request.getBindingId())
.queryParam("service_id", request.getServiceDefinitionId())
.queryParam("plan_id", request.getPlanId())
.toUriString();
}
}

0 comments on commit bd9ad52

Please sign in to comment.