Browse Source
Add a CloudFoundryEndpointHandlerMapping that can expose actuator endpoints for Cloud Foundry "appsmanager" to use. See gh-7108pull/7086/head
10 changed files with 388 additions and 34 deletions
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/* |
||||
* Copyright 2012-2016 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.boot.actuate.cloudfoundry; |
||||
|
||||
import java.util.LinkedHashSet; |
||||
import java.util.Set; |
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration; |
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; |
||||
import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint; |
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter; |
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; |
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; |
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
||||
import org.springframework.boot.cloud.CloudPlatform; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.web.cors.CorsConfiguration; |
||||
|
||||
/** |
||||
* {@link EnableAutoConfiguration Auto-configuration} to expose actuator endpoints for |
||||
* cloud foundry to use. |
||||
* |
||||
* @author Madhura Bhave |
||||
* @since 1.5.0 |
||||
*/ |
||||
@Configuration |
||||
@ConditionalOnProperty(prefix = "management.cloudfoundry", name = "enabled", matchIfMissing = false) |
||||
@ConditionalOnBean(MvcEndpoints.class) |
||||
@AutoConfigureAfter(EndpointWebMvcAutoConfiguration.class) |
||||
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY) |
||||
public class CloudFoundryActuatorAutoConfiguration { |
||||
|
||||
@Bean |
||||
public CloudFoundryEndpointHandlerMapping cloudFoundryEndpointHandlerMapping( |
||||
MvcEndpoints mvcEndpoints) { |
||||
Set<NamedMvcEndpoint> endpoints = new LinkedHashSet<NamedMvcEndpoint>( |
||||
mvcEndpoints.getEndpoints(NamedMvcEndpoint.class)); |
||||
CloudFoundryEndpointHandlerMapping mapping = new CloudFoundryEndpointHandlerMapping( |
||||
endpoints, getCorsConfiguration()); |
||||
mapping.setPrefix("/cloudfoundryapplication"); |
||||
return mapping; |
||||
} |
||||
|
||||
private CorsConfiguration getCorsConfiguration() { |
||||
CorsConfiguration corsConfiguration = new CorsConfiguration(); |
||||
corsConfiguration.addAllowedOrigin(CorsConfiguration.ALL); |
||||
return corsConfiguration; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
|
||||
/* |
||||
* Copyright 2012-2016 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.boot.actuate.cloudfoundry; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Collection; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint; |
||||
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; |
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; |
||||
import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint; |
||||
import org.springframework.web.cors.CorsConfiguration; |
||||
import org.springframework.web.servlet.HandlerExecutionChain; |
||||
import org.springframework.web.servlet.HandlerInterceptor; |
||||
import org.springframework.web.servlet.HandlerMapping; |
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; |
||||
|
||||
/** |
||||
* {@link HandlerMapping} to map {@link Endpoint}s to Cloud Foundry specific URLs. |
||||
* |
||||
* @author Madhura Bhave |
||||
*/ |
||||
class CloudFoundryEndpointHandlerMapping extends EndpointHandlerMapping { |
||||
|
||||
CloudFoundryEndpointHandlerMapping(Collection<? extends NamedMvcEndpoint> endpoints) { |
||||
super(endpoints); |
||||
} |
||||
|
||||
CloudFoundryEndpointHandlerMapping(Set<NamedMvcEndpoint> endpoints, |
||||
CorsConfiguration corsConfiguration) { |
||||
super(endpoints, corsConfiguration); |
||||
} |
||||
|
||||
@Override |
||||
protected String getPath(MvcEndpoint endpoint) { |
||||
if (endpoint instanceof NamedMvcEndpoint) { |
||||
return "/" + ((NamedMvcEndpoint) endpoint).getName(); |
||||
} |
||||
return super.getPath(endpoint); |
||||
} |
||||
|
||||
@Override |
||||
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, |
||||
HttpServletRequest request) { |
||||
HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request); |
||||
HandlerInterceptor[] interceptors = addSecurityInterceptor( |
||||
chain.getInterceptors()); |
||||
return new HandlerExecutionChain(chain.getHandler(), interceptors); |
||||
} |
||||
|
||||
private HandlerInterceptor[] addSecurityInterceptor(HandlerInterceptor[] existing) { |
||||
List<HandlerInterceptor> interceptors = new ArrayList<HandlerInterceptor>(); |
||||
interceptors.add(new SecurityInterceptor()); |
||||
if (existing != null) { |
||||
interceptors.addAll(Arrays.asList(existing)); |
||||
} |
||||
return interceptors.toArray(new HandlerInterceptor[interceptors.size()]); |
||||
} |
||||
|
||||
/** |
||||
* Security interceptor to check cloud foundry token. |
||||
*/ |
||||
static class SecurityInterceptor extends HandlerInterceptorAdapter { |
||||
|
||||
@Override |
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, |
||||
Object handler) throws Exception { |
||||
// Currently open
|
||||
return true; |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -0,0 +1,98 @@
@@ -0,0 +1,98 @@
|
||||
/* |
||||
* Copyright 2012-2016 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.boot.actuate.cloudfoundry; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration; |
||||
import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration; |
||||
import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcManagementContextConfiguration; |
||||
import org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration; |
||||
import org.springframework.boot.actuate.autoconfigure.ManagementWebSecurityAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; |
||||
import org.springframework.boot.test.util.EnvironmentTestUtils; |
||||
import org.springframework.mock.web.MockServletContext; |
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link CloudFoundryActuatorAutoConfiguration}. |
||||
* |
||||
* @author Madhura Bhave |
||||
*/ |
||||
public class CloudFoundryActuatorAutoConfigurationTests { |
||||
|
||||
private AnnotationConfigWebApplicationContext context; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
this.context = new AnnotationConfigWebApplicationContext(); |
||||
this.context.setServletContext(new MockServletContext()); |
||||
this.context.register(SecurityAutoConfiguration.class, |
||||
WebMvcAutoConfiguration.class, |
||||
ManagementWebSecurityAutoConfiguration.class, |
||||
JacksonAutoConfiguration.class, |
||||
HttpMessageConvertersAutoConfiguration.class, |
||||
EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class, |
||||
ManagementServerPropertiesAutoConfiguration.class, |
||||
PropertyPlaceholderAutoConfiguration.class, |
||||
EndpointWebMvcManagementContextConfiguration.class, |
||||
CloudFoundryActuatorAutoConfiguration.class); |
||||
} |
||||
|
||||
@After |
||||
public void close() { |
||||
if (this.context != null) { |
||||
this.context.close(); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void cloudFoundryPlatformActive() throws Exception { |
||||
EnvironmentTestUtils.addEnvironment(this.context, "VCAP_APPLICATION:---", |
||||
"management.cloudfoundry.enabled:true"); |
||||
this.context.refresh(); |
||||
CloudFoundryEndpointHandlerMapping handlerMapping = this.context.getBean( |
||||
"cloudFoundryEndpointHandlerMapping", |
||||
CloudFoundryEndpointHandlerMapping.class); |
||||
assertThat(handlerMapping.getPrefix()).isEqualTo("/cloudfoundryapplication"); |
||||
} |
||||
|
||||
@Test |
||||
public void cloudFoundryPlatformInactive() throws Exception { |
||||
this.context.refresh(); |
||||
assertThat(this.context.containsBean("cloudFoundryEndpointHandlerMapping")) |
||||
.isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void cloudFoundryManagementEndpointsDisabled() throws Exception { |
||||
EnvironmentTestUtils.addEnvironment(this.context, "VCAP_APPLICATION=---", |
||||
"management.cloudfoundry.enabled:false"); |
||||
this.context.refresh(); |
||||
assertThat(this.context.containsBean("cloudFoundryEndpointHandlerMapping")) |
||||
.isFalse(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,81 @@
@@ -0,0 +1,81 @@
|
||||
/* |
||||
* Copyright 2012-2016 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.boot.actuate.cloudfoundry; |
||||
|
||||
import java.util.Arrays; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.boot.actuate.endpoint.AbstractEndpoint; |
||||
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter; |
||||
import org.springframework.mock.web.MockHttpServletRequest; |
||||
import org.springframework.web.servlet.HandlerExecutionChain; |
||||
import org.springframework.web.servlet.HandlerInterceptor; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link CloudFoundryEndpointHandlerMapping}. |
||||
* |
||||
* @author Madhura Bhave |
||||
*/ |
||||
public class CloudFoundryEndpointHandlerMappingTests { |
||||
|
||||
@Test |
||||
public void getHandlerExecutionChainShouldHaveSecurityInterceptor() throws Exception { |
||||
TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a")); |
||||
CloudFoundryEndpointHandlerMapping handlerMapping = new CloudFoundryEndpointHandlerMapping( |
||||
Arrays.asList(endpoint)); |
||||
HandlerExecutionChain handlerExecutionChain = handlerMapping |
||||
.getHandlerExecutionChain(endpoint, new MockHttpServletRequest()); |
||||
HandlerInterceptor[] interceptors = handlerExecutionChain.getInterceptors(); |
||||
assertThat(interceptors).hasAtLeastOneElementOfType( |
||||
CloudFoundryEndpointHandlerMapping.SecurityInterceptor.class); |
||||
} |
||||
|
||||
@Test |
||||
public void getHandlerExecutionChainWhenEndpointHasPathShouldMapAgainstName() |
||||
throws Exception { |
||||
TestMvcEndpoint testMvcEndpoint = new TestMvcEndpoint(new TestEndpoint("a")); |
||||
testMvcEndpoint.setPath("something-else"); |
||||
CloudFoundryEndpointHandlerMapping handlerMapping = new CloudFoundryEndpointHandlerMapping( |
||||
Arrays.asList(testMvcEndpoint)); |
||||
assertThat(handlerMapping.getPath(testMvcEndpoint)).isEqualTo("/a"); |
||||
} |
||||
|
||||
private static class TestEndpoint extends AbstractEndpoint<Object> { |
||||
|
||||
TestEndpoint(String id) { |
||||
super(id); |
||||
} |
||||
|
||||
@Override |
||||
public Object invoke() { |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
|
||||
private static class TestMvcEndpoint extends EndpointMvcAdapter { |
||||
|
||||
TestMvcEndpoint(TestEndpoint delegate) { |
||||
super(delegate); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue