Skip to content

How can I make request like this by k8s python client, the below request is made by "kubectl edit", and it works! #641

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
SailorCrane opened this issue Oct 9, 2018 · 7 comments

Comments

@SailorCrane
Copy link

SailorCrane commented Oct 9, 2018

selection_007

"spec":{"$setElementOrder/ports":[{"port":3315}],"ports":[{"nodePort":30005,"port":3315,"protocol":"TCP","targetPort":3321},{"$patch":"delete","port":3310}]}}

PATCH https://61.164.142.194:6443/api/v1/namespaces/default/services/mysql

@tomplus
Copy link
Member

tomplus commented Oct 9, 2018

Have you tried patch_namespaced_service?

@SailorCrane
Copy link
Author

SailorCrane commented Oct 10, 2018

Have you tried patch_namespaced_service?

Yes, I have tried, but I do not know what should I set to patch_namespaced_service's body parameter.

I can set service's "target_port" a new value and it works.
but I set service's "port", it do not works!

selection_014

@roycaihw
Copy link
Member

roycaihw commented Oct 12, 2018

It's intended behavior. patch_namespaced_service sends strategic-merge-patch (also please refer to this doc) by default, service.spec.ports uses "merge" patchStrategy and "port" as patchMergeKey. Therefore:

  • service.spec.ports[0].target_port = 3321 sends a patch that contains {"port":3315, "targetPort":3321}-- apiserver sees patchMergeKey is the same as existing ports[0], so ports[0].target_port gets updated.
  • service.spec.ports[0].port = 3309 sends a patch that contains {"port":3309, "targetPort":XXXX(previous value)}-- apiserver sees patchMergeKey is different from existing ports[0], so it tries appending ports[1] to the list, but fails validation.

You could try enable debugging log but passing debug=True to configuration, to see the HTTP response from apiserver. The request is likely to fail the validation in apiserver:

HTTP response body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"Service \"my-service\" is invalid: spec.ports[1].name: Duplicate value: \"tests\"","reason":"Invalid","details":{"name":"my-service","kind":"Service","causes":[{"reason":"FieldValueDuplicate","message":"Duplicate value: \"tests\"","field":"spec.ports[1].name"}]},"code":422}

kubectl edit performs client-side apply (three-way diff) and calculates the needed patch body (the python client library currently doesn't have this functionality):

  • {"nodePort":30005,"port":3315,"protocol":"TCP","targetPort":3321} appends a ports[1] with same fields except port
  • {"$patch":"delete","port":3310}]} deletes the existing ports[0]

The result appears to be spec.ports[0].port getting updated.

@SailorCrane
Copy link
Author

It's intended behavior. patch_namespaced_service sends strategic-merge-patch (also please refer to this doc) by default, service.spec.ports uses "merge" patchStrategy and "port" as patchMergeKey. Therefore:

  • service.spec.ports[0].target_port = 3321 sends a patch that contains {"port":3315, "targetPort":3321}-- apiserver sees patchMergeKey is the same as existing ports[0], so ports[0].target_port gets updated.
  • service.spec.ports[0].port = 3309 sends a patch that contains {"port":3309, "targetPort":XXXX(previous value)}-- apiserver sees patchMergeKey is different from existing ports[0], so it tries appending ports[1] to the list, but fails validation.

You could try enable debugging log but passing debug=True to configuration, to see the HTTP response from apiserver. The request is likely to fail the validation in apiserver:

HTTP response body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"Service \"my-service\" is invalid: spec.ports[1].name: Duplicate value: \"tests\"","reason":"Invalid","details":{"name":"my-service","kind":"Service","causes":[{"reason":"FieldValueDuplicate","message":"Duplicate value: \"tests\"","field":"spec.ports[1].name"}]},"code":422}

kubectl edit performs client-side apply (three-way diff) and calculates the needed patch body (the python client library currently doesn't have this functionality):

  • {"nodePort":30005,"port":3315,"protocol":"TCP","targetPort":3321} appends a ports[1] with same fields except port
  • {"$patch":"delete","port":3310}]} deletes the existing ports[0]

The result appears to be spec.ports[0].port getting updated.

OK, I see your point, thanks very much!

So I wonder how can I make a request by python client to delete the port[0] of service?

@sanster23
Copy link

Any update on this as how to make a request by python client to delete the port[0] or any spec in service ?

@tomplus
Copy link
Member

tomplus commented Feb 28, 2019

You can use json-patch which is supported by k8s and this library:

api_instance = client.CoreV1Api()

body = [{"op": "replace", "path": "/spec/ports/0/port", "value": 8080}]

api_response = api_instance.patch_namespaced_service("my-service", "my-namespace", body)

More information:

@sanster23
Copy link

sanster23 commented Mar 1, 2019

Hey thanks for the response @tomplus
I also found the RFC for this here https://tools.ietf.org/html/rfc6902
And through that found a way to delete specs via json-patch.
patch = [{'op': 'remove', 'path': '/spec/ports/1'}] client.patch_namespaced_service(name=self._component_info.get('name'), namespace=self._component_info.get('namespace'), body=patch)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants