Merge lp:~gmb/charms/oneiric/buildbot-slave/charm-tests into lp:~yellow/charms/oneiric/buildbot-slave/trunk

Proposed by Graham Binns
Status: Merged
Merged at revision: 9
Proposed branch: lp:~gmb/charms/oneiric/buildbot-slave/charm-tests
Merge into: lp:~yellow/charms/oneiric/buildbot-slave/trunk
Diff against target: 416 lines (+155/-206)
8 files modified
.bzrignore (+1/-0)
hooks/install (+2/-1)
tests/100_buildbot-slave.config.test (+0/-72)
tests/200_buildbot-slave.test (+0/-58)
tests/buildbot-slave.test (+121/-0)
tests/config.test.yaml (+4/-5)
tests/get-unit-info (+0/-50)
tests/openport.py (+27/-20)
To merge this branch: bzr merge lp:~gmb/charms/oneiric/buildbot-slave/charm-tests
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+92098@code.launchpad.net

Description of the change

Changes from gmb and frankban 2012-02-08:
=========================================

 - We've added new, Python tests to the slave.
 - We have two working tests:
   - A test that the slave deploys successfully.
   - A test that the relation is set up correctly*
 - We have a test for deploying the slave with a script, but that currently fails all the time and we can't figure out why; it's disabled for now.

We've had to add some significant time.sleep() calls in order to avoid race conditions, particularly in the relationship test, since Juju does everything asynchronously and you can't rely on it actually having done things until you've checked juju status. I've added XXXs for these where appropriate.

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

This branch looks good. Here are the suggestions I had:

Instead of creating a command to just call it once (starting on
line 15 of the diff):

    - command('chmod', '+x', script)
    + chmod = command('chmod')
    + chmod('+x', script)

I suggest using "run":

    run('chmod', '+x', script)

Instead of doing your own wait-for-the-unit-to-come-up loop in
buildbot-slave.test you can use helpers.wait_for_unit().

We could use a HACKING.txt file like the master.

Regarding (do_not_)test_script: If I'm understanding correctly the
function of the script configuration options, we could test them without
going as far as the openport.py script goes. For example, we could have
a well-known file (say /tmp/the-slave-setup-script-works) that we verify
does not exist, run a deploy who's configuration specifies a test script
to create the file, and then verify that the file exists.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2012-02-07 19:39:04 +0000
3+++ .bzrignore 2012-02-08 18:09:23 +0000
4@@ -1,1 +1,2 @@
5 revision
6+examples
7
8=== modified file 'hooks/install'
9--- hooks/install 2012-02-07 15:55:26 +0000
10+++ hooks/install 2012-02-08 18:09:23 +0000
11@@ -68,7 +68,8 @@
12 log('Retrieving script: {}.'.format(url))
13 script = retrieve(url, path)
14 log('Changing script mode.')
15- command('chmod', '+x', script)
16+ chmod = command('chmod')
17+ chmod('+x', script)
18 log('Executing script: {}.'.format(script))
19 return subprocess.call([script] + shlex.split(str(args)))
20
21
22=== removed file 'tests/100_buildbot-slave.config.test'
23--- tests/100_buildbot-slave.config.test 2012-02-07 12:33:51 +0000
24+++ tests/100_buildbot-slave.config.test 1970-01-01 00:00:00 +0000
25@@ -1,72 +0,0 @@
26-#!/bin/sh
27-
28-# Copyright 2012 Canonical Ltd. This software is licensed under the
29-# GNU Affero General Public License version 3 (see the file LICENSE).
30-
31-set -e
32-
33-teardown() {
34- juju destroy-service buildbot-slave
35- if [ -n "$datadir" ] ; then
36- if [ -f $datadir/passed ]; then
37- rm -r $datadir
38- else
39- echo $datadir preserved
40- fi
41- fi
42-}
43-trap teardown EXIT
44-
45-juju deploy --repository=$PWD/../ local:buildbot-slave
46-juju expose buildbot-slave
47-
48-for try in `seq 1 600` ; do
49- slave_host=`juju status | tests/get-unit-info buildbot-slave public-address`
50- if [ -z "$slave_host" ] ; then
51- sleep 1
52- else
53- break
54- fi
55-done
56-
57-if [ -z "$slave_host" ] ; then
58- echo ERROR: status timed out
59- exit 1
60-fi
61-
62-datadir=`mktemp -d ${TMPDIR:-/tmp}/wget.test.XXXXXXX`
63-echo INFO: datadir=$datadir
64-
65-#slave_connected=$(
66-# wget --tries=100 --timeout=6 http://$slave_host:9000 -O - \
67-# -a $datadir/wget.log | grep -q 'UP!')
68-
69-assert_is_listening() {
70- local port=$1
71- listening=""
72- for try in `seq 1 10` ; do
73- if ! nc $slave_host $port < /dev/null ; then
74- continue
75- fi
76- listening="yes"
77- break
78- done
79-
80- if [ -z "$listening" ] ; then
81- echo "FAIL: not listening on port $port after 10 retries"
82- return 1
83- else
84- echo "PASS: listening on port $port"
85- return 0
86- fi
87-}
88-
89-assert_is_listening 9000
90-
91-touch $datadir/passed
92-
93-trap - EXIT
94-teardown
95-
96-echo INFO: PASS
97-exit 0
98
99=== removed file 'tests/200_buildbot-slave.test'
100--- tests/200_buildbot-slave.test 2012-02-07 12:33:51 +0000
101+++ tests/200_buildbot-slave.test 1970-01-01 00:00:00 +0000
102@@ -1,58 +0,0 @@
103-#!/bin/sh
104-
105-# Copyright 2012 Canonical Ltd. This software is licensed under the
106-# GNU Affero General Public License version 3 (see the file LICENSE).
107-
108-set -e
109-
110-teardown() {
111- juju destroy-service buildbot-slave
112- if [ -n "$datadir" ] ; then
113- if [ -f $datadir/passed ]; then
114- rm -r $datadir
115- else
116- echo $datadir preserved
117- fi
118- fi
119-}
120-trap teardown EXIT
121-
122-
123-juju deploy buildbot-master
124-juju deploy buildbot-slave
125-juju add-relation buildbot-slave buildbot-master
126-juju expose buildbot-master
127-
128-for try in `seq 1 600` ; do
129- master_host=`juju status | tests/get-unit-info buildbot-master public-address`
130- if [ -z "$master_host" ] ; then
131- sleep 1
132- else
133- break
134- fi
135-done
136-
137-if [ -z "$master_host" ] ; then
138- echo ERROR: status timed out
139- exit 1
140-fi
141-
142-datadir=`mktemp -d ${TMPDIR:-/tmp}/wget.test.XXXXXXX`
143-echo INFO: datadir=$datadir
144-
145-slave_connected=$(
146- wget --tries=100 --timeout=6 http://$master_host:8010/buildslaves -O - \
147- -a $datadir/wget.log | grep -q 'Idle')
148-
149-if [ -z $slave_connected ]; then
150- echo "ERROR: The slave is not connected after 600 seconds."
151- exit 1
152-fi
153-
154-touch $datadir/passed
155-
156-trap - EXIT
157-teardown
158-
159-echo INFO: PASS
160-exit 0
161
162=== added file 'tests/buildbot-slave.test'
163--- tests/buildbot-slave.test 1970-01-01 00:00:00 +0000
164+++ tests/buildbot-slave.test 2012-02-08 18:09:23 +0000
165@@ -0,0 +1,121 @@
166+#!/usr/bin/python
167+
168+# Copyright 2012 Canonical Ltd. This software is licensed under the
169+# GNU Affero General Public License version 3 (see the file LICENSE).
170+
171+import os
172+import time
173+import unittest
174+import urllib2
175+
176+from helpers import (
177+ command,
178+ encode_file,
179+ unit_info,
180+ )
181+from openport import (
182+ PORT,
183+ TEXT,
184+ )
185+
186+
187+# To run tests, having juju bootstrapped:
188+# JUJU_REPOSITORY=. oneiric/buildbot-slave/tests/buildbot-slave.test
189+# XXX 2012-02-08 gmb:
190+# This needs reducing; it's set high for debugging purposes only.
191+MAX_ATTEMPTS = 600
192+
193+juju = command('juju')
194+
195+
196+class TestCharm(unittest.TestCase):
197+
198+ def setUp(self):
199+ self.charm_name = 'buildbot-slave'
200+ self.repository = os.getenv('JUJU_REPOSITORY')
201+ self.environment = os.getenv('JUJU_ENVIRONMENT')
202+ self.tests_dir = os.path.dirname(os.path.abspath(__file__))
203+
204+ def deploy(self, config=None, sleep_time=0.1, charm_name=None):
205+ deploy_name = charm_name = charm_name or self.charm_name
206+ args = ['deploy']
207+ if self.repository is not None:
208+ args.append('--repository=' + self.repository)
209+ deploy_name = 'local:' + deploy_name
210+ if self.environment is not None:
211+ args.append('--environment=' + self.environment)
212+ if config:
213+ args.append('--config=' + config)
214+ args.append(deploy_name)
215+ juju(*args)
216+ status_attempts = 0
217+ while status_attempts < MAX_ATTEMPTS:
218+ status = unit_info(charm_name, 'state')
219+ if 'error' in status:
220+ return False
221+ if status == 'started':
222+ return True
223+ time.sleep(sleep_time)
224+ status_attempts += 1
225+ raise Exception("Charm took too long to deploy.")
226+
227+ def test_deploy(self):
228+ # Ensure the charm starts correctly when deployed.
229+ try:
230+ started = self.deploy()
231+ self.assertTrue(started)
232+ finally:
233+ juju('destroy-service', self.charm_name)
234+
235+ def do_not_test_script(self):
236+ # DISABLED BECAUSE IT FAILS ALL THE TIME.
237+ # Ensure a script supplied during deploy is correctly run.
238+ config = os.path.join(self.tests_dir, 'config.test.yaml')
239+ try:
240+ started = self.deploy(config=config)
241+ self.assertTrue(started)
242+ address = unit_info(self.charm_name, 'public-address')
243+ url = 'http://{}:{}'.format(address, PORT)
244+ try:
245+ stream = urllib2.urlopen(url)
246+ except urllib2.HTTPError:
247+ self.fail('Unable to connect to {}.'.format(url))
248+ self.assertEqual(TEXT, stream.read())
249+ finally:
250+# juju('destroy-service', self.charm_name)
251+ pass
252+
253+ def test_master_slave_relationship(self):
254+ master_charm_name = 'buildbot-master'
255+ try:
256+ self.deploy(charm_name=master_charm_name)
257+ self.deploy()
258+ juju('set', master_charm_name, 'extra-packages=git')
259+ encoded_config = encode_file(
260+ os.path.join(self.tests_dir, '..', 'examples', 'master.cfg'))
261+ juju(
262+ 'set', master_charm_name,
263+ 'config-file={}'.format(encoded_config))
264+ juju('expose', master_charm_name)
265+ juju('set', self.charm_name, 'builders=runtests')
266+ juju('add-relation', self.charm_name, master_charm_name)
267+ address = unit_info(master_charm_name, 'public-address')
268+ url = 'http://{}:{}/buildslaves'.format(address, 8010)
269+ # Wait for buildbot to restart, since there's a
270+ # potential race condition with an asynchronous service.
271+ while True:
272+ relation_info = unit_info(self.charm_name, 'relations')
273+ if relation_info['buildbot']['state'] == 'up':
274+ break
275+ time.sleep(0.1)
276+ try:
277+ stream = urllib2.urlopen(url)
278+ except urllib2.HTTPError:
279+ self.fail('Unable to connect to {}.'.format(url))
280+ self.assertIn('Idle', stream.read())
281+ finally:
282+ juju('destroy-service', self.charm_name)
283+ juju('destroy-service', master_charm_name)
284+
285+if __name__ == '__main__':
286+ unittest.main()
287
288=== modified file 'tests/config.test.yaml'
289--- tests/config.test.yaml 2012-02-01 17:13:41 +0000
290+++ tests/config.test.yaml 2012-02-08 18:09:23 +0000
291@@ -1,6 +1,5 @@
292 buildbot-slave:
293- installdir: /tmp/buildbot
294- name: example-slave
295- passwd: pass
296- script-retrieval-method: bzr
297- script-url: "http://bazaar.launchpad.net/~gmb/charms/oneiric/buildbot-slave/look-we-have-tests/tests/open-port.py"
298+ script-retrieval-method: bzr_cat
299+ script-url: "http://bazaar.launchpad.net/~yellow/charms/oneiric/buildbot-slave/charm-tests/tests/openport.py"
300+ script-path: openport.py
301+ script-args: -d
302
303=== removed file 'tests/get-unit-info'
304--- tests/get-unit-info 2012-02-07 12:33:51 +0000
305+++ tests/get-unit-info 1970-01-01 00:00:00 +0000
306@@ -1,50 +0,0 @@
307-#!/usr/bin/python
308-
309-# Copyright 2012 Canonical Ltd. This software is licensed under the
310-# GNU Affero General Public License version 3 (see the file LICENSE).
311-
312-# machines:
313-# 0: {dns-name: ec2-50-17-84-127.compute-1.amazonaws.com, instance-id: i-8c5c3fec}
314-# 1: {dns-name: ec2-184-73-102-113.compute-1.amazonaws.com, instance-id: i-14a2c174}
315-# 2: {dns-name: ec2-75-101-184-93.compute-1.amazonaws.com, instance-id: i-e0a2c180}
316-# services:
317-# mysql:
318-# charm: local:mysql-11
319-# relations: {db: wordpress}
320-# units:
321-# mysql/0:
322-# machine: 2
323-# relations:
324-# db: {state: up}
325-# state: started
326-# wordpress:
327-# charm: local:wordpress-31
328-# exposed: true
329-# relations: {db: mysql}
330-# units:
331-# wordpress/0:
332-# machine: 1
333-# open-ports: []
334-# relations: {}
335-# state: null
336-
337-import yaml
338-import sys
339-from subprocess import Popen, PIPE
340-
341-
342-def main():
343- d = yaml.safe_load(Popen(['juju','status'],stdout=PIPE).stdout)
344- srv = d.get("services", {}).get(sys.argv[1])
345- if srv is None:
346- return
347- units = srv.get("units", {})
348- if units is None:
349- return
350- item = units.items()[0][1].get(sys.argv[2])
351- if item is None:
352- return
353- print item
354-
355-if __name__ == "__main__":
356- main()
357
358=== added symlink 'tests/helpers.py'
359=== target is u'../../buildbot-master/hooks/helpers.py'
360=== renamed file 'tests/open-port.py' => 'tests/openport.py'
361--- tests/open-port.py 2012-02-07 12:33:51 +0000
362+++ tests/openport.py 2012-02-08 18:09:23 +0000
363@@ -6,26 +6,33 @@
364 # A little python script to open a port on the buildslave so that we can
365 # test that it's actually deployed successfully.
366
367+import os
368+import sys
369 import subprocess
370-import SimpleHTTPServer
371-import SocketServer
372+from BaseHTTPServer import (
373+ BaseHTTPRequestHandler,
374+ HTTPServer,
375+ )
376+
377
378 PORT = 9000
379-
380-simple_html = """
381-<html>
382- <head>
383- <title>Here's some HTML</title>
384- </head>
385- <body><p>The slave is UP!</p></body>
386-</html>
387-"""
388-
389-with open('/tmp/index.html', 'w') as index_file:
390- index_file.write(simple_html)
391-
392-subprocess.call(['open-port', '9000/TCP'])
393-Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
394-httpd = SocketServer.TCPServer(("", PORT), Handler)
395-#httpd.serve_forever()
396-print "HERE I AM!!!"
397+TEXT = 'The slave is UP!'
398+
399+
400+class Handler(BaseHTTPRequestHandler):
401+ def do_GET(self):
402+ self.send_response(200, 'OK')
403+ self.send_header('Content-type', 'text/palin')
404+ self.end_headers()
405+ self.wfile.write(TEXT)
406+
407+
408+if __name__ == "__main__":
409+ ## HACKETTY HACK.
410+ options = sys.argv[1:]
411+ if options and options[0] == '-d':
412+ # OH MY GOD.
413+ os.system('{} &'.format(sys.argv[0]))
414+ sys.exit(0)
415+ subprocess.call(['open-port', '{}/TCP'.format(PORT)])
416+ HTTPServer(('', PORT), Handler).serve_forever()

Subscribers

People subscribed via source and target branches

to all changes: