mirror of https://github.com/helm/helm
commit
d4bf89c52d
@ -0,0 +1,504 @@
|
|||||||
|
######################################################################
|
||||||
|
# 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()
|
@ -0,0 +1,616 @@
|
|||||||
|
######################################################################
|
||||||
|
# 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.
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import schema_validation
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
INVALID_PROPERTIES = "Invalid properties for 'template.py'"
|
||||||
|
|
||||||
|
|
||||||
|
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 testdata/ directory."""
|
||||||
|
|
||||||
|
full_path = os.path.join(GetFilePath(), '..', 'test', 'schemas', filename)
|
||||||
|
return open(full_path, 'r').read()
|
||||||
|
|
||||||
|
|
||||||
|
def RawValidate(raw_properties, schema_name, raw_schema):
|
||||||
|
return ImportsRawValidate(raw_properties, schema_name,
|
||||||
|
{schema_name: raw_schema})
|
||||||
|
|
||||||
|
|
||||||
|
def ImportsRawValidate(raw_properties, schema_name, import_map):
|
||||||
|
"""Takes raw properties, calls validate and returns yaml properties."""
|
||||||
|
properties = yaml.safe_load(raw_properties)
|
||||||
|
return schema_validation.Validate(properties, schema_name, 'template.py',
|
||||||
|
import_map)
|
||||||
|
|
||||||
|
|
||||||
|
class SchemaValidationTest(unittest.TestCase):
|
||||||
|
"""Tests of the schema portion of the template expansion library."""
|
||||||
|
|
||||||
|
def testDefaults(self):
|
||||||
|
schema_name = 'defaults.jinja.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
empty_properties = ''
|
||||||
|
expected_properties = """
|
||||||
|
alpha: alpha
|
||||||
|
one: 1
|
||||||
|
"""
|
||||||
|
self.assertEqual(yaml.safe_load(expected_properties),
|
||||||
|
RawValidate(empty_properties, schema_name, schema))
|
||||||
|
|
||||||
|
def testNestedDefaults(self):
|
||||||
|
schema_name = 'nested_defaults.py.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
properties = """
|
||||||
|
zone: us-central1-a
|
||||||
|
disks:
|
||||||
|
- name: backup # diskType and sizeGb set by default
|
||||||
|
- name: cache # sizeGb set by default
|
||||||
|
diskType: pd-ssd
|
||||||
|
- name: data # Nothing set by default
|
||||||
|
diskType: pd-ssd
|
||||||
|
sizeGb: 150
|
||||||
|
- name: swap # diskType set by default
|
||||||
|
sizeGb: 200
|
||||||
|
"""
|
||||||
|
expected_properties = """
|
||||||
|
zone: us-central1-a
|
||||||
|
disks:
|
||||||
|
- sizeGb: 100
|
||||||
|
diskType: pd-standard
|
||||||
|
name: backup
|
||||||
|
- sizeGb: 100
|
||||||
|
diskType: pd-ssd
|
||||||
|
name: cache
|
||||||
|
- sizeGb: 150
|
||||||
|
diskType: pd-ssd
|
||||||
|
name: data
|
||||||
|
- sizeGb: 200
|
||||||
|
diskType: pd-standard
|
||||||
|
name: swap
|
||||||
|
"""
|
||||||
|
self.assertEqual(yaml.safe_load(expected_properties),
|
||||||
|
RawValidate(properties, schema_name, schema))
|
||||||
|
|
||||||
|
def testNestedRefDefaults(self):
|
||||||
|
schema_name = 'ref_nested_defaults.py.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
properties = """
|
||||||
|
zone: us-central1-a
|
||||||
|
disks:
|
||||||
|
- name: backup # diskType and sizeGb set by default
|
||||||
|
- name: cache # sizeGb set by default
|
||||||
|
diskType: pd-ssd
|
||||||
|
- name: data # Nothing set by default
|
||||||
|
diskType: pd-ssd
|
||||||
|
sizeGb: 150
|
||||||
|
- name: swap # diskType set by default
|
||||||
|
sizeGb: 200
|
||||||
|
"""
|
||||||
|
expected_properties = """
|
||||||
|
zone: us-central1-a
|
||||||
|
disks:
|
||||||
|
- sizeGb: 100
|
||||||
|
diskType: pd-standard
|
||||||
|
name: backup
|
||||||
|
- sizeGb: 100
|
||||||
|
diskType: pd-ssd
|
||||||
|
name: cache
|
||||||
|
- sizeGb: 150
|
||||||
|
diskType: pd-ssd
|
||||||
|
name: data
|
||||||
|
- sizeGb: 200
|
||||||
|
diskType: pd-standard
|
||||||
|
name: swap
|
||||||
|
"""
|
||||||
|
self.assertEqual(yaml.safe_load(expected_properties),
|
||||||
|
RawValidate(properties, schema_name, schema))
|
||||||
|
|
||||||
|
def testInvalidDefault(self):
|
||||||
|
schema_name = 'invalid_default.jinja.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
empty_properties = ''
|
||||||
|
try:
|
||||||
|
RawValidate(empty_properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn(INVALID_PROPERTIES, e.message)
|
||||||
|
self.assertIn("'string' is not of type 'integer' at ['number']",
|
||||||
|
e.message)
|
||||||
|
|
||||||
|
def testRequiredDefault(self):
|
||||||
|
schema_name = 'required_default.jinja.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
empty_properties = ''
|
||||||
|
expected_properties = """
|
||||||
|
name: my_name
|
||||||
|
"""
|
||||||
|
self.assertEqual(yaml.safe_load(expected_properties),
|
||||||
|
RawValidate(empty_properties, schema_name, schema))
|
||||||
|
|
||||||
|
def testRequiredDefaultReference(self):
|
||||||
|
schema_name = 'req_default_ref.py.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
empty_properties = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
RawValidate(empty_properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn(INVALID_PROPERTIES, e.message)
|
||||||
|
self.assertIn("'my_name' is not of type 'integer' at ['number']",
|
||||||
|
e.message)
|
||||||
|
|
||||||
|
def testDefaultReference(self):
|
||||||
|
schema_name = 'default_ref.jinja.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
empty_properties = ''
|
||||||
|
expected_properties = 'number: 1'
|
||||||
|
|
||||||
|
self.assertEqual(yaml.safe_load(expected_properties),
|
||||||
|
RawValidate(empty_properties, schema_name, schema))
|
||||||
|
|
||||||
|
def testMissingQuoteInReference(self):
|
||||||
|
schema_name = 'missing_quote.py.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
properties = 'number: 1'
|
||||||
|
|
||||||
|
try:
|
||||||
|
RawValidate(properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(2, len(e.errors))
|
||||||
|
self.assertIn("Invalid schema '%s'" % schema_name, e.message)
|
||||||
|
self.assertIn("type 'NoneType' is not iterable", e.message)
|
||||||
|
self.assertIn('around your reference', e.message)
|
||||||
|
|
||||||
|
def testRequiredPropertyMissing(self):
|
||||||
|
schema_name = 'required.jinja.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
empty_properties = ''
|
||||||
|
try:
|
||||||
|
RawValidate(empty_properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn(INVALID_PROPERTIES, e.message)
|
||||||
|
self.assertIn("'name' is a required property", e.errors[0].message)
|
||||||
|
|
||||||
|
def testRequiredPropertyValid(self):
|
||||||
|
schema_name = 'required.jinja.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
properties = """
|
||||||
|
name: my-name
|
||||||
|
"""
|
||||||
|
self.assertEqual(yaml.safe_load(properties),
|
||||||
|
RawValidate(properties, schema_name, schema))
|
||||||
|
|
||||||
|
def testMultipleErrors(self):
|
||||||
|
schema_name = 'defaults.py.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
properties = """
|
||||||
|
one: not a number
|
||||||
|
alpha: 12345
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
RawValidate(properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(2, len(e.errors))
|
||||||
|
self.assertIn(INVALID_PROPERTIES, e.message)
|
||||||
|
self.assertIn("'not a number' is not of type 'integer' at ['one']",
|
||||||
|
e.message)
|
||||||
|
self.assertIn("12345 is not of type 'string' at ['alpha']", e.message)
|
||||||
|
|
||||||
|
def testNumbersValid(self):
|
||||||
|
schema_name = 'numbers.py.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
properties = """
|
||||||
|
minimum0: 0
|
||||||
|
exclusiveMin0: 1
|
||||||
|
maximum10: 10
|
||||||
|
exclusiveMax10: 9
|
||||||
|
even: 20
|
||||||
|
odd: 21
|
||||||
|
"""
|
||||||
|
self.assertEquals(yaml.safe_load(properties),
|
||||||
|
RawValidate(properties, schema_name, schema))
|
||||||
|
|
||||||
|
def testNumbersInvalid(self):
|
||||||
|
schema_name = 'numbers.py.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
properties = """
|
||||||
|
minimum0: -1
|
||||||
|
exclusiveMin0: 0
|
||||||
|
maximum10: 11
|
||||||
|
exclusiveMax10: 10
|
||||||
|
even: 21
|
||||||
|
odd: 20
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
RawValidate(properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(6, len(e.errors))
|
||||||
|
self.assertIn(INVALID_PROPERTIES, e.message)
|
||||||
|
self.assertIn("-1 is less than the minimum of 0 at ['minimum0']",
|
||||||
|
e.message)
|
||||||
|
self.assertIn(('0 is less than or equal to the minimum of 0'
|
||||||
|
" at ['exclusiveMin0']"), e.message)
|
||||||
|
self.assertIn("11 is greater than the maximum of 10 at ['maximum10']",
|
||||||
|
e.message)
|
||||||
|
self.assertIn(('10 is greater than or equal to the maximum of 10'
|
||||||
|
" at ['exclusiveMax10']"), e.message)
|
||||||
|
self.assertIn("21 is not a multiple of 2 at ['even']", e.message)
|
||||||
|
self.assertIn("{'multipleOf': 2} is not allowed for 20 at ['odd']",
|
||||||
|
e.message)
|
||||||
|
|
||||||
|
def testReference(self):
|
||||||
|
schema_name = 'reference.jinja.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
properties = """
|
||||||
|
odd: 6
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
RawValidate(properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn('even', e.message)
|
||||||
|
self.assertIn('is not allowed for 6', e.message)
|
||||||
|
|
||||||
|
def testBadSchema(self):
|
||||||
|
schema_name = 'bad.jinja.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
empty_properties = ''
|
||||||
|
try:
|
||||||
|
RawValidate(empty_properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(2, len(e.errors))
|
||||||
|
self.assertIn("Invalid schema '%s'" % schema_name, e.message)
|
||||||
|
self.assertIn("u'minimum' is a dependency of u'exclusiveMinimum'",
|
||||||
|
e.message)
|
||||||
|
self.assertIn("0 is not of type u'boolean'", e.message)
|
||||||
|
|
||||||
|
def testInvalidReference(self):
|
||||||
|
schema_name = 'invalid_reference.py.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
properties = 'odd: 1'
|
||||||
|
try:
|
||||||
|
RawValidate(properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn("Invalid schema '%s'" % schema_name, e.message)
|
||||||
|
self.assertIn('Unresolvable JSON pointer', e.message)
|
||||||
|
|
||||||
|
def testInvalidReferenceInSchema(self):
|
||||||
|
schema_name = 'invalid_reference_schema.py.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
empty_properties = ''
|
||||||
|
try:
|
||||||
|
RawValidate(empty_properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn("Invalid schema '%s'" % schema_name, e.message)
|
||||||
|
self.assertIn('Unresolvable JSON pointer', e.message)
|
||||||
|
|
||||||
|
def testMetadata(self):
|
||||||
|
schema_name = 'metadata.py.schema'
|
||||||
|
schema = ReadTestFile(schema_name)
|
||||||
|
properties = """
|
||||||
|
one: 2
|
||||||
|
alpha: beta
|
||||||
|
"""
|
||||||
|
self.assertEquals(yaml.safe_load(properties),
|
||||||
|
RawValidate(properties, schema_name, schema))
|
||||||
|
|
||||||
|
def testInvalidInput(self):
|
||||||
|
schema_name = 'schema'
|
||||||
|
schema = """
|
||||||
|
info:
|
||||||
|
title: Invalid Input
|
||||||
|
properties: invalid
|
||||||
|
"""
|
||||||
|
properties = """
|
||||||
|
one: 2
|
||||||
|
alpha: beta
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
RawValidate(properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn("Invalid schema '%s'" % schema_name, e.message)
|
||||||
|
self.assertIn("'invalid' is not of type u'object'", e.message)
|
||||||
|
|
||||||
|
def testPattern(self):
|
||||||
|
schema_name = 'schema'
|
||||||
|
schema = r"""
|
||||||
|
properties:
|
||||||
|
bad-zone:
|
||||||
|
pattern: \w+-\w+-\w+
|
||||||
|
zone:
|
||||||
|
pattern: \w+-\w+-\w+
|
||||||
|
"""
|
||||||
|
properties = """
|
||||||
|
bad-zone: abc
|
||||||
|
zone: us-central1-a
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
RawValidate(properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn('Invalid properties', e.message)
|
||||||
|
self.assertIn("'abc' does not match", e.message)
|
||||||
|
self.assertIn('bad-zone', e.message)
|
||||||
|
|
||||||
|
def testUniqueItems(self):
|
||||||
|
schema_name = 'schema'
|
||||||
|
schema = """
|
||||||
|
properties:
|
||||||
|
bad-list:
|
||||||
|
type: array
|
||||||
|
uniqueItems: true
|
||||||
|
list:
|
||||||
|
type: array
|
||||||
|
uniqueItems: true
|
||||||
|
"""
|
||||||
|
properties = """
|
||||||
|
bad-list:
|
||||||
|
- a
|
||||||
|
- b
|
||||||
|
- a
|
||||||
|
list:
|
||||||
|
- a
|
||||||
|
- b
|
||||||
|
- c
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
RawValidate(properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn('Invalid properties', e.message)
|
||||||
|
self.assertIn('has non-unique elements', e.message)
|
||||||
|
self.assertIn('bad-list', e.message)
|
||||||
|
|
||||||
|
def testUniqueItemsOnString(self):
|
||||||
|
schema_name = 'schema'
|
||||||
|
schema = """
|
||||||
|
properties:
|
||||||
|
ok-string:
|
||||||
|
type: string
|
||||||
|
uniqueItems: true
|
||||||
|
string:
|
||||||
|
type: string
|
||||||
|
uniqueItems: true
|
||||||
|
"""
|
||||||
|
properties = """
|
||||||
|
ok-string: aaa
|
||||||
|
string: abc
|
||||||
|
"""
|
||||||
|
self.assertEquals(yaml.safe_load(properties),
|
||||||
|
RawValidate(properties, schema_name, schema))
|
||||||
|
|
||||||
|
def testRequiredTopLevel(self):
|
||||||
|
schema_name = 'schema'
|
||||||
|
schema = """
|
||||||
|
info:
|
||||||
|
title: Invalid Input
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
"""
|
||||||
|
properties = """
|
||||||
|
one: 2
|
||||||
|
alpha: beta
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
RawValidate(properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn(INVALID_PROPERTIES, e.message)
|
||||||
|
self.assertIn("'name' is a required property", e.message)
|
||||||
|
|
||||||
|
def testEmptySchemaProperties(self):
|
||||||
|
schema_name = 'schema'
|
||||||
|
schema = """
|
||||||
|
info:
|
||||||
|
title: Empty Input
|
||||||
|
properties:
|
||||||
|
"""
|
||||||
|
properties = """
|
||||||
|
one: 2
|
||||||
|
alpha: beta
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
RawValidate(properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn("Invalid schema '%s'" % schema_name, e.message)
|
||||||
|
self.assertIn("None is not of type u'object' at [u'properties']",
|
||||||
|
e.message)
|
||||||
|
|
||||||
|
def testNoInput(self):
|
||||||
|
schema = """
|
||||||
|
info:
|
||||||
|
title: No other sections
|
||||||
|
"""
|
||||||
|
properties = """
|
||||||
|
one: 2
|
||||||
|
alpha: beta
|
||||||
|
"""
|
||||||
|
self.assertEquals(yaml.safe_load(properties),
|
||||||
|
RawValidate(properties, 'schema', schema))
|
||||||
|
|
||||||
|
def testEmptySchema(self):
|
||||||
|
schema = ''
|
||||||
|
properties = """
|
||||||
|
one: 2
|
||||||
|
alpha: beta
|
||||||
|
"""
|
||||||
|
self.assertEquals(yaml.safe_load(properties),
|
||||||
|
RawValidate(properties, 'schema', schema))
|
||||||
|
|
||||||
|
def testImportPathSchema(self):
|
||||||
|
schema = """
|
||||||
|
imports:
|
||||||
|
- path: a
|
||||||
|
- path: path/to/b
|
||||||
|
name: b
|
||||||
|
"""
|
||||||
|
properties = """
|
||||||
|
one: 2
|
||||||
|
alpha: beta
|
||||||
|
"""
|
||||||
|
|
||||||
|
import_map = {'schema': schema,
|
||||||
|
'a': '',
|
||||||
|
'b': ''}
|
||||||
|
|
||||||
|
self.assertEquals(yaml.safe_load(properties),
|
||||||
|
ImportsRawValidate(properties, 'schema', import_map))
|
||||||
|
|
||||||
|
def testImportSchemaMissing(self):
|
||||||
|
schema = ''
|
||||||
|
empty_properties = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
properties = yaml.safe_load(empty_properties)
|
||||||
|
schema_validation.Validate(properties, 'schema', 'template',
|
||||||
|
{'wrong_name': schema})
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn("Could not find schema file 'schema'", e.message)
|
||||||
|
|
||||||
|
def testImportsMalformedNotAList(self):
|
||||||
|
schema_name = 'schema'
|
||||||
|
schema = """
|
||||||
|
imports: not-a-list
|
||||||
|
"""
|
||||||
|
empty_properties = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
RawValidate(empty_properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn("Invalid schema '%s'" % schema_name, e.message)
|
||||||
|
self.assertIn("is not of type 'array' at ['imports']", e.message)
|
||||||
|
|
||||||
|
def testImportsMalformedMissingPath(self):
|
||||||
|
schema_name = 'schema'
|
||||||
|
schema = """
|
||||||
|
imports:
|
||||||
|
- name: no_path.yaml
|
||||||
|
"""
|
||||||
|
empty_properties = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
RawValidate(empty_properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn("Invalid schema '%s'" % schema_name, e.message)
|
||||||
|
self.assertIn("'path' is a required property", e.message)
|
||||||
|
|
||||||
|
def testImportsMalformedNonunique(self):
|
||||||
|
schema_name = 'schema'
|
||||||
|
schema = """
|
||||||
|
imports:
|
||||||
|
- path: a.yaml
|
||||||
|
name: a
|
||||||
|
- path: a.yaml
|
||||||
|
name: a
|
||||||
|
"""
|
||||||
|
empty_properties = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
RawValidate(empty_properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn("Invalid schema '%s'" % schema_name, e.message)
|
||||||
|
self.assertIn('non-unique elements', e.message)
|
||||||
|
|
||||||
|
def testImportsMalformedAdditionalProperties(self):
|
||||||
|
schema_name = 'schema'
|
||||||
|
schema = """
|
||||||
|
imports:
|
||||||
|
- path: a.yaml
|
||||||
|
gnome: a
|
||||||
|
"""
|
||||||
|
empty_properties = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
RawValidate(empty_properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(1, len(e.errors))
|
||||||
|
self.assertIn("Invalid schema '%s'" % schema_name, e.message)
|
||||||
|
self.assertIn('Additional properties are not allowed'
|
||||||
|
" ('gnome' was unexpected)", e.message)
|
||||||
|
|
||||||
|
def testImportAndInputErrors(self):
|
||||||
|
schema = """
|
||||||
|
imports:
|
||||||
|
- path: file
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
"""
|
||||||
|
empty_properties = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
RawValidate(empty_properties, 'schema', schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(2, len(e.errors))
|
||||||
|
self.assertIn("'file' requested in schema 'schema'", e.message)
|
||||||
|
self.assertIn("'name' is a required property", e.message)
|
||||||
|
|
||||||
|
def testImportAndInputSchemaErrors(self):
|
||||||
|
schema_name = 'schema'
|
||||||
|
schema = """
|
||||||
|
imports: not-a-list
|
||||||
|
required: not-a-list
|
||||||
|
"""
|
||||||
|
empty_properties = ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
RawValidate(empty_properties, schema_name, schema)
|
||||||
|
self.fail('Validation should fail')
|
||||||
|
except schema_validation.ValidationErrors as e:
|
||||||
|
self.assertEqual(2, len(e.errors))
|
||||||
|
self.assertIn("Invalid schema '%s'" % schema_name, e.message)
|
||||||
|
self.assertIn("is not of type 'array' at ['imports']", e.message)
|
||||||
|
self.assertIn("is not of type u'array' at [u'required']", e.message)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -0,0 +1,9 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with a lots of errors in it
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
exclusiveMin:
|
||||||
|
type: integer
|
||||||
|
exclusiveMinimum: 0
|
@ -0,0 +1,14 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with a property that has a referenced default value
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
number:
|
||||||
|
$ref: '#/level/mult'
|
||||||
|
|
||||||
|
level:
|
||||||
|
mult:
|
||||||
|
type: integer
|
||||||
|
multipleOf: 1
|
||||||
|
default: 1
|
@ -0,0 +1,12 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with properties that have default values
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
one:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
alpha:
|
||||||
|
type: string
|
||||||
|
default: alpha
|
@ -0,0 +1,12 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with properties that have default values
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
one:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
alpha:
|
||||||
|
type: string
|
||||||
|
default: alpha
|
@ -0,0 +1,11 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with a required integer property that has a default string value
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
required:
|
||||||
|
- number
|
||||||
|
properties:
|
||||||
|
number:
|
||||||
|
type: integer
|
||||||
|
default: string
|
@ -0,0 +1,10 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with references to something that doesnt exist
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
odd:
|
||||||
|
type: integer
|
||||||
|
not:
|
||||||
|
$ref: '#/wheeeeeee'
|
@ -0,0 +1,8 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with references to something that doesnt exist
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
odd:
|
||||||
|
$ref: '#/wheeeeeee'
|
@ -0,0 +1,20 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with properties that have extra metadata
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
one:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
metadata:
|
||||||
|
gcloud: is great!
|
||||||
|
compute: is awesome
|
||||||
|
alpha:
|
||||||
|
type: string
|
||||||
|
default: alpha
|
||||||
|
metadata:
|
||||||
|
- you
|
||||||
|
- can
|
||||||
|
- do
|
||||||
|
- anything
|
@ -0,0 +1,11 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with references
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
number:
|
||||||
|
$ref: #/number
|
||||||
|
|
||||||
|
number:
|
||||||
|
type: integer
|
@ -0,0 +1,33 @@
|
|||||||
|
info:
|
||||||
|
title: VM with Disks
|
||||||
|
author: Kubernetes
|
||||||
|
description: Creates a single vm, then attaches disks to it.
|
||||||
|
|
||||||
|
required:
|
||||||
|
- zone
|
||||||
|
|
||||||
|
properties:
|
||||||
|
zone:
|
||||||
|
type: string
|
||||||
|
description: GCP zone
|
||||||
|
default: us-central1-a
|
||||||
|
disks:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: Suffix for this disk
|
||||||
|
sizeGb:
|
||||||
|
type: integer
|
||||||
|
default: 100
|
||||||
|
diskType:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- pd-standard
|
||||||
|
- pd-ssd
|
||||||
|
default: pd-standard
|
||||||
|
additionalProperties: false
|
@ -0,0 +1,27 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with a lots of number properties and restrictions
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
minimum0:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
exclusiveMin0:
|
||||||
|
type: integer
|
||||||
|
minimum: 0
|
||||||
|
exclusiveMinimum: true
|
||||||
|
maximum10:
|
||||||
|
type: integer
|
||||||
|
maximum: 10
|
||||||
|
exclusiveMax10:
|
||||||
|
type: integer
|
||||||
|
maximum: 10
|
||||||
|
exclusiveMaximum: true
|
||||||
|
even:
|
||||||
|
type: integer
|
||||||
|
multipleOf: 2
|
||||||
|
odd:
|
||||||
|
type: integer
|
||||||
|
not:
|
||||||
|
multipleOf: 2
|
@ -0,0 +1,36 @@
|
|||||||
|
info:
|
||||||
|
title: VM with Disks
|
||||||
|
author: Kubernetes
|
||||||
|
description: Creates a single vm, then attaches disks to it.
|
||||||
|
|
||||||
|
required:
|
||||||
|
- zone
|
||||||
|
|
||||||
|
properties:
|
||||||
|
zone:
|
||||||
|
type: string
|
||||||
|
description: GCP zone
|
||||||
|
default: us-central1-a
|
||||||
|
disks:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/disk'
|
||||||
|
|
||||||
|
disk:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: Suffix for this disk
|
||||||
|
sizeGb:
|
||||||
|
type: integer
|
||||||
|
default: 100
|
||||||
|
diskType:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- pd-standard
|
||||||
|
- pd-ssd
|
||||||
|
default: pd-standard
|
||||||
|
additionalProperties: false
|
@ -0,0 +1,14 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with references
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
odd:
|
||||||
|
type: integer
|
||||||
|
not:
|
||||||
|
$ref: '#/even'
|
||||||
|
|
||||||
|
|
||||||
|
even:
|
||||||
|
multipleOf: 2
|
@ -0,0 +1,14 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with a required property that has a referenced default value
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
required:
|
||||||
|
- number
|
||||||
|
properties:
|
||||||
|
number:
|
||||||
|
$ref: '#/default_val'
|
||||||
|
|
||||||
|
default_val:
|
||||||
|
type: integer
|
||||||
|
default: my_name
|
@ -0,0 +1,10 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with a required property
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
@ -0,0 +1,11 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with a required property that has a default value
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
default: my_name
|
@ -0,0 +1 @@
|
|||||||
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
@ -0,0 +1,9 @@
|
|||||||
|
resources:
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: my_instance
|
||||||
|
properties:
|
||||||
|
zone: test-zone-a
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: my_instance
|
||||||
|
properties:
|
||||||
|
zone: test-zone-b
|
@ -0,0 +1,5 @@
|
|||||||
|
resources:
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: B
|
||||||
|
properties:
|
||||||
|
zone: test-zone-b
|
@ -0,0 +1,5 @@
|
|||||||
|
resources:
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: C
|
||||||
|
properties:
|
||||||
|
zone: test-zone-c
|
@ -0,0 +1,9 @@
|
|||||||
|
resources:
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: my_instance
|
||||||
|
properties:
|
||||||
|
zone: test-zone-a
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: my_instance
|
||||||
|
properties:
|
||||||
|
zone: test-zone-b
|
@ -0,0 +1,5 @@
|
|||||||
|
imports: ["duplicate_names_in_subtemplates.jinja"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: subtemplate
|
||||||
|
type: duplicate_names_in_subtemplates.jinja
|
@ -0,0 +1,7 @@
|
|||||||
|
imports: ["duplicate_names_B.jinja", "duplicate_names_C.jinja"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: A
|
||||||
|
type: duplicate_names_B.jinja
|
||||||
|
- name: B
|
||||||
|
type: duplicate_names_C.jinja
|
@ -0,0 +1,22 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: B
|
||||||
|
properties:
|
||||||
|
zone: test-zone-b
|
||||||
|
type: compute.v1.instance
|
||||||
|
- name: C
|
||||||
|
properties:
|
||||||
|
zone: test-zone-c
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: A
|
||||||
|
resources:
|
||||||
|
- name: B
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: duplicate_names_B.jinja
|
||||||
|
- name: B
|
||||||
|
resources:
|
||||||
|
- name: C
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: duplicate_names_C.jinja
|
@ -0,0 +1,7 @@
|
|||||||
|
imports: ["duplicate_names_B.jinja"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: A
|
||||||
|
type: duplicate_names_B.jinja
|
||||||
|
- name: B
|
||||||
|
type: compute.v1.instance
|
@ -0,0 +1,17 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: B
|
||||||
|
properties:
|
||||||
|
zone: test-zone-b
|
||||||
|
type: compute.v1.instance
|
||||||
|
- name: B
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: A
|
||||||
|
resources:
|
||||||
|
- name: B
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: duplicate_names_B.jinja
|
||||||
|
- name: B
|
||||||
|
type: compute.v1.instance
|
@ -0,0 +1,5 @@
|
|||||||
|
resources:
|
||||||
|
- name: helper
|
||||||
|
type: bar
|
||||||
|
properties:
|
||||||
|
test: {{ properties["foobar"] }}
|
@ -0,0 +1,4 @@
|
|||||||
|
properties:
|
||||||
|
foobar:
|
||||||
|
type: string
|
||||||
|
default: Use this schema
|
@ -0,0 +1,3 @@
|
|||||||
|
{%- macro GenerateMachineName(prefix='', suffix='') -%}
|
||||||
|
{{ prefix + "-" + suffix }}
|
||||||
|
{%- endmacro %}
|
@ -0,0 +1,8 @@
|
|||||||
|
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
"""Dummy helper methods invoked in other constructors."""
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateMachineName(prefix, suffix):
|
||||||
|
"""Generates name of a VM."""
|
||||||
|
return prefix + "-" + suffix
|
@ -0,0 +1 @@
|
|||||||
|
"""Package marker file."""
|
@ -0,0 +1,8 @@
|
|||||||
|
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
"""Dummy helper methods invoked in other constructors."""
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateMachineSize():
|
||||||
|
"""Generates size of a VM."""
|
||||||
|
return "big"
|
@ -0,0 +1,2 @@
|
|||||||
|
resources:
|
||||||
|
- name: foo properties: bar: baz
|
@ -0,0 +1,16 @@
|
|||||||
|
resources:
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: vm-created-by-cloud-config-{{ properties["deployment"] }}
|
||||||
|
properties:
|
||||||
|
zone: {{ properties["zone"] }}
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/{{ properties["project"] }}/zones/{{ properties["zone"] }}/machineTypes/f1-micro
|
||||||
|
disks:
|
||||||
|
- deviceName: boot
|
||||||
|
type: PERSISTENT
|
||||||
|
boot: true
|
||||||
|
autoDelete: true
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-{{ properties["deployment"] }}
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/{{ properties["project"] }}/global/networks/default
|
@ -0,0 +1,18 @@
|
|||||||
|
info:
|
||||||
|
title: Schema for a basic jinja template that includes default values
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
foo:
|
||||||
|
description: blah
|
||||||
|
type: string
|
||||||
|
zone:
|
||||||
|
type: string
|
||||||
|
default: test-zone
|
||||||
|
project:
|
||||||
|
type: string
|
||||||
|
default: test-project
|
||||||
|
deployment:
|
||||||
|
type: string
|
||||||
|
default: test-deployment
|
@ -0,0 +1,9 @@
|
|||||||
|
imports:
|
||||||
|
- path: "jinja_defaults.jinja"
|
||||||
|
- path: "jinja_defaults.jinja.schema"
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: jinja_defaults_name
|
||||||
|
type: jinja_defaults.jinja
|
||||||
|
properties:
|
||||||
|
foo: bar
|
@ -0,0 +1,29 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: vm-created-by-cloud-config-test-deployment
|
||||||
|
properties:
|
||||||
|
disks:
|
||||||
|
- autoDelete: true
|
||||||
|
boot: true
|
||||||
|
deviceName: boot
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-test-deployment
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
type: PERSISTENT
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/test-project/zones/test-zone/machineTypes/f1-micro
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/test-project/global/networks/default
|
||||||
|
zone: test-zone
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: jinja_defaults_name
|
||||||
|
properties:
|
||||||
|
deployment: test-deployment
|
||||||
|
foo: bar
|
||||||
|
project: test-project
|
||||||
|
zone: test-zone
|
||||||
|
resources:
|
||||||
|
- name: vm-created-by-cloud-config-test-deployment
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: jinja_defaults.jinja
|
@ -0,0 +1,4 @@
|
|||||||
|
Nothing here because this file should never be called.
|
||||||
|
The validation will fail before this file is used.
|
||||||
|
|
||||||
|
{{% Invalid
|
@ -0,0 +1,11 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with a required property
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
required:
|
||||||
|
- important
|
||||||
|
properties:
|
||||||
|
important:
|
||||||
|
type: string
|
||||||
|
|
@ -0,0 +1,9 @@
|
|||||||
|
imports:
|
||||||
|
- path: "jinja_missing_required.jinja"
|
||||||
|
- path: "jinja_missing_required.jinja.schema"
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: jinja_missing_required_resource_name
|
||||||
|
type: jinja_missing_required.jinja
|
||||||
|
properties:
|
||||||
|
less-important: an optional property
|
@ -0,0 +1,4 @@
|
|||||||
|
Nothing here because this file should never be called.
|
||||||
|
The validation will fail before this file is used.
|
||||||
|
|
||||||
|
{{% Invalid
|
@ -0,0 +1,22 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with several rules
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
number:
|
||||||
|
type: integer
|
||||||
|
short-string:
|
||||||
|
type: string
|
||||||
|
maxLength: 10
|
||||||
|
odd:
|
||||||
|
type: integer
|
||||||
|
not:
|
||||||
|
multipleOf: 2
|
||||||
|
abc:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- a
|
||||||
|
- b
|
||||||
|
- c
|
||||||
|
|
@ -0,0 +1,12 @@
|
|||||||
|
imports:
|
||||||
|
- path: "jinja_multiple_errors.jinja"
|
||||||
|
- path: "jinja_multiple_errors.jinja.schema"
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: jinja_multiple_errors
|
||||||
|
type: jinja_multiple_errors.jinja
|
||||||
|
properties:
|
||||||
|
number: a string
|
||||||
|
short-string: longer than 10 chars
|
||||||
|
odd: 6
|
||||||
|
abc: d
|
@ -0,0 +1,19 @@
|
|||||||
|
resources:
|
||||||
|
{% for name in ['name1', 'name2'] %}
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: {{ name }}
|
||||||
|
properties:
|
||||||
|
zone: test-zone
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/test-project/zones/test-zone/machineTypes/f1-micro
|
||||||
|
disks:
|
||||||
|
- deviceName: boot
|
||||||
|
type: PERSISTENT
|
||||||
|
boot: true
|
||||||
|
autoDelete: true
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-test-deployment
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/test-project/global/networks/default
|
||||||
|
{% endfor %}
|
||||||
|
|
@ -0,0 +1,6 @@
|
|||||||
|
imports: ["jinja_noparams.jinja"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: jinja_noparams_name
|
||||||
|
type: jinja_noparams.jinja
|
||||||
|
|
@ -0,0 +1,41 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: name1
|
||||||
|
properties:
|
||||||
|
disks:
|
||||||
|
- autoDelete: true
|
||||||
|
boot: true
|
||||||
|
deviceName: boot
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-test-deployment
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
type: PERSISTENT
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/test-project/zones/test-zone/machineTypes/f1-micro
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/test-project/global/networks/default
|
||||||
|
zone: test-zone
|
||||||
|
type: compute.v1.instance
|
||||||
|
- name: name2
|
||||||
|
properties:
|
||||||
|
disks:
|
||||||
|
- autoDelete: true
|
||||||
|
boot: true
|
||||||
|
deviceName: boot
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-test-deployment
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
type: PERSISTENT
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/test-project/zones/test-zone/machineTypes/f1-micro
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/test-project/global/networks/default
|
||||||
|
zone: test-zone
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: jinja_noparams_name
|
||||||
|
resources:
|
||||||
|
- name: name1
|
||||||
|
type: compute.v1.instance
|
||||||
|
- name: name2
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: jinja_noparams.jinja
|
@ -0,0 +1,18 @@
|
|||||||
|
resources:
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: vm-created-by-cloud-config-{{ properties["deployment"] }}
|
||||||
|
properties:
|
||||||
|
zone: {{ properties["zone"] }}
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/{{ properties["project"] }}/zones/{{ properties["zone"] }}/machineTypes/f1-micro
|
||||||
|
disks:
|
||||||
|
- deviceName: boot
|
||||||
|
type: PERSISTENT
|
||||||
|
boot: true
|
||||||
|
autoDelete: true
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-{{ properties["deployment"] }}
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/{{ properties["project"] }}/global/networks/default
|
||||||
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
|||||||
|
imports: ["jinja_template.jinja"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: jinja_template_name
|
||||||
|
type: jinja_template.jinja
|
||||||
|
properties:
|
||||||
|
zone: test-zone
|
||||||
|
project: test-project
|
||||||
|
deployment: test-deployment
|
||||||
|
|
@ -0,0 +1,28 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: vm-created-by-cloud-config-test-deployment
|
||||||
|
properties:
|
||||||
|
disks:
|
||||||
|
- autoDelete: true
|
||||||
|
boot: true
|
||||||
|
deviceName: boot
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-test-deployment
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
type: PERSISTENT
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/test-project/zones/test-zone/machineTypes/f1-micro
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/test-project/global/networks/default
|
||||||
|
zone: test-zone
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: jinja_template_name
|
||||||
|
properties:
|
||||||
|
deployment: test-deployment
|
||||||
|
project: test-project
|
||||||
|
zone: test-zone
|
||||||
|
resources:
|
||||||
|
- name: vm-created-by-cloud-config-test-deployment
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: jinja_template.jinja
|
@ -0,0 +1,18 @@
|
|||||||
|
resources:
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: vm-created-by-cloud-config-{{ env["deployment"] }}
|
||||||
|
properties:
|
||||||
|
zone: {{ properties["zone"] }}
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/{{ env["project"] }}/zones/{{ properties["zone"] }}/machineTypes/f1-micro
|
||||||
|
disks:
|
||||||
|
- deviceName: boot
|
||||||
|
type: PERSISTENT
|
||||||
|
boot: true
|
||||||
|
autoDelete: true
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-{{ env["deployment"] }}-{{ env["name"] }}-{{ env["type"] }}
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/{{ env["project"] }}/global/networks/default
|
||||||
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
|||||||
|
imports: ["jinja_template_with_env.jinja"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: jinja_template_with_env_name
|
||||||
|
type: jinja_template_with_env.jinja
|
||||||
|
properties:
|
||||||
|
zone: test-zone
|
||||||
|
|
@ -0,0 +1,26 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: vm-created-by-cloud-config-test-deployment
|
||||||
|
properties:
|
||||||
|
disks:
|
||||||
|
- autoDelete: true
|
||||||
|
boot: true
|
||||||
|
deviceName: boot
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-test-deployment-jinja_template_with_env_name-jinja_template_with_env.jinja
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
type: PERSISTENT
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/test-project/zones/test-zone/machineTypes/f1-micro
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/test-project/global/networks/default
|
||||||
|
zone: test-zone
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: jinja_template_with_env_name
|
||||||
|
properties:
|
||||||
|
zone: test-zone
|
||||||
|
resources:
|
||||||
|
- name: vm-created-by-cloud-config-test-deployment
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: jinja_template_with_env.jinja
|
@ -0,0 +1,6 @@
|
|||||||
|
{% import 'helpers/common.jinja' as common %}
|
||||||
|
resources:
|
||||||
|
- name: {{ common.GenerateMachineName("myFrontend", "prod") }}
|
||||||
|
type: compute.v1.instance
|
||||||
|
properties:
|
||||||
|
machineSize: big
|
@ -0,0 +1,5 @@
|
|||||||
|
imports: ["jinja_template_with_import.jinja", "helpers/common.jinja"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: jinja_template_with_import_name
|
||||||
|
type: jinja_template_with_import.jinja
|
@ -0,0 +1,13 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: myFrontend-prod
|
||||||
|
properties:
|
||||||
|
machineSize: big
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: jinja_template_with_import_name
|
||||||
|
resources:
|
||||||
|
- name: myFrontend-prod
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: jinja_template_with_import.jinja
|
@ -0,0 +1,7 @@
|
|||||||
|
{% import 'helpers/common.jinja' as common %}
|
||||||
|
resources:
|
||||||
|
- name: {{ common.GenerateMachineName("myFrontend", "prod") }}
|
||||||
|
type: compute.v1.instance
|
||||||
|
properties:
|
||||||
|
description: {{ imports[properties["description-file"]] }}
|
||||||
|
machineSize: big
|
@ -0,0 +1,7 @@
|
|||||||
|
imports: ["jinja_template_with_inlinedfile.jinja", "helpers/common.jinja", "description_text.txt"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: jinja_template_with_inlinedfile_name
|
||||||
|
type: jinja_template_with_inlinedfile.jinja
|
||||||
|
properties:
|
||||||
|
description-file: description_text.txt
|
@ -0,0 +1,21 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: myFrontend-prod
|
||||||
|
properties:
|
||||||
|
description: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||||
|
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
||||||
|
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
||||||
|
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
||||||
|
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat
|
||||||
|
non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||||
|
machineSize: big
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: jinja_template_with_inlinedfile_name
|
||||||
|
properties:
|
||||||
|
description-file: description_text.txt
|
||||||
|
resources:
|
||||||
|
- name: myFrontend-prod
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: jinja_template_with_inlinedfile.jinja
|
@ -0,0 +1,18 @@
|
|||||||
|
resources:
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: vm-created-by-cloud-config-{{ porcelain["deployment"] }}
|
||||||
|
properties:
|
||||||
|
zone: {{ properties["zone"] }}
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/{{ properties["project"] }}/zones/{{ properties["zone"] }}/machineTypes/f1-micro
|
||||||
|
disks:
|
||||||
|
- deviceName: boot
|
||||||
|
type: PERSISTENT
|
||||||
|
boot: true
|
||||||
|
autoDelete: true
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-{{ properties["deployment"] }}
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/{{ properties["project"] }}/global/networks/default
|
||||||
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
|||||||
|
imports: ["jinja_unresolved.jinja"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: jinja_template_name
|
||||||
|
type: jinja_unresolved.jinja
|
||||||
|
properties:
|
||||||
|
zone: test-zone
|
||||||
|
project: test-project
|
||||||
|
deployment: test-deployment
|
||||||
|
|
@ -0,0 +1,5 @@
|
|||||||
|
"""Return empty resources block."""
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateConfig(_):
|
||||||
|
return """resources:"""
|
@ -0,0 +1,6 @@
|
|||||||
|
imports:
|
||||||
|
- path: "no_properties.py"
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: test-resource
|
||||||
|
type: no_properties.py
|
@ -0,0 +1,6 @@
|
|||||||
|
config:
|
||||||
|
resources: []
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: test-resource
|
||||||
|
type: no_properties.py
|
@ -0,0 +1,6 @@
|
|||||||
|
"""Does nothing."""
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateConfig(_):
|
||||||
|
"""Returns empty string."""
|
||||||
|
return ''
|
@ -0,0 +1,6 @@
|
|||||||
|
imports:
|
||||||
|
- path: "no_resources.py"
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: test-resource
|
||||||
|
type: no_resources.py
|
@ -0,0 +1,17 @@
|
|||||||
|
resources:
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: vm-created-by-cloud-config-{{ properties["deployment"] }}
|
||||||
|
properties:
|
||||||
|
zone: {{ properties["zone"] }}
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/{{ properties["project"] }}/zones/{{ properties["zone"] }}/machineTypes/f1-micro
|
||||||
|
disks:
|
||||||
|
- deviceName: boot
|
||||||
|
type: PERSISTENT
|
||||||
|
boot: true
|
||||||
|
autoDelete: true
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-{{ properties["deployment"] }}
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/{{ properties["project"] }}/global/networks/default
|
||||||
|
|
@ -0,0 +1,37 @@
|
|||||||
|
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#% description: Creates a VM running a Salt master daemon in a Docker container.
|
||||||
|
#% parameters:
|
||||||
|
#% - name: masterAddress
|
||||||
|
#% type: string
|
||||||
|
#% description: Name of the Salt master VM.
|
||||||
|
#% required: true
|
||||||
|
#% - name: project
|
||||||
|
#% type: string
|
||||||
|
#% description: Name of the Cloud project.
|
||||||
|
#% required: true
|
||||||
|
#% - name: zone
|
||||||
|
#% type: string
|
||||||
|
#% description: Zone to create the resources in.
|
||||||
|
#% required: true
|
||||||
|
|
||||||
|
"""Generates config for a VM running a SaltStack master.
|
||||||
|
|
||||||
|
Just for fun this template is in Python, while the others in this
|
||||||
|
directory are in Jinja2.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateConfig(evaluation_context):
|
||||||
|
return """
|
||||||
|
resources:
|
||||||
|
- name: python_and_jinja_template_jinja_name
|
||||||
|
type: python_and_jinja_template.jinja
|
||||||
|
properties:
|
||||||
|
zone: %(zone)s
|
||||||
|
project: %(project)s
|
||||||
|
deployment: %(master)s
|
||||||
|
|
||||||
|
""" % {"master": evaluation_context.properties["masterAddress"],
|
||||||
|
"project": evaluation_context.properties["project"],
|
||||||
|
"zone": evaluation_context.properties["zone"]}
|
@ -0,0 +1,9 @@
|
|||||||
|
imports: ["python_and_jinja_template.jinja", "python_and_jinja_template.py"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: python_and_jinja_template_name
|
||||||
|
type: python_and_jinja_template.py
|
||||||
|
properties:
|
||||||
|
masterAddress: master-address
|
||||||
|
project: my-project
|
||||||
|
zone: my-zone
|
@ -0,0 +1,35 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: vm-created-by-cloud-config-master-address
|
||||||
|
properties:
|
||||||
|
disks:
|
||||||
|
- autoDelete: true
|
||||||
|
boot: true
|
||||||
|
deviceName: boot
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-master-address
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
type: PERSISTENT
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/my-project/zones/my-zone/machineTypes/f1-micro
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/my-project/global/networks/default
|
||||||
|
zone: my-zone
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: python_and_jinja_template_name
|
||||||
|
properties:
|
||||||
|
masterAddress: master-address
|
||||||
|
project: my-project
|
||||||
|
zone: my-zone
|
||||||
|
resources:
|
||||||
|
- name: python_and_jinja_template_jinja_name
|
||||||
|
properties:
|
||||||
|
deployment: master-address
|
||||||
|
project: my-project
|
||||||
|
zone: my-zone
|
||||||
|
resources:
|
||||||
|
- name: vm-created-by-cloud-config-master-address
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: python_and_jinja_template.jinja
|
||||||
|
type: python_and_jinja_template.py
|
@ -0,0 +1,3 @@
|
|||||||
|
"""Throws an exception."""
|
||||||
|
|
||||||
|
raise Exception
|
@ -0,0 +1,19 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with several errors
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
bad-type:
|
||||||
|
type: int
|
||||||
|
missing-cond:
|
||||||
|
type: string
|
||||||
|
exclusiveMaximum: 10
|
||||||
|
odd-string:
|
||||||
|
type: string
|
||||||
|
not:
|
||||||
|
multipleOf: 2
|
||||||
|
bad-enum:
|
||||||
|
type: string
|
||||||
|
enum: not a list
|
||||||
|
|
@ -0,0 +1,9 @@
|
|||||||
|
imports:
|
||||||
|
- path: "python_bad_schema.py"
|
||||||
|
- path: "python_bad_schema.py.schema"
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: python_bad_schema
|
||||||
|
type: python_bad_schema.py
|
||||||
|
properties:
|
||||||
|
innocent: true
|
@ -0,0 +1,14 @@
|
|||||||
|
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
"""Constructs a VM."""
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateConfig(_):
|
||||||
|
"""Generates config of a VM."""
|
||||||
|
return """
|
||||||
|
resources:
|
||||||
|
- name: myBackend
|
||||||
|
type: compute.v1.instance
|
||||||
|
properties:
|
||||||
|
machineSize: big
|
||||||
|
"""
|
@ -0,0 +1,9 @@
|
|||||||
|
imports: ["python_noparams.py"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: myFrontend
|
||||||
|
type: compute.v1.instance
|
||||||
|
properties:
|
||||||
|
machineSize: big
|
||||||
|
- name: python_noparams_name
|
||||||
|
type: python_noparams.py
|
@ -0,0 +1,19 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: myFrontend
|
||||||
|
properties:
|
||||||
|
machineSize: big
|
||||||
|
type: compute.v1.instance
|
||||||
|
- name: myBackend
|
||||||
|
properties:
|
||||||
|
machineSize: big
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: myFrontend
|
||||||
|
type: compute.v1.instance
|
||||||
|
- name: python_noparams_name
|
||||||
|
resources:
|
||||||
|
- name: myBackend
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: python_noparams.py
|
@ -0,0 +1,59 @@
|
|||||||
|
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#% description: Creates a VM running a Salt master daemon in a Docker container.
|
||||||
|
#% parameters:
|
||||||
|
#% - name: masterAddress
|
||||||
|
#% type: string
|
||||||
|
#% description: Name of the Salt master VM.
|
||||||
|
#% required: true
|
||||||
|
#% - name: project
|
||||||
|
#% type: string
|
||||||
|
#% description: Name of the Cloud project.
|
||||||
|
#% required: true
|
||||||
|
#% - name: zone
|
||||||
|
#% type: string
|
||||||
|
#% description: Zone to create the resources in.
|
||||||
|
#% required: true
|
||||||
|
|
||||||
|
"""Generates config for a VM running a SaltStack master.
|
||||||
|
|
||||||
|
Just for fun this template is in Python, while the others in this
|
||||||
|
directory are in Jinja2.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateConfig(evaluation_context):
|
||||||
|
return """
|
||||||
|
resources:
|
||||||
|
- type: compute.v1.firewall
|
||||||
|
name: %(master)s-firewall
|
||||||
|
properties:
|
||||||
|
network: https://www.googleapis.com/compute/v1/projects/%(project)s/global/networks/default
|
||||||
|
sourceRanges: [ "0.0.0.0/0" ]
|
||||||
|
allowed:
|
||||||
|
- IPProtocol: tcp
|
||||||
|
ports: [ "4505", "4506" ]
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: %(master)s
|
||||||
|
properties:
|
||||||
|
zone: %(zone)s
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/%(project)s/zones/%(zone)s/machineTypes/f1-micro
|
||||||
|
disks:
|
||||||
|
- deviceName: boot
|
||||||
|
type: PERSISTENT
|
||||||
|
boot: true
|
||||||
|
autoDelete: true
|
||||||
|
initializeParams:
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/%(project)s/global/networks/default
|
||||||
|
accessConfigs:
|
||||||
|
- name: External NAT
|
||||||
|
type: ONE_TO_ONE_NAT
|
||||||
|
metadata:
|
||||||
|
items:
|
||||||
|
- key: startup-script
|
||||||
|
value: startup-script-value
|
||||||
|
""" % {"master": evaluation_context.properties["masterAddress"],
|
||||||
|
"project": evaluation_context.env["project"],
|
||||||
|
"zone": evaluation_context.properties["zone"]}
|
@ -0,0 +1,14 @@
|
|||||||
|
info:
|
||||||
|
title: A simple python template that has a schema.
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
masterAddress:
|
||||||
|
type: string
|
||||||
|
default: slave-address
|
||||||
|
description: masterAddress
|
||||||
|
zone:
|
||||||
|
type: string
|
||||||
|
default: not-test-zone
|
||||||
|
description: zone
|
@ -0,0 +1,10 @@
|
|||||||
|
imports:
|
||||||
|
- path: "python_schema.py"
|
||||||
|
- path: "python_schema.py.schema"
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: python_schema
|
||||||
|
type: python_schema.py
|
||||||
|
properties:
|
||||||
|
masterAddress: master-address
|
||||||
|
zone: my-zone
|
@ -0,0 +1,46 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: master-address-firewall
|
||||||
|
properties:
|
||||||
|
allowed:
|
||||||
|
- IPProtocol: tcp
|
||||||
|
ports:
|
||||||
|
- '4505'
|
||||||
|
- '4506'
|
||||||
|
network: https://www.googleapis.com/compute/v1/projects/my-project/global/networks/default
|
||||||
|
sourceRanges:
|
||||||
|
- 0.0.0.0/0
|
||||||
|
type: compute.v1.firewall
|
||||||
|
- name: master-address
|
||||||
|
properties:
|
||||||
|
disks:
|
||||||
|
- autoDelete: true
|
||||||
|
boot: true
|
||||||
|
deviceName: boot
|
||||||
|
initializeParams:
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
type: PERSISTENT
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/my-project/zones/my-zone/machineTypes/f1-micro
|
||||||
|
metadata:
|
||||||
|
items:
|
||||||
|
- key: startup-script
|
||||||
|
value: startup-script-value
|
||||||
|
networkInterfaces:
|
||||||
|
- accessConfigs:
|
||||||
|
- name: External NAT
|
||||||
|
type: ONE_TO_ONE_NAT
|
||||||
|
network: https://www.googleapis.com/compute/v1/projects/my-project/global/networks/default
|
||||||
|
zone: my-zone
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: python_schema
|
||||||
|
properties:
|
||||||
|
masterAddress: master-address
|
||||||
|
zone: my-zone
|
||||||
|
resources:
|
||||||
|
- name: master-address-firewall
|
||||||
|
type: compute.v1.firewall
|
||||||
|
- name: master-address
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: python_schema.py
|
@ -0,0 +1,59 @@
|
|||||||
|
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#% description: Creates a VM running a Salt master daemon in a Docker container.
|
||||||
|
#% parameters:
|
||||||
|
#% - name: masterAddress
|
||||||
|
#% type: string
|
||||||
|
#% description: Name of the Salt master VM.
|
||||||
|
#% required: true
|
||||||
|
#% - name: project
|
||||||
|
#% type: string
|
||||||
|
#% description: Name of the Cloud project.
|
||||||
|
#% required: true
|
||||||
|
#% - name: zone
|
||||||
|
#% type: string
|
||||||
|
#% description: Zone to create the resources in.
|
||||||
|
#% required: true
|
||||||
|
|
||||||
|
"""Generates config for a VM running a SaltStack master.
|
||||||
|
|
||||||
|
Just for fun this template is in Python, while the others in this
|
||||||
|
directory are in Jinja2.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateConfig(evaluation_context):
|
||||||
|
return """
|
||||||
|
resources:
|
||||||
|
- type: compute.v1.firewall
|
||||||
|
name: %(master)s-firewall
|
||||||
|
properties:
|
||||||
|
network: https://www.googleapis.com/compute/v1/projects/%(project)s/global/networks/default
|
||||||
|
sourceRanges: [ "0.0.0.0/0" ]
|
||||||
|
allowed:
|
||||||
|
- IPProtocol: tcp
|
||||||
|
ports: [ "4505", "4506" ]
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: %(master)s
|
||||||
|
properties:
|
||||||
|
zone: %(zone)s
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/%(project)s/zones/%(zone)s/machineTypes/f1-micro
|
||||||
|
disks:
|
||||||
|
- deviceName: boot
|
||||||
|
type: PERSISTENT
|
||||||
|
boot: true
|
||||||
|
autoDelete: true
|
||||||
|
initializeParams:
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/%(project)s/global/networks/default
|
||||||
|
accessConfigs:
|
||||||
|
- name: External NAT
|
||||||
|
type: ONE_TO_ONE_NAT
|
||||||
|
metadata:
|
||||||
|
items:
|
||||||
|
- key: startup-script
|
||||||
|
value: startup-script-value
|
||||||
|
""" % {"master": evaluation_context.properties["masterAddress"],
|
||||||
|
"project": evaluation_context.properties["project"],
|
||||||
|
"zone": evaluation_context.properties["zone"]}
|
@ -0,0 +1,9 @@
|
|||||||
|
imports: ["python_template.py"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: python_template_name
|
||||||
|
type: python_template.py
|
||||||
|
properties:
|
||||||
|
masterAddress: master-address
|
||||||
|
project: my-project
|
||||||
|
zone: my-zone
|
@ -0,0 +1,47 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: master-address-firewall
|
||||||
|
properties:
|
||||||
|
allowed:
|
||||||
|
- IPProtocol: tcp
|
||||||
|
ports:
|
||||||
|
- '4505'
|
||||||
|
- '4506'
|
||||||
|
network: https://www.googleapis.com/compute/v1/projects/my-project/global/networks/default
|
||||||
|
sourceRanges:
|
||||||
|
- 0.0.0.0/0
|
||||||
|
type: compute.v1.firewall
|
||||||
|
- name: master-address
|
||||||
|
properties:
|
||||||
|
disks:
|
||||||
|
- autoDelete: true
|
||||||
|
boot: true
|
||||||
|
deviceName: boot
|
||||||
|
initializeParams:
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
type: PERSISTENT
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/my-project/zones/my-zone/machineTypes/f1-micro
|
||||||
|
metadata:
|
||||||
|
items:
|
||||||
|
- key: startup-script
|
||||||
|
value: startup-script-value
|
||||||
|
networkInterfaces:
|
||||||
|
- accessConfigs:
|
||||||
|
- name: External NAT
|
||||||
|
type: ONE_TO_ONE_NAT
|
||||||
|
network: https://www.googleapis.com/compute/v1/projects/my-project/global/networks/default
|
||||||
|
zone: my-zone
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: python_template_name
|
||||||
|
properties:
|
||||||
|
masterAddress: master-address
|
||||||
|
project: my-project
|
||||||
|
zone: my-zone
|
||||||
|
resources:
|
||||||
|
- name: master-address-firewall
|
||||||
|
type: compute.v1.firewall
|
||||||
|
- name: master-address
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: python_template.py
|
@ -0,0 +1,59 @@
|
|||||||
|
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#% description: Creates a VM running a Salt master daemon in a Docker container.
|
||||||
|
#% parameters:
|
||||||
|
#% - name: masterAddress
|
||||||
|
#% type: string
|
||||||
|
#% description: Name of the Salt master VM.
|
||||||
|
#% required: true
|
||||||
|
#% - name: project
|
||||||
|
#% type: string
|
||||||
|
#% description: Name of the Cloud project.
|
||||||
|
#% required: true
|
||||||
|
#% - name: zone
|
||||||
|
#% type: string
|
||||||
|
#% description: Zone to create the resources in.
|
||||||
|
#% required: true
|
||||||
|
|
||||||
|
"""Generates config for a VM running a SaltStack master.
|
||||||
|
|
||||||
|
Just for fun this template is in Python, while the others in this
|
||||||
|
directory are in Jinja2.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateConfig(evaluation_context):
|
||||||
|
return """
|
||||||
|
resources:
|
||||||
|
- type: compute.v1.firewall
|
||||||
|
name: %(master)s-firewall
|
||||||
|
properties:
|
||||||
|
network: https://www.googleapis.com/compute/v1/projects/%(project)s/global/networks/default
|
||||||
|
sourceRanges: [ "0.0.0.0/0" ]
|
||||||
|
allowed:
|
||||||
|
- IPProtocol: tcp
|
||||||
|
ports: [ "4505", "4506" ]
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: %(master)s
|
||||||
|
properties:
|
||||||
|
zone: %(zone)s
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/%(project)s/zones/%(zone)s/machineTypes/f1-micro
|
||||||
|
disks:
|
||||||
|
- deviceName: boot
|
||||||
|
type: PERSISTENT
|
||||||
|
boot: true
|
||||||
|
autoDelete: true
|
||||||
|
initializeParams:
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/%(project)s/global/networks/default
|
||||||
|
accessConfigs:
|
||||||
|
- name: External NAT
|
||||||
|
type: ONE_TO_ONE_NAT
|
||||||
|
metadata:
|
||||||
|
items:
|
||||||
|
- key: startup-script
|
||||||
|
value: startup-script-value
|
||||||
|
""" % {"master": evaluation_context.properties["masterAddress"],
|
||||||
|
"project": evaluation_context.env["project"],
|
||||||
|
"zone": evaluation_context.properties["zone"]}
|
@ -0,0 +1,8 @@
|
|||||||
|
imports: ["python_template_with_env.py"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: python_template_with_env_name
|
||||||
|
type: python_template_with_env.py
|
||||||
|
properties:
|
||||||
|
masterAddress: master-address
|
||||||
|
zone: my-zone
|
@ -0,0 +1,46 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: master-address-firewall
|
||||||
|
properties:
|
||||||
|
allowed:
|
||||||
|
- IPProtocol: tcp
|
||||||
|
ports:
|
||||||
|
- '4505'
|
||||||
|
- '4506'
|
||||||
|
network: https://www.googleapis.com/compute/v1/projects/my-project/global/networks/default
|
||||||
|
sourceRanges:
|
||||||
|
- 0.0.0.0/0
|
||||||
|
type: compute.v1.firewall
|
||||||
|
- name: master-address
|
||||||
|
properties:
|
||||||
|
disks:
|
||||||
|
- autoDelete: true
|
||||||
|
boot: true
|
||||||
|
deviceName: boot
|
||||||
|
initializeParams:
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
type: PERSISTENT
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/my-project/zones/my-zone/machineTypes/f1-micro
|
||||||
|
metadata:
|
||||||
|
items:
|
||||||
|
- key: startup-script
|
||||||
|
value: startup-script-value
|
||||||
|
networkInterfaces:
|
||||||
|
- accessConfigs:
|
||||||
|
- name: External NAT
|
||||||
|
type: ONE_TO_ONE_NAT
|
||||||
|
network: https://www.googleapis.com/compute/v1/projects/my-project/global/networks/default
|
||||||
|
zone: my-zone
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: python_template_with_env_name
|
||||||
|
properties:
|
||||||
|
masterAddress: master-address
|
||||||
|
zone: my-zone
|
||||||
|
resources:
|
||||||
|
- name: master-address-firewall
|
||||||
|
type: compute.v1.firewall
|
||||||
|
- name: master-address
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: python_template_with_env.py
|
@ -0,0 +1,20 @@
|
|||||||
|
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
"""Constructs a VM."""
|
||||||
|
import json
|
||||||
|
|
||||||
|
import helpers.common
|
||||||
|
import helpers.extra.common2
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateConfig(_):
|
||||||
|
"""Generates config of a VM."""
|
||||||
|
return """
|
||||||
|
resources:
|
||||||
|
- name: %s
|
||||||
|
type: compute.v1.instance
|
||||||
|
properties:
|
||||||
|
machineSize: %s
|
||||||
|
""" % (helpers.common.GenerateMachineName(
|
||||||
|
json.dumps('myFrontend').strip('"'), 'prod'),
|
||||||
|
helpers.extra.common2.GenerateMachineSize())
|
@ -0,0 +1,5 @@
|
|||||||
|
imports: ["python_template_with_import.py", "helpers/common.py", "helpers/common2.py", "helpers/__init__.py"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: python_template_with_import_name
|
||||||
|
type: python_template_with_import.py
|
@ -0,0 +1,13 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: myFrontend-prod
|
||||||
|
properties:
|
||||||
|
machineSize: big
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: python_template_with_import_name
|
||||||
|
resources:
|
||||||
|
- name: myFrontend-prod
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: python_template_with_import.py
|
@ -0,0 +1,22 @@
|
|||||||
|
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
"""Constructs a VM."""
|
||||||
|
|
||||||
|
# Verify that both ways of hierarchical imports work.
|
||||||
|
from helpers import common
|
||||||
|
import helpers.extra.common2
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateConfig(evaluation_context):
|
||||||
|
"""Generates config of a VM."""
|
||||||
|
return """
|
||||||
|
resources:
|
||||||
|
- name: %s
|
||||||
|
type: compute.v1.instance
|
||||||
|
properties:
|
||||||
|
description: %s
|
||||||
|
machineSize: %s
|
||||||
|
""" % (common.GenerateMachineName("myFrontend", "prod"),
|
||||||
|
evaluation_context.imports[
|
||||||
|
evaluation_context.properties["description-file"]],
|
||||||
|
helpers.extra.common2.GenerateMachineSize())
|
@ -0,0 +1,7 @@
|
|||||||
|
imports: ["python_template_with_inlinedfile.py", "helpers/common.py", "helpers/common2.py", "helpers/__init__.py", "description_text.txt"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: python_template_with_inlinedfile_name
|
||||||
|
type: python_template_with_inlinedfile.py
|
||||||
|
properties:
|
||||||
|
description-file: description_text.txt
|
@ -0,0 +1,21 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: myFrontend-prod
|
||||||
|
properties:
|
||||||
|
description: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||||
|
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
||||||
|
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
||||||
|
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
||||||
|
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat
|
||||||
|
non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||||
|
machineSize: big
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: python_template_with_inlinedfile_name
|
||||||
|
properties:
|
||||||
|
description-file: description_text.txt
|
||||||
|
resources:
|
||||||
|
- name: myFrontend-prod
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: python_template_with_inlinedfile.py
|
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
"""A python script that raise exceptions.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateConfig(unused_context):
|
||||||
|
raise NameError('No file found')
|
@ -0,0 +1,9 @@
|
|||||||
|
imports: ["python_with_exception.py"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: python_with_exception_name
|
||||||
|
type: python_with_exception.py
|
||||||
|
properties:
|
||||||
|
masterAddress: master-address
|
||||||
|
project: my-project
|
||||||
|
zone: my-zone
|
@ -0,0 +1,18 @@
|
|||||||
|
resources:
|
||||||
|
- type: compute.v1.instance
|
||||||
|
name: vm-created-by-cloud-config-{{ params["deployment"] }}
|
||||||
|
properties:
|
||||||
|
zone: test-zone
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/test-project/zones/test-zone/machineTypes/f1-micro
|
||||||
|
disks:
|
||||||
|
- deviceName: boot
|
||||||
|
type: PERSISTENT
|
||||||
|
boot: true
|
||||||
|
autoDelete: true
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-test-deployment
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/test-project/global/networks/default
|
||||||
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: vm-created-by-cloud-config-{{ params["deployment"] }}
|
||||||
|
properties:
|
||||||
|
disks:
|
||||||
|
- autoDelete: true
|
||||||
|
boot: true
|
||||||
|
deviceName: boot
|
||||||
|
initializeParams:
|
||||||
|
diskName: disk-created-by-cloud-config-test-deployment
|
||||||
|
sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140619
|
||||||
|
type: PERSISTENT
|
||||||
|
machineType: https://www.googleapis.com/compute/v1/projects/test-project/zones/test-zone/machineTypes/f1-micro
|
||||||
|
networkInterfaces:
|
||||||
|
- network: https://www.googleapis.com/compute/v1/projects/test-project/global/networks/default
|
||||||
|
zone: test-zone
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: vm-created-by-cloud-config-{{ params["deployment"] }}
|
||||||
|
type: compute.v1.instance
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue