Merge lp:~smoser/cloud-init/trunk.mcollective-cleanup into lp:~cloud-init-dev/cloud-init/trunk

Proposed by Scott Moser
Status: Merged
Merge reported by: Scott Moser
Merged at revision: not available
Proposed branch: lp:~smoser/cloud-init/trunk.mcollective-cleanup
Merge into: lp:~cloud-init-dev/cloud-init/trunk
Diff against target: 298 lines (+176/-62)
2 files modified
cloudinit/config/cc_mcollective.py (+52/-44)
tests/unittests/test_handler/test_handler_mcollective.py (+124/-18)
To merge this branch: bzr merge lp:~smoser/cloud-init/trunk.mcollective-cleanup
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Needs Fixing
Sergii Golovatiuk (community) Approve
cloud-init Commiters Pending
Review via email: mp+300394@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Sergii Golovatiuk (sgolovatiuk) wrote :

Refactoring looks good to me. It doesn't break anything. Also, Scott added more unit tests which is really good.

review: Approve
1258. By Scott Moser

move most of handle to apply_config, change tests.

1259. By Scott Moser

test dictionary in config

1260. By Scott Moser

whitespace

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

FAILED: Continuous integration, rev:1260
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~smoser/cloud-init/trunk.mcollective-cleanup/+merge/300394/+edit-commit-message

https://server-team-jenkins.canonical.com/job/cloud-init-ci/27/
Executed test runs:
    None: https://server-team-jenkins.canonical.com/job/lp-vote-on-merge/1/console

Click here to trigger a rebuild:
https://server-team-jenkins.canonical.com/job/cloud-init-ci/27/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

