Make ApiValidator to work with jsonschema>=4.0.0

As of Zed jsonschema version has been bumped to 4.14.0 in
upper-constraints, which means project should support running with
this version. This patch aims to fix ApiValidator that is not compatible
with new jsonschema.

Other deprecations, like FormatChecker.cls_checks decorator are left
intact and must be covered with follow-up patches.

We also move zuul queues to project scope as otherwise jobs do not
run due to config error.

We also replace iteritems with six method as iteritems have been dropped
from Python 3.

Change-Id: Ia8b69587aa9b3d04ffcdea7c6b97a8ae65f67534
(cherry picked from commit ebc6d74bf8)
This commit is contained in:
Dmitriy Rabotyagov 2022-11-16 13:58:35 +01:00 committed by Dmitriy Rabotyagov
parent c943f4a510
commit aab24afe34
4 changed files with 56 additions and 71 deletions

View File

@ -1,4 +1,5 @@
- project:
queue: sahara
templates:
- openstack-python3-zed-jobs
- periodic-stable-jobs
@ -22,7 +23,6 @@
- openstack-ansible-deploy-aio_sahara_metal-ubuntu-focal:
voting: false
gate:
queue: sahara
jobs:
- sahara-tests-scenario:
voting: false

View File

@ -259,7 +259,7 @@ class ApiValidatorTest(testtools.TestCase):
def test_validate_hostname(self):
schema = {
"type": "string",
"format": "hostname",
"format": "idn-hostname",
}
self._validate_success(schema, "abcd")

View File

@ -97,85 +97,70 @@ def validate_posix_path(entry):
return res is not None
class ConfigTypeMeta(type):
def __instancecheck__(cls, instance):
# configs should be dict
if not isinstance(instance, dict):
def is_config(cls, instance):
# configs should be dict
if not isinstance(instance, dict):
return False
# check dict content
for applicable_target, configs in six.iteritems(instance):
# upper-level dict keys (applicable targets) should be strings
if not isinstance(applicable_target, six.string_types):
return False
# check dict content
for applicable_target, configs in six.iteritems(instance):
# upper-level dict keys (applicable targets) should be strings
if not isinstance(applicable_target, six.string_types):
return False
# upper-level dict values should be dicts
if not isinstance(configs, dict):
return False
# check internal dict content
for config_name, config_value in six.iteritems(configs):
# internal dict keys should be strings
if not isinstance(config_name, six.string_types):
return False
# internal dict values should be strings or integers or bools
if not isinstance(config_value,
(six.string_types, six.integer_types)):
return False
return True
class SimpleConfigTypeMeta(type):
def __instancecheck__(cls, instance):
# configs should be dict
if not isinstance(instance, dict):
# upper-level dict values should be dicts
if not isinstance(configs, dict):
return False
# check dict content
for conf_name, conf_value in six.iteritems(instance):
# keys should be strings, values should be int, string or bool
if not isinstance(conf_name, six.string_types):
# check internal dict content
for config_name, config_value in six.iteritems(configs):
# internal dict keys should be strings
if not isinstance(config_name, six.string_types):
return False
if not isinstance(conf_value,
# internal dict values should be strings or integers or bools
if not isinstance(config_value,
(six.string_types, six.integer_types)):
return False
return True
return True
@six.add_metaclass(ConfigTypeMeta)
class ConfigsType(dict):
pass
def is_simple_config(cls, instance):
# configs should be dict
if not isinstance(instance, dict):
return False
# check dict content
for conf_name, conf_value in six.iteritems(instance):
# keys should be strings, values should be int, string or bool
if not isinstance(conf_name, six.string_types):
return False
if not isinstance(conf_value,
(six.string_types, six.integer_types)):
return False
return True
@six.add_metaclass(SimpleConfigTypeMeta)
class SimpleConfigsType(dict):
pass
class FlavorTypeMeta(type):
def __instancecheck__(cls, instance):
try:
int(instance)
except (ValueError, TypeError):
return (isinstance(instance, six.string_types)
and uuidutils.is_uuid_like(instance))
return (isinstance(instance, six.integer_types + six.string_types)
and type(instance) != bool)
@six.add_metaclass(FlavorTypeMeta)
class FlavorType(object):
pass
def is_flavor_type(cls, instance):
try:
int(instance)
except (ValueError, TypeError):
return (isinstance(instance, six.string_types)
and uuidutils.is_uuid_like(instance))
return (isinstance(instance, six.integer_types + six.string_types)
and type(instance) != bool)
class ApiValidator(jsonschema.Draft4Validator):
def __init__(self, schema):
format_checker = jsonschema.FormatChecker()
TYPE_CHECKER = jsonschema.Draft4Validator.TYPE_CHECKER.redefine_many({
"configs": is_config,
"flavor": is_flavor_type,
"simple_config": is_simple_config,
})
def __init__(self, schema, resolver=None,
format_checker=jsonschema.FormatChecker()):
super(ApiValidator, self).__init__(
schema, format_checker=format_checker, types={
"configs": ConfigsType,
"flavor": FlavorType,
"simple_config": SimpleConfigsType,
})
schema, format_checker=format_checker)

View File

@ -50,7 +50,7 @@ def wrap_entity(func):
def _get_all_tags(image_props):
tags = []
for key, value in image_props.iteritems():
for key, value in six.iteritems(image_props):
if key.startswith(PROP_TAG) and value:
tags.append(key)
return tags
@ -69,7 +69,7 @@ def _parse_tags(image_props):
def _serialize_metadata(image):
data = {}
for key, value in image.iteritems():
for key, value in six.iteritems(image):
if key.startswith('_sahara') and value:
data[key] = value
return data