Commit 80eaf615 by Tetsushi Awano Committed by Johannes Edmeier

Support CloudFoundry out of the box

This commit enables client and server to work out of the box on CloudFoundry. The client derives the registration info (url and metadata) from the VCAP environment variables. The server computes a unique instanceId and the correct headers for the cf router based on the metadata provided by instances. Also suppots the CloudFoundry discovery client ootb. closes #434
parent 34d08e58
/*
* Copyright 2014-2018 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 de.codecentric.boot.admin.client.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@lombok.Data
@ConfigurationProperties("vcap.application")
public class CloudFoundryApplicationProperties {
private String instanceId;
private String instanceIndex;
private List<String> uris = new ArrayList<>();
}
......@@ -52,10 +52,10 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
@Configuration
@EnableConfigurationProperties({ClientProperties.class, InstanceProperties.class})
@ConditionalOnWebApplication
@Conditional(SpringBootAdminClientEnabledCondition.class)
@AutoConfigureAfter(WebMvcEndpointManagementContextConfiguration.class)
@EnableConfigurationProperties({ClientProperties.class, InstanceProperties.class})
public class SpringBootAdminClientAutoConfiguration {
@Configuration
......@@ -107,18 +107,6 @@ public class SpringBootAdminClientAutoConfiguration {
}
@Bean
@ConditionalOnMissingBean
public ApplicationFactory applicationFactory(InstanceProperties instance,
ManagementServerProperties management,
ServerProperties server,
PathMappedEndpoints pathMappedEndpoints,
WebEndpointProperties webEndpoint,
MetadataContributor metadataContributor) {
return new DefaultApplicationFactory(instance, management, server, pathMappedEndpoints, webEndpoint,
metadataContributor);
}
@Bean
@Qualifier("registrationTaskScheduler")
public TaskScheduler registrationTaskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
......@@ -154,3 +142,4 @@ public class SpringBootAdminClientAutoConfiguration {
return new StartupDateMetadataContributor();
}
}
/*
* Copyright 2014-2018 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 de.codecentric.boot.admin.client.config;
import de.codecentric.boot.admin.client.registration.CloudFoundryApplicationFactory;
import de.codecentric.boot.admin.client.registration.metadata.CloudFoundryMetadataContributor;
import de.codecentric.boot.admin.client.registration.metadata.MetadataContributor;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnWebApplication
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
@Conditional(SpringBootAdminClientEnabledCondition.class)
@EnableConfigurationProperties(CloudFoundryApplicationProperties.class)
@AutoConfigureBefore({SpringBootAdminClientAutoConfiguration.class})
public class SpringBootAdminClientCloudFoundryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public CloudFoundryMetadataContributor cloudFoundryMetadataContributor(CloudFoundryApplicationProperties cloudFoundryApplicationProperties) {
return new CloudFoundryMetadataContributor(cloudFoundryApplicationProperties);
}
@Bean
@ConditionalOnMissingBean
public CloudFoundryApplicationFactory applicationFactory(InstanceProperties instance,
ManagementServerProperties management,
ServerProperties server,
PathMappedEndpoints pathMappedEndpoints,
WebEndpointProperties webEndpoint,
MetadataContributor metadataContributor,
CloudFoundryApplicationProperties cfApplicationProperties) {
return new CloudFoundryApplicationFactory(instance, management, server, pathMappedEndpoints, webEndpoint,
metadataContributor, cfApplicationProperties);
}
}
......@@ -20,8 +20,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
......@@ -57,9 +55,8 @@ public class SpringBootAdminClientEnabledCondition extends SpringBootCondition {
}
private ClientProperties getClientProperties(ConditionContext context) {
Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(context.getEnvironment());
ClientProperties clientProperties = new ClientProperties(context.getEnvironment());
new Binder(sources).bind("spring.boot.admin.client", Bindable.ofInstance(clientProperties));
Binder.get(context.getEnvironment()).bind("spring.boot.admin.client", Bindable.ofInstance(clientProperties));
return clientProperties;
}
}
/*
* Copyright 2014-2018 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 de.codecentric.boot.admin.client.registration;
import de.codecentric.boot.admin.client.config.CloudFoundryApplicationProperties;
import de.codecentric.boot.admin.client.config.InstanceProperties;
import de.codecentric.boot.admin.client.registration.metadata.MetadataContributor;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
import org.springframework.boot.autoconfigure.web.ServerProperties;
public class CloudFoundryApplicationFactory extends DefaultApplicationFactory {
private final CloudFoundryApplicationProperties cfApplicationProperties;
public CloudFoundryApplicationFactory(InstanceProperties instance,
ManagementServerProperties management,
ServerProperties server,
PathMappedEndpoints pathMappedEndpoints,
WebEndpointProperties webEndpoint,
MetadataContributor metadataContributor,
CloudFoundryApplicationProperties cfApplicationProperties) {
super(instance, management, server, pathMappedEndpoints, webEndpoint, metadataContributor);
this.cfApplicationProperties = cfApplicationProperties;
}
@Override
protected String getServiceBaseUrl() {
if (cfApplicationProperties.getUris().isEmpty()) {
return null;
}
String uri = cfApplicationProperties.getUris().get(0).toLowerCase();
return "http://" + uri;
}
}
......@@ -81,29 +81,28 @@ public class DefaultApplicationFactory implements ApplicationFactory {
protected String getServiceUrl() {
if (instance.getServiceUrl() != null) {
return UriComponentsBuilder.fromUriString(instance.getServiceUrl()).toUriString();
return instance.getServiceUrl();
}
String baseUrl = instance.getServiceBaseUrl();
if (getLocalServerPort() == null && StringUtils.isEmpty(baseUrl)) {
throw new IllegalStateException("couldn't determine local port. Please supply service-base-url.");
return UriComponentsBuilder.fromUriString(getServiceBaseUrl()).path("/").toUriString();
}
UriComponentsBuilder builder;
protected String getServiceBaseUrl() {
String baseUrl = instance.getServiceBaseUrl();
if (!StringUtils.isEmpty(baseUrl)) {
builder = UriComponentsBuilder.fromUriString(baseUrl);
} else {
builder = UriComponentsBuilder.newInstance()
.scheme(getScheme(server.getSsl()))
.host(getServiceHost())
.port(getLocalServerPort());
return baseUrl;
}
return builder.path("/").path(getServerContextPath()).toUriString();
if (getLocalServerPort() == null) {
throw new IllegalStateException("couldn't determine local port. Please supply service-base-url.");
}
protected String getServerContextPath() {
return "";
return UriComponentsBuilder.newInstance()
.scheme(getScheme(server.getSsl()))
.host(getServiceHost())
.port(getLocalServerPort())
.toUriString();
}
protected String getManagementUrl() {
......@@ -125,10 +124,7 @@ public class DefaultApplicationFactory implements ApplicationFactory {
}
if (isManagementPortEqual()) {
return UriComponentsBuilder.fromHttpUrl(getServiceUrl())
.path("/")
.path(getDispatcherServletPrefix())
.toUriString();
return this.getServiceUrl();
}
Ssl ssl = management.getSsl() != null ? management.getSsl() : server.getSsl();
......@@ -139,10 +135,6 @@ public class DefaultApplicationFactory implements ApplicationFactory {
.toUriString();
}
protected String getDispatcherServletPrefix() {
return "";
}
protected boolean isManagementPortEqual() {
return getLocalManagementPort() == null || getLocalManagementPort().equals(getLocalServerPort());
}
......
......@@ -24,12 +24,15 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointPr
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.web.server.Ssl;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponentsBuilder;
public class ServletApplicationFactory extends DefaultApplicationFactory {
private final ServletContext servletContext;
private final ServerProperties.Servlet servlet;
private final ManagementServerProperties.Servlet managementServlet;
private final ServerProperties server;
private final ManagementServerProperties management;
private final InstanceProperties instance;
public ServletApplicationFactory(InstanceProperties instance,
ManagementServerProperties management,
......@@ -40,29 +43,58 @@ public class ServletApplicationFactory extends DefaultApplicationFactory {
MetadataContributor metadataContributor) {
super(instance, management, server, pathMappedEndpoints, webEndpoint, metadataContributor);
this.servletContext = servletContext;
this.servlet = server.getServlet();
this.managementServlet = management.getServlet();
this.server = server;
this.management = management;
this.instance = instance;
}
@Override
protected String getServiceUrl() {
if (instance.getServiceUrl() != null) {
return instance.getServiceUrl();
}
return UriComponentsBuilder.fromUriString(getServiceBaseUrl())
.path("/")
.path(getServerContextPath())
.toUriString();
}
@Override
protected String getManagementBaseUrl() {
return UriComponentsBuilder.fromHttpUrl(super.getManagementBaseUrl())
String baseUrl = instance.getManagementBaseUrl();
if (!StringUtils.isEmpty(baseUrl)) {
return baseUrl;
}
if (isManagementPortEqual()) {
return UriComponentsBuilder.fromHttpUrl(getServiceUrl())
.path("/")
.path(getDispatcherServletPrefix())
.path(getManagementContextPath())
.toUriString();
}
Ssl ssl = management.getSsl() != null ? management.getSsl() : server.getSsl();
return UriComponentsBuilder.newInstance()
.scheme(getScheme(ssl))
.host(getManagementHost())
.port(getLocalManagementPort())
.path(getManagementContextPath())
.toUriString();
}
protected String getManagementContextPath() {
return managementServlet.getContextPath();
return management.getServlet().getContextPath();
}
@Override
protected String getServerContextPath() {
return servletContext.getContextPath();
}
@Override
protected String getDispatcherServletPrefix() {
return servlet.getServletPrefix();
return server.getServlet().getServletPrefix();
}
}
/*
* Copyright 2014-2018 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 de.codecentric.boot.admin.client.registration.metadata;
import de.codecentric.boot.admin.client.config.CloudFoundryApplicationProperties;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.util.StringUtils;
public class CloudFoundryMetadataContributor implements MetadataContributor {
private final CloudFoundryApplicationProperties cfApplicationProperties;
public CloudFoundryMetadataContributor(CloudFoundryApplicationProperties cfApplicationProperties) {
this.cfApplicationProperties = cfApplicationProperties;
}
@Override
public Map<String, String> getMetadata() {
if (StringUtils.hasText(this.cfApplicationProperties.getInstanceId()) &&
StringUtils.hasText(this.cfApplicationProperties.getInstanceIndex())) {
Map<String, String> map = new HashMap<>();
map.put("applicationId", this.cfApplicationProperties.getInstanceId());
map.put("instanceId", this.cfApplicationProperties.getInstanceIndex());
return map;
}
return Collections.emptyMap();
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration
\ No newline at end of file
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration,\
de.codecentric.boot.admin.client.config.SpringBootAdminClientCloudFoundryAutoConfiguration
......@@ -58,7 +58,7 @@ public class SpringBootAdminClientAutoConfigurationTest {
@Test
public void nonWebEnvironment() {
load("spring.main.admin.url:http://localhost:8081", "spring.boot.admin.client.enabled:true",
load("spring.boot.admin.client.url:http://localhost:8081", "spring.boot.admin.client.enabled:true",
"spring.main.web-application-type:none");
assertThat(context.getBeansOfType(ApplicationRegistrator.class).isEmpty());
}
......
/*
* Copyright 2014-2018 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 de.codecentric.boot.admin.client.config;
import de.codecentric.boot.admin.client.registration.ApplicationFactory;
import de.codecentric.boot.admin.client.registration.CloudFoundryApplicationFactory;
import de.codecentric.boot.admin.client.registration.DefaultApplicationFactory;
import de.codecentric.boot.admin.client.registration.metadata.CloudFoundryMetadataContributor;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.test.context.support.TestPropertySourceUtils;
import static org.assertj.core.api.Assertions.assertThat;
public class SpringBootAdminClientCloudFoundryAutoConfigurationTest {
private ConfigurableApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void non_cloud_platform() {
load("spring.boot.admin.client.url:http://localhost:8081");
assertThat(context.getBean(ApplicationFactory.class)).isInstanceOf(DefaultApplicationFactory.class);
assertThat(context.getBeansOfType(CloudFoundryMetadataContributor.class)).isEmpty();
}
@Test
public void cloudfoundry() {
String vcap = "VCAP_APPLICATION:{\"application_users\":[]," +
"\"instance_id\":\"bb7935245adf3e650dfb7c58a06e9ece\"," +
"\"instance_index\":0,\"version\":\"3464e092-1c13-462e-a47c-807c30318a50\"," +
"\"name\":\"foo\",\"uris\":[\"foo.cfapps.io\"]," +
"\"started_at\":\"2013-05-29 02:37:59 +0000\"," +
"\"started_at_timestamp\":1369795079," +
"\"host\":\"0.0.0.0\",\"port\":61034," +
"\"limits\":{\"mem\":128,\"disk\":1024,\"fds\":16384}," +
"\"version\":\"3464e092-1c13-462e-a47c-807c30318a50\"," +
"\"name\":\"dsyerenv\",\"uris\":[\"dsyerenv.cfapps.io\"]," +
"\"users\":[],\"start\":\"2013-05-29 02:37:59 +0000\"," +
"\"state_timestamp\":1369795079}";
load("spring.boot.admin.client.url:http://localhost:8081", vcap);
ApplicationFactory factory = context.getBean(ApplicationFactory.class);
CloudFoundryMetadataContributor contributor = context.getBean(CloudFoundryMetadataContributor.class);
assertThat(contributor.getMetadata()).containsEntry("applicationId", "bb7935245adf3e650dfb7c58a06e9ece")
.containsEntry("instanceId", "0");
assertThat(factory).isInstanceOf(CloudFoundryApplicationFactory.class);
assertThat(factory.createApplication().getServiceUrl()).isEqualTo("http://dsyerenv.cfapps.io/");
}
private void load(final String... envValues) {
SpringApplication springApplication = new SpringApplication(TestClientApplication.class);
springApplication.addListeners(new TestEnvListener(envValues));
this.context = springApplication.run("--server.port=0");
}
@Order(Ordered.HIGHEST_PRECEDENCE)
private static class TestEnvListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
private final String[] values;
private TestEnvListener(String[] values) {
this.values = values;
}
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(event.getEnvironment(), values);
}
}
@Configuration
@EnableAutoConfiguration
static class TestClientApplication {
}
}
/*
* Copyright 2014-2018 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 de.codecentric.boot.admin.client.registration;
import de.codecentric.boot.admin.client.config.CloudFoundryApplicationProperties;
import de.codecentric.boot.admin.client.config.InstanceProperties;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class CloudFoundryApplicationFactoryTest {
private InstanceProperties instanceProperties = new InstanceProperties();
private ServerProperties server = new ServerProperties();
private ManagementServerProperties management = new ManagementServerProperties();
private PathMappedEndpoints pathMappedEndpoints = mock(PathMappedEndpoints.class);
private WebEndpointProperties webEndpoint = new WebEndpointProperties();
private CloudFoundryApplicationProperties cfApplicationProperties = new CloudFoundryApplicationProperties();
private CloudFoundryApplicationFactory factory = new CloudFoundryApplicationFactory(instanceProperties, management,
server, pathMappedEndpoints, webEndpoint, () -> singletonMap("contributor", "test"), cfApplicationProperties);
@Before
public void setup() {
instanceProperties.setName("test");
}
@Test
public void should_use_application_uri() {
when(pathMappedEndpoints.getPath("health")).thenReturn("/actuator/health");
cfApplicationProperties.setUris(singletonList("application"));
Application app = factory.createApplication();
assertThat(app.getManagementUrl()).isEqualTo("http://application/actuator");
assertThat(app.getHealthUrl()).isEqualTo("http://application/actuator/health");
assertThat(app.getServiceUrl()).isEqualTo("http://application/");
}
}
/*
* Copyright 2014-2018 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 de.codecentric.boot.admin.client.registration.metadata;
import de.codecentric.boot.admin.client.config.CloudFoundryApplicationProperties;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class CloudFoundryMetadataContributorTest {
@Test
public void should_return_empty_metadata() {
CloudFoundryMetadataContributor contributor = new CloudFoundryMetadataContributor(
new CloudFoundryApplicationProperties());
assertThat(contributor.getMetadata()).isEmpty();
}
@Test
public void should_return_metadata() {
CloudFoundryApplicationProperties cfApplicationProperties = new CloudFoundryApplicationProperties();
cfApplicationProperties.setInstanceId("appId");
cfApplicationProperties.setInstanceIndex("1");
CloudFoundryMetadataContributor contributor = new CloudFoundryMetadataContributor(cfApplicationProperties);
assertThat(contributor.getMetadata()).containsEntry("applicationId", "appId").containsEntry("instanceId", "1");
}
}
/*
* Copyright 2014-2018 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 de.codecentric.boot.admin.server.config;
import de.codecentric.boot.admin.server.services.CloudFoundryInstanceIdGenerator;
import de.codecentric.boot.admin.server.services.HashingInstanceUrlIdGenerator;
import de.codecentric.boot.admin.server.services.InstanceIdGenerator;
import de.codecentric.boot.admin.server.web.client.CloudFoundryHttpHeaderProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
@AutoConfigureBefore({AdminServerAutoConfiguration.class})
public class AdminServerCloudFoundryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public InstanceIdGenerator instanceIdGenerator() {
return new CloudFoundryInstanceIdGenerator(new HashingInstanceUrlIdGenerator());
}
@Bean
@ConditionalOnMissingBean
public CloudFoundryHttpHeaderProvider cloudFoundryHttpHeaderProvider() {
return new CloudFoundryHttpHeaderProvider();
}
}
/*
* Copyright 2014-2018 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 de.codecentric.boot.admin.server.services;
import de.codecentric.boot.admin.server.domain.values.InstanceId;
import de.codecentric.boot.admin.server.domain.values.Registration;
import org.springframework.util.StringUtils;
/**
* Generates CF instance uniqueId "applicationId:instanceId" for CloudFoundry instance.
* Uses a fallback InstanceIdGenerator when the metadata isn't present.
*
* @author Tetsushi Awano
*/
public class CloudFoundryInstanceIdGenerator implements InstanceIdGenerator {
private final InstanceIdGenerator fallbackIdGenerator;
public CloudFoundryInstanceIdGenerator(InstanceIdGenerator fallbackIdGenerator) {
this.fallbackIdGenerator = fallbackIdGenerator;
}
@Override
public InstanceId generateId(Registration registration) {
String applicationId = registration.getMetadata().get("applicationId");
String instanceId = registration.getMetadata().get("instanceId");
if (StringUtils.hasText(applicationId) && StringUtils.hasText(instanceId)) {
return InstanceId.of(String.format("%s:%s", applicationId, instanceId));
}
return fallbackIdGenerator.generateId(registration);
}
}
/*
* Copyright 2014-2018 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 de.codecentric.boot.admin.server.web.client;
import de.codecentric.boot.admin.server.domain.entities.Instance;
import org.springframework.http.HttpHeaders;
import org.springframework.util.StringUtils;
/**
* Provides CloudFoundry related X-CF-APP-INSTANCE header for the {@link Instance} using the metadata for
* "applicationId" and "instanceId".
*
* @author Tetsushi Awano
*/
public class CloudFoundryHttpHeaderProvider implements HttpHeadersProvider {
@Override
public HttpHeaders getHeaders(Instance instance) {
String applicationId = instance.getRegistration().getMetadata().get("applicationId");
String instanceId = instance.getRegistration().getMetadata().get("instanceId");
if (StringUtils.hasText(applicationId) && StringUtils.hasText(instanceId)) {
HttpHeaders headers = new HttpHeaders();
headers.set("X-CF-APP-INSTANCE", applicationId + ":" + instanceId);
return headers;
}
return HttpHeaders.EMPTY;
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration,\
de.codecentric.boot.admin.server.config.AdminServerDiscoveryAutoConfiguration,\
de.codecentric.boot.admin.server.config.AdminServerHazelcastAutoConfiguration
\ No newline at end of file
de.codecentric.boot.admin.server.config.AdminServerHazelcastAutoConfiguration,\
de.codecentric.boot.admin.server.config.AdminServerCloudFoundryAutoConfiguration
/*
* Copyright 2014-2018 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 de.codecentric.boot.admin.server.services;
import de.codecentric.boot.admin.server.domain.values.InstanceId;
import de.codecentric.boot.admin.server.domain.values.Registration;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class CloudFoundryInstanceIdGeneratorTest {
private CloudFoundryInstanceIdGenerator instance = new CloudFoundryInstanceIdGenerator(
new HashingInstanceUrlIdGenerator());
@Test
public void test_cloud_foundry_instance_id() {
Registration registration = Registration.create("foo", "http://health")
.metadata("applicationId", "549e64cf-a478-423d-9d6d-02d803a028a8")
.metadata("instanceId", "0")
.build();
assertThat(instance.generateId(registration)).isEqualTo(
InstanceId.of("549e64cf-a478-423d-9d6d-02d803a028a8:0"));
}
@Test
public void test_health_url_instance_id() {
Registration registration = Registration.create("foo", "http://health").build();
assertThat(instance.generateId(registration)).isEqualTo(InstanceId.of("8f9960c48139"));
}
}
/*
* Copyright 2014-2018 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 de.codecentric.boot.admin.server.web.client;
import de.codecentric.boot.admin.server.domain.entities.Instance;
import de.codecentric.boot.admin.server.domain.values.InstanceId;
import de.codecentric.boot.admin.server.domain.values.Registration;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class CloudFoundryHttpHeaderProviderTest {
private CloudFoundryHttpHeaderProvider headersProvider = new CloudFoundryHttpHeaderProvider();
@Test
public void test_cloud_foundry_header() {
Registration registration = Registration.create("foo", "http://health")
.metadata("applicationId", "549e64cf-a478-423d-9d6d-02d803a028a8")
.metadata("instanceId", "0")
.build();
Instance instance = Instance.create(InstanceId.of("id")).register(registration);
assertThat(headersProvider.getHeaders(instance).get("X-CF-APP-INSTANCE")).containsOnly(
"549e64cf-a478-423d-9d6d-02d803a028a8:0");
}
@Test
public void test_no_header() {
Registration registration = Registration.create("foo", "http://health").build();
Instance instance = Instance.create(InstanceId.of("id")).register(registration);
assertThat(headersProvider.getHeaders(instance)).isEmpty();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment