diff --git a/.drone.yaml b/.drone.yaml new file mode 100644 index 0000000..faefff1 --- /dev/null +++ b/.drone.yaml @@ -0,0 +1,65 @@ + +kind: pipeline +type: docker +name: build + +clone: + disable: true + +trigger: + ref: + - refs/tags/** + - refs/heads/master + event: + - tag + +steps: + - name: clone + image: alpine/git + environment: + SSH_PRIVATE_KEY: + from_secret: ssh_private_key + commands: + - umask 077 + - mkdir -p /root/.ssh/ + - echo "$$SSH_PRIVATE_KEY" > /root/.ssh/id_rsa + - ssh-keyscan -t rsa -p 2222 git.hxjscloud.com >> ~/.ssh/known_hosts + - test $${DRONE_TAG} && git clone --branch $DRONE_TAG $DRONE_GIT_SSH_URL . + - test $${DRONE_TAG} || git clone --branch $DRONE_BRANCH $DRONE_GIT_SSH_URL . + - SHORT_COMMIT_ID=$${DRONE_COMMIT:0-8} + - TAG_LIST="latest" + - test $${DRONE_TAG} && TAG_LIST="$${TAG_LIST},$${DRONE_TAG}" + - test $${DRONE_TAG} || TAG_LIST="$${TAG_LIST},snapshot-$(date +%Y%m%d)-$${SHORT_COMMIT_ID}" + - echo $${TAG_LIST} > .tags + + - name: build + image: plugins/docker + settings: + auto_tag: true + repo: harbor.hxjscloud.com/ops/alertmanager-webhook + registry: harbor.hxjscloud.com + username: + from_secret: docker_username + password: + from_secret: docker_password + +--- +kind: secret +name: ssh_private_key +get: + path: kv/data/drone-ci + name: deploy_ssh_private_key + +--- +kind: secret +name: docker_username +get: + path: kv/data/drone-ci + name: registry_username + +--- +kind: secret +name: docker_password +get: + path: kv/data/drone-ci + name: registry_password \ No newline at end of file diff --git a/.gitignore b/.gitignore index 08ce83a..7fb4fe2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ .venv __pycache__ config/config.yaml -config/text_template.tpl \ No newline at end of file +config/text_template.tpl +venv +alert-* +k8s/config \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index b0727c2..9700fd3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -27,7 +27,9 @@ "args": [ "run", "--no-debugger", - "--no-reload" + "--no-reload", + "--host 0.0.0.0", + "--port 5001" ], "jinja": true } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..109dcdc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM harbor.hxjscloud.com/library/python:3-alpine as builder +USER root +#RUN apk add --no-cache rust cargo python3-dev libffi-dev +USER app +COPY --chown=app:app . ./webhook/ +WORKDIR /app/webhook +RUN python3 -m venv venv &&\ + source venv/bin/activate &&\ + pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip && \ + pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt && ls -alh + +FROM harbor.hxjscloud.com/library/python:3-alpine +#RUN apk add --no-cache libffi +COPY --chown=app:app --from=builder /app/webhook/ ./webhook +WORKDIR /app/webhook +CMD source /app/webhook/venv/bin/activate &&\ + MODE=prod waitress-serve --ident="webhook" --connection-limit=500 --call wsgi:run \ No newline at end of file diff --git a/app/app.py b/app/app.py index 37db31d..906d9f2 100644 --- a/app/app.py +++ b/app/app.py @@ -15,7 +15,7 @@ for k, v in config_yaml.items(): setattr(config, k, v) app = Flask(__name__, - root_path=join(dirname(abspath(__file__)), '..')) + root_path=join(dirname(abspath(__file__)), '..'), template_folder="config") app.config.from_object(config) app.json_encoder = JSONEncoder diff --git a/config/config_example.yaml b/config/config_example.yaml index 3afcaca..e0e588a 100644 --- a/config/config_example.yaml +++ b/config/config_example.yaml @@ -1,8 +1,14 @@ -bark_api: http://bark-server.default:8080/push -title: - firing: Homeserver 告警发生 - resolved: Homeserver 告警解除 - default: Homeserver -to: -- 'token' -template_file: text_template.tpl \ No newline at end of file +DEBUG: true + +APP_CONFIG: + bark_api: http://bark-server.default:8080/push + title: + firing: Homeserver 告警发生 + resolved: Homeserver 告警解除 + default: Homeserver + to: + - 'token' + severity: + - 'warning' + - 'critical' + template_file: text_template.tpl \ No newline at end of file diff --git a/config/text_template_example.tpl b/config/text_template_example.tpl index 62d2350..f1df2e0 100644 --- a/config/text_template_example.tpl +++ b/config/text_template_example.tpl @@ -1,22 +1,41 @@ -{%- for alert in alerts -%} - {%- if alert.status == 'firing' -%} +{%- if status == 'firing' -%} 告警发生: +{%- for alert in alerts %} 告警时间:{{alert.startsAt}} - {% if alert.labels.instance -%}涉及实例:{{alert.labels.instance}}{%- endif %} - {% if alert.labels.device -%}涉及设备:{{alert.labels.device}}{%- endif %} + {% if alert.labels.instance -%} + 涉及实例:{{alert.labels.instance}} + {% endif -%} + {% if alert.labels.device -%} + 涉及设备:{{alert.labels.device}} + {% endif -%} 告警名称:{{alert.labels.alertname}} 严重性:{{alert.labels.severity}} - 详情:{{alert.annotations.summary}} - {% if alert.annotations.value -%}告警数据:{{alert.annotations.value}}{%- endif %} - {%- else -%} + 详情:{{alert.annotations.description}} + {% if alert.annotations.value -%} + 告警数据:{{alert.annotations.value}} + {% endif -%} + {% if alert.annotations.runbook_url -%} + 参考文档:{{alert.annotations.runbook_url}} + {%- endif %} + --- +{%- endfor %} +{%- else -%} 告警恢复: +{%- for alert in alerts %} 告警时间:{{alert.startsAt}} 恢复时间:{{alert.endsAt}} - {% if alert.labels.instance -%}涉及实例:{{alert.labels.instance}}{%- endif %} - {% if alert.labels.device -%}涉及设备:{{alert.labels.device}}{%- endif %} + {% if alert.labels.instance -%} + 涉及实例:{{alert.labels.instance}} + {%- endif %} + {% if alert.labels.device -%} + 涉及设备:{{alert.labels.device}} + {%- endif %} 告警名称:{{alert.labels.alertname}} 严重性:{{alert.labels.severity}} 详情:{{alert.annotations.summary}} - {% if alert.annotations.value -%}告警数据:{{alert.annotations.value}}{%- endif %} - {%- endif -%} -{%- endfor -%} \ No newline at end of file + {% if alert.annotations.value -%} + 告警数据:{{alert.annotations.value}} + {%- endif %} + --- +{%- endfor %} +{%- endif -%} \ No newline at end of file diff --git a/k8s/base/deployment.yaml b/k8s/base/deployment.yaml new file mode 100644 index 0000000..0fd948b --- /dev/null +++ b/k8s/base/deployment.yaml @@ -0,0 +1,35 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: alertmanager-webhook +spec: + selector: + matchLabels: + app: alertmanager-webhook + template: + metadata: + labels: + app: alertmanager-webhook + spec: + volumes: + - name: config + configMap: + name: alertmanager-webhook + containers: + - name: alertmanager-webhook + volumeMounts: + - mountPath: /app/webhook/config + name: config + image: webhook + ports: + - containerPort: 8080 + name: web + readinessProbe: + httpGet: + port: web + path: /health + livenessProbe: + httpGet: + port: web + path: /health + initialDelaySeconds: 10 diff --git a/k8s/base/service.yaml b/k8s/base/service.yaml new file mode 100644 index 0000000..333a2ad --- /dev/null +++ b/k8s/base/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: alertmanager-webhook +spec: + selector: + app: alertmanager-webhook + ports: + - port: 8080 + protocol: TCP + targetPort: web diff --git a/k8s/kustomization.yaml b/k8s/kustomization.yaml new file mode 100644 index 0000000..5716ef0 --- /dev/null +++ b/k8s/kustomization.yaml @@ -0,0 +1,23 @@ +# yaml-language-server: $schema=https://json.schemastore.org/kustomization.json + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - base/deployment.yaml + - base/service.yaml +commonLabels: + app: alertmanager-webhook +configMapGenerator: + - name: alertmanager-webhook + files: + - config/config.yaml + - config/text_template.tpl +images: + - name: webhook + newName: '' + newTag: '' +patches: + - path: overlay.yaml + target: + kind: Deployment + labelSelector: app=alertmanager-webhook \ No newline at end of file diff --git a/k8s/overlay.yaml b/k8s/overlay.yaml new file mode 100644 index 0000000..a9fa58e --- /dev/null +++ b/k8s/overlay.yaml @@ -0,0 +1,12 @@ +kind: Deployment +metadata: + name: alertmanager-webhook +spec: + selector: + matchLabels: + app: alertmanager-webhook + template: + spec: + containers: + - name: alertmanager-webhook + resources: {} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 624ae57..7a9b8b6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,20 +1,23 @@ aniso8601==9.0.1 certifi==2022.6.15 -charset-normalizer==2.1.0 -click==8.1.3 -Flask==2.1.3 +charset-normalizer==2.0.12 +click==8.0.4 +dataclasses==0.8 +Flask==2.0.3 Flask-RESTful==0.3.9 idna==3.3 -importlib-metadata==4.12.0 -itsdangerous==2.1.2 -Jinja2==3.1.2 -MarkupSafe==2.1.1 +importlib-metadata==4.8.3 +itsdangerous==2.0.1 +Jinja2==3.0.3 +MarkupSafe==2.0.1 munch==2.5.0 -pkg_resources==0.0.0 +Paste==3.5.1 pytz==2022.1 PyYAML==6.0 -requests==2.28.1 +requests==2.27.1 six==1.16.0 +typing_extensions==4.1.1 urllib3==1.26.11 -Werkzeug==2.2.1 -zipp==3.8.1 +waitress==2.0.0 +Werkzeug==2.0.3 +zipp==3.6.0 diff --git a/test.py b/test.py new file mode 100644 index 0000000..d08450d --- /dev/null +++ b/test.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +from flask import Flask, render_template +import json + +app = Flask("test", template_folder="config") +data = {} + +with open("alert-test.json") as f: + data = json.load(f) + +@app.route("/") +def index(): + return render_template("text_template.tpl", **data) + +app.run(host="127.0.0.1", port=5003) \ No newline at end of file diff --git a/views/apis/bark_service.py b/views/apis/bark_service.py index f77e8e4..1a44b51 100644 --- a/views/apis/bark_service.py +++ b/views/apis/bark_service.py @@ -1,4 +1,4 @@ -from flask import request +from flask import request, render_template from flask_restful import Resource from utils.error_handler import error_handler from app.app import app @@ -11,11 +11,12 @@ class BarkResource(Resource): try: data = Munch.fromDict(request.get_json(silent=True)) app.logger.debug(data) - config = Munch.fromDict(app.config) - content = { + config = Munch.fromDict(app.config["APP_CONFIG"]) + + content = Munch.fromDict({ 'title': config.title.firing if data.status == 'firing' else config.title.resolved if data.status == 'resolved' else config.title.default, 'category': 'category' - } + }) # TODO: alertmanager message to bark content. ''' @@ -43,10 +44,11 @@ class BarkResource(Resource): ] } ''' - content.body = app.jinja_environment.get_template(config.template_file).render(data) + content.body = render_template(config.template_file, **data) + app.logger.debug(content.body) for to in config.to: - content['device_key'] = to + content.device_key = to response = requests.post( url = config.bark_api, headers = { diff --git a/wsgi.py b/wsgi.py new file mode 100644 index 0000000..4512e73 --- /dev/null +++ b/wsgi.py @@ -0,0 +1,6 @@ +from app import app +from paste.translogger import TransLogger +from logging import INFO + +def run(): + return TransLogger(app, logger_name='webhook', logging_level=INFO) \ No newline at end of file