Caldera docs
calderaapp.io

Einen Agent verbinden

Es gibt zwei Wege, einen Agent in Caldera zu bringen. Beide enden gleich: Der Agent ist ein Eintrag in Caldera, er meldet im Agent-Protokoll, und ein Admin sieht und steuert ihn. Sie unterscheiden sich darin, wer das Melden betreibt und wem der Prozess des Agents gehört.

Die zwei Wege

A. Eigener Reporter B. Blueprint und Runtime
Was meldet Wenige Zeilen, die selbst geschrieben und als eigener Prozess betrieben werden. Die Runtime caldera.pyz, die Caldera ausliefert.
Wer den Agent startet und stoppt Die eigene Umgebung (systemd, cron, eigener Code). Die Runtime (start, stop, Neustart mit Backoff).
Wie der Agent eingerichtet wird Von Hand: Checkout, Geheimnisse, Unit-Dateien. caldera init <code> aus einem veröffentlichten Blueprint.
Pause bedeutet Was der Agent damit macht (eine Datei, ein Flag, eine gestoppte Unit). Die Runtime stoppt den Prozess des Agents; Fortsetzen startet ihn.
Einstellungen Ein Schema wird deklariert und die Grenzen werden im eigenen Code durchgesetzt. Das Manifest deklariert Schema, Standardwerte und Grenzen; die Runtime setzt sie durch.
Updates Werden selbst ausgerollt. Caldera nennt eine Version; die Runtime lädt, prüft die Gesundheit, behält oder rollt zurück.
Zurückrollen Eigene Sache. caldera rollback auf dem Rechner, oder Caldera nennt die alte Version.
Braucht Python 3 Nein. Jede Sprache, die JSON per POST senden kann. Ja, auf dem Rechner des Agents.
Braucht Caldera zum Einrichten Nur um den Agent und sein Token anzulegen. Ja, für init (der Code). Zum Betrieb nie.
Geeignet für Einen Agent, der schon existiert und einen eigenen Scheduler hat, oder einen ohne eigenen Prozess. Einen neuen Agent, oder einen, der sich als ein dauerhaft laufender Prozess umbauen lässt.

Wie wählen

  • B wählen, wenn der Agent neu ist oder ein Programm, das als dauerhaft laufender Prozess laufen kann. Dann gibt es Einrichtung mit einem Befehl, Updates mit Gesundheitsprüfung und Rollback, das Einstellungsformular aus dem Manifest und Bots für den Zugriff auf die anderen Produkte der Suite.
  • A wählen, wenn der Agent ein eigenes Leben hat, das Caldera nicht besitzen soll. Die Prozess-Runtime startet einen Prozess und pausiert, indem sie ihn stoppt. Hat der Agent keinen solchen Prozess (seine Läufe sind zum Beispiel ein Timer und seine Pause ist eine Datei, die auch ein Chat-Bot setzt), passt die Prozess-Runtime nicht. Dann entweder den eigenen Reporter behalten oder den Adapter-Modus nutzen, in dem die Runtime nichts startet und stoppt und einen Befehl aufruft, den der Agent bereitstellt.
  • Mit A anfangen und später zu B wechseln ist möglich. Der Agent behält seinen Verlauf in Caldera nur, wenn derselbe Agent-Eintrag bleibt; eine Blueprint-Installation legt immer einen neuen Agent an. Der Wechsel steht unter Agents betreiben, mit dem Weg zurück.

Welcher Weg es auch ist, die feste Regel gilt: Der Agent darf Caldera zum Laufen nicht brauchen (siehe Konzepte).

Weg A: ein eigener Reporter

