Search code examples
javamavenjenkinscredentials

Pass Jenkins credentials to Maven Surefire plugin


Use Case

I am developing a templated pipeline solution so teams can build, test and scan their projects consistently with every job run in Jenkins. One of these projects uses Maven for dependency management and requires passing Jenkins credentials so integration tests can be run.

Examples

Passing credentials via command line arguments works just fine. For example...

withCredentials([
    [
        $class: 'UsernamePasswordMultiBinding', 
        credentialsId: 'TEST', 
        usernameVariable: 'TEST_USERNAME', 
        passwordVariable: 'TEST_PASSWORD'
    ]
]) {
    sh 'mvn clean install -D test_username=$TEST_USERNAME -D test_password=$TEST_PASSWORD'
}

I'd strongly prefer to pass these variables to the underlying plugins via each respective plugin's configuration (namely the Maven Surefire plugin). The justification for this preferred approach is to utilize a structured method to manage system properties. I've tried a number of methods including passing arguments by setting the <argLines> element of the Surefire configuration and setting the <systemPropertyValues> element, but neither method is working. Also, it's preferred to reference these properties via a YAML file to achieve externalized configuration, but I am not opposed to other methods of accessing this information if it's impossible to reference credential variables in a configuration file.

pom.xml

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.2</version>
    <configuration>
        <!-- Tacking 'env.' to the beginning of each variable name does not make a difference; tests still fail. -->
        <argLines>-D test_username=${TEST_USERNAME} -D test_password=${TEST_PASSWORD}</argLines>
        <systemPropertyVariables>
            <test_username>${TEST_USERNAME}</test_username>
            <test_username>${TEST_PASSWORD}</test_username>
        </systemPropertyVariables>
    </configuration>
</plugin>

bootstrap-test.yaml

datasource:
    jdbc_url: *omitted*
    username: ${test_username}
    password: ${test_password}

Results

If I try to run the tests in the Jenkins environment by specifying system properties in the configuration section, then the tests fail. As an aside I am passing these credentials off to Spring Boot, but the entityManagerFactory (org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder) fails to initialize.

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [com/acme/Config.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: Unable to resolve persistence unit root URL

I fear the issue might be due to Spring Boot's inability to access obfuscated credentials, or trying to access system properties within a configuration file too late in Maven's lifecycle.

I am also not entirely opposed to passing system properties via command line, but the templated solution will need to include an effective (secure) way to round up different credential types to pass off to the Maven invocation.


UPDATE 1:

Running this project locally (on a Windows machine, by the way) after exporting the variables does in fact work, but does not work in Jenkins. The method that worked locally involved setting either the environment or system property variables in the Surefire plugin configuration section. I did prefix references to environment variables created by Jenkins with env..


UPDATE 2:

Using the Maven antrun plugin I was able to echo the masked secrets exported by Jenkins.


Solution

  • I tried on a fresh jenkins and it works as expected. If plugin(java class) is able to read environment variables and jenkins inject environment variables, everything should work.

    I think your error is the environment variable syntax. Accoding to this you should use ${env.VARIABLE_NAME} to use environment variables in a pom.xml. So in your case, your pom.xml should be like this:

    <test_username>${env.TEST_USERNAME}</test_username>
    <test_username>${env.TEST_PASSWORD}</test_username>
    

    And then inject the required variables with

    withCredentials([
        [
            $class: 'UsernamePasswordMultiBinding', 
            credentialsId: 'TEST', 
            usernameVariable: 'TEST_USERNAME', 
            passwordVariable: 'TEST_PASSWORD'
        ]
    ]) {
        sh 'mvn clean install'
    }
    

    Java system properties and environment variables

    When you use -Dmykey = value on any java implementation, you are setting a System property, not an environment variable. Check this: