\subsubsection{Verifikation}
\label{ssub:verifikation}

Wie auch Software müssen auch Provisionierungsskripte getestet werden.
Dies gestaltet sich oft als schwierig, weil nicht immer eine Kopie des
aktuellen Produktionssystem zur Verfügung steht. Mit steigender Komplexität
steigt der Aufwand geschriebene 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...]}

Dieser überprüft den Rubyquellcode und die Templates des Cookbooks auf Syntaxfehler.
Allerdings treten viele Fehler erst zur Laufzeit auf, insbesonderen da Ruby
eine dynamische Programmiersprache. Ein anderes Programm ist foodcritic. Dies
ist eine statische Codeanalyse ähnlich jslint oder perlcritics. Dabei wird der
Rubycode gegen Regelsatz getestet, um so schlechten Stil zu erkennen oder
um Codeingstandards innerhalb eines Projekts einzuhalten. Diesen Regelsatz kann
man durch eigene Regeln erweitern.

\textbf{Chefspec}
\label{chefspec}

Chefspec baut auf das in Ruby verbreitete Testframework Rspec auf. Dies ist ein
Vertreter des BDD (TODO mehr Infos zu BDD, Integration von Chefspec). Wie
bereits in Abschnitt ~\ref{durchlauf_eines_recipes} erwähnt, gibt es 2 Phasen
bei der Ausführung von Chef. Bei Chefspec wird die 1. Phase, die (TODO
\$Phase)-phase, durchlaufen und danach die eigenen Tests ausgeführt. 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 durchzutesten, ohne das diese aufwendig
aufgesetzt werden müssen. Da Chefspec allerdings nicht wirklich Code auf dem
System ausführt, sind Integrationstest unerlässlich.

Der folgende Test wurde aus dem NTP-Cookbook
(~\ref{ssub:einrichtung-der-netzwerkdienste}) entnommen.

\begin{lstlisting}
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 \emph{chef\_run}-Block wird der fiktiven Node Attribute zugewiesen und das zu
testende Cookbook ausgeführt. Das Ergebnis wird in diesem Beispiel in dem Objekt
\emph{chef\_run} gespeichert. Gegen dieses Objekt wird getestet, ob bestimmte
Resourcen korrekt initialisiert wurden. In diesem Fall wird überprüft, ob

TODO Ausführung

TODO rake test

\textbf{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. Allerdings kann man durch einbinden, der Zeile:

\begin{lstlisting}
require "minitest/spec"
\end{lstlisting}

eine Rspec sehr ähnliche Syntax benutzen. Um Minitest Handler zu nutzen, muss
das Recipe aus minitest-handler-cookbook als erstes Recipe in der node geladen
werden. Minitest Handler durchsucht beim Durchlauf in jedem anderen cookbook, in
den Unterordnern von \emph{files/} nach dem Verzeichnis \emph{test} und lädt
alle Tests aus diesem Verzeichnis. Über die Beschreibungszeile:

\begin{lstlisting}
describe_recipe "ntp::default" do # 
#...
end
\end{lstlisting}

wird angeben für, welches Recipe der Test gedacht ist (In diesem Fall das
Defaultrecipe aus dem NTP-Cookbook). Wenn das entsprechende Recipe von der Node
ausgeführt wird, wird nach der entsprechende Test nach dem
Provisionierunsdurchlauf ebenfalls ausgeführt. Minitest Handler erweitert Rspec
um nützliche Methoden um den Status des Systems zu überprüfen. Hier ein Beispiel
aus dem bind cookbook, welches in
Abschnitt~\ref{ssub:einrichtung-der-netzwerkdienste} erwähnt wurde:

\begin{lstlisting}
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 \emph{assert\_sh} überprüft den Exitstatus eines Befehls und schlägt
fehl, wenn dieser ungleich null ist, während der \emph{service} den Status eines
Systemdienst sicherstellt. Es gibt noch weitere Testmethoden, wie das Überprüfen
von Verzeichnissen, Inhalte von Dateien oder Mountpoints.

% vim: set spell spelllang=de_de
