Blog: Latest Entries (15):


Traefik und Docker: Labels im Image

Um die Traefik Labels einzubauen hat man ja die Wahl diese im Image zu haben oder im Container. Während man die dem Container beim Starten geben kann, muss man die für das Image beim Build-Process schon haben. Ich benutze beides und muss sagen, dass ich an sich dafür bin die dem Container zu geben. Aber falls man sich mal fragt wie man dynamische Labels dem Image geben kann... ARG ist das Geheimnis.

Wenn ich nun eine dynamische Subdomain haben will:

FROM httpd:2.4
ARG subdomain
COPY ./index.html /usr/local/apache2/htdocs/index.html
LABEL traefik.enable=true traefik.frontend.rule=Host:$subdomain.example.com


Beim Bauen weißt man dem ARG so einen Wert zu:

docker build -t blubb:latest --build-arg subdomain="blubb" .


Hier kann man dann auch auf ENV-Variablen zurück greifen und die weiter durch reichen. Was sehr praktisch ist, wenn man sich in einem Gitlab-CI Job befindet.

Traefik + Gitlab-CI: Autodeployment

Nachdem ich meine wichtigsten Projekte in Docker-Container verfrachtet hatte und diese mit Traefik (1.7) als Reserve-Proxy seit Anfang des Jahres stabil laufen, war die Frage, was ich mit den ganzen anderen Domains mache, die nicht mehr oder noch nicht produktiv benutzt werden.
Ich hatte die Idee einen kleinen Docker-Container laufen zu lassen, auf den alle geparkten Domains zeigen und der nur eine kleine Info-Seite ausliefert. Weil das Projekt so schön übersichtlich ist und ich gerne schnell und einfach neue Domains hinzufügen will, ohne dann immer Container selbst stoppen und starten zu müssen, habe ich mich dazu entschieden hier mit Gitlab-CI ein automatisches Deployment zubauen. Mein Plan war es ein Dockerfile zu haben, das mir das Image baut und bei dem per Label auch die Domains schon angegeben sind, die der Container bedienen soll. Wenn ich einen neuen Tag setze soll dieser das passende Image bauen und auf meinem Server deployen. Ich brauche dann also nur noch eine Datei anpassen und der Rest läuft automatisch.
Dafür habe ich mir dann extra einen Gitlab-Account angelegt. Man hat da alles was man braucht und 2000 Minuten auf Shared-Runnern. Mehr als genug für meine Zwecke.

Ich habe also eine index.html und ein sehr einfaches Dockerfile (docker/Dockerfile):

FROM httpd:2.4
COPY ./index.html /usr/local/apache2/htdocs/index.html
LABEL traefik.enable=true traefik.frontend.rule=Host:darknovels.de,www.darknovels.de


Das wird dann also in einen Job gebaut und in einem nach gelagerten auf dem Server deployed. Dafür braucht man einmal einen User auf dem Server und 2 Variablen in Gitlab für den Runner.


sudo adduser dockerupload
sudo usermod -aG docker dockerupload


Dann erzeugt man sich für den User einen Key (ohne Passphrase):

su dockerupload
ssh-keygen -t rsa
cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys
exit


Vielleicht muss man da noch die /etc/ssh/sshd_config editieren, damit die authorized_keys-Datei verwendet wird.

Den Private-Key einmal kopieren und in SSH_PRIVATE_KEY unter Settings - CI /DI - Variables speichern. Damit wir uns sicher vor Angriffen verbinden können müssen wir noch den Server zu den bekannten Hosts hinzufügen. Den Inhalt von known_hosts bekommt man durch:


ssh-keyscan myserver.com


Einfach den gesamten Output kopieren und in den Gitlab Variablen unter SSH_KNOWN_HOSTS speichern. Nun hat man alles was man braucht.

bbcode-image


Jetzt folgt noch die .gitlab-ci.yml:

stages:
- build
- deploy

builddocker:
image: docker:git
stage: build
services:
- docker:dind
variables:
DOCKER_DRIVER: overlay
script:
- docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.gitlab.com
- docker build -t registry.gitlab.com/hpries/parkingpage:${CI_COMMIT_REF_SLUG} -f docker/Dockerfile .
- docker push registry.gitlab.com/hpries/parkingpage:${CI_COMMIT_REF_SLUG}
only:
- tags

