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

hel*_*ert 3 kubernetes kubectl kubernetes-custom-resources

我有一个 Kubernetes自定义资源的实例,我想使用 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"}
]
Run Code Online (Sandbox Code Playgroud)

我相当确定我的请求正文是一个有效的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
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

hel*_*ert 11

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

Kubernetes API 服务器不会为 JSON 补丁输入递归创建嵌套对象。此行为与RFC 6902 第 A.12 节中JSON 补丁规范一致:

A.12. 添加到不存在的目标

示例目标 JSON 文档:

{ "foo": "bar" }
Run Code Online (Sandbox Code Playgroud)

一个 JSON 补丁文档:

[
  { "op": "add", "path": "/baz/bat", "value": "qux" }
]
Run Code Online (Sandbox Code Playgroud)

这个 JSON Patch 文档,应用于上面的目标 JSON 文档,将导致错误(因此,它不会被应用),因为“添加”操作的目标位置既不引用文档的根,也不引用文档的成员现有对象,也不是现有数组的成员。

这就是原始请求失败的原因,当自定义资源没有.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
Run Code Online (Sandbox Code Playgroud)

显然,如果该属性已经包含您想要保留的数据,replace则将整个.status属性使用ing{}并不是一个好主意。

在这种情况下,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"
  }
}
Run Code Online (Sandbox Code Playgroud)

或者,或者,使用kubectl

$ kubectl patch mycrd.example.com test --type=merge \
    -p '{"status": {"foo": "bar"}}'
mycrd.example.com/test patched
Run Code Online (Sandbox Code Playgroud)