yaml 中的错误 - 可能是由于错误的转义,但无法识别问题

yaml 中的错误 - 可能是由于错误的转义,但无法识别问题

下面的行会导致错误 (... request body malformed."})。它是 user-data.yml 的一部分,与 cloud-init 一起使用,作为数字海洋 API 的一部分,在创建时引导服务器。

sed -ie '\$a\ \n\#Add logfile information\nlogfile /var/log/ntp.log' /etc/ntp.conf

基本上,它应该执行以下操作:

  • 追加一个空行
  • 在下一行添加注释
  • 将字符串添加到下一行

我是加载中 用户数据.yml 从一场狂欢脚本如下:

curl -X POST "https://api.digitalocean.com/v2/droplets" \
-d'{"name":"'$droplet_name'",
"region": "'$region'",
"size": "'$size'",
"image": "'$image'",
"backups":false,
"ipv6":false,
"private_networking":false,
"user_data":
"'"$(cat /user-data.yaml)"'",
  "ssh_keys": '$root_ssh_pub_key'}' \
  -H "Authorization: Bearer $api_key" \
  -H "Content-Type: application/json"

经过几个小时的黑客攻击后,我可能只是代码盲。

答案1

你的 sed 在我的系统上给出了“未终止的正则表达式”,你可能想做类似的事情:

echo abc > xy
sed -ie '$a\\n#Add logfile information\nlogfile /var/log/ntp.log' xy
cat xy

这使:

abc

#Add logfile information
logfile /var/log/ntp.log

至于 YAML 上传,我不确定这是否会影响您附加的内容。如果您无法检查上传的数据,我建议通过将其写入文件a来生成扩展的YAML xyz.yaml,然后使用curl上传它-d @xyz.yaml。这使您有机会确保上传的内容完全符合预期。

答案2

终于自己修好了...

您是否应该使用 DO API 并遵循一些教程,例如(参考文章的中间)您很可能会遇到以下错误之一。

错误1导致类似的问题...request body malformed要解决它,请确保您没有忘记转义 yaml 中的任何字符串/字符。

错误2第一眼看上去不会引发任何问题。你的 api 请求运行完毕,droplet 旋转起来,除非你没有做任何花哨的事情,否则你甚至可能不会注意到它(如果你不检查你的脚本对系统做了什么......)。但是,检查 cloud-init 的日志会显示它failed loading yaml blob。这是因为需要转义的字符没有足够频繁地转义(?!)。

例子:

  • 这将导致错误 1(错过转义美元字符):
    sed -i -e '$a\ \n#Add logfile information\nlogfile /var/log/ntp.log' /etc/ntp.conf
  • 这将导致错误 2(美元被巧妙地转义,但这里单次转义还不够......):
    sed -i -e '\$a\ \n#Add logfile information\nlogfile /var/log/ntp.log' /etc/ntp.conf

所以解决办法是:
sed -i -e '\\$a\\ \\n#Add logfile information\\nlogfile /var/log/ntp.log' /etc/ntp.conf

如果有人真的仍在阅读并理解我有多么沮丧,请告诉我您是否可以解释这里发生的事情...... - 实际上我并不热衷于再次深入研究这个问题,但另一方面我总是愿意学习。

我能理解的唯一原因是:当 bask 脚本拉取一个 yaml 并通过远程 shell 以 json 形式发送时,它需要更多的“照顾”,因此需要双重转义?!

答案3

您似乎想要上传一个包含多个键和值的 JSON 文档。该文件将是畸形的如果任何值无效,例如,如果字符串包含需要特定 JSON 编码的字符,例如文字换行符和双引号等。

为了确保所有值都正确编码,如果您使用某种形式的 JSON 处理工具将会有所帮助,例如,jq或者jo。使用这样的工具比通过转义各种字符等手动准备数据更好。


使用jq

jq -cn \
    --arg name "$droplet_name" \
    --arg size "$size" \
    --arg image "$image" \
    --argjson backups false \
    --argjson ipv6 false \
    --argjson private_networking false \
    --arg user_data "$(cat /user-data.yml)" \
    --arg ssh_keys "$root_ssh_pub_key" \
    '$ARGS.named'

基本上,用于--arg任何字符串,以及--argjson非字符串的值(数字和布尔值)。

如果/user-data.yml是一个大文档,您可能希望让其jq读取它,而不是在命令行上扩展它:

jq -cn -Rs \
    --arg name "$droplet_name" \
    --arg size "$size" \
    --arg image "$image" \
    --argjson backups false \
    --argjson ipv6 false \
    --argjson private_networking false \
    --arg ssh_keys "$root_ssh_pub_key" \
    '$ARGS.named + { user_data: input }' /user-data.yml

这也将保留文件的最后换行符/use-data.yml


jo

jo \
    name="$droplet_name" \
    size="$size" \
    image="$image" \
    backups=false \
    ipv6=false \
    private_networking=false \
    user_data=@/user-data.yml \
    ssh_keys="$root_ssh_pub_key"

在这里,我使用@letjo读取和编码用户数据文件。我本来可以使用use_data="$(cat ...)",但由于这样需要输入更多内容,所以我选择了更短、更易于阅读的变体。另请注意,它jo会自动推断值的类型,因此您将得到null缺失值,而不是空字符串。


无论您使用哪个命令,您都可以将该命令的输出直接传输到curl

json-generating-command |
curl --silent --show-error \
    --header "Authorization: Bearer $api_key" \
    --json @- 'some-URL-here'

相关内容