\subsection{Verifikation} \label{ssub:verifikation} Wie auch in der Softwareentwicklung müssen Konfigurationssysteme getestet werden. Dies gestaltet sich oft als schwierig, weil nicht immer eine exakte Kopie des aktuellen Produktionssystems zur Verfügung steht. Mit steigender Komplexität steigt der Aufwand, Cookbooks manuell zu testen. Im Folgenden werden verschiedene Möglichkeiten aufgeführt, wie dies automatisiert werden kann. Die erste und einfachste Methode ist der Befehl: \shellcmd{knife cookbook test [COOKBOOKS...]} Das Kommandozeilenprogramm \texttt{knife} ist ein Teil von Chef. Es ist das primäre Verwaltungsprogramm für Chef-Administratoren. Der Unterbefehl \texttt{cookbook test} überprüft den Ruby-Quellcode und die Templates des Cookbooks auf Syntaxfehler. Allerdings treten viele Fehler erst zur Laufzeit auf, im Besonderen da Ruby dynamisch typisiert ist und der Compiler beispielsweise Tippfehler in Methoden und Variablennamen nicht erkennen kann. Ein anderes Programm ist foodcritic. Es führt eine statische Codeanalyse ähnlich \href{http://www.jslint.com/}{JSlint} oder \href{http://perl-critic.stacka.to/}{Perl::Critic} auf der eigenen Codebasis durch. Dabei wird der Ruby-Quellcode gegen einen Regelsatz getestet, um so häufige Programmierfehler zu erkennen oder um Code-Konventionen innerhalb eines Projekts einzuhalten. Dieser Regelsatz kann durch eigene Regeln erweitern werden. \subsubsection{Chefspec} \label{chefspec} Chefspec baut auf das in Ruby verbreitete Testframework \href{http://rspec.info/}{RSpec} auf. Chefspec erweitert dabei RSpec um die Funktion, Cookbooks zu laden und stellt spezielle Matcher (RSpec-Terminologie für Assertions) bereit, um diese zu testen. Wie bereits in Abschnitt~\ref{ablauf_einer_provisionierung} erwähnt, gibt es zwei Phasen bei der Ausführung von Chef. Bei Chefspec wird der Provisionierungsprozess nur bis zur Convergingphase durchlaufen. Die eigenen Tests überprüfen nur die erzeugten \texttt{Ressourcen}. Dies hat den Vorteil, das Tests sehr schnell durchlaufen werden, da keine Änderungen an einem System vorgenommen werden müssen. Dies hat Vorteile beim Entwickeln, weil man auf diese Weise schnell Feedback bekommt. Das Zusammenspiel mehrerer Cookbooks lässt sich dadurch gut testen. Außerdem ermöglicht es, verschiedene Konfigurationen/Betriebssysteme zu testen, ohne das diese (zeit)aufwendig aufgesetzt werden müssen. Da Chefspec allerdings zu keinen Zeitpunkt Code auf dem System ausführt, sind weitere Integrationstest unerlässlich. Der folgende Test wurde aus dem selbst geschriebenen NTP-Cookbook (\ref{sub:einrichtung-der-netzwerkdienste}) entnommen. \begin{lstlisting}[language=Ruby,caption={Chefspec-Test für das NTP-Cookbook}] require_relative '../spec_helper' describe 'ntp::default' do let(:chef_run) do ChefSpec::Runner.new do |node| node.set["ntp"]["subnets"] = ["::1", "127.0.0.1", "172.28.128.0 mask 255.255.255.0 nomodify notrap nopeer"] end.converge(described_recipe) end it "should setup ntp" do chef_run.should install_package("ntp") chef_run.should render_file("/etc/ntp.conf").with_content("172.28.128.0") end end \end{lstlisting} Im \texttt{chef\_run}-Block wird dem fiktiven Node Attribute zugewiesen und das zu testende Cookbook ausgeführt. Das Ergebnis wird in diesem Beispiel in dem Objekt \texttt{chef\_run} gespeichert. Gegen dieses Objekt wird getestet, ob bestimmte \texttt{Ressourcen} korrekt initialisiert wurden. In diesem Fall wird überprüft, ob das Paket \texttt{ntp} installiert werden soll und ob das Subnetz in dem Template in der Konfigurationsdatei \texttt{/etc/ntp.conf} richtig gesetzt wird. Die Tests werden mit dem Befehl \texttt{rspec} ausgeführt. Wenn keine weiteren Argumente angegeben sind, führt dieses Programm alle Dateien unterhalb des Ordners \texttt{spec} aus, dessen Dateinamen auf \texttt{\_spec.rb} enden. Um alle drei oben genannten Testmethoden gleichzeitig ausführen zu lassen, wurde ein Rakefile geschrieben. \href{http://rake.rubyforge.org/}{Rake} ist das in Ruby geschriebene Äquivalent zu Make, welches ein verbreitetes Buildprogramm auf UNIX-ähnlichen Plattformen ist. Die Tests werden durch den Befehl: \shellcmd{rake test} ausgeführt. Dieser muss innerhalb Projektverzeichnisses aufgerufen werden. \subsubsection{Minitest-Handler} \label{minitest_handler} \href{https://github.com/btm/minitest-handler}{Minitest-Handler} hingegen wird nach jedem Provisionierungsdurchgang ausgeführt. Im Gegensatz zu Chefspec nutzt es das Minitest-Framework, welches schon mit Ruby mitgeliefert wird. Um Minitest-Handler zu nutzen, muss das Recipe aus \texttt{Minitest-Handler-Cookbook} als erstes Recipe in der Node geladen werden. Minitest-Handler durchsucht beim Durchlauf in jedem anderen Cookbook, in den Unterordnern in \texttt{files/} nach dem Verzeichnis \texttt{test} und lädt alle Tests aus diesem Verzeichnis. Über die Zeile: \begin{lstlisting}[language=Ruby] describe_recipe "ntp::default" do # #... end \end{lstlisting} wird angeben, zu welchem Test das Recipe gehört (In diesem Fall das Recipe aus dem NTP-Cookbook). Wenn das entsprechende Recipe von dem Node ausgeführt wird, wird der dazugehörige Test nach dem Provisionierungsdurchlauf ebenfalls ausgeführt. Minitest-Handler erweitert RSpec um nützliche Methoden, um den Status des Systems zu überprüfen. Nachfolgend ist ein Beispiel aus dem Bind-Cookbook, welches in Abschnitt~\ref{sub:einrichtung-der-netzwerkdienste} erwähnt wurde: \begin{lstlisting}[language=Ruby, caption={\texttt{Minitest}-Test für das Bind-Cookbook}] describe_recipe 'bind::default' do it "starts the named daemon" do service("bind9").must_be_running end it "should resolve dns" do assert_sh("dig localhost @localhost") end end \end{lstlisting} Die Methode \texttt{assert\_sh} überprüft den Exit-Code eines Befehls und schlägt fehl, wenn dieser ungleich der Zahl Null ist, während die \texttt{service}-Methode den Status eines Systemdienst überprüft. Weitere Testmethoden sind zum Beispiel das Überprüfen von Verzeichnissen, Inhalte von Dateien oder Mountpoints. Viele Fehler werden in der Regel schon von den Provider erkannt und festgestellt. Minitest-Handler kann dies Erweitern um protokollspezifische Tests durchzuführen oder das Testen von Funktionalität bestimmter Dienste. % vim: set spell spelllang=de_de