UNIXwork

Meine Lektionen aus der Entwicklung von dav 1.3

23. December 2019

Die Entwicklung von dav 1.3 ist ganz und gar nicht so verlaufen wie ich mir das vorgestellt hatte. Über ein Jahr später als geplant, konnte ich das Release dann vor kurzem endlich fertig stellen. Ich entwickel das jetzt allerdings im meiner Freizeit, daher variiert es natürlich, wie viel Zeit ich in die Entwicklung stecke. Trotzdem muss ich zugeben, dass die Zeit, die ich investiert habe, nicht unbedingt optimal genutzt wurde.

Schauen wir uns zunächst ein paar Zahlen der Entwicklung an.

version  date        loc          new-loc  n-month  lines/month
---------------------------------------------------------------
begin    2012-11-30      0 lines  0        0        0
1.0.0    2017-08-13  14633 lines  14633    57       256
1.1.0    2017-10-07  15416 lines  783      2        391      
1.2.0    2018-06-26  20465 lines  5049     8        631
1.3.0    2019-12-15  29743 lines  9278     18       515

Für die erste Version von dav habe ich natürlich relativ lange gebraucht. Es ist vermutlich deutlich einfacher, an eine bestehende Software, die man auch noch gut kennt, neues Zeug ranzubasteln, als etwas komplett neues aus dem Boden zu stampfen.

Bei der Entwicklung von dav 1.2 war ich am produktivsten. Aber wenn man nur nach lines/month geht, dann war dav 1.3 eigentlich kein Desaster (im Vergleich zu den anderen Releases). Was man aber sieht ist, dass sich die Code-Basis drastisch vergrößert hat. Viel zu viele neue Features haben den Weg in das Release gefunden. Genau das ist eines der Probleme.

Das größte Ärgernis dabei war, dass ich zwar relativ zügig neue Features implementiert hatte, aber sobald die minimal irgendwie funktioniert haben, bin ich zum nächsten neuen Feature übergegangen. Am Ende hatte ich haufen Neues, das aber nicht gut funktionierte.

Dazu kam auch ein größeres Refactoring der dav-sync Codebasis. Dieses hat natürlich bereits bestehende Funktionalität, die fehlerfrei lief, auch etwas zerstört.

Am Ende war ich Monatelang damit beschäftig, massenweise Tests zu schreiben, um überhaupt wieder Vertrauen in den Code zu entwickeln. Dies war jedoch recht erfolgreich, die Grundfunktionalität dürfte stabiler als vorher sein.

Was ich hoffentlich daraus gelernt habe: Dinge zuende entwickeln. Keine halben Sachen liegen lassen. Und am besten schließt man die Entwicklung eines neuen Features ab, in dem man diverse Tests dafür geschrieben hat, die alle problemlos durchlaufen.

Auch sollte man sich nicht zu viel auf einmal vornehmen. Ich hoffe ich kann den Spruch Release early, release often in Zukunft mehr anwenden. Das hat durchaus seine Vorteile.

Autor: Olaf | 0 Kommentare | Tags: dav, c

dav-sync: Symlinks synchronisieren

20. December 2019

Mit dav-sync ist es nun möglich, Symlinks zu synchronisieren. Eine Besonderheit dabei ist, dass unter Windows Verknüpfungen von dav-sync als Symlink interpretiert werden. Windows hat zwar auch richtige Symlinks, die sind aber Mist, da normale Benutzer beispielsweise keine anlegen dürfen.

Beim Synchronisieren wird dann für die Verknüpfung oder Symlink eine leere Resource angelegt und die relative Adresse des Targets in einer WebDAV-Property gespeichert. Damit hat man ein automatisches Mapping zwischen Symlinks und Windows-Verknüpfungen. Wer also gerne über Links seine Dateien organisiert, kann dies nun auch plattformübergreifend synchronisieren. Das gibt es sonst nirgends.

Konfigurieren muss man dafür nicht viel. In die sync.xml muss innerhalb des <directory>-Elements nur folgendes eingefügt werden:

<directory>

    ...

    <symlink-intern>sync</symlink-intern>

</directory>

Synchronisiert werden können dabei nur Links, die ein Target innerhalb des Sync-Directory haben. Links mit externen Targets können aus Sicherheitsgründen nicht synchronisiert werden.

Neben sync gibt es für das <symlink-intern>-Element noch zwei Werte, die angegeben werden können. Zum einen follow, womit angegeben wird, dass Symlinks gefolgt werden soll. Das Ziel wird als normale Datei hochgeladen. Dies ist auch der Default. Die andere Möglichkeit ist ignore, womit dav-sync dann Symlinks nicht mehr folgt, sondern ignoriert.

Es gibt auch noch das Element <symlink-extern> für Links mit einem externen Target. Hier sind die Werte follow und ignore möglich. Wie erwähnt ist sync, also das Synchronisieren von externen Links, nicht möglich.

Autor: Olaf | 0 Kommentare | Tags: dav, sync

dav-sync Datei-Splitting

19. December 2019

Seit dav-sync 1.3 gibt es das Feature, dass Dateien nicht nur als Ganzes synchronisiert werden können, sondern auch nur Teile einer Datei. Hierfür wird für die Datei eine feste Block-Größe definiert und die Datei wird dann in mehrere Blöcke mit dieser Größe eingeteilt. Lokal wird dann pro Block ein Hash gespeichert, so dass erkannt werden kann, welcher Block geändert wurde und synchronisiert werden muss. Auf dem Server wird statt der Datei eine Collection angelegt, in der die einzelnen Blöcke abgelegt werden.

Das Ganze funktioniert natürlich auch Problemlos mit Verschlüsselung. Jeder Block wird als eigenständige Datei hochgeladen und dabei wie sonst auch mit AES im CBC-Mode verschlüsselt.

Natürlich muss man, um das Feature zu nutzen, mal wieder etwas konfigurieren. Zunächst sollte man sich überlegen, welche Dateien überhaupt zerlegt werden sollen. Mögliche Konfigurationsmöglichkeiten sind zum einen, Dateien mit bestimmten Namen oder Pfaden auszuwählen, zum anderen kann auch eine Mindestdateigröße angegeben werden, ab der Dateien aufgeteilt werden sollen.

Die andere Sache, die man sich überlegen muss, ist, welche Größe die einzelnen Blöcke haben sollen. Diese sollte nicht zu klein sein, da sonst viele tausende kleine Dateien auf dem Server abgelegt werden könnten.

Die Konfiguration erfolgt dann in der Datei sync.xml. Hier muss innerhalb des Sync-Directory folgendes eingefügt werden:

<directory>
    ...

    <splitconfig>
    <!-- split all files with .vmdk file extension that are bigger than 100mb -->
        <split>
            <blocksize>10m</blocksize>
            <filter>
                <include>\.vmdk$</include>
            </filter>
            <minsize>100m</minsize>
        </split>
    </splitconfig>
</directory>

Pflichtangabe ist das Element <blocksize>. Damit wird die Blockgröße in Bytes, alternativ auch mit einem Suffix wie k, m oder g, angegeben. Dazu muss entweder <minsize> oder ein <filter> definiert sein. Beides zusammen geht natürlich auch.

Mit <filter> können Regex-Filter, die auf den Dateipfad angewendet werden, konfiguriert werden. Möglich sind zum einen <include>-Filter, die zunächst definieren, welche Dateien ausgewählt werden sollen. Dies kann dann noch mit <exclude> eingeschränkt werden. Die Doku enthält dazu mehr Details.

Mit <minsize> gibt man die Größe an, die eine Datei mindestens haben muss, damit sie aufgeteilt wird. Gibt man nur dieses Element an, ohne <filter>, kann man also pauschal alle Dateien ab einer bestimmten Größe splitten.

Es können auch mehrere split-Regeln definiert werden. Dazu muss nur innerhalb des Elements <splitconfig> ein weiteres <split>-Element eingefügt werden.

Mehr als die Konfiguration muss man dann auch nicht tun. An der Benutzung von dav-sync ändert sich nichts. Wenn alles korrekt konfiguriert ist, werden dann die entsprechenden Dateien aufgeteilt und nur geänderte Blöcke der Datei werden übertragen. Der Benutzer kriegt davon erstmal nichts mit, außer dass es vielleicht schneller geht.

Eine Einschränkung gibt es allerdings. Datei-Versionierung funktioniert nicht mit gesplitteten Dateien. Genauso funktioniert es nicht, wenn <backup-on-pull>true</backup-on-pull> für das Sync-Directory konfiguriert ist, dass bei jeder Dateiänderung ein Backup der Datei in das Trash-Verzeichnis verschoben wird. Der Grund ist, dass nicht mehr kostengünstig über ein move die Datei gesichert werden kann, weil keine komplette Datei übertragen wird.

