From 621982f47245a5295b0209036453a44a291e17d8 Mon Sep 17 00:00:00 2001 From: John Blum Date: Fri, 28 Jun 2019 13:02:52 -0700 Subject: [PATCH] Add functionality to target and extract VCAP environment variable configuration for a specific CloudCache Service Instance in PCF. Resolves gh-33. --- .../geode/core/env/VcapPropertySource.java | 61 +++++-- .../core/env/VcapPropertySourceUnitTests.java | 149 ++++++++++++++++++ 2 files changed, 196 insertions(+), 14 deletions(-) diff --git a/spring-geode/src/main/java/org/springframework/geode/core/env/VcapPropertySource.java b/spring-geode/src/main/java/org/springframework/geode/core/env/VcapPropertySource.java index 7f8131a0..944d5832 100644 --- a/spring-geode/src/main/java/org/springframework/geode/core/env/VcapPropertySource.java +++ b/spring-geode/src/main/java/org/springframework/geode/core/env/VcapPropertySource.java @@ -38,7 +38,9 @@ import org.springframework.geode.core.env.support.CloudCacheService; import org.springframework.geode.core.env.support.Service; import org.springframework.geode.core.env.support.User; import org.springframework.geode.core.util.ObjectUtils; +import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -128,7 +130,7 @@ public class VcapPropertySource extends PropertySource vcapServicePredicate; + private Predicate vcapServicePredicate; /** * Constructs a new {@link PropertySource} from the existing, required {@link EnumerablePropertySource} instance @@ -171,7 +173,24 @@ public class VcapPropertySource extends PropertySource findAllVcapServicesProperties() { - return findAllPropertiesByNameMatching(VCAP_SERVICES_PROPERTIES_PREDICATE); + return findTargetVcapServiceProperties(VCAP_SERVICES_PROPERTIES_PREDICATE); + } + + public Set findTargetVcapServiceProperties(Predicate vcapServicePropertiesPredicate) { + return findAllPropertiesByNameMatching(filterByVcapServicePropertiesPredicate(vcapServicePropertiesPredicate)); + } + + private Predicate filterByVcapServicePropertiesPredicate(Predicate vcapServicePropertiesPredicate) { + + return isValid(vcapServicePropertiesPredicate) + ? VCAP_SERVICES_PROPERTIES_PREDICATE.and(vcapServicePropertiesPredicate) + : VCAP_SERVICES_PROPERTIES_PREDICATE; + } + + private boolean isValid(Predicate vcapServicePropertiesPredicate) { + + return vcapServicePropertiesPredicate != null + && vcapServicePropertiesPredicate != VCAP_SERVICES_PROPERTIES_PREDICATE; } public Optional findFirstCloudCacheService() { @@ -208,11 +227,10 @@ public class VcapPropertySource extends PropertySource findFirstCloudCacheServiceName() { - Iterable vcapServicesProperties = findAllVcapServicesProperties(); + Iterable vcapServicesProperties = findTargetVcapServiceProperties(getVcapServicePredicate()); - Predicate vcapServicePredicate = resolveVcapServicePredicate(); - - return findAllPropertiesByValueMatching(vcapServicesProperties, vcapServicePredicate).stream() + return findAllPropertiesByValueMatching(vcapServicesProperties, CLOUD_CACHE_AND_GEMFIRE_SERVICE_PREDICATE) + .stream() .filter(propertyName -> propertyName.endsWith(".tags")) .map(propertyName -> propertyName.substring(VCAP_SERVICES_PROPERTY.length())) .map(propertyName -> propertyName.substring(0, propertyName.indexOf("."))) @@ -259,26 +277,41 @@ public class VcapPropertySource extends PropertySource resolveVcapServicePredicate() { - - return this.vcapServicePredicate != null - ? this.vcapServicePredicate - : CLOUD_CACHE_AND_GEMFIRE_SERVICE_PREDICATE; - } - @Nullable @Override public Object getProperty(String name) { return getSource().getProperty(name); } + @NonNull + protected Predicate getVcapServicePredicate() { + + return this.vcapServicePredicate != null + ? this.vcapServicePredicate + : propertyName -> true; + } + @Override @SuppressWarnings("all") public Iterator iterator() { return Collections.unmodifiableList(Arrays.asList(getSource().getPropertyNames())).iterator(); } - public VcapPropertySource withVcapServicePredicate(Predicate vcapServicePredicate) { + @NonNull + public VcapPropertySource withVcapServiceName(@NonNull String serviceName) { + + Assert.hasText(serviceName, "Service name is required"); + + String resolvedServiceName = StringUtils.trimAllWhitespace(serviceName); + + Predicate vcapServiceNamePredicate = propertyName -> + propertyName.startsWith(String.format("%1$s%2$s.", VCAP_SERVICES_PROPERTY, resolvedServiceName)); + + return withVcapServicePredicate(vcapServiceNamePredicate); + } + + @NonNull + public VcapPropertySource withVcapServicePredicate(@Nullable Predicate vcapServicePredicate) { this.vcapServicePredicate = vcapServicePredicate; diff --git a/spring-geode/src/test/java/org/springframework/geode/core/env/VcapPropertySourceUnitTests.java b/spring-geode/src/test/java/org/springframework/geode/core/env/VcapPropertySourceUnitTests.java index 461feca0..2d41647c 100644 --- a/spring-geode/src/test/java/org/springframework/geode/core/env/VcapPropertySourceUnitTests.java +++ b/spring-geode/src/test/java/org/springframework/geode/core/env/VcapPropertySourceUnitTests.java @@ -16,6 +16,7 @@ package org.springframework.geode.core.env; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; @@ -32,6 +33,7 @@ import java.util.Arrays; import java.util.Optional; import java.util.Properties; import java.util.Set; +import java.util.function.Predicate; import org.junit.Test; @@ -352,6 +354,62 @@ public class VcapPropertySourceUnitTests { verify(mockPropertySource, times(1)).getPropertyNames(); } + @Test + public void findTargetVcapApplicationPropertiesIsSuccessful() { + + EnumerablePropertySource mockPropertySource = mock(EnumerablePropertySource.class); + + String[] propertyNames = { + "vcap.services.jblum-pcc.credentials.locators", + "vcap.services.jblum-pcc.credentials.users", + "vcap.application.host", + "vcap.services.test-pcc.credentials.locators", + "vcap.services.test-pcc.credentials.users", + "vcap.application.name", + "vcap.services.jblum-pcc.name", + "vcap.services.test-pcc.name", + "vcap.application.port", + "vcap.services.jblum-pcc.plan", + "vcap.application.space_name", + "vcap.services.test-pcc.plan", + "vcap.application.uris", + "vcap.services.jblum-pcc.tags", + "vcap.services.test-pcc.tags" + }; + + when(mockPropertySource.containsProperty(anyString())).thenAnswer(invocation -> + Arrays.asList(propertyNames).contains(invocation.getArgument(0, String.class))); + + when(mockPropertySource.getName()).thenReturn("vcap"); + when(mockPropertySource.getPropertyNames()).thenReturn(propertyNames); + + VcapPropertySource propertySource = VcapPropertySource.from(mockPropertySource); + + assertThat(propertySource).isNotNull(); + assertThat(propertySource.getSource()).isEqualTo(mockPropertySource); + + Predicate testPccServicePredicate = propertyName -> propertyName.contains("test-pcc"); + + Set testPccServicePropertyNames = + propertySource.findTargetVcapServiceProperties(testPccServicePredicate); + + assertThat(testPccServicePropertyNames).isNotNull(); + + assertThat(testPccServicePropertyNames) + .describedAs("PCC Service Properties [%s]", testPccServicePropertyNames) + .hasSize(5); + + assertThat(testPccServicePropertyNames) + .containsExactlyInAnyOrder("vcap.services.test-pcc.credentials.locators", + "vcap.services.test-pcc.credentials.users", "vcap.services.test-pcc.name", + "vcap.services.test-pcc.plan", "vcap.services.test-pcc.tags"); + + verify(mockPropertySource, times(1)).getName(); + verify(mockPropertySource, times(1)).containsProperty(eq("vcap.application.name")); + verify(mockPropertySource, times(1)).containsProperty(eq("vcap.application.uris")); + verify(mockPropertySource, times(1)).getPropertyNames(); + } + @Test public void findFirstCloudCacheServiceNameReturnsOptionalOfServiceName() { @@ -602,4 +660,95 @@ public class VcapPropertySourceUnitTests { assertThat(majorTom.getPassword().orElse(null)).isEqualTo("s3cUr3"); assertThat(majorTom.getRole().map(User.Role::isClusterOperator).orElse(false)).isTrue(); } + + @Test + @SuppressWarnings("unchecked") + public void withMockVcapServicePredicateConfiguresVcapServicePredicateReturnsThis() { + + Properties properties = new Properties(); + + properties.setProperty("vcap.application.name", "withVcapServicePredicateConfiguresVcapServicePredicateReturnsThis"); + properties.setProperty("vcap.application.uris", "{}"); + + Predicate mockVcapServicePredicate = mock(Predicate.class); + + VcapPropertySource propertySource = VcapPropertySource.from(properties); + + assertThat(propertySource).isNotNull(); + assertThat(propertySource.getVcapServicePredicate()).isNotEqualTo(mockVcapServicePredicate); + assertThat(propertySource.withVcapServicePredicate(mockVcapServicePredicate)).isEqualTo(propertySource); + assertThat(propertySource.getVcapServicePredicate()).isEqualTo(mockVcapServicePredicate); + } + + @Test + public void withNullVcapServicePredicateAllowsAndSetsNullReturnsThis() { + + Properties properties = new Properties(); + + properties.setProperty("vcap.application.name", "withNullVcapServicePredicateAllowsAndSetsNullReturnsThis"); + properties.setProperty("vcap.application.uris", "{}"); + + VcapPropertySource propertySource = VcapPropertySource.from(properties); + + assertThat(propertySource).isNotNull(); + assertThat(propertySource.getVcapServicePredicate()).isNotNull(); + assertThat(propertySource.withVcapServicePredicate(null)).isEqualTo(propertySource); + assertThat(propertySource.getVcapServicePredicate()).isNotNull(); + } + + private void testWithInvalidVcapServiceName(String serviceName) { + + Properties properties = new Properties(); + + properties.setProperty("vcap.application.name", "testWithInvalidVcapServiceName"); + properties.setProperty("vcap.application.uris", "{}"); + + VcapPropertySource propertySource = VcapPropertySource.from(properties); + + assertThat(propertySource).isNotNull(); + + propertySource.withVcapServiceName(serviceName); + + fail("Expected VcapPropertySource.withServiceName(%s) to fail", serviceName); + } + + @Test(expected = IllegalArgumentException.class) + public void withBlankVcapServiceNameThrowsIllegalArgumentException() { + testWithInvalidVcapServiceName(" "); + } + + @Test(expected = IllegalArgumentException.class) + public void withEmptyVcapServiceNameThrowsIllegalArgumentException() { + testWithInvalidVcapServiceName(""); + } + + @Test(expected = IllegalArgumentException.class) + public void withNullVcapServiceNameThrowsIllegalArgumentException() { + testWithInvalidVcapServiceName(null); + } + + @Test + public void withValidVcapSeviceNameConfiguresVcapServicePredicateReturnsThis() { + + Properties properties = new Properties(); + + properties.setProperty("vcap.application.name", "withValidVcapSeviceNameConfiguresVcapServicePredicateReturnsThis"); + properties.setProperty("vcap.application.uris", "{}"); + + VcapPropertySource propertySource = VcapPropertySource.from(properties); + + assertThat(propertySource).isNotNull(); + assertThat(propertySource.withVcapServiceName("test-pcc")).isEqualTo(propertySource); + + Predicate vcapServicePredicate = propertySource.getVcapServicePredicate(); + + assertThat(vcapServicePredicate).isNotNull(); + assertThat(vcapServicePredicate.test("vcap.services.test-pcc.name")).isTrue(); + assertThat(vcapServicePredicate.test("vcap.services.test-pcc.tags")).isTrue(); + assertThat(vcapServicePredicate.test("vcap.application.test-pcc.name")).isFalse(); + assertThat(vcapServicePredicate.test("vcap.test-pcc.name")).isFalse(); + assertThat(vcapServicePredicate.test("test-pcc.name")).isFalse(); + assertThat(vcapServicePredicate.test("test-pcc")).isFalse(); + assertThat(vcapServicePredicate.test("vcap.services.junk-pcc.name")).isFalse(); + } }