Merge lp:~harlowja/cloud-init/yum-repo-conf-write into lp:~cloud-init-dev/cloud-init/trunk
- yum-repo-conf-write
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 712 |
Proposed branch: | lp:~harlowja/cloud-init/yum-repo-conf-write |
Merge into: | lp:~cloud-init-dev/cloud-init/trunk |
Diff against target: |
346 lines (+221/-29) 9 files modified
cloudinit/config/cc_yum_add_repo.py (+106/-0) cloudinit/distros/__init__.py (+19/-21) cloudinit/sources/DataSourceAltCloud.py (+1/-1) cloudinit/util.py (+1/-2) doc/examples/cloud-config-yum-repo.txt (+20/-0) tests/configs/sample1.yaml (+0/-1) tests/unittests/test_handler/test_handler_yum_add_repo.py (+68/-0) tests/unittests/test_runs/test_simple_run.py (+4/-2) tools/hacking.py (+2/-2) |
To merge this branch: | bzr merge lp:~harlowja/cloud-init/yum-repo-conf-write |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Scott Moser | Needs Fixing | ||
Review via email: mp+129998@code.launchpad.net |
Commit message
Description of the change
- 690. By Joshua Harlow
-
Fix copyright.
- 691. By Joshua Harlow
-
Update comments as to why format is the way it is.
- 692. By Joshua Harlow
-
Add in a created by cloud-init header.
- 693. By Joshua Harlow
-
Make the yum value 'string' translation a function.
- 694. By Joshua Harlow
-
More cleanups, fix header function.
- 695. By Joshua Harlow
-
1. Rebased with trunk
2. Added example cloud-config
3. Added functioning test for yum config
Joshua Harlow (harlowja) wrote : | # |
> Pep8:
> cloudinit/
> tests/unittests
> alphabetical order (cloudinit.
> cloudinit.settings)
> tests/unittests
> lines (3)
>
> Also, needs documentation in doc/examples somewhere.
All fixed.
Scott Moser (smoser) wrote : | # |
It looks good now, except for some probably unintended whitespace cleanups / unrelated changes.
I went ahead and un-applied those, merged, and then re-applied them as a separate commit.
Joshua Harlow (harlowja) wrote : | # |
Thanks much, making sure I run pep8 more often now that it doesn't warn about everything under the sun.
Preview Diff
1 | === added file 'cloudinit/config/cc_yum_add_repo.py' |
2 | --- cloudinit/config/cc_yum_add_repo.py 1970-01-01 00:00:00 +0000 |
3 | +++ cloudinit/config/cc_yum_add_repo.py 2012-11-08 04:57:21 +0000 |
4 | @@ -0,0 +1,106 @@ |
5 | +# vi: ts=4 expandtab |
6 | +# |
7 | +# Copyright (C) 2012 Yahoo! Inc. |
8 | +# |
9 | +# Author: Joshua Harlow <harlowja@yahoo-inc.com> |
10 | +# |
11 | +# This program is free software: you can redistribute it and/or modify |
12 | +# it under the terms of the GNU General Public License version 3, as |
13 | +# published by the Free Software Foundation. |
14 | +# |
15 | +# This program is distributed in the hope that it will be useful, |
16 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | +# GNU General Public License for more details. |
19 | +# |
20 | +# You should have received a copy of the GNU General Public License |
21 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
22 | + |
23 | +import os |
24 | + |
25 | +from cloudinit import util |
26 | + |
27 | +import configobj |
28 | + |
29 | + |
30 | +def _canonicalize_id(repo_id): |
31 | + repo_id = repo_id.lower().replace("-", "_") |
32 | + repo_id = repo_id.replace(" ", "_") |
33 | + return repo_id |
34 | + |
35 | + |
36 | +def _format_repo_value(val): |
37 | + if isinstance(val, (bool)): |
38 | + # Seems like yum prefers 1/0 |
39 | + return str(int(val)) |
40 | + if isinstance(val, (list, tuple)): |
41 | + # Can handle 'lists' in certain cases |
42 | + # See: http://bit.ly/Qqrf1t |
43 | + return "\n ".join([_format_repo_value(v) for v in val]) |
44 | + if not isinstance(val, (basestring, str)): |
45 | + return str(val) |
46 | + return val |
47 | + |
48 | + |
49 | +## TODO(harlowja): move to distro? |
50 | +# See man yum.conf |
51 | +def _format_repository_config(repo_id, repo_config): |
52 | + to_be = configobj.ConfigObj() |
53 | + to_be[repo_id] = {} |
54 | + # Do basic translation of the items -> values |
55 | + for (k, v) in repo_config.items(): |
56 | + # For now assume that people using this know |
57 | + # the format of yum and don't verify keys/values further |
58 | + to_be[repo_id][k] = _format_repo_value(v) |
59 | + lines = to_be.write() |
60 | + lines.insert(0, "# Created by cloud-init on %s" % (util.time_rfc2822())) |
61 | + return "\n".join(lines) |
62 | + |
63 | + |
64 | +def handle(name, cfg, _cloud, log, _args): |
65 | + repos = cfg.get('yum_repos') |
66 | + if not repos: |
67 | + log.debug(("Skipping module named %s," |
68 | + " no 'yum_repos' configuration found"), name) |
69 | + return |
70 | + repo_base_path = util.get_cfg_option_str(cfg, 'yum_repo_dir', |
71 | + '/etc/yum.repos.d/') |
72 | + repo_locations = {} |
73 | + repo_configs = {} |
74 | + for (repo_id, repo_config) in repos.items(): |
75 | + canon_repo_id = _canonicalize_id(repo_id) |
76 | + repo_fn_pth = os.path.join(repo_base_path, "%s.repo" % (canon_repo_id)) |
77 | + if os.path.exists(repo_fn_pth): |
78 | + log.info("Skipping repo %s, file %s already exists!", |
79 | + repo_id, repo_fn_pth) |
80 | + continue |
81 | + elif canon_repo_id in repo_locations: |
82 | + log.info("Skipping repo %s, file %s already pending!", |
83 | + repo_id, repo_fn_pth) |
84 | + continue |
85 | + if not repo_config: |
86 | + repo_config = {} |
87 | + # Do some basic sanity checks/cleaning |
88 | + n_repo_config = {} |
89 | + for (k, v) in repo_config.items(): |
90 | + k = k.lower().strip().replace("-", "_") |
91 | + if k: |
92 | + n_repo_config[k] = v |
93 | + repo_config = n_repo_config |
94 | + missing_required = 0 |
95 | + for req_field in ['baseurl']: |
96 | + if not req_field in repo_config: |
97 | + log.warn(("Repository %s does not contain a %s" |
98 | + " configuration 'required' entry"), |
99 | + repo_id, req_field) |
100 | + missing_required += 1 |
101 | + if not missing_required: |
102 | + repo_configs[canon_repo_id] = repo_config |
103 | + repo_locations[canon_repo_id] = repo_fn_pth |
104 | + else: |
105 | + log.warn("Repository %s is missing %s required fields, skipping!", |
106 | + repo_id, missing_required) |
107 | + for (c_repo_id, path) in repo_locations.items(): |
108 | + repo_blob = _format_repository_config(c_repo_id, |
109 | + repo_configs.get(c_repo_id)) |
110 | + util.write_file(path, repo_blob) |
111 | |
112 | === modified file 'cloudinit/distros/__init__.py' |
113 | --- cloudinit/distros/__init__.py 2012-11-07 15:42:54 +0000 |
114 | +++ cloudinit/distros/__init__.py 2012-11-08 04:57:21 +0000 |
115 | @@ -81,7 +81,7 @@ |
116 | |
117 | def _get_arch_package_mirror_info(self, arch=None): |
118 | mirror_info = self.get_option("package_mirrors", []) |
119 | - if arch == None: |
120 | + if not arch: |
121 | arch = self.get_primary_arch() |
122 | return _get_arch_package_mirror_info(mirror_info, arch) |
123 | |
124 | @@ -187,23 +187,23 @@ |
125 | # inputs. If something goes wrong, we can end up with a system |
126 | # that nobody can login to. |
127 | adduser_opts = { |
128 | - "gecos": '--comment', |
129 | - "homedir": '--home', |
130 | - "primary_group": '--gid', |
131 | - "groups": '--groups', |
132 | - "passwd": '--password', |
133 | - "shell": '--shell', |
134 | - "expiredate": '--expiredate', |
135 | - "inactive": '--inactive', |
136 | - "selinux_user": '--selinux-user', |
137 | - } |
138 | + "gecos": '--comment', |
139 | + "homedir": '--home', |
140 | + "primary_group": '--gid', |
141 | + "groups": '--groups', |
142 | + "passwd": '--password', |
143 | + "shell": '--shell', |
144 | + "expiredate": '--expiredate', |
145 | + "inactive": '--inactive', |
146 | + "selinux_user": '--selinux-user', |
147 | + } |
148 | |
149 | adduser_opts_flags = { |
150 | - "no_user_group": '--no-user-group', |
151 | - "system": '--system', |
152 | - "no_log_init": '--no-log-init', |
153 | - "no_create_home": "-M", |
154 | - } |
155 | + "no_user_group": '--no-user-group', |
156 | + "system": '--system', |
157 | + "no_log_init": '--no-log-init', |
158 | + "no_create_home": "-M", |
159 | + } |
160 | |
161 | # Now check the value and create the command |
162 | for option in kwargs: |
163 | @@ -312,11 +312,9 @@ |
164 | raise e |
165 | util.ensure_dir(path, 0755) |
166 | |
167 | - def write_sudo_rules(self, |
168 | - user, |
169 | - rules, |
170 | - sudo_file="/etc/sudoers.d/90-cloud-init-users", |
171 | - ): |
172 | + def write_sudo_rules(self, user, rules, sudo_file=None): |
173 | + if not sudo_file: |
174 | + sudo_file = "/etc/sudoers.d/90-cloud-init-users" |
175 | |
176 | content_header = "# user rules for %s" % user |
177 | content = "%s\n%s %s\n\n" % (content_header, user, rules) |
178 | |
179 | === modified file 'cloudinit/sources/DataSourceAltCloud.py' |
180 | --- cloudinit/sources/DataSourceAltCloud.py 2012-09-21 00:56:22 +0000 |
181 | +++ cloudinit/sources/DataSourceAltCloud.py 2012-11-08 04:57:21 +0000 |
182 | @@ -47,7 +47,7 @@ |
183 | 'instance-id': 455, |
184 | 'local-hostname': 'localhost', |
185 | 'placement': {}, |
186 | - } |
187 | +} |
188 | |
189 | |
190 | def read_user_data_callback(mount_dir): |
191 | |
192 | === modified file 'cloudinit/util.py' |
193 | --- cloudinit/util.py 2012-10-23 16:58:32 +0000 |
194 | +++ cloudinit/util.py 2012-11-08 04:57:21 +0000 |
195 | @@ -1193,8 +1193,7 @@ |
196 | indent=4, |
197 | explicit_start=True, |
198 | explicit_end=True, |
199 | - default_flow_style=False, |
200 | - ) |
201 | + default_flow_style=False) |
202 | return formatted |
203 | |
204 | |
205 | |
206 | === added file 'doc/examples/cloud-config-yum-repo.txt' |
207 | --- doc/examples/cloud-config-yum-repo.txt 1970-01-01 00:00:00 +0000 |
208 | +++ doc/examples/cloud-config-yum-repo.txt 2012-11-08 04:57:21 +0000 |
209 | @@ -0,0 +1,20 @@ |
210 | +#cloud-config |
211 | +# vim: syntax=yaml |
212 | +# |
213 | +# Add yum repository configuration to the system |
214 | +# |
215 | +# The following example adds the file /etc/yum.repos.d/epel_testing.repo |
216 | +# which can then subsequently be used by yum for later operations. |
217 | +yum_repos: |
218 | + # The name of the repository |
219 | + epel-testing: |
220 | + # Any repository configuration options |
221 | + # See: man yum.conf |
222 | + # |
223 | + # This one is required! |
224 | + baseurl: http://download.fedoraproject.org/pub/epel/testing/5/$basearch |
225 | + enabled: false |
226 | + failovermethod: priority |
227 | + gpgcheck: true |
228 | + gpgkey: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL |
229 | + name: Extra Packages for Enterprise Linux 5 - Testing |
230 | |
231 | === modified file 'tests/configs/sample1.yaml' |
232 | --- tests/configs/sample1.yaml 2012-07-03 01:43:54 +0000 |
233 | +++ tests/configs/sample1.yaml 2012-11-08 04:57:21 +0000 |
234 | @@ -50,4 +50,3 @@ |
235 | |
236 | |
237 | byobu_by_default: user |
238 | -output: {all: '| tee -a /var/log/cloud-init-output.log'} |
239 | |
240 | === added file 'tests/unittests/test_handler/test_handler_yum_add_repo.py' |
241 | --- tests/unittests/test_handler/test_handler_yum_add_repo.py 1970-01-01 00:00:00 +0000 |
242 | +++ tests/unittests/test_handler/test_handler_yum_add_repo.py 2012-11-08 04:57:21 +0000 |
243 | @@ -0,0 +1,68 @@ |
244 | +from cloudinit import helpers |
245 | +from cloudinit import util |
246 | + |
247 | +from cloudinit.config import cc_yum_add_repo |
248 | + |
249 | +from tests.unittests import helpers |
250 | + |
251 | +import logging |
252 | + |
253 | +from StringIO import StringIO |
254 | + |
255 | +import configobj |
256 | + |
257 | +LOG = logging.getLogger(__name__) |
258 | + |
259 | + |
260 | +class TestConfig(helpers.FilesystemMockingTestCase): |
261 | + def setUp(self): |
262 | + super(TestConfig, self).setUp() |
263 | + self.tmp = self.makeDir(prefix="unittest_") |
264 | + |
265 | + def test_bad_config(self): |
266 | + cfg = { |
267 | + 'yum_repos': { |
268 | + 'epel-testing': { |
269 | + 'name': 'Extra Packages for Enterprise Linux 5 - Testing', |
270 | + # Missing this should cause the repo not to be written |
271 | + #'baseurl': 'http://blah.org/pub/epel/testing/5/$basearch', |
272 | + 'enabled': False, |
273 | + 'gpgcheck': True, |
274 | + 'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL', |
275 | + 'failovermethod': 'priority', |
276 | + }, |
277 | + }, |
278 | + } |
279 | + self.patchUtils(self.tmp) |
280 | + cc_yum_add_repo.handle('yum_add_repo', cfg, None, LOG, []) |
281 | + with self.assertRaises(IOError): |
282 | + util.load_file("/etc/yum.repos.d/epel_testing.repo") |
283 | + |
284 | + def test_write_config(self): |
285 | + cfg = { |
286 | + 'yum_repos': { |
287 | + 'epel-testing': { |
288 | + 'name': 'Extra Packages for Enterprise Linux 5 - Testing', |
289 | + 'baseurl': 'http://blah.org/pub/epel/testing/5/$basearch', |
290 | + 'enabled': False, |
291 | + 'gpgcheck': True, |
292 | + 'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL', |
293 | + 'failovermethod': 'priority', |
294 | + }, |
295 | + }, |
296 | + } |
297 | + self.patchUtils(self.tmp) |
298 | + cc_yum_add_repo.handle('yum_add_repo', cfg, None, LOG, []) |
299 | + contents = util.load_file("/etc/yum.repos.d/epel_testing.repo") |
300 | + contents = configobj.ConfigObj(StringIO(contents)) |
301 | + expected = { |
302 | + 'epel_testing': { |
303 | + 'name': 'Extra Packages for Enterprise Linux 5 - Testing', |
304 | + 'failovermethod': 'priority', |
305 | + 'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL', |
306 | + 'enabled': '0', |
307 | + 'baseurl': 'http://blah.org/pub/epel/testing/5/$basearch', |
308 | + 'gpgcheck': '1', |
309 | + } |
310 | + } |
311 | + self.assertEquals(expected, dict(contents)) |
312 | |
313 | === modified file 'tests/unittests/test_runs/test_simple_run.py' |
314 | --- tests/unittests/test_runs/test_simple_run.py 2012-10-28 02:25:48 +0000 |
315 | +++ tests/unittests/test_runs/test_simple_run.py 2012-11-08 04:57:21 +0000 |
316 | @@ -37,11 +37,13 @@ |
317 | self.replicateTestRoot('simple_ubuntu', new_root) |
318 | cfg = { |
319 | 'datasource_list': ['None'], |
320 | - 'write_files': [{ |
321 | + 'write_files': [ |
322 | + { |
323 | 'path': '/etc/blah.ini', |
324 | 'content': 'blah', |
325 | 'permissions': 0755, |
326 | - }], |
327 | + }, |
328 | + ], |
329 | 'cloud_init_modules': ['write-files'], |
330 | } |
331 | cloud_cfg = util.yaml_dumps(cfg) |
332 | |
333 | === modified file 'tools/hacking.py' |
334 | --- tools/hacking.py 2012-08-22 18:12:32 +0000 |
335 | +++ tools/hacking.py 2012-11-08 04:57:21 +0000 |
336 | @@ -66,8 +66,8 @@ |
337 | # handle import x |
338 | # use .lower since capitalization shouldn't dictate order |
339 | split_line = import_normalize(physical_line.strip()).lower().split() |
340 | - split_previous = import_normalize(lines[line_number - 2] |
341 | - ).strip().lower().split() |
342 | + split_previous = import_normalize(lines[line_number - 2]) |
343 | + split_previous = split_previous.strip().lower().split() |
344 | # with or without "as y" |
345 | length = [2, 4] |
346 | if (len(split_line) in length and len(split_previous) in length and |
Pep8: distros/ __init_ _.py:442: 45: W291 trailing whitespace /test_datasourc e/test_ configdrive. py:10:1: N306: imports not in alphabetical order (cloudinit. sources. datasourceconfi gdrive, cloudinit.settings) /test_datasourc e/test_ configdrive. py:16:1: E303 too many blank lines (3)
cloudinit/
tests/unittests
tests/unittests
Also, needs documentation in doc/examples somewhere.