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
=== modified file 'hooks/helpers.py'
--- hooks/helpers.py 2012-03-01 13:55:37 +0000
+++ hooks/helpers.py 2012-03-02 11:26:19 +0000
@@ -6,15 +6,21 @@
6__metaclass__ = type6__metaclass__ = type
7__all__ = [7__all__ = [
8 'get_config',8 'get_config',
9 'juju_status',
9 'log',10 'log',
10 'log_entry',11 'log_entry',
11 'log_exit',12 'log_exit',
13 'make_charm_config_file',
12 'relation_get',14 'relation_get',
13 'relation_set',15 'relation_set',
14 'unit_info',16 'unit_info',
17 'wait_for_machine',
18 'wait_for_page_contents',
19 'wait_for_relation',
20 'wait_for_unit',
15 ]21 ]
1622
17from collections import namedtuple23from contextlib import contextmanager
18import json24import json
19import operator25import operator
20from shelltoolbox import (26from shelltoolbox import (
@@ -22,14 +28,13 @@
22 run,28 run,
23 script_name,29 script_name,
24 )30 )
31import os
25import tempfile32import tempfile
26import time33import time
27import urllib234import urllib2
28import yaml35import yaml
2936
3037
31Env = namedtuple('Env', 'uid gid home')
32
33log = command('juju-log')38log = command('juju-log')
3439
3540
@@ -67,10 +72,18 @@
67 return charm_config_file72 return charm_config_file
6873
6974
75def juju_status(key):
76 return yaml.safe_load(run('juju', 'status'))[key]
77
78
79def get_charm_revision(service_name):
80 service = juju_status('services')[service_name]
81 return int(service['charm'].split('-')[-1])
82
83
70def unit_info(service_name, item_name, data=None):84def unit_info(service_name, item_name, data=None):
71 if data is None:85 services = juju_status('services') if data is None else data['services']
72 data = yaml.safe_load(run('juju', 'status'))86 service = services.get(service_name)
73 service = data['services'].get(service_name)
74 if service is None:87 if service is None:
75 # XXX 2012-02-08 gmb:88 # XXX 2012-02-08 gmb:
76 # This allows us to cope with the race condition that we89 # This allows us to cope with the race condition that we
@@ -83,8 +96,27 @@
83 return item96 return item
8497
8598
86def get_machine_data():99@contextmanager
87 return yaml.safe_load(run('juju', 'status'))['machines']100def maintain_charm_revision(path=None):
101 if path is None:
102 path = os.path.join(os.path.dirname(__file__), '..', 'revision')
103 revision = open(path).read()
104 try:
105 yield revision
106 finally:
107 with open(path, 'w') as f:
108 f.write(revision)
109
110
111def upgrade_charm(service_name, timeout=120):
112 next_revision = get_charm_revision(service_name) + 1
113 start_time = time.time()
114 run('juju', 'upgrade-charm', service_name)
115 while get_charm_revision(service_name) != next_revision:
116 if time.time() - start_time >= timeout:
117 raise RuntimeError('timeout waiting for charm to be upgraded')
118 time.sleep(0.1)
119 return next_revision
88120
89121
90def wait_for_machine(num_machines=1, timeout=300):122def wait_for_machine(num_machines=1, timeout=300):
@@ -98,7 +130,7 @@
98 # to tell what environment we're working in (LXC vs EC2) is to check130 # to tell what environment we're working in (LXC vs EC2) is to check
99 # the dns-name of the first machine. If it's localhost we're in LXC131 # the dns-name of the first machine. If it's localhost we're in LXC
100 # and we can just return here.132 # and we can just return here.
101 if get_machine_data()[0]['dns-name'] == 'localhost':133 if juju_status('machines')[0]['dns-name'] == 'localhost':
102 return134 return
103 start_time = time.time()135 start_time = time.time()
104 while True:136 while True:
@@ -106,7 +138,7 @@
106 # not a machine that we need to wait for. This will only work138 # not a machine that we need to wait for. This will only work
107 # for EC2 environments, which is why we return early above if139 # for EC2 environments, which is why we return early above if
108 # we're in LXC.140 # we're in LXC.
109 machine_data = get_machine_data()141 machine_data = juju_status('machines')
110 non_zookeeper_machines = [142 non_zookeeper_machines = [
111 machine_data[key] for key in machine_data.keys()[1:]]143 machine_data[key] for key in machine_data.keys()[1:]]
112 if len(non_zookeeper_machines) >= num_machines:144 if len(non_zookeeper_machines) >= num_machines:
113145
=== added symlink 'hooks/upgrade-charm'
=== target is u'install'
=== modified file 'revision'
--- revision 2012-02-29 09:59:39 +0000
+++ revision 2012-03-02 11:26:19 +0000
@@ -1,2 +1,1 @@
1111
2
32
=== modified file 'tests/buildbot-slave.test'
--- tests/buildbot-slave.test 2012-02-14 15:11:47 +0000
+++ tests/buildbot-slave.test 2012-03-02 11:26:19 +0000
@@ -4,15 +4,18 @@
4# GNU Affero General Public License version 3 (see the file LICENSE).4# GNU Affero General Public License version 3 (see the file LICENSE).
55
6import os6import os
7import time
7import unittest8import unittest
89
9from helpers import (10from helpers import (
10 command,11 command,
11 make_charm_config_file,12 make_charm_config_file,
13 maintain_charm_revision,
12 unit_info,14 unit_info,
13 wait_for_page_contents,15 wait_for_page_contents,
14 wait_for_relation,16 wait_for_relation,
15 wait_for_unit,17 wait_for_unit,
18 upgrade_charm,
16 )19 )
17from create_file import (20from create_file import (
18 CONTENT,21 CONTENT,
@@ -155,6 +158,17 @@
155 self.assertEqual(installdir, ssh('cat {}'.format(PATH)))158 self.assertEqual(installdir, ssh('cat {}'.format(PATH)))
156 ssh('rm {}'.format(PATH))159 ssh('rm {}'.format(PATH))
157160
161 def test_upgrade_charm(self):
162 # Ensure the charm can be upgraded without errors.
163 self.deploy(self.charm_name)
164 wait_for_unit(self.charm_name)
165 with maintain_charm_revision():
166 upgrade_charm(self.charm_name)
167 # Wait for the charm to upgrade using sleep, since there is no
168 # other confirmation at the moment but the state to remain 'started'.
169 time.sleep(10)
170 self.assertEqual('started', unit_info(self.charm_name, 'state'))
171
158172
159if __name__ == '__main__':173if __name__ == '__main__':
160 unittest.main()174 unittest.main()
161175
=== modified symlink 'tests/test.cfg'
=== target was u'../../buildbot-master/tests/test.cfg'
--- tests/test.cfg 1970-01-01 00:00:00 +0000
+++ tests/test.cfg 2012-03-02 11:26:19 +0000
@@ -0,0 +1,118 @@
1# -*- python -*-
2# ex: set syntax=python:
3
4# This is a sample buildmaster config file. It must be installed as
5# 'master.cfg' in your buildmaster's base directory.
6
7# This is the dictionary that the buildmaster pays attention to. We also use
8# a shorter alias to save typing.
9c = BuildmasterConfig = {}
10
11####### BUILDSLAVES
12
13# The 'slaves' list defines the set of recognized buildslaves. Each element is
14# a BuildSlave object, specifying a username and password. The same username and
15# password must be configured on the slave.
16from buildbot.buildslave import BuildSlave
17c['slaves'] = []
18
19# 'slavePortnum' defines the TCP port to listen on for connections from slaves.
20# This must match the value configured into the buildslaves (with their
21# --master option)
22c['slavePortnum'] = 9989
23
24####### CHANGESOURCES
25
26# the 'change_source' setting tells the buildmaster how it should find out
27# about source code changes. Here we point to the buildbot clone of pyflakes.
28
29from buildbot.changes.gitpoller import GitPoller
30c['change_source'] = GitPoller(
31 'git://github.com/buildbot/pyflakes.git',
32 branch='master', pollinterval=1200)
33
34####### SCHEDULERS
35
36# Configure the Schedulers, which decide how to react to incoming changes. In this
37# case, just kick off a 'runtests' build
38
39from buildbot.scheduler import Scheduler
40c['schedulers'] = []
41c['schedulers'].append(Scheduler(name="all", branch=None,
42 treeStableTimer=None,
43 builderNames=["runtests"]))
44
45####### BUILDERS
46
47# The 'builders' list defines the Builders, which tell Buildbot how to perform a build:
48# what steps, and which slaves can execute them. Note that any particular build will
49# only take place on one slave.
50
51from buildbot.process.factory import BuildFactory
52from buildbot.steps.source import Git
53from buildbot.steps.shell import ShellCommand
54
55factory = BuildFactory()
56# check out the source
57factory.addStep(Git(repourl='git://github.com/buildbot/pyflakes.git', mode='copy'))
58# run the tests (note that this will require that 'trial' is installed)
59factory.addStep(ShellCommand(command=["trial", "pyflakes"]))
60
61from buildbot.config import BuilderConfig
62
63c['builders'] = [
64 BuilderConfig(name="runtests",
65 # Buildbot enforces that the slavenames list must not be empty. Our
66 # wrapper will remove the empty string and replace it with proper values.
67 slavenames=[''],
68 factory=factory),
69 ]
70
71####### STATUS TARGETS
72
73# 'status' is a list of Status Targets. The results of each build will be
74# pushed to these targets. buildbot/status/*.py has a variety to choose from,
75# including web pages, email senders, and IRC bots.
76
77c['status'] = []
78
79from buildbot.status import html
80from buildbot.status.web import auth, authz
81authz_cfg=authz.Authz(
82 # change any of these to True to enable; see the manual for more
83 # options
84 gracefulShutdown = False,
85 forceBuild = True, # use this to test your slave once it is set up
86 forceAllBuilds = False,
87 pingBuilder = False,
88 stopBuild = False,
89 stopAllBuilds = False,
90 cancelPendingBuild = False,
91)
92c['status'].append(html.WebStatus(http_port=8010, authz=authz_cfg))
93
94####### PROJECT IDENTITY
95
96# the 'projectName' string will be used to describe the project that this
97# buildbot is working on. For example, it is used as the title of the
98# waterfall HTML page. The 'projectURL' string will be used to provide a link
99# from buildbot HTML pages to your project's home page.
100
101c['projectName'] = "Pyflakes"
102c['projectURL'] = "http://divmod.org/trac/wiki/DivmodPyflakes"
103
104# the 'buildbotURL' string should point to the location where the buildbot's
105# internal web server (usually the html.WebStatus page) is visible. This
106# typically uses the port number set in the Waterfall 'status' entry, but
107# with an externally-visible host name which the buildbot cannot figure out
108# without some help.
109
110c['buildbotURL'] = "http://localhost:8010/"
111
112####### DB URL
113
114# This specifies what database buildbot uses to store change and scheduler
115# state. You can leave this at its default for all but the largest
116# installations.
117c['db_url'] = "sqlite:///state.sqlite"
118

Subscribers

People subscribed via source and target branches