Merge lp:~1chb1n/charms/trusty/percona-cluster/next-amulet-initial into lp:~openstack-charmers-archive/charms/trusty/percona-cluster/next

Proposed by Ryan Beisner
Status: Work in progress
Proposed branch: lp:~1chb1n/charms/trusty/percona-cluster/next-amulet-initial
Merge into: lp:~openstack-charmers-archive/charms/trusty/percona-cluster/next
Diff against target: 608 lines (+317/-48)
20 files modified
Makefile (+5/-7)
hooks/charmhelpers/contrib/charmsupport/nrpe.py (+3/-1)
hooks/charmhelpers/contrib/database/mysql.py (+2/-2)
hooks/charmhelpers/core/hookenv.py (+40/-1)
hooks/charmhelpers/core/host.py (+5/-1)
hooks/charmhelpers/core/services/helpers.py (+2/-2)
hooks/charmhelpers/core/strutils.py (+2/-2)
hooks/charmhelpers/core/unitdata.py (+1/-1)
tests/00-setup.sh (+23/-21)
tests/015-basic-trusty-icehouse (+9/-0)
tests/016-basic-trusty-juno (+11/-0)
tests/017-basic-trusty-kilo (+11/-0)
tests/018-basic-utopic-juno (+9/-0)
tests/019-basic-vivid-kilo (+9/-0)
tests/100-deploy_test.py (+1/-1)
tests/110-broken-mysqld.py (+1/-1)
tests/120-kill-9-mysqld.py (+1/-1)
tests/basic_deployment.py (+159/-0)
tests/charmhelpers/contrib/amulet/utils.py (+8/-1)
tests/charmhelpers/contrib/openstack/amulet/deployment.py (+15/-6)
To merge this branch: bzr merge lp:~1chb1n/charms/trusty/percona-cluster/next-amulet-initial
Reviewer Review Type Date Requested Status
OpenStack Charmers Pending
Review via email: mp+257198@code.launchpad.net

Description of the change

