Posts

  • Integrationstest mit Docker Compose und Azure Service-Containers

    In einem vorherigen Blog-Post habe ich gezeigt, wie man mit Hilfe von Testcontainers Integrationstests in einer .NET-Anwendung umsetzen kann.

    Möchte man die Docker-Container nicht innerhalb des Test-Codes verwalten, bietet es sich an, die Container unabhängig von der Test-Ausführung zu starten und zu stoppen. Docker Compose und das Azure DevOps-Feature Service-Containers bieten sich hier als Alternativen an.

    Weiterlesen
  • Integrationstest in .NET mit Testcontainern

    In der Software-Entwicklung ist es essentiell, den geschriebenen Code ausreichend zu testen. Neben Unit-Tests, die einzelne Komponenten isoliert prüfen, betrachten Integrationstests das Zusammenspiel mehrerer Komponenten. Hat man nun aber externe Abhängigkeiten wie z.B. einen Datenbank oder einen Message Broker, wird das Testen komplexer.

    Damit die externen Systeme auch in den Integrations-Tests reproduzierbar und vorhersehbar zur Verfügung stehen, kann man sie in Docker-Containern ausführen. Ein einfaches Setup solcher Service-Container ermöglicht die Bibliothek Testcontainers.

    Weiterlesen
  • Leichtgewichtige Architekturdokumentation mit ADRs

    Erreicht eine Software-Anwendung eine gewisse Größe, ist eine Dokumentation der einzelnen Komponenten und deren Zusammenspiel sinnvoll. Auch das Festhalten von Architektur-Entscheidungen ist wichtig, um auch Monate oder Jahre später noch nachvollziehen zu können, warum eine bestimmte Lösung gewählt wurde. Zukünftige Kolleg:innen und euer zukünftiges Ich werden es euch danken.

    Eine leichtgewichtige Methode, um Architektur-Entscheidungen zu dokumentieren, stellen Architectural Decision Records, kurz ADRs, dar.

    Weiterlesen
  • Architektur-Refactoring mit ArchUnitNET

    Neben den funktionalen Anforderungen an ein Software-Produkt, welche sich mittels Unit- und Integrationstests absichern lassen, existieren auch eine Reihe von nicht-funktionalen Anforderungen. Aspekte der Sicherheit oder der Performance einer Anwendung lassen sich durch End-to-End Tests abdecken. Um die Wartbarkeit und Erweiterbarkeit einer Software sicherzustellen, kann man neben statischer Code-Analyse auch automatisierte Architektur-Tests einsetzen. Damit lassen sich Abhängigkeiten zwischen Komponenten oder die Einhaltung von Namenskonventionen überprüfen.

    Auch beim Refactoring von bestehendem Code können sich solche Architektur-Tests als sinnvoll erweisen.

    Mit ArchUnitNET lassen sich Architektur-Regeln als Code definieren und automatisiert testen.

    Weiterlesen
  • Uptime Kuma in Azure Web App for Containers

    Uptime Kuma ist ein self-hosted Monitoring System, das es erlaubt, die Verfügbarkeit von unterschiedlichen Services zu überwachen. Es bietet eine Vielzahl von Monitoring-Typen an, darunter für verschiedene Arten von HTTP-Endpunkte wie Websites oder REST-APIs.

    Da es auch als fertiges Docker Image zur Verfügung steht, kann man es einfach als Azure App Service deployen. Für ein Ausrollen mittels Terraform genügt eine einfache Konfiguration.

    Weiterlesen
  • Refactor to Purity

    Pure Functions sind Program-Methoden, die ohne Seiteneffekte auszulösen ausgeführt werden können. In der funktionalen Programmierung sind sie eher die Regel als die Ausnahme. In den meisten objektorientierten Sprachen hingegen begegnet man ihnen aber eher weniger, oder zumindest werden sie häufig nicht als das Mittel der Wahl in Betracht gezogen. Im dotnet-Umfeld wird viel über Dependency Injection und mehr oder weniger umfangreiche Abstraktionen mittels Interfaces abgehandelt.

    Wie man von einer Codebasis mit vielen solchen Indirektionen zu einer einfacheren Variante kommt, die ein Vieles an überflüssiger Komplexität entfernt, soll der folgende Artikel zeigen.

    Weiterlesen
  • Einfaches Test-Setup mit Dummy-Factories

    Zur sinnvollen Verifizierung der Korrektheit von Software gehören aussagekräftige Tests. Eine immer wiederkehrende Tätigkeit ist das Schreiben von Quellcode, der Testdaten erzeugt oder beschreibt. Dies kann je nach Umfang der Daten mühsam sein und den Entwicklungsprozess ausbremsen und im schlimmsten Fall dazu führen, dass auf Tests ganz verzichtet wird. Eine elegante Möglichkeit, Testdaten einfach und schnell zu erzeugen, sind Dummy-Factories.

    Weiterlesen
  • Docker Scale-to-Zero mit Traefik und Sablier

    Wenn man Web-Anwendungen oder -Services betreibt, die nur sporadisch genutzt werden, kann es sinnvoll sein, diese nur bei einem konkreten Bedarf zu starten. Im Umfeld von Docker-Containern und Kubernetes existiert für solche Anwendungsfälle das Konzept des Scale-to-Zero, also das Herunterskalieren von Workloads auf Null.

    Für HTTP-Services in Verbindung mit einem Reverse-Proxy bietet Sablier eine einfache Möglichkeit, Scale-to-Zero in der eigenen Infrastruktur umzusetzen.

    Weiterlesen
  • DNS-Automatisierung mit DNSControl

    Die Konfiguration von DNS-Zonen gehört bei den meisten (Web-)Software-Projekte dazu. Ob beim initialen Setup oder im Laufe der Zeit müssen immer wieder Domains registriert und DNS-Einträge eingerichtet oder angepasst werden. Das kann man zwar zumeist über das Webinterface des entsprechenden Anbieters von Hand erledigen, was aber fehleranfällig, schlecht nachvollziehbar und nicht zu automatisieren ist.

    Nutzt man zur Provisionierung seiner Cloud-Infrastruktur Infrastructure-as-Code Tools wie Terraform, kann man die DNS-Konfiguration natürlich auch darüber steuern. Allerdings ist man damit bei der Auswahl des DNS-Providers auf die großen Hyperscaler beschränkt.

    Einfacher und mit weniger Konfigurationsaufwand erlaubt es DNSControl, die DNS-Konfiguration für dutzende große und kleine Provider als Javascript-Code zu definieren. Dadurch lässt sich die DNS-Umgebung automatisiert und reproduzierbar aufsetzen und verwalten. Die Konfiguration ist versioniert in einem Git-Repository abgelegt und kann einem Code-Review unterzogen werden, was das Vorgehen perfekt in die DevOps-Toolchain integriert.

    Weiterlesen
  • Property-based testing in C# mit FsCheck

    Zum Handwerkszeug bei der Entwicklung von Software gehört es, (sinnvolle) automatisierte Tests zu schreiben. Mittels Unit-, Integration- und End-to-end-Tests wird die korrekte Funktionalität unseres Codes sichergestellt und wir erhalten die Sicherheit, dass wird beim Umsetzen neuer Anforderungen und beim Refactoring bestehenden Codes keine vorhandene Funktionalität kaputt machen. Michael Feathers definiert Legacy Software sogar als Code, der nicht getestet ist.

    legacy code is code without tests

    Michael Feathers

    Neben den verbreiteten Ansätzen von Example-based testing oder Snapshot testing gibt es noch weitere Möglichkeiten, die uns helfen, die Korrektheit unseres Codes zu überprüfen. Einer dieser Ansätze ist Property-based testing.

    Property-based testing prüft nicht einzelne Werte auf Übereinstimmung, sondern zielt auf die Verifikation von Eigenschaften ab, die unsere Implementierung aufweisen müssen. So ist für die mathematische Addition die Kommutativität eine Eigenschaft, die wir überprüfen können, ohne uns konkrete Zahlen anzuschauen. Für die Addition zweier Zahlen a und b gilt immer:

    a + b = b + a
    
    Weiterlesen
  • Devcontainer mit Visual Studio Code

    Für das Ausführen von Anwendungen hat sich, vor allem im Cloud-Umfeld, die Verwendung von Containern etabliert. Mit dem Aufkommen von Docker wurde es einfach, Software in einer definierten Umgebung auszuführen, welche sich einfach reproduzieren lässt. Sämtliche Runtime-Abhängigkeiten werden in ein Container-Image verpackt und können einfach auf einem anderen System ausgeführt werden. Die Diskrepanz zwischen unterschiedlichen Stages im Entwicklungsprozess (Development, Test, Produktion) wird reduziert, works on my machine zählt nicht mehr als Ausrede.

    Um nun nicht nur die Laufzeit- sondern auch die Entwicklungs-Umgebung reproduzierbar und portierbar zu gestalten, bieten sich Development Containers an.

    Weiterlesen
  • Statische Code-Analyse für Terraform

    Statische Code-Analyse ist ein wichtiger Bestandteil der Qualitätssicherung von Software-Projekten. Durch sie können Fehler bereits frühzeitig in einer kurzen Feedback-Schleife erkannt und behoben werden. Der Quellcode wird anhand von definierten Regeln auf Fehler und potenzielle Schwachstellen hin überprüft und je nach verwendetem Tool wird direkt eine mögliche Lösung angeboten.

    Mit dem aufkommen von Infrastructure As Code-Ansätzen werden nun auch zunehmend Infrastruktur-Beschreibungen in Form von Code- oder zumindest von strukturierten Textdateien hinterlegt. Damit lassen sich die Vorteile der statischen Code-Analyse auch auf Infrastruktur-Definitionen anwenden.

    Weiterlesen
  • AKS Kubernetes Update

    Kubernetes ist der de-facto Standard für Container-Orchestrierung. Möchte man den Betrieb nicht selber bewerkstelligen, greift man zu einer Managed Lösung wie Microsofts Azure Kubernetes Service (AKS). Damit werden einem weite Teile des operativen Betriebs abgenommen. Auch die Aktualisierung der Kubernetes-Version kann komplett von Azure übernommen werden. Wie man auto-upgrade konfiguriert, erklärt Microsoft in diesem Artikel. Möchte man die Version und den Zeitpunkt eines Upgrades seines Cluster aber selber festlegen, kann man dies über die Azure CLI oder noch besser via Terraform erledigen. Voraussetzung ist natürlich, dass man seine Kubernetes-Infrastruktur mittels Terraform provisioniert.

    Weiterlesen
  • Mailgraph Docker Container

    Postfix gehört vermutlich immer noch zu den am weitesten verbreiteten freien SMTP-Servern. Wer einen Emailserver selber hostet und die Menge an ein- und ausgehenden Emails im Blick haben möchte, stößt schnell auf das Tool Mailgraph von David Schweikert. Mailgraph besteht aus einem Perl-Skript, das die Logfiles von Postfix auswertet und eine RRDtool-Datenbank befüllt und einem CGI-Skript, welches die Daten in Form von Grafiken übersichtlich im Browser darstellt.

    Weiterlesen