Initial revision

This commit is contained in:
Dave Syer
2014-07-17 15:47:07 +01:00
commit 7409ed5337
18 changed files with 1128 additions and 0 deletions

12
.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
*~
#*
*#
.#*
.classpath
.project
.settings
.springBeans
.gradle
build
bin
/target/

97
README.md Normal file
View File

@@ -0,0 +1,97 @@
Integration between [Cloudfoundry](https://github.com/cloudfoundry)
and the [Spring Platform](https://github.com/spring-platform).
Add this project to a Spring Boot REST service and deploy to
Cloudfoundry (and use the Actuator for maximum flexibility). It will
expose service-broker endpoints automatically (look in /mappings for
/v2/*) and you can register it with the Cloud Controller as described
here:
[http://docs.cloudfoundry.org/services/managing-service-brokers.html](http://docs.cloudfoundry.org/services/managing-service-brokers.html).
Example script to deploy and register a broker:
```
DOMAIN=mydomain.net
cf push app -p target/*.jar --no-start
cf env app | grep SPRING_PROFILES_ACTIVE || cf set-env app SPRING_PROFILES_ACTIVE cloud
cf env app | grep APPLICATION_DOMAIN || cf set-env app APPLICATION_DOMAIN ${DOMAIN}
cf services | grep configserver && cf bind app configserver
cf restart app
cf create-service-broker app user secure http://app.${DOMAIN}
for f in `cf curl /v2/service_plans | grep '\"guid' | sed -e 's/.*: "//' -e 's/".*//'`; do
cf curl v2/service_plans/$f -X PUT -d '{"public":true}'
done
cf create-service app free appi
```
At which point you have a service called "app" and a service instance called "appi":
```
$ cf marketplace
OK
service plans description
app free Singleton service app
$ cf services
Getting services in org default / space development as admin...
OK
name service plan bound apps
appi app free
```
Your application can define a configuration property
`application.domain` (defaults to "cfapps.io") which will be used to
construct the credentials for any app that binds to your service. Or
it can define the URI directly using
`cloudfoundry.service.definition.metadata.uri`.
You can change some other basic metadata by setting config properties:
* `cloudfoundry.service.definition.*` is bound to a
`ServiceDefinition` (defined in spring-boot-cf-service-broker) which
has optional setters for plans and metadata.
* `cloudfoundry.service.broker.*` is bound to an internal bean. It has
optional setters for "name" (the service name), "description" (user
friendly description) and "prefix" (used to create a unique id from
the name).
An app which binds to your service will get credentials that contain a
"uri" property linking to your service. A Spring Boot app can bind to
that through the `vcap.services.[service].credentials.uri` environment
property.
If your service also has a
[Eureka core](https://github.com/Netflix/eureka) dependency, and you
can expose it as a Eureka service, then any service which registers
with Eureka will also become a Cloudfoundry service. Example app with
Eureka server (include jersey 1.13 to get the JAX-RS dependencies):
```
@Configuration
@EnableAutoConfiguration
public class Application extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public FilterRegistrationBean jersey() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new ServletContainer());
bean.addInitParameter("com.sun.jersey.config.property.WebPageContentRegex",
"(/|/(flex/|images/|js/|css/|jsp/|admin/|v2/catalog|v2/service_instances).*)");
bean.addInitParameter("com.sun.jersey.config.property.packages",
"com.sun.jersey;com.netflix");
return bean;
}
}
```

75
pom.xml Normal file
View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.platform</groupId>
<artifactId>spring-platform-cloudfoundry</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-platform-cloudfoundry</name>
<description>Spring Patform Cloudfoundry integration project</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.1.5.BUILD-SNAPSHOT</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.platform</groupId>
<artifactId>spring-platform-netflix</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.platform</groupId>
<artifactId>spring-platform-netflix-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.cloudfoundry</groupId>
<artifactId>spring-boot-cf-service-broker</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>com.netflix.eureka</groupId>
<artifactId>eureka-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>1.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
</project>

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.cloudfoundry.broker;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.cloudfoundry.community.servicebroker.model.Catalog;
import org.cloudfoundry.community.servicebroker.model.ServiceDefinition;
import org.cloudfoundry.community.servicebroker.service.BeanCatalogService;
import org.cloudfoundry.community.servicebroker.service.CatalogService;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.ActionType;
import com.netflix.eureka.lease.LeaseManager;
/**
* @author Dave Syer
*
*/
public class CatalogLeaseManager implements LeaseManager<InstanceInfo>, LeaseManagerLite,
CatalogService {
private Map<String, ServiceDefinition> definitions = new HashMap<String, ServiceDefinition>();
private List<ServiceDefinition> values = new ArrayList<ServiceDefinition>();
public CatalogLeaseManager(InstanceInfo config) {
register(config, false);
}
@Override
public Catalog getCatalog() {
return new Catalog(values);
}
@Override
public ServiceDefinition getServiceDefinition(String serviceId) {
return new BeanCatalogService(getCatalog()).getServiceDefinition(serviceId);
}
@Override
public void register(InstanceInfo info, boolean isReplication) {
register(info, 0, isReplication);
}
@Override
public void register(InstanceInfo info, int leaseDuration, boolean isReplication) {
if (!definitions.containsKey(info.getAppName())) {
ServiceDefinition definition = getServiceDefinition(info);
definitions.put(info.getAppName(), definition);
values.add(definition);
}
}
@Override
public boolean cancel(String appName, String id, boolean isReplication) {
ServiceDefinition definition = definitions.remove(appName);
if (definition != null) {
values.remove(definition);
}
return true;
}
@Override
public boolean renew(String appName, String id, boolean isReplication) {
if (definitions.containsKey(appName)) {
definitions.get(appName).getMetadata()
.put("timestamp", System.currentTimeMillis());
}
return true;
}
@Override
public void evict() {
Collection<ServiceDefinition> definitions = new ArrayList<ServiceDefinition>(
this.definitions.values());
for (ServiceDefinition definition : definitions) {
InstanceInfo info = (InstanceInfo) definition.getMetadata().get("info");
if (info.getActionType() == ActionType.DELETED) {
cancel(info.getAppName(), info.getId(), false);
}
}
}
private ServiceDefinition getServiceDefinition(InstanceInfo info) {
String name = info.getAppName().toLowerCase();
ServiceDefinition definition = new FreeServiceDefinitionFactory("eureka-").create(name, "Eureka-brokered service");
Map<String, Object> map = new HashMap<String, Object>();
map.put("info", (Object) info);
map.put("uri", info.getHomePageUrl());
map.put("timestamp", System.currentTimeMillis());
definition.setMetadata(map);
return definition;
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.cloudfoundry.broker;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.cloudfoundry.community.servicebroker.model.Plan;
import org.cloudfoundry.community.servicebroker.model.ServiceDefinition;
import org.springframework.util.DigestUtils;
/**
* @author Dave Syer
*
*/
public class FreeServiceDefinitionFactory {
private final String prefix;
public FreeServiceDefinitionFactory() {
this("");
}
public FreeServiceDefinitionFactory(String prefix) {
this.prefix = prefix;
}
public ServiceDefinition create(String name, String description) {
return new ServiceDefinition(getId(name), name, description, true, getPlans(name));
}
private List<Plan> getPlans(String appName) {
Plan plan = new Plan(getId(appName + "-free"), "free",
"This is a default service plan. All services are created equally.",
getServiceDefinitionMetadata(appName), true);
return new ArrayList<Plan>(Arrays.asList(plan));
}
private Map<String, Object> getServiceDefinitionMetadata(String appName) {
Map<String, Object> sdMetadata = new HashMap<String, Object>();
sdMetadata.put("displayName", appName + "-service");
sdMetadata.put("longDescription", "Platform Service for " + appName);
sdMetadata.put("providerDisplayName", "Pivotal");
sdMetadata.put("documentationUrl", "https://github.com/spring-platform");
sdMetadata.put("supportUrl", "https://github.com/spring-platform");
return sdMetadata;
}
protected String getId(String name) {
try {
String id = DigestUtils.md5DigestAsHex((prefix + name).getBytes("UTF-8"));
return id;
}
catch (UnsupportedEncodingException e) {
throw new IllegalStateException();
}
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.cloudfoundry.broker;
import com.netflix.appinfo.InstanceInfo;
/**
* @author Dave Syer
*
*/
public interface LeaseManagerLite {
void register(final InstanceInfo info, final boolean isReplication);
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.cloudfoundry.broker;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.ReflectionUtils;
/**
* @author Dave Syer
*
*/
public class PiggybackMethodInterceptor implements MethodInterceptor {
private Object delegate;
private Class<?>[] types;
public PiggybackMethodInterceptor(Object delegate, Class<?>... types) {
this.delegate = delegate;
this.types = types;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = invocation.proceed();
invokeAfter(invocation.getMethod(), invocation.getArguments());
return result;
}
private void invokeAfter(Method method, Object[] arguments) throws Exception {
for (Class<?> type : types) {
Method target = getTarget(type, method);
if (target != null) {
target.invoke(delegate, arguments);
return;
}
}
}
private Method getTarget(Class<?> type, Method method) {
Method target = ReflectionUtils.findMethod(type, method.getName(),
method.getParameterTypes());
if (target != null) {
return target;
}
return null;
}
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.cloudfoundry.broker;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import org.cloudfoundry.community.servicebroker.config.BrokerApiVersionConfig;
import org.cloudfoundry.community.servicebroker.model.Catalog;
import org.cloudfoundry.community.servicebroker.model.ServiceDefinition;
import org.cloudfoundry.community.servicebroker.service.BeanCatalogService;
import org.cloudfoundry.community.servicebroker.service.CatalogService;
import org.cloudfoundry.community.servicebroker.service.ServiceInstanceBindingService;
import org.cloudfoundry.community.servicebroker.service.ServiceInstanceService;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.platform.netflix.eureka.EurekaRegistryAvailableEvent;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.providers.EurekaConfigBasedInstanceInfoProvider;
import com.netflix.eureka.PeerAwareInstanceRegistry;
import com.netflix.eureka.lease.LeaseManager;
/**
* Autoconfiguration providing simple service-broker endpoints and Netflix eureka server
* features. The service-broker endpoints will be enabled if <a
* href="https://github.com/cloudfoundry-community/spring-boot-cf-service-broker"
* >spring-boot-cf-service-broker</a> is on the classpath. By default they just make the
* current app into a service brooker for itself. The Eureka features will be added if <a
* href="https://github.com/Netflix/eureka">ereka-core</a> is on the classpath. When
* active any Eureka services registered using native Netflix APIs will be available as
* Cloudfoundry services. They consist of an interceptor for the {@link LeaseManager} that
* manages the {@link Catalog} of Cloudfoundry servvices.
*
*
* @author Dave Syer
*
*/
@Configuration
@ComponentScan(basePackages = { "demo", "org.cloudfoundry.community.servicebroker" }, excludeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = BrokerApiVersionConfig.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = BeanCatalogService.class) })
@ConditionalOnClass(ServiceInstanceRepository.class)
@ConditionalOnWebApplication
public class ServiceBrokerAutoConfiguration {
@Configuration
@ConditionalOnMissingClass(name="com.netflix.eureka.PeerAwareInstanceRegistry")
@ConditionalOnMissingBean(CatalogService.class)
protected static class CatalogConfiguration {
@Autowired
private BrokerProperties broker;
@Bean
public BeanCatalogService catalogService() {
return new BeanCatalogService(catalog());
}
@Bean
public Catalog catalog() {
return new Catalog(Arrays.asList(serviceDefinition()));
}
@Bean
@ConfigurationProperties("cloudfoundry.service.definition")
public ServiceDefinition serviceDefinition() {
return new FreeServiceDefinitionFactory(broker.getPrefix()).create(broker.getName(),
broker.getDescription());
}
@Component
@ConfigurationProperties("cloudfoundry.server.broker")
public static class BrokerProperties {
private String prefix;
@Value("${spring.application.name:application}")
private String name;
private String description;
public String getPrefix() {
return prefix==null ? name + "-" : prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description == null ? "Singleton service app" : description;
}
public void setDescription(String description) {
this.description = description;
}
}
}
@Configuration
@ConditionalOnClass(PeerAwareInstanceRegistry.class)
protected static class EurekaCatalogConfiguration {
@Bean
public CatalogLeaseManager catalogLeaseManager(EurekaInstanceConfig config) {
InstanceInfo info = new EurekaConfigBasedInstanceInfoProvider(config).get();
return new CatalogLeaseManager(info);
}
}
@Configuration
@ConditionalOnClass(PeerAwareInstanceRegistry.class)
protected static class Initializer implements
ApplicationListener<EurekaRegistryAvailableEvent> {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private CatalogLeaseManager leaseManager;
@Override
public void onApplicationEvent(EurekaRegistryAvailableEvent event) {
ProxyFactory factory = new ProxyFactory(
PeerAwareInstanceRegistry.getInstance());
factory.addAdvice(new PiggybackMethodInterceptor(leaseManager,
LeaseManager.class, LeaseManagerLite.class));
factory.setProxyTargetClass(true);
Field field = ReflectionUtils.findField(PeerAwareInstanceRegistry.class,
"instance");
try {
// Awful ugly hack to work around lack of DI in eureka
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
ReflectionUtils.setField(field, null, factory.getProxy());
}
catch (Exception e) {
throw new IllegalStateException("Cannot modify instance registry", e);
}
}
}
@Bean
@ConditionalOnMissingBean(ServiceInstanceService.class)
public SimpleServiceInstanceService serviceInstanceService(
ServiceInstanceRepository repository) {
return new SimpleServiceInstanceService(repository);
}
@Bean
@ConditionalOnMissingBean(ServiceInstanceBindingService.class)
public SimpleServiceInstanceBindingService serviceInstanceBindingService(
CatalogService catalog, ServiceInstanceBindingRepository repository) {
return new SimpleServiceInstanceBindingService(catalog, repository);
}
@Bean
@ConditionalOnMissingBean(ServiceInstanceRepository.class)
public SimpleServiceInstanceRepository serviceInstanceRepository(
CatalogService catalog) {
List<ServiceDefinition> definitions = catalog.getCatalog()
.getServiceDefinitions();
ServiceDefinition definition = definitions.iterator().next();
return new SimpleServiceInstanceRepository(definition.getId());
}
@Bean
@ConditionalOnMissingBean(ServiceInstanceBindingRepository.class)
public SimpleServiceInstanceBindingRepository serviceInstanceBindingRepository(
CatalogService catalog) {
List<ServiceDefinition> definitions = catalog.getCatalog()
.getServiceDefinitions();
ServiceDefinition definition = definitions.iterator().next();
return new SimpleServiceInstanceBindingRepository(definition.getId());
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.cloudfoundry.broker;
import org.cloudfoundry.community.servicebroker.model.ServiceInstanceBinding;
/**
* @author Dave Syer
*
*/
public interface ServiceInstanceBindingRepository {
ServiceInstanceBinding findOne(String bindingId);
void delete(String id);
void save(ServiceInstanceBinding binding);
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.cloudfoundry.broker;
import java.util.List;
import org.cloudfoundry.community.servicebroker.model.ServiceInstance;
/**
* @author Dave Syer
*
*/
public interface ServiceInstanceRepository {
List<ServiceInstance> findAll();
ServiceInstance findOne(String serviceInstanceId);
void save(ServiceInstance instance);
void delete(String id);
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.cloudfoundry.broker;
import org.cloudfoundry.community.servicebroker.model.ServiceInstanceBinding;
/**
* @author Dave Syer
*
*/
public class SimpleServiceInstanceBindingRepository implements ServiceInstanceBindingRepository {
private String serviceDefinitionId;
public SimpleServiceInstanceBindingRepository(String serviceDefinitionId) {
this.serviceDefinitionId = serviceDefinitionId;
}
@Override
public ServiceInstanceBinding findOne(String id) {
return new ServiceInstanceBinding(id, serviceDefinitionId, null, null, null);
}
@Override
public void save(ServiceInstanceBinding instance) {
}
@Override
public void delete(String id) {
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.cloudfoundry.broker;
import java.util.HashMap;
import java.util.Map;
import org.cloudfoundry.community.servicebroker.exception.ServiceBrokerException;
import org.cloudfoundry.community.servicebroker.exception.ServiceInstanceBindingExistsException;
import org.cloudfoundry.community.servicebroker.model.ServiceDefinition;
import org.cloudfoundry.community.servicebroker.model.ServiceInstance;
import org.cloudfoundry.community.servicebroker.model.ServiceInstanceBinding;
import org.cloudfoundry.community.servicebroker.service.CatalogService;
import org.cloudfoundry.community.servicebroker.service.ServiceInstanceBindingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
* Simple impl to bind services.
*
* @author sgreenberg@gopivotal.com
*
*/
@Service
public class SimpleServiceInstanceBindingService implements ServiceInstanceBindingService {
private ServiceInstanceBindingRepository repository;
private CatalogService catalog;
@Value("${application.domain:cfapps.io}")
private String applicationDomain;
@Autowired
public SimpleServiceInstanceBindingService(CatalogService catalog,
ServiceInstanceBindingRepository repository) {
this.catalog = catalog;
this.repository = repository;
}
@Override
public ServiceInstanceBinding createServiceInstanceBinding(String bindingId,
ServiceInstance serviceInstance, String serviceId, String planId,
String appGuid) throws ServiceInstanceBindingExistsException,
ServiceBrokerException {
ServiceInstanceBinding binding = repository.findOne(bindingId);
if (binding != null && binding.getAppGuid()!=null) {
throw new ServiceInstanceBindingExistsException(binding);
}
if (!isBindingPermitted(serviceInstance, serviceId, planId, appGuid)) {
throw new ServiceBrokerException(
"Binding is not permitted for this plan, app and service");
}
binding = new ServiceInstanceBinding(bindingId, serviceInstance.getId(),
getCredentials(serviceInstance, serviceId), null, appGuid);
repository.save(binding);
return binding;
}
protected boolean isBindingPermitted(ServiceInstance serviceInstance,
String serviceId, String planId, String appGuid) {
return true;
}
protected Map<String, Object> getCredentials(ServiceInstance instance,
String serviceId) {
Map<String, Object> credentials = new HashMap<String, Object>();
credentials.put("uri", findUriFromService(serviceId));
credentials.put("domain", applicationDomain);
return credentials;
}
protected String findUriFromService(String serviceId) {
ServiceDefinition definition = catalog.getServiceDefinition(serviceId);
if (definition != null && definition.getId().equals(serviceId)) {
String uri = (String) definition.getMetadata().get("uri");
if (uri != null) {
return uri;
}
return "http://" + definition.getName() + "." + applicationDomain;
}
throw new IllegalStateException("Cannot locate service in catalog: " + serviceId);
}
@Override
public ServiceInstanceBinding getServiceInstanceBinding(String id) {
return repository.findOne(id);
}
@Override
public ServiceInstanceBinding deleteServiceInstanceBinding(String id)
throws ServiceBrokerException {
ServiceInstanceBinding binding = getServiceInstanceBinding(id);
if (binding != null) {
repository.delete(id);
}
return binding;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.cloudfoundry.broker;
import java.util.ArrayList;
import java.util.List;
import org.cloudfoundry.community.servicebroker.model.ServiceInstance;
/**
* @author Dave Syer
*
*/
public class SimpleServiceInstanceRepository implements ServiceInstanceRepository {
private String serviceDefinitionId;
public SimpleServiceInstanceRepository(String serviceDefinitionId) {
// TODO: add dashboard URL
this.serviceDefinitionId = serviceDefinitionId;
}
@Override
public List<ServiceInstance> findAll() {
return new ArrayList<ServiceInstance>();
}
@Override
public ServiceInstance findOne(String serviceInstanceId) {
return new ServiceInstance(serviceInstanceId, serviceDefinitionId, null, null, null, null);
}
@Override
public void save(ServiceInstance instance) {
}
@Override
public void delete(String id) {
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.cloudfoundry.broker;
import java.util.List;
import org.cloudfoundry.community.servicebroker.exception.ServiceBrokerException;
import org.cloudfoundry.community.servicebroker.exception.ServiceInstanceExistsException;
import org.cloudfoundry.community.servicebroker.model.ServiceDefinition;
import org.cloudfoundry.community.servicebroker.model.ServiceInstance;
import org.cloudfoundry.community.servicebroker.service.ServiceInstanceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SimpleServiceInstanceService implements ServiceInstanceService {
private ServiceInstanceRepository repository;
@Autowired
public SimpleServiceInstanceService(ServiceInstanceRepository repository) {
this.repository = repository;
}
@Override
public List<ServiceInstance> getAllServiceInstances() {
return repository.findAll();
}
@Override
public ServiceInstance createServiceInstance(ServiceDefinition service,
String serviceInstanceId, String planId, String organizationGuid,
String spaceGuid)
throws ServiceInstanceExistsException, ServiceBrokerException {
ServiceInstance instance = repository.findOne(serviceInstanceId);
if (instance != null && instance.getOrganizationGuid()!=null) {
throw new ServiceInstanceExistsException(instance);
}
instance = new ServiceInstance(serviceInstanceId, service.getId(),
planId, organizationGuid, spaceGuid, null);
repository.save(instance);
return instance;
}
@Override
public ServiceInstance getServiceInstance(String id) {
return repository.findOne(id);
}
@Override
public ServiceInstance deleteServiceInstance(String id) throws ServiceBrokerException {
ServiceInstance instance = repository.findOne(id);
repository.delete(id);
return instance;
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.platform.cloudfoundry.broker.ServiceBrokerAutoConfiguration

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.cloudfoundry.broker.sample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.sun.jersey.spi.container.servlet.ServletContainer;
@Configuration
@EnableAutoConfiguration
public class Application extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public FilterRegistrationBean jersey() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new ServletContainer());
bean.addInitParameter("com.sun.jersey.config.property.WebPageContentRegex",
"(/|/(flex/|images/|js/|css/|jsp/|admin/|v2/catalog|v2/service_instances).*)");
bean.addInitParameter("com.sun.jersey.config.property.packages",
"com.sun.jersey;com.netflix");
return bean;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.cloudfoundry.broker.sample;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class ApplicationTests {
@Test
public void contextLoads() {
}
}

View File

@@ -0,0 +1,15 @@
debug: true
spring:
application:
name: eureka
management:
context-path: /admin
eureka:
server:
waitTimeInMsWhenSyncEmpty: 1000
client:
serviceUrl:
defaultZone: http://localhost:8080/v2/
default.defaultZone: http://localhost:8080/v2/
registerWithEureka: false
fetchRegistry: false