Skip to content

Commit

Permalink
Hacking
Browse files Browse the repository at this point in the history
  • Loading branch information
mbhave committed Jul 9, 2020
1 parent 14fd1c9 commit 9a79cdd
Show file tree
Hide file tree
Showing 8 changed files with 371 additions and 30 deletions.
@@ -0,0 +1,39 @@
/*
* Copyright 2012-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.boot.context.config;

import java.io.IOException;
import java.util.Collections;

import org.springframework.boot.env.VolumeMountDirectoryPropertySource;

/**
*
* {@link ConfigDataLoader} for directory locations mounted as volumes.
*
* @author Madhura Bhave
*/
class ConfigVolumeMountLoader implements ConfigDataLoader<ConfigVolumeMountLocation> {

@Override
public ConfigData load(ConfigVolumeMountLocation location) throws IOException {
VolumeMountDirectoryPropertySource source = new VolumeMountDirectoryPropertySource(location.getLocation(),
location.getPath());
return new ConfigData(Collections.singletonList(source));
}

}
@@ -0,0 +1,76 @@
/*
* Copyright 2012-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.boot.context.config;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;

import org.springframework.util.Assert;

/**
* {@link ConfigDataLocation} backed by a directory mounted as a volume.
*
* @author Madhura Bhave
*/
class ConfigVolumeMountLocation extends ConfigDataLocation {

private final String location;

private final Path path;

public ConfigVolumeMountLocation(String location) {
Assert.notNull(location, "Location must not be null");
this.location = getLocation(location);
this.path = Paths.get(location);
}

private String getLocation(String location) {
return "volumemount (" + location + ")";
}

String getLocation() {
return this.location;
}

public Path getPath() {
return this.path;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
ConfigVolumeMountLocation other = (ConfigVolumeMountLocation) obj;
return Objects.equals(this.path, other.path);
}

@Override
public int hashCode() {
return this.path.hashCode();
}

@Override
public String toString() {
return this.location;
}

}
@@ -0,0 +1,54 @@
/*
* Copyright 2012-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.boot.context.config;

import java.util.Collections;
import java.util.List;

import org.apache.commons.logging.Log;

import org.springframework.boot.context.properties.bind.Binder;

/**
* {@link ConfigDataLocationResolver} for volume mounted locations such as Kubernetes
* ConfigMaps and Secrets.
*
* @author Madhura Bhave
*/
class ConfigVolumeMountLocationResolver implements ConfigDataLocationResolver<ConfigVolumeMountLocation> {

private static final String VOLUME_MOUNT_URL_PREFIX = "volumemount:";

private final Log logger;

public ConfigVolumeMountLocationResolver(Log logger) {
this.logger = logger;
}

@Override
public boolean isResolvable(Binder binder, ConfigDataLocation parent, String location) {
return (location != null && location.startsWith(VOLUME_MOUNT_URL_PREFIX));
}

@Override
public List<ConfigVolumeMountLocation> resolve(Binder binder, ConfigDataLocation parent, String location) {
ConfigVolumeMountLocation volumeMountLocation = new ConfigVolumeMountLocation(
location.split(VOLUME_MOUNT_URL_PREFIX)[1]);
return Collections.singletonList(volumeMountLocation);
}

}
Expand Up @@ -69,7 +69,7 @@
* @author Phillip Webb
* @since 2.4.0
*/
public class PathDirectoryPropertySource extends EnumerablePropertySource<Path> implements OriginLookup<String> {
public class VolumeMountDirectoryPropertySource extends EnumerablePropertySource<Path> implements OriginLookup<String> {

private static final int MAX_DEPTH = 100;

Expand All @@ -80,25 +80,25 @@ public class PathDirectoryPropertySource extends EnumerablePropertySource<Path>
private final Set<Option> options;

/**
* Create a new {@link PathDirectoryPropertySource} instance.
* Create a new {@link VolumeMountDirectoryPropertySource} instance.
* @param name the name of the property source
* @param sourceDirectory the underlying source directory
*/
public PathDirectoryPropertySource(String name, Path sourceDirectory) {
public VolumeMountDirectoryPropertySource(String name, Path sourceDirectory) {
this(name, sourceDirectory, EnumSet.noneOf(Option.class));
}

/**
* Create a new {@link PathDirectoryPropertySource} instance.
* Create a new {@link VolumeMountDirectoryPropertySource} instance.
* @param name the name of the property source
* @param sourceDirectory the underlying source directory
* @param options the property source options
*/
public PathDirectoryPropertySource(String name, Path sourceDirectory, Option... options) {
public VolumeMountDirectoryPropertySource(String name, Path sourceDirectory, Option... options) {
this(name, sourceDirectory, EnumSet.copyOf(Arrays.asList(options)));
}

private PathDirectoryPropertySource(String name, Path sourceDirectory, Set<Option> options) {
private VolumeMountDirectoryPropertySource(String name, Path sourceDirectory, Set<Option> options) {
super(name, sourceDirectory);
Assert.isTrue(Files.exists(sourceDirectory), "Directory '" + sourceDirectory + "' does not exist");
Assert.isTrue(Files.isDirectory(sourceDirectory), "File '" + sourceDirectory + "' is not a directory");
Expand Down
@@ -0,0 +1,57 @@
/*
* Copyright 2012-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.boot.context.config;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import org.springframework.core.env.PropertySource;
import org.springframework.util.FileCopyUtils;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link ConfigVolumeMountLoader}.
*
* @author Madhura Bhave
*/
public class ConfigVolumeMountLoaderTests {

private ConfigVolumeMountLoader loader = new ConfigVolumeMountLoader();

@TempDir
Path directory;

@Test
void loadReturnsConfigDataWithPropertySource() throws IOException {
File file = this.directory.resolve("hello").toFile();
file.getParentFile().mkdirs();
FileCopyUtils.copy("world".getBytes(StandardCharsets.UTF_8), file);
ConfigVolumeMountLocation location = new ConfigVolumeMountLocation(this.directory.toString());
ConfigData configData = this.loader.load(location);
assertThat(configData.getPropertySources().size()).isEqualTo(1);
PropertySource<?> source = configData.getPropertySources().get(0);
assertThat(source.getName()).isEqualTo("volumemount (" + this.directory.toString() + ")");
assertThat(source.getProperty("hello").toString()).isEqualTo("world");
}

}
@@ -0,0 +1,58 @@
/*
* Copyright 2012-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.boot.context.config;

import java.util.List;

import org.junit.jupiter.api.Test;

import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.logging.DeferredLog;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

/**
* Tests for {@link ConfigVolumeMountLocationResolver}.
*
* @author Madhura Bhave
*/
class ConfigVolumeMountLocationResolverTests {

private ConfigVolumeMountLocationResolver resolver = new ConfigVolumeMountLocationResolver(new DeferredLog());

private Binder binder = mock(Binder.class);

@Test
void isResolvableWhenPrefixMatchesReturnsTrue() {
assertThat(this.resolver.isResolvable(this.binder, null, "volumemount:/etc/config")).isTrue();
}

@Test
void isResolvableWhenPrefixDoesNotMatchReturnsFalse() {
assertThat(this.resolver.isResolvable(this.binder, null, "http://etc/config")).isFalse();
assertThat(this.resolver.isResolvable(this.binder, null, "/etc/config")).isFalse();
}

@Test
void resolveShouldReturnConfigVolumeMountLocation() {
List<ConfigVolumeMountLocation> locations = this.resolver.resolve(this.binder, null, "volumemount:/etc/config");
assertThat(locations.size()).isEqualTo(1);
assertThat(locations).extracting("location").containsExactly("volumemount (/etc/config)");
}

}
@@ -0,0 +1,57 @@
/*
* Copyright 2012-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.boot.context.config;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;

/**
* Tests for {@link ConfigVolumeMountLocation}.
*
* @author Madhura Bhave
*/
public class ConfigVolumeMountLocationTests {

@Test
void constructorWhenLocationIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new ConfigVolumeMountLocation(null))
.withMessage("Location must not be null");
}

@Test
void equalsWhenPathIsTheSameReturnsTrue() {
ConfigVolumeMountLocation location = new ConfigVolumeMountLocation("/etc/config");
ConfigVolumeMountLocation other = new ConfigVolumeMountLocation("/etc/config");
assertThat(location).isEqualTo(other);
}

@Test
void equalsWhenPathIsDifferentReturnsFalse() {
ConfigVolumeMountLocation location = new ConfigVolumeMountLocation("/etc/config");
ConfigVolumeMountLocation other = new ConfigVolumeMountLocation("other-location");
assertThat(location).isNotEqualTo(other);
}

@Test
void toStringShouldAppendVolumeMountPrefixToLocation() {
ConfigVolumeMountLocation location = new ConfigVolumeMountLocation("/etc/config");
assertThat(location.toString()).isEqualTo("volumemount (/etc/config)");
}

}

0 comments on commit 9a79cdd

Please sign in to comment.