Schritte

  1. Den Agent anlegen. Ein Admin öffnet die Übersicht, drückt "Neuer Agent", vergibt einen Namen und erhält das Token einmalig (caldera_agt_...) mit zwei fertigen Zeilen:

    CALDERA_URL=https://app.calderaapp.io
    CALDERA_TOKEN=caldera_agt_...
    

    Gespeichert wird nur ein Hash des Tokens. Geht es verloren, wird es im Menü des Agents erneuert ("Token erneuern"): Das alte Token funktioniert sofort nicht mehr.

  2. Die zwei Zeilen dorthin legen, wo der Reporter sie liest, in eine private Datei auf dem Rechner des Agents (Modus 600). Nie in ein Repository.

  3. Den Reporter schreiben, gegen das Agent-Protokoll. Die Regeln, die einen Agent von Caldera unabhängig machen:

    • ein eigener Prozess (ein systemd-User-Service oder ein Sidecar);
    • ein Timeout bei jeder Anfrage;
    • ist Caldera nicht erreichbar, langsam oder antwortet unsinnig: eine Zeile loggen, das nächste Intervall abwarten, erneut versuchen, sonst nichts ändern;
    • Pause, Grenzen und Einstellungen sind die eigenen Dateien des Agents;
    • ein Befehl ist ein Wunsch: gegen die eigenen Grenzen prüfen, anwenden oder ablehnen und mit einem Ack in der nächsten Meldung antworten;
    • nur senden, was die Seite zeigen soll. Mail-Inhalte, Nachrichten und Ähnliches bleiben beim Agent. Logs sind die einzige Ausnahme, und der Agent entscheidet, welche er herausgibt.
  4. Starten und die Seite prüfen. Der Agent erscheint nach der ersten Meldung in der Übersicht.

  5. Die Unabhängigkeitsprobe machen.

Ein minimaler Reporter

Das ist die kleinste Schleife, die die Regeln einhält. handle durch eigene Prüfungen ersetzen. Sie benutzt nur die Python-Standardbibliothek; jede Sprache funktioniert.

import json
import os
import time
import urllib.error
import urllib.request

URL = os.environ["CALDERA_URL"].rstrip("/")
TOKEN = os.environ["CALDERA_TOKEN"]


def post(path, body):
    request = urllib.request.Request(
        URL + "/api/agent/v1" + path,
        data=json.dumps(body).encode(),
        method="POST",
        headers={"Authorization": "Bearer " + TOKEN, "Content-Type": "application/json"},
    )
    with urllib.request.urlopen(request, timeout=30) as response:  # immer ein Timeout
        return json.load(response)


def handle(command):
    """Gibt (ok, message) zurück, oder None bei einem `log`-Befehl (der Upload ist die Antwort)."""
    if command["type"] == "log":
        # das Log senden mit post("/log", {"command": command["id"], "name": ..., "text": ...})
        return None
    return False, "This agent does not support that."  # eine Ablehnung ist eine normale Antwort


acks, interval = [], 30
while True:
    report = {
        "protocol": 1,
        "state": {"level": "ok", "label": "Ready"},
        "controls": ["pause", "resume"],
        "acks": acks,
    }
    try:
        answer = post("/report", report)
        acks = []  # zugestellt
        interval = max(10, min(300, int(answer.get("interval", 30))))
        for command in answer.get("commands", []):
            result = handle(command)
            if result is not None:
                acks.append({"id": command["id"], "ok": result[0], "message": result[1]})
    except (urllib.error.URLError, OSError, ValueError) as error:
        print("caldera unreachable:", error)  # eine Zeile, dann das nächste Intervall
    time.sleep(interval if not acks else 2)

Was das Beispiel zeigt und worauf es ankommt:

  • acks werden erst geleert, nachdem eine Meldung zugestellt wurde. So geht eine Antwort nicht an einer fehlgeschlagenen Anfrage verloren.
  • Ein Befehl wird höchstens einmal zugestellt. Geht die Antwort auf eine Meldung verloren, geht der Befehl mit ihr verloren, und Caldera lässt ihn nach zehn Minuten fehlschlagen. Das ist Absicht: run_now darf nie zweimal laufen, nur weil ein Netz eine Antwort verloren hat. Eine Person kann ihn erneut senden.
  • Ein null in einer Meldung gilt als "nicht vorhanden", außer in settings.values, wo null bedeutet "diese Einstellung ist nicht gesetzt". Besser ist es, ein ungesetztes Feld wegzulassen.
  • Anteile (Gauges, Anteile an Gegenständen) sind Zahlen von 0 bis 1. Zeiten sind Unix-Sekunden.

Ein Muster: ein zeitgesteuerter Agent mit einer Datei als Pause

Ein Agent, dessen Läufe ein Timer sind und dessen Pause eine Datei ist, die auch andere (zum Beispiel ein Chat-Bot) setzen und löschen, ist der typische Fall für einen eigenen Reporter. Der Reporter ist ein eigener Service neben dem Timer. Calderas pause legt dieselbe Datei an wie der Chat-Befehl, deshalb lässt sich eine in Caldera gesetzte Pause ohne Caldera aufheben. resume löscht die Datei und sagt, wenn eine andere Sperre (eine Person arbeitet in einer Sitzung, ein Build läuft) den Agent weiter warten lässt. run_now legt eine Datei an, nach der der Scheduler des Timers sucht. Einstellungen sind eine feste Liste von Feldern mit harten Grenzen, in die eigene Konfiguration des Agents geschrieben; alles andere wird abgelehnt, und ein set-Befehl gilt ganz oder gar nicht. Logs sind auf Namen beschränkt, die der Agent selbst kennt, und es werden nur die letzten 512 KB gesendet.

