使用jq在JSON数组中添加或更新对象

kod*_*nja 1 json jq

我正在尝试使用jq解析 JSON(oc process ...实际上是 OpenShift命令的输出),并使用新的键/值对添加/更新 aenv数组container

样本输入

{
  "kind": "List",
  "apiVersion": "v1",
  "metadata": {},
  "items": [
    {
      "apiVersion": "v1",
      "kind": "Service",
      "metadata": {
        "annotations": {
          "description": "Exposes and load balances the node.js application pods"
        },
        "name": "myapp-web"
      },
      "spec": {
        "ports": [
          {
            "name": "web",
            "port": 3000,
            "protocol": "TCP",
            "targetPort": 3000
          }
        ],
        "selector": {
          "name": "myapp"
        }
      }
    },
    {
      "apiVersion": "v1",
      "kind": "Route",
      "metadata": {
        "name": "myapp-web"
      },
      "spec": {
        "host": "app.internal.io",
        "port": {
          "targetPort": "web"
        },
        "to": {
          "kind": "Service",
          "name": "myapp-web"
        }
      }
    },
    {
      "apiVersion": "v1",
      "kind": "DeploymentConfig",
      "metadata": {
        "annotations": {
          "description": "Defines how to deploy the application server"
        },
        "name": "myapp"
      },
      "spec": {
        "replicas": 1,
        "selector": {
          "name": "myapp"
        },
        "strategy": {
          "type": "Rolling"
        },
        "template": {
          "metadata": {
            "labels": {
              "name": "myapp"
            },
            "name": "myapp"
          },
          "spec": {
            "containers": [
              {
                "env": [
                  {
                    "name": "A_ENV",
                    "value": "a-value"
                  }
                ],
                "image": "node",
                "name": "myapp-node",
                "ports": [
                  {
                    "containerPort": 3000,
                    "name": "app",
                    "protocol": "TCP"
                  }
                ]
              }
            ]
          }
        },
        "triggers": [
          {
            "type": "ConfigChange"
          }
        ]
      }
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

在这个 JSON 中,我想执行以下操作:

  • 找到DeploymentConfig对象
  • 检查它是否env在第一个容器中有数组
  • 如果是,则{"name": "B_ENV", "value": "b-value"}在其中添加一个新对象
  • 如果没有,添加env 阵列,与对象{"name": "B_ENV", "value": "b-value"}在它

到目前为止,我能够解决其中的一部分,在那里我能够找到相关的对象,并将新的 env var 添加到容器中:

{
  "kind": "List",
  "apiVersion": "v1",
  "metadata": {},
  "items": [
    {
      "apiVersion": "v1",
      "kind": "Service",
      "metadata": {
        "annotations": {
          "description": "Exposes and load balances the node.js application pods"
        },
        "name": "myapp-web"
      },
      "spec": {
        "ports": [
          {
            "name": "web",
            "port": 3000,
            "protocol": "TCP",
            "targetPort": 3000
          }
        ],
        "selector": {
          "name": "myapp"
        }
      }
    },
    {
      "apiVersion": "v1",
      "kind": "Route",
      "metadata": {
        "name": "myapp-web"
      },
      "spec": {
        "host": "app.internal.io",
        "port": {
          "targetPort": "web"
        },
        "to": {
          "kind": "Service",
          "name": "myapp-web"
        }
      }
    },
    {
      "apiVersion": "v1",
      "kind": "DeploymentConfig",
      "metadata": {
        "annotations": {
          "description": "Defines how to deploy the application server"
        },
        "name": "myapp"
      },
      "spec": {
        "replicas": 1,
        "selector": {
          "name": "myapp"
        },
        "strategy": {
          "type": "Rolling"
        },
        "template": {
          "metadata": {
            "labels": {
              "name": "myapp"
            },
            "name": "myapp"
          },
          "spec": {
            "containers": [
              {
                "env": [
                  {
                    "name": "A_ENV",
                    "value": "a-value"
                  }
                ],
                "image": "node",
                "name": "myapp-node",
                "ports": [
                  {
                    "containerPort": 3000,
                    "name": "app",
                    "protocol": "TCP"
                  }
                ]
              }
            ]
          }
        },
        "triggers": [
          {
            "type": "ConfigChange"
          }
        ]
      }
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

这能够按预期插入新的 env var,但输出是一个数组,如下所示。此外,它不处理env数组可能根本不存在的部分。

我希望能够产生与输入相同的输出,但添加了新的 env var。

示例输出

[
  {
    "apiVersion": "v1",
    "kind": "Service",
    "metadata": {
      "annotations": {
        "description": "Exposes and load balances the node.js application pods"
      },
      "name": "myapp-web"
    },
    "spec": {
      "ports": [
        {
          "name": "web",
          "port": 3000,
          "protocol": "TCP",
          "targetPort": 3000
        }
      ],
      "selector": {
        "name": "myapp"
      }
    }
  },
  {
    "apiVersion": "v1",
    "kind": "Route",
    "metadata": {
      "name": "myapp-web"
    },
    "spec": {
      "host": "app.internal.io",
      "port": {
        "targetPort": "web"
      },
      "to": {
        "kind": "Service",
        "name": "myapp-web"
      }
    }
  },
  {
    "apiVersion": "v1",
    "kind": "DeploymentConfig",
    "metadata": {
      "annotations": {
        "description": "Defines how to deploy the application server"
      },
      "name": "myapp"
    },
    "spec": {
      "replicas": 1,
      "selector": {
        "name": "myapp"
      },
      "strategy": {
        "type": "Rolling"
      },
      "template": {
        "metadata": {
          "labels": {
            "name": "myapp"
          },
          "name": "myapp"
        },
        "spec": {
          "containers": [
            {
              "env": [
                {
                  "name": "A_ENV",
                  "value": "a-value"
                },
                {
                  "name": "B_ENV",
                  "value": "b-value"
                }
              ],
              "image": "node",
              "name": "myapp-node",
              "ports": [
                {
                  "containerPort": 3000,
                  "name": "app",
                  "protocol": "TCP"
                }
              ]
            }
          ]
        }
      },
      "triggers": [
        {
          "type": "ConfigChange"
        }
      ]
    }
  }
]
Run Code Online (Sandbox Code Playgroud)

这是可行的,还是与jq.

编辑 1

我刚刚意识到env数组的条件添加/更新已经由|=语法处理了!所以,我基本上只需要能够获得输入相同的结构,并在相关数组中添加相关的 env var。

Jef*_*ado 6

您几乎拥有它,但您需要重新构建过滤器以保留完整结果。您要确保没有任何过滤器更改上下文。通过从 开始.items,您将它从根对象更改为items数组。这本身并不是什么大问题,但你用它做什么很重要。请记住,由于应用了更改,分配/更新会保留原始上下文。因此,如果您根据更新编写过滤器,它将对您有用。

为此,您需要找到项目中env第一个容器的数组DeploymentConfig。首先让我们发现:

.items[] | select(.kind == "DeploymentConfig").spec.template.spec.containers[0].env
Run Code Online (Sandbox Code Playgroud)

您不需要在错误处理方面做任何其他事情,因为它select不会产生任何结果。从那里,您只需要通过添加新值来更新数组。

(.items[] | select(.kind == "DeploymentConfig").spec.template.spec.containers[0].env) +=
    [{name:"B_ENV",value:"b-value"}]
Run Code Online (Sandbox Code Playgroud)

如果数组存在,它将添加新项目。如果没有,它将创建一个新env数组。如果env不是数组,那将是一个不同的问题。