client icon indicating copy to clipboard operation
client copied to clipboard

PATCH API

Open t089 opened this issue 10 months ago • 2 comments

This is to open a discussion on how to support PATCH API. Are there already some thoughts / ideas how this could work? The k8s API supports both JSON Patch and JSON Merge Patch requests.

t089 avatar Mar 14 '25 15:03 t089

@t089 Hey there, take a look at this, any feedback is much appreciated!!

I have a draft implementation for (Strategic) Merge Patch, which I tried to make type-safe.

It would look like this:

// Construct an instance of some resource, e.g. a Deployment
// Populate only fields that should be patched
let deployment = apps.v1.Deployment(
  spec: apps.v1.DeploymentSpec(
    replicas: 2,
    selector: meta.v1.LabelSelector(),
    template: core.v1.PodTemplateSpec(
      spec: core.v1.PodSpec(
        containers: [
          core.v1.Container(
            image: "nginx",
            name: "patch-demo"
          )
        ]
      )
    )
  )
)

// Create an instance of MergePatch
let patch: MergePatch = deployment.mergePatch()!
// OR
// Create an instance of Strategic MergePatch
let patch: MergePatch = deployment.strategicMergePatch()!

// Patch the resource
let patched = try! await client.appsV1.deployments.patch(in: .default, resource: "some-deployment", patch)

The MergePatch instance above would contain only non-nil and non-empty fields of the resource and would be serialised to the following JSON:

{
  "apiVersion": "apps/v1"
  "kind": "Deployment"
  "spec": {
    "replicas": 2,
    "template": {
       "spec": {
          "containers": [
            {
              "name": "patch-demo",
              "image": "nginx"
            }
          ]
       }
    }
  }
}

PS: apiVersion and kind are ignored by the API Server.


A MergePatch instance can also be loaded from a YAML file:

let patch = try MergePatch.load(contentsOf: "/some/path/to/manifest.yaml")

iabudiab avatar Mar 17 '25 22:03 iabudiab

Sounds interesting. Of course, this only works if the structs have only optional fields. Is this currently the case?

How would you "nil-out" a value? Is this even a thing?

Eg, if you want to remove only the annotation with name "xyz" what would you do? The patch should look like this, shouldn't it?

{
  "meta": {
    "annotations": {
      "xyz": null
    }
  }
}

t089 avatar Mar 21 '25 05:03 t089