kubernetes-client / javascript

Javascript client
Apache License 2.0
2.01k stars 512 forks source link

Patch pvc to expand and get "Unsupported Media Type" Error #1734

Closed Daxcor69 closed 3 months ago

Daxcor69 commented 3 months ago

Describe the bug I am using openebs replicated storage. It has volume expansion available in the CSI. I am able to expand the volumes manually with no issues. When I try to expand the volume using the javascript client I get an error.

Client Version 0.20.0

Server Version k8s 1.29.2

To Reproduce run the following code

Expected behavior To see the volume in k8s change from 5Gi to 10Gi

Example Code

const k8s = require("@kubernetes/client-node");
const kc = new k8s.KubeConfig();
kc.loadFromCluster();
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);

  const patch = [
    {
      op: "replace",
      path: "/spec/resources/requests/",
      storage: '10Gi',
    },
  ];

  try {
    await k8sApi.patchNamespacedPersistentVolumeClaim(
      'demo',
      "fvtt",
      patch,
      undefined,
      undefined,
      undefined,
      undefined,
      { headers: { "Content-type": k8s.PatchUtils.PATCH_FORMAT_JSON_PATCH } }
    );

    return res.status(200).send("true");
  } catch (e) {
    errorLog("k8sCont", "expandVolume", e.message, req, "error");
    return res.status(200).send("false");
  }

Environment (please complete the following information):

Additional context Add any other context about the problem here.

Daxcor69 commented 3 months ago

I have also tried v 0.21.0 same result

mstruebing commented 3 months ago

Could you attach the error to the issue as well? Currently we only see your code but not what the error says which could help a lot. 🫶

Daxcor69 commented 3 months ago

HttpError: HTTP request failed
    at Request._callback (C:\Users\bradl\Projects\applications-maya\kubernetes_api\node_modules\@kubernetes\client-node\dist\gen\api\coreV1Api.js:12542:36)
    at self.callback (C:\Users\bradl\Projects\applications-maya\kubernetes_api\node_modules\request\request.js:185:22)
    at Request.emit (node:events:514:28)
    at Request.<anonymous> (C:\Users\bradl\Projects\applications-maya\kubernetes_api\node_modules\request\request.js:1154:10)
    at Request.emit (node:events:514:28)
    at IncomingMessage.<anonymous> (C:\Users\bradl\Projects\applications-maya\kubernetes_api\node_modules\request\request.js:1076:12)
    at Object.onceWrapper (node:events:628:28)
    at IncomingMessage.emit (node:events:526:35)
    at endReadableNT (node:internal/streams/readable:1359:12)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
  response: <ref *1> IncomingMessage {
    _readableState: ReadableState {
      objectMode: false,
      highWaterMark: 16384,
      buffer: BufferList { head: null, tail: null, length: 0 },
      length: 0,
      pipes: [],
      flowing: true,
      ended: true,
      endEmitted: true,
      reading: false,
      constructed: true,
      sync: true,
      needReadable: false,
      emittedReadable: false,
      readableListening: false,
      resumeScheduled: false,
      errorEmitted: false,
      emitClose: true,
      autoDestroy: true,
      destroyed: true,
      errored: null,
      closed: true,
      closeEmitted: true,
      defaultEncoding: 'utf8',
      awaitDrainWriters: null,
      multiAwaitDrain: false,
      readingMore: true,
      dataEmitted: true,
      decoder: null,
      encoding: null,
      [Symbol(kPaused)]: false
    },
    _events: [Object: null prototype] {
      end: [Array],
      close: [Array],
      data: [Function (anonymous)],
      error: [Function (anonymous)]
    },
    _eventsCount: 4,
    _maxListeners: undefined,
    socket: TLSSocket {
      _tlsOptions: [Object],
      _secureEstablished: true,
      _securePending: false,
      _newSessionPending: false,
      _controlReleased: true,
      secureConnecting: false,
      _SNICallback: null,
      servername: 'k8s.vm.foundryserver.com',
      alpnProtocol: false,
      authorized: true,
      authorizationError: null,
      encrypted: true,
      _events: [Object: null prototype],
      _eventsCount: 10,
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: 'k8s.vm.foundryserver.com',
      _closeAfterHandlingError: false,
      _readableState: [ReadableState],
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: undefined,
      _server: null,
      ssl: [TLSWrap],
      _requestCert: true,
      _rejectUnauthorized: true,
      parser: null,
      _httpMessage: [ClientRequest],
      [Symbol(res)]: [TLSWrap],
      [Symbol(verified)]: true,
      [Symbol(pendingSession)]: null,
      [Symbol(async_id_symbol)]: 1609,
      [Symbol(kHandle)]: [TLSWrap],
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: null,
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kCapture)]: false,
      [Symbol(kSetNoDelay)]: false,
      [Symbol(kSetKeepAlive)]: false,
      [Symbol(kSetKeepAliveInitialDelay)]: 0,
      [Symbol(kBytesRead)]: 0,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(connect-options)]: [Object]
    },
    httpVersionMajor: 1,
    httpVersionMinor: 1,
    httpVersion: '1.1',
    complete: true,
    rawHeaders: [
      'Audit-Id',
      'ffaabc4d-a76e-4805-9481-41fba61d5029',
      'Cache-Control',
      'no-cache, private',
      'Content-Type',
      'application/json',
      'X-Kubernetes-Pf-Flowschema-Uid',
      'fbf0df97-bf33-410e-bd8b-5c01c6691c74',
      'X-Kubernetes-Pf-Prioritylevel-Uid',
      '33093343-c494-4765-a2d5-7e6a67ee1469',
      'Date',
      'Wed, 19 Jun 2024 13:06:43 GMT',
      'Content-Length',
      '165',
      'Connection',
      'close'
    ],
    rawTrailers: [],
    joinDuplicateHeaders: undefined,
    aborted: false,
    upgrade: false,
    url: '',
    method: null,
    statusCode: 415,
    statusMessage: 'Unsupported Media Type',
    client: TLSSocket {
      _tlsOptions: [Object],
      _secureEstablished: true,
      _securePending: false,
      _newSessionPending: false,
      _controlReleased: true,
      secureConnecting: false,
      _SNICallback: null,
      servername: 'k8s.vm.foundryserver.com',
      alpnProtocol: false,
      authorized: true,
      authorizationError: null,
      encrypted: true,
      _events: [Object: null prototype],
      _eventsCount: 10,
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: 'k8s.vm.foundryserver.com',
      _closeAfterHandlingError: false,
      _readableState: [ReadableState],
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: undefined,
      _server: null,
      ssl: [TLSWrap],
      _requestCert: true,
      _rejectUnauthorized: true,
      parser: null,
      _httpMessage: [ClientRequest],
      [Symbol(res)]: [TLSWrap],
      [Symbol(verified)]: true,
      [Symbol(pendingSession)]: null,
      [Symbol(async_id_symbol)]: 1609,
      [Symbol(kHandle)]: [TLSWrap],
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: null,
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kCapture)]: false,
      [Symbol(kSetNoDelay)]: false,
      [Symbol(kSetKeepAlive)]: false,
      [Symbol(kSetKeepAliveInitialDelay)]: 0,
      [Symbol(kBytesRead)]: 0,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(connect-options)]: [Object]
    },
    _consuming: false,
    _dumped: false,
    req: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 5,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      strictContentLength: false,
      _contentLength: 70,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: false,
      socket: [TLSSocket],
      _header: 'PATCH /api/v1/namespaces/fvtt/persistentvolumeclaims/demo?force%5Bheaders%5D%5BContent-type%5D=application%2Fjson-patch%2Bjson HTTP/1.1\r\n' +
        'Accept: application/json\r\n' +
        'host: k8s.vm.foundryserver.com:6443\r\n' +
        'content-type: application/json\r\n' +
        'content-length: 70\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'PATCH',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      joinDuplicateHeaders: undefined,
      path: '/api/v1/namespaces/fvtt/persistentvolumeclaims/demo?force%5Bheaders%5D%5BContent-type%5D=application%2Fjson-patch%2Bjson',
      _ended: true,
      res: [Circular *1],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'k8s.vm.foundryserver.com',
      protocol: 'https:',
      [Symbol(kCapture)]: false,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype],
      [Symbol(errored)]: null,
      [Symbol(kHighWaterMark)]: 16384,
      [Symbol(kRejectNonStandardBodyWrites)]: false,
      [Symbol(kUniqueHeaders)]: null
    },
    request: Request {
      _events: [Object: null prototype],
      _eventsCount: 5,
      _maxListeners: undefined,
      method: 'PATCH',
      headers: [Object],
      uri: [Url],
      useQuerystring: false,
      body: '[{"op":"replace","path":"/spec/resources/requests/","storage":"10Gi"}]',
      ca: <Buffer 2d 2d 2d 2d 2d 42 45 47 49 4e 20 43 45 52 54 49 46 49 43 41 54 45 2d 2d 2d 2d 2d 0a 4d 49 49 44 42 54 43 43 41 65 32 67 41 77 49 42 41 67 49 49 47 55 ... 1057 more bytes>,
      cert: <Buffer 2d 2d 2d 2d 2d 42 45 47 49 4e 20 43 45 52 54 49 46 49 43 41 54 45 2d 2d 2d 2d 2d 0a 4d 49 49 44 4b 54 43 43 41 68 47 67 41 77 49 42 41 67 49 49 4b 53 ... 1105 more bytes>,
      key: <Buffer 2d 2d 2d 2d 2d 42 45 47 49 4e 20 52 53 41 20 50 52 49 56 41 54 45 20 4b 45 59 2d 2d 2d 2d 2d 0a 4d 49 49 45 6f 77 49 42 41 41 4b 43 41 51 45 41 2b 4a ... 1625 more bytes>,
      callback: [Function (anonymous)],
      readable: true,
      writable: true,
      explicitMethod: true,
      _qs: [Querystring],
      _auth: [Auth],
      _oauth: [OAuth],
      _multipart: [Multipart],
      _redirect: [Redirect],
      _tunnel: [Tunnel],
      setHeader: [Function (anonymous)],
      hasHeader: [Function (anonymous)],
      getHeader: [Function (anonymous)],
      removeHeader: [Function (anonymous)],
      localAddress: undefined,
      pool: [Object],
      dests: [],
      __isRequestRequest: true,
      _callback: [Function (anonymous)],
      proxy: null,
      tunnel: true,
      setHost: true,
      originalCookieHeader: undefined,
      _disableCookies: true,
      _jar: undefined,
      port: '6443',
      host: 'k8s.vm.foundryserver.com',
      url: [Url],
      path: '/api/v1/namespaces/fvtt/persistentvolumeclaims/demo?force%5Bheaders%5D%5BContent-type%5D=application%2Fjson-patch%2Bjson',
      _json: true,
      httpModule: [Object],
      agentClass: [Function: Agent],
      agent: [Agent],
      _started: true,
      href: 'https://k8s.vm.foundryserver.com:6443/api/v1/namespaces/fvtt/persistentvolumeclaims/demo?force%5Bheaders%5D%5BContent-type%5D=application%2Fjson-patch%2Bjson',
      req: [ClientRequest],
      ntick: true,
      response: [Circular *1],
      originalHost: 'k8s.vm.foundryserver.com:6443',
      originalHostHeaderName: 'host',
      responseContent: [Circular *1],
      _destdata: true,
      _ended: true,
      _callbackCalled: true,
      [Symbol(kCapture)]: false
    },
    toJSON: [Function: responseToJSON],
    caseless: Caseless { dict: [Object] },
    body: {
      kind: 'Status',
      apiVersion: 'v1',
      metadata: {},
      status: 'Failure',
      message: '415: Unsupported Media Type',
      reason: 'UnsupportedMediaType',
      details: {},
      code: 415
    },
    [Symbol(kCapture)]: false,
    [Symbol(kHeaders)]: {
      'audit-id': 'ffaabc4d-a76e-4805-9481-41fba61d5029',
      'cache-control': 'no-cache, private',
      'content-type': 'application/json',
      'x-kubernetes-pf-flowschema-uid': 'fbf0df97-bf33-410e-bd8b-5c01c6691c74',
      'x-kubernetes-pf-prioritylevel-uid': '33093343-c494-4765-a2d5-7e6a67ee1469',
      date: 'Wed, 19 Jun 2024 13:06:43 GMT',
      'content-length': '165',
      connection: 'close'
    },
    [Symbol(kHeadersCount)]: 16,
    [Symbol(kTrailers)]: null,
    [Symbol(kTrailersCount)]: 0
  },
  body: {
    kind: 'Status',
    apiVersion: 'v1',
    metadata: {},
    status: 'Failure',
    message: '415: Unsupported Media Type',
    reason: 'UnsupportedMediaType',
    details: {},
    code: 415
  },
  statusCode: 415
}```
mstruebing commented 3 months ago

Can you try to add another undefined? As far as I can see you are setting the headers object to the force property.

    await k8sApi.patchNamespacedPersistentVolumeClaim(
      'demo',
      "fvtt",
      patch,
      undefined,
      undefined,
      undefined,
      undefined,
      undefined,
      { headers: { "Content-type": k8s.PatchUtils.PATCH_FORMAT_JSON_PATCH } }
    );

The signature of this function is:

    /**
     * partially update the specified PersistentVolumeClaim
     * @param name name of the PersistentVolumeClaim
     * @param namespace object name and auth scope, such as for teams and projects
     * @param body 
     * @param pretty If \&#39;true\&#39;, then the output is pretty printed.
     * @param dryRun When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed
     * @param fieldManager fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).
     * @param fieldValidation fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.
     * @param force Force is going to \&quot;force\&quot; Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.
     */
    public async patchNamespacedPersistentVolumeClaim (name: string, namespace: string, body: object, pretty?: string, dryRun?: string, fieldManager?: string, fieldValidation?: string, force?: boolean, options: {headers: {[name: string]: string}} = {headers: {}}) : Promise<{ response: http.IncomingMessage; body: V1PersistentVolumeClaim;  }> {
Daxcor69 commented 3 months ago

That was the issue. Sorry for not reading the docs more clearly.