banshee is under active development: collectors will keep changing, and a version you publish will likely need adjustments. For stable use, wait for version 1.0.0.

Containers

banshee/containers-collection/containers/kubernetes/https/

nodes

Whether each node of a Kubernetes cluster is ready, under memory, disk or process id pressure, or cordoned, by its API.

  • containers
  • kubernetes
  • k8s
  • node
  • kubelet
  • https
  • api

by banshee1 installation1 version

v1Latest

Published on October 27, 2026. First version.

Keen 1.0.0 · 90 lines
// The nodes of a Kubernetes cluster, by its API with the token of a service account (GET /api/v1/nodes): for
// each node, whether it is ready, whether the kubelet reports a lack of memory, of disk or of process ids,
// and whether new pods may be scheduled on it.
// The address is the API server's, the host's: https://<host:address>:<port>/.
need('protocol:http');
$address = need('host:address');
$token = need('protocol:http:token');
$port = variable('kubernetes:port', 'integer', array(
    'label'   => 'API port',
    'help'    => 'The port of the Kubernetes API server: 6443 for a cluster set up by kubeadm, often 443 for a managed one.',
    'default' => 6443,
));

per('node', 'Nodes');
output('ready', 'text', array('label' => 'Ready', 'per' => 'node'));
output('memory-pressure', 'text', array('label' => 'Memory pressure', 'per' => 'node'));
output('disk-pressure', 'text', array('label' => 'Disk pressure', 'per' => 'node'));
output('pid-pressure', 'text', array('label' => 'Process id pressure', 'per' => 'node'));
output('schedulable', 'text', array('label' => 'Schedulable', 'per' => 'node'));

watch.expect('not-ready', 'Node not ready', 'ready', array('critical' => array('True')));
watch.state('memory-pressure', 'Memory pressure', 'memory-pressure', array('warning' => array('True')));
watch.state('disk-pressure', 'Disk pressure', 'disk-pressure', array('warning' => array('True')));
watch.state('pid-pressure', 'Process id pressure', 'pid-pressure', array('warning' => array('True')));
watch.state('cordoned', 'Node cordoned', 'schedulable', array('warning' => array('no'), 'enabled' => false));
watch.missing('missing', 'Node missing', 'ready', array('enabled' => false));

// The items of a list, read page by page: the API cuts a list at limit items and gives, under
// metadata.continue, the token of the next page, until the last. $query, when not empty, is added to the
// parameters of the address. The token goes in the header Authorization: Bearer; the answer is not secret,
// its body having carried none.
function api_list($base, $path, $query, $token) {
    $items = array();
    $next = '';
    while (true) {
        $url = $base . $path . '?limit=500';
        if ($query != '') {
            $url = $url . '&' . $query;
        }
        if ($next != '') {
            $url = $url . '&continue=' . url.encode($next);
        }
        $answer = http.get($url, headers=array('Authorization' => 'Bearer ' . $token));
        if ($answer['status'] == 401) {
            fail('misconfigured', 'Kubernetes refuses the token, code 401');
        }
        if ($answer['status'] == 403) {
            fail('misconfigured', 'The service account may not list ' . $path . ', code 403');
        }
        if ($answer['status'] != 200) {
            fail('unreachable', 'Kubernetes answers ' . $path . ' with code ' . $answer['status']);
        }
        $page = json.read($answer['body']);
        foreach ($page['items'] as $item) {
            $items = list.add($items, $item);
        }
        $next = list.get($page['metadata'], 'continue', '');
        if ($next == '') {
            break;
        }
    }
    return $items;
}

// {"metadata": {"name": "worker-1"}, "spec": {"unschedulable": true}, "status": {"conditions": [{"type":
// "Ready", "status": "True"}, {"type": "MemoryPressure", "status": "False"}, ...]}}. Each condition is True,
// False or Unknown, Unknown when the node has stopped reporting; a condition the node does not give reads
// Unknown. unschedulable is only written when it is true: a node cordoned.
$nodes = api_list('https://' . $address . ':' . $port, '/api/v1/nodes', '', $token);
if (list.count($nodes) == 0) {
    fail('misconfigured', 'Kubernetes lists no node');
}
foreach ($nodes as $node) {
    $name = $node['metadata']['name'];
    $conditions = array('Ready' => 'Unknown', 'MemoryPressure' => 'Unknown', 'DiskPressure' => 'Unknown',
        'PIDPressure' => 'Unknown');
    foreach (list.get($node['status'], 'conditions', array()) as $condition) {
        $conditions[$condition['type']] = $condition['status'];
    }
    value('ready', $conditions['Ready'], $name);
    value('memory-pressure', $conditions['MemoryPressure'], $name);
    value('disk-pressure', $conditions['DiskPressure'], $name);
    value('pid-pressure', $conditions['PIDPressure'], $name);

    $schedulable = 'yes';
    if (list.get($node['spec'], 'unschedulable', false)) {
        $schedulable = 'no';
    }
    value('schedulable', $schedulable, $name);
}