REST Endpoints
Interaction with a running instance of kotekan takes place via a set of REST endpoints.
New stages should adhere to some basic conventions for where to put them and how to interact.
Both GET and POST endpoints are supported,
messages to the latter should be formatted as json strings.
Endpoints should follow a standard structure, to help people locate them:
Framework
The kotekan framework registers a number of points
/start [POST]Request an idle instance to begin operation,
requires a (json-encoded) config to be passed.
/stop [GET]Tell an active kotekan to shut down and clean up its
current running configuration.
/status [GET]returns the state of the system (active or not).
/config [GET]Returns the current system configuration.
/config_md5sum [GET]Returns an MD5 hash of the config file (based on the json string with no spaces).
Only exists if kotekan was build with OpenSSL support included
/version [GET]Returns the current kotekan version information; including build options.
/endpoints [GET]Returns all available REST endpoints in the system.
/metrics [GET]Returns text containing Prometheus-formatted
metrics which serve a host of system state properties.
Per-stage
Individual stages can register REST endpoints using code similar to:
using namespace std::placeholders;
restServer &rest_server = restServer::instance();
// register a POST endpoint
rest_server.register_post_callback(unique_name + "/my_post_endpoint",
std::bind(&myKotekanPorcess::endpoint_callback_func, this, _1, _2));
// register a GET endpoint
rest_server.register_get_callback(unique_name + "/my_get_endpoint",
std::bind(&myKotekanPorcess::endpoint_callback_func, this, _1));
Stages should always register endpoints relative to /unique_name.
The endpoint should be removed in the destructor of the stage registering it:
restServer &rest_server = restServer::instance();
// Remove a GET call back
rest_server.remove_get_callback(unique_name + "/my_get_endpoint");
// Remove a POST call back
rest_server.remove_json_callback(unique_name + "/my_post_endpoint");
Shared Endpoints
If several stages need to share one endpoint, the endpoint can be created by the configUpdater.
-
class configUpdater
Kotekan core component that creates endpoints defined in the config that stages can subscribe to to receive updates.
An endpoint will be created for every updatable config block defined in the configuration file. updatable blocks can be anywhere in the configuration tree, but may not be inside another updatable block. They need to contain a key kotekan_update_endpoint with the value "json". They also need to contain initial values for all fields that subscribing stages will expect on an update.
Example:
foo:
bar:
kotekan_update_endpoint: "json"
some_value: 0
some_other_value: 1
In the config block of a stage that wants to get updates of a specific updatable block, either a key “updatable_config” with the full path to the updatable block as a value has to exist, or an object called “updatable_config” with a list of all updatable config blocks.
Example:
my_stage:
updatable_config: "/foo/bar"
or my_stage:
updatable_config:
bar: "/foo/bar"
fu: "/foo/fu"
Every stage that subscribes to this update endpoint by calling
configUpdater config_updater = configUpdater::instance();
config_updater.subscribe(this, std::bind(&my_stage::my_callback, this, _1));
or std::map<std::string, std::function<bool(nlohmann::json &)> callbacks;
callbacks["bar"] = std::bind(&my_stage::my_bar_callback, this, _1);
callbacks["fu"] = std::bind(&my_stage::my_fu_callback, this, _1);
configUpdater::instance().subscribe(this, callbacks);
will receive an initial update on each callback function with the initial values defined in the config file (in the first example {"some_value": 0, some_other_value: 1}). That’s why the stage must be ready to receive updates before it subscribes.
All and only the variables defined in the updatable config block in the config file are guaranteed to be in the json block passed to the stage callback function. It is up to the stage, though, to check the data types, sizes and the actual values in the callback function and return false if anything is wrong.
The stage must be ready to receive updates before it subscribes and it has to apply save threading principles.
- Author
Rick Nitsche
Public Functions
-
configUpdater(const configUpdater&) = delete
-
void operator=(const configUpdater&) = delete
-
void apply_config(Config &config)
Set and apply the static config to configUpdater.
- Parameters:
config – The config.
-
void reset()
Reset the configUpdater.
Removes all REST endpoints and clears all memory of subscribers and endpoints. This should be called before destruction of the subscribers, to prevent the callbacks being called afterwards.
-
void subscribe(const kotekan::Stage *subscriber, std::function<bool(nlohmann::json&)> callback)
Subscribe to the updatable blocks of a Kotekan Stage.
The callback function has to return True on success and False otherwise. The block of the calling stage in the configuration file should have a key named “updatable_config” that defines the full path to the updatable block. As usual if not found at that level, it will search up the tree.
- Parameters:
-
-
void subscribe(const kotekan::Stage *subscriber, std::map<std::string, std::function<bool(nlohmann::json&)>> callbacks)
Subscribe to all updatable blocks of a Kotekan Stage.
The callback functions have to return True on success and False otherwise. The block of the calling stage in the configuration file should have an object named “updatable_config” with values that define the full path to an updatable block, each. The names in the callbacks map refer to the names of these values. If an “updatable_config” block is not found in the current stage it will search up the config tree, but all callback keys must be contained within this single block.
- Parameters:
-
-
void subscribe(const std::string &name, std::function<bool(nlohmann::json&)> callback)
Subscribe to an updatable block.
This function does not enforce the config structure and should only be used in special cases (Like when called from somewhere else than a Kotekan Stage). The callback function has to return True on success and False otherwise.
- Parameters:
-
-
void rest_callback(connectionInstance &con, nlohmann::json &json)
This should be called by restServer.
Aliases
To make things easier to access, it is possible to define aliases to endpoints in
the config under the aliases: block in the rest_server block:
rest_server:
aliases:
new_name: existing_endpoint
The above maps /new_name to /existing_endpoint
The list of aliases available is given by the /endpoints endpoint.
CPU Affinity
The CPU affinity defaults to the global cpu_affinity: property
To override that and pin it to say cores 3,4:
rest_server:
cpu_affinity: [3,4]
Binding and Addresses
The REST server binds on a specific address and port when started:
Use restServer::start(bind_address, port) to select the interface and port.
The helper restServer::isValidAddress() performs a syntactic validation of the
address string before attempting to bind.
Valid address forms:
Not accepted by isValidAddress():
Addresses including ports: 127.0.0.1:8080, example.com:12048
IPv6 literals: e.g., ::1, 2001:db8::1 (bare address, no brackets)
Hostnames, e.g. localhost, example-host, sub.domain
URIs with schemes: http://example.com
Bracketed IPv6 forms: [::1]
To verify that an address and port can actually be used on the host, call
restServer::canBindToAddress(bind_address, port).