skip to Main Content

I am trying to process the output of a specific function that uses process.stdout / process.stdin to print the commands results to the terminal. To be more specific, this Kubernetes function https://github.com/kubernetes-client/javascript/blob/master/src/exec.ts with this usage:

const exec = new k8s.Exec(kc);
exec.exec('default', 'nginx-4217019353-9gl4s', 'nginx', command,
    process.stdout, process.stderr, process.stdin,
    true /* tty */,
    (status) => {
        console.log('Exited with status:');
        console.log(JSON.stringify(status, null, 2));
    });

While the function above may print something like that to the terminal:

Everything is up.
Time running: 5min 23sec.

Exited with status:
{
  "metadata": {},
  "status": "Success"
} 

My goal is to capture

Everything is up.
Time running: 5min 23sec.

in a variable so that I can process it further.

2

Answers


  1. Chosen as BEST ANSWER

    Creating an own stream object instead of using the one from process / console works good.

    var Stream = require('stream');
        
    var ws = new Stream;
    ws.writable = true;
    ws.str = "";
    ws.str = 0;
    
    ws.string = function(){
      return ws.str;
    }
    
    ws.clearStr = function(){
      ws.str = "";
    }
    
    ws.write = function(buf) {
      ws.str += buf.toString();
      ws.bytes += buf.length;
    }
    
    ws.end = function(buf) {
       if(arguments.length) ws.write(buf);
       ws.writable = false;
    }
    
    async function kubeExec(ns, pod, container, cmd){
      ws.clearStr();
      ret = new Promise((resolve,reject) =>{
        execV1Api.exec(ns,pod,container,cmd, ws, process.stderr , process.stdin,true /* tty */,
          async function(ret){
              if(ret.status == "Success"){
                resolve(ws.str);
              } else {
                console.log(JSON.stringify(status, null, 2));
              }
          });
      });
    
      return ret.then( (str) => { return str;} );
    }
    

  2. An year later, and I have this exact need, and still no viable solution to use the exec API to capture the Writable stream. The problem with this API is that the Writable is also async, so you’ll need to wait for it to yield data. But you can’t wait while calling the exec.exec, because the command won’t be executed, and so you either end up with no data, or infinite loop.

    The best solution I can come up with is to use another process to run the kubectl exec directly (e.g. if you’re writing tests with cypress, use cy.exec() and then wait for the response). Below is a working solution for me:

    // in this command, I count how many deadletter messages are there in the event storage
    const podName = 'event-storage-8687775474-4snsc';
    const command = `kubectl exec ${podName} -- wc -l deadletter.jsonl`;
    
    cy.exec(command).then((response) => {
      const count = parseInt(response.stdout, 10);
      expect(count).to.equal(0); // verify that there are zero deadletter messages
    });
    

    It’s worth nothing that you can combine this with other k8s client API, such as the listNamespacedPod to get the actual podName above at runtime, like this:

    const kc = new KubeConfig();
    kc.loadFromDefault();
    
    const k8sApi = kc.makeApiClient(CoreV1Api);
    
    k8sApi.listNamespacedPod(namespace).then((res) => {
      const list: V1PodList = res.body as V1PodList;
    
      const podName = list.items
        .filter((item) => item.metadata.name.startsWith('event-storage'))
        .map((item) => item.metadata.name)[0]; // [0] - assuming that there is only one such pod
    
      const command = `kubectl exec ${podName} -- wc -l deadletter.jsonl`;
    
      cy.exec(command).then((response) => {
        const count = parseInt(response.stdout, 10);
        expect(count).to.equal(0);
      });
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search