Skip to content

Commit aa84c0e

Browse files
committed
Add feature to generate diffs for helm values.yaml file between releases
This feature adds the capability to compare changes to the configuration options in the helm values.yaml files between the current and previous release. This was included in the 1.1 release but did not get merged. Here is the link to the original PR: #3420 Fixes: #4384
1 parent 682293f commit aa84c0e

File tree

1 file changed

+270
-3
lines changed

1 file changed

+270
-3
lines changed

scripts/tablegen.py

Lines changed: 270 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import sys
2121
import os
2222
import re
23+
import requests
2324

2425
from ruamel import yaml
2526

@@ -35,6 +36,8 @@
3536
VALUES_YAML = "values.yaml"
3637
ISTIO_IO_DIR = os.path.abspath(__file__ + "/../../")
3738
CONFIG_INDEX_DIR = "content/docs/reference/config/installation-options/index.md"
39+
CONFIG_INDEX_DIFF_DIR = "content/docs/reference/config/installation-options-changes/index.md"
40+
CONFIG_IGNORE_LIST = ["global.hub"]
3841

3942
def endOfTheList(context, lineNum, lastLineNum, totalNum):
4043
flag = 0
@@ -67,7 +70,15 @@ def endOfTheList(context, lineNum, lastLineNum, totalNum):
6770

6871
return True, valueList
6972

73+
# ordered dictionary to store the configuration options for the subcomponents of Istio. This
74+
# will be used to populate a new index.md
7075
prdict = collections.defaultdict(list)
76+
# ordered dictionary to store the differences of configuration options between the new
77+
# index.md and the previous version (i.e, configurations options already listed in the index.md).
78+
od_diff = collections.defaultdict(list)
79+
od_diff_new = collections.defaultdict(list)
80+
od_diff_removed = collections.defaultdict(list)
81+
od_diff_unchanged = collections.defaultdict(list)
7182

7283
def decode_helm_yaml(s):
7384
ret_val = ''
@@ -307,10 +318,243 @@ def sanitizeValueStr(value):
307318
if value != None and regex.search(value) != None:
308319
value = value.replace("|", "\|");
309320
return value
310-
311-
321+
322+
# Compares the configuration option value from the newly discovered set of values (stored
323+
# in prdict dictionary) and its previous version (stored in index.md). If there is no
324+
# change in the configuration option value between the 2 versions, it will be ignored. If
325+
# there are any differences, we will store the differences (will track differences for key,
326+
# value and description of a configuration option) in the 'od_diff' dictionary. The values
327+
# stored in this dictionary will later be written to CONFIG_INDEX_DIFF_DIR.
328+
#
329+
# The difference between the configuration option values is stored in the CONFIG_INDEX_DIFF_DIR
330+
# in the format:
331+
# | KEY | OLD DEFAULT VALUE | NEW DEFAULT VALUE | OLD DESCRIPTION | NEW DESCRIPTION |
332+
# | ------ | ------------ | ------------ | ------------ | ------------ |
333+
# | Key | oldValue | newValue | oldDesc | newDesc |
334+
#
335+
# If a configuration option is present only in the latest version, then the oldKey, oldValue
336+
# and oldDescription will be represented as 'n/a' (vice-versa applies to newKey, newValue and
337+
# newDescription).
338+
#
339+
# oValue - configuration option from the existing index.md
340+
# nValue - configuration option from the current processing of configuration options to be
341+
# stored in a new version of index.md
342+
# k - istio component name for which these configuration options are being processed. This is
343+
# used to populate the contents of 'od_diff' dictionary.
344+
#
345+
def compareValues(oValue, nValue, k):
346+
# oValue and nVAlue contains configuration option in the format:
347+
# '| `<Key>` | `<Value>` | `<Description>` |
348+
# This needs to be split in order to get the Key, Value and Description values to compare.
349+
oldKey = ''
350+
oldValue = ''
351+
oldDesc = ''
352+
353+
newKey = ''
354+
newValue = ''
355+
newDesc = ''
356+
357+
key = None
358+
359+
if nValue is not None:
360+
groups = re.search("\| \`(.*)\` \| \`(.*)\` \| (.*) |", nValue.strip())
361+
if groups:
362+
newKey = groups.group(1)
363+
newValue = groups.group(2)
364+
newDesc = groups.group(3)
365+
366+
if oValue is not None and nValue is not None:
367+
if len(oValue) == 1:
368+
item = oValue[0]
369+
370+
if item == nValue:
371+
key = newKey
372+
oValue.remove(item)
373+
od_diff_unchanged[k].append("| `%s` | `%s` | %s |" % (newKey, newValue.rstrip(), newDesc))
374+
else:
375+
groups = re.search("\| \`(.*)\` \| \`(.*)\` \|\s*(.*)\s*\|", item.strip())
376+
if groups:
377+
oldKey = groups.group(1)
378+
oldValue = groups.group(2)
379+
oldDesc = groups.group(3)
380+
key = oldKey
381+
382+
if oldKey in CONFIG_IGNORE_LIST:
383+
oValue.remove(item)
384+
return key
385+
386+
if oldValue != newValue:
387+
if oldValue is None:
388+
oldValue = 'n/a'
389+
if newValue is None:
390+
newValue = 'n/a'
391+
392+
if oldDesc.strip() != newDesc.strip():
393+
if (newDesc == None or newDesc == '') and (oldDesc is None or oldDesc == ''):
394+
pass
395+
if oldDesc is None:
396+
oldDesc = 'n/a'
397+
if newDesc is None or newDesc == '':
398+
newDesc = 'n/a'
399+
oValue.remove(item)
400+
od_diff[k].append("| `%s` | `%s` | `%s` | %s | %s |" % (newKey, oldValue.rstrip(), newValue.rstrip(), oldDesc, newDesc))
401+
else:
402+
# This is the case where values are the same but descriptions are different. Right now, there is nothing more to do since
403+
# we do not care about displaying values that haven't changed between releases.
404+
oValue.remove(item)
405+
406+
#od_diff_unchanged[k].append("| `%s` | `%s` | %s |" % (newKey, newValue.rstrip(), newDesc))
407+
else:
408+
foundItem = 'false'
409+
for item in oValue:
410+
if item == nValue:
411+
key = newKey
412+
oValue.remove(item)
413+
od_diff_unchanged[k].append("| `%s` | `%s` | %s |" % (newKey, newValue.rstrip(), newDesc))
414+
foundItem = 'true'
415+
break
416+
else:
417+
groups = re.search("\| \`(.*)\` \| \`(.*)\` \|\s*(.*)\s*\|", item.strip())
418+
if groups:
419+
oldKey = groups.group(1)
420+
oldValue = groups.group(2)
421+
oldDesc = groups.group(3)
422+
423+
if oldKey == newKey:
424+
if oldValue == newValue and oldDesc != newDesc:
425+
key = newKey
426+
od_diff[k].append("| `%s` | `%s` | `%s` | %s | %s |" % (newKey, oldValue.rstrip(), newValue.rstrip(), oldDesc, newDesc))
427+
oValue.remove(item)
428+
foundItem = 'true'
429+
break
430+
431+
if foundItem == 'false':
432+
od_diff_new[k].append("| `%s` | `%s` | %s |" % (newKey, newValue.rstrip(), newDesc))
433+
elif oValue is None:
434+
key = newKey
435+
od_diff_new[k].append("| `%s` | `%s` | %s |" % (newKey, newValue.rstrip(), newDesc))
436+
elif nValue is None:
437+
for item in oValue:
438+
groups = re.search("\| \`(.*)\` \| \`(.*)\` \|\s*(.*)\s*\|", item.strip())
439+
if groups:
440+
oldKey = groups.group(1)
441+
oldValue = groups.group(2)
442+
oldDesc = groups.group(3)
443+
444+
key = oldKey
445+
od_diff_removed[k].append("| `%s` | `%s` | %s |" % (oldKey, oldValue.rstrip(), oldDesc))
446+
447+
return key
448+
449+
#
450+
# Get the previous release number so that we can retrieve the index.md for that
451+
# release. The release branches are tagged in the following format: release-<number>
452+
#
453+
def getPreviousRelease():
454+
req = requests.get('https://api.github.com/repos/istio/istio.io/branches')
455+
jsonData = req.json()
456+
previousRelease = 0.0
457+
458+
for x in jsonData:
459+
releaseName = x['name']
460+
if releaseName.startswith('release-'):
461+
releaseNum = releaseName.split('release-')
462+
if releaseNum[1] > previousRelease:
463+
previousRelease = releaseNum[1]
464+
return previousRelease
465+
466+
#
467+
# Get the index.md for the previous release.
468+
#
469+
def getContentFromPreviousRelease(releaseName):
470+
istio_url = 'https://raw.githubusercontent.com/istio/istio.io/release-' + releaseName +'/content/docs/reference/config/installation-options/index.md'
471+
req = requests.get(istio_url)
472+
content = req.text
473+
indexMap = collections.defaultdict(list)
474+
475+
# store all the configurations options from the index.md file into the indexMap
476+
# dictionary. This will be used to compare the values with the latest version
477+
# of configuration options.
478+
data = content.split('\n')
479+
for d in data:
480+
if d.rstrip() != '' and d != '| Key | Default Value | Description |' and d != '| --- | --- | --- |' and d[0:1] == '|' and d[-1] == '|':
481+
groups = re.search("\| \`(.*)\` \| \`(.*)\` \| (.*) |", d.strip())
482+
if groups:
483+
key = groups.group(1)
484+
if key in indexMap:
485+
value = indexMap.get(key)
486+
value.append(d.strip())
487+
else:
488+
indexMap[key].append(d.strip())
489+
return indexMap
490+
491+
def writeVersionDiffs(index_diff_file):
492+
meta = ""
493+
494+
for d in index_diff_file:
495+
meta = meta + d
496+
if "<!-- AUTO-GENERATED-START -->" in d:
497+
break
498+
499+
index_diff_file.seek(0)
500+
index_diff_file.write(meta)
501+
502+
'''
503+
if od_diff_unchanged:
504+
index_diff_file.write('\n## Unmodified configuration options\n')
505+
506+
for k, v in od_diff_unchanged.items():
507+
index_diff_file.write("\n### Unmodified `%s` key/value pairs\n\n" % k)
508+
index_diff_file.write('| Key | Default Value | Description |\n')
509+
index_diff_file.write('| --- | --- | --- |\n')
510+
511+
for value in v:
512+
index_diff_file.write('%s\n' % (value))
513+
'''
514+
515+
if od_diff:
516+
index_diff_file.write('\n## Modified configuration options\n')
517+
518+
for k, v in od_diff.items():
519+
index_diff_file.write("\n### Modified `%s` key/value pairs\n\n" % k)
520+
index_diff_file.write('| Key | Old Default Value | New Default Value | Old Description | New Description |\n')
521+
index_diff_file.write('| --- | --- | --- | --- | --- |\n')
522+
523+
for value in v:
524+
index_diff_file.write('%s\n' % (value))
525+
526+
if od_diff_new:
527+
index_diff_file.write('\n## New configuration options\n')
528+
529+
for k, v in od_diff_new.items():
530+
index_diff_file.write("\n### New `%s` key/value pairs\n\n" % k)
531+
index_diff_file.write('| Key | Default Value | Description |\n')
532+
index_diff_file.write('| --- | --- | --- |\n')
533+
534+
for value in v:
535+
index_diff_file.write('%s\n' % (value))
536+
537+
if od_diff_removed:
538+
index_diff_file.write('\n## Removed configuration options\n')
539+
540+
for k, v in od_diff_removed.items():
541+
index_diff_file.write("\n### Removed `%s` key/value pairs\n\n" % k)
542+
index_diff_file.write('| Key | Default Value | Description |\n')
543+
index_diff_file.write('| --- | --- | --- |\n')
544+
545+
for value in v:
546+
index_diff_file.write('%s\n' % (value))
547+
548+
index_diff_file.write("\n<!-- AUTO-GENERATED-END -->\n")
549+
312550
with open(os.path.join(ISTIO_IO_DIR, CONFIG_INDEX_DIR), 'r') as f:
313551
endReached = False
552+
key = ''
553+
# A list used to track the configuration options that has been compared and processed when going
554+
# through the configurations processed in the latest version
555+
indexList = []
556+
previousRelease = getPreviousRelease()
557+
indexMap = getContentFromPreviousRelease(previousRelease)
314558

