Skip validation for abstract documents & add unit tests.
This commit is contained in:
parent
cb29a3f0ba
commit
a0df0c459d
|
@ -13,17 +13,20 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import jsonschema
|
import jsonschema
|
||||||
|
from oslo_log import log as logging
|
||||||
|
import six
|
||||||
|
|
||||||
from deckhand.engine.schema.v1_0 import default_policy_validation
|
from deckhand.engine.schema.v1_0 import default_policy_validation
|
||||||
from deckhand.engine.schema.v1_0 import default_schema_validation
|
from deckhand.engine.schema.v1_0 import default_schema_validation
|
||||||
from deckhand import errors
|
from deckhand import errors
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DocumentValidation(object):
|
class DocumentValidation(object):
|
||||||
"""Class for document validation logic for YAML files.
|
"""Class for document validation logic for YAML files.
|
||||||
|
|
||||||
This class is responsible for parsing, validating and retrieving secret
|
This class is responsible for performing built-in validations on Documents.
|
||||||
values for values stored in the YAML file.
|
|
||||||
|
|
||||||
:param data: YAML data that requires secrets to be validated, merged and
|
:param data: YAML data that requires secrets to be validated, merged and
|
||||||
consolidated.
|
consolidated.
|
||||||
|
@ -45,6 +48,8 @@ class DocumentValidation(object):
|
||||||
- `deckhand-document-schema-validation`
|
- `deckhand-document-schema-validation`
|
||||||
- `deckhand-policy-validation`
|
- `deckhand-policy-validation`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# TODO: Use the correct validation based on the Document's schema.
|
||||||
internal_validations = [
|
internal_validations = [
|
||||||
{'version': 'v1', 'fqn': 'deckhand-document-schema-validation',
|
{'version': 'v1', 'fqn': 'deckhand-document-schema-validation',
|
||||||
'schema': default_schema_validation},
|
'schema': default_schema_validation},
|
||||||
|
@ -56,7 +61,7 @@ class DocumentValidation(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def schema(self):
|
def schema(self):
|
||||||
# TODO: return schema based on version and kind.
|
# TODO: return schema based on Document's schema.
|
||||||
return [v['schema'] for v in self.internal_validations
|
return [v['schema'] for v in self.internal_validations
|
||||||
if v['version'] == self.schema_version][0].schema
|
if v['version'] == self.schema_version][0].schema
|
||||||
|
|
||||||
|
@ -64,11 +69,22 @@ class DocumentValidation(object):
|
||||||
"""Pre-validate that the YAML file is correctly formatted."""
|
"""Pre-validate that the YAML file is correctly formatted."""
|
||||||
self._validate_with_schema()
|
self._validate_with_schema()
|
||||||
|
|
||||||
# TODO(fm577c): Query Deckhand API to validate "src" values.
|
|
||||||
|
|
||||||
def _validate_with_schema(self):
|
def _validate_with_schema(self):
|
||||||
# Validate the document using the schema defined by the document's
|
# Validate the document using the document's ``schema``. Only validate
|
||||||
# `schemaVersion` and `kind`.
|
# concrete documents.
|
||||||
|
try:
|
||||||
|
abstract = self.data['metadata']['layeringDefinition'][
|
||||||
|
'abstract']
|
||||||
|
is_abstract = six.text_type(abstract).lower() == 'true'
|
||||||
|
except KeyError as e:
|
||||||
|
raise errors.InvalidFormat(
|
||||||
|
"Could not find 'abstract' property from document.")
|
||||||
|
|
||||||
|
if is_abstract:
|
||||||
|
LOG.info(
|
||||||
|
"Skipping validation for the document because it is abstract")
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
schema_version = self.data['schema'].split('/')[-1]
|
schema_version = self.data['schema'].split('/')[-1]
|
||||||
doc_schema_version = self.SchemaVersion(schema_version)
|
doc_schema_version = self.SchemaVersion(schema_version)
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
|
|
||||||
class DeckhandException(Exception):
|
class DeckhandException(Exception):
|
||||||
"""Base Nova Exception
|
"""Base Deckhand Exception
|
||||||
To correctly use this class, inherit from it and define
|
To correctly use this class, inherit from it and define
|
||||||
a 'msg_fmt' property. That msg_fmt will get printf'd
|
a 'msg_fmt' property. That msg_fmt will get printf'd
|
||||||
with the keyword arguments provided to the constructor.
|
with the keyword arguments provided to the constructor.
|
||||||
|
|
|
@ -17,6 +17,7 @@ import os
|
||||||
import testtools
|
import testtools
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
import mock
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from deckhand.engine import document_validation
|
from deckhand.engine import document_validation
|
||||||
|
@ -80,7 +81,6 @@ class TestDocumentValidation(testtools.TestCase):
|
||||||
"is a required property.")
|
"is a required property.")
|
||||||
invalid_data = [
|
invalid_data = [
|
||||||
(self._corrupt_data('data'), 'data'),
|
(self._corrupt_data('data'), 'data'),
|
||||||
(self._corrupt_data('metadata'), 'metadata'),
|
|
||||||
(self._corrupt_data('metadata.schema'), 'schema'),
|
(self._corrupt_data('metadata.schema'), 'schema'),
|
||||||
(self._corrupt_data('metadata.name'), 'name'),
|
(self._corrupt_data('metadata.name'), 'name'),
|
||||||
(self._corrupt_data('metadata.substitutions'), 'substitutions'),
|
(self._corrupt_data('metadata.substitutions'), 'substitutions'),
|
||||||
|
@ -92,3 +92,29 @@ class TestDocumentValidation(testtools.TestCase):
|
||||||
with six.assertRaisesRegex(self, errors.InvalidFormat,
|
with six.assertRaisesRegex(self, errors.InvalidFormat,
|
||||||
expected_err % missing_key):
|
expected_err % missing_key):
|
||||||
document_validation.DocumentValidation(invalid_entry)
|
document_validation.DocumentValidation(invalid_entry)
|
||||||
|
|
||||||
|
def test_initialization_missing_abstract_section(self):
|
||||||
|
expected_err = ("Could not find 'abstract' property from document.")
|
||||||
|
|
||||||
|
invalid_data = [
|
||||||
|
self._corrupt_data('metadata'),
|
||||||
|
self._corrupt_data('metadata.layeringDefinition'),
|
||||||
|
self._corrupt_data('metadata.layeringDefinition.abstract'),
|
||||||
|
]
|
||||||
|
|
||||||
|
for invalid_entry in invalid_data:
|
||||||
|
with six.assertRaisesRegex(self, errors.InvalidFormat,
|
||||||
|
expected_err):
|
||||||
|
document_validation.DocumentValidation(invalid_entry)
|
||||||
|
|
||||||
|
@mock.patch.object(document_validation, 'LOG', autospec=True)
|
||||||
|
def test_initialization_with_abstract_document(self, mock_log):
|
||||||
|
abstract_data = copy.deepcopy(self.data)
|
||||||
|
|
||||||
|
for true_val in (True, 'true', 'True'):
|
||||||
|
abstract_data['metadata']['layeringDefinition']['abstract'] = True
|
||||||
|
|
||||||
|
document_validation.DocumentValidation(abstract_data)
|
||||||
|
mock_log.info.assert_called_once_with(
|
||||||
|
"Skipping validation for the document because it is abstract")
|
||||||
|
mock_log.info.reset_mock()
|
||||||
|
|
|
@ -8,7 +8,7 @@ metadata:
|
||||||
genesis: enabled
|
genesis: enabled
|
||||||
master: enabled
|
master: enabled
|
||||||
layeringDefinition:
|
layeringDefinition:
|
||||||
abstract: true
|
abstract: false
|
||||||
layer: region
|
layer: region
|
||||||
parentSelector:
|
parentSelector:
|
||||||
required_key_a: required_label_a
|
required_key_a: required_label_a
|
||||||
|
|
Loading…
Reference in New Issue