Merge ~smoser/cloud-init:feature/login-warn into cloud-init:master
- Git
- lp:~smoser/cloud-init
- feature/login-warn
- Merge into master
Proposed by
Scott Moser
Status: | Merged |
---|---|
Merged at revision: | c81ea53bbdc4ada9d2b52430e106aeb3c38b4e0a |
Proposed branch: | ~smoser/cloud-init:feature/login-warn |
Merge into: | cloud-init:master |
Diff against target: |
469 lines (+250/-56) 7 files modified
cloudinit/cmd/main.py (+39/-0) cloudinit/helpers.py (+1/-0) cloudinit/sources/DataSourceEc2.py (+5/-42) cloudinit/warnings.py (+139/-0) packages/debian/rules.in (+1/-0) tools/Z99-cloudinit-warnings.sh (+30/-0) tools/ds-identify (+35/-14) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
cloud-init Commiters | Pending | ||
Review via email: mp+318844@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Scott Moser (smoser) wrote : | # |
Revision history for this message
Ryan Harper (raharper) wrote : | # |
fix "create add support" -> "add support" in the above message (or at least fix in commit message).
Two comments below; generally looks good.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/cloudinit/cmd/main.py b/cloudinit/cmd/main.py |
2 | index 7c65257..6ff4e1c 100644 |
3 | --- a/cloudinit/cmd/main.py |
4 | +++ b/cloudinit/cmd/main.py |
5 | @@ -29,6 +29,7 @@ from cloudinit import templater |
6 | from cloudinit import url_helper |
7 | from cloudinit import util |
8 | from cloudinit import version |
9 | +from cloudinit import warnings |
10 | |
11 | from cloudinit import reporting |
12 | from cloudinit.reporting import events |
13 | @@ -413,10 +414,48 @@ def main_init(name, args): |
14 | # give the activated datasource a chance to adjust |
15 | init.activate_datasource() |
16 | |
17 | + di_report_warn(datasource=init.datasource, cfg=init.cfg) |
18 | + |
19 | # Stage 10 |
20 | return (init.datasource, run_module_section(mods, name, name)) |
21 | |
22 | |
23 | +def di_report_warn(datasource, cfg): |
24 | + if 'di_report' not in cfg: |
25 | + LOG.debug("no di_report found in config.") |
26 | + return |
27 | + |
28 | + dicfg = cfg.get('di_report', {}) |
29 | + if not isinstance(dicfg, dict): |
30 | + LOG.warn("di_report config not a dictionary: %s", dicfg) |
31 | + return |
32 | + |
33 | + dslist = dicfg.get('datasource_list') |
34 | + if dslist is None: |
35 | + LOG.warn("no 'datasource_list' found in di_report.") |
36 | + return |
37 | + elif not isinstance(dslist, list): |
38 | + LOG.warn("di_report/datasource_list not a list: %s", dslist) |
39 | + return |
40 | + |
41 | + # ds.__module__ is like cloudinit.sources.DataSourceName |
42 | + # where Name is the thing that shows up in datasource_list. |
43 | + modname = datasource.__module__.rpartition(".")[2] |
44 | + if modname.startswith(sources.DS_PREFIX): |
45 | + modname = modname[len(sources.DS_PREFIX):] |
46 | + else: |
47 | + LOG.warn("Datasource '%s' came from unexpected module '%s'.", |
48 | + datasource, modname) |
49 | + |
50 | + if modname in dslist: |
51 | + LOG.debug("used datasource '%s' from '%s' was in di_report's list: %s", |
52 | + datasource, modname, dslist) |
53 | + return |
54 | + |
55 | + warnings.show_warning('dsid_missing_source', cfg, |
56 | + source=modname, dslist=str(dslist)) |
57 | + |
58 | + |
59 | def main_modules(action_name, args): |
60 | name = args.mode |
61 | # Cloud-init 'modules' stages are broken up into the following sub-stages |
62 | diff --git a/cloudinit/helpers.py b/cloudinit/helpers.py |
63 | index 38f5f89..7435d58 100644 |
64 | --- a/cloudinit/helpers.py |
65 | +++ b/cloudinit/helpers.py |
66 | @@ -340,6 +340,7 @@ class Paths(object): |
67 | "vendordata": "vendor-data.txt.i", |
68 | "instance_id": ".instance-id", |
69 | "manual_clean_marker": "manual-clean", |
70 | + "warnings": "warnings", |
71 | } |
72 | # Set when a datasource becomes active |
73 | self.datasource = ds |
74 | diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py |
75 | index c7df806..6f01a13 100644 |
76 | --- a/cloudinit/sources/DataSourceEc2.py |
77 | +++ b/cloudinit/sources/DataSourceEc2.py |
78 | @@ -9,7 +9,6 @@ |
79 | # This file is part of cloud-init. See LICENSE file for license information. |
80 | |
81 | import os |
82 | -import textwrap |
83 | import time |
84 | |
85 | from cloudinit import ec2_utils as ec2 |
86 | @@ -17,6 +16,7 @@ from cloudinit import log as logging |
87 | from cloudinit import sources |
88 | from cloudinit import url_helper as uhelp |
89 | from cloudinit import util |
90 | +from cloudinit import warnings |
91 | |
92 | LOG = logging.getLogger(__name__) |
93 | |
94 | @@ -224,7 +224,8 @@ class DataSourceEc2(sources.DataSource): |
95 | return |
96 | if self.cloud_platform == Platforms.UNKNOWN: |
97 | warn_if_necessary( |
98 | - util.get_cfg_by_path(cfg, STRICT_ID_PATH, STRICT_ID_DEFAULT)) |
99 | + util.get_cfg_by_path(cfg, STRICT_ID_PATH, STRICT_ID_DEFAULT), |
100 | + cfg) |
101 | |
102 | |
103 | def read_strict_mode(cfgval, default): |
104 | @@ -265,7 +266,7 @@ def parse_strict_mode(cfgval): |
105 | return mode, sleep |
106 | |
107 | |
108 | -def warn_if_necessary(cfgval): |
109 | +def warn_if_necessary(cfgval, cfg): |
110 | try: |
111 | mode, sleep = parse_strict_mode(cfgval) |
112 | except ValueError as e: |
113 | @@ -275,45 +276,7 @@ def warn_if_necessary(cfgval): |
114 | if mode == "false": |
115 | return |
116 | |
117 | - show_warning(sleep) |
118 | - |
119 | - |
120 | -def show_warning(sleep): |
121 | - message = textwrap.dedent(""" |
122 | - **************************************************************** |
123 | - # This system is using the EC2 Metadata Service, but does not # |
124 | - # appear to be running on Amazon EC2 or one of cloud-init's # |
125 | - # known platforms that provide a EC2 Metadata service. In the # |
126 | - # future, cloud-init may stop reading metadata from the EC2 # |
127 | - # Metadata Service unless the platform can be identified # |
128 | - # # |
129 | - # If you are seeing this message, please file a bug against # |
130 | - # cloud-init at https://bugs.launchpad.net/cloud-init/+filebug # |
131 | - # Make sure to include the cloud provider your instance is # |
132 | - # running on. # |
133 | - # # |
134 | - # For more information see # |
135 | - # https://bugs.launchpad.net/cloud-init/+bug/1660385 # |
136 | - # # |
137 | - # After you have filed a bug, you can disable this warning by # |
138 | - # launching your instance with the cloud-config below, or # |
139 | - # putting that content into # |
140 | - # /etc/cloud/cloud.cfg.d/99-ec2-datasource.cfg # |
141 | - # # |
142 | - # #cloud-config # |
143 | - # datasource: # |
144 | - # Ec2: # |
145 | - # strict_id: false # |
146 | - # # |
147 | - """) |
148 | - closemsg = "" |
149 | - if sleep: |
150 | - closemsg = " [sleeping for %d seconds] " % sleep |
151 | - message += closemsg.center(64, "*") |
152 | - print(message) |
153 | - LOG.warn(message) |
154 | - if sleep: |
155 | - time.sleep(sleep) |
156 | + warnings.show_warning('non_ec2_md', cfg, mode=True, sleep=sleep) |
157 | |
158 | |
159 | def identify_aws(data): |
160 | diff --git a/cloudinit/warnings.py b/cloudinit/warnings.py |
161 | new file mode 100644 |
162 | index 0000000..3206d4e |
163 | --- /dev/null |
164 | +++ b/cloudinit/warnings.py |
165 | @@ -0,0 +1,139 @@ |
166 | +# This file is part of cloud-init. See LICENSE file for license information. |
167 | + |
168 | +from cloudinit import helpers |
169 | +from cloudinit import log as logging |
170 | +from cloudinit import util |
171 | + |
172 | +import os |
173 | +import time |
174 | + |
175 | +LOG = logging.getLogger() |
176 | + |
177 | +WARNINGS = { |
178 | + 'non_ec2_md': """ |
179 | +This system is using the EC2 Metadata Service, but does not appear to |
180 | +be running on Amazon EC2 or one of cloud-init's known platforms that |
181 | +provide a EC2 Metadata service. In the future, cloud-init may stop |
182 | +reading metadata from the EC2 Metadata Service unless the platform can |
183 | +be identified. |
184 | + |
185 | +If you are seeing this message, please file a bug against |
186 | +cloud-init at |
187 | + https://bugs.launchpad.net/cloud-init/+filebug?field.tags=dsid |
188 | +Make sure to include the cloud provider your instance is |
189 | +running on. |
190 | + |
191 | +For more information see |
192 | + https://bugs.launchpad.net/bugs/1660385 |
193 | + |
194 | +After you have filed a bug, you can disable this warning by |
195 | +launching your instance with the cloud-config below, or |
196 | +putting that content into |
197 | + /etc/cloud/cloud.cfg.d/99-ec2-datasource.cfg |
198 | + |
199 | +#cloud-config |
200 | +datasource: |
201 | + Ec2: |
202 | + strict_id: false""", |
203 | + 'dsid_missing_source': """ |
204 | +A new feature in cloud-init identified possible datasources for |
205 | +this system as: |
206 | + {dslist} |
207 | +However, the datasource used was: {source} |
208 | + |
209 | +In the future, cloud-init will only attempt to use datasources that |
210 | +are identified or specifically configured. |
211 | +For more information see |
212 | + https://bugs.launchpad.net/bugs/1669675 |
213 | + |
214 | +If you are seeing this message, please file a bug against |
215 | +cloud-init at |
216 | + https://bugs.launchpad.net/cloud-init/+filebug?field.tags=dsid |
217 | +Make sure to include the cloud provider your instance is |
218 | +running on. |
219 | + |
220 | +After you have filed a bug, you can disable this warning by launching |
221 | +your instance with the cloud-config below, or putting that content |
222 | +into /etc/cloud/cloud.cfg.d/99-warnings.cfg |
223 | + |
224 | +#cloud-config |
225 | +warnings: |
226 | + dsid_missing_source: off""", |
227 | +} |
228 | + |
229 | + |
230 | +def _get_warn_dir(cfg): |
231 | + paths = helpers.Paths( |
232 | + path_cfgs=cfg.get('system_info', {}).get('paths', {})) |
233 | + return paths.get_ipath_cur('warnings') |
234 | + |
235 | + |
236 | +def _load_warn_cfg(cfg, name, mode=True, sleep=None): |
237 | + # parse cfg['warnings']['name'] returning boolean, sleep |
238 | + # expected value is form of: |
239 | + # (on|off|true|false|sleep)[,sleeptime] |
240 | + # boolean True == on, False == off |
241 | + default = (mode, sleep) |
242 | + if not cfg or not isinstance(cfg, dict): |
243 | + return default |
244 | + |
245 | + ncfg = util.get_cfg_by_path(cfg, ('warnings', name)) |
246 | + if ncfg is None: |
247 | + return default |
248 | + |
249 | + if ncfg in ("on", "true", True): |
250 | + return True, None |
251 | + |
252 | + if ncfg in ("off", "false", False): |
253 | + return False, None |
254 | + |
255 | + mode, _, csleep = ncfg.partition(",") |
256 | + if mode != "sleep": |
257 | + return default |
258 | + |
259 | + if csleep: |
260 | + try: |
261 | + sleep = int(csleep) |
262 | + except ValueError: |
263 | + return default |
264 | + |
265 | + return True, sleep |
266 | + |
267 | + |
268 | +def show_warning(name, cfg=None, sleep=None, mode=True, **kwargs): |
269 | + # kwargs are used for .format of the message. |
270 | + # sleep and mode are default values used if |
271 | + # cfg['warnings']['name'] is not present. |
272 | + if cfg is None: |
273 | + cfg = {} |
274 | + |
275 | + mode, sleep = _load_warn_cfg(cfg, name, mode=mode, sleep=sleep) |
276 | + if not mode: |
277 | + return |
278 | + |
279 | + msg = WARNINGS[name].format(**kwargs) |
280 | + msgwidth = 70 |
281 | + linewidth = msgwidth + 4 |
282 | + |
283 | + fmt = "# %%-%ds #" % msgwidth |
284 | + topline = "*" * linewidth + "\n" |
285 | + fmtlines = [] |
286 | + for line in msg.strip("\n").splitlines(): |
287 | + fmtlines.append(fmt % line) |
288 | + |
289 | + closeline = topline |
290 | + if sleep: |
291 | + sleepmsg = " [sleeping for %d seconds] " % sleep |
292 | + closeline = sleepmsg.center(linewidth, "*") + "\n" |
293 | + |
294 | + util.write_file( |
295 | + os.path.join(_get_warn_dir(cfg), name), |
296 | + topline + "\n".join(fmtlines) + "\n" + topline) |
297 | + |
298 | + LOG.warn(topline + "\n".join(fmtlines) + "\n" + closeline) |
299 | + |
300 | + if sleep: |
301 | + LOG.debug("sleeping %d seconds for warning '%s'" % (sleep, name)) |
302 | + time.sleep(sleep) |
303 | + |
304 | +# vi: ts=4 expandtab |
305 | diff --git a/packages/debian/rules.in b/packages/debian/rules.in |
306 | index 3df6053..053b764 100755 |
307 | --- a/packages/debian/rules.in |
308 | +++ b/packages/debian/rules.in |
309 | @@ -12,6 +12,7 @@ override_dh_install: |
310 | install -d debian/cloud-init/etc/rsyslog.d |
311 | cp tools/21-cloudinit.conf debian/cloud-init/etc/rsyslog.d/21-cloudinit.conf |
312 | install -D ./tools/Z99-cloud-locale-test.sh debian/cloud-init/etc/profile.d/Z99-cloud-locale-test.sh |
313 | + install -D ./tools/Z99-cloudinit-warnings.sh debian/cloud-init/etc/profile.d/Z99-cloudinit-warnings.sh |
314 | |
315 | override_dh_auto_test: |
316 | ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS))) |
317 | diff --git a/tools/Z99-cloudinit-warnings.sh b/tools/Z99-cloudinit-warnings.sh |
318 | new file mode 100644 |
319 | index 0000000..b237786 |
320 | --- /dev/null |
321 | +++ b/tools/Z99-cloudinit-warnings.sh |
322 | @@ -0,0 +1,30 @@ |
323 | +#!/bin/sh |
324 | +# This file is part of cloud-init. See LICENSE file for license information. |
325 | + |
326 | +# Purpose: show user warnings on login. |
327 | + |
328 | +cloud_init_warnings() { |
329 | + local skipf="" warning="" idir="/var/lib/cloud/instance" n=0 |
330 | + local warndir="$idir/warnings" |
331 | + local ufile="$HOME/.cloud-warnings.skip" sfile="$warndir/.skip" |
332 | + [ -d "$warndir" ] || return 0 |
333 | + [ ! -f "$ufile" ] || return 0 |
334 | + [ ! -f "$skipf" ] || return 0 |
335 | + |
336 | + for warning in "$warndir"/*; do |
337 | + [ -f "$warning" ] || continue |
338 | + cat "$warning" |
339 | + n=$((n+1)) |
340 | + done |
341 | + [ $n -eq 0 ] && return 0 |
342 | + echo "" |
343 | + echo "Disable the warnings above by:" |
344 | + echo " touch $ufile" |
345 | + echo "or" |
346 | + echo " touch $sfile" |
347 | +} |
348 | + |
349 | +cloud_init_warnings 1>&2 |
350 | +unset cloud_init_warnings |
351 | + |
352 | +# vi: syntax=sh ts=4 expandtab |
353 | diff --git a/tools/ds-identify b/tools/ds-identify |
354 | index 9711a23..d7b2a0b 100755 |
355 | --- a/tools/ds-identify |
356 | +++ b/tools/ds-identify |
357 | @@ -10,8 +10,9 @@ |
358 | # default setting is: |
359 | # search,found=all,maybe=all,notfound=disable |
360 | # |
361 | -# report: write config to /run/cloud-init/cloud.cfg.report (instead of |
362 | -# /run/cloud-init/cloud.cfg, which effectively makes this dry-run). |
363 | +# report: write config to /run/cloud-init/cloud.cfg, but |
364 | +# namespaced under 'di_report'. Thus cloud-init can still see |
365 | +# the result, but has no affect. |
366 | # enable: do nothing |
367 | # ds-identify writes no config and just exits success |
368 | # the caller (cloud-init-generator) then enables cloud-init to run |
369 | @@ -108,6 +109,7 @@ DI_ON_FOUND="" |
370 | DI_ON_MAYBE="" |
371 | DI_ON_NOTFOUND="" |
372 | |
373 | +DI_EC2_STRICT_ID_DEFAULT="true" |
374 | |
375 | error() { |
376 | set -- "ERROR:" "$@"; |
377 | @@ -720,7 +722,7 @@ dscheck_Ec2() { |
378 | return $DS_FOUND |
379 | fi |
380 | |
381 | - local default="true" |
382 | + local default="${DI_EC2_STRICT_ID_DEFAULT}" |
383 | if ec2_read_strict_setting "$default"; then |
384 | strict="$_RET" |
385 | else |
386 | @@ -867,15 +869,16 @@ _print_info() { |
387 | } |
388 | |
389 | write_result() { |
390 | - local runcfg="${PATH_RUN_CI_CFG}" ret="" line="" |
391 | - if [ "$DI_REPORT" = "true" ]; then |
392 | - # if report is true, then we write to .report, but touch the other. |
393 | - : > "$runcfg" |
394 | - runcfg="$runcfg.report" |
395 | - fi |
396 | - for line in "$@"; do |
397 | - echo "$line" |
398 | - done > "$runcfg" |
399 | + local runcfg="${PATH_RUN_CI_CFG}" ret="" line="" pre="" |
400 | + { |
401 | + if [ "$DI_REPORT" = "true" ]; then |
402 | + echo "di_report:" |
403 | + pre=" " |
404 | + fi |
405 | + for line in "$@"; do |
406 | + echo "${pre}$line"; |
407 | + done |
408 | + } > "$runcfg" |
409 | ret=$? |
410 | [ $ret -eq 0 ] || { |
411 | error "failed to write to ${runcfg}" |
412 | @@ -884,10 +887,23 @@ write_result() { |
413 | return 0 |
414 | } |
415 | |
416 | +record_notfound() { |
417 | + # in report mode, report nothing was found. |
418 | + # if not report mode: only report the negative result. |
419 | + # reporting an empty list would mean cloud-init would not search |
420 | + # any datasources. |
421 | + if [ "$DI_REPORT" = "true" ]; then |
422 | + found -- |
423 | + else |
424 | + local msg="# reporting not found result. notfound=${DI_ON_NOTFOUND}." |
425 | + local DI_REPORT="true" |
426 | + found -- "$msg" |
427 | + fi |
428 | +} |
429 | + |
430 | found() { |
431 | # found(ds1, [ds2 ...], [-- [extra lines]]) |
432 | local list="" ds="" |
433 | - # always we write the None datasource last. |
434 | while [ $# -ne 0 ]; do |
435 | if [ "$1" = "--" ]; then |
436 | shift |
437 | @@ -900,6 +916,8 @@ found() { |
438 | # do not pass an empty line through. |
439 | shift |
440 | fi |
441 | + # always write the None datasource last. |
442 | + list="${list:+${list}, }None" |
443 | write_result "datasource_list: [ $list ]" "$@" |
444 | return |
445 | } |
446 | @@ -956,6 +974,7 @@ _read_config() { |
447 | if [ "$keyname" = "_unset" ]; then |
448 | return 1 |
449 | fi |
450 | + _RET="" |
451 | return 0 |
452 | } |
453 | |
454 | @@ -1170,13 +1189,15 @@ _main() { |
455 | return |
456 | fi |
457 | |
458 | + # record the empty result. |
459 | + record_notfound |
460 | case "$DI_ON_NOTFOUND" in |
461 | $DI_DISABLED) |
462 | debug 1 "No result. notfound=$DI_DISABLED. returning $ret_dis." |
463 | return $ret_dis |
464 | ;; |
465 | $DI_ENABLED) |
466 | - debug 1 "notfound=$DI_ENABLED. returning $ret_en" |
467 | + debug 1 "No result. notfound=$DI_ENABLED. returning $ret_en" |
468 | return $ret_en;; |
469 | esac |
470 |
There is a series of commits here that together create add support for cloud/instance/ warnings/ if it exists.
a.) cloud-init showing warnings to a user who ssh's in
This is accomplished by adding a profile.d script that shows content
in /var/lib/
b.) warns the user when they have used a datasource that ds-identify in
report mode did not identify.