Skip to content

Commit

Permalink
feat(systemd): Warn user of unexpected run mode (#5209)
Browse files Browse the repository at this point in the history
On systemd, services are started by PID 1. When this doesn't happen, cloud-init
is in an unknown run state and should warn the user.

Reorder pid log to be able to reuse Distro information.

Add docstring deprecating util.is_Linux().
  • Loading branch information
holmanb committed Aug 6, 2024
1 parent 046bdd5 commit 2eadd0d
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 9 deletions.
31 changes: 22 additions & 9 deletions cloudinit/cmd/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,29 @@ def print_exc(msg=""):
sys.stderr.write("\n")


def log_ppid():
if util.is_Linux():
def log_ppid(distro, bootstage_name):
if distro.is_linux:
ppid = os.getppid()
LOG.info("PID [%s] started cloud-init.", ppid)
log = LOG.info
extra_message = ""
if 1 != ppid and distro.uses_systemd():
log = LOG.warning
extra_message = (
" Unsupported configuration: boot stage called "
"outside of systemd"
)
log(
"PID [%s] started cloud-init '%s'.%s",
ppid,
bootstage_name,
extra_message,
)


def welcome(action, msg=None):
if not msg:
msg = welcome_format(action)
util.multi_log("%s\n" % (msg), console=False, stderr=True, log=LOG)
log_ppid()
return msg


Expand Down Expand Up @@ -333,10 +345,8 @@ def main_init(name, args):
# objects config as it may be different from init object
# 10. Run the modules for the 'init' stage
# 11. Done!
if not args.local:
w_msg = welcome_format(name)
else:
w_msg = welcome_format("%s-local" % (name))
bootstage_name = "init-local" if args.local else "init"
w_msg = welcome_format(bootstage_name)
init = stages.Init(ds_deps=deps, reporter=args.reporter)
# Stage 1
init.read_cfg(extract_fns(args))
Expand Down Expand Up @@ -364,6 +374,7 @@ def main_init(name, args):
# config applied. We send the welcome message now, as stderr/out have
# been redirected and log now configured.
welcome(name, msg=w_msg)
log_ppid(init.distro, bootstage_name)

# re-play early log messages before logging was setup
for lvl, msg in early_logs:
Expand Down Expand Up @@ -591,7 +602,8 @@ def main_modules(action_name, args):
# the modules objects configuration
# 5. Run the modules for the given stage name
# 6. Done!
w_msg = welcome_format("%s:%s" % (action_name, name))
bootstage_name = "%s:%s" % (action_name, name)
w_msg = welcome_format(bootstage_name)
init = stages.Init(ds_deps=[], reporter=args.reporter)
# Stage 1
init.read_cfg(extract_fns(args))
Expand Down Expand Up @@ -628,6 +640,7 @@ def main_modules(action_name, args):

# now that logging is setup and stdout redirected, send welcome
welcome(name, msg=w_msg)
log_ppid(init.distro, bootstage_name)

if name == "init":
util.deprecate(
Expand Down
3 changes: 3 additions & 0 deletions cloudinit/distros/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ def __init__(self, name, cfg, paths):
self.package_managers: List[PackageManager] = []
self._dhcp_client = None
self._fallback_interface = None
self.is_linux = True

def _unpickle(self, ci_pkl_version: int) -> None:
"""Perform deserialization fixes for Distro."""
Expand All @@ -187,6 +188,8 @@ def _unpickle(self, ci_pkl_version: int) -> None:
self._dhcp_client = None
if not hasattr(self, "_fallback_interface"):
self._fallback_interface = None
if not hasattr(self, "is_linux"):
self.is_linux = True

def _validate_entry(self, entry):
if isinstance(entry, str):
Expand Down
7 changes: 7 additions & 0 deletions cloudinit/distros/bsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ def __init__(self, name, cfg, paths):
cfg["rsyslog_svcname"] = "rsyslogd"
self.osfamily = platform.system().lower()
self.net_ops = bsd_netops.BsdNetOps
self.is_linux = False

def _unpickle(self, ci_pkl_version: int) -> None:
super()._unpickle(ci_pkl_version)

# this needs to be after the super class _unpickle to override it
self.is_linux = False

def _read_system_hostname(self):
sys_hostname = self._read_hostname(self.hostname_conf_fn)
Expand Down
6 changes: 6 additions & 0 deletions cloudinit/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,12 @@ def multi_log(

@lru_cache()
def is_Linux():
"""deprecated: prefer Distro object's `is_linux` property
Multiple sources of truth is bad, and already know whether we are
working with Linux from the Distro class. Using Distro offers greater code
reusablity, cleaner code, and easier maintenance.
"""
return "Linux" in platform.system()


Expand Down

0 comments on commit 2eadd0d

Please sign in to comment.