Reference:fa53c7f4089c7502a8012e17a0d6269f0efc474ebb414c78669cbd94dd576d817e94be0450a1faff
131 lines
4.8 KiB
Diff
131 lines
4.8 KiB
Diff
From 5864217bf933927982ea3af2d93c2baccbaa3ba4 Mon Sep 17 00:00:00 2001
|
|
From: Andrew Lee <andrew.lee@metaswitch.com>
|
|
Date: Thu, 7 Apr 2022 21:52:44 +0100
|
|
Subject: [PATCH 3/8] =?UTF-8?q?BUG=201473527:=20module=20ssh-authkey-finge?=
|
|
=?UTF-8?q?rprints=20fails=20Input/output=20error=E2=80=A6=20(#1340)?=
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Reference:https://github.com/canonical/cloud-init/commit/fa53c7f4086f5937bc9bd328dba9f91ca73b6614
|
|
Conflict:tools/.github-cla-signers not change.
|
|
|
|
Don't error if we cannot log to /dev/console
|
|
|
|
We've seen instances on VMware of serial consoles not being set up
|
|
correctly by the kernel, making /dev/ttyS0 not set up correctly, and
|
|
hence /dev/console not writeable to.
|
|
|
|
In such circumstances, cloud-init should not fail, instead it should
|
|
gracefully fall back to logging to stdout.
|
|
|
|
The only time cloud-init tries to write to `/dev/console` is in the
|
|
`multi_log` command- which is called by the
|
|
ssh-authkey-fingerprints module
|
|
|
|
LP: #1473527
|
|
---
|
|
cloudinit/util.py | 33 +++++++++++++++++++++++++--------
|
|
tests/unittests/test_util.py | 27 +++++++++++++++++++++++++++
|
|
2 files changed, 52 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/cloudinit/util.py b/cloudinit/util.py
|
|
index ef1b588..d5e8277 100644
|
|
--- a/cloudinit/util.py
|
|
+++ b/cloudinit/util.py
|
|
@@ -359,20 +359,37 @@ def find_modules(root_dir):
|
|
return entries
|
|
|
|
|
|
+def write_to_console(conpath, text):
|
|
+ with open(conpath, "w") as wfh:
|
|
+ wfh.write(text)
|
|
+ wfh.flush()
|
|
+
|
|
+
|
|
def multi_log(text, console=True, stderr=True,
|
|
log=None, log_level=logging.DEBUG, fallback_to_stdout=True):
|
|
if stderr:
|
|
sys.stderr.write(text)
|
|
if console:
|
|
conpath = "/dev/console"
|
|
+ writing_to_console_worked = False
|
|
if os.path.exists(conpath):
|
|
- with open(conpath, 'w') as wfh:
|
|
- wfh.write(text)
|
|
- wfh.flush()
|
|
- elif fallback_to_stdout:
|
|
- # A container may lack /dev/console (arguably a container bug). If
|
|
- # it does not exist, then write output to stdout. this will result
|
|
- # in duplicate stderr and stdout messages if stderr was True.
|
|
+ try:
|
|
+ write_to_console(conpath, text)
|
|
+ writing_to_console_worked = True
|
|
+ except OSError:
|
|
+ console_error = "Failed to write to /dev/console"
|
|
+ sys.stdout.write(f"{console_error}\n")
|
|
+ if log:
|
|
+ log.log(logging.WARNING, console_error)
|
|
+
|
|
+ if fallback_to_stdout and not writing_to_console_worked:
|
|
+ # A container may lack /dev/console (arguably a container bug).
|
|
+ # Additionally, /dev/console may not be writable to on a VM (again
|
|
+ # likely a VM bug or virtualization bug).
|
|
+ #
|
|
+ # If either of these is the case, then write output to stdout.
|
|
+ # This will result in duplicate stderr and stdout messages if
|
|
+ # stderr was True.
|
|
#
|
|
# even though upstart or systemd might have set up output to go to
|
|
# /dev/console, the user may have configured elsewhere via
|
|
@@ -1948,7 +1965,7 @@ def write_file(
|
|
omode="wb",
|
|
preserve_mode=False,
|
|
*,
|
|
- ensure_dir_exists=True
|
|
+ ensure_dir_exists=True,
|
|
):
|
|
"""
|
|
Writes a file with the given content and sets the file mode as specified.
|
|
diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py
|
|
index bc30c90..0b01337 100644
|
|
--- a/tests/unittests/test_util.py
|
|
+++ b/tests/unittests/test_util.py
|
|
@@ -576,6 +576,33 @@ class TestMultiLog(helpers.FilesystemMockingTestCase):
|
|
util.multi_log('something', fallback_to_stdout=False)
|
|
self.assertEqual('', self.stdout.getvalue())
|
|
|
|
+ @mock.patch(
|
|
+ "cloudinit.util.write_to_console",
|
|
+ mock.Mock(side_effect=OSError("Failed to write to console")),
|
|
+ )
|
|
+ def test_logs_go_to_stdout_if_writing_to_console_fails_and_fallback_true(
|
|
+ self,
|
|
+ ):
|
|
+ self._createConsole(self.root)
|
|
+ util.multi_log("something", fallback_to_stdout=True)
|
|
+ self.assertEqual(
|
|
+ "Failed to write to /dev/console\nsomething",
|
|
+ self.stdout.getvalue(),
|
|
+ )
|
|
+
|
|
+ @mock.patch(
|
|
+ "cloudinit.util.write_to_console",
|
|
+ mock.Mock(side_effect=OSError("Failed to write to console")),
|
|
+ )
|
|
+ def test_logs_go_nowhere_if_writing_to_console_fails_and_fallback_false(
|
|
+ self,
|
|
+ ):
|
|
+ self._createConsole(self.root)
|
|
+ util.multi_log("something", fallback_to_stdout=False)
|
|
+ self.assertEqual(
|
|
+ "Failed to write to /dev/console\n", self.stdout.getvalue()
|
|
+ )
|
|
+
|
|
def test_logs_go_to_log_if_given(self):
|
|
log = mock.MagicMock()
|
|
logged_string = 'something very important'
|
|
--
|
|
2.40.0
|
|
|