Merge "[jsonschema] Require specifying additionalProperties explicitly"

This commit is contained in:
Zuul 2018-02-02 19:11:12 +00:00 committed by Gerrit Code Review
commit 2480e5da0e
13 changed files with 63 additions and 27 deletions

View File

@ -79,7 +79,8 @@ class CeilometerSampleGenerator(context.Context):
"created_at": { "created_at": {
"type": "string" "type": "string"
} }
} },
"additionalProperties": False
} }
}, },
"batch_size": { "batch_size": {

View File

@ -73,12 +73,15 @@ class HeatDataplane(context.Context):
}, },
"files": { "files": {
"type": "object", "type": "object",
"additionalProperties": True
}, },
"parameters": { "parameters": {
"type": "object", "type": "object",
"additionalProperties": True
}, },
"context_parameters": { "context_parameters": {
"type": "object", "type": "object",
"additionalProperties": True
}, },
}, },
"additionalProperties": False "additionalProperties": False

View File

@ -39,7 +39,8 @@ class EC2ServerGenerator(context.Context):
"name": { "name": {
"type": "string" "type": "string"
} }
} },
"additionalProperties": False
}, },
"flavor": { "flavor": {
"type": "object", "type": "object",
@ -47,7 +48,8 @@ class EC2ServerGenerator(context.Context):
"name": { "name": {
"type": "string" "type": "string"
} }
} },
"additionalProperties": False
}, },
"servers_per_tenant": { "servers_per_tenant": {
"type": "integer", "type": "integer",

View File

@ -70,12 +70,13 @@ class ShareNetworks(context.Context):
"properties": { "properties": {
"use_share_networks": { "use_share_networks": {
"type": "boolean", "type": "boolean",
"description": "specifies whether manila should use share " "description": "Specifies whether manila should use share "
"networks for share creation or not."}, "networks for share creation or not."},
"share_networks": { "share_networks": {
"type": "object", "type": "object",
"description": SHARE_NETWORKS_ARG_DESCR "description": SHARE_NETWORKS_ARG_DESCR,
"additionalProperties": True
}, },
}, },
"additionalProperties": False "additionalProperties": False

View File

@ -48,7 +48,8 @@ class MonascaMetricGenerator(context.Context):
"url": { "url": {
"type": "string" "type": "string"
} }
} },
"additionalProperties": False
}, },
"metrics_per_tenant": { "metrics_per_tenant": {
"type": "integer", "type": "integer",
@ -65,7 +66,8 @@ class MonascaMetricGenerator(context.Context):
"value_meta_value": { "value_meta_value": {
"type": "string" "type": "string"
} }
} },
"additionalProperties": False
} }
} }
}, },

View File

@ -48,7 +48,8 @@ class Router(context.Context):
"properties": { "properties": {
"network_id": {"type": "string"}, "network_id": {"type": "string"},
"enable_snat": {"type": "boolean"} "enable_snat": {"type": "boolean"}
} },
"additionalProperties": False
}, },
"network_id": { "network_id": {
"description": "Network ID", "description": "Network ID",
@ -62,7 +63,8 @@ class Router(context.Context):
"properties": { "properties": {
"ip_address": {"type": "string"}, "ip_address": {"type": "string"},
"subnet_id": {"type": "string"} "subnet_id": {"type": "string"}
} },
"additionalProperties": False,
} }
}, },
"distributed": { "distributed": {

View File

@ -32,7 +32,8 @@ class Lbaas(context.Context):
"$schema": consts.JSON_SCHEMA, "$schema": consts.JSON_SCHEMA,
"properties": { "properties": {
"pool": { "pool": {
"type": "object" "type": "object",
"additionalProperties": True
}, },
"lbaas_version": { "lbaas_version": {
"type": "integer", "type": "integer",

View File

@ -38,14 +38,16 @@ class ServerGenerator(context.Context):
"type": "object", "type": "object",
"properties": { "properties": {
"name": {"type": "string"} "name": {"type": "string"}
} },
"additionalProperties": False
}, },
"flavor": { "flavor": {
"description": "Name of flavor to boot server(s) with.", "description": "Name of flavor to boot server(s) with.",
"type": "object", "type": "object",
"properties": { "properties": {
"name": {"type": "string"} "name": {"type": "string"}
} },
"additionalProperties": False
}, },
"servers_per_tenant": { "servers_per_tenant": {
"description": "Number of servers to boot in each Tenant.", "description": "Number of servers to boot in each Tenant.",
@ -60,11 +62,18 @@ class ServerGenerator(context.Context):
"type": "array", "type": "array",
"description": "List of networks to attach to server.", "description": "List of networks to attach to server.",
"items": {"oneOf": [ "items": {"oneOf": [
{"type": "object", {
"properties": {"net-id": {"type": "string"}}, "type": "object",
"description": "Network ID in a format like OpenStack API" "properties": {"net-id": {"type": "string"}},
" expects to see."}, "description": "Network ID in a format like OpenStack "
{"type": "string", "description": "Network ID."}]}, "API expects to see.",
"additionalProperties": False
},
{
"type": "string",
"description": "Network ID."
}
]},
"minItems": 1 "minItems": 1
} }
}, },

View File

@ -77,10 +77,12 @@ class SaharaCluster(context.Context):
} }
}, },
"node_configs": { "node_configs": {
"type": "object" "type": "object",
"additionalProperties": True
}, },
"cluster_configs": { "cluster_configs": {
"type": "object" "type": "object",
"additionalProperties": True
}, },
"enable_anti_affinity": { "enable_anti_affinity": {
"type": "boolean" "type": "boolean"

View File

@ -34,6 +34,7 @@ class ProfilesGenerator(context.Context):
}, },
"properties": { "properties": {
"type": "object", "type": "object",
"additionalProperties": True,
} }
}, },
"additionalProperties": False, "additionalProperties": False,

