diff --git a/README.md b/README.md index e81419f..55363fd 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ The following configuration variables are supported: * CASCB_VAULT_URL - The URL to Vault server * CASCB_VAULT_USER - The username used to login * CASCB_VAULT_PW - The password used to login +* CASCB_VAULT_PATHS - Comma-separated list of paths from which plugin should retrieve configuration * CASCB_VAULT_FILE - Path to properties file that will be scanned for above variables Each of the variables is supported with either `CASCB_` prefix as indicated in diff --git a/run.sh b/run.sh index 831c87c..0ae2e5e 100755 --- a/run.sh +++ b/run.sh @@ -7,4 +7,5 @@ docker run -ti \ -e CASCB_VAULT_URL=http://172.17.0.2:8200 \ -e CASCB_VAULT_USER=jenkins \ -e CASCB_VAULT_PW=S3cRet \ + -e CASCB_VAULT_PATHS=secret/jenkins/config \ torinthiel/jenkins-bootstrap diff --git a/src/main/groovy/pl/torinthiel/jenkins/bootstrap/ConfigRetriever.groovy b/src/main/groovy/pl/torinthiel/jenkins/bootstrap/ConfigRetriever.groovy index 89448e5..e091c78 100644 --- a/src/main/groovy/pl/torinthiel/jenkins/bootstrap/ConfigRetriever.groovy +++ b/src/main/groovy/pl/torinthiel/jenkins/bootstrap/ConfigRetriever.groovy @@ -8,6 +8,7 @@ enum Configs { VAULT_URL, VAULT_USER, VAULT_PW, + VAULT_PATHS, VAULT_FILE } diff --git a/src/main/groovy/pl/torinthiel/jenkins/bootstrap/VaultAccessor.groovy b/src/main/groovy/pl/torinthiel/jenkins/bootstrap/VaultAccessor.groovy index 22c38a4..f183bf7 100644 --- a/src/main/groovy/pl/torinthiel/jenkins/bootstrap/VaultAccessor.groovy +++ b/src/main/groovy/pl/torinthiel/jenkins/bootstrap/VaultAccessor.groovy @@ -31,7 +31,7 @@ class VaultAccessor { } void configureVault() { - String vaultUrl = configVars.get(VAULT_URL).orElseThrow({new IllegalArgumentException("CASCB_VAULT_URL not provided")}) + String vaultUrl = getOrThrow(VAULT_URL) VaultConfig config = new VaultConfig() .address(vaultUrl) .build() @@ -49,10 +49,15 @@ class VaultAccessor { } void readVariables(VaultConfig config) { - def data = vault.logical().read("secret/jenkins/config").getData() + def path = getOrThrow(VAULT_PATHS) + def data = vault.logical().read(path).getData() values.putAll(data) } + private String getOrThrow(Configs configName) { + return configVars.get(configName).orElseThrow({new IllegalArgumentException("CASCB_${configName} not provided")}) + } + String getValue(VaultConfigKey key) { return values.get(key.path) } diff --git a/src/test/java/pl/torinthiel/jenkins/bootstrap/SmokeIT.java b/src/test/java/pl/torinthiel/jenkins/bootstrap/SmokeIT.java index 8d83cec..16eee4f 100644 --- a/src/test/java/pl/torinthiel/jenkins/bootstrap/SmokeIT.java +++ b/src/test/java/pl/torinthiel/jenkins/bootstrap/SmokeIT.java @@ -80,6 +80,7 @@ private void prepareJenkinsContainer() { .withEnv("CASCB_VAULT_URL", "http://vault:8200/") .withEnv("CASCB_VAULT_USER", "jenkins") .withEnv("CASCB_VAULT_PW", "S3cRet") + .withEnv("CASCB_VAULT_PATHS", "secret/jenkins/config") .withExposedPorts(JENKINS_PORT); } diff --git a/src/test/java/pl/torinthiel/jenkins/bootstrap/VaultAccessorTest.java b/src/test/java/pl/torinthiel/jenkins/bootstrap/VaultAccessorTest.java index d015a4b..aad5588 100644 --- a/src/test/java/pl/torinthiel/jenkins/bootstrap/VaultAccessorTest.java +++ b/src/test/java/pl/torinthiel/jenkins/bootstrap/VaultAccessorTest.java @@ -6,9 +6,11 @@ import java.util.Optional; import java.util.function.Function; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.function.Executable; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -37,6 +39,7 @@ void setUp() { config.addMapping(Configs.VAULT_URL, "random_url"); config.addMapping(Configs.VAULT_USER, "username"); config.addMapping(Configs.VAULT_PW, "password"); + config.addMapping(Configs.VAULT_PATHS, "secret/jenkins/config"); } @Test @@ -75,6 +78,33 @@ void shouldNotAskTwiceForValue() throws VaultException { verify(vault.logical(), times(2)).read("secret/jenkins/config"); } + @Test + void shouldReadVaultPathFromEnv() throws VaultException { + Map errorMap = new HashMap<>(); + errorMap.put("cascb_ssh_key", "Wrong value"); + Map resultsMap = new HashMap<>(); + resultsMap.put("cascb_ssh_key", "Correct value"); + when(vault.logical().read("secret/jenkins/config").getData()).thenReturn(errorMap); + when(vault.logical().read("secret/jenkins/correct").getData()).thenReturn(resultsMap); + config.addMapping(Configs.VAULT_PATHS, "secret/jenkins/correct"); + + VaultAccessor acc = new VaultAccessor(config, factory); + acc.configureVault(); + + String retVal = acc.getValue(VaultConfigKey.SSH_KEY); + assertEquals("Correct value", retVal); + } + + @Test + void shouldThrowErrorWhenRequiredParamMissing() { + config.removeMapping(Configs.VAULT_URL); + + Assertions.assertThrows(IllegalArgumentException.class, (Executable) () -> { + VaultAccessor acc = new VaultAccessor(config, factory); + acc.configureVault(); + }, "CASCB_VAULT_URL is not provided" + ); + } } class MockConfigVars implements Retriever { @@ -88,4 +118,8 @@ public Optional get(Configs configName) { public void addMapping(Configs key, String value) { config.put(key, value); } + + public void removeMapping(Configs key) { + config.remove(key); + } }