jenkinsci / lockable-resources-plugin

Lock resources against concurrent use
https://plugins.jenkins.io/lockable-resources
MIT License
87 stars 182 forks source link

Declarative Pipelines: Resource locked even if stage is skipped when trying to lock resource across multiple stages #450

Closed g3n35i5 closed 1 year ago

g3n35i5 commented 1 year ago

Jenkins and plugins versions report

Environment ```text Jenkins: 2.375.2 OS: Linux - 5.10.0-14-amd64 --- ace-editor:1.1 analysis-model-api:10.22.0 ansicolor:1.0.2 ant:481.v7b_09e538fcca antisamy-markup-formatter:155.v795fb_8702324 apache-httpcomponents-client-4-api:4.5.13-138.v4e7d9a_7b_a_e61 artifactory:3.18.0 authentication-tokens:1.4 azure-ad:313.v14b_f37ff114d azure-commons:1.1.3 azure-sdk:118.v43f74dd9ca_dc badge:1.9.1 basic-branch-build-strategies:71.vc1421f89888e bitbucket-push-and-pull-request:2.8.3 blueocean:1.27.1 blueocean-autofavorite:1.2.5 blueocean-bitbucket-pipeline:1.27.1 blueocean-commons:1.27.1 blueocean-config:1.27.1 blueocean-core-js:1.27.1 blueocean-dashboard:1.27.1 blueocean-display-url:2.4.1 blueocean-events:1.27.1 blueocean-git-pipeline:1.27.1 blueocean-github-pipeline:1.27.1 blueocean-i18n:1.27.1 blueocean-jira:1.27.1 blueocean-jwt:1.27.1 blueocean-personalization:1.27.1 blueocean-pipeline-api-impl:1.27.1 blueocean-pipeline-editor:1.27.1 blueocean-pipeline-scm-api:1.27.1 blueocean-rest:1.27.1 blueocean-rest-impl:1.27.1 blueocean-web:1.27.1 bootstrap4-api:4.6.0-5 bootstrap5-api:5.2.1-3 bouncycastle-api:2.27 branch-api:2.1071.v1a_188a_562481 build-failure-analyzer:2.4.0 build-monitor-plugin:1.13+build.202205140447 build-name-setter:2.2.0 build-timeout:1.28 caffeine-api:2.9.3-65.v6a_47d0f4d1fe cccc:0.6 checks-api:1.8.1 cloudbees-bitbucket-branch-source:791.vb_eea_a_476405b cloudbees-folder:6.800.v71307ca_b_986b cobertura:1.17 code-coverage-api:3.4.1 command-launcher:90.v669d7ccb_7c31 commons-lang3-api:3.12.0-36.vd97de6465d5b_ commons-text-api:1.10.0-27.vb_fa_3896786a_7 config-file-provider:3.11.1 configuration-as-code:1569.vb_72405b_80249 configuration-as-code-groovy:1.1 copyartifact:1.48 cppcheck:1.26 credentials:1214.v1de940103927 credentials-binding:523.vd859a_4b_122e6 dark-theme:262.v0202a_4c8fb_6a dashboard-view:2.466.vdfefd95a_b_f8d data-tables-api:1.12.1-4 disk-usage:0.28 display-url-api:2.3.7 docker-commons:1.21 docker-custom-build-environment:1.7.3 docker-workflow:563.vd5d2e5c4007f doxygen:178.v6ea_ef5f7dfdb dtkit-api:3.0.2 durable-task:503.v57154d18d478 echarts-api:5.4.0-1 email-ext:2.93 embeddable-build-status:312.vf2de01b_051d0 external-monitor-job:203.v683c09d993b_9 favorite:2.4.1 file-operations:1.11 folder-properties:1.2.1 font-awesome-api:6.2.1-1 forensics-api:1.17.0 git:5.0.0 git-client:4.1.0 git-forensics:1.11.0 git-server:99.va_0826a_b_cdfa_d github:1.36.1 github-api:1.303-400.v35c2d8258028 github-branch-source:1701.v00cc8184df93 github-checks:1.0.19 global-build-stats:244.v27c8a_2e50a_34 gradle:2.2 handlebars:3.0.8 handy-uri-templates-2-api:2.1.8-22.v77d5b_75e6953 htmlpublisher:1.31 htmlresource:1.02 http_request:1.16 image-gallery:2.0.2 influxdb:3.4 instance-identity:142.v04572ca_5b_265 ionicons-api:31.v4757b_6987003 ivy:2.4 jackson2-api:2.14.1-313.v504cdd45c18b jacoco:3.3.2 jakarta-activation-api:2.0.1-2 jakarta-mail-api:2.0.1-2 javadoc:226.v71211feb_e7e9 javax-activation-api:1.2.0-5 javax-mail-api:1.6.2-8 jaxb:2.3.7-1 jdk-tool:63.v62d2fd4b_4793 jenkins-design-language:1.27.1 jersey2-api:2.38-1 jfrog:1.0.5 jira:3.8 jira-steps:2.0.141.vd0c6e6dc83f0 jjwt-api:0.11.5-77.v646c772fddb_0 jnr-posix-api:3.1.16-1 job-dsl:1.81 jquery:1.12.4-1 jquery-detached:1.2.1 jquery3-api:3.6.1-2 jsch:0.1.55.61.va_e9ee26616e7 junit:1166.va_436e268e972 ldap:659.v8ca_b_a_fe79fa_d locale:226.v008e1b_58cb_b_0 lockable-resources:1113.vf66761f17f1e mailer:438.v02c7f0a_12fa_4 mapdb-api:1.0.9-28.vf251ce40855d matrix-auth:3.1.6 matrix-project:785.v06b_7f47b_c631 maven-plugin:3.20 mercurial:1260.vdfb_723cdcc81 metrics:4.2.13-420.vea_2f17932dd6 mina-sshd-api-common:2.9.2-50.va_0e1f42659a_a mina-sshd-api-core:2.9.2-50.va_0e1f42659a_a momentjs:1.1.1 monitoring:1.91.0 okhttp-api:4.9.3-108.v0feda04578cf pam-auth:1.10 pipeline-build-step:2.18 pipeline-github-lib:38.v445716ea_edda_ pipeline-graph-analysis:202.va_d268e64deb_3 pipeline-groovy-lib:629.vb_5627b_ee2104 pipeline-input-step:466.v6d0a_5df34f81 pipeline-milestone-step:111.v449306f708b_7 pipeline-model-api:2.2118.v31fd5b_9944b_5 pipeline-model-definition:2.2118.v31fd5b_9944b_5 pipeline-model-extensions:2.2118.v31fd5b_9944b_5 pipeline-rest-api:2.29 pipeline-stage-step:296.v5f6908f017a_5 pipeline-stage-tags-metadata:2.2118.v31fd5b_9944b_5 pipeline-stage-view:2.29 pipeline-utility-steps:2.15.0 plain-credentials:143.v1b_df8b_d3b_e48 plot:2.1.12 plugin-util-api:2.20.0 popper-api:1.16.1-3 popper2-api:2.11.6-2 prism-api:1.29.0-2 proxmox:0.7.1 pubsub-light:1.17 resource-disposer:0.20 role-strategy:587.v2872c41fa_e51 scm-api:631.v9143df5b_e4a_a script-security:1228.vd93135a_2fb_25 sidebar-link:2.2.1 simple-theme-plugin:136.v23a_15f86c53d snakeyaml-api:1.33-90.v80dcb_3814d35 sonar:2.15 sse-gateway:1.26 ssh-agent:295.v9ca_a_1c7cc3a_a_ ssh-credentials:305.v8f4381501156 ssh-slaves:2.854.v7fd446b_337c9 sshd:3.275.v9e17c10f2571 structs:324.va_f5d6774f3a_d theme-manager:1.6 throttle-concurrents:2.10 timestamper:1.21 token-macro:321.vd7cc1f2a_52c8 trilead-api:2.84.v72119de229b_7 valgrind:0.28 variant:59.vf075fe829ccb warnings-ng:9.22.0 windows-slaves:1.8.1 workflow-aggregator:590.v6a_d052e5a_a_b_5 workflow-api:1208.v0cc7c6e0da_9e workflow-basic-steps:994.vd57e3ca_46d24 workflow-cps:3606.v0b_d8b_e512dcf workflow-cps-global-lib:609.vd95673f149b_b workflow-durable-task-step:1223.v7f1a_98a_8863e workflow-job:1268.v6eb_e2ee1a_85a workflow-multibranch:716.vc692a_e52371b_ workflow-scm-step:400.v6b_89a_1317c9a_ workflow-step-api:639.v6eca_cd8c04a_a_ workflow-support:839.v35e2736cfd5c working-hours:1.1 ws-cleanup:0.44 xray-connector:2.6.1 xunit:3.1.2 ```

