0

我已经设置了 jenkins,现在我想将节点添加到 jenkins,我正在按照How-to-Connect-to-Remote-SSH-Slaves中的步骤操作。

有使用slave node私钥创建凭证的步骤。我尝试使用 ansible 的 jenkins api 创建它。

剧本中的任务是

- name: Read private key file content of slave1
  slurp:
      src: "{{ ansible_env.HOME }}/.ssh/slave1"
  register: private_key_file
  tags:
      - credential

- name: Add credential to add node
  uri:
      body: |
          json={
              "": "0",
              "credentials": {
                "scope": "GLOBAL",
                "id": "jenkins_linux_slave1_auth",
                "username": "jenkins",
                "password": "",
                "privateKeySource": {
                  "stapler-class": "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource",
                  "privateKey": "{{ private_key_file['content'] | b64decode }}",
                },
                "description": "Jenkins Linux Slave1 Authentication",
                "stapler-class": "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey"
              }
            }
      force_basic_auth: yes
      method: POST
      password: "{{ jenkins_user_token }}"
      status_code: 302
      url: "{{ jenkins_url }}/credentials/store/system/domain/_/createCredentials"
      user: "{{ jenkins_user }}"
      validate_certs: no
  tags:
      - credential

但这失败了

$ ansible-playbook -i hosts jenkins_config.yaml --verbose -u root --ask-pass -b -e "jenkins_user=admin" -e "jenkins_user_token=admin" --tags credential
Using /root/ansible_playbooks/ansible.cfg as config file
SSH password:

PLAY [jenkins_servers] ***************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************************************************************************************************
ok: [localhost]

TASK [jenkins_config : Read private key file content of slave1] **********************************************************************************************************************************************************************
ok: [localhost] => {"changed": false, "content": "Content", "encoding": "base64", "source": "/root/.ssh/slave1"}

TASK [jenkins_config : Add credential to add node] ***********************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"cache_control": "no-cache,no-store,must-revalidate", "changed": false, "connection": "close", "content": "\n\n\n\n\n  \n  <!DOCTYPE html><html><head resURL=\"/static/92560838\" data-rooturl=\"\" data-resurl=\"/static/92560838\">\n    \n\n    <title>Jenkins [Jenkins]</title><link rel=\"stylesheet\" href=\"/static/92560838/css/layout-common.css\" type=\"text/css\" /><link rel=\"stylesheet\" href=\"/static/92560838/css/style.css\" type=\"text/css\" /><link rel=\"stylesheet\" href=\"/static/92560838/css/color.css\" type=\"text/css\" /><link rel=\"stylesheet\" href=\"/static/92560838/css/responsive-grid.css\" type=\"text/css\" /><link rel=\"shortcut icon\" href=\"/static/92560838/favicon.ico\" type=\"image/vnd.microsoft.icon\" /><link color=\"black\" rel=\"mask-icon\" href=\"/images/mask-icon.svg\" /><script>var isRunAsTest=false; var rootURL=\"\"; var resURL=\"/static/92560838\";</script><script src=\"/static/92560838/scripts/prototype.js\" type=\"text/javascript\"></script><script src=\"/static/92560838/scripts/behavior.js\" type=\"text/javascript\"></script><script src='/adjuncts/92560838/org/kohsuke/stapler/bind.js' type='text/javascript'></script><script src=\"/static/92560838/scripts/yui/yahoo/yahoo-min.js\"></script><script src=\"/static/92560838/scripts/yui/dom/dom-min.js\"></script><script src=\"/static/92560838/scripts/yui/event/event-min.js\"></script><script src=\"/static/92560838/scripts/yui/animation/animation-min.js\"></script><script src=\"/static/92560838/scripts/yui/dragdrop/dragdrop-min.js\"></script><script src=\"/static/92560838/scripts/yui/container/container-min.js\"></script><script src=\"/static/92560838/scripts/yui/connection/connection-min.js\"></script>
"content_length": "27072", "content_type": "text/html;charset=utf-8", "date": "Mon, 22 Jan 2018 15:58:04 GMT", "expires": "Thu, 01 Jan 1970 00:00:00 GMT", "msg": "Status code was not [302]: HTTP Error 500: Server Error", "redirected": false, "server": "Jetty(9.4.z-SNAPSHOT)", "set_cookie": "JSESSIONID.8c551130=node0v7u4nqouxb1l1kcnd5139in7616.node0;Path=/;Secure;HttpOnly", "status": 500, "url": "http://localhost:8080/credentials/store/system/domain/_/createCredentials", "x_content_type_options": "nosniff", "x_frame_options": "sameorigin", "x_hudson": "1.395", "x_hudson_theme": "default", "x_instance_identity": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgORb1hlBOGx2LlN77veNlsGhwykedmrM+XqbHh0Fq4UGHQKhFicOXSPKiuOr4XpfWRZtYAy7jrY59cCF4So2qHionFUBjQespaALVC+aWQC3qIaZC4NlDgZXz+xQCFFrgW8G2iX2DA5kbPuwuIsv4WlgWvk8Z3kNmAxr16xhRq1R+RmdtVnmTaRyZiHdyQXdVpNHuYzITHfzUyIVIa8elbylS2CgDBMKRasxf7ewX++5Qp5rM5OzDbb5QLAueDhoyjAN2aA+jibUfYKoO7rwgpO3zMbIxuT9SKI7DMM7+TOEDQaH9SF0n+l/WXqpN6PLur9/o/10gsPghMXaV//KpQIDAQAB", "x_jenkins": "2.89.3", "x_jenkins_session": "92560838"}
    to retry, use: --limit @/root/ansible_playbooks/jenkins_config.retry

PLAY RECAP ***************************************************************************************************************************************************************************************************************************
localhost : ok=2    changed=0    unreachable=0    failed=1

我检查了 jenkins 日志,它显示 json 解析错误。

Jan 22, 2018 11:02:17 AM org.eclipse.jetty.server.handler.ContextHandler$Context log
WARNING: Error while serving http://localhost:8080/credentials/store/system/domain/_/createCredentials
java.lang.reflect.InvocationTargetException
    at org.kohsuke.stapler.Function$MethodFunction.invoke(Function.java:347)
    at org.kohsuke.stapler.interceptor.RequirePOST$Processor.invoke(RequirePOST.java:52)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: javax.servlet.ServletException: Failed to parse JSON:{
    "": "0",
    "credentials": {
      "scope": "GLOBAL",
      "id": "jenkins_linux_slave1_auth",
      "username": "jenkins",
      "password": "",
      "privateKeySource": {
        "stapler-class": "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource",
        "privateKey": "-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
<MULTI LINE PRIVATE KEY>
-----END RSA PRIVATE KEY-----
",
      },
      "description": "Jenkins Linux Slave1 Authentication",
      "stapler-class": "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey"
    }
  }

    at org.kohsuke.stapler.RequestImpl.getSubmittedForm(RequestImpl.java:1021)
    at com.cloudbees.plugins.credentials.CredentialsStoreAction$DomainWrapper.doCreateCredentials(CredentialsStoreAction.java:832)
    at java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:627)
    at org.kohsuke.stapler.Function$MethodFunction.invoke(Function.java:343)
    ... 84 more
Caused by: net.sf.json.JSONException: Unterminated string at character 365 of {
    "": "0",
    "credentials": {
      "scope": "GLOBAL",
      "id": "jenkins_linux_slave1_auth",
      "username": "jenkins",
      "password": "",
      "privateKeySource": {
        "stapler-class": "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource",
        "privateKey": "-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
<MULTI LINE PRIVATE KEY>
-----END RSA PRIVATE KEY-----
",
      },
      "description": "Jenkins Linux Slave1 Authentication",
      "stapler-class": "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey"
    }
  }
    at net.sf.json.util.JSONTokener.syntaxError(JSONTokener.java:499)
    at net.sf.json.util.JSONTokener.nextString(JSONTokener.java:237)
    at net.sf.json.util.JSONTokener.nextValue(JSONTokener.java:345)
    at net.sf.json.JSONObject._fromJSONTokener(JSONObject.java:955)
    at net.sf.json.JSONObject.fromObject(JSONObject.java:156)
    at net.sf.json.util.JSONTokener.nextValue(JSONTokener.java:348)
    at net.sf.json.JSONObject._fromJSONTokener(JSONObject.java:955)
    at net.sf.json.JSONObject.fromObject(JSONObject.java:156)
    at net.sf.json.util.JSONTokener.nextValue(JSONTokener.java:348)
    at net.sf.json.JSONObject._fromJSONTokener(JSONObject.java:955)
    at net.sf.json.JSONObject._fromString(JSONObject.java:1145)
    at net.sf.json.JSONObject.fromObject(JSONObject.java:162)
    at net.sf.json.JSONObject.fromObject(JSONObject.java:132)
    at org.kohsuke.stapler.RequestImpl.getSubmittedForm(RequestImpl.java:1019)
    ... 87 more

如果我将私有文件的内容手动复制到privateKey有效负载中,它可以正常工作。

复制文件内容 in 和 有什么区别slurp['content]|b64decode

4

1 回答 1

1
  • 私钥包含需要转义以生成有效 JSON 的换行符。
  • 字段后有多余的逗号privateKey,您需要将其删除

尝试这个:

...
body: |
    json={
        "": "0",
        "credentials": {
          "scope": "GLOBAL",
          "id": "jenkins_linux_slave1_auth",
          "username": "jenkins",
          "password": "",
          "privateKeySource": {
            "stapler-class": "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource",
            "privateKey": {{ private_key_file['content'] | b64decode | tojson }}
          },
          "description": "Jenkins Linux Slave1 Authentication",
          "stapler-class": "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey"
        }
      }
...
于 2018-02-15T15:41:35.983 回答