Jenkins's Tips

It's been a while so I just post a few tips when working on Jenkins that I have found. Just a heart beat post to keep this blog alive :D. I'm still in high pressure my current job, unfortunately!


I refer $JENKINS_HOME below should be /var/lib/jenkins if you don't use the custom jenkins home directory.
  • Trigger a Jenkins job via REST API: as you might know Jenkins job can be triggered via REST API. So you will have multiple-ways to trigger it
Simplest way is using curl tool (you can even trigger a scriptler with REST API)
curl -X POST [Jenkins server]/scriptler/run/[scriptlerID]?Param=Value --user [username]:[password] 

Use any http method to send a post request (with authentication header to authorize your access to Jenkins server)
Sample in Groovy:

def int invokeRemoteJob(String urlPath, String urlParameters, String userToken) {
  URL url = new URL(urlPath + urlParameters)
  URLConnection uConnection = url.openConnection()

  String basicAuth = "Basic " + javax.xml.bind.DatatypeConverter.printBase64Binary(userToken.getBytes())
  uConnection.setRequestProperty ("Authorization", basicAuth)
  uConnection.setRequestMethod("POST")

  uConnection.connect()
  int response = uConnection.getResponseCode()
  uConnection.disconnect()
  return response

}

String urlPath = "http://[JenkinsServer]:8080/[JobPath]"
String urlParameters = "buildWithParameters?Param1=Value1&Param2=Value2"
String userToken = "[user]:[password]"
invokeRemoteJob(urlPath, urlParameters, userToken)

Sample in Powershell:
$jenkinsJob = "http://[JenkinsServer]:8080/[JobPath]/buildWithParameters?Param=Value"
$requestUri = New-Object System.Uri($jenkinsJob)
$encoded =  [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes([User] + ":" + [Password]))
Invoke-RestMethod -Uri "$requestUri" -Method POST -Headers @{"Authorization" = "Basic $encoded"}



  • Get Jenkins object via JSON REST API: any Hudson object can be retrieved via REST API to manipulate the status of Jenkins server from remote.
Sample to get list of Jenkins slaves (node, or computer object)

def computerAPI = new URL("[JenkinsServer]/computer/api/json")
def parser = new JsonSlurper().setType(JsonParserType.INDEX_OVERLAY)
def jsonList = parser.parseText(computerAPI.getText())
def nodes = jsonList.computer
 
for (node in nodes) {
     //Do something here
}

  • Execute process and retrieve return code from Groovy: you can execute a process by using execute() from Groovy String

def String sCommand = "[ExecuteProcess]"
def sOut = new StringBuffer()
def sErr = new StringBuffer()
def oProc = sCommand.execute()
oProc.consumeProcessOutput(sOut, sErr)
oProc.waitForOrKill(1000)
def String sReturn = sOut.toString().trim()

  • Work with Environment Variable in job: in a job, when you want to create an environment variable, here are some ways to create
From server side, you can use Hudson object model

def build = Thread.currentThread().executable
// Get environment variable
def String sExistProp = build.getEnvironment(listener).get('MyProp')
// Set environment variable
def ParametersAction pAction = new ParametersAction([
  new StringParameterValue("Prop1", "Prop1Value")
])
build.addAction(pAction)

From client side (slave mode) then you have to use Inject Environment Variable plugin
// Get environment variable
def String sExistProp = System.getenv('MyProp')

// Set environment variable
File fProp = new File("env.prop")
fProp.text = "Prop1=Prop1Value"

Then inject this env.prop file. Please note, from Jenkins 2.x, due to the security issues, it won't allow to create new environment variable within the job without declaration from the parameter. In order to use the old behaviour you have to set this system setting hudson.model.ParametersAction.keepUndefinedParameters to true. To set this property, create a groovy file (for ex: params.groovy) with the script in info session under $JENKINS_HOME/init.groovy.d

  • Use non-serializable object in Jenkins pipeline: this is a basic understand on CPS mode of Jenkins pipeline based on Groovy CPS plugin. Directly, in my case, I want to parse the json data, I have to use the notation @NonCPS on my function via this guide.

@NonCPS
def jsonParse(String json) {
    def jsonObject = new groovy.json.JsonSlurperClassic().parseText(json)
    return jsonObject
}

  • Use external library from public Maven repo: you can use @Grab notation to grab the public library on Maven repo by searching for its public Maven repo then use the public GAV. For example with snakeyaml lib, you can find it here

<!-- https://mvnrepository.com/artifact/org.yaml/snakeyaml -->
<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.18</version>
</dependency>
Then in your pipeline code you will grab it
@Grab(group = 'org.yaml', module = 'snakeyaml', version = '1.18')


  • Add a text parameters (multilines) into properties file: when you have a text parameter with multiple lines that context a set of variables and values (some kind of a map), an easy way to have them in the environment variables is using this snippet code in windows batch:

setlocal EnableDelayedExpansion
(echo ![ParamName]!)>"%WORKSPACE%\[FileName].prop"

Then inject this file for environment variable


  • Use UserCrumb to trigger REST API for Jenkins 2.x

From Jenkins 2.x, there is an option to turn on Prevent Cross Site Request and apply the crumb issuer from Jenkins, so in order to call the CSRF, you have to get the Jenkins crumb of a proper user and add it into the http header


userToken=[user]:[apitoken]
jenkinsServer=http://[jenkinsserver]
jenkinsJob=[job/Path]
jobParams=buildWithParameters\?[Param]=[Value]
userCrumb=$(curl --user $userToken $jenkinsServer/crumbIssuer/api/xml?xpath=concat\(//crumbRequestField,%22:%22,//crumb\))
curl -I -X POST --user $userToken $jenkinsServer/$jenkinsJob/$jobParams -H $userCrumb


  • Use python virtualenv in Jenkins pipeline script

Python virtualenv is a magic helper that assist to build python with different environment settings, however, there is a tricky part when running with interactive mode vs non-interactive mode from Jenkins's design.Not sure it's proper to call about interactive issue but when you call [virtualenv]/bin/activate from pipeline script then the next call it won't have the $PATH setting from the activation. Because the activation is performed within a shell session where our pipeline code create each shell session for an 'sh' call, so you have to wrap the activation call within a shell script block.

Please also note the activate script that need to call the bash source, which the Jenkins pipeline is support on shell environment so you should interpret into '. ./bin/activate'

So the script block should look like this

sh """cd [virtualenv]
      . ./bin/activate
      pip install requirement.txt
      python ./action.py
      """

When you want to run another call within the virtualenv, you have to add the activation again into the shell script block or you can take a look on this plugin.


  • Multibranch project fails to scan for branch indexing
Multibranch projects will store the git status of the branch indexing in $JENKINS_HOME/caches/git-<hash>/.git. In case you shutdown jenkins forcible, there might be a case it left a git lock (config.lock file) in the git status so next time you will not allowed to scan the branch indexing. Remove that config.lock file will help.


  • Change jenkins user password
When you use local jenkins user, the user's password will be encrypted in $JENKINS_HOME/users/<username_hash>/config.xml. There are some solutions when you are locked out of your Jenkins.
  1. If you are the Jenkins owner, you can disable the login feature by update file $JENKINS_HOME/config.xml to replace <useSecurity> from 'true' to 'false'. Then restart jenkins
  2. If you only need to reset user password. Please use below solution. This was worked for me on those options in this discussion. Use python with bcrypt module
import bcrypt
bcrypt.hashpw("<my_password>", bcrypt.gensalt(round=10, prefix=b"2a"))
'hashed_pwd'

Then open $JENKINS_HOME/users/<username_hash>/config.xml file and replace the new hashed password to

<passwordHash>#jbcrypt:<hashed_pwd></passwordHash>

Restart jenkins service to take the change into affect.


These are some tips when I'm using Jenkins and I found its worth for a note so whenever anyone have something relate might pass here for a reference.
PS: I will update more when I have something news.

Comments