Jenkinsfile/Groovy:为什么curl命令会导致"bad request"



由于这个问题的答案,我最近了解了withCredentialsDSL。试图使用@RamKamath的答案,即以下Jenkinsfile:

pipeline {
agent any
stages {
stage( "1" ) {
steps {
script {
def credId = "cred_id_stored_in_jenkins"
withCredentials([usernamePassword(credentialsId: credId,
passwordVariable: 'password',
usernameVariable: 'username')]) {
String url = "https://bitbucket.company.com/rest/build-status/1.0/commits"
String commit = '0000000000000000000000000000000000000001'
Map dict = [:]
dict.state = "INPROGRESS"
dict.key = "foo_002"
dict.url = "http://server:8080/blue/organizations/jenkins/job/detail/job/002/pipeline"
List command = []
command.add("curl -f -L")
command.add('-u ${username}:${password}')
command.add("-H \"Content-Type: application/json\"")
command.add("-X POST ${url}/${commit}")
command.add("-d \''${JsonOutput.toJson(dict)}'\'")

sh(script: command.join(' '))
}
}
}
}
}
}

curl命令本身因为报告的"错误请求"而失败。错误。这是Jenkins控制台输出的代码片段:

+ curl -f -L -u ****:**** -H "Content-Type:application/json" -X POST https://bitbucket.company.com/rest/build-status/1.0/commits/0000000000000000000000000000000000000001 -d '{"state":"INPROGRESS","key":"foo_002","url":"http://server:8080/blue/organizations/jenkins/job/detail/job/002/pipeline"}'
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100   153    0     0  100   153      0   4983 --:--:-- --:--:-- --:--:--  5100
curl: (22) The requested URL returned error: 400 Bad request

我理解-u ****:****-u的掩码username:password参数。
如果我将该字符串复制/粘贴到shell中,并将掩码值替换为实际值,curl命令工作:

$ curl -f -L -u super_user:super_password -H "Content-Type:application/json" -X POST https://bitbucket.company.com/rest/build-status/1.0/commits/0000000000000000000000000000000000000001 -d '{"state":"INPROGRESS","key":"foo_002","url":"http://server:8080/blue/organizations/jenkins/job/detail/job/002/pipeline"}'
$ 

怎么回事?为什么curl命令会导致error 400/"错误请求";当Jenkins执行它时,但同样的命令在手动执行时运行良好?

请注意:按照建议,我把-u ${username}:${password}用单引号括起来,而不是双引号。


更新:我觉得好像字符串插值有问题,因为如果我修改Jenkinsfile以添加硬编码的用户名/密码,即

command.add('-u super_user:super_password')

…而不是

command.add('-u ${username}:${password}')

…那么curl命令仍然像以前一样失败,即因为error: 400 Bad request

有人能帮我找出什么是错的,大概与命令汇编,和/或sh()调用?


更新

我通过删除withCredentials()简化了这个问题。甚至这个简化的curl调用也失败了:

pipeline {
agent any
stages {
stage( "1" ) {
steps {
script {
def credId = "cred_id_stored_in_jenkins"
String url = "https://bitbucket.company.com/rest/build-status/1.0/commits"
String commit = '0000000000000000000000000000000000000001'
Map dict = [:]
dict.state = "INPROGRESS"
dict.key = "foo_002"
dict.url = "http://server:8080/blue/organizations/jenkins/job/detail/job/002/pipeline"
List command = []
command.add("curl -f -L")
command.add('-u super_user:super_password')
command.add("-H \"Content-Type: application/json\"")
command.add("-X POST ${url}/${commit}")
command.add("-d \''${JsonOutput.toJson(dict)}'\'")
sh(script: command.join(' '))
}
}
}
}
}

我尝试了你的脚本内联一个管道项目的改编:

pipeline {
agent any
stages {
stage( "1" ) {
steps {
script {
def username = '********' // my real jenkins user
def password = '********' // my real jenkins pwd
String url = "http://localhost:8083/api/json"
String commit = ""
List command = []
command.add("'C:/Program Files/Git/mingw64/bin/curl.exe' -f -L")
command.add("-u ${username}:${password}")
command.add('-H "Content-Type: application/json"') // no "..." string delimiters and escaping necessary
//command.add("-X POST ${url}/${commit}")
command.add("-X GET ${url}/${commit}")  // GET instead
//command.add("-d \''${JsonOutput.toJson(dict)}'\'")

sh(script: command.join(' '))
}
}
}
}
}
控制台输出

+ 'C:/Program Files/Git/mingw64/bin/curl.exe' -f -L -u ********:******** -H 'Content-Type: application/json' -X GET http://localhost:8083/api/json/
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  3232  100  3232    0     0  42667      0 --:--:-- --:--:-- --:--:-- 43675{"_class":"hudson.model.Hudson","assignedLabels":[{"name":"master"}],"mode":"NORMAL","nodeDescription":"the master Jenkins node","nodeName":"","numExecutors":2,"description":null,"jobs":
...

我记得Jenkinsfile的转义和引号

使用凭据绑定插件

stage( "curl withCredentials" ) {
steps {
script {
withCredentials([usernamePassword(
credentialsId: 'jenkins-user',
passwordVariable: 'password',
usernameVariable: 'username')]) {

String url = "http://localhost:8083/api/json"
String commit = ""
List command = []
command.add("'C:/Program Files/Git/mingw64/bin/curl.exe' -f -L")
command.add("-u ${username}:${password}")
command.add('-H "Content-Type: application/json"') // no "..." string delimiter and escaping necessary
//command.add("-X POST ${url}/${commit}")
command.add("-X GET ${url}/${commit}")  // GET instead
//command.add("-d \''${JsonOutput.toJson(dict)}'\'")

sh(script: command.join(' '))
}
}
}
}            
控制台输出

+ 'C:/Program Files/Git/mingw64/bin/curl.exe' -f -L -u ****:**** -H 'Content-Type: application/json' -X GET http://localhost:8083/api/json/
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  3231  100  3231    0     0  42247      0 --:--:-- --:--:-- --:--:-- 42513
100  3231  100  3231    0     0  42216      0 --:--:-- --:--:-- --:--:-- 42513{"_class":"hudson.model.Hudson","assignedLabels":[{"name":"master"}],"mode":"NORMAL","nodeDescription":"the master Jenkins node","nodeName":"","numExecutors":2,"description":null,"jobs":
...

这个问题原来是字符串转义问题。对我来说,有效的解决方案(包括withCredentials(),这不是问题的一个因素)是:

pipeline {
agent any
stages {
stage( "1" ) {
steps {
script {
def credId = "cred_id_stored_in_jenkins"
String url = "https://bitbucket.company.com/rest/build-status/1.0/commits"
String commit = '0000000000000000000000000000000000000001'
withCredentials([usernamePassword(credentialsId: credId,
passwordVariable: 'password',
usernameVariable: 'username')]) {
Map dict = [:]
dict.state = "INPROGRESS"
dict.key = "foo_002"
dict.url = http://server:8080/blue/organizations/jenkins/job/detail/job/002/pipeline"
def cmd = "curl -f -L" +
"-u ${username}:${password} " +
"-H "Content-Type: application/json" " +
"-X POST ${url}/${commit} " 
"-d '${JsonOutput.toJson(dict)}'")

sh(script: cmd)
}
}
}
}
}
}

我确信List.join()的一些变化会起作用-并且没有具体的原因我恢复使用+来加入字符串,而不是我正在黑客攻击,并解决了刚刚工作的第一件事。在Jenkins中,转义字符串似乎是它自己的小地狱圈,所以我不想在那里花费更多的时间。

在这个过程中出现了一些奇怪的事情:

首先,在Windows与Unix/bash中行为似乎是不同的:@GeroldBroser(他的帮助是无价的)能够在他的Windows环境中获得一个工作解决方案,字符串转义更接近/与我原来的帖子相同;然而,我无法在我的Unix/bash环境中重现他的结果(Jenkinssh调用在我的设置中使用bash)。

最后,我的印象是,记录到Jenkins作业控制台输出的文本是字面上的所执行的内容——但这似乎并不完全正确。
总结我与@GeroldBroser的部分评论讨论:
curl命令,当Jenkins运行error: 400 Bad request失败时,但是如果我复制/粘贴/执行在bash shell中记录在我的Jenkins作业控制台输出中的curl命令,它就成功了。
通过使用--trace-ascii /dev/stdout选项到curl,我能够发现curl命令,当在bash中成功运行时,发送141个字节,但是当Jenkins运行失败时,发送143个字节:额外的2个字节是在JSON内容前后的'(单引号)字符的前前后后。
这让我沿着疯狂的道路走向地狱的圈子,走向诅咒的城堡,走向疯狂的王座,那就是詹金斯串逃跑,我最终到达了上面的工作解决方案。

值得注意的是:有了这个工作解决方案,我不能再复制/粘贴curl命令——正如我的Jenkins作业控制台输出中登录的那样——到bash shell并成功执行。因此,在Jenkins作业控制台输出中记录的与运行的并不总是正确的。(即复制/粘贴)在shell.">

相关内容

最新更新