Erwähnenswert wäre auch noch, dass Datei-Splitting ohne Konfiguration der sync.xml möglich wäre, denn es kann im Prinzip pro Datei einzelnd festgelegt werden, ob diese gesplittet werden soll. Einfache dav-sync-Befehle, um das festzulegen, haben es jedoch nicht in das Release geschafft und ohne Befehle wäre das zu Hacky für diesen Artikel.

Autor: Olaf | 0 Kommentare | Tags: dav, sync

dav-sync Versioning

17. December 2019

Ich habe in dav-sync von Anfang an die Datensicherheit an erste Stelle gestellt. Geprägt von Horrorgeschichten über Synchronisations-Programme, die massenweise Daten niedergemetzelt haben, wollte ich mir und allen dav-sync Usern dies ersparen. Daher gab es schon von Anfang an als zusätzliche Sicherung das Trash-Verzeichnis, in das Dateien verschoben wurden, statt sie zu löschen.

Neu ist jetzt das Versioning-Feature, womit auf dem Server mehrere Versionen von Dateien gespeichert werden können. Dies ist in zwei Varianten implementiert. Einmal über einfache Kopien von den Dateien, die in einer versteckten Collection abgelegt werden. Die andere Variante nutzt die WebDAV-Erweiterung DeltaV, wofür es jedoch praktisch keine Server-Implementierungen gibt. Noch nicht ;-)

Im Folgenden möchte ich die Konfiguration und die Benutzung der dav-sync Versionierung erläutern.

Zunächst benötigt man natürlich erst einmal eine Konfiguration für ein Verzeichnis, welches synchronisiert werden soll. In diesem Artikel habe ich die Einrichtung und grundlengende Benutzung von dav-sync erklärt. Für die Versionierung muss nur noch die Konfiguration (sync.xml) etwas angepasst werden. Hierfür muss nur innerhalb des <directory>-Elements folgendes ergänzt werden:

<versioning type="simple" always="true" />

Das type="simple" steht für die Versionierung über einfache Kopien der Dateien. Alternativ könnte hier statt simple auch deltav stehen. Hierfür benötigt man natürlich einen DeltaV-kompatiblen Server. Apache, Nextcloud oder praktisch alle anderen WebDAV-Server unterstützten dies jedoch nicht.

Das Attribut always="true" gibt an, dass bei jedem Hochladen einer Datei eine neue Version angelegt werden soll. Hier könnte natürlich auch false angegeben werden, dann wird nur eine neue Version gespeichert, wenn dies manuell angefordert wird. Hierfür gibt es für die dav-sync Kommandos push und archive die Option -S. Wird diese angegeben, wird für jede hochgeladene Datei die vorherige Version gespeichert.

Damit ist die Konfiguration auch schon abgeschlossen. Alte Versionen von Dateien werden standardmäßig in der Collection .dav-version-history (unterhalb des Sync-Directory) abgelegt. Wer will könnte noch den Speicherort der Versions-History mit dem <history>-Element anpassen.

<versioning type="simple" always="true">
    <history>/.backup/</history>
</versioning>

Jetzt werden immer alte Versionen von Dateien gespeichert. Wer hingegen always="false" benutzt, kann beim Uploaden von Dateien folgendermaßen die Version speichern:

$ dav push -S mydir

Um jetzt alte Versionen von Dateien wiederherzustellen, benötigt man zwei dav-sync Befehle: list-versions und restore. Beide Befehle arbeiten mit lokalen Dateipfaden, man kann daher einen absoluten oder relativen Pfad zu der Datei angeben und dav-sync bestimmt dann automatisch das passende Sync-Directory dazu (wenn es eindeutig ist, ansonsten muss man es mit der Option -s angeben). Als Beispiel gehen wir davon aus, dass wir uns in der Shell im Verzeichnis befinden, in dem die Datei liegt.

$ dav-sync list-versions test.txt
name: 1576420562-69ba29c77659c2db
lastmodified: Sun, 15 Dec 2019 10:39:30 GMT
url: https://example.com/webdav/.history/157/1576420562-69ba29c77659c2db

name: 1576420572-b6a1862b0b1c04e6
lastmodified: Sun, 15 Dec 2019 11:12:28 GMT
url: https://example.com/webdav/.history/157/1576420572-b6a1862b0b1c04e6

name: 1576420613-8b1260a7a6943580
lastmodified: Sun, 15 Dec 2019 15:33:06 GMT
url: https://example.com/webdav/.history/157/1576420613-8b1260a7a6943580

list-versions zeigt alle verfügbaren vorherigen Versionen einer Datei an. Was wir aus der Ausgabe benötigen ist das Feld name. Den Namen können wir bei dem Befehl restore benutzen, um diese Version wiederherzustellen.

$ dav-sync restore -V 576420572-b6a1862b0b1c04e6 test.txt
get: /test.txt
Result: 1 file pulled, 0 errors

Dies downloaded die angegebene Version und ersetzt damit die lokale Datei.

Eine Einschränkung ist noch, dass es keine einfache Möglichkeit gibt, gelöschte Dateien wiederherzustellen. Wenn man versehentlich eine Datei löscht, kann man unmittelbar danach zwar mit dav-sync restore -s <syncdir> alle gelöschten Dateien wiederherstellen. Dies funktioniert jedoch nicht mehr, sobald man nach dem Löschen den Befehl push ausführt und somit auf dem Server die Datei gelöscht wird. Dann ist die Datei zwar noch in der History vorhanden, es gibt jedoch keinen Befehl, der die Datei wiederherstellen kann. Da bleibt einem nur übrig, die gesamte History zu durchsuchen.

Autor: Olaf | 0 Kommentare | Tags: dav, sync

dav secret store

16. December 2019

Eines der neuen Features in dav 1.3 ist der Secret-Store. Dieser bietet eine alternative Methode um Authentifizierungsdaten zu speichern. Bisher war es nur möglich, in der config.xml optional Benutzer und Passwort abzulegen. Dies hat natürlich den Nachteil, dass das Passwort damit im Klartext abgespeichert ist.

Der Secret-Store ist eine passwortgeschützte Ablage für Credentials, die durch eine eindeutige ID identifiziert werden. Credentials enthalten dann einen Benutzernamen, ein Passwort und optional zugehörige URLs. Die Credential-IDs und URLs werden unverschlüsselt gespeichert. Damit kann dav überprüfen, ob für eine angefragte URL Zugangadaten im Secret Store vorhanden sind. Wenn dies der Fall ist, wird nach dem Master-Passwort gefragt. Mit diesem wird der Secret-Store dann entschlüsselt und die gespeicherten Zugangsdaten passend zur URL verwendet.

Für die Verwaltung des Secret Store gibt es mehrere Befehle. Neue Credentials können mit dav add-user angelegt werden. Dies fragt zunächst das Master-Passwort ab, um entweder einen bestehenden Secret Store zu entschlüsseln oder um einen neuen anzulegen. Danach muss ein Credentials Identifier angegeben werden, der eindeutig sein muss. Es folgen die eigentlichen Authentifizierungsinformationen, nämlich Benutzername und Passwort. Optional können dazu noch mehrere Locations gespeichert werden.

$ dav add-user
Master password: 
Credentials identifier: user1
User: myuser
Password: 
Location (optional): http://example.com/
Location (optional):

Wenn danach nun eine der angegebenen URLs aufgerufen wird, werden die im Secret Store gespeicherten Authentifizierungsinformationen genutzt. Hierfür fragt dav nach dem Secret Store Passwort:

dav list http://example.com/
Master password:
...

Dies funktioniert auch, wenn man nicht direkt über eine URL, sondern einen Repository-Namen zugreift. Wenn für die Repository-URL im Secret-Store Credentials hinterlegt sind, werden diese genutzt. Es kann auch im Secret-Store als Location ein Repository-Name oder ein Repository-Name mit zugehörigen Pfad gespeichert werden.

Location (optional): myrepo/path/

Eine weitere Möglichkeit um Repositories mit Credentials zu verknüpfen ist, in der config.xml die Credentials-ID anzugeben. Hierfür gibt es das Element <stored-user>.

<repository>
    <name>myrepo</name>
    <url>http://example.com/webdav/</url>
    <stored-user>user1</stored-user>
</repository>

Weitere Befehle Administrations-Befehle für den Secret-Store sind remove-user, edit-user und list-users. Diese sind interaktive Befehle (bis auf list-users) und dürften einfach zu benutzen sein, wenn man das Konzept des Secret-Stores verstanden hat.

Autor: Olaf | 0 Kommentare | Tags: dav
Weiter