Inject Configuration into multiple Jenkins jobs via DSL - jenkins

I have a number of maven project in Jenkins and I would like a mechanism where i can inject a piece of configuration into all of them at once.
or example if I decide to change the logRotation.
I found this can be done via the DSL plugin, but I so far I am only able to inject one project at a time.
mavenJob('EXAMPLE_TEST1') {
publishers {
textFinder(/There are test failures/, '', false, false, true)
}
}
The above works and inject a piece of config for the textFinder plugin into a maven project
How do i achieve the same for EXAMPLE_TEST2, 3, 4, 5, 6 etc
Appreciate any help

I decided to go a little further and create an array of all jobs in Jenkins
import hudson.model.*
allTests(Hudson.instance.items)
def allTests(items){
def list = []
for (item in items) {
def name = item.getName()
if (name.contains("API")){
} else {
list << item.getName()
}
}
list.each { job ->
mavenJob("${job}")
{
publishers {
textFinder(/There are test failures/, '', false, false, true)
}
}
}
}
Sorry for the above format
So this will now grab every job in Jenkins that does not contain the text API and add it to an array and will inject the configuration required.

Related

Create variable in shared library for Jenkinsfile

I'm new to shared libraries in Jenkins, and fairly new to Groovy as well.
I have several multibranch pipelines for different projects. I have setup email notifications for each job using an environmental variable containing a list of email addresses, which works just fine. However, several jobs share the same email addresses (depending on the project it's for) and I'd like to create a shared library for a master email list, so I don't have to update the list in each job individually if say I want to add or remove someone. I'm having trouble defining a variable in a library that can be used later in the Jenkinsfile. This is a simplified version of what I've been trying:
shared library (basically a copy paste of the environmental variables I was originally using in the individual Jenkinsfiles/jobs, which works):
Jenkinsfile-shared-libraries\vars\masterEmailList
def call () {
environment {
project1EmailList = "user1#xyz.com, user2#xyz.com, user3#xyz.com"
project2EmailList = "user2#xyz.com, user4#xyz.com, user5#xyz.com"
}
}
Jenkinsfile
#Library('Jenkinsfile-shared-libraries') _
pipeline {
agent any
stages {
stage ('email list for project 1') {
steps {
masterEmailList()
echo env.project1EmailList
}
}
}
}
The echo returns "null" rather than the email list of the project like I would expect.
Any guidance would be much appreciated!
Cheers.
The "Defining global variables" section of https://www.jenkins.io/doc/book/pipeline/shared-libraries/#defining-global-variables helped solve this one.
shared library:
Jenkinsfile-shared-libraries\vars\masterEmailList
def project1EmailList() {
"user1#xyz.com, user2#xyz.com, user3#xyz.com"
}
def project2EmailList() {
"user2#xyz.com, user4#xyz.com, user5#xyz.com"
}
Jenkinsfile:
#Library('Jenkinsfile-shared-libraries') _
pipeline {
agent any
stages {
stage ('email list for project 1') {
steps {
script {
echo masterEmailList.project1EmailList
}
}
}
}
}

Set StashNotifier with Groovy

I am trying to add the StashNotifier configuration to Jenkins via Groovy
import jenkins.model.*;
import org.jenkinsci.plugins.stashNotifier.*
def instance = Jenkins.getInstance()
def bitbucket = instance.getDescriptor(StashNotifier)
println "--> configure Stash Notifier..."
def bitBucketNotifier = new StashNotifier (
"https://servername:8443", //stashServerBaseUrl
"user", //credentialsId
false, //ignoreUnverifiedSSLPeer
"", //commitSha1
false, //includeBuildNumberInKey
"", //projectKey
false, //prependParentProjectKey
false //disableInprogressNotification
)
bitbucket.save()
println "--> configure Stash Notifier... done"
The xml configuration I am trying to implement is
<?xml version='1.0' encoding='UTF-8'?>
<org.jenkinsci.plugins.stashNotifier.StashNotifier_-DescriptorImpl plugin="stashNotifier#1.11.6">
<credentialsId>user</credentialsId>
<stashRootUrl>https://servername:8443/</stashRootUrl>
<ignoreUnverifiedSsl>false</ignoreUnverifiedSsl>
<includeBuildNumberInKey>false</includeBuildNumberInKey>
<prependParentProjectKey>false</prependParentProjectKey>
<disableInprogressNotification>false</disableInprogressNotification>
</org.jenkinsci.plugins.stashNotifier.StashNotifier_-DescriptorImpl>
I am new to Java and groovy, but I cannot get this to work. I feel I am close, probably missing one or two little bits.
I am trying to get Jenkins to configure upon start-up and then reconfigure itself if any changes are made to core integrations. In this case, the BitBucket server won't change but if users do make the change to point at something else, Jenkins is reconfigured to point at the correct thing
#flue42 has a great answer, though going through the JSON and FormData is a bit tough to follow. Instead you can simply write the values to their respective properties on the object and save it.
If you happened to have multiple Stash servers you wanted to notify (not suppported at the global configuration level), you'll need to override the global setting on a per job basis, see my other answer with the skeleton of doing it using the Job DSL plugin.
Perhaps the most annoying part of looking at this to implement myself was in the code it references stashServerBaseUrl (as you noted in your comment) but then the required name in the Groovy/form is stashRootUrl. The place to find the right names is right around here
#!groovy
import jenkins.model.*;
import org.jenkinsci.plugins.stashNotifier.*;
String url = 'https://stashblablablabla';
String credentials = '01111111-e222-3333-eeff-4f4444e44bc4';
def j = Jenkins.getInstance();
def stash = instance.getDescriptor(StashNotifier)
stash.stashRootUrl = url //stashServerBaseUrl
stash.credentialsId = credentials //credentialsId
stash.ignoreUnverifiedSsl = false //ignoreUnverifiedSSLPeer
stash.includeBuildNumberInKey = false //includeBuildNumberInKey
stash.projectKey = "" //projectKey
stash.prependParentProjectKey = false //prependParentProjectKey
stash.disableInprogressNotification = false //disableInprogressNotification
stash.save()
I've done something like that :
#!groovy
import jenkins.model.*;
import org.jenkinsci.plugins.*;
import net.sf.json.JSONObject;
String url = 'https://stashblablablabla';
String credentials = '01111111-e222-3333-eeff-4f4444e44bc4';
def j = Jenkins.getInstance();
def stash = j.getExtensionList(
stashNotifier.StashNotifier.DescriptorImpl.class)[0];
def formData = [
stashRootUrl: url,
credentialsId: credentials,
ignoreUnverifiedSsl: false,
includeBuildNumberInKey: false,
prependParentProjectKey: false,
disableInprogressNotification: false,
considerUnstableAsSuccess: false
] as JSONObject;
stash.configure(null, formData);
j.save();
I've figured that solution out reading that:
https://github.com/jenkinsci/stashnotifier-plugin/blob/release/1.x/src/main/java/org/jenkinsci/plugins/stashNotifier/StashNotifier.java
https://github.com/samrocketman/jenkins-bootstrap-slack/blob/master/scripts/configure-slack.groovy
It appears to be working in my test platform.
If you are looking to do it via Job DSL you can use this example from the Job DSL repo.
// notify Stash using the global Jenkins settings
job('example-1') {
publishers {
stashNotifier()
}
}
// notify Stash using the global Jenkins settings and sets keepRepeatedBuilds to true
job('example-2') {
publishers {
stashNotifier {
keepRepeatedBuilds()
}
}
}
https://github.com/jenkinsci/job-dsl-plugin/blob/master/job-dsl-core/src/main/docs/examples/javaposse/jobdsl/dsl/helpers/publisher/PublisherContext/stashNotifier.groovy

how to get the trigger information in Jenkins programmatically

I need to add the next build time scheduled in a build email notification after a build in Jenkins.
The trigger can be "Build periodically" or "Poll SCM", or anything with schedule time.
I know the trigger info is in the config.xml file e.g.
<triggers>
<hudson.triggers.SCMTrigger>
<spec>8 */2 * * 1-5</spec>
<ignorePostCommitHooks>false</ignorePostCommitHooks>
</hudson.triggers.SCMTrigger>
</triggers>
and I also know how to get the trigger type and spec with custom scripting from the config.xml file, and calculate the next build time.
I wonder if Jenkins has the API to expose this information out-of-the-box. I have done the search, but not found anything.
I realise you probably no longer need help with this, but I just had to solve the same problem, so here is a script you can use in the Jenkins console to output all trigger configurations:
#!groovy
Jenkins.instance.getAllItems().each { it ->
if (!(it instanceof jenkins.triggers.SCMTriggerItem)) {
return
}
def itTrigger = (jenkins.triggers.SCMTriggerItem)it
def triggers = itTrigger.getSCMTrigger()
println("Job ${it.name}:")
triggers.each { t->
println("\t${t.getSpec()}")
println("\t${t.isIgnorePostCommitHooks()}")
}
}
This will output all your jobs that use SCM configuration, along with their specification (cron-like expression regarding when to run) and whether post-commit hooks are set to be ignored.
You can modify this script to get the data as JSON like this:
#!groovy
import groovy.json.*
def result = [:]
Jenkins.instance.getAllItems().each { it ->
if (!(it instanceof jenkins.triggers.SCMTriggerItem)) {
return
}
def itTrigger = (jenkins.triggers.SCMTriggerItem)it
def triggers = itTrigger.getSCMTrigger()
triggers.each { t->
def builder = new JsonBuilder()
result[it.name] = builder {
spec "${t.getSpec()}"
ignorePostCommitHooks "${t.isIgnorePostCommitHooks()}"
}
}
}
return new JsonBuilder(result).toPrettyString()
And then you can use the Jenkins Script Console web API to get this from an HTTP client.
For example, in curl, you can do this by saving your script as a text file and then running:
curl --data-urlencode "script=$(<./script.groovy)" <YOUR SERVER>/scriptText
If Jenkins is using basic authentication, you can supply that with the -u <USERNAME>:<PASSWORD> argument.
Ultimately, the request will result in something like this:
{
"Build Project 1": {
"spec": "H/30 * * * *",
"ignorePostCommitHooks": "false"
},
"Test Something": {
"spec": "#hourly",
"ignorePostCommitHooks": "false"
},
"Deploy ABC": {
"spec": "H/20 * * * *",
"ignorePostCommitHooks": "false"
}
}
You should be able to tailor these examples to fit your specific use case. It seems you won't need to access this remotely but just from a job, but I also included the remoting part as it might come in handy for someone else.

Providing different values in Jenkins dsl configure block to create different jobs

I need my builds to timeout at a specific time (deadline) but currently Jenkins dsl only support the "absolute" strategy. So I tried to write the configure block but couldn't create jobs with different deadline values.
def settings = [
[
jobname: 'job1',
ddl: '13:10:00'
],
[
jobname: 'job2',
ddl: '14:05:00'
]
]
for (i in settings) {
job(i.jobname) {
configure {
it / buildWrappers << 'hudson.plugins.build__timeout.BuildTimeoutWrapper' {
strategy(class:'hudson.plugins.build_timeout.impl.DeadlineTimeOutStrategy') {
deadlineTime(i.ddl)
deadlineToleranceInMinutes(1)
}
}
}
steps {
// some stuff to do here
}
}
}
The above script gives me two jobs with the same deadline time(14:05:00):
<project>
<actions></actions>
<description></description>
<keepDependencies>false</keepDependencies>
<properties></properties>
<scm class='hudson.scm.NullSCM'></scm>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers></triggers>
<concurrentBuild>false</concurrentBuild>
<builders></builders>
<publishers></publishers>
<buildWrappers>
<hudson.plugins.build__timeout.BuildTimeoutWrapper>
<strategy class='hudson.plugins.build_timeout.impl.DeadlineTimeOutStrategy'>
<deadlineTime>14:05:00</deadlineTime>
<deadlineToleranceInMinutes>1</deadlineToleranceInMinutes>
</strategy>
</hudson.plugins.build__timeout.BuildTimeoutWrapper>
</buildWrappers>
</project>
I found this question but still couldn't get it to work.
You can use the Automatic Generated API
The generated DSL is only supported when running in Jenkins, e.g. it is
not available when running from the command line or in the Playground.
Use The Configure Block to generate custom config elements when not
running in Jenkins.
The generated DSL will not work for all plugins, e.g. if a plugin does
not use the #DataBoundConstructor and #DataBoundSetter annotations to
declare parameters. In that case The Configure Block can be used to
generate the config XML.
Fortunately the Timeout plugin support DataBoundConstructors
#DataBoundConstructor
public DeadlineTimeOutStrategy(String deadlineTime, int deadlineToleranceInMinutes) {
this.deadlineTime = deadlineTime;
this.deadlineToleranceInMinutes = deadlineToleranceInMinutes <= MINIMUM_DEADLINE_TOLERANCE_IN_MINUTES ? MINIMUM_DEADLINE_TOLERANCE_IN_MINUTES
: deadlineToleranceInMinutes;
}
So you should be able to do something like
def settings = [
[
jobname: 'job1',
ddl: '13:10:00'
],
[
jobname: 'job2',
ddl: '14:05:00'
]
]
for (i in settings) {
job(i.jobname) {
wrappers {
buildTimeoutWrapper {
strategy {
deadlineTimeOutStrategy {
deadlineTime(i.ddl)
deadlineToleranceInMinutes(1)
}
}
timeoutEnvVar('WHAT_IS_THIS_FOR')
}
}
steps {
// some stuff to do here
}
}
}
There is an extra layer in BuildTimeoutWrapper which houses the different strategies
When using nested classes you need to set the first letter of the class to lowercase
EDIT
You can see this in your own Jenkins install by using the 'Job DSL API Reference' link in a jobs page
http://<your jenkins>/plugin/job-dsl/api-viewer/index.html#method/javaposse.jobdsl.dsl.helpers.wrapper.WrapperContext.buildTimeoutWrapper
I saw very similar behaviour to this in a Jenkins DSL groovy script.
I was looping over a List of Maps in a for each, and I also have a configure closure like your example.
The behaviour I saw was that the Map object in the configure closure seemed to be the same for all iterations of the for each loop. Similar to how you are seeing the same deadline time.
I was actually referencing the same value in the Map both inside and outside the configure closure and the DSL was outputting different values. Outside the configure closure was as expected, but inside was the same value for all iterations.
My solution was just to use a variable to reference the Map value and use that both inside and outside the configure closure, when I did that, the value was consistent.
For your example (just adding a deadlineValue variable, and setting it outside the configure closure):
for (i in settings) {
def deadlineValue = i.ddl
job(i.jobname) {
configure {
it / buildWrappers << 'hudson.plugins.build__timeout.BuildTimeoutWrapper' {
strategy(class:'hudson.plugins.build_timeout.impl.DeadlineTimeOutStrategy') {
deadlineTime(deadlineValue)
deadlineToleranceInMinutes(1)
}
}
}
steps {
// some stuff to do here
}
}
}
I would not expect this to make a difference, but it worked for me.
However I agree as per the the other solution it is better to use buildTimeoutWrapper, so you can avoid using the configure block.
See: <Your Jenkins URL>/plugin/job-dsl/api-viewer/index.html#path/javaposse.jobdsl.dsl.DslFactory.job-wrappers-buildTimeoutWrapper-strategy-deadlineTimeOutStrategy for more details, obviously you'll need the Build Timeout plugin installed.
For my example I still needed the configure closure for the MultiJob plugin where some parameters were still not configurable via the DSL api.

Can a Job DSL script be tested

Ideally I'd like to be able to invoke the script with some kind of unit test before I have it execute on a Jenkins.
Is there any way to test a Job DSL script other than having jenkins run it?
Besides the examples in job-dsl-gradle-example, you can also go a step further and write tests for individual files or jobs. For example let's assume you have a job configuration file located in jobs/deployJob.groovy
import javaposse.jobdsl.dsl.DslScriptLoader
import javaposse.jobdsl.dsl.MemoryJobManagement
import javaposse.jobdsl.dsl.ScriptRequest
import spock.lang.Specification
class TestDeployJobs extends Specification {
def 'test basic job configuration'() {
given:
URL scriptURL = new File('jobs').toURI().toURL()
ScriptRequest scriptRequest = new ScriptRequest('deployJob.groovy', null, scriptURL)
MemoryJobManagement jobManagement = new MemoryJobManagement()
when:
DslScriptLoader.runDslEngine(scriptRequest, jobManagement)
then:
jobManagement.savedConfigs.each { String name, String xml ->
with(new XmlParser().parse(new StringReader(xml))) {
// Make sure jobs only run manually
triggers.'hudson.triggers.TimerTrigger'.spec.text().isEmpty()
// only deploy every environment once at a time
concurrentBuild.text().equals('false')
// do a workspace cleanup
buildWrappers.'hudson.plugins.ws__cleanup.PreBuildCleanup'
// make sure masked passwords are active
!buildWrappers.'com.michelin.cio.hudson.plugins.maskpasswords.MaskPasswordsBuildWrapper'.isEmpty()
}
}
}
}
This way you are able to go through every XML node you want to make sure to have all the right values set.
Have a look at the job-dsl-gradle-example. The repo contains a test for DSL scripts.
Doing it in the same way as crasp but using Jenkins test harness as explained in Jenkins Unit Test page, which is slower but would work with auto-generated DSL giving syntax errors as explained here.
After setting the code as explained here, you can just do a test like this one:
#Unroll
void 'check descriptions #file.name'(File file) {
given:
JobManagement jobManagement = new JenkinsJobManagement(System.out, [:], new File('.'))
Jenkins jenkins = jenkinsRule.jenkins
when:
GeneratedItems items = new DslScriptLoader(jobManagement).runScript(file.text)
then:
if (!items.jobs.isEmpty()) {
items.jobs.each { GeneratedJob generatedJob ->
String text = getItemXml(generatedJob, jenkins)
with(new XmlParser().parse(new StringReader(text))) {
// Has some description
!description.text().isEmpty()
}
}
}
where:
file << TestUtil.getJobFiles()
}

Resources