Support awareness and discovery of secure (Auth and SSL eanbled) Apache Geode clusters using @EnableClusterAware annotation functionality.

Improves log details about the runtime environment (e.g. cloud, standalone/self-managed).

Resolves gh-57.

Resolves gh-99.
This commit is contained in:
John Blum
2021-01-15 01:32:11 -08:00
parent d08525708b
commit 4a20f7be61
4 changed files with 1059 additions and 85 deletions

View File

@@ -0,0 +1,224 @@
/*
* Copyright 2020 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
*
* https://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.geode.boot.autoconfigure.cluster.aware;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.File;
import java.io.IOException;
import java.security.KeyStore;
import java.util.List;
import java.util.stream.Collectors;
import javax.net.ssl.SSLContext;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustAllStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.GemfireUtils;
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions;
import org.springframework.data.gemfire.config.annotation.EnableManager;
import org.springframework.data.gemfire.config.support.RestTemplateConfigurer;
import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport;
import org.springframework.geode.config.annotation.EnableClusterAware;
import org.springframework.geode.security.TestSecurityManager;
import org.springframework.geode.util.GeodeConstants;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import example.app.books.model.Book;
import example.app.books.model.ISBN;
/**
* Integration Tests testing the {@link EnableClusterAware} annotation configuration when the Apache Geode cluster
* (server(s)) are secure (i.e. when both Authentication and TLS/SSL are enabled).
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.springframework.boot.ApplicationRunner
* @see org.springframework.boot.autoconfigure.SpringBootApplication
* @see org.springframework.boot.builder.SpringApplicationBuilder
* @see org.springframework.boot.test.context.SpringBootTest
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Profile
* @see org.springframework.data.gemfire.GemfireTemplate
* @see org.springframework.data.gemfire.config.annotation.CacheServerApplication
* @see org.springframework.data.gemfire.config.annotation.EnableManager
* @see org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport
* @see org.springframework.geode.config.annotation.EnableClusterAware
* @see org.springframework.geode.security.TestSecurityManager
* @see org.springframework.test.annotation.DirtiesContext
* @see org.springframework.test.context.ActiveProfiles
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.4.1
*/
@ActiveProfiles({ "cluster-aware-with-secure-client", "ssl" })
@DirtiesContext
@RunWith(SpringRunner.class)
@SpringBootTest(
classes = SecureClusterAwareConfigurationIntegrationTests.TestGeodeClientConfiguration.class,
properties = {
"spring.data.gemfire.management.require-https=true",
"spring.data.gemfire.security.username=test",
"spring.data.gemfire.security.password=test"
},
webEnvironment = SpringBootTest.WebEnvironment.NONE
)
@SuppressWarnings("unused")
public class SecureClusterAwareConfigurationIntegrationTests extends ForkingClientServerIntegrationTestsSupport {
@BeforeClass
public static void startGeodeServer() throws IOException {
startGemFireServer(TestGeodeServerConfiguration.class,
"-Dspring.profiles.active=cluster-aware-with-secure-server,ssl");
}
@Autowired
@Qualifier("booksTemplate")
private GemfireTemplate booksTemplate;
@Test
public void clientServerConfigurationAndConfigurationIsSuccessful() {
Book book = Book.newBook("Book of Job")
.identifiedBy(ISBN.autoGenerated());
this.booksTemplate.put(book.getIsbn(), book);
Book returnedBook = this.booksTemplate.get(book.getIsbn());
assertThat(returnedBook).isNotNull();
assertThat(returnedBook).isEqualTo(book);
assertThat(returnedBook).isNotSameAs(book);
}
@SpringBootApplication
@EnableClusterAware
@EnableEntityDefinedRegions(basePackageClasses = Book.class)
@Profile(("cluster-aware-with-secure-client"))
static class TestGeodeClientConfiguration {
static final String TEST_TRUSTED_KEYSTORE_FILENAME = "test-trusted.keystore";
@Bean
RestTemplateConfigurer secureClientHttpRequestConfigurer(Environment environment) {
return restTemplate -> {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new ClassPathResource(TEST_TRUSTED_KEYSTORE_FILENAME).getInputStream(),
environment.getProperty("spring.data.gemfire.security.ssl.truststore.password").toCharArray());
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(keyStore, TrustAllStrategy.INSTANCE)
.build();
HttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier()))
.build();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
}
catch (Exception cause) {
throw new RuntimeException(cause);
}
};
}
}
@SpringBootApplication
@CacheServerApplication(name = "SecureClusterAwareConfigurationIntegrationTestsServer")
@EnableManager(start = true)
@Profile("cluster-aware-with-secure-server")
static class TestGeodeServerConfiguration {
private static final String GEODE_HOME_PROPERTY = GeodeConstants.GEMFIRE_PROPERTY_PREFIX + "home";
public static void main(String[] args) throws IOException {
resolveAndConfigureGeodeHome();
new SpringApplicationBuilder(TestGeodeServerConfiguration.class)
.web(WebApplicationType.NONE)
.build()
.run(args);
}
private static void resolveAndConfigureGeodeHome() throws IOException {
ClassPathResource resource = new ClassPathResource("/geode-home");
File resourceFile = resource.getFile();
System.setProperty(GEODE_HOME_PROPERTY, resourceFile.getAbsolutePath());
}
@Bean
TestSecurityManager securityManager() {
return new TestSecurityManager();
}
@Bean
ApplicationRunner peerCacheVerifier(GemFireCache cache) {
return args -> {
assertThat(cache).isNotNull();
assertThat(GemfireUtils.isPeer(cache)).isTrue();
assertThat(cache.getName()).isEqualTo("SecureClusterAwareConfigurationIntegrationTestsServer");
List<String> regionNames = cache.rootRegions().stream()
.map(Region::getName)
.collect(Collectors.toList());
assertThat(regionNames)
.describedAs("Expected no Regions; but was [%s]", regionNames)
.isEmpty();
};
}
}
}