From 81e59ac553278e700c25ae9d08aa08af1a9b3910 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C4=93teris=20Caune?=
Date: Wed, 28 Oct 2020 14:28:32 +0200
Subject: [PATCH] Add support for script's exit status in ping URLs
Fixes: #429
---
CHANGELOG.md | 1 +
hc/api/tests/test_ping.py | 20 ++++++++++++++++
hc/api/urls.py | 1 +
hc/api/views.py | 5 +++-
templates/docs/attaching_logs.html | 19 ++++-----------
templates/docs/attaching_logs.md | 20 ++++------------
templates/docs/bash.html | 14 +++++------
templates/docs/bash.md | 12 +++++-----
templates/docs/http_api.html | 26 +++++++++++++++++++++
templates/docs/http_api.md | 29 +++++++++++++++++++++++
templates/docs/signalling_failures.html | 29 ++++++++++++++---------
templates/docs/signalling_failures.md | 31 +++++++++++++++++--------
12 files changed, 140 insertions(+), 67 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1556865b..ea0d51d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
- When saving a phone number, remove any invisible unicode characers
- Update the read-only dashboard's CSS for better mobile support (#442)
- Reduce the number of SQL queries used in the "Get Checks" API call
+- Add support for script's exit status in ping URLs (#429)
## v1.17.0 - 2020-10-14
diff --git a/hc/api/tests/test_ping.py b/hc/api/tests/test_ping.py
index 023a141c..81af3f84 100644
--- a/hc/api/tests/test_ping.py
+++ b/hc/api/tests/test_ping.py
@@ -224,3 +224,23 @@ class PingTestCase(BaseTestCase):
ping = Ping.objects.latest("id")
self.assertEqual(ping.scheme, "http")
self.assertEqual(ping.kind, "ign")
+
+ def test_zero_exit_status_works(self):
+ r = self.client.get("/ping/%s/0" % self.check.code)
+ self.assertEqual(r.status_code, 200)
+
+ self.check.refresh_from_db()
+ self.assertEqual(self.check.status, "up")
+
+ ping = Ping.objects.latest("id")
+ self.assertEqual(ping.kind, None)
+
+ def test_nonzero_exit_status_works(self):
+ r = self.client.get("/ping/%s/123" % self.check.code)
+ self.assertEqual(r.status_code, 200)
+
+ self.check.refresh_from_db()
+ self.assertEqual(self.check.status, "down")
+
+ ping = Ping.objects.latest("id")
+ self.assertEqual(ping.kind, "fail")
diff --git a/hc/api/urls.py b/hc/api/urls.py
index a2109e03..7546b68a 100644
--- a/hc/api/urls.py
+++ b/hc/api/urls.py
@@ -32,6 +32,7 @@ urlpatterns = [
path("ping/", views.ping, name="hc-ping"),
path("ping//fail", views.ping, {"action": "fail"}, name="hc-fail"),
path("ping//start", views.ping, {"action": "start"}, name="hc-start"),
+ path("ping//", views.ping),
path("api/v1/checks/", views.checks),
path("api/v1/checks/", views.single, name="hc-api-single"),
path("api/v1/checks/", views.get_check_by_unique_key),
diff --git a/hc/api/views.py b/hc/api/views.py
index b2ad28c7..0702f803 100644
--- a/hc/api/views.py
+++ b/hc/api/views.py
@@ -30,7 +30,7 @@ class BadChannelException(Exception):
@csrf_exempt
@never_cache
-def ping(request, code, action="success"):
+def ping(request, code, action="success", exitstatus=0):
check = get_object_or_404(Check, code=code)
headers = request.META
@@ -41,6 +41,9 @@ def ping(request, code, action="success"):
ua = headers.get("HTTP_USER_AGENT", "")
body = request.body.decode()
+ if exitstatus > 0:
+ action = "fail"
+
if check.methods == "POST" and method != "POST":
action = "ign"
diff --git a/templates/docs/attaching_logs.html b/templates/docs/attaching_logs.html
index 2617ea2e..6e4cfdcd 100644
--- a/templates/docs/attaching_logs.html
+++ b/templates/docs/attaching_logs.html
@@ -4,8 +4,8 @@
If the request body looks like a UTF-8 string, SITE_NAME will log the
first 10 kilobytes (10 000 bytes) of the request body, so you can inspect it later.
Logging Command Output
-In this example, we run certbot renew
, capture its output, and submit
-the captured output to SITE_NAME:
+In this example, we run certbot renew
, capture its output (both the stdout
+and stderr streams), and submit the captured output to SITE_NAME:
#!/bin/sh
m=$(/usr/bin/certbot renew 2>&1)
@@ -13,24 +13,13 @@ curl -fsS -m 10 --retry 5 --data-r
-In Combination with the /fail
Endpoint
+In Combination with the /fail
and /{exit-status}
Endpoints
We can extend the previous example and signal either success or failure
depending on the exit code:
#!/bin/sh
-url=PING_URL
-
m=$(/usr/bin/certbot renew 2>&1)
-
-if [ $? -ne 0 ]; then url=$url/fail; fi
-curl -fsS -m 10 --retry 5 --data-raw "$m" $url
-
-
-
-The above script can be packaged in a single line. The one-line
-version sacrifices some readability, but it can be used directly in crontab,
-without using a wrapper script:
-m=$(/usr/bin/certbot renew 2>&1); curl -fsS --data-raw "$m" "PING_URL$([ $? -ne 0 ] && echo -n /fail)"
+curl -fsS -m 10 --retry 5 --data-raw "$m" PING_URL/$?
diff --git a/templates/docs/attaching_logs.md b/templates/docs/attaching_logs.md
index ee70a931..7ff13d83 100644
--- a/templates/docs/attaching_logs.md
+++ b/templates/docs/attaching_logs.md
@@ -8,8 +8,8 @@ first 10 kilobytes (10 000 bytes) of the request body, so you can inspect it lat
## Logging Command Output
-In this example, we run `certbot renew`, capture its output, and submit
-the captured output to SITE_NAME:
+In this example, we run `certbot renew`, capture its output (both the stdout
+and stderr streams), and submit the captured output to SITE_NAME:
```bash
#!/bin/sh
@@ -18,7 +18,7 @@ m=$(/usr/bin/certbot renew 2>&1)
curl -fsS -m 10 --retry 5 --data-raw "$m" PING_URL
```
-## In Combination with the `/fail` Endpoint
+## In Combination with the `/fail` and `/{exit-status}` Endpoints
We can extend the previous example and signal either success or failure
depending on the exit code:
@@ -26,20 +26,8 @@ depending on the exit code:
```bash
#!/bin/sh
-url=PING_URL
-
m=$(/usr/bin/certbot renew 2>&1)
-
-if [ $? -ne 0 ]; then url=$url/fail; fi
-curl -fsS -m 10 --retry 5 --data-raw "$m" $url
-```
-
-The above script can be packaged in a single line. The one-line
-version sacrifices some readability, but it can be used directly in crontab,
-without using a wrapper script:
-
-```bash
-m=$(/usr/bin/certbot renew 2>&1); curl -fsS --data-raw "$m" "PING_URL$([ $? -ne 0 ] && echo -n /fail)"
+curl -fsS -m 10 --retry 5 --data-raw "$m" PING_URL/$?
```
## Using Runitor
diff --git a/templates/docs/bash.html b/templates/docs/bash.html
index 73664b37..8ffeaf79 100644
--- a/templates/docs/bash.html
+++ b/templates/docs/bash.html
@@ -31,19 +31,17 @@ hides error messages.
Redirect curl's stdout to /dev/null (error messages still go to stderr).
Signalling Failure from Shell Scripts
-You can append /fail
to any ping URL and use the resulting URL to actively
-signal a failure. The following example:
-
-- runs
/usr/bin/certbot renew
-- if the certbot command is successful (exit code 0), sends HTTP GET to
PING_URL
-- otherwise, sends HTTP GET to
PING_URL/fail
-
+You can append /fail
or /{exit-status}
to any ping URL and use the resulting URL
+to actively signal a failure. The exit status should be a 0-255 integer.
+SITE_NAME will interpret exit status 0 as success, and all non-zero values as failures.
+The following example runs /usr/bin/certbot renew
, and uses the $?
variable to
+look up its exit status:
#!/bin/sh
# Payload here:
/usr/bin/certbot renew
# Ping SITE_NAME
-curl -m 10 --retry 5 "PING_URL$([ $? -ne 0 ] && echo -n /fail)"
+curl -m 10 --retry 5 PING_URL/$?
diff --git a/templates/docs/bash.md b/templates/docs/bash.md
index 37d35ea2..8cd3129a 100644
--- a/templates/docs/bash.md
+++ b/templates/docs/bash.md
@@ -40,12 +40,12 @@ Here's what each curl parameter does:
## Signalling Failure from Shell Scripts
-You can append `/fail` to any ping URL and use the resulting URL to actively
-signal a failure. The following example:
+You can append `/fail` or `/{exit-status}` to any ping URL and use the resulting URL
+to actively signal a failure. The exit status should be a 0-255 integer.
+SITE_NAME will interpret exit status 0 as success, and all non-zero values as failures.
-* runs `/usr/bin/certbot renew`
-* if the certbot command is successful (exit code 0), sends HTTP GET to `PING_URL`
-* otherwise, sends HTTP GET to `PING_URL/fail`
+The following example runs `/usr/bin/certbot renew`, and uses the `$?` variable to
+look up its exit status:
```bash
#!/bin/sh
@@ -53,7 +53,7 @@ signal a failure. The following example:
# Payload here:
/usr/bin/certbot renew
# Ping SITE_NAME
-curl -m 10 --retry 5 "PING_URL$([ $? -ne 0 ] && echo -n /fail)"
+curl -m 10 --retry 5 PING_URL/$?
```
## Logging Command Output
diff --git a/templates/docs/http_api.html b/templates/docs/http_api.html
index 18c599d6..d2e8c156 100644
--- a/templates/docs/http_api.html
+++ b/templates/docs/http_api.html
@@ -82,6 +82,32 @@ optional but enables a few extra features:
+HTTP/1.1 200 OK
+Server: nginx
+Date: Wed, 29 Jan 2020 09:58:23 GMT
+Content-Type: text/plain; charset=utf-8
+Content-Length: 2
+Connection: close
+Access-Control-Allow-Origin: *
+
+OK
+
+
+
+Report Script's Exit Status
+HEAD|GET|POST PING_ENDPOINT{uuid}/{exit-status}
+
+
+
+Sends a success or failure signal depending on the exit status
+included in the URL. The exit status is a 0-255 integer. SITE_NAME
+interprets 0 as success, and all other values as failure.
+Example
+GET /5bf66975-d4c7-4bf5-bcc8-b8d8a82ea278/1 HTTP/1.0
+Host: hc-ping.com
+
+
+
HTTP/1.1 200 OK
Server: nginx
Date: Wed, 29 Jan 2020 09:58:23 GMT
diff --git a/templates/docs/http_api.md b/templates/docs/http_api.md
index 66c03d02..7beca14b 100644
--- a/templates/docs/http_api.md
+++ b/templates/docs/http_api.md
@@ -105,3 +105,32 @@ Access-Control-Allow-Origin: *
OK
```
+
+## Report Script's Exit Status
+
+```text
+HEAD|GET|POST PING_ENDPOINT{uuid}/{exit-status}
+```
+
+Sends a success or failure signal depending on the exit status
+included in the URL. The exit status is a 0-255 integer. SITE_NAME
+interprets 0 as success, and all other values as failure.
+
+**Example**
+
+```http
+GET /5bf66975-d4c7-4bf5-bcc8-b8d8a82ea278/1 HTTP/1.0
+Host: hc-ping.com
+```
+
+```http
+HTTP/1.1 200 OK
+Server: nginx
+Date: Wed, 29 Jan 2020 09:58:23 GMT
+Content-Type: text/plain; charset=utf-8
+Content-Length: 2
+Connection: close
+Access-Control-Allow-Origin: *
+
+OK
+```
diff --git a/templates/docs/signalling_failures.html b/templates/docs/signalling_failures.html
index 564a3ff5..ba7b065b 100644
--- a/templates/docs/signalling_failures.html
+++ b/templates/docs/signalling_failures.html
@@ -1,19 +1,26 @@
Signalling failures
-Append /fail
to a ping URL and use it to actively signal a failure.
-Requesting the /fail
URL will immediately mark the check as "down".
-You can use this feature to minimize the delay from your monitored service failing
-to you getting a notification.
+You can actively signal a failure to SITE_NAME by slightly changing the
+ping URL: append either /fail
or /{exit-status}
to your normal ping URL.
+The exit status should be a 0-255 integer. SITE_NAME will interpret
+exit status 0 as success, and all non-zero values as failures.
+Examples:
+# Reports failure by appending the /fail suffix:
+curl --retry 3 PING_URL/fail
+
+# Reports failure by appending a non-zero exit status:
+curl --retry 3 PING_URL/1
+
+
+
+By actively signalling failures to SITE_NAME, you can minimize the delay from your
+monitored service encountering a problem to you getting notified about it.
Shell Scripts
-The below shell script sends either a "success" or "failure" ping depending on
-command's (certbot in this example) exit code:
+The below shell script appends $?
(a special variable which contains the
+exit status of the last executed command) to the ping URL:
#!/bin/sh
-url=PING_URL
-
/usr/bin/certbot renew
-
-if [ $? -ne 0 ]; then url=$url/fail; fi
-curl --retry 3 $url
+curl --retry 3 PING_URL/$?
diff --git a/templates/docs/signalling_failures.md b/templates/docs/signalling_failures.md
index 1b69164c..50376591 100644
--- a/templates/docs/signalling_failures.md
+++ b/templates/docs/signalling_failures.md
@@ -1,24 +1,35 @@
# Signalling failures
-Append `/fail` to a ping URL and use it to actively signal a failure.
-Requesting the `/fail` URL will immediately mark the check as "down".
-You can use this feature to minimize the delay from your monitored service failing
-to you getting a notification.
+You can actively signal a failure to SITE_NAME by slightly changing the
+ping URL: append either `/fail` or `/{exit-status}` to your normal ping URL.
+The exit status should be a 0-255 integer. SITE_NAME will interpret
+exit status 0 as success, and all non-zero values as failures.
+
+Examples:
+
+```bash
+
+# Reports failure by appending the /fail suffix:
+curl --retry 3 PING_URL/fail
+
+# Reports failure by appending a non-zero exit status:
+curl --retry 3 PING_URL/1
+```
+
+By actively signalling failures to SITE_NAME, you can minimize the delay from your
+monitored service encountering a problem to you getting notified about it.
## Shell Scripts
-The below shell script sends either a "success" or "failure" ping depending on
-command's (certbot in this example) exit code:
+The below shell script appends `$?` (a special variable which contains the
+exit status of the last executed command) to the ping URL:
```bash
#!/bin/sh
-url=PING_URL
-
/usr/bin/certbot renew
+curl --retry 3 PING_URL/$?
-if [ $? -ne 0 ]; then url=$url/fail; fi
-curl --retry 3 $url
```
## Python