Skip to main content
Version: 2.2.x

More Details

Now that you have seen a working example of the basics, it's time to look closer at what we did and add more to it.

More about the Bootstrap configuration

The bootstrap is the "only configuration the the agent needs" - it contains all the information that is required to pull in the workflow configuration, either from a local file or a remote url.

It also contains the administrator defined secrets and the hosts (urls and http methods) the agent is allow to communicate with, none of which can be changed remotely.

The bootstrap can contain multiple definitions, each running in their own isolated context, with their own secrets and host definitions.

You can take a look at the API reference for the bootstrap - this might be overwhelming, but a brief skim read will give you an idea of what can be configured.

In the previous example, we set the minimum requirements to get the bootstrap working, lets cover off what we set. Here are the properties that we added, and a brief explanation of them:

  • remoteConfiguration : this is the workflow configuration file that the packager produced - this can be a file local to the agent or a URL
  • deploymentId : this is a unique identifier which is used to tag the metrics produced while loading the bootstrap
  • agentPrivateKey : the private key used to decrypt the workflow configuration and matches the public key used to package the configuration

The other properties that you can configure include: the configuration check interval, authentication headers, error handling of the bootstrap loading, and the definition of the hosts that can be communicated with.

More about the Workflow configuration

The workflow configuration is how the agent is given instructions to perform the tasks you require.

In the previous example we defined workflows : this is the bulk of any configuration and defines the tasks/steps that you want the agent to perform.

In the example we defined a single Workflow and gave it a unique name. We also defined a single Step - it was a very simple step and simply printed out hello world.

A Step is the most fundamental unit in an opscotch workflow; it is a templated action that generally does the following:

  • make a call to a URL
  • process the response
  • take zero or more actions

The actions that can be taken are generally

  • send a metric
  • invoke one or more steps

The ability to invoke one or more steps means that we can create a graph of tasks, and in doing so enable us to construct sophisticated workflows to obtain the data you need, process and deliver it to where you need it to be.

For example the first step may retrieve a value from an API (call a URL). It might then assess the value (process response), and if required it might invoke another step (take action) which sends a message on slack - which is a step that calls out to a slack webhook url (call a url), simply makes sure that the request was accepted (process response), and perhaps sends a metric to record that a slack message was sent (take action).

This is how we can chain steps together to create a sophisticated workflow.

More about the Step

As mentioned previously the Step is the fundamental building block of the opscotch workflow. Each step performs a discrete task and you arrange steps to perform a sophisticated workflow.

The step is actually a templated function - it has a predefined execution order and you supply the specific implementation for each "hook" in the pattern. Thinking about the previously mentioned pattern of: make a call to a URL, then process the response, and then take zero or more actions: the "hooks" that are available to complete the implementation are:

  • authenticationProcessor : optionally allows you to dynamically choose how to prepare for authentication challenges perhaps by calling out to a complex workflow to obtain authentication credentials
  • splitGenerator : optionally allows you to dynamically transform any input data into a list to be iterated on
  • urlGenerator : allows you to dynamically construct a HTTP URL to call (optionally per item generated by the splitGenerator)
  • payloadGenerator : allows you to dynamically construct a HTTP payload body (ie json, webforms etc)
  • itemResultProcessor : when using a splitGenerator, optionally used to process individual responses
  • resultsProcessor : allows you to dynamically assess the HTTP response and decide what action to take

Each one of these "hooks" is called a JavascriptSource - and you express your tasks in Javascript.

The JavascriptSource

In the previous example we created a JavascriptSource that printed out Hello World - we did this with JavaScript.

The JavascriptSource is a "blank canvas" where you code in JavaScript how to process your data. The JavascriptSource runs in the agent in an isolated context and has access to a very limited set of data and functions.

The JavascriptSource has two properties of note:

  • script : allows you to define JavaScript directly in the workflow configuration - this is what we did in the previous example
  • resource : allows you to include a resource file: a self contained JavaScript file. This is the most common method. See below

These are available as properties on the JavascriptSource object and is useful when you only have a single script to execute i.e.:

{
"payloadGenerator" : {
"resource" : "..."
}
}

However, if you have multiple scripts that you'd like to execute on this step source, you can wrap them in the the processors array property like so:

{
"payloadGenerator" : {
"processors": [
{
"resource" : "..."
},
{
"resource" : "..."
}
]
}
}

The JavascriptContext

The JavascriptContext is the only bridge between the JavascriptSource and the agent - it creates a highly controlled safe environment to operate in. Inside the JavascriptSource the JavascriptContext object is available from the context object. ie context.getData().

The JavascriptContext makes available the following sets of data to operate on - note that the data will be in its raw form and you'll need to parse it accordingly ie jsonBody = JSON.parse(context.getPassedMessageAsString()):

  • context.getPassedMessageAsString(): return the message body that was passed to the step
  • context.getMessageBodyAsString(): returns the current message body - the message body could have been changed during the execution of the step, for example after the URL call was executed the message body would be the HTTP response
  • context.getData(): returns the data structure that was provided to the step in the configuration (see below)

The other sets of functionality are:

  • send message to another step or send a metric
  • work with timestamps
  • work with step properties
  • work with persistent data
  • work with HTTP headers, URLs and payload bodies

Be sure to review the JavascriptContext API for all the functions.

The Data property

The data property is on many objects and these objects are in a tree hierarchy, with the JavaScriptSource at the leaf node. When the agent loads each JavaScriptSource leaf node, it traverses up the parent nodes collecting and merging the configuration, such that each JavaScriptSource will have access to the data properties of its ancestors including the bootstrap.

The Trigger property

The trigger property is defined on steps that we want to start a workflow execution and is therefore optional. In the example the trigger that was defined to only run one time when the agent starts up by setting "runOnce" : true

Take a look at the other options on the Trigger

The Resource files

Resource files allow for reusable or complex Javascript to be stored in an easy to use place. In the previous example when discussing the packager, the configuration had the following property "resourceDirs" : ["."] - this defined the resource directory - in this case it wasn't used. In a normal scenario the resourceDirs would be an array of directories on disk that the packager (or test runner) has access to, such as "resourceDirs" : ["./resources"] - this will depend on were you store your resources.

Wrapping up

Now you know more details about how to write more sophisticated workflows that our Hello World example.

Now look at a full example using what you've learnt.