Merge lp:~billy-olsen/charms/trusty/rabbitmq-server/ch-sync-cli-fix into lp:~openstack-charmers-archive/charms/trusty/rabbitmq-server/next

Proposed by Billy Olsen
Status: Merged
Merged at revision: 104
Proposed branch: lp:~billy-olsen/charms/trusty/rabbitmq-server/ch-sync-cli-fix
Merge into: lp:~openstack-charmers-archive/charms/trusty/rabbitmq-server/next
Diff against target: 424 lines (+366/-2)
8 files modified
charm-helpers-tests.yaml (+1/-0)
hooks/charmhelpers/core/hookenv.py (+16/-1)
tests/charmhelpers/cli/__init__.py (+195/-0)
tests/charmhelpers/cli/benchmark.py (+36/-0)
tests/charmhelpers/cli/commands.py (+32/-0)
tests/charmhelpers/cli/host.py (+31/-0)
tests/charmhelpers/cli/unitdata.py (+39/-0)
tests/charmhelpers/core/hookenv.py (+16/-1)
To merge this branch: bzr merge lp:~billy-olsen/charms/trusty/rabbitmq-server/ch-sync-cli-fix
Reviewer Review Type Date Requested Status
Edward Hope-Morley Approve
Ryan Beisner (community) Approve
Review via email: mp+266619@code.launchpad.net
To post a comment you must log in.
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #7375 rabbitmq-server-next for billy-olsen mp266619
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/7375/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_unit_test #6843 rabbitmq-server-next for billy-olsen mp266619
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/6843/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_amulet_test #5546 rabbitmq-server-next for billy-olsen mp266619
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [functional_test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/11975719/
Build: http://10.245.162.77:8080/job/charm_amulet_test/5546/

104. By Billy Olsen

Include cli in the rmq amulet

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #7376 rabbitmq-server-next for billy-olsen mp266619
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/7376/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_unit_test #6844 rabbitmq-server-next for billy-olsen mp266619
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/6844/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_amulet_test #5548 rabbitmq-server-next for billy-olsen mp266619
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [functional_test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/11976402/
Build: http://10.245.162.77:8080/job/charm_amulet_test/5548/

Revision history for this message
Ryan Beisner (1chb1n) wrote :

I would suggest merging this, even though the amulet test fails. It was previously failing that functional test.

This merge proposal resolves an import error, introduced by a charm-helpers CLI.

review: Approve
Revision history for this message
Ryan Beisner (1chb1n) wrote :

PS FYI, this is the only os-charm which had an amulet test affected by the CLI helper addition.

Revision history for this message
Ryan Beisner (1chb1n) wrote :

Regarding the pre-existing test failure, we will still need to address that separately.

Revision history for this message
Edward Hope-Morley (hopem) wrote :

Lets get this landed. It does not touch any code outside of amulet tests (which are broken anyway).

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'charm-helpers-tests.yaml'
--- charm-helpers-tests.yaml 2015-04-13 22:11:34 +0000
+++ charm-helpers-tests.yaml 2015-07-31 22:01:37 +0000
@@ -2,4 +2,5 @@
2branch: lp:charm-helpers2branch: lp:charm-helpers
3include:3include:
4 - core4 - core
5 - cli
5 - contrib.ssl6 - contrib.ssl
67
=== modified file 'hooks/charmhelpers/core/hookenv.py'
--- hooks/charmhelpers/core/hookenv.py 2015-07-31 13:11:07 +0000
+++ hooks/charmhelpers/core/hookenv.py 2015-07-31 22:01:37 +0000
@@ -34,7 +34,22 @@
34import tempfile34import tempfile
35from subprocess import CalledProcessError35from subprocess import CalledProcessError
3636
37from charmhelpers.cli import cmdline37try:
38 from charmhelpers.cli import cmdline
39except ImportError as e:
40 # due to the anti-pattern of partially synching charmhelpers directly
41 # into charms, it's possible that charmhelpers.cli is not available;
42 # if that's the case, they don't really care about using the cli anyway,
43 # so mock it out
44 if str(e) == 'No module named cli':
45 class cmdline(object):
46 @classmethod
47 def subcommand(cls, *args, **kwargs):
48 def _wrap(func):
49 return func
50 return _wrap
51 else:
52 raise
3853
39import six54import six
40if not six.PY3:55if not six.PY3:
4156
=== added directory 'tests/charmhelpers/cli'
=== added file 'tests/charmhelpers/cli/__init__.py'
--- tests/charmhelpers/cli/__init__.py 1970-01-01 00:00:00 +0000
+++ tests/charmhelpers/cli/__init__.py 2015-07-31 22:01:37 +0000
@@ -0,0 +1,195 @@
1# Copyright 2014-2015 Canonical Limited.
2#
3# This file is part of charm-helpers.
4#
5# charm-helpers is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License version 3 as
7# published by the Free Software Foundation.
8#
9# charm-helpers is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
16
17import inspect
18import argparse
19import sys
20
21from six.moves import zip
22
23from charmhelpers.core import unitdata
24
25
26class OutputFormatter(object):
27 def __init__(self, outfile=sys.stdout):
28 self.formats = (
29 "raw",
30 "json",
31 "py",
32 "yaml",
33 "csv",
34 "tab",
35 )
36 self.outfile = outfile
37
38 def add_arguments(self, argument_parser):
39 formatgroup = argument_parser.add_mutually_exclusive_group()
40 choices = self.supported_formats
41 formatgroup.add_argument("--format", metavar='FMT',
42 help="Select output format for returned data, "
43 "where FMT is one of: {}".format(choices),
44 choices=choices, default='raw')
45 for fmt in self.formats:
46 fmtfunc = getattr(self, fmt)
47 formatgroup.add_argument("-{}".format(fmt[0]),
48 "--{}".format(fmt), action='store_const',
49 const=fmt, dest='format',
50 help=fmtfunc.__doc__)
51
52 @property
53 def supported_formats(self):
54 return self.formats
55
56 def raw(self, output):
57 """Output data as raw string (default)"""
58 if isinstance(output, (list, tuple)):
59 output = '\n'.join(map(str, output))
60 self.outfile.write(str(output))
61
62 def py(self, output):
63 """Output data as a nicely-formatted python data structure"""
64 import pprint
65 pprint.pprint(output, stream=self.outfile)
66
67 def json(self, output):
68 """Output data in JSON format"""
69 import json
70 json.dump(output, self.outfile)
71
72 def yaml(self, output):
73 """Output data in YAML format"""
74 import yaml
75 yaml.safe_dump(output, self.outfile)
76
77 def csv(self, output):
78 """Output data as excel-compatible CSV"""
79 import csv
80 csvwriter = csv.writer(self.outfile)
81 csvwriter.writerows(output)
82
83 def tab(self, output):
84 """Output data in excel-compatible tab-delimited format"""
85 import csv
86 csvwriter = csv.writer(self.outfile, dialect=csv.excel_tab)
87 csvwriter.writerows(output)
88
89 def format_output(self, output, fmt='raw'):
90 fmtfunc = getattr(self, fmt)
91 fmtfunc(output)
92
93
94class CommandLine(object):
95 argument_parser = None
96 subparsers = None
97 formatter = None
98 exit_code = 0
99
100 def __init__(self):
101 if not self.argument_parser:
102 self.argument_parser = argparse.ArgumentParser(description='Perform common charm tasks')
103 if not self.formatter:
104 self.formatter = OutputFormatter()
105 self.formatter.add_arguments(self.argument_parser)
106 if not self.subparsers:
107 self.subparsers = self.argument_parser.add_subparsers(help='Commands')
108
109 def subcommand(self, command_name=None):
110 """
111 Decorate a function as a subcommand. Use its arguments as the
112 command-line arguments"""
113 def wrapper(decorated):
114 cmd_name = command_name or decorated.__name__
115 subparser = self.subparsers.add_parser(cmd_name,
116 description=decorated.__doc__)
117 for args, kwargs in describe_arguments(decorated):
118 subparser.add_argument(*args, **kwargs)
119 subparser.set_defaults(func=decorated)
120 return decorated
121 return wrapper
122
123 def test_command(self, decorated):
124 """
125 Subcommand is a boolean test function, so bool return values should be
126 converted to a 0/1 exit code.
127 """
128 decorated._cli_test_command = True
129 return decorated
130
131 def no_output(self, decorated):
132 """
133 Subcommand is not expected to return a value, so don't print a spurious None.
134 """
135 decorated._cli_no_output = True
136 return decorated
137
138 def subcommand_builder(self, command_name, description=None):
139 """
140 Decorate a function that builds a subcommand. Builders should accept a
141 single argument (the subparser instance) and return the function to be
142 run as the command."""
143 def wrapper(decorated):
144 subparser = self.subparsers.add_parser(command_name)
145 func = decorated(subparser)
146 subparser.set_defaults(func=func)
147 subparser.description = description or func.__doc__
148 return wrapper
149
150 def run(self):
151 "Run cli, processing arguments and executing subcommands."
152 arguments = self.argument_parser.parse_args()
153 argspec = inspect.getargspec(arguments.func)
154 vargs = []
155 kwargs = {}
156 for arg in argspec.args:
157 vargs.append(getattr(arguments, arg))
158 if argspec.varargs:
159 vargs.extend(getattr(arguments, argspec.varargs))
160 if argspec.keywords:
161 for kwarg in argspec.keywords.items():
162 kwargs[kwarg] = getattr(arguments, kwarg)
163 output = arguments.func(*vargs, **kwargs)
164 if getattr(arguments.func, '_cli_test_command', False):
165 self.exit_code = 0 if output else 1
166 output = ''
167 if getattr(arguments.func, '_cli_no_output', False):
168 output = ''
169 self.formatter.format_output(output, arguments.format)
170 if unitdata._KV:
171 unitdata._KV.flush()
172
173
174cmdline = CommandLine()
175
176
177def describe_arguments(func):
178 """
179 Analyze a function's signature and return a data structure suitable for
180 passing in as arguments to an argparse parser's add_argument() method."""
181
182 argspec = inspect.getargspec(func)
183 # we should probably raise an exception somewhere if func includes **kwargs
184 if argspec.defaults:
185 positional_args = argspec.args[:-len(argspec.defaults)]
186 keyword_names = argspec.args[-len(argspec.defaults):]
187 for arg, default in zip(keyword_names, argspec.defaults):
188 yield ('--{}'.format(arg),), {'default': default}
189 else:
190 positional_args = argspec.args
191
192 for arg in positional_args:
193 yield (arg,), {}
194 if argspec.varargs:
195 yield (argspec.varargs,), {'nargs': '*'}
0196
=== added file 'tests/charmhelpers/cli/benchmark.py'
--- tests/charmhelpers/cli/benchmark.py 1970-01-01 00:00:00 +0000
+++ tests/charmhelpers/cli/benchmark.py 2015-07-31 22:01:37 +0000
@@ -0,0 +1,36 @@
1# Copyright 2014-2015 Canonical Limited.
2#
3# This file is part of charm-helpers.
4#
5# charm-helpers is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License version 3 as
7# published by the Free Software Foundation.
8#
9# charm-helpers is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
16
17from . import cmdline
18from charmhelpers.contrib.benchmark import Benchmark
19
20
21@cmdline.subcommand(command_name='benchmark-start')
22def start():
23 Benchmark.start()
24
25
26@cmdline.subcommand(command_name='benchmark-finish')
27def finish():
28 Benchmark.finish()
29
30
31@cmdline.subcommand_builder('benchmark-composite', description="Set the benchmark composite score")
32def service(subparser):
33 subparser.add_argument("value", help="The composite score.")
34 subparser.add_argument("units", help="The units the composite score represents, i.e., 'reads/sec'.")
35 subparser.add_argument("direction", help="'asc' if a lower score is better, 'desc' if a higher score is better.")
36 return Benchmark.set_composite_score
037
=== added file 'tests/charmhelpers/cli/commands.py'
--- tests/charmhelpers/cli/commands.py 1970-01-01 00:00:00 +0000
+++ tests/charmhelpers/cli/commands.py 2015-07-31 22:01:37 +0000
@@ -0,0 +1,32 @@
1# Copyright 2014-2015 Canonical Limited.
2#
3# This file is part of charm-helpers.
4#
5# charm-helpers is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License version 3 as
7# published by the Free Software Foundation.
8#
9# charm-helpers is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
16
17"""
18This module loads sub-modules into the python runtime so they can be
19discovered via the inspect module. In order to prevent flake8 from (rightfully)
20telling us these are unused modules, throw a ' # noqa' at the end of each import
21so that the warning is suppressed.
22"""
23
24from . import CommandLine # noqa
25
26"""
27Import the sub-modules which have decorated subcommands to register with chlp.
28"""
29import host # noqa
30import benchmark # noqa
31import unitdata # noqa
32from charmhelpers.core import hookenv # noqa
033
=== added file 'tests/charmhelpers/cli/host.py'
--- tests/charmhelpers/cli/host.py 1970-01-01 00:00:00 +0000
+++ tests/charmhelpers/cli/host.py 2015-07-31 22:01:37 +0000
@@ -0,0 +1,31 @@
1# Copyright 2014-2015 Canonical Limited.
2#
3# This file is part of charm-helpers.
4#
5# charm-helpers is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License version 3 as
7# published by the Free Software Foundation.
8#
9# charm-helpers is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
16
17from . import cmdline
18from charmhelpers.core import host
19
20
21@cmdline.subcommand()
22def mounts():
23 "List mounts"
24 return host.mounts()
25
26
27@cmdline.subcommand_builder('service', description="Control system services")
28def service(subparser):
29 subparser.add_argument("action", help="The action to perform (start, stop, etc...)")
30 subparser.add_argument("service_name", help="Name of the service to control")
31 return host.service
032
=== added file 'tests/charmhelpers/cli/unitdata.py'
--- tests/charmhelpers/cli/unitdata.py 1970-01-01 00:00:00 +0000
+++ tests/charmhelpers/cli/unitdata.py 2015-07-31 22:01:37 +0000
@@ -0,0 +1,39 @@
1# Copyright 2014-2015 Canonical Limited.
2#
3# This file is part of charm-helpers.
4#
5# charm-helpers is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License version 3 as
7# published by the Free Software Foundation.
8#
9# charm-helpers is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
16
17from . import cmdline
18from charmhelpers.core import unitdata
19
20
21@cmdline.subcommand_builder('unitdata', description="Store and retrieve data")
22def unitdata_cmd(subparser):
23 nested = subparser.add_subparsers()
24 get_cmd = nested.add_parser('get', help='Retrieve data')
25 get_cmd.add_argument('key', help='Key to retrieve the value of')
26 get_cmd.set_defaults(action='get', value=None)
27 set_cmd = nested.add_parser('set', help='Store data')
28 set_cmd.add_argument('key', help='Key to set')
29 set_cmd.add_argument('value', help='Value to store')
30 set_cmd.set_defaults(action='set')
31
32 def _unitdata_cmd(action, key, value):
33 if action == 'get':
34 return unitdata.kv().get(key)
35 elif action == 'set':
36 unitdata.kv().set(key, value)
37 unitdata.kv().flush()
38 return ''
39 return _unitdata_cmd
040
=== modified file 'tests/charmhelpers/core/hookenv.py'
--- tests/charmhelpers/core/hookenv.py 2015-07-31 13:11:07 +0000
+++ tests/charmhelpers/core/hookenv.py 2015-07-31 22:01:37 +0000
@@ -34,7 +34,22 @@
34import tempfile34import tempfile
35from subprocess import CalledProcessError35from subprocess import CalledProcessError
3636
37from charmhelpers.cli import cmdline37try:
38 from charmhelpers.cli import cmdline
39except ImportError as e:
40 # due to the anti-pattern of partially synching charmhelpers directly
41 # into charms, it's possible that charmhelpers.cli is not available;
42 # if that's the case, they don't really care about using the cli anyway,
43 # so mock it out
44 if str(e) == 'No module named cli':
45 class cmdline(object):
46 @classmethod
47 def subcommand(cls, *args, **kwargs):
48 def _wrap(func):
49 return func
50 return _wrap
51 else:
52 raise
3853
39import six54import six
40if not six.PY3:55if not six.PY3:

Subscribers

People subscribed via source and target branches