diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..2c68ba6 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,15 @@ +--- +kind: pipeline +name: default +type: docker + +steps: +- name: docker + image: plugins/docker + settings: + repo: yavook/kiwi-simple-metrics + auto_tag: true + username: + from_secret: DOCKER_USERNAME + password: + from_secret: DOCKER_PASSWORD \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 3049ac8..901abe3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,10 +10,10 @@ "request": "launch", "module": "kiwi_simple_metrics.main", "env": { - "METRIC__INTERVAL": "5", - "METRIC__LOG__ENABLED": "True", - "METRIC__DISK__PATHS": "[\"/var\", \"/\", \"/dev\"]", - "METRIC__EXTERNAL__EXECUTABLES": "[\"${workspaceFolder}/dummy-metric\"]", + "METRICS__INTERVAL": "5", + "METRICS__LOG__ENABLED": "True", + "METRICS__EXTERNAL__ENABLED": "True", + "METRICS__EXTERNAL__EXECUTABLES": "[\"${workspaceFolder}/example/dummy-metric\"]", }, "justMyCode": true } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..40d7e0d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.11-alpine + +ENV \ + PYTHONUNBUFFERED=1 + +COPY . /usr/src/app + +RUN set -ex; \ + # buildtime deps + apk add --no-cache \ + --virtual .build-deps \ + build-base \ + gcc \ + linux-headers \ + ; + +RUN set -ex; \ + pip3 --no-cache-dir install /usr/src/app + +ENTRYPOINT ["kiwi-simple-metrics"] \ No newline at end of file diff --git a/README.md b/README.md index a3b3729..4199891 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,53 @@ -# vscode-python3 +# kiwi-simple-metrics -Use this template to jumpstart python development using Visual Studio Code! \ No newline at end of file +[![Build Status](https://github.drone.yavook.de/api/badges/yavook/kiwi-simple-metrics/status.svg)](https://github.drone.yavook.de/yavook/kiwi-simple-metrics) + +> `kiwi` - simple, consistent, powerful + +A lightweight monitoring solution for [`kiwi-scp`](https://github.com/yavook/kiwi-scp), created with [`uptime-kuma`](https://github.com/louislam/uptime-kuma) in mind. Also [on Docker Hub](https://hub.docker.com/r/yavook/kiwi-simple-metrics). + +## Quick start + +The minimal config to add to one of your `docker-compose.yml` is this: + +```yaml +metrics: + image: yavook/kiwi-simple-metrics:0.1 +``` + +- admittedly not useful, but it *does* run monitoring +- every 600 seconds (10 minutes), metrics are evaluated +- measures cpu, memory and swap, and disk usage at "/" +- does not log to stdout +- does not trigger any webhooks + +Every aspect of kiwi-simple-metrics can be tweaked by environment variables, so this is a more reasonable configuration example: + +```yaml +metrics: + image: yavook/kiwi-simple-metrics:0.1 + environment: + METRICS__LOG__ENABLED: "True" + METRICS__WEBHOOK__URL: "https://my.webhook.host/success?report={result}" + METRICS__WEBHOOK__FAIL: "https://my.webhook.host/failure?report={result}" +``` + +- same metrics as above +- logs reports to stdout +- triggers webhooks (`{result}` is the placeholder for the result string) + +## Configuration + +These are the environment variables you most likely need: + +- `METRICS__INTERVAL`: Time in seconds between metrics evaluation (default: `600`, i.e. 10 minutes) +- `METRICS__LOG__ENABLED`: If truthy, logs reports to stdout (default: `False`) +- `METRICS__[M]__ENABLED`, `METRICS__[M]__THRESHOLD` (with `[M]` from `CPU`, `MEMORY`, `DISK`): Enable or disable each metric, and set its failure threshold +- `METRICS__MEMORY__SWAP`: How swap space is handled in the "memory" report (default: `include`) +- `METRICS__DISK__PATHS`: At which paths the disk usage is measured (default: `["/"]`) +- `METRICS__EXTERNAL__ENABLED`, `METRICS__EXTERNAL__EXECUTABLES`: Setup for `external values`, as further defined in [`metrics/external.py`](./tree/master/kiwi_simple_metrics/metrics/external.py) (default: `False`, `[]`) +- `METRICS__WEBHOOK__URL`, `METRICS__WEBHOOK__FAIL`: Which webhooks to push the reports to (default: `None`, `None`) + +All settings can be found in the `SETTINGS` variable defined by module [`kiwi_simple_metrics.settings` in `settings/__init__.py`](./tree/master/kiwi_simple_metrics/settings/__init__.py). For default values, refer to the respective python files. + +Example: The above `METRICS__LOG__ENABLED` refers to `SETTINGS.log.enabled`, defined by model [`LogSettings` in `settings/misc.py`](./tree/master/kiwi_simple_metrics/settings/misc.py). diff --git a/dummy-metric b/dummy-metric deleted file mode 100755 index 284bcdb..0000000 --- a/dummy-metric +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -echo "Dummy" -echo "95" -echo "normal" -awk "BEGIN{srand(); r=rand(); print r * 100}" - -exit 0 \ No newline at end of file diff --git a/example/dummy-metric b/example/dummy-metric new file mode 100755 index 0000000..d869c76 --- /dev/null +++ b/example/dummy-metric @@ -0,0 +1,8 @@ +#!/bin/sh + +echo "Dummy" # name +echo "95" # threshold +echo "normal" # inversion +awk "BEGIN{srand(); r=rand(); print r * 100}" # value + +exit 0 \ No newline at end of file diff --git a/kiwi_simple_metrics/metrics/_report.py b/kiwi_simple_metrics/metrics/_report.py index e974ab1..5d8bac4 100644 --- a/kiwi_simple_metrics/metrics/_report.py +++ b/kiwi_simple_metrics/metrics/_report.py @@ -102,6 +102,9 @@ class Report: reports = [data.report for data in get_data()] + if not reports: + return None + return cls( result=settings.report_outer.format( name=settings.name, @@ -123,7 +126,7 @@ class Report: requests.get( url=str(url).format( - urllib.parse.quote_plus(self.result) + result=urllib.parse.quote_plus(self.result) ), verify=not SETTINGS.webhook.insecure, ) diff --git a/kiwi_simple_metrics/settings/__init__.py b/kiwi_simple_metrics/settings/__init__.py index 0d98373..7c42a83 100644 --- a/kiwi_simple_metrics/settings/__init__.py +++ b/kiwi_simple_metrics/settings/__init__.py @@ -6,7 +6,7 @@ from .metric import MetricSettings class Settings(BaseSettings): model_config = SettingsConfigDict( - env_prefix="METRIC__", + env_prefix="METRICS__", env_nested_delimiter="__", ) diff --git a/kiwi_simple_metrics/settings/metric.py b/kiwi_simple_metrics/settings/metric.py index d1b5b68..f785510 100644 --- a/kiwi_simple_metrics/settings/metric.py +++ b/kiwi_simple_metrics/settings/metric.py @@ -1,8 +1,8 @@ import math from typing import Any, Literal -from pydantic import (BaseModel, DirectoryPath, Field, FieldValidationInfo, - FilePath, field_validator) +from pydantic import (BaseModel, DirectoryPath, FieldValidationInfo, FilePath, + field_validator) class MetricSettings(BaseModel): @@ -54,7 +54,7 @@ class CpuMS(MetricSettings): name: str = "CPU" threshold: float = math.inf - # timespan to analyze average CPU usage + # timespan in seconds to measure average CPU usage interval: float = 1 @@ -81,21 +81,19 @@ class DiskMS(MetricSettings): count: int | None = 1 # paths to check for disk space - paths: list[DirectoryPath] = Field(default_factory=list) + paths: list[DirectoryPath] = [DirectoryPath("/")] class ExternalMS(MetricSettings): name: str = "External Metric" + enabled: bool = False threshold: float = 0 + # always include all defined external values! + count: None = None + # path to executable files - executables: list[FilePath] = Field(default_factory=list) + executables: list[FilePath] = [] # wait at most this many seconds for each executable timeout: int = 60 - - @field_validator("count", mode="after") - @classmethod - def force_none(cls, _) -> int | None: - """Don't accept a `count` value for the external metric!""" - return None diff --git a/min_fail_example.py b/min_fail_example.py deleted file mode 100644 index c812183..0000000 --- a/min_fail_example.py +++ /dev/null @@ -1,25 +0,0 @@ -from pydantic import BaseModel - - -class SubModel(BaseModel): - optional: int = 42 - required: str - - -class Settings(BaseModel): - sub: SubModel = SubModel(required="foo") - sub2: SubModel = SubModel(required="bar") - - -def main() -> None: - settings = Settings.model_validate({ - "sub": {"optional": "69"}, - }) - - # settings = Settings() - - print(settings.model_dump()) - - -if __name__ == "__main__": - main() diff --git a/pyproject.toml b/pyproject.toml index 657e38b..5e5ff75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] authors = ["Jörn-Michael Miehe <40151420+ldericher@users.noreply.github.com>"] -description = "" +description = "A lightweight monitoring solution for kiwi-scp" license = "MIT" name = "kiwi-simple-metrics" packages = [{include = "kiwi_simple_metrics"}] @@ -9,8 +9,8 @@ version = "0.1.0" [tool.poetry.dependencies] psutil = "^5.9.5" -python = "^3.11" pydantic-settings = "^2.0.3" +python = "^3.11" requests = "^2.31.0" [tool.poetry.group.dev.dependencies]