使用 Ansible 安装 Jenkins 插件
Install Jenkins Plugins with Ansible
我正在尝试使用 ansible 安装 jenkins master,到目前为止一切似乎都运行良好。但是现在我偶然发现了插件安装。
我从 ansible 找到了 jenkins_plugin 指令,但我不知道要提供哪些参数。我想我只是错误地识别了插件(即没有使用正确的名称)。我不知道如何确定我应该使用哪个 name/id。
- name: Install Plugin "active-directory"
jenkins_plugin:
name: "active-directory"
environment: "{{proxy_env}}"
结果为 HTTP 404
fatal: [<SERVERNAME>]: FAILED! => {
"changed": false,
"details": "HTTP Error 404: Not Found",
"invocation": {
"module_args": {
"attributes": null,
"backup": null,
"client_cert": null,
"client_key": null,
"content": null,
"delimiter": null,
"directory_mode": null,
"follow": false,
"force": false,
"force_basic_auth": true,
"group": "jenkins",
"http_agent": "ansible-httpget",
"jenkins_home": "/var/lib/jenkins",
"mode": "0644",
"name": "active-directory",
"owner": "jenkins",
"params": null,
"regexp": null,
"remote_src": null,
"selevel": null,
"serole": null,
"setype": null,
"seuser": null,
"src": null,
"state": "present",
"timeout": 30.0,
"unsafe_writes": null,
"updates_expiration": 86400,
"updates_url": "https://updates.jenkins.io",
"url": "http://localhost:8080",
"url_password": null,
"url_username": null,
"use_proxy": true,
"validate_certs": true,
"version": null,
"with_dependencies": true
}
},
"msg": "Cannot get CSRF"
}
我想这是正确的,因为我没有使用正确的 names/ids
有没有办法确定要使用哪些标识符?我可以从 jenkins ui 或文件系统中获取名称吗?有没有我不明白的模式?或者互联网上是否有某种文档?
提前谢谢大家。我很感激任何提示。最好的问候,塞巴斯蒂安
更新2019-08-06
我尝试了一些不同的方法来获取插件。我意识到问题更有可能是代理身份验证。我可以通过 curl 解析更新站点。
curl -x http://proxy.server.com:80 --proxy-user myUsername https://updates.jenkins.io
这是 curl 的 html 响应。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Index of /</title>
</head>
<body>
<h1>Index of /</h1>
<table>
<tr><th valign="top"><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr>
<tr><th colspan="5"><hr></th></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="2.121/">2.121/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="2.138/">2.138/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="2.150/">2.150/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="2.164/">2.164/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="2.176/">2.176/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="current/">current/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="download/">download/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="experimental/">experimental/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="latest/">latest/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/text.gif" alt="[TXT]"></td><td><a href="latestCore.txt">latestCore.txt</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> 5 </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="plugin-documentation-urls.json">plugin-documentation-urls.json</a></td><td align="right">2019-08-06 15:12 </td><td align="right">152K</td><td> </td></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="plugin-versions.json">plugin-versions.json</a></td><td align="right">2019-08-06 15:12 </td><td align="right">8.3M</td><td> </td></tr>
<tr><td valign="top"><img src="/icons/text.gif" alt="[TXT]"></td><td><a href="pluginCount.txt">pluginCount.txt</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> 4 </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="release-history.json">release-history.json</a></td><td align="right">2019-08-06 15:12 </td><td align="right">2.7M</td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="stable-2.121/">stable-2.121/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="stable-2.138/">stable-2.138/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="stable-2.150/">stable-2.150/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="stable-2.164/">stable-2.164/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="stable-2.176/">stable-2.176/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="stable/">stable/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="temporary-experimental-java11/">temporary-experimental-java11/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="update-center.actual.json">update-center.actual.json</a></td><td align="right">2019-08-06 15:12 </td><td align="right">1.7M</td><td> </td></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="update-center.json">update-center.json</a></td><td align="right">2019-08-06 15:12 </td><td align="right">1.7M</td><td> </td></tr>
<tr><td valign="top"><img src="/icons/text.gif" alt="[TXT]"></td><td><a href="update-center.json.html">update-center.json.html</a></td><td align="right">2019-08-06 15:12 </td><td align="right">1.7M</td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="updates/">updates/</a></td><td align="right">2019-08-06 12:20 </td><td align="right"> - </td><td> </td></tr>
<tr><th colspan="5"><hr></th></tr>
</table>
<p>
You can rsync these files via <tt>rsync -avz rsync://rsync.osuosl.org/jenkins/updates/ somewhere</tt>.
</p>
<p>
For more information about the layout of update center,
see <a href="https://github.com/jenkinsci/backend-update-center2/blob/master/site/LAYOUT.md">this document</a>.
</p></body></html>
但是我的ansible playbook没有得到任何回应。但当我更改任务定义时,它的行为似乎有所不同:
- 代理设置包括用户名和密码
- 代理设置包括用户名但没有密码
- 没有任何代理设置
任务定义:
- name: Install Plugin "active-directory"
jenkins_plugin:
name: "{{ item.key }}"
environment:
http_proxy: http://USER:PASS@proxy.server.com:80
https_proxy: http://USER:PASS@proxy.server.com:80
with_items:
- { key: "active-directory", version: "2.16" }
当然,我使用的是真实的用户名、密码和服务器名称,而不是此处的占位符。
# with password
failed: [s00260415uv] (item={u'version': u'2.16', u'key': u'active-directory'}) => {"ansible_loop_var": "item", "changed": false, "details": "HTTP Error 404: Not Found", "item": {"key": "active-directory", "version": "2.16"}, "msg": "Cannot get CSRF"}
# without password
failed: [s00260415uv] (item={u'version': u'2.16', u'key': u'active-directory'}) => {"ansible_loop_var": "item", "changed": false, "details": "HTTP Error 407: Proxy Authentication Required", "item": {"key": "active-directory", "version": "2.16"}, "msg": "Cannot get CSRF"}
# without any proxy settings
failed: [s00260415uv] (item={u'version': u'2.16', u'key': u'active-directory'}) => {"ansible_loop_var": "item", "changed": false, "details": "HTTP Error 403: Forbidden", "item": {"key": "active-directory", "version": "2.16"}, "msg": "Cannot get CSRF"}
似乎没有代理设置访问被拒绝,只有用户名代理身份验证不完整,用户名和密码,虽然提到的具有相同用户名,密码和代理服务器的卷曲有效,但找不到目标。
我仍然无法下载插件。
更新2019-08-07
我在上次尝试中跟踪了所有 HTTP 请求,并在我的跟踪中找到了与 404 消息对应的行。
2019-08-07 09:39:42 x.x.x.x y.y.y.y > GET localhost:8080 http://localhost:8080/api/json HTTP/1.1 - -
2019-08-07 09:39:42 y.y.y.y x.x.x.x < - - - HTTP/1.1 404 Not Found
来自我的 jenkins 服务器(运行 ansible)的那个 URL 的 curl 结果是:
curl http://localhost:8080/api/json
Authentication required
<!--
You are authenticated as: anonymous
有什么方法可以将 jenkins unser 添加到插件安装中吗?
我找到了答案(至少暂时如此)。 404 状态不是来自任何远程主机,而是来自我的 jenkins 实例本身。詹金斯没有在本地主机上收听。所以我将 url 更改为实际的 servername 。现在这工作正常。但是我仍然必须找出如何使用 localhost 来代替,因为我想在不同阶段(=不同的服务器名称)使用相同的剧本 fpr 几个 jenkins 安装。
无论如何,这是正确的片段。
# Jenkins master: install plugins
- name: Install Plugins on behalf of jenkins user {{jenkins_admin_user}}
jenkins_plugin:
name: "{{ item.key }}"
url: http://s00260415uv:8080
url_username: "{{jenkins_admin_user}}"
url_password: "{{jenkins_admin_pass}}"
environment:
http_proxy: http://<USER>:<PASS>@<PROXY_SERVER>:80
https_proxy: http://<USER>:<PASS>@<PROXY_SERVER>:80
register: plugin_result
until: plugin_result is success
retries: 10
delay: 2
with_items: # no version = latest
- { key: "ace-editor"}
- { key: "active-directory"}
- { key: "..."}
我正在尝试使用 ansible 安装 jenkins master,到目前为止一切似乎都运行良好。但是现在我偶然发现了插件安装。
我从 ansible 找到了 jenkins_plugin 指令,但我不知道要提供哪些参数。我想我只是错误地识别了插件(即没有使用正确的名称)。我不知道如何确定我应该使用哪个 name/id。
- name: Install Plugin "active-directory"
jenkins_plugin:
name: "active-directory"
environment: "{{proxy_env}}"
结果为 HTTP 404
fatal: [<SERVERNAME>]: FAILED! => {
"changed": false,
"details": "HTTP Error 404: Not Found",
"invocation": {
"module_args": {
"attributes": null,
"backup": null,
"client_cert": null,
"client_key": null,
"content": null,
"delimiter": null,
"directory_mode": null,
"follow": false,
"force": false,
"force_basic_auth": true,
"group": "jenkins",
"http_agent": "ansible-httpget",
"jenkins_home": "/var/lib/jenkins",
"mode": "0644",
"name": "active-directory",
"owner": "jenkins",
"params": null,
"regexp": null,
"remote_src": null,
"selevel": null,
"serole": null,
"setype": null,
"seuser": null,
"src": null,
"state": "present",
"timeout": 30.0,
"unsafe_writes": null,
"updates_expiration": 86400,
"updates_url": "https://updates.jenkins.io",
"url": "http://localhost:8080",
"url_password": null,
"url_username": null,
"use_proxy": true,
"validate_certs": true,
"version": null,
"with_dependencies": true
}
},
"msg": "Cannot get CSRF"
}
我想这是正确的,因为我没有使用正确的 names/ids
有没有办法确定要使用哪些标识符?我可以从 jenkins ui 或文件系统中获取名称吗?有没有我不明白的模式?或者互联网上是否有某种文档?
提前谢谢大家。我很感激任何提示。最好的问候,塞巴斯蒂安
更新2019-08-06 我尝试了一些不同的方法来获取插件。我意识到问题更有可能是代理身份验证。我可以通过 curl 解析更新站点。
curl -x http://proxy.server.com:80 --proxy-user myUsername https://updates.jenkins.io
这是 curl 的 html 响应。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>Index of /</title>
</head>
<body>
<h1>Index of /</h1>
<table>
<tr><th valign="top"><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr>
<tr><th colspan="5"><hr></th></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="2.121/">2.121/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="2.138/">2.138/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="2.150/">2.150/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="2.164/">2.164/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="2.176/">2.176/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="current/">current/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="download/">download/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="experimental/">experimental/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="latest/">latest/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/text.gif" alt="[TXT]"></td><td><a href="latestCore.txt">latestCore.txt</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> 5 </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="plugin-documentation-urls.json">plugin-documentation-urls.json</a></td><td align="right">2019-08-06 15:12 </td><td align="right">152K</td><td> </td></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="plugin-versions.json">plugin-versions.json</a></td><td align="right">2019-08-06 15:12 </td><td align="right">8.3M</td><td> </td></tr>
<tr><td valign="top"><img src="/icons/text.gif" alt="[TXT]"></td><td><a href="pluginCount.txt">pluginCount.txt</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> 4 </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="release-history.json">release-history.json</a></td><td align="right">2019-08-06 15:12 </td><td align="right">2.7M</td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="stable-2.121/">stable-2.121/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="stable-2.138/">stable-2.138/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="stable-2.150/">stable-2.150/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="stable-2.164/">stable-2.164/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="stable-2.176/">stable-2.176/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="stable/">stable/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="temporary-experimental-java11/">temporary-experimental-java11/</a></td><td align="right">2019-08-06 15:12 </td><td align="right"> - </td><td> </td></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="update-center.actual.json">update-center.actual.json</a></td><td align="right">2019-08-06 15:12 </td><td align="right">1.7M</td><td> </td></tr>
<tr><td valign="top"><img src="/icons/unknown.gif" alt="[ ]"></td><td><a href="update-center.json">update-center.json</a></td><td align="right">2019-08-06 15:12 </td><td align="right">1.7M</td><td> </td></tr>
<tr><td valign="top"><img src="/icons/text.gif" alt="[TXT]"></td><td><a href="update-center.json.html">update-center.json.html</a></td><td align="right">2019-08-06 15:12 </td><td align="right">1.7M</td><td> </td></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="updates/">updates/</a></td><td align="right">2019-08-06 12:20 </td><td align="right"> - </td><td> </td></tr>
<tr><th colspan="5"><hr></th></tr>
</table>
<p>
You can rsync these files via <tt>rsync -avz rsync://rsync.osuosl.org/jenkins/updates/ somewhere</tt>.
</p>
<p>
For more information about the layout of update center,
see <a href="https://github.com/jenkinsci/backend-update-center2/blob/master/site/LAYOUT.md">this document</a>.
</p></body></html>
但是我的ansible playbook没有得到任何回应。但当我更改任务定义时,它的行为似乎有所不同: - 代理设置包括用户名和密码 - 代理设置包括用户名但没有密码 - 没有任何代理设置
任务定义:
- name: Install Plugin "active-directory"
jenkins_plugin:
name: "{{ item.key }}"
environment:
http_proxy: http://USER:PASS@proxy.server.com:80
https_proxy: http://USER:PASS@proxy.server.com:80
with_items:
- { key: "active-directory", version: "2.16" }
当然,我使用的是真实的用户名、密码和服务器名称,而不是此处的占位符。
# with password
failed: [s00260415uv] (item={u'version': u'2.16', u'key': u'active-directory'}) => {"ansible_loop_var": "item", "changed": false, "details": "HTTP Error 404: Not Found", "item": {"key": "active-directory", "version": "2.16"}, "msg": "Cannot get CSRF"}
# without password
failed: [s00260415uv] (item={u'version': u'2.16', u'key': u'active-directory'}) => {"ansible_loop_var": "item", "changed": false, "details": "HTTP Error 407: Proxy Authentication Required", "item": {"key": "active-directory", "version": "2.16"}, "msg": "Cannot get CSRF"}
# without any proxy settings
failed: [s00260415uv] (item={u'version': u'2.16', u'key': u'active-directory'}) => {"ansible_loop_var": "item", "changed": false, "details": "HTTP Error 403: Forbidden", "item": {"key": "active-directory", "version": "2.16"}, "msg": "Cannot get CSRF"}
似乎没有代理设置访问被拒绝,只有用户名代理身份验证不完整,用户名和密码,虽然提到的具有相同用户名,密码和代理服务器的卷曲有效,但找不到目标。
我仍然无法下载插件。
更新2019-08-07 我在上次尝试中跟踪了所有 HTTP 请求,并在我的跟踪中找到了与 404 消息对应的行。
2019-08-07 09:39:42 x.x.x.x y.y.y.y > GET localhost:8080 http://localhost:8080/api/json HTTP/1.1 - -
2019-08-07 09:39:42 y.y.y.y x.x.x.x < - - - HTTP/1.1 404 Not Found
来自我的 jenkins 服务器(运行 ansible)的那个 URL 的 curl 结果是:
curl http://localhost:8080/api/json
Authentication required
<!--
You are authenticated as: anonymous
有什么方法可以将 jenkins unser 添加到插件安装中吗?
我找到了答案(至少暂时如此)。 404 状态不是来自任何远程主机,而是来自我的 jenkins 实例本身。詹金斯没有在本地主机上收听。所以我将 url 更改为实际的 servername 。现在这工作正常。但是我仍然必须找出如何使用 localhost 来代替,因为我想在不同阶段(=不同的服务器名称)使用相同的剧本 fpr 几个 jenkins 安装。
无论如何,这是正确的片段。
# Jenkins master: install plugins
- name: Install Plugins on behalf of jenkins user {{jenkins_admin_user}}
jenkins_plugin:
name: "{{ item.key }}"
url: http://s00260415uv:8080
url_username: "{{jenkins_admin_user}}"
url_password: "{{jenkins_admin_pass}}"
environment:
http_proxy: http://<USER>:<PASS>@<PROXY_SERVER>:80
https_proxy: http://<USER>:<PASS>@<PROXY_SERVER>:80
register: plugin_result
until: plugin_result is success
retries: 10
delay: 2
with_items: # no version = latest
- { key: "ace-editor"}
- { key: "active-directory"}
- { key: "..."}