|
|
|
# Copyright 2015 The Kubernetes Authors All 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.
|
|
|
|
|
|
|
|
"""Basic unit tests for template expansion library."""
|
|
|
|
|
|
|
|
import expansion
|
|
|
|
import os
|
|
|
|
import unittest
|
|
|
|
import yaml
|
|
|
|
|
|
|
|
|
|
|
|
def GetFilePath():
|
|
|
|
"""Find our source and data files."""
|
|
|
|
return os.path.dirname(os.path.abspath(__file__))
|
|
|
|
|
|
|
|
|
|
|
|
def ReadTestFile(filename):
|
|
|
|
"""Returns contents of a file from the test/ directory."""
|
|
|
|
|
|
|
|
full_path = GetFilePath() + '/../test/templates/' + filename
|
|
|
|
test_file = open(full_path, 'r')
|
|
|
|
return test_file.read()
|
|
|
|
|
|
|
|
|
|
|
|
def GetTestBasePath(filename):
|
|
|
|
"""Returns the base path of a file from the testdata/ directory."""
|
|
|
|
|
|
|
|
full_path = GetFilePath() + '/../test/templates/' + filename
|
|
|
|
return os.path.dirname(full_path)
|
|
|
|
|
|
|
|
|
|
|
|
class ExpansionTest(unittest.TestCase):
|
|
|
|
"""Tests basic functionality of the template expansion library."""
|
|
|
|
|
|
|
|
EMPTY_RESPONSE = 'config:\n resources: []\nlayout:\n resources: []\n'
|
|
|
|
|
|
|
|
def testEmptyExpansion(self):
|
|
|
|
template = ''
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template)
|
|
|
|
|
|
|
|
self.assertEqual('', expanded_template)
|
|
|
|
|
|
|
|
def testNoResourcesList(self):
|
|
|
|
template = 'imports: [ test.import ]'
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template)
|
|
|
|
|
|
|
|
self.assertEqual(self.EMPTY_RESPONSE, expanded_template)
|
|
|
|
|
|
|
|
def testResourcesListEmpty(self):
|
|
|
|
template = 'resources:'
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template)
|
|
|
|
|
|
|
|
self.assertEqual(self.EMPTY_RESPONSE, expanded_template)
|
|
|
|
|
|
|
|
def testSimpleNoExpansionTemplate(self):
|
|
|
|
template = ReadTestFile('simple.yaml')
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('simple_result.yaml')
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testJinjaExpansion(self):
|
|
|
|
template = ReadTestFile('jinja_template.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['jinja_template.jinja'] = ReadTestFile('jinja_template.jinja')
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template, imports)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('jinja_template_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testJinjaWithNoParamsExpansion(self):
|
|
|
|
template = ReadTestFile('jinja_noparams.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['jinja_noparams.jinja'] = ReadTestFile('jinja_noparams.jinja')
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template, imports)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('jinja_noparams_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testPythonWithNoParamsExpansion(self):
|
|
|
|
template = ReadTestFile('python_noparams.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['python_noparams.py'] = ReadTestFile('python_noparams.py')
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template, imports)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('python_noparams_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testPythonExpansion(self):
|
|
|
|
template = ReadTestFile('python_template.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['python_template.py'] = ReadTestFile('python_template.py')
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template, imports)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('python_template_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testPythonAndJinjaExpansion(self):
|
|
|
|
template = ReadTestFile('python_and_jinja_template.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['python_and_jinja_template.py'] = ReadTestFile(
|
|
|
|
'python_and_jinja_template.py')
|
|
|
|
|
|
|
|
imports['python_and_jinja_template.jinja'] = ReadTestFile(
|
|
|
|
'python_and_jinja_template.jinja')
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template, imports)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('python_and_jinja_template_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testNoImportErrors(self):
|
|
|
|
template = 'resources: \n- type: something.jinja\n name: something'
|
|
|
|
expansion.Expand(template, {})
|
|
|
|
|
|
|
|
def testInvalidConfig(self):
|
|
|
|
template = ReadTestFile('invalid_config.yaml')
|
|
|
|
|
|
|
|
try:
|
|
|
|
expansion.Expand(
|
|
|
|
template)
|
|
|
|
self.fail('Expansion should fail')
|
|
|
|
except expansion.ExpansionError as e:
|
|
|
|
self.assertNotIn(os.path.basename(expansion.__name__), e.message,
|
|
|
|
'Do not leak internals')
|
|
|
|
|
|
|
|
def testJinjaWithImport(self):
|
|
|
|
template = ReadTestFile('jinja_template_with_import.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['jinja_template_with_import.jinja'] = ReadTestFile(
|
|
|
|
'jinja_template_with_import.jinja')
|
|
|
|
imports['helpers/common.jinja'] = ReadTestFile(
|
|
|
|
'helpers/common.jinja')
|
|
|
|
|
|
|
|
yaml_template = yaml.safe_load(template)
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
str(yaml_template), imports)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('jinja_template_with_import_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testJinjaWithInlinedFile(self):
|
|
|
|
template = ReadTestFile('jinja_template_with_inlinedfile.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['jinja_template_with_inlinedfile.jinja'] = ReadTestFile(
|
|
|
|
'jinja_template_with_inlinedfile.jinja')
|
|
|
|
imports['helpers/common.jinja'] = ReadTestFile(
|
|
|
|
'helpers/common.jinja')
|
|
|
|
|
|
|
|
imports['description_text.txt'] = ReadTestFile('description_text.txt')
|
|
|
|
|
|
|
|
yaml_template = yaml.safe_load(template)
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
str(yaml_template), imports)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('jinja_template_with_inlinedfile_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testPythonWithImport(self):
|
|
|
|
template = ReadTestFile('python_template_with_import.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['python_template_with_import.py'] = ReadTestFile(
|
|
|
|
'python_template_with_import.py')
|
|
|
|
|
|
|
|
imports['helpers/common.py'] = ReadTestFile('helpers/common.py')
|
|
|
|
imports['helpers/extra/common2.py'] = ReadTestFile(
|
|
|
|
'helpers/extra/common2.py')
|
|
|
|
imports['helpers/extra'] = ReadTestFile('helpers/extra/__init__.py')
|
|
|
|
|
|
|
|
yaml_template = yaml.safe_load(template)
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
str(yaml_template), imports)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('python_template_with_import_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testPythonWithInlinedFile(self):
|
|
|
|
template = ReadTestFile('python_template_with_inlinedfile.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['python_template_with_inlinedfile.py'] = ReadTestFile(
|
|
|
|
'python_template_with_inlinedfile.py')
|
|
|
|
|
|
|
|
imports['helpers/common.py'] = ReadTestFile('helpers/common.py')
|
|
|
|
imports['helpers/extra/common2.py'] = ReadTestFile(
|
|
|
|
'helpers/extra/common2.py')
|
|
|
|
|
|
|
|
imports['description_text.txt'] = ReadTestFile('description_text.txt')
|
|
|
|
|
|
|
|
yaml_template = yaml.safe_load(template)
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
str(yaml_template), imports)
|
|
|
|
|
|
|
|
result_file = ReadTestFile(
|
|
|
|
'python_template_with_inlinedfile_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testPythonWithEnvironment(self):
|
|
|
|
template = ReadTestFile('python_template_with_env.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['python_template_with_env.py'] = ReadTestFile(
|
|
|
|
'python_template_with_env.py')
|
|
|
|
|
|
|
|
env = {'project': 'my-project'}
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template, imports, env)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('python_template_with_env_result.yaml')
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testJinjaWithEnvironment(self):
|
|
|
|
template = ReadTestFile('jinja_template_with_env.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['jinja_template_with_env.jinja'] = ReadTestFile(
|
|
|
|
'jinja_template_with_env.jinja')
|
|
|
|
|
|
|
|
env = {'project': 'test-project', 'deployment': 'test-deployment'}
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template, imports, env)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('jinja_template_with_env_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testMissingNameErrors(self):
|
|
|
|
template = 'resources: \n- type: something.jinja\n'
|
|
|
|
|
|
|
|
try:
|
|
|
|
expansion.Expand(template, {})
|
|
|
|
self.fail('Expansion should fail')
|
|
|
|
except expansion.ExpansionError as e:
|
|
|
|
self.assertTrue('not have a name' in e.message)
|
|
|
|
|
|
|
|
def testDuplicateNamesErrors(self):
|
|
|
|
template = ReadTestFile('duplicate_names.yaml')
|
|
|
|
|
|
|
|
try:
|
|
|
|
expansion.Expand(template, {})
|
|
|
|
self.fail('Expansion should fail')
|
|
|
|
except expansion.ExpansionError as e:
|
|
|
|
self.assertTrue(("Resource name 'my_instance' is not unique"
|
|
|
|
" in config.") in e.message)
|
|
|
|
|
|
|
|
def testDuplicateNamesInSubtemplates(self):
|
|
|
|
template = ReadTestFile('duplicate_names_in_subtemplates.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['duplicate_names_in_subtemplates.jinja'] = ReadTestFile(
|
|
|
|
'duplicate_names_in_subtemplates.jinja')
|
|
|
|
|
|
|
|
try:
|
|
|
|
expansion.Expand(
|
|
|
|
template, imports)
|
|
|
|
self.fail('Expansion should fail')
|
|
|
|
except expansion.ExpansionError as e:
|
|
|
|
self.assertTrue('not unique in duplicate_names_in_subtemplates.jinja'
|
|
|
|
in e.message)
|
|
|
|
|
|
|
|
def testDuplicateNamesMixedLevel(self):
|
|
|
|
template = ReadTestFile('duplicate_names_mixed_level.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['duplicate_names_B.jinja'] = ReadTestFile(
|
|
|
|
'duplicate_names_B.jinja')
|
|
|
|
imports['duplicate_names_C.jinja'] = ReadTestFile(
|
|
|
|
'duplicate_names_C.jinja')
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template, imports)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('duplicate_names_mixed_level_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testDuplicateNamesParentChild(self):
|
|
|
|
template = ReadTestFile('duplicate_names_parent_child.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['duplicate_names_B.jinja'] = ReadTestFile(
|
|
|
|
'duplicate_names_B.jinja')
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template, imports)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('duplicate_names_parent_child_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
# Note, this template will fail in the frontend for duplicate resource names
|
|
|
|
|
|
|
|
def testTemplateReturnsEmpty(self):
|
|
|
|
template = ReadTestFile('no_resources.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['no_resources.py'] = ReadTestFile(
|
|
|
|
'no_resources.py')
|
|
|
|
|
|
|
|
try:
|
|
|
|
expansion.Expand(
|
|
|
|
template, imports)
|
|
|
|
self.fail('Expansion should fail')
|
|
|
|
except expansion.ExpansionError as e:
|
|
|
|
self.assertIn('Template did not return a \'resources:\' field.',
|
|
|
|
e.message)
|
|
|
|
self.assertIn('no_resources.py', e.message)
|
|
|
|
|
|
|
|
def testJinjaDefaultsSchema(self):
|
|
|
|
# Loop 1000 times to make sure we don't rely on dictionary ordering.
|
|
|
|
for unused_x in range(0, 1000):
|
|
|
|
template = ReadTestFile('jinja_defaults.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['jinja_defaults.jinja'] = ReadTestFile(
|
|
|
|
'jinja_defaults.jinja')
|
|
|
|
imports['jinja_defaults.jinja.schema'] = ReadTestFile(
|
|
|
|
'jinja_defaults.jinja.schema')
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template, imports,
|
|
|
|
validate_schema=True)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('jinja_defaults_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testPythonDefaultsOverrideSchema(self):
|
|
|
|
template = ReadTestFile('python_schema.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['python_schema.py'] = ReadTestFile('python_schema.py')
|
|
|
|
imports['python_schema.py.schema'] = ReadTestFile('python_schema.py.schema')
|
|
|
|
|
|
|
|
env = {'project': 'my-project'}
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template, imports, env=env,
|
|
|
|
validate_schema=True)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('python_schema_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testJinjaMissingRequiredPropertySchema(self):
|
|
|
|
template = ReadTestFile('jinja_missing_required.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['jinja_missing_required.jinja'] = ReadTestFile(
|
|
|
|
'jinja_missing_required.jinja')
|
|
|
|
imports['jinja_missing_required.jinja.schema'] = ReadTestFile(
|
|
|
|
'jinja_missing_required.jinja.schema')
|
|
|
|
|
|
|
|
try:
|
|
|
|
expansion.Expand(
|
|
|
|
template, imports,
|
|
|
|
validate_schema=True)
|
|
|
|
self.fail('Expansion error expected')
|
|
|
|
except expansion.ExpansionError as e:
|
|
|
|
self.assertIn('Invalid properties', e.message)
|
|
|
|
self.assertIn("'important' is a required property", e.message)
|
|
|
|
self.assertIn('jinja_missing_required_resource_name', e.message)
|
|
|
|
|
|
|
|
def testJinjaErrorFileMessage(self):
|
|
|
|
template = ReadTestFile('jinja_unresolved.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['jinja_unresolved.jinja'] = ReadTestFile('jinja_unresolved.jinja')
|
|
|
|
|
|
|
|
try:
|
|
|
|
expansion.Expand(
|
|
|
|
template, imports,
|
|
|
|
validate_schema=False)
|
|
|
|
self.fail('Expansion error expected')
|
|
|
|
except expansion.ExpansionError as e:
|
|
|
|
self.assertIn('jinja_unresolved.jinja', e.message)
|
|
|
|
|
|
|
|
def testJinjaMultipleErrorsSchema(self):
|
|
|
|
template = ReadTestFile('jinja_multiple_errors.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['jinja_multiple_errors.jinja'] = ReadTestFile(
|
|
|
|
'jinja_multiple_errors.jinja')
|
|
|
|
imports['jinja_multiple_errors.jinja.schema'] = ReadTestFile(
|
|
|
|
'jinja_multiple_errors.jinja.schema')
|
|
|
|
|
|
|
|
try:
|
|
|
|
expansion.Expand(
|
|
|
|
template, imports,
|
|
|
|
validate_schema=True)
|
|
|
|
self.fail('Expansion error expected')
|
|
|
|
except expansion.ExpansionError as e:
|
|
|
|
self.assertIn('Invalid properties', e.message)
|
|
|
|
self.assertIn("'a string' is not of type 'integer'", e.message)
|
|
|
|
self.assertIn("'d' is not one of ['a', 'b', 'c']", e.message)
|
|
|
|
self.assertIn("'longer than 10 chars' is too long", e.message)
|
|
|
|
self.assertIn("{'multipleOf': 2} is not allowed for 6", e.message)
|
|
|
|
|
|
|
|
def testPythonBadSchema(self):
|
|
|
|
template = ReadTestFile('python_bad_schema.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['python_bad_schema.py'] = ReadTestFile(
|
|
|
|
'python_bad_schema.py')
|
|
|
|
imports['python_bad_schema.py.schema'] = ReadTestFile(
|
|
|
|
'python_bad_schema.py.schema')
|
|
|
|
|
|
|
|
try:
|
|
|
|
expansion.Expand(
|
|
|
|
template, imports,
|
|
|
|
validate_schema=True)
|
|
|
|
self.fail('Expansion error expected')
|
|
|
|
except expansion.ExpansionError as e:
|
|
|
|
self.assertIn('Invalid schema', e.message)
|
|
|
|
self.assertIn("'int' is not valid under any of the given schemas",
|
|
|
|
e.message)
|
|
|
|
self.assertIn("'maximum' is a dependency of u'exclusiveMaximum'",
|
|
|
|
e.message)
|
|
|
|
self.assertIn("10 is not of type u'boolean'", e.message)
|
|
|
|
self.assertIn("'not a list' is not of type u'array'", e.message)
|
|
|
|
|
|
|
|
def testNoProperties(self):
|
|
|
|
template = ReadTestFile('no_properties.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['no_properties.py'] = ReadTestFile(
|
|
|
|
'no_properties.py')
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template, imports,
|
|
|
|
validate_schema=True)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('no_properties_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
def testNestedTemplateSchema(self):
|
|
|
|
template = ReadTestFile('use_helper.yaml')
|
|
|
|
|
|
|
|
imports = {}
|
|
|
|
imports['use_helper.jinja'] = ReadTestFile(
|
|
|
|
'use_helper.jinja')
|
|
|
|
imports['use_helper.jinja.schema'] = ReadTestFile(
|
|
|
|
'use_helper.jinja.schema')
|
|
|
|
imports['helper.jinja'] = ReadTestFile(
|
|
|
|
'helper.jinja')
|
|
|
|
imports['helper.jinja.schema'] = ReadTestFile(
|
|
|
|
'helper.jinja.schema')
|
|
|
|
|
|
|
|
expanded_template = expansion.Expand(
|
|
|
|
template, imports,
|
|
|
|
validate_schema=True)
|
|
|
|
|
|
|
|
result_file = ReadTestFile('use_helper_result.yaml')
|
|
|
|
|
|
|
|
self.assertEquals(result_file, expanded_template)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
unittest.main()
|