In einem unserer Projekte verwenden wir seit langer Zeit OpenCms als Redaktionssystem, ergänzt um ein Backend zur Realisierung von Fachlogik. Da wir agil arbeiten, haben wir natürlich auch den Anspruch, agil zu testen. Erste Maßnahme in Sprint 1 des aktuellen Teilprojekts war demnach das Aufsetzen einer Umgebung für Continuous Integration. Die zugehörige User Story hatte den Namen „Als Tester möchte ich tagesaktuell testen können, um Fehler frühzeitig zu finden“. Im Folgenden beschreibe ich, wie wir den Anspruch „tagesaktuell“ durch automatisierte Deployments realisiert haben.
Obwohl wir in der echten Zielumgebung sowohl OpenCms als auch die zugehörige Datenbank im Cluster betreiben, entschieden wir uns für die CI-Umgebung, eine einfache Installation auf einem einzelnen Server vorzunehmen. Aus der Erfahrung heraus sind Deployments von OpenCms-Modulen im Cluster technisch komplex, da alle importierten Ressourcen automatisch repliziert werden. Gleiches gilt für die Datenbank. Diese Komplexität vermeiden wir somit auf dem Testsystem. Wir sind uns dabei der Tatsache bewusst, dass wir Fehler die potenziell nur im Cluster auftreten erst nach dem Deployment in die Cluster-Umgebung finden können.
Um tagesaktuell testen zu können, ist natürlich auch ein tägliches Deployment der Cms-Module und der Anwendungen notwendig. Da wir alle Anwendungen und Cms-Module bereits seit längerer Zeit automatisch von Hudson bauen und paketieren lassen, fehlte uns also eigentlich nur noch das Bindeglied zwischen Hudson-Build und Deployment auf der Testumgebung.
Wir haben also Schritte festgelegt, die automatisch passieren müssen:
- Nach einem erfolgreichen Hudson-Build muss ein Upload des Artefakts auf den Testserver stattfinden.
- Der Testserver muss täglich zu einer definierten Uhrzeit alle von Hudson hochgeladenen Artefakte deployen.
- Nach dem Deployment soll eine Mail mit Log-Informationen verschickt werden.
Da Hudson die jeweiligen Builds mit Maven durchführt, war der Upload auf den Testserver einfach zu realisieren. Hierfür haben wir einen Ant-Call in die deploy-Phase eingehängt, der mittels SCP das Artefakt auf die Testmaschine kopiert.
Nun kommen wir zum eigentlichen Deployment. Für die lokale Entwicklung nutzen wir bereits seit mehreren Jahren Features der OpenCms Shell. Hiermit synchronisieren wir die Module zwischen Eclipse und OpenCms, und importieren unter anderem auch komplette Sites mit Testcontent.
Um ein Modul zu importieren, bedienen wir uns der Shell-Features „loginUser“, „deleteModule“ und „importModule“. Ein Beispiel:
1loginUser Admin admin 2deleteModule de.codecentric.example 3importModule /opt/somewhere/autodeploy/de.codecentric.example.zip 4exit
Hier sind natürlich die Zugangsdaten, der Modulname und der Pfad zum Modul-ZIP anzupassen.
Da wir viele Module parallel entwickeln und auch deployen wollen, haben wir entschieden, dynamisch ein Skript für die OpenCms Shell zu generieren, welches dann ausgeführt wird. Ich zeige nun das kommentierte Bash-Skript, das unser Deployment vorbereitet und durchführt:
1#!/bin/bash 2HOME_DIR=/opt/somewhere 3PACKAGE_DIR=$HOME_DIR/autodeploy 4JBOSS_BIN_DIR=$HOME_DIR/jboss/bin 5JBOSS_SERVER_DIR=$HOME_DIR/jboss/server/opencms 6OPENCMS_BASE=$JBOSS_SERVER_DIR/deploy/cms.war/WEB-INF 7 8LOG_FILE=$PACKAGE_DIR/log 9LOG_MAILTO=receiver@example.net 10LOG_MAILFROM=autodeploy@yourhost 11 12CMS_USER=Admin 13CMS_PASSWORD=admin 14 15JAVA=/opt/jdk1.6.0_12/bin/java
Nun sind alle wesentlichen Variablen gesetzt. Eine Convenience-Methode für das Auflisten der gefundenen Module folgt:
1function list { 2 if [ 0 != $# ]; then 3 echo "Folgende Module wurden gefunden:" 4 i=1 5 6 for package in $@ 7 do 8 echo " $i. `basename "$package"`" 9 ((i=$i+1)) 10 done 11 else 12 echo -e "Keine Module gefunden.\n" 13 fi 14}
Es folgt das Herzstück unseres Skripts: die deploy-Funktion. Als Parameter erhält sie eine Liste von Pfaden zu ZIP-Files, also zu den Cms-Modulen, die deployed werden sollen. Nur wenn diese Liste nicht leer ist, wird das Cms-Shell-Skript generiert. Alles was nach dem Hinzufügen von „exit“ zu dem Skript noch folgt ist kopiert aus der mitgelieferten cmsshell.sh (siehe WEB-INF des WARs) und ein wenig angepasst. Der nötige Classpath wird befüllt, anschließend wird die Cms-Shell mit dem vorher generierten Skript gestartet und das Deployment der Module findet statt. Eine letzte Anmerkung: wir haben hier noch „purgeJspRepository“ eingefügt, um sicherzustellen dass alle eventuell im FlexCache befindlichen JSP-Fragmente entfernt werden.
1function deploy { 2 if [ 0 != $# ]; then 3 cd $PACKAGE_DIR 4 5 # Add login statement to the CMS script 6 echo "loginUser $CMS_USER $CMS_PASSWORD" > script 7 8 # Add import statements to the CMS script 9 for zipfile in $@ 10 do 11 echo "deleteModule \"`basename $zipfile .zip`\"" >> script 12 echo "importModule \"$zipfile\"" >> script 13 done 14 15 # Purge JSP Repo 16 echo "purgeJSPRepository" >> script 17 18 # Add exit statement to the CMS script 19 echo "exit" >> script 20 21 for JAR in $JBOSS_SERVER_DIR/lib/*.jar; do 22 TOMCAT_CLASSPATH="$TOMCAT_CLASSPATH:$JAR" 23 done 24 25 OPENCMS_CLASSPATH="" 26 for JAR in $JBOSS_SERVER_DIR/deploy/web.war/WEB-INF/lib/*.jar; do 27 OPENCMS_CLASSPATH="$OPENCMS_CLASSPATH:$JAR" 28 done 29 30 $JAVA -classpath "$OPENCMS_CLASSPATH:$TOMCAT_CLASSPATH:classes" org.opencms.main.CmsShell -base="$OPENCMS_BASE" -script=script >> $LOG_FILE 31 fi 32}
Das Skript enthält ansonsten noch einige Hilfsmethoden, die (in unserem Fall) JBoss stoppen und starten, die Mail mit dem Log versenden und nach dem Deployment aufräumen:
1function stopJBOSS { 2 $JBOSS_BIN_DIR/shutdown.sh 3 tail -f $JBOSS_SERVER_DIR/log/server.log > $PACKAGE_DIR/jboss_down.log & 4 until tail -n 1 $JBOSS_SERVER_DIR/log/server.log | grep "Shutdown complete"; do sleep 0.01; done 5 killall tail 6} 7 8function startJBOSS { 9 rm $JBOSS_SERVER_DIR/tmp/* -r 10 rm $JBOSS_SERVER_DIR/work/* -r 11 $JBOSS_BIN_DIR/startup.sh 12 tail -f $JBOSS_SERVER_DIR/log/server.log > $PACKAGE_DIR/jboss_up.log & 13 until tail -n 1 $JBOSS_SERVER_DIR/log/server.log | grep "Started"; do sleep 0.01; done 14 killall tail 15} 16 17function mailLog { 18 mailsubject="Auto-Deploy-Log vom `date "+%d.%m.%Y, %H:%M Uhr"`" 19 cat $LOG_FILE $PACKAGE_DIR/jboss_down.log $PACKAGE_DIR/jboss_up.log | mail -s "$mailsubject" $LOG_MAILTO -a "From: $LOG_MAILFROM" 20} 21 22function cleanup { 23 rm $PACKAGE_DIR/* 24}
Hier ist zu beachten, dass startup.sh und shutdown.sh nicht zur normalen JBoss-Distribution gehören sondern selbstgemachte Wrapper-Skripte sind.
Dann fehlt eigentlich nur noch der Teil des Skripts, der die tatsächliche Arbeit erledigt:
1echo -e "Suche nach Modulen in: $PACKAGE_DIR" 2 3packages=`find $PACKAGE_DIR -name *.zip` 4 5# Do some work! :) 6stopJBOSS 7list $packages 8deploy $packages 9startJBOSS 10mailLog 11cleanup
Wie man sieht, werden hier einfach aus dem PACKAGE_DIR alle dort befindlichen ZIPs als OpenCms-Module betrachtet und deployed. Unsere aktuell im Einsatz befindliche Version des Skripts tut noch einige weitere Dinge – zum Beispiel werden WAR-Files automatisch deployed, es werden Regeln für eine der Anwendungen aktualisiert, und zukünftig werden auch SQL-Skripte automatisch bei jedem Deployment ausgeführt. Das Skript ist natürlich frei nach dem persönlichen Geschmack erweiter- und änderbar.
Mit Hilfe dieses Skripts erfüllen wir passgenau die Anforderung „Als Tester möchte ich tagesaktuell testen können, um Fehler frühzeitig zu finden“. Notfalls kann das Skript sogar während des Tages manuell angestoßen werden um schnell einen Bugfix deployen und testen zu können. Selbstverständlich laufen nach dem automatischen Deployment auch sofort unsere automatisierten Fachtests. Wir haben also eine sehr gute und sehr agile Umgebung für Continuous Integration geschaffen, die uns beim Entwickeln qualitativ hochwertiger Software erheblich unterstützt.
Eine Notiz zum Ende dieses Artikels: ursprünglich wurde unser Autodeploy-Skript nur in Kombination mit OpenCms 7.5.2 und JBoss 4.2.3 genutzt. Mittlerweile hat ein anderes Projekt bei uns im Haus das Skript auch für automatisierte Deployments im Kontext von BEA WebLogic erfolgreich zum Einsatz gebracht.
Für die Zukunft überlegen wir, eventuell auch Deployments in die Zielumgebung, zumindest auf die erste von drei Stages, zumindest partiell zu automatisieren. Wir werden sehen, ob OCEE (Infos zu OCEE hier ) sich mit der Cms Shell verträgt. Wenn dazu schon jemand Erfahrungen hat, freue ich mich über entsprechende Kommentare.
Weitere Beiträge
von Robert Spielmann
Dein Job bei codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
Weitere Artikel in diesem Themenbereich
Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog-Autor*in
Robert Spielmann
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.