Skip to content

Commit 80eaf61

Browse files
tetsushiawanojoshiste
authored andcommitted
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 codecentric#434
1 parent 34d08e5 commit 80eaf61

19 files changed

+689
-53
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2014-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package de.codecentric.boot.admin.client.config;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
import org.springframework.boot.context.properties.ConfigurationProperties;
22+
23+
@lombok.Data
24+
@ConfigurationProperties("vcap.application")
25+
public class CloudFoundryApplicationProperties {
26+
private String instanceId;
27+
private String instanceIndex;
28+
private List<String> uris = new ArrayList<>();
29+
}

spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientAutoConfiguration.java

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@
5252
import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
5353

5454
@Configuration
55-
@EnableConfigurationProperties({ClientProperties.class, InstanceProperties.class})
5655
@ConditionalOnWebApplication
5756
@Conditional(SpringBootAdminClientEnabledCondition.class)
5857
@AutoConfigureAfter(WebMvcEndpointManagementContextConfiguration.class)
58+
@EnableConfigurationProperties({ClientProperties.class, InstanceProperties.class})
5959
public class SpringBootAdminClientAutoConfiguration {
6060

6161
@Configuration
@@ -106,18 +106,6 @@ public ApplicationRegistrator registrator(ClientProperties client,
106106
return new ApplicationRegistrator(builder.build(), client, applicationFactory);
107107
}
108108

109-
@Bean
110-
@ConditionalOnMissingBean
111-
public ApplicationFactory applicationFactory(InstanceProperties instance,
112-
ManagementServerProperties management,
113-
ServerProperties server,
114-
PathMappedEndpoints pathMappedEndpoints,
115-
WebEndpointProperties webEndpoint,
116-
MetadataContributor metadataContributor) {
117-
return new DefaultApplicationFactory(instance, management, server, pathMappedEndpoints, webEndpoint,
118-
metadataContributor);
119-
}
120-
121109
@Bean
122110
@Qualifier("registrationTaskScheduler")
123111
public TaskScheduler registrationTaskScheduler() {
@@ -154,3 +142,4 @@ public StartupDateMetadataContributor startupDateMetadataContributor() {
154142
return new StartupDateMetadataContributor();
155143
}
156144
}
145+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2014-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package de.codecentric.boot.admin.client.config;
18+
19+
import de.codecentric.boot.admin.client.registration.CloudFoundryApplicationFactory;
20+
import de.codecentric.boot.admin.client.registration.metadata.CloudFoundryMetadataContributor;
21+
import de.codecentric.boot.admin.client.registration.metadata.MetadataContributor;
22+
23+
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
24+
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
25+
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
26+
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
27+
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
28+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
29+
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
30+
import org.springframework.boot.autoconfigure.web.ServerProperties;
31+
import org.springframework.boot.cloud.CloudPlatform;
32+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
33+
import org.springframework.context.annotation.Bean;
34+
import org.springframework.context.annotation.Conditional;
35+
import org.springframework.context.annotation.Configuration;
36+
37+
@Configuration
38+
@ConditionalOnWebApplication
39+
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
40+
@Conditional(SpringBootAdminClientEnabledCondition.class)
41+
@EnableConfigurationProperties(CloudFoundryApplicationProperties.class)
42+
@AutoConfigureBefore({SpringBootAdminClientAutoConfiguration.class})
43+
public class SpringBootAdminClientCloudFoundryAutoConfiguration {
44+
@Bean
45+
@ConditionalOnMissingBean
46+
public CloudFoundryMetadataContributor cloudFoundryMetadataContributor(CloudFoundryApplicationProperties cloudFoundryApplicationProperties) {
47+
return new CloudFoundryMetadataContributor(cloudFoundryApplicationProperties);
48+
}
49+
50+
@Bean
51+
@ConditionalOnMissingBean
52+
public CloudFoundryApplicationFactory applicationFactory(InstanceProperties instance,
53+
ManagementServerProperties management,
54+
ServerProperties server,
55+
PathMappedEndpoints pathMappedEndpoints,
56+
WebEndpointProperties webEndpoint,
57+
MetadataContributor metadataContributor,
58+
CloudFoundryApplicationProperties cfApplicationProperties) {
59+
return new CloudFoundryApplicationFactory(instance, management, server, pathMappedEndpoints, webEndpoint,
60+
metadataContributor, cfApplicationProperties);
61+
}
62+
}

spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/config/SpringBootAdminClientEnabledCondition.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
2121
import org.springframework.boot.context.properties.bind.Bindable;
2222
import org.springframework.boot.context.properties.bind.Binder;
23-
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
24-
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
2523
import org.springframework.context.annotation.ConditionContext;
2624
import org.springframework.core.type.AnnotatedTypeMetadata;
2725

@@ -57,9 +55,8 @@ public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeM
5755
}
5856

5957
private ClientProperties getClientProperties(ConditionContext context) {
60-
Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(context.getEnvironment());
6158
ClientProperties clientProperties = new ClientProperties(context.getEnvironment());
62-
new Binder(sources).bind("spring.boot.admin.client", Bindable.ofInstance(clientProperties));
59+
Binder.get(context.getEnvironment()).bind("spring.boot.admin.client", Bindable.ofInstance(clientProperties));
6360
return clientProperties;
6461
}
6562
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2014-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package de.codecentric.boot.admin.client.registration;
18+
19+
import de.codecentric.boot.admin.client.config.CloudFoundryApplicationProperties;
20+
import de.codecentric.boot.admin.client.config.InstanceProperties;
21+
import de.codecentric.boot.admin.client.registration.metadata.MetadataContributor;
22+
23+
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
24+
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
25+
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
26+
import org.springframework.boot.autoconfigure.web.ServerProperties;
27+
28+
public class CloudFoundryApplicationFactory extends DefaultApplicationFactory {
29+
private final CloudFoundryApplicationProperties cfApplicationProperties;
30+
31+
public CloudFoundryApplicationFactory(InstanceProperties instance,
32+
ManagementServerProperties management,
33+
ServerProperties server,
34+
PathMappedEndpoints pathMappedEndpoints,
35+
WebEndpointProperties webEndpoint,
36+
MetadataContributor metadataContributor,
37+
CloudFoundryApplicationProperties cfApplicationProperties) {
38+
super(instance, management, server, pathMappedEndpoints, webEndpoint, metadataContributor);
39+
this.cfApplicationProperties = cfApplicationProperties;
40+
}
41+
42+
@Override
43+
protected String getServiceBaseUrl() {
44+
if (cfApplicationProperties.getUris().isEmpty()) {
45+
return null;
46+
}
47+
48+
String uri = cfApplicationProperties.getUris().get(0).toLowerCase();
49+
return "http://" + uri;
50+
}
51+
}

spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/DefaultApplicationFactory.java

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -81,29 +81,28 @@ protected String getName() {
8181

8282
protected String getServiceUrl() {
8383
if (instance.getServiceUrl() != null) {
84-
return UriComponentsBuilder.fromUriString(instance.getServiceUrl()).toUriString();
84+
return instance.getServiceUrl();
8585
}
8686

87+
return UriComponentsBuilder.fromUriString(getServiceBaseUrl()).path("/").toUriString();
88+
}
89+
90+
protected String getServiceBaseUrl() {
8791
String baseUrl = instance.getServiceBaseUrl();
88-
if (getLocalServerPort() == null && StringUtils.isEmpty(baseUrl)) {
89-
throw new IllegalStateException("couldn't determine local port. Please supply service-base-url.");
90-
}
9192

92-
UriComponentsBuilder builder;
9393
if (!StringUtils.isEmpty(baseUrl)) {
94-
builder = UriComponentsBuilder.fromUriString(baseUrl);
95-
} else {
96-
builder = UriComponentsBuilder.newInstance()
97-
.scheme(getScheme(server.getSsl()))
98-
.host(getServiceHost())
99-
.port(getLocalServerPort());
94+
return baseUrl;
10095
}
10196

102-
return builder.path("/").path(getServerContextPath()).toUriString();
103-
}
97+
if (getLocalServerPort() == null) {
98+
throw new IllegalStateException("couldn't determine local port. Please supply service-base-url.");
99+
}
104100

105-
protected String getServerContextPath() {
106-
return "";
101+
return UriComponentsBuilder.newInstance()
102+
.scheme(getScheme(server.getSsl()))
103+
.host(getServiceHost())
104+
.port(getLocalServerPort())
105+
.toUriString();
107106
}
108107

109108
protected String getManagementUrl() {
@@ -125,10 +124,7 @@ protected String getManagementBaseUrl() {
125124
}
126125

127126
if (isManagementPortEqual()) {
128-
return UriComponentsBuilder.fromHttpUrl(getServiceUrl())
129-
.path("/")
130-
.path(getDispatcherServletPrefix())
131-
.toUriString();
127+
return this.getServiceUrl();
132128
}
133129

134130
Ssl ssl = management.getSsl() != null ? management.getSsl() : server.getSsl();
@@ -139,10 +135,6 @@ protected String getManagementBaseUrl() {
139135
.toUriString();
140136
}
141137

142-
protected String getDispatcherServletPrefix() {
143-
return "";
144-
}
145-
146138
protected boolean isManagementPortEqual() {
147139
return getLocalManagementPort() == null || getLocalManagementPort().equals(getLocalServerPort());
148140
}

spring-boot-admin-client/src/main/java/de/codecentric/boot/admin/client/registration/ServletApplicationFactory.java

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@
2424
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
2525
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
2626
import org.springframework.boot.autoconfigure.web.ServerProperties;
27+
import org.springframework.boot.web.server.Ssl;
28+
import org.springframework.util.StringUtils;
2729
import org.springframework.web.util.UriComponentsBuilder;
2830

2931
public class ServletApplicationFactory extends DefaultApplicationFactory {
3032
private final ServletContext servletContext;
31-
private final ServerProperties.Servlet servlet;
32-
private final ManagementServerProperties.Servlet managementServlet;
33+
private final ServerProperties server;
34+
private final ManagementServerProperties management;
35+
private final InstanceProperties instance;
3336

3437
public ServletApplicationFactory(InstanceProperties instance,
3538
ManagementServerProperties management,
@@ -40,29 +43,58 @@ public ServletApplicationFactory(InstanceProperties instance,
4043
MetadataContributor metadataContributor) {
4144
super(instance, management, server, pathMappedEndpoints, webEndpoint, metadataContributor);
4245
this.servletContext = servletContext;
43-
this.servlet = server.getServlet();
44-
this.managementServlet = management.getServlet();
46+
this.server = server;
47+
this.management = management;
48+
this.instance = instance;
4549
}
4650

51+
4752
@Override
48-
protected String getManagementBaseUrl() {
49-
return UriComponentsBuilder.fromHttpUrl(super.getManagementBaseUrl())
53+
protected String getServiceUrl() {
54+
if (instance.getServiceUrl() != null) {
55+
return instance.getServiceUrl();
56+
}
57+
58+
return UriComponentsBuilder.fromUriString(getServiceBaseUrl())
5059
.path("/")
60+
.path(getServerContextPath())
61+
.toUriString();
62+
}
63+
64+
@Override
65+
protected String getManagementBaseUrl() {
66+
String baseUrl = instance.getManagementBaseUrl();
67+
68+
if (!StringUtils.isEmpty(baseUrl)) {
69+
return baseUrl;
70+
}
71+
72+
if (isManagementPortEqual()) {
73+
return UriComponentsBuilder.fromHttpUrl(getServiceUrl())
74+
.path("/")
75+
.path(getDispatcherServletPrefix())
76+
.path(getManagementContextPath())
77+
.toUriString();
78+
}
79+
80+
Ssl ssl = management.getSsl() != null ? management.getSsl() : server.getSsl();
81+
return UriComponentsBuilder.newInstance()
82+
.scheme(getScheme(ssl))
83+
.host(getManagementHost())
84+
.port(getLocalManagementPort())
5185
.path(getManagementContextPath())
5286
.toUriString();
5387
}
5488

5589
protected String getManagementContextPath() {
56-
return managementServlet.getContextPath();
90+
return management.getServlet().getContextPath();
5791
}
5892

59-
@Override
6093
protected String getServerContextPath() {
6194
return servletContext.getContextPath();
6295
}
6396

64-
@Override
6597
protected String getDispatcherServletPrefix() {
66-
return servlet.getServletPrefix();
98+
return server.getServlet().getServletPrefix();
6799
}
68100
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2014-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package de.codecentric.boot.admin.client.registration.metadata;
18+
19+
import de.codecentric.boot.admin.client.config.CloudFoundryApplicationProperties;
20+
21+
import java.util.Collections;
22+
import java.util.HashMap;
23+
import java.util.Map;
24+
import org.springframework.util.StringUtils;
25+
26+
public class CloudFoundryMetadataContributor implements MetadataContributor {
27+
private final CloudFoundryApplicationProperties cfApplicationProperties;
28+
29+
public CloudFoundryMetadataContributor(CloudFoundryApplicationProperties cfApplicationProperties) {
30+
this.cfApplicationProperties = cfApplicationProperties;
31+
}
32+
33+
@Override
34+
public Map<String, String> getMetadata() {
35+
if (StringUtils.hasText(this.cfApplicationProperties.getInstanceId()) &&
36+
StringUtils.hasText(this.cfApplicationProperties.getInstanceIndex())) {
37+
Map<String, String> map = new HashMap<>();
38+
map.put("applicationId", this.cfApplicationProperties.getInstanceId());
39+
map.put("instanceId", this.cfApplicationProperties.getInstanceIndex());
40+
return map;
41+
}
42+
return Collections.emptyMap();
43+
}
44+
}

0 commit comments

Comments
 (0)