【问题标题】:multi-line yaml input to mustache template outputs as JSON多行 yaml 输入到 mustache 模板输出为 JSON
【发布时间】:2018-12-18 09:23:28
【问题描述】:

我有以下shell sn-p

inputs="ingress_test_inputs.yaml"
auth_annotations="    # type of authentication
    nginx.ingress.kubernetes.io/auth-type: basic
    # name of the secret that contains the user/password definitions
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    # message to display with an appropriate context why the authentication is required
    nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'"
echo "---" >$inputs
echo "namespace: qa" >> $inputs
echo "auth_annotations: ${auth_annotations}" >> $inputs

echo "----- Ingress inputs (${inputs}) -----"
cat $inputs

echo 'apiversion: extenstions/v1beta
kind: Ingress
metadata:
  name: aname
  annotations:
    kubernetes.io/ingress.class: "nginx-internal"
    nginx.ingress.kubernetes.io/server-snippet: |
      add_header Content-Security-Policy      "frame-ancestors 'self'";
      {{{auth_annotations}}}
spec:
  rules:
    - host: bla-bla-bla.{{namespace}}.example.com' >ingress.mustache
echo "----- Raw Ingress (ingress.mustache): -----"
cat ingress.mustache

mustache $inputs ingress.mustache > ingress-1.0.yaml

echo "----- Will apply the following ingress: -----"
cat ingress-1.0.yaml

但是,当我运行此命令时,auth_annotations 的输出似乎被转换为 JSON 格式(元素之间有 =>,末尾有逗号)(参见规范之前的行:)...

----- Ingress inputs (ingress_test_inputs.yaml) -----
---
namespace: qa
auth_annotations:     # type of authentication
    nginx.ingress.kubernetes.io/auth-type: basic
    # name of the secret that contains the user/password definitions
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    # message to display with an appropriate context why the authentication is required
    nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'
----- Raw Ingress (ingress.mustache): -----
apiversion: extenstions/v1beta
kind: Ingress
metadata:
  name: aname
  annotations:
    kubernetes.io/ingress.class: "nginx-internal"
    nginx.ingress.kubernetes.io/server-snippet: |
      add_header Content-Security-Policy      "frame-ancestors self";
      {{{auth_annotations}}}
----- Will apply the following ingress: -----
apiversion: extenstions/v1beta
kind: Ingress
metadata:
  name: aname
  annotations:
    kubernetes.io/ingress.class: "nginx-internal"
    nginx.ingress.kubernetes.io/server-snippet: |
      add_header Content-Security-Policy      "frame-ancestors self";
      {"nginx.ingress.kubernetes.io/auth-type"=>"basic", "nginx.ingress.kubernetes.io/auth-secret"=>"basic-auth", "nginx.ingress.kubernetes.io/auth-realm"=>"Authentication Required - foo"}

我原以为我的原始 YAML 会完整地粘贴到这些行中。它甚至剥离了 cmets(我并不真正关心)但是,这不是我所期望的行为。为什么 mustache 对待多行输入与单行输入不同?

我曾尝试搜索类似的问题,但未能找到答案。

编辑:添加了单行变量,用于比较输入。

