Blog: Latest Entries (15):


PHP API Platform: Speed run

Die Zeiten wo man eine API mit:

echo json_encode($result);
die();

in einem einfachen Controller implementierte und eher ein Beiwerk waren sind schon länger vorbei. Heute baut große Clients mit Vue.js oder React und PHP liefert keinen HTML-Code mehr aus sondern implementiert nur noch eine reine REST-API.

Bei REST-APIs mit klassischen Resources und GET, PUT, POST, PATCH, DELETE kann man alles sehr gut verallgemeinern, so dass ein Framework kaum noch Anpassungen benötigen, um eine lauffähige API bereit stellen zu können. Eines dieser Frameworks ist das in PHP implementierte API Platform. Zusammen mit Symfony 4 kommt noch schneller zu einer REST-API als mit
Spring-Boot oder Meecrowave.

Ich skizziere hier einen kleinen Speed run, der aufzeigt wie man sehr schnell eine recht komplexe REST-API implementieren kann.
Nicht alles wird für alle nötig sein, aber es gibt einen guten Überblick für eigene Projekte. Ich geh von einem vorhandenen Symfony 4
Projekt aus, in das API Platform mit integriert wird.

1. Installation:

composer require api


2. Die Resource:
Im Grunde nimmt man eine Doctrine-Entity und erweitert die um 2 Annotationen (ApiResource und ApiProperty für die Id).


namespace hp\examples\api\entity;

use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;

/**
* Example-Resource
*
* @ORM\Entity
* @ORM\Table(name="blubb")
*
* @ApiResource(
* )
*/
class Blubb {
/**
* @var int
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*
* @ApiProperty(
* identifier=true
* )
*/
private $id;

/**
* @var string
*/
private $value = ''

....
....
....
}


Den Rest der Entity wie einen argumentlosen Constructor und die Getter/Setter wird sicher jeder selbst schnellst erzeugen können.

Eine Migration oder die Table per Doctrine zu erzeugen kann sicher auch jeder selber und braucht hier nicht weiter ausgeführt werden.

3. Pfad zu den Resourcen:

Hier passen wir das Paths Field in config/packages/api_platform.yaml an:

api_platform:
mapping:
paths: ['%kernel.project_dir%/src/examples/api/entity']
patch_formats:
json: ['application/merge-patch+json']
swagger:
versions: [3]


Nun weiß API Platform, wo es nach den Resources suchen soll.

4. URL-Prefix anpassen:
In der config/routes/api_platform.yaml kann noch schnell ein Prefix für die API bzw den API-Controller gesetzt werden.


api_platform:
resource: .
type: api_platform
prefix: /api/examples/


5. Starten und Testen:

Nun kann die Anwendung auch schon gestartet werden, wenn sie nicht schon ist. Also in meinem Fall den Docker-Container starten, der einen Apache auf dem Port 8080 öffnet.

Ob die Routen vorhanden sind prüfen wir mit:

php bin/console debug:router


Wenn die Routen angezeigt werden kann http://localhost:8080/api/examples/docs.json aufgerufen werden und hier sollten alle Pfade zu der Resource aufgelistet sein. Sollte die Route nicht gefunden werden hilft ein php bin/console cache:clear. Um die Resource wirklich auch testen zu können, findet man eine Swagger-Umgebung unter http://localhost:8080/api/examples/blubbs.html. Wie man sieht hängt ein 's' am dem Namen der Klasse und der Name ist auch nicht in Camelcase geschrieben. Wenn man die JSON-Ausgabe sehen möchte muss man nur ../blubbs.json anstelle der HTML-Variante aufrufen.

6. DTOs und Delegations
Nicht immer will man die Entity so raus reichen, wie sie ist. Auch will man vielleicht Objekte durch einfache Strings ersetzen. Auch beim Anlegen will man vielleicht Business-Keys und keine echten Ids nutzen, die aber in der Ausgabe dann doch zusätzlich mit angezeigt werden sollen.
Dafür kann man bei API-Platform DataTransformer und Stellvertreter-Objekte definieren. Die DataTransformer muss man nur anlegen und werden durch das autowire von Symfony direkt auch schon verwendet.

