Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 86 additions & 33 deletions tools/deprecate_features/deprecate_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,98 @@
import subprocess
import fileinput

grep_output = subprocess.check_output('grep -r "deprecated = true" api/*', shell=True)

filenames_and_fields = set()

# Compile the set of deprecated fields and the files they're in, deduping via set.
deprecated_regex = re.compile(r'.*\/([^\/]*.proto):[^=]* ([^= ]+) =.*')
for line in grep_output.splitlines():
match = deprecated_regex.match(line)
if match:
filenames_and_fields.add(tuple([match.group(1), match.group(2)]))
else:
print 'no match in ' + line + ' please address manually!'

# Now discard any deprecated features already listed in runtime_features
exiting_deprecated_regex = re.compile(r'.*"envoy.deprecated_features.(.*):(.*)",.*')
with open('source/common/runtime/runtime_features.h', 'r') as features:
for line in features.readlines():
match = exiting_deprecated_regex.match(line)

# Sorts out the list of deprecated proto fields which should be disallowed and returns a tuple of
# email and code changes.
def deprecate_proto():
grep_output = subprocess.check_output('grep -r "deprecated = true" api/*', shell=True)

filenames_and_fields = set()

# Compile the set of deprecated fields and the files they're in, deduping via set.
deprecated_regex = re.compile(r'.*\/([^\/]*.proto):[^=]* ([^= ]+) =.*')
for line in grep_output.splitlines():
match = deprecated_regex.match(line)
if match:
filenames_and_fields.discard(tuple([match.group(1), match.group(2)]))
filenames_and_fields.add(tuple([match.group(1), match.group(2)]))
else:
print 'no match in ' + line + ' please address manually!'

# Now discard any deprecated features already listed in runtime_features
exiting_deprecated_regex = re.compile(r'.*"envoy.deprecated_features.(.*):(.*)",.*')
with open('source/common/runtime/runtime_features.cc', 'r') as features:
for line in features.readlines():
match = exiting_deprecated_regex.match(line)
if match:
filenames_and_fields.discard(tuple([match.group(1), match.group(2)]))

# Finally sort out the code to add to runtime_features.cc and a canned email for envoy-announce.
code_snippets = []
email_snippets = []
for (filename, field) in filenames_and_fields:
code_snippets.append(' "envoy.deprecated_features.' + filename + ':' + field + '",\n')
email_snippets.append(field + ' from ' + filename + '\n')
code = ''.join(code_snippets)
email = ''
if email_snippets:
email = ('\nThe following deprecated configuration fields will be disallowed by default:\n' +
''.join(email_snippets))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, but one slight Pythonic nit. Could we do '\n'.join(email_snippets)'? That way the above append becomes a bit more declarative. Same for code_snippets (and below..).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can, but I'd prefer not to if I can talk you out of it :-)

If I pull newlines out of email snippets I think I should do it for the code for consistency. But for code it's a need-to-have and for email it's a nice-to-have to have the trailing \n after the join. I mean I could append another newline after both but if I want it after each snippet rather than between each snippet I think having it in the snippet rather than having it in the join is semantically correct.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's semantically correct, I'm just suggesting there are more Pythonic ways to do it if you're after Python style suggestions. Since the join value is substituted into a string in both cases, you can write something like 'foo bar:\n %s\n' % '\n'.join(stuff without any new lines in it). This discussion is really me being nit-picky, but reflects my understanding of Python best practices.


# Finally sort out the code to add to runtime_features.h and a canned email for envoy-announce.
code = ''
email = 'The latest Envoy release will deprecate the following configuration fields:\n'
for (filename, field) in filenames_and_fields:
code += (' "envoy.deprecated_features.' + filename + ':' + field + '",\n')
email += (field + ' from ' + filename + '\n')
return email, code


# Sorts out the list of features which should be default enabled and returns a tuple of
# email and code changes.
def flip_runtime_features():
grep_output = subprocess.check_output(
'grep -r "envoy.reloadable_features\." source/*', shell=True)

features_to_flip = set()

# Compile the set of features to flip, deduping via set.
deprecated_regex = re.compile(r'.*"(envoy.reloadable_features\.[^"]+)".*')
for line in grep_output.splitlines():
match = deprecated_regex.match(line)
if match:
features_to_flip.add(match.group(1))
else:
print 'no match in ' + line + ' please address manually!'

print '\n\nSuggested runtime changes: '
print code
# Exempt the two test flags.
features_to_flip.remove('envoy.reloadable_features.my_feature_name')
features_to_flip.remove('envoy.reloadable_features.test_feature_true')

if not raw_input('Apply runtime changes? [yN] ').strip().lower() in ('y', 'yes'):
code_snippets = []
email_snippets = []
for (feature) in features_to_flip:
code_snippets.append(' "' + feature + '",\n')
email_snippets.append(feature + '\n')
code = ''.join(code_snippets)
email = ''
if email_snippets:
email = 'the following features will be defaulted to true:\n' + ''.join(email_snippets)

return email, code


# Gather code and suggested email changes.
runtime_email, runtime_features_code = flip_runtime_features()
deprecate_email, deprecate_code = deprecate_proto()

email = ('The Envoy maintainer team is cutting the next Envoy release. In the new release ' +
runtime_email + deprecate_email)

print '\n\nSuggested envoy-announce email: \n'
print email

if not raw_input('Apply relevant runtime changes? [yN] ').strip().lower() in ('y', 'yes'):
exit(1)

for line in fileinput.FileInput('source/common/runtime/runtime_features.h', inplace=1):
for line in fileinput.FileInput('source/common/runtime/runtime_features.cc', inplace=1):
if 'envoy.reloadable_features.test_feature_true' in line:
line = line.replace(line, line + runtime_features_code)
if 'envoy.deprecated_features.deprecated.proto:is_deprecated_fatal' in line:
line = line.replace(line, line + code)
line = line.replace(line, line + deprecate_code)
print line,

print '\nChanges applied. Please create an upstream PR and send the following to envoy-announce:\n'

print email
print '\nChanges applied. Please send the email above to envoy-announce.\n'