pushtoserver:
image: kroniak/ssh-client:3.6
stage: deploy
script:
- mkdir ~/.ssh
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
- eval "$(ssh-agent -s)"
- ssh-add <(echo "$SSH_PRIVATE_KEY")
- ssh dockerupload@hannespries.de "docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.gitlab.com"
- ssh dockerupload@hannespries.de "docker stop pageparking" || true
- ssh dockerupload@hannespries.de "docker rm pageparking" || true
- ssh dockerupload@hannespries.de "docker rmi registry.gitlab.com/hpries/pageparking:${CI_COMMIT_REF_SLUG}" || true
- ssh dockerupload@hannespries.de "docker run --name pageparking --network=web -d registry.gitlab.com/hpries/pageparking:${CI_COMMIT_REF_SLUG}"
only:
- tags


bbcode-image


CI_BUILD_TOKEN muss nicht gesetzt werden, sondern wird direkt von Gitlab bereit gestellt.

bbcode-image


Einen Tag setzten, dann etwas warten, dann liegt hinter der Domain die index.html und ist direkt schon über HTTPS erreichbar.

Geht ziemlich schnell und einfach.. wenn man gute Beispiele hat! Sind wir nicht alle etwas DevOps?

Menschen und Sicherheit

Nach den Weihnachtstagen frage ich mich immer, warum viele Menschen so viel gegen Sicherheit haben? Auf der einen Seite halten sie schon das Lastschriftverfahren für zu unsicher, als dass man es verwenden könnte (man kann Amazon ja nicht trauen...) und Homebanking wird in deren Vorstellungen ja auch dauernd gehackt, ohne dass der Benutzer was dafür kann. Das ganze Internet gilt als unsicher und gefährlich. Am liebsten würden sie es gar nicht nutzen... das einzige was diese Menschen noch viel weniger nutzen möchten und als noch viel schlimmer als das Internet ansehen sind Sicherheitsmaßnahmen.

Ich habe früher gelernt, dass man auf Geld, Schlüssel und Ausweispapiere gut aufpassen soll. Wenn du deinen Schlüssel verlierst, sollte man das Schloss auswechseln, weil es könnte sein, dass jemand herausfindet zu welchen Haus der Schlüssel gehört und dort einbricht. Das hab von den selben Menschen gelernt, die
einen doof angucken, wenn man denen erzählt, dass es nicht gut ist PIN und Karte zusammen in der Brieftasche aufzubewahren. "Ich merk mir doch nicht noch einen PIN" (also einen zweiten... bei zwei Karten) kommt dann als Antwort. Dass man damit sich und sein Geld schützt, ist oft nicht wirklich rüber zu bringen. PINs sind eine Gängelung des Kunden durch die Banken. Er wird nur gesehen, dass die Banken einen zwingen einen PIN auswendig zu lernen. Der Sinn und Zweck dahinter wird oft nicht mal versucht zu erfassen. Wobei momentan EC-Karten bei vielen sowie so als absolut unsicher gelten, seit man damit kontaktlos bezahlen kann. Da muss ja nur jemand in der Nähe sein und schon ist das eigene Bankkonto leer geräumt. Also auf jeden Fall solche Schutzhüllen für die Karten kaufen. Auch für alte Karten, die gar kein NFC/RFID können, weil man kann sich nicht sicher sein, dass nicht doch jemand es mit so einem Lesegerät hinbekommt, das Konto leer zu räumen. Kontaktloses Bezahlen ist also kein extra implementiertes Feature sondern eine Sicherheitslücke die in einigen Karten vorkommt (und man kann sich nicht sicher sein ob man betroffen ist).

