将 JSON 补丁应用于 Kubernetes 自定义资源时出错

Error while applying JSON Patch to Kubernetes Custom Resource

我有一个 Kubernetes Custom Resource 实例,我想使用 JSON 补丁通过 Kubernetes API 进行修补。

这是我的 PATCH 请求:

PATCH /apis/example.com/v1alpha1/namespaces/default/mycrd/test HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
[other headers omitted for brevity...]

[
  {"op": "replace", "path": "/status/foo", value: "bar"}
]

我相当确定我的请求正文是有效的 JSON patch,并且我之前已经使用类似 API 成功更新了核心(非 CRD)API 资源电话。 CRD 有一个 openAPIV3Schema 定义,明确允许 .status.foo 存在并且属于 string.

类型

上述请求被 Kubernetes API 服务器拒绝,响应如下:

HTTP/1.1 422 Unprocessable Entity
Conent-Type: application/json
[other headers omitted for brevity...]

{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "the server rejected our request due to an error in our request",
  "reason": "Invalid",
  "details": {},
  "code": 422
}

根据 CRD documentation,CRD 应支持具有 application/json-patch+json 内容类型的 PATCH 请求。但出于某种原因,在 Kubernetes 没有告诉我 为什么 的情况下,该请求似乎无效。 API 服务器 pod 在其日志流中也没有任何相关消息。

在命令行中使用kubectl patch也会出现同样的错误:

$ kubectl patch mycrd.example.com test --type=json -p '[{"op": "replace", "path": "/status/foo", "value": "bar"}]'   
The  "" is invalid

发生此错误的可能原因是什么?我有哪些选项可以进一步调试?

在输入问题的同时找到了(或至少部分)答案...

Kubernetes API 服务器不会为 JSON 补丁输入递归创建嵌套对象。 此行为与 JSON Patch specification in RFC 6902, section A.12 一致:

A.12. Adding to a Nonexistent Target

An example target JSON document:

{ "foo": "bar" }

A JSON Patch document:

[
  { "op": "add", "path": "/baz/bat", "value": "qux" }
]

This JSON Patch document, applied to the target JSON document above, would result in an error (therefore, it would not be applied), because the "add" operation's target location that references neither the root of the document, nor a member of an existing object, nor a member of an existing array.

这就是当自定义资源没有 .status 属性 开头时原始请求失败的原因。以下两个后续调用(第二个是原始调用)将成功完成:

$ kubectl patch mycrd.example.com test --type=json \
    -p '[{"op": "replace", "path": "/status", "value": {}}]'
mycrd.example.com/test patched
$ kubectl patch mycrd.example.com test --type=json \
    -p '[{"op": "replace", "path": "/status/foo", "value": "bar"}]'
mycrd.example.com/test patched

显然,如果 属性 已经包含您想要的数据,replace 将整个 .status 属性 与 {} 合并并不是一个好主意保持。

在这种情况下 JSON 补丁的合适替代方案是 JSON Merge Patch:

PATCH /apis/example.com/v1alpha1/namespaces/default/mycrd/test HTTP/1.1
Accept: application/json
Content-Type: application/merge-patch+json
[other headers omitted for brevity...]

{
  "status": {
    "foo": "bar"
  }
}

或者,或者,使用 kubectl:

$ kubectl patch mycrd.example.com test --type=merge \
    -p '{"status": {"foo": "bar"}}'
mycrd.example.com/test patched