简体   繁体   English

Groovy/Jenkins:如何将 sh(script:"curl...") 重构为 URL?

[英]Groovy/Jenkins: how to refactor sh(script:"curl ...") to URL?

My Jenkins pipeline currently successfully invokes Bitbucket REST API by invoking curl in a shell, as in the code below:我的 Jenkins 管道目前通过在 shell 中调用curl成功调用了 Bitbucket REST API,如下代码所示:

// Jenkinsfile
@Library('my-sandbox-libs@dev') my_lib

pipeline {
  agent any

  stages {
    stage( "1" ) { steps { script { echo "hello" } } }

    stage( "2" ) {
      steps {
        script {
          log = new org.log.Log()
          def cred_id = "bitbucket_cred_id"
          def url_base = "https://bitbucket.company.com"
          def commit = "76136485c45df256a62cbc2c3c5f1f3efcc86258"
          def status =
                       //"INPROGRESS",
                       //"SUCCESSFUL",
                       "FAILED"
          def viz_url = "https://path/to/nowhere"
          try {
            my_lib.notifyBitbucketBuildStatus(cred_id,
                                              url_base,
                                              commit,
                                              status,
                                              "foo",
                                              42,
                                              viz_url,
                                              log)
          }
        }
      }
    }

    stage( "3" ) { steps { script { echo "world" } } }
  }

  post { always { script { echo log.asJsonString() } } }
}
import groovy.json.JsonOutput

def notifyBitbucketBuildStatus(cred_id,
                               url_base,
                               commit,
                               build_state,
                               build_info_name,
                               build_info_number,
                               viz_url,
                               log) {
  def rest_path = "rest/build-status/1.0/commits"
  def dict = [:]
  dict.state = build_state
  dict.key = "${build_info_name}_${build_info_number}"
  dict.url = viz_url
  withCredentials([string(credentialsId: cred_id,
                          variable: 'auth_token')]) {
    def cmd = "curl -f -L " +
              "-H \"Authorization: Bearer ${auth_token}\" " +
              "-H \"Content-Type:application/json\" " +
              "-X POST ${url_base}/${rest_path}/${commit} " +
              "-d \'${JsonOutput.toJson(dict)}\'"
    if ( 0 != sh(script: cmd, returnStatus: true) ) {
      log.warn("Failed updating build status with Bitbucket")
    }
  }
}

I would like to refactor function notifyBitbucketBuildStatus() to use a "native" Groovy-language solution, rather than invoking curl in a shell. I read the following on this topic:我想重构 function notifyBitbucketBuildStatus()以使用“本机”Groovy 语言解决方案,而不是在curl中调用 curl。我阅读了有关此主题的以下内容:

  1. https://www.baeldung.com/groovy-web-services https://www.baeldung.com/groovy-web-services
  2. Groovy built-in REST/HTTP client? Groovy 内置 REST/HTTP 客户端?

...based on which I thought the refactored function would look like this: ...基于此,我认为重构后的 function 将如下所示:

def notifyBitbucketBuildStatus(cred_id,
                               url_base,
                               commit,
                               build_state,
                               build_info_name,
                               build_info_number,
                               viz_url,
                               log) {
  def rest_path = "rest/build-status/1.0/commits"
  def dict = [:]
  dict.state = build_state
  dict.key = "${build_info_name}_${build_info_number}"
  dict.url = viz_url
  def req = new URL("${url_base}/${rest_path}/${commit}").openConnection()
  req.setRequestMethod("POST")
  req.setDoOutput(true)
  req.setRequestProperty("Content-Type", "application/json")
  withCredentials([string(credentialsId: cred_id,
                          variable: 'auth_token')]) {
    req.setRequestProperty("Authorization", "Bearer ${auth_token}")
  }
  def msg = JsonOutput.toJson(dict)
  req.getOutputStream().write(msg.getBytes("UTF-8"));
  if ( 200 != req.getResponseCode() ) {
    log.warn("Failed updating build status with Bitbucket")
  }
}