Ein einfaches Beispiel:

BlubbIn

namespace hp\examples\api\entity;

class BlubbIn {
public $value;
public $logComment;
}


BlubbOut

namespace hp\examples\api\entity;

class BlubbOut {
public $id;
public $value;
public $md5sum;
}


Nun brauchen wir auch zwei DataTransformer:

Incoming

namespace hp\examples\api\entity\transformer;

use ApiPlatform\Core\DataTransformer\DataTransformerInterface;

class BlubbInTransformer implements DataTransformerInterface {
public function transform($object, string $to, array $context = []) {
$entity = new Blubb();
$entity->setValue($object->value);

\hp\Logger::log($object->logComment);
return $entity;
}

public function supportsTransformation($data, string $to, array $context = []): bool
{
return $to === Blubb::class && null !== ($context['input']['class'] ?? null);
}
}


outgoing

namespace hp\examples\api\entity\transformer;

use ApiPlatform\Core\DataTransformer\DataTransformerInterface;

class BlubbOutTransformer implements DataTransformerInterface {
public function transform($object, string $to, array $context = []) {
$dto = new BlubbOut();
$dto->id = $object->getId();
$dto->value = $object->getValue();
$dto->md5sum = md5($object->getValue() . $object->getId());
return $dto;
}

public function supportsTransformation($data, string $to, array $context = []): bool
{
return $to === BlubbOut::class && $data instanceof Blubb;
}
}


Die beiden DTOs müssen noch an der Resource referenziert werden:

...

/**
* Example-Resource
*
* @ORM\Entity
* @ORM\Table(name="blubb")
*
* @ApiResource(
* input=hp\examples\api\entity\BlubbIn::class,
* output=hp\examples\api\entity\BlubbOut::class
* )
*/

...


Nun nutzt man das BlubbIn-Format um etwas an die API zu senden, es wird als die Entity in de DB gespeichert und als Rückgabe sieht die Daten im Format von BlubbOut. Dieses ist etwas, was ich bei vielen Frameworks immer vermisst habe... Object-Replacements wie beim Serialisieren unter Java mit writeReplace. Gerade bei REST-APIs ist so etwas extrem praktisch und man kann so auch aus einer Entity verschiedene Domain-abhängige (Bounded-Context) DTOs für verschiedene API-Endpoints erzeugen.

7. Authorization und Security
Klar sind einfache API-Keys die man im Request mitgibt nicht die beste oder finale Lösung, aber sie sind so schön einfach. In API Platform lässt sich so etwas relativ einfach implementieren. Dafür braucht man Symfony Voters, die mich etwas an die Realms aus Tomcat erinnern. Man reicht eine Role rein und die Logik liefert einfach true oder false zurück. Das Beispiel ist nicht sehr fein-granular, aber sollte reichen, um die Grundidee zu vermitteln.

Die Resource schützen:

...

/**
* Example-Resource
*
* @ORM\Entity
* @ORM\Table(name="blubb")
*
* @ApiResource(
* attributes={"security"="is_granted('EXAMPLE_API_ACTION')"},
* input=hp\examples\api\entity\BlubbIn::class,
* output=hp\examples\api\entity\BlubbOut::class
* )
*/

...


Eine ausführlichere Doku mit der Verwendung des Object findet man hier bei API Platform.

Den Voter zu implementieren ist an sich genau so einfach, wie einen DataTransformer zu implementieren und funktioniert genau nach der selben Logik, die ich auf die Art auch immer gerne implementiere.


namespace namespace hp\examples\api\security;

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;

class ExampleAPIVoter extends Voter {

...
...
...

protected function supports($attribute, $subject) {
return $attribute == 'EXAMPLE_API_ACTION';
}

protected function voteOnAttribute($attribute, $subject, TokenInterface $token) {
return $this->apiKey == $this->getUserRequestApiKey();
}
}


Mit autowire muss man auch nicht groß etwas in die services.yaml eintragen, wenn man nichts aus der env-config oder
so braucht.

Feature-Flags: Neue Features, neue Versionen und alles beim Alten

Gerade bei den Shopware Plugins war es wichtig, dass sich die Funktionsweise eines Plugins nicht einfach ändert. Ich hab erlebt was passieren kann, wenn sich bei einen Newsletter-Plugin in einer Minor-Version plötzlich das Verhalten änderte. Es wurden plötzlich alle Kunden mit Gruppe übertragen und nicht mehr nur die einer bestimmten Gruppe und weil niemand sich die Änderungen groß angesehen hat, bekamen plötzlich alle den Newsletter. Das war doof.

Kaum jemand liest den Changelog oder nur selektiv nach bestimmten Fixes. Wenn ich als Shopware-Betreiber einen Fix für ein Plugin brauche, habe ich meistens keine Zeit jedes Verhalten und jede Funktion des Plugins nochmal durch zu testen. Ich will nur mein Problem beheben und keine neuen bekommen. Jedes neue Feature muss also in den default Einstellungen erst einmal deaktiviert sein. Nur Fixes dürfen ungefragt greifen.

Funktionen im Beta-Stadium dürfen auch nicht aktivierbar sein, wenn sie nicht stable sind. Unfertiger-Code darf mit ausgeliefert werden, wenn es nicht anders geht! ABER er muss auch hart im Code deaktiviert sein. Oder auch im Build-Prozess aus der Config-XML auskommentiert werden. Da muss dann aber sichergestellt sein, dass geprüft wird, ob das Config-Value überhaupt gesetzt ist und wenn nicht, dass es als deaktiviert gilt.
Aber wenn es nicht zwingend nötig ist, sollte man unfertige Features nie mit ausliefern und per Feature-Flag deaktivieren. Am Ende wird es sicher noch mal überarbeitet und es funktioniert dann anders. Weglassen minimiert das Risiko auf Fehler und dass irgendwer es doch aktiviert und nutzt.

Fazit: Feature-Flags sind wichtig, aber nicht dafür da um unfertige Features mit ausliefern zu können (ein Feature pro Feature-Branch!!!!)


i don't like Release-Branches

Ich halte Release-Branches für sehr problematisch. Manchmal können sie nützlich sein, aber in 99% aller Fälle sind sie überflüssig und bringen mehr Probleme mit sich. Nur wenn wirklich mehrere von einander abhängige Features gleichzeitig entwickelt werden wäre ein Release-Branch denkbar. Ob der dann auch wirklich einen Vorteil bringt, muss jeder dann für sich entscheiden.

Das Problem bei Release-Branches ist, dass alles was release-bereit ist auch da rein kommt. Schnell hat man das Problem, dass man seinen kleinen Fix da rein merged und feststellt, dass 3 weitere Features da drin liegen. Sie sind ja release-bereit, aber ein Release war noch nicht wirklich nötig. Entweder ändern die was, wo man die Benutzer noch instruieren muss oder sie sind nicht so wichtig man hat den Release auf den Ende des Sprints geschoben. Jetzt steht man da und muss erst mal von allen anderen die Info einholen, ob man die Features mit seinem Fix mit releasen kann oder man jetzt doch einen eigenen
Release-Branch aufmachen sollte.


Bloß weil etwas release-fertig ist, heißt es nicht dass ein Release möglich ist!


Die meisten Features und Fixes stehen für sich selbst und sie in einen gemeinsamen Release-Branch zu bringen blockiert schnelle Releases einzelner Features. Für jeden Feature-Branch einen eigenen Release-Branch auf zu machen ist aber auch nicht sinnvoll. Der Feature-Branch sollte meiner Meinung nach auch sein eigener Release-Branch sein... also man braucht nur den Feature-Branch.

