diff --git a/geode-spring-boot-starter/src/main/java/org/springframework/boot/data/geode/core/env/VcapPropertySource.java b/geode-spring-boot-starter/src/main/java/org/springframework/boot/data/geode/core/env/VcapPropertySource.java new file mode 100644 index 00000000..3b7b77f4 --- /dev/null +++ b/geode-spring-boot-starter/src/main/java/org/springframework/boot/data/geode/core/env/VcapPropertySource.java @@ -0,0 +1,235 @@ +/* + * Copyright 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.boot.data.geode.core.env; + +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException; +import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException; + +import java.net.URL; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import org.springframework.boot.data.geode.core.env.support.CloudCacheService; +import org.springframework.boot.data.geode.core.env.support.Service; +import org.springframework.boot.data.geode.core.env.support.User; +import org.springframework.boot.data.geode.core.util.ObjectUtils; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.lang.Nullable; +import org.springframework.util.StringUtils; + +/** + * The {@link VcapPropertySource} class is a Spring {@link PropertySource} to process + * {@literal VCAP} environment properties in Pivotal CloudFoundry. + * + * @author John Blum + * @see java.lang.Iterable + * @see java.util.Properties + * @see org.springframework.boot.data.geode.core.env.support.Service + * @see org.springframework.boot.data.geode.core.env.support.User + * @see org.springframework.core.env.EnumerablePropertySource + * @see org.springframework.core.env.Environment + * @see org.springframework.core.env.PropertySource + * @since 1.0.0 + */ +@SuppressWarnings("unused") +public class VcapPropertySource extends PropertySource> implements Iterable { + + private static final String CLOUD_CACHE_TAG_NAME = "cloudcache"; + private static final String GEMFIRE_TAG_NAME = "gemfire"; + private static final String THIS_PROPERTY_SOURCE_NAME = "boot.data.gemfire.vcap"; + private static final String VCAP_APPLICATION_PROPERTY = "vcap.application."; + private static final String VCAP_APPLICATION_NAME_PROPERTY = VCAP_APPLICATION_PROPERTY + "name"; + private static final String VCAP_APPLICATION_URIS_PROPERTY = VCAP_APPLICATION_PROPERTY + "uris"; + private static final String VCAP_PROPERTY_SOURCE_NAME = "vcap"; + private static final String VCAP_SERVICES_PROPERTY = "vcap.services."; + private static final String VCAP_SERVICES_SERVICE_NAME_GFSH_URL_PROPERTY = "vcap.services.%s.credentials.urls.gfsh"; + private static final String VCAP_SERVICES_SERVICE_NAME_LOCATORS_PROPERTY = "vcap.services.%s.credentials.locators"; + private static final String VCAP_SERVICES_SERVICE_NAME_USERS_PROPERTY = "vcap.services.%s.credentials.users[%d]"; + + private static final Predicate CLOUD_CACHE_SERVICE_PREDICATE = + propertyValue -> String.valueOf(propertyValue).toLowerCase().contains(CLOUD_CACHE_TAG_NAME); + + private static final Predicate GEMFIRE_SERVICE_PREDICATE = + propertyValue -> String.valueOf(propertyValue).toLowerCase().contains(GEMFIRE_TAG_NAME); + + private static final Predicate CLOUD_CACHE_AND_GEMFIRE_SERVICE_PREDICATE = + CLOUD_CACHE_SERVICE_PREDICATE.and(GEMFIRE_SERVICE_PREDICATE); + + private static final Predicate VCAP_APPLICATION_PROPERTIES_PREDICATE = + propertyName -> String.valueOf(propertyName).trim().toLowerCase().startsWith(VCAP_APPLICATION_PROPERTY); + + private static final Predicate VCAP_REQUIRED_PROPERTIES_PREDICATE = + propertySource -> propertySource.containsProperty(VCAP_APPLICATION_NAME_PROPERTY) + && propertySource.containsProperty(VCAP_APPLICATION_URIS_PROPERTY); + + private static final Predicate VCAP_SERVICES_PROPERTIES_PREDICATE = + propertyName -> String.valueOf(propertyName).trim().toLowerCase().startsWith(VCAP_SERVICES_PROPERTY); + + public static VcapPropertySource from(Environment environment) { + + return Optional.ofNullable(environment) + .filter(env -> env instanceof ConfigurableEnvironment) + .map(env -> ((ConfigurableEnvironment) env).getPropertySources()) + .map(propertySources -> propertySources.get(VCAP_PROPERTY_SOURCE_NAME)) + .map(VcapPropertySource::from) + .orElseThrow(() -> newIllegalArgumentException( + "Environment [%1$s] was not configurable or does not contain an enumerable [%2$s] PropertySource", + environment, VCAP_PROPERTY_SOURCE_NAME)); + } + + public static VcapPropertySource from(Properties properties) { + + return Optional.ofNullable(properties) + .map(it -> new PropertiesPropertySource(THIS_PROPERTY_SOURCE_NAME, properties)) + .filter(VCAP_REQUIRED_PROPERTIES_PREDICATE) + .map(VcapPropertySource::new) + .orElseThrow(() -> newIllegalArgumentException("Properties are required")); + } + + public static VcapPropertySource from(PropertySource propertySource) { + + return Optional.ofNullable(propertySource) + .filter(it -> VCAP_PROPERTY_SOURCE_NAME.equals(it.getName())) + .filter(it -> it instanceof EnumerablePropertySource) + .filter(VCAP_REQUIRED_PROPERTIES_PREDICATE) + .map(it -> (EnumerablePropertySource) it) + .map(VcapPropertySource::new) + .orElseThrow(() -> newIllegalArgumentException( + "A valid EnumerablePropertySource named [%s] with VCAP properties is required", + VCAP_PROPERTY_SOURCE_NAME)); + } + + private VcapPropertySource(EnumerablePropertySource propertySource) { + super(THIS_PROPERTY_SOURCE_NAME, propertySource); + } + + protected Set findAllPropertiesByNameMatching(Predicate predicate) { + return findAllPropertiesByNameMatching(this, predicate); + } + + protected Set findAllPropertiesByNameMatching(Iterable properties, Predicate predicate) { + + return StreamSupport.stream(properties.spliterator(), false) + .filter(predicate) + .collect(Collectors.toSet()); + } + + protected Set findAllPropertiesByValueMatching(Predicate predicate) { + return findAllPropertiesByValueMatching(this, predicate); + } + + protected Set findAllPropertiesByValueMatching(Iterable properties, Predicate predicate) { + + return StreamSupport.stream(properties.spliterator(), false) + .filter(propertyName -> predicate.test(getProperty(propertyName))) + .collect(Collectors.toSet()); + } + + public Set findAllVcapApplicationProperties() { + return findAllPropertiesByNameMatching(VCAP_APPLICATION_PROPERTIES_PREDICATE); + } + + public Set findAllVcapServicesProperties() { + return findAllPropertiesByNameMatching(VCAP_SERVICES_PROPERTIES_PREDICATE); + } + + public CloudCacheService findFirstCloudCacheService() { + + String serviceName = findFirstCloudCacheServiceName(); + + CloudCacheService service = CloudCacheService.with(serviceName); + + Optional.ofNullable(getProperty(String.format(VCAP_SERVICES_SERVICE_NAME_LOCATORS_PROPERTY, serviceName))) + .map(String::valueOf) + .ifPresent(service::withLocators); + + Optional.ofNullable(getProperty(String.format(VCAP_SERVICES_SERVICE_NAME_GFSH_URL_PROPERTY, service))) + .map(String::valueOf) + .map(urlString -> ObjectUtils.doOperationSafely(() -> new URL(urlString))) + .ifPresent(service::withGfshUrl); + + return service; + } + + public String findFirstCloudCacheServiceName() { + + Iterable vcapServicesProperties = findAllVcapServicesProperties(); + + 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("."))) + .filter(StringUtils::hasText) + .sorted(String.CASE_INSENSITIVE_ORDER) + .findFirst() + .orElseThrow(() -> + newIllegalStateException("No service with tags [%1$s, %2$s] was found", + CLOUD_CACHE_TAG_NAME, GEMFIRE_TAG_NAME)); + } + + public Optional findFirstUserByRoleClusterOperator(Service service) { + + String serviceName = service.getName(); + String userPropertyName = String.format(VCAP_SERVICES_SERVICE_NAME_USERS_PROPERTY, serviceName, 0); + + for (int index = 1; containsProperty(userPropertyName+".roles"); index++) { + + String roles = String.valueOf(getProperty(userPropertyName+".roles")); + + if (roles.contains(User.Role.CLUSTER_OPERATOR.name().toLowerCase())) { + break; + } + + userPropertyName = String.format(VCAP_SERVICES_SERVICE_NAME_USERS_PROPERTY, serviceName, index); + } + + if (containsProperty(userPropertyName+".username")) { + + String username = String.valueOf(getProperty(userPropertyName+".username")); + String password = String.valueOf(getProperty(userPropertyName+".password")); + + return Optional.of(User.with(username).withPassword(password).withRole(User.Role.CLUSTER_OPERATOR)); + } + + return Optional.empty(); + } + + @Nullable + @Override + @SuppressWarnings("all") + public Object getProperty(String name) { + return getSource().getProperty(name); + } + + @Override + @SuppressWarnings("all") + public Iterator iterator() { + return Collections.unmodifiableList(Arrays.asList(getSource().getPropertyNames())).iterator(); + } +} diff --git a/geode-spring-boot-starter/src/test/java/org/springframework/boot/data/geode/core/env/VcapPropertySourceUnitTests.java b/geode-spring-boot-starter/src/test/java/org/springframework/boot/data/geode/core/env/VcapPropertySourceUnitTests.java new file mode 100644 index 00000000..b6a162d4 --- /dev/null +++ b/geode-spring-boot-starter/src/test/java/org/springframework/boot/data/geode/core/env/VcapPropertySourceUnitTests.java @@ -0,0 +1,510 @@ +/* + * Copyright 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.boot.data.geode.core.env; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import java.net.URL; +import java.util.Arrays; +import java.util.Properties; +import java.util.Set; + +import org.junit.Test; +import org.springframework.boot.data.geode.core.env.support.CloudCacheService; +import org.springframework.boot.data.geode.core.env.support.Service; +import org.springframework.boot.data.geode.core.env.support.User; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.PropertySource; + +/** + * The VcapPropertySourceUnitTests class... + * + * @author John Blum + * @since 1.0.0 + */ +public class VcapPropertySourceUnitTests { + + @Test + public void fromEnvironmentIsSuccessful() { + + ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class); + + MutablePropertySources propertySources = spy(new MutablePropertySources()); + + PropertySource mockVcapPropertySource = mock(EnumerablePropertySource.class); + + when(mockEnvironment.getPropertySources()).thenReturn(propertySources); + doReturn(mockVcapPropertySource).when(propertySources).get(eq("vcap")); + when(mockVcapPropertySource.getName()).thenReturn("vcap"); + when(mockVcapPropertySource.containsProperty(anyString())).thenReturn(true); + + VcapPropertySource propertySource = VcapPropertySource.from(mockEnvironment); + + assertThat(propertySource).isNotNull(); + assertThat(propertySource.getSource()).isEqualTo(mockVcapPropertySource); + + verify(mockEnvironment, times(1)).getPropertySources(); + verify(propertySources, times(1)).get(eq("vcap")); + verify(mockVcapPropertySource, times(1)) + .containsProperty(eq("vcap.application.name")); + verify(mockVcapPropertySource, times(1)) + .containsProperty(eq("vcap.application.uris")); + } + + @Test(expected = IllegalArgumentException.class) + public void fromNonConfigurableEnvironmentThrowsIllegalArgumentException() { + + Environment mockEnvironment = mock(Environment.class); + + try { + VcapPropertySource.from(mockEnvironment); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage( + "Environment [%s] was not configurable or does not contain an enumerable [vcap] PropertySource", + mockEnvironment); + + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + verifyZeroInteractions(mockEnvironment); + } + } + + @Test(expected = IllegalArgumentException.class) + public void fromConfigurableEnvironmentWithNoVcapPropertySourceThrowsIllegalArgumentException() { + + ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class); + + MutablePropertySources propertySources = spy(new MutablePropertySources()); + + when(mockEnvironment.getPropertySources()).thenReturn(propertySources); + doReturn(null).when(propertySources).get(anyString()); + + try { + VcapPropertySource.from(mockEnvironment); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage( + "Environment [%s] was not configurable or does not contain an enumerable [vcap] PropertySource", + mockEnvironment); + + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + verify(mockEnvironment, times(1)).getPropertySources(); + verify(propertySources, times(1)).get(eq("vcap")); + } + } + + @Test(expected = IllegalArgumentException.class) + public void fromConfigurableEnvironmentWithNonEnumerableVcapPropertySourceThrowsIllegalArgumentException() { + + ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class); + + MutablePropertySources propertySources = spy(new MutablePropertySources()); + + PropertySource mockPropertySource = mock(PropertySource.class); + + when(mockEnvironment.getPropertySources()).thenReturn(propertySources); + doReturn(mockPropertySource).when(propertySources).get(eq("vcap")); + when(mockPropertySource.getName()).thenReturn("vcap"); + + try { + VcapPropertySource.from(mockEnvironment); + } + catch (IllegalArgumentException expected) { + + assertThat(expected) + .hasMessage("A valid EnumerablePropertySource named [vcap] with VCAP properties is required", + mockEnvironment); + + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + verify(mockEnvironment, times(1)).getPropertySources(); + verify(propertySources, times(1)).get(eq("vcap")); + verify(mockPropertySource, times(1)).getName(); + verifyNoMoreInteractions(mockPropertySource); + } + } + + @Test(expected = IllegalArgumentException.class) + public void fromConfigurableEnvironmentWithEnumerableVcapPropertySourceHavingNoRequiredPropertiesThrowsIllegalArgumentException() { + + ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class); + + MutablePropertySources propertySources = spy(new MutablePropertySources()); + + PropertySource mockPropertySource = mock(EnumerablePropertySource.class); + + when(mockEnvironment.getPropertySources()).thenReturn(propertySources); + doReturn(mockPropertySource).when(propertySources).get(eq("vcap")); + when(mockPropertySource.getName()).thenReturn("vcap"); + when(mockPropertySource.containsProperty(eq("vcap.application.name"))).thenReturn(true); + when(mockPropertySource.containsProperty(eq("vcap.application.uris"))).thenReturn(false); + + try { + VcapPropertySource.from(mockEnvironment); + } + catch (IllegalArgumentException expected) { + + assertThat(expected) + .hasMessage("A valid EnumerablePropertySource named [vcap] with VCAP properties is required", + mockEnvironment); + + assertThat(expected).hasNoCause(); + + throw expected; + } + finally { + verify(mockEnvironment, times(1)).getPropertySources(); + verify(propertySources, times(1)).get(eq("vcap")); + verify(mockPropertySource, times(1)).getName(); + verify(mockPropertySource, times(1)) + .containsProperty(eq("vcap.application.name")); + verify(mockPropertySource, times(1)) + .containsProperty(eq("vcap.application.uris")); + } + } + + @Test + public void fromPropertiesIsSuccessful() { + + Properties vcap = new Properties(); + + vcap.setProperty("vcap.application.name", "testApp"); + vcap.setProperty("vcap.application.uris", "boot-app.apps.cloud.net"); + + + VcapPropertySource propertySource = VcapPropertySource.from(vcap); + + assertThat(propertySource).isNotNull(); + assertThat(propertySource.getSource()).isInstanceOf(PropertiesPropertySource.class); + assertThat(propertySource.getProperty("vcap.application.name")).isEqualTo("testApp"); + assertThat(propertySource.getProperty("vcap.application.uris")).isEqualTo("boot-app.apps.cloud.net"); + } + + @Test(expected = IllegalArgumentException.class) + public void fromPropertiesHavingNoRequiredProperties() { + + try { + VcapPropertySource.from(new Properties()); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("Properties are required"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test(expected = IllegalArgumentException.class) + public void fromNullPropertiesThrowsIllegalArgumentException() { + + try { + VcapPropertySource.from((Properties) null); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("Properties are required"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + public void findAllVcapApplicationPropertiesIsSuccessful() { + + EnumerablePropertySource mockPropertySource = mock(EnumerablePropertySource.class); + + String[] propertyNames = { + "vcap.services.jblum-pcc.credentials.locators", + "vcap.services.jblum-pcc.credentials.users", + "vcap.application.host", + "vcap.application.name", + "vcap.services.jblum-pcc.name", + "vcap.application.port", + "vcap.application.space_name", + "vcap.services.jblum-pcc.plan", + "vcap.application.uris", + "vcap.services.jblum-pcc.tags" + }; + + when(mockPropertySource.getName()).thenReturn("vcap"); + + when(mockPropertySource.containsProperty(anyString())).thenAnswer(invocation -> + Arrays.asList(propertyNames).contains(invocation.getArgument(0))); + + when(mockPropertySource.getPropertyNames()).thenReturn(propertyNames); + + VcapPropertySource propertySource = VcapPropertySource.from(mockPropertySource); + + assertThat(propertySource).isNotNull(); + assertThat(propertySource.getSource()).isEqualTo(mockPropertySource); + + Set vcapApplicationProperties = propertySource.findAllVcapApplicationProperties(); + + assertThat(vcapApplicationProperties).isNotNull(); + assertThat(vcapApplicationProperties).hasSize(5); + assertThat(vcapApplicationProperties) + .containsExactlyInAnyOrder("vcap.application.host", "vcap.application.name", "vcap.application.port", + "vcap.application.space_name", "vcap.application.uris"); + + 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 findAllVcapServicesPropertiesIsSuccessful() { + + EnumerablePropertySource mockPropertySource = mock(EnumerablePropertySource.class); + + String[] propertyNames = { + "vcap.services.jblum-pcc.credentials.locators", + "vcap.services.jblum-pcc.credentials.users", + "vcap.application.host", + "vcap.application.name", + "vcap.services.jblum-pcc.name", + "vcap.application.port", + "vcap.application.space_name", + "vcap.services.jblum-pcc.plan", + "vcap.application.uris", + "vcap.services.jblum-pcc.tags" + }; + + when(mockPropertySource.getName()).thenReturn("vcap"); + + when(mockPropertySource.containsProperty(anyString())).thenAnswer(invocation -> + Arrays.asList(propertyNames).contains(invocation.getArgument(0))); + + when(mockPropertySource.getPropertyNames()).thenReturn(propertyNames); + + VcapPropertySource propertySource = VcapPropertySource.from(mockPropertySource); + + assertThat(propertySource).isNotNull(); + assertThat(propertySource.getSource()).isEqualTo(mockPropertySource); + + Set vcapApplicationProperties = propertySource.findAllVcapServicesProperties(); + + assertThat(vcapApplicationProperties).isNotNull(); + assertThat(vcapApplicationProperties).hasSize(5); + assertThat(vcapApplicationProperties) + .containsExactlyInAnyOrder("vcap.services.jblum-pcc.credentials.locators", + "vcap.services.jblum-pcc.credentials.users", "vcap.services.jblum-pcc.name", + "vcap.services.jblum-pcc.plan", "vcap.services.jblum-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 findFirstCloudCacheServiceNameReturnsServiceName() { + + Properties vcap = new Properties(); + + vcap.setProperty("vcap.application.name", "boot-example"); + vcap.setProperty("vcap.services.test-pcc.name", "test-pcc"); + vcap.setProperty("vcap.services.test-pcc.plan", "small"); + vcap.setProperty("vcap.services.test-pcc.tags", "pivotal,database,cloudcache,gemfire"); + vcap.setProperty("vcap.application.space_name", "outerspace"); + vcap.setProperty("vcap.services.jblum-pcc.name", "jblum-pcc"); + vcap.setProperty("vcap.services.jblum-pcc.plan", "small"); + vcap.setProperty("vcap.services.jblum-pcc.tags", "cloudcache,database,gemfire,pivotal"); + vcap.setProperty("vcap.application.uris", "boot-example.boot-apps.apps.cloud.net"); + vcap.setProperty("vcap.services.a-pcc.name", "a-pcc"); + vcap.setProperty("vcap.services.a-pcc.plan", "huge"); + vcap.setProperty("vcap.services.a-pcc.tags", "pivotal,cloudcache,database"); + + VcapPropertySource propertySource = VcapPropertySource.from(vcap); + + assertThat(propertySource).isNotNull(); + assertThat(propertySource.findFirstCloudCacheServiceName()).isEqualTo("jblum-pcc"); + } + + @Test(expected = IllegalStateException.class) + public void findFirstCloudCacheServiceNameWithInvalidTagsThrowsIllegalStateException() { + + Properties vcap = new Properties(); + + vcap.setProperty("vcap.application.name", "boot-example"); + vcap.setProperty("vcap.services.test-pcc.name", "test-pcc"); + vcap.setProperty("vcap.services.test-pcc.plan", "small"); + vcap.setProperty("vcap.services.test-pcc.tags", "pivotal,gemfire,database"); + vcap.setProperty("vcap.application.space_name", "outerspace"); + vcap.setProperty("vcap.services.a-pcc.tags", "pivotal,cloudcache,database"); + vcap.setProperty("vcap.application.uris", "boot-example.boot-apps.apps.cloud.net"); + + VcapPropertySource propertySource = VcapPropertySource.from(vcap); + + assertThat(propertySource).isNotNull(); + + try { + propertySource.findFirstCloudCacheServiceName(); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("No service with tags [cloudcache, gemfire] was found"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test(expected = IllegalStateException.class) + public void findFirstCloudCacheServiceNameWithNoTagsThrowsIllegalStateException() { + + Properties vcap = new Properties(); + + vcap.setProperty("vcap.application.name", "boot-example"); + vcap.setProperty("vcap.services.test-pcc.name", "test-pcc"); + vcap.setProperty("vcap.services.test-pcc.plan", "small"); + vcap.setProperty("vcap.application.space_name", "outerspace"); + vcap.setProperty("vcap.application.uris", "boot-example.boot-apps.apps.cloud.net"); + + VcapPropertySource propertySource = VcapPropertySource.from(vcap); + + assertThat(propertySource).isNotNull(); + + try { + propertySource.findFirstCloudCacheServiceName(); + } + catch (IllegalStateException expected) { + + assertThat(expected).hasMessage("No service with tags [cloudcache, gemfire] was found"); + assertThat(expected).hasNoCause(); + + throw expected; + } + } + + @Test + public void findFirstCloudCacheServiceReturnsCloudCacheService() throws Exception { + + URL gfshUrl = new URL("http://skullbox:7070/v1/gemfire"); + + Properties vcap = new Properties(); + + vcap.setProperty("vcap.services.test-pcc.name", "test-pcc"); + vcap.setProperty("vcap.services.test-pcc.plan", "huge"); + vcap.setProperty("vcap.application.name", "boot-example"); + vcap.setProperty("vcap.services.test-pcc.credentials.locators", "sandbox[1234],toolbox,xbox[6789]"); + vcap.setProperty("vcap.application.space_name", "outerspace"); + vcap.setProperty("vcap.services.test-pcc.credentials.urls.gfsh", gfshUrl.toExternalForm()); + vcap.setProperty("vcap.application.uris", "boot-example.boot-apps.apps.cloud.net"); + vcap.setProperty("vcap.services.test-pcc.tags", "pivotal,cloudcache , database, gemfire "); + + VcapPropertySource propertySource = VcapPropertySource.from(vcap); + + assertThat(propertySource).isNotNull(); + + CloudCacheService cloudCacheService = propertySource.findFirstCloudCacheService(); + + assertThat(cloudCacheService).isNotNull(); + assertThat(cloudCacheService.getGfshUrl().orElse(null)).isEqualTo(gfshUrl); + assertThat(cloudCacheService.getLocatorList()).containsExactly( + CloudCacheService.Locator.newLocator("sandbox", 1234), + CloudCacheService.Locator.newLocator("toolbox", 10334), + CloudCacheService.Locator.newLocator("xbox", 6789) + ); + } + + @Test + public void findFirstUserByRoleClusterOperatorReturnsUser() { + + Properties vcap = new Properties(); + + vcap.setProperty("vcap.application.name", "boot-example"); + vcap.setProperty("vcap.services.test-pcc.name", "test-pcc"); + vcap.setProperty("vcap.services.test-pcc.credentials.users[0].username", "jdoe"); + vcap.setProperty("vcap.services.test-pcc.credentials.users[0].roles", "developer,poweruser,seaswab"); + vcap.setProperty("vcap.services.test-pcc.credentials.users[0].password", "test"); + vcap.setProperty("vcap.services.test-pcc.tags", "pivotal,cloudcache , database, gemfire "); + vcap.setProperty("vcap.application.space_name", "outerspace"); + vcap.setProperty("vcap.services.a-pcc.name", "a-pcc"); + vcap.setProperty("vcap.services.a-pcc.credentials.users[0].username", "admin"); + vcap.setProperty("vcap.services.a-pcc.credentials.users[0].roles", "cluster_admin"); + vcap.setProperty("vcap.services.a-pcc.credentials.users[0].password", "p@55w0rd"); + vcap.setProperty("vcap.services.a-pcc.credentials.users[1].username", "root"); + vcap.setProperty("vcap.services.a-pcc.credentials.users[1].roles", "cluster_operator"); + vcap.setProperty("vcap.services.a-pcc.credentials.users[1].password", "p@55w0rd"); + vcap.setProperty("vcap.services.a-pcc.tags", "pivotal,cloudcache,database"); + vcap.setProperty("vcap.application.uris", "boot-example.boot-apps.apps.cloud.net"); + vcap.setProperty("vcap.services.jblum-pcc.name", "jblum-pcc"); + vcap.setProperty("vcap.services.jblum-pcc.credentials.users[0].username", "majorTom"); + vcap.setProperty("vcap.services.jblum-pcc.credentials.users[0].roles", "cluster_operator,ground_contoller"); + vcap.setProperty("vcap.services.jblum-pcc.credentials.users[0].password", "s3cUr3"); + vcap.setProperty("vcap.services.jblum-pcc.credentials.users[1].username", "jimbo"); + vcap.setProperty("vcap.services.jblum-pcc.credentials.users[1].roles", "cluster_fuck"); + vcap.setProperty("vcap.services.jblum-pcc.credentials.users[1].password", "p@55!t"); + vcap.setProperty("vcap.services.jblum-pcc.credentials.users[2].username", "buster"); + vcap.setProperty("vcap.services.jblum-pcc.credentials.users[2].roles", "cluster_operator"); + vcap.setProperty("vcap.services.jblum-pcc.credentials.users[2].password", "p@55!t"); + vcap.setProperty("vcap.services.jblum-pcc.tags", "pivotal,gemfire,database"); + + VcapPropertySource propertySource = VcapPropertySource.from(vcap); + + assertThat(propertySource).isNotNull(); + assertThat(propertySource.findFirstUserByRoleClusterOperator(Service.with("test-pcc")).isPresent()).isFalse(); + + User root = propertySource.findFirstUserByRoleClusterOperator(Service.with("a-pcc")).orElse(null); + + assertThat(root).isNotNull(); + assertThat(root.getName()).isEqualTo("root"); + assertThat(root.getPassword().orElse(null)).isEqualTo("p@55w0rd"); + assertThat(root.getRole().orElse(null).isClusterOperator()).isTrue(); + + User majorTom = propertySource.findFirstUserByRoleClusterOperator(Service.with("jblum-pcc")).orElse(null); + + assertThat(majorTom).isNotNull(); + assertThat(majorTom.getName()).isEqualTo("majorTom"); + assertThat(majorTom.getPassword().orElse(null)).isEqualTo("s3cUr3"); + assertThat(majorTom.getRole().orElse(null).isClusterOperator()).isTrue(); + } +}