Full Example
Our previous examples have been... simple and do not even begin to show what opscotch can do.
Lets write a workflow that actually uses some real world-data.
Here is the use case:
We want to continually collect a city's temperature and send that as a metric.
We can use a service to get the weather data: https://openweathermap.org/ has an API that returns the current weather.
This example is a simplistic "real world" example to demonstrate the opscotch agent's mechanics. There is so much potential in what is possible - limited only by your imagination.
Getting started
You can follow along with this demonstration with your own opscotch agent.
However, if you're feeling overwhelmed, read this page to the end without running the agent yourself.
When you see how easy it is, follow along with your own opscotch agent.
Before you start, you'll need a few prerequisites:
- A Java runtime installed (to run the tester)
- The opscotch test runner (jar)
You should have completed the previous example and have been able to run an opscotch workflow on an agent. If you have not done that, please do it first.
We'll be working in your operating system's terminal, so you need to know what that is and how to use it.
You'll need a copy of the opscotch agent and configuration packager executables that is compatible with the operating system that you are using.
Create a directory somewhere to work in, and put all the files there, including the opscotch executables.
Ensure you've read the Basic Concepts, specifically the "moving parts".
You'll need an opscotch (trial) license key.
- Save all text files as UTF-8 with LF line feeds.
- Any file paths in text files need to either use
/
instead of\
or escape\
like\\
File setup
In this example, you'll be working with files. For your reference, we'll outline the file structure now for you to refer back to:
working directory
|\_ configs (configs for deploying to the agent)
| \_ license.text (your license file)
| \_ agent.public.key (agent public key file)
| \_ weather (weather configs for deploying to the agent)
| \_ weather.bootstrap.json (weather bootstrap file)
| \_ weather.config.json (weather workflow configuration file)
| \_ weather.config (weather packaged workflow configuration file)
| \_ weather.package.config.json (weather packaging configuration file)
|\_ resources (common javascript files)
| \_ weather (weather specific javascript files)
| \_ weather-resultsprocessor.js (weather results processor)
| \_ weather-urlgenerator.js (weather url generator)
|\_ tests (tests folder)
| \_ weather (weather tests)
| \_ weather.test.json (the weather test file)
| \_ weather.bootstrap.json (the weather test bootstrap file)
| \_ weather.config.json (the weather test configuration file)
| \_ weather.response.json (the weather API response file)
\_ testrunner.config.json (configures the test runner)
Create a folder in the working directory called resources
and then one called weather
(i.e. resources/weather
) - this will be where we store all the javascript files we create.
Create a folder in the working directory called tests
and then one called weather
(i.e. tests/weather
) - this will be where we store the testing files we create.
- Linux/macOS
- Windows
mkdir -p configs/weather resources/weather tests/weather
mkdir configs && mkdir configs\weather && mkdir resources && mkdir resources\weather && mkdir tests && mkdir tests\weather
The Weather Data Collection Task
We'll be pulling our data from OpenWeatherMap. You can get an idea about that here, and you can get your free API key here.
For this example, we'll let you use our API key; however we can't guarantee it will work.
a83da2d308613877a88910dce9568dd5
The URL that we're going to use looks like this, and we'll want to provide the city name and the API key:
https://api.openweathermap.org/data/2.5/weather?units=metric&q={city name}&appid={API key}
And when called effectively returns:
{
"main": {
"temp": 21.55
},
"dt": 1560350645,
"name": "Wellington"
}
Our opscotch task will be as follows:
Every 10 minutes, do this:
- construct the URL to include the city name and the API key.
- execute a call to the URL.
- parse the response into a JSON object.
- send the temperature as a metric.
Approach
As this is a complete example, we're going to take a best-practice approach and do the following:
- Understand the HTTP call.
- Create a test.
- Create a workflow executed against the test.
- Create an agent-deployable workflow.
Understanding the HTTP call
The first task in creating a workflow is understanding what needs to be done; this often starts with understanding the HTTP call and the resulting data.
The URL that we're going to use looks like this:
https://api.openweathermap.org/data/2.5/weather?units=metric&q={city name}&appid={API key}
The best thing to do is to execute the HTTP call using your terminal and check out the results:
- Linux/macOS
- Windows
export OPENWEATHERMAP_KEY=a83da2d308613877a88910dce9568dd5
export CITY_NAME=Wellington
curl "https://api.openweathermap.org/data/2.5/weather?units=metric&q=$CITY_NAME&appid=$OPENWEATHERMAP_KEY"
$env:OPENWEATHERMAP_KEY = 'a83da2d308613877a88910dce9568dd5'
$env:CITY_NAME = 'Wellington'
Invoke-WebRequest -Uri "https://api.openweathermap.org/data/2.5/weather?units=metric&q=$env:CITY_NAME&appid=$env:OPENWEATHERMAP_KEY"
You should get results like this:
{
"coord": {
"lon": 174.7756,
"lat": -41.2866
},
"weather": [
{
"id": 803,
"main": "Clouds",
"description": "broken clouds",
"icon": "04d"
}
],
"base": "stations",
"main": {
"temp": 12.53,
"feels_like": 11.95,
"temp_min": 12.53,
"temp_max": 15.51,
"pressure": 1016,
"humidity": 81
},
"visibility": 10000,
"wind": {
"speed": 10.29,
"deg": 340
},
"clouds": {
"all": 54
},
"dt": 1666737490,
"sys": {
"type": 2,
"id": 2007945,
"country": "NZ",
"sunrise": 1666718172,
"sunset": 1666767204
},
"timezone": 46800,
"id": 2179537,
"name": "Wellington",
"cod": 200
}
When we review out task: "collect the temperature from a city", we can reduce the JSON down to just what we need: the temperature and the timestamp:
{
"main": {
"temp": 12.53
},
"dt": 1666737490
}
Now that we have an understanding of the available data and how to get it, we can start with the tests.
Testing
Tests are essential: for every workflow you write, you should write a test.
A test means you can be confident that the opscotch agent executes consistently, especially important during workflow development and upgrades.
The Test Runner Configuration
Let's create the test runner configuration - this tells the test runner how to find our resources and tests: create a file in the working directory called testrunner.config.json
with this content:
{
"resourceDirs": [
"resources"
],
"testDirs": [
"tests"
],
"license": "license.txt"
}
You'll need to replace the contents of the license file with your supplied license (i.e. paste in the contents of your license).
The Weather Test File
In the tests/weather
directory, create a file called weather.test.json
- this is where we set up our test for the weather workflow. Take a look at the test schema in the API reference.
Files named *.test.json
are recognised and discovered by the test runner as test files.
Add this content:
{
"fromDirectory": "weather",
"useThisTestBootstrapFile": "weather.bootstrap.json",
"useThisTestConfigFile": "weather.config.json",
"testThisStep" : "current-temperature",
"mockEndpointResponses" : [
{
"whenThisIsCalled" : "http://mockserver/data/2.5/weather?units=metric&q=Wellington&appid=1234",
"returnThisFile" : "weather.response.json"
}
],
"theseMetricsShouldHaveBeenSent": [
{
"key" : "temperature",
"value" : 12.53,
"timestamp" : 1666737490
}
]
}
What we've setup so far:
fromDirectory
tells the test runner the working directory for the test.useThisTestBootstrapFile
tells the test what Bootstrap file to load - this will be specific for the test and wouldn't be deployed to an agent.useThisTestConfigFile
tells the test what workflow file to load.testThisStep
tells the test which step to execute.mockEndpointResponses
tells the test how to respond to HTTP requests from the agent.theseMetricsShouldHaveBeenSent
tells the test what metrics to expect the agent to send during the test.
These files don't exist yet; let's create them.
The weather.bootstrap.json
This Bootstrap construct offers us a few advantages:
- Defines allowed hosts -security comes first in the agent, and you're not allowed to call out to any old host.
- Defines allowed HTTP methods and paths.
- Abstracts the host details from the workflow configuration such that we can use the same configuration with different hosts.
- Enables testing the workflow configuration without actually using a real server.
weather.bootstrap.json
Bootstrap file is the same as any other Bootstrap file and, in this case, will be specific for the test: since this is a test, we want to be in total control of what data the agent gets when it makes an API call. If we let the agent call out to the actual API, we'd get the constantly changing weather data which makes for ineffective testing. The way we get around this is to mock the API response for the test. We start this process with the test Bootstrap file, and instead of defining a real OpenWeatherMap API server, we'll define one called mockserver
for the test to use.
Let's do that now: update the contents of tests/weather/weather.bootstrap.json
to this:
{
"hosts" : {
"openweathermap" : {
"host" : "http://mockserver",
"allowlist" : [
["GET", "/data/2.5/weather"]
]
}
}
}
This defines:
- A host record called
openweathermap
that points to a hosthttp://mockserver
(the test runner - this would behttps://api.openweathermap.org
in the actual Bootstrap file) - An
allowlist
that permits an HTTPGET
call on the path/data/2.5/weather
The weather.response.json
In the test configuration, we defined mockEndpointResponses
to be the following:
{
"mockEndpointResponses" : [
{
"whenThisIsCalled" : "http://mockserver/data/2.5/weather?units=metric&q=Wellington&appid=1234",
"returnThisFile" : "weather.response.json"
}
]
}
When the agent calls http://mockserver/data/2.5/weather?units=metric&q=Wellington&appid=1234
during the test, the contents of weather.response.json
will be returned. Since this is an array, we can define any number of these.
Notice that we refer to http://mockserver
- which comes from the test Bootstrap.
Remember that we're pretending (mocking) to be the OpenWeatherMap API server - the contents of weather.response.json
should be the contents of what the OpenWeatherMap API server would return. However, if we set the content to be the trimmed-down version (i.e. only what we want to use), then we're also giving a clue to anyone reading the tests about the data that we care about: so let's set the content of tests/weather/weather.response.json
to this:
{
"main": {
"temp": 12.53
},
"dt": 1666737490
}
The Weather Metric
In the test configuration file, we defined this:
{
"theseMetricsShouldHaveBeenSent": [
{
"key" : "temperature",
"value" : 12.53,
"timestamp" : 1666737490
}
]
}
This declares that when the test runs, we expect the agent to send a metric with a key
of temperature
, a value
of 12.53
, and a timestamp
of 1666737490
- thats from the dt
field on the OpenWeatherMap API response.
If it does not happen the test will fail - its up to us to make this happen in the workflow!
You don't need to define the expected timestamp because sometimes you won't know - for the curious there are ways around this.
The Weather Workflow
Now we're done (for now) with the test plumbing, and we've defined our expectation (the metric) we can work on the workflow to do all the work (exciting)!
Always keep the goal in mind:
We want to continually collect a city's temperature and send that as a metric.
Take a look at the workflow structure that we need to work in:
- We'll create a
workflow
. - We'll create a workflow
step
. - Since we want it to trigger every minutes we'll add a timer tigger.
- Since we're calling a URL the step will need a
urlGenerator
. - And we'll need to process the response with a
resultsProcessor
.
Let's give it a go: set the contents of tests/weather/weather.config.json
:
{
"workflows": [
{
"name" : "Current Temperature",
"steps" : [
{
"type" : "scripted",
"stepId" : "current-temperature",
"trigger" : {
"timer" : {
"period" : 60000
}
},
"urlGenerator" : {
"script" : ""
},
"resultsProcessor" : {
"script" : ""
}
}
]
}
]
}
So far, we've defined the following
- A workflow with a friendly name.
- A
scripted
step with an ID and a timer. - An empty
urlGenerator
andresultsProcessor
.
At this stage, we should be able to run the test and have it fail... why? Because we've not met the expectation of the metric being sent, we've not even called the URL!
Let's live on the edge and learn how to run the test even though it will fail.
Running the (failing) test
Running the test involves two processes:
- The test runner.
- The opscotch agent.
You'll need to be able to run both these at the same time in different terminal windows.
In the first terminal, in your working directory: run the test runner with the testrunner.config.json
file :
- Linux/macOS
- Windows
java -jar testrunner.jar testrunner.config.json
java.exe -jar testrunner.jar testrunner.config.json
Executing this will start the test runner, and you'll see a log line similar to this:
Start the opscotch agent with this argument: <your working directory>/test.bootstrap.json
Copy the path in the log and start the opscotch agent in the other terminal window (you can leave the agent running):
- Linux/macOS
- Windows
opscotch-agent <your working directory>/test.bootstrap.json
opscotch-agent.exe <your working directory>/test.bootstrap.json
Executing the agent command, you'll see these key logs:
Agent Starting
opscotch test suite: READY
When the test runner sees the opscotch test suite: READY
log, it will start running the test and output these key logs:
opscotch agent READY, starting tests
Test Started: [1] tests/weather/weather.test.json
Test Finished: [FAILED] [1] file=tests/weather/weather.test.json: mock://http://localhost:9999/data/2.5/weather?appid=1234&q=Wellington&units=metric Received message count. Expected: <1> but was: <0>
This clearly says that the test expected the URL to be called and it was not.
Let's fix that.
Implement the urlGenerator
Let's think about how the urlGenerator
might work:
- As defined in the test, this is what we expect to be called:
http://mockserver/data/2.5/weather?units=metric&q=Wellington&appid=1234
- broken down:
http://mockserver
is the host that's defined in the Bootstrap by the named hostopenweathermap
/data/2.5/weather?units=metric&q=Wellington&appid=1234
is the path and the query string
- broken down:
There are a few ways we can tackle this:
- We can hard code the URL 'as is' - this would work fast, but it's not very flexible if we want to reuse this workflow.
- We can parameterise the city name and appid arguments - this will take a bit longer but makes the workflow reusable.
Let's do both (nothing like a bit of instant gratification to pique one's interest).
Hardcoded URL
Adding a hardcoded urlGenerator
would look like this:
{
"workflows": [
{
"name" : "Current Temperature",
"steps" : [
{
"type": "scripted",
"stepId": "current-temperature",
"trigger" : {
"timer" : {
"period" : 60000
}
},
"urlGenerator" : {
"script" : "context.setUrl('openweathermap','/data/2.5/weather?units=metric&q=Wellington&appid=1234');"
},
"resultsProcessor" : {
"script" : ""
}
}
]
}
]
}
urlGenerator
is a JavascriptProcessor
, and more specifically, it is a JavaScriptSource
that allows you to define either a Javascript resource
or a Javascript script
- both of which are... Javascript!
To interact with the agent from Javascript, you can only use the JavascriptContext
, which is referred to as context
in Javascript. In the example above, the urlGenerator.script
property is a string of Javascript and is calling context.setUrl(hostref, path)
- the hostref
is the named host in the Bootstrap, and the path
is the URL path to call.
You can't just enter any old URL with any old host - opscotch is secure by design, and the person controlling the Bootstrap is the only one who can authorise which hosts can be called.
Re-run the tests and see what happens:
- Linux/macOS
- Windows
java -jar testrunner.jar testrunner.config.json
java.exe -jar testrunner.jar testrunner.config.json
This time the test runner log outputs:
Test Finished: [FAILED] [1] file=tests/weather/weather.test.json: Message with body {"token":"native-test","timestamp":0,"name":"temperature","value":12.53,"dimensionMap":{},"type":"metric"} was expected
This is clearly saying that the metric was expected and not received. Let's continue with the instant gratification and implement metric sending.
Metric sending
At this point, we've been able to construct our URL and ask the agent to execute the HTTP call. We'll now implement the resultsProcessor
: the agent will pass us the HTTP response, and we'll send a metric (to the test runner).
In our test, we declared the expected metric:
{
"theseMetricsShouldHaveBeenSent": [
{
"key" : "temperature",
"value" : 12.53,
"timestamp" : 1666737490
}
]
}
Take a look at the JavascriptContext
and find the sendMetric()
method. There are several versions: the most basic version is sendMetric(key, value)
- using this one, you don't supply a timestamp; the agent will create one for you. The next version includes a timestamp sendMetric(timestamp, key, value)
- this one is good for us as we have a timestamp in the data.
Let's think about how we'll process the response from the agent (from the openweathermap API server, well actually: mocked from the test runner). It's going to pass us this:
{
"main": {
"temp": 12.53
},
"dt": 1666737490
}
We'll use the context method getMessageBodyAsString()
, which, as it says, returns a string:
body = context.getMessageBodyAsString();
To create our metric using the method described above, we need to get hold of the value and the timestamp - since the body
is currently a JSON string, it would make sense to parse it into a JSON object using the standard Javascript JSON.parse()
method:
body = context.getMessageBodyAsString();
jsonObject = JSON.parse(body);
Now we can use the Javascript object notation to extract the value and timestamp into variables:
body = context.getMessageBodyAsString();
jsonObject = JSON.parse(body);
timestamp = jsonObject.dt;
value = jsonObject.main.temp;
We're almost ready to send our metric - just one more thing: what is the key
? In the test, we said "temperature"
, - so that's the key we'll set. Let's do it:
body = context.getMessageBodyAsString();
jsonObject = JSON.parse(body);
timestamp = jsonObject.dt;
value = jsonObject.main.temp;
key = "temperature";
context.sendMetric(timestamp, key, value);
Done! That was easy.
If you're coding along, you're probably wondering: "where do I put all that?" - you have two options:
- Directly on the
script
property of theresultsProcessor
. - In a file references by the
resource
property of theresultsProcessor
.
As we've already demonstrated using the script
property, we'll use the resource
property - arguably the correct thing to do as this is more than a simple one-liner.
Create a file in resources/weather/weather-resultsprocessor.js
and update the contents to the script above (the one with the sendMetric
command!)
Update tests\weather\weather.config.json
with the resource
set on the resultsProcessor
:
{
"workflows": [
{
"name" : "Current Temperature",
"steps" : [
{
"type": "scripted",
"stepId": "current-temperature",
"trigger" : {
"timer" : {
"period" : 60000
}
},
"urlGenerator" : {
"script" : "context.setUrl('openweathermap','/data/2.5/weather?units=metric&q=Wellington&appid=1234');"
},
"resultsProcessor" : {
"resource" : "weather/weather-resultsprocessor.js"
}
}
]
}
]
}
Re-run the tests and see what happens:
- Linux/macOS
- Windows
java -jar testrunner.jar testrunner.config.json
java.exe -jar testrunner.jar testrunner.config.json
This time the test runner log outputs:
Test Finished: [SUCCESSFUL] [1] file=tests/weather/weather.test.json
🎉 🎉 CONGRATULATIONS 🎉🎉
You've successfully created a workflow that meets the criteria of your test!
Can we make this better? Remember the hardcoded URL? Ick. Let's do that properly.
Parameterised URL
Remember this classy piece of instant gratification:
{
"urlGenerator" : {
"script" : "context.setUrl('openweathermap','/data/2.5/weather?units=metric&q=Wellington&appid=1234');"
}
}
We hard coded this q=Wellington&appid=1234
which, if we want this workflow to be flexible and reusable, we should make the city name and appid into parameters.
In opscotch, we have the data
construct - this concept allows us to pass properties into our Javascript. Take a look at the Bootstrap and workflow schemas. You'll see multiple "data": {}
scattered around - this indicates that you can provide a data object that will be passed into your Javascript and be available on context.getData()
(which will return the entire data object) or context.getData(key)
, which will only return the property named by that key.
Data objects are inherited and merged, so if any object in the configuration tree declares a data object, it will be available to the descendant Javascript. This even extends to the Bootstrap: if you declare a data
object on the Bootstrap it will be available to any Javascripts that execute in the workflows loaded by that Bootstrap.
How can we use this to parameterise the URL?
We can enhance our urlGenerator
: add the data
object with the city name and appid:
{
"urlGenerator" : {
"script" : "context.setUrl('openweathermap','/data/2.5/weather?units=metric&q=Wellington&appid=1234');",
"data" : {
"cityname" : "Wellington",
"appid" : "1234"
}
}
}
We'll now modify our script to get the parameters:
cityname = context.getData("cityname");
appid = context.getData("appid");
Then we'll use those to generate our URL:
cityname = context.getData("cityname");
appid = context.getData("appid");
context.setUrl('openweathermap','/data/2.5/weather?units=metric&q=' + cityname + '&appid=' + appid);
As our script is not a simple one-liner, let's throw it into a resource
file: Create a file in resources/weather/weather-urlgenerator.js
and update the contents to the script above.
Then update the workflow:
{
"workflows": [
{
"name" : "Current Temperature",
"steps" : [
{
"type": "scripted",
"stepId": "current-temperature",
"trigger" : {
"timer" : {
"period" : 60000
}
},
"urlGenerator" : {
"resource" : "weather/weather-urlgenerator.js",
"data" : {
"cityname" : "Wellington",
"appid" : "1234"
}
},
"resultsProcessor" : {
"resource" : "weather/weather-resultsprocessor.js"
}
}
]
}
]
}
Re-run the tests and see what happens:
- Linux/macOS
- Windows
java -jar testrunner.jar testrunner.config.json
java.exe -jar testrunner.jar testrunner.config.json
The test runner log outputs:
Test Finished: [SUCCESSFUL] [1] file=tests/weather/weather.test.json
Did it actually work? Try changing the appid
and re-running the test - it should fail.
Its always good to double check.
Good Job! However, simply adding the data object to the urlGenerator
doesn't make the workflow reusable - the city name and the appid are still hardcoded into the workflow: let's move the data object onto the Bootstrap.
Update the contents of tests/weather/weather.bootstrap.json
to this:
{
"hosts" : {
"openweathermap" : {
"host" : "http://mockserver",
"allowlist" : [
["GET", "/data/2.5/weather"]
]
}
},
"data" : {
"cityname" : "Wellington",
"appid" : "1234"
}
}
Then update the workflow to remove the data object:
{
"workflows": [
{
"name" : "Current Temperature",
"steps" : [
{
"type": "scripted",
"stepId": "current-temperature",
"trigger" : {
"timer" : {
"period" : 60000
}
},
"urlGenerator" : {
"resource" : "weather/weather-urlgenerator.js"
},
"resultsProcessor" : {
"resource" : "weather/weather-resultsprocessor.js"
}
}
]
}
]
}
Re-run the tests and see what happens:
- Linux/macOS
- Windows
java -jar testrunner.jar testrunner.config.json
java.exe -jar testrunner.jar testrunner.config.json
The test runner log outputs:
Test Finished: [SUCCESSFUL] [1] file=tests/weather/weather.test.json
Deploying to the Agent
Now we have a rock-solid workflow and a bootstrap example: we can set up the agent to execute the workflow!
Quickly review the "hello world" example where you started up the agent. Remind yourself of:
- The agent bootstrap.
- The keys.
- The packager.
Lets start with the workflow, copy tests/weather/weather.config.json
to configs/weather/weather.config.json
Now the packager: create the packaging configuration file configs/weather/weather.package.config.json
with the following contents:
{
"license" : "license.txt",
"agentPublicKey" : "configs/agent.public.key",
"resourceDirs" : ["resources"]
}
Run the packager from the working directory:
- Linux/macOS
- Windows
cat configs/weather/weather.config.json | opscotch-packager workflow configs/weather/weather.package.config.json > configs/weather/weather.config
type configs/weather/weather.config.json | opscotch-packager.exe workflow configs/weather/weather.package.config.json > configs/weather/weather.config
Outputs something like:
Success: TestConfig file version: 1d787496 file: configs/weather/weather.config
Now the Bootstrap: create the file configs/weather/weather.bootstrap.json
with the following content:
[
{
"remoteConfiguration" : "configs/weather/weather.config",
"deploymentId" : "weather-examples",
"agentPrivateKey" : "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRRGF0VGRhcC81TVQ5bzkKemYyUXZ1akZQeTUrWmZsYi9UZzZBRkROSERCcHpQMldSZU5TNkJkL3JqaEE4NnpiTWJlN2pxcFZJbFp0eFQ0dwpKYzNMM0c0UmxOVEM5VVczMmxyNjBJYWRFOU1xcTZYdENicWVGQlVxV0tFb1RjaWQyVUlFSDRpMG9sbkF2ditICnRKeUJrZ3NVV0w5cmN4dFloODZmK3dqVEFoSmNCS1RMcFZkakFoMHJkS0RuSmxXSHJ4NkswNVlZc2RLR1pyTHcKSTJCekpSZytlREp3SEo5MExUUEhVTFB0Ni9QN1ppUDBObHc5ZnYzYjlncVJiaW1ZMzFUQ2xxZ2k2eXR1K2dhRgpMbjNzdW1nUjBZVUR1d0RhaGlYZVlPZ0JtdjBjS3RuWlBMZitwblpubThRR3ZWcVF2T2JXSlpzUmYreXc1Nk91CkRMYUJhYnFqQWdNQkFBRUNnZ0VBQ3FpdVMvTElCL3dVVE5MUE90Q3pFM25pMERJMEZ2R2VYZVZLQ3RVRzN3RW8Ka3J4Uk9lY3B6Z1UxcEtTN2xlczBZeVd1MDN0YTd2cWY3MFRJWk5rWFhDLyt0b214bmlZOExPRWd6bUw2OWFkcgo3TU4rbnRtbnRtcWZZQkY2WjRjQkdxWld5VWJvVE9hQ0dhRUp4NTdRZjJ0YjJPYkJQRzFSK3FZSFpoRkJ4ODR6CkVMTllhSkZLdFV2cTl0L1RpbFI2VExYZU8yOGhFejJSUnRVanRLc1JKWlRtdkRkNVZtRkNJZnV3UjRPZHJIUjMKTndSRnBwbmdMc1Y1ZWxjN25TYXBuaG1pZnVscEFnZFJjclFjaGpEYjlpa2s5Uk9nWmprOHozNkFWempQT1E1NgpoelVmTWcvMk54eFJNOHpBdC9xdExmdFN0NlpOTHJCVjFENHVDbXBWY1FLQmdRRHdkRWszRnF0dWptNFhxQ2o3Cm55Q0pqN3VBQjFGR0ZlemU1cFM1SVIyU3o0Ri9FQW9PTTlUbjA3cWNpamY2c0RTVGF5WEh2V0xlVTdENkljZSsKb3AycXZUTXpHMTRxZHpMTkk2SVVNbERZWVJVYTVtVHFzY1hCeCtxMTUxOE4zUVhpQ1IvQ1EwbGR4SFVRTXkzcgo3NXlKTXd1aEtrTVdHZGVSVXpVN0N5SFpoUUtCZ1FEbzJRS1c3WGIxNW1TY2xEVElMc1JrZVk3TEdtZTRweFk5CmU2aHJPUjd3bjE3ZTR5NSsxQmdpcDU0KzR5WitWbWRLcjVQNUdXVW9UQVkwMXRPZ1Q2NmJYMkNHSTBwVFB6OEMKdDRGQS9OT0IrNnNETHhVMDh1dHR6clBEclhWSldLZjNoSHEwRXZTTzJtbUZ6NDV0eE93RDBPZzZHeGZuQll4cwpaYjNQdTRBb0J3S0JnUURuSmxLUHc1b3ZZSHBUQVpQTU42K1dyZmFJWUFCd1NIbDUyZDJVamxJWXBLUVBHc01mCjNoSElmbENCVlpuMHd0ajM4bVBTUEt4RG45NmN4a1ozYTJuNERZT2ZtS3FRVmlBZTlpZEVGUTZFdytiL0FnWjMKVzBnSlBmN1N1VVkyVklmb1ArOG8xM2c2ekxYZ1NlTFdVWFNBcS9KOUR0eUk3WFMrUG1nSWgyNFY1UUtCZ0VXYQpDTUlndlh5SG4rK0dCYmtJOGIrdTdsWWIwSUZMd01Ra0lKWGNmemtpQ25QSnFPNVVhOXFBRERkMEl0K0U2RjlVCjJJQ1BiTU44NWlZSlNUMFVWSGN4TFdNclRWNVErMzdPWjU2TGRKWFlLcWhLL0tYVkx6YWxvT3FQQVE0cHFCdSsKeUFibjI1NlAveGIya2ZhUjlKSzNjWk5jNGVvMnFHRUdrSlIxMGJzbEFvR0FPNHNwekVaTllUQmhIVXZmNUVpRgpFNHM3amVZNE1nSjcrRUhIZm9jTEFPd3QxcmR1V21LQTNTSDhoOWk4bmhUaEhuTGVKc1c3dld6Qk1NQnAvWWFHCmxMUVFtNk8xNytKbVpRakM2OFBiditpcUlOTzlzcm5VUEhkMEU2WEcxUzZwUTVZVExzQXJscmtBZzBScGIzTDkKVDBYWW03MFprYy81WGZuOVNFd0dEV0k9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K",
"hosts" : {
"openweathermap" : {
"host" : "https://api.openweathermap.org",
"allowlist" : [
["GET", "/data/2.5/weather"]
]
}
},
"data" : {
"cityname" : "Wellington",
"appid" : "a83da2d308613877a88910dce9568dd5"
}
}
]
Start the agent with the Bootstrap file:
- Linux/macOS
- Windows
opscotch-agent configs/weather/weather.bootstrap.json
opscotch-agent.exe configs/weather/weather.bootstrap.json
Look out for the logs:
Agent Starting
Bootstrap configuration loaded
New configuration loaded configversion:43368e10 for deploymentId:weather-examples from fileversion:1d787496
Remote call to: https://api.openweathermap.org/data/2.5/weather?units=metric&q=Wellington&appid=a83da2d308613877a88910dce9568dd5
Registered new metric "temperature", metaData: deploymentId=weather-examples