What Operating System are you using (both controller, and any agents involved in the problem)?

Linux, Jenkins main node and plain linux build agent (But doesn't matter in my opinion)

Reproduction steps

I want to lock a resource across multiple stages according to the example in the documentation.

Run a job with the following Jenkinsfile (And make sure you don't have a lockable resource with the label DOES_NOT_EXIST :laughing:)

pipeline {
  agent any
  stages {
    stage('Parent Stage') {
      when {
        expression { false }
      }
      options {
        lock(resource: null, label: 'DOES_NOT_EXIST', variable: 'LOCKED_RESOURCE', quantity: 1)
      }
      stages {
        stage('First Stage') {
          steps {
            script {
              echo "Hello from first stage"
          }
          }
        }
        stage('Second Stage') {
          steps {
            script {
              echo "Hello from second stage"
            }
          }
        }
      }
    }
  }
}

Expected Results

The stage Parent Stage is marked as skipped so I'd expect that the options section is skipped, too.

Actual Results

The pipeline fails

java.lang.IllegalArgumentException: The resource label does not exist: DOES_NOT_EXIST.

Anything else?

I'm not sure whether this is an actual bug.

I can imagine that the fact that the options section is executed even though the stage should be skipped is the desired behavior of Jenkins. However, are there any other methods to lock a resource across multiple stages in declarative pipelines?

mPokornyETM commented 1 year ago

I can imagine that the fact that the options section is executed even though the stage should be skipped is the desired behavior of Jenkins.

Yes it looks soo.

However, are there any other methods to lock a resource across multiple stages in declarative pipelines?

Like here but you make the stages inside the lock step. https://github.com/jenkinsci/lockable-resources-plugin/blob/master/src/doc/examples/scripted-vs-declarative-pipeline.md I hope it works. I do not use declarative pipeline because scripted pipeline has more power 😇

mPokornyETM commented 1 year ago

@g3n35i5 Please. Might you try, if my proposal works? When you have functional example, can you try to extend the documentation. Maybe a one more automatic test will be fine. Currently we hav no tests for declarative pipelines.

g3n35i5 commented 1 year ago

Unfortunately, this doesn't work. What I've tried so far:

Options in stages

pipeline {
  agent any
  stages {
    stage('Parent Stage') {
      when {
        expression { false }
      }
      stages {
        options {
          lock(resource: null, label: 'DOES_NOT_EXIST', variable: 'LOCKED_RESOURCE', quantity: 1)
        }
        stage('First Stage') {
          steps {
            script {
              echo "Hello from first stage"
            }
          }
        }
        stage('Second Stage') {
          steps {
            script {
              echo "Hello from second stage"
            }
          }
        }
      }
    }
  }
}

Stages in lock

pipeline {
  agent any
  stages {
    stage('Parent Stage') {
      when {
        expression { false }
      }
      stages {
        lock(resource: null, label: 'DOES_NOT_EXIST', variable: 'LOCKED_RESOURCE', quantity: 1) {
          stage('First Stage') {
            steps {
              script {
                echo "Hello from first stage"
              }
            }
          }
          stage('Second Stage') {
            steps {
              script {
                echo "Hello from second stage"
              }
            }
          }
        }
      }
    }
  }
}

Both attempts are not valid syntax. Do you have another idea or did I misunderstand your instructions?

mPokornyETM commented 1 year ago

Unfortunately, this doesn't work. What I've tried so far:

Options in stages

pipeline {
  agent any
  stages {
    stage('Parent Stage') {
      when {
        expression { false }
      }
      stages {
        options {
          lock(resource: null, label: 'DOES_NOT_EXIST', variable: 'LOCKED_RESOURCE', quantity: 1)
        }
        stage('First Stage') {
          steps {
            script {
              echo "Hello from first stage"
            }
          }
        }
        stage('Second Stage') {
          steps {
            script {
              echo "Hello from second stage"
            }
          }
        }
      }
    }
  }
}

Stages in lock

pipeline {
  agent any
  stages {
    stage('Parent Stage') {
      when {
        expression { false }
      }
      stages {
        lock(resource: null, label: 'DOES_NOT_EXIST', variable: 'LOCKED_RESOURCE', quantity: 1) {
          stage('First Stage') {
            steps {
              script {
                echo "Hello from first stage"
              }
            }
          }
          stage('Second Stage') {
            steps {
              script {
                echo "Hello from second stage"
              }
            }
          }
        }
      }
    }
  }
}

Both attempts are not valid syntax. Do you have another idea or did I misunderstand your instructions?

According to this documentation https://www.jenkins.io/doc/book/pipeline/syntax/#when you shall use 'steps' instead of 'stages' after 'when'. I thing this shall works:

pipeline {
  agent any
  stages {
    stage('Parent Stage') {
      when {
        expression { false }
      }
      steps {
        lock(resource: null, label: 'DOES_NOT_EXIST', variable: 'LOCKED_RESOURCE', quantity: 1) {
          stage('First Stage') {
            steps {
              script {
                echo "Hello from first stage"
              }
            }
          }
          stage('Second Stage') {
            steps {
              script {
                echo "Hello from second stage"
              }
            }
          }
        }
      }
    }
  }
}

But as you can see it is really hard to make an easy logic in parametrized pipelines. Therefore I use scripted pipelines (pure Groovy)

mPokornyETM commented 1 year ago

Note for developer, check if the example works and bring it into documentation. Also automated test with scripted pipeline is good idea.

g3n35i5 commented 1 year ago

According to this documentation https://www.jenkins.io/doc/book/pipeline/syntax/#when you shall use 'steps' instead of 'stages' after 'when'. I thing this shall works:

pipeline {
  agent any
  stages {
    stage('Parent Stage') {
      when {
        expression { false }
      }
      steps {
        lock(resource: null, label: 'DOES_NOT_EXIST', variable: 'LOCKED_RESOURCE', quantity: 1) {
          stage('First Stage') {
            steps {
              script {
                echo "Hello from first stage"
              }
            }
          }
          stage('Second Stage') {
            steps {
              script {
                echo "Hello from second stage"
              }
            }
          }
        }
      }
    }
  }
}

But as you can see it is really hard to make an easy logic in parametrized pipelines. Therefore I use scripted pipelines (pure Groovy)

Unfortunately, this doesn't work either:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 10: Invalid step "stage" used - not allowed in this context - The stage step cannot be used in Declarative Pipelines @ line 10, column 11.
             stage('First Stage') {
             ^

WorkflowScript: 17: Invalid step "stage" used - not allowed in this context - The stage step cannot be used in Declarative Pipelines @ line 17, column 11.
             stage('Second Stage') {
             ^

2 errors
mPokornyETM commented 1 year ago

Ok, let me see. I will try it later in my environment

PayBas commented 1 year ago

@g3n35i5 have you looked into the beforeOptions flag: https://www.jenkins.io/doc/book/pipeline/syntax/#when

g3n35i5 commented 1 year ago

@g3n35i5 have you looked into the beforeOptions flag: https://www.jenkins.io/doc/book/pipeline/syntax/#when

You're my hero! Thank you very much, that did the trick. I wasn't aware of this setting.