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