Динамические параллельные этапы в конвейере Jenkins за пределами блока 'script' - PullRequest
0 голосов
/ 10 ноября 2018

Я пытаюсь построить параллельные этапы динамически, как показано здесь и здесь . В частности, я пытаюсь сделать это в функции, определенной за пределами конвейера, например:

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
}

Проблема в том, что, похоже, это работает только тогда, когда код динамической параллельной генерации находится внутри блока script {} внутри блока steps {} конвейера (как видно из первого источника).

При выполнении чего-то похожего на фрагмент кода выше, я вижу эту ошибку в 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

Есть ли способ определить ее как функцию так, как я показал в исходном фрагменте кода, или я застрял с кучей блоков script {} в моем определении конвейера?

1 Ответ

0 голосов
/ 10 ноября 2018

Декларативный конвейер не позволяет помещать код Groovy в блок steps {} - он ожидает допустимый шаг конвейера Jenkins в этом месте. Вот почему появился блок script {}, который можно поместить в блок steps {} для выполнения некоторого кода Groovy.

Если вам нужна гибкость и непредвзятый синтаксис, вы можете вместо этого использовать сценарий конвейера . Здесь вы можете смешивать код Groovy с существующими шагами конвейера практически без ограничений.

Документация Jenkins кратко объясняет разницу между обоими подходами и почему они существуют:

Когда был впервые создан трубопровод Jenkins, в качестве основы был выбран Groovy. Jenkins уже давно поставляется со встроенным Groovy движком, чтобы предоставить расширенные возможности сценариев для администраторов и пользователей. Кроме того, разработчики Jenkins Pipeline сочли Groovy прочной основой для построения того, что сейчас называется DSL «Scripted Pipeline». [2].

Поскольку это полнофункциональная среда программирования, Scripted Pipeline предлагает пользователям Jenkins огромную гибкость и расширяемость. Кривая обучения Groovy, как правило, не желательна для всех членов данной группы, поэтому декларативный конвейер был создан, чтобы предлагать более простой и продуманный синтаксис для разработки Jenkins Pipeline.

Обе являются в основном одной и той же подсистемой конвейера. Оба они являются надежными реализациями «Конвейер как код». Они оба могут использовать шаги, встроенные в Pipeline или предоставляемые плагинами. Оба могут использовать общие библиотеки

Ваш пример в скриптовом конвейере может выглядеть так:

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

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

И декларативный конвейер с блоком script {} в шагах будет выглядеть так:

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
}
...