FALSCH:
bbcode-image
blocking release-branch.. nicht gut


RICHTIG:
bbcode-image


Dieser Post fußt auf meinen Post Git State Konzept.

Summernote: Image Resizing

Das direkte Einbinden als Base64 Data-URL ist echt praktisch, aber man will ja nicht, dass die Benutzer nun endlos riesige Bilder darüber einbinden. Deswegen ist es eine gute Idee, die Bilder beim Einfügen auch gleich zu verkleinern. Das ist zum Glück mit dem Canvas-Element sehr einfach.

Die Callback-Function:

let onImageUploadFunc = function (elementId) {
return function(image) {
resizeImage(image[0], elementId);
}
};

function resizeImage(file, elementId) {
let image = new Image();
let url = window.URL ? window.URL : window.webkitURL;

image.src = url.createObjectURL(file);
image.onload = function (e) {
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");

let width = 600;
let factor = image.width / width;
let height = image.height / factor;

if(image.height > image.width) {
height = 300;
factor = image.height / height;
width = image.width / factor;
}

canvas.width = width;
canvas.height = height;

ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
$(elementId).summernote('editor.insertImage', canvas.toDataURL('jpeg', 0.7));
};
}


Verwendung:

$('#editSolution').summernote(
{callbacks:{onImageUpload: onImageUploadFunc('#editSolution')}}
);


bbcode-image

Release-unrelevante Änderungen direkt in den Master?

Nur ein kleiner Fix an der README.md oder ein Kommentar in der docker-compose.yml? Braucht es einen feature-Branch? An sich sind solche Änderungen es ja nicht wert die Versionsnummer zu erhöhen, weil sich am releasten Code ja nichts ändert. Aber das einfach so in den Master commiten, obwohl darin nur Abbildungen von release-Tags als Commits zu finden sollten (nach dem ersten Release)?

Das Release als lauffähiges Artefakt eines Dev-Standes würde sagen: Ja, es sollte wie eine Code-Änderung behandelt werden. Eine Version bezieht sich nicht nur auf das fertige Artefakt sondern auf auf dessen Sources aus denen das Artefakt erstellt wurde. Eine falsche Angabe in der README.md ist genau so ein Fehler wie ein Fehler im Code und könnte später zu Missverständnissen und nicht lauffähigen Artefakten führen.

bbcode-image



Also jeder Fix und jede Verbesserung soll einen eigenen Branch und einen Release-Tag erhalten.


Meine Erfahrungen aus Shopware-Plugin Zeiten sagt dann eher, dass man nicht jeden Release auch wirklich releasen muss. Wenn 0.3.2 im Vergleich zu 0.3.1 keine Verbesserung für den Enduser bringt, muss kein öffentlicher/public Release erfolgen sondern es bleibt ein privater Release, der für die Developers gedacht ist.

Docker: Test SSH-Server

Manchmal muss man z.B. das Kopieren von Dateien auf einen Server per SCP testen. Oder auch einfache Deployments auf einem Server. Hier ist ein kleines SSH-Server Image mit Bash und Rsync.


FROM sickp/alpine-sshd:7.5
RUN apk update
RUN apk add bash
RUN apk add rsync


Und in einer docker-compose.yml

version: "3.0"

services:
ssh-server:
build: .
ports:
- "2222:22"


User: root
Password: root

Man kann aber auch authorized-keys hinterlegen, wie auf der Seite des Base-Images erklärt wird.

Ist es ok mit einem dreckigen Master zu starten?

Ist es okay mit einem dreckigen Master-Branch zu starten?


Meiner Meinung nach ist es vollkommen ok. Ein leeres Projekt als stable zu deklarieren macht keinen Sinn und ist in den meisten Fällen auch nicht deploybar, da z.B. eine build.xml oder .gitlab-ci.yml noch fehlen.

