From b01ac77f98f613fba0e8b221b2b4dcb73517d37a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 8 Oct 2025 13:19:39 +0100 Subject: [PATCH] api: Catch correct exception on schema validation error The 'SchemaValidator.validate' method (from 'ironic.api.validation.validators') already catches the 'jsonschema.ValidationError' exception and raises Ironic's own 'InvalidParameterValue' exception in its place. Thus, we need to catch the latter, not the former. Change-Id: Ic8668afe5a2ff85a5c089c7adae9c8af541f7e84 Signed-off-by: Stephen Finucane Partial-bug: #2127079 (cherry picked from commit 986524ab22f86a109c0a419696a4847d68de2706) --- ironic/api/validation/__init__.py | 4 +- .../unit/api/validation/test_validators.py | 51 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/ironic/api/validation/__init__.py b/ironic/api/validation/__init__.py index 91647ecc92..cab00e3c7c 100644 --- a/ironic/api/validation/__init__.py +++ b/ironic/api/validation/__init__.py @@ -16,7 +16,6 @@ import functools import inspect import typing as ty -import jsonschema.exceptions from oslo_config import cfg from oslo_log import log from oslo_serialization import jsonutils @@ -24,6 +23,7 @@ from webob import exc as webob_exc from ironic import api from ironic.api.validation import validators +from ironic.common import exception from ironic.common.i18n import _ CONF = cfg.CONF @@ -381,7 +381,7 @@ def response_body_schema( max_version, is_body=True, ) - except jsonschema.exceptions.ValidationError: + except exception.InvalidParameterValue: if CONF.api.response_validation == 'warn': LOG.exception('Schema failed to validate') else: diff --git a/ironic/tests/unit/api/validation/test_validators.py b/ironic/tests/unit/api/validation/test_validators.py index da3c1d79c2..a01ae14359 100644 --- a/ironic/tests/unit/api/validation/test_validators.py +++ b/ironic/tests/unit/api/validation/test_validators.py @@ -10,6 +10,9 @@ # License for the specific language governing permissions and limitations # under the License. +from unittest import mock + +from ironic.api import validation from ironic.api.validation import validators from ironic.common import exception from ironic.tests import base as test_base @@ -44,3 +47,51 @@ class TestSchemaValidator(test_base.TestCase): validator.validate, 'invalid date-time' ) + + +class TestResponseBodyValidation(test_base.TestCase): + + def setUp(self): + super().setUp() + + self.schema = { + 'type': 'object', + 'properties': {'foo': {'type': 'string'}}, + 'required': ['foo'], + } + + @mock.patch.object(validation, 'LOG', autospec=True) + def test_response_validation__error(self, mock_log): + self.config(response_validation='error', group='api') + + @validation.response_body_schema(self.schema) + def test_func(): + return {'foo': 123} + + self.assertRaises(exception.InvalidParameterValue, test_func) + mock_log.exception.assert_not_called() + + @mock.patch.object(validation, 'LOG', autospec=True) + def test_response_validation__warn(self, mock_log): + self.config(response_validation='warn', group='api') + + @validation.response_body_schema(self.schema) + def test_func(): + return {'foo': 123} + + result = test_func() + self.assertEqual({'foo': 123}, result) + mock_log.exception.assert_called_once_with('Schema failed to validate') + + @mock.patch.object(validation, 'LOG', autospec=True) + def test_response_validation__ignore(self, mock_log): + self.config(response_validation='ignore', group='api') + + @validation.response_body_schema(self.schema) + def test_func(): + return {'foo': 123} + + # Should not call validator at all in ignore mode + result = test_func() + self.assertEqual({'foo': 123}, result) + mock_log.exception.assert_not_called()