cfdc
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
*~
|
||||
#*
|
||||
*#
|
||||
|
||||
.#*
|
||||
.classpath
|
||||
.project
|
||||
@@ -11,5 +12,5 @@
|
||||
*.iml
|
||||
build
|
||||
bin
|
||||
/target/
|
||||
**/target/
|
||||
.factorypath
|
||||
|
||||
178
pom.xml
178
pom.xml
@@ -1,130 +1,76 @@
|
||||
<?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>
|
||||
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>
|
||||
|
||||
<artifactId>spring-cloud-cloudfoundry</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>spring-cloud-cloudfoundry</artifactId>
|
||||
<version>1.0.2.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>spring-cloud-cloudfoundry</name>
|
||||
<description>Spring Patform Cloudfoundry integration project</description>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>1.0.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath /> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<scm>
|
||||
<url>https://github.com/spring-cloud/spring-cloud-consul</url>
|
||||
<connection>
|
||||
scm:git:git://github.com/spring-cloud/spring-cloud-consul.git
|
||||
</connection>
|
||||
<developerConnection>
|
||||
scm:git:ssh://git@github.com/spring-cloud/spring-cloud-consul.git
|
||||
</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/spring-cloud/spring-cloud-cloudfoundry</url>
|
||||
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-cloudfoundry.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-cloudfoundry.git</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
<modules>
|
||||
<module>spring-cloud-cloudfoundry-discovery</module>
|
||||
<module>spring-cloud-cloudfoundry-web</module>
|
||||
<module>spring-cloud-cloudfoundry-sample</module>
|
||||
</modules>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.0.3.BUILD-SNAPSHOT</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
<artifactId>spring-cloud-cloudfoundry-discovery</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependency>
|
||||
<groupId>org.cloudfoundry</groupId>
|
||||
<artifactId>cloudfoundry-client-lib</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
<properties>
|
||||
<cloudfoundry.version>0.0.1-SNAPSHOT</cloudfoundry.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>spring-boot-starter-log4j</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-security</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cloudfoundry</groupId>
|
||||
<artifactId>spring-boot-cf-service-broker</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cloudfoundry</groupId>
|
||||
<artifactId>cloudfoundry-client-lib</artifactId>
|
||||
<version>1.0.4</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>docs</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
||||
80
spring-cloud-cloudfoundry-discovery/pom.xml
Normal file
80
spring-cloud-cloudfoundry-discovery/pom.xml
Normal file
@@ -0,0 +1,80 @@
|
||||
<?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>
|
||||
|
||||
<artifactId>spring-cloud-cloudfoundry-discovery</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<description>Spring Cloud CloudFoundry Discovery</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-cloudfoundry</artifactId>
|
||||
<version>1.0.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.archaius</groupId>
|
||||
<artifactId>archaius-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!---->
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-ribbon</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<scope>test</scope>
|
||||
<artifactId>feign-slf4j</artifactId>
|
||||
</dependency>
|
||||
<!---->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cloudfoundry</groupId>
|
||||
<artifactId>cloudfoundry-client-lib</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.cloud.cloudfoundry.discovery;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.cloudfoundry.client.lib.CloudCredentials;
|
||||
import org.cloudfoundry.client.lib.CloudFoundryClient;
|
||||
import org.cloudfoundry.client.lib.domain.CloudApplication;
|
||||
import org.cloudfoundry.client.lib.domain.InstanceInfo;
|
||||
import org.cloudfoundry.client.lib.domain.InstanceState;
|
||||
import org.cloudfoundry.client.lib.domain.InstancesInfo;
|
||||
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A Cloud Foundry v2 API-aware implementation of the {@link DiscoveryClient discovery client}
|
||||
* SPI. Cloud Foundry already retains a registry of running applications which we expose here as services.
|
||||
* Newer versions of Cloud Foundry support instance-specific networking, but as the Cloud Foundry client API doesn't
|
||||
* yet support that, this {@link DiscoveryClient implementation doesn't either}.
|
||||
* <p/>
|
||||
* You need to provide an instance of the {@link CloudFoundryClient}. A workable configuration looks like this:
|
||||
* <p/>
|
||||
* <pre class="code">
|
||||
* @Bean
|
||||
* CloudFoundryClient cloudFoundryClient(
|
||||
* @Value("${MY_CUSTOM_CF_API:https://api.run.pivotal.io}") String api,
|
||||
* CloudCredentials cc) throws MalformedURLException {
|
||||
* CloudFoundryClient cloudFoundryClient = new CloudFoundryClient(cc, URI.create(api).toURL());
|
||||
* cloudFoundryClient.login();
|
||||
* return cloudFoundryClient;
|
||||
* }
|
||||
* </pre>
|
||||
* <p/>
|
||||
* You can configure all sorts of other things including which Cloud Foundry cloud controller URI to use,
|
||||
* how and whether to use an HTTP proxy, and more using alternative constructors. As configured above, the client
|
||||
* will talk to all services and applications deployed in <EM>all</EM> spaces and organizations. Use one of the
|
||||
* {@link CloudFoundryClient#CloudFoundryClient(CloudCredentials, URL, String, String)} variants to specify which space
|
||||
* and organization to use.
|
||||
* <p/>
|
||||
*
|
||||
* @author <A href="mailto:josh@joshlong.com">Josh Long</A>
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CloudFoundryDiscoveryClient implements DiscoveryClient {
|
||||
|
||||
private static final String DESCRIPTION = "Cloud Foundry " + DiscoveryClient.class.getName() + " implementation";
|
||||
|
||||
private final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
private final ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
|
||||
|
||||
private final CloudFoundryClient cloudFoundryClient;
|
||||
|
||||
private final String vcapApplicationName;
|
||||
|
||||
public CloudFoundryDiscoveryClient(CloudFoundryClient cloudFoundryClient, Environment environment) {
|
||||
|
||||
this.cloudFoundryClient = cloudFoundryClient;
|
||||
|
||||
String vcapApplication = environment.getProperty("VCAP_APPLICATION");
|
||||
|
||||
try {
|
||||
JsonNode jsonNode = objectMapper.readTree(vcapApplication);
|
||||
JsonNode appNameNode = jsonNode.get("application_name");
|
||||
|
||||
this.vcapApplicationName = appNameNode.toString().replaceAll("\"", "");
|
||||
|
||||
this.log.debug("Current ServiceInstance information...");
|
||||
this.log.debug("\tvcapApplicationName: " + this.vcapApplicationName);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return DESCRIPTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceInstance getLocalServiceInstance() {
|
||||
CloudApplication application = this.cloudFoundryClient.getApplication(this.vcapApplicationName);
|
||||
List<ServiceInstance> serviceInstances =
|
||||
this.createServiceInstancesFromCloudApplications(Collections.singletonList(application));
|
||||
return serviceInstances.size() > 0 ? serviceInstances.iterator().next() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ServiceInstance> getInstances(String s) {
|
||||
CloudApplication applications = this.cloudFoundryClient.getApplication(s);
|
||||
return this.createServiceInstancesFromCloudApplications(
|
||||
Collections.singletonList(applications));
|
||||
}
|
||||
|
||||
private boolean isRunning(CloudApplication ca) {
|
||||
InstancesInfo ii = this.cloudFoundryClient.getApplicationInstances(ca);
|
||||
List<InstanceInfo> instances;
|
||||
if (ii != null && (instances = ii.getInstances()) != null) {
|
||||
for (InstanceInfo resolved : instances) {
|
||||
InstanceState state = resolved.getState();
|
||||
if (state != null && state.equals(InstanceState.RUNNING)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getServices() {
|
||||
List<String> services = new ArrayList<>();
|
||||
List<CloudApplication> applications = this.cloudFoundryClient.getApplications();
|
||||
Set<String> serviceIds = new HashSet<>();
|
||||
for (CloudApplication ca : applications) {
|
||||
if (isRunning(ca)) {
|
||||
serviceIds.add(ca.getName());
|
||||
}
|
||||
}
|
||||
services.addAll(serviceIds);
|
||||
return services;
|
||||
}
|
||||
|
||||
protected List<ServiceInstance> createServiceInstancesFromCloudApplications(
|
||||
Collection<CloudApplication> cloudApplications) {
|
||||
Set<ServiceInstance> serviceInstances = new HashSet<>();
|
||||
for (CloudApplication ca : cloudApplications) {
|
||||
if (isRunning(ca)) {
|
||||
serviceInstances.add(new CloudFoundryServiceInstance(ca));
|
||||
}
|
||||
}
|
||||
List<ServiceInstance> instances = new ArrayList<>();
|
||||
instances.addAll(serviceInstances);
|
||||
return instances;
|
||||
}
|
||||
|
||||
public static class CloudFoundryServiceInstance extends DefaultServiceInstance {
|
||||
|
||||
private final CloudApplication cloudApplication;
|
||||
|
||||
public CloudApplication getCloudApplication() {
|
||||
return cloudApplication;
|
||||
}
|
||||
|
||||
public CloudFoundryServiceInstance(CloudApplication ca) {
|
||||
super(ca.getName(),
|
||||
ca.getUris().iterator().next(),
|
||||
80,
|
||||
false);
|
||||
|
||||
this.cloudApplication = ca;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.cloud.cloudfoundry.discovery;
|
||||
|
||||
import org.cloudfoundry.client.lib.CloudCredentials;
|
||||
import org.cloudfoundry.client.lib.CloudFoundryClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* @author <A href="mailto:josh@joshlong.com">Josh Long</A>
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
public class CloudFoundryDiscoveryClientConfiguration {
|
||||
|
||||
@Autowired
|
||||
private CloudFoundryDiscoveryProperties cloudFoundryDiscoveryProperties;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(CloudCredentials.class)
|
||||
public CloudCredentials cloudCredentials() {
|
||||
return new CloudCredentials(this.cloudFoundryDiscoveryProperties.getEmail(),
|
||||
this.cloudFoundryDiscoveryProperties.getPassword());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(CloudFoundryClient.class)
|
||||
public CloudFoundryClient cloudFoundryClient(CloudCredentials cc) throws MalformedURLException {
|
||||
CloudFoundryClient cloudFoundryClient = new CloudFoundryClient(cc,
|
||||
URI.create(this.cloudFoundryDiscoveryProperties.getCloudControllerUrl()).toURL());
|
||||
cloudFoundryClient.login();
|
||||
return cloudFoundryClient;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(CloudFoundryDiscoveryClient.class)
|
||||
public CloudFoundryDiscoveryClient cloudFoundryDiscoveryClient(
|
||||
CloudFoundryClient cloudFoundryClient, Environment environment) {
|
||||
return new CloudFoundryDiscoveryClient(cloudFoundryClient, environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CloudFoundryDiscoveryProperties cloudFoundryDiscoveryProperties() {
|
||||
return new CloudFoundryDiscoveryProperties();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.cloud.cloudfoundry.discovery;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author <A href="mailto:josh@joshlong.com">Josh Long</A>
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.cloud.cloudfoundry.discovery")
|
||||
public class CloudFoundryDiscoveryProperties {
|
||||
|
||||
private String cloudControllerUrl = "https://api.run.pivotal.io";
|
||||
|
||||
private String email;
|
||||
|
||||
private String password;
|
||||
|
||||
public String getCloudControllerUrl() {
|
||||
return cloudControllerUrl;
|
||||
}
|
||||
|
||||
public void setCloudControllerUrl(String cloudControllerUrl) {
|
||||
this.cloudControllerUrl = cloudControllerUrl;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.cloud.cloudfoundry.discovery;
|
||||
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
import com.netflix.config.DynamicStringProperty;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import org.cloudfoundry.client.lib.CloudFoundryClient;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* @author <A href="mailto:josh@joshlong.com">Josh Long</A>
|
||||
*/
|
||||
@Configuration
|
||||
public class CloudFoundryRibbonClientConfiguration {
|
||||
|
||||
protected static final String DEFAULT_NAMESPACE = "ribbon";
|
||||
protected static final String VALUE_NOT_SET = "__not__set__";
|
||||
|
||||
@Value("${ribbon.client.name}")
|
||||
private String serviceId;
|
||||
|
||||
public CloudFoundryRibbonClientConfiguration (){ }
|
||||
|
||||
public CloudFoundryRibbonClientConfiguration (String svcId) {
|
||||
this.serviceId = svcId;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ServerList<?> ribbonServerList(CloudFoundryClient cloudFoundryClient, IClientConfig config) {
|
||||
CloudFoundryServerList cloudFoundryServerList = new CloudFoundryServerList(cloudFoundryClient);
|
||||
cloudFoundryServerList.initWithNiwsConfig(config);
|
||||
return cloudFoundryServerList;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
// FIXME: what should this be?
|
||||
setProp(this.serviceId, CommonClientConfigKey.DeploymentContextBasedVipAddresses.key(), this.serviceId);
|
||||
setProp(this.serviceId, CommonClientConfigKey.EnableZoneAffinity.key(), "true");
|
||||
}
|
||||
|
||||
protected void setProp(String serviceId, String suffix, String value) {
|
||||
// how to set the namespace properly?
|
||||
String key = getKey(serviceId, suffix);
|
||||
DynamicStringProperty property = getProperty(key);
|
||||
if (property.get().equals(VALUE_NOT_SET)) {
|
||||
ConfigurationManager.getConfigInstance().setProperty(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected DynamicStringProperty getProperty(String key) {
|
||||
return DynamicPropertyFactory.getInstance().getStringProperty(key, VALUE_NOT_SET);
|
||||
}
|
||||
|
||||
protected String getKey(String serviceId, String suffix) {
|
||||
return serviceId + "." + DEFAULT_NAMESPACE + "." + suffix;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.cloud.cloudfoundry.discovery;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import org.cloudfoundry.client.lib.domain.CloudApplication;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:josh@joshlong.com">Josh Long</A>
|
||||
*/
|
||||
public class CloudFoundryServer extends Server {
|
||||
|
||||
private final MetaInfo metaInfo;
|
||||
|
||||
public CloudFoundryServer(final CloudApplication cloudApplication) {
|
||||
|
||||
super(cloudApplication.getUris().iterator().next(), 80);
|
||||
|
||||
this.metaInfo = new MetaInfo() {
|
||||
@Override
|
||||
public String getAppName() {
|
||||
return cloudApplication.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerGroup() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceIdForDiscovery() {
|
||||
return cloudApplication.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInstanceId() {
|
||||
return cloudApplication.getName();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaInfo getMetaInfo() {
|
||||
return metaInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.cloud.cloudfoundry.discovery;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.AbstractServerList;
|
||||
import org.cloudfoundry.client.lib.CloudFoundryClient;
|
||||
import org.cloudfoundry.client.lib.domain.CloudApplication;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <A href="mailto:josh@joshlong.com">Josh Long</A>
|
||||
*/
|
||||
public class CloudFoundryServerList extends AbstractServerList<CloudFoundryServer> {
|
||||
|
||||
protected String serviceId;
|
||||
|
||||
private final CloudFoundryClient cloudFoundryClient;
|
||||
|
||||
public CloudFoundryServerList(CloudFoundryClient cloudFoundryClient) {
|
||||
this.cloudFoundryClient = cloudFoundryClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initWithNiwsConfig(IClientConfig iClientConfig) {
|
||||
this.serviceId = iClientConfig.getClientName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CloudFoundryServer> getInitialListOfServers() {
|
||||
return this.cloudFoundryServers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CloudFoundryServer> getUpdatedListOfServers() {
|
||||
return this.cloudFoundryServers();
|
||||
}
|
||||
|
||||
protected List<CloudFoundryServer> cloudFoundryServers() {
|
||||
CloudApplication cloudApplications = this.cloudFoundryClient.getApplication(this.serviceId);
|
||||
return Collections.singletonList(new CloudFoundryServer(cloudApplications));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.cloud.cloudfoundry.discovery;
|
||||
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Convenience annotation for clients to enable Cloud Foundry discovery configuration (specifically).
|
||||
* Use this (optionally) in case you want discovery and know for sure that it is Cloud Foundry you want.
|
||||
* All it does is turn on discovery and let the auto-configuration find the Cloud Foundry classes.
|
||||
*
|
||||
* @author <a href="mailto:josh@joshlong.com">Josh Long</A>
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@EnableDiscoveryClient
|
||||
public @interface EnableCloudFoundryClient {
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.cloud.cloudfoundry.discovery;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnBean(SpringClientFactory.class)
|
||||
@ConditionalOnProperty(value = "ribbon.cloudfoundry.enabled", matchIfMissing = true)
|
||||
@AutoConfigureAfter(RibbonAutoConfiguration.class)
|
||||
@RibbonClients(defaultConfiguration = CloudFoundryRibbonClientConfiguration.class)
|
||||
public class RibbonCloudFoundryAutoConfiguration {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.cloudfoundry.discovery.RibbonCloudFoundryAutoConfiguration
|
||||
|
||||
# Discovery Client Configuration
|
||||
org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
|
||||
org.springframework.cloud.cloudfoundry.discovery.CloudFoundryDiscoveryClientConfiguration
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.cloud.cloudfoundry.discovery;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.cloudfoundry.client.lib.CloudCredentials;
|
||||
import org.cloudfoundry.client.lib.CloudFoundryClient;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author <A href= "josh@joshlong.com">Josh Long</A>
|
||||
*/
|
||||
public class CloudFoundryAutoConfigurationTest {
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
String hiServiceServiceId = "foo-service";
|
||||
|
||||
Object vcapAppl = "{\"limits\":{\"mem\":1024,\"disk\":1024,\"fds\":16384},\"application_version\":" +
|
||||
"\"36eff082-96d6-498f-8214-508fda72ba65\",\"application_name\":\"" + hiServiceServiceId +
|
||||
"\",\"application_uris\"" +
|
||||
":[\"" + hiServiceServiceId +
|
||||
".cfapps.io\"],\"version\":\"36eff082-96d6-498f-8214-508fda72ba65\",\"name\":" +
|
||||
"\"hi-service\",\"space_name\":\"joshlong\",\"space_id\":\"e0cd969c-3461-41ae-abde-4e11bb5acbd1\"," +
|
||||
"\"uris\":[\"hi-service.cfapps.io\"],\"users\":null,\"application_id\":\"af350f7c-88c4-4e35-a04e-698a1dbc7354\"," +
|
||||
"\"instance_id\":\"e4843ca23bd947b28e6d4cb3f9b92cbb\",\"instance_index\":0,\"host\":\"0.0.0.0\",\"port\":61590," +
|
||||
"\"started_at\":\"2015-05-07 20:00:10 +0000\",\"started_at_timestamp\":1431028810,\"start\":\"2015-05-07 20:00:10 +0000\"," +
|
||||
"\"state_timestamp\":1431028810}";
|
||||
|
||||
this.context = new SpringApplicationBuilder()
|
||||
.properties(Collections.singletonMap("VCAP_APPLICATION", vcapAppl))
|
||||
.sources(SimpleConfiguration.class)
|
||||
.run();
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() throws Throwable {
|
||||
synchronized (this) {
|
||||
if (null != this.context)
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients
|
||||
@EnableAutoConfiguration
|
||||
public static class SimpleConfiguration {
|
||||
|
||||
@Bean
|
||||
CloudCredentials cloudCredentials() {
|
||||
return Mockito.mock(CloudCredentials.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
CloudFoundryClient cloudFoundryClient() {
|
||||
return Mockito.mock(CloudFoundryClient.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextLoaded() {
|
||||
LogFactory.getLog(getClass()).debug("contextLoad()");
|
||||
Assert.assertTrue(this.context.getBeansOfType(CloudFoundryDiscoveryClient.class).size() > 0);
|
||||
Assert.assertTrue(this.context.getBeansOfType(CloudFoundryDiscoveryProperties.class).size() > 0);
|
||||
Assert.assertTrue(this.context.getBeansOfType(CloudFoundryClient.class).size() > 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.cloud.cloudfoundry.discovery;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.cloudfoundry.client.lib.CloudFoundryClient;
|
||||
import org.cloudfoundry.client.lib.domain.CloudApplication;
|
||||
import org.cloudfoundry.client.lib.domain.InstanceInfo;
|
||||
import org.cloudfoundry.client.lib.domain.InstanceState;
|
||||
import org.cloudfoundry.client.lib.domain.InstancesInfo;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.mock;
|
||||
|
||||
/**
|
||||
* @author <A href="mailto:josh@Joshlong.com">Josh Long</A>
|
||||
*/
|
||||
public class CloudFoundryDiscoveryClientTest {
|
||||
|
||||
private final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
private CloudFoundryDiscoveryClient cloudFoundryDiscoveryClient;
|
||||
|
||||
private CloudApplication cloudApplication;
|
||||
|
||||
private String hiServiceServiceId = "hi-service";
|
||||
|
||||
private CloudFoundryClient cloudFoundryClient;
|
||||
|
||||
private CloudApplication fakeCloudApplication(String name, String... uri) {
|
||||
CloudApplication cloudApplication = mock(CloudApplication.class);
|
||||
given(cloudApplication.getName()).willReturn(name);
|
||||
given(cloudApplication.getUris()).willReturn(Arrays.asList(uri));
|
||||
return cloudApplication;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.cloudFoundryClient = mock(CloudFoundryClient.class);
|
||||
Environment environment = mock(Environment.class);
|
||||
|
||||
given(environment.getProperty("VCAP_APPLICATION"))
|
||||
.willReturn("{\"limits\":{\"mem\":1024,\"disk\":1024,\"fds\":16384},\"application_version\":" +
|
||||
"\"36eff082-96d6-498f-8214-508fda72ba65\",\"application_name\":\"" + hiServiceServiceId +
|
||||
"\",\"application_uris\"" +
|
||||
":[\"" + hiServiceServiceId +
|
||||
".cfapps.io\"],\"version\":\"36eff082-96d6-498f-8214-508fda72ba65\",\"name\":" +
|
||||
"\"hi-service\",\"space_name\":\"joshlong\",\"space_id\":\"e0cd969c-3461-41ae-abde-4e11bb5acbd1\"," +
|
||||
"\"uris\":[\"hi-service.cfapps.io\"],\"users\":null,\"application_id\":\"af350f7c-88c4-4e35-a04e-698a1dbc7354\"," +
|
||||
"\"instance_id\":\"e4843ca23bd947b28e6d4cb3f9b92cbb\",\"instance_index\":0,\"host\":\"0.0.0.0\",\"port\":61590," +
|
||||
"\"started_at\":\"2015-05-07 20:00:10 +0000\",\"started_at_timestamp\":1431028810,\"start\":\"2015-05-07 20:00:10 +0000\"," +
|
||||
"\"state_timestamp\":1431028810}");
|
||||
|
||||
List<CloudApplication> cloudApplications = new ArrayList<>();
|
||||
cloudApplications.add(fakeCloudApplication(this.hiServiceServiceId, "hi-service.cfapps.io", "hi-service-1.cfapps.io"));
|
||||
cloudApplications.add(fakeCloudApplication("config-service", "conf-service.cfapps.io", "conf-service-1.cfapps.io"));
|
||||
|
||||
given(this.cloudFoundryClient.getApplications())
|
||||
.willReturn(cloudApplications);
|
||||
|
||||
cloudApplication = cloudApplications.get(0);
|
||||
given(this.cloudFoundryClient.getApplication(this.hiServiceServiceId))
|
||||
.willReturn(cloudApplication);
|
||||
|
||||
given(this.cloudFoundryClient.getApplication(this.hiServiceServiceId))
|
||||
.willReturn(this.cloudApplication);
|
||||
|
||||
InstanceInfo instanceInfo = mock(InstanceInfo.class);
|
||||
InstancesInfo instancesInfo = mock(InstancesInfo.class);
|
||||
given(instancesInfo.getInstances())
|
||||
.willReturn(Collections.singletonList(instanceInfo));
|
||||
given(instanceInfo.getState())
|
||||
.willReturn(InstanceState.RUNNING);
|
||||
|
||||
given(this.cloudFoundryClient.getApplicationInstances(this.cloudApplication))
|
||||
.willReturn(instancesInfo);
|
||||
|
||||
this.cloudFoundryDiscoveryClient = new CloudFoundryDiscoveryClient(cloudFoundryClient, environment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServiceResolution() {
|
||||
List<String> serviceNames = this.cloudFoundryDiscoveryClient.getServices();
|
||||
|
||||
Assert.assertTrue("there should be one registered service.", serviceNames.contains(
|
||||
this.hiServiceServiceId));
|
||||
|
||||
for (String serviceName : serviceNames) {
|
||||
this.log.debug("\t discovered serviceName: " + serviceName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstances() {
|
||||
List<ServiceInstance> instances = this.cloudFoundryDiscoveryClient.getInstances(
|
||||
this.hiServiceServiceId);
|
||||
assertEquals(instances.size(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalServiceInstanceRunning() {
|
||||
|
||||
InstanceInfo instanceInfo = mock(InstanceInfo.class);
|
||||
InstancesInfo instancesInfo = mock(InstancesInfo.class);
|
||||
given(instancesInfo.getInstances()).willReturn(Collections.singletonList(instanceInfo));
|
||||
given(instanceInfo.getState()).willReturn(InstanceState.RUNNING);
|
||||
|
||||
given(cloudFoundryClient.getApplicationInstances(this.cloudApplication)).willReturn(instancesInfo);
|
||||
|
||||
ServiceInstance localServiceInstance = this.cloudFoundryDiscoveryClient.getLocalServiceInstance();
|
||||
assertTrue(localServiceInstance.getHost().contains("hi-service.cfapps.io"));
|
||||
assertTrue(localServiceInstance.getServiceId().equals(this.hiServiceServiceId));
|
||||
assertEquals(localServiceInstance.getPort(), 80);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalServiceInstanceNotRunning() {
|
||||
|
||||
InstanceInfo instanceInfo = mock(InstanceInfo.class);
|
||||
InstancesInfo instancesInfo = mock(InstancesInfo.class);
|
||||
given(instancesInfo.getInstances()).willReturn(Collections.singletonList(instanceInfo));
|
||||
given(instanceInfo.getState()).willReturn(InstanceState.CRASHED);
|
||||
|
||||
given(cloudFoundryClient.getApplicationInstances(this.cloudApplication)).willReturn(instancesInfo);
|
||||
|
||||
ServiceInstance localServiceInstance = this.cloudFoundryDiscoveryClient.getLocalServiceInstance();
|
||||
assertNull(localServiceInstance);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.cloud.cloudfoundry.discovery;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import org.cloudfoundry.client.lib.CloudFoundryClient;
|
||||
import org.cloudfoundry.client.lib.domain.CloudApplication;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.mock;
|
||||
|
||||
/**
|
||||
* @author <A href="mailto:josh@Joshlong.com">Josh Long</A>
|
||||
*/
|
||||
public class CloudFoundryServerListTest {
|
||||
|
||||
private CloudFoundryServerList cloudFoundryServerList;
|
||||
private String serviceId = "foo-service";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
CloudApplication cloudApplication = mock(CloudApplication.class);
|
||||
given(cloudApplication.getUris()).will(new Answer<List<String>>() {
|
||||
@Override
|
||||
public List<String> answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
return Arrays.asList("a-url.com", "b-url.com");
|
||||
}
|
||||
});
|
||||
|
||||
CloudFoundryClient cloudFoundryClient = mock(CloudFoundryClient.class);
|
||||
given(cloudFoundryClient.getApplication(this.serviceId)).willReturn(cloudApplication);
|
||||
|
||||
IClientConfig iClientConfig = mock(IClientConfig.class);
|
||||
given(iClientConfig.getClientName()).willReturn(this.serviceId);
|
||||
|
||||
this.cloudFoundryServerList = new CloudFoundryServerList(cloudFoundryClient);
|
||||
this.cloudFoundryServerList.initWithNiwsConfig(iClientConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListOfServers() {
|
||||
List<CloudFoundryServer> initialListOfServers = this.cloudFoundryServerList.getInitialListOfServers();
|
||||
List<CloudFoundryServer> updatedListOfServers = this.cloudFoundryServerList.getUpdatedListOfServers();
|
||||
Assert.assertEquals(updatedListOfServers, initialListOfServers);
|
||||
Assert.assertTrue(initialListOfServers.size() == 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInit() {
|
||||
Assert.assertEquals(this.cloudFoundryServerList.serviceId, this.serviceId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.cloud.cloudfoundry.discovery;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import org.cloudfoundry.client.lib.domain.CloudApplication;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.mock;
|
||||
|
||||
/**
|
||||
* @author <A href="mailto:josh@Joshlong.com">Josh Long</A>
|
||||
*/
|
||||
public class CloudFoundryServerTest {
|
||||
|
||||
private CloudFoundryServer cloudFoundryServer;
|
||||
private List<String> urls = Arrays.asList("a-url.com", "b-url.com");
|
||||
private String serverName = "server-name";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
CloudApplication cloudApplication = mock(CloudApplication.class);
|
||||
given(cloudApplication.getUris()).willReturn(this.urls);
|
||||
given(cloudApplication.getName()).willReturn(this.serverName);
|
||||
given(cloudApplication.getRunningInstances()).willReturn(1);
|
||||
this.cloudFoundryServer = new CloudFoundryServer(cloudApplication);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProperConstruction() {
|
||||
Server.MetaInfo metaInfo = this.cloudFoundryServer.getMetaInfo();
|
||||
|
||||
Assert.assertEquals(metaInfo.getAppName(), this.serverName);
|
||||
Assert.assertEquals(metaInfo.getServiceIdForDiscovery(), this.serverName);
|
||||
Assert.assertEquals(metaInfo.getInstanceId(), this.serverName);
|
||||
Assert.assertEquals(this.cloudFoundryServer.getHost(), this.urls.get(0));
|
||||
}
|
||||
}
|
||||
20
spring-cloud-cloudfoundry-sample/hi-service.groovy
Normal file
20
spring-cloud-cloudfoundry-sample/hi-service.groovy
Normal file
@@ -0,0 +1,20 @@
|
||||
// to run on ur local machine this use:
|
||||
// spring run hi-service.groovy
|
||||
//
|
||||
// to deploy this to Cloud Foundry, use
|
||||
// spring jar hi.jar hi-service.groovy
|
||||
// cf push hi-service -p hi.jar
|
||||
|
||||
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@RestController
|
||||
class GreetingRestController {
|
||||
|
||||
@RequestMapping("/hi/{name}")
|
||||
def hi(@PathVariable String name) {
|
||||
[greeting: "Hello, " + name + "!"]
|
||||
}
|
||||
}
|
||||
61
spring-cloud-cloudfoundry-sample/pom.xml
Normal file
61
spring-cloud-cloudfoundry-sample/pom.xml
Normal file
@@ -0,0 +1,61 @@
|
||||
<?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>
|
||||
|
||||
<artifactId>spring-cloud-cloudfoundry-sample</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-cloudfoundry</artifactId>
|
||||
<version>1.0.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!--skip deploy -->
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-ribbon</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.feign</groupId>
|
||||
<artifactId>feign-slf4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-cloudfoundry-discovery</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.cloud.cloudfoundry.sample;
|
||||
|
||||
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
|
||||
import org.apache.commons.lang.builder.ToStringStyle;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This example assumes you've registered an application on <A href="http://cloudfoundry.org">Cloud Foundry</A>
|
||||
* named {@code hi-service} that responds with a String at {@code /hi/{name}}. There is a sample file in the project
|
||||
* root called {@code hi-service.groovy} which you can deploy using the {@code spring} CLI and the {@code cf} CLI that
|
||||
* works appropriately for this demonstration.
|
||||
*
|
||||
* @author <a href="mailto:josh@joshlong.com">Josh Long</a>
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients
|
||||
public class DemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DemoApplication.class, args);
|
||||
}
|
||||
|
||||
private Log log = LogFactory.getLog(getClass());
|
||||
|
||||
@Bean
|
||||
CommandLineRunner consume(final LoadBalancerClient loadBalancerClient,
|
||||
final DiscoveryClient discoveryClient,
|
||||
final HiServiceClient hiServiceClient,
|
||||
final RestTemplate restTemplate) {
|
||||
|
||||
return new CommandLineRunner() {
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
|
||||
// this demonstrates using the CF/Ribbon-aware RestTemplate interceptor
|
||||
log.info("=====================================");
|
||||
log.info("Hi: " + restTemplate.getForEntity("http://hi-service/hi/{name}", String.class, "Josh"));
|
||||
|
||||
// this demonstrates using the Spring Cloud Commons DiscoveryClient abstraction
|
||||
log.info("=====================================");
|
||||
for (String svc : discoveryClient.getServices()) {
|
||||
log.info("service = " + svc);
|
||||
List<ServiceInstance> instances = discoveryClient.getInstances(svc);
|
||||
for (ServiceInstance si : instances) {
|
||||
log.info("\t" + ReflectionToStringBuilder.reflectionToString(si, ToStringStyle.MULTI_LINE_STYLE));
|
||||
}
|
||||
}
|
||||
|
||||
log.info("=====================================");
|
||||
log.info("local: ");
|
||||
log.info("\t" + ReflectionToStringBuilder.reflectionToString(
|
||||
discoveryClient.getLocalServiceInstance(), ToStringStyle.MULTI_LINE_STYLE));
|
||||
|
||||
// this demonstrates using a CF/Ribbon-aware Feign client
|
||||
log.info("=====================================");
|
||||
log.info("Hi:" + hiServiceClient.hi("Josh"));
|
||||
|
||||
// this demonstrates using the Spring Cloud Commons LoadBalancerClient
|
||||
log.info("=====================================");
|
||||
ServiceInstance choose = loadBalancerClient.choose("hi-service");
|
||||
log.info("chose: " + '(' + choose.getServiceId() + ") " + choose.getHost() + ':' + choose.getPort());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FeignClient("hi-service")
|
||||
interface HiServiceClient {
|
||||
|
||||
@RequestMapping(value = "/hi/{name}", method = RequestMethod.GET)
|
||||
Map<String, Object> hi(@PathVariable("name") String name);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
spring.application.name=test-app
|
||||
spring.cloud.cloudfoundry.discovery.email=starbuxman@gmail.com
|
||||
spring.cloud.cloudfoundry.discovery.password=xxxx
|
||||
VCAP_APPLICATION={"limits":{"mem":1024,"disk":1024,"fds":16384},"application_version":"36eff082-96d6-498f-8214-508fda72ba65","application_name":"hi-service","application_uris":["hi-service.cfapps.io"],"version":"36eff082-96d6-498f-8214-508fda72ba65","name":"hi-service","space_name":"joshlong","space_id":"e0cd969c-3461-41ae-abde-4e11bb5acbd1","uris":["hi-service.cfapps.io"],"users":null,"application_id":"af350f7c-88c4-4e35-a04e-698a1dbc7354","instance_id":"e4843ca23bd947b28e6d4cb3f9b92cbb","instance_index":0,"host":"0.0.0.0","port":61590,"started_at":"2015-05-07 20:00:10 +0000","started_at_timestamp":1431028810,"start":"2015-05-07 20:00:10 +0000","state_timestamp":1431028810}
|
||||
@@ -0,0 +1 @@
|
||||
spring.application.name=test-app
|
||||
71
spring-cloud-cloudfoundry-web/pom.xml
Normal file
71
spring-cloud-cloudfoundry-web/pom.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?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>
|
||||
|
||||
<artifactId>spring-cloud-cloudfoundry-web</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<description>Spring Cloud CloudFoundry Web</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-cloudfoundry</artifactId>
|
||||
<version>1.0.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>spring-boot-starter-log4j</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,2 @@
|
||||
include::intro.adoc[]
|
||||
|
||||
46
spring-cloud-cloudfoundry-web/src/main/asciidoc/ghpages.sh
Executable file
46
spring-cloud-cloudfoundry-web/src/main/asciidoc/ghpages.sh
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash -x
|
||||
|
||||
git remote set-url --push origin `git config remote.origin.url | sed -e 's/^git:/https:/'`
|
||||
|
||||
if ! (git remote set-branches --add origin gh-pages && git fetch -q); then
|
||||
echo "No gh-pages, so not syncing"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! [ -d target/generated-docs ]; then
|
||||
echo "No gh-pages sources in target/generated-docs, so not syncing"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Stash any outstanding changes
|
||||
###################################################################
|
||||
git diff-index --quiet HEAD
|
||||
dirty=$?
|
||||
if [ "$dirty" != "0" ]; then git stash; fi
|
||||
|
||||
# Switch to gh-pages branch to sync it with master
|
||||
###################################################################
|
||||
git checkout gh-pages
|
||||
|
||||
for f in target/generated-docs/*; do
|
||||
file=${f#target/generated-docs/*}
|
||||
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then
|
||||
# Not ignored...
|
||||
cp -rf $f .
|
||||
git add -A $file
|
||||
fi
|
||||
done
|
||||
|
||||
git commit -a -m "Sync docs from master to gh-pages"
|
||||
|
||||
# Uncomment the following push if you want to auto push to
|
||||
# the gh-pages branch whenever you commit to master locally.
|
||||
# This is a little extreme. Use with care!
|
||||
###################################################################
|
||||
git push origin gh-pages
|
||||
|
||||
# Finally, switch back to the master branch and exit block
|
||||
git checkout master
|
||||
if [ "$dirty" != "0" ]; then git stash pop; fi
|
||||
|
||||
exit 0
|
||||
15
spring-cloud-cloudfoundry-web/src/main/asciidoc/intro.adoc
Normal file
15
spring-cloud-cloudfoundry-web/src/main/asciidoc/intro.adoc
Normal file
@@ -0,0 +1,15 @@
|
||||
Spring Cloud for Cloudfoundry makes it easy to run
|
||||
https://github.com/spring-cloud[Spring Cloud] apps in
|
||||
https://github.com/cloudfoundry[Cloud Foundry] (the Platform as a
|
||||
Service). Cloud Foundry has the notion of a "service", which is
|
||||
middlware that you "bind" to an app, essentially providing it with an
|
||||
environment variable containing credentials (e.g. the location and
|
||||
username to use for the service).
|
||||
|
||||
Add this project as a dependency to any Spring Cloud UI app or REST
|
||||
service and deploy to Cloudfoundry. If you use Spring Cloud Security
|
||||
OAuth2 features this will make them bindable to Cloud Foundry services
|
||||
instead of enironment properties in `spring.oauth2.*`. For a UI app you can
|
||||
declare `@EnableOAuth2Sso` and bind to a service called "sso", and for
|
||||
a service you can add `@EnableOAuth2Resource` and bind to a service
|
||||
called "resource" (see below for how to change the names).
|
||||
@@ -0,0 +1,50 @@
|
||||
Here's a Spring Cloud app with OAuth2 SSO:
|
||||
|
||||
.app.groovy
|
||||
[source,java]
|
||||
----
|
||||
@Controller
|
||||
@EnableOAuth2Sso
|
||||
class Application {
|
||||
|
||||
@RequestMapping('/')
|
||||
String home() {
|
||||
'Hello World'
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
If you run it without any service bindings:
|
||||
|
||||
----
|
||||
$ spring jar app.jar app.groovy
|
||||
$ cf push -p app.jar
|
||||
----
|
||||
|
||||
it will be secure with (Spring Boot default) Basic authentication,
|
||||
i.e. the password will be in the logs (or set it with
|
||||
`security.user.password` as normal). To turn on OAuth2 SSO all you
|
||||
need to do is bind the app to a service with the right
|
||||
credentials. For example, a
|
||||
http://docs.pivotal.io/pivotalcf/devguide/services/user-provided.html[user-provided
|
||||
service] can be created like this on PWS:
|
||||
|
||||
----
|
||||
$ cf create-user-provided-service sso -p '{clientId:"<my-client>",clientSecret:"<my-secret>",userInfoUri:"https://uaa.run.pivotal.io/userinfo",tokenUri: "https://login.run.pivotal.io/oauth/token",authorizationUri:"https://login.run.pivotal.io/oauth/authorize"}
|
||||
----
|
||||
|
||||
Then bind and restart the app:
|
||||
|
||||
----
|
||||
$ cf bind app sso
|
||||
$ cf restart app
|
||||
----
|
||||
|
||||
and visit it in a browser. It will redirect to the Cloud Foundry (PWS)
|
||||
login server instead of challenging for Basic authentication. The
|
||||
`clientId` and `clientSecret` are credentials of a registered client
|
||||
in Cloud Foundry. To get a Cloud Foundry client registration for
|
||||
testing please ask your local platform administrator if it's a private
|
||||
instance).
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
= Spring Cloud for Cloud Foundry
|
||||
|
||||
include::intro.adoc[]
|
||||
|
||||
== Quickstart
|
||||
|
||||
include::quickstart.adoc[]
|
||||
|
||||
== How Does it Work?
|
||||
|
||||
=== OAuth2 Single Sign On
|
||||
|
||||
Spring Cloud Security provides the `@EnableOAuth2Sso` annotation and
|
||||
binds the app to environment properties in `spring.oauth2.\*`. Spring Cloud
|
||||
for Cloud Foundry just sets up default environment properties so that
|
||||
it all just works if you bind to a Cloud Foundry service instance
|
||||
called "sso". The service credentials are mapped to the SSO
|
||||
properties, i.e. (from `spring.oauth2.client.*`) `clientId`, `clientSecret`,
|
||||
`tokenUri`, `authorizationUri`, (and from `spring.oauth2.resource.*`)
|
||||
`userInfoUri`, `tokenInfoUri`, `keyValue`, `keyUri`. Refer to the
|
||||
Spring Cloud Security documentation for details of which combinations
|
||||
will work together. The main thing is that in Cloud Foundry you only
|
||||
need one service to cover all the necessary credentials.
|
||||
|
||||
To use a different service instance name (i.e. not "sso") just set
|
||||
`spring.oauth2.sso.serviceId` to your custom name.
|
||||
|
||||
=== JWT Tokens
|
||||
|
||||
Spring Cloud Security already has support for decoding JWT tokens if
|
||||
you just provide the verification key (as an environment property). In
|
||||
Cloud Foundry you can pick that property up from a service binding
|
||||
(`keyValue` or `keyUri`).
|
||||
|
||||
For example the `keyUri` in PWS is
|
||||
"https://uaa.run.pivotal.io/token_key":
|
||||
|
||||
----
|
||||
$ curl https://uaa.run.pivotal.io/token_key
|
||||
{"alg":"SHA256withRSA","value":"-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----\n"}d
|
||||
----
|
||||
|
||||
=== OAuth2 Resource Server
|
||||
|
||||
Similarly, the `@EnableOAuth2Resource` annotation will protect your
|
||||
API endpoints if you bind to a service instance called "resource".
|
||||
The "sso" service above will work for a resource server as well (so
|
||||
just bind to that if it's there). If the OAuth2 tokens are JWTs (as in
|
||||
Cloud Foundry), it is common to use a separate service for resources
|
||||
to avoid a network round trip decoding the token on every access. A
|
||||
user-provided-service for an OAuth2 resource can be created like this
|
||||
on PWS:
|
||||
|
||||
----
|
||||
$ cf create-user-provided-service resource -p '{keyUri:"https://uaa.run.pivotal.io/token_key"}
|
||||
----
|
||||
|
||||
To use JWT you need to add the verification key as either
|
||||
`keyValue` or `keyUri` (these could be added to the "sso"
|
||||
service or the "resource" service if you have one).
|
||||
|
||||
To use a different sercice instance name (i.e. not "resource" or
|
||||
"sso") just set `spring.oauth2.resource.serviceId` to your custom name.
|
||||
|
||||
=== Default Environment Keys
|
||||
|
||||
The precise mapppings are as follows:
|
||||
|
||||
* `spring.oauth2.sso.\*` to `vcap.services.${spring.oauth2.sso.serviceId:sso}.credentials.*`
|
||||
|
||||
* `spring.oauth2.client.\*` to `vcap.services.${spring.oauth2.sso.serviceId:sso}.credentials.tokenUri:${vcap.services.${spring.oauth2.resource.serviceId:resource}.credentials.*`
|
||||
|
||||
* `spring.oauth2.resource.(jwt).\*` to `vcap.services.${spring.oauth2.resource.serviceId:resource}.credentials.tokenUri:${vcap.services.${spring.oauth2.sso.serviceId:sso}.credentials.*`
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.cloudfoundry.web;
|
||||
package org.springframework.cloud.cloudfoundry.discovery;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
@@ -33,4 +33,4 @@ import org.springframework.context.annotation.Import;
|
||||
@Import(StickyFilterConfiguration.class)
|
||||
public @interface EnableStickyFilter {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.cloudfoundry.web;
|
||||
package org.springframework.cloud.cloudfoundry.discovery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
@@ -34,34 +34,33 @@ import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class StickyFilterConfiguration {
|
||||
|
||||
private String cookie = UUID.randomUUID().toString();
|
||||
|
||||
@Autowired
|
||||
public void init(EurekaInstanceConfigBean eurekaInstance) {
|
||||
eurekaInstance.getMetadataMap().put("cookie", cookie);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean stickyCloudFoundryFilter() {
|
||||
FilterRegistrationBean filter = new FilterRegistrationBean();
|
||||
filter.setOrder(Ordered.LOWEST_PRECEDENCE);
|
||||
filter.setFilter(new OncePerRequestFilter() {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
if (!response.containsHeader("Set-Cookie")) {
|
||||
response.addCookie(new Cookie("JSESSIONID", cookie));
|
||||
}
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
});
|
||||
return filter;
|
||||
}
|
||||
private String cookie = UUID.randomUUID().toString();
|
||||
|
||||
}
|
||||
@Autowired
|
||||
public void init(EurekaInstanceConfigBean eurekaInstance) {
|
||||
eurekaInstance.getMetadataMap().put("cookie", cookie);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean stickyCloudFoundryFilter() {
|
||||
FilterRegistrationBean filter = new FilterRegistrationBean();
|
||||
filter.setOrder(Ordered.LOWEST_PRECEDENCE);
|
||||
filter.setFilter(new OncePerRequestFilter() {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
if (!response.containsHeader("Set-Cookie")) {
|
||||
response.addCookie(new Cookie("JSESSIONID", cookie));
|
||||
}
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
});
|
||||
return filter;
|
||||
}
|
||||
|
||||
}
|
||||
30
spring-cloud-cloudfoundry-web/src/main/ruby/generate_readme.sh
Executable file
30
spring-cloud-cloudfoundry-web/src/main/ruby/generate_readme.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
base_dir = File.join(File.dirname(__FILE__),'../../..')
|
||||
src_dir = File.join(base_dir, "/src/main/asciidoc")
|
||||
require 'asciidoctor'
|
||||
require 'optparse'
|
||||
|
||||
options = {}
|
||||
file = "#{src_dir}/README.adoc"
|
||||
|
||||
OptionParser.new do |o|
|
||||
o.on('-o OUTPUT_FILE', 'Output file (default is stdout)') { |file| options[:to_file] = file unless file=='-' }
|
||||
o.on('-h', '--help') { puts o; exit }
|
||||
o.parse!
|
||||
end
|
||||
|
||||
file = ARGV[0] if ARGV.length>0
|
||||
|
||||
srcDir = File.dirname(file)
|
||||
out = "// Do not edit this file (e.g. go instead to src/main/asciidoc)\n\n"
|
||||
doc = Asciidoctor.load_file file, safe: :safe, parse: false
|
||||
out << doc.reader.read
|
||||
|
||||
unless options[:to_file]
|
||||
puts out
|
||||
else
|
||||
File.open(options[:to_file],'w+') do |file|
|
||||
file.write(out)
|
||||
end
|
||||
end
|
||||
@@ -1,42 +0,0 @@
|
||||
debug: true
|
||||
server:
|
||||
port: 8080
|
||||
spring:
|
||||
application:
|
||||
name: eureka
|
||||
security:
|
||||
basic:
|
||||
enabled: false
|
||||
management:
|
||||
context-path: /admin
|
||||
eureka:
|
||||
server:
|
||||
enabled: false
|
||||
spring:
|
||||
oauth2:
|
||||
sso:
|
||||
tokenUri: http://localhost:8080/uaa/oauth/token
|
||||
authorizationUri: http://localhost:8080/uaa/oauth/authorize
|
||||
clientId: app
|
||||
clientSecret: appclientsecret
|
||||
logging:
|
||||
level:
|
||||
com.netflix.discovery: 'OFF'
|
||||
org.springframework.security: DEBUG
|
||||
|
||||
---
|
||||
spring:
|
||||
profiles: github
|
||||
spring:
|
||||
oauth2:
|
||||
sso:
|
||||
tokenUri: https://github.com/login/oauth/access_token
|
||||
authorizationUri: https://github.com/login/oauth/authorize
|
||||
clientId: bd1c0a783ccdd1c9b9e4
|
||||
clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
|
||||
authenticationScheme: form
|
||||
resource:
|
||||
clientId: ${spring.oauth2.sso.clientId}
|
||||
userInfoUri: https://api.github.com/user
|
||||
preferTokenInfo: false
|
||||
|
||||
Reference in New Issue
Block a user