Pipeline execution

The current running pipeline is available within Pipeline Expressions as execution. From there, you can navigate to different parts of the pipeline. For example, to reference the name of the first stage you can use ${ execution.stages[0]['name'] }.

The current stage

Values for the current stage context are available by their variable names. In the source JSON, they will be defined under the context object. if I see context.package=ubuntu in the current stage of source JSON, then if I use ${ package } in this stage, it will resolve to the package ubuntu.

Other stages

Values for the other stages context are also available by using ${ #stage('stage name'). To access stage attributes, you can also call ${ #stage('stage name')['id']}. To access the values passed into the stage, use ${ #stage('stage name')['context']['baseAmiName']}. This is equal to use ${ execution.stages[0]['context']['baseAmiName'] }.

Pipeline parameters

If we have parameters setup at the pipeline configuration page, then it will be available all over the entire pipeline. If you have parameters setup as branch name = master, then you can use ${ parameters['branch name'] } in any stage to call it. This is equal to use ${execution.parameters['branch name']}.

Trigger values

The trigger.buildInfo field in jenkins triggers will contain details about the jenkins execution. To access the version hash of the git commit in Jenkins, for example, we can ask for ${ trigger.buildInfo.scm[0]['sha1'] }.

Property files

Jenkins has a feature to archive its value into property files, this can be used as a way to provide values to Spinnaker. If we have env:

COMMITER_NAME=andrew
BRANCH_NAME=mybranch

And archived as build.propertiesor properties or whatever the name is, then ${ trigger.properties['COMMITER_NAME']}(if use Jenkins job as trigger) will resolve to andrew. ==Any stage that runs after will be able to access these parameters as normal context fields. ${ COMMITER_NAME } will resolve to andrew.== If you have multiple Jenkins jobs and they all provide same properties across diffrerent stages, you may have problem with conflict property names. A way around this is to reference the particular stage. ${ #stage( 'first jenkins job' )['context']['COMMITER_NAME'] }.

Manual Judgment choices

Judgment can be called over shortcut ${ #judgment( 'manual judgment stage name' )}

Hints on using expression

  1. The status attribute of a stage is actually an enum internally, not a string (although it shows up as a string in the JSON). To compare the status to a string, you’ll need to call .toString() on the result. For example: #stage('Validation')['status'].toString() == 'SUCCEEDED'
  2. To call static methods, use this syntax T(fully.qualified.class.name).methodName(). For example, to calculate the date 5 days from now, you can call: ${ T(java.time.LocalDate).now().plusDays(5).toString() }.
  3. You can set a default value within expressions by using ?:. For example, ${ parameters.region ?: 'us-east-1'}.
  4. To filter a list of stages by type, you can use the expression ${ execution.stages.?[ type == 'bake' ] }.
  5. Sometimes you want to enter a list of values as a parameter. For example, a list of regions or firewalls. A useful tip here is to ask the user to enter them as a list of comma separated values us-east-1,us-west-1,eu-west-1 and then use the split() method to make these into a map. Your expression would look like this parameters.regions.split(',').
  6. To reference another variable in an expression. You can do this by prepending #root in front of the expression name. In the following expression, I can set a value depending on the baseLabel field: ${ #root.baseLabel ?: 'noBaseLabelValue' }.

    Appendix

  7. execution : refers to the current pipeline execution.

  8. parameters : pipeline parameters.

  9. trigger : pipeline trigger

  10. scmInfo : this is a shortcut to the git details of either the trigger or the last executed jenkins stage. scmInfo.sha1 will give you the hash of the last build. scmInfo.branch will give you the branch name.

  11. deployedServerGroups : this is a reference to the server group that was created by the last deploy stage. Here is an example of the contents of a deployedServerGroups : [{account=prod,capacity={desired=1, max=1, min=1}, parentStage=23452655-c6de-4aac-b529-55e1357dfee7, region=us-west-1, ami=ami-575eb013, amiSuffix=201505190020, baseLabel=release, baseOs=trusty, package=rush, storeType=ebs, vmType=pv, serverGroup=rush-prestaging-v049}]

  12. To evaluate expression syntax and content:

    $ export PIPELINE_ID=01CD8CWFA97RG1WYSCAZY4RBX8
    $ curl -u username:password http://gate.spinnaker.ca:8084/pipelines/$PIPELINE_ID/evaluateExpression -H"Content-Type: text/plain" --data '${ deployedServerGroups[0].serverGroup }'
    

    output: {"result":"sample-frontend-canary-v016"} Note: This validation tool only works with .x type of calls, e.g --data '${ execution.stages[14].context.artifacts[0].metadata.tag }' will work, while --data '${ execution.stages[14]['context'].artifacts[0]['metadata'].['tag'] }' or ${ #stage('Stage name')} will not work.

Trigger a pipeline from webhook

Pipelines can be triggered via webhook call just like the way it works with Jenkins. You basically call Spinnaker gate’s url like this on Linux:

curl https://spinnaker-gate.test.com/webhooks/webhook/<source> -X POST -H "content-type: application/json" -d '{ "action": "create",  "parameters": { "username": "bottest"} }'

Note: if this runs on windows, we need to escape " inside {}, so it will become:

curl https://spinnaker-gate.test.com/webhooks/webhook/<source> -X POST -H "content-type: application/json" -d "{ \"action\": \"create\",  \"parameters\": { \"username\": \"bottest\"} }"

where source represents service endpoint, can be seen as name of your pipeline or a service group, action belongs to Payload Constraints in pipeline’s Automated Triggers config, can be used to identify what corresponding action should be taken in the pipeline or to be used to store link password, and parameters provides env value that can be used in a run.

In Spinnaker, you should put a conditional expression like this:

${trigger.payloadConstraints.action == 'create'} or ${trigger.payloadConstraints.action == 'get'}

this will make only those relevant stages run if the trigger info includes create or get, but if there’s a stage after this stage and it doesn’t have any condition expression, it will run from the very beginning of the pipeline if your traffic doesn’t contain the defined trigger info. Don’t use ${trigger.payloadConstraints.action == 'create' or 'get'}, this is wrong.

Send console output via notification

The console output of a stage can be sent via notification such as through Slack. What you need to do is to find out correct stage keys. If we have a stage called Copy membership from template user that runs after some other stages, then we can collect its log by using:

${  #stage( "Copy membership from template user").context.jobStatus.logs }

this is equivalent as using:

${  execution.stages[2].context.jobStatus.logs}

so to test if this is valid, we can use:

curl -u username:password http://gate.spinnaker.ca:8084/pipelines/$PIPELINE_ID/evaluateExpression -H"Content-Type: text/plain" --data '${  #stage( "Copy membership from template user").context.jobStatus.logs }'

there’s a limitation about this, the log key only has value after a stage finihes its run, which means if we put this in current stage’s notification tab, it will not be albe to find any info or giving warnings like Failed to evaluate [text] EL1007E: Property or field 'logs' cannot be found on null, to bypass it we can use an extra stage such as wait to send notification and recall this action stage from there.