Adding dsl version validation

* service version validation added. It will be executed before
  deployment and as part of validate command for service
  definitions.
* dsl_version field now is required field
* initial dsl_version = "0.1.0" added
* doc describing dsl versioning added

Depends-On: I0abe9781300c794d690b0822fc2a857db3ca8ea5
Depends-On: Ib755efe784a1c4bc04fb49827baa268513dd27e1
Depends-On: I62520035507ca7c5ac97a0e34062fa9eac26e2f6
Depends-On: I9afb7143dc876b839297c5bf5c03b156e0daa8c2
Depends-On: I5a05d2538a3315ce2e652219a6cd5aead9c7d617
Depends-On: I4a0c00b40b2af4f213daf7e43419dd46951fc4bc
Depends-On: Id988118f78fcdfe0599abe27959590f56f3617d3
Depends-On: I965814b147179435ef2cf94abc362df866495f8f
Depends-On: Ic9e4e79e848ab944e5910b57cc5cd524f1f97ae0
Depends-On: Ic8d9c334ac57a13066db38562423523bd23b671f
Depends-On: I581b587d36e03d60c5fb04e7dbf0c184c992b526
Depends-On: I4d4b9f363138b85198222ac2e770930ce7bd6ab5
Depends-On: I1728ee34eef02668cb54b1f54291a66d609dd6c4
Depends-On: I4d4b9f363138b85198222ac2e770930ce7bd6ab5
Change-Id: I220d99e76220c124d782c97de51112869c727148
This commit is contained in:
Andrey Pavlov 2016-11-18 17:20:29 +00:00
parent 194cf8664b
commit 2ffb9d57d0
7 changed files with 112 additions and 7 deletions

View File

@ -234,3 +234,35 @@ Application definition language
Please refer to :doc:`dsl` for detailed description of CCP DSL syntax.
DSL versioning
==============
Some changes in CCP framework are backward compatible and some of them are not.
To prevent situations when service definitions are being processed by
incompatible version of CCP framework, DSL versioning has been implemented.
DSL versioning is based on Semantic Versioning model. Version has a format
``MAJOR.MINOR.PATCH`` and is being defined in ``dsl_version`` field of
:file:`fuel-ccp/__init__.py` module. Each service definition contains
``dsl_version`` field with the version of DSL it was implemented/updated for.
During the validation phase of :command:`ccp deploy` those versions will be
compared according to the following rules:
#. if DSL version of ``fuel-ccp`` is less than service's DSL version -
they are incompatible - error will be printed, deployment will be
aborted;
#. if ``MAJOR`` parts of these versions are different - they are incompatible
- error will be printed, deployment will be aborted;
#. otherwise they are compatible and deployment can be continued.
For ``dsl_version`` in ``fuel-ccp`` repository you should increment:
#. MAJOR version when you make incompatible changes in DSL;
#. MINOR version when you make backward-compatible changes in DSL;
#. PATCH version when you make fixes that do not change DSL, but affect
processing flow.
If you made a change in service definition that is not supposed to work with
the current ```dsl_version```, you should bump it to the minimal appropriate
number.

View File

@ -17,3 +17,4 @@ import pbr.version
version_info = pbr.version.VersionInfo("fuel_ccp")
__version__ = version_info.version_string()
dsl_version = "0.1.0"

View File

@ -84,6 +84,8 @@ class Deploy(BaseCommand):
validation_service.validate_service_definitions(
components_map, components)
validation_service.validate_service_versions(
components_map, components)
deploy.deploy_components(components_map, components)
@ -257,9 +259,11 @@ class CCPApp(app.App):
'%(asctime)s %(levelname)-8s %(name)-15s %(message)s'
def __init__(self, **kwargs):
ccp_version = "%s, dsl version %s" % (fuel_ccp.__version__,
fuel_ccp.dsl_version)
super(CCPApp, self).__init__(
description='Containerized Control Plane tool',
version=fuel_ccp.__version__,
version=ccp_version,
command_manager=commandmanager.CommandManager('ccp.cli'),
deferred_help=True,
**kwargs

View File

@ -131,6 +131,8 @@ class TestDeploy(TestParser):
self.useFixture(fixtures.MockPatch(
'fuel_ccp.common.utils.get_deploy_components_info',
return_value={}))
self.useFixture(fixtures.MockPatch(
'fuel_ccp.validation.service.validate_service_versions'))
self.argv += self.add_argv
self._run_app()
if self.components is None:

View File

@ -1,8 +1,11 @@
import fixtures
import mock
import testscenarios
from fuel_ccp.tests import base
from fuel_ccp.validation import base as base_validation
from fuel_ccp.validation import deploy as deploy_validation
from fuel_ccp.validation import service as service_validation
COMPONENTS_MAP = {
@ -80,3 +83,36 @@ class TestDeployValidation(base.TestCase):
'deployment: service2',
deploy_validation.validate_requested_components,
{'service1'}, COMPONENTS_MAP)
class TestServiceValidation(testscenarios.WithScenarios, base.TestCase):
scenarios = (
('incompatible', {'version': '0.3.0', 'raises': RuntimeError}),
('incompatible_major', {'version': '1.0.0', 'raises': RuntimeError}),
('compatible', {'version': '0.1.0'}),
('larget_but_compatible', {'version': '0.1.0'})
)
raises = None
def setUp(self):
super(TestServiceValidation, self).setUp()
self.useFixture(fixtures.MockPatch("fuel_ccp.dsl_version",
"0.2.0"))
def test_validation(self):
components_map = {
"test": {
"service_content": {
"dsl_version": self.version
}
}
}
if self.raises:
self.assertRaises(
self.raises, service_validation.validate_service_versions,
components_map, ['test']
)
else:
service_validation.validate_service_versions(
components_map, ['test']
)

View File

@ -13,6 +13,8 @@ def validate(components, types):
component_map = utils.get_deploy_components_info()
service_validation.validate_service_definitions(component_map,
components)
service_validation.validate_service_versions(component_map,
components)
elif validation_type == "dockerfiles":
dockerfiles.validate()
else:

View File

@ -2,6 +2,7 @@ import copy
from distutils import version
import logging
import fuel_ccp
from fuel_ccp.validation import base as validation_base
import jsonschema
@ -166,7 +167,7 @@ PROBE_SCHEMA = {
SERVICE_SCHEMA = {
"type": "object",
"additionalProperties": False,
"required": ["service"],
"required": ["dsl_version", "service"],
"properties": {
"dsl_version": {
@ -292,7 +293,7 @@ SERVICE_SCHEMA = {
}
def validate_service_definitions(components_map, components):
def validate_service_definitions(components_map, components=None):
if not components:
components = components_map.keys()
else:
@ -316,7 +317,34 @@ def validate_service_definitions(components_map, components):
"not passed.".format(len(not_passed_components), len(components))
)
else:
LOG.info(
"Validation of service definitions for %s components passed "
"successfully!", len(components)
)
LOG.info("Service definitions validation passed successfully")
def validate_service_versions(components_map, components=None):
if not components:
components = components_map.keys()
incompatible_services = []
parser_version = version.StrictVersion(fuel_ccp.dsl_version)
for component in components:
service_version = version.StrictVersion(
components_map[component]['service_content']['dsl_version'])
if service_version > parser_version:
LOG.error('%s: Service version validation failed: service version '
'(%s) greater than parser version (%s)',
component, str(service_version), str(parser_version))
incompatible_services.append(component)
continue
service_major_version = service_version.version[0]
parser_major_version = parser_version.version[0]
if service_major_version != parser_major_version:
LOG.error("%s: Service version validation failed: major versions "
"of service (%s) and parser (%s) are not equal",
component, str(service_version), str(parser_version))
incompatible_services.append(component)
if incompatible_services:
raise RuntimeError('The following services have incompatible versions:'
' %s' % ', '.join(incompatible_services))
else:
LOG.info('Service versions validation passed successfully')