Aber eines der schlimmsten Dinge, die in den letzten Monaten passiert ist und viele Menschen wirklich extrem geschockt hat und den Glauben an die Sicherheit der Banking-System hat verlieren lassen, war PSD2. Viele trauen sich nicht mehr Homebanking zu benutzen oder sind gar nicht mehr in der Lage dazu... weil eine zusätzliche Sicherheitsabfrage beim Login auftaucht. Wie schaltet man das ab? - Garnicht? Weil es gut ist, dass man eine 2-factor Auth auch beim Banking hat?
Allein die Abschaffung der TAN-Listen (ich war eher gesagt geschockt, dass einige Banken, die immer noch hatten) war hart. SMS-TAN ging irgendwie noch, auch wenn dem Handy (nicht Smartphone!) auch nicht wirklich getraut wurde. TANs die nicht festgeschrieben sind und geklaut werden können... denen kann man nicht trauen. Aber wenn dann noch SMS-TAN abgeschafft wird (weil SMS-Nachrichten eben nicht verschlüsselt sind) kommen viele an den Punkt, wo für die Homebanking absolut nicht mehr nutzbar wird. Foto-TAN oder noch viel schlimmer eine Smartphone-App mit Push-Benachrichtigung und Fingerabdruckscanner.
Da ist das Vertrauen dann ganz weg. Die Lösungen sind nicht komplizierter zu nutzen (teilweise sogar einfacher), ihnen wird nur nicht mehr getraut und deswegen werden sie nicht mehr benutzt. Je sicherer etwas wird, desto unverständlicher ist es für die meisten und desto größer auch die instinktive Ablehnung dagegen.
Die beste Lösung für viele war die einfache TAN-Liste. iTAN war schon etwas seltsam... weil vorher weiß die Bank (die die Liste gedruckt und mir zugeschickt hat) welche TAN hinter welcher Nummer auf MEINEM Zettel stehen?

Fazit:
Desto sicherer ein System ist, desto mehr Menschen werden versuchen es zu meiden oder zu umgehen, weil viele Menschen unsicheren Systemen mehr vertrauen als sicheren Systemen.

Traefik + Homepages

Seit heute laufen die meisten meiner Homepages als Docker-Container mit Traefik als Reserve-Proxy. Es war teilweise ein sehr harter Kampf mit vielen kleinen Fehlern. Wenn man sauber von vorne anfängt sollten weniger Fehler auftreten.

Was man beachten sollte:
- Images müssen Port 80 exposen
- HTTPS-Umleitungen aus htaccess-Dateien entfernen (Traefik kümmert sich darum)
- Datenbanken über Adminer oder PHPMyAdmin initialisieren und nicht über init-Scripte
- Man braucht ein eigenes Netzwerk in Docker wie "web"
- traefik.frontend.rule und traefik.enable reichen als Tags
- man macht vieles ungesichert während des Setups, dass muss man später alles wieder absichern (die Traefik-UI/API!)
- Immer Versionen für die Images angeben und nie LATEST (sonst hat man plötzlich neue Probleme)

Ich habe noch Traefik 1.7 laufen, aber das funktioniert so weit sehr gut und es gibt viele Hilfen. Für 2.0 gibt noch nicht so viele Hilfen und Beispiele. Für jeden Container habe ich auch ein eigenes Dockerfile, damit man da kleine Modifikationen an den Images machen (auch wenn es nur mal zum Testen ist).

Emails laufen nun alle über poste.io der unabhängig von Traefik läuft und einen eigenen Port für die Web-UI nutzt. Das Setup ging schneller und läuft schon seit einigen Wochen extrem stabil und filtert Spam sehr viel besser als meine vorherige selbst gebaute Lösung.

Blackmagic Intensity: HDMI-Out Monitor

Das Problem beim Capturen von Videos ist an sich immer die Latenz. Die Verarbeitung des Signals braucht eben bis die Encoder-Hardware durchlaufen wird und dass kann einige MS bis Sekunden dauern. Den Effekt kann man allein schon bei TV-Geräten sehen, wenn man Bild und Ton von z.B. einem Sat-Receiver getrennt überträgt. Da kann es schnell passieren, dass der Ton im Soundsystem ein wenig schneller verarbeitet wird als das Bild im TV-Gerät und so der Ton immer schon etwas weiter ist. AV-Receiver sorgen dafür, dass alles synchron bleiben oder bei anderen Soundsytemen kann eine Verzögerung per Hand einstellen, um Ton und Bild synchron zu halten.

