Update validatedesign message

Update validatedesign failure message to return more detailed error
message.

Change-Id: I56f11ecd3030532d90b421dce5d8959b28d40cbb
This commit is contained in:
Samantha Blanco 2018-05-24 16:23:58 -04:00
parent ec912fde36
commit 2cd353da21
4 changed files with 111 additions and 45 deletions

View File

@ -11,44 +11,19 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import json
import logging import logging
import falcon
from promenade.config import Configuration from promenade.config import Configuration
from promenade.control import base from promenade.control import base
from promenade import exceptions from promenade import exceptions
from promenade import policy from promenade import policy
from promenade.utils.validation_message import ValidationMessage
from promenade import validation from promenade import validation
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class ValidateDesignResource(base.BaseResource): class ValidateDesignResource(base.BaseResource):
def _return_msg(self, resp, result):
if result['err_count'] == 0:
message = "Promenade validations succeeded."
status_code = falcon.HTTP_200
status = "Success"
else:
message = "Promenade validations failed."
status_code = falcon.HTTP_400
status = "Failure"
resp.body = json.dumps({
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": status,
"message": message,
"reason": "Validation",
"details": {
"errorCount": result['err_count'],
"messageList": result['msg'],
},
"code": status_code,
})
@policy.ApiEnforcer('kubernetes_provisioner:post_validatedesign') @policy.ApiEnforcer('kubernetes_provisioner:post_validatedesign')
def on_post(self, req, resp): def on_post(self, req, resp):
@ -58,9 +33,12 @@ class ValidateDesignResource(base.BaseResource):
config = Configuration.from_design_ref( config = Configuration.from_design_ref(
href, allow_missing_substitutions=False) href, allow_missing_substitutions=False)
result = validation.check_design(config) result = validation.check_design(config)
except exceptions.InvalidFormatError as e: except (exceptions.InvalidFormatError,
msg = "Invalid JSON Format: %s" % str(e) exceptions.DeckhandException) as e:
result = {'msg': [msg], 'err_count': 1} if isinstance(e, exceptions.InvalidFormatError):
except exceptions.DeckhandException as e: msg = "Invalid JSON Format: %s" % str(e)
result = {'msg': [str(e)], 'err_count': 1} else:
return self._return_msg(resp, result) msg = str(e)
result = ValidationMessage()
result.add_error_message(msg, name=e.title)
return result.get_output()

View File

View File

@ -0,0 +1,89 @@
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import falcon
import json
class ValidationMessage(object):
""" ValidationMessage per UCP convention:
https://github.com/att-comdev/ucp-integration/blob/master/docs/source/api-conventions.rst#output-structure # noqa
Construction of ValidationMessage message:
:param string message: Validation failure message.
:param boolean error: True or False, if this is an error message.
:param string name: Identifying name of the validation.
:param string level: The severity of validation result, as "Error",
"Warning", or "Info"
:param string schema: The schema of the document being validated.
:param string doc_name: The name of the document being validated.
:param string diagnostic: Information about what lead to the message,
or details for resolution.
"""
def __init__(self):
self.error_count = 0
self.details = {'errorCount': 0, 'messageList': []}
self.output = {
'kind': 'Status',
'apiVersion': 'v1.0',
'metadata': {},
'reason': 'Validation',
'details': self.details,
}
def add_error_message(self,
msg,
name=None,
schema=None,
doc_name=None,
diagnostic=None):
new_error = {
'message': msg,
'error': True,
'name': name,
'documents': [],
'level': "Error",
'diagnostic': diagnostic,
'kind': 'ValidationMessage'
}
if schema and doc_name:
self.output['documents'].append(dict(schema=schema, name=doc_name))
self.details['errorCount'] += 1
self.details['messageList'].append(new_error)
def get_output(self, code=falcon.HTTP_400):
""" Return ValidationMessage message.
:returns: The ValidationMessage for the Validation API response.
:rtype: dict
"""
if self.details['errorCount'] != 0:
self.output['code'] = code
self.output['message'] = 'Promenade validations failed'
self.output['status'] = 'Failure'
else:
self.output['code'] = falcon.HTTP_200
self.output['message'] = 'Promenade validations succeeded'
self.output['status'] = 'Success'
return self.output
def get_output_json(self):
""" Return ValidationMessage message as JSON.
:returns: The ValidationMessage formatted in JSON, for logging.
:rtype: json
"""
return json.dumps(self.output, indent=2)

View File

@ -14,42 +14,41 @@
from promenade import exceptions from promenade import exceptions
from promenade import logging from promenade import logging
import copy from promenade.utils.validation_message import ValidationMessage
import jsonschema import jsonschema
import os import os
import pkg_resources import pkg_resources
import yaml import yaml
__all__ = ['check_schema', 'check_schemas'] __all__ = ['check_schema', 'check_schemas']
result_template = {'msg': [], 'err_count': 0}
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def check_design(config): def check_design(config):
kinds = ['Docker', 'HostSystem', 'Kubelet', 'KubernetesNetwork'] kinds = ['Docker', 'HostSystem', 'Kubelet', 'KubernetesNetwork']
result = copy.deepcopy(result_template) validation_msg = ValidationMessage()
for kind in kinds: for kind in kinds:
count = 0 count = 0
for doc in config.documents: for doc in config.documents:
schema = doc.get('schema', None) schema = doc.get('schema', None)
if not schema: if not schema:
result['msg'].append( msg = '"schema" is a required document key.'
str( validation_msg.add_error_message(
exceptions.ValidationException( msg, name=exceptions.ValidationException(msg))
'"schema" is a required document key.'))) return validation_msg
result['err_count'] += 1
return result
name = schema.split('/')[1] name = schema.split('/')[1]
if name == kind: if name == kind:
count += 1 count += 1
if count != 1: if count != 1:
msg = ('There are {0} {1} documents. However, there should be one.' msg = ('There are {0} {1} documents. However, there should be one.'
).format(count, kind) ).format(count, kind)
result['msg'].append( validation_msg.add_error_message(
str(exceptions.ValidationException(description=msg))) msg,
result['err_count'] += 1 name=exceptions.ValidationException(msg),
return result schema=schema,
doc_name=kind)
return validation_msg
def check_schemas(documents, schemas=None): def check_schemas(documents, schemas=None):