Develop with the Kleened API

The way to interact with Kleened is through the HTTP API. Using a language and web-framework of your choice, you can build helpful scripts or entire applications on top of Kleene.

Kleened’s API is HTTP-based and consists of a RESTful API and a bunch of WebSockets. You can try the RESTful API using standard CLI tools such as wget, fetch, and curl. To some extent you can try out the WebSockets with websocat.

Since the REST-API is defined using the OpenAPI specification, it is possible to generate API clients in multiple languages.

View the API reference

You can view the reference for the latest version of the API or choose a specific version.

Examples

The following sections provide a few examples using curl/websocat and Python, respectively.

When using curl to connect over a unix socket, the hostname is not important. The CLI-examples use localhost with curl, but any hostname would work.

Note

The Python-examples use the OpenAPI-generated client embedded in Klee. It is a slightly modified version of the client generated by openapi-python-client. However, you can download the spec here and generate a clean client, if needed.

List images

Listing images. In this case there are two images in the list. The example is equivalent to klee image ls.

$ sudo curl --unix-socket /var/run/kleened.sock http://localhost/images/list
[
  {
    "buildargs": {},
    "command": [
      "/bin/sh",
      "-c",
      "echo \"Hello World\""
    ],
    "created": "2023-11-16T11:39:05.215568Z",
    "env": [],
    "id": "be56be1db5af",
    "instructions": [
      [
        "FROM FreeBSD-13.2-STABLE:latest",
        ""
      ],
      [
        "RUN ls -lh /",
        "@0551db16e1d2"
      ],
      [
        "CMD echo \"Hello World\"",
        ""
      ]
          ],
    "layer_id": "c74a290a1406",
    "name": "FreeBSD",
    "tag": "testing",
    "user": "root"
  },
  {
    "buildargs": {},
    "command": [
      "/bin/sh",
      "/etc/rc"
    ],
    "created": "2023-11-16T11:35:09.440185Z",
    "env": [],
    "id": "25963b5053e5",
    "instructions": [],
    "layer_id": "a9cb0ebd68b0",
    "name": "FreeBSD-13.2-STABLE",
    "tag": "latest",
    "user": "root"
  },
]
import httpx

from klee.client.api.default.image_list import sync_detailed as image_list
from klee.client.client import Client

kwargs = {}

response = image_list(
    httpx.HTTPTransport(uds="/var/run/kleened.sock"),
    client=Client(base_url="http://localhost"),
    **kwargs
)
for image in response.parsed:
    print(image.id)

Create and start a container

Equivalent to klee run -a FreeBSD:testing ls

$ curl --unix-socket /var/run/kleened.sock -H "Content-Type: application/json" \
  -d '{"image": "FreeBSD-13.2-STABLE:latest", "cmd": ["/bin/sh", "-c", "ls"]}' \
  -X POST http://localhost/containers/create
{"id":"7816a46bd063"}

$ curl --unix-socket /var/run/kleened.sock -H "Content-Type: application/json" \
  -d '{"container_id": "7816a46bd063"}' \
  -X POST http://localhost/exec/create
{"id":"c00fa9c8c32e"}

# The first JSON-object is supplied by the user interactively - could be piped in as well.
# Could not get unix-socket working, using ipv6 instead
$ websocat ws://\[::1\]:8080/exec/start
{"attach": true, "exec_id": "c00fa9c8c32e", "start_container": true}
{"data":"","message":"","msg_type":"starting"}
.cshrc .profile COPYRIGHT bin boot dev etc lib libexec media mnt net proc rescue root sbin sys tmp usr var
^C
import json
from contextlib import asynccontextmanager

import httpx
import websockets
from websockets.sync.client import unix_connect

from klee.client.client import Client
from klee.client.api.default.container_create import sync_detailed as container_create
from klee.client.api.default.exec_create import sync_detailed as exec_create
from klee.client.models.container_config import ContainerConfig
from klee.client.models.exec_config import ExecConfig

kwargs = {}


def exec_start(exec_id):
    conn = unix_connect("/var/run/kleened.sock", uri="ws://localhost/exec/start")
    conn.send(json.dumps({"exec_id": exec_id, "attach": True, "start_container": True}))
    while True:
        try:
            message = conn.recv()
        except websockets.exceptions.ConnectionClosed as closed:
            print(closed)
            break

        print(message)


transport = httpx.HTTPTransport(uds="/var/run/kleened.sock")
client = Client(base_url="http://localhost")

response = container_create(
    transport=transport,
    client=client,
    **{
        "json_body": ContainerConfig.from_dict(
            {"image": "FreeBSD-13.2-STABLE:latest", "cmd": ["/bin/sh", "-c", "ls"]}
        )
    }
)

response = exec_create(
    transport=transport,
    client=client,
    **{"json_body": ExecConfig.from_dict({"container_id": response.parsed.id})}
)

exec_start(response.parsed.id)

Stop a running container

Equivalent to klee stop 511b20226c28

curl --unix-socket /var/run/kleened.sock -X POST http://localhost/containers/511b20226c28/stop

{"id":"511b20226c28"}
import httpx

from klee.client.api.default.container_stop import sync_detailed as container_stop
from klee.client.client import Client

response = container_stop(
    httpx.HTTPTransport(uds="/var/run/kleened.sock"),
    client=Client(base_url="http://localhost"),
    **{"container_id": "7f5175d852d5"}
)

print(response.parsed.id)