\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
