Unverified Commit ef508b42 by Spencer Gibb

Merge branch 'master' into 2.0.x

parents 4995844f df7f3109
......@@ -688,6 +688,9 @@ turbine:
appConfig: customers
----
If you need to customize which cluster names should be used by Turbine (you don't want to store cluster names in
`turbine.aggregator.clusterConfig` configuration) provide a bean of type `TurbineClustersProvider`.
The `clusterName` can be customized by a SPEL expression in `turbine.clusterNameExpression` with root an instance of `InstanceInfo`. The default value is `appName`, which means that the Eureka serviceId ends up as the cluster key (i.e. the `InstanceInfo` for customers has an `appName` of "CUSTOMERS"). A different example would be `turbine.clusterNameExpression=aSGName`, which would get the cluster name from the AWS ASG name. Another example:
----
......@@ -830,6 +833,7 @@ To set the `IRule` for a service name `users` you could set the following:
----
users:
ribbon:
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
----
......@@ -1497,9 +1501,39 @@ The location of the backend can be specified as either a "serviceId"
url: http://example.com/users_service
----
These simple url-routes don't get executed as a `HystrixCommand` nor can you loadbalance multiple URLs with Ribbon.
To achieve this, specify a service-route and configure a Ribbon client for the
serviceId (this currently requires disabling Eureka support in Ribbon:
These simple url-routes don't get executed as a `HystrixCommand` nor do they loadbalance multiple URLs with Ribbon.
To achieve this, you can specify a `serviceId` with a static list of servers:
.application.yml
[source,yaml]
----
zuul:
routes:
echo:
path: /myusers/**
serviceId: myusers-service
stripPrefix: true
hystrix:
command:
myusers-service:
execution:
isolation:
thread:
timeoutInMilliseconds: ...
myusers-service:
ribbon:
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
ListOfServers: http://example1.com,http://example2.com
ConnectTimeout: 1000
ReadTimeout: 3000
MaxTotalHttpConnections: 500
MaxConnectionsPerHost: 100
----
Another method is specifiying a service-route and configure a Ribbon client for the
serviceId (this requires disabling Eureka support in Ribbon:
see <<spring-cloud-ribbon-without-eureka,above for more information>>), e.g.
.application.yml
......@@ -1697,11 +1731,17 @@ Security. The assumption in this case is that the downstream services
might add these headers too, and we want the values from the proxy.
To not discard these well known security headers in case Spring Security is on the classpath you can set `zuul.ignoreSecurityHeaders` to `false`. This can be useful if you disabled the HTTP Security response headers in Spring Security and want the values provided by downstream services
=== The Routes Endpoint
=== Management Endpoints
If you are using `@EnableZuulProxy` with tha Spring Boot Actuator you
will enable (by default) an additional endpoint, available via HTTP as
`/routes`. A GET to this endpoint will return a list of the mapped
If you are using `@EnableZuulProxy` with the Spring Boot Actuator you
will enable (by default) two additional endpoints:
* Routes
* Filters
==== Routes Endpoint
A GET to the routes endpoint at `/routes` will return a list of the mapped
routes:
.GET /routes
......@@ -1740,6 +1780,12 @@ NOTE: the routes should respond automatically to changes in the
service catalog, but the POST to /routes is a way to force the change
to happen immediately.
==== Filters Endpoint
A GET to the filters endpoint at `/filters` will return a map of Zuul
filters by type. For each filter type in the map, you will find a list
of all the filters of that type, along with their details.
=== Strangulation Patterns and Local Forwards
A common pattern when migrating an existing application or API is to
......
......@@ -28,7 +28,7 @@
<spring-cloud-config.version>2.0.0.BUILD-SNAPSHOT</spring-cloud-config.version>
<spring-cloud-stream.version>Elmhurst.BUILD-SNAPSHOT</spring-cloud-stream.version>
<!-- Has to be a stable version (not one that depends on this version of netflix): -->
<spring-cloud-contract.version>1.1.2.RELEASE</spring-cloud-contract.version>
<donotreplacespring-cloud-contract.version>1.1.2.RELEASE</donotreplacespring-cloud-contract.version>
<!-- Plugin versions -->
<maven-compiler-plugin.version>3.6.1</maven-compiler-plugin.version>
......@@ -115,7 +115,7 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-dependencies</artifactId>
<version>${spring-cloud-contract.version}</version>
<version>${donotreplacespring-cloud-contract.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
......
package org.springframework.cloud.netflix.zuul;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.filters.FilterRegistry;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedResource;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* Endpoint for listing Zuul filters.
*
* @author Daryl Robbins
* @author Gregor Zurowski
*/
@ManagedResource(description = "List Zuul filters")
public class FiltersEndpoint extends AbstractEndpoint<Map<String, List<Map<String, Object>>>> {
private static final String ID = "filters";
private final FilterRegistry filterRegistry;
public FiltersEndpoint(FilterRegistry filterRegistry) {
super(ID, true);
this.filterRegistry = filterRegistry;
}
@ManagedAttribute
@Override
public Map<String, List<Map<String, Object>>> invoke() {
// Map of filters by type
final Map<String, List<Map<String, Object>>> filterMap = new TreeMap<>();
for (ZuulFilter filter : this.filterRegistry.getAllFilters()) {
// Ensure that we have a list to store filters of each type
if (!filterMap.containsKey(filter.filterType())) {
filterMap.put(filter.filterType(), new ArrayList<Map<String, Object>>());
}
final Map<String, Object> filterInfo = new LinkedHashMap<>();
filterInfo.put("class", filter.getClass().getName());
filterInfo.put("order", filter.filterOrder());
filterInfo.put("disabled", filter.isFilterDisabled());
filterInfo.put("static", filter.isStaticFilter());
filterMap.get(filter.filterType()).add(filterInfo);
}
return filterMap;
}
}
......@@ -17,9 +17,6 @@
package org.springframework.cloud.netflix.ribbon;
import static org.junit.Assert.assertEquals;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -27,6 +24,7 @@ import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoCon
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfigurationIntegrationTests.TestConfiguration;
import org.springframework.cloud.netflix.test.TestUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
......@@ -35,6 +33,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import static org.junit.Assert.assertEquals;
/**
* @author Dave Syer
*/
......@@ -48,6 +48,7 @@ public class RibbonAutoConfigurationIntegrationTests {
@Test
public void serverListIsConfigured() throws Exception {
TestUtils.assumeTestIgnored(RibbonAutoConfigurationIntegrationTests.class);
IClientConfig config = this.factory.getClientConfig("client");
assertEquals(25000,
config.getPropertyAsInteger(CommonClientConfigKey.ConnectTimeout, 3000));
......
/*
* Copyright 2013-2017 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.netflix.test;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assume.assumeThat;
public class TestUtils {
public static void assumeTestIgnored(Class clazz) {
assumeTestIgnored(clazz.getSimpleName());
}
public static void assumeTestIgnored(String name) {
assumeThat("Test ignored",
System.getenv("SPRING_CLOUD_NETFLIX_IGNORE_TESTS"),
not(containsString(name)));
}
}
package org.springframework.cloud.netflix.zuul;
import com.netflix.zuul.ZuulFilter;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
import static org.hibernate.validator.internal.util.Contracts.assertTrue;
import static org.junit.Assert.assertEquals;
/**
* Tests for Filters endpoint
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FiltersEndpointApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
value = { "server.contextPath: /app" })
public class FiltersEndpointTests {
@Autowired
private FiltersEndpoint endpoint;
@Test
public void getFilters() {
final Map<String, List<Map<String, Object>>> filters = endpoint.invoke();
boolean foundFilter = false;
if (filters.containsKey("sample")) {
for (Map<String, Object> filterInfo : filters.get("sample")) {
if (TestFilter.class.getName().equals(filterInfo.get("class"))) {
foundFilter = true;
// Verify filter's attributes
assertEquals(0, filterInfo.get("order"));
break; // the search is over
}
}
}
assertTrue(foundFilter, "Could not find expected sample filter from filters endpoint");
}
}
@Configuration
@EnableAutoConfiguration
@RestController
@EnableZuulProxy
class FiltersEndpointApplication {
@Bean
public ZuulFilter sampleFilter() {
return new TestFilter();
}
}
class TestFilter extends ZuulFilter {
@Override
public String filterType() {
return "sample";
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
return null;
}
@Override
public int filterOrder() {
return 0;
}
}
\ No newline at end of file
......@@ -24,5 +24,6 @@ import com.netflix.appinfo.InstanceInfo;
*/
public interface CloudEurekaInstanceConfig extends EurekaInstanceConfig {
void setNonSecurePort(int port);
void setSecurePort(int securePort);
InstanceInfo.InstanceStatus getInitialStatus();
}
......@@ -17,8 +17,6 @@
package org.springframework.cloud.netflix.eureka;
import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceId;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
......@@ -62,7 +60,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertyResolver;
import org.springframework.util.StringUtils;
import com.netflix.appinfo.ApplicationInfoManager;
......@@ -73,6 +70,8 @@ import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.EurekaClientConfig;
import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceId;
/**
* @author Dave Syer
* @author Spencer Gibb
......@@ -130,13 +129,21 @@ public class EurekaClientAutoConfiguration {
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils) {
String hostname = getProperty("eureka.instance.hostname");
boolean preferIpAddress = Boolean.parseBoolean(getProperty("eureka.instance.prefer-ip-address"));
boolean isSecurePortEnabled = Boolean.parseBoolean(getProperty("eureka.instance.secure-port-enabled"));
int nonSecurePort = Integer.valueOf(env.getProperty("server.port", env.getProperty("port", "8080")));
int managementPort = Integer.valueOf(env.getProperty("management.port", String.valueOf(nonSecurePort)));
String managementContextPath = env.getProperty("management.context-path", env.getProperty("server.servlet.context-path", "/"));
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
instance.setNonSecurePort(nonSecurePort);
instance.setInstanceId(getDefaultInstanceId(env));
instance.setPreferIpAddress(preferIpAddress);
if(isSecurePortEnabled) {
int securePort = Integer.valueOf(env.getProperty("server.port", env.getProperty("port", "8080")));
instance.setSecurePort(securePort);
}
if (managementPort != nonSecurePort && managementPort != 0) {
if (StringUtils.hasText(hostname)) {
instance.setHostname(hostname);
......@@ -152,6 +159,7 @@ public class EurekaClientAutoConfiguration {
if (StringUtils.hasText(healthCheckUrlPath)) {
instance.setHealthCheckUrlPath(healthCheckUrlPath);
}
String scheme = instance.getSecurePortEnabled() ? "https" : "http";
try {
URL base = new URL(scheme, instance.getHostname(), managementPort, managementContextPath);
......
......@@ -36,6 +36,7 @@ import org.springframework.core.Ordered;
* @author Spencer Gibb
* @author Jon Schneider
* @author Jakub Narloch
* @author raiyan
*/
public class EurekaAutoServiceRegistration implements AutoServiceRegistration, SmartLifecycle, Ordered {
......@@ -61,9 +62,15 @@ public class EurekaAutoServiceRegistration implements AutoServiceRegistration, S
@Override
public void start() {
// only set the port if the nonSecurePort is 0 and this.port != 0
if (this.port.get() != 0 && this.registration.getNonSecurePort() == 0) {
this.registration.setNonSecurePort(this.port.get());
// only set the port if the nonSecurePort or securePort is 0 and this.port != 0
if (this.port.get() != 0) {
if (this.registration.getNonSecurePort() == 0) {
this.registration.setNonSecurePort(this.port.get());
}
if (this.registration.getSecurePort() == 0 && this.registration.isSecure()) {
this.registration.setSecurePort(this.port.get());
}
}
// only initialize if nonSecurePort is greater than 0 and it isn't already running
......
......@@ -193,6 +193,14 @@ public class EurekaRegistration implements Registration, Closeable {
return this.instanceConfig.getNonSecurePort();
}
public void setSecurePort(int port) {
this.instanceConfig.setSecurePort(port);
}
public int getSecurePort() {
return this.instanceConfig.getSecurePort();
}
@Override
public void close() throws IOException {
this.eurekaClient.shutdown();
......
......@@ -99,6 +99,23 @@ public class EurekaClientAutoConfigurationTests {
}
@Test
public void securePortPeriods() {
testSecurePort("server.port");
}
@Test
public void securePortUnderscores() {
testSecurePort("SERVER_PORT");
}
@Test
public void securePort() {
testSecurePort("PORT");
assertEquals("eurekaClient",
this.context.getBeanDefinition("eurekaClient").getFactoryMethodName());
}
@Test
public void managementPort() {
TestPropertyValues.of("server.port=8989",
"management.port=9999").applyTo(this.context);
......@@ -378,6 +395,13 @@ public class EurekaClientAutoConfigurationTests {
assertEquals(8888, getInstanceConfig().getNonSecurePort());
}
private void testSecurePort(String propName) {
EnvironmentTestUtils.addEnvironment(this.context, "eureka.instance.securePortEnabled=true");
addEnvironment(this.context, propName + ":8443");
setupContext();
assertEquals(8443, getInstanceConfig().getSecurePort());
}
private EurekaInstanceConfigBean getInstanceConfig() {
return this.context.getBean(EurekaInstanceConfigBean.class);
}
......
......@@ -15,7 +15,7 @@
<description>Spring Cloud Netflix Hystrix Contract</description>
<properties>
<main.basedir>${basedir}/..</main.basedir>
<spring-cloud-contract.version>1.1.2.RELEASE</spring-cloud-contract.version>
<donotreplacespring-cloud-contract.version>1.1.2.RELEASE</donotreplacespring-cloud-contract.version>
</properties>
<dependencies>
<dependency>
......@@ -30,7 +30,7 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-verifier</artifactId>
<version>${spring-cloud-contract.version}</version>
<version>${donotreplacespring-cloud-contract.version}</version>
</dependency>
</dependencies>
</project>
......@@ -101,7 +101,7 @@
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<version>${donotreplacespring-cloud-contract.version}</version>
<extensions>true</extensions>
<configuration>
<baseClassMappings>
......
/*
* Copyright 2013-2017 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.netflix.turbine;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.List;
/**
* Provides clusters names for Turbine based on configuration value.
*
* @author Anastasiia Smirnova
*/
public class ConfigurationBasedTurbineClustersProvider implements TurbineClustersProvider {
private static final Log log = LogFactory.getLog(ConfigurationBasedTurbineClustersProvider.class);
private final TurbineAggregatorProperties properties;
public ConfigurationBasedTurbineClustersProvider(TurbineAggregatorProperties turbineAggregatorProperties) {
this.properties = turbineAggregatorProperties;
}
@Override
public List<String> getClusterNames() {
List<String> clusterNames = properties.getClusterConfig();
log.trace("Using clusters names: " + clusterNames);
return clusterNames;
}
}
/*
* Copyright 2013-2017 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.netflix.turbine;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.ArrayList;
import java.util.List;
/**
* Provides clusters names for Turbine based on applications names registered in Eureka.
*
* @author Anastasiia Smirnova
*/
public class EurekaBasedTurbineClustersProvider implements TurbineClustersProvider {
private static final Log log = LogFactory.getLog(EurekaBasedTurbineClustersProvider.class);
private final EurekaClient eurekaClient;
public EurekaBasedTurbineClustersProvider(EurekaClient eurekaClient) {
this.eurekaClient = eurekaClient;
}
@Override
public List<String> getClusterNames() {
Applications applications = eurekaClient.getApplications();
List<Application> registeredApplications = applications.getRegisteredApplications();
List<String> appNames = new ArrayList<>(registeredApplications.size());
for (Application application : registeredApplications) {
appNames.add(application.getName());
}
log.trace("Using clusters names: " + appNames);
return appNames;
}
}
......@@ -16,12 +16,8 @@
package org.springframework.cloud.netflix.turbine;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.DynamicStringProperty;
import com.netflix.turbine.data.AggDataFromCluster;
import com.netflix.turbine.discovery.Instance;
import com.netflix.turbine.handler.PerformanceCriteria;
......@@ -43,8 +39,11 @@ public class SpringAggregatorFactory implements ClusterMonitorFactory<AggDataFro
private static final Log log = LogFactory.getLog(SpringAggregatorFactory.class);
private static final DynamicStringProperty aggClusters = DynamicPropertyFactory
.getInstance().getStringProperty("turbine.aggregator.clusterConfig", null);
private final TurbineClustersProvider clustersProvider;
public SpringAggregatorFactory(TurbineClustersProvider clustersProvider) {
this.clustersProvider = clustersProvider;
}
/**
* @return {@link com.netflix.turbine.monitor.cluster.ClusterMonitor}<
......@@ -73,7 +72,7 @@ public class SpringAggregatorFactory implements ClusterMonitorFactory<AggDataFro
@Override
public void initClusterMonitors() {
for (String clusterName : getClusterNames()) {
for (String clusterName : clustersProvider.getClusterNames()) {
ClusterMonitor<AggDataFromCluster> clusterMonitor = (ClusterMonitor<AggDataFromCluster>) findOrRegisterAggregateMonitor(clusterName);
clusterMonitor.registerListenertoClusterMonitor(this.StaticListener);
try {
......@@ -87,27 +86,12 @@ public class SpringAggregatorFactory implements ClusterMonitorFactory<AggDataFro
}
}
private List<String> getClusterNames() {
List<String> clusters = new ArrayList<String>();
String clusterNames = aggClusters.get();
if (clusterNames == null || clusterNames.trim().length() == 0) {
clusters.add("default");
}
else {
String[] parts = aggClusters.get().split(",");
for (String s : parts) {
clusters.add(s);
}
}
return clusters;
}
/**
* shutdown all configured cluster monitors
*/
@Override
public void shutdownClusterMonitors() {
for (String clusterName : getClusterNames()) {
for (String clusterName : clustersProvider.getClusterNames()) {
ClusterMonitor<AggDataFromCluster> clusterMonitor = (ClusterMonitor<AggDataFromCluster>) AggregateClusterMonitor
.findOrRegisterAggregateMonitor(clusterName);
clusterMonitor.stopMonitor();
......
/*
* Copyright 2013-2017 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.netflix.turbine;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* @author Anastasiia Smirnova
*/
@ConfigurationProperties("turbine.aggregator")
public class TurbineAggregatorProperties {
private static final String DEFAULT = "default";
/**
* The list of cluster names.
*/
private List<String> clusterConfig = Collections.singletonList(DEFAULT);
public List<String> getClusterConfig() {
return clusterConfig;
}
public void setClusterConfig(List<String> clusterConfig) {
this.clusterConfig = clusterConfig;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
TurbineAggregatorProperties that = (TurbineAggregatorProperties) o;
return Objects.equals(clusterConfig, that.clusterConfig);
}
@Override
public int hashCode() {
return Objects.hash(clusterConfig);
}
@Override
public String toString() {
return "TurbineAggregatorProperties{" + "clusterConfig='" + clusterConfig + '\''
+ '}';
}
}
/*
* Copyright 2013-2017 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.netflix.turbine;
import java.util.List;
/**
* Interface that gives possibility to customize which clusters names Turbine will use.
*
* @author Anastasiia Smirnova
*/
public interface TurbineClustersProvider {
List<String> getClusterNames();
}
......@@ -16,6 +16,7 @@
package org.springframework.cloud.netflix.turbine;
import com.netflix.turbine.monitor.cluster.ClusterMonitorFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
......@@ -43,18 +44,40 @@ public class TurbineHttpConfiguration {
}
@Bean
@ConditionalOnMissingBean(name = "turbineStreamServlet")
public ServletRegistrationBean turbineStreamServlet() {
return new ServletRegistrationBean(new TurbineStreamServlet(), "/turbine.stream");
}
@Bean
@ConditionalOnMissingBean
public TurbineProperties turbineProperties() {
return new TurbineProperties();
}
@Bean
public TurbineLifecycle turbineLifecycle(InstanceDiscovery instanceDiscovery) {
return new TurbineLifecycle(instanceDiscovery);
@ConditionalOnMissingBean
public TurbineAggregatorProperties turbineAggregatorProperties() {
return new TurbineAggregatorProperties();
}
@Bean
@ConditionalOnMissingBean
public TurbineLifecycle turbineLifecycle(InstanceDiscovery instanceDiscovery,
ClusterMonitorFactory<?> factory) {
return new TurbineLifecycle(instanceDiscovery, factory);
}
@Bean
@ConditionalOnMissingBean
public ClusterMonitorFactory clusterMonitorFactory(TurbineClustersProvider clustersProvider) {
return new SpringAggregatorFactory(clustersProvider);
}
@Bean
@ConditionalOnMissingBean
public TurbineClustersProvider clustersProvider(TurbineAggregatorProperties turbineAggregatorProperties) {
return new ConfigurationBasedTurbineClustersProvider(turbineAggregatorProperties);
}
@Configuration
......
......@@ -16,6 +16,7 @@
package org.springframework.cloud.netflix.turbine;
import com.netflix.turbine.monitor.cluster.ClusterMonitorFactory;
import org.springframework.context.SmartLifecycle;
import org.springframework.core.Ordered;
......@@ -29,11 +30,13 @@ import com.netflix.turbine.plugins.PluginsFactory;
public class TurbineLifecycle implements SmartLifecycle, Ordered {
private final InstanceDiscovery instanceDiscovery;
private final ClusterMonitorFactory<?> factory;
private boolean running;
private volatile boolean running;
public TurbineLifecycle(InstanceDiscovery instanceDiscovery) {
public TurbineLifecycle(InstanceDiscovery instanceDiscovery, ClusterMonitorFactory<?> factory) {
this.instanceDiscovery = instanceDiscovery;
this.factory = factory;
}
@Override
......@@ -48,7 +51,7 @@ public class TurbineLifecycle implements SmartLifecycle, Ordered {
@Override
public void start() {
PluginsFactory.setClusterMonitorFactory(new SpringAggregatorFactory());
PluginsFactory.setClusterMonitorFactory(factory);
PluginsFactory.setInstanceDiscovery(instanceDiscovery);
TurbineInit.init();
}
......
package org.springframework.cloud.netflix.turbine;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class ConfigurationBasedTurbineClustersProviderTest {
@Test
public void shouldReturnDefaultClusterIfConfigurationIsEmpty() throws Exception {
TurbineAggregatorProperties properties = new TurbineAggregatorProperties();
TurbineClustersProvider provider = new ConfigurationBasedTurbineClustersProvider(
properties);
List<String> clusterNames = provider.getClusterNames();
assertThat(clusterNames).containsOnly("default");
}
@Test
public void shouldReturnConfiguredClusters() throws Exception {
TurbineAggregatorProperties properties = new TurbineAggregatorProperties();
properties.setClusterConfig(Arrays.asList("cluster1", "cluster2", "cluster3"));
TurbineClustersProvider provider = new ConfigurationBasedTurbineClustersProvider(
properties);
List<String> clusterNames = provider.getClusterNames();
assertThat(clusterNames).containsOnly("cluster1", "cluster2", "cluster3");
}
}
\ No newline at end of file
package org.springframework.cloud.netflix.turbine;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import org.junit.Test;
import java.util.List;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class EurekaBasedTurbineClustersProviderTest {
EurekaClient eurekaClient = mock(EurekaClient.class);
TurbineClustersProvider provider = new EurekaBasedTurbineClustersProvider(eurekaClient);
@Test
public void shouldProvideAllClustersNames() throws Exception {
Applications applications = registeredApplications(asList(application("service1"),
application("service2"), application("service3")));
when(eurekaClient.getApplications()).thenReturn(applications);
List<String> clusterNames = provider.getClusterNames();
assertThat(clusterNames).containsOnly("service1", "service2", "service3");
}
private Applications registeredApplications(List<Application> registered) {
Applications applications = mock(Applications.class);
when(applications.getRegisteredApplications()).thenReturn(registered);
return applications;
}
private Application application(String name) {
Application application = mock(Application.class);
when(application.getName()).thenReturn(name);
return application;
}
}
\ No newline at end of file
package org.springframework.cloud.netflix.turbine;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment;
public class TurbineAggregatorPropertiesTest {
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@After
public void clear() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void shouldHaveDefaultConfiguration() throws Exception {
setupContext();
TurbineAggregatorProperties actual = getProperties();
assertThat(actual.getClusterConfig()).containsOnly("default");
}
@Test
public void shouldLoadCustomProperties() {
addEnvironment(this.context,
"turbine.aggregator.clusterConfig=cluster1, cluster2, cluster3");
setupContext();
TurbineAggregatorProperties actual = getProperties();
assertThat(actual.getClusterConfig()).containsOnly("cluster1", "cluster2",
"cluster3");
}
private void setupContext() {
this.context.register(TestConfiguration.class);
this.context.refresh();
}
private TurbineAggregatorProperties getProperties() {
return this.context.getBean(TurbineAggregatorProperties.class);
}
@Configuration
@EnableConfigurationProperties(TurbineAggregatorProperties.class)
static class TestConfiguration {
}
}
\ No newline at end of file
......@@ -57,6 +57,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import com.netflix.zuul.filters.FilterRegistry;
/**
* @author Spencer Gibb
* @author Dave Syer
......@@ -158,17 +160,24 @@ public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
@Configuration
@ConditionalOnClass(Health.class)
protected static class RoutesEndpointConfiguration {
protected static class EndpointConfiguration {
@Autowired(required = false)
private TraceRepository traces;
@Bean
@ConditionalOnEnabledEndpoint
public RoutesEndpoint zuulEndpoint(RouteLocator routeLocator) {
public RoutesEndpoint routesEndpoint(RouteLocator routeLocator) {
return new RoutesEndpoint(routeLocator);
}
@ConditionalOnEnabledEndpoint
@Bean
public FiltersEndpoint filtersEndpoint() {
FilterRegistry filterRegistry = FilterRegistry.instance();
return new FiltersEndpoint(filterRegistry);
}
@Bean
public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
TraceProxyRequestHelper helper = new TraceProxyRequestHelper();
......
......@@ -31,6 +31,9 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;
import org.springframework.cloud.netflix.zuul.RoutesMvcEndpoint;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.context.ApplicationEventPublisher;
......
......@@ -25,6 +25,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
......
......@@ -27,6 +27,7 @@ import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
......
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