CodeReady Containers for OKD unter Windows 10

Die Firma RedHat bietet mit Openshift eine sehr umfassende Kubernetes Distribution. Bei OKD handelt es sich um das entsprechende Open Source Upstream-Projekt. Für Entwickler-Teams oder den Einsatz in der Produktion wird Openshift normalerweise in einem Cluster aus mehreren Server-Knoten oder VMs installiert. Für die Entwicklung von Images, Templates, Kubernetes-Operatoren oder um sich mit Openshift vertraut zu machen, ist eine kleine lokale Mini-Installation von Vorteil. Früher gab es hierfür z.B. minishift oder minikube. Für Openshift 4 wurde diese Form von Umgebung in "CodeReady Containers" (CRC) umbenannt.

Die Installationsanleitung für Linux, Mac OS und Windows befindet sich hier: Getting Started Guide und der OKD-Download der CRC-Umgebung hier: Download CodeReady Containers for OKD.

Zusätzlich zur Installationsanleitung sollten unter Windows 10 folgende Punkte beachtet werden:

  • Das Windows 10 System sollte mindestens 16 GB RAM haben, besser 32 GB. Bei 16 GB RAM müssen evtl. Anwendungen und Dienste vor dem Start der CRC-Umgebung gestoppt werden.
  • Der Installationsbenutzer muss zu einem lokalen Administrator werden können und zusätzlich Mitglied der Gruppe "Hyper-V-Administratoren" sein.

Installation und Start sind nach dem Download, in einer klassichen Windows Eingabeaufforderung, wie folgt möglich:

crc setup
crc start -n 8.8.8.8

Bei Fehlern kann der Debug-Output über die Zusatzoption "--log-level debug" aktiviert werden.

Für erste Tests der Umgebung kann z.B. mein Demojee auf Github genutzt werden:
https://github.com/FrankBergerITServices/openshift-jee-sample

Die Corona-Warn-App - Das cwa-server Backend in einer Docker Umgebung

Da der Code der Corona-Warn-App nun komplett auf GitHub veröffentlicht wurde, ergibt sich nun für uns die Chance Möglichkeiten und Probleme in der Software-Entwicklung an einem sehr aktuellen Projekt live mitzuverfolgen. Mir ist bewusst, dass Tracking-Apps politisch und ethisch enormes Konfliktpotenzial bieten. Meiner Meinung nach wird der Nutzen derartiger Apps stark überschätzt und die entstehenden ethischen Probleme leider ignoriert. Erfolg oder Misserfolg werden sich wohl erst hinterher genau mit Fakten belegen lassen. Es gibt also einiges zu lernen! Im nachfolgenden Post möchte ich Euch zeigen wie Ihr das cwa-server Backend der App in einer Docker Umgebung aufsetzen könnt. Danach werden wir mit einem kleinen Python-Script einen Submission-Key an das Backend schicken und das Ergebnis in der Datenbank überprüfen.

CWA – Das cwa-server Backend in einer Docker Umgebung

[Durch Click auf die Play-Schaltfläche erfolgt ein Verbindungsaufbau zu YouTube/Google und es werden Daten an an diese externen Dienste übertragen]

Um Euch lokal eine passende Entwicklungs-Umgebung aufzubauen empfehle ich folgende Werkzeuge:

Sobald Ihr die Tools installiert habt, könnt Ihr den Code auch schon von GitHub clonen und die Umgebung mit Docker-Compose aufbauen lassen:

mkdir -p /c/cwa
cd /c/cwa
git clone https://github.com/corona-warn-app/cwa-server.git
cd cwa-server
docker-compose build
docker-compose up

Sobald die Docker Container laufen könnt Ihr z.B. mit einem eigenen kleinen Python-Client auf das Backend zugreifen. Der HTTP Body des POST-Request besteht in diesem Fall leider nicht aus klassischem Text oder Daten im JSON-Format. Für die API wird Protobuf von Google genutzt, dabei handelt es sich um ein schlankes binär Format. Für unseren Python-Client müssen wir zunächst mit den .proto-Files aus dem Source Code und dem Protobuf-Compiler von Google passende Python-Klassen bauen. Bei dieser Gelegenheit installieren wir auch gleich noch das Protobuf-Binding für Python. Evtl. müsst Ihr noch die Pfade an Eure Umgebung anpassen:

pip install protobuf
mkdir -p /c/cwa/cwa-client
cd /c/cwa/cwa-client
/c/tools/protoc/bin/protoc.exe --python_out=/c/cwa/cwa-client \
 --proto_path=/c/cwa/cwa-server/common/protocols/src/main/proto \
 app/coronawarn/server/common/protocols/external/exposurenotification/temporary_exposure_key_export.proto \
 app/coronawarn/server/common/protocols/internal/submission_payload.proto

Damit müsstet Ihr dann folgenden Python-Client ausführen können:

import http.client
from datetime import datetime, timedelta
from app.coronawarn.server.common.protocols.internal.submission_payload_pb2 import SubmissionPayload

submission = SubmissionPayload()
key = submission.keys.add()
key.key_data = bytes("FranksDemoKey222", "utf-8")
key.rolling_start_interval_number = int(((datetime.today() - timedelta(days=14)).timestamp())/600)
key.rolling_period = 144
key.transmission_risk_level = 4

conn = http.client.HTTPConnection('localhost', 8000)
headers = {'Content-Type': 'application/x-protobuf',
           'cwa-fake': '0',
           'cwa-authorization': 'edc07f08-a1aa-11ea-bb37-0242ac130002'}
conn.request("POST", "/version/v1/diagnosis-keys", submission.SerializeToString(), headers)
response = conn.getresponse()
print(response.status, response.reason)

Um auf die Postgres Datenbank zugreifen zu können, haben uns die CWA-Entwickler pgAdmin als Web-Oberfläche in einem eigenen Container mitgeliefert. Ihr müsst dort noch die Datenbank-Verbindung einrichten. Danach könnt Ihr mit folgendem SQL Statement prüfen ob unser POST-Request zu einem Eintrag in der Datenbank geführt hat:

SELECT encode(key_data, 'escape')
     , rolling_period
	 , rolling_start_interval_number
	 , submission_timestamp
	 , transmission_risk_level
	FROM public.diagnosis_key
	WHERE key_data like 'FranksDemoKey%';

Oracle DataPump via PL/SQL - DBMS_DATAPUMP

Oracle DataPump hat sich bei vielen Anwendern aufgrund seiner Vorteile als Import/Export-Werkzeug durchgesetzt. Neben den Werkzeugen impdp und expdp lässt sich die DataPump auch via PL/SQL steuern. Hierzu dient das Package DBMS_DATAPUMP. Die Verwendung für einen Schema-Export zeigt folgendes Beispiel:

DECLARE
  v_DPHandle_n      NUMBER;
  v_TimeStamp_vc    VARCHAR2(15);
  v_JobName_vc      VARCHAR2(30);
  v_DumpFilename_vc VARCHAR2(50);
  v_LogFilename_vc  VARCHAR2(50);
  v_JobStatus_vc    VARCHAR2(50);
  v_SchemaName_vc   VARCHAR2(8)  := 'SCOTT';
  
  -- dieses Oracle-Verzeichnis muss evtl. vorher angelegt werden
  --
  -- CREATE OR REPLACE DIRECTORY DPUMP_TEST_DIR AS '/tmp';
  --
  -- evtl. muessen auch Schreib-/Lese-Rechte gesetzt werden
  --
  -- GRANT READ, WRITE ON DIRECTORY DPUMP_TEST_DIR TO orauser;
  --
  v_DumpDir_vc      VARCHAR2(20) := 'DPUMP_TEST_DIR';
