yellow team mailing list archive
-
yellow team
-
Mailing list archive
-
Message #00535
[Merge] lp:~frankban/charms/oneiric/buildbot-slave/upgrade-charm into lp:~yellow/charms/oneiric/buildbot-slave/trunk
Francesco Banconi has proposed merging lp:~frankban/charms/oneiric/buildbot-slave/upgrade-charm into lp:~yellow/charms/oneiric/buildbot-slave/trunk.
Requested reviews:
Launchpad Yellow Squad (yellow)
For more details, see:
https://code.launchpad.net/~frankban/charms/oneiric/buildbot-slave/upgrade-charm/+merge/95534
== 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
--
https://code.launchpad.net/~frankban/charms/oneiric/buildbot-slave/upgrade-charm/+merge/95534
Your team Launchpad Yellow Squad is requested to review the proposed merge of lp:~frankban/charms/oneiric/buildbot-slave/upgrade-charm into lp:~yellow/charms/oneiric/buildbot-slave/trunk.
=== 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 @@
__metaclass__ = type
__all__ = [
'get_config',
+ 'juju_status',
'log',
'log_entry',
'log_exit',
+ 'make_charm_config_file',
'relation_get',
'relation_set',
'unit_info',
+ 'wait_for_machine',
+ 'wait_for_page_contents',
+ 'wait_for_relation',
+ 'wait_for_unit',
]
-from collections import namedtuple
+from contextlib import contextmanager
import json
import operator
from shelltoolbox import (
@@ -22,14 +28,13 @@
run,
script_name,
)
+import os
import tempfile
import time
import urllib2
import yaml
-Env = namedtuple('Env', 'uid gid home')
-
log = command('juju-log')
@@ -67,10 +72,18 @@
return charm_config_file
+def juju_status(key):
+ return yaml.safe_load(run('juju', 'status'))[key]
+
+
+def get_charm_revision(service_name):
+ service = juju_status('services')[service_name]
+ return int(service['charm'].split('-')[-1])
+
+
def unit_info(service_name, item_name, data=None):
- if data is None:
- data = yaml.safe_load(run('juju', 'status'))
- service = data['services'].get(service_name)
+ services = juju_status('services') if data is None else data['services']
+ service = services.get(service_name)
if service is None:
# XXX 2012-02-08 gmb:
# This allows us to cope with the race condition that we
@@ -83,8 +96,27 @@
return item
-def get_machine_data():
- return yaml.safe_load(run('juju', 'status'))['machines']
+@contextmanager
+def maintain_charm_revision(path=None):
+ if path is None:
+ path = os.path.join(os.path.dirname(__file__), '..', 'revision')
+ revision = open(path).read()
+ try:
+ yield revision
+ finally:
+ with open(path, 'w') as f:
+ f.write(revision)
+
+
+def upgrade_charm(service_name, timeout=120):
+ next_revision = get_charm_revision(service_name) + 1
+ start_time = time.time()
+ run('juju', 'upgrade-charm', service_name)
+ while get_charm_revision(service_name) != next_revision:
+ if time.time() - start_time >= timeout:
+ raise RuntimeError('timeout waiting for charm to be upgraded')
+ time.sleep(0.1)
+ return next_revision
def wait_for_machine(num_machines=1, timeout=300):
@@ -98,7 +130,7 @@
# to tell what environment we're working in (LXC vs EC2) is to check
# the dns-name of the first machine. If it's localhost we're in LXC
# and we can just return here.
- if get_machine_data()[0]['dns-name'] == 'localhost':
+ if juju_status('machines')[0]['dns-name'] == 'localhost':
return
start_time = time.time()
while True:
@@ -106,7 +138,7 @@
# not a machine that we need to wait for. This will only work
# for EC2 environments, which is why we return early above if
# we're in LXC.
- machine_data = get_machine_data()
+ machine_data = juju_status('machines')
non_zookeeper_machines = [
machine_data[key] for key in machine_data.keys()[1:]]
if len(non_zookeeper_machines) >= num_machines:
=== 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 @@
1
-
=== 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 @@
# GNU Affero General Public License version 3 (see the file LICENSE).
import os
+import time
import unittest
from helpers import (
command,
make_charm_config_file,
+ maintain_charm_revision,
unit_info,
wait_for_page_contents,
wait_for_relation,
wait_for_unit,
+ upgrade_charm,
)
from create_file import (
CONTENT,
@@ -155,6 +158,17 @@
self.assertEqual(installdir, ssh('cat {}'.format(PATH)))
ssh('rm {}'.format(PATH))
+ def test_upgrade_charm(self):
+ # Ensure the charm can be upgraded without errors.
+ self.deploy(self.charm_name)
+ wait_for_unit(self.charm_name)
+ with maintain_charm_revision():
+ upgrade_charm(self.charm_name)
+ # Wait for the charm to upgrade using sleep, since there is no
+ # other confirmation at the moment but the state to remain 'started'.
+ time.sleep(10)
+ self.assertEqual('started', unit_info(self.charm_name, 'state'))
+
if __name__ == '__main__':
unittest.main()
=== 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 @@
+# -*- python -*-
+# ex: set syntax=python:
+
+# This is a sample buildmaster config file. It must be installed as
+# 'master.cfg' in your buildmaster's base directory.
+
+# This is the dictionary that the buildmaster pays attention to. We also use
+# a shorter alias to save typing.
+c = BuildmasterConfig = {}
+
+####### BUILDSLAVES
+
+# The 'slaves' list defines the set of recognized buildslaves. Each element is
+# a BuildSlave object, specifying a username and password. The same username and
+# password must be configured on the slave.
+from buildbot.buildslave import BuildSlave
+c['slaves'] = []
+
+# 'slavePortnum' defines the TCP port to listen on for connections from slaves.
+# This must match the value configured into the buildslaves (with their
+# --master option)
+c['slavePortnum'] = 9989
+
+####### CHANGESOURCES
+
+# the 'change_source' setting tells the buildmaster how it should find out
+# about source code changes. Here we point to the buildbot clone of pyflakes.
+
+from buildbot.changes.gitpoller import GitPoller
+c['change_source'] = GitPoller(
+ 'git://github.com/buildbot/pyflakes.git',
+ branch='master', pollinterval=1200)
+
+####### SCHEDULERS
+
+# Configure the Schedulers, which decide how to react to incoming changes. In this
+# case, just kick off a 'runtests' build
+
+from buildbot.scheduler import Scheduler
+c['schedulers'] = []
+c['schedulers'].append(Scheduler(name="all", branch=None,
+ treeStableTimer=None,
+ builderNames=["runtests"]))
+
+####### BUILDERS
+
+# The 'builders' list defines the Builders, which tell Buildbot how to perform a build:
+# what steps, and which slaves can execute them. Note that any particular build will
+# only take place on one slave.
+
+from buildbot.process.factory import BuildFactory
+from buildbot.steps.source import Git
+from buildbot.steps.shell import ShellCommand
+
+factory = BuildFactory()
+# check out the source
+factory.addStep(Git(repourl='git://github.com/buildbot/pyflakes.git', mode='copy'))
+# run the tests (note that this will require that 'trial' is installed)
+factory.addStep(ShellCommand(command=["trial", "pyflakes"]))
+
+from buildbot.config import BuilderConfig
+
+c['builders'] = [
+ BuilderConfig(name="runtests",
+ # Buildbot enforces that the slavenames list must not be empty. Our
+ # wrapper will remove the empty string and replace it with proper values.
+ slavenames=[''],
+ factory=factory),
+ ]
+
+####### STATUS TARGETS
+
+# 'status' is a list of Status Targets. The results of each build will be
+# pushed to these targets. buildbot/status/*.py has a variety to choose from,
+# including web pages, email senders, and IRC bots.
+
+c['status'] = []
+
+from buildbot.status import html
+from buildbot.status.web import auth, authz
+authz_cfg=authz.Authz(
+ # change any of these to True to enable; see the manual for more
+ # options
+ gracefulShutdown = False,
+ forceBuild = True, # use this to test your slave once it is set up
+ forceAllBuilds = False,
+ pingBuilder = False,
+ stopBuild = False,
+ stopAllBuilds = False,
+ cancelPendingBuild = False,
+)
+c['status'].append(html.WebStatus(http_port=8010, authz=authz_cfg))
+
+####### PROJECT IDENTITY
+
+# the 'projectName' string will be used to describe the project that this
+# buildbot is working on. For example, it is used as the title of the
+# waterfall HTML page. The 'projectURL' string will be used to provide a link
+# from buildbot HTML pages to your project's home page.
+
+c['projectName'] = "Pyflakes"
+c['projectURL'] = "http://divmod.org/trac/wiki/DivmodPyflakes"
+
+# the 'buildbotURL' string should point to the location where the buildbot's
+# internal web server (usually the html.WebStatus page) is visible. This
+# typically uses the port number set in the Waterfall 'status' entry, but
+# with an externally-visible host name which the buildbot cannot figure out
+# without some help.
+
+c['buildbotURL'] = "http://localhost:8010/"
+
+####### DB URL
+
+# This specifies what database buildbot uses to store change and scheduler
+# state. You can leave this at its default for all but the largest
+# installations.
+c['db_url'] = "sqlite:///state.sqlite"
+
Follow ups