diff --git a/DEV_GUIDE.md b/DEV_GUIDE.md index df8299aa3b..6324b0d388 100644 --- a/DEV_GUIDE.md +++ b/DEV_GUIDE.md @@ -337,6 +337,7 @@ has been applied ineffectively. * `SKIP_TEARDOWN`: variable for development purposes to avoid keep deploying and deleting deployments each run. Default value: `false` * `CONTAINER_CONFIG_PATH`: directory where `config.json` file is located. This file contains the pull secrets to be used by the container engine. Default value: `$HOME/.docker/config.json` +* `VAULT_CHART_VERSION`: version of Vault Helm Chart to be used by the System Tests for Envelope Encryption. Default value: `0.27.0` * `SKIP_STRIMZI_INSTALL`: skip strimzi installation. Default value: `false` diff --git a/kroxylicious-kms-provider-hashicorp-vault-test-support/src/main/java/io/kroxylicious/kms/provider/hashicorp/vault/VaultTestKmsFacade.java b/kroxylicious-kms-provider-hashicorp-vault-test-support/src/main/java/io/kroxylicious/kms/provider/hashicorp/vault/VaultTestKmsFacade.java index 1c0b7d6df6..e629fc5aa9 100644 --- a/kroxylicious-kms-provider-hashicorp-vault-test-support/src/main/java/io/kroxylicious/kms/provider/hashicorp/vault/VaultTestKmsFacade.java +++ b/kroxylicious-kms-provider-hashicorp-vault-test-support/src/main/java/io/kroxylicious/kms/provider/hashicorp/vault/VaultTestKmsFacade.java @@ -20,6 +20,7 @@ import java.util.function.Supplier; import org.testcontainers.DockerClientFactory; +import org.testcontainers.utility.DockerImageName; import org.testcontainers.vault.VaultContainer; import com.fasterxml.jackson.core.JsonProcessingException; @@ -36,7 +37,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; public class VaultTestKmsFacade extends AbstractVaultTestKmsFacade { - private static final String HASHICORP_VAULT = "hashicorp/vault:1.15"; + public static final DockerImageName HASHICORP_VAULT = DockerImageName.parse("hashicorp/vault:1.15"); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private final HttpClient vaultClient = HttpClient.newHttpClient(); diff --git a/kroxylicious-systemtests/pom.xml b/kroxylicious-systemtests/pom.xml index e18362dafb..e38e2b3477 100644 --- a/kroxylicious-systemtests/pom.xml +++ b/kroxylicious-systemtests/pom.xml @@ -122,6 +122,10 @@ io.kroxylicious kroxylicious-kms-provider-hashicorp-vault-test-support + + org.testcontainers + testcontainers + diff --git a/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/Environment.java b/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/Environment.java index 298760cd83..4086083e84 100644 --- a/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/Environment.java +++ b/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/Environment.java @@ -30,6 +30,7 @@ private Environment() { private static final String SKIP_TEARDOWN_ENV = "SKIP_TEARDOWN"; public static final String STRIMZI_FEATURE_GATES_ENV = "STRIMZI_FEATURE_GATES"; private static final String CONTAINER_CONFIG_PATH_ENV = "CONTAINER_CONFIG_PATH"; + private static final String VAULT_CHART_VERSION_ENV = "VAULT_CHART_VERSION"; private static final String SKIP_STRIMZI_INSTALL_ENV = "SKIP_STRIMZI_INSTALL"; /** @@ -65,6 +66,7 @@ private Environment() { private static final String SKIP_TEARDOWN_DEFAULT = "false"; private static final String STRIMZI_FEATURE_GATES_DEFAULT = ""; private static final String CONTAINER_CONFIG_PATH_DEFAULT = System.getProperty("user.home") + "/.docker/config.json"; + private static final String VAULT_CHART_VERSION_DEFAULT = "0.27.0"; private static final String SKIP_STRIMZI_INSTALL_DEFAULT = "false"; /** @@ -95,6 +97,8 @@ private Environment() { public static final boolean SKIP_STRIMZI_INSTALL = Boolean.parseBoolean(getOrDefault(SKIP_STRIMZI_INSTALL_ENV, SKIP_STRIMZI_INSTALL_DEFAULT)); + public static final String VAULT_CHART_VERSION = getOrDefault(VAULT_CHART_VERSION_ENV, VAULT_CHART_VERSION_DEFAULT); + private static String getOrDefault(String varName, String defaultValue) { return getOrDefault(varName, String::toString, defaultValue); } diff --git a/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/installation/vault/Vault.java b/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/installation/vault/Vault.java index fa6e602cf4..6df46813a9 100644 --- a/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/installation/vault/Vault.java +++ b/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/installation/vault/Vault.java @@ -20,6 +20,7 @@ import io.fabric8.kubernetes.api.model.ServicePort; +import io.kroxylicious.systemtests.Environment; import io.kroxylicious.systemtests.k8s.exception.KubeClusterException; import io.kroxylicious.systemtests.resources.manager.ResourceManager; import io.kroxylicious.systemtests.utils.DeploymentUtils; @@ -88,6 +89,36 @@ public boolean isAvailable() { } } + /** + * Gets the installed version. + * + * @return the version + */ + public String getVersionInstalled() { + try (var output = new ByteArrayOutputStream(); + var error = new ByteArrayOutputStream(); + var exec = kubeClient().getClient().pods() + .inNamespace(deploymentNamespace) + .withName(VAULT_POD_NAME) + .writingOutput(output) + .writingError(error) + .exec("sh", "-c", VAULT_CMD + " version")) { + int exitCode = exec.exitCode().join(); + if (exitCode != 0) { + throw new UnsupportedOperationException(error.toString()); + } + // version returned with format: Vault v1.15.2 (blah blah), build blah + String version = output.toString().split("\\s+")[1].replace("v", ""); + if (!version.matches("^(\\d+)(?:\\.(\\d+))?(?:\\.(\\*|\\d+))?$")) { + throw new NumberFormatException("Invalid version format: " + version); + } + return version; + } + catch (IOException e) { + throw new UncheckedIOException(e); + } + } + /** * Deploy. * @@ -100,7 +131,8 @@ public void deploy() { } ResourceManager.helmClient().addRepository(VAULT_HELM_REPOSITORY_NAME, VAULT_HELM_REPOSITORY_URL); - ResourceManager.helmClient().namespace(deploymentNamespace).install(VAULT_HELM_CHART_NAME, VAULT_SERVICE_NAME, Optional.empty(), + ResourceManager.helmClient().namespace(deploymentNamespace).install(VAULT_HELM_CHART_NAME, VAULT_SERVICE_NAME, + Optional.of(Environment.VAULT_CHART_VERSION), Optional.of(getHelmOverridePath()), Optional.of(Map.of("server.dev.devRootToken", vaultRootToken))); diff --git a/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/k8s/HelmClient.java b/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/k8s/HelmClient.java index bd7a043009..f97ccf759d 100644 --- a/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/k8s/HelmClient.java +++ b/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/k8s/HelmClient.java @@ -35,6 +35,9 @@ public class HelmClient { private static String helmCommand; private Optional namespace = Optional.empty(); + /** + * Instantiates a new Helm client. + */ public HelmClient() { if (!clientAvailable()) { throw new KubeClusterException.NotFound("No helm client found on $PATH. $PATH=" + System.getenv("PATH")); diff --git a/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/resources/vault/KubeVaultTestKmsFacade.java b/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/resources/vault/KubeVaultTestKmsFacade.java index da2cf9ec01..a63873da71 100644 --- a/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/resources/vault/KubeVaultTestKmsFacade.java +++ b/kroxylicious-systemtests/src/main/java/io/kroxylicious/systemtests/resources/vault/KubeVaultTestKmsFacade.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.kroxylicious.kms.provider.hashicorp.vault.AbstractVaultTestKmsFacade; +import io.kroxylicious.kms.provider.hashicorp.vault.VaultTestKmsFacade; import io.kroxylicious.kms.service.TestKekManager; import io.kroxylicious.kms.service.UnknownAliasException; import io.kroxylicious.systemtests.executor.ExecResult; @@ -49,6 +50,12 @@ public class KubeVaultTestKmsFacade extends AbstractVaultTestKmsFacade { private final String podName; private final Vault vault; + /** + * Instantiates a new Kube vault test kms facade. + * + * @param namespace the namespace + * @param podName the pod name + */ public KubeVaultTestKmsFacade(String namespace, String podName) { this.namespace = namespace; this.podName = podName; @@ -63,6 +70,10 @@ public boolean isAvailable() { @Override public void startVault() { vault.deploy(); + if (!isCorrectVersionInstalled()) { + throw new KubeClusterException("Vault version installed " + getVaultVersion() + " does not match with the expected: '" + + VaultTestKmsFacade.HASHICORP_VAULT + "'"); + } runVaultCommand(VAULT_CMD, LOGIN, VAULT_ROOT_TOKEN); } @@ -126,11 +137,44 @@ protected URI getVaultUrl() { return URI.create("http://" + vault.getVaultUrl()); } + /** + * Gets vault version. + * + * @return the vault version + */ + public String getVaultVersion() { + return vault.getVersionInstalled(); + } + @Override public TestKekManager getTestKekManager() { return new VaultTestKekManager(); } + private boolean isCorrectVersionInstalled() { + String installedVersion = getVaultVersion(); + String expectedVersion = VaultTestKmsFacade.HASHICORP_VAULT.getVersionPart(); + + return compareVersions(installedVersion, expectedVersion) == 0; + } + + private int compareVersions(String currentVersion, String expectedVersion) { + Objects.requireNonNull(expectedVersion); + + String[] currentParts = currentVersion.split("\\."); + String[] expectedParts = expectedVersion.split("\\."); + + for (int i = 0; i < expectedParts.length; i++) { + int currentPart = i < currentParts.length ? Integer.parseInt(currentParts[i]) : 0; + int expectedPart = Integer.parseInt(expectedParts[i]); + int comparison = Integer.compare(currentPart, expectedPart); + if (comparison != 0) { + return comparison; + } + } + return 0; + } + private class VaultTestKekManager implements TestKekManager { public void generateKek(String alias) { @@ -202,5 +246,4 @@ private ExecResult runVaultCommand(String... command) { } return execResult; } - }