Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-51865

Stage locks are created for skipped stages in declarative pipeline

      Given the following declarative pipeline:

      pipeline {
        stages {
          stage('Example stage') {
            when { expression { false } }
            options { lock resource: 'example resource' }
            steps { // ... }
          }
        }
      }
      

      Even though 'example stage' is skipped due to the when conditional, a lockable resource 'example resource' is created (if it doesn't already exist) and a lock is acquired on it. I think this is counter intuitive. The consequence is also really bad – a skipped stage might actually make the whole build hang (possibly for a long time) because it needs to acquire a lock on a busy resource (typically used by another build).

          [JENKINS-51865] Stage locks are created for skipped stages in declarative pipeline

          Ok, got it. So this is a feature request. I was really happy when the 'beforeAgent' option was introduced to the when block. Guess I'm looking for a 'beforeLock' option too, then.

          The lock is released as it should. The problem (or rather my problem) is that the lock is required at all. My current workaround is to rename the lock resource when the stage should be skipped, but it makes the code a little messy.

          Thomas Johansen added a comment - Ok, got it. So this is a feature request. I was really happy when the 'beforeAgent' option was introduced to the when block. Guess I'm looking for a 'beforeLock' option too, then. The lock is released as it should. The problem (or rather my  problem) is that the lock is required at all. My current workaround is to rename the lock resource when the stage should be skipped, but it makes the code a little messy.

          Andrew Bayer added a comment -

          So the only way we could address this directly would be to add a beforeOptions flag to when - but I'm not sure that's actually worth doing. You can always put lock or timeout (and any other block-scoped options) in your steps instead. The more complexity we add to when flags (since we're already adding beforeInput in JENKINS-50880), the hairier the code gets, and this case is one that can largely be worked around (unlike with agent and input). The workaround would create scenarios where, say, timeout or lock wouldn't be in place for acquiring the agent or waiting for input, but it would still work fine for everything in steps. Does that sound reasonable to you?

          Andrew Bayer added a comment - So the only way we could address this directly would be to add a beforeOptions flag to when - but I'm not sure that's actually worth doing. You can always put lock or timeout (and any other block-scoped options ) in your steps instead. The more complexity we add to when flags (since we're already adding beforeInput in JENKINS-50880 ), the hairier the code gets, and this case is one that can largely be worked around (unlike with agent and input ). The workaround would create scenarios where, say, timeout or lock wouldn't be in place for acquiring the agent or waiting for input , but it would still work fine for everything in steps . Does that sound reasonable to you?

          abayer perhaps we could work around this by using an expression in the quantity of the lock, e.g.

          pipeline {
            stages {
              stage('Example stage') {
                when { expression { false } }
                options { lock resource: 'example resource', quantity: expr ? 1 : 0 }
                steps { // ... }
              }
            }
          }
          

          What I do not know is if declarative supports numerical expressions

          Stephen Connolly added a comment - abayer perhaps we could work around this by using an expression in the quantity of the lock, e.g. pipeline { stages { stage( 'Example stage' ) { when { expression { false } } options { lock resource: 'example resource' , quantity: expr ? 1 : 0 } steps { // ... } } } } What I do not know is if declarative supports numerical expressions

          so `quantity:0` doens't work as that is the default value and seems to indicate "grab all".

          I'm now trying:

          pipeline {
            stages {
              stage('Example stage') {
                when { branch 'master' }
                options { lock resource: "${BRANCH_NAME=='master' ? 'example resource':'dummy'}" }
                steps { // ... }
              }
            }
          }
          

          We'll see if that is an acceptable workaround for my use case

          Stephen Connolly added a comment - so `quantity:0` doens't work as that is the default value and seems to indicate "grab all". I'm now trying: pipeline { stages { stage( 'Example stage' ) { when { branch 'master' } options { lock resource: "${BRANCH_NAME== 'master' ? 'example resource' : 'dummy' }" } steps { // ... } } } } We'll see if that is an acceptable workaround for my use case

          Ok that is an acceptable workaround for my use case

          Stephen Connolly added a comment - Ok that is an acceptable workaround for my use case

          Falko Modler added a comment -

          stephenconnolly thanks for sharing your workaround.

          Unfortunately this won't work in case the criteria to check of is calculcated in a previous stage, e.g.:

          pipeline {
            stages {
              stage('Calculate criteria') {
                steps {
                  script {
                    someCriteria = true
                  }
                }
              }
              stage('Example stage') {
                when {
                  expression {
                    return someCriteria
                  }
                }
                options { lock resource: "${someCriteria ? 'example resource':'dummy'}" }
                steps { // ... }
              }
            }
          }
          

          This will fail in options with:

          groovy.lang.MissingPropertyException: No such property: someCriteria for class: groovy.lang.Binding

          Falko Modler added a comment - stephenconnolly thanks for sharing your workaround. Unfortunately this won't work in case the criteria to check of is calculcated in a previous stage, e.g.: pipeline { stages { stage( 'Calculate criteria' ) { steps { script { someCriteria = true } } } stage( 'Example stage' ) { when { expression { return someCriteria } } options { lock resource: "${someCriteria ? 'example resource' : 'dummy' }" } steps { // ... } } } } This will fail in options with: groovy.lang.MissingPropertyException: No such property: someCriteria for class: groovy.lang.Binding

          D Pasto added a comment -

          It also doesn’t work because either you hardcore “dummy” and potentially block on it or you randomize and create all sorts of crap in your Jenkins config

          D Pasto added a comment - It also doesn’t work because either you hardcore “dummy” and potentially block on it or you randomize and create all sorts of crap in your Jenkins config

          Falko Modler added a comment - - edited

          wgc123

          It also doesn’t work because either you hardcore “dummy” and potentially block on it

          Those potential blocks/locks are very short-lived. You could also define a pool of multiple "dummy" resources to further reduce the (already very small) impact.

          So this partial workaround is better than nothing.

          abayer

          You can always put lock or timeout (and any other block-scoped options) in your steps instead.

          Unfortunately, this is no solution/workaround for post blocks. E.g. you lock some external resource/server and in post you want to collect the server's logfiles (regardless of the build status).
          So IMHO, beforeOptions is still needed.

          Falko Modler added a comment - - edited wgc123 It also doesn’t work because either you hardcore “dummy” and potentially block on it Those potential blocks/locks are very short-lived. You could also define a pool of multiple "dummy" resources to further reduce the (already very small) impact. So this partial workaround is better than nothing. abayer You can always put lock or timeout (and any other block-scoped options) in your steps instead. Unfortunately, this is no solution/workaround for post blocks. E.g. you lock some external resource/server and in post you want to collect the server's logfiles (regardless of the build status). So IMHO, beforeOptions is still needed.

          Falko Modler added a comment -

          Falko Modler added a comment - I created a PR for this: https://github.com/jenkinsci/pipeline-model-definition-plugin/pull/356

          Falko Modler added a comment - - edited

          bitwiseman I have just resolved this ticket because the fix is included in 1.4.0. I am not familiar with the overall ticket workflow, though.
          So I leave it up to you (or abayer) to close this issue.

          Falko Modler added a comment - - edited bitwiseman I have just resolved this ticket because the fix is included in 1.4.0. I am not familiar with the overall ticket workflow, though. So I leave it up to you (or abayer ) to close this issue.

            famod Falko Modler
            thxmasj Thomas Johansen
            Votes:
            5 Vote for this issue
            Watchers:
            11 Start watching this issue

              Created:
              Updated:
              Resolved: