Skip to content

Commit f352215

Browse files
authored
Support exclude key on interpolation for himl (#23)
1 parent 54a4a13 commit f352215

File tree

3 files changed

+112
-3
lines changed

3 files changed

+112
-3
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,21 @@
11
cluster: cluster1
2+
3+
testkey: |-
4+
# Set to true to log user information returned from LDAP
5+
verbose_logging = true
6+
7+
[[servers]]
8+
# Ldap server host
9+
host = "someaddress"
10+
11+
# Default port is 389 or 636 if use_ssl = true
12+
port = 389
13+
14+
start_tls = true
15+
16+
skipInterpolation_key: "{{`this is a {{ value }} that should not be interpolated`}}"
17+
skipInterpolation_key2: "before {{`this is a {{ value }} that should not be interpolated`}} after"
18+
skipInterpolation_dict:
19+
skipInterpolation_key: "{{`this is a {{ value }} that should not be interpolated`}}"
20+
skipInterpolation_list:
21+
- "{{`this is a {{ value }} that should not be interpolated`}}"

himl/config_generator.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
import yaml
1818
from deepmerge import Merger
1919

20-
from .interpolation import InterpolationResolver, InterpolationValidator, SecretResolver, DictIterator, replace_parent_working_directory
20+
from .interpolation import InterpolationResolver, EscapingResolver, InterpolationValidator, SecretResolver, DictIterator, replace_parent_working_directory
2121
from .python_compat import iteritems, primitive_types, PY3
2222
from .remote_state import S3TerraformRemoteStateRetriever
2323

2424
logger = logging.getLogger(__name__)
2525

26+
2627
class ConfigProcessor(object):
2728

2829
def process(self, cwd=None, path=None, filters=(), exclude_keys=(), enclosing_key=None, remove_enclosing_key=None, output_format="yaml",
@@ -82,6 +83,8 @@ def process(self, cwd=None, path=None, filters=(), exclude_keys=(), enclosing_ke
8283
else:
8384
data = generator.generated_data
8485

86+
generator.clean_escape_characters()
87+
8588
formatted_data = generator.output_data(data, output_format)
8689

8790
if print_data:
@@ -271,6 +274,13 @@ def resolve_secrets(self, default_aws_profile):
271274
def validate_interpolations(self):
272275
self.interpolation_validator.check_all_interpolations_resolved(self.generated_data)
273276

277+
def clean_escape_characters(self):
278+
"""
279+
Method should clean the escaping characters {{` and `}} from all escaped values
280+
"""
281+
resolver = EscapingResolver()
282+
self.generated_data = resolver.resolve_escaping(self.generated_data)
283+
274284
@staticmethod
275285
def should_use_block(value):
276286
"""

himl/interpolation.py

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,21 @@
1515

1616

1717
def is_interpolation(value):
18-
return isinstance(value, string_types) and '{{' in value and '}}' in value
18+
return isinstance(value, string_types) and '{{' in value and '}}' in value \
19+
and not is_escaped_interpolation(value)
20+
21+
22+
def is_escaped_interpolation(value):
23+
return isinstance(value, string_types) and '{{`' in value and '`}}' in value
24+
25+
26+
def is_fully_escaped_interpolation(value):
27+
return isinstance(value, string_types) and value.startswith('{{`') and value.endswith('`}}')
1928

2029

2130
def is_full_interpolation(value):
22-
return is_interpolation(value) and value.startswith('{{') and value.endswith('}}')
31+
return is_interpolation(value) and value.startswith('{{') and value.endswith('}}') \
32+
and not is_fully_escaped_interpolation(value)
2333

2434

2535
def remove_white_spaces(value):
@@ -47,6 +57,19 @@ def resolve_interpolations(self, data):
4757
return data
4858

4959

60+
class EscapingResolver(object):
61+
62+
def resolve_escaping(self, data):
63+
"""
64+
Should do one last check through all values to ensure the ones that were escaped are cleaned of the escape
65+
sequence
66+
"""
67+
from_dict_injector = DictEscapingResolver(data, FromDictInjector())
68+
from_dict_injector.clean_escapes(data)
69+
70+
return data
71+
72+
5073
class SecretResolver(object):
5174

5275
def resolve_secrets(self, data, default_aws_profile):
@@ -81,6 +104,21 @@ def loop_all_items(self, data, process_func):
81104
return data
82105

83106

107+
class AbstractEscapingResolver(DictIterator):
108+
109+
def clean_escapes(self, data):
110+
return self.loop_all_items(data, self.clean_escape)
111+
112+
def clean_escape(self, line):
113+
# Check if line is escaped
114+
if not is_escaped_interpolation(line):
115+
return line
116+
return self.do_clean_escapes(line)
117+
118+
def do_clean_escapes(self, line):
119+
pass
120+
121+
84122
class AbstractInterpolationResolver(DictIterator):
85123

86124
def resolve_interpolations(self, data):
@@ -97,6 +135,18 @@ def do_resolve_interpolation(self, line):
97135
pass
98136

99137

138+
class DictEscapingResolver(AbstractEscapingResolver):
139+
def __init__(self, data, from_dict_injector):
140+
AbstractEscapingResolver.__init__(self)
141+
self.data = data
142+
self.from_dict_injector = from_dict_injector
143+
self.full_blob_injector = FullBlobInjector()
144+
145+
def do_clean_escapes(self, line):
146+
updated_line = self.from_dict_injector.resolve(line, self.data)
147+
return self.full_blob_injector.clean(updated_line)
148+
149+
100150
class DictInterpolationResolver(AbstractInterpolationResolver):
101151
def __init__(self, data, from_dict_injector):
102152
AbstractInterpolationResolver.__init__(self)
@@ -178,6 +228,27 @@ def resolve(self, line, data):
178228

179229
return resolved_value if is_valid_value else line
180230

231+
def clean(self, line):
232+
# {{` something {{ value }} `}}
233+
if is_fully_escaped_interpolation(line):
234+
resolved_value = self.get_value_from_escaping(line)
235+
is_valid_value = resolved_value is not None
236+
237+
return resolved_value if is_valid_value else line
238+
239+
# before {{` {{value}} `}} after
240+
elif is_escaped_interpolation(line):
241+
prefix = line[0:line.find('{{`')]
242+
escaping_string = line[line.find('{{`'):line.find('`}}') + 3]
243+
suffix = line[line.find('`}}') + 3:len(line)]
244+
resolved_value = prefix + self.get_value_from_escaping(escaping_string) + suffix
245+
is_valid_value = resolved_value is not None
246+
247+
return resolved_value if is_valid_value else line
248+
249+
# nothing to clean
250+
return line
251+
181252
@staticmethod
182253
def get_inner_value(keys, data):
183254
for key in keys:
@@ -194,3 +265,11 @@ def get_keys_from_interpolation(line):
194265
line = line[2:-2]
195266

196267
return line.split('.')
268+
269+
@staticmethod
270+
def get_value_from_escaping(line):
271+
# remove {{` and `}}
272+
line = line[3:-3]
273+
274+
return line
275+

0 commit comments

Comments
 (0)