this got merged at 4264783ae982e48d184e5991a67deab6f63a9054

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cloudinit/config/cc_mcollective.py'
2--- cloudinit/config/cc_mcollective.py 2016-07-14 15:28:46 +0000
3+++ cloudinit/config/cc_mcollective.py 2016-07-19 15:17:25 +0000
4@@ -36,67 +36,75 @@
5 LOG = logging.getLogger(__name__)
6
7
8-def configure(config):
9+def configure(config, server_cfg=SERVER_CFG,
10+ pubcert_file=PUBCERT_FILE, pricert_file=PRICERT_FILE):
11 # Read server.cfg values from the
12 # original file in order to be able to mix the rest up
13 try:
14- mcollective_config = ConfigObj(SERVER_CFG, file_error=True)
15+ mcollective_config = ConfigObj(server_cfg, file_error=True)
16+ existed = True
17 except IOError:
18- LOG.warn("Did not find file %s", SERVER_CFG)
19- mcollective_config = ConfigObj(config)
20- else:
21- for (cfg_name, cfg) in config.items():
22- if cfg_name == 'public-cert':
23- util.write_file(PUBCERT_FILE, cfg, mode=0o644)
24- mcollective_config[
25- 'plugin.ssl_server_public'] = PUBCERT_FILE
26- mcollective_config['securityprovider'] = 'ssl'
27- elif cfg_name == 'private-cert':
28- util.write_file(PRICERT_FILE, cfg, mode=0o600)
29- mcollective_config[
30- 'plugin.ssl_server_private'] = PRICERT_FILE
31- mcollective_config['securityprovider'] = 'ssl'
32+ LOG.debug("Did not find file %s", server_cfg)
33+ mcollective_config = ConfigObj()
34+ existed = False
35+
36+ for (cfg_name, cfg) in config.items():
37+ if cfg_name == 'public-cert':
38+ util.write_file(pubcert_file, cfg, mode=0o644)
39+ mcollective_config[
40+ 'plugin.ssl_server_public'] = pubcert_file
41+ mcollective_config['securityprovider'] = 'ssl'
42+ elif cfg_name == 'private-cert':
43+ util.write_file(pricert_file, cfg, mode=0o600)
44+ mcollective_config[
45+ 'plugin.ssl_server_private'] = pricert_file
46+ mcollective_config['securityprovider'] = 'ssl'
47+ else:
48+ if isinstance(cfg, six.string_types):
49+ # Just set it in the 'main' section
50+ mcollective_config[cfg_name] = cfg
51+ elif isinstance(cfg, (dict)):
52+ # Iterate through the config items, create a section if
53+ # it is needed and then add/or create items as needed
54+ if cfg_name not in mcollective_config.sections:
55+ mcollective_config[cfg_name] = {}
56+ for (o, v) in cfg.items():
57+ mcollective_config[cfg_name][o] = v
58 else:
59- if isinstance(cfg, six.string_types):
60- # Just set it in the 'main' section
61- mcollective_config[cfg_name] = cfg
62- elif isinstance(cfg, (dict)):
63- # Iterate through the config items, create a section if
64- # it is needed and then add/or create items as needed
65- if cfg_name not in mcollective_config.sections:
66- mcollective_config[cfg_name] = {}
67- for (o, v) in cfg.items():
68- mcollective_config[cfg_name][o] = v
69- else:
70- # Otherwise just try to convert it to a string
71- mcollective_config[cfg_name] = str(cfg)
72+ # Otherwise just try to convert it to a string
73+ mcollective_config[cfg_name] = str(cfg)
74+
75+ if existed:
76 # We got all our config as wanted we'll rename
77 # the previous server.cfg and create our new one
78- util.rename(SERVER_CFG, "%s.old" % (SERVER_CFG))
79+ util.rename(server_cfg, "%s.old" % (server_cfg))
80
81 # Now we got the whole file, write to disk...
82 contents = BytesIO()
83 mcollective_config.write(contents)
84- contents = contents.getvalue()
85- util.write_file(SERVER_CFG, contents, mode=0o644)
86+ util.write_file(server_cfg, contents.getvalue(), mode=0o644)
87+
88+
89+def apply_config(config, install_packages):
90+ # Start by installing the mcollective package ...
91+ install_packages(("mcollective",))
92+
93+ # ... and then update the mcollective configuration
94+ if 'conf' in config:
95+ configure(config=config['conf'])
96+
97+ # restart mcollective to handle updated config
98+ util.subp(['service', 'mcollective', 'restart'], capture=False)
99+ return True
100
101
102 def handle(name, cfg, cloud, log, _args):
103-
104 # If there isn't a mcollective key in the configuration don't do anything
105 if 'mcollective' not in cfg:
106 log.debug(("Skipping module named %s, "
107 "no 'mcollective' key in configuration"), name)
108 return
109
110- mcollective_cfg = cfg['mcollective']
111-
112- # Start by installing the mcollective package ...
113- cloud.distro.install_packages(("mcollective",))
114-
115- # ... and then update the mcollective configuration
116- if 'conf' in mcollective_cfg:
117- configure(config=mcollective_cfg['conf'])
118-
119- # Start mcollective
120- util.subp(['service', 'mcollective', 'start'], capture=False)
121+ return apply_config(
122+ config=cfg['mcollective'],
123+ install_packages=cloud.distro.install_packages)
124
125=== modified file 'tests/unittests/test_handler/test_handler_mcollective.py'
126--- tests/unittests/test_handler/test_handler_mcollective.py 2016-07-14 15:28:46 +0000
127+++ tests/unittests/test_handler/test_handler_mcollective.py 2016-07-19 15:17:25 +0000
128@@ -1,10 +1,11 @@
129 from cloudinit.config import cc_mcollective
130 from cloudinit import util
131
132-from .. import helpers
133+from ..helpers import mock, FilesystemMockingTestCase, TestCase
134
135 import configobj
136 import logging
137+import os
138 import shutil
139 from six import BytesIO
140 import tempfile
141@@ -12,11 +13,43 @@
142 LOG = logging.getLogger(__name__)
143
144
145-class TestConfig(helpers.FilesystemMockingTestCase):
146+STOCK_CONFIG = """\
147+main_collective = mcollective
148+collectives = mcollective
149+libdir = /usr/share/mcollective/plugins
150+logfile = /var/log/mcollective.log
151+loglevel = info
152+daemonize = 1
153+
154+# Plugins
155+securityprovider = psk
156+plugin.psk = unset
157+
158+connector = activemq
159+plugin.activemq.pool.size = 1
160+plugin.activemq.pool.1.host = stomp1
161+plugin.activemq.pool.1.port = 61613
162+plugin.activemq.pool.1.user = mcollective
163+plugin.activemq.pool.1.password = marionette
164+
165+# Facts
166+factsource = yaml
167+plugin.yaml = /etc/mcollective/facts.yaml
168+"""
169+
170+
171+class TestConfig(FilesystemMockingTestCase):
172 def setUp(self):
173 super(TestConfig, self).setUp()
174 self.tmp = tempfile.mkdtemp()
175 self.addCleanup(shutil.rmtree, self.tmp)
176+ # "./": make os.path.join behave correctly with abs path as second arg
177+ self.server_cfg = os.path.join(
178+ self.tmp, "./" + cc_mcollective.SERVER_CFG)
179+ self.pubcert_file = os.path.join(
180+ self.tmp, "./" + cc_mcollective.PUBCERT_FILE)
181+ self.pricert_file = os.path.join(
182+ self.tmp, self.tmp, "./" + cc_mcollective.PRICERT_FILE)
183
184 def test_basic_config(self):
185 cfg = {
186@@ -38,23 +71,96 @@
187 },
188 },
189 }
190+ expected = cfg['mcollective']['conf']
191+
192 self.patchUtils(self.tmp)
193 cc_mcollective.configure(cfg['mcollective']['conf'])
194- contents = util.load_file("/etc/mcollective/server.cfg", decode=False)
195+ contents = util.load_file(cc_mcollective.SERVER_CFG, decode=False)
196 contents = configobj.ConfigObj(BytesIO(contents))
197- expected = {
198- 'loglevel': 'debug',
199- 'connector': 'rabbitmq',
200- 'logfile': '/var/log/mcollective.log',
201- 'ttl': '4294957',
202- 'collectives': 'mcollective',
203- 'main_collective': 'mcollective',
204- 'securityprovider': 'psk',
205- 'daemonize': '1',
206- 'factsource': 'yaml',
207- 'direct_addressing': '1',
208- 'plugin.psk': 'unset',
209- 'libdir': '/usr/share/mcollective/plugins',
210- 'identity': '1',
211- }
212 self.assertEqual(expected, dict(contents))
213+
214+ def test_existing_config_is_saved(self):
215+ cfg = {'loglevel': 'warn'}
216+ util.write_file(self.server_cfg, STOCK_CONFIG)
217+ cc_mcollective.configure(config=cfg, server_cfg=self.server_cfg)
218+ self.assertTrue(os.path.exists(self.server_cfg))
219+ self.assertTrue(os.path.exists(self.server_cfg + ".old"))
220+ self.assertEqual(util.load_file(self.server_cfg + ".old"),
221+ STOCK_CONFIG)
222+
223+ def test_existing_updated(self):
224+ cfg = {'loglevel': 'warn'}
225+ util.write_file(self.server_cfg, STOCK_CONFIG)
226+ cc_mcollective.configure(config=cfg, server_cfg=self.server_cfg)
227+ cfgobj = configobj.ConfigObj(self.server_cfg)
228+ self.assertEqual(cfg['loglevel'], cfgobj['loglevel'])
229+
230+ def test_dictionary_in_config(self):
231+ cfg = {'loglevel': 'debug', 'subfoo': {'subkey': 'subval'}}
232+ cc_mcollective.configure(config=cfg, server_cfg=self.server_cfg)
233+ cfgobj = configobj.ConfigObj(self.server_cfg)
234+ self.assertEqual(cfg['loglevel'], cfgobj['loglevel'])
235+ contents = util.load_file(self.server_cfg)
236+ self.assertIn('[subfoo]', contents)
237+ self.assertEqual(cfgobj['subfoo']['subkey'], 'subval')
238+
239+ def test_certificats_written(self):
240+ # check public-cert and private-cert keys in config get written
241+ cfg = {'loglevel': 'debug',
242+ 'public-cert': "this is my public-certificate",
243+ 'private-cert': "secret private certificate"}
244+
245+ cc_mcollective.configure(
246+ config=cfg, server_cfg=self.server_cfg,
247+ pricert_file=self.pricert_file, pubcert_file=self.pubcert_file)
248+
249+ found = configobj.ConfigObj(self.server_cfg)
250+
251+ # make sure these didnt get written in
252+ self.assertFalse('public-cert' in found)
253+ self.assertFalse('private-cert' in found)
254+
255+ # these need updating to the specified paths
256+ self.assertEqual(found['plugin.ssl_server_public'], self.pubcert_file)
257+ self.assertEqual(found['plugin.ssl_server_private'], self.pricert_file)
258+
259+ # and the security provider should be ssl
260+ self.assertEqual(found['securityprovider'], 'ssl')
261+
262+ self.assertEqual(
263+ util.load_file(self.pricert_file), cfg['private-cert'])
264+ self.assertEqual(
265+ util.load_file(self.pubcert_file), cfg['public-cert'])
266+
267+
268+class TestApplyConfig(TestCase):
269+ def setUp(self):
270+ super(TestApplyConfig, self).setUp()
271+ self.installed_packages = []
272+
273+ def _install_packages(self, pkglist):
274+ self.installed_packages.extend(pkglist)
275+
276+ @mock.patch("cloudinit.config.cc_mcollective.util.subp")
277+ @mock.patch("cloudinit.config.cc_mcollective.configure")
278+ def test_with_conf_configure_called(self, configure, subp):
279+ mycfg = {'conf': {'loglevel': 'warn'}}
280+ cc_mcollective.apply_config(
281+ mycfg, install_packages=self._install_packages)
282+ self.assertTrue(configure.called_once)
283+ self.assertEqual(configure.call_args_list,
284+ [mock.call(config=mycfg['conf'])])
285+ self.assertEqual(subp.call_args_list[0][0][0],
286+ ['service', 'mcollective', 'restart'])
287+ self.assertEqual(self.installed_packages, ['mcollective'])
288+
289+ @mock.patch("cloudinit.config.cc_mcollective.util.subp")
290+ @mock.patch("cloudinit.config.cc_mcollective.configure")
291+ def test_without_conf_configure_not_called(self, configure, subp):
292+ mycfg = {'foo': 'bar'}
293+ cc_mcollective.apply_config(
294+ mycfg, install_packages=self._install_packages)
295+ self.assertFalse(configure.called)
296+ self.assertEqual(subp.call_args_list[0][0][0],
297+ ['service', 'mcollective', 'restart'])
298+ self.assertEqual(self.installed_packages, ['mcollective'])