Retrieving output from REST API exec call

I’m having an issue retrieving the output from an exec command issued from the API.

Using a simple ls command to illustrate

curl -s -k -X POST -d '{ "command": ["ls", "/"], "record-output": true, "interactive": false }' https://127.0.0.1:8443/1.0/containers/nginx/exec | jq

Produces the result

{
  "type": "async",
  "status": "Operation created",
  "status_code": 100,
  "operation": "/1.0/operations/c5e10c58-6d4b-49c3-a8b8-a0b48cec0550",
  "error_code": 0,
  "error": "",
  "metadata": {
    "id": "c5e10c58-6d4b-49c3-a8b8-a0b48cec0550",
    "class": "task",
    "description": "Executing command",
    "created_at": "2021-04-23T14:35:17.049309743+02:00",
    "updated_at": "2021-04-23T14:35:17.049309743+02:00",
    "status": "Running",
    "status_code": 103,
    "resources": {
      "containers": [
        "/1.0/containers/nginx"
      ],
      "instances": [
        "/1.0/instances/nginx"
      ]
    },
    "metadata": null,
    "may_cancel": false,
    "err": "",
    "location": "none"
  }
}

According to the documentation (https://linuxcontainers.org/lxd/docs/master/rest-api#10instancesnameexec)

I expect the return value to contain an output entry, but no such entry is present. What am I missing?

The output you’re showing indicates it’s still running, I believe if you hit it again shortly after, it should show it’s completed and metadata should point you to the right endpoints.

I tried calling the url returned in operation like so

curl -s -k https://127.0.0.1:8443/1.0/operations/0c26f718-96d1-4268-bfd8-315dba7ed93e

But that just returns an error no matter how long I wait.

{
  "error": "not found",
  "error_code": 404,
  "type": "error"
}

Operations disappear within 5s of them being complete.
In this case what you probably should do is get the UUID from the POST call and then immediately call /1.0/operations/UUID/wait. This will then return once it’s over.

I’m trying to do the same thing as lxdpls but using the unix-socket connection and I never get any results back. I get the following on a simple exec call:

curl -s --unix-socket /var/snap/lxd/common/lxd/unix.socket -H "Content-Type: application/json" -X POST -d @hello.json lxd/1.0/containers/golab/exec | jq
{
  "type": "async",
  "status": "Operation created",
  "status_code": 100,
  "operation": "/1.0/operations/d71cca5b-6e1a-4f70-944d-6fbc6f1e7435",
  "error_code": 0,
  "error": "",
  "metadata": {
    "id": "d71cca5b-6e1a-4f70-944d-6fbc6f1e7435",
    "class": "task",
    "description": "Executing command",
    "created_at": "2021-04-25T22:33:56.964351576-04:00",
    "updated_at": "2021-04-25T22:33:56.964351576-04:00",
    "status": "Running",
    "status_code": 103,
    "resources": {
      "containers": [
        "/1.0/containers/golab"
      ],
      "instances": [
        "/1.0/instances/golab"
      ]
    },
    "metadata": null,
    "may_cancel": false,
    "err": "",
    "location": "none"
  }
}

A call to /1.0/operations/d71cca5b-6e1a-4f70-944d-6fbc6f1e7435 immediately after this call returns nothing. I also get nothing if I use wait or try to list all of the operations within 5 seconds of the original call.

The hello.js file looks like:

 {
     "command": ["ls"],
     "interactive": false,
     "record-output": true
 }

If I run lxc exec golab ls, I get the expected result. What am I doing incorrectly and how do you get the results back from an exec call via the API?

stgraber@castiana:~$ curl --unix-socket /var/snap/lxd/common/lxd/unix.socket lxd$(curl -s --unix-socket /var/snap/lxd/common/lxd/unix.socket lxd/1.0/instances/a1/exec -X POST -d '{"command": ["ls", "/"], "record-output": true}' | jq -r .operation)/wait | jq .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   660  100   660    0     0   644k      0 --:--:-- --:--:-- --:--:--  644k
{
  "type": "sync",
  "status": "Success",
  "status_code": 200,
  "operation": "",
  "error_code": 0,
  "error": "",
  "metadata": {
    "id": "04ca019e-1603-4ac0-82ec-e34dbeb2cea4",
    "class": "task",
    "description": "Executing command",
    "created_at": "2021-04-26T16:12:04.041047945-04:00",
    "updated_at": "2021-04-26T16:12:04.073573128-04:00",
    "status": "Success",
    "status_code": 200,
    "resources": {
      "containers": [
        "/1.0/containers/a1"
      ],
      "instances": [
        "/1.0/instances/a1"
      ]
    },
    "metadata": {
      "output": {
        "1": "/1.0/instances/a1/logs/exec_04ca019e-1603-4ac0-82ec-e34dbeb2cea4.stdout",
        "2": "/1.0/instances/a1/logs/exec_04ca019e-1603-4ac0-82ec-e34dbeb2cea4.stderr"
      },
      "return": 0
    },
    "may_cancel": false,
    "err": "",
    "location": "none"
  }
}
1 Like

Great! That works perfectly. From the examples it wasn’t clear. Now it is. Thanks.