Skip to content

Action hooks

Action hooks allows you to customize your control plane by injecting custom code at different phase of the lifecycle of your SaaS operations

Action hooks example

x-omnistrate-actionhooks:
      - scope: CLUSTER
        type: INIT
        commandTemplate: >
          PGPASSWORD={{ $var.postgresqlRootPassword }} psql -U postgres
          -h writer {{ $var.postgresqlDatabase }} -c "create extension vector"

As an example, in the above case, we are enabling vector extension for Postgres on cluster initialization.

Example usecases

Action hooks can be used for wide variety of use-cases. Here are some examples:

  • To enable extensions on cluster creation
  • To detect process deadlatches
  • To run rebalance command after adding or removing a node
  • To run post upgrade action

Action hooks concepts

Types of Action hooks

  • health check
  • post-start
  • add
  • remove
  • init
  • post-start
  • pre-stop
  • post-upgrade

If you have a requirement to add action hooks at other stages/operations, please reach out to us at support@omnistrate.com

Action hooks runtime environment

Action hooks are run as independent pods to minimize any runtime impact on the other data plane (aka application) resources. Having said that, action hooks can interact with other resources to perform its function.

Action hooks are defined based on when you want to run them, i.e. defining an action on service component doesn’t mean that it will be executed inside that component pod. Instead, it will be run based on the lifecycle of the service component as defined by the action type. In the above example, action hook will be run when cluster resource is first initialized.

Let's say tenant #2 is getting initialized and as you can see action hook is getting run as an indenpendent pod alongside service component pod:

Action hooks environment

Action hooks programming model

We support bash scripts as a runtime environment. Btw, you can also invoke any third-party service/function directly from the action hooks

If you would like to see python based runtime environment or have other suggestion, please reach out to us at support@omnistrate.com with more details on your usecase.

Action hooks variables

You can inject any system or dynamic variables into your action hook. For the full list, please see this and this.

Action hooks scope

Action hooks are categorized into two scopes:

  • Node: for every node of type service component, we will run the associated action hook
  • Cluster: for every service component instance, we will run the associated action hook

Action hooks with node scope:

  • Health Check
  • Post Start
  • Add
  • Remove

Action hooks with cluster scope:

  • Init
  • Post Start
  • Pre Stop
  • Post Upgrade

Action hooks lifecycle

In this section, we will discuss how different action hooks types are run for different SaaS operations.

To illustrate it, let's say we are building a service with Cluster resource containing two service components: SC1 and SC2. Here is how a dependency structure may look like:

Action hooks example

Now, if you have to define different action hook types on the Cluster resource, here is how they will get executed for each of the SaaS operations:

  • Provisioning operation: As an example, you can see init action hook is run after provisioning of all the service components associated with the Cluster resource. Action hooks provisioning

  • Start/Restart operation Action hooks start

  • Upgrade operation Action hooks upgrade

  • Stop operation Action hooks stop

  • Add capacity operation Action hooks add capacity

  • Remove capacity operation Action hooks remove capacity

  • Deprovisioning operation Action hooks deprovisioning

How to configure action hooks?

Use x-omnistrate-actionhooks compose tag. For more details on compose tags, see this

Alternatively, you can use register action hook API here or follow our intuitive UI to configure your hooks.

More examples

Scaling example

In this example, we are using action hooks to add a node to MongoDB cluster on scale up:

services:
  mongodb-primary:
    image: docker.io/omnistrate/mongodb:6.0-3
    x-omnistrate-actionhooks:
      - scope: NODE
        type: ADD
        commandTemplate: |
          #!/bin/bash
          set -ex

          # Check if NODE_NAME is not equal to 'mongodb-primary-0'
          if [ "$NODE_NAME" != {{ $sys.compute.nodes[0].name }} ]; then
              # Run the mongosh command
              mongosh "mongodb://{{ $var.mongodbUsername }}:{{ $var.mongodbPassword }}@{{ $sys.compute.nodes[0].name }}:27017/?authMechanism=DEFAULT" --eval "rs.add( { host: '{{ $sys.compute.node.name }}' } )"
          fi

Healthcheck example

In this example, we are using action hooks to configure deep process liveness check on Couchbase Server.

#!/bin/sh

IP=${IP:=127.0.0.1}
PORT=${PORT:=8091}
QUERY_PORT=${QUERY_PORT:=8093}

USERNAME=${USERNAME:="Administrator"}
PASSWORD=${PASSWORD:="password"}

N1QL_STMT='SELECT name FROM `travel-sample`.inventory.hotel LIMIT 1'

i=1
numbered_echo() {
    echo "[$i] $@"
    i=$(expr $i + 1)
}


# Check if couchbase server is up
check_db() {
    curl --silent --output /dev/null http://$IP:$PORT
    echo $?
}

SUCCESS_CODE=200
EXIT_CODE=0
response=""
# Issue POST and extract response
launch_query() {
    # final_cmd="curl --silent $QUERY"
    numbered_echo "curl --write-out %{http_code} --output /dev/null --silent -u $USERNAME:$PASSWORD http://$IP:$QUERY_PORT/query/service -d \"statement=$N1QL_STMT\""
    response=$(curl --write-out %{http_code} --output /dev/null --silent -u $USERNAME:$PASSWORD http://$IP:$QUERY_PORT/query/service -d "statement=$N1QL_STMT")
    echo $response
    if [ $response -ne $SUCCESS_CODE ]
    then
        EXIT_CODE=1
        echo "Received: $response Expected: $SUCCESS_CODE => Exiting with exit code: $EXIT_CODE"
        exit $EXIT_CODE
    fi  
}

# Wait until cb db is ready
until [[ $(check_db) = 0 ]]; do
    numbered_echo "cb db not available yet"
    sleep 1
done

numbered_echo "cb db is available"

# LAUNCH QUERY
launch_query 

exit $EXIT_CODE