io.scalecube.config.vault.VaultConfigSource Maven / Gradle / Ivy
The newest version!
package io.scalecube.config.vault;
import com.bettercloud.vault.EnvironmentLoader;
import com.bettercloud.vault.VaultConfig;
import com.bettercloud.vault.VaultException;
import com.bettercloud.vault.response.LogicalResponse;
import io.scalecube.config.ConfigProperty;
import io.scalecube.config.ConfigSourceNotAvailableException;
import io.scalecube.config.source.ConfigSource;
import io.scalecube.config.source.LoadedConfigProperty;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
/**
* This class is an implementation of {@link ConfigSource} for Vault.
*
* @see Vault Project
*/
public class VaultConfigSource implements ConfigSource {
private static final Logger LOGGER = System.getLogger(VaultConfigSource.class.getName());
private static final EnvironmentLoader ENVIRONMENT_LOADER = new EnvironmentLoader();
private static final String PATHS_SEPARATOR = ":";
private final VaultInvoker vault;
private final Collection secretsPaths;
private VaultConfigSource(VaultInvoker vault, Collection secretsPaths) {
this.vault = vault;
this.secretsPaths = new ArrayList<>(secretsPaths);
}
@Override
public Map loadConfig() {
Map result = new HashMap<>();
for (String path : secretsPaths) {
try {
LogicalResponse response = vault.invoke(vault -> vault.logical().read(path));
final Map pathProps =
response.getData().entrySet().stream()
.map(LoadedConfigProperty::withNameAndValue)
.map(LoadedConfigProperty.Builder::build)
.collect(Collectors.toMap(LoadedConfigProperty::name, Function.identity()));
result.putAll(pathProps);
} catch (VaultException ex) {
if (ex.getHttpStatusCode() == 404) {
LOGGER.log(Level.ERROR, "Unable to load config properties from: " + path);
} else {
throw new ConfigSourceNotAvailableException(ex);
}
} catch (Exception ex) {
LOGGER.log(Level.ERROR, "Unable to load config properties from: " + path, ex);
throw new ConfigSourceNotAvailableException(ex);
}
}
return result;
}
public static final class Builder {
private Function builderFunction =
Function.identity();
private VaultInvoker invoker;
private Set secretsPaths =
Optional.ofNullable(
Optional.ofNullable(ENVIRONMENT_LOADER.loadVariable("VAULT_SECRETS_PATH"))
.orElse(ENVIRONMENT_LOADER.loadVariable("VAULT_SECRETS_PATHS")))
.map(s -> s.split(PATHS_SEPARATOR))
.map(Arrays::asList)
.map(HashSet::new)
.orElseGet(HashSet::new);
public Builder() {}
/**
* Appends {@code secretsPath} to {@code secretsPaths}.
*
* @param secretsPath secretsPath (may contain value with paths separated by {@code :})
* @return this builder
* @deprecated will be removed in future releases without notice, use {@link
* #addSecretsPath(String...)} or {@link #secretsPaths(Collection)}.
*/
@Deprecated
public Builder secretsPath(String secretsPath) {
this.secretsPaths.addAll(toSecretsPaths(Collections.singletonList(secretsPath)));
return this;
}
/**
* Appends one or several secretsPath\es to {@code secretsPaths}.
*
* @param secretsPath one or several secretsPath\es (each value may contain paths separated by
* {@code :})
* @return this builder
*/
public Builder addSecretsPath(String... secretsPath) {
this.secretsPaths.addAll(toSecretsPaths(Arrays.asList(secretsPath)));
return this;
}
/**
* Setter for {@code secretsPaths}.
*
* @param secretsPaths collection of secretsPath\es (each value may contain paths separated by
* colon)
* @return this builder
*/
public Builder secretsPaths(Collection secretsPaths) {
this.secretsPaths = toSecretsPaths(secretsPaths);
return this;
}
private static Set toSecretsPaths(Collection secretsPaths) {
return secretsPaths.stream()
.flatMap(s -> Arrays.stream(s.split(PATHS_SEPARATOR)))
.collect(Collectors.toSet());
}
public Builder invoker(VaultInvoker invoker) {
this.invoker = invoker;
return this;
}
public Builder vault(UnaryOperator opts) {
this.builderFunction = this.builderFunction.andThen(opts);
return this;
}
public Builder config(UnaryOperator vaultConfig) {
this.builderFunction = this.builderFunction.andThen(b -> b.options(vaultConfig));
return this;
}
public Builder tokenSupplier(VaultTokenSupplier supplier) {
this.builderFunction = this.builderFunction.andThen(b -> b.tokenSupplier(supplier));
return this;
}
/**
* Builds vault config source.
*
* @return instance of {@link VaultConfigSource}
*/
public VaultConfigSource build() {
return new VaultConfigSource(
invoker != null ? invoker : builderFunction.apply(new VaultInvoker.Builder()).build(),
secretsPaths);
}
}
}