bbcode-image
Master mit dirty Commit vor dem ersten Release


Ich vertrete die Meinung, dass der Master erst nach dem ersten Release stable gehalten werden muss. Es folgt der Regel, dass im Master der letzte nutzbare Stand liegt. Wenn kein Release vorhanden ist, ist der letzte Commit der Dev-Version, die nutzbarste Version die man finden kann. In dem Sinne bemühe ich bei der Regel eine Art Fallback auf die Dev-Version, für die Zeit wo kein Release existiert. Nach dem ersten Release ist immer das letzte Release die nutzbarste Version.

Es gibt also immer eine nutzbare Version und nie "Nichts".

Dieser Post fußt auf meinen Post Git State Konzept.

WSL und Vue Probleme

Wenn ich in Ubuntu unter meinem Windows 10 Node.js und NPM installiere macht es nur Probleme. "npm install" beschwert sich dass 'in' unbekannt sei.

Momentan läuft es bei mir so am Besten:


curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get update && sudo apt-get install -y nodejs


Dann funktioniert auch die Vue-CLI Installation:


npm install -g @vue/cli
npm install -g @vue/cli-service-global


Unter Windows direkt... da war alles noch schlimmer.

CoreUI mit JSON-Schema

Eine Admin-UI lebt nicht nur von Tables sondern auch von sehr vielen Forms. Die möchte man ja nur ungern komplett per Hand bauen sondern etwas wie JSON-Schema benutzen, da man das auch auf Server-Seite für die Validierung nutzen kann.

CoreUI und vue-form-generator arbeiten gut zusammen.

Man muss vue-form-generator nur per NPM installieren.

bbcode-image



<template>
<CRow>
<CCol col="12" md="6">
<CCard>
<CCardHeader>
JSON-Schema Form
</CCardHeader>
<CCardBody>
<vue-form-generator :schema="schema" :model="model" :options="formOptions">
</vue-form-generator>
</CCardBody>
<CCardFooter>
<CButton block color="primary">Submit</CButton>
</CCardFooter>
</CCard>
</CCol>
</CRow>
</template>

<script>
import Vue from 'vue'
import VueFormGenerator from 'vue-form-generator'

Vue.use(VueFormGenerator);

export default {
name: 'Alerts2',
data () {
return {
model: {
id: 1,
name: 'John Doe',
password: 'J0hnD03!x4',
skills: ['Javascript', 'VueJS'],
email: 'john.doe@gmail.com',
status: true
},
schema: {
fields: [
{
type: 'input',
inputType: 'text',
label: 'ID (disabled text field)',
model: 'id',
readonly: true,
disabled: true
},
{
type: 'input',
inputType: 'text',
label: 'Name',
model: 'name',
placeholder: 'Your name',
featured: true,
required: true
},
{
type: 'input',
inputType: 'password',
label: 'Password',
model: 'password',
min: 6,
required: true,
hint: 'Minimum 6 characters',
validator: VueFormGenerator.validators.string
},
{
type: 'select',
label: 'Skills',
model: 'skills',
values: ['Javascript', 'VueJS', 'CSS3', 'HTML5']
},
{
type: 'input',
inputType: 'email',
label: 'E-mail',
model: 'email',
placeholder: 'User's e-mail address'
},
{
type: 'checkbox',
label: 'Status',
model: 'status',
default: true
}
]
},
formOptions: {
validateAfterLoad: true,
validateAfterChanged: true,
validateAsync: true
}
}
}
}
</script>


Ja das ist einfach das Beispiel, das ich kopiert habe.... aber es funktioniert!

bbcode-image

CoreUI: Ein Admin-Template für Vue

Schon als ich mit React arbeiten musste, war ich sehr von den vorgefertigten Admin-Templates angetan. Jetzt darf ich auch mal damit rumspielen und meine ersten Erfahrungen damit sind schon relativ positiv. Da ich mit meinem Job synchron bleiben wollte habe ich mit CoreUI angefangen.

Die Installation über GitHub ist einfach und schnell.

Um ein eigens View anzulegen muss man an sich drei Dinge tun.

1. Die View anlegen:

bbcode-image


bbcode-image


2. In router/index.js die Component importieren und in die Router-Config einfügen:

bbcode-image

bbcode-image


3. In containers/_nav.js einen Link für die Sidebar einfügen:

bbcode-image


4. Dann den Server starten:

npm run serve


Und unter http://localhost:8080 angucken.

bbcode-image


Geht an sich alles ganz einfach.

Git State Konzept

Am Freitag hat mir mein Kollege 2 Links zu Blogposts geschickt, die sich mit der Frage beschäftigen ob Git-Flow in der heutigen Zeit überhaupt noch funktioniert oder ob Git-Flow veraltet ist. Der 1. Blogpost zeigt erstmal nur Probleme auf und enthält keine Lösungen. Es passiert zu leicht das Release-Branches zu lange leben und dann darin selbst Entwicklung geschieht. Es dauert relativ lange bis eine Änderung durch die verschiedenen Branches im Master ankommen und am Schlimmsten ist noch, dass bei parallelen Entwicklungen ein nicht releaster Branch einen anderen aktuelleren, der einfach schneller war, blockiert.

Ja. Das ist jetzt nicht neu. Über diese Probleme habe ich 2009 schon im dem damaligen ERP-Team diskutiert (mein Gott waren wir damals schon modern...). Die Lösung hier ist einfach dass man harte Feature- und Code-Freezes braucht. Auch darf die Fachabteilung nicht erst im Release-Branch das erste Mal die neuen Features sehen. Ich habe es so erlebt. Dann kamen die neuen Anforderungen, Änderungen der gerade erst implementierten Features. Das soll aber so sein. Wenn das so ist braucht man aber auch noch das. Das ist falsch und muss so funktionieren... Alles Dinge die schon viel früher hätten klar sein müssen und erst dann hätte es zu einem Release-Branch kommen dürfen. Der Stand eines Feature-Branchs muss genau so auf einem System für Test und Abnahmen deploybar sein wie ein Release-Branch. Anders gesagt jeder Stand muss einfach immer präsentierbar sein!

Der 2. Blogpost brachte jetzt auch nicht wirklich neue Erkenntnisse, was am Ende der Author auch selbst schreibt.

Ich halte die Darstellung von Branches in parallelen Slots oder Lanes, die in dem Sinne ein Rennen um die Aktualität austragen für vollkommen falsch. Es darf auch nicht den develop-Branch oder den einen Release-Branch geben, der auch dann immer deckungsgleich mit dem Stand des Deployments auf einem System ist. In Zeiten von Docker und Reverse-Proxies zusammen mit Wildcard-Subdomains sind feste Systeme sowie so überholt. Jeder Branch kann ein System haben, auf dem Test, Abnahmen und Dokumentation stattfinden kann.
Das gilt auch für Tags. Branches sind variabel und ändern sich immer wieder. Tags sind statisch und damit perfekt für Zwecke, wo man kontrolliert bestimmte Stände deployen möchte. Tags persitieren einen bestimmten State/Zustand des gesamten Git-Repositories. Branches bilden einen State/Zustand aus der Sicht eines bestimmten Entwicklers oder eines bestimmten Features ab.

Deswegen halte ich feste Branches wie mit festen Aufgaben für komplett falsch. Ein Feature = ein Branch und am Ende steht ein Tag, der den gewünschten State/Zustand persitiert.

Ein einfacher Feature Branch:
bbcode-image


Ja es gibt noch einen Master-Branch, der aber in dem Sinne nur ein 2D Abbild des wilden mehr dimensionallen Feature Raum ist. Wenn wir jeden Feature-Branch als Vektor der auf das einzelne Feature/Tag als Ziel zeigt versteht, ist der Master einfach die Projektion aller Vektoren auf eine Fläche. Diese vereinfachte Projektion hilft Feature-Branches vor dem Release auf den aktuellen Stand (was andere Features und Fixes angeht) zu bringen.

Es gibt auch immer mal Abstimmungsprobleme bei Features, die auf einander aufbauen. Interfaces haben minimale Abweichungen oder ein kleiner Satz in der Dokumentation wurde falsch verstanden. Was also wenn ein Feature doch noch eine kleine Änderung braucht, weil Entwicklungen parallel liefen?

Beides wird gleichzeitig fertig (ein extra Release-Branch wäre möglich):
bbcode-image


Das Basis-Feature geht vorher live:
bbcode-image
Das bessere und flexiblere Vorgehen


Gibt es einen Unterschied zwischen Fixes und Features? Nein. Beides sind Improvements des aktuellen States/Zustands. Wenn einem in einem Feature-Branch ein allgemeines Problem auffällt, fixt man das Problem und merged den Master mit der neuen Version erneut in den Feature-Branch.

Zwischenzeitlicher Fix:
bbcode-image


Es ist an sich kein Unterschied zwischen einem Fix und einen weiteren Feature-Branch, außer dass der Fix-Branch sehr viel kurzlebiger ist und wohl weniger Commits enthält.

Der Master ist immer stable, weil nur Release-Tags darauf abgebildet sind.

Dieses Herangehen macht es sehr einfach jeden State/Zustand auf System abzubilden. Jeder Branch ist unter seinem Namen zu finden und Tags werden nach Typ auf Systeme gemappt.

Tag auf System:
- release-XXX auf das produktive System
- staging auf das Staging-System (1:1 Namenabbildung)
- demo1-n auch 1:1 per Namen abbilden

Bei staging muss man den Tag löschen und neu anlegen, so kann jeder Zustand auf dem Staging-System deployt werden oder besser gesagt, wird ein Deployment durchgeführt das dann unter der Staging-Domain erreichbar ist. Hier gibt es eine tolle Anleitung wie man solche Systeme mit Traefik oder Kubernetes ganz einfach bauen kann. Ich werde das aber vielleicht auch noch mal genauer beschreiben, wie ich es für gut halte.

Denn Branches halte ich persönlich es nicht für wert wo anders als lokal in einem Docker-Container zu laufen. Da kann ich um es der Fachabteilung lieber schnell einen Tag erstellen und diesen nach dem Input der Abteilung auch wieder unter selben Namen neu anlegen oder unter einen neuen wenn zwei mögliche Umsetzungen verglichen werden sollen (macht das mal mit Git-Flow!).

Edit: Ich habe die per Hand gezeichneten Diagramme durch vollständigere Diagramme, die per Software erstellt wurden, ausgetauscht.

Home Office

Arbeiten und nebenbei Folding At Home auf der Workstation laufen lassen.

MS Teams läuft wirklich nicht immer ganz so rund. Aber im Gegensatz zu den Berichten vieler anderer habe ich mit meiner kleinen 1Gbit Kabel Internetleitung und VPN keine Probleme. Also alles super so weit.

bbcode-image

Lenovo IdeaPad z570 und Win10

Bis jetzt hatte ich nie Probleme Windows 10 auf einem PC oder Notebook zu installieren, dass ursprünglich mit Vista, Win7 oder Win8 lief. Aber es gibt immer ein erstes mal. Hier bei handelte es sich um ein Lenovo IdeaPad z570, das ungefähr Jahrgang 2011/2012 ist. Es ist also noch gar nicht so alt. Ein Lenovo T61 aus dem Jahr 2007 und zwei T500 aus dem Jahr 2008 hatten keine Probleme mit Windows 10. Das z570 hatte auch nur ein Problem mit Windows 10: Es ging nicht aus. Auch bei einem Neustart fuhr es zwar runter, aber verblieb dann in einem undefinierten Zustand in dem die LEDs der Zusatztasten und des Power-Switch weiter leuchteten und der Lüfter voll aufdrehte.
Schon bei der Installation musste ich beim ersten Neustart das Notebook per Hand ausschalten. Als Windows 10 dann installiert war führte es dazu, dass sich das WLAN danach ausgeschaltet hatte und nicht wieder einzuschalten war. Windows verwies immer auf den Schalter am Gehäuse, aber der war eingeschaltet. Als erstes befürchtete ich, dass ich ein BIOS-Update machen müsste, aber nach vielen Herumprobieren und Versuchen, wich der Vermutung eine klare Sicherheit, dass es so sein musste.
Also BIOS-Update herunter geladen, gestartet und direkt in einen Bluescreen gelaufen. Selbst mit Adminrechten kam wieder der Bluescreen.
Zum Glück hatte ich die Festplatte mit Windows 7 noch daneben liegen (das mich auch direkt beim letzten Herunter fahren mit ein paar Updates beglückt hatte). Also die Festplatte wieder eingebaut.

bbcode-image


bbcode-image


Da merkte man erst mal wie schnell doch die neue SSD war, auch wenn es die billigste SSD war, die ich bei Amazon gefunden hatte. Windows bootete wieder und das Programm für das BIOS-Update lief sofort einmal sauber und schnell durch.

bbcode-image


Dann wieder Festplatte gegen die SSD getauscht. Windows 10 gestartet und wieder herunter gefahren. Die Lichter leuchteten, leuchteten und gingen dann doch aus. Der Lüfter stoppte.

Fazit: Immer Windows 7 für Notfälle weiter vorhalten und vielleicht mal ein BIOS-Update einplanen.

Das z570 war aber auch sonst schon leicht mitgenommen. Das Gehäuse des Lüfters war so eingedrückt, dass der Lüfter fest saß und alles extrem heiß wurde. Mit etwas Gewalt und biegen, war der Lüfter wieder frei zu bekommen und lief sogar direkt wieder. Ich war mir nicht sicher gewesen, dass er nicht doch dadurch kaputt gegangen war.

bbcode-image


Da ich ihn ja sowie so ausbauen musste, wurde auch gleich die total
eingetrocknete Wärmeleitpaste für CPU und GPU ersetzt und auch der Chipsatz bekam welche ab. Der hatte zwar eine Verbindung zum Kühlkörper der CPU aber selbst keine Wärmeleitpaste.

Noch die 2x 2GB gegen ein 8GB Modul ausgetauscht und alles lief so schnell, dass das Notebook sicher noch 3-4 Jahre ohne Probleme seine Aufgaben erledigen kann.

Mini-USB Kamera brauchbar machen

Kleine USB-Kameras sind sehr praktisch, wenn man mit ihnen 3D-Drucke überwachen, Bilderkennung oder auch kleine IoT-Projekte mit QR-Code Scans realisieren will.

bbcode-image


Ich hatte mir eine sehr kleine Kamera aus China bestellt, war aber mit der Bildqualität nie wirklich zufrieden. Das Bild war immer unscharf und Lichter bildeten immer unschöne Ränder um alles.

bbcode-image


Am Ende habe ich mir jetzt das Objektiv genauer angesehen und den IR-Filter endeckt, der einfach als kleine Plättchen hinten rauf geklebt war. Mit Hilfe eines feinen Schraubenziehers und viel Gewalt gelang es mir den IR-Filter zu entfernen/weg zu brechen.

bbcode-image


Nun ist zwar mein graue Jacke etwas lila, aber das Bild ist sehr scharf und und hell. mit einen IR720 Filter kann bestimmt gute Aufnahmen im IR-Bereich machen und bei Kunstlicht sind die leichten Verfärbungen sowie so kein Problem mehr.

Older posts:

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