[jsonschema] Require specifying additionalProperties
explicitly
Validation of objects via jsonschema become meaningless if there is no `additionalProperties: False`. In case of complex schemas, it is easy to miss this property in some child property. Let's require always specifing additionalProperties to simplify the life of reviewers and do not miss anything. Change-Id: I49693a7968e820010751909a48f06f3f784c5721
This commit is contained in:
parent
dbdb175270
commit
8a95c29f55
@ -79,7 +79,8 @@ class CeilometerSampleGenerator(context.Context):
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
}
|
||||
},
|
||||
"batch_size": {
|
||||
|
@ -73,12 +73,15 @@ class HeatDataplane(context.Context):
|
||||
},
|
||||
"files": {
|
||||
"type": "object",
|
||||
"additionalProperties": True
|
||||
},
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"additionalProperties": True
|
||||
},
|
||||
"context_parameters": {
|
||||
"type": "object",
|
||||
"additionalProperties": True
|
||||
},
|
||||
},
|
||||
"additionalProperties": False
|
||||
|
@ -39,7 +39,8 @@ class EC2ServerGenerator(context.Context):
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
},
|
||||
"flavor": {
|
||||
"type": "object",
|
||||
@ -47,7 +48,8 @@ class EC2ServerGenerator(context.Context):
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
},
|
||||
"servers_per_tenant": {
|
||||
"type": "integer",
|
||||
|
@ -70,12 +70,13 @@ class ShareNetworks(context.Context):
|
||||
"properties": {
|
||||
"use_share_networks": {
|
||||
"type": "boolean",
|
||||
"description": "specifies whether manila should use share "
|
||||
"description": "Specifies whether manila should use share "
|
||||
"networks for share creation or not."},
|
||||
|
||||
"share_networks": {
|
||||
"type": "object",
|
||||
"description": SHARE_NETWORKS_ARG_DESCR
|
||||
"description": SHARE_NETWORKS_ARG_DESCR,
|
||||
"additionalProperties": True
|
||||
},
|
||||
},
|
||||
"additionalProperties": False
|
||||
|
@ -48,7 +48,8 @@ class MonascaMetricGenerator(context.Context):
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
},
|
||||
"metrics_per_tenant": {
|
||||
"type": "integer",
|
||||
@ -65,7 +66,8 @@ class MonascaMetricGenerator(context.Context):
|
||||
"value_meta_value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -48,7 +48,8 @@ class Router(context.Context):
|
||||
"properties": {
|
||||
"network_id": {"type": "string"},
|
||||
"enable_snat": {"type": "boolean"}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
},
|
||||
"network_id": {
|
||||
"description": "Network ID",
|
||||
@ -62,7 +63,8 @@ class Router(context.Context):
|
||||
"properties": {
|
||||
"ip_address": {"type": "string"},
|
||||
"subnet_id": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
}
|
||||
},
|
||||
"distributed": {
|
||||
|
@ -32,7 +32,8 @@ class Lbaas(context.Context):
|
||||
"$schema": consts.JSON_SCHEMA,
|
||||
"properties": {
|
||||
"pool": {
|
||||
"type": "object"
|
||||
"type": "object",
|
||||
"additionalProperties": True
|
||||
},
|
||||
"lbaas_version": {
|
||||
"type": "integer",
|
||||
|
@ -38,14 +38,16 @@ class ServerGenerator(context.Context):
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
},
|
||||
"flavor": {
|
||||
"description": "Name of flavor to boot server(s) with.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
},
|
||||
"servers_per_tenant": {
|
||||
"description": "Number of servers to boot in each Tenant.",
|
||||
@ -60,11 +62,18 @@ class ServerGenerator(context.Context):
|
||||
"type": "array",
|
||||
"description": "List of networks to attach to server.",
|
||||
"items": {"oneOf": [
|
||||
{"type": "object",
|
||||
"properties": {"net-id": {"type": "string"}},
|
||||
"description": "Network ID in a format like OpenStack API"
|
||||
" expects to see."},
|
||||
{"type": "string", "description": "Network ID."}]},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {"net-id": {"type": "string"}},
|
||||
"description": "Network ID in a format like OpenStack "
|
||||
"API expects to see.",
|
||||
"additionalProperties": False
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Network ID."
|
||||
}
|
||||
]},
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
|
@ -77,10 +77,12 @@ class SaharaCluster(context.Context):
|
||||
}
|
||||
},
|
||||
"node_configs": {
|
||||
"type": "object"
|
||||
"type": "object",
|
||||
"additionalProperties": True
|
||||
},
|
||||
"cluster_configs": {
|
||||
"type": "object"
|
||||
"type": "object",
|
||||
"additionalProperties": True
|
||||
},
|
||||
"enable_anti_affinity": {
|
||||
"type": "boolean"
|
||||
|
@ -34,6 +34,7 @@ class ProfilesGenerator(context.Context):
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"additionalProperties": True,
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
|
@ -55,7 +55,8 @@ class BaseCustomImageGenerator(context.Context):
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
},
|
||||
"flavor": {
|
||||
"type": "object",
|
||||
@ -63,7 +64,8 @@ class BaseCustomImageGenerator(context.Context):
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
|
@ -49,7 +49,8 @@ class AuditTemplateGenerator(context.Context):
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
},
|
||||
"strategy": {
|
||||
"type": "object",
|
||||
@ -57,9 +58,11 @@ class AuditTemplateGenerator(context.Context):
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": False
|
||||
},
|
||||
},
|
||||
"additionalProperties": False,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -13,6 +13,7 @@
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import json
|
||||
|
||||
from rally.common.plugin import plugin
|
||||
from rally import plugins
|
||||
@ -35,9 +36,10 @@ class ConfigSchemasTestCase(test.TestCase):
|
||||
def fail(self, p, schema, msg):
|
||||
super(ConfigSchemasTestCase, self).fail(
|
||||
"Config schema of plugin '%s' (%s) is invalid. %s "
|
||||
"(Schema: %s)" % (p.get_name(),
|
||||
"Schema: \n%s" % (p.get_name(),
|
||||
"%s.%s" % (p.__module__, p.__name__),
|
||||
msg, schema))
|
||||
msg,
|
||||
json.dumps(schema, indent=3)))
|
||||
|
||||
def _check_anyOf_or_oneOf(self, p, schema, definitions):
|
||||
if "anyOf" in schema or "oneOf" in schema:
|
||||
@ -61,6 +63,11 @@ class ConfigSchemasTestCase(test.TestCase):
|
||||
if unexpected_keys:
|
||||
self.fail(p, schema, ("Found unexpected key(s) for object type: "
|
||||
"%s." % ", ".join(unexpected_keys)))
|
||||
if "additionalProperties" not in schema:
|
||||
self.fail(p, schema,
|
||||
"'additionalProperties' is required field for objects. "
|
||||
"Specify `'additionalProperties': True` explicitly to "
|
||||
"accept not validated properties.")
|
||||
|
||||
if "patternProperties" in schema:
|
||||
if "properties" in schema:
|
||||
@ -147,7 +154,7 @@ class ConfigSchemasTestCase(test.TestCase):
|
||||
pass
|
||||
elif schema == {}:
|
||||
# NOTE(andreykurilin): an empty dict means that the user can
|
||||
# transmit whatever he want in whatever he want format. It is
|
||||
# transmit whatever he wants in whatever he wants format. It is
|
||||
# not the case which we want to support.
|
||||
self.fail(p, schema, "Empty schema is not allowed.")
|
||||
elif "$ref" in schema:
|
||||
@ -161,7 +168,7 @@ class ConfigSchemasTestCase(test.TestCase):
|
||||
@plugins.ensure_plugins_are_loaded
|
||||
def test_schema_is_valid(self):
|
||||
for p in plugin.Plugin.get_all():
|
||||
if not hasattr(p, "CONFIG_SCHEMA"):
|
||||
if not hasattr(p, "CONFIG_SCHEMA") or "tests.unit" in p.__module__:
|
||||
continue
|
||||
# allow only top level definitions
|
||||
definitions = p.CONFIG_SCHEMA.get("definitions", {})
|
||||
|
Loading…
x
Reference in New Issue
Block a user