bbcode-image


Bei der Blackmagic Intensity hat man die Möglichkeit am HDMI-Out einen weiteren Monitor zu betreiben, der das Bild und den Ton verzögerungsfrei darstellt, während die Darstellung und Verarbeitung auf dem PC (Capture-System) verzögert dargestellt werden kann. Wenn man einen SNES-Mini anschließt kann man sehr gut nachvollziehen, warum man das auch braucht. Ohne Monitor hängt das Bild immer etwas hinterher. Wenn man in Super Mario World springt, wird das Bild auf dem PC diese Aktion erst eine halbe Sekunde später anzeigen. Das Spiel wird dadurch also an sich unspielbar. Sollten noch Filter auf das Bild angewendet werden oder es mit anderen Video-Quellen abgemischt werden, kann die Verzögerung sogar noch größer werden.

Über den HDMI-Out merkt man wirklich keine Verzögerung und kann normal spielen.


Down im Dezember

Es wird mal Zeit sich der Zukunft zu zuwenden und nicht mehr in der Vergangenheit zu leben. Deswegen werde ich endlich mal alle meine Seiten und Projekte in Docker-Container verfrachten und mit Traefik meinen Server neu strukturieren. Deswegen kann es im Dezember bei meinem Blog und MP4toGIF.com zu längeren Downzeiten im Dezember kommen. Ich überlege auch einen günstigen zweiten Server nur für Emails zu nehmen.

Das ganze sollte mir dann auch die Möglichkeiten geben spontan mal andere Dienste zu deployen. Im Kopf habe ich da so:

* Passbolt (als Ersatz für KeePasssXC)
* nextCloud um von OneDrive weg zu kommen
* und vielleicht Satis um meine PHP-Libs zu bündeln

Ich hoffe mal, dass alles ganz einfach und super funktioniert :-)

Amazon Echo Flex: Zuviel Geld für zuwenig Klang

Der Amazon Echo Flex sah auf den ersten Blick nach einem perfekten Gerät für Küche oder Bad aus. Der passt in eine Steckdose und steht nirgends im Weg. Er ist relativ günstig und wenn er nur etwas schlechter wäre als ein Echo Dot, wäre er immer noch sehr gut.
Leider ist aber die Klangqualität nicht etwas schlechter sondern sehr viel schlechter als beim Dot. Musik ist wirklich kein Highlight, aber die Stimme von Alexa klingt nochmals schlimmer. Extrem blechernd. Der Lautsprecher darin scheint billiger als nötig gewesen zu sein. Selbst ein "OK" von Alexa klingt einfach nicht mehr schön.

bbcode-image
Echo Flex in der Küche (mit Leonardo)


Wenn man bedenkt, dass man den Flex momentan für 19,99 EUR bekommt und den Dot für 22 EUR, sollte man nicht mal einen Gedanken an den Flex und lieber einen Gedanken mehr an seine Ohren verschwenden und den Dot kaufen!

VSCode: PHP Debugging

Es war ein harter Kampf.. aber ich war siegreich. Nachdem ich durch den Wechsel meines Arbeitgebers auch den Zugriff auf Intellij + PHP-Plugin einbüßen musste, bin ich jetzt vollständig auf VSCode gewechselt.

Wie ich XDebug + VSCode + Docker zum Laufen bekommen habe.

Dockerfile (nur der wichtige Teil):

RUN pecl install xdebug-2.8.0 && docker-php-ext-enable xdebug
RUN echo 'zend_extension="/usr/local/lib/php/extensions/no-debug-non-zts-20151012/xdebug.so"' >> /usr/local/etc/php/php.ini
RUN echo 'xdebug.remote_port=9000' >> /usr/local/etc/php/php.ini
RUN echo 'xdebug.remote_enable=1' >> /usr/local/etc/php/php.ini
RUN echo 'xdebug.remote_host=host.docker.internal' >> /usr/local/etc/php/php.ini


Lokale php.ini (ich habe 2.8.0 herunter geladen und als php_xdebug.dll gespeichert):

zend_extension=c:\php\ext\php_xdebug.dll

#.....

[XDebug]
xdebug.remote_enable = 1
xdebug.remote_autostart = 1


Dann habe ich dieses Plugin in VSCode installiert. Mit Hilfe dieses Boilerplate-Projekts habe ich mir eine passende .vscode/launch.json gebaut.


{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for XDebug",
"type": "php",
"request": "launch",
"port": 9000,
"pathMappings": {
"/var/www/html": "${workspaceFolder}/"
},
"xdebugSettings": {
"max_data": 65535,
"show_hidden": 1,
"max_children": 100,
"max_depth": 5
}
}
]
}


Dann VSCode neugeladen. Docker gestartet und in die Debug-Ansicht von VSCode gewechselt.

bbcode-image


Dort das ""Listen for XDebug" Profile starten und es sollte funktionieren.

bbcode-image


PHPStorm/Intellij ist da viel einfacher, weil man nicht selbst noch JSON-Dateien anlegen und editieren muss. Aber es läuft!


Shopware 6: Ein einfaches Beispiel-Plugin

Plugins für Shopware 6 zu schreiben ist an sich garnicht so unterschiedlich zu dem selben Vorgang für Shopware 5. Was nur anders geworden ist, dass Shopware eigene XMLs und Enlight-Components durch den Composer und Symfony abgelöst wurden. Man kann nun wirklich Namespaces definieren, die unabhängig vom Plugin-Namen sind und es ist kein Problem mehr den Composer mit Plugins zu verwenden, sondern es ist jetzt der zu gehende Weg.

Setup

Wie gehabt kann man sein Plugin unter custom/plugins anlegen.


custom/plugins/HPrOrderExample


Zu erst sollte man mit der composer.json anfangen.


{
"name": "hpr/order-example",
"description": "Order Example",
"version": "v1.0.1",
"license": "MIT",
"authors": [
{
"name": "Hannes Pries"
}
],
"require": {
"shopware/platform": "dev-master"
},
"type": "shopware-platform-plugin",
"autoload": {
"psr-4": {
"HPr\\OrderExample": "src/"
}
},
"extra": {
"shopware-plugin-class": "HPr\\OrderExample\\OrderExample",
"copyright": "(c) by Hannes Pries",
"label": {
"de-DE": "Order-Beispiel für SW6",
"en-GB": "Order-Example for SW6"
}
}
}


Wichtig in der composer.json ist einmal der PSR-4 Namespace den man auf das src/ Verzeichnis setzt und der "extra" Teil. Dort wird alles definiert, was mit Shopware zu tun hat. Hier wird auch die zentrale Plugin-Class angegeben, die nicht mehr wie in Shopware 5 den selben Namen des Verzeichnisses des Plugins haben muss. Auch sieht man hier dass der Namespace-Name nichts mehr mit dem Verzeichniss-Namen zu tun hat.


Wir könnten durch die composer.json das Plugin auch komplett außerhalb der Shopware-Installation entwickeln, weil die gesamte Plattform als Dependency eingebunden ist. Deswegen müssen wir auch einmal den Composer ausführen.


php composer install


Plugin programmieren
Nun fangen wir mit dem Schreiben des Plugins an.

Die Plugin-Klasse enthält erst einmal keine weitere Logik:


<?php
namespace HPr\OrderExample;

use Shopware\Core\Framework\Plugin;

class OrderExample extends Plugin{

}


Wir wollen auf ein Event reagieren. Dafür benutzen wir eine Subscriber-Klasse. Während man diese in Shopware 5 nicht unbedingt brauchte und alles in der Plugin-Klasse erldedigen konnte, muss man nun eine extra Klasse nutzen und diese auch in der services.xml eintragen. Die Id des Services ist der fullqualified-classname des Subscribers

src/Resources/services.xml

<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="HPr\OrderExample\Subscribers\OnOrderSaved">
<tag name="kernel.event_subscriber"/>
</service>
</services>
</container>

Resources ist vordefiniert, kann aber in der Plugin-Klasse angepasst werden, so dass diese Dateien auch in einem anderen Package liegen könnten.

src/Subscribers/OnOrderSaved

<?php
namespace HPr\OrderExample\Subscribers;

use Shopware\Core\Checkout\Order\OrderEvents;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class OnOrderSaved implements EventSubscriberInterface {
/**
* @return array The event names to listen to
*/
public static function getSubscribedEvents()
{
return [
OrderEvents::ORDER_WRITTEN_EVENT => ['dumpOrderData']
];
}

public function dumpOrderData(EntityWrittenEvent $event) {
file_put_contents('/var/test/orderdump.json', json_encode($event->getContext()));
}
}

Hier sieht man, dass nur noch Symfony benutzt wird und keine Shopware eigenen Events.

Wir schreiben einfach die Order-Entity, die gespeichert wurde in eine Datei. Dort können wir UUID und ähnliches der Order finden.

Das ganze kann man dann weiter ausbauen und auf andere Order-Events (die man alle in der OrderEvents.php finden kann) reagieren.

Installieren

Damit ist das Plugin auch schon soweit fertig. Den Composer haben wir schon ausgeführt (ansonsten müssen wir es spätestens jetzt machen).

Nun geht es über die UI oder die Console weiter. Dort wird das Plugin installiert und und aktiviert.

bbcode-image


bbcode-image


Wenn man nun eine Bestellung ausführt wird, deren Entity in die oben angegebene Datei geschrieben.

Alexa und das Küchen Licht

Heute wurde die Küche Tradfridi- und Alexarisiert. Beim Einrichten der Routinen kam es aber zu dem Problem, das "Alexa, Küchenlicht an" nur dazu führte, dass Alexa das nur damit quittierte "Das Gerät Küchenlicht konnte nicht gefunden werden". Das war seltsam denn z.B. "Alexa, Schlafzimmer Licht an" ohne Probleme funktionierte. "Küche Licht an" klingt aber doof. Vielleicht kommen einige jetzt schon auf die Lösung. "Alexa, Küchen Licht an" funktioniert. Also wenn man das Problem hat, einfach mal versuchen Worte auseinander zu schreiben.

bbcode-image


Über die Kämpfe mit dem Ikea Tradfri-System schreibe ich ein anders mal. Spoiler: Meine Nerven haben es nur knapp überlebt.

EasyComponent.JS: Framework Beispiel

Frameworks gelten ganz ganz oft als etwas sehr kompliziertes und mächtiges Werkzeug, das nur von wenigen auserwählten entwickelt und designed wird. Wenn ein Framework etwas nicht kann wird es meistens so hingenommen und darauf gehofft, dass es vielleicht doch noch irgendwann mal eingebaut wird. Meistens würde man sich nie trauen, sich dabei einzubringen oder einzumischen.

Man selbst ist ja einfacher "Anwender" und wie die eigenen Anwender, die das eigene Programm nutzen, das wieder das Framework nutzt, sollte man sich nicht die Dinge einmischen, von denen man keine Ahnung hat.

Ich habe über die Jahre doch ein paar Frameworks gebaut. Gute, schlechte, noch schlechtere und auch welche, die durch die allgemeine Frameworks ersetzt wurden. Auch mal ein Bugfix für ein vorhandenes Framework war dabei.

Wichtig ist dabei, dass man sich klar wird, dass Frameworks im Kern meistens alles andere als hoch komplex sind. Das würde schon dem
Zweck des Frameworks widersprechen, das ja für möglichst viele Anwendungszwecke funktionieren soll. Der Core ist meistens sehr einfach und klar strukturiert. Komplexität kommt meistens über Zusatzfunkionen rein, die wieder rum nur die Kernfunktionen bedienen. Wenn diese Kernfunktionen gemischt werden und kann es am Anfang sehr unübersichtlich erscheinen, weil teilweise nicht ganz klar ist, was wofür da ist.

Bei React mit Hooks kann selbst das Mischen von useState und useEffect teilweise kompliziert sein, weil aus der Sicht des Entwicklers vielleicht, von diesen Funktionen was anderes Erwartet wird, als im Framework-Kern damit wirklich erreicht wird.

Frameworks machen keine Zauberrei aber dafür sehr viel Referenz-Verwaltung. Meiner Erfahrung nach beschäftigt man sich im Framework-Kern zu 75% allein damit Instanzen und Referenzen zu erzeugen und zu verwalten.

Um das ganze etwas zu verdeutlichen habe ich mich einmal hingesetzt und in unter einer Stunde (hatte ich mir auch so vorgegeben) ein kleines Framework gebaut, das useState und useEffect reimplementiert und zeigt, wie man das Verhalten dieser Funktionen nutzt, aber auch, welche Funktion für das Framework wirklich dahinter steckt. Weil am Ende kommt man zu meist immer zu selben Strukturen, die bei allen Frameworks immer nur etwas anders benannt und implementiert sind.

Github - EasyComponent.JS Code & Präsentation
https://github.com/annonyme/easycomponents_presentation

Docker: sFTP-Server für schnelle Tests

Manchmal muss den Upload von Dateien testen. Nicht immer nutzt man etwas wie S3 sondern oft auch noch ganz klassische Umgebungen mit SCP oder sFTP. Wenn man schnell und unkompliziert den Upload auf einen sFTP-Server testen will, kann sich so einen ganz schnell und einfach mit Docker erzeugen.


docker run -p 2222:22 -d atmoz/sftp foo:pass:::upload

https://github.com/fixate/docker-sftp

man kann sich dann auf localhost:2222 mit dem Benutzer foo und dem Passwort pass verbinden. Für Dateien steht das Upload-Verzeichnis bereit. Wenn man ihn nicht mehr braucht, wirft man den Container einfach weg und kann das nächste mal sauber mit einem neuen starten.

Wann ist eine Software "unwartbar"?

Jeder kennt Software, die man nicht mehr anfassen möchte. Man sagt, die Software wäre nicht mehr wartbar. Aber was bedeutet das überhaupt?


Die Definition von Wahnsinn ist, immer wieder das Gleiche zu tun und andere Ergebnisse zu erwarten.

von irgendwem.. vielleicht Einstein

Wenn eine Software unwartbar wird, gilt etwas sehr ähnliches.


Die Definition von unwartbar ist, an zwei augenscheinlich gleich funktionierenden Stellen etwas zu ändern und bei beiden das selbe Ergebnis zu erwarten... das es dann aber nicht ist!

von Hannes Pries... nach einen entsprechenden Tag

Oft ist solche Software organisch gewachsen. Organisch gewachsen bedeutet in diesem Fall, dass man nie das grundlegende System erweitert und verbessert hat, sondern einfach neue Funktionen drum herum gebaut hat. Auch wird oft Code kopiert und im Laufe der Zeit immer nur die einzelnen Code-Blöcke angepasst, wo es nötig ist und die anderen so belassen. Besser wäre es gewesen, den Code auszulagern, so dass an allen Stellen die Änderungen greifen.

Oft sind es gar nicht die ganz alten Code-Teile der aller ersten Version, die die Probleme machen, weil diese noch einem Konzept folgten. Mit der Zeit ist das Konzept verloren gegangen und die damaligen Entwickler sind oft schon lange nicht mehr dabei. Es gibt natürlich keine Doku, Namespaces und eine besonders übersichtliche Sortierung von Klassen sind meistens nicht vorhanden, gab es zu der Zeit einfach nicht oder wurden durch die ganzen Erweiterungen selbst zerstört.
Mit der Zeit setzt sich die Annahme durch, dass der damalige Code einfach schlecht war und man es erneuern sollte, wenn man etwas neues programmiert. Das führt dazu, dass jeder das auch macht und plötzlich 3-4 Implementierungen für ein und das selbe gibt.
Dumm nur, dass auch alle diese Implementierungen auch immer verwendet werden und so sich niemand traut eine der Implementierungen abzuschalten, weil es nicht der eigene Code ist und man sich nicht sicher ist, ob es wirklich 100%ig den selben Funktionsumfang hat.

Aber irgendwann muss man daran und dann baut man einfach etwas wieder ran und entfernt nicht einfach die Implementierung und nutzt die aktuellste.

Lustig ist auch, dass man oft gegen Ende der Lebenszeit einer Software, wenn man die Strukturen für einen Rewrite analysiert, feststellt, was das grundlegende alte Framework schon alles konnte und vieles was man später mit viel Aufwand eingebaut hat, schon lange da war. Oft auch sogar in einer gar nicht so schlechten Implementierung.

Jeder hat bei so etwas genau so mitgemacht, oder? Zeitdruck... neu bauen und nicht alten Code lesen. Wirklich mal mysqli-Funktionen gegen PDO ersetzen? Wäre toll, aber wer will sich wirklich die Arbeit machen, weil es ja doch alles irgendwie funktioniert. Das System verwendet viele PHP-Dateien und keine zentrale index.php? Kann man ja schnell bauen.. macht man aber nie und muss in vielen Dateien oft die selben Dinge ändern.

Was ist die Lösung? Sich klar werden, dass es nichts mehr bringt und das System sauber neu schreiben. Nicht für sich (naja.. doch irgendwie schon), sondern für andere Entwickler. Es muss ein System sein, wo man von ausgeht, dass es für jeden Fall eine Lösung mitbringt. Das System wird den selben Weg gehen und man muss versuchen schon ganz am Anfang anfangen gegen zu steuern auch noch kein anderer daran mit arbeitet.


Keep it simple! Keep it clean!

hat das schon mal wer anderes gesagt? Bestimmt!

Wenig Code, klare Interfaces mit 100%ig aussagekräftige Namen. Die Codevervollständigung ist die Suche nach der richtigen Klasse, also müssen alle Klassen richtig benannt sein.

Es ist alle schwerer als man denkt, aber Frameworks und Standards wie PSR bei PHP helfen da sehr. Sich einfach auf den Stil und die Konventionen des Frameworks einlassen und sich aneignen. Dann klappt es schon. Auch wenn es schwer ist.

Spring Boot mit HTTPS: schnelle Lösung

Gerade wenn man Apps (PWAs) entwickelt, die zwingend HTTPS brauchen, weil man auf Dinge wie die interne Kamera des Geräts zugreifen möchte, kommt man zu dem Problem auch die Middleware mit HTTPS betreiben zu müssen. Kein Browser mag Mixed-Content. Wenn die Middleware mit Spring Boot geschrieben ist, kommt man aber auch da ziemlich schnell und einfach zu einer Lösung.
Das funktioniert sogar ohne komplexe Keystore Aktionen und irgendwelchen Signieren von gebauten Artefakten.

Die einfachste Lösung ist sich ein PKCS#12-Certificate (als das als Datei, PKCS#11 ist das von den Smartcards) zu erzeugen und direkt als Keystore zu nutzen.


keytool -genkeypair -alias pkcs12test -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore pkcs12test.p12 -validity 3650


Neben dem Alias sollte man auch den Dateinamen der p12-Datei anpassen. Danach muss man viele Daten angeben (die man sonst in openssl in der Conf-Datei ablegen würde). Die p12-Datei kann man nun einfach im resources-Bereich im Default-Package ablegen (bzw. auch in jedem anderen Package, wo es per getResource erreichbar ist).

Danach muss man die Daten nur noch in der application.properties eintragen.


server.ssl.key-store-type=PKCS12
server.ssl.key-store=classpath:pkcs12test.p12
server.ssl.key-store-password=your_password
server.ssl.key-alias=pkcs12test


Danach sollte Spring Boot wie vorher auf dem konfigurierten Port starten, aber ab jetzt nur noch per HTTPS erreichbar sein.

Das Certificate ist natürlich nur self-signed und eignet sich nur für Dev-Umgebungen. Bevor die PWA auf die Middleware zu greifen kann, muss man mit dem Browser die Middleware einmal aufrufen und den Zugriff für den Browser erlauben. Danach sollte es dann gehen.

Die ausführlichere Anleitung findet man hier:
https://www.baeldung.com/spring-boot-https-self-signed-certificate

Older posts:

Möchtest Du AdSense-Werbung erlauben und mir damit helfen die laufenden Kosten des Blogs tragen zu können?