javascript: Missing Content-Type header on patch operation

Hi, Everytime I try to patch a node, I hit a HTTP 415 the server responded with the status code 415 but did not return more information.

After some researches, I found that the API wait for Content-Type: application/strategic-merge-patch+json header for PATCH operations. But this header is not set.

Is there any way I can override headers before sending the request ?

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 7
  • Comments: 22 (13 by maintainers)

Commits related to this issue

Most upvoted comments

For anyone still running into this, the solution suggested doesn’t work as described anymore as the function signature has changed slightly.

The function now has more args which need to come before the options array, also passing null makes the api client pass empty values for dryRun and co. instead of skipping it. Passing undefined instead of null works for me.

What works for me (@kubernetes/client-node@0.11.1) is:

const headers = { 'content-type': 'application/strategic-merge-patch+json' }
patchNamespacedDeployment(name, namespace, body, undefined, undefined, undefined, undefined, { headers }) 

Ok, we have the workaround nowadays to specify custom http headers with request , as described in https://github.com/kubernetes-client/javascript/issues/19#issuecomment-497534040. But this still is not the real solution to PATCH calls.

Why is “Content-Type”: “application/merge-patch+json” simply not the default for patch operations? 😦

I can take a pass at trying to change swagger-codegen.

I created an issue on swagger-codegen https://github.com/swagger-api/swagger-codegen/issues/9035 and created a strawperson proposal for a solution https://github.com/swagger-api/swagger-codegen/pull/9036.

Shouldn’t PATCH operations have the Content-Type header set to application/strategic-merge-patch+json by default. It is kind of annoying to use this client if you have to mutate the client object before every patch operation.

I.e. it would greatly improve the usability of this client if the patch operations in api.ts with:

let headerParams: any = (<any>Object).assign(
  {'Content-Type': 'application/strategic-merge-patch+json'},
  this.defaultHeaders,
);

@runarberg I agree this is what’s missing. The swagger spec clearly states that this endpoint “consumes”: ["application/json-patch+json", "application/merge-patch+json", "application/strategic-merge-patch+json"].

And since this file is generated from the swagger spec I’m guessing something have to be done over at swagger-codegen or at the gen part of this project. @brendanburns could you help out to answer this? I started looking at a permanent solution but it was a lot to go through.

For anyone who’s stumbling across this issue and needs a workaround similar to the one of @msonnabaum but a bit more generic:

const coreApi = kc.makeApiClient(PatchedCoreV1Api);

const patch: any = { spec: { replicas: 0 } };
const patchDeploymentOperation = () => appsApi.patchNamespacedDeployment('your-deployment', 'your-namespace', patch);
console.log(await appsApi.patchApiCall(patchDeploymentOperation));

class PatchedAppsV1Api extends k8s.Apps_v1Api {

    patchApiCall<T>(patchOperation: () => Promise<{ response: http.IncomingMessage; body: T; }>): Promise<{
        response: http.IncomingMessage;
        body: T;
    }> {
        const oldDefaultHeaders = this.defaultHeaders;
        this.defaultHeaders = {
            "Content-Type": "application/strategic-merge-patch+json",
            ...this.defaultHeaders,
        };
        const returnValue = patchOperation();
        this.defaultHeaders = oldDefaultHeaders;
        return returnValue;
    }
}

Right, that makes sense.

This seemed to work to solve my immediate problem:

class PatchedK8sApi extends k8s.Core_v1Api {

  patchNamespacedConfigMap(...args) {
    const oldDefaultHeaders = this.defaultHeaders;
    this.defaultHeaders = {
      "Content-Type": "application/strategic-merge-patch+json",
      ...this.defaultHeaders,
    };
    const returnValue = super.patchNamespacedConfigMap.apply(this, args);
    this.defaultHeaders = oldDefaultHeaders;
    return returnValue;
  }
}

I appreciate you reopening the issue though so we can get a client-side fix in.