k8s-client(java)从6.0.1升级到11.0.0出现patch问题may not be specified for non-apply patch/cannot unmarshal...

背景:kubernetes-client/java升级,复杂的patch出现各种问题,并且没有找到解决方案,经过研究&测试,找到了解决方案,希望能帮助到使用kubernetes-client/java客户端的同学;

patch方法调用出现异常:

{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"PatchOptions.meta.k8s.io "" is invalid: force: Forbidden: may not be specified for non-apply patch","reason":"Invalid","details":{"group":"meta.k8s.io","kind":"PatchOptions","causes":[{"reason":"FieldValueForbidden","message":"Forbidden: may not be specified for non-apply patch","field":"force"}]},"code":422}

参考issue:https://github.com/kubernetes-client/java/issues/958
修改,发现并没有什么作用

于是给客户端提了issue
https://github.com/kubernetes-client/java/issues/1575
并没有什么结论…

然后查看各种文档 & 社区提问,均没有回复…

查看源码发现:
1.客户端6.0.1在使用的时候指定header是merge-patch;
2.客户端11.0.0使用的时候指定header是[json-patch,merge-patch]数组,每次都会检测json-patch,且没有参数可以控制;
所以只能支持到json-patch,使用merge的时候无论传参是什么,都无法通过;

json-patch使用方法:

      // patch方法1 json-patch 需要 zjsonpatch 包, 支持JsonArray,V1Patch 两种数据类型作为参数
      // 禁用move
//      EnumSet<DiffFlags> flags = DiffFlags.dontNormalizeOpIntoMoveAndCopy().clone();
//      JsonNode patchNode = JsonDiff.asJson(source, target, flags);

//      JsonArray finalBody = new JsonArray();
//      patchNode.forEach((JsonNode node)->{
//        JsonObject item = new JsonObject();
//        String op = node.path("op").textValue();
//        if(!"remove".equalsIgnoreCase(op)){
//          item.add("op", new JsonPrimitive(op));
//          item.add("path", new JsonPrimitive(node.path("path").textValue()));
//          if(node.path("value").isNumber()){
//            item.add("value", new JsonPrimitive(node.path("value").numberValue()));
//          } else if(node.path("value").isBoolean()){
//            item.add("value", new JsonPrimitive(node.path("value").booleanValue()));
//          } else if(node.path("value").isObject()){
//            // 不支持
//          } else {
//            item.add("value", new JsonPrimitive(node.path("value").textValue()));
//          }
//          finalBody.add(item);
//        }
//      });
//      res = apiInstance.patchNamespacedCustomObject(this.apiGroup, this.apiVersion, this.apiNameSpace, this.apiPlural, name, body, dryRun, fieldManager, null);

如果只是简单的使用,上面方法完全可以支持;
逻辑也很简单,先通过对比方法找到与老的资源文件(yml/json)不一样的地方(比如多了一行,op为add,少了了一行,则op为remove,对比方法得出的就是这样一个集合),然后通过patch方法修正,达到修改资源文件的目的,这也是最符合k8s语意的方案
注:一般的jsonPatch(标准规则)是有5种,add/remove/move/replace/copy,k8s是不支持move/copy的,所以有了上面禁用的操作;

测试下来发现需求还是不满足,因为有多个nodeGroup的场景,通过对比方法只能得到一个对象,还是没法表示一个yaml的层级关系(需求比较灵活,支持的场景过多),于是继续测试…

和k8s团队的人聊了下,一般都是通过上面的方法,先对比,再patch,也就是最符合patch语意的方案,要想支持大改资源文件的情况,就必须使用merge-patch,或者直接使用update;

于是有两个方案:1.update(直接替换)2.merge-patch;由于update可能会有并发覆盖问题,于是放弃update方案,继续测试Java客户端merge-patch;(k8s团队推荐使用go,然而切换成本过高…继续使用java客户端)

继续看源码实现:
发现最后是调用patchNamespacedCustomObjectCall方法,发出http请求到k8s。

这个方法是可以带上header参数的,即可以指定json-patch还是merge-patch;于是继续查找有没有对应的API把这个接口通过其他方式暴露出来…最后发现有个PatchUtils类是对外开放的,于是通过这个类完成实现;

逻辑很简单:绕过CustomObjectsApi这一层,调用PatchUtils自己实现;

结论

// k8s-client 11.0.0 merge patch使用方法(直接跳过CustomObjectsApi这一层)

public Object patchObject(String name, Object body) {
    Object res = null;
    try {
      // 方法2,merge-patch,json-patch都支持
      res = PatchUtils.patch(
          Object.class,
          () -> apiInstance.patchNamespacedCustomObjectCall(
              this.apiGroup, this.apiVersion, this.apiNameSpace, this.apiPlural, name,
              new V1Patch(JSON.toJSONString(body)),
              dryRun, fieldManager, null, null
          ),
          V1Patch.PATCH_FORMAT_JSON_MERGE_PATCH,
          apiInstance.getApiClient()
      );

      log.info("apiInstance.patchNamespacedCustomObject res = {}", JSON.toJSONString(res));
    } catch (ApiException e) {
      log.error("Exception when calling patchObject");
      log.error("Reason: " + e.getResponseBody());
      e.printStackTrace();
    }
    return res;
  }
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值