Merge lp:~frankban/charms/oneiric/buildbot-slave/upgrade-charm into lp:~yellow/charms/oneiric/buildbot-slave/trunk

Proposed by Francesco Banconi
Status: Merged
Approved by: Graham Binns
Approved revision: 21
Merged at revision: 20
Proposed branch: lp:~frankban/charms/oneiric/buildbot-slave/upgrade-charm
Merge into: lp:~yellow/charms/oneiric/buildbot-slave/trunk
Diff against target: 286 lines (+174/-11)
4 files modified
hooks/helpers.py (+42/-10)
revision (+0/-1)
tests/buildbot-slave.test (+14/-0)
tests/test.cfg (+118/-0)
To merge this branch: bzr merge lp:~frankban/charms/oneiric/buildbot-slave/upgrade-charm
Reviewer Review Type Date Requested Status
Graham Binns (community) code Approve
Review via email: mp+95534@code.launchpad.net

Description of the change

== Changes ==

- Added upgrade-charm symlink
- Updated helpers (with some refactoring to functions running "juju status")

The file helper.py is in sync with the one present in
lp:~frankban/charms/oneiric/buildbot-master/upgrade-charm

Currently the upgrade-charm test does not work due to a bug of juju:
see https://bugs.launchpad.net/juju/+bug/941873

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'hooks/helpers.py'
2--- hooks/helpers.py 2012-03-01 13:55:37 +0000
3+++ hooks/helpers.py 2012-03-02 11:26:19 +0000
4@@ -6,15 +6,21 @@
5 __metaclass__ = type
6 __all__ = [
7 'get_config',
8+ 'juju_status',
9 'log',
10 'log_entry',
11 'log_exit',
12+ 'make_charm_config_file',
13 'relation_get',
14 'relation_set',
15 'unit_info',
16+ 'wait_for_machine',
17+ 'wait_for_page_contents',
18+ 'wait_for_relation',
19+ 'wait_for_unit',
20 ]
21
22-from collections import namedtuple
23+from contextlib import contextmanager
24 import json
25 import operator
26 from shelltoolbox import (
27@@ -22,14 +28,13 @@
28 run,
29 script_name,
30 )
31+import os
32 import tempfile
33 import time
34 import urllib2
35 import yaml
36
37
38-Env = namedtuple('Env', 'uid gid home')
39-
40 log = command('juju-log')
41
42
43@@ -67,10 +72,18 @@
44 return charm_config_file
45
46
47+def juju_status(key):
48+ return yaml.safe_load(run('juju', 'status'))[key]
49+
50+
51+def get_charm_revision(service_name):
52+ service = juju_status('services')[service_name]
53+ return int(service['charm'].split('-')[-1])
54+
55+
56 def unit_info(service_name, item_name, data=None):
57- if data is None:
58- data = yaml.safe_load(run('juju', 'status'))
59- service = data['services'].get(service_name)
60+ services = juju_status('services') if data is None else data['services']
61+ service = services.get(service_name)
62 if service is None:
63 # XXX 2012-02-08 gmb:
64 # This allows us to cope with the race condition that we
65@@ -83,8 +96,27 @@
66 return item
67
68
69-def get_machine_data():
70- return yaml.safe_load(run('juju', 'status'))['machines']
71+@contextmanager
72+def maintain_charm_revision(path=None):
73+ if path is None:
74+ path = os.path.join(os.path.dirname(__file__), '..', 'revision')
75+ revision = open(path).read()
76+ try:
77+ yield revision
78+ finally:
79+ with open(path, 'w') as f:
80+ f.write(revision)
81+
82+
83+def upgrade_charm(service_name, timeout=120):
84+ next_revision = get_charm_revision(service_name) + 1
85+ start_time = time.time()
86+ run('juju', 'upgrade-charm', service_name)
87+ while get_charm_revision(service_name) != next_revision:
88+ if time.time() - start_time >= timeout:
89+ raise RuntimeError('timeout waiting for charm to be upgraded')
90+ time.sleep(0.1)
91+ return next_revision
92
93
94 def wait_for_machine(num_machines=1, timeout=300):
95@@ -98,7 +130,7 @@
96 # to tell what environment we're working in (LXC vs EC2) is to check
97 # the dns-name of the first machine. If it's localhost we're in LXC
98 # and we can just return here.
99- if get_machine_data()[0]['dns-name'] == 'localhost':
100+ if juju_status('machines')[0]['dns-name'] == 'localhost':
101 return
102 start_time = time.time()
103 while True:
104@@ -106,7 +138,7 @@
105 # not a machine that we need to wait for. This will only work
106 # for EC2 environments, which is why we return early above if
107 # we're in LXC.
108- machine_data = get_machine_data()
109+ machine_data = juju_status('machines')
110 non_zookeeper_machines = [
111 machine_data[key] for key in machine_data.keys()[1:]]
112 if len(non_zookeeper_machines) >= num_machines:
113
114=== added symlink 'hooks/upgrade-charm'
115=== target is u'install'
116=== modified file 'revision'
117--- revision 2012-02-29 09:59:39 +0000
118+++ revision 2012-03-02 11:26:19 +0000
119@@ -1,2 +1,1 @@
120 1
121-
122
123=== modified file 'tests/buildbot-slave.test'
124--- tests/buildbot-slave.test 2012-02-14 15:11:47 +0000
125+++ tests/buildbot-slave.test 2012-03-02 11:26:19 +0000
126@@ -4,15 +4,18 @@
127 # GNU Affero General Public License version 3 (see the file LICENSE).
128
129 import os
130+import time
131 import unittest
132
133 from helpers import (
134 command,
135 make_charm_config_file,
136+ maintain_charm_revision,
137 unit_info,
138 wait_for_page_contents,
139 wait_for_relation,
140 wait_for_unit,
141+ upgrade_charm,
142 )
143 from create_file import (
144 CONTENT,
145@@ -155,6 +158,17 @@
146 self.assertEqual(installdir, ssh('cat {}'.format(PATH)))
147 ssh('rm {}'.format(PATH))
148
149+ def test_upgrade_charm(self):
150+ # Ensure the charm can be upgraded without errors.
151+ self.deploy(self.charm_name)
152+ wait_for_unit(self.charm_name)
153+ with maintain_charm_revision():
154+ upgrade_charm(self.charm_name)
155+ # Wait for the charm to upgrade using sleep, since there is no
156+ # other confirmation at the moment but the state to remain 'started'.
157+ time.sleep(10)
158+ self.assertEqual('started', unit_info(self.charm_name, 'state'))
159+
160
161 if __name__ == '__main__':
162 unittest.main()
163
164=== modified symlink 'tests/test.cfg'
165=== target was u'../../buildbot-master/tests/test.cfg'
166--- tests/test.cfg 1970-01-01 00:00:00 +0000
167+++ tests/test.cfg 2012-03-02 11:26:19 +0000
168@@ -0,0 +1,118 @@
169+# -*- python -*-
170+# ex: set syntax=python:
171+
172+# This is a sample buildmaster config file. It must be installed as
173+# 'master.cfg' in your buildmaster's base directory.
174+
175+# This is the dictionary that the buildmaster pays attention to. We also use
176+# a shorter alias to save typing.
177+c = BuildmasterConfig = {}
178+
179+####### BUILDSLAVES
180+
181+# The 'slaves' list defines the set of recognized buildslaves. Each element is
182+# a BuildSlave object, specifying a username and password. The same username and
183+# password must be configured on the slave.
184+from buildbot.buildslave import BuildSlave
185+c['slaves'] = []
186+
187+# 'slavePortnum' defines the TCP port to listen on for connections from slaves.
188+# This must match the value configured into the buildslaves (with their
189+# --master option)
190+c['slavePortnum'] = 9989
191+
192+####### CHANGESOURCES
193+
194+# the 'change_source' setting tells the buildmaster how it should find out
195+# about source code changes. Here we point to the buildbot clone of pyflakes.
196+
197+from buildbot.changes.gitpoller import GitPoller
198+c['change_source'] = GitPoller(
199+ 'git://github.com/buildbot/pyflakes.git',
200+ branch='master', pollinterval=1200)
201+
202+####### SCHEDULERS
203+
204+# Configure the Schedulers, which decide how to react to incoming changes. In this
205+# case, just kick off a 'runtests' build
206+
207+from buildbot.scheduler import Scheduler
208+c['schedulers'] = []
209+c['schedulers'].append(Scheduler(name="all", branch=None,
210+ treeStableTimer=None,
211+ builderNames=["runtests"]))
212+
213+####### BUILDERS
214+
215+# The 'builders' list defines the Builders, which tell Buildbot how to perform a build:
216+# what steps, and which slaves can execute them. Note that any particular build will
217+# only take place on one slave.
218+
219+from buildbot.process.factory import BuildFactory
220+from buildbot.steps.source import Git
221+from buildbot.steps.shell import ShellCommand
222+
223+factory = BuildFactory()
224+# check out the source
225+factory.addStep(Git(repourl='git://github.com/buildbot/pyflakes.git', mode='copy'))
226+# run the tests (note that this will require that 'trial' is installed)
227+factory.addStep(ShellCommand(command=["trial", "pyflakes"]))
228+
229+from buildbot.config import BuilderConfig
230+
231+c['builders'] = [
232+ BuilderConfig(name="runtests",
233+ # Buildbot enforces that the slavenames list must not be empty. Our
234+ # wrapper will remove the empty string and replace it with proper values.
235+ slavenames=[''],
236+ factory=factory),
237+ ]
238+
239+####### STATUS TARGETS
240+
241+# 'status' is a list of Status Targets. The results of each build will be
242+# pushed to these targets. buildbot/status/*.py has a variety to choose from,
243+# including web pages, email senders, and IRC bots.
244+
245+c['status'] = []
246+
247+from buildbot.status import html
248+from buildbot.status.web import auth, authz
249+authz_cfg=authz.Authz(
250+ # change any of these to True to enable; see the manual for more
251+ # options
252+ gracefulShutdown = False,
253+ forceBuild = True, # use this to test your slave once it is set up
254+ forceAllBuilds = False,
255+ pingBuilder = False,
256+ stopBuild = False,
257+ stopAllBuilds = False,
258+ cancelPendingBuild = False,
259+)
260+c['status'].append(html.WebStatus(http_port=8010, authz=authz_cfg))
261+
262+####### PROJECT IDENTITY
263+
264+# the 'projectName' string will be used to describe the project that this
265+# buildbot is working on. For example, it is used as the title of the
266+# waterfall HTML page. The 'projectURL' string will be used to provide a link
267+# from buildbot HTML pages to your project's home page.
268+
269+c['projectName'] = "Pyflakes"
270+c['projectURL'] = "http://divmod.org/trac/wiki/DivmodPyflakes"
271+
272+# the 'buildbotURL' string should point to the location where the buildbot's
273+# internal web server (usually the html.WebStatus page) is visible. This
274+# typically uses the port number set in the Waterfall 'status' entry, but
275+# with an externally-visible host name which the buildbot cannot figure out
276+# without some help.
277+
278+c['buildbotURL'] = "http://localhost:8010/"
279+
280+####### DB URL
281+
282+# This specifies what database buildbot uses to store change and scheduler
283+# state. You can leave this at its default for all but the largest
284+# installations.
285+c['db_url'] = "sqlite:///state.sqlite"
286+

Subscribers

People subscribed via source and target branches