diff --git a/arquillian-ape-nosql/pom.xml b/arquillian-ape-nosql/pom.xml index 0ba0b819..2fcce2be 100644 --- a/arquillian-ape-nosql/pom.xml +++ b/arquillian-ape-nosql/pom.xml @@ -15,7 +15,8 @@ 2.9.0 2.4.3 2.0.0 - 1.0.0-rc.2 + 1.0.0-rc.3 + 2.0.0 arquillian-ape-nosql-parent @@ -40,6 +41,11 @@ nosqlunit-couchbase ${version.nosqlunit} + + com.lordofthejars + nosqlunit-vault + ${version.nosqlunit} + org.mongodb mongodb-driver @@ -55,6 +61,11 @@ java-client ${version.couchbase} + + com.bettercloud + vault-java-driver + ${version.vault} + org.awaitility awaitility @@ -71,6 +82,8 @@ mongodb-ftest redis redis-ftest + vault + vault-ftest diff --git a/arquillian-ape-nosql/vault-ftest/pom.xml b/arquillian-ape-nosql/vault-ftest/pom.xml new file mode 100644 index 00000000..69c8819b --- /dev/null +++ b/arquillian-ape-nosql/vault-ftest/pom.xml @@ -0,0 +1,71 @@ + + + + + org.arquillian.ape + arquillian-ape-nosql-parent + 2.0.0-SNAPSHOT + + + 4.0.0 + + + + + arquillian-ape-nosql-vault-ftest + jar + + Arquillian Ape NoSQL Vault Functional Test + + + + com.lordofthejars + nosqlunit-vault + test + + + com.bettercloud + vault-java-driver + + + org.arquillian.cube + arquillian-cube-docker + test + + + org.arquillian.ape + arquillian-ape-nosql-vault + ${project.version} + test + + + org.jboss.arquillian.junit + arquillian-junit-standalone + test + + + junit + junit + + + org.assertj + assertj-core + + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + + + diff --git a/arquillian-ape-nosql/vault-ftest/src/main/java/org/arquillian/ape/nosql/vault/Secrets.java b/arquillian-ape-nosql/vault-ftest/src/main/java/org/arquillian/ape/nosql/vault/Secrets.java new file mode 100644 index 00000000..6cc0eba6 --- /dev/null +++ b/arquillian-ape-nosql/vault-ftest/src/main/java/org/arquillian/ape/nosql/vault/Secrets.java @@ -0,0 +1,22 @@ +package org.arquillian.ape.nosql.vault; + +import com.bettercloud.vault.Vault; +import com.bettercloud.vault.VaultConfig; +import com.bettercloud.vault.VaultException; +import java.util.Map; + +public class Secrets { + + private com.bettercloud.vault.VaultConfig vaultConfig; + private Vault vault; + + public Secrets(String host, int port, String token) throws VaultException { + this.vaultConfig = new VaultConfig(String.format("http://%s:%d", host, port), token); + this.vault = new Vault(this.vaultConfig); + } + + public Map getFooSecret() throws VaultException { + return this.vault.logical().read("secret/foo").getData(); + } + +} diff --git a/arquillian-ape-nosql/vault-ftest/src/test/java/org/arquillian/ape/nosql/vault/VaultTest.java b/arquillian-ape-nosql/vault-ftest/src/test/java/org/arquillian/ape/nosql/vault/VaultTest.java new file mode 100644 index 00000000..7cd1f430 --- /dev/null +++ b/arquillian-ape-nosql/vault-ftest/src/test/java/org/arquillian/ape/nosql/vault/VaultTest.java @@ -0,0 +1,52 @@ +package org.arquillian.ape.nosql.vault; + +import com.bettercloud.vault.VaultException; +import java.util.Map; +import org.arquillian.ape.nosql.NoSqlPopulator; +import org.arquillian.cube.docker.impl.client.containerobject.dsl.Container; +import org.arquillian.cube.docker.impl.client.containerobject.dsl.DockerContainer; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.arquillian.ape.nosql.vault.VaultOptions.options; +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(Arquillian.class) +public class VaultTest { + + public static final String ROOT_TOKEN = "c56a4180-65aa-42ec-a945-5fd21dec0538"; + @ArquillianResource + @Vault + NoSqlPopulator vaultPopulator; + + @DockerContainer + Container vault = Container.withContainerName("vault") + .fromImage("vault:0.7.0") + .withPortBinding(8200) + .withEnvironment("VAULT_DEV_ROOT_TOKEN_ID", ROOT_TOKEN) + .build(); + + @Test + public void should_read_secrets_from_vault() throws VaultException { + + // given + + vaultPopulator.forServer("http://" + vault.getIpAddress(), vault.getBindPort(8200)) + .usingDataSet("mysecret.yml") + .withOptions(options() + .token(ROOT_TOKEN) + .build()) + .execute(); + + // when + + final Secrets secrets = new Secrets(vault.getIpAddress(), vault.getBindPort(8200), ROOT_TOKEN); + final Map data = secrets.getFooSecret(); + + // then + + assertThat(data).containsEntry("zip", "zap").containsEntry("a", "b"); + } +} diff --git a/arquillian-ape-nosql/vault-ftest/src/test/resources/mysecret.yml b/arquillian-ape-nosql/vault-ftest/src/test/resources/mysecret.yml new file mode 100644 index 00000000..b1951a99 --- /dev/null +++ b/arquillian-ape-nosql/vault-ftest/src/test/resources/mysecret.yml @@ -0,0 +1,3 @@ +- secret/foo: + zip: zap + a: b diff --git a/arquillian-ape-nosql/vault/pom.xml b/arquillian-ape-nosql/vault/pom.xml new file mode 100644 index 00000000..820fa899 --- /dev/null +++ b/arquillian-ape-nosql/vault/pom.xml @@ -0,0 +1,40 @@ + + + + + org.arquillian.ape + arquillian-ape-nosql-parent + 2.0.0-SNAPSHOT + + + 4.0.0 + + arquillian-ape-nosql-vault + jar + + Arquillian Ape NoSQL Vault + + + + com.bettercloud + vault-java-driver + provided + + + com.lordofthejars + nosqlunit-vault + provided + + + org.arquillian.ape + arquillian-ape-nosql-core + ${project.version} + + + junit + junit + + + + diff --git a/arquillian-ape-nosql/vault/src/main/java/org/arquillian/ape/nosql/vault/Vault.java b/arquillian-ape-nosql/vault/src/main/java/org/arquillian/ape/nosql/vault/Vault.java new file mode 100644 index 00000000..72e95ee3 --- /dev/null +++ b/arquillian-ape-nosql/vault/src/main/java/org/arquillian/ape/nosql/vault/Vault.java @@ -0,0 +1,16 @@ +package org.arquillian.ape.nosql.vault; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.arquillian.ape.api.Populator; + +/** + * Annotation to set Populator of Vault. + */ +@Populator +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface Vault { +} diff --git a/arquillian-ape-nosql/vault/src/main/java/org/arquillian/ape/nosql/vault/VaultOptions.java b/arquillian-ape-nosql/vault/src/main/java/org/arquillian/ape/nosql/vault/VaultOptions.java new file mode 100644 index 00000000..69d9c421 --- /dev/null +++ b/arquillian-ape-nosql/vault/src/main/java/org/arquillian/ape/nosql/vault/VaultOptions.java @@ -0,0 +1,180 @@ +package org.arquillian.ape.nosql.vault; + +import com.bettercloud.vault.VaultConfig; +import com.bettercloud.vault.VaultException; +import java.io.File; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class VaultOptions implements Map { + + static final String TOKEN = "token"; + static final String SSL_PEM_UTF_8 = "sslPemUtf8"; + static final String SSL_PEM_FILE = "sslPemFile"; + static final String SSL_PEM_RESOURCE = "sslPemResource"; + static final String SSL_VERIFY = "sslVerify"; + static final String OPEN_TIMEOUT = "openTimeout"; + static final String READ_TIMEOUT = "readTimeout"; + + private Map options = new HashMap<>(); + + private VaultOptions() { + } + + VaultOptions(Map options) { + this.options.putAll(options); + } + + public static VaultConfigurationOptions options() { + return new VaultConfigurationOptions(); + } + + @Override + public int size() { + return options.size(); + } + + @Override + public boolean isEmpty() { + return options.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return options.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return options.containsValue(value); + } + + @Override + public Object get(Object key) { + return options.get(key); + } + + @Override + public Object put(String key, Object value) { + return options.put(key, value); + } + + @Override + public Object remove(Object key) { + return options.remove(key); + } + + @Override + public void putAll(Map m) { + options.putAll(m); + } + + @Override + public void clear() { + options.clear(); + } + + @Override + public Set keySet() { + return options.keySet(); + } + + @Override + public Collection values() { + return options.values(); + } + + @Override + public Set> entrySet() { + return options.entrySet(); + } + + void configure(VaultConfig vaultConfig) { + if (this.options.containsKey(TOKEN)) { + vaultConfig.token((String) this.options.get(TOKEN)); + } + + if (this.options.containsKey(SSL_PEM_FILE)) { + try { + vaultConfig.sslPemFile((File) this.options.get(SSL_PEM_FILE)); + } catch (VaultException e) { + throw new IllegalArgumentException(e); + } + } + + if (this.options.containsKey(SSL_PEM_RESOURCE)) { + try { + vaultConfig.sslPemResource((String) this.options.get(SSL_PEM_RESOURCE)); + } catch (VaultException e) { + throw new IllegalArgumentException(e); + } + } + + if (this.options.containsKey(SSL_PEM_UTF_8)) { + vaultConfig.sslPemUTF8((String) this.options.get(SSL_PEM_UTF_8)); + } + + if (this.options.containsKey(SSL_VERIFY)) { + vaultConfig.sslVerify((Boolean) this.options.get(SSL_VERIFY)); + } + + if (this.options.containsKey(OPEN_TIMEOUT)) { + vaultConfig.openTimeout((Integer) this.options.get(OPEN_TIMEOUT)); + } + + if (this.options.containsKey(READ_TIMEOUT)) { + vaultConfig.readTimeout((Integer) this.options.get(READ_TIMEOUT)); + } + + } + + public static class VaultConfigurationOptions { + private VaultOptions vaultOptions = new VaultOptions(); + + private VaultConfigurationOptions() { + } + + public VaultConfigurationOptions token(String token) { + this.vaultOptions.put(TOKEN, token); + return this; + } + + public VaultConfigurationOptions sslPemUtf8(String sslPemUtf8) { + this.vaultOptions.put(SSL_PEM_UTF_8, sslPemUtf8); + return this; + } + + public VaultConfigurationOptions sslPemFile(File sslPemFile) { + this.vaultOptions.put(SSL_PEM_FILE, sslPemFile); + return this; + } + + public VaultConfigurationOptions sslPemResource(String classpathResource) { + this.vaultOptions.put(SSL_PEM_RESOURCE, classpathResource); + return this; + } + + public VaultConfigurationOptions sslVerify(Boolean sslVerify) { + this.vaultOptions.put(SSL_VERIFY,sslVerify); + return this; + } + + public VaultConfigurationOptions openTimeout(Integer openTimeout) { + this.vaultOptions.put(OPEN_TIMEOUT, openTimeout); + return this; + } + + public VaultConfigurationOptions readTimeout(Integer readTimeout) { + this.vaultOptions.put(READ_TIMEOUT, readTimeout); + return this; + } + + public VaultOptions build() { + return this.vaultOptions; + } + + } + +} diff --git a/arquillian-ape-nosql/vault/src/main/java/org/arquillian/ape/nosql/vault/VaultPopulatorExtension.java b/arquillian-ape-nosql/vault/src/main/java/org/arquillian/ape/nosql/vault/VaultPopulatorExtension.java new file mode 100644 index 00000000..fd797a57 --- /dev/null +++ b/arquillian-ape-nosql/vault/src/main/java/org/arquillian/ape/nosql/vault/VaultPopulatorExtension.java @@ -0,0 +1,15 @@ +package org.arquillian.ape.nosql.vault; + +import org.arquillian.ape.nosql.NoSqlPopulatorEnricher; +import org.arquillian.ape.spi.PopulatorService; +import org.jboss.arquillian.core.spi.LoadableExtension; +import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider; + +public class VaultPopulatorExtension implements LoadableExtension { + + @Override + public void register(ExtensionBuilder extensionBuilder) { + extensionBuilder.service(PopulatorService.class, VaultPopulatorService.class) + .service(ResourceProvider.class, NoSqlPopulatorEnricher.class); + } +} diff --git a/arquillian-ape-nosql/vault/src/main/java/org/arquillian/ape/nosql/vault/VaultPopulatorService.java b/arquillian-ape-nosql/vault/src/main/java/org/arquillian/ape/nosql/vault/VaultPopulatorService.java new file mode 100644 index 00000000..35c1d08a --- /dev/null +++ b/arquillian-ape-nosql/vault/src/main/java/org/arquillian/ape/nosql/vault/VaultPopulatorService.java @@ -0,0 +1,62 @@ +package org.arquillian.ape.nosql.vault; + +import com.bettercloud.vault.VaultConfig; +import com.lordofthejars.nosqlunit.vault.DefaultVaultInsertionStrategy; +import com.lordofthejars.nosqlunit.vault.VaultClientCallback; +import java.net.URI; +import java.util.List; +import java.util.Map; +import org.arquillian.ape.core.DataSetLoader; +import org.arquillian.ape.nosql.NoSqlPopulatorService; + +public class VaultPopulatorService implements NoSqlPopulatorService { + + private VaultConfig vaultConfig; + + @Override + public void connect(String host, int bindPort, String database, Map customOptions) { + this.vaultConfig = new VaultConfig(); + vaultConfig.address(host + ":" + bindPort); + VaultOptions vaultOptions = new VaultOptions(customOptions); + vaultOptions.configure(vaultConfig); + } + + @Override + public void connect(URI uri, String database, Map customOptions) { + this.vaultConfig = new VaultConfig(); + vaultConfig.address(uri.toString()); + VaultOptions vaultOptions = new VaultOptions(customOptions); + vaultOptions.configure(vaultConfig); + } + + @Override + public void disconnect() { + + } + + @Override + public void execute(List resources) { + DefaultVaultInsertionStrategy vaultInsertionStrategy = new DefaultVaultInsertionStrategy(); + VaultClientCallback vaultClientCallback = () -> vaultConfig; + + resources.stream() + .map(DataSetLoader::resolve) + .forEach(dataset -> { + try { + vaultInsertionStrategy.insert(vaultClientCallback, dataset); + } catch (Throwable throwable) { + throw new IllegalStateException(throwable); + } + }); + } + + @Override + public void clean() { + throw new UnsupportedOperationException(); + } + + @Override + public Class getPopulatorAnnotation() { + return Vault.class; + } +} diff --git a/arquillian-ape-nosql/vault/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension b/arquillian-ape-nosql/vault/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension new file mode 100644 index 00000000..4a35b221 --- /dev/null +++ b/arquillian-ape-nosql/vault/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension @@ -0,0 +1 @@ +org.arquillian.ape.nosql.vault.VaultPopulatorExtension