Add initial openstack amulet tests; sync charmhelpers; keep existing tests disabled, pending validation (http://pad.lv/1446169).

To post a comment you must log in.

Unmerged revisions

63. By Ryan Beisner

amulet tests - add for openstack supported releases; disable existing tests, pending validation

62. By Ryan Beisner

amulet tests - update 00-setup, remove precise definition, mark existing tests non-executable until validated.

61. By Ryan Beisner

amulet tests - separate existing tests

60. By Ryan Beisner

amulet tests - add supported series/release definitions

59. By Ryan Beisner

amulet tests - rename existing

58. By Ryan Beisner

update makefile

57. By Ryan Beisner

sync charmhelpers

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2015-04-20 10:53:43 +0000
3+++ Makefile 2015-04-22 21:53:21 +0000
4@@ -3,18 +3,16 @@
5 export PYTHONPATH := hooks
6
7 lint:
8- @flake8 --exclude hooks/charmhelpers hooks
9+ @flake8 --exclude hooks/charmhelpers hooks unit_tests tests
10 @charm proof
11
12 unit_test:
13 @$(PYTHON) /usr/bin/nosetests --nologcapture unit_tests
14
15-test:
16- @echo Starting amulet tests...
17- #NOTE(beisner): can remove -v after bug 1320357 is fixed
18- # https://bugs.launchpad.net/amulet/+bug/1320357
19- # @juju test -v -p AMULET_HTTP_PROXY,AMULET_OS_VIP --timeout 2700
20- echo "Tests disables; http://pad.lv/1446169"
21+functional_test:
22+ @echo Starting Amulet tests...
23+ @juju test -v -p AMULET_HTTP_PROXY,AMULET_OS_VIP --timeout 2700
24+ # Previous tests disabled; http://pad.lv/1446169
25
26 bin/charm_helpers_sync.py:
27 @mkdir -p bin
28
29=== modified file 'hooks/charmhelpers/contrib/charmsupport/nrpe.py'
30--- hooks/charmhelpers/contrib/charmsupport/nrpe.py 2015-02-20 00:30:39 +0000
31+++ hooks/charmhelpers/contrib/charmsupport/nrpe.py 2015-04-22 21:53:21 +0000
32@@ -247,7 +247,9 @@
33
34 service('restart', 'nagios-nrpe-server')
35
36- for rid in relation_ids("local-monitors"):
37+ monitor_ids = relation_ids("local-monitors") + \
38+ relation_ids("nrpe-external-master")
39+ for rid in monitor_ids:
40 relation_set(relation_id=rid, monitors=yaml.dump(monitors))
41
42
43
44=== modified file 'hooks/charmhelpers/contrib/database/mysql.py'
45--- hooks/charmhelpers/contrib/database/mysql.py 2015-04-21 09:29:08 +0000
46+++ hooks/charmhelpers/contrib/database/mysql.py 2015-04-22 21:53:21 +0000
47@@ -6,7 +6,7 @@
48 import os
49 import glob
50
51-from string import upper
52+# from string import upper
53
54 from charmhelpers.core.host import (
55 mkdir,
56@@ -348,7 +348,7 @@
57 key, mem = line.split(':', 2)
58 if key == 'MemTotal':
59 mtot, modifier = mem.strip().split(' ')
60- return '%s%s' % (mtot, upper(modifier[0]))
61+ return '%s%s' % (mtot, modifier[0].upper())
62
63 def parse_config(self):
64 """Parse charm configuration and calculate values for config files."""
65
66=== modified file 'hooks/charmhelpers/core/hookenv.py'
67--- hooks/charmhelpers/core/hookenv.py 2015-02-04 18:56:00 +0000
68+++ hooks/charmhelpers/core/hookenv.py 2015-04-22 21:53:21 +0000
69@@ -20,11 +20,13 @@
70 # Authors:
71 # Charm Helpers Developers <juju@lists.ubuntu.com>
72
73+from __future__ import print_function
74 import os
75 import json
76 import yaml
77 import subprocess
78 import sys
79+import errno
80 from subprocess import CalledProcessError
81
82 import six
83@@ -87,7 +89,18 @@
84 if not isinstance(message, six.string_types):
85 message = repr(message)
86 command += [message]
87- subprocess.call(command)
88+ # Missing juju-log should not cause failures in unit tests
89+ # Send log output to stderr
90+ try:
91+ subprocess.call(command)
92+ except OSError as e:
93+ if e.errno == errno.ENOENT:
94+ if level:
95+ message = "{}: {}".format(level, message)
96+ message = "juju-log: {}".format(message)
97+ print(message, file=sys.stderr)
98+ else:
99+ raise
100
101
102 class Serializable(UserDict):
103@@ -566,3 +579,29 @@
104 def charm_dir():
105 """Return the root directory of the current charm"""
106 return os.environ.get('CHARM_DIR')
107+
108+
109+@cached
110+def action_get(key=None):
111+ """Gets the value of an action parameter, or all key/value param pairs"""
112+ cmd = ['action-get']
113+ if key is not None:
114+ cmd.append(key)
115+ cmd.append('--format=json')
116+ action_data = json.loads(subprocess.check_output(cmd).decode('UTF-8'))
117+ return action_data
118+
119+
120+def action_set(values):
121+ """Sets the values to be returned after the action finishes"""
122+ cmd = ['action-set']
123+ for k, v in list(values.items()):
124+ cmd.append('{}={}'.format(k, v))
125+ subprocess.check_call(cmd)
126+
127+
128+def action_fail(message):
129+ """Sets the action status to failed and sets the error message.
130+
131+ The results set by action_set are preserved."""
132+ subprocess.check_call(['action-fail', message])
133
134=== modified file 'hooks/charmhelpers/core/host.py'
135--- hooks/charmhelpers/core/host.py 2015-03-03 02:26:12 +0000
136+++ hooks/charmhelpers/core/host.py 2015-04-22 21:53:21 +0000
137@@ -339,12 +339,16 @@
138 def pwgen(length=None):
139 """Generate a random pasword."""
140 if length is None:
141+ # A random length is ok to use a weak PRNG
142 length = random.choice(range(35, 45))
143 alphanumeric_chars = [
144 l for l in (string.ascii_letters + string.digits)
145 if l not in 'l0QD1vAEIOUaeiou']
146+ # Use a crypto-friendly PRNG (e.g. /dev/urandom) for making the
147+ # actual password
148+ random_generator = random.SystemRandom()
149 random_chars = [
150- random.choice(alphanumeric_chars) for _ in range(length)]
151+ random_generator.choice(alphanumeric_chars) for _ in range(length)]
152 return(''.join(random_chars))
153
154
155
156=== modified file 'hooks/charmhelpers/core/services/helpers.py'
157--- hooks/charmhelpers/core/services/helpers.py 2015-03-03 02:26:12 +0000
158+++ hooks/charmhelpers/core/services/helpers.py 2015-04-22 21:53:21 +0000
159@@ -139,7 +139,7 @@
160
161 def __init__(self, *args, **kwargs):
162 self.required_keys = ['host', 'user', 'password', 'database']
163- super(HttpRelation).__init__(self, *args, **kwargs)
164+ RelationContext.__init__(self, *args, **kwargs)
165
166
167 class HttpRelation(RelationContext):
168@@ -154,7 +154,7 @@
169
170 def __init__(self, *args, **kwargs):
171 self.required_keys = ['host', 'port']
172- super(HttpRelation).__init__(self, *args, **kwargs)
173+ RelationContext.__init__(self, *args, **kwargs)
174
175 def provide_data(self):
176 return {
177
178=== modified file 'hooks/charmhelpers/core/strutils.py'
179--- hooks/charmhelpers/core/strutils.py 2015-02-25 17:29:36 +0000
180+++ hooks/charmhelpers/core/strutils.py 2015-04-22 21:53:21 +0000
181@@ -33,9 +33,9 @@
182
183 value = value.strip().lower()
184
185- if value in ['y', 'yes', 'true', 't']:
186+ if value in ['y', 'yes', 'true', 't', 'on']:
187 return True
188- elif value in ['n', 'no', 'false', 'f']:
189+ elif value in ['n', 'no', 'false', 'f', 'off']:
190 return False
191
192 msg = "Unable to interpret string value '%s' as boolean" % (value)
193
194=== modified file 'hooks/charmhelpers/core/unitdata.py'
195--- hooks/charmhelpers/core/unitdata.py 2015-02-25 17:29:36 +0000
196+++ hooks/charmhelpers/core/unitdata.py 2015-04-22 21:53:21 +0000
197@@ -443,7 +443,7 @@
198 data = hookenv.execution_environment()
199 self.conf = conf_delta = self.kv.delta(data['conf'], 'config')
200 self.rels = rels_delta = self.kv.delta(data['rels'], 'rels')
201- self.kv.set('env', data['env'])
202+ self.kv.set('env', dict(data['env']))
203 self.kv.set('unit', data['unit'])
204 self.kv.set('relid', data.get('relid'))
205 return conf_delta, rels_delta
206
207=== modified file 'tests/00-setup.sh'
208--- tests/00-setup.sh 2015-04-15 12:11:46 +0000
209+++ tests/00-setup.sh 2015-04-22 21:53:21 +0000
210@@ -1,24 +1,26 @@
211-#!/bin/bash -x
212-# The script installs amulet and other tools needed for the amulet tests.
213-
214-# Get the status of the amulet package, this returns 0 of package is installed.
215-dpkg -s amulet
216-if [ $? -ne 0 ]; then
217- # Install the Amulet testing harness.
218- sudo add-apt-repository -y ppa:juju/stable
219- sudo apt-get update
220- sudo apt-get install -y -q amulet juju-core charm-tools
221-fi
222-
223-
224-PACKAGES="python3 python3-yaml"
225-for pkg in $PACKAGES; do
226- dpkg -s python3
227- if [ $? -ne 0 ]; then
228- sudo apt-get install -y -q $pkg
229- fi
230-done
231-
232+#!/bin/bash
233+
234+set -ex
235+
236+sudo add-apt-repository --yes ppa:juju/stable
237+sudo apt-get update --yes
238+sudo apt-get install --yes python-amulet \
239+ python3 \
240+ python3-yaml \
241+ python3-pip \
242+ python-keystoneclient
243+
244+# Enable http proxy if AMULET_HTTP_PROXY is set
245+#if [[ -n "$AMULET_HTTP_PROXY" ]]; then
246+# export HTTP_PROXY=$AMULET_HTTP_PROXY
247+# export HTTPS_PROXY=$(echo $AMULET_HTTP_PROXY | sed 's/http/https/g')
248+# export http_proxy=$HTTP_PROXY
249+# export https_proxy=$HTTPS_PROXY
250+# env | egrep "proxy|PROXY"
251+#fi
252+
253+# Setup for generic mysql amulet test (Not in main < Vivid, pip it instead)
254+#sudo -E pip3 install PyMySQL
255
256 #if [ ! -f "$(dirname $0)/../local.yaml" ]; then
257 # echo "To run these amulet tests a vip is needed, create a file called \
258
259=== added file 'tests/015-basic-trusty-icehouse'
260--- tests/015-basic-trusty-icehouse 1970-01-01 00:00:00 +0000
261+++ tests/015-basic-trusty-icehouse 2015-04-22 21:53:21 +0000
262@@ -0,0 +1,9 @@
263+#!/usr/bin/python
264+
265+"""Amulet tests on a basic mysql deployment on trusty-icehouse."""
266+
267+from basic_deployment import PerconaClusterBasicDeployment
268+
269+if __name__ == '__main__':
270+ deployment = PerconaClusterBasicDeployment(series='trusty')
271+ deployment.run_tests()
272
273=== added file 'tests/016-basic-trusty-juno'
274--- tests/016-basic-trusty-juno 1970-01-01 00:00:00 +0000
275+++ tests/016-basic-trusty-juno 2015-04-22 21:53:21 +0000
276@@ -0,0 +1,11 @@
277+#!/usr/bin/python
278+
279+"""Amulet tests on a basic mysql deployment on trusty-juno."""
280+
281+from basic_deployment import PerconaClusterBasicDeployment
282+
283+if __name__ == '__main__':
284+ deployment = PerconaClusterBasicDeployment(series='trusty',
285+ openstack='cloud:trusty-juno',
286+ source='cloud:trusty-updates/juno')
287+ deployment.run_tests()
288
289=== added file 'tests/017-basic-trusty-kilo'
290--- tests/017-basic-trusty-kilo 1970-01-01 00:00:00 +0000
291+++ tests/017-basic-trusty-kilo 2015-04-22 21:53:21 +0000
292@@ -0,0 +1,11 @@
293+#!/usr/bin/python
294+
295+"""Amulet tests on a basic mysql deployment on trusty-kilo."""
296+
297+from basic_deployment import PerconaClusterBasicDeployment
298+
299+if __name__ == '__main__':
300+ deployment = PerconaClusterBasicDeployment(series='trusty',
301+ openstack='cloud:trusty-kilo',
302+ source='cloud:trusty-updates/kilo')
303+ deployment.run_tests()
304
305=== added file 'tests/018-basic-utopic-juno'
306--- tests/018-basic-utopic-juno 1970-01-01 00:00:00 +0000
307+++ tests/018-basic-utopic-juno 2015-04-22 21:53:21 +0000
308@@ -0,0 +1,9 @@
309+#!/usr/bin/python
310+
311+"""Amulet tests on a basic mysql deployment on utopic-juno."""
312+
313+from basic_deployment import PerconaClusterBasicDeployment
314+
315+if __name__ == '__main__':
316+ deployment = PerconaClusterBasicDeployment(series='utopic')
317+ deployment.run_tests()
318
319=== added file 'tests/019-basic-vivid-kilo'
320--- tests/019-basic-vivid-kilo 1970-01-01 00:00:00 +0000
321+++ tests/019-basic-vivid-kilo 2015-04-22 21:53:21 +0000
322@@ -0,0 +1,9 @@
323+#!/usr/bin/python
324+
325+"""Amulet tests on a basic mysql deployment on vivid-kilo."""
326+
327+from basic_deployment import PerconaClusterBasicDeployment
328+
329+if __name__ == '__main__':
330+ deployment = PerconaClusterBasicDeployment(series='vivid')
331+ deployment.run_tests()
332
333=== renamed file 'tests/10-deploy_test.py' => 'tests/100-deploy_test.py' (properties changed: +x to -x)
334--- tests/10-deploy_test.py 2015-03-06 15:35:01 +0000
335+++ tests/100-deploy_test.py 2015-04-22 21:53:21 +0000
336@@ -1,7 +1,7 @@
337 #!/usr/bin/python3
338 # test percona-cluster (3 nodes)
339
340-import basic_deployment
341+import 1xx_basic_deployment
342 import time
343
344
345
346=== renamed file 'tests/20-broken-mysqld.py' => 'tests/110-broken-mysqld.py' (properties changed: +x to -x)
347--- tests/20-broken-mysqld.py 2015-03-06 15:35:01 +0000
348+++ tests/110-broken-mysqld.py 2015-04-22 21:53:21 +0000
349@@ -1,7 +1,7 @@
350 #!/usr/bin/python3
351 # test percona-cluster (3 nodes)
352
353-import basic_deployment
354+import 1xx_basic_deployment
355 import time
356
357
358
359=== renamed file 'tests/30-kill-9-mysqld.py' => 'tests/120-kill-9-mysqld.py' (properties changed: +x to -x)
360--- tests/30-kill-9-mysqld.py 2015-04-15 14:24:59 +0000
361+++ tests/120-kill-9-mysqld.py 2015-04-22 21:53:21 +0000
362@@ -1,7 +1,7 @@
363 #!/usr/bin/python3
364 # test percona-cluster (3 nodes)
365
366-import basic_deployment
367+import 1xx_basic_deployment
368 import time
369
370
371
372=== renamed file 'tests/basic_deployment.py' => 'tests/1xx_basic_deployment.py'
373=== added file 'tests/basic_deployment.py'
374--- tests/basic_deployment.py 1970-01-01 00:00:00 +0000
375+++ tests/basic_deployment.py 2015-04-22 21:53:21 +0000
376@@ -0,0 +1,159 @@
377+#!/usr/bin/python
378+
379+import amulet
380+
381+from charmhelpers.contrib.openstack.amulet.deployment import (
382+ OpenStackAmuletDeployment
383+)
384+
385+from charmhelpers.contrib.openstack.amulet.utils import ( # noqa
386+ OpenStackAmuletUtils,
387+ DEBUG,
388+ ERROR
389+)
390+
391+# Use DEBUG to turn on debug logging
392+u = OpenStackAmuletUtils(DEBUG)
393+
394+
395+class PerconaClusterBasicDeployment(OpenStackAmuletDeployment):
396+ """Amulet tests on a basic percona-cluster deployment."""
397+
398+ def __init__(self, series=None, openstack=None, source=None,
399+ git=False, stable=False):
400+ """Deploy the test environment."""
401+ super(PerconaClusterBasicDeployment, self).__init__(series, openstack,
402+ source, stable)
403+ self.git = git
404+ self._add_services()
405+ self._add_relations()
406+ self._configure_services()
407+ self._deploy()
408+ self._initialize_tests()
409+
410+ def _add_services(self):
411+ """Add services
412+
413+ Add the services that we're testing, where MySQL is local,
414+ and the rest of the service are from lp branches that are
415+ compatible with the local charm (e.g. stable or next).
416+ """
417+ this_service = {'name': 'mysql'}
418+ other_services = [{'name': 'keystone'}]
419+ super(PerconaClusterBasicDeployment, self)._add_services(this_service,
420+ other_services)
421+
422+ def _add_relations(self):
423+ """Add all of the relations for the services."""
424+ relations = {'mysql:shared-db': 'keystone:shared-db'}
425+ super(PerconaClusterBasicDeployment, self)._add_relations(relations)
426+
427+ def _configure_services(self):
428+ """Configure all of the services."""
429+
430+ mysql_config = {}
431+ keystone_config = {'admin-password': 'openstack',
432+ 'admin-token': 'ubuntutesting'}
433+
434+ configs = {'mysql': mysql_config,
435+ 'keystone': keystone_config}
436+ super(PerconaClusterBasicDeployment, self)._configure_services(configs)
437+
438+ def _initialize_tests(self):
439+ """Perform final initialization before tests get run."""
440+ # Access the sentries for inspecting service units
441+ self.mysql_sentry = self.d.sentry.unit['mysql/0']
442+ self.keystone_sentry = self.d.sentry.unit['keystone/0']
443+
444+ # Authenticate keystone admin
445+ self.keystone = u.authenticate_keystone_admin(self.keystone_sentry,
446+ user='admin',
447+ password='openstack',
448+ tenant='admin')
449+
450+ def test_100_services(self):
451+ """Verify the expected services are running on the corresponding
452+ service units."""
453+ commands = {
454+ self.mysql_sentry: ['status mysql'],
455+ self.keystone_sentry: ['status keystone']
456+ }
457+ ret = u.validate_services(commands)
458+ if ret:
459+ amulet.raise_status(amulet.FAIL, msg=ret)
460+
461+ def test_120_pxc_keystone_database_query(self):
462+ """Verify that the user table in the keystone mysql database
463+ contains an admin user with a specific email address."""
464+
465+ cmd = ("export FOO=$(sudo cat /var/lib/mysql/mysql.passwd);"
466+ "mysql -u root -p$FOO -e "
467+ "\"SELECT extra FROM keystone.user WHERE name='admin';\"")
468+
469+ output, retcode = self.mysql_sentry.run(cmd)
470+ u.log.debug('command: `{}` returned {}'.format(cmd, retcode))
471+ u.log.debug('output:\n{}'.format(output))
472+
473+ if retcode:
474+ msg = "command `{}` returned {}".format(cmd, str(retcode))
475+ amulet.raise_status(amulet.FAIL, msg=msg)
476+
477+ if "juju@localhost" not in output:
478+ msg = ("keystone mysql database query produced "
479+ "unexpected data:\n{}".format(output))
480+ amulet.raise_status(amulet.FAIL, msg=msg)
481+
482+ def test_150_pxc_shared_db_relation(self):
483+ """Verify the mysql shared-db relation data"""
484+ unit = self.mysql_sentry
485+ relation = ['shared-db', 'keystone:shared-db']
486+ expected_data = {
487+ 'allowed_units': 'keystone/0',
488+ 'private-address': u.valid_ip,
489+ 'password': u.not_null,
490+ 'db_host': u.valid_ip
491+ }
492+ ret = u.validate_relation_data(unit, relation, expected_data)
493+ if ret:
494+ message = u.relation_error('mysql shared-db', ret)
495+ amulet.raise_status(amulet.FAIL, msg=message)
496+
497+ def test_200_pxc_default_config(self):
498+ """Verify some important confg data in the mysql config file's
499+ mysqld section."""
500+ unit = self.mysql_sentry
501+ conf = '/etc/mysql/my.cnf'
502+ relation = unit.relation('shared-db', 'keystone:shared-db')
503+ u.log.debug('relation: {}'.format(relation))
504+ expected = {'user': 'mysql',
505+ 'socket': '/var/run/mysqld/mysqld.sock',
506+ 'port': '3306',
507+ 'basedir': '/usr',
508+ 'datadir': '/var/lib/mysql',
509+ 'myisam-recover': 'BACKUP',
510+ 'query_cache_size': '0',
511+ 'query_cache_type': '0',
512+ 'tmpdir': '/tmp',
513+ 'bind-address': '0.0.0.0',
514+ 'log_error': '/var/log/mysql/error.log',
515+ 'character-set-server': 'utf8'}
516+
517+ ret = u.validate_config_data(unit, conf, 'mysqld', expected)
518+ if ret:
519+ message = "mysql config error: {}".format(ret)
520+ amulet.raise_status(amulet.FAIL, msg=message)
521+
522+ def test_900_restart_on_config_change(self):
523+ """Verify that mysql is restarted when the config is changed."""
524+
525+ logging.debug("Changing config on mysql...")
526+ self.d.configure('mysql', {'dataset-size': '50%'})
527+
528+ logging.debug("Checking that the service has restarted...")
529+ if not u.service_restarted(self.mysql_sentry, 'mysql',
530+ '/etc/mysql/my.cnf',
531+ sleep_time=30):
532+ self.d.configure('mysql', {'dataset-size': '80%'})
533+ message = "mysql service didn't restart after config change"
534+ amulet.raise_status(amulet.FAIL, msg=message)
535+ self.d.configure('mysql', {'dataset-size': '80%'})
536
537=== modified file 'tests/charmhelpers/contrib/amulet/utils.py'
538--- tests/charmhelpers/contrib/amulet/utils.py 2015-04-15 14:23:37 +0000
539+++ tests/charmhelpers/contrib/amulet/utils.py 2015-04-22 21:53:21 +0000
540@@ -79,6 +79,9 @@
541 for k, v in six.iteritems(commands):
542 for cmd in v:
543 output, code = k.run(cmd)
544+ self.log.debug('{} `{}` returned '
545+ '{}'.format(k.info['unit_name'],
546+ cmd, code))
547 if code != 0:
548 return "command `{}` returned {}".format(cmd, str(code))
549 return None
550@@ -86,7 +89,11 @@
551 def _get_config(self, unit, filename):
552 """Get a ConfigParser object for parsing a unit's config file."""
553 file_contents = unit.file_contents(filename)
554- config = ConfigParser.ConfigParser()
555+
556+ # NOTE(beisner): by default, ConfigParser does not handle options
557+ # with no value, such as the flags used in the mysql my.cnf file.
558+ # https://bugs.python.org/issue7005
559+ config = ConfigParser.ConfigParser(allow_no_value=True)
560 config.readfp(io.StringIO(file_contents))
561 return config
562
563
564=== modified file 'tests/charmhelpers/contrib/openstack/amulet/deployment.py'
565--- tests/charmhelpers/contrib/openstack/amulet/deployment.py 2015-04-17 10:04:59 +0000
566+++ tests/charmhelpers/contrib/openstack/amulet/deployment.py 2015-04-22 21:53:21 +0000
567@@ -46,15 +46,22 @@
568 stable or next branches for the other_services."""
569 base_charms = ['mysql', 'mongodb']
570
571+ if self.series in ['precise', 'trusty']:
572+ base_series = self.series
573+ else:
574+ base_series = self.current_next
575+
576 if self.stable:
577 for svc in other_services:
578- temp = 'lp:charms/{}'
579- svc['location'] = temp.format(svc['name'])
580+ temp = 'lp:charms/{}/{}'
581+ svc['location'] = temp.format(base_series,
582+ svc['name'])
583 else:
584 for svc in other_services:
585 if svc['name'] in base_charms:
586- temp = 'lp:charms/{}'
587- svc['location'] = temp.format(svc['name'])
588+ temp = 'lp:charms/{}/{}'
589+ svc['location'] = temp.format(base_series,
590+ svc['name'])
591 else:
592 temp = 'lp:~openstack-charmers/charms/{}/{}/next'
593 svc['location'] = temp.format(self.current_next,
594@@ -99,10 +106,12 @@
595 Return an integer representing the enum value of the openstack
596 release.
597 """
598+ # Must be ordered by OpenStack release (not by Ubuntu release):
599 (self.precise_essex, self.precise_folsom, self.precise_grizzly,
600 self.precise_havana, self.precise_icehouse,
601- self.trusty_icehouse, self.trusty_juno, self.trusty_kilo,
602- self.utopic_juno, self.vivid_kilo) = range(10)
603+ self.trusty_icehouse, self.trusty_juno, self.utopic_juno,
604+ self.trusty_kilo, self.vivid_kilo) = range(10)
605+
606 releases = {
607 ('precise', None): self.precise_essex,
608 ('precise', 'cloud:precise-folsom'): self.precise_folsom,

Subscribers

People subscribed via source and target branches