mirror of https://github.com/helm/helm
Merge 5f7101c536
into 5343c35b7e
commit
f150a8391b
@ -0,0 +1,236 @@
|
|||||||
|
######################################################################
|
||||||
|
# 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.
|
||||||
|
######################################################################
|
||||||
|
"""Util to handle references during expansion."""
|
||||||
|
|
||||||
|
import jsonpath
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
# Generally the regex is for this pattern: $(ref.NAME.PATH)
|
||||||
|
# NOTE: This will find matches when a reference is present, but the 2nd group
|
||||||
|
# does NOT necessarily match the 'path' in the reference.
|
||||||
|
REF_PATTERN = re.compile(r'\$\(ref\.(.*?)\.(.*)\)')
|
||||||
|
|
||||||
|
# Regex for the beginning of a reference.
|
||||||
|
REF_PREFIX_PATTERN = re.compile(r'\$\(ref\.')
|
||||||
|
|
||||||
|
|
||||||
|
def HasReference(string):
|
||||||
|
"""Returns true if the string contains a reference.
|
||||||
|
|
||||||
|
We are only looking for the first part of a reference, from there we assume
|
||||||
|
the user meant to use a reference, and will fail at a later point if no
|
||||||
|
complete reference is found.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
string: The string to parse to see if it contains '$(ref.'.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if there is at least one reference inside the string.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ExpansionReferenceError: If we see '$(ref.' but do not find a complete
|
||||||
|
reference, we raise this error.
|
||||||
|
"""
|
||||||
|
return ReferenceMatcher(string).FindReference()
|
||||||
|
|
||||||
|
|
||||||
|
def _BuildReference(name, path):
|
||||||
|
"""Takes name and path and returns '$(ref.name.path)'.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: String, name of the resource being referenced.
|
||||||
|
path: String, jsonPath to the value being referenced.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
String, the complete reference string in the expected format.
|
||||||
|
"""
|
||||||
|
return '$(ref.%s.%s)' % (name, path)
|
||||||
|
|
||||||
|
|
||||||
|
def _ExtractWithJsonPath(ref_obj, name, path, raise_exception=True):
|
||||||
|
"""Given a path and an object, use jsonpath to extract the value.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ref_obj: Dict obj, the thing being referenced.
|
||||||
|
name: Name of the resource being referenced, for the error message.
|
||||||
|
path: Path to follow on the ref_obj to get the desired value.
|
||||||
|
raise_exception: boolean, set to False and this function will return None
|
||||||
|
if no value was found at the path instead of throwing an exception.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Either the value found at the path, or if raise_exception=False, None.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ExpansionReferenceError: if there was a error when evaluation the path, or
|
||||||
|
no value was found.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
result = jsonpath.jsonpath(ref_obj, path)
|
||||||
|
# jsonpath should either return a list or False
|
||||||
|
if not isinstance(result, list):
|
||||||
|
if raise_exception:
|
||||||
|
raise Exception('No value found.')
|
||||||
|
return None
|
||||||
|
# If jsonpath returns a list of a single item, it is lying.
|
||||||
|
# It really found that item, and put it in a list.
|
||||||
|
# If the reference is to a list, jsonpath will return a list of a list.
|
||||||
|
if len(result) == 1:
|
||||||
|
return result[0]
|
||||||
|
# But if jsonpath returns a list with multiple elements, the path involved
|
||||||
|
# wildcards, and the user expects a list of results.
|
||||||
|
return result
|
||||||
|
# This will usually be an IndexOutOfBounds error, but not always...
|
||||||
|
except Exception as e: # pylint: disable=broad-except
|
||||||
|
if raise_exception:
|
||||||
|
raise ExpansionReferenceError(_BuildReference(name, path), e.message)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def PopulateReferences(node, output_map):
|
||||||
|
return _TraverseNode(node, None, output_map)
|
||||||
|
|
||||||
|
|
||||||
|
def _TraverseNode(node, list_references=None, output_map=None):
|
||||||
|
"""Traverse a dict/list/element to find and resolve references.
|
||||||
|
|
||||||
|
Same as DocumentReferenceHandler.java. This function traverses a dictionary
|
||||||
|
that can contain dicts, lists, and elements.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
node: Object to traverse: dict, list, or string
|
||||||
|
list_references: If present, we will append all references we find to it.
|
||||||
|
References will be in the form of a (name, path) tuple.
|
||||||
|
output_map: Map of resource name to map of output object name to output
|
||||||
|
value. If present, we will replace references with the values they
|
||||||
|
reference.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The node. If we were provided an output_map, we'll replace references with
|
||||||
|
the value they reference.
|
||||||
|
"""
|
||||||
|
if isinstance(node, dict):
|
||||||
|
for key in node:
|
||||||
|
node[key] = _TraverseNode(node[key], list_references, output_map)
|
||||||
|
elif isinstance(node, list):
|
||||||
|
for i in range(len(node)):
|
||||||
|
node[i] = _TraverseNode(node[i], list_references, output_map)
|
||||||
|
elif isinstance(node, str):
|
||||||
|
rm = ReferenceMatcher(node)
|
||||||
|
while rm.FindReference():
|
||||||
|
if list_references is not None:
|
||||||
|
list_references.append((rm.name, rm.path))
|
||||||
|
if output_map is not None:
|
||||||
|
if rm.name not in output_map:
|
||||||
|
continue
|
||||||
|
# It is possible that an output value and real resource share a name.
|
||||||
|
# In this case, a path could be valid for the real resource but not the
|
||||||
|
# output. So we don't fail, we let the reference exists as is.
|
||||||
|
value = _ExtractWithJsonPath(output_map[rm.name], rm.name, rm.path,
|
||||||
|
raise_exception=True)
|
||||||
|
if value is not None:
|
||||||
|
node = node.replace(_BuildReference(rm.name, rm.path), value)
|
||||||
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
class ReferenceMatcher(object):
|
||||||
|
"""Finds and extracts references from strings.
|
||||||
|
|
||||||
|
Same as DocumentReferenceHandler.java. This class is meant to be similar to
|
||||||
|
the re2 matcher class, but specifically tuned for references.
|
||||||
|
"""
|
||||||
|
content = None
|
||||||
|
name = None
|
||||||
|
path = None
|
||||||
|
|
||||||
|
def __init__(self, content):
|
||||||
|
self.content = content
|
||||||
|
|
||||||
|
def FindReference(self):
|
||||||
|
"""Returns True if the string contains a reference and saves it.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if the content still contains a reference. If so, it also updates the
|
||||||
|
name and path values to that of the most recently found reference. At the
|
||||||
|
same time it moves the pointer in the content forward to be ready to find
|
||||||
|
the next reference.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ExpansionReferenceError: If we see '$(ref.' but do not find a complete
|
||||||
|
reference, we raise this error.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# First see if the content contains '$(ref.'
|
||||||
|
if not REF_PREFIX_PATTERN.search(self.content):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If so, then we say there is a reference here.
|
||||||
|
# Next make sure we find NAME and some PATH with the close paren
|
||||||
|
match = REF_PATTERN.search(self.content)
|
||||||
|
if not match:
|
||||||
|
# Has '$(ref.' but not '$(ref.NAME.PATH)'
|
||||||
|
raise ExpansionReferenceError(self.content, 'Malformed reference.')
|
||||||
|
|
||||||
|
# The regex matcher can only tell us that a complete reference exists.
|
||||||
|
# We need to count parentheses to find the end of the reference.
|
||||||
|
# Consider "$(ref.NAME.path())())" which is a string containing a reference
|
||||||
|
# and ending with "())"
|
||||||
|
|
||||||
|
open_group = 1 # Count the first '(' in '$(ref...'
|
||||||
|
end_ref = 0 # To hold the position of the end of the reference
|
||||||
|
end_name = match.end(1) # The position of the end of the name
|
||||||
|
|
||||||
|
# Iterate through the path until we find the matching close paren to the
|
||||||
|
# open paren that started the reference.
|
||||||
|
for i in xrange(end_name, len(self.content)):
|
||||||
|
c = self.content[i]
|
||||||
|
if c == '(':
|
||||||
|
open_group += 1
|
||||||
|
elif c == ')':
|
||||||
|
open_group -= 1
|
||||||
|
|
||||||
|
# Once we have matched all of our open parens, we have found the end.
|
||||||
|
if open_group == 0:
|
||||||
|
end_ref = i
|
||||||
|
break
|
||||||
|
|
||||||
|
if open_group != 0:
|
||||||
|
# There are unmatched parens.
|
||||||
|
raise ExpansionReferenceError(self.content, 'Malformed reference.')
|
||||||
|
|
||||||
|
# Save the name
|
||||||
|
self.name = match.group(1)
|
||||||
|
# Skip the period after name, and save the path
|
||||||
|
self.path = self.content[end_name + 1: end_ref]
|
||||||
|
|
||||||
|
# Move the content forward to be ready to find the next reference
|
||||||
|
self.content = self.content[end_ref:]
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class ExpansionReferenceError(Exception):
|
||||||
|
"""Exception raised when jsonPath cannot find the referenced value.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
reference: the reference processed that results in the error
|
||||||
|
message: the detailed message of the error
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, reference, message):
|
||||||
|
self.reference = reference
|
||||||
|
self.message = message + ' Reference: ' + str(reference)
|
||||||
|
super(ExpansionReferenceError, self).__init__(self.message)
|
@ -0,0 +1,277 @@
|
|||||||
|
######################################################################
|
||||||
|
# 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 references library."""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from references import _ExtractWithJsonPath
|
||||||
|
from references import _TraverseNode
|
||||||
|
from references import ExpansionReferenceError
|
||||||
|
from references import HasReference
|
||||||
|
from references import ReferenceMatcher
|
||||||
|
|
||||||
|
|
||||||
|
class ReferencesTest(unittest.TestCase):
|
||||||
|
|
||||||
|
# Tests for HasReference
|
||||||
|
|
||||||
|
def testBasicReference(self):
|
||||||
|
self.assertTrue(HasReference('$(ref.name.path)'))
|
||||||
|
|
||||||
|
def testEmbeddedReference(self):
|
||||||
|
self.assertTrue(HasReference('contains reference $(ref.name.path) EOM'))
|
||||||
|
|
||||||
|
def testComplexPath(self):
|
||||||
|
self.assertTrue(HasReference('$(ref.name.path[0].to().very.cool["thing"])'))
|
||||||
|
|
||||||
|
def testComplexName(self):
|
||||||
|
self.assertTrue(HasReference('$(ref.name-is-superCool.path)'))
|
||||||
|
|
||||||
|
def testMissingGroupClose(self):
|
||||||
|
try:
|
||||||
|
HasReference('almost a reference $(ref.name.path')
|
||||||
|
self.Fail('Expected Reference exception')
|
||||||
|
except ExpansionReferenceError as e:
|
||||||
|
self.assertTrue('Malformed' in e.message)
|
||||||
|
self.assertTrue('$(ref.name.path' in e.message)
|
||||||
|
|
||||||
|
def testMissingGroupOpen(self):
|
||||||
|
# Not close enough to find a match
|
||||||
|
self.assertFalse(HasReference('almost a reference $ref.name.path)'))
|
||||||
|
|
||||||
|
def testMissingPath(self):
|
||||||
|
try:
|
||||||
|
self.assertTrue(HasReference('almost a reference $(ref.name)'))
|
||||||
|
self.Fail('Expected Reference exception')
|
||||||
|
except ExpansionReferenceError as e:
|
||||||
|
self.assertTrue('Malformed' in e.message)
|
||||||
|
self.assertTrue('$(ref.name)' in e.message)
|
||||||
|
|
||||||
|
def testUnmatchedParens(self):
|
||||||
|
try:
|
||||||
|
self.assertTrue(HasReference('almost a reference $(ref.name.path()'))
|
||||||
|
self.Fail('Expected Reference exception')
|
||||||
|
except ExpansionReferenceError as e:
|
||||||
|
self.assertTrue('Malformed' in e.message)
|
||||||
|
self.assertTrue('$(ref.name.path()' in e.message)
|
||||||
|
|
||||||
|
def testMissingRef(self):
|
||||||
|
self.assertFalse(HasReference('almost a reference $(name.path)'))
|
||||||
|
|
||||||
|
# Test for ReferenceMatcher
|
||||||
|
|
||||||
|
def testMatchBasic(self):
|
||||||
|
matcher = ReferenceMatcher('$(ref.NAME.PATH)')
|
||||||
|
self.assertTrue(matcher.FindReference())
|
||||||
|
self.assertEquals(matcher.name, 'NAME')
|
||||||
|
self.assertEquals(matcher.path, 'PATH')
|
||||||
|
self.assertFalse(matcher.FindReference())
|
||||||
|
|
||||||
|
def testMatchComplexPath(self):
|
||||||
|
matcher = ReferenceMatcher('inside a $(ref.NAME.path[?(@.price<10)].val)!')
|
||||||
|
self.assertTrue(matcher.FindReference())
|
||||||
|
self.assertEquals(matcher.name, 'NAME')
|
||||||
|
self.assertEquals(matcher.path, 'path[?(@.price<10)].val')
|
||||||
|
self.assertFalse(matcher.FindReference())
|
||||||
|
|
||||||
|
def testMatchInString(self):
|
||||||
|
matcher = ReferenceMatcher('inside a $(ref.NAME.PATH) string')
|
||||||
|
self.assertTrue(matcher.FindReference())
|
||||||
|
self.assertEquals(matcher.name, 'NAME')
|
||||||
|
self.assertEquals(matcher.path, 'PATH')
|
||||||
|
self.assertFalse(matcher.FindReference())
|
||||||
|
|
||||||
|
def testMatchTwo(self):
|
||||||
|
matcher = ReferenceMatcher('two $(ref.NAME1.PATH1) inside '
|
||||||
|
'a $(ref.NAME2.PATH2) string')
|
||||||
|
self.assertTrue(matcher.FindReference())
|
||||||
|
self.assertEquals(matcher.name, 'NAME1')
|
||||||
|
self.assertEquals(matcher.path, 'PATH1')
|
||||||
|
self.assertTrue(matcher.FindReference())
|
||||||
|
self.assertEquals(matcher.name, 'NAME2')
|
||||||
|
self.assertEquals(matcher.path, 'PATH2')
|
||||||
|
self.assertFalse(matcher.FindReference())
|
||||||
|
|
||||||
|
def testMatchGoodAndBad(self):
|
||||||
|
matcher = ReferenceMatcher('$(ref.NAME.PATH) good and $(ref.NAME.PATH bad')
|
||||||
|
self.assertTrue(matcher.FindReference())
|
||||||
|
self.assertEquals(matcher.name, 'NAME')
|
||||||
|
self.assertEquals(matcher.path, 'PATH')
|
||||||
|
try:
|
||||||
|
matcher.FindReference()
|
||||||
|
self.Fail('Expected Reference exception')
|
||||||
|
except ExpansionReferenceError as e:
|
||||||
|
self.assertTrue('Malformed' in e.message)
|
||||||
|
self.assertTrue('$(ref.NAME.PATH bad' in e.message)
|
||||||
|
|
||||||
|
def testAlmostMatch(self):
|
||||||
|
matcher = ReferenceMatcher('inside a $(ref.NAME.PATH with no close paren')
|
||||||
|
try:
|
||||||
|
matcher.FindReference()
|
||||||
|
self.Fail('Expected Reference exception')
|
||||||
|
except ExpansionReferenceError as e:
|
||||||
|
self.assertTrue('Malformed' in e.message)
|
||||||
|
self.assertTrue('$(ref.NAME.PATH ' in e.message)
|
||||||
|
|
||||||
|
# Tests for _TraverseNode
|
||||||
|
|
||||||
|
def testFindAllReferences(self):
|
||||||
|
ref_list = []
|
||||||
|
node = {'a': ['a $(ref.name1.path1) string',
|
||||||
|
'$(ref.name2.path2)',
|
||||||
|
123,],
|
||||||
|
'b': {'a1': 'another $(ref.name3.path3) string',},
|
||||||
|
'c': 'yet another $(ref.name4.path4) string',}
|
||||||
|
|
||||||
|
traversed_node = _TraverseNode(node, ref_list, None)
|
||||||
|
|
||||||
|
self.assertEquals(node, traversed_node)
|
||||||
|
self.assertEquals(4, len(ref_list))
|
||||||
|
self.assertTrue(('name1', 'path1') in ref_list)
|
||||||
|
self.assertTrue(('name2', 'path2') in ref_list)
|
||||||
|
self.assertTrue(('name3', 'path3') in ref_list)
|
||||||
|
self.assertTrue(('name4', 'path4') in ref_list)
|
||||||
|
|
||||||
|
def testReplaceReference(self):
|
||||||
|
ref_map = {'name1': {'path1a': '1a',
|
||||||
|
'path1b': '1b',},
|
||||||
|
'name2': {'path2a': '2a',},}
|
||||||
|
|
||||||
|
node = {'a': ['a $(ref.name1.path1a) string',
|
||||||
|
'$(ref.name2.path2a)',
|
||||||
|
123,],
|
||||||
|
'b': {'a1': 'another $(ref.name1.path1b) string',},
|
||||||
|
'c': 'yet another $(ref.name2.path2a)$(ref.name2.path2a) string',}
|
||||||
|
|
||||||
|
expt = {'a': ['a 1a string',
|
||||||
|
'2a',
|
||||||
|
123,],
|
||||||
|
'b': {'a1': 'another 1b string',},
|
||||||
|
'c': 'yet another 2a2a string',}
|
||||||
|
|
||||||
|
self.assertEquals(expt, _TraverseNode(node, None, ref_map))
|
||||||
|
|
||||||
|
def testReplaceNotFoundReferencePath(self):
|
||||||
|
ref_map = {'name1': {'path1a': '1a',},}
|
||||||
|
|
||||||
|
node = {'a': ['a $(ref.name1.path1a) string',
|
||||||
|
'b $(ref.name1.path1b)',
|
||||||
|
'c $(ref.name2.path2a)'],}
|
||||||
|
|
||||||
|
try:
|
||||||
|
_TraverseNode(node, None, ref_map)
|
||||||
|
self.Fail('Expected ExpansionReferenceError')
|
||||||
|
except ExpansionReferenceError as e:
|
||||||
|
self.assertTrue('No value found' in e.message)
|
||||||
|
self.assertTrue('$(ref.name1.path1b)' in e.message)
|
||||||
|
|
||||||
|
def testReplaceNotFoundReferenceName(self):
|
||||||
|
ref_map = {'name1': {'path1a': '1a',},}
|
||||||
|
|
||||||
|
node = {'a': ['a $(ref.name1.path1a) string',
|
||||||
|
'c $(ref.name2.path2a)'],}
|
||||||
|
|
||||||
|
expt = {'a': ['a 1a string',
|
||||||
|
'c $(ref.name2.path2a)'],}
|
||||||
|
|
||||||
|
self.assertEquals(expt, _TraverseNode(node, None, ref_map))
|
||||||
|
|
||||||
|
# Tests for _ExtractWithJsonPath
|
||||||
|
|
||||||
|
def testExtractFromList(self):
|
||||||
|
ref_map = {'a': ['one', 'two', 'three',],}
|
||||||
|
|
||||||
|
self.assertEquals('two', _ExtractWithJsonPath(ref_map, 'foo', 'a[1]'))
|
||||||
|
|
||||||
|
def testExtractFromMap(self):
|
||||||
|
ref_map = {'a': {'b': {'c': 'd'}}}
|
||||||
|
|
||||||
|
self.assertEquals('d', _ExtractWithJsonPath(ref_map, 'foo', 'a.b.c'))
|
||||||
|
|
||||||
|
def testExtractList(self):
|
||||||
|
ref_map = {'a': ['one', 'two', 'three',],}
|
||||||
|
|
||||||
|
self.assertEquals(['one', 'two', 'three',],
|
||||||
|
_ExtractWithJsonPath(ref_map, 'foo', 'a'))
|
||||||
|
|
||||||
|
def testExtractListOfSingleItem(self):
|
||||||
|
ref_map = {'a': ['one'],}
|
||||||
|
|
||||||
|
self.assertEquals(['one'], _ExtractWithJsonPath(ref_map, 'foo', 'a'))
|
||||||
|
|
||||||
|
def testExtractListWithWildcard(self):
|
||||||
|
ref_map = {'a': ['one', 'two', 'three',],}
|
||||||
|
|
||||||
|
self.assertEquals(['one', 'two', 'three',],
|
||||||
|
_ExtractWithJsonPath(ref_map, 'foo', 'a[*]'))
|
||||||
|
|
||||||
|
def testExtractMap(self):
|
||||||
|
ref_map = {'a': {'b': {'c': 'd'}}}
|
||||||
|
|
||||||
|
self.assertEquals({'c': 'd'}, _ExtractWithJsonPath(ref_map, 'foo', 'a.b'))
|
||||||
|
|
||||||
|
def testExtractFalse(self):
|
||||||
|
ref_map = {'a': False}
|
||||||
|
|
||||||
|
self.assertEquals(False, _ExtractWithJsonPath(ref_map, 'foo', 'a'))
|
||||||
|
|
||||||
|
def testExtractFail_BadIndex(self):
|
||||||
|
ref_map = {'a': ['one', 'two', 'three',],}
|
||||||
|
|
||||||
|
# IndexError
|
||||||
|
try:
|
||||||
|
_ExtractWithJsonPath(ref_map, 'foo', 'a[3]')
|
||||||
|
self.fail('Expected Reference error')
|
||||||
|
except ExpansionReferenceError as e:
|
||||||
|
self.assertTrue('foo.a[3]' in e.message)
|
||||||
|
self.assertTrue('index out of range' in e.message)
|
||||||
|
|
||||||
|
self.assertFalse(_ExtractWithJsonPath(ref_map, 'foo', 'a[3]',
|
||||||
|
raise_exception=False))
|
||||||
|
|
||||||
|
def testExtractFail_NotAList(self):
|
||||||
|
ref_map = {'a': {'b': {'c': 'd'}}}
|
||||||
|
|
||||||
|
try:
|
||||||
|
_ExtractWithJsonPath(ref_map, 'foo', 'a.b[0]')
|
||||||
|
self.fail('Expected Reference error')
|
||||||
|
except ExpansionReferenceError as e:
|
||||||
|
self.assertTrue('foo.a.b[0]' in e.message)
|
||||||
|
self.assertTrue('No value found.' in e.message)
|
||||||
|
|
||||||
|
self.assertFalse(_ExtractWithJsonPath(ref_map, 'foo', 'a.b[0]',
|
||||||
|
raise_exception=False))
|
||||||
|
|
||||||
|
def testExtractFail_BadKey(self):
|
||||||
|
ref_map = {'a': {'b': {'c': 'd'}}}
|
||||||
|
|
||||||
|
self.assertFalse(_ExtractWithJsonPath(ref_map, 'foo', 'a.b.d',
|
||||||
|
raise_exception=False))
|
||||||
|
|
||||||
|
def testExtractFail_NoObject(self):
|
||||||
|
ref_map = {'a': {'b': {'c': 'd'}}}
|
||||||
|
|
||||||
|
self.assertFalse(_ExtractWithJsonPath(ref_map, 'foo', 'a.b.c.d',
|
||||||
|
raise_exception=False))
|
||||||
|
|
||||||
|
def testExtractFail_MalformedPath(self):
|
||||||
|
ref_map = {'a': {'b': {'c': 'd'}}}
|
||||||
|
|
||||||
|
self.assertFalse(_ExtractWithJsonPath(ref_map, 'foo', 'a.b[2',
|
||||||
|
raise_exception=False))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -1,3 +1,6 @@
|
|||||||
|
--allow-all-external
|
||||||
pyyaml
|
pyyaml
|
||||||
Jinja2
|
Jinja2
|
||||||
Jsonschema
|
Jsonschema
|
||||||
|
--allow-unverified Jsonpath
|
||||||
|
Jsonpath
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
info:
|
||||||
|
title: Schema with properties that are themselves objects
|
||||||
|
|
||||||
|
imports:
|
||||||
|
|
||||||
|
properties:
|
||||||
|
one:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
size:
|
||||||
|
type: integer
|
||||||
|
two:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
size:
|
||||||
|
type: integer
|
@ -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"]]|replace("\n","\n\n ") }}'
|
||||||
|
machineSize: big
|
@ -0,0 +1,8 @@
|
|||||||
|
imports: ["jinja_multilinefile.jinja", "helpers/common.jinja", "multiline.txt"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: jinja_multilinefile_name
|
||||||
|
type: jinja_multilinefile.jinja
|
||||||
|
properties:
|
||||||
|
description-file: multiline.txt
|
||||||
|
|
@ -0,0 +1,42 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: myFrontend-prod
|
||||||
|
properties:
|
||||||
|
description: '-----BEGIN TEST CERT-----
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Thelastlineisntalways65characters.Notsurewhy...
|
||||||
|
|
||||||
|
-----END TEST CERT-----
|
||||||
|
|
||||||
|
'
|
||||||
|
machineSize: big
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: jinja_multilinefile_name
|
||||||
|
properties:
|
||||||
|
description-file: multiline.txt
|
||||||
|
resources:
|
||||||
|
- name: myFrontend-prod
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: jinja_multilinefile.jinja
|
@ -0,0 +1,13 @@
|
|||||||
|
-----BEGIN TEST CERT-----
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
Thelastlineisntalways65characters.Notsurewhy...
|
||||||
|
-----END TEST CERT-----
|
@ -0,0 +1,11 @@
|
|||||||
|
"""Basic firewall template."""
|
||||||
|
|
||||||
|
|
||||||
|
def GenerateConfig(evaluation_context):
|
||||||
|
return """
|
||||||
|
resources:
|
||||||
|
- type: compute.v1.firewall
|
||||||
|
name: %(master)s-firewall
|
||||||
|
properties:
|
||||||
|
sourceRanges: [ "0.0.0.0/0" ]
|
||||||
|
""" % {"master": evaluation_context.properties["firewallname"]}
|
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
properties:
|
||||||
|
firewallname:
|
||||||
|
type: string
|
||||||
|
default: defaultname
|
@ -0,0 +1,6 @@
|
|||||||
|
imports:
|
||||||
|
- path: "no_properties_schema_defaults.py"
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: test-resource
|
||||||
|
type: no_properties_schema_defaults.py
|
@ -0,0 +1,16 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: defaultname-firewall
|
||||||
|
properties:
|
||||||
|
sourceRanges:
|
||||||
|
- 0.0.0.0/0
|
||||||
|
type: compute.v1.firewall
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: test-resource
|
||||||
|
properties:
|
||||||
|
firewallname: defaultname
|
||||||
|
resources:
|
||||||
|
- name: defaultname-firewall
|
||||||
|
type: compute.v1.firewall
|
||||||
|
type: no_properties_schema_defaults.py
|
@ -0,0 +1,9 @@
|
|||||||
|
resources:
|
||||||
|
- name: {{ env['name'] }}-backend
|
||||||
|
type: instance_builder.jinja
|
||||||
|
properties:
|
||||||
|
instance-name: {{ env['name'] }}-backend-vm
|
||||||
|
target-ip: {{ properties['frontend-ip'] }}
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: $(ref.{{ env['name'] }}-backend.ip)
|
@ -0,0 +1,8 @@
|
|||||||
|
resources:
|
||||||
|
- name: chain-template
|
||||||
|
type: one_simple.jinja
|
||||||
|
outputs:
|
||||||
|
- name: parent_ip
|
||||||
|
# We expect the ip value from one_simple.jinja, which in turn
|
||||||
|
# comes from simple.jinja, 192.168.0.0
|
||||||
|
value: $(ref.chain-template.intermediate_ip)
|
@ -0,0 +1,23 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
layout:
|
||||||
|
outputs:
|
||||||
|
- name: parent_ip
|
||||||
|
value: 192.168.0.0
|
||||||
|
resources:
|
||||||
|
- name: chain-template
|
||||||
|
outputs:
|
||||||
|
- name: intermediate_ip
|
||||||
|
value: 192.168.0.0
|
||||||
|
resources:
|
||||||
|
- name: simple-template
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: 192.168.0.0
|
||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
type: simple.jinja
|
||||||
|
type: one_simple.jinja
|
@ -0,0 +1,7 @@
|
|||||||
|
resources:
|
||||||
|
- name: simple-template
|
||||||
|
type: simple.jinja
|
||||||
|
outputs:
|
||||||
|
- name: parent_ip
|
||||||
|
# We expect the ip value from simple.jinja, 192.168.0.0
|
||||||
|
value: $(ref.simple-template.ip)
|
@ -0,0 +1,17 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
layout:
|
||||||
|
outputs:
|
||||||
|
- name: parent_ip
|
||||||
|
value: 192.168.0.0
|
||||||
|
resources:
|
||||||
|
- name: simple-template
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: 192.168.0.0
|
||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
type: simple.jinja
|
@ -0,0 +1,9 @@
|
|||||||
|
resources:
|
||||||
|
- name: one
|
||||||
|
type: simple-instance
|
||||||
|
{# properties['value'] is a refernce, not yet a number. #}
|
||||||
|
{# So this shouldn't output anything. #}
|
||||||
|
{% if properties['value'] is number %}
|
||||||
|
- name: two
|
||||||
|
type: simple-instance
|
||||||
|
{% endif %}
|
@ -0,0 +1,7 @@
|
|||||||
|
resources:
|
||||||
|
- name: one
|
||||||
|
type: output_one.jinja
|
||||||
|
- name: conditional
|
||||||
|
type: conditional.jinja
|
||||||
|
properties:
|
||||||
|
value: $(ref.one.one)
|
@ -0,0 +1,18 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: one
|
||||||
|
type: simple-instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: one
|
||||||
|
outputs:
|
||||||
|
- name: one
|
||||||
|
value: 1
|
||||||
|
type: output_one.jinja
|
||||||
|
- name: conditional
|
||||||
|
properties:
|
||||||
|
value: $(ref.one.one)
|
||||||
|
resources:
|
||||||
|
- name: one
|
||||||
|
type: simple-instance
|
||||||
|
type: conditional.jinja
|
@ -0,0 +1,8 @@
|
|||||||
|
resources:
|
||||||
|
- name: simple-consume-template
|
||||||
|
type: one_consume.jinja
|
||||||
|
- name: consume-simple
|
||||||
|
type: simple-instance
|
||||||
|
properties:
|
||||||
|
# Get the output value of simple.jinja, we expect 192.168.0.0
|
||||||
|
target: $(ref.simple-consume-template.intermediate_ip)
|
@ -0,0 +1,32 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
- name: sub-consume-simple
|
||||||
|
properties:
|
||||||
|
target: 192.168.0.0
|
||||||
|
type: simple-instance
|
||||||
|
- name: consume-simple
|
||||||
|
properties:
|
||||||
|
target: 192.168.0.0
|
||||||
|
type: simple-instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: simple-consume-template
|
||||||
|
outputs:
|
||||||
|
- name: intermediate_ip
|
||||||
|
value: 192.168.0.0
|
||||||
|
resources:
|
||||||
|
- name: simple-template
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: 192.168.0.0
|
||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
type: simple.jinja
|
||||||
|
- name: sub-consume-simple
|
||||||
|
type: simple-instance
|
||||||
|
type: one_consume.jinja
|
||||||
|
- name: consume-simple
|
||||||
|
type: simple-instance
|
@ -0,0 +1,8 @@
|
|||||||
|
resources:
|
||||||
|
- name: simple-template
|
||||||
|
type: simple.jinja
|
||||||
|
- name: consume-simple
|
||||||
|
type: simple-instance
|
||||||
|
properties:
|
||||||
|
# Get the output value of simple.jinja, we expect 192.168.0.0
|
||||||
|
target: $(ref.simple-template.ip)
|
@ -0,0 +1,20 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
- name: consume-simple
|
||||||
|
properties:
|
||||||
|
target: 192.168.0.0
|
||||||
|
type: simple-instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: simple-template
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: 192.168.0.0
|
||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
type: simple.jinja
|
||||||
|
- name: consume-simple
|
||||||
|
type: simple-instance
|
@ -0,0 +1,9 @@
|
|||||||
|
resources:
|
||||||
|
- name: {{ env['name'] }}-frontend
|
||||||
|
type: instance_builder.jinja
|
||||||
|
properties:
|
||||||
|
instance-name: {{ env['name'] }}-frontend-vm
|
||||||
|
target-ip: {{ properties['backend-ip'] }}
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: $(ref.{{ env['name'] }}-frontend.ip)
|
@ -0,0 +1,8 @@
|
|||||||
|
resources:
|
||||||
|
- name: {{ properties['instance-name'] }}
|
||||||
|
type: simple-instance
|
||||||
|
properties:
|
||||||
|
target: {{ properties['target-ip'] }}
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: $(ref.{{ properties['instance-name'] }}.network[0].ip)
|
@ -0,0 +1,5 @@
|
|||||||
|
resources:
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
- name: type
|
||||||
|
value: my-kubernetes
|
@ -0,0 +1,9 @@
|
|||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
outputs:
|
||||||
|
- name: ips
|
||||||
|
value:
|
||||||
|
- 192.168.0.0
|
||||||
|
- 192.168.0.1
|
||||||
|
- 192.168.0.2
|
@ -0,0 +1,12 @@
|
|||||||
|
resources:
|
||||||
|
- name: simple-template
|
||||||
|
type: list_output.jinja
|
||||||
|
- name: consume-list
|
||||||
|
type: simple-instance
|
||||||
|
properties:
|
||||||
|
# Get the first output value of list_output.jinja, we expect 192.168.0.1
|
||||||
|
first-ip: $(ref.simple-template.ips[1])
|
||||||
|
outputs:
|
||||||
|
- name: second-ip
|
||||||
|
# We expect the 2nd ip from list_outputs.jinja, 192.168.0.2
|
||||||
|
value: $(ref.simple-template.ips[2])
|
@ -0,0 +1,26 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
- name: consume-list
|
||||||
|
properties:
|
||||||
|
first-ip: 192.168.0.1
|
||||||
|
type: simple-instance
|
||||||
|
layout:
|
||||||
|
outputs:
|
||||||
|
- name: second-ip
|
||||||
|
value: 192.168.0.2
|
||||||
|
resources:
|
||||||
|
- name: simple-template
|
||||||
|
outputs:
|
||||||
|
- name: ips
|
||||||
|
value:
|
||||||
|
- 192.168.0.0
|
||||||
|
- 192.168.0.1
|
||||||
|
- 192.168.0.2
|
||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
type: list_output.jinja
|
||||||
|
- name: consume-list
|
||||||
|
type: simple-instance
|
@ -0,0 +1,12 @@
|
|||||||
|
resources:
|
||||||
|
- name: simple-template
|
||||||
|
type: simple.jinja
|
||||||
|
- name: sub-consume-simple
|
||||||
|
type: simple-instance
|
||||||
|
properties:
|
||||||
|
# Get the output value of simple.jinja, we expect 192.168.0.0
|
||||||
|
target: $(ref.simple-template.ip)
|
||||||
|
outputs:
|
||||||
|
- name: intermediate_ip
|
||||||
|
# We expect the ip value from simple.jinja, 192.168.0.0
|
||||||
|
value: $(ref.simple-template.ip)
|
@ -0,0 +1,7 @@
|
|||||||
|
resources:
|
||||||
|
- name: simple-template
|
||||||
|
type: simple.jinja
|
||||||
|
outputs:
|
||||||
|
- name: intermediate_ip
|
||||||
|
# We expect the ip value from simple.jinja, 192.168.0.0
|
||||||
|
value: $(ref.simple-template.ip)
|
@ -0,0 +1,5 @@
|
|||||||
|
resources:
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
- name: one
|
||||||
|
value: 1
|
@ -0,0 +1,5 @@
|
|||||||
|
resources:
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
- name: type
|
||||||
|
value: helper.jinja
|
@ -0,0 +1,5 @@
|
|||||||
|
resources:
|
||||||
|
- name: foo
|
||||||
|
type: output_template.jinja
|
||||||
|
- name: bar
|
||||||
|
type: $(ref.foo.type)
|
@ -0,0 +1,6 @@
|
|||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: 192.168.0.0
|
@ -0,0 +1,6 @@
|
|||||||
|
resources:
|
||||||
|
- type: simple-type
|
||||||
|
name: simple-instance
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: 192.168.0.0
|
@ -0,0 +1,11 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: simple-instance
|
||||||
|
type: simple-type
|
||||||
|
layout:
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: 192.168.0.0
|
||||||
|
resources:
|
||||||
|
- name: simple-instance
|
||||||
|
type: simple-type
|
@ -0,0 +1,17 @@
|
|||||||
|
resources:
|
||||||
|
- name: frontend
|
||||||
|
type: instance_builder.jinja
|
||||||
|
properties:
|
||||||
|
instance-name: mixer
|
||||||
|
target-ip: $(ref.backend.ip)
|
||||||
|
|
||||||
|
- name: backend
|
||||||
|
type: instance_builder.jinja
|
||||||
|
properties:
|
||||||
|
instance-name: workflow
|
||||||
|
target-ip: $(ref.frontend.ip)
|
||||||
|
outputs:
|
||||||
|
- name: frontend-ip
|
||||||
|
value: $(ref.frontend.ip)
|
||||||
|
- name: backend-ip
|
||||||
|
value: $(ref.backend.ip)
|
@ -0,0 +1,39 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: mixer
|
||||||
|
properties:
|
||||||
|
target: $(ref.workflow.network[0].ip)
|
||||||
|
type: simple-instance
|
||||||
|
- name: workflow
|
||||||
|
properties:
|
||||||
|
target: $(ref.mixer.network[0].ip)
|
||||||
|
type: simple-instance
|
||||||
|
layout:
|
||||||
|
outputs:
|
||||||
|
- name: frontend-ip
|
||||||
|
value: $(ref.mixer.network[0].ip)
|
||||||
|
- name: backend-ip
|
||||||
|
value: $(ref.workflow.network[0].ip)
|
||||||
|
resources:
|
||||||
|
- name: frontend
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: $(ref.mixer.network[0].ip)
|
||||||
|
properties:
|
||||||
|
instance-name: mixer
|
||||||
|
target-ip: $(ref.backend.ip)
|
||||||
|
resources:
|
||||||
|
- name: mixer
|
||||||
|
type: simple-instance
|
||||||
|
type: instance_builder.jinja
|
||||||
|
- name: backend
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: $(ref.workflow.network[0].ip)
|
||||||
|
properties:
|
||||||
|
instance-name: workflow
|
||||||
|
target-ip: $(ref.frontend.ip)
|
||||||
|
resources:
|
||||||
|
- name: workflow
|
||||||
|
type: simple-instance
|
||||||
|
type: instance_builder.jinja
|
@ -0,0 +1,6 @@
|
|||||||
|
resources:
|
||||||
|
- name: simple-template
|
||||||
|
type: simple.jinja
|
||||||
|
outputs:
|
||||||
|
- name: port
|
||||||
|
value: 88
|
@ -0,0 +1,17 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
layout:
|
||||||
|
outputs:
|
||||||
|
- name: port
|
||||||
|
value: 88
|
||||||
|
resources:
|
||||||
|
- name: simple-template
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: 192.168.0.0
|
||||||
|
resources:
|
||||||
|
- name: simple-name
|
||||||
|
type: simple-instance
|
||||||
|
type: simple.jinja
|
@ -0,0 +1,8 @@
|
|||||||
|
resources:
|
||||||
|
- name: kubernetes-cluster
|
||||||
|
type: kubernetes.jinja
|
||||||
|
- name: sub-thingy
|
||||||
|
type: $(ref.kubernetes-cluster.type)
|
||||||
|
outputs:
|
||||||
|
- name: type
|
||||||
|
value: $(ref.kubernetes-cluster.type)
|
@ -0,0 +1,5 @@
|
|||||||
|
resources:
|
||||||
|
- name: type-helper
|
||||||
|
type: type.jinja
|
||||||
|
- name: thingy
|
||||||
|
type: $(ref.type-helper.type)
|
@ -0,0 +1,23 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: sub-thingy
|
||||||
|
type: my-kubernetes
|
||||||
|
- name: thingy
|
||||||
|
type: my-kubernetes
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: type-helper
|
||||||
|
outputs:
|
||||||
|
- name: type
|
||||||
|
value: my-kubernetes
|
||||||
|
resources:
|
||||||
|
- name: kubernetes-cluster
|
||||||
|
outputs:
|
||||||
|
- name: type
|
||||||
|
value: my-kubernetes
|
||||||
|
type: kubernetes.jinja
|
||||||
|
- name: sub-thingy
|
||||||
|
type: $(ref.kubernetes-cluster.type)
|
||||||
|
type: type.jinja
|
||||||
|
- name: thingy
|
||||||
|
type: $(ref.type-helper.type)
|
@ -0,0 +1,14 @@
|
|||||||
|
resources:
|
||||||
|
- name: mixer
|
||||||
|
type: frontend.jinja
|
||||||
|
properties:
|
||||||
|
backend-ip: $(ref.workflow.ip)
|
||||||
|
- name: workflow
|
||||||
|
type: backend.jinja
|
||||||
|
properties:
|
||||||
|
frontend-ip: $(ref.mixer.ip)
|
||||||
|
outputs:
|
||||||
|
- name: frontend-ip
|
||||||
|
value: $(ref.mixer.ip)
|
||||||
|
- name: backend-ip
|
||||||
|
value: $(ref.workflow.ip)
|
@ -0,0 +1,55 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: mixer-frontend-vm
|
||||||
|
properties:
|
||||||
|
target: $(ref.workflow-backend-vm.network[0].ip)
|
||||||
|
type: simple-instance
|
||||||
|
- name: workflow-backend-vm
|
||||||
|
properties:
|
||||||
|
target: $(ref.mixer-frontend-vm.network[0].ip)
|
||||||
|
type: simple-instance
|
||||||
|
layout:
|
||||||
|
outputs:
|
||||||
|
- name: frontend-ip
|
||||||
|
value: $(ref.mixer-frontend-vm.network[0].ip)
|
||||||
|
- name: backend-ip
|
||||||
|
value: $(ref.workflow-backend-vm.network[0].ip)
|
||||||
|
resources:
|
||||||
|
- name: mixer
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: $(ref.mixer-frontend-vm.network[0].ip)
|
||||||
|
properties:
|
||||||
|
backend-ip: $(ref.workflow.ip)
|
||||||
|
resources:
|
||||||
|
- name: mixer-frontend
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: $(ref.mixer-frontend-vm.network[0].ip)
|
||||||
|
properties:
|
||||||
|
instance-name: mixer-frontend-vm
|
||||||
|
target-ip: $(ref.workflow.ip)
|
||||||
|
resources:
|
||||||
|
- name: mixer-frontend-vm
|
||||||
|
type: simple-instance
|
||||||
|
type: instance_builder.jinja
|
||||||
|
type: frontend.jinja
|
||||||
|
- name: workflow
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: $(ref.workflow-backend-vm.network[0].ip)
|
||||||
|
properties:
|
||||||
|
frontend-ip: $(ref.mixer.ip)
|
||||||
|
resources:
|
||||||
|
- name: workflow-backend
|
||||||
|
outputs:
|
||||||
|
- name: ip
|
||||||
|
value: $(ref.workflow-backend-vm.network[0].ip)
|
||||||
|
properties:
|
||||||
|
instance-name: workflow-backend-vm
|
||||||
|
target-ip: $(ref.mixer.ip)
|
||||||
|
resources:
|
||||||
|
- name: workflow-backend-vm
|
||||||
|
type: simple-instance
|
||||||
|
type: instance_builder.jinja
|
||||||
|
type: backend.jinja
|
@ -0,0 +1,38 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: mixer-frontend-vm
|
||||||
|
properties:
|
||||||
|
target: $(ref.workflow.ip)
|
||||||
|
type: simple-instance
|
||||||
|
- name: workflow-backend-vm
|
||||||
|
properties:
|
||||||
|
target: $(ref.mixer.ip)
|
||||||
|
type: simple-instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: mixer
|
||||||
|
properties:
|
||||||
|
backend-ip: $(ref.workflow.ip)
|
||||||
|
resources:
|
||||||
|
- name: mixer-frontend
|
||||||
|
properties:
|
||||||
|
instance-name: mixer-frontend-vm
|
||||||
|
target-ip: $(ref.workflow.ip)
|
||||||
|
resources:
|
||||||
|
- name: mixer-frontend-vm
|
||||||
|
type: simple-instance
|
||||||
|
type: instance_builder.jinja
|
||||||
|
type: frontend.jinja
|
||||||
|
- name: workflow
|
||||||
|
properties:
|
||||||
|
frontend-ip: $(ref.mixer.ip)
|
||||||
|
resources:
|
||||||
|
- name: workflow-backend
|
||||||
|
properties:
|
||||||
|
instance-name: workflow-backend-vm
|
||||||
|
target-ip: $(ref.mixer.ip)
|
||||||
|
resources:
|
||||||
|
- name: workflow-backend-vm
|
||||||
|
type: simple-instance
|
||||||
|
type: instance_builder.jinja
|
||||||
|
type: backend.jinja
|
@ -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."""
|
||||||
|
|
||||||
|
resource = {}
|
||||||
|
resource['name'] = common.GenerateMachineName('myFrontend', 'prod')
|
||||||
|
resource['type'] = 'compute.v1.instance'
|
||||||
|
resource['properties'] = {
|
||||||
|
'description': evaluation_context.imports[
|
||||||
|
evaluation_context.properties['description-file']],
|
||||||
|
'machineSize': helpers.extra.common2.GenerateMachineSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {'resources': [resource]}
|
@ -0,0 +1,8 @@
|
|||||||
|
imports: ["python_multilinefile.py", "helpers/common.py", "helpers/common2.py", "helpers/__init__.py", "multiline.txt"]
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- name: python_multilinefile_name
|
||||||
|
type: python_multilinefile.py
|
||||||
|
properties:
|
||||||
|
description-file: multiline.txt
|
||||||
|
|
@ -0,0 +1,42 @@
|
|||||||
|
config:
|
||||||
|
resources:
|
||||||
|
- name: myFrontend-prod
|
||||||
|
properties:
|
||||||
|
description: '-----BEGIN TEST CERT-----
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Lotsof65characterlineswillfollow.Onlyseveralmorecharacterstogo!!
|
||||||
|
|
||||||
|
Thelastlineisntalways65characters.Notsurewhy...
|
||||||
|
|
||||||
|
-----END TEST CERT-----
|
||||||
|
|
||||||
|
'
|
||||||
|
machineSize: big
|
||||||
|
type: compute.v1.instance
|
||||||
|
layout:
|
||||||
|
resources:
|
||||||
|
- name: python_multilinefile_name
|
||||||
|
properties:
|
||||||
|
description-file: multiline.txt
|
||||||
|
resources:
|
||||||
|
- name: myFrontend-prod
|
||||||
|
type: compute.v1.instance
|
||||||
|
type: python_multilinefile.py
|
Loading…
Reference in new issue