script

[!TIP] to list methods on a class instance:

thing.metaClass.methods*.name.sort().unique()

to determine a class from an instance:

thing.class
// or
thing.getClass()

usage

[!TIP]

  • CSRF Protection Explained

    • if you authenticate your API calls with a username and a user API token then a crumb is not required from Jenkins 2.96

  • to get CSRF crumb via curl

    $ SERVER="https://localhost:8080"
    $ COOKIEJAR="$(mktemp)"
    $ CRUMB=$(curl -u "admin:admin" -s --cookie-jar "$COOKIEJAR" "$SERVER/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,%22:%22,//crumb)")
    # to run script via curl
    $ curl -d "script=System.getProperties()" \
           -u "admin:admin" \
           --cookie "$COOKIEJAR" \
           -H "$CRUMB" \
           https://${SERVER}/scriptText
  • or

    $ SERVER="https://localhost:8080"
    $ COOKIEJAR="$(mktemp)"
    $ CRUMB=$(curl -u "admin:admin" \
                   --cookie-jar "${COOKIEJAR}" \
                   'https://${SERVER}/crumbIssuer/api/json' |
                   jq -r '[.crumbRequestField, .crumb] | join(":")'
             )
    # verify
    $ echo $CRUMB
    Jenkins-Crumb:c11dc*******************************************************e463
    $ curl -d "script=System.getProperties()" \
           -u "admin:admin" \
           -s \
           --cookie "$COOKIEJAR" \
           -H "$CRUMB" \
           https://${SERVER}/scriptText
    $ curl --data-urlencode "script=$(< ./script.groovy)" \
           -s \
           --netrc-file ~/.netrc \
           --cookie "${COOKIEJAR}" \
           -H "${CRUMB}" \
           https://${SERVER}/scriptText
  • remote access

    $ curl -d "script=<your_script_here>" https://jenkins/script
    
    # or to get output as a plain text result (no HTML)
    $ curl -d "script=<your_script_here>" https://jenkins/scriptText
  • curl submitting groovy file

    $ curl --data-urlencode "script=$(< ./somescript.groovy)" https://jenkins/scriptText
    • via api token

      $ curl --user 'username:api-token' \
             --data-urlencode \
             "script=$(< ./somescript.groovy)" \
             https://jenkins/scriptText
    • via python

      with open('somescript.groovy', 'r') as fd:
        data = fd.read()
      r = requests.post('https://jenkins/scriptText', auth=('username', 'api-token'), data={'script': data})

setup system property (temporary)

[!TIP|label:references:]

  • timestampe

    System.setProperty( 'org.apache.commons.jelly.tags.fmt.timeZone', 'America/Los_Angeles' )
  • shell step aborts

    System.setProperty( 'org.jenkinsci.plugins.durabletask.BourneShellScript.HEARTBEAT_CHECK_INTERVAL', 36000 )
  • to clear property

    System.clearProperty( 'hudson.model.DirectoryBrowserSupport.CSP' )
  • to get property

    System.getProperty( 'hudson.model.DirectoryBrowserSupport.CSP' )
  • to get all properties

    System.getProperties()
    
    // or
    System.getProperties().sort().collectEntries{[ (it.key), (it.value) ]}
    System.getProperties().sort().each { println "${it.key} ~> ${it.value}" }
    System.getProperties().sort().collect{ "${it.key} ~> ${it.value}" }.join('\n')

extend built-in node executor

import jenkins.model.*
jenkins.model.Jenkins.instance.setNumExecutors(5)

execute shell script in console

[!TIP|label:references:]

println ( 'uname -a'.execute().text )