315559
data = f.read().split('\n')
316560
for d in data:
@@ -332,11 +576,34 @@ def sanitizeValueStr(value):
332576
print '| Key | Default Value | Description |'
333577
print '| --- | --- | --- |'
334578
for value in v:
335-
print ('%s' % (value))
579+
print ('%s' % (value))
580+
# Compare configuration option values from the latest version
581+
# with the older version.
582+
groups = re.search("\| \`(.*)\` \| \`(.*)\` \| (.*) |", value.strip())
583+
if groups:
584+
key = groups.group(1)
585+
indexValue = indexMap.get(key)
586+
587+
indexList.append(compareValues(indexValue, value, k))
336588
print ('')
337589

338590
for d in data:
339591
if "<!-- AUTO-GENERATED-END -->" in d:
340592
endReached = True
341593
if endReached:
342594
print d
595+
596+
# We want to include any configuration options that was discovered in
597+
# the older version but not available in the current version
598+
for k in indexMap.keys():
599+
key = k.split('.')[0]
600+
indexList.append(compareValues(indexMap.get(k), None, key))
601+
602+
# This index.md file is used to track the differences of configuration
603+
# option values between the current and previous release. All the
604+
# differences in configuration option values between the current
605+
# and previous release (tracked in the 'od_diff' dictionary) will be
606+
# written to the index.md file
607+
index_diff_file = open(os.path.join(ISTIO_IO_DIR, CONFIG_INDEX_DIFF_DIR), 'r+')
608+
writeVersionDiffs(index_diff_file)
609+
index_diff_file.close()

0 commit comments

Comments
 (0)