Jenkins Pipeline 中的“脚本"块外的动态并行阶段 [英] Dynamic Parallel stages in Jenkins Pipeline outside 'script' block

查看:16
本文介绍了Jenkins Pipeline 中的“脚本"块外的动态并行阶段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试动态构建并行阶段,如 here这里.具体来说,我试图在管道范围之外定义的函数中执行此操作,例如:

I am trying to construct parallel stages dynamically, as demonstrated here and here. Specifically, I am trying to do this in a function defined outside the scope of the pipeline, e.g.:

pipeline{
  stages{
    stage('CI'){
      steps{
        doDynamicParallelSteps()
      }
    }
  }
}

def doDynamicParallelSteps(){
  tests = [:]
  for (f in findFiles(glob: '**/html/*.html')) {
    tests["${f}"] = {
      node {
        stage("${f}") {
          echo '${f}'
        }
      }
    }
  }
  parallel tests
}

问题是,这似乎仅在动态并行生成代码位于管道的 steps{} 块内的脚本块内时才有效(如第一个源代码所示).

The problem is, it seems this only works when the dynamic parallel generation code is inside a script{} block within the steps{} block of the pipeline (as seen in the first source).

当运行类似于上面的代码片段时,我在 jenkins 中看到了这个错误:

When running something similar to the code snippet above, I see this error in jenkins:

hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: java.lang.String.call() is applicable for argument types: (java.lang.String, org.jenkinsci.plugins.workflow.cps.CpsClosure2) values: [teststage, org.jenkinsci.plugins.workflow.cps.CpsClosure2@2e1b48b4]
Possible solutions: wait(), any(), trim(), size(), next(), size()
    at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onMethodCall(SandboxInterceptor.java:153)
    at org.kohsuke.groovy.sandbox.impl.Checker$1.call(Checker.java:155)
    at org.kohsuke.groovy.sandbox.impl.Checker.checkedCall(Checker.java:159)
    at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.methodCall(SandboxInvoker.java:17)
    at WorkflowScript.parallelHandler(WorkflowScript:1383)
    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.GeneratedMethodAccessor110.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:182)
    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

有什么方法可以按照我在初始代码片段中显示的方式将它定义为一个函数,还是我坚持在我的管道定义中有大量脚本{} 块?

Is there any way to have it defined as a function in the way I showed in the initial code snippet, or am I stuck with having a ton of script{} blocks in my pipeline definition?

推荐答案

声明式管道不允许您将 Groovy 代码放入 steps {} 块中 - 它需要在这个地方有一个有效的 Jenkins 管道步骤.这就是引入 script {} 块的原因,该块可以放在 steps {} 块中以执行一些 Groovy 代码.

The declarative pipeline does not allow you to put Groovy code inside steps {} block - it expects a valid Jenkins pipeline step in this place. This is why script {} block got introduced that can be put inside the steps {} block to execute some Groovy code.

如果您需要灵活性和中立的语法,那么您可以改用脚本化管道.在这里,您可以将 Groovy 代码与现有管道步骤混合,几乎没有任何限制.

If you need flexibility and non-opinionated syntax then you might use scripted pipeline instead. Here you can mix Groovy code with existing pipeline steps with almost no limitations.

Jenkins 文档简要解释了这两种方法之间的区别以及它们存在的原因:

Jenkins documentation explains shortly the difference between both approaches and why they exist:

当 Jenkins Pipeline 第一次创建时,Groovy 被选为基础.Jenkins 长期以来一直提供嵌入式 Groovy 引擎,为管理员和用户提供高级脚本功能.此外,Jenkins Pipeline 的实现者发现 Groovy 是构建现在称为脚本化管道"DSL 的坚实基础.[2].

When Jenkins Pipeline was first created, Groovy was selected as the foundation. Jenkins has long shipped with an embedded Groovy engine to provide advanced scripting capabilities for admins and users alike. Additionally, the implementors of Jenkins Pipeline found Groovy to be a solid foundation upon which to build what is now referred to as the "Scripted Pipeline" DSL. [2].

由于它是一个功能齐全的编程环境,Scripted Pipeline 为 Jenkins 用户提供了极大的灵活性和可扩展性.Groovy 学习曲线通常不适合给定团队的所有成员,因此创建声明式管道是为了为创作 Jenkins 管道提供更简单、更有主见的语法.

As it is a fully featured programming environment, Scripted Pipeline offers a tremendous amount of flexibility and extensibility to Jenkins users. The Groovy learning-curve isn’t typically desirable for all members of a given team, so Declarative Pipeline was created to offer a simpler and more opinionated syntax for authoring Jenkins Pipeline.

两者本质上是相同的管道子系统.它们都是管道即代码"的持久实现.他们都能够使用内置于 Pipeline 或由插件提供的步骤.两者都可以使用共享库

Both are fundamentally the same Pipeline sub-system underneath. They are both durable implementations of "Pipeline as code." They are both able to use steps built into Pipeline or provided by plugins. Both are able to utilize Shared Libraries

您在脚本化管道中的示例可能如下所示:

Your example in scripted pipeline may look like this:

node {
  stage('CI') {
    doDynamicParallelSteps()
  }
}

def doDynamicParallelSteps(){
  tests = [:]
  for (f in findFiles(glob: '**/html/*.html')) {
    tests["${f}"] = {
      node {
        stage("${f}") {
          echo '${f}'
        }
      }
    }
  }
  parallel tests
}

在步骤中带有 script {} 块的声明性管道如下所示:

And declarative pipeline with script {} block in steps would look like this:

pipeline{
  agent any
  stages{
    stage('CI'){
      steps{
        script {
            doDynamicParallelSteps()
        }
      }
    }
  }
}

def doDynamicParallelSteps(){
  tests = [:]
  for (f in findFiles(glob: '**/html/*.html')) {
    tests["${f}"] = {
      node {
        stage("${f}") {
          echo '${f}'
        }
      }
    }
  }
  parallel tests
}

这篇关于Jenkins Pipeline 中的“脚本"块外的动态并行阶段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