-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add DevServices support for Vault extension
- Loading branch information
Showing
10 changed files
with
357 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<artifactId>quarkus-vault-parent</artifactId> | ||
<groupId>io.quarkus</groupId> | ||
<version>999-SNAPSHOT</version> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<artifactId>quarkus-vault-deployment-spi</artifactId> | ||
<name>Quarkus - Vault - Deployment - SPI</name> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>io.quarkus</groupId> | ||
<artifactId>quarkus-core-deployment</artifactId> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
17 changes: 17 additions & 0 deletions
17
...rc/main/java/io/quarkus/vault/deployment/devservices/DevServicesVaultResultBuildItem.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package io.quarkus.vault.deployment.devservices; | ||
|
||
import java.util.Map; | ||
|
||
import io.quarkus.builder.item.SimpleBuildItem; | ||
|
||
public final class DevServicesVaultResultBuildItem extends SimpleBuildItem { | ||
private final Map<String, String> properties; | ||
|
||
public DevServicesVaultResultBuildItem(Map<String, String> properties) { | ||
this.properties = properties; | ||
} | ||
|
||
public Map<String, String> getProperties() { | ||
return properties; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
202 changes: 202 additions & 0 deletions
202
...vault/deployment/src/main/java/io/quarkus/vault/deployment/DevServicesVaultProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
package io.quarkus.vault.deployment; | ||
|
||
import java.io.Closeable; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.OptionalInt; | ||
|
||
import org.apache.commons.lang3.RandomStringUtils; | ||
import org.jboss.logging.Logger; | ||
import org.testcontainers.utility.DockerImageName; | ||
import org.testcontainers.vault.VaultContainer; | ||
|
||
import io.quarkus.bootstrap.classloading.QuarkusClassLoader; | ||
import io.quarkus.deployment.IsDockerWorking; | ||
import io.quarkus.deployment.IsNormal; | ||
import io.quarkus.deployment.annotations.BuildProducer; | ||
import io.quarkus.deployment.annotations.BuildStep; | ||
import io.quarkus.deployment.builditem.LaunchModeBuildItem; | ||
import io.quarkus.deployment.builditem.RunTimeConfigurationDefaultBuildItem; | ||
import io.quarkus.deployment.builditem.ServiceStartBuildItem; | ||
import io.quarkus.runtime.LaunchMode; | ||
import io.quarkus.runtime.configuration.ConfigUtils; | ||
import io.quarkus.vault.deployment.devservices.DevServicesVaultResultBuildItem; | ||
import io.quarkus.vault.runtime.config.DevServicesConfig; | ||
import io.quarkus.vault.runtime.config.VaultBuildTimeConfig; | ||
|
||
public class DevServicesVaultProcessor { | ||
private static final Logger log = Logger.getLogger(DevServicesVaultProcessor.class); | ||
private static final String VAULT_IMAGE = "vault:1.7.1"; | ||
private static final int VAULT_EXPOSED_PORT = 8200; | ||
private static final String CONFIG_PREFIX = "quarkus.vault."; | ||
private static final String URL_CONFIG_KEY = CONFIG_PREFIX + "url"; | ||
private static final String AUTH_CONFIG_PREFIX = CONFIG_PREFIX + "authentication."; | ||
private static final String CLIENT_TOKEN_CONFIG_KEY = AUTH_CONFIG_PREFIX + "client-token"; | ||
private static volatile List<Closeable> closeables; | ||
private static volatile DevServicesConfig capturedDevServicesConfiguration; | ||
private static volatile boolean first = true; | ||
private final IsDockerWorking isDockerWorking = new IsDockerWorking(true); | ||
|
||
@BuildStep(onlyIfNot = IsNormal.class) | ||
public DevServicesVaultResultBuildItem startVaultContainers(LaunchModeBuildItem launchMode, | ||
BuildProducer<RunTimeConfigurationDefaultBuildItem> runTimeConfiguration, | ||
BuildProducer<ServiceStartBuildItem> serviceStartBuildItemBuildProducer, VaultBuildTimeConfig config) { | ||
|
||
DevServicesConfig currentDevServicesConfiguration = config.devservices; | ||
|
||
// figure out if we need to shut down and restart any existing Vault container | ||
// if not and the Vault container have already started we just return | ||
if (closeables != null) { | ||
boolean restartRequired = launchMode.getLaunchMode() == LaunchMode.TEST; | ||
if (!restartRequired) { | ||
restartRequired = !currentDevServicesConfiguration.equals(capturedDevServicesConfiguration); | ||
} | ||
if (!restartRequired) { | ||
return null; | ||
} | ||
for (Closeable closeable : closeables) { | ||
try { | ||
closeable.close(); | ||
} catch (Throwable e) { | ||
log.error("Failed to stop Vault container", e); | ||
} | ||
} | ||
closeables = null; | ||
capturedDevServicesConfiguration = null; | ||
} | ||
|
||
capturedDevServicesConfiguration = currentDevServicesConfiguration; | ||
|
||
StartResult startResult = startContainer(currentDevServicesConfiguration); | ||
if (startResult == null) { | ||
return null; | ||
} | ||
|
||
runTimeConfiguration.produce(new RunTimeConfigurationDefaultBuildItem(URL_CONFIG_KEY, startResult.url)); | ||
runTimeConfiguration | ||
.produce(new RunTimeConfigurationDefaultBuildItem(CLIENT_TOKEN_CONFIG_KEY, startResult.clientToken)); | ||
|
||
Map<String, String> connectionProperties = new HashMap<>(); | ||
connectionProperties.put(URL_CONFIG_KEY, startResult.url); | ||
connectionProperties.put(CLIENT_TOKEN_CONFIG_KEY, startResult.clientToken); | ||
|
||
closeables = Collections.singletonList(startResult.closeable); | ||
|
||
if (first) { | ||
first = false; | ||
Runnable closeTask = new Runnable() { | ||
@Override | ||
public void run() { | ||
if (closeables != null) { | ||
for (Closeable closeable : closeables) { | ||
try { | ||
closeable.close(); | ||
} catch (Throwable t) { | ||
log.error("Failed to stop Vault container", t); | ||
} | ||
} | ||
} | ||
first = true; | ||
closeables = null; | ||
capturedDevServicesConfiguration = null; | ||
} | ||
}; | ||
QuarkusClassLoader cl = (QuarkusClassLoader) Thread.currentThread().getContextClassLoader(); | ||
((QuarkusClassLoader) cl.parent()).addCloseTask(closeTask); | ||
Thread closeHookThread = new Thread(closeTask, "Vault container shutdown thread"); | ||
Runtime.getRuntime().addShutdownHook(closeHookThread); | ||
((QuarkusClassLoader) cl.parent()).addCloseTask(new Runnable() { | ||
@Override | ||
public void run() { | ||
Runtime.getRuntime().removeShutdownHook(closeHookThread); | ||
} | ||
}); | ||
} | ||
return new DevServicesVaultResultBuildItem(connectionProperties); | ||
} | ||
|
||
private StartResult startContainer(DevServicesConfig devServicesConfig) { | ||
if (!devServicesConfig.enabled) { | ||
// explicitly disabled | ||
log.debug("Not starting devservices for Vault as it has been disabled in the config"); | ||
return null; | ||
} | ||
|
||
if (!isDockerWorking.getAsBoolean()) { | ||
log.warn("Please configure Vault URL or get a working docker instance"); | ||
return null; | ||
} | ||
|
||
boolean needToStart = !ConfigUtils.isPropertyPresent(URL_CONFIG_KEY); | ||
if (!needToStart) { | ||
log.debug("Not starting devservices for default Vault client as url have been provided"); | ||
return null; | ||
} | ||
|
||
String token = RandomStringUtils.randomAlphanumeric(10); | ||
|
||
DockerImageName dockerImageName = DockerImageName.parse(devServicesConfig.imageName.orElse(VAULT_IMAGE)) | ||
.asCompatibleSubstituteFor(VAULT_IMAGE); | ||
FixedPortVaultContainer vaultContainer = new FixedPortVaultContainer(dockerImageName, devServicesConfig.port) | ||
.withVaultToken(token); | ||
|
||
if (devServicesConfig.transitEnabled) { | ||
vaultContainer.withInitCommand("secrets enable transit"); | ||
} | ||
|
||
if (devServicesConfig.pkiEnabled) { | ||
vaultContainer.withInitCommand("secrets enable pki"); | ||
} | ||
|
||
vaultContainer.start(); | ||
|
||
String url = "http://" + vaultContainer.getHost() + ":" + vaultContainer.getPort(); | ||
return new StartResult(url, token, | ||
new Closeable() { | ||
@Override | ||
public void close() { | ||
vaultContainer.close(); | ||
} | ||
}); | ||
} | ||
|
||
private static class StartResult { | ||
private final String url; | ||
private final String clientToken; | ||
private final Closeable closeable; | ||
|
||
public StartResult(String url, String clientToken, Closeable closeable) { | ||
this.url = url; | ||
this.clientToken = clientToken; | ||
this.closeable = closeable; | ||
} | ||
} | ||
|
||
private static class FixedPortVaultContainer extends VaultContainer<FixedPortVaultContainer> { | ||
OptionalInt fixedExposedPort; | ||
|
||
public FixedPortVaultContainer(DockerImageName dockerImageName, OptionalInt fixedExposedPort) { | ||
super(dockerImageName); | ||
this.fixedExposedPort = fixedExposedPort; | ||
} | ||
|
||
@Override | ||
protected void configure() { | ||
super.configure(); | ||
if (fixedExposedPort.isPresent()) { | ||
addFixedExposedPort(fixedExposedPort.getAsInt(), VAULT_EXPOSED_PORT); | ||
} else { | ||
addExposedPort(VAULT_EXPOSED_PORT); | ||
} | ||
} | ||
|
||
public int getPort() { | ||
if (fixedExposedPort.isPresent()) { | ||
return fixedExposedPort.getAsInt(); | ||
} | ||
return super.getMappedPort(VAULT_EXPOSED_PORT); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
...nsions/vault/runtime/src/main/java/io/quarkus/vault/runtime/config/DevServicesConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package io.quarkus.vault.runtime.config; | ||
|
||
import java.util.Objects; | ||
import java.util.Optional; | ||
import java.util.OptionalInt; | ||
|
||
import io.quarkus.runtime.annotations.ConfigGroup; | ||
import io.quarkus.runtime.annotations.ConfigItem; | ||
|
||
@ConfigGroup | ||
public class DevServicesConfig { | ||
|
||
/** | ||
* If DevServices has been explicitly enabled or disabled. DevServices is generally enabled | ||
* by default, unless there is an existing configuration present. | ||
* <p> | ||
* When DevServices is enabled Quarkus will attempt to automatically configure and start | ||
* a database when running in Dev or Test mode and when Docker is running. | ||
*/ | ||
@ConfigItem(defaultValue = "true") | ||
public boolean enabled; | ||
|
||
/** | ||
* The container image name to use, for container based DevServices providers. | ||
*/ | ||
@ConfigItem | ||
public Optional<String> imageName; | ||
|
||
/** | ||
* Optional fixed port the dev service will listen to. | ||
* <p> | ||
* If not defined, the port will be chosen randomly. | ||
*/ | ||
@ConfigItem | ||
public OptionalInt port; | ||
|
||
/** | ||
* Should the Transit secret engine be enabled | ||
*/ | ||
@ConfigItem(defaultValue = "false") | ||
public boolean transitEnabled; | ||
|
||
/** | ||
* Should the PKI secret engine be enabled | ||
*/ | ||
@ConfigItem(defaultValue = "false") | ||
public boolean pkiEnabled; | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
DevServicesConfig that = (DevServicesConfig) o; | ||
return enabled == that.enabled && Objects.equals(imageName, | ||
that.imageName) && Objects.equals(port, | ||
that.port); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(enabled, imageName, port); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "DevServicesConfig{" + | ||
"enabled=" + enabled + | ||
", imageName=" + imageName + | ||
", port=" + port + | ||
'}'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.