View File

@ -55,7 +55,8 @@ class BaseCustomImageGenerator(context.Context):
"name": { "name": {
"type": "string" "type": "string"
} }
} },
"additionalProperties": False
}, },
"flavor": { "flavor": {
"type": "object", "type": "object",
@ -63,7 +64,8 @@ class BaseCustomImageGenerator(context.Context):
"name": { "name": {
"type": "string" "type": "string"
} }
} },
"additionalProperties": False
}, },
"username": { "username": {
"type": "string" "type": "string"

View File

@ -49,7 +49,8 @@ class AuditTemplateGenerator(context.Context):
"name": { "name": {
"type": "string" "type": "string"
} }
} },
"additionalProperties": False
}, },
"strategy": { "strategy": {
"type": "object", "type": "object",
@ -57,9 +58,11 @@ class AuditTemplateGenerator(context.Context):
"name": { "name": {
"type": "string" "type": "string"
} }
} },
"additionalProperties": False
}, },
}, },
"additionalProperties": False,
}, },
} }
}, },

View File

@ -13,6 +13,7 @@
# under the License. # under the License.
import copy import copy
import json
from rally.common.plugin import plugin from rally.common.plugin import plugin
from rally import plugins from rally import plugins
@ -35,9 +36,10 @@ class ConfigSchemasTestCase(test.TestCase):
def fail(self, p, schema, msg): def fail(self, p, schema, msg):
super(ConfigSchemasTestCase, self).fail( super(ConfigSchemasTestCase, self).fail(
"Config schema of plugin '%s' (%s) is invalid. %s " "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__), "%s.%s" % (p.__module__, p.__name__),
msg, schema)) msg,
json.dumps(schema, indent=3)))
def _check_anyOf_or_oneOf(self, p, schema, definitions): def _check_anyOf_or_oneOf(self, p, schema, definitions):
if "anyOf" in schema or "oneOf" in schema: if "anyOf" in schema or "oneOf" in schema:
@ -61,6 +63,11 @@ class ConfigSchemasTestCase(test.TestCase):
if unexpected_keys: if unexpected_keys:
self.fail(p, schema, ("Found unexpected key(s) for object type: " self.fail(p, schema, ("Found unexpected key(s) for object type: "
"%s." % ", ".join(unexpected_keys))) "%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 "patternProperties" in schema:
if "properties" in schema: if "properties" in schema:
@ -147,7 +154,7 @@ class ConfigSchemasTestCase(test.TestCase):
pass pass
elif schema == {}: elif schema == {}:
# NOTE(andreykurilin): an empty dict means that the user can # 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. # not the case which we want to support.
self.fail(p, schema, "Empty schema is not allowed.") self.fail(p, schema, "Empty schema is not allowed.")
elif "$ref" in schema: elif "$ref" in schema:
@ -161,7 +168,7 @@ class ConfigSchemasTestCase(test.TestCase):
@plugins.ensure_plugins_are_loaded @plugins.ensure_plugins_are_loaded
def test_schema_is_valid(self): def test_schema_is_valid(self):
for p in plugin.Plugin.get_all(): 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 continue
# allow only top level definitions # allow only top level definitions
definitions = p.CONFIG_SCHEMA.get("definitions", {}) definitions = p.CONFIG_SCHEMA.get("definitions", {})