Soll ein solcher Agent zusätzlich Einrichtung und Updates von Caldera bekommen, gibt es den Adapter-Modus.

Gauges aus der letzten eigenen Beobachtung

Ein Gauge, das aus der letzten eigenen Beobachtung des Agents gebaut wird (zum Beispiel das Kontingent nach dem letzten Modellaufruf), kann der Seite des Anbieters hinterherhinken, weil andere Sitzungen dasselbe Kontingent verbrauchen und ein Agent, der seine Läufe über einer Schwelle anhält, keine neue Beobachtung macht. Die Zeit der Messung gehört in das detail des Gauges (zum Beispiel "Stand 06.10. 14:05, ..."), damit ein veralteter Wert als veraltet erkennbar ist. Siehe Agents betreiben.

Weg B: ein Blueprint unter der Runtime

Schritte

  1. Den Blueprint schreiben. Ein Manifest caldera.agent.yaml, die Dateien des Agents und ein Health-Befehl. Siehe Blueprints und Das Manifest.
  2. Eine Version hochladen und veröffentlichen, im Workspace (Blueprints, der Blueprint, "Version hochladen", dann "Veröffentlichen"). Eine veröffentlichte Version ändert sich nie.
  3. Einen Install-Code ausstellen ("Installieren"): Version und Agent-Name wählen. Caldera zeigt zwei Befehle.
  4. Auf dem Rechner: die zwei Befehle ausführen: caldera.pyz laden und prüfen, dann python3 caldera.pyz init <code> --url https://app.calderaapp.io. Das Werkzeug zeigt, was eingerichtet wird, fragt auf dem Rechner nach den deklarierten Geheimnissen (sie erreichen Caldera nie) und löst erst dann den Code ein. Es schreibt das Verzeichnis, installiert eine systemd-User-Unit und startet sie.
  5. Prüfen: python3 caldera.pyz status --dir <dir> und die Seite des Agents.
  6. Die Unabhängigkeitsprobe machen.

Die Einzelheiten jedes Schritts stehen unter Die Runtime und Blueprints.

Ein Muster: ein dauerhaft laufender Prozess

Ein Agent, der zur Prozess-Runtime passt, ist ein Programm mit eigener Zeitplanung, zum Beispiel ein Prozess mit einem Thread je Aufgabe. Er schreibt alle 30 Sekunden und bei jeder Änderung atomar eine Status-Datei, mit einem Heartbeat, den sein Health-Befehl liest. Pause heißt, dass die Runtime den Prozess stoppt; "Jetzt ausführen" ist eine Datei, die der Agent jede Sekunde prüft. Seine Geheimnisse und Daten bleiben, wo sie waren, außerhalb des Bundles, und das Manifest deklariert nur die Pfade, die ein Update überleben müssen. Offline pausiert caldera pause auf dem Rechner.

Die Unabhängigkeitsprobe

Gehört zum Verbinden jedes Agents. Das Ergebnis im eigenen Repository des Agents festhalten, denn dort liegt der Reporter.

Weg A:

  1. CALDERA_URL auf einen Host setzen, den es nicht gibt (zum Beispiel https://caldera.invalid), und den Reporter-Service neu starten.
  2. Einen Lauf starten. Er muss zu Ende laufen; das Log darf nur sagen, dass Caldera nicht erreichbar ist.
  3. Zuerst in Caldera eine Pause setzen, dann Caldera wie in Schritt 1 unerreichbar machen. Der eigene Kanal des Agents muss die Pause aufheben können.
  4. CALDERA_URL wiederherstellen, neu starten; die nächste Meldung kommt binnen eines Intervalls an.

Was nie passieren darf: ein Lauf wartet auf Caldera, eine Pause lässt sich nur aus Caldera aufheben, oder ein Fehler im Reporter hält den Scheduler des Agents an.

Weg B (Runtime):

  1. Die URL der Runtime auf nichts richten oder das Netz sperren. Der Prozess des Agents muss weiterlaufen; status, pause, resume und rollback müssen funktionieren.
  2. python3 caldera.pyz pause --dir <dir> und danach resume müssen offline funktionieren.
  3. Die Runtime zu stoppen darf den Agent nicht stoppen.