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

Calling another vars step in inheritance context fails with MissingMethodException

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Major Major
    • workflow-cps-plugin
    • None
    • The latest and greatest, e.g. Jenkins (core) 2.115, workflow-cps 2.47

      Like in JENKINS-50731:

      Thanks to the fix of JENKINS-45982 I dared to continue with one of my original approaches using inheritance in shared Jenkins pipeline library classes, but stumbled over this problem.

      I am not sure anymore (updated) whether or not this is really a bug: because I had already "qualifiyied" calls to 'acme.<steps>' with the script context (or so) also before switching back to using inheritance (thanks to the fix of JENKINS-45982) SpecialBuild below was basically duplicating things of Build like this:

      class SpecialBuild {
      
        def script
        def acme
      
        SpecialBuild(def script) {
          this.script = script
          this.acme = script.acme
        }
      
        void build() {
          acme.build() { // ! Using the 'acme.build' step from 'vars/acme.groovy'
            script.node {
              acme.skipableStage("init") { // Direct usage of 'acme.skipableStage' step worked
                script.echo "acme 'Pipeline from SCM Script'"
              }
      
              acme.skipableStage('checkout') { // Direct usage of 'acme.skipableStage' step worked
                acme.svnCheckout('UpdateWithCleanUpdater')
              }
            }
          }
        }
        
      }
      

      How to reproduce?

      (I tried to include only minimal things and have not tested it after this "thinning out", so maybe there are small mistakes...)

      "Jenkinsfile"
      #!/usr/bin/env groovy
      
      echo 'Sandbox pipeline'
      
      @Library('acme-shared-library') _
      
      acme.specialBuild() {
      }
      
      Classes in shared pipeline library ('src' section)
      • 'src/Build.groovy'
        #!/usr/bin/env groovy
        
        class Build implements Serializable {
        
          def script
          def acme
        
          Build(def script) {
            this.script = Objects.requireNonNull(script, 'script must not be null')
            this.acme = script.acme
          }
        
          void build(Closure body) {
            try {
              body()
            } catch (e) {
            	acme.notifyFailing(...) // Direct access/usage of 'acme' works here
        
            	// While there would be no need to re-throw the exception to propagate the error (because the build result must be set
            	// to failure for email-ext anyhow before), re-throw it for e.g. script approval requests:
            	throw e
            }
          }
        
        }
        
      • 'src/SpecialBuild.groovy'
        #!/usr/bin/env groovy
        
        class SpecialBuild extends Build {
        
          SpecialBuild(def script) {
            super(script)
          }
        
          void build() {
            super.build() { // Mind this instead of 'acme.build()' like before using inheritance
              script.node {
                script.acme.skipableStage("init") { // This works
                  script.echo "acme 'Pipeline from SCM Script'"
                }
        
                acme.skipableStage('checkout') { // ! This does NOT work
                  acme.svnCheckout('UpdateWithCleanUpdater')
                }
              }
            }
          }
          
        }
        
      Corresponding steps in shared pipeline library ('vars' section)
      • 'vars/acme.groovy'
        #!/usr/bin/env groovy
        
        void svnCheckout(String workspaceUpdater = 'UpdateUpdater') {
          // ...
        }
        
        void build(Map args, Closure body) {
          // ...
          def build = new Build(this)
          build.build(body)
        }
        
        void skipableStage(String stageName, Closure body) {
          skipableStage(stageName, false, body)
        }
        void skipableStage(String stageName, def skipCondition, Closure body) {
          skipableStage(stageName, skipCondition, null, body)
        }
        void skipableStage(String stageName, Closure skipBody, Closure body) {
          skipableStage(stageName, false, skipBody, body)
        }
        void skipableStage(String stageName, def skipCondition, Closure skipBody, Closure body) {
          // ...
        }
        
        void specialBuild() {
          // ...
          def acmeSpecialBuild = new SpecialBuild(this)
          acmeSpecialBuild.build()
        }
        
        void notifyFailing(...) { ... }
        

      Current log/error

      Note that this slightly differs to what would expect as it is from the original problem (with less reduced shared library code).

      ...
      [Pipeline] echo
      Sandbox pipeline
      [Pipeline] node
      Running on Jenkins in /var/lib/jenkins/workspace/Sandbox/acme.buildDockerImage
      [Pipeline] {
      [Pipeline] stage
      [Pipeline] { (init)
      [Pipeline] echo
      acme 'Pipeline from SCM Script'
      [Pipeline] }
      [Pipeline] // stage
      [Pipeline] }
      [Pipeline] // node
      [Pipeline] echo
      Pipeline problem: Build failed (init) due to: "groovy.lang.MissingMethodException: No signature of method: static acme.skipableStage() is applicable for argument types: (java.lang.String, org.jenkinsci.plugins.workflow.cps.CpsClosure2) values: [checkout, org.jenkinsci.plugins.workflow.cps.CpsClosure2@4faabfc3]
      Possible solutions: skipableStage(java.lang.String, groovy.lang.Closure), skipableStage(java.lang.String, groovy.lang.Closure, groovy.lang.Closure), skipableStage(java.lang.String, java.lang.Object, groovy.lang.Closure), skipableStage(java.lang.String, java.lang.Object, groovy.lang.Closure, groovy.lang.Closure)" => Please check the "Console/Log Output" => Failure notification will be sent...
      ...
      [Pipeline] // stage
      [Pipeline] End of Pipeline
      hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: static acme.skipableStage() is applicable for argument types: (java.lang.String, org.jenkinsci.plugins.workflow.cps.CpsClosure2) values: [checkout, org.jenkinsci.plugins.workflow.cps.CpsClosure2@4faabfc3]
      Possible solutions: skipableStage(java.lang.String, groovy.lang.Closure), skipableStage(java.lang.String, groovy.lang.Closure, groovy.lang.Closure), skipableStage(java.lang.String, java.lang.Object, groovy.lang.Closure), skipableStage(java.lang.String, java.lang.Object, groovy.lang.Closure, groovy.lang.Closure)
      	at groovy.lang.MetaClassImpl.invokeStaticMissingMethod(MetaClassImpl.java:1501)
      	at groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1487)
      	at org.codehaus.groovy.runtime.callsite.StaticMetaClassSite.call(StaticMetaClassSite.java:53)
      	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
      	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
      	at com.cloudbees.groovy.cps.sandbox.DefaultInvoker.methodCall(DefaultInvoker.java:20)
      	at SpecialBuild.build(file:/var/lib/jenkins/jobs/Sandbox/jobs/acme.build/builds/12/libs/acme-shared-library/src/SpecialBuild.groovy:??)
      	at ___cps.transform___(Native Method)
      	at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:57)
      	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:109)
      	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:82)
      	at sun.reflect.GeneratedMethodAccessor206.invoke(Unknown Source)
      	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      	at java.lang.reflect.Method.invoke(Method.java:498)
      	at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
      	at com.cloudbees.groovy.cps.impl.ClosureBlock.eval(ClosureBlock.java:46)
      	at com.cloudbees.groovy.cps.Next.step(Next.java:83)
      	at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:174)
      	at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:163)
      	at org.codehaus.groovy.runtime.GroovyCategorySupport$ThreadCategoryInfo.use(GroovyCategorySupport.java:122)
      	at org.codehaus.groovy.runtime.GroovyCategorySupport.use(GroovyCategorySupport.java:261)
      	at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:163)
      	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.access$101(SandboxContinuable.java:34)
      	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.lambda$run0$0(SandboxContinuable.java:59)
      	at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox.runInSandbox(GroovySandbox.java:108)
      	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.run0(SandboxContinuable.java:58)
      	at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:174)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:332)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$200(CpsThreadGroup.java:83)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:244)
      	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:232)
      	at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:64)
      	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
      	at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:131)
      	at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28)
      	at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:59)
      	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
      	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
      	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
      	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
      	at java.lang.Thread.run(Thread.java:748)
      Finished: FAILURE
      

      Workaround that works:

      • Instead of calling acme.skipableStage(...) in SpecialBuild.build() => call script.acme.skipableStage(...)

          [JENKINS-50736] Calling another vars step in inheritance context fails with MissingMethodException

            Unassigned Unassigned
            reinholdfuereder Reinhold Füreder
            Votes:
            3 Vote for this issue
            Watchers:
            7 Start watching this issue

              Created:
              Updated: