cloud-init-dev team mailing list archive
-
cloud-init-dev team
-
Mailing list archive
-
Message #01619
[Merge] ~smoser/cloud-init:feature/login-warn into cloud-init:master
Scott Moser has proposed merging ~smoser/cloud-init:feature/login-warn into cloud-init:master.
Requested reviews:
cloud init development team (cloud-init-dev)
For more details, see:
https://code.launchpad.net/~smoser/cloud-init/+git/cloud-init/+merge/318844
--
Your team cloud init development team is requested to review the proposed merge of ~smoser/cloud-init:feature/login-warn into cloud-init:master.
diff --git a/cloudinit/cmd/main.py b/cloudinit/cmd/main.py
index 7c65257..5b7f953 100644
--- a/cloudinit/cmd/main.py
+++ b/cloudinit/cmd/main.py
@@ -29,6 +29,7 @@ from cloudinit import templater
from cloudinit import url_helper
from cloudinit import util
from cloudinit import version
+from cloudinit import warnings
from cloudinit import reporting
from cloudinit.reporting import events
@@ -413,10 +414,44 @@ def main_init(name, args):
# give the activated datasource a chance to adjust
init.activate_datasource()
+ di_report_warn(datasource=init.datasource, cfg=init.cfg)
+
# Stage 10
return (init.datasource, run_module_section(mods, name, name))
+def di_report_warn(datasource, cfg):
+ dicfg = cfg.get('di_report', {})
+ if not isinstance(dicfg, dict):
+ LOG.warn("di_report config not a dictionary: %s", dicfg)
+ return
+
+ dslist = dicfg.get('datasource_list')
+ if dslist is None:
+ LOG.warn("no 'datasource_list' found in di_report.")
+ return
+ elif not isinstance(dslist, list):
+ LOG.warn("di_report/datasource_list not a list: %s", dslist)
+ return
+
+ # ds.__module__ is like cloudinit.sources.DataSourceName
+ # where Name is the thing that shows up in datasource_list.
+ modname = datasource.__module__.rpartition(".")[2]
+ if modname.startswith(sources.DS_PREFIX):
+ modname = modname[len(sources.DS_PREFIX):]
+ else:
+ LOG.warn("Datasource '%s' came from unexpected module '%s'.",
+ datasource, modname)
+
+ if modname in dslist:
+ LOG.debug("used datasource '%s' from '%s' was in di_report's list: %s",
+ datasource, modname, dslist)
+ return
+
+ warnings.show_warning('dsid_missing_source', cfg,
+ source=modname, dslist=str(dslist))
+
+
def main_modules(action_name, args):
name = args.mode
# Cloud-init 'modules' stages are broken up into the following sub-stages
diff --git a/cloudinit/helpers.py b/cloudinit/helpers.py
index 38f5f89..7435d58 100644
--- a/cloudinit/helpers.py
+++ b/cloudinit/helpers.py
@@ -340,6 +340,7 @@ class Paths(object):
"vendordata": "vendor-data.txt.i",
"instance_id": ".instance-id",
"manual_clean_marker": "manual-clean",
+ "warnings": "warnings",
}
# Set when a datasource becomes active
self.datasource = ds
diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py
index c7df806..01046c8 100644
--- a/cloudinit/sources/DataSourceEc2.py
+++ b/cloudinit/sources/DataSourceEc2.py
@@ -9,7 +9,6 @@
# This file is part of cloud-init. See LICENSE file for license information.
import os
-import textwrap
import time
from cloudinit import ec2_utils as ec2
@@ -17,6 +16,7 @@ from cloudinit import log as logging
from cloudinit import sources
from cloudinit import url_helper as uhelp
from cloudinit import util
+from cloudinit import warnings
LOG = logging.getLogger(__name__)
@@ -224,7 +224,8 @@ class DataSourceEc2(sources.DataSource):
return
if self.cloud_platform == Platforms.UNKNOWN:
warn_if_necessary(
- util.get_cfg_by_path(cfg, STRICT_ID_PATH, STRICT_ID_DEFAULT))
+ util.get_cfg_by_path(cfg, STRICT_ID_PATH, STRICT_ID_DEFAULT),
+ cfg)
def read_strict_mode(cfgval, default):
@@ -265,55 +266,14 @@ def parse_strict_mode(cfgval):
return mode, sleep
-def warn_if_necessary(cfgval):
+def warn_if_necessary(cfgval, cfg):
try:
mode, sleep = parse_strict_mode(cfgval)
except ValueError as e:
LOG.warn(e)
return
- if mode == "false":
- return
-
- show_warning(sleep)
-
-
-def show_warning(sleep):
- message = textwrap.dedent("""
- ****************************************************************
- # This system is using the EC2 Metadata Service, but does not #
- # appear to be running on Amazon EC2 or one of cloud-init's #
- # known platforms that provide a EC2 Metadata service. In the #
- # future, cloud-init may stop reading metadata from the EC2 #
- # Metadata Service unless the platform can be identified #
- # #
- # If you are seeing this message, please file a bug against #
- # cloud-init at https://bugs.launchpad.net/cloud-init/+filebug #
- # Make sure to include the cloud provider your instance is #
- # running on. #
- # #
- # For more information see #
- # https://bugs.launchpad.net/cloud-init/+bug/1660385 #
- # #
- # After you have filed a bug, you can disable this warning by #
- # launching your instance with the cloud-config below, or #
- # putting that content into #
- # /etc/cloud/cloud.cfg.d/99-ec2-datasource.cfg #
- # #
- # #cloud-config #
- # datasource: #
- # Ec2: #
- # strict_id: false #
- # #
- """)
- closemsg = ""
- if sleep:
- closemsg = " [sleeping for %d seconds] " % sleep
- message += closemsg.center(64, "*")
- print(message)
- LOG.warn(message)
- if sleep:
- time.sleep(sleep)
+ warnings.show_warning('non_ec2_md', cfg, mode=mode, sleep=sleep)
def identify_aws(data):
diff --git a/cloudinit/warnings.py b/cloudinit/warnings.py
new file mode 100644
index 0000000..10dae9c
--- /dev/null
+++ b/cloudinit/warnings.py
@@ -0,0 +1,137 @@
+# This file is part of cloud-init. See LICENSE file for license information.
+
+from cloudinit import helpers
+from cloudinit import log as logging
+from cloudinit import util
+
+import os
+import time
+
+LOG = logging.getLogger()
+
+WARNINGS = {
+ 'non_ec2_md': """
+This system is using the EC2 Metadata Service, but does not appear to
+be running on Amazon EC2 or one of cloud-init's known platforms that
+provide a EC2 Metadata service. In the future, cloud-init may stop
+reading metadata from the EC2 Metadata Service unless the platform can
+be identified.
+
+If you are seeing this message, please file a bug against
+cloud-init at
+ https://bugs.launchpad.net/cloud-init/+filebug?field.tags=dsid
+Make sure to include the cloud provider your instance is
+running on.
+
+For more information see
+ https://bugs.launchpad.net/cloud-init/+bug/1660385
+
+After you have filed a bug, you can disable this warning by
+launching your instance with the cloud-config below, or
+putting that content into
+ /etc/cloud/cloud.cfg.d/99-ec2-datasource.cfg
+
+#cloud-config
+datasource:
+ Ec2:
+ strict_id: false""",
+ 'dsid_missing_source': """
+A new feature in cloud-init identified possible datasources for
+this system as:
+ {dslist}
+However, the datasource used was: {source}
+
+In the future, cloud-init will start to only attempt to use
+datasources that are identified.
+
+If you are seeing this message, please file a bug against
+cloud-init at
+ https://bugs.launchpad.net/cloud-init/+filebug?field.tags=dsid
+Make sure to include the cloud provider your instance is
+running on.
+
+After you have filed a bug, you can disable this warning by launching
+your instance with the cloud-config below, or putting that content
+into /etc/cloud/cloud.cfg.d/99-warnings.cfg
+
+#cloud-config
+warnings:
+ dsid_missing_source: off"""
+
+}
+
+
+def _get_warn_dir(cfg):
+ paths = helpers.Paths(
+ path_cfgs=cfg.get('system_info', {}).get('paths', {}))
+ return paths.get_ipath_cur('warnings')
+
+
+def _load_warn_cfg(cfg, name, mode=True, sleep=None):
+ # parse cfg['warnings']['name'] returning boolean, sleep
+ # expected value is form of:
+ # (on|off|true|false|sleep)[,sleeptime]
+ # boolean True == on, False == off
+ default = (mode, sleep)
+ if not cfg or not isinstance(cfg, dict):
+ return default
+
+ ncfg = util.get_cfg_by_path(cfg, ('warnings', name))
+ if ncfg is None:
+ return default
+
+ if ncfg in ("on", "true", True):
+ return True, None
+
+ if ncfg in ("off", "false", False):
+ return False, None
+
+ mode, _, csleep = ncfg.partition(",")
+ if mode != "sleep":
+ return default
+
+ if csleep:
+ try:
+ sleep = int(csleep)
+ except ValueError:
+ return default
+
+ return True, sleep
+
+
+def show_warning(name, cfg=None, sleep=None, mode=True, **kwargs):
+ # kwargs are used for .format of the message.
+ # sleep and mode are default values used if
+ # cfg['warnings']['name'] is not present.
+ if cfg is None:
+ cfg = {}
+
+ mode, sleep = _load_warn_cfg(cfg, name, mode=mode, sleep=sleep)
+ if not mode:
+ return
+
+ msg = WARNINGS[name].format(**kwargs)
+ msgwidth = 70
+ linewidth = msgwidth + 4
+
+ fmt = "# %%-%ds #" % msgwidth
+ topline = "*" * linewidth + "\n"
+ fmtlines = []
+ for line in msg.strip("\n").splitlines():
+ fmtlines.append(fmt % line)
+
+ closeline = topline
+ if sleep:
+ sleepmsg = " [sleeping for %d seconds] " % sleep
+ closeline = sleepmsg.center(linewidth, "*") + "\n"
+
+ util.write_file(
+ os.path.join(_get_warn_dir(cfg), name),
+ topline + "\n".join(fmtlines) + "\n" + topline)
+
+ LOG.warn(topline + "\n".join(fmtlines) + "\n" + closeline)
+
+ if sleep:
+ time.sleep(sleep)
+
+# vi: ts=4 expandtab
diff --git a/packages/debian/rules.in b/packages/debian/rules.in
index 9b00435..053b764 100755
--- a/packages/debian/rules.in
+++ b/packages/debian/rules.in
@@ -11,6 +11,8 @@ override_dh_install:
dh_install
install -d debian/cloud-init/etc/rsyslog.d
cp tools/21-cloudinit.conf debian/cloud-init/etc/rsyslog.d/21-cloudinit.conf
+ install -D ./tools/Z99-cloud-locale-test.sh debian/cloud-init/etc/profile.d/Z99-cloud-locale-test.sh
+ install -D ./tools/Z99-cloudinit-warnings.sh debian/cloud-init/etc/profile.d/Z99-cloudinit-warnings.sh
override_dh_auto_test:
ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS)))
diff --git a/tools/Z99-cloud-locale-test.sh b/tools/Z99-cloud-locale-test.sh
old mode 100755
new mode 100644
index 5912bae..4978d87
--- a/tools/Z99-cloud-locale-test.sh
+++ b/tools/Z99-cloud-locale-test.sh
@@ -11,90 +11,90 @@
# of how to fix them.
locale_warn() {
- local bad_names="" bad_lcs="" key="" val="" var="" vars="" bad_kv=""
- local w1 w2 w3 w4 remain
+ local bad_names="" bad_lcs="" key="" val="" var="" vars="" bad_kv=""
+ local w1 w2 w3 w4 remain
- # if shell is zsh, act like sh only for this function (-L).
- # The behavior change will not permenently affect user's shell.
- [ "${ZSH_NAME+zsh}" = "zsh" ] && emulate -L sh
+ # if shell is zsh, act like sh only for this function (-L).
+ # The behavior change will not permenently affect user's shell.
+ [ "${ZSH_NAME+zsh}" = "zsh" ] && emulate -L sh
- # locale is expected to output either:
- # VARIABLE=
- # VARIABLE="value"
- # locale: Cannot set LC_SOMETHING to default locale
- while read -r w1 w2 w3 w4 remain; do
- case "$w1" in
- locale:) bad_names="${bad_names} ${w4}";;
- *)
- key=${w1%%=*}
- val=${w1#*=}
- val=${val#\"}
- val=${val%\"}
- vars="${vars} $key=$val";;
- esac
- done
- for bad in $bad_names; do
- for var in ${vars}; do
- [ "${bad}" = "${var%=*}" ] || continue
- val=${var#*=}
- [ "${bad_lcs#* ${val}}" = "${bad_lcs}" ] &&
- bad_lcs="${bad_lcs} ${val}"
- bad_kv="${bad_kv} $bad=$val"
- break
- done
- done
- bad_lcs=${bad_lcs# }
- bad_kv=${bad_kv# }
- [ -n "$bad_lcs" ] || return 0
+ # locale is expected to output either:
+ # VARIABLE=
+ # VARIABLE="value"
+ # locale: Cannot set LC_SOMETHING to default locale
+ while read -r w1 w2 w3 w4 remain; do
+ case "$w1" in
+ locale:) bad_names="${bad_names} ${w4}";;
+ *)
+ key=${w1%%=*}
+ val=${w1#*=}
+ val=${val#\"}
+ val=${val%\"}
+ vars="${vars} $key=$val";;
+ esac
+ done
+ for bad in $bad_names; do
+ for var in ${vars}; do
+ [ "${bad}" = "${var%=*}" ] || continue
+ val=${var#*=}
+ [ "${bad_lcs#* ${val}}" = "${bad_lcs}" ] &&
+ bad_lcs="${bad_lcs} ${val}"
+ bad_kv="${bad_kv} $bad=$val"
+ break
+ done
+ done
+ bad_lcs=${bad_lcs# }
+ bad_kv=${bad_kv# }
+ [ -n "$bad_lcs" ] || return 0
- printf "_____________________________________________________________________\n"
- printf "WARNING! Your environment specifies an invalid locale.\n"
- printf " The unknown environment variables are:\n %s\n" "$bad_kv"
- printf " This can affect your user experience significantly, including the\n"
- printf " ability to manage packages. You may install the locales by running:\n\n"
+ printf "_____________________________________________________________________\n"
+ printf "WARNING! Your environment specifies an invalid locale.\n"
+ printf " The unknown environment variables are:\n %s\n" "$bad_kv"
+ printf " This can affect your user experience significantly, including the\n"
+ printf " ability to manage packages. You may install the locales by running:\n\n"
- local bad invalid="" to_gen="" sfile="/usr/share/i18n/SUPPORTED"
- local pkgs=""
- if [ -e "$sfile" ]; then
- for bad in ${bad_lcs}; do
- grep -q -i "${bad}" "$sfile" &&
- to_gen="${to_gen} ${bad}" ||
- invalid="${invalid} ${bad}"
- done
- else
- printf " sudo apt-get install locales\n"
- to_gen=$bad_lcs
- fi
- to_gen=${to_gen# }
+ local bad invalid="" to_gen="" sfile="/usr/share/i18n/SUPPORTED"
+ local pkgs=""
+ if [ -e "$sfile" ]; then
+ for bad in ${bad_lcs}; do
+ grep -q -i "${bad}" "$sfile" &&
+ to_gen="${to_gen} ${bad}" ||
+ invalid="${invalid} ${bad}"
+ done
+ else
+ printf " sudo apt-get install locales\n"
+ to_gen=$bad_lcs
+ fi
+ to_gen=${to_gen# }
- local pkgs=""
- for bad in ${to_gen}; do
- pkgs="${pkgs} language-pack-${bad%%_*}"
- done
- pkgs=${pkgs# }
+ local pkgs=""
+ for bad in ${to_gen}; do
+ pkgs="${pkgs} language-pack-${bad%%_*}"
+ done
+ pkgs=${pkgs# }
- if [ -n "${pkgs}" ]; then
- printf " sudo apt-get install ${pkgs# }\n"
- printf " or\n"
- printf " sudo locale-gen ${to_gen# }\n"
- printf "\n"
- fi
- for bad in ${invalid}; do
- printf "WARNING: '${bad}' is an invalid locale\n"
- done
+ if [ -n "${pkgs}" ]; then
+ printf " sudo apt-get install ${pkgs# }\n"
+ printf " or\n"
+ printf " sudo locale-gen ${to_gen# }\n"
+ printf "\n"
+ fi
+ for bad in ${invalid}; do
+ printf "WARNING: '${bad}' is an invalid locale\n"
+ done
- printf "To see all available language packs, run:\n"
- printf " apt-cache search \"^language-pack-[a-z][a-z]$\"\n"
- printf "To disable this message for all users, run:\n"
- printf " sudo touch /var/lib/cloud/instance/locale-check.skip\n"
- printf "_____________________________________________________________________\n\n"
+ printf "To see all available language packs, run:\n"
+ printf " apt-cache search \"^language-pack-[a-z][a-z]$\"\n"
+ printf "To disable this message for all users, run:\n"
+ printf " sudo touch /var/lib/cloud/instance/locale-check.skip\n"
+ printf "_____________________________________________________________________\n\n"
- # only show the message once
- : > ~/.cloud-locale-test.skip 2>/dev/null || :
+ # only show the message once
+ : > ~/.cloud-locale-test.skip 2>/dev/null || :
}
[ -f ~/.cloud-locale-test.skip -o -f /var/lib/cloud/instance/locale-check.skip ] ||
- locale 2>&1 | locale_warn
+ locale 2>&1 | locale_warn
unset locale_warn
-# vi: ts=4 noexpandtab
+# vi: ts=4 expandtab
diff --git a/tools/Z99-cloudinit-warnings.sh b/tools/Z99-cloudinit-warnings.sh
new file mode 100644
index 0000000..d3d385e
--- /dev/null
+++ b/tools/Z99-cloudinit-warnings.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+# Copyright (C) 2017, Canonical Group, Ltd.
+#
+# Author: Scott Moser <scott.moser@xxxxxxxxxx>
+#
+# This file is part of cloud-init. See LICENSE file for license information.
+
+# Purpose: show user warnings on login.
+
+cloud_init_warnings() {
+ local skipf="" warning="" idir="/var/lib/cloud/instance" n=0
+ local warndir="$idir/warnings"
+ local ufile="$HOME/.cloud-warnings.skip" sfile="$warndir/.skip"
+ [ -d "$warndir" ] || return 0
+ [ ! -f "$ufile" ] || return 0
+ [ ! -f "$skipf" ] || return 0
+
+ for warning in "$warndir"/*; do
+ [ -f "$warning" ] || continue
+ cat "$warning"
+ n=$((n+1))
+ done
+ [ $n -eq 0 ] || return 0
+ echo ""
+ echo "Disable the warnings above by:"
+ echo " touch $ufile"
+ echo "or"
+ echo " touch $sfile"
+}
+
+cloud_init_warnings 1>&2
+unset cloud_init_warnings
+
+# vi: syntax=sh ts=4 expandtab
diff --git a/tools/ds-identify b/tools/ds-identify
index 9711a23..fd2a46c 100755
--- a/tools/ds-identify
+++ b/tools/ds-identify
@@ -10,8 +10,9 @@
# default setting is:
# search,found=all,maybe=all,notfound=disable
#
-# report: write config to /run/cloud-init/cloud.cfg.report (instead of
-# /run/cloud-init/cloud.cfg, which effectively makes this dry-run).
+# report: write config to /run/cloud-init/cloud.cfg, but
+# namespaced under 'di_report'. Thus cloud-init can still see
+# the result, but has no affect.
# enable: do nothing
# ds-identify writes no config and just exits success
# the caller (cloud-init-generator) then enables cloud-init to run
@@ -867,15 +868,16 @@ _print_info() {
}
write_result() {
- local runcfg="${PATH_RUN_CI_CFG}" ret="" line=""
- if [ "$DI_REPORT" = "true" ]; then
- # if report is true, then we write to .report, but touch the other.
- : > "$runcfg"
- runcfg="$runcfg.report"
- fi
- for line in "$@"; do
- echo "$line"
- done > "$runcfg"
+ local runcfg="${PATH_RUN_CI_CFG}" ret="" line="" pre=""
+ {
+ if [ "$DI_REPORT" = "true" ]; then
+ echo "di_report:"
+ pre=" "
+ fi
+ for line in "$@"; do
+ echo "${pre}$line";
+ done
+ } > "$runcfg"
ret=$?
[ $ret -eq 0 ] || {
error "failed to write to ${runcfg}"
@@ -956,6 +958,7 @@ _read_config() {
if [ "$keyname" = "_unset" ]; then
return 1
fi
+ _RET=""
return 0
}
Follow ups