diff --git a/docs/src/main/asciidoc/spring-cloud-commons.adoc b/docs/src/main/asciidoc/spring-cloud-commons.adoc index 69c481d3..5d30195e 100644 --- a/docs/src/main/asciidoc/spring-cloud-commons.adoc +++ b/docs/src/main/asciidoc/spring-cloud-commons.adoc @@ -616,3 +616,41 @@ HasFeatures localFeatures() { ---- Each of these beans should go in an appropriately guarded `@Configuration`. + + +=== Spring Cloud Compatibility Verification + +Due to the fact that some users have problem with setting up Spring Cloud application, we've decided +to add a compatibility verification mechanism. It will break if your current setup is not compatible +with Spring Cloud requirements, together with a report, showing what exactly went wrong. + +At the moment we verify which version of Spring Boot is added to your classpath. + +Example of a report + +---- +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +Your project setup is incompatible with our requirements due to following reasons: + +- Spring Boot [1.5.16.RELEASE] is not compatible with this Spring Cloud release train + + +Action: + +Consider applying the following actions: + +- Change Spring Boot version to one of the following versions [1.2.x, 1.3.x] . +You can find the latest Spring Boot versions here [https://spring.io/projects/spring-boot#learn]. +If you want to learn more about the Spring Cloud Release train compatibility, you can visit this page [https://spring.io/projects/spring-cloud#overview] and check the [Release Trains] section. +---- + + +In order to disable this feature, set `spring.cloud.compatibility-verifier.enabled` to `false`. +If you want to override the compatible Spring Boot versions, just set the +`spring.cloud.compatibility-verifier.compatible-boot-versions` property with a comma separated list +of compatible Spring Boot versions. \ No newline at end of file diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityNotMetException.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityNotMetException.java new file mode 100644 index 00000000..6be61a3f --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityNotMetException.java @@ -0,0 +1,29 @@ +/* + * Copyright 2002-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 org.springframework.cloud.configuration; + +import java.util.List; + +/** + * Exception thrown when the current setup is not compatible + * + * @author Marcin Grzejszczak + * @since 1.3.6 + */ +class CompatibilityNotMetException extends RuntimeException { + final List results; + + CompatibilityNotMetException(List results) { + this.results = results; + } +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityNotMetFailureAnalyzer.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityNotMetFailureAnalyzer.java new file mode 100644 index 00000000..c4703e99 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityNotMetFailureAnalyzer.java @@ -0,0 +1,46 @@ +package org.springframework.cloud.configuration; + +import java.util.List; + +import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; +import org.springframework.boot.diagnostics.FailureAnalysis; + +/** + * Analyzer for the {@link CompatibilityNotMetException}. Prints a list of found + * issues and actions that should be taken to fix them. + * + * @author Marcin Grzejszczak + * @since 1.3.6 + */ +public final class CompatibilityNotMetFailureAnalyzer extends AbstractFailureAnalyzer { + @Override + protected FailureAnalysis analyze(Throwable rootFailure, CompatibilityNotMetException cause) { + return new FailureAnalysis(getDescription(cause), getAction(cause), cause); + } + + private String getDescription(CompatibilityNotMetException ex) { + return String.format("Your project setup is incompatible with our requirements " + + "due to following reasons:%s", descriptions(ex.results)); + } + + private String descriptions(List results) { + StringBuilder builder = new StringBuilder("\n\n"); + for (VerificationResult result : results) { + builder.append("- ").append(result.description).append("\n"); + } + return builder.toString(); + } + + private String getAction(CompatibilityNotMetException ex) { + return String.format("Consider applying the following actions:%s", + actions(ex.results)); + } + + private String actions(List results) { + StringBuilder builder = new StringBuilder("\n\n"); + for (VerificationResult result : results) { + builder.append("- ").append(result.action).append("\n"); + } + return builder.toString(); + } +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityPredicate.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityPredicate.java new file mode 100644 index 00000000..5e564ed7 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityPredicate.java @@ -0,0 +1,21 @@ +/* + * Copyright 2002-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 org.springframework.cloud.configuration; + +/** + * So that can be used for jdk 7 - otherwise we would use a predicate + * @author Marcin Grzejszczak + */ +interface CompatibilityPredicate { + boolean isCompatible(); +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifier.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifier.java new file mode 100644 index 00000000..c6ae7b60 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifier.java @@ -0,0 +1,24 @@ +/* + * Copyright 2002-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 org.springframework.cloud.configuration; + +/** + * Implementations will verify the compatibility and return a result that + * says whether the check is compatible or not for the current release train + * + * @author Marcin Grzejszczak + * @since 1.3.6 + */ +interface CompatibilityVerifier { + VerificationResult verify(); +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierAutoConfiguration.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierAutoConfiguration.java new file mode 100644 index 00000000..c31916d4 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierAutoConfiguration.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002-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 org.springframework.cloud.configuration; + +import java.util.List; + +import org.springframework.boot.autoconfigure.AutoConfigureOrder; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +/** + * {@link EnableAutoConfiguration Auto-configuration} that fails the build fast for incompatible + * versions of dependencies (e.g. invalid version of Boot). + * + * @author Marcin Grzejszczak + * @since 1.3.6 + */ +@Configuration +@ConditionalOnProperty(value = "spring.cloud.compatibility-verifier.enabled", matchIfMissing = true) +@AutoConfigureOrder(0) +@EnableConfigurationProperties(CompatibilityVerifierProperties.class) +public class CompatibilityVerifierAutoConfiguration { + + @Bean + CompositeCompatibilityVerifier compositeCompatibilityVerifier(List verifiers) { + CompositeCompatibilityVerifier verifier = new CompositeCompatibilityVerifier(verifiers); + verifier.verifyDependencies(); + return verifier; + } + + @Bean + SpringBootVersionVerifier springBootVersionVerifier( + CompatibilityVerifierProperties properties) { + return new SpringBootVersionVerifier(properties.getCompatibleBootVersions()); + } + +} + diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierProperties.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierProperties.java new file mode 100644 index 00000000..09a17ca9 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierProperties.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002-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 org.springframework.cloud.configuration; + +import java.util.Collections; +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author Marcin Grzejszczak + */ +@ConfigurationProperties("spring.cloud.compatibility-verifier") +public class CompatibilityVerifierProperties { + + /** + * Enables creation of Spring Cloud compatibility verification. + */ + private boolean enabled; + + /** + * Default accepted versions for the Spring Boot dependency. You can set {@code x} + * for the patch version if you don't want to specify a concrete value. + * Example: {@code 3.4.x} + */ + private List compatibleBootVersions = Collections.singletonList("2.0.x"); + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public List getCompatibleBootVersions() { + return this.compatibleBootVersions; + } + + public void setCompatibleBootVersions(List compatibleBootVersions) { + this.compatibleBootVersions = compatibleBootVersions; + } +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompositeCompatibilityVerifier.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompositeCompatibilityVerifier.java new file mode 100644 index 00000000..7a01fc6c --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompositeCompatibilityVerifier.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002-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 org.springframework.cloud.configuration; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Iterates over {@link CompatibilityVerifier} and prepares a report if exceptions were found + */ +class CompositeCompatibilityVerifier { + + private static final Log log = LogFactory.getLog(CompositeCompatibilityVerifier.class); + + private final List verifiers; + + CompositeCompatibilityVerifier(List verifiers) { + this.verifiers = verifiers; + } + + void verifyDependencies() { + List errors = verifierErrors(); + if (errors.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("All conditions are passing"); + } + return; + } + throw new CompatibilityNotMetException(errors); + } + + private List verifierErrors() { + List errors = new ArrayList<>(); + for (CompatibilityVerifier verifier : this.verifiers) { + VerificationResult result = verifier.verify(); + if (result.isNotCompatible()) { + errors.add(result); + } + } + return errors; + } +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/SpringBootVersionVerifier.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/SpringBootVersionVerifier.java new file mode 100644 index 00000000..525a014e --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/SpringBootVersionVerifier.java @@ -0,0 +1,178 @@ +/* + * Copyright 2002-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 org.springframework.cloud.configuration; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.boot.SpringBootVersion; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.util.StringUtils; + +/** + * Verifies if Spring Boot has proper version + */ +class SpringBootVersionVerifier implements CompatibilityVerifier { + + private static final Log log = LogFactory.getLog(SpringBootVersionVerifier.class); + + final Map ACCEPTED_VERSIONS = new HashMap() { + { + this.put("1.5", is1_5()); + this.put("2.0", is2_0()); + this.put("2.1", is2_1()); + } + }; + + private final List acceptedVersions; + + SpringBootVersionVerifier(List acceptedVersions) { + this.acceptedVersions = acceptedVersions; + } + + @Override + public VerificationResult verify() { + boolean matches = springBootVersionMatches(); + if (matches) { + return VerificationResult.compatible(); + } + return VerificationResult.notCompatible(errorDescription(), action()); + } + + CompatibilityPredicate is1_5() { + return new CompatibilityPredicate() { + + @Override + public String toString() { + return "Predicate for Boot 1.5"; + } + + @Override + public boolean isCompatible() { + try { + // deprecated 1.5 + Class.forName("org.springframework.boot.context.config.ResourceNotFoundException"); + return true; + } + catch (ClassNotFoundException e) { + return false; + } + } + }; + } + + private boolean bootVersionFromManifest(String s) { + String version = getVersionFromManifest(); + if (log.isDebugEnabled()) { + log.debug("Version found in Boot manifest [" + version + "]"); + } + return StringUtils.hasText(version) && version.startsWith(s); + } + + String getVersionFromManifest() { + return SpringBootVersion.getVersion(); + } + + CompatibilityPredicate is2_0() { + return new CompatibilityPredicate() { + @Override + public String toString() { + return "Predicate for Boot 2.0"; + } + + @Override + public boolean isCompatible() { + + try { + // present in 2.0, 1.5 missing in 2.1 + SpringApplicationBuilder.class.getMethod("web", boolean.class); + return !is1_5().isCompatible(); + } + catch (NoSuchMethodException e) { + return false; + } + } + }; + } + + CompatibilityPredicate is2_1() { + return new CompatibilityPredicate() { + + @Override + public String toString() { + return "Predicate for Boot 2.1"; + } + + @Override + public boolean isCompatible() { + try { + // since 2.1 + Class.forName("org.springframework.boot.task.TaskExecutorCustomizer"); + return true; + } + catch (ClassNotFoundException e) { + return false; + } + + + } + }; + } + + private String errorDescription() { + String versionFromManifest = getVersionFromManifest(); + if (StringUtils.hasText(versionFromManifest)) { + return String.format("Spring Boot [%s] is not compatible with this Spring Cloud release train", versionFromManifest); + } + return "Spring Boot is not compatible with this Spring Cloud release train"; + } + + private String action() { + return String.format("Change Spring Boot version to one of the following versions %s .\n" + + "You can find the latest Spring Boot versions here [%s]. \n" + + "If you want to learn more about the Spring Cloud Release train compatibility, you " + + "can visit this page [%s] and check the [Release Trains] section.", + this.acceptedVersions, "https://spring.io/projects/spring-boot#learn", "https://spring.io/projects/spring-cloud#overview"); + } + + private boolean springBootVersionMatches() { + for (String acceptedVersion : acceptedVersions) { + if (bootVersionFromManifest(acceptedVersion)) { + return true; + } + else { + // 2.0, 2.1 + CompatibilityPredicate predicate = ACCEPTED_VERSIONS.get( + acceptedVersionWithoutX(acceptedVersion)); + if (predicate != null && predicate.isCompatible()) { + if (log.isDebugEnabled()) { + log.debug("Predicate [" + predicate + "] was matched"); + } + return true; + } + } + } + return false; + } + + private String acceptedVersionWithoutX(String acceptedVersion) { + if (acceptedVersion.endsWith(".x")) { + return acceptedVersion.substring(0, acceptedVersion.indexOf(".x")); + } + return acceptedVersion; + } +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/VerificationResult.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/VerificationResult.java new file mode 100644 index 00000000..e3b53b3d --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/VerificationResult.java @@ -0,0 +1,48 @@ +/* + * Copyright 2002-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 org.springframework.cloud.configuration; + +import org.springframework.util.StringUtils; + +/** + * @author Marcin Grzejszczak + */ +class VerificationResult { + final String description; + final String action; + + // if OK + private VerificationResult() { + this.description = ""; + this.action = ""; + } + + // if not OK + private VerificationResult(String errorDescription, String action) { + this.description = errorDescription; + this.action = action; + } + + static VerificationResult compatible() { + return new VerificationResult(); + } + + static VerificationResult notCompatible(String errorDescription, String action) { + return new VerificationResult(errorDescription, action); + } + + boolean isNotCompatible() { + return StringUtils.hasText(this.description) || StringUtils.hasText(this.action); + } + +} diff --git a/spring-cloud-commons/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-commons/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 00000000..04e0911a --- /dev/null +++ b/spring-cloud-commons/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,46 @@ +{ + "properties": [ + { + "defaultValue": "true", + "name": "spring.cloud.refresh.enabled", + "description": "Enables autoconfiguration for the refresh scope and associated features.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": true, + "name": "spring.cloud.httpclientfactories.apache.enabled", + "description": "Enables creation of Apache Http Client factory beans.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": "true", + "name": "spring.cloud.httpclientfactories.ok.enabled", + "description": "Enables creation of OK Http Client factory beans.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": true, + "name": "spring.cloud.util.enabled", + "description": "Enables creation of Spring Cloud utility beans.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": true, + "name": "spring.cloud.features.enabled", + "description": "Enables the features endpoint.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": true, + "name": "spring.cloud.discovery.enabled", + "description": "Enables discovery client health indicators.", + "type": "java.lang.Boolean" + }, + { + "defaultValue": true, + "name": "spring.cloud.discovery.client.composite-indicator.enabled", + "description": "Enables discovery client composite health indicator.", + "type": "java.lang.Boolean" + } + ] +} \ No newline at end of file diff --git a/spring-cloud-commons/src/main/resources/META-INF/spring.factories b/spring-cloud-commons/src/main/resources/META-INF/spring.factories index 12fba068..4b22aa70 100644 --- a/spring-cloud-commons/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-commons/src/main/resources/META-INF/spring.factories @@ -11,9 +11,14 @@ org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancerAutoC org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\ org.springframework.cloud.commons.httpclient.HttpClientConfiguration,\ org.springframework.cloud.commons.util.UtilAutoConfiguration,\ +org.springframework.cloud.configuration.CompatibilityVerifierAutoConfiguration,\ org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration # Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.cloud.client.HostInfoEnvironmentPostProcessor + +# Failure Analyzers +org.springframework.boot.diagnostics.FailureAnalyzer=\ +org.springframework.cloud.configuration.CompatibilityNotMetFailureAnalyzer \ No newline at end of file diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/CompatibilityVerifierAutoConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/CompatibilityVerifierAutoConfigurationTests.java new file mode 100644 index 00000000..5b821d1a --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/CompatibilityVerifierAutoConfigurationTests.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-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 org.springframework.cloud.configuration; + +import org.assertj.core.api.BDDAssertions; +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.SpringRunner; + +/** + * @author Marcin Grzejszczak + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class CompatibilityVerifierAutoConfigurationTests { + + @Autowired + MyCompatibilityVerifier myMismatchVerifier; + + @Test + public void contextLoads() { + BDDAssertions.then(this.myMismatchVerifier.called).isTrue(); + } + + @Configuration + @EnableAutoConfiguration + static class TestConfiguration { + @Bean + MyCompatibilityVerifier myMismatchVerifier() { + return new MyCompatibilityVerifier(); + } + } + + private static class MyCompatibilityVerifier implements CompatibilityVerifier { + + boolean called; + + @Override + public VerificationResult verify() { + this.called = true; + return VerificationResult.compatible(); + } + } +} + diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/CompatibilityVerifierDisabledAutoConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/CompatibilityVerifierDisabledAutoConfigurationTests.java new file mode 100644 index 00000000..ba803079 --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/CompatibilityVerifierDisabledAutoConfigurationTests.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002-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 org.springframework.cloud.configuration; + +import org.assertj.core.api.BDDAssertions; +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.Configuration; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author Marcin Grzejszczak + */ +@RunWith(SpringRunner.class) +@SpringBootTest(properties = "spring.cloud.compatibility-verifier.enabled=false") +public class CompatibilityVerifierDisabledAutoConfigurationTests { + + @Autowired(required = false) + CompositeCompatibilityVerifier compositeCompatibilityVerifier; + + @Test + public void contextLoads() { + BDDAssertions.then(this.compositeCompatibilityVerifier).isNull(); + } + + @Configuration + @EnableAutoConfiguration + static class TestConfiguration { + } +} + diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/CompatibilityVerifierFailureAutoConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/CompatibilityVerifierFailureAutoConfigurationTests.java new file mode 100644 index 00000000..a081b851 --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/CompatibilityVerifierFailureAutoConfigurationTests.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-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 org.springframework.cloud.configuration; + +import org.assertj.core.api.BDDAssertions; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.rule.OutputCapture; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.NestedExceptionUtils; + +/** + * @author Marcin Grzejszczak + */ +public class CompatibilityVerifierFailureAutoConfigurationTests { + + @Rule public OutputCapture outputCapture = new OutputCapture(); + + @Test + public void contextFailsToLoad() { + try { + SpringApplication.run(TestConfiguration.class, + "--spring.cloud.compatibility-verifier.compatible-boot-versions=1.2.x,1.3.x"); + BDDAssertions.fail("should throw exception"); + } catch (BeanCreationException ex) { + Throwable cause = NestedExceptionUtils.getRootCause(ex); + BDDAssertions.then(((CompatibilityNotMetException) cause).results).hasSize(1); + } + } + + @Configuration + @EnableAutoConfiguration + static class TestConfiguration { + } +} + diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/CompatibilityVerifierTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/CompatibilityVerifierTests.java new file mode 100644 index 00000000..23487d7e --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/CompatibilityVerifierTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-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 org.springframework.cloud.configuration; + +import java.util.ArrayList; +import java.util.List; + +import org.assertj.core.api.BDDAssertions; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.boot.test.rule.OutputCapture; + +/** + * @author Marcin Grzejszczak + */ +public class CompatibilityVerifierTests { + + @Rule public OutputCapture outputCapture = new OutputCapture(); + + @Test + public void should_not_print_the_report_when_no_errors_were_found() { + CompositeCompatibilityVerifier verifier = new CompositeCompatibilityVerifier(new ArrayList()); + + verifier.verifyDependencies(); + + BDDAssertions.then(outputCapture.toString()).doesNotContain("SPRING CLOUD VERIFICATION FAILED"); + } + + @Test + public void should_print_the_report_when_errors_were_found() { + List list = new ArrayList<>(); + list.add(new CompatibilityVerifier() { + @Override + public VerificationResult verify() { + return VerificationResult.notCompatible("Wrong Boot version", "Use Boot version 1.2"); + } + }); + list.add(new CompatibilityVerifier() { + @Override + public VerificationResult verify() { + return VerificationResult.notCompatible("Wrong JDK version", "Use JDK 25"); + } + }); + CompositeCompatibilityVerifier verifier = new CompositeCompatibilityVerifier(list); + + try { + verifier.verifyDependencies(); + BDDAssertions.fail("should fail"); + } catch (CompatibilityNotMetException ex) { + BDDAssertions.then(ex.results).hasSize(2); + } + } + +} \ No newline at end of file diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/SpringBootDependencyTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/SpringBootDependencyTests.java new file mode 100644 index 00000000..92ffb579 --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/SpringBootDependencyTests.java @@ -0,0 +1,209 @@ +/* + * Copyright 2002-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 org.springframework.cloud.configuration; + +import java.util.Collections; +import java.util.List; + +import org.assertj.core.api.BDDAssertions; +import org.junit.Test; + +/** + * @author Marcin Grzejszczak + */ +public class SpringBootDependencyTests { + + @Test + public void should_read_concrete_version_from_manifest() { + List acceptedVersions = Collections.singletonList("2.0.3.RELEASE"); + SpringBootVersionVerifier + versionVerifier = new SpringBootVersionVerifier(acceptedVersions) { + @Override + String getVersionFromManifest() { + return "2.0.3.RELEASE"; + } + }; + versionVerifier.ACCEPTED_VERSIONS.clear(); + + VerificationResult verificationResult = versionVerifier.verify(); + + BDDAssertions.then(verificationResult.description).isEmpty(); + BDDAssertions.then(verificationResult.action).isEmpty(); + } + + @Test + public void should_read_concrete_version_from_manifest_and_return_false_when_version_is_not_matched() { + List acceptedVersions = Collections.singletonList("2.0.9.RELEASE"); + SpringBootVersionVerifier + versionVerifier = new SpringBootVersionVerifier(acceptedVersions) { + @Override + String getVersionFromManifest() { + return "2.0.3.RELEASE"; + } + }; + versionVerifier.ACCEPTED_VERSIONS.clear(); + + VerificationResult verificationResult = versionVerifier.verify(); + + BDDAssertions.then(verificationResult.description).isNotEmpty(); + BDDAssertions.then(verificationResult.action).isNotEmpty(); + } + + @Test + public void should_read_concrete_version_from_manifest_and_return_false_when_minor_version_is_not_matched() { + List acceptedVersions = Collections.singletonList("2.0"); + SpringBootVersionVerifier + versionVerifier = new SpringBootVersionVerifier(acceptedVersions) { + @Override + String getVersionFromManifest() { + return "2.1.3.RELEASE"; + } + }; + versionVerifier.ACCEPTED_VERSIONS.clear(); + + VerificationResult verificationResult = versionVerifier.verify(); + + BDDAssertions.then(verificationResult.description).isNotEmpty(); + BDDAssertions.then(verificationResult.action).isNotEmpty(); + } + + @Test + public void should_read_concrete_version_from_manifest_and_match_it_against_minor_version() { + List acceptedVersions = Collections.singletonList("2.0"); + SpringBootVersionVerifier + versionVerifier = new SpringBootVersionVerifier(acceptedVersions) { + @Override + String getVersionFromManifest() { + return "2.0.3.RELEASE"; + } + }; + versionVerifier.ACCEPTED_VERSIONS.clear(); + + VerificationResult verificationResult = versionVerifier.verify(); + + BDDAssertions.then(verificationResult.description).isEmpty(); + BDDAssertions.then(verificationResult.action).isEmpty(); + } + + @Test + public void should_match_against_predicate() { + List acceptedVersions = Collections.singletonList("2.5"); + SpringBootVersionVerifier + versionVerifier = new SpringBootVersionVerifier(acceptedVersions) { + @Override + String getVersionFromManifest() { + return ""; + } + }; + versionVerifier.ACCEPTED_VERSIONS.clear(); + versionVerifier.ACCEPTED_VERSIONS.put("2.5", new CompatibilityPredicate() { + @Override + public boolean isCompatible() { + return true; + } + }); + + VerificationResult verificationResult = versionVerifier.verify(); + + BDDAssertions.then(verificationResult.description).isEmpty(); + BDDAssertions.then(verificationResult.action).isEmpty(); + } + + @Test + public void should_fail_to_match_against_predicate_when_none_is_matching() { + List acceptedVersions = Collections.singletonList("2.5"); + SpringBootVersionVerifier + versionVerifier = new SpringBootVersionVerifier(acceptedVersions) { + @Override + String getVersionFromManifest() { + return ""; + } + }; + versionVerifier.ACCEPTED_VERSIONS.clear(); + + VerificationResult verificationResult = versionVerifier.verify(); + + BDDAssertions.then(verificationResult.description).isNotEmpty(); + BDDAssertions.then(verificationResult.action).isNotEmpty(); + } + + @Test + public void should_match_against_current_manifest() { + List acceptedVersions = Collections.singletonList("1.5"); + SpringBootVersionVerifier + versionVerifier = new SpringBootVersionVerifier(acceptedVersions); + versionVerifier.ACCEPTED_VERSIONS.clear(); + + VerificationResult verificationResult = versionVerifier.verify(); + + BDDAssertions.then(verificationResult.description).isEmpty(); + BDDAssertions.then(verificationResult.action).isEmpty(); + } + + @Test + public void should_match_against_current_predicate() { + List acceptedVersions = Collections.singletonList("1.5"); + SpringBootVersionVerifier + versionVerifier = new SpringBootVersionVerifier(acceptedVersions){ + @Override + String getVersionFromManifest() { + return ""; + } + }; + versionVerifier.ACCEPTED_VERSIONS.clear(); + versionVerifier.ACCEPTED_VERSIONS.put("1.5", versionVerifier.is1_5()); + + VerificationResult verificationResult = versionVerifier.verify(); + + BDDAssertions.then(verificationResult.description).isEmpty(); + BDDAssertions.then(verificationResult.action).isEmpty(); + } + + @Test + public void should_match_against_current_predicate_with_version_ending_with_x() { + List acceptedVersions = Collections.singletonList("1.5.x"); + SpringBootVersionVerifier + versionVerifier = new SpringBootVersionVerifier(acceptedVersions){ + @Override + String getVersionFromManifest() { + return ""; + } + }; + versionVerifier.ACCEPTED_VERSIONS.clear(); + versionVerifier.ACCEPTED_VERSIONS.put("1.5", versionVerifier.is1_5()); + + VerificationResult verificationResult = versionVerifier.verify(); + + BDDAssertions.then(verificationResult.description).isEmpty(); + BDDAssertions.then(verificationResult.action).isEmpty(); + } + + @Test + public void should_fail_to_match_against_predicate_for_non_current_versions() { + List acceptedVersions = Collections.singletonList("1.5"); + SpringBootVersionVerifier + versionVerifier = new SpringBootVersionVerifier(acceptedVersions) { + @Override + String getVersionFromManifest() { + return ""; + } + }; + versionVerifier.ACCEPTED_VERSIONS.remove("1.5"); + + VerificationResult verificationResult = versionVerifier.verify(); + + BDDAssertions.then(verificationResult.description).isNotEmpty(); + BDDAssertions.then(verificationResult.action).isNotEmpty(); + } + +} \ No newline at end of file