-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow the characters _:./+-@= to be used in tag keys during deploy #1798
Changes from all commits
988a84c
53c2f68
3c94914
8155f61
8838421
fc890c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,16 +8,57 @@ | |
|
||
import click | ||
|
||
PARAM_AND_METADATA_KEY_REGEX = '([A-Za-z0-9\\"]+)' | ||
|
||
def _value_regex(delim): | ||
return f'(\\"(?:\\\\.|[^\\"\\\\]+)*\\"|(?:\\\\.|[^{delim}\\"\\\\]+)+)' | ||
|
||
def _generate_match_regex(match_pattern, delim): | ||
|
||
KEY_REGEX = '([A-Za-z0-9\\"]+)' | ||
# Use this regex when you have space as delimiter Ex: "KeyName1=string KeyName2=string" | ||
VALUE_REGEX_SPACE_DELIM = _value_regex(" ") | ||
# Use this regex when you have comma as delimiter Ex: "KeyName1=string,KeyName2=string" | ||
VALUE_REGEX_COMMA_DELIM = _value_regex(",") | ||
""" | ||
Creates a regex string based on a match pattern (also a regex) that is to be | ||
run on a string (which may contain escaped quotes) that is separated by delimiters. | ||
|
||
Parameters | ||
---------- | ||
match_pattern: (str) regex pattern to match | ||
delim: (str) delimiter that is respected when identifying matching groups with generated regex. | ||
|
||
Returns | ||
------- | ||
str: regex expression | ||
|
||
""" | ||
|
||
# Non capturing groups reduces duplicates in groups, but does not reduce matches. | ||
return f'(\\"(?:\\\\{match_pattern}|[^\\"\\\\]+)*\\"|(?:\\\\{match_pattern}|[^{delim}\\"\\\\]+)+)' | ||
|
||
|
||
def _unquote_wrapped_quotes(value): | ||
r""" | ||
Removes wrapping double quotes and any '\ ' characters. They are usually added to preserve spaces when passing | ||
value thru shell. | ||
|
||
Examples | ||
-------- | ||
>>> _unquote_wrapped_quotes('val\ ue') | ||
value | ||
|
||
>>> _unquote_wrapped_quotes("hel\ lo") | ||
hello | ||
|
||
Parameters | ||
---------- | ||
value : str | ||
Input to unquote | ||
|
||
Returns | ||
------- | ||
Unquoted string | ||
""" | ||
if value and (value[0] == value[-1] == '"'): | ||
# Remove quotes only if the string is wrapped in quotes | ||
value = value.strip('"') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The intention here is to only remove the first and last quotes right? Do we need to worry about the case where there are two Below was run in Python3.8
Doing a slice might be better here:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This only cares about wrapping quotes and not the entire nesting, can change the function name to reflect that. |
||
|
||
return value.replace("\\ ", " ").replace('\\"', '"') | ||
|
||
|
||
class CfnParameterOverridesType(click.ParamType): | ||
|
@@ -36,8 +77,12 @@ class CfnParameterOverridesType(click.ParamType): | |
# If Both ParameterKey pattern and KeyPairName=MyKey should not be present | ||
# while adding parameter overrides, if they are, it | ||
# can result in unpredicatable behavior. | ||
_pattern_1 = r"(?:ParameterKey={key},ParameterValue={value})".format(key=KEY_REGEX, value=VALUE_REGEX_SPACE_DELIM) | ||
_pattern_2 = r"(?:(?: ){key}={value})".format(key=KEY_REGEX, value=VALUE_REGEX_SPACE_DELIM) | ||
# Use this regex when you have space as delimiter Ex: "KeyName1=string KeyName2=string" | ||
VALUE_REGEX_SPACE_DELIM = _generate_match_regex(match_pattern=".", delim=" ") | ||
_pattern_1 = r"(?:ParameterKey={key},ParameterValue={value})".format( | ||
key=PARAM_AND_METADATA_KEY_REGEX, value=VALUE_REGEX_SPACE_DELIM | ||
) | ||
_pattern_2 = r"(?:(?: ){key}={value})".format(key=PARAM_AND_METADATA_KEY_REGEX, value=VALUE_REGEX_SPACE_DELIM) | ||
|
||
ordered_pattern_match = [_pattern_1, _pattern_2] | ||
|
||
|
@@ -80,39 +125,10 @@ def convert(self, value, param, ctx): | |
|
||
# 'groups' variable is a list of tuples ex: [(key1, value1), (key2, value2)] | ||
for key, param_value in groups: | ||
result[self._unquote(key)] = self._unquote(param_value) | ||
result[_unquote_wrapped_quotes(key)] = _unquote_wrapped_quotes(param_value) | ||
|
||
return result | ||
|
||
@staticmethod | ||
def _unquote(value): | ||
r""" | ||
Removes wrapping double quotes and any '\ ' characters. They are usually added to preserve spaces when passing | ||
value thru shell. | ||
|
||
Examples | ||
-------- | ||
>>> _unquote('val\ ue') | ||
value | ||
|
||
>>> _unquote("hel\ lo") | ||
hello | ||
|
||
Parameters | ||
---------- | ||
value : str | ||
Input to unquote | ||
|
||
Returns | ||
------- | ||
Unquoted string | ||
""" | ||
if value and (value[0] == value[-1] == '"'): | ||
# Remove quotes only if the string is wrapped in quotes | ||
value = value.strip('"') | ||
|
||
return value.replace("\\ ", " ").replace('\\"', '"') | ||
|
||
|
||
class CfnMetadataType(click.ParamType): | ||
""" | ||
|
@@ -121,8 +137,10 @@ class CfnMetadataType(click.ParamType): | |
""" | ||
|
||
_EXAMPLE = 'KeyName1=string,KeyName2=string or {"string":"string"}' | ||
# Use this regex when you have comma as delimiter Ex: "KeyName1=string,KeyName2=string" | ||
VALUE_REGEX_COMMA_DELIM = _generate_match_regex(match_pattern=".", delim=",") | ||
|
||
_pattern = r"(?:{key}={value})".format(key=KEY_REGEX, value=VALUE_REGEX_COMMA_DELIM) | ||
_pattern = r"(?:{key}={value})".format(key=PARAM_AND_METADATA_KEY_REGEX, value=VALUE_REGEX_COMMA_DELIM) | ||
|
||
# NOTE(TheSriram): name needs to be added to click.ParamType requires it. | ||
name = "" | ||
|
@@ -167,8 +185,10 @@ class CfnTags(click.ParamType): | |
""" | ||
|
||
_EXAMPLE = "KeyName1=string KeyName2=string" | ||
# Tags have additional constraints and they allow "+ - = . _ : / @" apart from alpha-numerics. | ||
TAG_REGEX = '[A-Za-z0-9\\"_:\\.\\/\\+-\\@=]' | ||
|
||
_pattern = r"{key}={value}".format(key=KEY_REGEX, value=VALUE_REGEX_SPACE_DELIM) | ||
_pattern = r"{tag}={tag}".format(tag=_generate_match_regex(match_pattern=TAG_REGEX, delim=" ")) | ||
|
||
# NOTE(TheSriram): name needs to be added to click.ParamType requires it. | ||
name = "" | ||
|
@@ -191,7 +211,7 @@ def convert(self, value, param, ctx): | |
for group in groups: | ||
key, v = group | ||
# assign to result['KeyName1'] = string and so on. | ||
result[key] = v | ||
result[_unquote_wrapped_quotes(key)] = _unquote_wrapped_quotes(v) | ||
|
||
if fail: | ||
return self.fail( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You say "They are usually added". So does that mean there is a case that we wouldn't want to replace the double or '\ ' characters from the input?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont think so, we will want to replace it. "Usually added" maybe a misnomer, preserving spaces inside quotes or even escaping quotes within quotes requires a
\
(escape) character.