Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,49 @@ It is possible (although not necessary) to explicitly work with docker networks.

```

## Working with Truststores

Often comes a Truststore into play while working with Jenkins and Java. Jenkins can accommodate necessary certificates in its truststore so Java applications like Maven (and others too!) can successfully interact with other parties, like download artifacts from artifact repositories or transport data over the network. Even so, it may be necessary to provide these Java applications with the right certificates when otherwise encrypted communication would fail without doing so.

## Simple Truststore pipeline

For such circumstances this library provides a small snippet. The global `truststore` variable ensures that any truststore files which are copied in the process are also removed at the end of both `copy` and `use` actions.

In order to successfully provide a truststore to any Java process this sequence must be in order:

1. copy the truststore with `truststore.copy()`
1. use the copied truststore with `truststore.use { truststoreFile -> }`

Here is a more elaborate example:

```
Library ('zalenium-build-lib') _

node 'master' {
truststore.copy()
}
node 'docker' {
truststore.use { truststoreFile ->
javaOrMvn "-Djavax.net.ssl.trustStore=${truststoreFile} -Djavax.net.ssl.trustStorePassword=changeit"
}
}
```

## Alternative Ways of Configuration

It is possible to supply a different truststore than the Jenkins one. Also it is possible to provide a different name in order to avoid filename collision:

```
Library ('zalenium-build-lib') _

node('master') {
truststore.copy('/path/to/alternative/truststore/file.jks')
}
node('anotherNode') {
//truststore.use ... as usual
}
```

## Locking

Right now, only one Job can run Zalenium Tests at a time.
Expand Down
13 changes: 13 additions & 0 deletions vars/helper.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
String generateZaleniumJobName() {
return "${JOB_BASE_NAME}_${BUILD_NUMBER}"
}

String findHostName() {
String regexMatchesHostName = 'https?://([^:/]*)'

// Storing matcher in a variable might lead to java.io.NotSerializableException: java.util.regex.Matcher
if (!(env.JENKINS_URL =~ regexMatchesHostName)) {
script.error 'Unable to determine hostname from env.JENKINS_URL. Expecting http(s)://server:port/jenkins'
}
return (env.JENKINS_URL =~ regexMatchesHostName)[0][1]
}
5 changes: 5 additions & 0 deletions vars/helper.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- generateZaleniumJobName() - creates a jobname unique to the current Jenkins job. For the 99th Jenkins build of the job
"ACME" the job name would be "ACME_99".

- findHostName() - returns the host name of the current Jenkins node. This is usually needed for getting the FQDN of the
Cloudogu EcoSystem instance which runs the Jenkins master node.
38 changes: 38 additions & 0 deletions vars/truststore.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Provides a simple way to use a Java trust store over different Jenkins nodes.
*
* @param pathToTruststore provides the path to a Java truststore. It defaults to the truststore on the Jenkins master.
*/
def copy(String pathToTruststore = '/var/lib/jenkins/truststore.jks') {
String truststoreFile = 'truststore.jks'
String stashName = 'truststore'

sh "cp ${pathToTruststore} ./${truststoreFile}"
stash includes: truststoreFile, name: stashName
sh "rm -f ./${truststoreFile}"
}

/**
* Unstashes the previously stashed truststore to the current workspace. The truststore will be deleted after the closure
* body finished.
*
* <code>truststore.copy()</code> MUST be called beforehand, otherwise there would be no truststore to be unstashed.
*
* @param closure this closure is executed after the truststore was successfully unstashed.
*/
def use(Closure inner) {
String truststoreFile = 'truststore.jks'
String stashName = 'truststore'

try {
unstash name: stashName

inner.call(truststoreFile)

} catch (Exception ex) {
echo "withTruststore failed because an exception occurred: ${ex}"
throw ex
} finally {
sh "rm -f ./${truststoreFile}"
}
}
25 changes: 25 additions & 0 deletions vars/truststore.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Copies and provides a truststore file for safe use over different Jenkins nodes. When the build job finishes there
will be no truststore file left in the Jenkins workspace.

By default Jenkins's truststore will be used: `/var/lib/jenkins/truststore.jks`

(optional) Parameters:

- pathToTruststore - This path pointing to the truststore file will be used instead of the default path.

Method summary:

- copy() - copies a truststore file and stashed it for later use.
- use { truststoreFile -> } - enables the usage of the previously copied truststore file. The truststore file which
was extracted from the stash will be deleted once the body finishes.

Exemplary calls:

node 'master' {
truststore.copy()
}
node 'docker' {
truststore.use { truststoreFile ->
javaOrMvn "-Djavax.net.ssl.trustStore=${truststoreFile} -Djavax.net.ssl.trustStorePassword=changeit"
}
}
75 changes: 75 additions & 0 deletions vars/withMavenSettings.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Provides a simple custom maven settings.xml file to current working directory with Maven Central mirror in the
* current CES instance.
*
* Example call:
*
* <pre>
* nexusCreds = usernamePassword(credentialsId: 'jenkinsNexusServiceUser', passwordVariable: 'PASSWORD', usernameVariable: 'USERNAME')
* withMavenSettings(nexusCreds, 'cesinstance.stage.host.tld', '/usr/share/maven') { settingsXml ->
* // do your maven call here
* mvn "-s ${settingsXml} test"
* } // settings.xml will be removed automatically
* </pre>
*
* @param nexusCredentials Jenkins credentials which provide USERNAME and PASSWORD to an account which enables Nexus interaction
* @param cesFQDN the full qualified domain name of the current CES instance, f. i. <code>cesinstance.stage.host.tld</code>
* @param pathToLocalMavenRepository without the .m2 directory part, f. i. <code>/usr/share/maven</code>. The suffix <code>/.m2/repository</code> will be added automatically.
*/
def settings(def nexusCredentials, String cesFQDN, String pathToLocalMavenRepository, Closure closure) {
echo "write settings.xml to ${pathToLocalMavenRepository}"
String settingsXml = "settings.xml"
withCredentials([nexusCredentials]) {
writeFile file: settingsXml, text: """
<settings>
<localRepository>${pathToLocalMavenRepository}/.m2/repository</localRepository>
<servers>
<server>
<id>${cesFQDN}</id>
<username>${USERNAME}</username>
<password>${PASSWORD}</password>
</server>
</servers>
<mirrors>
<mirror>
<id>${cesFQDN}</id>
<name>${cesFQDN} Central Mirror</name>
<url>https://${cesFQDN}/nexus/repository/itzbundshared/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
</settings>"""
}

try {
closure.call(settingsXml)
} finally {
sh "rm -f ${settingsXml}"
}
}

def mvnWithSettings(def nexusCredentials, String cesFQDN, String mvnCallArgs) {
def currentHome = env.HOME
settings(nexusCredentials, cesFQDN, currentHome) { settingsXml ->
mvn settingsXml, mvnCallArgs
}
}

/**
* This method extracts the Maven 3 installation from Jenkins and calls Maven with the given settings.xml and Maven arguments
*/
def mvn(String settingsXml, String mvnCallArgs) {
def mvnHome = tool 'M3'

mvnWithHome(mvnHome, settingsXml, mvnCallArgs)
}

def customMvnWithSettings(def nexusCredentials, String cesFQDN, String mvnHome, String pathToLocalMavenRepository, String mvnCallArgs) {
settings(nexusCredentials, cesFQDN, pathToLocalMavenRepository) { settingsXml ->
mvnWithHome(mvnHome, settingsXml, mvnCallArgs)
}
}

def mvnWithHome(String mvnHome, String settingsXml, String mvnCallArgs) {
sh "${mvnHome}/bin/mvn -s ${settingsXml} --batch-mode -V -U -e -Dsurefire.useFile=false ${mvnCallArgs}"
}
47 changes: 47 additions & 0 deletions vars/withMavenSettings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
Provides sophisticated Maven support for custom `settings.xml` files which refer to a central maven mirror in the
context of a Cloudogu EcoSystem. This global variable writes custom `settings.xml` files and can additionally invoke a
provided or custom Maven call.

Any `settings.xml` being writting will be removed once the settings body is left.

Once written, the `settings.xml`'s structure will look like this:

|
+->settings
| +-> localRepository
| `-> servers
| `-> server
| +-> id (generated with FQDN)
| +-> username (taken from credentials)
| `-> password (taken from credentials)
|
`-> mirrors
`-> mirror
+-> id (reference to the server id)
+-> name (generated from FQDN)
+-> url (generated from FQDN)
+-> mirrorOf (fixed value of central maven mirror)

