Orso Labs

Temporary override of Concourse pipeline parameters

WARNING: This makes sense only if each feature branch has its own short-lived pipeline. If on the other hand the project has only one permanent pipeline, for the master branch, the approach explained here is strongly discouraged because it bypasses the git repository, destroying the notion of configuration as code.

For example, we are debugging a pipeline failure and we would like to temporarily change the build flags. We would like to avoid to temporarily commit these changes in the feature branch, to reduce the risk to merge these changes to the master branch by mistake.

We can obtain this using together the following features:

Tasks: inline or nested

As a reminder, there are two ways to define a task in Concourse:

This article shows how to enable custom parameters for both types.

The default values

We store the default values in a separate key/value pairs YAML file, settings.yml, that we pass to fly with the --load-vars-from flag. For this example, its contents are:

greetings: default greeting
fruits: default fruits

The simplest possible pipeline

Assuming that the pipeline file is called nested-param-pipeline.yml, the simplest possible has only one task, inline:

---
jobs:
- name: hello
  plan:
  - task: inline
    config:
      platform: linux
      image_resource:
        type: docker-image
        source: {repository: alpine}
      run:
        path: /bin/sh
        args:
          - -c
          - |
            set -o errexit
            echo "Running from inline task"
            echo "FRUITS: $FRUITS"
            echo "GREETINGS: $GREETINGS"            
    params:
      FRUITS: ((fruits))
      GREETINGS: ((greetings))

If we want to use the default values for ((fruits)) and ((greetings)), we set the pipeline as follows:

fly -t vm set-pipeline -p nested-param -c nested-param-pipeline.yml \
    -l settings.yml

If on the other hand we want to partially override, we keep passing settings.yml (otherwhise we would loose the value for ((fruits))) and we override ((greetings)) as follows:

fly -t vm set-pipeline -p nested-param -c nested-param-pipeline.yml \
    -l settings.yml \
    -v "greetings=custom greetings"

A more realistic example

A more realistic example has a nested task. We just copy and paste the same task configuration of the inline example and put it in a separate file, nested-param-task.yml:

---
platform: linux
image_resource:
  type: docker-image
  source: {repository: alpine}
run:
  path: /bin/sh
  args:
    - -c
    - |
      set -o errexit
      echo "Running from nested task"
      echo "FRUITS: $FRUITS"
      echo "GREETINGS: $GREETINGS"      
params:
  FRUITS: ((fruits))
  GREETINGS: ((greetings))

The gotcha is that in the pipeline we have to repeat the params stanza, otherwhise they are not propagated. Thinking about it, it makes sense, since fly changes only the content of the pipeline file itself, not of the tasks files referred from it.

We also have to introduce the git resource where we store the task file:

---
resources:
- name: concourse-pipelines.git
  type: git
  source:
    uri: https://github.com/marco-m/concourse-pipelines.git
    branch: master

jobs:
- name: hello
  plan:
  - get: concourse-pipelines.git
    trigger: false
  - task: inline
    config:
      platform: linux
      image_resource:
        type: docker-image
        source: {repository: alpine}
      run:
        path: /bin/sh
        args:
          - -c
          - |
            set -o errexit
            echo "Running from inline task"
            echo "FRUITS: $FRUITS"
            echo "GREETINGS: $GREETINGS"            
    params:
      FRUITS: ((fruits))
      GREETINGS: ((greetings))
  - task: nested
    file: concourse-pipelines.git/nested-param/nested-param-task.yml
    params:
      FRUITS: ((fruits))
      GREETINGS: ((greetings))

Final run with default value

The sequence

fly -t vm set-pipeline -p nested-param -c nested-param-pipeline.yml \
    -l settings.yml
fly -t vm unpause-pipeline -p nested-param
fly -t vm trigger-job -j nested-param/hello -w

Will print:

Running from inline task
FRUITS: default fruits
GREETINGS: default fruits

Running from nested task
FRUITS: default fruits
GREETINGS: default fruits

Final run with custom temporary value

The sequence

fly -t vm set-pipeline -p nested-param -c nested-param-pipeline.yml \
    -l settings.yml \
    -v "greetings=custom greetings"
fly -t vm unpause-pipeline -p nested-param
fly -t vm trigger-job -j nested-param/hello -w

Will print:

Running from inline task
FRUITS: default fruits
GREETINGS: custom greetings

Running from nested task
FRUITS: default fruits
GREETINGS: custom greetings

How long will the temporary override last ?

Once we override the value of greetings, for how long will the override last ?

It will last until a new fly set-pipeline with the default values will be called, so this temporary can actually last a long time!

Note that the override will survive also if we push a commit that changes the pipeline file! The reason is not specific to the technique explained here, it is related to the fact that each change to a pipeline file must be declared to Concourse via fly set-pipeline. This can be confusing, since on the other hand, changes to task or script files will be picked up without intervention. Said in another way, Concourse currently (January 2019) doesn’t support loading the pipeline file from a repository, as opposed to, for example, .travis.yml.

To know more about this topic, have a look at Bootstrapping Concourse pipelines.

Update

Concourse 6.5.0 (2020-08-21) introduced the experimental set_pipeline step in the pipeline configuration, so things can be simplified.

Source code

The full source code is available in the concourse-pipelines repository.

#concourse #pipelines #devops #open source