kubernetes-client / c

Official C client library for Kubernetes
Apache License 2.0
141 stars 45 forks source link

CoreV1API_patchNamespacedPod API returns 422 error, #196

Closed ReddyArunreddy closed 1 year ago

ReddyArunreddy commented 1 year ago

Hi I'm using CoreV1API_patchNamespacedPod() API to update pod labels but getting 422 error code. not able to identify the problem. My code is as below. Pod sample-pod-0 is already present in cluster

include

include

include

include

include

include

void create_a_pod(apiClient_t apiClient) { char namespace = "default";

 char *body = "[{\"op\": \"replace\", \"path\": \"/metadata/labels/mode\", \"value\": \"stand-by\" }]";
 const char *parse_end = NULL;
 cJSON *event_json_obj = cJSON_ParseWithOpts(body, &parse_end, 1);
 if (!event_json_obj) {
         fprintf(stderr, "Cannot create JSON from string: [%s].\n", parse_end);
                 return;
 }

 object_t *update_body = object_parseFromJSON(event_json_obj);
 printf("obj=%s\n",update_body->temporary);

v1_pod_t *apod = CoreV1API_patchNamespacedPod(apiClient, "sample-pod-0",namespace,update_body , NULL, NULL, NULL,NULL,0);
printf("code=%ld Reci=%s\n", apiClient->response_code,apiClient->dataReceived);
if(apod)
{
    printf("Name=%s\n",apod->metadata->name);
    printf("Name=%s\n",apod->status->message);
    printf("Name=%s\n",apod->status->reason);
    printf("Pod_ip=%s\n",apod->status->pod_ip);
    printf("kind=%s\n",apod->kind);
}
v1_pod_free(apod);

}

int main(int argc, char *argv[]) {

int rc = 0;

char *baseName = NULL;
sslConfig_t *sslConfig = NULL;
list_t *apiKeys = NULL;
apiClient_t *k8sApiClient = NULL;

rc = load_kube_config(&baseName, &sslConfig, &apiKeys, NULL);
if (0 == rc) {
    k8sApiClient = apiClient_create_with_base_path(baseName, sslConfig, apiKeys);
} else {
    printf("Cannot load kubernetes configuration.\n");
    return -1;
}

if (k8sApiClient) {
    create_a_pod(k8sApiClient);
}

free_client_config(baseName, sslConfig, apiKeys);
baseName = NULL;
sslConfig = NULL;
apiKeys = NULL;

apiClient_free(k8sApiClient);
k8sApiClient = NULL;
apiClient_unsetupGlobalEnv();

return 0;

}

ityuhui commented 1 year ago

I'll check out this issue.

You can enable debugging as follows to check why the request headers/body are invalid (which would give a 422 error):

# If you want to use `gdb` to debug the C client library, add `-DCMAKE_BUILD_TYPE=Debug` to the cmake command line, e.g.
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/usr/local ..

There is a "patch" example that works nicely with a generic client for your reference: https://github.com/kubernetes-client/c/blob/c223563bda143b3ca9e38cbee5782b37cad040a3/examples/generic/main.c#L49

brendandburns commented 1 year ago

My guess is that the content-type which is being sent is incorrect. I think that the content-type should be application/merge-patch+json

There's some related info (for the Typescript client) here https://stackoverflow.com/questions/67520468/patch-k8s-custom-resource-with-kubernetes-client-node

ReddyArunreddy commented 1 year ago

Hello, I tried with generic API and content-type as application/merge-patch+json. now getting no error but label is not modifying @ityuhui @brendandburns . it is still showing active only.

const char *patchBody = "{\"op\": \"replace\", \"path\": \"/template/spec/metadata/labels/mode\", \"value\": \"stand-by\" }";

list_t *contentType = list_createList();
// Kubernetes supports multiple content types:

 list_addElement(contentType, "application/merge-patch+json");
char *patch = Generic_patchNamespacedResource(genericClient,"default", "sample-pod", patchBody, NULL, NULL, NULL, NULL, contentType);
printf("PATCH=%s\n", patch);

OUTPUT:
OK

PATCH={"kind":"StatefulSet","apiVersion":"apps/v1","metadata":{"name":"sample-pod","namespace":"default","uid":"d74b2293-f87e-4eb9-a964-6ef1af46468f","resourceVersion":"6765553","generation":1,"creationTimestamp":"2023-06-16T06:52:00Z","labels":{"mode":"stand-by"},"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"apps/v1\",\"kind\":\"StatefulSet\",\"metadata\":{\"annotations\":{},\"name\":\"sample-pod\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"sample-pod\"}},\"serviceName\":\"sample-pod\",\"template\":{\"metadata\":{\"labels\":{\"app\":\"sample-pod\",\"mode\":\"active\"}},\"spec\":{\"containers\":[{\"image\":\"k8_docker_server_test_image\",\"imagePullPolicy\":\"IfNotPresent\",\"name\":\"sample-pod1\"}]}}}}\n"},"managedFields":[{"manager":"kubectl-client-side-apply","operation":"Update","apiVersion":"apps/v1","time":"2023-06-16T06:52:00Z","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:kubectl.kubernetes.io/last-applied-configuration":{}}},"f:spec":{"f:podManagementPolicy":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:serviceName":{},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:mode":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"sample-pod1\"}":{".":{},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}},"f:updateStrategy":{"f:rollingUpdate":{".":{},"f:partition":{}},"f:type":{}}}}},{"manager":"kube-controller-manager","operation":"Update","apiVersion":"apps/v1","time":"2023-06-16T06:52:02Z","fieldsType":"FieldsV1","fieldsV1":{"f:status":{"f:availableReplicas":{},"f:collisionCount":{},"f:currentReplicas":{},"f:currentRevision":{},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updateRevision":{},"f:updatedReplicas":{}}},"subresource":"status"},{"manager":"unknown","operation":"Update","apiVersion":"apps/v1","time":"2023-06-19T05:09:52Z","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:mode":{}}}}}]},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"sample-pod"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"sample-pod","mode":"active"}},"spec":{"containers":[{"name":"sample-pod1","image":"k8_docker_server_test_image","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","securityContext":{},"schedulerName":"default-scheduler"}},"serviceName":"sample-pod","podManagementPolicy":"OrderedReady","updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}},"revisionHistoryLimit":10},"status":{"observedGeneration":1,"replicas":1,"readyReplicas":1,"currentReplicas":1,"updatedReplicas":1,"currentRevision":"sample-pod-948ff7778","updateRevision":"sample-pod-948ff7778","collisionCount":0,"availableReplicas":1}}

ityuhui commented 1 year ago

I tried using the following code, it works:

genericClient_t *genericClient = genericClient_create(apiClient, NULL, "v1", "pods");
const char *patchBody = "[{\"op\": \"replace\", \"path\": \"/metadata/labels/mode\", \"value\": \"stand-by\" }]";
list_t *contentType = list_createList();
list_addElement(contentType, "application/json-patch+json");
char *patch = Generic_patchNamespacedResource(genericClient, "default", "test-pod-8", patchBody, NULL, NULL, NULL, NULL, contentType);
...

I think application/merge-patch+json can also work with patchBody in some other formats.

ityuhui commented 1 year ago

FYI

CoreV1API_patchNamespacedPod returns 422 because:

A query parameter force is sent to API server in CoreV1API_patchNamespacedPod, but this field is forbidden:

{"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}

The C client cannot stop sending because there is a limitation that we have to send "int" type parameter at any time. https://github.com/kubernetes-client/c/issues/176#issuecomment-1451851482

Please use the generic client for patching now.

ydcpp commented 1 year ago

I had the same issue, I'm using generic client now and it works. Thank you @ityuhui

ReddyArunreddy commented 1 year ago

Yes it working with generic API. Thank you for support @ityuhui @brendandburns .

brendandburns commented 1 year ago

closing this as the issue is resolved (at least as resolved as it can be given #176)