Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Vault #42

Merged
merged 17 commits into from
Apr 20, 2018
39 changes: 39 additions & 0 deletions config-vault/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.scalecube</groupId>
<artifactId>config-parent</artifactId>
<version>0.3.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>config-vault</artifactId>
<properties>
<enforcer.skip>true</enforcer.skip>
</properties>

<dependencies>
<dependency>
<groupId>io.scalecube</groupId>
<artifactId>config</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.bettercloud</groupId>
<artifactId>vault-java-driver</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>vault</artifactId>
<version>1.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>co.unruly</groupId>
<artifactId>java-8-matchers</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package io.scalecube.config.vault;

import static java.util.Objects.requireNonNull;

import io.scalecube.config.ConfigProperty;
import io.scalecube.config.ConfigSourceNotAvailableException;
import io.scalecube.config.source.ConfigSource;
import io.scalecube.config.source.LoadedConfigProperty;
import io.scalecube.config.utils.ThrowableUtil;

import com.bettercloud.vault.EnvironmentLoader;
import com.bettercloud.vault.SslConfig;
import com.bettercloud.vault.Vault;
import com.bettercloud.vault.VaultConfig;
import com.bettercloud.vault.VaultException;
import com.bettercloud.vault.response.LogicalResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
* This class is a {@link ConfigSource} implemented for Vault
*
* @see <a href="https://www.vaultproject.io/">Vault Project</a>
*/
public class VaultConfigSource implements ConfigSource {

static final Logger LOGGER = LoggerFactory.getLogger(VaultConfigSource.class);

private final Vault vault;
private final String secretsPath;

/**
* Create a new {@link VaultConfigSource} with the given {@link Builder}. <br>
*
* @param builder configuration to create vault access with.
*
*/
VaultConfigSource(Builder builder) {
this.secretsPath = builder.secretsPath();
vault = new Vault(builder.config);
}

private void checkVaultStatus() throws VaultException {
if (vault.seal().sealStatus().getSealed()) {
throw new VaultException("Vault is sealed");
}
Boolean initialized = vault.debug().health().getInitialized();
if (!initialized) {
throw new VaultException("Vault not yet initialized");
}
}

@Override
public Map<String, ConfigProperty> loadConfig() {
try {
checkVaultStatus();
LogicalResponse response = vault.logical().read(this.secretsPath);
return response.getData().entrySet().stream().map(LoadedConfigProperty::withNameAndValue)
.map(LoadedConfigProperty.Builder::build)
.collect(Collectors.toMap(LoadedConfigProperty::name, Function.identity()));
} catch (VaultException vaultException) {
LOGGER.warn("unable to load config properties", vaultException);
throw new ConfigSourceNotAvailableException(vaultException);
}
}

/**
* This builder method is used internally for test purposes. please use it only for tests. Please note the following
* required environment variables are required.
* <ul>
* <li><code>VAULT_SECRETS_PATH</pre> is the path to use (defaults to <code>secret</code>)</li>
* <li><code>VAULT_TOKEN</code> is the {@link VaultConfig#token(String) token} to use</li>
* <li><code>VAULT_ADDR</code> is the {@link VaultConfig#address(String) address} of the vault (API)</li>
* </ul>
*/
public static Builder builder() {
return builder(new EnvironmentLoader());
}

/**
* This builder method is used internally for test purposes. please use it only for tests
*
* @param environmentLoader an {@link EnvironmentLoader}
*/
static Builder builder(EnvironmentLoader environmentLoader) {
return builder(environmentLoader.loadVariable("VAULT_ADDR"),
environmentLoader.loadVariable("VAULT_TOKEN"),
environmentLoader.loadVariable("VAULT_SECRETS_PATH"));
}

public static Builder builder(String address, String token, String secretsPath) {
return new Builder(address, token, secretsPath);
}

public static final class Builder {

final VaultConfig config = new VaultConfig();
private final String secretsPath;

Builder(String address, String token, String secretsPath) {
config.address(requireNonNull(address, "Missing address"))
.token(requireNonNull(token, "Missing token"))
.sslConfig(new SslConfig());
this.secretsPath = requireNonNull(secretsPath, "Missing secretsPath");
}

public Builder connectTimeout(int connectTimeout) {
config.openTimeout(connectTimeout);
return this;
}

public Builder readTimeout(int readTimeout) {
config.readTimeout(readTimeout);
return this;
}

public VaultConfigSource build() {
try {
this.config.build();
return new VaultConfigSource(this);
} catch (VaultException propogateException) {
LOGGER.error("Unable to build " + VaultConfigSource.class.getSimpleName(), propogateException);
throw ThrowableUtil.propagate(propogateException);
}
}

public String secretsPath() {
return secretsPath;
}
}
}
Loading