reporting: add new logging type events
Adds new reporting type events "INFO", "WARNING", and "ERROR" to be used for context logging. These can be invoked with the new `.info`, `.warning`, and `.error` methods on the context object accordingly. Useful for things like warning/errors on autoinstall configuartions.
This commit is contained in:
parent
c25e28ef2c
commit
980411a670
|
@ -17,10 +17,17 @@ import copy
|
|||
import logging
|
||||
|
||||
from curtin.reporter import available_handlers, update_configuration
|
||||
from curtin.reporter.events import report_finish_event, report_start_event, status
|
||||
from curtin.reporter.events import (
|
||||
ReportingEvent,
|
||||
report_event,
|
||||
report_finish_event,
|
||||
report_start_event,
|
||||
status,
|
||||
)
|
||||
from curtin.reporter.handlers import LogHandler as CurtinLogHandler
|
||||
|
||||
from subiquity.server.controller import NonInteractiveController
|
||||
from subiquitycore.context import Context
|
||||
|
||||
|
||||
class LogHandler(CurtinLogHandler):
|
||||
|
@ -76,3 +83,18 @@ class ReportingController(NonInteractiveController):
|
|||
report_finish_event(
|
||||
context.full_name(), description, result, level=context.level
|
||||
)
|
||||
|
||||
def report_info_event(self, context: Context, message: str):
|
||||
"""Report an "info" event."""
|
||||
event = ReportingEvent("info", context.full_name(), message, level="INFO")
|
||||
report_event(event)
|
||||
|
||||
def report_warning_event(self, context: Context, message: str):
|
||||
"""Report a "warning" event."""
|
||||
event = ReportingEvent("warning", context.full_name(), message, level="WARNING")
|
||||
report_event(event)
|
||||
|
||||
def report_error_event(self, context: Context, message: str):
|
||||
"""Report an "error" event."""
|
||||
event = ReportingEvent("error", context.full_name(), message, level="ERROR")
|
||||
report_event(event)
|
||||
|
|
|
@ -13,11 +13,17 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import jsonschema
|
||||
from curtin.reporter.events import status as CurtinStatus
|
||||
from jsonschema.validators import validator_for
|
||||
|
||||
from subiquity.server.controllers.reporting import ReportingController
|
||||
from subiquitycore.context import Context
|
||||
from subiquitycore.context import Status as ContextStatus
|
||||
from subiquitycore.tests import SubiTestCase
|
||||
from subiquitycore.tests.mocks import MockedApplication, make_app
|
||||
|
||||
|
||||
class TestReportingController(SubiTestCase):
|
||||
|
@ -29,3 +35,85 @@ class TestReportingController(SubiTestCase):
|
|||
)
|
||||
|
||||
JsonValidator.check_schema(ReportingController.autoinstall_schema)
|
||||
|
||||
|
||||
@patch("subiquity.server.controllers.reporting.report_event")
|
||||
class TestReportingCurtinCalls(SubiTestCase):
|
||||
def setUp(self):
|
||||
app: MockedApplication = make_app()
|
||||
self.controller: ReportingController = ReportingController(app)
|
||||
self.context: Context = app.context
|
||||
|
||||
@patch("subiquity.server.controllers.reporting.report_start_event")
|
||||
def test_start_event(self, report_start_event, report_event):
|
||||
self.controller.report_start_event(self.context, "description")
|
||||
|
||||
# Calls specific start event method
|
||||
report_start_event.assert_called_with(
|
||||
self.context.full_name(), "description", level=self.context.level
|
||||
)
|
||||
|
||||
# Not the generic one
|
||||
report_event.assert_not_called()
|
||||
|
||||
@patch("subiquity.server.controllers.reporting.report_finish_event")
|
||||
def test_finish_event(self, report_finish_event, report_event):
|
||||
self.controller.report_finish_event(
|
||||
self.context, "description", ContextStatus.FAIL
|
||||
)
|
||||
|
||||
# Calls specific finish event method
|
||||
report_finish_event.assert_called_with(
|
||||
self.context.full_name(),
|
||||
"description",
|
||||
CurtinStatus.FAIL,
|
||||
level=self.context.level,
|
||||
)
|
||||
|
||||
# Not the generic one
|
||||
report_event.assert_not_called()
|
||||
|
||||
# Test default WARN
|
||||
status = Mock()
|
||||
status.name = "NEW LEVEL"
|
||||
self.controller.report_finish_event(self.context, "description", status)
|
||||
|
||||
report_finish_event.assert_called_with(
|
||||
self.context.full_name(),
|
||||
"description",
|
||||
CurtinStatus.WARN,
|
||||
level=self.context.level,
|
||||
)
|
||||
|
||||
@patch("subiquity.server.controllers.reporting.ReportingEvent")
|
||||
def test_info_event(self, mock_class, report_event):
|
||||
self.controller.report_info_event(self.context, "description")
|
||||
|
||||
mock_class.assert_called_with(
|
||||
"info",
|
||||
self.context.full_name(),
|
||||
"description",
|
||||
level="INFO",
|
||||
)
|
||||
|
||||
@patch("subiquity.server.controllers.reporting.ReportingEvent")
|
||||
def test_warning_event(self, mock_class, report_event):
|
||||
self.controller.report_warning_event(self.context, "description")
|
||||
|
||||
mock_class.assert_called_with(
|
||||
"warning",
|
||||
self.context.full_name(),
|
||||
"description",
|
||||
level="WARNING",
|
||||
)
|
||||
|
||||
@patch("subiquity.server.controllers.reporting.ReportingEvent")
|
||||
def test_error_event(self, mock_class, report_event):
|
||||
self.controller.report_error_event(self.context, "description")
|
||||
|
||||
mock_class.assert_called_with(
|
||||
"error",
|
||||
self.context.full_name(),
|
||||
"description",
|
||||
level="ERROR",
|
||||
)
|
||||
|
|
|
@ -372,11 +372,14 @@ class SubiquityServer(Application):
|
|||
# - special sections of the install, which set "is-install-context"
|
||||
# where we want to report the event anyways
|
||||
#
|
||||
# - special event types:
|
||||
# - warn
|
||||
# - error
|
||||
#
|
||||
# For non-interactive installs (i.e., full autoinstall) we report
|
||||
# everything.
|
||||
|
||||
force_reporting: bool = install_context
|
||||
force_reporting: bool = install_context or event_type in ["warning", "error"]
|
||||
|
||||
# self.interactive=None could be an interactive install, we just
|
||||
# haven't found out yet
|
||||
|
@ -388,8 +391,6 @@ class SubiquityServer(Application):
|
|||
if controller is None or controller.interactive():
|
||||
return
|
||||
|
||||
# Otherwise it came from the server
|
||||
|
||||
# Create the message out of the name of the reporter and optionally
|
||||
# the description
|
||||
name: str = context.full_name()
|
||||
|
@ -432,6 +433,21 @@ class SubiquityServer(Application):
|
|||
listener.report_finish_event(context, description, status)
|
||||
self._maybe_push_to_journal("finish", context, description)
|
||||
|
||||
def report_info_event(self, context: Context, message: str) -> None:
|
||||
for listener in self.event_listeners:
|
||||
listener.report_info_event(context, message)
|
||||
self._maybe_push_to_journal("info", context, message)
|
||||
|
||||
def report_warning_event(self, context: Context, message: str) -> None:
|
||||
for listener in self.event_listeners:
|
||||
listener.report_warning_event(context, message)
|
||||
self._maybe_push_to_journal("warning", context, message)
|
||||
|
||||
def report_error_event(self, context: Context, message: str) -> None:
|
||||
for listener in self.event_listeners:
|
||||
listener.report_error_event(context, message)
|
||||
self._maybe_push_to_journal("error", context, message)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
return self._state
|
||||
|
|
|
@ -378,3 +378,81 @@ class TestEventReporting(SubiTestCase):
|
|||
journal_send_mock.assert_called_once()
|
||||
else:
|
||||
journal_send_mock.assert_not_called()
|
||||
|
||||
@parameterized.expand(
|
||||
(
|
||||
# interactive, pushed to journal
|
||||
(True, False),
|
||||
(None, False),
|
||||
(False, True),
|
||||
)
|
||||
)
|
||||
def test_push_info_events(self, interactive, expect_pushed):
|
||||
"""Test info event publication"""
|
||||
|
||||
context: Context = Context(
|
||||
self.server, "MockContext", "description", None, "INFO"
|
||||
)
|
||||
self.server.interactive = interactive
|
||||
|
||||
with patch("subiquity.server.server.journal.send") as journal_send_mock:
|
||||
self.server.report_info_event(context, "message")
|
||||
|
||||
if not expect_pushed:
|
||||
journal_send_mock.assert_not_called()
|
||||
else:
|
||||
journal_send_mock.assert_called_once()
|
||||
# message is the only positional argument
|
||||
(message,) = journal_send_mock.call_args.args
|
||||
self.assertIn("message", message)
|
||||
self.assertNotIn("description", message)
|
||||
|
||||
@parameterized.expand(
|
||||
(
|
||||
# interactive
|
||||
(True,),
|
||||
(None,),
|
||||
(False,),
|
||||
)
|
||||
)
|
||||
def test_push_warning_events(self, interactive):
|
||||
"""Test warning event publication"""
|
||||
|
||||
context: Context = Context(
|
||||
self.server, "MockContext", "description", None, "INFO"
|
||||
)
|
||||
self.server.interactive = interactive
|
||||
|
||||
with patch("subiquity.server.server.journal.send") as journal_send_mock:
|
||||
self.server.report_warning_event(context, "message")
|
||||
|
||||
journal_send_mock.assert_called_once()
|
||||
# message is the only positional argument
|
||||
(message,) = journal_send_mock.call_args.args
|
||||
self.assertIn("message", message)
|
||||
self.assertNotIn("description", message)
|
||||
|
||||
@parameterized.expand(
|
||||
(
|
||||
# interactive
|
||||
(True,),
|
||||
(None,),
|
||||
(False,),
|
||||
)
|
||||
)
|
||||
def test_push_error_events(self, interactive):
|
||||
"""Test error event publication"""
|
||||
|
||||
context: Context = Context(
|
||||
self.server, "MockContext", "description", None, "INFO"
|
||||
)
|
||||
self.server.interactive = interactive
|
||||
|
||||
with patch("subiquity.server.server.journal.send") as journal_send_mock:
|
||||
self.server.report_error_event(context, "message")
|
||||
|
||||
journal_send_mock.assert_called_once()
|
||||
# message is the only positional argument
|
||||
(message,) = journal_send_mock.call_args.args
|
||||
self.assertIn("message", message)
|
||||
self.assertNotIn("description", message)
|
||||
|
|
|
@ -118,6 +118,15 @@ class Context:
|
|||
c = c.parent
|
||||
return default
|
||||
|
||||
def info(self, message: str) -> None:
|
||||
self.app.report_info_event(self, message)
|
||||
|
||||
def warning(self, message: str) -> None:
|
||||
self.app.report_warning_event(self, message)
|
||||
|
||||
def error(self, message: str) -> None:
|
||||
self.app.report_error_event(self, message)
|
||||
|
||||
|
||||
def with_context(name=None, description="", **context_kw):
|
||||
def decorate(meth):
|
||||
|
|
|
@ -36,6 +36,7 @@ def make_app(model=None):
|
|||
app.base_model = model
|
||||
else:
|
||||
app.base_model = mock.Mock()
|
||||
app.add_event_listener = mock.Mock()
|
||||
app.controllers = mock.Mock()
|
||||
app.context = Context.new(app)
|
||||
app.exit = mock.Mock()
|
||||
|
|
Loading…
Reference in New Issue