BEGIN
  v_TimeStamp_vc    := TO_CHAR(sysdate, 'YYYYMMDD-HH24MISS');
  v_JobName_vc      := v_TimeStamp_vc || '_' || v_SchemaName_vc;
  
  -- wichtig ist der Platzhalter %U, je nach Parallelitaet werden so
  -- mehrere Dump-Files erzeugt
  v_DumpFilename_vc := v_SchemaName_vc || '_' || v_TimeStamp_vc || '_%U.dmp';
  v_LogFilename_vc  :=  v_SchemaName_vc || '_' || v_TimeStamp_vc || '.log';
    
  -- Datapump-Job oeffnen  
  v_DPHandle_n := DBMS_DATAPUMP.open(
    operation   => 'EXPORT',
    job_mode    => 'SCHEMA',
    job_name    => v_JobName_vc
  );

  -- Dump-File fuer den Job definieren
  DBMS_DATAPUMP.add_file(
    handle    => v_DPHandle_n,
    filename  => v_DumpFilename_vc,
    directory => v_DumpDir_vc
  );

  -- Log-File definieren
  DBMS_DATAPUMP.add_file(
    handle    => v_DPHandle_n,
    filename  => v_LogFilename_vc,
    directory => v_DumpDir_vc,
    filetype  => DBMS_DATAPUMP.KU$_FILE_TYPE_LOG_FILE
  );

  -- welches Schema soll exportiert werden?
  DBMS_DATAPUMP.metadata_filter(
    handle => v_DPHandle_n,
    name   => 'SCHEMA_EXPR',
    value  => '= ''' || v_SchemaName_vc || ''''
  );
    
  -- Parallelitaet auf 4 setzen  
  DBMS_DATAPUMP.SET_PARALLEL(
    handle => v_DPHandle_n,
    degree => 4
  );
    
  -- eigene Eintraege im Log-File sind moeglich
  DBMS_DATAPUMP.LOG_ENTRY(
    handle => v_DPHandle_n,
    message => '---> Schema Export via PL/SQL'
  );

  -- Job starten
  DBMS_DATAPUMP.start_job(v_DPHandle_n);
 
  -- auf das Job Ende wird normalerweise nicht gewartet
  -- mit WAIT_FOR_JOB laesst sich dies aber bei Bedarf
  -- realisieren
  DBMS_DATAPUMP.WAIT_FOR_JOB(
    handle => v_DPHandle_n,
    job_state => v_JobStatus_vc
  );
  
END;
/

Ein allgemeiner Hinweis zu Oracle DataPump:
DataPump-Prozesse laufen immer auf dem Oracle-Server!
Dies hat in der Praxis folgende Auswirkungen:

  • Der Betriebssystem-Benutzer, dem die Oracle-Server Prozesse zugeordnet sind, braucht Schreib-Rechte auf dem gewünschten Export-Verzeichnis (läuft die Datenbank-Instanz als Windows-Dienst und dem Benutzer LocalSystem ist kein Schreiben auf Netzwerk-Shares möglich, da dieser Benutzer keine Netzwerk-Shares sieht)
  • Es gibt keinen Client-/Server-Modus (z.B. Export eines Schemas von einem entfernten Oracle Server über einen lokalen Oracle Client)

Links for Oracle Application Express

Mario a colleague of mine is starting to work on an project to extend an PL/SQL-based web application - so I thought it would be a good idea to give him a view links for Oracle Application Express - Que se divierta con ella.

Sometimes customers have the need for an ad hoc web development tool, especially when an PHP- or Java-based approach seams to be oversized. One solution provided from Oracle is called Application Express (APEX) - the former name was HTML-DB.

To get an first impression from APEX I recommend to view the APEX Quick Tour at the Oracle website.

The Official Home Page for Oracle Application Express can be found here.

To get you started with APEX and AJAX I have to links for you:

Well and for Charting you have a lot of options too:

Reading Excel Files with Perl

Sometimes you need to read data from Excel files with Perl. There are several Perl Modules (Spreadsheet::ParseExcel, Spreadsheet::ParseExcel::Simple) that make that job quite easy. Spreadsheet::ParseExcel::Simple is an lightweight abstraction of Spreadsheet::ParseExcel.

Sample Code:

#!/usr/bin/perl -w

use Spreadsheet::ParseExcel;

my $parser = new Spreadsheet::ParseExcel();
my $xls = $parser->Parse("PerlExcelTest.xls")
  or die "Can't read File!";

# Loop over all tables in Excel file
for(my $i = 0; $i < $xls->{SheetCount}; $i++) {
  $sheet = $xls->{Worksheet}[$i];

  print "\nSheet: " . $sheet->{Name} . "\n";

  # has the current sheet cells?
  next unless (exists ($sheet->{MaxRow}) and (exists ($sheet->{MaxCol})));

  # loop over the row of the current sheet
  foreach my $row ($sheet->{MinRow} .. $sheet->{MaxRow}) {
    
    # loop over the cells in the current row
    foreach my $column ($sheet->{MinCol} .. $sheet->{MaxCol}) {
      next unless (defined $sheet->{Cells}[$row][$column]);

      print $sheet->{Cells}[$row][$column]->Value . ' | ';
    }
    print "\n";
  }
}

During my tests I came across several issues:

  • If you create Excel files with OpenOffice numeric values show up as string GENERAL. There is a patch for that problem. Please note also that you could use $cell->Val instead of $cell->Value.
  • I also have problems to extract date values from the excel file. In that case I am not able to determine the type of a date cell. Normally I should see Date but I turns out to be Numeric.