diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6c6aca3..30c8c7a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -76,8 +76,7 @@ pages: <<: *pull_cache stage: deploy script: - - pwd - - ls + - sed -e "/Unreleased/,+1d" -i CHANGELOG.md - make docs - echo "https://framasoft.frama.io/framaspace/argos/* https://argos-monitoring.framasoft.org/:splat 301" > public/_redirects artifacts: diff --git a/CHANGELOG.md b/CHANGELOG.md index da118ed..9d84fea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## [Unreleased] +- 💄 — Correctly show results on small screens +- 📝💄 — Add opengraph tags to documentation site (#62) +- 🔨 — Add a small web server to browse documentation when developing +- ✨ — Add new check type: http-to-https (#61) +- 👷 — Remove Unreleased section from CHANGELOG when publishing documentation +- 🩹 — Severity of ssl-certificate-expiration’s errors is now UNKNOWN (#60) +- 💄 — Better display of results’ error details + ## 0.4.1 Date: 2024-09-18 diff --git a/Makefile b/Makefile index 53d3315..9d6bec1 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,8 @@ docs: cog ## Build the docs if [ ! -e "public/mermaid.min.js" ]; then curl -sL $$(grep mermaid.min.js public/search.html | cut -f 2 -d '"') --output public/mermaid.min.js; fi sed -e 's@https://unpkg.com/mermaid[^"]*"@mermaid.min.js"@' -i public/search.html public/genindex.html sed -e 's@https://unpkg.com/mermaid[^"]*"@../mermaid.min.js"@' -i public/developer/models.html public/developer/overview.html +docs-webserver: docs + python3 -m http.server -d public -b 127.0.0.1 8001 cog: ## Run cog, to integrate the CLI options to the docs. venv/bin/cog -r docs/*.md test: venv ## Run the tests diff --git a/argos/checks/checks.py b/argos/checks/checks.py index d1fc530..e729f72 100644 --- a/argos/checks/checks.py +++ b/argos/checks/checks.py @@ -4,6 +4,7 @@ import json import re from datetime import datetime +from httpx import URL from jsonpointer import resolve_pointer, JsonPointerException from argos.checks.base import ( @@ -55,6 +56,33 @@ class HTTPStatusIn(BaseCheck): ) +class HTTPToHTTPS(BaseCheck): + """Checks that the HTTP to HTTPS redirection status code is the expected one.""" + + config = "http-to-https" + expected_cls = ExpectedStringValue + + async def run(self) -> dict: + task = self.task + url = URL(task.url).copy_with(scheme="http") + response = await self.http_client.request(method="get", url=url, timeout=60) + + expected_dict = json.loads(self.expected) + expected = range(300, 400) + if "range" in expected_dict: + expected = range(expected_dict["range"][0], expected_dict["range"][1]) + if "value" in expected_dict: + expected = range(expected_dict["value"], expected_dict["value"] + 1) + if "list" in expected_dict: + expected = expected_dict["list"] + + return self.response( + status=response.status_code in expected, + expected=self.expected, + retrieved=response.status_code, + ) + + class HTTPHeadersContain(BaseCheck): """Checks that response headers contains the expected headers (without checking their values)""" @@ -313,6 +341,8 @@ class SSLCertificateExpiration(BaseCheck): @classmethod async def finalize(cls, config, result, **context): + if result.status == Status.ERROR: + return result.status, Severity.UNKNOWN if result.status != Status.ON_CHECK: return result.status, Severity.WARNING diff --git a/argos/config-example.yaml b/argos/config-example.yaml index 763367c..2999e12 100644 --- a/argos/config-example.yaml +++ b/argos/config-example.yaml @@ -107,6 +107,21 @@ websites: - headers-contain: - "content-encoding" - "content-type" + # Check that there is a HTTP to HTTPS redirection with 3xx status code + - http-to-https: true + # Check that there is a HTTP to HTTPS redirection with 301 status code + - http-to-https: 301 + # Check that there is a HTTP to HTTPS redirection with a status code + # in the provided range (stop value excluded) + - http-to-https: + start: 301 + stop: 308 + # Check that there is a HTTP to HTTPS redirection with a status code + # in the provided list + - http-to-https: + - 301 + - 302 + - 307 - path: "/admin/" checks: # Check that the return HTTP status is one of those diff --git a/argos/schemas/config.py b/argos/schemas/config.py index 520613a..7cc29d1 100644 --- a/argos/schemas/config.py +++ b/argos/schemas/config.py @@ -79,12 +79,26 @@ def parse_checks(value): if name not in available_names: msg = f"Check should be one of f{available_names}. ({name} given)" raise ValueError(msg) - if isinstance(expected, int): - expected = str(expected) - if isinstance(expected, list): - expected = json.dumps(expected) - if isinstance(expected, dict): - expected = json.dumps(expected) + if name == "http-to-https": + if isinstance(expected, int) and expected in range(300, 400): + expected = json.dumps({"value": expected}) + elif isinstance(expected, list): + expected = json.dumps({"list": expected}) + elif ( + isinstance(expected, dict) + and "start" in expected + and "stop" in expected + ): + expected = json.dumps({"range": [expected["start"], expected["stop"]]}) + else: + expected = json.dumps({"range": [300, 400]}) + else: + if isinstance(expected, int): + expected = str(expected) + if isinstance(expected, list): + expected = json.dumps(expected) + if isinstance(expected, dict): + expected = json.dumps(expected) return (name, expected) diff --git a/argos/server/routes/views.py b/argos/server/routes/views.py index fe48566..6dc5ace 100644 --- a/argos/server/routes/views.py +++ b/argos/server/routes/views.py @@ -14,6 +14,7 @@ from passlib.context import CryptContext from sqlalchemy import func from sqlalchemy.orm import Session +from argos.checks.base import Status from argos.schemas import Config from argos.server import queries from argos.server.models import Result, Task, User @@ -190,7 +191,7 @@ async def get_result_view( """Show the details of a result""" result = db.query(Result).get(result_id) return templates.TemplateResponse( - "result.html", {"request": request, "result": result} + "result.html", {"request": request, "result": result, "error": Status.ERROR} ) @@ -220,6 +221,7 @@ async def get_task_results_view( "results": results, "task": task, "description": description, + "error": Status.ERROR, }, ) diff --git a/argos/server/static/styles.css b/argos/server/static/styles.css index 6b03c9e..870bafb 100644 --- a/argos/server/static/styles.css +++ b/argos/server/static/styles.css @@ -1,5 +1,22 @@ @import url("pico.min.css"); +.display-small { + display: none; + text-align: center; +} +@media (max-width: 767px) { + .display-large { + display: none !important; + } + .display-small { + display: block; + } + .display-small article { + display: inline-block; + width: 24%; + } +} + code { white-space: pre-wrap; } diff --git a/argos/server/templates/index.html b/argos/server/templates/index.html index 455697d..87bd07a 100644 --- a/argos/server/templates/index.html +++ b/argos/server/templates/index.html @@ -44,7 +44,29 @@
{{ result.context['error_details'] | replace('\n', '
') | safe }}
+ {{ result.context["error_details"] | replace("\n", "
") | safe }}
+ status-is: \"200\"
|
| `status-in` | Check that the returned status code is in the list of codes you expect. | status-in:
- 200
- 302
|
-| `body-contains` | Check that the returned body contains a given string. | `body-contains: "Hello world"` |
-| `body-like` | Check that the returned body matches a given regex. | `body-like: "Hel+o w.*"` |
+| `body-contains` | Check that the returned body contains a given string. | body-contains: "Hello world"
|
+| `body-like` | Check that the returned body matches a given regex. | body-like: "Hel+o w.*"
|
| `headers-contain` | Check that the response contains the expected headers. | headers-contain:
- "content-encoding"
- "content-type"
|
| `headers-have` | Check that the response contains the expected headers with the expected value. | headers-have:
content-encoding: "gzip"
content-type: "text/html"
|
| `headers-like` | Check that response headers contains the expected headers and that the values matches the provided regexes. | headers-like:
content-encoding: "gzip\|utf"
content-type: "text/(html\|css)"
|
| `json-contains` | Check that JSON response contains the expected structure. | json-contains:
- /foo/bar/0
- /timestamp
|
| `json-has` | Check that JSON response contains the expected structure and values. | json-has:
/maintenance: false
/productname: "Nextcloud"
|
| `json-like` | Check that JSON response contains the expected structure and that the values matches the provided regexes. | json-like:
/productname: ".\*cloud"
/versionstring: "29\\\\..\*"
|
-| `json-is` | Check that JSON response is the exact expected JSON object. | `json-is: '{"foo": "bar", "baz": 42}'`|
+| `json-is` | Check that JSON response is the exact expected JSON object. | json-is: '{"foo": "bar", "baz": 42}'
|
+| `http-to-https` | Check that the HTTP version of the domain redirects to HTTPS. Multiple choices of configuration. | http-to-https: true
http-to-https: 301
http-to-https:
start: 301
stop: 308
http-to-https:
- 301
- 302
- 307
|
```{code-block} yaml
---
@@ -34,6 +38,21 @@ caption: argos-config.yaml
- headers-contain:
- "content-encoding"
- "content-type"
+ # Check that there is a HTTP to HTTPS redirection with 3xx status code
+ - http-to-https: true
+ # Check that there is a HTTP to HTTPS redirection with 301 status code
+ - http-to-https: 301
+ # Check that there is a HTTP to HTTPS redirection with a status code
+ # in the provided range (stop value excluded)
+ - http-to-https:
+ start: 301
+ stop: 308
+ # Check that there is a HTTP to HTTPS redirection with a status code
+ # in the provided list
+ - http-to-https:
+ - 301
+ - 302
+ - 307
- path: "/foobar"
checks:
- status-in:
diff --git a/docs/cli.md b/docs/cli.md
index cd17663..9d018af 100644
--- a/docs/cli.md
+++ b/docs/cli.md
@@ -1,3 +1,6 @@
+---
+description: How to use Argos from the command line.
+---
# Command-line interface