// or
println ( 'printenv'.execute().in.text )
  • result

    Linux devops-jenkins-685cf57df9-znfs8 4.19.12-1.el7.elrepo.x86_64 #1 SMP Fri Dec 21 11:06:36 EST 2018 x86_64 GNU/Linux
  • or

    import hudson.util.RemotingDiagnostics
    import jenkins.model.Jenkins
    
    String agentName = 'your agent name'
    // groovy script you want executed on an agent
    groovyScript = '''
      println System.getenv("PATH")
      println "uname -a".execute().text
    '''.stripIndent()
    
    String result
    jenkins.model.Jenkins.instance.slaves.find { agent ->
      agent.name == agentName
    }.with { agent ->
      result = RemotingDiagnostics.executeGroovy( groovyScript, agent.channel )
    }
    println result

read & write files

// write
new File('/tmp/file.txt').withWriter('UTF-8') { writer ->
  try {
    writer << 'hello world\n'
  } finally {
    writer.close()
  }
}

// read
new File('/tmp/file.txt').text
  • write file in agent

    import hudson.FilePath
    import hudson.remoting.Channel
    import jenkins.model.Jenkins
    
    String agentName = 'some-agent'
    String filePath  = '/tmp/file.txt'
    
    Channel agentChannel = jenkins.model.Jenkins.instance.slaves.find { agent ->
      agent.name == agentName
    }.channel
    
    new FilePath( agentChannel, filePath ).write().with { os ->
      try {
        os << 'hello world\n'
      } finally {
        os.close()
      }
    }
  • read file from an agent

    import hudson.FilePath
    import hudson.remoting.Channel
    import jenkins.model.Jenkins
    
    import java.io.BufferedReader
    import java.io.InputStreamReader
    import java.nio.charset.StandardCharsets
    import java.util.stream.Collectors
    
    String agentName = 'some-agent'
    String filePath  = '/tmp/file.txt'
    
    Channel agentChannel = jenkins.model.Jenkins.instance.slaves.find { it.name == agentName }.channel
    
    String fileContents = ''
    new FilePath(agentChannel, filePath).read().with { is ->
      try {
        fileContents = new BufferedReader(
                         new InputStreamReader(is, StandardCharsets.UTF_8))
                            .lines()
                            .collect(Collectors.joining("\n")
                       )
      } finally {
        is.close()
      }
    } // with
    
    // print contents of the file from the agent
    println '==='
    println(fileContents)
    println '==='

nslookup

[!NOTE|label:references:]

if( !binding.hasVariable('domain') ) {
  domain = 'example.com'
}

if( !(domain in String) ) {
  throw new Exception('PARAMETER ERROR: domain must be a string.')
}

InetAddress dnsInetAddress = InetAddress.getByName domain
println dnsInetAddress.hostAddress

jenkins system

import jenkins.model.*;
import org.jenkinsci.main.modules.sshd.*;

jenins.model.Jenkins instance = jenkins.model.Jenkins.instance
instance.setDisableRememberMe( false )
instance.setNumExecutors( 2 )
instance.setSystemMessage( '<h2>welcome to the Jenkins Master</h2>' )
instance.setRawBuildsDir()
instance.save()

def sshd = SSHD.get()
sshd.setPort( 12345 )
sshd.save()

println( "Groovy: ${GroovySystem.version}" )
println( "Jenkins: ${jenkins.model.Jenkins.instance.getVersion()}" )
println( "OS: ${System.getProperty('os.name')} - ${System.getProperty('os.version')}" )
println( "Java: ${System.getProperty('java.version')} - ${System.getProperty('java.vm.vendor')} (${System.getProperty('java.vm.name')})" )
println "---"

jenkins.model.Jenkins.instance.pluginManager.plugins
       .collect()
       .sort { it.getShortName() }
       .each {
           plugin -> println("${plugin.getShortName()}:${plugin.getVersion()}")
       }
return

modify log level

System.setProperty( 'org.apache.commons.logging.Log', 'org.apache.commons.logging.impl.SimpleLog'     );
System.setProperty( 'org.apache.commons.logging.simplelog.showdatetime', 'true'                       );
System.setProperty( 'org.apache.commons.logging.simplelog.log.httpclient.wire.header', 'error'        );
System.setProperty( 'org.apache.commons.logging.simplelog.log.org.apache.http', 'error'               );
System.setProperty( 'log4j.logger.org.apache.http', 'error'                                           );
System.setProperty( 'log4j.logger.org.apache.http.wire', 'error'                                      );
System.setProperty( 'org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient', 'error' );
  • check log level

    println System.getProperty( 'org.apache.commons.logging.Log'                                         );
    println System.getProperty( 'org.apache.commons.logging.simplelog.showdatetime'                      );
    println System.getProperty( 'org.apache.commons.logging.simplelog.log.httpclient.wire.header'        );
    println System.getProperty( 'org.apache.commons.logging.simplelog.log.org.apache.http'               );
    println System.getProperty( 'log4j.logger.org.apache.http'                                           );
    println System.getProperty( 'log4j.logger.org.apache.http.wire'                                      );
    println System.getProperty( 'org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient' );

RABC

[!NOTE|label:references:]

jobs & builds

[!TIP|label:get more:]

list build status with percentage

get all builds status during certain start-end time

hudson.FilePath workspace = hudson.model.Executor.currentExecutor().getCurrentWorkspace()
  • get absolute path

    println( "script directory: ${new File(__FILE__).parent.absolutePath}" )

// you have to install the Shelve Project Plugin on your Jenkins Master
// the maximum value for daysBack is 365, going beyond 365 will break the script.

import org.jvnet.hudson.plugins.shelveproject.ShelveProjectTask

def daysBack = 365;
jenkins.model.Jenkins.instance.getAllItems( AbstractProject.class ).each { it ->
  def lastBuild = it.getLastBuild()
  if( lastBuild != null ) {
    def back = Calendar.getInstance()
    back.set( Calendar.DAY_OF_YEAR,back.get(Calendar.DAY_OF_YEAR) - daysBack )

    if ( lastBuild.getTime().compareTo(back.getTime()) < 0 ) {
      println it.name + " was built over " + daysBack + " days ago: " + lastBuild.getTime()
      if ( it instanceof AbstractProject ){
        def spt=  new ShelveProjectTask(it)
        Hudson.getInstance().getQueue().schedule(spt , 0 );
      } else {
        println it.name + " was not shelved ----------- "
      }
    }

  }
}

plugins

jenkins.model.Jenkins.instance
       .pluginManager
       .plugins
       .each { plugin ->
          println ( "${plugin.getDisplayName()} (${plugin.getShortName()}): ${plugin.getVersion()}" )
       }

list for helm-value.yaml

jenkins.model.Jenkins.instance
       .pluginManager
       .plugins
       .sort(false) { a, b ->
          a.getShortName().toLowerCase() <=> b.getShortName().toLowerCase()
       }
       .each { plugin ->
          println "- ${plugin.getShortName()}:${plugin.getVersion()}"
       }
"DONE"

ExtensionList.lookup( UnprotectedRootAction ).each {
  println String.format( "URL: '%s/' provided by '%s' in '%s'",
                         it.urlName,
                         Jenkins.get().pluginManager.whichPlugin(it.class)?.shortName?:"Jenkins Core",
                         it.class.name
          )
}

list plugin and dependencies

println jenkins.model.Jenkins.instance.pluginManager.plugins
               .sort(false) { a, b ->
                                a.getShortName().toLowerCase() <=> b.getShortName().toLowerCase()
                            }
               .collect { plugin ->
                            "~~> ${plugin.shortName} : ${plugin.version} : ${plugin.displayName}" +
                            ( plugin.dependants   ? "\n\t+++ ${plugin.dependants.join('\n\t+++ ')}"   : '' )  +
                            ( plugin.dependencies ? "\n\t... ${plugin.dependencies.join('\n\t... ')}" : '' )
                        }
               .join('\n')
  • get specific plugin dependencies

    List<String> keywords = [ 'jsch' ]
    
    println jenkins.model.Jenkins.instance.pluginManager.plugins
                   .findAll { plugin -> keywords.any { it == plugin.shortName } }
                   .sort(false) { a, b ->
                                    a.getShortName().toLowerCase() <=> b.getShortName().toLowerCase()
                                }
                   .collect { plugin ->
                                "~~> ${plugin.shortName} : ${plugin.version} : ${plugin.displayName}" +
                                ( plugin.dependants   ? "\n\t+++ ${plugin.dependants.join('\n\t+++ ')}"   : '' )  +
                                ( plugin.dependencies ? "\n\t... ${plugin.dependencies.join('\n\t... ')}" : '' )
                            }
                   .join('\n')
  • get dependency tree

    import hudson.PluginWrapper
    
    def getDependencyTree( String keyword, Integer benchmark = 2, Integer index = 0 ) {
      String prefix        = index ? '\t' + "|\t"*(index-1) + "|... " : ''
      PluginWrapper plugin = jenkins.model.Jenkins.instance.pluginManager.plugins.find { keyword == it.shortName }
      List dependencies    = plugin.collect { it.dependencies }.flatten() ?: []
    
      println prefix + "${plugin.shortName} ( ${plugin.version} )"
    
      if ( dependencies && benchmark != index ) {
        dependencies.collect{ it.shortName }.each { getDependencyTree (it, benchmark, index+1) }
      }
    }
    
    getDependencyTree( 'jsch', 100 )
    
    "DONE"
    • result

      jsch ( 0.2.8-65.v052c39de79b_2 )
          |... ssh-credentials ( 305.v8f4381501156 )
          |   |... credentials ( 1254.vb_96f366e7b_a_d )
          |   |   |... structs ( 324.va_f5d6774f3a_d )
          |   |   |   |... javax-activation-api ( 1.2.0-6 )
          |   |   |   |... javax-mail-api ( 1.6.2-9 )
          |   |   |   |   |... javax-activation-api ( 1.2.0-6 )
          |   |   |   |... instance-identity ( 173.va_37c494ec4e5 )
          |   |   |   |   |... bouncycastle-api ( 2.28 )
          |   |   |... configuration-as-code ( 1647.ve39ca_b_829b_42 )
          |   |   |   |... caffeine-api ( 3.1.6-115.vb_8b_b_328e59d8 )
          |   |   |   |... commons-text-api ( 1.10.0-36.vc008c8fcda_7b_ )
          |   |   |   |   |... commons-lang3-api ( 3.12.0-36.vd97de6465d5b_ )
          |   |   |   |   |   |... javax-activation-api ( 1.2.0-6 )
          |   |   |   |   |   |... javax-mail-api ( 1.6.2-9 )
          |   |   |   |   |   |   |... javax-activation-api ( 1.2.0-6 )
          |   |   |   |   |   |... instance-identity ( 173.va_37c494ec4e5 )
          |   |   |   |   |   |   |... bouncycastle-api ( 2.28 )
          |   |   |   |... snakeyaml-api ( 1.33-95.va_b_a_e3e47b_fa_4 )
          |   |... trilead-api ( 2.84.v72119de229b_7 )
          |   |... instance-identity ( 173.va_37c494ec4e5 )
          |   |   |... bouncycastle-api ( 2.28 )
          |... trilead-api ( 2.84.v72119de229b_7 )
          |... javax-activation-api ( 1.2.0-6 )
          |... javax-mail-api ( 1.6.2-9 )
          |   |... javax-activation-api ( 1.2.0-6 )
          |... instance-identity ( 173.va_37c494ec4e5 )
          |   |... bouncycastle-api ( 2.28 )
  • others

    def plugins = jenkins.model.Jenkins.instance
                         .pluginManager
                         .plugins
                         .sort(false) { a, b ->
                           a.getShortName().toLowerCase() <=> b.getShortName().toLowerCase()
                         }
    
    println "jenkins instance : ${jenkins.model.Jenkins.instance.getComputer('').hostName} + ${jenkins.model.Jenkins.instance.rootUrl}\n" +
            "installed plugins:\n=================="
    plugins.each { plugin ->
      println "  ${plugin.getShortName()} : ${plugin.getVersion()} | ${plugin.getDisplayName()}"
    }
    
    println "\nplugins dependency tree (...: dependencies; +++: dependants) :\n======================="
    plugins.each { plugin ->
      println """
        ${plugin.getShortName()} : ${plugin.getVersion()} | ${plugin.getDisplayName()}
        +++ ${plugin.getDependants()}
        ... ${plugin.getDependencies()}
    
      """
    }
  • or

    def jenkins = jenkins.model.Jenkins.instance
    
    println """
      Jenkins Instance : ${jenkins.getComputer('').hostName} + ${jenkins.rootUrl}
      Installed Plugins:
      ==================
    """
    jenkins.pluginManager
           .plugins
           .sort(false) { a, b ->
             a.getShortName().toLowerCase() <=> b.getShortName().toLowerCase()
           }.each { plugin ->
             println "${plugin.getShortName()}: ${plugin.getVersion()} | ${plugin.getDisplayName()}"
           }
    
    println """
      Plugins Dependency tree (...: dependencies; +++: dependants) :
      =======================
    """
    jenkins.pluginManager
           .plugins
           .sort(false) { a, b ->
             a.getShortName().toLowerCase() <=> b.getShortName().toLowerCase()
           }.each { plugin ->
             println """
               ${plugin.getShortName()} : ${plugin.getVersion()} | ${plugin.getDisplayName()}
               +++ ${plugin.getDependants()}
               ... ${plugin.getDependencies()}
    
             """
           }

