armada/armada/tests/unit/utils/test_validate.py

330 lines
10 KiB
Python

# 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 yaml
import testtools
from armada.tests.unit import base
from armada.utils import validate
template_chart = """
schema: armada/Chart/v1
metadata:
schema: metadata/Document/v1
name: example-chart
data:
chart_name: keystone
release: keystone
namespace: undercloud
timeout: 100
install:
no_hooks: false
upgrade:
no_hooks: false
values: {}
source:
type: git
location: git://github.com/example/example
subpath: example-chart
reference: master
dependencies: []
"""
template_chart_group = """
schema: armada/ChartGroup/v1
metadata:
schema: metadata/Document/v1
name: example-manifest
data:
description: this is sample
chart_group:
- example-chart
"""
template_manifest = """
schema: armada/Manifest/v1
metadata:
schema: metadata/Document/v1
name: example-manifest
data:
release_prefix: example
chart_groups:
- example-chart
"""
class BaseValidateTest(base.ArmadaTestCase):
def setUp(self):
super(BaseValidateTest, self).setUp()
self.basepath = os.path.join(os.path.dirname(__file__), os.pardir)
def _build_error_message(self, document, name, message):
return "Invalid document [{}] {}: {}.".format(document, name, message)
class ValidateOwnExamplesTestCase(BaseValidateTest):
"""Validates that each of the relevant example documents under
``armada/examples`` passes internal Armada schema validation.
"""
def test_own_document_examples(self):
examples_path = os.path.join(os.getcwd(), 'examples')
example_files = [
os.path.join(examples_path, f) for f in os.listdir(examples_path)
if os.path.isfile(os.path.join(examples_path, f))
]
validated_manifests = []
for example_file in example_files:
with open(example_file) as f:
documents = yaml.safe_load_all(f.read())
# If the example file doesn't have a document with
# armada/Manifest/v1 then skip validating it as the example could
# merely be an override.
has_manifest = any(
x['schema'] == 'armada/Manifest/v1' for x in documents)
if not has_manifest:
continue
validated_manifests.append(example_file)
valid, _ = validate.validate_armada_documents(list(documents))
self.assertTrue(valid)
self.assertTrue(validated_manifests)
class ValidateTestCase(BaseValidateTest):
def test_validate_load_schemas(self):
expected_schemas = [
'armada/Chart/v1', 'armada/ChartGroup/v1', 'armada/Manifest/v1'
]
for expected_schema in expected_schemas:
self.assertIn(expected_schema, validate.SCHEMAS)
def test_validate_armada_yaml_passes(self):
template = '{}/resources/valid_armada_document.yaml'.format(
self.basepath)
with open(template) as f:
documents = yaml.safe_load_all(f.read())
valid, details = validate.validate_armada_documents(list(documents))
self.assertTrue(valid)
def test_validate_manifest_passes(self):
manifest = yaml.safe_load(template_manifest)
is_valid, error = validate.validate_armada_document(manifest)
self.assertTrue(is_valid)
def test_validate_chart_group_with_values(self):
test_chart_group = """
---
schema: armada/ChartGroup/v1
metadata:
name: kubernetes-proxy
schema: metadata/Document/v1
data:
description: Kubernetes proxy
name: kubernetes-proxy
sequenced: true
chart_group:
- proxy
---
schema: armada/Chart/v1
metadata:
name: proxy
schema: metadata/Document/v1
data:
chart_name: proxy
timeout: 600
release: kubernetes-proxy
source:
subpath: proxy
type: local
location: "/etc/genesis/armada/assets/charts"
namespace: kube-system
upgrade:
no_hooks: true
values:
images:
tags:
proxy: gcr.io/google_containers/hyperkube-amd64:v1.8.6
network:
kubernetes_netloc: 127.0.0.1:6553
dependencies:
- helm-toolkit
---
schema: armada/Chart/v1
metadata:
name: helm-toolkit
schema: metadata/Document/v1
data:
chart_name: helm-toolkit
wait:
timeout: 600
release: helm-toolkit
source:
reference: master
subpath: helm-toolkit
location: https://git.openstack.org/openstack/openstack-helm-infra
type: git
namespace: helm-toolkit
upgrade:
no_hooks: true
values: {}
dependencies: []
"""
chart_group = yaml.safe_load_all(test_chart_group)
is_valid, error = validate.validate_armada_documents(list(chart_group))
self.assertTrue(is_valid)
def test_validate_group_passes(self):
chart_group = yaml.safe_load(template_chart_group)
is_valid, error = validate.validate_armada_document(chart_group)
self.assertTrue(is_valid)
def test_validate_chart_passes(self):
chart = yaml.safe_load(template_chart)
is_valid, error = validate.validate_armada_document(chart)
self.assertTrue(is_valid)
@testtools.skipUnless(
base.is_connected(),
'validate_manifest_url requires network connectivity.')
def test_validate_manifest_url(self):
value = 'url'
self.assertFalse(validate.validate_manifest_url(value))
value = 'https://raw.githubusercontent.com/openstack/' \
'airship-armada/master/examples/simple.yaml'
self.assertTrue(validate.validate_manifest_url(value))
class ValidateNegativeTestCase(BaseValidateTest):
def test_validate_load_duplicate_schemas_expect_runtime_error(self):
"""Validate that calling ``validate._load_schemas`` results in a
``RuntimeError`` being thrown, because the call is made during module
import, and importing the schemas again in manually results in
duplicates.
"""
with self.assertRaisesRegexp(RuntimeError,
'Duplicate schema specified for: .*'):
validate._load_schemas()
def test_validate_no_dictionary_expect_type_error(self):
expected_error = 'The provided input "invalid" must be a dictionary.'
self.assertRaisesRegexp(TypeError, expected_error,
validate.validate_armada_documents,
['invalid'])
def test_validate_invalid_chart_armada_manifest(self):
template = '{}/resources/valid_armada_document.yaml'.format(
self.basepath)
with open(template) as f:
documents = list(yaml.safe_load_all(f.read()))
mariadb_document = [
d for d in documents if d['metadata']['name'] == 'mariadb'
][0]
del mariadb_document['data']['release']
_, error_messages = validate.validate_armada_documents(documents)
expected_error = self._build_error_message(
'armada/Chart/v1', 'mariadb', "'release' is a required property")
self.assertEqual(1, len(error_messages))
self.assertEqual(expected_error, error_messages[0]['message'])
def test_validate_validate_group_without_required_chart_group(self):
template_manifest = """
schema: armada/ChartGroup/v1
metadata:
schema: metadata/Document/v1
name: example-manifest
data:
description: this is sample
"""
document = yaml.safe_load(template_manifest)
is_valid, error = validate.validate_armada_document(document)
expected_error = self._build_error_message(
'armada/ChartGroup/v1', 'example-manifest',
"'chart_group' is a required property")
self.assertFalse(is_valid)
self.assertEqual(error[0]['message'], expected_error)
def test_validate_manifest_without_required_release_prefix(self):
template_manifest = """
schema: armada/Manifest/v1
metadata:
schema: metadata/Document/v1
name: example-manifest
data:
chart_groups:
- example-group
"""
document = yaml.safe_load(template_manifest)
is_valid, error = validate.validate_armada_document(document)
expected_error = self._build_error_message(
'armada/Manifest/v1', 'example-manifest',
"'release_prefix' is a required property")
self.assertFalse(is_valid)
self.assertEqual(error[0]['message'], expected_error)
def test_validate_chart_without_required_release_property(self):
template_manifest = """
schema: armada/Chart/v1
metadata:
schema: metadata/Document/v1
name: example-chart
data:
chart_name: keystone
namespace: undercloud
timeout: 100
install:
no_hooks: false
upgrade:
no_hooks: false
values: {}
source:
type: git
location: git://github.com/example/example
subpath: example-chart
reference: master
dependencies:
- dep-chart
"""
document = yaml.safe_load(template_manifest)
is_valid, error = validate.validate_armada_document(document)
expected_error = self._build_error_message(
'armada/Chart/v1', 'example-chart',
"'release' is a required property")
self.assertFalse(is_valid)
self.assertEqual(error[0]['message'], expected_error)