【问题讨论】:

    标签: json templates yaml mustache


    【解决方案1】:

    这里有几个问题:

    • Mustache 不知道 ingress.mustache 是一个 YAML 文件。它不会将其解析为 YAML,实际上假定它是 HTML(因为它会使用 HTML 实体转义 HTML 特殊字符)。

      由此产生的问题是,当提示插入您的值时,mustache 不知道当前的缩进等,因此它无法粘贴您的 YAML,同时通过添加到您的原始缩进中来保持相对结构完整。 $inputs.

    • Mustache 使用 YAML 进行输入,但不用于输出。您看到的是 mustache 对 auth_annotations 值的表示。 Mustache 已将 YAML 解析为内部结构,但不知道您希望将其呈现为 YAML。

    • 问题不在于单行与多行,而是简单内容(标量)与复杂内容(映射)。

    为了能够正确缩进你的变量,你必须带着小胡子走过结构并通过一个部分将它们插入。然而,mustache 不能遍历映射值,只能遍历序列。所以你需要做的是让你的输入成为一个可以通过 mustache 迭代的序列:

    auth_annotations="    # type of authentication
        - {key: nginx.ingress.kubernetes.io/auth-type, value: basic}
          # name of the secret that contains the user/password definitions
        - {key: nginx.ingress.kubernetes.io/auth-secret, value: basic-auth}
          # message to display with an appropriate context why the authentication is required
        - {key: nginx.ingress.kubernetes.io/auth-realm, value: 'Authentication Required - foo'}"
    

    然后,您可以通过修改模板插入它,使用小胡子部分迭代您的列表:

    echo 'apiversion: extenstions/v1beta
    kind: Ingress
    metadata:
      name: aname
      annotations:
        kubernetes.io/ingress.class: "nginx-internal"
        nginx.ingress.kubernetes.io/server-snippet: |
          add_header Content-Security-Policy      "frame-ancestors 'self'";
        {{#auth_annotations}}
        {{key}}: {{value}}
        {{/auth_annotations}}
    spec:
      rules:
        - host: bla-bla-bla.{{namespace}}.example.com' >ingress.mustache
    

    【讨论】:

    • 我在 YAML 周围放了三重 {{{,这样它就不会认为它是 HTML。当然,如果它对输出是 YAML 一无所知,它应该将其粘贴为纯文本而不假设我想要 JSON?缩进可能有点混乱。我试过使用 bash 版本的“mo”,这实际上可以按预期工作,所以我认为这是 Ruby 版本的 mustache 中的一个错误。
    • 问题是 afaik mustache 没有定义如何呈现复杂值(映射和序列)。由于 YAML 已经被解析,它不像“粘贴纯文本”那么容易,因为该值现在是内存中的结构,而不仅仅是 YAML 节点的文本。渲染 YAML 是一个复杂的过程,所以 mustache 渲染一个简化的形式是可以理解的——也就是说,btw,而不是 JSON。事实上,如果它是 JSON,它也是有效的 YAML,因为 YAML 是 JSON 的超集。 JSON 没有=>
    • 我刚刚检查过:您得到的是 Ruby 呈现 hash(用于将 YAML 映射解析为的原生 Ruby 数据结构)。另请注意,YAML 通常不是往返的,因为(其中)YAML 定义映射中键的顺序不能传达内容信息,因此大多数实现将其解析为无序的 dict/hash/map。这意味着即使 mustache 将结构呈现为 YAML,也不能保证您以相同的顺序获取密钥。
    【解决方案2】:

    这是脚本的精简版本,它演示了问题。

    inputs="ingress_test_inputs.yaml"
    auth_annotations="
        foo: bar baz
        sam: jam man"
    echo "namespace: qa" > $inputs
    echo "auth_annotations: ${auth_annotations}" >> $inputs
    
    echo "----- Ingress inputs (${inputs}) -----"
    cat $inputs
    
    echo '---
    metadata:
      annotations:
        nginx/thing:
          another_thing:
          {{{auth_annotations}}}
    spec:
      rules:
        - host: bla-bla-bla.{{{ namespace }}}.example.com' >ingress.mustache
    
    echo "----- Raw Ingress (ingress.mustache): -----"
    cat ingress.mustache
    
    mustache $inputs ingress.mustache > ingress-1.0.yaml
    
    echo "----- Will apply the following ingress: -----"
    cat ingress-1.0.yaml
    

    这给出了与我发布的原始问题相同的问题。

    ----- Ingress inputs (ingress_test_inputs.yaml) -----
    namespace: qa
    auth_annotations:
        foo: bar baz
        sam: jam man
    ----- Raw Ingress (ingress.mustache): -----
    ---
    metadata:
      annotations:
        nginx/thing:
          another_thing:
          {{{auth_annotations}}}
    spec:
      rules:
        - host: bla-bla-bla.{{{ namespace }}}.example.com
    ----- Will apply the following ingress: -----
    ---
    metadata:
      annotations:
        nginx/thing:
          another_thing:
          {"foo"=>"bar baz", "sam"=>"jam man"}
    spec:
      rules:
        - host: bla-bla-bla.qa.example.com
    

    但是,如果我将“mustache”替换为 bash 实现的“mo”,它可以工作:-

    ----- Ingress inputs (ingress_test_inputs.yaml) -----
    namespace: qa
    auth_annotations:
        foo: bar baz
        sam: jam man
    ----- Raw Ingress (ingress.mustache): -----
    ---
    metadata:
      annotations:
        nginx/thing:
          another_thing:
          {{{auth_annotations}}}
    spec:
      rules:
        - host: bla-bla-bla.{{{ namespace }}}.example.com
    ----- Will apply the following ingress: -----
    namespace: qa
    auth_annotations:
        foo: bar baz
        sam: jam man
    ---
    metadata:
      annotations:
        nginx/thing:
          another_thing:
    
    spec:
      rules:
        - host: bla-bla-bla..example.com
    

    在我看来,这表明 Ruby gem 中存在错误,因为当它作为输出的一部分时,它不能正确处理 YAML。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多