Skip to content

Learn the basic of extending the Chaos Toolkit

The Chaos Toolkit comes with a rich catalog of extensions. Sometimes however, this library is not enough. So, how do you go about extending the Chaos Toolkit?

It turns out there are a variety of ways.

Generate a binary and execute it from the experiment

The Chaos Toolkit is written in Python 3, but you may not have the Python skills or willingness to develop it. One quick solution is to write your own small binary (say you are a rust or golang shop) and call it as a process action. Keep in mind that the binary may be used as a probe in the steady-state so ensure its returned value is easily processed via regex or jsonpath tolerance types. At the very least, the process should signal through its exit code if it completed normally (with 0).

For example, let’s create a simple binary with rust. In a file named echo.rs:

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    println!(&args[1..]);
}
rustc echo.rs
./echo hello world
["hello", "world"]
echo $?
0

An action using this binary would look like this:

{
    "name": "echo-my-message",
    "type": "action",
    "provider": {
        "type": "process",
        "path": "echo",
        "arguments": "hello world"
    }
}
name: echo-my-message
type: action
provider:
  type: process
  path: echo
  arguments: hello world

This assumes the binary is on the chaos PATH and the user has permissions.

Call a HTTP endpoint

Sometimes, you have HTTP endpoints that are used internally for specific operational tasks. They are internals and crafted for your unique needs. This can be enough to create the bespoke condition for your probes and actions to call via a http provider.

Create a simple Python function

While extending with a piece of Python code may sound more work, it does not have to be. Indeed, you can create a simple Python module and have a set of functions that are called from your experiment directly. THe trick is to ensure the module can be found in the PYTHONPATH.

Let’s see an example:

Assuming a Python module called kettle.py:

from chaoslib.types import Configuration, Secrets

__all__ = ["put_on"]


def put_on(temperature: int = 90, configuration: Configuration = None,
           secrets: Secrets = None) -> None:
    """
    Sets the kettle to the expected temperature (celsius) and put it on.

    Will return when the kettle has reached it.
    """
    # do something here
    pass

If kettle.py lives next to experiment.json then the action could be:

{
    "name": "put-kettle-on",
    "type": "action",
    "provider": {
        "type": "python",
        "module": "kettle",
        "func": "put_on",
        "arguments": {
            "temperature": 80
        }
    }
}

You could finally make it available as follows:

ls
kettle.py experiment.json
export PYTHONPATH=`pwd`
chaos run experiment.json

The Python module does not have to live next to the experiment. It can also contain more than one function of course.