Ein Mirror für U8

Posted by Bokan on Wednesday, July 10, 2024

Ich fange an mit einer kurzen Anekdote: ich habe einem befreundeten IT-Menschen und begeisterten Arch Linux Benutzer erzählt, auf welcher Basis die Version 8 von Uberspace aufgebaut wird und welche Vorteile wir uns davon erhoffen. Er schmunzelte und meinte, “Das wird sofort auseinander fallen”. Little did he know: wir haben einen Plan.

Kurzer Thowback: Wir bauen Uberspace 8 auf Basis von Arch Linux und managen die Packages mit pacman. Wir wollen damit unsere vergangenen Blocker in CentOS und anderen Distros mit strengen Release Zyklen schon vom Design beginnend aus dem Weg räumen und für die Zukunft flexibler aufgestellt sein, damit neue Uberspace Versionen nie wieder von Grund auf neu entwickelt werden müssen. Auf eine Distro mit “Rolling Release” umzusteigen, birgt aber ganz eigene Tücken. Die Flexibilität ist Segen und Fluch zugleich. Darauf spielte der befreundete IT-Mensch an.

  • Jeden Moment (praktisch aber jeden Tag) kann eine neue Version von einem Package veröffentlicht werden, die vielleicht einen Bug enthält (dessen Fix jedoch genauso schnell rauskommt) oder die aufgrund von Abhängigkeiten andere Packages kaputt machen könnte.

oder

  • ein Nauti entdeckt einen Fehler auf dem Uberspace Host corvus, den wir auf einem anderen Uberspace Host corona nicht nachstellen können - corona wurde eine Woche vor corvus angelegt und hat deshalb womöglich ganz andere Package Versionen als corvus.

Wir brauchen also erstmal eine Möglichkeit einen definierten Zustand an Package Versionen festzupinnen. Daraus folgend ist es auch notwendig diese festgepinnten Package Versionen für einen späteren Abruf verfügbar zu halten und die Funktion bereitstellen, einzelne Packages auf eine ältere oder neuere Version down- bzw. upzugraden.

Der Prozess

Ich bin zu Uberspace gekommen als die Anforderungen im Großen und Ganzen schon feststanden. Es war meinen Kollegen schon klar, dass hier ein Arch Package Mirror mit Spezialfunktionen gebaut werden muss. Für mich war es dennoch wichtig die Architektur, das Datenmodell und das gewünschte Verhalten der Spezialfunktionen grosso modo visuell darzustellen. Das hilft dabei über ein abstraktes Ding zu reden, das noch nicht existiert und hält bei der Implementierung das Ziel vor Augen. Wir haben das scherzhaft “Documentation Driven Development” genannt (ok, das gibt es tatsächlich).

Aus solchen Konzept- und Modellzeichnungen sind dann schnell mal Epics mit Issues geschrieben und schon kann die Umsetzung beginnen.

Die (Grund-)Funktionen

Ich habe vorhin schon angedeutet, was dieser Mirror speziell können muss, aber hier nochmal zusammengefasst:

  1. Der Mirror soll sich von einem offiziellen Arch Mirror einmal täglich alle Packages eines Repositories abholen
  2. Alle Packages an diesem Tag sollen mit ihrer Version einem “State” also einem Zustand zugeordnet werden
  3. So ein State soll über eine wohldefinierte URL verfügbar sein, sodass pacman sich damit synchronisieren kann
  4. Damit sich die URL nicht jeden Tag ändert, soll der Mirror bestimmte States über fixe Branches (“testing”, “staging”, “stable”) verfügbar halten
  5. Einzelne Paketversionen in einem State sollen (mit dependency check) veränderbar sein

Wir haben natürlich auch noch weitere Anforderungen z.B. Arch User Repositories oder unsere eigenen Tools als Arch Packages in den Mirror einzubinden, aber die genannten Funktionen reichen einstweilen aus. Da wir quasi den Arch Rolling Release Zyklus ein wenig einsperren, hat uns ein Kollege sogleich einen passenden Namen für den Mirror geliefert: paccage von “Package Cage”, also einem Käfig für Packages.

Der Korrektheit halber sei erwähnt, dass die Branches aus Funktion 4 bei uns “Hostgroups” heißen, aus dem einfachen Grund, dass wir unsere Uberspace Hosts nach Nutzungszweck gruppieren. Nautis sollen vorderranging auf Hosts der Gruppe “stable” sein, das sind also “produktive” Hosts; bestimmte produktive Hosts sind als earlybirds für Rollouts auserkoren und gehören zur Hostruppe “staging”. Eine weitere Gruppe von Hosts ist nur für Testzwecke da, dafür die Hostgruppe “testing”.

Die Umsetzung

Wir haben nun also klar abgegrenzt was paccage können muss - somit war auch klar, dass es ein ausreichend komplexes Unterfangen ist, welches mit ein paar Python Command Line Scripts nicht gelöst werden kann. Wir brauchen eine sinnvolle Datenhaltung und eine kleine API - damit wird paccage eine kleine Web Applikation, für die Django gerade noch nicht zu übertrieben ist.

In der ersten Iteration war es “nur” mein Ziel Funktion 1 umzusetzen und die Packages von paccage mit einer URL herunterladen zu können. Dafür musste ich nur einen Django Management Command schreiben, welcher alle Packages eines Repositories, falls nicht existent, runterlädt und mit all seinen Metadaten in die Datenbank schreibt. Ein Django URL Pattern sorgt dafür, dass ich mich nicht um die tatsächlichen Namen der Packages Files im Filesystem kümmern muss, um die Packages tatsächlich runterladen zu können (das war ein kleiner Paradigmenwechsel für mich).

Als nächsten Schritt wurde das Datenmodell in Django weiter ausgebaut um Funktionen 2 - 4 einzubauen. Der aufwendiste Teil dabei war, für jeden State die Package Metadaten zu sammeln und diese als Metadata Files zu speichern mit denen pacman etwas anfangen kann. Dabei wurden Annahmen die ich über *.tar.gz. Files hatte, zerstört. Sie müssen nicht alphabetisch sortiert sein! Ich kann nicht einfach zu *.tar.gz Dateien weiteren Inhalt anhängen. So ist eben die Arbeit mit dem Werkzeug “Computer”.

Mit Funktion 4 abgeschlossen, arbeite ich derzeit daran, paccage mit unserer CI elegant ausrollen zu lassen und das ist wiederrum ein eigener Brocken. In diesem Sinne ist das Projekt paccage to be continued….

Foto von Erik Eastman auf Unsplash