Uberspace 7 - Episode 3
In Uberspace 7 - Episode 2 haben wir das Thema Tests angerissen:
Nachdem Features definiert wurden sollte man anfangen, Tests zu schreiben und damit die Anforderungen an die Features genau definieren.
Aber wie sieht so ein Test eigentlich aus? Beispiel: Jeder User bekommt mit seinem Account eine Mailbox, die er mit seinem SSH-Kennwort abrufen kann. Ein Test dafür würde folgendermaßen aussehen:
- Einen User auf dem Server anlegen
- Ein SSH-Passwort setzen
- Eine Mail an User schicken
- Der User loggt sich per IMAP ein
- User holt Mail 1 per IMAP ab (Es kann nur eine Mail da sein, der User ist ganz frisch angelegt und das Postfach war vor der Testmail leer)
- Wir überprüfen den Inhalt der Mail auf einen bestimmten String
- Profit!
Diesen Test haben wir mit Ansible realisiert. Und jetzt mal Butter bei die Fische, so sieht das in Code aus: Die Variablen testuser
und dummypassword
haben wir bereits an anderer Stelle definiert, in ansible_fqdn
steht der Hostname des Servers. <{{ testuser }}@{{ ansible_fqdn }}>
ist also die E-Mail-Adresse des Testusers.
Schritt 1 und 2 läuft als User root
auf der Maschine, so wie das Webinterface die Skripte auch ansteuern würde. Auf das expect
-Modul gehen wir gleich noch ein.
- name: Create test account
command: "/usr/local/sbin/uberspace-account-create -u {{ testuser }}"
- name: Set dummypassword for testuser
expect:
command: "passwd {{ testuser }}"
responses:
.*password: "{{ dummypassword }}"
Die Mail verschicken wir auch als root
, wir prüfen ja in diesem Test nicht das Versenden von Mails, sondern den Empfang.
- name: Send mail to test account
mail:
host: 'localhost'
port: 25
to: "John Doe <{{ testuser }}@{{ ansible_fqdn }}>"
subject: 'Test successful'
ignore_errors: yes
Die weiteren Schritte werden als der Testuser ausgeführt:
- name: Check IMAP login and see if mail arrived
expect:
command: "openssl s_client -connect {{ ansible_fqdn }}:993 -quiet"
responses:
Dovecot ready: "a1 LOGIN {{ testuser }} {{ dummypassword }}"
Logged in: "a2 LIST \"\" \"*\""
List completed: "a3 EXAMINE INBOX"
Examine completed: "a4 FETCH 1 BODY[]"
Fetch completed: "a5 LOGOUT"
register: imapconversation
Für alle, die das IMAP-Protokol nicht sprechen, gibt es z.B. bei anta.net eine kleine Spickhilfe. Das expect
-Modul wartet auf bestimmte Ausgaben und antwortet dann mit definierten Antworten. Die komplette Ausgabe des Prozesses schreiben wir uns dann in die Variable imapconversation
.
So weit, so gut. Jetzt müssen wir nur noch überprüfen, ob der Betreff der Mail (“Test successful” auch irgendwo in der Variable vorkommt:
- name: Check assertions
assert:
that:
- "'Test successful' in imapconversation.stdout"
Und wenn das der Fall ist, ist der Test grün und wir können sagen: E-Mail-Empfang und Zugriff per IMAP klappt. Die Ausgabe von Ansible sieht dann so aus:
TASK [Create test account] *****************************************************
changed: [asteroid-dev]
TASK [Set dummypassword for testuser] ******************************************
changed: [asteroid-dev]
TASK [Send mail to test account] ***********************************************
ok: [asteroid-dev]
TASK [Check IMAP login and see if mail arrived] ********************************
changed: [asteroid-dev]
TASK [Check assertions] ********************************************************
ok: [asteroid-dev]
So soll’s sein, läuft also.
Hier noch als Bonus der Inhalt von imapconversation
(Wir haben auf unseren Testmaschinen ein selbst signiertes Zertifikat):
"depth=0 OU = IMAP server, CN = imap.example.com, emailAddress = postmaster@example.com\r\nverify error:num=18:self signed certificate\r\nverify return:1\r\ndepth=0 OU = IMAP server, CN = imap.example.com, emailAddress = postmaster@example.com\r\nverify return:1\r\n* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE AUTH=PLAIN] Dovecot ready.\r\r\na1 OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS SPECIAL-USE BINARY MOVE] Logged in\r\r\n* LIST (\\HasNoChildren) \".\" INBOX\r\r\na2 OK List completed.\r\r\n* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\r\n* OK [PERMANENTFLAGS ()] Read-only mailbox.\r\r\n* 1 EXISTS\r\r\n* 1 RECENT\r\r\n* OK [UNSEEN 1] First unseen.\r\r\n* OK [UIDVALIDITY 1468428977] UIDs valid\r\r\n* OK [UIDNEXT 2] Predicted next UID\r\r\na3 OK [READ-ONLY] Examine completed (0.010 secs).\r\r\n* 1 FETCH (BODY[] {677}\r\r\nReturn-Path: <root>\r\r\nDelivered-To: test@localhost.localdomain\r\r\nReceived: (qmail 30064 invoked from network); 13 Jul 2016 16:56:17 -0000\r\r\nReceived: from unknown (HELO localhost.localdomain) (::1)\r\r\n by ip6-localhost with SMTP; 13 Jul 2016 16:56:17 -0000\r\r\nContent-Type: multipart/mixed; boundary=\"===============4134123165338466742==\"\r\r\nMIME-Version: 1.0\r\r\nSubject: Test successful\r\r\nFrom: root\r\r\nX-Mailer: Ansible\r\r\nTo: John Doe <test@localhost.localdomain>\r\r\n\r\r\nMultipart message\r\r\n--===============4134123165338466742==\r\r\nContent-Type: text/plain; charset=\"us-ascii\"\r\r\nMIME-Version: 1.0\r\r\nContent-Transfer-Encoding: 7bit\r\r\n\r\r\nTest successful\r\r\n\r\r\n\r\r\n--===============4134123165338466742==--\r\r\n)\r\r\na4 OK Fetch completed.\r\r\n* BYE Logging out\r\r\na5 OK Logout completed."
Header von Wikimedia (CC BY-SA 3.0))