diff --git a/.gitignore b/.gitignore index 43f1f96..18d87f6 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ venv .env public *.swp +config.yaml +dist diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7facd39..07afde1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,7 +29,7 @@ pytest: <<: *pull_cache stage: test script: - - make tests + - make test djlint: <<: *pull_cache diff --git a/Makefile b/Makefile index 248d8db..94757f4 100644 --- a/Makefile +++ b/Makefile @@ -5,20 +5,21 @@ ORANGE=\033[0;33m BLUE=\033[0;34m NC=\033[0m # No Color -.PHONY: tests djlint pylint +.PHONY: test djlint pylint venv: ## Create the venv python3 -m venv venv develop: venv ## Install the dev dependencies venv/bin/pip install -e ".[dev,docs]" -docs: cog ## Build the docs +docs: cog public/mermaid.min.js ## Build the docs venv/bin/sphinx-build docs public - curl -sL $$(grep mermaid.min.js public/search.html | cut -f 2 -d '"') --output public/mermaid.min.js 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 +public/mermaid.min.js: + curl -sL $$(grep mermaid.min.js public/search.html | cut -f 2 -d '"') --output public/mermaid.min.js cog: ## Run cog, to integrate the CLI options to the docs. venv/bin/cog -r docs/*.md -tests: venv ## Run the tests +test: venv ## Run the tests venv/bin/pytest ruff: venv venv/bin/ruff format --check . @@ -28,7 +29,7 @@ pylint: venv ## Runs pylint on the code venv/bin/pylint argos pylint-alembic: venv ## Runs pylint on alembic migration files venv/bin/pylint --disable invalid-name,no-member alembic/versions/*.py -lint: djlint pylint +lint: djlint pylint pylint-alembic ruff help: @python3 -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) diff --git a/README.md b/README.md index 2573204..e88108a 100644 --- a/README.md +++ b/README.md @@ -16,20 +16,6 @@ Internally, a HTTP API is exposed, and a job queue is used to distribute the che - **Python**: 3.11+ - **Backends**: SQLite (development), PostgreSQL 14+ (production) -## Todo: - -- [ ] Do not return empty list on / when no results from agents. (!17) -- [X] donner un aperçu rapide de l’état de la supervision. -- [X] Use background tasks for alerting (#23) -- [X] Delete outdated tasks from config (#19, !25) -- [X] Implement alerting tasks (#15, 16, !13) -- [X] Handles multiple alerting backends (email, sms, gotify) (!13) -- [X] add an "unknown" severity for check errors (!17) -- [X] Add a command to generate new authentication token (#22) -- [ ] Add a way to specify the severity of the alerts in the config -- [ ] Allow passing a dict to check -- [ ] A configuration flag can automatically add a check of 301 redirection from HTTP to HTTPS - ## License Copyright © 2023 Alexis Métaireau, Framasoft diff --git a/argos/__init__.py b/argos/__init__.py index e69de29..1cf6267 100644 --- a/argos/__init__.py +++ b/argos/__init__.py @@ -0,0 +1 @@ +VERSION = "0.1.0" diff --git a/argos/commands.py b/argos/commands.py index 6de52ce..ca87647 100644 --- a/argos/commands.py +++ b/argos/commands.py @@ -9,6 +9,7 @@ from alembic import command from alembic.config import Config from argos import logging +from argos import VERSION from argos.agent import ArgosAgent @@ -40,6 +41,11 @@ def server(): pass +@cli.command() +def version(): + click.echo(VERSION) + + @cli.command() @click.argument("server_url", envvar="ARGOS_AGENT_SERVER_URL") @click.argument("auth", envvar="ARGOS_AGENT_TOKEN") @@ -111,8 +117,11 @@ def validate_max_results(ctx, param, value): @click.option( "--max-lock-seconds", default=100, - help="The number of seconds after which a lock is considered stale, must be higher than 60 " - "(the checks have a timeout value of 60 seconds)", + help=( + "The number of seconds after which a lock is " + "considered stale, must be higher than 60 " + "(the checks have a timeout value of 60 seconds)" + ), callback=validate_max_lock_seconds, ) @coroutine diff --git a/argos/server/models.py b/argos/server/models.py index c0c3d01..065fc33 100644 --- a/argos/server/models.py +++ b/argos/server/models.py @@ -1,4 +1,5 @@ """Database models""" + from datetime import datetime, timedelta from typing import List, Literal @@ -87,12 +88,12 @@ class Task(Base): class Result(Base): - """There is multiple results per tasks. + """There are multiple results per task. - The results uses the informations returned by the agents. + The results store information returned by the agents. - The status is "Was the agent able to do the check?" while the severity - depends on the return value of the check. + You can read `status` as "Was the agent able to do the check?" + while the `severity` depends on the return value of the check. """ __tablename__ = "results" @@ -120,14 +121,16 @@ class Result(Base): class ConfigCache(Base): - """Contains some informations on the previous config state + """Database model containing information on the current state + of the configuration. - Used to quickly determine if we need to update the tasks. - There is currently two cached settings: + This is used to determine if tasks are to be updated. + + These settings are cached: - general_frequency: the content of general.frequency setting, in minutes ex: 5 - - websites_hash: the sha256sum of websites setting, to allow a quick - comparison without looping through all websites + - websites_hash: the hash (sha256sum) of websites setting, to allow a quick + comparison without looping through all websites. ex: 8b886e7db7b553fe99f6d5437f31745987e243c77b2109b84cf9a7f8bf7d75b1 """ diff --git a/.env.example b/conf/.env.example similarity index 100% rename from .env.example rename to conf/.env.example diff --git a/config-example.yaml b/conf/config-example.yaml similarity index 100% rename from config-example.yaml rename to conf/config-example.yaml diff --git a/log_conf.yaml b/conf/log_conf.yaml similarity index 100% rename from log_conf.yaml rename to conf/log_conf.yaml diff --git a/config.yaml b/config.yaml deleted file mode 100644 index 5c71b89..0000000 --- a/config.yaml +++ /dev/null @@ -1,64 +0,0 @@ -general: - frequency: "1m" # Run checks every minute. - # Which way do you want to be warned when a check goes to that severity? - alerts: - ok: - - local - warning: - - local - critical: - - local - unknown: - - local -# mail: -# mailfrom: no-reply@example.org -# host: 127.0.0.1 -# port: 25 -# ssl: False -# starttls: False -# auth: -# login: foo -# password: bar -# addresses: -# - foo@admin.example.org -# - bar@admin.example.org -# gotify: -# - url: https://example.org -# tokens: -# - foo -# - bar - -service: - secrets: - - "1234" - # Secrets can be generated using `openssl rand -base64 32`. - -ssl: - thresholds: - - "1d": critical - - "5d": warning - -# It's also possible to define the checks in another file -# with the include syntax: -# -# websites: !include websites.yaml -# -websites: - - domain: "https://mypads.example.org" - paths: - - path: "/mypads/" - checks: - - status-is: 200 - - body-contains: '
' - - ssl-certificate-expiration: "on-check" - - path: "/admin/" - checks: - - status-is: 401 - - domain: "https://munin.example.org" - paths: - - path: "/" - checks: - - status-is: 301 - - path: "/munin/" - checks: - - status-is: 401 diff --git a/docs/api.md b/docs/api.md index 2260d14..7fe1b2a 100644 --- a/docs/api.md +++ b/docs/api.md @@ -10,6 +10,8 @@ To access the API, you need to pass an authentication token in the `Authorizatio "Authorization": "Bearer " + token ``` +See the [CLI documentation](cli.md#server-generate-token-command) to generate tokens. + ## Endpoints You can also have access to the Swagger API documentation at `https:///docs`, and the ReDoc documentation at `https:///redoc`. diff --git a/docs/capture.png b/docs/capture.png deleted file mode 100644 index 5a65fcd..0000000 Binary files a/docs/capture.png and /dev/null differ diff --git a/docs/cli.md b/docs/cli.md index 2e70714..472f8d6 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -26,8 +26,9 @@ Options: --help Show this message and exit. Commands: - agent Get and run tasks to the provided server. + agent Get and run tasks to the provided server. server + version ``` + ## Configure +### Configure the checks + The quickest way to get started is to copy the `config-example.yaml` file and edit it: ```bash -cp config-example.yaml config.yaml +cp conf/config-example.yaml config.yaml ``` + + You can read more about the configuration in the [configuration section](../configuration.md). -## Starting the server +### Configure the server Environment variables are used to configure the server. You can also put them in an `.env` file: -```{literalinclude} ../../.env.example +```{literalinclude} ../../conf/.env.example --- caption: .env --- @@ -37,6 +73,8 @@ caption: .env Please note that the only supported database engines are SQLite for development and PostgreSQL for production. +## Starting the server + Then you can start the server: ```bash @@ -45,12 +83,30 @@ argos server start The server reads the `yaml` file at startup, and populates the tasks queue with the checks defined in the configuration. +## Generating a token + +The agent needs an authentication token to be able to communicate with the server. + +You can generate an authentication token with the following command: +```bash +argos server generate-token +``` + +Add the token in the configuration file, in the following setting: + +```yaml +service: + secrets: + - "auth-token" +``` + ## Running the agent -You can run the agent on the same machine as the server, or on a different machine. The only requirement is that the agent can reach the server. +You can run the agent on the same machine as the server, or on a different machine. +The only requirement is that the agent can reach the server. ```bash -argos agent http://localhost:8000 "" +argos agent http://localhost:8000 "auth-token" ``` ## Cleaning the database @@ -61,6 +117,6 @@ Here is a crontab example, which will clean the db each hour: ```bash # Run the cleaning tasks every hour (at minute 7) -# Keeps 10 results per task, and locks the tasks for 1 hour +# Keeps 10 results per task, and remove tasks’ locks older than 1 hour 7 * * * * argos server cleandb --max-results 10 --max-lock-seconds 3600 ``` diff --git a/docs/installation/postgresql.md b/docs/installation/postgresql.md index 89fe344..97ea49e 100644 --- a/docs/installation/postgresql.md +++ b/docs/installation/postgresql.md @@ -1,10 +1,4 @@ -# Use with PostgreSQL - -If you intend to use argos with Postgresql, which is recommended for production, you can install it with the following commands: - -```bash -pip install -e ".[postgres]" -``` +# Install and configure PostgreSQL Here are a few steps for you to install PostgreSQL on your system: diff --git a/pyproject.toml b/pyproject.toml index ef6c3b6..fa193fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,27 +1,33 @@ [build-system] -requires = ["setuptools", "setuptools-scm"] -build-backend = "setuptools.build_meta" +requires = ["hatchling"] +build-backend = "hatchling.build" [project] name = "argos-monitoring" -version = "0.1.0" +dynamic = ["version"] description = "Distributed supervision tool for HTTP." authors = [ { name = "Alexis Métaireau", email = "alexis@notmyidea.org" }, + { name = "Luc Didry", email = "luc+argos@framasoft.org" }, ] readme = "README.md" classifiers = [ - "Programming Language :: Python :: 3.11", - "Topic :: Internet :: WWW/HTTP", - "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", + "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)", + "Intended Audience :: System Administrators", ] dependencies = [ "alembic>=1.13.0,<1.14", "click>=8.1,<9", "fastapi>=0.103,<0.104", + "gunicorn>=21.2,<22", "httpx>=0.25,<1", "Jinja2>=3.0,<4", + "psycopg2-binary>=2.9,<3", "pydantic[email]>=2.4,<3", "pydantic-settings>=2.0,<3", "python-multipart>=0.0.9,<1", @@ -31,7 +37,6 @@ dependencies = [ "sqlalchemy-utils>=0.41,<1", "tenacity>=8.2,<9", "uvicorn>=0.23,<1", - "gunicorn>=21.2,<22", ] [project.optional-dependencies] @@ -47,9 +52,7 @@ dev = [ "respx>=0.20,<1", "ruff==0.1.5,<1", "sphinx-autobuild", -] -postgres = [ - "psycopg2-binary>=2.9,<3", + "hatch==1.9.4", ] docs = [ "cogapp", @@ -61,14 +64,25 @@ docs = [ ] [project.urls] -homepage = "https://framagit.org/framasoft/framaspace/argos" +homepage = "https://framasoft.frama.io/framaspace/argos/" repository = "https://framagit.org/framasoft/framaspace/argos" "Funding" = "https://framasoft.org/en/#support" "Tracker" = "https://framagit.org/framasoft/framaspace/argos/-/issues" -[tool.setuptools] +[tool.hatch.build.targets.sdist] +include = [ + "/argos", + "/docs", + "/tests", +] + +[tool.hatch.build.targets.wheel] packages = ["argos"] +[tool.hatch.version] +path = "argos/__init__.py" + + [project.scripts] argos = "argos.commands:cli"