Add prototype implementation of the Spring Cloud Bindings BingindsPropertyProcessor interface.
Resolves gh-100.
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.cloud.bindings;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* Abstract utility class to assess the Spring {@link Environment}
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.springframework.core.env.Environment
|
||||
* @since 1.4.1
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class Guards {
|
||||
|
||||
public static final String SPRING_CLOUD_BOOT_BINDINGS_ENABLED_PROPERTY =
|
||||
"org.springframework.cloud.bindings.boot.enable";
|
||||
|
||||
public static final String SPRING_CLOUD_BOOT_BINDINGS_TYPE_ENABLED_PROPERTY =
|
||||
"org.springframework.cloud.bindings.boot.%s.enable";
|
||||
|
||||
public static boolean isGlobalEnabled(Environment environment) {
|
||||
return environment.getProperty(SPRING_CLOUD_BOOT_BINDINGS_ENABLED_PROPERTY, Boolean.class, false);
|
||||
}
|
||||
|
||||
public static boolean isTypeEnabled(Environment environment, String type) {
|
||||
|
||||
String propertyName = String.format(SPRING_CLOUD_BOOT_BINDINGS_TYPE_ENABLED_PROPERTY, type);
|
||||
|
||||
return environment.getProperty(propertyName, Boolean.class, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.cloud.bindings;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.data.gemfire.util.ArrayUtils;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Maps a source set of properties to a target set of properties.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.util.Map
|
||||
* @see java.util.function.Function
|
||||
* @since 1.4.1
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class MapMapper {
|
||||
|
||||
private final Map<String, String> source;
|
||||
private final Map<String, Object> target;
|
||||
|
||||
public MapMapper(@NonNull Map<String, String> source, @NonNull Map<String, Object> target) {
|
||||
|
||||
Assert.notNull(source, "Source Map must not be null");
|
||||
Assert.notNull(target, "Target Map must not be null");
|
||||
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
protected @NonNull Map<String, String> getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
protected @NonNull Map<String, Object> getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public Source from(@NonNull String... keys) {
|
||||
|
||||
String[] resolvedKeys = Arrays.stream(ArrayUtils.nullSafeArray(keys, String.class))
|
||||
.filter(StringUtils::hasText)
|
||||
.collect(Collectors.toList())
|
||||
.toArray(new String[0]);
|
||||
|
||||
return new Source(resolvedKeys);
|
||||
}
|
||||
|
||||
protected interface TriFunction<T, U, V, R> {
|
||||
R apply(T t, U u, V v);
|
||||
}
|
||||
|
||||
public class Source {
|
||||
|
||||
private final String[] keys;
|
||||
|
||||
private Source(@NonNull String[] keys) {
|
||||
|
||||
Assert.notNull(keys, "The String array of keys must not be null");
|
||||
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public void to(String key) {
|
||||
to(key, v -> v);
|
||||
}
|
||||
|
||||
public void to(@NonNull String key, @NonNull Function<String, Object> function) {
|
||||
|
||||
String[] keys = this.keys;
|
||||
|
||||
Assert.state(keys.length == 1,
|
||||
String.format("Source size [%d] cannot be transformed as one argument", keys.length));
|
||||
|
||||
Map<String, String> source = getSource();
|
||||
|
||||
if (Arrays.stream(keys).allMatch(source::containsKey)) {
|
||||
getTarget().put(key, function.apply(source.get(keys[0])));
|
||||
}
|
||||
}
|
||||
|
||||
public void to(String key, TriFunction<String, String, String, Object> function) {
|
||||
|
||||
String[] keys = this.keys;
|
||||
|
||||
Assert.state(keys.length == 3,
|
||||
String.format("Source size [%d] cannot be consumed as three arguments", keys.length));
|
||||
|
||||
Map<String, String> source = getSource();
|
||||
|
||||
if (Arrays.stream(keys).allMatch(source::containsKey)) {
|
||||
getTarget().put(key, function.apply(source.get(keys[0]), source.get(keys[1]), source.get(keys[2])));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.cloud.bindings.boot;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.bindings.Bindings;
|
||||
import org.springframework.cloud.bindings.boot.BindingsPropertiesProcessor;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.geode.cloud.bindings.Guards;
|
||||
import org.springframework.geode.cloud.bindings.MapMapper;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
* A Spring Cloud Bindings {@link BindingsPropertiesProcessor} for Apache Geode.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.util.Map
|
||||
* @see org.springframework.cloud.bindings.Bindings
|
||||
* @see org.springframework.cloud.bindings.boot.BindingsPropertiesProcessor
|
||||
* @see org.springframework.core.env.Environment
|
||||
* @see org.springframework.geode.cloud.bindings.Guards
|
||||
* @see org.springframework.geode.cloud.bindings.MapMapper
|
||||
* @see Guards
|
||||
* @since 1.4.1
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class GeodeBindingsPropertiesProcessor implements BindingsPropertiesProcessor {
|
||||
|
||||
public static final String TYPE = "gemfire";
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void process(@NonNull Environment environment, @NonNull Bindings bindings,
|
||||
@NonNull Map<String, Object> properties) {
|
||||
|
||||
if (Guards.isTypeEnabled(environment, TYPE)) {
|
||||
|
||||
bindings.filterBindings(TYPE).forEach(binding -> {
|
||||
|
||||
// TODO - Change! Current mappings and property configuration is based on VMware Tanzu GemFire for VMS
|
||||
// (i.e. PCC in PCF).
|
||||
|
||||
MapMapper mapMapper = new MapMapper(binding.getSecret(), properties);
|
||||
|
||||
mapMapper.from("gemfire.security-username").to("spring.data.gemfire.security.username");
|
||||
mapMapper.from("gemfire.security-password").to("spring.data.gemfire.security.password");
|
||||
mapMapper.from("gemfire.locators").to("spring.data.gemfire.pool.locators");
|
||||
mapMapper.from("gemfire.http-service-bind-address").to("spring.data.gemfire.management.http.host");
|
||||
mapMapper.from("gemfire.http-service-port").to("spring.data.gemfire.management.http.port");
|
||||
|
||||
properties.put("spring.data.gemfire.management.require-https", true);
|
||||
properties.put("spring.data.gemfire.management.use-http", true);
|
||||
properties.put("spring.data.gemfire.security.ssl.use-default-context", true);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.cloud.bindings.boot.BindingsPropertiesProcessor=\
|
||||
org.springframework.geode.cloud.bindings.boot.GeodeBindingsPropertiesProcessor
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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.cloud.bindings.boot;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.bindings.Binding;
|
||||
import org.springframework.cloud.bindings.Bindings;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.gemfire.tests.support.MapBuilder;
|
||||
import org.springframework.geode.cloud.bindings.Guards;
|
||||
|
||||
/**
|
||||
* Unit Tests for {@link GeodeBindingsPropertiesProcessor}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see org.junit.Test
|
||||
* @see org.mockito.Mockito
|
||||
* @see org.springframework.geode.cloud.bindings.boot.GeodeBindingsPropertiesProcessor
|
||||
* @since 1.4.1
|
||||
*/
|
||||
public class GeodeBindingsPropertiesProcessorUnitTests {
|
||||
|
||||
@Test
|
||||
public void processMapsCloudPropertiesToFrameworkPropertiesCorrectly() {
|
||||
|
||||
String propertyName = String.format(Guards.SPRING_CLOUD_BOOT_BINDINGS_TYPE_ENABLED_PROPERTY,
|
||||
GeodeBindingsPropertiesProcessor.TYPE);
|
||||
|
||||
Environment mockEnvironment = mock(Environment.class);
|
||||
|
||||
doReturn(true)
|
||||
.when(mockEnvironment).getProperty(eq(propertyName), eq(Boolean.class), eq(true));
|
||||
|
||||
Path mockPath= mock(Path.class);
|
||||
|
||||
Map<String, String> secret = MapBuilder.<String, String>newMapBuilder()
|
||||
.put(Binding.TYPE, GeodeBindingsPropertiesProcessor.TYPE)
|
||||
.put("gemfire.security-username", "DarthVader")
|
||||
.put("gemfire.security-password", "s5!thL0rd")
|
||||
.put("gemfire.locators", "mustafar[61616]")
|
||||
.put("gemfire.http-service-bind-address", "10.100.101.69")
|
||||
.put("gemfire.http-service-port", "7070")
|
||||
.build();
|
||||
|
||||
Binding testBinding = new Binding("gemfire-test-binding-name", mockPath, secret);
|
||||
|
||||
Bindings testBindings = new Bindings(testBinding);
|
||||
|
||||
GeodeBindingsPropertiesProcessor bindingsPropertiesProcessor = new GeodeBindingsPropertiesProcessor();
|
||||
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
bindingsPropertiesProcessor.process(mockEnvironment, testBindings, properties);
|
||||
|
||||
assertThat(properties)
|
||||
.containsEntry("spring.data.gemfire.security.username", secret.get("gemfire.security-username"))
|
||||
.containsEntry("spring.data.gemfire.security.password", secret.get("gemfire.security-password"))
|
||||
.containsEntry("spring.data.gemfire.pool.locators", secret.get("gemfire.locators"))
|
||||
.containsEntry("spring.data.gemfire.management.http.host", secret.get("gemfire.http-service-bind-address"))
|
||||
.containsEntry("spring.data.gemfire.management.http.port", secret.get("gemfire.http-service-port"));
|
||||
|
||||
assertThat(Boolean.TRUE.equals(properties.get("spring.data.gemfire.management.require-https"))).isTrue();
|
||||
assertThat(Boolean.TRUE.equals(properties.get("spring.data.gemfire.management.use-http"))).isTrue();
|
||||
assertThat(Boolean.TRUE.equals(properties.get("spring.data.gemfire.security.ssl.use-default-context"))).isTrue();
|
||||
|
||||
verify(mockEnvironment, times(1))
|
||||
.getProperty(eq(propertyName), eq(Boolean.class), eq(true));
|
||||
|
||||
verifyNoMoreInteractions(mockEnvironment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processDoesNotMapPropertiesWhenGemFireTypeIsDisabled() {
|
||||
|
||||
String propertyName = String.format(Guards.SPRING_CLOUD_BOOT_BINDINGS_TYPE_ENABLED_PROPERTY,
|
||||
GeodeBindingsPropertiesProcessor.TYPE);
|
||||
|
||||
Environment mockEnvironment = mock(Environment.class);
|
||||
|
||||
doReturn(false)
|
||||
.when(mockEnvironment).getProperty(eq(propertyName), eq(Boolean.class), eq(true));
|
||||
|
||||
Path mockPath= mock(Path.class);
|
||||
|
||||
Binding testBinding = new Binding("gemfire-test-binding-name", mockPath,
|
||||
Collections.singletonMap(Binding.TYPE, GeodeBindingsPropertiesProcessor.TYPE));
|
||||
|
||||
Bindings testBindings = new Bindings(testBinding);
|
||||
|
||||
GeodeBindingsPropertiesProcessor bindingsPropertiesProcessor = new GeodeBindingsPropertiesProcessor();
|
||||
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
bindingsPropertiesProcessor.process(mockEnvironment, testBindings, properties);
|
||||
|
||||
assertThat(properties).isEmpty();
|
||||
|
||||
verify(mockEnvironment, times(1))
|
||||
.getProperty(eq(propertyName), eq(Boolean.class), eq(true));
|
||||
|
||||
verifyNoMoreInteractions(mockEnvironment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processDoesNotMapPropertiesWhenGemFireTypeIsNotPresent() {
|
||||
|
||||
String propertyName = String.format(Guards.SPRING_CLOUD_BOOT_BINDINGS_TYPE_ENABLED_PROPERTY,
|
||||
GeodeBindingsPropertiesProcessor.TYPE);
|
||||
|
||||
Environment mockEnvironment = mock(Environment.class);
|
||||
|
||||
doReturn(true)
|
||||
.when(mockEnvironment).getProperty(eq(propertyName), eq(Boolean.class), eq(true));
|
||||
|
||||
Path mockPath= mock(Path.class);
|
||||
|
||||
Binding testBinding = new Binding("mock-test-binding-name", mockPath,
|
||||
Collections.singletonMap(Binding.TYPE, "mock"));
|
||||
|
||||
Bindings testBindings = new Bindings(testBinding);
|
||||
|
||||
GeodeBindingsPropertiesProcessor bindingsPropertiesProcessor = new GeodeBindingsPropertiesProcessor();
|
||||
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
bindingsPropertiesProcessor.process(mockEnvironment, testBindings, properties);
|
||||
|
||||
assertThat(properties).isEmpty();
|
||||
|
||||
verify(mockEnvironment, times(1))
|
||||
.getProperty(eq(propertyName), eq(Boolean.class), eq(true));
|
||||
|
||||
verifyNoMoreInteractions(mockEnvironment);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user