aromanovich / jsl

A Python DSL for describing JSON schemas
http://jsl.readthedocs.org/
Other
218 stars 21 forks source link

host-name format on StringField fails to validate #20

Closed roopeshvaddepally closed 8 years ago

roopeshvaddepally commented 8 years ago

I took the jsonschema example of fstab file, your example in tutorial write up to FSTabEntry, but doesn't implement the FSTab part of it, so I tried to complete it. This fails to validate the schema for format 'host-name' in NFS class and server attribute.

If I change server part to simple StringField it passes.

Here is the complete code I used.

import json
from jsonschema import validate

class DiskDevice(jsl.Document):
    class Options:
        additional_properties = False
        definition_id = 'diskDevice'
    type = jsl.StringField(enum=['disk'], required=True)
    device = jsl.StringField(pattern='^/dev/[^/]+(/[^/]+)*$', required=True)

class DiskUUID(jsl.Document):
    class Options:
        additional_properties = False
        definition_id = 'diskUUID'
    type = jsl.StringField(enum=['disk'], required=True)
    label = jsl.StringField(pattern='^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}'
                                    '-[a-fA-F0-9]{12}$', required=True)

class NFS(jsl.Document):
    class Options:
        additional_properties = False
        definition_id = 'nfs'
    type = jsl.StringField(enum=['nfs'], required=True)
    remotePath = jsl.StringField(pattern='^(/[^/]+)+$', required=True)
    server = jsl.OneOfField([
        jsl.StringField(format='host-name'),
        jsl.StringField(format='ipv4'),
        jsl.StringField(format='ipv6'),
    ], required=True)

class TmpFS(jsl.Document):
    class Options:
        additional_properties = False
        definition_id = 'tmpfs'
    type = jsl.StringField(enum=['tmpfs'], required=True)
    sizeInMB = jsl.IntField(minimum=16,maximum=512, required=True)

class Entry(jsl.Document):
    class Options:
        additional_properties = False
    storage = jsl.OneOfField([
        jsl.DocumentField(DiskDevice, as_ref=True),
        jsl.DocumentField(DiskUUID, as_ref=True),
        jsl.DocumentField(NFS, as_ref=True),
        jsl.DocumentField(TmpFS, as_ref=True),
    ])
    fstype = jsl.StringField(enum=['ext3', 'ext4', 'btrfs'], required=False)
    options = jsl.ArrayField(min_items=1,unique_items=True, required=False)
    readonly = jsl.BooleanField(required=False)

class FSTable(jsl.Document):
    class Options:
        additional_properties = True
        pattern_properties={
            "^(/[^/]+)+$": jsl.DocumentField(Entry, as_ref=True)
        }
    root = jsl.DocumentField(Entry, name='/', required=True)

print(json.dumps(FSTable.get_schema(), indent=4))

validate({
    "/": {
        "storage": {
            "type": "disk",
            "device": "/dev/sda1"
        },
        "fstype": "btrfs",
        "readonly": True
    },
    "/var": {
        "storage": {
            "type": "disk",
            "label": "8f3ba6f4-5c70-46ec-83af-0d5434953e5f"
        },
        "fstype": "ext4",
        "options": [ "nosuid" ]
    },
    "/tmp": {
        "storage": {
            "type": "tmpfs",
            "sizeInMB": 64
        }
    },
    "/var/www": {
        "storage": {
            "type": "nfs",
            "server": "my.nfs.server",
            "remotePath": "/exports/mypath"
        }
    }
}, FSTable.get_schema())

Here is the traceback:

ValidationError                           Traceback (most recent call last)
/tmp/ipython_edit_912m39yl/ipython_edit_lpduaw0l.py in <module>()
     89         }
     90     }
---> 91 }, FSTable.get_schema())
     92

/auto/home.nas01a/rvaddepally/.pyenv/versions/3.6-dev/envs/myenv/lib/python3.6/site-packages/jsonschema/validators.py in validate(instance, schema, cls, *args, **kwargs)
    476         cls = validator_for(schema)
    477     cls.check_schema(schema)
--> 478     cls(schema, *args, **kwargs).validate(instance)

/auto/home.nas01a/rvaddepally/.pyenv/versions/3.6-dev/envs/myenv/lib/python3.6/site-packages/jsonschema/validators.py in validate(self, *args, **kwargs)
    121         def validate(self, *args, **kwargs):
    122             for error in self.iter_errors(*args, **kwargs):
--> 123                 raise error
    124
    125         def is_type(self, instance, type):

ValidationError: {'server': 'my.nfs.server', 'remotePath': '/exports/mypath', 'type': 'nfs'} is not valid under any of the given schemas

Failed validating 'oneOf' in schema['patternProperties']['^(/[^/]+)+$']['properties']['storage']:
    {'oneOf': [{'$ref': '#/definitions/diskDevice'},
               {'$ref': '#/definitions/diskUUID'},
               {'$ref': '#/definitions/nfs'},
               {'$ref': '#/definitions/tmpfs'}]}

On instance['/var/www']['storage']:
    {'remotePath': '/exports/mypath',
     'server': 'my.nfs.server',
     'type': 'nfs'}
aromanovich commented 8 years ago
class NFS(jsl.Document):
    class Options:
        additional_properties = False
        definition_id = 'nfs'
    type = jsl.StringField(enum=['nfs'], required=True)
    remotePath = jsl.StringField(pattern='^(/[^/]+)+$', required=True)
    server = jsl.OneOfField([
        jsl.StringField(format='host-name'),
        jsl.StringField(format='ipv4'),
        jsl.StringField(format='ipv6'),
    ], required=True)

validate({
    "type": "nfs",
    "server": "my.nfs.server",
    "remotePath": "/exports/mypath"
}, NFS.get_schema())

results in

jsonschema.exceptions.ValidationError: 'my.nfs.server' is valid under each of {'type': 'string', 'format': 'ipv4'}, {'type': 'string', 'format': 'ipv6'}, {'type': 'string', 'format': 'host-name'}

The problem is that jsonschema.validate does not check formats by default (see jsonschema docs). If a format checker is specified, validation passes:

import jsonschema
jsonschema.validate({
    "type": "nfs",
    "server": "my.nfs.server",
    "remotePath": "/exports/mypath"
}, NFS.get_schema(), format_checker=jsonschema.FormatChecker())