Zum Inhalt

CUE lesen

Die Konfiguration der Hosts und der DNS-Einträge ist in CUE geschrieben. Diese Seite erklärt so viel CUE, wie man braucht, um unsere Dateien zu lesen und kleine Änderungen sicher vorzunehmen. Sie ist kein Sprachkurs; was in unseren Dateien nicht vorkommt, steht hier nicht.

Felder und Verschachtelung

Eine CUE-Datei besteht aus Feldern: ein Name, ein Doppelpunkt, ein Wert. Ein Wert kann selbst wieder Felder enthalten, in geschweiften Klammern.

Statt mehrerer Klammern schreibt man oft alle Namen hintereinander, getrennt durch Doppelpunkte. Diese Zeile aus syslet/werner/stack-hedgedoc.cue:

Text Only
image: hedgedoc: tag: "1.12.0"

bedeutet dasselbe wie:

Text Only
1
2
3
4
5
image: {
    hedgedoc: {
        tag: "1.12.0"
    }
}

Den „Pfad“ zu einem Wert schreibt man mit Punkten: image.hedgedoc.tag.

Namen, die einen Bindestrich enthalten, stehen in Anführungszeichen, zum Beispiel containers: "hedgedoc-server": spec: {.

Werte

Texte (Strings) stehen in Anführungszeichen, Zahlen und true/false ohne. Listen stehen in eckigen Klammern, die Einträge durch Kommas getrennt, zum Beispiel PublishPort: ["9189:9187"].

Viele Werte in den Stack-Dateien sehen aus wie true, sind aber Strings: Internal: "true" oder ReadOnly: "true". Das sind Einstellungen, die syslet unverändert in die Konfigurationsdateien auf dem Host schreibt, und dort ist alles Text. Was ein String sein muss und was nicht, gibt syslet vor; ein Wert ohne Anführungszeichen an der falschen Stelle ist ein Fehler (siehe Typische Fehler).

Längere Texte über mehrere Zeilen stehen zwischen drei Anführungszeichen """, etwa die Caddy-Konfiguration in syslet/werner/infra-caddy.cue.

Kommentare

Alles hinter // ist ein Kommentar und wird ignoriert. Wir nutzen Kommentare, um Abschnitte zu gliedern (// Volumes, // Containers) und um zu begründen, warum etwas so eingestellt ist.

Verweise

Ein Wert kann auf einen anderen Wert verweisen. In Strings geht das mit \( … ):

Text Only
Image: "quay.io/hedgedoc/hedgedoc:\(image.hedgedoc.tag)"

Hier wird \(image.hedgedoc.tag) durch den Wert von image.hedgedoc.tag ersetzt. Deshalb steht die Version ganz oben in der Stack-Datei und nur dort.

Ohne Anführungszeichen verweist man direkt, wie in desec/desec_garage_lab_de.cue:

Text Only
{subname: "md", type: "A", records: [ipam.netcup_vps.a]},

Die IP-Adresse steht einmal oben in der Datei unter ipam, alle Einträge für werner verweisen darauf. Wer eine Version oder IP-Adresse ändert, muss deshalb nicht nach weiteren Stellen suchen.

Packages

Die erste Zeile jeder Datei nennt ihr Package, zum Beispiel package syslet. CUE liest alle Dateien eines Packages zusammen, als wären sie eine einzige Datei: alle Dateien im aktuellen Ordner und die Dateien desselben Packages in den Ordnern darüber.

Wenn man in syslet/werner/ ausrollt, gehören also alle Dateien in syslet/werner/ dazu und zusätzlich die gemeinsamen Dateien direkt in syslet/, etwa syslet/syslet.cue. Deshalb gilt syslet.cue für jeden Host, ohne dass ein Host es eigens einbinden muss. Aus demselben Grund kann syslet/werner/stack-vaultwarden.cue mit settings.mailjet.hostname auf einen Wert aus syslet/mailjet.cue verweisen.

Die Dateien in syslet/containerhost/ sieht werner dagegen nicht, und umgekehrt.

Die import-Zeilen am Anfang einer Datei holen Definitionen aus anderen Packages, vor allem aus syslet selbst.

Zusammenführen

Dasselbe Feld darf an mehreren Stellen stehen, auch in verschiedenen Dateien. CUE führt alle Stellen zu einem Wert zusammen; das heißt Unifikation.

So beginnt jede Stack-Datei mit sysdef: { und ergänzt darin ihre eigenen Container, Volumes und Netzwerke. Am Ende gibt es ein einziges sysdef, das alles enthält.

Ein Beispiel über zwei Dateien: Die Stack-Datei beschreibt den Container hedgedoc-server, und syslet/werner/host.cue ergänzt für denselben Container das Speicherlimit:

Text Only
"hedgedoc-server": spec: unit: Container: Memory: "256M"

Zusammenführen geht nur, solange sich die Stellen nicht widersprechen. Steht dasselbe Feld an zwei Stellen mit verschiedenen Werten, ist das ein Konflikt, und CUE bricht ab.

Definitionen

Namen, die mit # beginnen, sind Definitionen: Vorlagen, die nicht selbst ausgerollt werden, sondern festlegen, wie etwas aussehen muss, oder aus Eingaben etwas erzeugen.

#Sysdef in syslet/syslet.cue legt fest, welche Felder in sysdef erlaubt sind und welche Werte sie haben dürfen. Ein Tippfehler in einem Feldnamen oder ein falscher Wert fällt deshalb schon auf dem eigenen Rechner auf, bevor etwas ausgerollt wird, nicht erst auf dem Host.

Andere Definitionen nimmt man mit Eingaben und liest das Ergebnis ab:

Text Only
Label: (#Labels & {in: {partOf: "hedgedoc", component: "server", name: "hedgedoc"}}).out

Das liest man so: „Nimm die Vorlage #Labels, setze diese Werte bei in ein und nimm das Ergebnis out.“ Was die Vorlage erzeugt, steht bei ihrer Definition, hier in syslet/werner/host.cue. So entstehen in allen Stack-Dateien gleichartige Einträge, ohne dass man sie in jede Datei kopiert. Nach demselben Muster funktionieren tools.#SysdefAssignNetwork und tools.#SysdefLock aus syslet (siehe syslet in diesem Repo).

Versteckte Felder

Felder, die mit _ beginnen, sind Hilfsfelder. Man kann auf sie verweisen, sie werden aber nicht ausgerollt. In stack-hedgedoc.cue rechnet _mem zum Beispiel aus den Einstellungen der Datenbank ihr Speicherlimit aus, und _caddyTemplate in infra-caddy.cue ist die Vorlage für die Caddy-Konfiguration.

Was man zum Ändern nicht verstehen muss

In den gemeinsamen Dateien stehen ein paar weitere Sprachmittel, etwa for-Schleifen in syslet/restic.cue oder @embed in den host.cue-Dateien. Diese Dateien ändern sich selten; für Stack-Dateien und DNS-Einträge braucht man sie nicht.

Wo man was findet

Version eines Images: In den meisten Stack-Dateien ganz oben als image: <app>: tag:. Hilfscontainer wie Datenbanken haben ihre Version direkt im Feld Image: des Containers, zum Beispiel postgres:17.10-alpine in stack-hedgedoc.cue.

DNS-Eintrag: In desec/desec_<domain>.cue je Eintrag eine Zeile mit subname (der Teil vor der Domain, md für md.garage-lab.de), type und records. IP-Adressen stehen oben unter ipam.

Wie man eine Änderung dann ausrollt, steht in Änderung ausrollen; vor jedem Wechsel der Image-Version zusätzlich Image-Version anheben.

Typische Fehler beim Ändern

Fehler in CUE-Dateien fallen auf, bevor etwas ausgerollt wird: cue fmt meldet Syntaxfehler, und cue cmd plan bricht bei jedem Fehler ab, ohne den Host zu berühren. Die Meldung nennt Datei, Zeile und Spalte.

Komma oder Klammer vergessen. Mehrere Einträge in einer Zeile brauchen Kommas dazwischen; eine geöffnete Klammer muss wieder geschlossen werden. Die Meldungen sehen zum Beispiel so aus:

Text Only
1
2
3
4
missing ',' in list literal:
    ./stack-hedgedoc.cue:121:23
expected '}', found 'EOF':
    ./stack-hedgedoc.cue:150:2

Zwei Werte für dasselbe Feld. Steht ein Feld an zwei Stellen mit verschiedenen Werten, etwa weil man eine Zeile kopiert und das Original nicht gelöscht hat, meldet CUE einen Konflikt und nennt beide Stellen:

Text Only
1
2
3
image.hedgedoc.tag: conflicting values "1.13.0" and "1.12.0":
    ./stack-hedgedoc.cue:8:23
    ./stack-hedgedoc.cue:9:23

Falscher Typ. Internal: true statt Internal: "true", oder eine Zahl in Anführungszeichen, wo syslet eine Zahl erwartet: CUE meldet auch das als Konflikt, weil der Wert nicht zu dem passt, was die Definition vorgibt.

Weiterlesen