From 6e32acbae124cd0eeb7b0245ffa02a1755faff40 Mon Sep 17 00:00:00 2001 From: Bryan Strassner Date: Mon, 16 Apr 2018 13:46:31 -0500 Subject: [PATCH] [402036] add DeploymentStrategy schema In support of a larger effort to introduce a DeploymentStrategy document for Shipyard to use to drive baremetal node deployment, this change adds a schema and tests for that schema. Change-Id: I94c41f0ee8c8cf9c3762c5c1af575798fb1dccb4 --- .../schemas/deploymentConfiguration.yaml | 3 - .../schemas/deploymentStrategy.yaml | 74 ++++++++++++++++++ tests/unit/schemas/base_schema_validation.py | 51 +++++++++++++ tests/unit/schemas/conftest.py | 31 ++++++++ .../schemas/test_deployment_configuration.py | 52 +------------ .../unit/schemas/test_deployment_strategy.py | 38 ++++++++++ .../deploymentStrategy_bad_values_1.yaml | 16 ++++ .../deploymentStrategy_bad_values_2.yaml | 16 ++++ .../deploymentStrategy_bad_values_3.yaml | 15 ++++ .../deploymentStrategy_bad_values_4.yaml | 18 +++++ .../deploymentStrategy_bad_values_5.yaml | 20 +++++ .../deploymentStrategy_full_valid.yaml | 75 +++++++++++++++++++ .../deploymentStrategy_min_with_content.yaml | 15 ++++ .../deploymentStrategy_minimal.yaml | 11 +++ tests/unit/yaml_samples/empty.yaml | 0 tests/unit/yaml_samples/total_garbage.yaml | 44 +++++++++++ 16 files changed, 425 insertions(+), 54 deletions(-) create mode 100644 shipyard_airflow/schemas/deploymentStrategy.yaml create mode 100644 tests/unit/schemas/base_schema_validation.py create mode 100644 tests/unit/schemas/conftest.py create mode 100644 tests/unit/schemas/test_deployment_strategy.py create mode 100644 tests/unit/yaml_samples/deploymentStrategy_bad_values_1.yaml create mode 100644 tests/unit/yaml_samples/deploymentStrategy_bad_values_2.yaml create mode 100644 tests/unit/yaml_samples/deploymentStrategy_bad_values_3.yaml create mode 100644 tests/unit/yaml_samples/deploymentStrategy_bad_values_4.yaml create mode 100644 tests/unit/yaml_samples/deploymentStrategy_bad_values_5.yaml create mode 100644 tests/unit/yaml_samples/deploymentStrategy_full_valid.yaml create mode 100644 tests/unit/yaml_samples/deploymentStrategy_min_with_content.yaml create mode 100644 tests/unit/yaml_samples/deploymentStrategy_minimal.yaml create mode 100644 tests/unit/yaml_samples/empty.yaml create mode 100644 tests/unit/yaml_samples/total_garbage.yaml diff --git a/shipyard_airflow/schemas/deploymentConfiguration.yaml b/shipyard_airflow/schemas/deploymentConfiguration.yaml index 91466f1a..2b955aeb 100644 --- a/shipyard_airflow/schemas/deploymentConfiguration.yaml +++ b/shipyard_airflow/schemas/deploymentConfiguration.yaml @@ -1,4 +1,3 @@ - --- schema: 'deckhand/DataSchema/v1' metadata: @@ -16,8 +15,6 @@ data: properties: deployment_strategy: type: 'string' - enum: - - 'all-at-once' deploy_interval: type: 'integer' deploy_timeout: diff --git a/shipyard_airflow/schemas/deploymentStrategy.yaml b/shipyard_airflow/schemas/deploymentStrategy.yaml new file mode 100644 index 00000000..51bda9f0 --- /dev/null +++ b/shipyard_airflow/schemas/deploymentStrategy.yaml @@ -0,0 +1,74 @@ +--- +schema: 'deckhand/DataSchema/v1' +metadata: + schema: metadata/Control/v1 + name: shipyard/DeploymentStrategy/v1 + labels: + application: shipyard +data: + $schema: 'http://json-schema.org/schema#' + id: 'https://github.com/att-comdev/shipyard/deploymentStrategy.yaml' + type: 'object' + required: + - groups + properties: + groups: + type: 'array' + minItems: 0 + items: + type: 'object' + required: + - name + - critical + - depends_on + - selectors + properties: + name: + type: 'string' + minLength: 1 + critical: + type: 'boolean' + depends_on: + type: 'array' + minItems: 0 + items: + type: 'string' + selectors: + type: 'array' + minItems: 0 + items: + type: 'object' + minProperties: 1 + properties: + node_names: + type: 'array' + items: + type: 'string' + node_labels: + type: 'array' + items: + type: 'string' + node_tags: + type: 'array' + items: + type: 'string' + rack_names: + type: 'array' + items: + type: 'string' + additionalProperties: false + success_criteria: + type: 'object' + minProperties: 1 + properties: + percent_successful_nodes: + type: 'integer' + minimum: 0 + maximum: 100 + minimum_successful_nodes: + type: 'integer' + minimum: 0 + maximum_failed_nodes: + type: 'integer' + minimum: 0 + additionalProperties: false diff --git a/tests/unit/schemas/base_schema_validation.py b/tests/unit/schemas/base_schema_validation.py new file mode 100644 index 00000000..13685769 --- /dev/null +++ b/tests/unit/schemas/base_schema_validation.py @@ -0,0 +1,51 @@ +# 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 logging +import os +import yaml + +import jsonschema +import pkg_resources +import pytest + +from jsonschema.exceptions import ValidationError + +LOG = logging.getLogger(__name__) + + +class BaseSchemaValidationTest(object): + def _test_validate(self, schema, expect_failure, input_files, input): + """validates input yaml against schema. + :param schema: schema yaml file + :param expect_failure: should the validation pass or fail. + :param input_files: pytest fixture used to access the test input files + :param input: test input yaml doc filename""" + schema_dir = pkg_resources.resource_filename('shipyard_airflow', + 'schemas') + schema_filename = os.path.join(schema_dir, schema) + schema_file = open(schema_filename, 'r') + schema = yaml.safe_load(schema_file) + + input_file = input_files.join(input) + instance_file = open(str(input_file), 'r') + instance = yaml.safe_load(instance_file) + + LOG.info('Input: %s, Schema: %s', input_file, schema_filename) + + if expect_failure: + # TypeError is raised when he input document is not well formed. + with pytest.raises((ValidationError, TypeError)): + jsonschema.validate(instance['data'], schema['data']) + else: + jsonschema.validate(instance['data'], schema['data']) diff --git a/tests/unit/schemas/conftest.py b/tests/unit/schemas/conftest.py new file mode 100644 index 00000000..4e79dc98 --- /dev/null +++ b/tests/unit/schemas/conftest.py @@ -0,0 +1,31 @@ +# Copyright 2017 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 os + +import pytest +import shutil + + +@pytest.fixture(scope='module') +def input_files(tmpdir_factory, request): + tmpdir = tmpdir_factory.mktemp('data') + samples_dir = os.path.dirname(str( + request.fspath)) + "/" + "../yaml_samples" + samples = os.listdir(samples_dir) + + for f in samples: + src_file = samples_dir + "/" + f + dst_file = str(tmpdir) + "/" + f + shutil.copyfile(src_file, dst_file) + return tmpdir diff --git a/tests/unit/schemas/test_deployment_configuration.py b/tests/unit/schemas/test_deployment_configuration.py index 2700ad5f..7b40b71f 100644 --- a/tests/unit/schemas/test_deployment_configuration.py +++ b/tests/unit/schemas/test_deployment_configuration.py @@ -11,44 +11,7 @@ # 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 logging -import os -import yaml - -import jsonschema -import pkg_resources -import pytest -import shutil - -from jsonschema.exceptions import ValidationError - -LOG = logging.getLogger(__name__) - - -class BaseSchemaValidationTest(object): - def _test_validate(self, schema, expect_failure, input_files, input): - """validates input yaml against schema. - :param schema: schema yaml file - :param expect_failure: should the validation pass or fail. - :param input_files: pytest fixture used to access the test input files - :param input: test input yaml doc filename""" - schema_dir = pkg_resources.resource_filename('shipyard_airflow', - 'schemas') - schema_filename = os.path.join(schema_dir, schema) - schema_file = open(schema_filename, 'r') - schema = yaml.safe_load(schema_file) - - input_file = input_files.join(input) - instance_file = open(str(input_file), 'r') - instance = yaml.safe_load(instance_file) - - LOG.info('Input: %s, Schema: %s', input_file, schema_filename) - - if expect_failure: - with pytest.raises(ValidationError): - jsonschema.validate(instance['data'], schema['data']) - else: - jsonschema.validate(instance['data'], schema['data']) +from .base_schema_validation import BaseSchemaValidationTest class TestValidation(BaseSchemaValidationTest): @@ -63,16 +26,3 @@ class TestValidation(BaseSchemaValidationTest): def test_validate_deploy_config_minimal_valid(self, input_files): self._test_validate('deploymentConfiguration.yaml', False, input_files, 'deploymentConfiguration_minimal_valid.yaml') - - @pytest.fixture(scope='module') - def input_files(self, tmpdir_factory, request): - tmpdir = tmpdir_factory.mktemp('data') - samples_dir = os.path.dirname(str( - request.fspath)) + "/" + "../yaml_samples" - samples = os.listdir(samples_dir) - - for f in samples: - src_file = samples_dir + "/" + f - dst_file = str(tmpdir) + "/" + f - shutil.copyfile(src_file, dst_file) - return tmpdir diff --git a/tests/unit/schemas/test_deployment_strategy.py b/tests/unit/schemas/test_deployment_strategy.py new file mode 100644 index 00000000..c610dc9e --- /dev/null +++ b/tests/unit/schemas/test_deployment_strategy.py @@ -0,0 +1,38 @@ +# Copyright 2017 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. +from .base_schema_validation import BaseSchemaValidationTest + + +class TestValidation(BaseSchemaValidationTest): + def test_validate_deploy_config_full_valid(self, input_files): + self._test_validate('deploymentStrategy.yaml', False, input_files, + 'deploymentStrategy_full_valid.yaml') + + self._test_validate('deploymentStrategy.yaml', False, input_files, + 'deploymentStrategy_minimal.yaml') + + self._test_validate('deploymentStrategy.yaml', False, input_files, + 'deploymentStrategy_min_with_content.yaml') + + for testnum in range(1, 5): + self._test_validate( + 'deploymentStrategy.yaml', True, input_files, + 'deploymentStrategy_bad_values_{}.yaml'.format(testnum) + ) + + self._test_validate('deploymentStrategy.yaml', True, input_files, + 'total_garbage.yaml') + + self._test_validate('deploymentStrategy.yaml', True, input_files, + 'empty.yaml') diff --git a/tests/unit/yaml_samples/deploymentStrategy_bad_values_1.yaml b/tests/unit/yaml_samples/deploymentStrategy_bad_values_1.yaml new file mode 100644 index 00000000..dc7711f4 --- /dev/null +++ b/tests/unit/yaml_samples/deploymentStrategy_bad_values_1.yaml @@ -0,0 +1,16 @@ +--- +schema: shipyard/DeploymentStrategy/v1 +metadata: + schema: metadata/Document/v1 + name: deployment-strategy + layeringDefinition: + abstract: false + layer: global + storagePolicy: cleartext +data: + groups: +# name is a min length of 1 + - name: "" + critical: false + depends_on: [] + selectors: [] diff --git a/tests/unit/yaml_samples/deploymentStrategy_bad_values_2.yaml b/tests/unit/yaml_samples/deploymentStrategy_bad_values_2.yaml new file mode 100644 index 00000000..6903c12a --- /dev/null +++ b/tests/unit/yaml_samples/deploymentStrategy_bad_values_2.yaml @@ -0,0 +1,16 @@ +--- +schema: shipyard/DeploymentStrategy/v1 +metadata: + schema: metadata/Document/v1 + name: deployment-strategy + layeringDefinition: + abstract: false + layer: global + storagePolicy: cleartext +data: + groups: + - name: a_group +# critical is boolean + critical: cheese sandwich + depends_on: [] + selectors: [] diff --git a/tests/unit/yaml_samples/deploymentStrategy_bad_values_3.yaml b/tests/unit/yaml_samples/deploymentStrategy_bad_values_3.yaml new file mode 100644 index 00000000..f2c6f375 --- /dev/null +++ b/tests/unit/yaml_samples/deploymentStrategy_bad_values_3.yaml @@ -0,0 +1,15 @@ +--- +schema: shipyard/DeploymentStrategy/v1 +metadata: + schema: metadata/Document/v1 + name: deployment-strategy + layeringDefinition: + abstract: false + layer: global + storagePolicy: cleartext +data: + groups: + - name: some_group + critical: true +# depends_on missing + selectors: [] diff --git a/tests/unit/yaml_samples/deploymentStrategy_bad_values_4.yaml b/tests/unit/yaml_samples/deploymentStrategy_bad_values_4.yaml new file mode 100644 index 00000000..154b5ce9 --- /dev/null +++ b/tests/unit/yaml_samples/deploymentStrategy_bad_values_4.yaml @@ -0,0 +1,18 @@ +--- +schema: shipyard/DeploymentStrategy/v1 +metadata: + schema: metadata/Document/v1 + name: deployment-strategy + layeringDefinition: + abstract: false + layer: global + storagePolicy: cleartext +data: + groups: + - name: my_name_is + critical: false + depends_on: [] + selectors: +# node_names are strings + - node_names: [false, true, false] + node_labels: [] diff --git a/tests/unit/yaml_samples/deploymentStrategy_bad_values_5.yaml b/tests/unit/yaml_samples/deploymentStrategy_bad_values_5.yaml new file mode 100644 index 00000000..d363f3f8 --- /dev/null +++ b/tests/unit/yaml_samples/deploymentStrategy_bad_values_5.yaml @@ -0,0 +1,20 @@ +--- +schema: shipyard/DeploymentStrategy/v1 +metadata: + schema: metadata/Document/v1 + name: deployment-strategy + layeringDefinition: + abstract: false + layer: global + storagePolicy: cleartext +data: + groups: + - name: my_group + critical: false + depends_on: [] + selectors: + - node_names: [] + node_labels: [] + success_criteria: +# should be 100 or less + percent_successful_nodes: 190 diff --git a/tests/unit/yaml_samples/deploymentStrategy_full_valid.yaml b/tests/unit/yaml_samples/deploymentStrategy_full_valid.yaml new file mode 100644 index 00000000..d218bd8d --- /dev/null +++ b/tests/unit/yaml_samples/deploymentStrategy_full_valid.yaml @@ -0,0 +1,75 @@ +--- +schema: shipyard/DeploymentStrategy/v1 +metadata: + schema: metadata/Document/v1 + name: deployment-strategy + layeringDefinition: + abstract: false + layer: global + storagePolicy: cleartext +data: + groups: + - name: control-nodes + critical: true + depends_on: + - ntp-node + selectors: + - node_names: [] + node_labels: [] + node_tags: + - control + rack_names: + - rack03 + success_criteria: + percent_successful_nodes: 90 + minimum_successful_nodes: 3 + maximum_failed_nodes: 1 + - name: compute-nodes-1 + critical: false + depends_on: + - control-nodes + selectors: + - node_names: [] + node_labels: [] + rack_names: + - rack01 + node_tags: + - compute + success_criteria: + percent_successful_nodes: 50 + - name: compute-nodes-2 + critical: false + depends_on: + - control-nodes + selectors: + - node_names: [] + node_labels: [] + rack_names: + - rack02 + node_tags: + - compute + success_criteria: + percent_successful_nodes: 50 + - name: monitoring-nodes + critical: false + depends_on: [] + selectors: + - node_names: [] + node_labels: [] + node_tags: + - monitoring + rack_names: + - rack03 + - rack02 + - rack01 + - name: ntp-node + critical: true + depends_on: [] + selectors: + - node_names: + - ntp01 + node_labels: [] + node_tags: [] + rack_names: [] + success_criteria: + minimum_successful_nodes: 1 diff --git a/tests/unit/yaml_samples/deploymentStrategy_min_with_content.yaml b/tests/unit/yaml_samples/deploymentStrategy_min_with_content.yaml new file mode 100644 index 00000000..65e1930c --- /dev/null +++ b/tests/unit/yaml_samples/deploymentStrategy_min_with_content.yaml @@ -0,0 +1,15 @@ +--- +schema: shipyard/DeploymentStrategy/v1 +metadata: + schema: metadata/Document/v1 + name: deployment-strategy + layeringDefinition: + abstract: false + layer: global + storagePolicy: cleartext +data: + groups: + - name: some-nodes + critical: false + depends_on: [] + selectors: [] diff --git a/tests/unit/yaml_samples/deploymentStrategy_minimal.yaml b/tests/unit/yaml_samples/deploymentStrategy_minimal.yaml new file mode 100644 index 00000000..3f36a115 --- /dev/null +++ b/tests/unit/yaml_samples/deploymentStrategy_minimal.yaml @@ -0,0 +1,11 @@ +--- +schema: shipyard/DeploymentStrategy/v1 +metadata: + schema: metadata/Document/v1 + name: deployment-strategy + layeringDefinition: + abstract: false + layer: global + storagePolicy: cleartext +data: + groups: [] diff --git a/tests/unit/yaml_samples/empty.yaml b/tests/unit/yaml_samples/empty.yaml new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/yaml_samples/total_garbage.yaml b/tests/unit/yaml_samples/total_garbage.yaml new file mode 100644 index 00000000..15bab1c2 --- /dev/null +++ b/tests/unit/yaml_samples/total_garbage.yaml @@ -0,0 +1,44 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras euismod +sed urna nec posuere. Phasellus vel arcu vestibulum, mattis ligula eu, +pulvinar magna. Cras mollis velit quis maximus gravida. Morbi nec ligula +neque. Cras vitae cursus tellus, ut ornare enim. Nulla vel suscipit +arcu, in auctor ipsum. Ut a maximus magna. Integer massa risus, +tristique sit amet urna sit amet, facilisis bibendum lectus. Vivamus +vehicula urna in purus lacinia, sit amet tincidunt diam consequat. +Quisque ut metus vitae mauris condimentum sollicitudin. Pellentesque +urna nibh, mollis eu dui ac, molestie malesuada quam. Aliquam fringilla +faucibus orci, a tincidunt dui sollicitudin ac. Nullam enim velit, +imperdiet ut nulla quis, efficitur tincidunt eros. Curabitur nisi justo, +tristique non ornare vitae, mollis eu tortor. In non congue libero. +Mauris et tincidunt sem. Quisque sed congue diam, non ultrices turpis. +Pellentesque lobortis quam justo, facilisis sollicitudin mi imperdiet +sed. Ut nec leo placerat, gravida odio id, hendrerit erat. Praesent +placerat diam mi, et blandit mi sollicitudin et. Proin ligula sapien, +faucibus eget arcu vel, rhoncus vestibulum ipsum. Morbi tristique +pharetra diam non faucibus. Nam scelerisque, leo ut tempus fermentum, +dolor odio tempus nisl, et volutpat ante est id enim. Integer venenatis +scelerisque augue, quis porta lorem dapibus non. Sed arcu purus, iaculis +vitae sem sit amet, ultrices pretium leo. Nulla ultricies eleifend +tempus. Aenean elementum ipsum id auctor faucibus. Cras quis ipsum +vehicula, auctor velit et, dignissim sem. Duis sed nunc sagittis, +interdum dui consequat, iaculis purus. Curabitur quam ex, pellentesque +nec sapien ut, sodales lacinia enim. Etiam hendrerit sem eu turpis +euismod, quis luctus tortor iaculis. Vivamus a rutrum orci. Class aptent +taciti sociosqu ad litora torquent per conubia nostra, per inceptos +himenaeos. Pellentesque habitant morbi tristique senectus et netus et +malesuada fames ac turpis egestas. Aenean non neque ultrices, consequat +erat vitae, porta lorem. Phasellus fringilla fringilla imperdiet. +Quisque in nulla at elit sodales vestibulum ac eget mauris. Ut vel purus +nec metus ultrices aliquet in sed leo. Mauris vel congue velit. Donec +quam turpis, venenatis tristique sem nec, condimentum fringilla orci. +Sed eu feugiat dui. Proin vulputate lacus id blandit tempor. Vivamus +sollicitudin tincidunt ultrices. Aenean sit amet orci efficitur, +condimentum mi vel, condimentum nisi. Cras pellentesque, felis vel +maximus volutpat, turpis arcu dapibus metus, vitae fringilla massa lorem +et elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. +Duis lorem velit, laoreet tincidunt fringilla at, vestibulum eget risus. +Pellentesque ullamcorper venenatis lectus, a mattis lectus feugiat vel. +Suspendisse potenti. Duis suscipit malesuada risus nec egestas. Vivamus +maximus, neque quis egestas rhoncus, mauris purus fringilla nisl, ut +fringilla odio nunc sit amet justo. Phasellus at dui quis magna +elementum sagittis. Nullam sed luctus felis, ac tincidunt erat.