Custom tests

Introduction

Custom tests are pieces of code that are deployed to Tentacle's infrastructure and run on interval. Tentacle comes with a library of modules specially crafted with monitoring in mind.

Creating custom tests

Custom tests can be created instantly when launching an Agent or as Templates that can be reused in multiple Agents.

Custom tests are written in JavaScript (there is nothing inherent in Tentacle that limits the platform to JavaScript — or any one language for that matter — it was simply selected because it's the lingua franca of the internet) and writing code for your Agents to run is fairly straight-forward. There are, however, a couple of things to consider:

  • The default outcome is for a test to fail. You have to explicitly call task.ok() if you want an agent to report a test as successful. task.fail() can be used to add some context pertaining to why a test has failed.
  • The agent will fail if the test in it's entirety couldn't be carried out before a maximum time-threshold has been reached.
  • All scripts run from top to bottom every time a test is run. They also run on different machines every time. A value assigned to a variable in the root-scope will not be available next time a test is run.

The default outcome of a custom test is to fail. You must explicitly tell Tentacle that a test didn't fail as described above.

Consult the API Reference below for examples and documentation regarding available functions.

Templates

A Template can be created if you want to reuse the same code in multiple Agents. Templates consist of code, a name and Configuration.

Configuration

Multiple agents can implement the same template. But it's not always desirable to use the exact same settings for all Agents. This is solved by separating the configuration from the code.

You can, when creating a template, also create a blueprint — or Manifest — for what configuration can be/must be available. A Manifest is a simple JSON object that contains meta data describing the configuration structure. The Manifest is used to build a form allowing users launching Agents with the Template to input custom configuration details.

Revisions

Every time you edit a Template a new Revision is created. The new revision is not automatically deployed to all Agents using the Template. You have to re-launch the Agents with the updated Revision.

Manifest reference

A manifest is defined as a JSON object containing the key fields with a list of fields as its value:

{
    "fields": []
}

Fields

Fields are JSON objects. Available keys vary with field-types, but the following keys are available for all fields:

KeyTypeDescription
typeStringDefines the type of the field and data contained.
labelStringLabel displayed when rendering the form.
nameStringName by which the data is accessed in the code (see config.get()).
requiredBooleanDictates whether a value has to be entered or not..
defaultAnyValue to be used if no value was entered/selected in the form.

Example field:

{ "type": "number", "label": "port", "name": "port", "default": 80 }

Available field types

TypeDescription
stringStores a String value. Renders a standard input field.
numberStores a number value (Integer or Float). Renders an input field of the same type.
booleanStores a boolean value. Renders a checkbox.
choiceRequires the additional key options that should contain a list of objects with the keys value and name. Renders a selectbox.
compoundRequires the additional key: fields that should contain a list of nested fields. The type renders as a group of fields with add/remove buttons.

API reference

Certificates cert

Module to work with certificates.

cert.validity cert.validity(hostname, [port])

Retrieve validity data from host.

Parameters
ParameterTypeDescription
hostnameStringName of the host you want to check.
portIntegerPort to use when connecting (default 443).
Return value

Calls to cert.validity() return a promise that, when the call is successful, is resolved with an argument named result:

AttributeTypeDescription
result.validInSecondsIntegerThe number of seconds until the certificate becomes valid. Values <1 indicate that the certificate is already valid.
result.invalidInSecondsIntegerThe number of seconds until the certificate becomes invalid. Values <1 indicate that the certificate is already invalid.
Examples
var threshold = 1;

cert.validity('domain.com').then((resp) => {
    var validForDays = resp.invalidInSeconds / 86400;

    if (resp.validInSeconds > 1) {
        task.fail('Not valid yet');
    } else if (validForDays <= threshold) {
        task.fail(validForDays);
    } else {
        task.ok(validForDays);
    }
}).catch((error) => {
    task.fail(error);
});

Configuration config

Manage configuration entered when launching an Agent.

config.load config.load()

Load the configuration.

Parameters

The function has no parameters.

Return value

Calls to config.load() return a promise that, when the call is successful, is resolved with an argument named cfg. cfg is an Object that contains a hierarchy of configuration values as specified by the Manifest.

Example

Given the following Manifest:

{
    "fields": [
        { "type": "string", "label": "URL", "name": "url", "required": true }
    ]
}

You would access the configuration values as follows:

config.load().then((cfg) => {
    task.ok(cfg.url);
});

DNS dns

Module to work with DNS servers.

dns.resolve dns.resolve(hostname[, rrtype])

Resolve hostname into recordes of type specified by rrtype.

Parameters
ParameterTypeDescription
hostnameStringName of the host you want to resolve.
rrtypeStringThe record type you want to resolve (e.g. 'A').

Valid rrtype values:

  • A - IPV4 addresses
  • AAAA - IPV6 addresses
  • MX - Mail exchange records
  • TXT - Text records
  • SRV - Service records
  • PTR - Pointer records
  • NS - Name server records
  • CNAME - Canonical name records
  • SOA - Start of authority records
  • NAPTR - Naome authority pointer records
Return value

Calls to dns.resolve() return a promise that, when the call is successful, is resolved with an argument named result:

AttributeTypeDescription
result.answersArrayAn array of results (a result is an object with a ttl-key and either a value-key, or exchange and priority in case of a MX record, or primary, rname et al in case of a SOA record.
result.timeIntegerThe time in milliseconds it took to resolve the hostname.
Examples
dns.resolve('domain.com', 'a').then((resp) => {
    if (resp.time < 1000) {
        task.ok(resp);
   } else {
       task.fail(resp);
   }
}).catch((error) => {
    task.fail(error);
});

HTTP requests http

The http module exposes a set of functions for interfacing with HTTP-based services. All first-level functions are asynchronous and return a promise.

http.delete http.delete(url, [options])

Make a HTTP DELETE request to url with options.

Parameters
ParameterTypeDescription
urlStringURL of the service to connect to. Example: http://www.tentacle.com.
optionsObjectSee http.request().
Return value

Calls to http.delete() return a promise that, when the call is successful, is resolved with an argument named response:

AttributeTypeDescription
response.codeIntegerStandard HTTP status code.
response.json()PromiseRead the body and attempt to decode the contents from JSON. Returns a promise that is resolved with the decoded value.
response.text()PromiseRead the body and treat it as text. Returns a promise that resolves with the value.

Calls that fail are rejected with a single argument: error.

Examples
http.delete('http://www.domain.com/resource/').then((resp) => {
    return resp.json();
}).then((value) => {
    task.ok(value);
}).catch((error) => {
    task.fail(error);
});

http.get http.get(url, [options])

Make a HTTP GET request to url with options.

Parameters
ParameterTypeDescription
urlStringURL of the service to connect to. Example: http://www.tentacle.com.
optionsObjectSee http.request().
Return value

Calls to http.get() return a promise that, when the call is successful, is resolved with an argument named response:

AttributeTypeDescription
response.codeIntegerStandard HTTP status code.
response.json()PromiseRead the body and attempt to decode the contents from JSON. Returns a promise that is resolved with the decoded value.
response.text()PromiseRead the body and treat it as text. Returns a promise that resolves with the value.

Calls that fail are rejected with a single argument: error.

Examples
http.get('http://www.domain.com').then((resp) => {
    return resp.json();
}).then((value) => {
    task.ok(value);
}).catch((error) => {
    task.fail(error);
});

http.head http.head(url, [options])

Make a HTTP HEAD request to url with options.

Parameters
ParameterTypeDescription
urlStringURL of the service to connect to. Example: http://www.tentacle.com.
optionsObjectSee http.request().
Return value

Calls to http.head() return a promise that, when the call is successful, is resolved with an argument named response:

AttributeTypeDescription
response.codeIntegerStandard HTTP status code.
response.json()PromiseRead the body and attempt to decode the contents from JSON. Returns a promise that is resolved with the decoded value.
response.text()PromiseRead the body and treat it as text. Returns a promise that resolves with the value.

Calls that fail are rejected with a single argument: error.

Examples
http.head('http://www.domain.com/').then((resp) => {
    return resp.json();
}).then((value) => {
    task.ok(value);
}).catch((error) => {
    task.fail(error);
});

http.post http.post(url, [options])

Make a HTTP POST request to url with options.

Parameters
ParameterTypeDescription
urlStringURL of the service to connect to. Example: http://www.tentacle.com.
optionsObjectSee http.request().
Return value

Calls to http.post() return a promise that, when the call is successful, is resolved with an argument named response:

AttributeTypeDescription
response.codeIntegerStandard HTTP status code.
response.json()PromiseRead the body and attempt to decode the contents from JSON. Returns a promise that is resolved with the decoded value.
response.text()PromiseRead the body and treat it as text. Returns a promise that resolves with the value.

Calls that fail are rejected with a single argument: error.

Examples
var config = {
    body: 'Hello world!'
};

http.post('http://www.domain.com', config).then((resp) => {
    return resp.json();
}).then((value) => {
    task.ok(value);
}).catch((error) => {
    task.fail(error);
});

http.request http.get(method, url, [options])

Make a HTTP method request to url with options.

Parameters
ParameterTypeDescription
methodStringThe method of the request (GET, POST, DELETE, HEAD, PATCH, or PUT)
urlStringURL of the service to connect to. Example: http://www.tentacle.com.
optionsObjectObject containing additional data and configuration.
options.bodyStringThe body to send with the request.
options.headersObjectObject containing all additional headers to send.
options.queryParamsObjectObject containing query parameters to add to the URL.
options.followRedirectBooleanDictating whether or not redirection should be followed automatically (default false). A maximum of five redirections will be followed.
Return value

Calls to http.request() return a promise that, when the call is successful, is resolved with an argument named response:

AttributeTypeDescription
response.codeIntegerStandard HTTP status code.
response.json()PromiseRead the body and attempt to decode the contents from JSON. Returns a promise that is resolved with the decoded value.
response.text()PromiseRead the body and treat it as text. Returns a promise that resolves with the value.

Calls that fail are rejected with a single argument: error.

Examples
http.request('GET', 'http://www.domain.com').then((resp) => {
    return resp.json();
}).then((value) => {
    task.ok(value);
}).catch((error) => {
    task.fail(error);
});

Report back task

Functions that report results back to Tentacle. All functions in this module will halt the execution of the test after the scope the call was made from has been exited.

task.ok task.ok([message])

Report that the tests was performed successfully. The function accepts one optional argument:

ParameterTypeDescription
contextJSON SerializableContext to return back to Tentacle.

task.fail task.fail([message])

Report that the tests failed. The function accepts one optional argument:

ParameterTypeDescription
contextJSON SerializableContext to return back to Tentacle.

WebSockets ws

ws.open ws.open(url, options)

Open a WebSocket connection to url with options.

Parameters
ParameterTypeDescription
urlStringURL of the service to connect to. Example: ws://www.tentacle.com.
optionsObjectObject containing additional data and configuration.
options.headersObjectObject containing all additional headers to send.
options.subprotocolsArray or StringA string or array containing subprotocol(s).
Return value

Calls to ws.connect() return a promise that, when the call is successful, is resolved with an argument named conn:

AttributeTypeDescription
conn.send(frame)PromiseSend frame to server.
conn.receive()PromiseReads one frame or blocks until one is available. Returns a promise that resolves with the frame and rejects with an error message.

Calls that fail are rejected with a single argument: error.

Examples
const opts = { subprotocols: 'chat' };

ws.open('ws://www.domain.com', opts).then((conn) => {
    conn.send('test');
    return conn.receive();
}).then((frame) => {
    task.ok(frame);
}).catch((error) => {
    task.fail(error);
});

Examples

Simple HTTP test

A simple test that connects to a web site and verifies that the response code is 200.

Manifest

The following manifest creates a form with a single input: url.

{
    "fields": [
        { "type": "string", "label": "URL", "name": "url", "required": true }
    ]
}

Code

The code below loads the configuration, sends a GET-request to the URL specified in the form, and marks the test as "ok" if the response code equals 200.

config.load().then((cfg) => {
    http.get(cfg.url).then((resp) => {
        if (resp.code === 200) {
            task.ok(resp.time);
        }
    });
});