From 4bec23799eb76d98820cb6e076dfa0e90548799e Mon Sep 17 00:00:00 2001 From: schultzie Date: Tue, 2 May 2023 13:06:44 -0700 Subject: [PATCH 1/7] Add auto health check creation --- src/autostake/Health.mjs | 50 +++++++++++++++++++++++++++++----------- src/autostake/index.mjs | 4 ++-- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/autostake/Health.mjs b/src/autostake/Health.mjs index aa2ebad1..63171865 100644 --- a/src/autostake/Health.mjs +++ b/src/autostake/Health.mjs @@ -4,51 +4,75 @@ import { timeStamp } from '../utils/Helpers.mjs' class Health { constructor(config, opts) { - const { address, uuid } = config || {} - const { dryRun } = opts || {} + const { address, uuid, name, api_key } = config || {} + const { dryRun, networkName } = opts || {} + console.log(networkName); this.address = address || 'https://hc-ping.com' this.uuid = uuid + this.name = name || networkName + this.api_key = api_key this.dryRun = dryRun this.logs = [] + this.pingUrl = this.getOrCreateHealthCheck(); } - started(...args){ + getOrCreateHealthCheck(...args) { + if (!this.api_key) return; + + let config = { + headers: { + "X-Api-Key": this.api_key, + } + } + + let data = { + "name": this.name, "channels": "*", "timeout": 43200, "grace": 86400, "unique": ["name"] + } + + try { + var response = await axios.post([this.address, 'api/v2/checks/'].join('/'), data, config).then((res) => res.data.pingUrl); + } catch (error) { + timeStamp("Health Check creation failed: " + error); + } + } + + started(...args) { timeStamp(...args) - if(this.uuid) timeStamp('Starting health', [this.address, this.uuid].join('/')) + if (this.uuid) timeStamp('Starting health', [this.address, this.uuid].join('/')) return this.ping('start', [args.join(' ')]) } - success(...args){ + success(...args) { timeStamp(...args) return this.ping(undefined, [...this.logs, args.join(' ')]) } - failed(...args){ + failed(...args) { timeStamp(...args) return this.ping('fail', [...this.logs, args.join(' ')]) } - log(...args){ + log(...args) { timeStamp(...args) this.logs = [...this.logs, args.join(' ')] } - addLogs(logs){ + addLogs(logs) { this.logs = this.logs.concat(logs) } - async sendLog(){ + async sendLog() { await this.ping('log', this.logs) this.logs = [] } - async ping(action, logs){ - if(!this.uuid) return - if(this.dryRun) return timeStamp('DRYRUN: Skipping health check ping') + async ping(action, logs) { + if (!this.uuid) return + if (this.dryRun) return timeStamp('DRYRUN: Skipping health check ping') return axios.request({ method: 'POST', - url: _.compact([this.address, this.uuid, action]).join('/'), + url: _.compact([this.address, this.uuid, action]).join('/'), data: logs.join("\n") }).catch(error => { timeStamp('Health ping failed', error.message) diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 4c3bfdae..545bcfe3 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -36,8 +36,8 @@ export default function Autostake(mnemonic, opts) { return async () => { if (networkNames && networkNames.length && !networkNames.includes(data.name)) return if (data.enabled === false) return - - const health = new Health(data.healthCheck, { dryRun: opts.dryRun }) + console.log(data); + const health = new Health(data.healthCheck, { dryRun: opts.dryRun, networkName: data.name }) health.started('⚛') const results = await runWithRetry(data, health) const { success, skipped } = results[results.length - 1] || {} From a8c9c4efbc48af4173e308536637a16be80b2156 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Tue, 2 May 2023 13:33:48 -0700 Subject: [PATCH 2/7] Update check Signed-off-by: Dylan Schultz --- src/autostake/Health.mjs | 47 +++++++++++++++++----------------- src/autostake/index.mjs | 2 +- src/networks.local.json.sample | 8 ------ 3 files changed, 25 insertions(+), 32 deletions(-) delete mode 100644 src/networks.local.json.sample diff --git a/src/autostake/Health.mjs b/src/autostake/Health.mjs index 63171865..525c95cc 100644 --- a/src/autostake/Health.mjs +++ b/src/autostake/Health.mjs @@ -4,38 +4,17 @@ import { timeStamp } from '../utils/Helpers.mjs' class Health { constructor(config, opts) { - const { address, uuid, name, api_key } = config || {} + const { address, uuid, name, apiKey } = config || {} const { dryRun, networkName } = opts || {} - console.log(networkName); this.address = address || 'https://hc-ping.com' this.uuid = uuid this.name = name || networkName - this.api_key = api_key + this.apiKey = apiKey this.dryRun = dryRun this.logs = [] this.pingUrl = this.getOrCreateHealthCheck(); } - getOrCreateHealthCheck(...args) { - if (!this.api_key) return; - - let config = { - headers: { - "X-Api-Key": this.api_key, - } - } - - let data = { - "name": this.name, "channels": "*", "timeout": 43200, "grace": 86400, "unique": ["name"] - } - - try { - var response = await axios.post([this.address, 'api/v2/checks/'].join('/'), data, config).then((res) => res.data.pingUrl); - } catch (error) { - timeStamp("Health Check creation failed: " + error); - } - } - started(...args) { timeStamp(...args) if (this.uuid) timeStamp('Starting health', [this.address, this.uuid].join('/')) @@ -61,6 +40,28 @@ class Health { this.logs = this.logs.concat(logs) } + async getOrCreateHealthCheck(...args) { + if (!this.apiKey) return; + + let config = { + headers: { + "X-Api-Key": this.apiKey, + } + } + + let data = { + "name": this.name, "channels": "*", "timeout": 43200, "grace": 86400, "unique": ["name"] + } + + try { + await axios.post([this.address, 'api/v2/checks/'].join('/'), data, config).then((res) => { + this.uuid = res.data.ping_url.split('/')[4]; + }); + } catch (error) { + timeStamp("Health Check creation failed: " + error); + } + } + async sendLog() { await this.ping('log', this.logs) this.logs = [] diff --git a/src/autostake/index.mjs b/src/autostake/index.mjs index 545bcfe3..309d54c2 100644 --- a/src/autostake/index.mjs +++ b/src/autostake/index.mjs @@ -36,7 +36,7 @@ export default function Autostake(mnemonic, opts) { return async () => { if (networkNames && networkNames.length && !networkNames.includes(data.name)) return if (data.enabled === false) return - console.log(data); + const health = new Health(data.healthCheck, { dryRun: opts.dryRun, networkName: data.name }) health.started('⚛') const results = await runWithRetry(data, health) diff --git a/src/networks.local.json.sample b/src/networks.local.json.sample deleted file mode 100644 index b5f19027..00000000 --- a/src/networks.local.json.sample +++ /dev/null @@ -1,8 +0,0 @@ -{ - "akash": { - "prettyName": "Akash", - "restUrl": [ - "https://rest.validator.com/osmosis" - ] - } -} From 3ac7ea78db8b1abda54602c291b07d4b3e6b5854 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Tue, 2 May 2023 13:51:36 -0700 Subject: [PATCH 3/7] cleanup Signed-off-by: Dylan Schultz --- src/autostake/Health.mjs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/autostake/Health.mjs b/src/autostake/Health.mjs index 525c95cc..38e46e9b 100644 --- a/src/autostake/Health.mjs +++ b/src/autostake/Health.mjs @@ -7,12 +7,19 @@ class Health { const { address, uuid, name, apiKey } = config || {} const { dryRun, networkName } = opts || {} this.address = address || 'https://hc-ping.com' - this.uuid = uuid this.name = name || networkName this.apiKey = apiKey this.dryRun = dryRun + this.uuid = uuid this.logs = [] - this.pingUrl = this.getOrCreateHealthCheck(); + this.getOrCreateHealthCheck() + + if (address) { + // This is necessary as the default provider - hc-ping.com - has a built in ping mechanism + // whereas providing self-hosted addresses do NOT. + // https://healthchecks.selfhosted.com/ping/{uuid} rather than https://hc-ping.com/{uuid} + this.address = this.address + "/ping" + } } started(...args) { @@ -55,10 +62,10 @@ class Health { try { await axios.post([this.address, 'api/v2/checks/'].join('/'), data, config).then((res) => { - this.uuid = res.data.ping_url.split('/')[4]; + this.uuid = res.data.ping_url.split('/')[4] }); } catch (error) { - timeStamp("Health Check creation failed: " + error); + timeStamp("Health Check creation failed: " + error) } } From 6d3d40e83e088732449c800c1a3a51b6712883e4 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Tue, 2 May 2023 13:56:08 -0700 Subject: [PATCH 4/7] Add sample back in Signed-off-by: Dylan Schultz --- src/networks.local.json.sample | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/networks.local.json.sample diff --git a/src/networks.local.json.sample b/src/networks.local.json.sample new file mode 100644 index 00000000..b5f19027 --- /dev/null +++ b/src/networks.local.json.sample @@ -0,0 +1,8 @@ +{ + "akash": { + "prettyName": "Akash", + "restUrl": [ + "https://rest.validator.com/osmosis" + ] + } +} From 554c8d58e08219ced125ba625f41c57b0fbfc151 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Tue, 2 May 2023 13:59:08 -0700 Subject: [PATCH 5/7] Move UUID back Signed-off-by: Dylan Schultz --- src/autostake/Health.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autostake/Health.mjs b/src/autostake/Health.mjs index 38e46e9b..2a828208 100644 --- a/src/autostake/Health.mjs +++ b/src/autostake/Health.mjs @@ -7,10 +7,10 @@ class Health { const { address, uuid, name, apiKey } = config || {} const { dryRun, networkName } = opts || {} this.address = address || 'https://hc-ping.com' + this.uuid = uuid this.name = name || networkName this.apiKey = apiKey this.dryRun = dryRun - this.uuid = uuid this.logs = [] this.getOrCreateHealthCheck() From 9693c2e0ddba11c20fda7cf8987dd4b0f505cf39 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Wed, 3 May 2023 08:29:46 -0700 Subject: [PATCH 6/7] Add config for gracePeriod and timeout Signed-off-by: Dylan Schultz --- src/autostake/Health.mjs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/autostake/Health.mjs b/src/autostake/Health.mjs index 2a828208..3521ca46 100644 --- a/src/autostake/Health.mjs +++ b/src/autostake/Health.mjs @@ -4,11 +4,13 @@ import { timeStamp } from '../utils/Helpers.mjs' class Health { constructor(config, opts) { - const { address, uuid, name, apiKey } = config || {} + const { address, uuid, name, apiKey, timeout, gracePeriod } = config || {} const { dryRun, networkName } = opts || {} this.address = address || 'https://hc-ping.com' - this.uuid = uuid this.name = name || networkName + this.gracePeriod = gracePeriod || 86400 // default 24 hours + this.timeout = timeout || 86400 // default 24 hours + this.uuid = uuid this.apiKey = apiKey this.dryRun = dryRun this.logs = [] @@ -57,7 +59,7 @@ class Health { } let data = { - "name": this.name, "channels": "*", "timeout": 43200, "grace": 86400, "unique": ["name"] + "name": this.name, "channels": "*", "timeout": this.timeout, "grace": this.gracePeriod, "unique": ["name"] } try { From 9d1906961674418a9c3b25e8c269612266b37a18 Mon Sep 17 00:00:00 2001 From: Dylan Schultz Date: Wed, 3 May 2023 08:40:41 -0700 Subject: [PATCH 7/7] Add documentation for auto-healthchecks Signed-off-by: Dylan Schultz --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 4342a328..a0003ad9 100644 --- a/README.md +++ b/README.md @@ -347,6 +347,24 @@ Add your Check UUID to the relevant network in your `networks.local.json` config } ``` +If you wish for your health checks to be created automatically, you can provide an [API key](https://healthchecks.io/docs/api/) to manage it for you. +The default behavior is for the check to be named after the network, but this can be overridden with the `name` property. + +**Note that the `uuid` is not necessary when using an `apiKey` to create checks automatically.** + +```JSON +{ + "cheqd": { + "healthCheck": { + "apiKey": "12_CanM1Q3T72uGH4kc32G14BdA4Emc4y", + "name": "cheqd every 12 hours", // optional, defaults to the network name + "timeout": 43200, // optional, expected seconds between each run + "gracePeriod": 86400, // optional, grace period seconds before it notifies + } + } +} +``` + ### Submitting your operator #### Setup your REStake operator