Example call:

nexusCreds = usernamePassword(credentialsId: 'jenkinsNexusServiceUser', passwordVariable: 'PASSWORD', usernameVariable: 'USERNAME')
withMavenSettings.mvnWithSettings(nexusCreds, 'cesinstance.stage.host.tld', '/usr/share/maven') { settingsXml ->
// do something with the settings.xml here
} // settings.xml will be removed automatically

Example call with included Maven call:

withMavenSettings.mvnWithSettings(nexusCreds, 'cesinstance.stage.host.tld', 'yourMavenGoal')

- settings(def nexusCredentials, String cesFQDN, String pathToLocalMavenRepository, Closure closure) - Writes a
`settings.xml` file with the given Nexus credentials and executes the closure body. This method provides a parameter
`settingsXml` which contains the relative file path to the 'settings.xml' file.

- mvn(String settingsXml, String mvnCallArgs) - call the Jenkins Tool Maven with a provided `settings.xml`

- mvnWithSettings(def nexusCredentials, String cesFQDN, String mvnCallArgs) - call Jenkins Tool Maven wrapped within a
settings file. This is basically a convenience mix of `settings` and `mvn`

- customMvnWithSettings(def nexusCredentials, String cesFQDN, String mvnHome, String pathToLocalMavenRepository, String mvnCallArgs) -
similar to `mvnWithSettings` but calls a custom Maven installation instead. It is necessary to know about the location of
the Maven installation. Calling this method can make sense if the Maven installation resides in a Docker container.