scriptApproval

backup & restore all scriptApproval items

  • backup

    import java.lang.reflect.*
    import jenkins.model.Jenkins
    import jenkins.model.*
    import org.jenkinsci.plugins.scriptsecurity.scripts.*
    import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.*
    import static groovy.json.JsonOutput.*
    
    scriptApproval = ScriptApproval.get()
    alreadyApproved = new HashSet<>(Arrays.asList(scriptApproval.getApprovedSignatures()))
    println prettyPrint( toJson(alreadyApproved.sort()) )
  • restore

    def scriptApproval = org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval.get()
    
    String[] signs = [
      'method org.jenkinsci.plugins.workflow.steps.FlowInterruptedException getCauses' ,
      'method org.jenkinsci.plugins.workflow.support.steps.input.Rejection getUser'
    ]
    
    for( String sign : signs ) {
      scriptApproval.approveSignature( sign )
    }
    
    scriptApproval.save()
  • example

    import java.lang.reflect.*;
    import jenkins.model.Jenkins;
    import jenkins.model.*;
    import org.jenkinsci.plugins.scriptsecurity.scripts.*;
    import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.*;
    import static groovy.json.JsonOutput.*
    
    ScriptApproval scriptApproval = ScriptApproval.get()
    HashSet<String> alreadyApproved = new HashSet<>(Arrays.asList(scriptApproval.getApprovedSignatures()))
    
    Closure approveSignature = { String signature ->
      if ( ! alreadyApproved?.contains(signature) ) scriptApproval.approveSignature( signature )
    }
    
    [
      'field org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval$PendingSignature dangerous'         ,
      'field org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval$PendingSignature signature'         ,
      'method org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval$PendingThing getContext'           ,
      'method org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval approveSignature java.lang.String' ,
      'method org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval getPendingScripts'                 ,
      'method org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval getPendingSignatures'              ,
      'staticMethod org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval get'                         ,
      'staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods flatten java.util.Set'                  ,
      'method org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper getRawBuild'
    ].each { println "~~> ${it}"; approveSignature(it) }
    
    scriptApproval.save()
  • Jenkinsfile

    #!/usr/bin/env groovy
    
    import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval
    
    timestamps { ansiColor('xterm') {
    
      def requester = currentBuild.rawBuild.getCause(UserIdCause.class)?.getUserId() ?: 'jenkins'
      final List<String> description = []
    
      try {
    
        ScriptApproval scriptApproval = ScriptApproval.get()
        final LinkedHashSet<String> pendingScripts   = new HashSet<>(Arrays.asList( scriptApproval.getPendingScripts() )).flatten()
        final LinkedHashSet<String> pendingSignature = new HashSet<>(Arrays.asList( scriptApproval.getPendingSignatures() )).flatten()
    
        if ( ! pendingScripts && ! pendingSignature ) {
          currentBuild.description = 'NOT_BUILT: nothing can be approved'
          currentBuild.rawBuild.executor.interrupt( Result.NOT_BUILT )
        }
    
        if ( pendingScripts ) {
          println 'scripts pending approval ...'
          pendingScripts.collect().each { ps ->
            String log = "${ps.context.user}@${ps.context.psem.fullName} : ${ps.hash} ( ${ps.language.class.simpleName} )"
            description << log
            println "~~> ${log}. scripts: \n ${ps.script}"
            scriptApproval.approveScript( ps.hash )
          }
          scriptApproval.save()
        } // pendingScripts
    
        if ( pendingSignature ) {
          println 'signatures pending approval ...'
          pendingSignature.collect().each { ps ->
            String signature = ps.signature
            if ( ! ps.dangerous ) {
              description << signature
              println "~~> '${signature}'"
              scriptApproval.approveSignature( signature )
            } else {
              println "~~> '${signature}' is too dangerous to be approval automatically. contact with Jenkins administrator."
            }
            scriptApproval.save()
          }
        }
    
      } catch(e) {
        def sw = new StringWriter()
        e.printStackTrace( new PrintWriter(sw) )
        echo sw.toString()
        throw e
      } finally {
        if ( description ) {
          currentBuild.description = "@${requesterId} " +
                                     "${buildResults.isSuccess(currentBuild.currentResult) ? 'successful' : 'failed to'} " +
                                     "approved : '${description.join('; ')}'"
        }
      } // try/catch/finally
    
    }} // ansiColor | timestamps
    
    // vim:tabstop=2:softtabstop=2:shiftwidth=2:expandtab:filetype=Jenkinsfile
    • automatic approval

      // libs.groovy
      def autoAccept( Closure body ) {
        try {
          body()
        } catch ( org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException e ) {
          String msg = "NOT_BUILT : interrupted by approval scripts or signature"
          def cause = { msg as String } as CauseOfInterruption
          currentBuild.rawBuild.executor.interrupt( Result.NOT_BUILT, cause )
          currentBuild.description = msg
          build wait: false, job: '/marslo/scriptApproval'
        }
      }
      
      // jenkinsfile
      libs.autoAccept() {
        ...content...
      }

automatic approval all pending

  • list pending scriptApproval

    import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval
    
    ScriptApproval scriptApproval = ScriptApproval.get()
    scriptApproval.pendingScripts.each { scriptApproval.approveScript( it.hash ) }
  • job dsl support for scriptapproval

    import jenkins.model.Jenkins
    
    def scriptApproval = jenkins.model.Jenkins.instance
                                .getExtensionList('org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval')[0]
    def hashesToApprove = scriptApproval.pendingScripts
                                        .findAll{ it.script.startsWith(approvalPrefix) }
                                        .collect{ it.getHash() }
    hashesToApprove.each { scriptApproval.approveScript(it) }

[!NOTE|label:@deprecated]

  • file: $JENKINS_HOME/init.groovy.d/disable-script-security.groovy

import javaposse.jobdsl.plugin.GlobalJobDslSecurityConfiguration
import jenkins.model.GlobalConfiguration

// disable Job DSL script approval
GlobalConfiguration.all().get(GlobalJobDslSecurityConfiguration.class).useScriptSecurity=false
GlobalConfiguration.all().get(GlobalJobDslSecurityConfiguration.class).save()

Last updated