Merge ~chad.smith/cloud-init:ubuntu/xenial into cloud-init:ubuntu/xenial
- Git
- lp:~chad.smith/cloud-init
- ubuntu/xenial
- Merge into ubuntu/xenial
Proposed by
Chad Smith
Status: | Merged |
---|---|
Merged at revision: | b5ca0b1343faaf1a802ff3ff1c4231e1304ad770 |
Proposed branch: | ~chad.smith/cloud-init:ubuntu/xenial |
Merge into: | cloud-init:ubuntu/xenial |
Diff against target: |
1042 lines (+258/-289) 23 files modified
cloudinit/config/cc_lxd.py (+1/-1) cloudinit/config/cc_ntp.py (+3/-1) cloudinit/config/cc_resizefs.py (+13/-30) cloudinit/config/cc_users_groups.py (+2/-1) cloudinit/config/schema.py (+1/-1) debian/changelog (+14/-0) debian/patches/series (+0/-1) dev/null (+0/-181) doc/examples/cloud-config-user-groups.txt (+3/-3) tests/cloud_tests/testcases/__init__.py (+7/-0) tests/cloud_tests/testcases/base.py (+8/-4) tests/cloud_tests/testcases/examples/including_user_groups.py (+6/-0) tests/cloud_tests/testcases/examples/including_user_groups.yaml (+5/-2) tests/cloud_tests/testcases/main/command_output_simple.py (+16/-0) tests/cloud_tests/testcases/modules/ntp.yaml (+2/-2) tests/cloud_tests/testcases/modules/user_groups.py (+6/-0) tests/cloud_tests/testcases/modules/user_groups.yaml (+5/-2) tests/unittests/test_handler/test_handler_lxd.py (+8/-8) tests/unittests/test_handler/test_handler_ntp.py (+12/-11) tests/unittests/test_handler/test_handler_resizefs.py (+57/-34) tests/unittests/test_handler/test_schema.py (+36/-1) tools/read-dependencies (+36/-5) tools/run-centos (+17/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Server Team CI bot | continuous-integration | Approve | |
Scott Moser | Pending | ||
Review via email: mp+332670@code.launchpad.net |
Commit message
Description of the change
Upstream snapshot or master for SRU in xenial
To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote : | # |
review:
Approve
(continuous-integration)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/cloudinit/config/cc_lxd.py b/cloudinit/config/cc_lxd.py | |||
2 | index e6262f8..09374d2 100644 | |||
3 | --- a/cloudinit/config/cc_lxd.py | |||
4 | +++ b/cloudinit/config/cc_lxd.py | |||
5 | @@ -72,7 +72,7 @@ def handle(name, cfg, cloud, log, args): | |||
6 | 72 | type(init_cfg)) | 72 | type(init_cfg)) |
7 | 73 | init_cfg = {} | 73 | init_cfg = {} |
8 | 74 | 74 | ||
10 | 75 | bridge_cfg = lxd_cfg.get('bridge') | 75 | bridge_cfg = lxd_cfg.get('bridge', {}) |
11 | 76 | if not isinstance(bridge_cfg, dict): | 76 | if not isinstance(bridge_cfg, dict): |
12 | 77 | log.warn("lxd/bridge config must be a dictionary. found a '%s'", | 77 | log.warn("lxd/bridge config must be a dictionary. found a '%s'", |
13 | 78 | type(bridge_cfg)) | 78 | type(bridge_cfg)) |
14 | diff --git a/cloudinit/config/cc_ntp.py b/cloudinit/config/cc_ntp.py | |||
15 | index 15ae1ec..d43d060 100644 | |||
16 | --- a/cloudinit/config/cc_ntp.py | |||
17 | +++ b/cloudinit/config/cc_ntp.py | |||
18 | @@ -100,7 +100,9 @@ def handle(name, cfg, cloud, log, _args): | |||
19 | 100 | LOG.debug( | 100 | LOG.debug( |
20 | 101 | "Skipping module named %s, not present or disabled by cfg", name) | 101 | "Skipping module named %s, not present or disabled by cfg", name) |
21 | 102 | return | 102 | return |
23 | 103 | ntp_cfg = cfg.get('ntp', {}) | 103 | ntp_cfg = cfg['ntp'] |
24 | 104 | if ntp_cfg is None: | ||
25 | 105 | ntp_cfg = {} # Allow empty config which will install the package | ||
26 | 104 | 106 | ||
27 | 105 | # TODO drop this when validate_cloudconfig_schema is strict=True | 107 | # TODO drop this when validate_cloudconfig_schema is strict=True |
28 | 106 | if not isinstance(ntp_cfg, (dict)): | 108 | if not isinstance(ntp_cfg, (dict)): |
29 | diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py | |||
30 | index f774baa..0d282e6 100644 | |||
31 | --- a/cloudinit/config/cc_resizefs.py | |||
32 | +++ b/cloudinit/config/cc_resizefs.py | |||
33 | @@ -145,25 +145,6 @@ RESIZE_FS_PRECHECK_CMDS = { | |||
34 | 145 | } | 145 | } |
35 | 146 | 146 | ||
36 | 147 | 147 | ||
37 | 148 | def rootdev_from_cmdline(cmdline): | ||
38 | 149 | found = None | ||
39 | 150 | for tok in cmdline.split(): | ||
40 | 151 | if tok.startswith("root="): | ||
41 | 152 | found = tok[5:] | ||
42 | 153 | break | ||
43 | 154 | if found is None: | ||
44 | 155 | return None | ||
45 | 156 | |||
46 | 157 | if found.startswith("/dev/"): | ||
47 | 158 | return found | ||
48 | 159 | if found.startswith("LABEL="): | ||
49 | 160 | return "/dev/disk/by-label/" + found[len("LABEL="):] | ||
50 | 161 | if found.startswith("UUID="): | ||
51 | 162 | return "/dev/disk/by-uuid/" + found[len("UUID="):] | ||
52 | 163 | |||
53 | 164 | return "/dev/" + found | ||
54 | 165 | |||
55 | 166 | |||
56 | 167 | def can_skip_resize(fs_type, resize_what, devpth): | 148 | def can_skip_resize(fs_type, resize_what, devpth): |
57 | 168 | fstype_lc = fs_type.lower() | 149 | fstype_lc = fs_type.lower() |
58 | 169 | for i, func in RESIZE_FS_PRECHECK_CMDS.items(): | 150 | for i, func in RESIZE_FS_PRECHECK_CMDS.items(): |
59 | @@ -172,14 +153,15 @@ def can_skip_resize(fs_type, resize_what, devpth): | |||
60 | 172 | return False | 153 | return False |
61 | 173 | 154 | ||
62 | 174 | 155 | ||
65 | 175 | def is_device_path_writable_block(devpath, info, log): | 156 | def maybe_get_writable_device_path(devpath, info, log): |
66 | 176 | """Return True if devpath is a writable block device. | 157 | """Return updated devpath if the devpath is a writable block device. |
67 | 177 | 158 | ||
69 | 178 | @param devpath: Path to the root device we want to resize. | 159 | @param devpath: Requested path to the root device we want to resize. |
70 | 179 | @param info: String representing information about the requested device. | 160 | @param info: String representing information about the requested device. |
71 | 180 | @param log: Logger to which logs will be added upon error. | 161 | @param log: Logger to which logs will be added upon error. |
72 | 181 | 162 | ||
74 | 182 | @returns Boolean True if block device is writable | 163 | @returns devpath or updated devpath per kernel commandline if the device |
75 | 164 | path is a writable block device, returns None otherwise. | ||
76 | 183 | """ | 165 | """ |
77 | 184 | container = util.is_container() | 166 | container = util.is_container() |
78 | 185 | 167 | ||
79 | @@ -189,12 +171,12 @@ def is_device_path_writable_block(devpath, info, log): | |||
80 | 189 | devpath = util.rootdev_from_cmdline(util.get_cmdline()) | 171 | devpath = util.rootdev_from_cmdline(util.get_cmdline()) |
81 | 190 | if devpath is None: | 172 | if devpath is None: |
82 | 191 | log.warn("Unable to find device '/dev/root'") | 173 | log.warn("Unable to find device '/dev/root'") |
84 | 192 | return False | 174 | return None |
85 | 193 | log.debug("Converted /dev/root to '%s' per kernel cmdline", devpath) | 175 | log.debug("Converted /dev/root to '%s' per kernel cmdline", devpath) |
86 | 194 | 176 | ||
87 | 195 | if devpath == 'overlayroot': | 177 | if devpath == 'overlayroot': |
88 | 196 | log.debug("Not attempting to resize devpath '%s': %s", devpath, info) | 178 | log.debug("Not attempting to resize devpath '%s': %s", devpath, info) |
90 | 197 | return False | 179 | return None |
91 | 198 | 180 | ||
92 | 199 | try: | 181 | try: |
93 | 200 | statret = os.stat(devpath) | 182 | statret = os.stat(devpath) |
94 | @@ -207,7 +189,7 @@ def is_device_path_writable_block(devpath, info, log): | |||
95 | 207 | devpath, info) | 189 | devpath, info) |
96 | 208 | else: | 190 | else: |
97 | 209 | raise exc | 191 | raise exc |
99 | 210 | return False | 192 | return None |
100 | 211 | 193 | ||
101 | 212 | if not stat.S_ISBLK(statret.st_mode) and not stat.S_ISCHR(statret.st_mode): | 194 | if not stat.S_ISBLK(statret.st_mode) and not stat.S_ISCHR(statret.st_mode): |
102 | 213 | if container: | 195 | if container: |
103 | @@ -216,8 +198,8 @@ def is_device_path_writable_block(devpath, info, log): | |||
104 | 216 | else: | 198 | else: |
105 | 217 | log.warn("device '%s' not a block device. cannot resize: %s" % | 199 | log.warn("device '%s' not a block device. cannot resize: %s" % |
106 | 218 | (devpath, info)) | 200 | (devpath, info)) |
109 | 219 | return False | 201 | return None |
110 | 220 | return True | 202 | return devpath # The writable block devpath |
111 | 221 | 203 | ||
112 | 222 | 204 | ||
113 | 223 | def handle(name, cfg, _cloud, log, args): | 205 | def handle(name, cfg, _cloud, log, args): |
114 | @@ -242,8 +224,9 @@ def handle(name, cfg, _cloud, log, args): | |||
115 | 242 | info = "dev=%s mnt_point=%s path=%s" % (devpth, mount_point, resize_what) | 224 | info = "dev=%s mnt_point=%s path=%s" % (devpth, mount_point, resize_what) |
116 | 243 | log.debug("resize_info: %s" % info) | 225 | log.debug("resize_info: %s" % info) |
117 | 244 | 226 | ||
120 | 245 | if not is_device_path_writable_block(devpth, info, log): | 227 | devpth = maybe_get_writable_device_path(devpth, info, log) |
121 | 246 | return | 228 | if not devpth: |
122 | 229 | return # devpath was not a writable block device | ||
123 | 247 | 230 | ||
124 | 248 | resizer = None | 231 | resizer = None |
125 | 249 | if can_skip_resize(fs_type, resize_what, devpth): | 232 | if can_skip_resize(fs_type, resize_what, devpth): |
126 | diff --git a/cloudinit/config/cc_users_groups.py b/cloudinit/config/cc_users_groups.py | |||
127 | index b80d1d3..f363000 100644 | |||
128 | --- a/cloudinit/config/cc_users_groups.py | |||
129 | +++ b/cloudinit/config/cc_users_groups.py | |||
130 | @@ -15,7 +15,8 @@ options, see the ``Including users and groups`` config example. | |||
131 | 15 | Groups to add to the system can be specified as a list under the ``groups`` | 15 | Groups to add to the system can be specified as a list under the ``groups`` |
132 | 16 | key. Each entry in the list should either contain a the group name as a string, | 16 | key. Each entry in the list should either contain a the group name as a string, |
133 | 17 | or a dictionary with the group name as the key and a list of users who should | 17 | or a dictionary with the group name as the key and a list of users who should |
135 | 18 | be members of the group as the value. | 18 | be members of the group as the value. **Note**: Groups are added before users, |
136 | 19 | so any users in a group list must already exist on the system. | ||
137 | 19 | 20 | ||
138 | 20 | The ``users`` config key takes a list of users to configure. The first entry in | 21 | The ``users`` config key takes a list of users to configure. The first entry in |
139 | 21 | this list is used as the default user for the system. To preserve the standard | 22 | this list is used as the default user for the system. To preserve the standard |
140 | diff --git a/cloudinit/config/schema.py b/cloudinit/config/schema.py | |||
141 | index bb291ff..ca7d0d5 100644 | |||
142 | --- a/cloudinit/config/schema.py | |||
143 | +++ b/cloudinit/config/schema.py | |||
144 | @@ -74,7 +74,7 @@ def validate_cloudconfig_schema(config, schema, strict=False): | |||
145 | 74 | try: | 74 | try: |
146 | 75 | from jsonschema import Draft4Validator, FormatChecker | 75 | from jsonschema import Draft4Validator, FormatChecker |
147 | 76 | except ImportError: | 76 | except ImportError: |
149 | 77 | logging.warning( | 77 | logging.debug( |
150 | 78 | 'Ignoring schema validation. python-jsonschema is not present') | 78 | 'Ignoring schema validation. python-jsonschema is not present') |
151 | 79 | return | 79 | return |
152 | 80 | validator = Draft4Validator(schema, format_checker=FormatChecker()) | 80 | validator = Draft4Validator(schema, format_checker=FormatChecker()) |
153 | diff --git a/debian/changelog b/debian/changelog | |||
154 | index 5a66def..e7ffc6f 100644 | |||
155 | --- a/debian/changelog | |||
156 | +++ b/debian/changelog | |||
157 | @@ -1,3 +1,17 @@ | |||
158 | 1 | cloud-init (17.1-25-g17a15f9e-0ubuntu1~16.04.1) xenial-proposed; urgency=medium | ||
159 | 2 | |||
160 | 3 | * New upstream snapshot. | ||
161 | 4 | - resizefs: Fix regression when system booted with root=PARTUUID= | ||
162 | 5 | (LP: #1725067) | ||
163 | 6 | - tools: make yum package installation more reliable | ||
164 | 7 | - citest: fix remaining warnings raised by integration tests. | ||
165 | 8 | - citest: show the class actual class name in results. | ||
166 | 9 | - ntp: fix config module schema to allow empty ntp config | ||
167 | 10 | (LP: #1724951) | ||
168 | 11 | - tools: disable fastestmirror if using proxy [Joshua Powers] | ||
169 | 12 | |||
170 | 13 | -- Chad Smith <chad.smith@canonical.com> Mon, 23 Oct 2017 14:54:05 -0600 | ||
171 | 14 | |||
172 | 1 | cloud-init (17.1-18-gd4f70470-0ubuntu1~16.04.2) xenial-proposed; urgency=medium | 15 | cloud-init (17.1-18-gd4f70470-0ubuntu1~16.04.2) xenial-proposed; urgency=medium |
173 | 2 | 16 | ||
174 | 3 | * cherry-pick 41152f1: schema: Log debug instead of warning when | 17 | * cherry-pick 41152f1: schema: Log debug instead of warning when |
175 | diff --git a/debian/patches/cpick-41152f1-schema-Log-debug-instead-of-warning-when-jsonschema-is b/debian/patches/cpick-41152f1-schema-Log-debug-instead-of-warning-when-jsonschema-is | |||
176 | 4 | deleted file mode 100644 | 18 | deleted file mode 100644 |
177 | index 6b9e784..0000000 | |||
178 | --- a/debian/patches/cpick-41152f1-schema-Log-debug-instead-of-warning-when-jsonschema-is | |||
179 | +++ /dev/null | |||
180 | @@ -1,181 +0,0 @@ | |||
181 | 1 | From 41152f10ddbd8681cdac44b408038a4f23ab02df Mon Sep 17 00:00:00 2001 | ||
182 | 2 | From: Scott Moser <smoser@brickies.net> | ||
183 | 3 | Date: Tue, 17 Oct 2017 16:12:59 -0400 | ||
184 | 4 | Subject: [PATCH] schema: Log debug instead of warning when jsonschema is not | ||
185 | 5 | available. | ||
186 | 6 | |||
187 | 7 | When operating in expected path, cloud-init should avoid logging with | ||
188 | 8 | warning. That causes 'WARNING' messages in /var/log/cloud-init.log. | ||
189 | 9 | By default, warnings also go to the console. | ||
190 | 10 | |||
191 | 11 | Since jsonschema is a optional dependency, and not present on xenial | ||
192 | 12 | and zesty, cloud-init should not warn there. | ||
193 | 13 | |||
194 | 14 | Also here: | ||
195 | 15 | * Add a test to integration tests to assert that there are no | ||
196 | 16 | warnings in /var/log/cloud-init.log. | ||
197 | 17 | * Update one integration test that did show warning and the related | ||
198 | 18 | documentation and examples. | ||
199 | 19 | |||
200 | 20 | LP: #1724354 | ||
201 | 21 | --- | ||
202 | 22 | cloudinit/config/cc_users_groups.py | 3 ++- | ||
203 | 23 | cloudinit/config/schema.py | 2 +- | ||
204 | 24 | doc/examples/cloud-config-user-groups.txt | 6 +++--- | ||
205 | 25 | tests/cloud_tests/testcases/base.py | 4 ++++ | ||
206 | 26 | tests/cloud_tests/testcases/examples/including_user_groups.py | 6 ++++++ | ||
207 | 27 | tests/cloud_tests/testcases/examples/including_user_groups.yaml | 7 +++++-- | ||
208 | 28 | tests/cloud_tests/testcases/modules/user_groups.py | 6 ++++++ | ||
209 | 29 | tests/cloud_tests/testcases/modules/user_groups.yaml | 7 +++++-- | ||
210 | 30 | 8 files changed, 32 insertions(+), 9 deletions(-) | ||
211 | 31 | |||
212 | 32 | Index: cloud-init/cloudinit/config/cc_users_groups.py | ||
213 | 33 | =================================================================== | ||
214 | 34 | --- cloud-init.orig/cloudinit/config/cc_users_groups.py | ||
215 | 35 | +++ cloud-init/cloudinit/config/cc_users_groups.py | ||
216 | 36 | @@ -15,7 +15,8 @@ options, see the ``Including users and g | ||
217 | 37 | Groups to add to the system can be specified as a list under the ``groups`` | ||
218 | 38 | key. Each entry in the list should either contain a the group name as a string, | ||
219 | 39 | or a dictionary with the group name as the key and a list of users who should | ||
220 | 40 | -be members of the group as the value. | ||
221 | 41 | +be members of the group as the value. **Note**: Groups are added before users, | ||
222 | 42 | +so any users in a group list must already exist on the system. | ||
223 | 43 | |||
224 | 44 | The ``users`` config key takes a list of users to configure. The first entry in | ||
225 | 45 | this list is used as the default user for the system. To preserve the standard | ||
226 | 46 | Index: cloud-init/cloudinit/config/schema.py | ||
227 | 47 | =================================================================== | ||
228 | 48 | --- cloud-init.orig/cloudinit/config/schema.py | ||
229 | 49 | +++ cloud-init/cloudinit/config/schema.py | ||
230 | 50 | @@ -74,7 +74,7 @@ def validate_cloudconfig_schema(config, | ||
231 | 51 | try: | ||
232 | 52 | from jsonschema import Draft4Validator, FormatChecker | ||
233 | 53 | except ImportError: | ||
234 | 54 | - logging.warning( | ||
235 | 55 | + logging.debug( | ||
236 | 56 | 'Ignoring schema validation. python-jsonschema is not present') | ||
237 | 57 | return | ||
238 | 58 | validator = Draft4Validator(schema, format_checker=FormatChecker()) | ||
239 | 59 | Index: cloud-init/doc/examples/cloud-config-user-groups.txt | ||
240 | 60 | =================================================================== | ||
241 | 61 | --- cloud-init.orig/doc/examples/cloud-config-user-groups.txt | ||
242 | 62 | +++ cloud-init/doc/examples/cloud-config-user-groups.txt | ||
243 | 63 | @@ -1,8 +1,8 @@ | ||
244 | 64 | # Add groups to the system | ||
245 | 65 | -# The following example adds the ubuntu group with members foo and bar and | ||
246 | 66 | -# the group cloud-users. | ||
247 | 67 | +# The following example adds the ubuntu group with members 'root' and 'sys' | ||
248 | 68 | +# and the empty group cloud-users. | ||
249 | 69 | groups: | ||
250 | 70 | - - ubuntu: [foo,bar] | ||
251 | 71 | + - ubuntu: [root,sys] | ||
252 | 72 | - cloud-users | ||
253 | 73 | |||
254 | 74 | # Add users to the system. Users are added after groups are added. | ||
255 | 75 | Index: cloud-init/tests/cloud_tests/testcases/base.py | ||
256 | 76 | =================================================================== | ||
257 | 77 | --- cloud-init.orig/tests/cloud_tests/testcases/base.py | ||
258 | 78 | +++ cloud-init/tests/cloud_tests/testcases/base.py | ||
259 | 79 | @@ -72,6 +72,10 @@ class CloudTestCase(unittest.TestCase): | ||
260 | 80 | result = self.get_status_data(self.get_data_file('result.json')) | ||
261 | 81 | self.assertEqual(len(result['errors']), 0) | ||
262 | 82 | |||
263 | 83 | + def test_no_warnings_in_log(self): | ||
264 | 84 | + """Warnings should not be found in the log.""" | ||
265 | 85 | + self.assertNotIn("WARN", self.get_data_file('cloud-init.log')) | ||
266 | 86 | + | ||
267 | 87 | |||
268 | 88 | class PasswordListTest(CloudTestCase): | ||
269 | 89 | """Base password test case class.""" | ||
270 | 90 | Index: cloud-init/tests/cloud_tests/testcases/examples/including_user_groups.py | ||
271 | 91 | =================================================================== | ||
272 | 92 | --- cloud-init.orig/tests/cloud_tests/testcases/examples/including_user_groups.py | ||
273 | 93 | +++ cloud-init/tests/cloud_tests/testcases/examples/including_user_groups.py | ||
274 | 94 | @@ -40,4 +40,10 @@ class TestUserGroups(base.CloudTestCase) | ||
275 | 95 | out = self.get_data_file('user_cloudy') | ||
276 | 96 | self.assertRegex(out, r'cloudy:x:[0-9]{3,4}:') | ||
277 | 97 | |||
278 | 98 | + def test_user_root_in_secret(self): | ||
279 | 99 | + """Test root user is in 'secret' group.""" | ||
280 | 100 | + user, _, groups = self.get_data_file('root_groups').partition(":") | ||
281 | 101 | + self.assertIn("secret", groups.split(), | ||
282 | 102 | + msg="User root is not in group 'secret'") | ||
283 | 103 | + | ||
284 | 104 | # vi: ts=4 expandtab | ||
285 | 105 | Index: cloud-init/tests/cloud_tests/testcases/examples/including_user_groups.yaml | ||
286 | 106 | =================================================================== | ||
287 | 107 | --- cloud-init.orig/tests/cloud_tests/testcases/examples/including_user_groups.yaml | ||
288 | 108 | +++ cloud-init/tests/cloud_tests/testcases/examples/including_user_groups.yaml | ||
289 | 109 | @@ -8,7 +8,7 @@ cloud_config: | | ||
290 | 110 | #cloud-config | ||
291 | 111 | # Add groups to the system | ||
292 | 112 | groups: | ||
293 | 113 | - - secret: [foobar,barfoo] | ||
294 | 114 | + - secret: [root] | ||
295 | 115 | - cloud-users | ||
296 | 116 | |||
297 | 117 | # Add users to the system. Users are added after groups are added. | ||
298 | 118 | @@ -24,7 +24,7 @@ cloud_config: | | ||
299 | 119 | - name: barfoo | ||
300 | 120 | gecos: Bar B. Foo | ||
301 | 121 | sudo: ALL=(ALL) NOPASSWD:ALL | ||
302 | 122 | - groups: cloud-users | ||
303 | 123 | + groups: [cloud-users, secret] | ||
304 | 124 | lock_passwd: true | ||
305 | 125 | - name: cloudy | ||
306 | 126 | gecos: Magic Cloud App Daemon User | ||
307 | 127 | @@ -49,5 +49,8 @@ collect_scripts: | ||
308 | 128 | user_cloudy: | | ||
309 | 129 | #!/bin/bash | ||
310 | 130 | getent passwd cloudy | ||
311 | 131 | + root_groups: | | ||
312 | 132 | + #!/bin/bash | ||
313 | 133 | + groups root | ||
314 | 134 | |||
315 | 135 | # vi: ts=4 expandtab | ||
316 | 136 | Index: cloud-init/tests/cloud_tests/testcases/modules/user_groups.py | ||
317 | 137 | =================================================================== | ||
318 | 138 | --- cloud-init.orig/tests/cloud_tests/testcases/modules/user_groups.py | ||
319 | 139 | +++ cloud-init/tests/cloud_tests/testcases/modules/user_groups.py | ||
320 | 140 | @@ -40,4 +40,10 @@ class TestUserGroups(base.CloudTestCase) | ||
321 | 141 | out = self.get_data_file('user_cloudy') | ||
322 | 142 | self.assertRegex(out, r'cloudy:x:[0-9]{3,4}:') | ||
323 | 143 | |||
324 | 144 | + def test_user_root_in_secret(self): | ||
325 | 145 | + """Test root user is in 'secret' group.""" | ||
326 | 146 | + user, _, groups = self.get_data_file('root_groups').partition(":") | ||
327 | 147 | + self.assertIn("secret", groups.split(), | ||
328 | 148 | + msg="User root is not in group 'secret'") | ||
329 | 149 | + | ||
330 | 150 | # vi: ts=4 expandtab | ||
331 | 151 | Index: cloud-init/tests/cloud_tests/testcases/modules/user_groups.yaml | ||
332 | 152 | =================================================================== | ||
333 | 153 | --- cloud-init.orig/tests/cloud_tests/testcases/modules/user_groups.yaml | ||
334 | 154 | +++ cloud-init/tests/cloud_tests/testcases/modules/user_groups.yaml | ||
335 | 155 | @@ -7,7 +7,7 @@ cloud_config: | | ||
336 | 156 | #cloud-config | ||
337 | 157 | # Add groups to the system | ||
338 | 158 | groups: | ||
339 | 159 | - - secret: [foobar,barfoo] | ||
340 | 160 | + - secret: [root] | ||
341 | 161 | - cloud-users | ||
342 | 162 | |||
343 | 163 | # Add users to the system. Users are added after groups are added. | ||
344 | 164 | @@ -23,7 +23,7 @@ cloud_config: | | ||
345 | 165 | - name: barfoo | ||
346 | 166 | gecos: Bar B. Foo | ||
347 | 167 | sudo: ALL=(ALL) NOPASSWD:ALL | ||
348 | 168 | - groups: cloud-users | ||
349 | 169 | + groups: [cloud-users, secret] | ||
350 | 170 | lock_passwd: true | ||
351 | 171 | - name: cloudy | ||
352 | 172 | gecos: Magic Cloud App Daemon User | ||
353 | 173 | @@ -48,5 +48,8 @@ collect_scripts: | ||
354 | 174 | user_cloudy: | | ||
355 | 175 | #!/bin/bash | ||
356 | 176 | getent passwd cloudy | ||
357 | 177 | + root_groups: | | ||
358 | 178 | + #!/bin/bash | ||
359 | 179 | + groups root | ||
360 | 180 | |||
361 | 181 | # vi: ts=4 expandtab | ||
362 | diff --git a/debian/patches/series b/debian/patches/series | |||
363 | index 8bba1e4..7e909af 100644 | |||
364 | --- a/debian/patches/series | |||
365 | +++ b/debian/patches/series | |||
366 | @@ -1,4 +1,3 @@ | |||
367 | 1 | azure-use-walinux-agent.patch | 1 | azure-use-walinux-agent.patch |
368 | 2 | ds-identify-behavior-xenial.patch | 2 | ds-identify-behavior-xenial.patch |
369 | 3 | stable-release-no-jsonschema-dep.patch | 3 | stable-release-no-jsonschema-dep.patch |
370 | 4 | cpick-41152f1-schema-Log-debug-instead-of-warning-when-jsonschema-is | ||
371 | diff --git a/doc/examples/cloud-config-user-groups.txt b/doc/examples/cloud-config-user-groups.txt | |||
372 | index 9c5202f..0554d1f 100644 | |||
373 | --- a/doc/examples/cloud-config-user-groups.txt | |||
374 | +++ b/doc/examples/cloud-config-user-groups.txt | |||
375 | @@ -1,8 +1,8 @@ | |||
376 | 1 | # Add groups to the system | 1 | # Add groups to the system |
379 | 2 | # The following example adds the ubuntu group with members foo and bar and | 2 | # The following example adds the ubuntu group with members 'root' and 'sys' |
380 | 3 | # the group cloud-users. | 3 | # and the empty group cloud-users. |
381 | 4 | groups: | 4 | groups: |
383 | 5 | - ubuntu: [foo,bar] | 5 | - ubuntu: [root,sys] |
384 | 6 | - cloud-users | 6 | - cloud-users |
385 | 7 | 7 | ||
386 | 8 | # Add users to the system. Users are added after groups are added. | 8 | # Add users to the system. Users are added after groups are added. |
387 | diff --git a/tests/cloud_tests/testcases/__init__.py b/tests/cloud_tests/testcases/__init__.py | |||
388 | index 47217ce..a29a092 100644 | |||
389 | --- a/tests/cloud_tests/testcases/__init__.py | |||
390 | +++ b/tests/cloud_tests/testcases/__init__.py | |||
391 | @@ -5,6 +5,7 @@ | |||
392 | 5 | import importlib | 5 | import importlib |
393 | 6 | import inspect | 6 | import inspect |
394 | 7 | import unittest | 7 | import unittest |
395 | 8 | from unittest.util import strclass | ||
396 | 8 | 9 | ||
397 | 9 | from tests.cloud_tests import config | 10 | from tests.cloud_tests import config |
398 | 10 | from tests.cloud_tests.testcases.base import CloudTestCase as base_test | 11 | from tests.cloud_tests.testcases.base import CloudTestCase as base_test |
399 | @@ -37,6 +38,12 @@ def get_suite(test_name, data, conf): | |||
400 | 37 | 38 | ||
401 | 38 | class tmp(test_class): | 39 | class tmp(test_class): |
402 | 39 | 40 | ||
403 | 41 | _realclass = test_class | ||
404 | 42 | |||
405 | 43 | def __str__(self): | ||
406 | 44 | return "%s (%s)" % (self._testMethodName, | ||
407 | 45 | strclass(self._realclass)) | ||
408 | 46 | |||
409 | 40 | @classmethod | 47 | @classmethod |
410 | 41 | def setUpClass(cls): | 48 | def setUpClass(cls): |
411 | 42 | cls.data = data | 49 | cls.data = data |
412 | diff --git a/tests/cloud_tests/testcases/base.py b/tests/cloud_tests/testcases/base.py | |||
413 | index bb545ab..1706f59 100644 | |||
414 | --- a/tests/cloud_tests/testcases/base.py | |||
415 | +++ b/tests/cloud_tests/testcases/base.py | |||
416 | @@ -16,10 +16,6 @@ class CloudTestCase(unittest.TestCase): | |||
417 | 16 | conf = None | 16 | conf = None |
418 | 17 | _cloud_config = None | 17 | _cloud_config = None |
419 | 18 | 18 | ||
420 | 19 | def shortDescription(self): | ||
421 | 20 | """Prevent nose from using docstrings.""" | ||
422 | 21 | return None | ||
423 | 22 | |||
424 | 23 | @property | 19 | @property |
425 | 24 | def cloud_config(self): | 20 | def cloud_config(self): |
426 | 25 | """Get the cloud-config used by the test.""" | 21 | """Get the cloud-config used by the test.""" |
427 | @@ -72,6 +68,14 @@ class CloudTestCase(unittest.TestCase): | |||
428 | 72 | result = self.get_status_data(self.get_data_file('result.json')) | 68 | result = self.get_status_data(self.get_data_file('result.json')) |
429 | 73 | self.assertEqual(len(result['errors']), 0) | 69 | self.assertEqual(len(result['errors']), 0) |
430 | 74 | 70 | ||
431 | 71 | def test_no_warnings_in_log(self): | ||
432 | 72 | """Warnings should not be found in the log.""" | ||
433 | 73 | self.assertEqual( | ||
434 | 74 | [], | ||
435 | 75 | [l for l in self.get_data_file('cloud-init.log').splitlines() | ||
436 | 76 | if 'WARN' in l], | ||
437 | 77 | msg="'WARN' found inside cloud-init.log") | ||
438 | 78 | |||
439 | 75 | 79 | ||
440 | 76 | class PasswordListTest(CloudTestCase): | 80 | class PasswordListTest(CloudTestCase): |
441 | 77 | """Base password test case class.""" | 81 | """Base password test case class.""" |
442 | diff --git a/tests/cloud_tests/testcases/examples/including_user_groups.py b/tests/cloud_tests/testcases/examples/including_user_groups.py | |||
443 | index 67af527..93b7a82 100644 | |||
444 | --- a/tests/cloud_tests/testcases/examples/including_user_groups.py | |||
445 | +++ b/tests/cloud_tests/testcases/examples/including_user_groups.py | |||
446 | @@ -40,4 +40,10 @@ class TestUserGroups(base.CloudTestCase): | |||
447 | 40 | out = self.get_data_file('user_cloudy') | 40 | out = self.get_data_file('user_cloudy') |
448 | 41 | self.assertRegex(out, r'cloudy:x:[0-9]{3,4}:') | 41 | self.assertRegex(out, r'cloudy:x:[0-9]{3,4}:') |
449 | 42 | 42 | ||
450 | 43 | def test_user_root_in_secret(self): | ||
451 | 44 | """Test root user is in 'secret' group.""" | ||
452 | 45 | user, _, groups = self.get_data_file('root_groups').partition(":") | ||
453 | 46 | self.assertIn("secret", groups.split(), | ||
454 | 47 | msg="User root is not in group 'secret'") | ||
455 | 48 | |||
456 | 43 | # vi: ts=4 expandtab | 49 | # vi: ts=4 expandtab |
457 | diff --git a/tests/cloud_tests/testcases/examples/including_user_groups.yaml b/tests/cloud_tests/testcases/examples/including_user_groups.yaml | |||
458 | index 0aa7ad2..469d03c 100644 | |||
459 | --- a/tests/cloud_tests/testcases/examples/including_user_groups.yaml | |||
460 | +++ b/tests/cloud_tests/testcases/examples/including_user_groups.yaml | |||
461 | @@ -8,7 +8,7 @@ cloud_config: | | |||
462 | 8 | #cloud-config | 8 | #cloud-config |
463 | 9 | # Add groups to the system | 9 | # Add groups to the system |
464 | 10 | groups: | 10 | groups: |
466 | 11 | - secret: [foobar,barfoo] | 11 | - secret: [root] |
467 | 12 | - cloud-users | 12 | - cloud-users |
468 | 13 | 13 | ||
469 | 14 | # Add users to the system. Users are added after groups are added. | 14 | # Add users to the system. Users are added after groups are added. |
470 | @@ -24,7 +24,7 @@ cloud_config: | | |||
471 | 24 | - name: barfoo | 24 | - name: barfoo |
472 | 25 | gecos: Bar B. Foo | 25 | gecos: Bar B. Foo |
473 | 26 | sudo: ALL=(ALL) NOPASSWD:ALL | 26 | sudo: ALL=(ALL) NOPASSWD:ALL |
475 | 27 | groups: cloud-users | 27 | groups: [cloud-users, secret] |
476 | 28 | lock_passwd: true | 28 | lock_passwd: true |
477 | 29 | - name: cloudy | 29 | - name: cloudy |
478 | 30 | gecos: Magic Cloud App Daemon User | 30 | gecos: Magic Cloud App Daemon User |
479 | @@ -49,5 +49,8 @@ collect_scripts: | |||
480 | 49 | user_cloudy: | | 49 | user_cloudy: | |
481 | 50 | #!/bin/bash | 50 | #!/bin/bash |
482 | 51 | getent passwd cloudy | 51 | getent passwd cloudy |
483 | 52 | root_groups: | | ||
484 | 53 | #!/bin/bash | ||
485 | 54 | groups root | ||
486 | 52 | 55 | ||
487 | 53 | # vi: ts=4 expandtab | 56 | # vi: ts=4 expandtab |
488 | diff --git a/tests/cloud_tests/testcases/main/command_output_simple.py b/tests/cloud_tests/testcases/main/command_output_simple.py | |||
489 | index fe4c767..857881c 100644 | |||
490 | --- a/tests/cloud_tests/testcases/main/command_output_simple.py | |||
491 | +++ b/tests/cloud_tests/testcases/main/command_output_simple.py | |||
492 | @@ -15,4 +15,20 @@ class TestCommandOutputSimple(base.CloudTestCase): | |||
493 | 15 | data.splitlines()[-1].strip()) | 15 | data.splitlines()[-1].strip()) |
494 | 16 | # TODO: need to test that all stages redirected here | 16 | # TODO: need to test that all stages redirected here |
495 | 17 | 17 | ||
496 | 18 | def test_no_warnings_in_log(self): | ||
497 | 19 | """Warnings should not be found in the log. | ||
498 | 20 | |||
499 | 21 | This class redirected stderr and stdout, so it expects to find | ||
500 | 22 | a warning in cloud-init.log to that effect.""" | ||
501 | 23 | redirect_msg = 'Stdout, stderr changing to' | ||
502 | 24 | warnings = [ | ||
503 | 25 | l for l in self.get_data_file('cloud-init.log').splitlines() | ||
504 | 26 | if 'WARN' in l] | ||
505 | 27 | self.assertEqual( | ||
506 | 28 | [], [w for w in warnings if redirect_msg not in w], | ||
507 | 29 | msg="'WARN' found inside cloud-init.log") | ||
508 | 30 | self.assertEqual( | ||
509 | 31 | 1, len(warnings), | ||
510 | 32 | msg="Did not find %s in cloud-init.log" % redirect_msg) | ||
511 | 33 | |||
512 | 18 | # vi: ts=4 expandtab | 34 | # vi: ts=4 expandtab |
513 | diff --git a/tests/cloud_tests/testcases/modules/ntp.yaml b/tests/cloud_tests/testcases/modules/ntp.yaml | |||
514 | index fbef431..2530d72 100644 | |||
515 | --- a/tests/cloud_tests/testcases/modules/ntp.yaml | |||
516 | +++ b/tests/cloud_tests/testcases/modules/ntp.yaml | |||
517 | @@ -4,8 +4,8 @@ | |||
518 | 4 | cloud_config: | | 4 | cloud_config: | |
519 | 5 | #cloud-config | 5 | #cloud-config |
520 | 6 | ntp: | 6 | ntp: |
523 | 7 | pools: {} | 7 | pools: [] |
524 | 8 | servers: {} | 8 | servers: [] |
525 | 9 | collect_scripts: | 9 | collect_scripts: |
526 | 10 | ntp_installed: | | 10 | ntp_installed: | |
527 | 11 | #!/bin/bash | 11 | #!/bin/bash |
528 | diff --git a/tests/cloud_tests/testcases/modules/user_groups.py b/tests/cloud_tests/testcases/modules/user_groups.py | |||
529 | index 67af527..93b7a82 100644 | |||
530 | --- a/tests/cloud_tests/testcases/modules/user_groups.py | |||
531 | +++ b/tests/cloud_tests/testcases/modules/user_groups.py | |||
532 | @@ -40,4 +40,10 @@ class TestUserGroups(base.CloudTestCase): | |||
533 | 40 | out = self.get_data_file('user_cloudy') | 40 | out = self.get_data_file('user_cloudy') |
534 | 41 | self.assertRegex(out, r'cloudy:x:[0-9]{3,4}:') | 41 | self.assertRegex(out, r'cloudy:x:[0-9]{3,4}:') |
535 | 42 | 42 | ||
536 | 43 | def test_user_root_in_secret(self): | ||
537 | 44 | """Test root user is in 'secret' group.""" | ||
538 | 45 | user, _, groups = self.get_data_file('root_groups').partition(":") | ||
539 | 46 | self.assertIn("secret", groups.split(), | ||
540 | 47 | msg="User root is not in group 'secret'") | ||
541 | 48 | |||
542 | 43 | # vi: ts=4 expandtab | 49 | # vi: ts=4 expandtab |
543 | diff --git a/tests/cloud_tests/testcases/modules/user_groups.yaml b/tests/cloud_tests/testcases/modules/user_groups.yaml | |||
544 | index 71cc9da..22b5d70 100644 | |||
545 | --- a/tests/cloud_tests/testcases/modules/user_groups.yaml | |||
546 | +++ b/tests/cloud_tests/testcases/modules/user_groups.yaml | |||
547 | @@ -7,7 +7,7 @@ cloud_config: | | |||
548 | 7 | #cloud-config | 7 | #cloud-config |
549 | 8 | # Add groups to the system | 8 | # Add groups to the system |
550 | 9 | groups: | 9 | groups: |
552 | 10 | - secret: [foobar,barfoo] | 10 | - secret: [root] |
553 | 11 | - cloud-users | 11 | - cloud-users |
554 | 12 | 12 | ||
555 | 13 | # Add users to the system. Users are added after groups are added. | 13 | # Add users to the system. Users are added after groups are added. |
556 | @@ -23,7 +23,7 @@ cloud_config: | | |||
557 | 23 | - name: barfoo | 23 | - name: barfoo |
558 | 24 | gecos: Bar B. Foo | 24 | gecos: Bar B. Foo |
559 | 25 | sudo: ALL=(ALL) NOPASSWD:ALL | 25 | sudo: ALL=(ALL) NOPASSWD:ALL |
561 | 26 | groups: cloud-users | 26 | groups: [cloud-users, secret] |
562 | 27 | lock_passwd: true | 27 | lock_passwd: true |
563 | 28 | - name: cloudy | 28 | - name: cloudy |
564 | 29 | gecos: Magic Cloud App Daemon User | 29 | gecos: Magic Cloud App Daemon User |
565 | @@ -48,5 +48,8 @@ collect_scripts: | |||
566 | 48 | user_cloudy: | | 48 | user_cloudy: | |
567 | 49 | #!/bin/bash | 49 | #!/bin/bash |
568 | 50 | getent passwd cloudy | 50 | getent passwd cloudy |
569 | 51 | root_groups: | | ||
570 | 52 | #!/bin/bash | ||
571 | 53 | groups root | ||
572 | 51 | 54 | ||
573 | 52 | # vi: ts=4 expandtab | 55 | # vi: ts=4 expandtab |
574 | diff --git a/tests/unittests/test_handler/test_handler_lxd.py b/tests/unittests/test_handler/test_handler_lxd.py | |||
575 | index f132a77..e0d9ab6 100644 | |||
576 | --- a/tests/unittests/test_handler/test_handler_lxd.py | |||
577 | +++ b/tests/unittests/test_handler/test_handler_lxd.py | |||
578 | @@ -5,17 +5,16 @@ from cloudinit.sources import DataSourceNoCloud | |||
579 | 5 | from cloudinit import (distros, helpers, cloud) | 5 | from cloudinit import (distros, helpers, cloud) |
580 | 6 | from cloudinit.tests import helpers as t_help | 6 | from cloudinit.tests import helpers as t_help |
581 | 7 | 7 | ||
582 | 8 | import logging | ||
583 | 9 | |||
584 | 10 | try: | 8 | try: |
585 | 11 | from unittest import mock | 9 | from unittest import mock |
586 | 12 | except ImportError: | 10 | except ImportError: |
587 | 13 | import mock | 11 | import mock |
588 | 14 | 12 | ||
589 | 15 | LOG = logging.getLogger(__name__) | ||
590 | 16 | 13 | ||
591 | 14 | class TestLxd(t_help.CiTestCase): | ||
592 | 15 | |||
593 | 16 | with_logs = True | ||
594 | 17 | 17 | ||
595 | 18 | class TestLxd(t_help.TestCase): | ||
596 | 19 | lxd_cfg = { | 18 | lxd_cfg = { |
597 | 20 | 'lxd': { | 19 | 'lxd': { |
598 | 21 | 'init': { | 20 | 'init': { |
599 | @@ -41,7 +40,7 @@ class TestLxd(t_help.TestCase): | |||
600 | 41 | def test_lxd_init(self, mock_util): | 40 | def test_lxd_init(self, mock_util): |
601 | 42 | cc = self._get_cloud('ubuntu') | 41 | cc = self._get_cloud('ubuntu') |
602 | 43 | mock_util.which.return_value = True | 42 | mock_util.which.return_value = True |
604 | 44 | cc_lxd.handle('cc_lxd', self.lxd_cfg, cc, LOG, []) | 43 | cc_lxd.handle('cc_lxd', self.lxd_cfg, cc, self.logger, []) |
605 | 45 | self.assertTrue(mock_util.which.called) | 44 | self.assertTrue(mock_util.which.called) |
606 | 46 | init_call = mock_util.subp.call_args_list[0][0][0] | 45 | init_call = mock_util.subp.call_args_list[0][0][0] |
607 | 47 | self.assertEqual(init_call, | 46 | self.assertEqual(init_call, |
608 | @@ -55,7 +54,8 @@ class TestLxd(t_help.TestCase): | |||
609 | 55 | cc = self._get_cloud('ubuntu') | 54 | cc = self._get_cloud('ubuntu') |
610 | 56 | cc.distro = mock.MagicMock() | 55 | cc.distro = mock.MagicMock() |
611 | 57 | mock_util.which.return_value = None | 56 | mock_util.which.return_value = None |
613 | 58 | cc_lxd.handle('cc_lxd', self.lxd_cfg, cc, LOG, []) | 57 | cc_lxd.handle('cc_lxd', self.lxd_cfg, cc, self.logger, []) |
614 | 58 | self.assertNotIn('WARN', self.logs.getvalue()) | ||
615 | 59 | self.assertTrue(cc.distro.install_packages.called) | 59 | self.assertTrue(cc.distro.install_packages.called) |
616 | 60 | install_pkg = cc.distro.install_packages.call_args_list[0][0][0] | 60 | install_pkg = cc.distro.install_packages.call_args_list[0][0][0] |
617 | 61 | self.assertEqual(sorted(install_pkg), ['lxd', 'zfs']) | 61 | self.assertEqual(sorted(install_pkg), ['lxd', 'zfs']) |
618 | @@ -64,7 +64,7 @@ class TestLxd(t_help.TestCase): | |||
619 | 64 | def test_no_init_does_nothing(self, mock_util): | 64 | def test_no_init_does_nothing(self, mock_util): |
620 | 65 | cc = self._get_cloud('ubuntu') | 65 | cc = self._get_cloud('ubuntu') |
621 | 66 | cc.distro = mock.MagicMock() | 66 | cc.distro = mock.MagicMock() |
623 | 67 | cc_lxd.handle('cc_lxd', {'lxd': {}}, cc, LOG, []) | 67 | cc_lxd.handle('cc_lxd', {'lxd': {}}, cc, self.logger, []) |
624 | 68 | self.assertFalse(cc.distro.install_packages.called) | 68 | self.assertFalse(cc.distro.install_packages.called) |
625 | 69 | self.assertFalse(mock_util.subp.called) | 69 | self.assertFalse(mock_util.subp.called) |
626 | 70 | 70 | ||
627 | @@ -72,7 +72,7 @@ class TestLxd(t_help.TestCase): | |||
628 | 72 | def test_no_lxd_does_nothing(self, mock_util): | 72 | def test_no_lxd_does_nothing(self, mock_util): |
629 | 73 | cc = self._get_cloud('ubuntu') | 73 | cc = self._get_cloud('ubuntu') |
630 | 74 | cc.distro = mock.MagicMock() | 74 | cc.distro = mock.MagicMock() |
632 | 75 | cc_lxd.handle('cc_lxd', {'package_update': True}, cc, LOG, []) | 75 | cc_lxd.handle('cc_lxd', {'package_update': True}, cc, self.logger, []) |
633 | 76 | self.assertFalse(cc.distro.install_packages.called) | 76 | self.assertFalse(cc.distro.install_packages.called) |
634 | 77 | self.assertFalse(mock_util.subp.called) | 77 | self.assertFalse(mock_util.subp.called) |
635 | 78 | 78 | ||
636 | diff --git a/tests/unittests/test_handler/test_handler_ntp.py b/tests/unittests/test_handler/test_handler_ntp.py | |||
637 | index 4f29124..3abe578 100644 | |||
638 | --- a/tests/unittests/test_handler/test_handler_ntp.py | |||
639 | +++ b/tests/unittests/test_handler/test_handler_ntp.py | |||
640 | @@ -293,23 +293,24 @@ class TestNtp(FilesystemMockingTestCase): | |||
641 | 293 | 293 | ||
642 | 294 | def test_ntp_handler_schema_validation_allows_empty_ntp_config(self): | 294 | def test_ntp_handler_schema_validation_allows_empty_ntp_config(self): |
643 | 295 | """Ntp schema validation allows for an empty ntp: configuration.""" | 295 | """Ntp schema validation allows for an empty ntp: configuration.""" |
645 | 296 | invalid_config = {'ntp': {}} | 296 | valid_empty_configs = [{'ntp': {}}, {'ntp': None}] |
646 | 297 | distro = 'ubuntu' | 297 | distro = 'ubuntu' |
647 | 298 | cc = self._get_cloud(distro) | 298 | cc = self._get_cloud(distro) |
648 | 299 | ntp_conf = os.path.join(self.new_root, 'ntp.conf') | 299 | ntp_conf = os.path.join(self.new_root, 'ntp.conf') |
649 | 300 | with open('{0}.tmpl'.format(ntp_conf), 'wb') as stream: | 300 | with open('{0}.tmpl'.format(ntp_conf), 'wb') as stream: |
650 | 301 | stream.write(NTP_TEMPLATE) | 301 | stream.write(NTP_TEMPLATE) |
653 | 302 | with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf): | 302 | for valid_empty_config in valid_empty_configs: |
654 | 303 | cc_ntp.handle('cc_ntp', invalid_config, cc, None, []) | 303 | with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf): |
655 | 304 | cc_ntp.handle('cc_ntp', valid_empty_config, cc, None, []) | ||
656 | 305 | with open(ntp_conf) as stream: | ||
657 | 306 | content = stream.read() | ||
658 | 307 | default_pools = [ | ||
659 | 308 | "{0}.{1}.pool.ntp.org".format(x, distro) | ||
660 | 309 | for x in range(0, cc_ntp.NR_POOL_SERVERS)] | ||
661 | 310 | self.assertEqual( | ||
662 | 311 | "servers []\npools {0}\n".format(default_pools), | ||
663 | 312 | content) | ||
664 | 304 | self.assertNotIn('Invalid config:', self.logs.getvalue()) | 313 | self.assertNotIn('Invalid config:', self.logs.getvalue()) |
665 | 305 | with open(ntp_conf) as stream: | ||
666 | 306 | content = stream.read() | ||
667 | 307 | default_pools = [ | ||
668 | 308 | "{0}.{1}.pool.ntp.org".format(x, distro) | ||
669 | 309 | for x in range(0, cc_ntp.NR_POOL_SERVERS)] | ||
670 | 310 | self.assertEqual( | ||
671 | 311 | "servers []\npools {0}\n".format(default_pools), | ||
672 | 312 | content) | ||
673 | 313 | 314 | ||
674 | 314 | @skipIf(_missing_jsonschema_dep, "No python-jsonschema dependency") | 315 | @skipIf(_missing_jsonschema_dep, "No python-jsonschema dependency") |
675 | 315 | def test_ntp_handler_schema_validation_warns_non_string_item_type(self): | 316 | def test_ntp_handler_schema_validation_warns_non_string_item_type(self): |
676 | diff --git a/tests/unittests/test_handler/test_handler_resizefs.py b/tests/unittests/test_handler/test_handler_resizefs.py | |||
677 | index 3e5d436..29d5574 100644 | |||
678 | --- a/tests/unittests/test_handler/test_handler_resizefs.py | |||
679 | +++ b/tests/unittests/test_handler/test_handler_resizefs.py | |||
680 | @@ -1,9 +1,9 @@ | |||
681 | 1 | # This file is part of cloud-init. See LICENSE file for license information. | 1 | # This file is part of cloud-init. See LICENSE file for license information. |
682 | 2 | 2 | ||
683 | 3 | from cloudinit.config.cc_resizefs import ( | 3 | from cloudinit.config.cc_resizefs import ( |
686 | 4 | can_skip_resize, handle, is_device_path_writable_block, | 4 | can_skip_resize, handle, maybe_get_writable_device_path) |
685 | 5 | rootdev_from_cmdline) | ||
687 | 6 | 5 | ||
688 | 6 | from collections import namedtuple | ||
689 | 7 | import logging | 7 | import logging |
690 | 8 | import textwrap | 8 | import textwrap |
691 | 9 | 9 | ||
692 | @@ -138,47 +138,48 @@ class TestRootDevFromCmdline(CiTestCase): | |||
693 | 138 | invalid_cases = [ | 138 | invalid_cases = [ |
694 | 139 | 'BOOT_IMAGE=/adsf asdfa werasef root adf', 'BOOT_IMAGE=/adsf', ''] | 139 | 'BOOT_IMAGE=/adsf asdfa werasef root adf', 'BOOT_IMAGE=/adsf', ''] |
695 | 140 | for case in invalid_cases: | 140 | for case in invalid_cases: |
697 | 141 | self.assertIsNone(rootdev_from_cmdline(case)) | 141 | self.assertIsNone(util.rootdev_from_cmdline(case)) |
698 | 142 | 142 | ||
699 | 143 | def test_rootdev_from_cmdline_with_root_startswith_dev(self): | 143 | def test_rootdev_from_cmdline_with_root_startswith_dev(self): |
700 | 144 | """Return the cmdline root when the path starts with /dev.""" | 144 | """Return the cmdline root when the path starts with /dev.""" |
701 | 145 | self.assertEqual( | 145 | self.assertEqual( |
703 | 146 | '/dev/this', rootdev_from_cmdline('asdf root=/dev/this')) | 146 | '/dev/this', util.rootdev_from_cmdline('asdf root=/dev/this')) |
704 | 147 | 147 | ||
705 | 148 | def test_rootdev_from_cmdline_with_root_without_dev_prefix(self): | 148 | def test_rootdev_from_cmdline_with_root_without_dev_prefix(self): |
706 | 149 | """Add /dev prefix to cmdline root when the path lacks the prefix.""" | 149 | """Add /dev prefix to cmdline root when the path lacks the prefix.""" |
708 | 150 | self.assertEqual('/dev/this', rootdev_from_cmdline('asdf root=this')) | 150 | self.assertEqual( |
709 | 151 | '/dev/this', util.rootdev_from_cmdline('asdf root=this')) | ||
710 | 151 | 152 | ||
711 | 152 | def test_rootdev_from_cmdline_with_root_with_label(self): | 153 | def test_rootdev_from_cmdline_with_root_with_label(self): |
712 | 153 | """When cmdline root contains a LABEL, our root is disk/by-label.""" | 154 | """When cmdline root contains a LABEL, our root is disk/by-label.""" |
713 | 154 | self.assertEqual( | 155 | self.assertEqual( |
714 | 155 | '/dev/disk/by-label/unique', | 156 | '/dev/disk/by-label/unique', |
716 | 156 | rootdev_from_cmdline('asdf root=LABEL=unique')) | 157 | util.rootdev_from_cmdline('asdf root=LABEL=unique')) |
717 | 157 | 158 | ||
718 | 158 | def test_rootdev_from_cmdline_with_root_with_uuid(self): | 159 | def test_rootdev_from_cmdline_with_root_with_uuid(self): |
719 | 159 | """When cmdline root contains a UUID, our root is disk/by-uuid.""" | 160 | """When cmdline root contains a UUID, our root is disk/by-uuid.""" |
720 | 160 | self.assertEqual( | 161 | self.assertEqual( |
721 | 161 | '/dev/disk/by-uuid/adsfdsaf-adsf', | 162 | '/dev/disk/by-uuid/adsfdsaf-adsf', |
723 | 162 | rootdev_from_cmdline('asdf root=UUID=adsfdsaf-adsf')) | 163 | util.rootdev_from_cmdline('asdf root=UUID=adsfdsaf-adsf')) |
724 | 163 | 164 | ||
725 | 164 | 165 | ||
727 | 165 | class TestIsDevicePathWritableBlock(CiTestCase): | 166 | class TestMaybeGetDevicePathAsWritableBlock(CiTestCase): |
728 | 166 | 167 | ||
729 | 167 | with_logs = True | 168 | with_logs = True |
730 | 168 | 169 | ||
732 | 169 | def test_is_device_path_writable_block_false_on_overlayroot(self): | 170 | def test_maybe_get_writable_device_path_none_on_overlayroot(self): |
733 | 170 | """When devpath is overlayroot (on MAAS), is_dev_writable is False.""" | 171 | """When devpath is overlayroot (on MAAS), is_dev_writable is False.""" |
734 | 171 | info = 'does not matter' | 172 | info = 'does not matter' |
736 | 172 | is_writable = wrap_and_call( | 173 | devpath = wrap_and_call( |
737 | 173 | 'cloudinit.config.cc_resizefs.util', | 174 | 'cloudinit.config.cc_resizefs.util', |
738 | 174 | {'is_container': {'return_value': False}}, | 175 | {'is_container': {'return_value': False}}, |
741 | 175 | is_device_path_writable_block, 'overlayroot', info, LOG) | 176 | maybe_get_writable_device_path, 'overlayroot', info, LOG) |
742 | 176 | self.assertFalse(is_writable) | 177 | self.assertIsNone(devpath) |
743 | 177 | self.assertIn( | 178 | self.assertIn( |
744 | 178 | "Not attempting to resize devpath 'overlayroot'", | 179 | "Not attempting to resize devpath 'overlayroot'", |
745 | 179 | self.logs.getvalue()) | 180 | self.logs.getvalue()) |
746 | 180 | 181 | ||
748 | 181 | def test_is_device_path_writable_block_warns_missing_cmdline_root(self): | 182 | def test_maybe_get_writable_device_path_warns_missing_cmdline_root(self): |
749 | 182 | """When root does not exist isn't in the cmdline, log warning.""" | 183 | """When root does not exist isn't in the cmdline, log warning.""" |
750 | 183 | info = 'does not matter' | 184 | info = 'does not matter' |
751 | 184 | 185 | ||
752 | @@ -190,43 +191,43 @@ class TestIsDevicePathWritableBlock(CiTestCase): | |||
753 | 190 | exists_mock_path = 'cloudinit.config.cc_resizefs.os.path.exists' | 191 | exists_mock_path = 'cloudinit.config.cc_resizefs.os.path.exists' |
754 | 191 | with mock.patch(exists_mock_path) as m_exists: | 192 | with mock.patch(exists_mock_path) as m_exists: |
755 | 192 | m_exists.return_value = False | 193 | m_exists.return_value = False |
757 | 193 | is_writable = wrap_and_call( | 194 | devpath = wrap_and_call( |
758 | 194 | 'cloudinit.config.cc_resizefs.util', | 195 | 'cloudinit.config.cc_resizefs.util', |
759 | 195 | {'is_container': {'return_value': False}, | 196 | {'is_container': {'return_value': False}, |
760 | 196 | 'get_mount_info': {'side_effect': fake_mount_info}, | 197 | 'get_mount_info': {'side_effect': fake_mount_info}, |
761 | 197 | 'get_cmdline': {'return_value': 'BOOT_IMAGE=/vmlinuz.efi'}}, | 198 | 'get_cmdline': {'return_value': 'BOOT_IMAGE=/vmlinuz.efi'}}, |
764 | 198 | is_device_path_writable_block, '/dev/root', info, LOG) | 199 | maybe_get_writable_device_path, '/dev/root', info, LOG) |
765 | 199 | self.assertFalse(is_writable) | 200 | self.assertIsNone(devpath) |
766 | 200 | logs = self.logs.getvalue() | 201 | logs = self.logs.getvalue() |
767 | 201 | self.assertIn("WARNING: Unable to find device '/dev/root'", logs) | 202 | self.assertIn("WARNING: Unable to find device '/dev/root'", logs) |
768 | 202 | 203 | ||
770 | 203 | def test_is_device_path_writable_block_does_not_exist(self): | 204 | def test_maybe_get_writable_device_path_does_not_exist(self): |
771 | 204 | """When devpath does not exist, a warning is logged.""" | 205 | """When devpath does not exist, a warning is logged.""" |
772 | 205 | info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none' | 206 | info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none' |
774 | 206 | is_writable = wrap_and_call( | 207 | devpath = wrap_and_call( |
775 | 207 | 'cloudinit.config.cc_resizefs.util', | 208 | 'cloudinit.config.cc_resizefs.util', |
776 | 208 | {'is_container': {'return_value': False}}, | 209 | {'is_container': {'return_value': False}}, |
779 | 209 | is_device_path_writable_block, '/I/dont/exist', info, LOG) | 210 | maybe_get_writable_device_path, '/I/dont/exist', info, LOG) |
780 | 210 | self.assertFalse(is_writable) | 211 | self.assertIsNone(devpath) |
781 | 211 | self.assertIn( | 212 | self.assertIn( |
782 | 212 | "WARNING: Device '/I/dont/exist' did not exist." | 213 | "WARNING: Device '/I/dont/exist' did not exist." |
783 | 213 | ' cannot resize: %s' % info, | 214 | ' cannot resize: %s' % info, |
784 | 214 | self.logs.getvalue()) | 215 | self.logs.getvalue()) |
785 | 215 | 216 | ||
787 | 216 | def test_is_device_path_writable_block_does_not_exist_in_container(self): | 217 | def test_maybe_get_writable_device_path_does_not_exist_in_container(self): |
788 | 217 | """When devpath does not exist in a container, log a debug message.""" | 218 | """When devpath does not exist in a container, log a debug message.""" |
789 | 218 | info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none' | 219 | info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none' |
791 | 219 | is_writable = wrap_and_call( | 220 | devpath = wrap_and_call( |
792 | 220 | 'cloudinit.config.cc_resizefs.util', | 221 | 'cloudinit.config.cc_resizefs.util', |
793 | 221 | {'is_container': {'return_value': True}}, | 222 | {'is_container': {'return_value': True}}, |
796 | 222 | is_device_path_writable_block, '/I/dont/exist', info, LOG) | 223 | maybe_get_writable_device_path, '/I/dont/exist', info, LOG) |
797 | 223 | self.assertFalse(is_writable) | 224 | self.assertIsNone(devpath) |
798 | 224 | self.assertIn( | 225 | self.assertIn( |
799 | 225 | "DEBUG: Device '/I/dont/exist' did not exist in container." | 226 | "DEBUG: Device '/I/dont/exist' did not exist in container." |
800 | 226 | ' cannot resize: %s' % info, | 227 | ' cannot resize: %s' % info, |
801 | 227 | self.logs.getvalue()) | 228 | self.logs.getvalue()) |
802 | 228 | 229 | ||
804 | 229 | def test_is_device_path_writable_block_raises_oserror(self): | 230 | def test_maybe_get_writable_device_path_raises_oserror(self): |
805 | 230 | """When unexpected OSError is raises by os.stat it is reraised.""" | 231 | """When unexpected OSError is raises by os.stat it is reraised.""" |
806 | 231 | info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none' | 232 | info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none' |
807 | 232 | with self.assertRaises(OSError) as context_manager: | 233 | with self.assertRaises(OSError) as context_manager: |
808 | @@ -234,41 +235,63 @@ class TestIsDevicePathWritableBlock(CiTestCase): | |||
809 | 234 | 'cloudinit.config.cc_resizefs', | 235 | 'cloudinit.config.cc_resizefs', |
810 | 235 | {'util.is_container': {'return_value': True}, | 236 | {'util.is_container': {'return_value': True}, |
811 | 236 | 'os.stat': {'side_effect': OSError('Something unexpected')}}, | 237 | 'os.stat': {'side_effect': OSError('Something unexpected')}}, |
813 | 237 | is_device_path_writable_block, '/I/dont/exist', info, LOG) | 238 | maybe_get_writable_device_path, '/I/dont/exist', info, LOG) |
814 | 238 | self.assertEqual( | 239 | self.assertEqual( |
815 | 239 | 'Something unexpected', str(context_manager.exception)) | 240 | 'Something unexpected', str(context_manager.exception)) |
816 | 240 | 241 | ||
818 | 241 | def test_is_device_path_writable_block_non_block(self): | 242 | def test_maybe_get_writable_device_path_non_block(self): |
819 | 242 | """When device is not a block device, emit warning return False.""" | 243 | """When device is not a block device, emit warning return False.""" |
820 | 243 | fake_devpath = self.tmp_path('dev/readwrite') | 244 | fake_devpath = self.tmp_path('dev/readwrite') |
821 | 244 | util.write_file(fake_devpath, '', mode=0o600) # read-write | 245 | util.write_file(fake_devpath, '', mode=0o600) # read-write |
822 | 245 | info = 'dev=/dev/root mnt_point=/ path={0}'.format(fake_devpath) | 246 | info = 'dev=/dev/root mnt_point=/ path={0}'.format(fake_devpath) |
823 | 246 | 247 | ||
825 | 247 | is_writable = wrap_and_call( | 248 | devpath = wrap_and_call( |
826 | 248 | 'cloudinit.config.cc_resizefs.util', | 249 | 'cloudinit.config.cc_resizefs.util', |
827 | 249 | {'is_container': {'return_value': False}}, | 250 | {'is_container': {'return_value': False}}, |
830 | 250 | is_device_path_writable_block, fake_devpath, info, LOG) | 251 | maybe_get_writable_device_path, fake_devpath, info, LOG) |
831 | 251 | self.assertFalse(is_writable) | 252 | self.assertIsNone(devpath) |
832 | 252 | self.assertIn( | 253 | self.assertIn( |
833 | 253 | "WARNING: device '{0}' not a block device. cannot resize".format( | 254 | "WARNING: device '{0}' not a block device. cannot resize".format( |
834 | 254 | fake_devpath), | 255 | fake_devpath), |
835 | 255 | self.logs.getvalue()) | 256 | self.logs.getvalue()) |
836 | 256 | 257 | ||
838 | 257 | def test_is_device_path_writable_block_non_block_on_container(self): | 258 | def test_maybe_get_writable_device_path_non_block_on_container(self): |
839 | 258 | """When device is non-block device in container, emit debug log.""" | 259 | """When device is non-block device in container, emit debug log.""" |
840 | 259 | fake_devpath = self.tmp_path('dev/readwrite') | 260 | fake_devpath = self.tmp_path('dev/readwrite') |
841 | 260 | util.write_file(fake_devpath, '', mode=0o600) # read-write | 261 | util.write_file(fake_devpath, '', mode=0o600) # read-write |
842 | 261 | info = 'dev=/dev/root mnt_point=/ path={0}'.format(fake_devpath) | 262 | info = 'dev=/dev/root mnt_point=/ path={0}'.format(fake_devpath) |
843 | 262 | 263 | ||
845 | 263 | is_writable = wrap_and_call( | 264 | devpath = wrap_and_call( |
846 | 264 | 'cloudinit.config.cc_resizefs.util', | 265 | 'cloudinit.config.cc_resizefs.util', |
847 | 265 | {'is_container': {'return_value': True}}, | 266 | {'is_container': {'return_value': True}}, |
850 | 266 | is_device_path_writable_block, fake_devpath, info, LOG) | 267 | maybe_get_writable_device_path, fake_devpath, info, LOG) |
851 | 267 | self.assertFalse(is_writable) | 268 | self.assertIsNone(devpath) |
852 | 268 | self.assertIn( | 269 | self.assertIn( |
853 | 269 | "DEBUG: device '{0}' not a block device in container." | 270 | "DEBUG: device '{0}' not a block device in container." |
854 | 270 | ' cannot resize'.format(fake_devpath), | 271 | ' cannot resize'.format(fake_devpath), |
855 | 271 | self.logs.getvalue()) | 272 | self.logs.getvalue()) |
856 | 272 | 273 | ||
857 | 274 | def test_maybe_get_writable_device_path_returns_cmdline_root(self): | ||
858 | 275 | """When root device is UUID in kernel commandline, update devpath.""" | ||
859 | 276 | # XXX Long-term we want to use FilesystemMocking test to avoid | ||
860 | 277 | # touching os.stat. | ||
861 | 278 | FakeStat = namedtuple( | ||
862 | 279 | 'FakeStat', ['st_mode', 'st_size', 'st_mtime']) # minimal def. | ||
863 | 280 | info = 'dev=/dev/root mnt_point=/ path=/does/not/matter' | ||
864 | 281 | devpath = wrap_and_call( | ||
865 | 282 | 'cloudinit.config.cc_resizefs', | ||
866 | 283 | {'util.get_cmdline': {'return_value': 'asdf root=UUID=my-uuid'}, | ||
867 | 284 | 'util.is_container': False, | ||
868 | 285 | 'os.path.exists': False, # /dev/root doesn't exist | ||
869 | 286 | 'os.stat': { | ||
870 | 287 | 'return_value': FakeStat(25008, 0, 1)} # char block device | ||
871 | 288 | }, | ||
872 | 289 | maybe_get_writable_device_path, '/dev/root', info, LOG) | ||
873 | 290 | self.assertEqual('/dev/disk/by-uuid/my-uuid', devpath) | ||
874 | 291 | self.assertIn( | ||
875 | 292 | "DEBUG: Converted /dev/root to '/dev/disk/by-uuid/my-uuid'" | ||
876 | 293 | " per kernel cmdline", | ||
877 | 294 | self.logs.getvalue()) | ||
878 | 295 | |||
879 | 273 | 296 | ||
880 | 274 | # vi: ts=4 expandtab | 297 | # vi: ts=4 expandtab |
881 | diff --git a/tests/unittests/test_handler/test_schema.py b/tests/unittests/test_handler/test_schema.py | |||
882 | index b8fc893..648573f 100644 | |||
883 | --- a/tests/unittests/test_handler/test_schema.py | |||
884 | +++ b/tests/unittests/test_handler/test_schema.py | |||
885 | @@ -4,11 +4,12 @@ from cloudinit.config.schema import ( | |||
886 | 4 | CLOUD_CONFIG_HEADER, SchemaValidationError, annotated_cloudconfig_file, | 4 | CLOUD_CONFIG_HEADER, SchemaValidationError, annotated_cloudconfig_file, |
887 | 5 | get_schema_doc, get_schema, validate_cloudconfig_file, | 5 | get_schema_doc, get_schema, validate_cloudconfig_file, |
888 | 6 | validate_cloudconfig_schema, main) | 6 | validate_cloudconfig_schema, main) |
890 | 7 | from cloudinit.util import write_file | 7 | from cloudinit.util import subp, write_file |
891 | 8 | 8 | ||
892 | 9 | from cloudinit.tests.helpers import CiTestCase, mock, skipIf | 9 | from cloudinit.tests.helpers import CiTestCase, mock, skipIf |
893 | 10 | 10 | ||
894 | 11 | from copy import copy | 11 | from copy import copy |
895 | 12 | import os | ||
896 | 12 | from six import StringIO | 13 | from six import StringIO |
897 | 13 | from textwrap import dedent | 14 | from textwrap import dedent |
898 | 14 | from yaml import safe_load | 15 | from yaml import safe_load |
899 | @@ -364,4 +365,38 @@ class MainTest(CiTestCase): | |||
900 | 364 | self.assertIn( | 365 | self.assertIn( |
901 | 365 | 'Valid cloud-config file {0}'.format(myyaml), m_stdout.getvalue()) | 366 | 'Valid cloud-config file {0}'.format(myyaml), m_stdout.getvalue()) |
902 | 366 | 367 | ||
903 | 368 | |||
904 | 369 | class CloudTestsIntegrationTest(CiTestCase): | ||
905 | 370 | """Validate all cloud-config yaml schema provided in integration tests. | ||
906 | 371 | |||
907 | 372 | It is less expensive to have unittests validate schema of all cloud-config | ||
908 | 373 | yaml provided to integration tests, than to run an integration test which | ||
909 | 374 | raises Warnings or errors on invalid cloud-config schema. | ||
910 | 375 | """ | ||
911 | 376 | |||
912 | 377 | @skipIf(_missing_jsonschema_dep, "No python-jsonschema dependency") | ||
913 | 378 | def test_all_integration_test_cloud_config_schema(self): | ||
914 | 379 | """Validate schema of cloud_tests yaml files looking for warnings.""" | ||
915 | 380 | schema = get_schema() | ||
916 | 381 | testsdir = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) | ||
917 | 382 | integration_testdir = os.path.sep.join( | ||
918 | 383 | [testsdir, 'cloud_tests', 'testcases']) | ||
919 | 384 | errors = [] | ||
920 | 385 | out, _ = subp(['find', integration_testdir, '-name', '*yaml']) | ||
921 | 386 | for filename in out.splitlines(): | ||
922 | 387 | test_cfg = safe_load(open(filename)) | ||
923 | 388 | cloud_config = test_cfg.get('cloud_config') | ||
924 | 389 | if cloud_config: | ||
925 | 390 | cloud_config = safe_load( | ||
926 | 391 | cloud_config.replace("#cloud-config\n", "")) | ||
927 | 392 | try: | ||
928 | 393 | validate_cloudconfig_schema( | ||
929 | 394 | cloud_config, schema, strict=True) | ||
930 | 395 | except SchemaValidationError as e: | ||
931 | 396 | errors.append( | ||
932 | 397 | '{0}: {1}'.format( | ||
933 | 398 | filename, e)) | ||
934 | 399 | if errors: | ||
935 | 400 | raise AssertionError(', '.join(errors)) | ||
936 | 401 | |||
937 | 367 | # vi: ts=4 expandtab syntax=python | 402 | # vi: ts=4 expandtab syntax=python |
938 | diff --git a/tools/read-dependencies b/tools/read-dependencies | |||
939 | index 2a64868..421f470 100755 | |||
940 | --- a/tools/read-dependencies | |||
941 | +++ b/tools/read-dependencies | |||
942 | @@ -30,9 +30,35 @@ DISTRO_PKG_TYPE_MAP = { | |||
943 | 30 | 'suse': 'suse' | 30 | 'suse': 'suse' |
944 | 31 | } | 31 | } |
945 | 32 | 32 | ||
947 | 33 | DISTRO_INSTALL_PKG_CMD = { | 33 | MAYBE_RELIABLE_YUM_INSTALL = [ |
948 | 34 | 'sh', '-c', | ||
949 | 35 | """ | ||
950 | 36 | error() { echo "$@" 1>&2; } | ||
951 | 37 | n=0; max=10; | ||
952 | 38 | bcmd="yum install --downloadonly --assumeyes --setopt=keepcache=1" | ||
953 | 39 | while n=$(($n+1)); do | ||
954 | 40 | error ":: running $bcmd $* [$n/$max]" | ||
955 | 41 | $bcmd "$@" | ||
956 | 42 | r=$? | ||
957 | 43 | [ $r -eq 0 ] && break | ||
958 | 44 | [ $n -ge $max ] && { error "gave up on $bcmd"; exit $r; } | ||
959 | 45 | nap=$(($n*5)) | ||
960 | 46 | error ":: failed [$r] ($n/$max). sleeping $nap." | ||
961 | 47 | sleep $nap | ||
962 | 48 | done | ||
963 | 49 | error ":: running yum install --cacheonly --assumeyes $*" | ||
964 | 50 | yum install --cacheonly --assumeyes "$@" | ||
965 | 51 | """, | ||
966 | 52 | 'reliable-yum-install'] | ||
967 | 53 | |||
968 | 54 | DRY_DISTRO_INSTALL_PKG_CMD = { | ||
969 | 34 | 'centos': ['yum', 'install', '--assumeyes'], | 55 | 'centos': ['yum', 'install', '--assumeyes'], |
970 | 35 | 'redhat': ['yum', 'install', '--assumeyes'], | 56 | 'redhat': ['yum', 'install', '--assumeyes'], |
971 | 57 | } | ||
972 | 58 | |||
973 | 59 | DISTRO_INSTALL_PKG_CMD = { | ||
974 | 60 | 'centos': MAYBE_RELIABLE_YUM_INSTALL, | ||
975 | 61 | 'redhat': MAYBE_RELIABLE_YUM_INSTALL, | ||
976 | 36 | 'debian': ['apt', 'install', '-y'], | 62 | 'debian': ['apt', 'install', '-y'], |
977 | 37 | 'ubuntu': ['apt', 'install', '-y'], | 63 | 'ubuntu': ['apt', 'install', '-y'], |
978 | 38 | 'opensuse': ['zypper', 'install'], | 64 | 'opensuse': ['zypper', 'install'], |
979 | @@ -80,8 +106,8 @@ def get_parser(): | |||
980 | 80 | help='Additionally install continuous integration system packages ' | 106 | help='Additionally install continuous integration system packages ' |
981 | 81 | 'required for build and test automation.') | 107 | 'required for build and test automation.') |
982 | 82 | parser.add_argument( | 108 | parser.add_argument( |
985 | 83 | '-v', '--python-version', type=str, dest='python_version', default=None, | 109 | '-v', '--python-version', type=str, dest='python_version', |
986 | 84 | choices=["2", "3"], | 110 | default=None, choices=["2", "3"], |
987 | 85 | help='Override the version of python we want to generate system ' | 111 | help='Override the version of python we want to generate system ' |
988 | 86 | 'package dependencies for. Defaults to the version of python ' | 112 | 'package dependencies for. Defaults to the version of python ' |
989 | 87 | 'this script is called with') | 113 | 'this script is called with') |
990 | @@ -219,10 +245,15 @@ def pkg_install(pkg_list, distro, test_distro=False, dry_run=False): | |||
991 | 219 | '(dryrun)' if dry_run else '', ' '.join(pkg_list))) | 245 | '(dryrun)' if dry_run else '', ' '.join(pkg_list))) |
992 | 220 | install_cmd = [] | 246 | install_cmd = [] |
993 | 221 | if dry_run: | 247 | if dry_run: |
995 | 222 | install_cmd.append('echo') | 248 | install_cmd.append('echo') |
996 | 223 | if os.geteuid() != 0: | 249 | if os.geteuid() != 0: |
997 | 224 | install_cmd.append('sudo') | 250 | install_cmd.append('sudo') |
999 | 225 | install_cmd.extend(DISTRO_INSTALL_PKG_CMD[distro]) | 251 | |
1000 | 252 | cmd = DISTRO_INSTALL_PKG_CMD[distro] | ||
1001 | 253 | if dry_run and distro in DRY_DISTRO_INSTALL_PKG_CMD: | ||
1002 | 254 | cmd = DRY_DISTRO_INSTALL_PKG_CMD[distro] | ||
1003 | 255 | install_cmd.extend(cmd) | ||
1004 | 256 | |||
1005 | 226 | if distro in ['centos', 'redhat']: | 257 | if distro in ['centos', 'redhat']: |
1006 | 227 | # CentOS and Redhat need epel-release to access oauthlib and jsonschema | 258 | # CentOS and Redhat need epel-release to access oauthlib and jsonschema |
1007 | 228 | subprocess.check_call(install_cmd + ['epel-release']) | 259 | subprocess.check_call(install_cmd + ['epel-release']) |
1008 | diff --git a/tools/run-centos b/tools/run-centos | |||
1009 | index d44d514..d58ef3e 100755 | |||
1010 | --- a/tools/run-centos | |||
1011 | +++ b/tools/run-centos | |||
1012 | @@ -123,7 +123,22 @@ prep() { | |||
1013 | 123 | return 0 | 123 | return 0 |
1014 | 124 | fi | 124 | fi |
1015 | 125 | error "Installing prep packages: ${needed}" | 125 | error "Installing prep packages: ${needed}" |
1017 | 126 | yum install --assumeyes ${needed} | 126 | set -- $needed |
1018 | 127 | local n max r | ||
1019 | 128 | n=0; max=10; | ||
1020 | 129 | bcmd="yum install --downloadonly --assumeyes --setopt=keepcache=1" | ||
1021 | 130 | while n=$(($n+1)); do | ||
1022 | 131 | error ":: running $bcmd $* [$n/$max]" | ||
1023 | 132 | $bcmd "$@" | ||
1024 | 133 | r=$? | ||
1025 | 134 | [ $r -eq 0 ] && break | ||
1026 | 135 | [ $n -ge $max ] && { error "gave up on $bcmd"; exit $r; } | ||
1027 | 136 | nap=$(($n*5)) | ||
1028 | 137 | error ":: failed [$r] ($n/$max). sleeping $nap." | ||
1029 | 138 | sleep $nap | ||
1030 | 139 | done | ||
1031 | 140 | error ":: running yum install --cacheonly --assumeyes $*" | ||
1032 | 141 | yum install --cacheonly --assumeyes "$@" | ||
1033 | 127 | } | 142 | } |
1034 | 128 | 143 | ||
1035 | 129 | start_container() { | 144 | start_container() { |
1036 | @@ -153,6 +168,7 @@ start_container() { | |||
1037 | 153 | if [ ! -z "${http_proxy-}" ]; then | 168 | if [ ! -z "${http_proxy-}" ]; then |
1038 | 154 | debug 1 "configuring proxy ${http_proxy}" | 169 | debug 1 "configuring proxy ${http_proxy}" |
1039 | 155 | inside "$name" sh -c "echo proxy=$http_proxy >> /etc/yum.conf" | 170 | inside "$name" sh -c "echo proxy=$http_proxy >> /etc/yum.conf" |
1040 | 171 | inside "$name" sed -i s/enabled=1/enabled=0/ /etc/yum/pluginconf.d/fastestmirror.conf | ||
1041 | 156 | fi | 172 | fi |
1042 | 157 | } | 173 | } |
1043 | 158 | 174 |
PASSED: Continuous integration, rev:b5ca0b1343f aaf1a802ff3ff1c 4231e1304ad770 /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 436/
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
SUCCESS: MAAS Compatability Testing
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 436/rebuild
https:/