Script API
Script API
Der NOREYA REPO MEISTER verfügt über eine API mit der belibige Scriptsprachen ausgeführt werden können.
Dazu können z.b: Python, Bash oder Perl verwendet werden.
1.1) Pfad für Scripte
Der Standardpfad für Scripte ist
/var/lib/deb-repo-diff/scripts
.
Scripte können dort unter einem beliebigen Name abgelegt werden und werden einfach sortiert nach einander ausgeführt.
Executable Bit
Das “executable bit” muss für alle Scripte gesetzt sein!Dies erfolgt typischerweiße mittels e.g:
chmod
+x
/var/lib/deb-repo-diff/scripts/01_notify.py
.
TODO: Benutzer
1.2) Umgebungsbedingungen
1.2.1) Sicherheitskontext
Die Scripte werden im Kontext des
repo-sync.service
ausgeführt.
Dies bedeutet das diese stark eingeschränkte
Berechtigungen haben.
Der Benutzer/Gruppen Kontext ist
debrepo:www-data
.
Schreibzugriff ist nur auf die Verzeichnisse
/srv
und /var
möglich.
Es wird empfohlen vom Script aus über
Netzwerkschnittstellen wie HTTP/TCP/UDS
zu
interagieren.
Schlägt die Ausführung eines Scripts fehl wird mit
dem nächsten Script fortgefahren.
Auch die weitere Ausführung des
repo-sync.service
wird nicht
beeinträchtigt.
Der repo-sync.service
beendet
allerdings mit einem Fehlercode.
1.2.1) Header
Das Script muss über einen gültigen Shebang
Header verfügen um ausgeführt zu werden.
z.B:
Bash:
#!/bin/bash -e
oder Python:
#!/usr/bin/python3
1.2.1) Umgebungsvariabeln
Alle Umgebungsvariablen werden als Strings übergeben.
Umgebungsvariable | Bescheibung | Mögliche WErte |
---|---|---|
REPO_NAME | “display_name” des Repos | e.g: “debian-12” |
REPO_UID | “uid” des Repos | e.g: “3” |
SUCCESS | Script erfolgreich oder nicht | “TRUE” oder “FALSE” im Fehlerfall |
NEW_PKGS_JSON_PATH_V1 | Pfad zu einer JSON Datei die die neuen Pakete enthält | e.g: /tmp/repo-changes-fe51afbf-0e3b-4a65-8f35-79d8af8d8842.json |
NEW_PKGS_YAML_PATH_V1 | Pfad zu einer YAML Datei die die neuen Pakete enthält | e.g: /tmp/repo-changes-fe51afbf-0e3b-4a65-8f35-79d8af8d8842.yaml |
Im Fehlerfall enthalten die NEW_PKGS_*
Dateien leere Listen und sollten nicht gelesen
werden.
Der Beispielhafe Inhalt eine YAML Datei ist:
repo_name: debian-security-10/11-arm/amd64
repo_uid: 5
only_watched: false
releases:
- name: bullseye-security
new_packages:
- name: php7.4
version: 7.4.33-1+deb11u7
sub_packages:
- name: libapache2-mod-php7.4
version: 7.4.33-1+deb11u7
arch: arm64
- name: libaache2-mpod-php7.4
version: 7.4.33-1+deb11u7
arch: amd64
- name: php7.4-xsl
version: 7.4.33-1+deb11u7
arch: all
#...
- name: bookworm-security
new_packages: []
Die JSON Datei ist exakt gleich aufgebaut.
Die Struktur entspricht dem Repo Aufbau.
Sind in einem Release keine neuen Pakete ist die
entsprechende List leer.
Die Pakete sind nach ihrem Source
Namen
gruppiert.
Die sub_packages
beschreiben die
tatsächlich installierbaren Pakete.
Die arch
gibt die Architektur an.
Die only_watched
ist true wenn
eine [watchlist gesetzt ist und der flag TODO].
1.2.2) Kompatibilität
Das Schema der NEW_PKGS_*_PATH_V1
Daten
ist garantiert.
Es ist jedoch möglich das neue Felder hinzugefügt
werden.
Dies muss vom Script berücksichtigt werden!
Neue Schemas werden unter
NEW_PKGS_*_PATH_V2/V3...
bereitgestellt.
Sicherheitswarnung
Die Software ist nicht dazu bestimmt in öffentlichen Netzwerken erreichbar zu sein.Es ist notwendig die Zugriffsmöglichkeiten auf die Software über das Netzwerk strikt auf den bestimmten Personenkreis einzuschränken!
1.3) Ausführungszeiten
Die Scripte werden, nach dem alle Repos
synchronisiert, wurden ausgeführt vom
repo-sync.service
.
Die Ausführung erfolgt pro Repo und Script.
e.g:
repo: debian-11 script: 01_notify.py
repo: debian-11 script: 02_notify.sh
repo: debian-12 script: 01_notify.py
repo: debian-12 script: 02_notify.sh
2) Nutzungsbeispiele
Die Script API kann genutzt werden um direkt nach
der Synchronisierung Ereignisse oder Meldungen
auszulösen.
Typische Beispiele sind:
- Versenden von Emails/Messenger Nachrichten
- Starten von Test-Pipelines im CI System
TODO: Kopieren Beispielscript von Verzeichniss
Ein Python Script welches Emails versendet findet
sich hier:
Es ist notwendig folgendes Systempaket zu installieren
apt install python3-yaml
.
#!/usr/bin/python3
import os
import json
import yaml # apt install python3-yaml
import smtplib
import socket
import ssl
from email.message import EmailMessage
from email.utils import formatdate
from pathlib import Path
def send_mail(subject, msg):
try:
if not EMAIL_PASSWORD:
print("No email password set")
exit(1)
if len(str(msg)) > 1_000_000: # ~1MiB
msg = msg[:1_000_000] + '\n Message truncated here...'
email = EmailMessage()
email['Subject'] = subject
email['From'] = EMAIL_SENDER
email['To'] = EMAIL_RECEIVER
email['Date'] = formatdate(localtime=True)
email.set_content(str(msg))
context = ssl.create_default_context()
with smtplib.SMTP(EMAIL_SMTP_SRV, EMAIL_PORT, timeout=60) as server:
server.starttls(context=context)
server.login(EMAIL_SENDER, EMAIL_PASSWORD)
server.sendmail(EMAIL_SENDER, EMAIL_RECEIVER, email.as_string())
print('Sending mail done!')
except Exception as ex:
print('Failed sending mail: ' + str(ex))
if __name__ == '__main__':
try:
print("Script executing")
SEND_JSON_MAIL = False
SEND_YAML_MAIL = True
EMAIL_PORT = 587 # For starttls
EMAIL_SMTP_SRV = "mail.example.com"
EMAIL_SENDER = "updates@example.com"
EMAIL_RECEIVER = "user@example.com"
EMAIL_PASSWORD = Path("/var/lib/deb-repo-diff/passwd").read_text().strip("\n").strip()
PREFIX = "[" + str(socket.gethostname()) + "] "
repo_name = "Unknown"
repo_uid = ""
if os.getenv("REPO_NAME"):
repo_name = os.getenv("REPO_NAME")
if os.getenv("REPO_UID"):
repo_uid = os.getenv("REPO_UID")
if os.getenv("SUCCESS") == "FALSE":
send_mail(PREFIX + "Failed to sync repo '" + repo_name + "' (" + repo_uid + ")", "Check the server log for errors!")
exit(0)
elif os.getenv("SUCCESS") == "TRUE":
pass
else:
raise RuntimeError("SUCCESS env variable has invalid value!")
print("Processing JSON")
changes_json = os.getenv("NEW_PKGS_JSON_PATH_V1")
if changes_json:
changes = json.loads(Path(changes_json).read_text())
subject = PREFIX + "Updates for repo"
if changes["repo_name"]:
subject = (PREFIX + "Updates for repo '" + repo_name + "' (" + repo_uid + ")")
for release in changes["releases"]:
for new_pkg in release["new_packages"]:
del new_pkg["sub_packages"] # Simplify the view by removing the sub_packages
if SEND_JSON_MAIL:
send_mail(subject, str(json.dumps(changes, indent=4)))
else:
raise RuntimeError("No NEW_PKGS_JSON_PATH_V1 set")
print("Processing YAML")
changes_yaml = os.getenv("NEW_PKGS_YAML_PATH_V1")
if changes_yaml:
changes_yaml = Path(changes_yaml).read_text()
changes = yaml.safe_load(changes_yaml)
for release in changes["releases"]:
for new_pkg in release["new_packages"]:
del new_pkg["sub_packages"] # Simplify the view by removing the sub_packages
if SEND_YAML_MAIL:
send_mail(PREFIX + "Updates for repo '" + repo_name + "' (" + repo_uid + ")", yaml.safe_dump(changes, indent=4))
else:
raise RuntimeError("No NEW_PKGS_YAML_PATH_V1 set")
exit(0)
except Exception as ex:
print("Script failed: " + str(ex), flush=True)
exit(1)