\subsubsection{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 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...]}

Das Kommandozeilenprogramm \emph{knife} ist Teil von Chef. Es ist das primäre
Verwaltungsprogramm für Adminstratoren.  Der Unterbefehl \emph{cookbook test}
überprüft den Rubyquellcode und die Templates des Cookbooks auf Syntaxfehler.
Allerdings treten viele Fehler erst zur Laufzeit auf, insbesonderes da Ruby eine
dynamische Programmiersprache ist. 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 Rubycode gegen einen Regelsatz getestet, um so häufige
Programmierfehler zu erkennen oder um Codingstandards innerhalb eines Projekts
einzuhalten. Dieser Regelsatz kann durch eigene Regeln erweitern werden.

\textbf{Chefspec}
\label{chefspec}

Chefspec baut auf das, in Ruby verbreitete, Testframework
\href{http://rspec.info/}{RSpec} auf. Rspec ist ein Testframework, welches auf
\href{http://dannorth.net/2012/05/31/bdd-is-like-tdd-if/}{Behavior Driven
Development} (kurz BDD) basiert. Hierbei dokumentieren sich Testcases selbst
durch Einfügen von Beschreibungen. RSpec kann Sätze aus diesen Beschreibungen
bilden und so im Falle eines fehlgeschlagen Tests schnell darüber Auskunft
geben, was der Test getestet hat und aus welchen Grund dieser fehlgeschlagen
ist. 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 \emph{Resources}. 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 (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{ssub:einrichtung-der-netzwerkdienste}) entnommen.

\begin{lstlisting}[language=Ruby]
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
\emph{Resources} korrekt initialisiert wurden. In diesem Fall wird überprüft, ob
das Paket \emph{ntp} installiert werden soll und ob das Subnetz in dem Template
in der Konfigurationsdatei \emph{/etc/ntp.conf} richtig gesetzt wird.

Die Tests werden mit dem Befehl \emph{rspec} ausgeführt. Wenn keine weiteren Argumente
angegeben sind, führt dieses Programm alle Dateien unterhalb des Ordners \emph{spec}
aus, dessen Dateinamen auf \emph{\_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 Task \emph{test}
ausgeführt:

\shellcmd{rake test}

Dieser muss innerhalb Projektverzeichnises aufgerufen werden.

\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 Syntax benutzen, die RSpec sehr ähnliche ist. Um Minitest-Handler zu
nutzen, muss das Recipe aus \emph{Minitest-Handler-Cookbook} als erstes Recipe
in der node geladen werden. Minitest-Handler durchsucht beim Durchlauf in jedem
anderen Cookbook, in den Unterordnern in \emph{files/} nach dem Verzeichnis
\emph{test} und lädt alle Tests aus diesem Verzeichnis. Über die
Beschreibungszeile:

\begin{lstlisting}[language=Ruby]
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 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 ein Beispiel aus dem Bind-Cookbook,
welches in Abschnitt~\ref{ssub:einrichtung-der-netzwerkdienste} erwähnt wurde:

\begin{lstlisting}[language=Ruby]
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 die \emph{service}-Methode den
Status eines Systemdienst sicherstellt.  Weitere Testmedhoden 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.

TODO Resume und Ausblick

% vim: set spell spelllang=de_de