...but this generates the exception java.io.NotSerializableException: sun.net.www.protocol.https.HttpsURLConnectionImpl ...但这会生成异常java.io.NotSerializableException: sun.net.www.protocol.https.HttpsURLConnectionImpl

That "not serializable" made me think the error had something to do with a failure to transform something to a string, so I also tried this, but it did not change the error: “不可序列化”让我认为错误与无法将某些内容转换为字符串有关,所以我也尝试了这个,但它并没有改变错误:

def msg = JsonOutput.toJson(dict).toString()

What is wrong with the refactored code that uses class URL , and what is the right way to use it to invoke the REST API?使用 class URL的重构代码有什么问题,使用它调用 REST API 的正确方法是什么?

For the life of me, I can't see what's different between the above and the linked Stack Overflow Q&A, and my inexperience with the language is such that I rely largely on adapting existing example.对于我的生活,我看不出上面和链接的 Stack Overflow Q&A 之间有什么不同,而且我对这门语言缺乏经验,所以我在很大程度上依赖于改编现有的例子。

Solution解决方案

I would highly suggest you use the HTTP Request and the Pipeline Steps Utility plugin for this.我强烈建议您为此使用HTTP 请求Pipeline Steps Utility 插件 You can then use those steps in a Groovy script as follows然后,您可以在 Groovy 脚本中使用这些步骤,如下所示

node('master') {
    
    withCredentials([string(credentialsId: cred_id, variable: 'auth_token')]) {
        def response = httpRequest url: "https://jsonplaceholder.typicode.com/todos", customHeaders: [[name: 'Authorization', value: "Bearer ${auth_token}"]]
    }

    if( response.status != 200 ) {
        error("Service returned a ${response.status}")
    }

    def json = readJSON text: response.content
    
    println "The User ID is ${json[0]['userId']}"
    println "The follow json obj is ${json}"
    
}

Obviously you can modify the code if you want to build a method, and you will need to update with the appropriate URL.显然,如果你想构建一个方法,你可以修改代码,你将需要使用适当的 URL 进行更新。

I found a sucky and unsatisfying answer - but an answer n.netheless - that I posted here: https://stackoverflow.com/a/69486890/5437543我发现了一个糟糕且不令人满意的答案 - 但答案 n.netheless - 我在这里发布: https://stackoverflow.com/a/69486890/5437543

I hate that solution because it would appear to demonstrate that the Jenkins/Groovy language itself imposes an artificial contrivance on how I can organize my code.我讨厌这个解决方案,因为它似乎证明了 Jenkins/Groovy 语言本身对我如何组织代码强加了人为的设计。 Effectively, I am prevented from doing实际上,我被阻止做

// Jenkinsfile
@Library('my-sandbox-libs@dev') my_lib

pipeline {
  agent any

  stages {
    stage( "1" ) { steps { script { my_lib.func() } } }
  }
}
// vars/my_lib.groovy

def func() {
  def post = new URL("https://whatever").openConnection();
  ...
  withCredentials([string(credentialsId: cred_id,
                          variable: 'auth_token')]) {
    req.setRequestProperty("Authorization", "Bearer ${auth_token}")
  }
  ...
}

...and I am forced to do ......我被迫

// Jenkinsfile
@Library('my-sandbox-libs@dev') my_lib

pipeline {
  agent any

  stages {
    stage( "1" ) { steps { script { my_lib.func(my_lib.getCred()) } } }
  }
}
// vars/my_lib.groovy

def getCred() {
  withCredentials([string(credentialsId: cred_id,
                          variable: 'auth_token')]) {
    return auth_token
  }
}

def func(auth_token) {
  def post = new URL("https://whatever").openConnection();
  ...
  req.setRequestProperty("Authorization", "Bearer ${auth_token}")
  ...
}

Extremely dissatisfying conclusion.非常不满意的结论。 I hope another answerer can point out a solution that doesn't rely on this contrived code organization.我希望另一个回答者可以指出一个不依赖于这种人为的代码组织的解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM