Bindings
* Simplify name from CnbBindings (a convention that makes my eyes bleed) so that you aren't stuttering. I'm still a bit undecided on the naming of the modules themselves as well. * Documentation, documentation, documentation! * Lots of the final keyword. Prevents unanticipated modification via extension. * Constructor/Method overloading allows you to isolate the behaviors that are really hard to test. Note that I've isolated everything that needs to read from the environment and feel confident that the methods that include it are so trivial they're not worth testing. Some overloads are non-public to enable testing but not later usage. * Since this is a public API, I'm being really verbose with the nullability annotations. Most of the time you wouldn't use them, by they do make IDE users' lives better. Signed-off-by: Ben Hale <bhale@vmware.com>
This commit is contained in:
@@ -26,7 +26,7 @@ import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.cloud.cnb.core.CNBBindingsSingleton;
|
||||
import org.springframework.cloud.cnb.core.CnbBinding;
|
||||
import org.springframework.cloud.cnb.core.CnbBindings;
|
||||
import org.springframework.cloud.cnb.core.Bindings;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.Ordered;
|
||||
@@ -77,7 +77,7 @@ public class CnbBindingsPostProcessor implements EnvironmentPostProcessor, Order
|
||||
// TODO: allow users to disable processing of a given binding by name, kind, or processor
|
||||
|
||||
increaseInvocationCount();
|
||||
CnbBindings bindings = CNBBindingsSingleton.getCnbBindingsInstance();
|
||||
Bindings bindings = CNBBindingsSingleton.getCnbBindingsInstance();
|
||||
|
||||
if (bindings.hasBindings()) {
|
||||
List<CnbBinding> allBindings = bindings.findAllBindings();
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.Map;
|
||||
|
||||
import mockit.MockUp;
|
||||
|
||||
import org.springframework.cloud.cnb.core.CnbBindings;
|
||||
import org.springframework.cloud.cnb.core.Bindings;
|
||||
|
||||
|
||||
/**
|
||||
@@ -36,7 +36,7 @@ public class EnvMock {
|
||||
this.mockUp = new MockUp<System>() {
|
||||
@mockit.Mock
|
||||
public String getenv(String name) {
|
||||
if (name.equalsIgnoreCase(CnbBindings.CNB_BINDINGS)) {
|
||||
if (name.equalsIgnoreCase(Bindings.CNB_BINDINGS)) {
|
||||
return cnbBindingsPath;
|
||||
}
|
||||
return env.get(name);
|
||||
|
||||
@@ -16,12 +16,9 @@
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.cnb.core;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* A representation of a collection of bindings as defined by the
|
||||
* <a href="https://github.com/buildpacks/spec/blob/master/extensions/bindings.md">Cloud Native Buildpacks Specification</a>.
|
||||
*/
|
||||
public final class Bindings {
|
||||
|
||||
/**
|
||||
* The name of the environment variable to read to determine the bindings file system root. Specified by the Cloud
|
||||
* Native Buildpacks Specification.
|
||||
*/
|
||||
public static final String CNB_BINDINGS = "CNB_BINDINGS";
|
||||
|
||||
private final List<CnbBinding> bindings;
|
||||
|
||||
/**
|
||||
* Creates a new {@code Bindings} instance, using the {@code $CNB_BINDINGS} environment variable to determine the
|
||||
* file system root. If the {@code $CNB_BINDINGS} environment variable is not set, an empty {@code Bindings} is
|
||||
* returned. If the directory does not exist, an empty {@code Bindings} is returned.
|
||||
*/
|
||||
public Bindings() {
|
||||
this(System.getenv(CNB_BINDINGS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code Bindings} instance, using the specified {@code path}. If the directory does not exist, an
|
||||
* empty {@code Bindings} is returned.
|
||||
*
|
||||
* @param path the path to populate the {@code Bindings} from.
|
||||
*/
|
||||
public Bindings(@NotNull String path) {
|
||||
Path p = Paths.get(path);
|
||||
|
||||
if (!Files.exists(p)) {
|
||||
this.bindings = Collections.emptyList();
|
||||
} else if (!Files.isDirectory(p)) {
|
||||
throw new IllegalArgumentException(String.format("%s is not a directory", p));
|
||||
} else {
|
||||
try {
|
||||
this.bindings = Files.list(p)
|
||||
.map(c -> new CnbBinding(c))
|
||||
.collect(Collectors.toList());
|
||||
} catch (IOException e) {
|
||||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the {@code $CNB_BINDINGS} is and zero or more bindings will be available.
|
||||
*
|
||||
* @return {@code true} if {@code $CNB_BINDINGS} is set, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean hasBindings() {
|
||||
return hasBindings(System.getenv());
|
||||
}
|
||||
|
||||
static boolean hasBindings(@NotNull Map<String, String> environment) {
|
||||
return environment.containsKey(CNB_BINDINGS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Binding} with a given name.
|
||||
*
|
||||
* @param name the name of the {@code Binding} to find.
|
||||
* @return the {@code Binding} with a given name if it exists, {@code null} otherwise.
|
||||
*/
|
||||
public @Nullable CnbBinding findBinding(@NotNull String name) {
|
||||
for (CnbBinding binding : this.bindings) {
|
||||
if (binding.getName().equals(name)) {
|
||||
return binding;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the {@link Binding}s that were found during construction.
|
||||
*/
|
||||
public @NotNull List<CnbBinding> findBindings() {
|
||||
return this.bindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns zero or more {@link Binding}s with a given kind. Equivalent to {@link #findBindings(String, String)}.
|
||||
*
|
||||
* @param kind the kind of the {@code Binding} to find.
|
||||
* @return the collection of {@code Binding}s with a given kind.
|
||||
*/
|
||||
public @NotNull List<CnbBinding> findBindings(@Nullable String kind) {
|
||||
return findBindings(kind, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return zero or more {@link Binding}s with a given kind and provider. If {@code kind} or {@code provider} are
|
||||
* {@code null}, the result is not filtered on that argument.
|
||||
*
|
||||
* @param kind the kind of {@code Binding} to find.
|
||||
* @param provider the provider of {@code Binding} to find
|
||||
* @return the collection of {@code Binding}s with a given kind and provider.
|
||||
*/
|
||||
public @NotNull List<CnbBinding> findBindings(@Nullable String kind, @Nullable String provider) {
|
||||
List<CnbBinding> bindings = new ArrayList<>();
|
||||
|
||||
for (CnbBinding binding : this.bindings) {
|
||||
if ((kind == null || binding.getKind().equals(kind)) &&
|
||||
(provider == null) || binding.getProvider().equals(provider)) {
|
||||
|
||||
bindings.add(binding);
|
||||
}
|
||||
}
|
||||
|
||||
return bindings;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,15 +21,15 @@ package org.springframework.cloud.cnb.core;
|
||||
*/
|
||||
public final class CNBBindingsSingleton {
|
||||
|
||||
private static CnbBindings INSTANCE;
|
||||
private static Bindings INSTANCE;
|
||||
|
||||
private CNBBindingsSingleton() {
|
||||
|
||||
}
|
||||
|
||||
public synchronized static CnbBindings getCnbBindingsInstance() {
|
||||
public synchronized static Bindings getCnbBindingsInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new CnbBindings();
|
||||
INSTANCE = new Bindings();
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 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.cnb.core;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides access to CNB Bindings.
|
||||
*
|
||||
* @author Emily Casey
|
||||
*/
|
||||
public class CnbBindings {
|
||||
|
||||
public static final String CNB_BINDINGS = "CNB_BINDINGS";
|
||||
|
||||
private List<CnbBinding> bindings = new ArrayList<>();
|
||||
|
||||
public CnbBindings() {
|
||||
String cnbBindingsDir = System.getenv(CNB_BINDINGS);
|
||||
if (cnbBindingsDir == null) {
|
||||
return;
|
||||
}
|
||||
File bindingsDir = new File(cnbBindingsDir);
|
||||
if (!bindingsDir.exists()) {
|
||||
return;
|
||||
}
|
||||
if (!bindingsDir.isDirectory()) {
|
||||
throw new IllegalStateException(String.format("CNB_BINDINGS '%s' is not a directory", bindingsDir.toString()));
|
||||
}
|
||||
for (File file : bindingsDir.listFiles()) {
|
||||
if (!file.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
this.bindings.add(new CnbBinding(file));
|
||||
}
|
||||
}
|
||||
|
||||
public List<CnbBinding> findAllBindings() {
|
||||
return this.bindings;
|
||||
}
|
||||
|
||||
public boolean hasBindings() {
|
||||
return System.getenv(CNB_BINDINGS) != null;
|
||||
}
|
||||
|
||||
public CnbBinding findBindingByName(String name) {
|
||||
for (CnbBinding binding : this.bindings) {
|
||||
if (binding.getName().equals(name)) {
|
||||
return binding;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.cnb.core;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class BindingsTests {
|
||||
|
||||
@Test
|
||||
void constructFromNonExistentDirectory() {
|
||||
String path = "src/test/resources/non-existent";
|
||||
Bindings b = new Bindings(path);
|
||||
|
||||
assertThat(b.findBindings()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructFromNonDirectory() throws IOException {
|
||||
String path = File.createTempFile("bindings", "").getPath();
|
||||
Bindings b = new Bindings(path);
|
||||
|
||||
assertThat(b.findBindings()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void construct() {
|
||||
String path = "src/test/resources";
|
||||
Bindings b = new Bindings(path);
|
||||
|
||||
assertThat(b.findBindings()).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasBindings() {
|
||||
assertThat(Bindings.hasBindings(Collections.emptyMap())).isFalse();
|
||||
assertThat(Bindings.hasBindings(Collections.singletonMap("CNB_BINDINGS", ""))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllBindings() {
|
||||
String path = "src/test/resources";
|
||||
Bindings b = new Bindings(path);
|
||||
|
||||
assertThat(b.findBindings()).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findBindingsByKind() {
|
||||
String path = "src/test/resources";
|
||||
Bindings b = new Bindings(path);
|
||||
|
||||
assertThat(b.findBindings("test-kind-1", null)).containsExactly(new Binding());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findBindingsByProvider() {
|
||||
String path = "src/test/resources";
|
||||
Bindings b = new Bindings(path);
|
||||
|
||||
assertThat(b.findBindings(null, "test-provider-1")).containsExactly(new Binding());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,14 +29,14 @@ public class CnbBindingsHasBindingsTests {
|
||||
@Test
|
||||
public void testHasBindings() {
|
||||
new EnvMock("some/path");
|
||||
CnbBindings cnbBindings = new CnbBindings();
|
||||
assertThat(cnbBindings.hasBindings()).isTrue();
|
||||
Bindings bindings = new Bindings();
|
||||
assertThat(bindings.hasBindings()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testHasBindingsFalse() {
|
||||
CnbBindings cnbBindings = new CnbBindings();
|
||||
assertThat(cnbBindings.hasBindings()).isFalse();
|
||||
Bindings bindings = new Bindings();
|
||||
assertThat(bindings.hasBindings()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 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.cnb.core;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.cnb.core.test.EnvMock;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class CnbBindingsTests {
|
||||
|
||||
@Test
|
||||
public void testBindings() {
|
||||
Path resourceDirectory = Paths.get("src", "test", "resources", "test-bindings");
|
||||
String bindingsDir = resourceDirectory.toFile().getAbsolutePath();
|
||||
|
||||
new EnvMock(bindingsDir);
|
||||
|
||||
CnbBindings cnbBindings = new CnbBindings();
|
||||
|
||||
List<CnbBinding> bindings = cnbBindings.findAllBindings();
|
||||
assertThat(bindings.size()).isEqualTo(1);
|
||||
|
||||
CnbBinding mysqlBinding = cnbBindings.findBindingByName("p-mysql");
|
||||
Map<String, String> mysqlSecret = mysqlBinding.getSecret();
|
||||
assertThat(mysqlSecret).containsEntry("hostname", "10.0.4.35")
|
||||
.containsEntry("port", "3306")
|
||||
.containsEntry("name", "mysql_name")
|
||||
.containsEntry("username", "mysql_username")
|
||||
.containsEntry("password", "mysql_password")
|
||||
.containsEntry("uri",
|
||||
"mysql://mysql_username:mysql_password@10.0.4.35:3306/cf_2e23d10a_8738_8c3c_66cf_13e44422698c?reconnect=true")
|
||||
.containsEntry("jdbcUrl",
|
||||
"jdbc:mysql://10.0.4.35:3306/cf_2e23d10a_8738_8c3c_66cf_13e44422698c?user=mysql_username&password=mysql_password");
|
||||
Map<String, String> mysqlMetadata = mysqlBinding.getAllMetadata();
|
||||
assertThat(mysqlMetadata).containsEntry("kind", "mysql")
|
||||
.containsEntry("provider", "p-mysql")
|
||||
.containsEntry("tags", "mysql,relational");
|
||||
assertThat(mysqlBinding.getKind()).isEqualTo("mysql");
|
||||
assertThat(mysqlBinding.getProvider()).isEqualTo("p-mysql");
|
||||
assertThat(mysqlBinding.getTags()).contains("mysql", "relational");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 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.cnb.core.test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import mockit.MockUp;
|
||||
|
||||
import org.springframework.cloud.cnb.core.CnbBindings;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @author Emily Casey
|
||||
**/
|
||||
public class EnvMock {
|
||||
private MockUp<?> mockUp;
|
||||
|
||||
public EnvMock(String cnbBindingsPath) {
|
||||
|
||||
Map<String, String> env = System.getenv();
|
||||
this.mockUp = new MockUp<System>() {
|
||||
@mockit.Mock
|
||||
public String getenv(String name) {
|
||||
if (name.equalsIgnoreCase(CnbBindings.CNB_BINDINGS)) {
|
||||
return cnbBindingsPath;
|
||||
}
|
||||
return env.get(name);
|
||||
}
|
||||
|
||||
@mockit.Mock
|
||||
public Map getenv() {
|
||||
Map<String, String> finalMap = new HashMap<>();
|
||||
finalMap.putAll(env);
|
||||
finalMap.put("CNB_BINDINGS", cnbBindingsPath);
|
||||
return finalMap;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
mysql
|
||||
@@ -1 +0,0 @@
|
||||
p-mysql
|
||||
@@ -1 +0,0 @@
|
||||
mysql,relational
|
||||
@@ -1 +0,0 @@
|
||||
10.0.4.35
|
||||
@@ -1 +0,0 @@
|
||||
jdbc:mysql://10.0.4.35:3306/cf_2e23d10a_8738_8c3c_66cf_13e44422698c?user=mysql_username&password=mysql_password
|
||||
@@ -1 +0,0 @@
|
||||
mysql_name
|
||||
@@ -1 +0,0 @@
|
||||
mysql_password
|
||||
@@ -1 +0,0 @@
|
||||
3306
|
||||
@@ -1 +0,0 @@
|
||||
mysql://mysql_username:mysql_password@10.0.4.35:3306/cf_2e23d10a_8738_8c3c_66cf_13e44422698c?reconnect=true
|
||||
@@ -1 +0,0 @@
|
||||
mysql_username
|
||||
@@ -0,0 +1 @@
|
||||
test-kind-1
|
||||
@@ -0,0 +1 @@
|
||||
test-provider-1
|
||||
@@ -0,0 +1 @@
|
||||
test-value
|
||||
@@ -0,0 +1 @@
|
||||
test-kind-2
|
||||
@@ -0,0 +1 @@
|
||||
test-provider-2
|
||||
@@ -0,0 +1 @@
|
||||
test-value
|
||||
7
pom.xml
7
pom.xml
@@ -28,6 +28,7 @@
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<jetbrains-annotations.version>19.0.0</jetbrains-annotations.version>
|
||||
<spring-boot.version>2.2.7.RELEASE</spring-boot.version>
|
||||
|
||||
<!-- Plugins -->
|
||||
@@ -63,6 +64,12 @@
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>${jetbrains-annotations.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user