mirror of https://github.com/istio/istio.io.git
Add feature to generate diffs for helm values.yaml file between releases (#4402)
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: https://github.com/istio/istio.io/pull/3420 Fixes: #4384
This commit is contained in:
parent
9554bee587
commit
f8f2404d64
|
@ -23,6 +23,12 @@ import re
|
|||
|
||||
from ruamel import yaml
|
||||
|
||||
#
|
||||
# This script generates the installation options from the helm charts
|
||||
# for the current release (by parsing the values.yaml files under the
|
||||
# charts and subcharts directory).
|
||||
#
|
||||
|
||||
#
|
||||
# Reads a documented Helm values.yaml file and produces a
|
||||
# MD formatted table. pip install ruamel to obtain the proper
|
||||
|
@ -72,7 +78,7 @@ prdict = collections.defaultdict(list)
|
|||
def decode_helm_yaml(s):
|
||||
ret_val = ''
|
||||
#
|
||||
# Iterate through all the directories under /istio/install/kubernetes/heml/subcharts
|
||||
# Iterate through all the directories under /istio/install/kubernetes/helm/subcharts
|
||||
# and process the configuration options from the respective values.yaml. The
|
||||
# configuration option name is the name of the directory that contains values.yaml.
|
||||
# This name will be passed in to the the function process_helm_yaml
|
||||
|
@ -340,3 +346,4 @@ with open(os.path.join(ISTIO_IO_DIR, CONFIG_INDEX_DIR), 'r') as f:
|
|||
endReached = True
|
||||
if endReached:
|
||||
print d
|
||||
|
||||
|
|
|
@ -0,0 +1,602 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# Copyright 2017,2018 Istio 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.
|
||||
|
||||
import collections
|
||||
import linecache
|
||||
import string
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
|
||||
from ruamel import yaml
|
||||
|
||||
#
|
||||
# This script generates the installation option differences between the
|
||||
# current release and the previous release. It generates the index.md content for
|
||||
# the current release from the values.yaml files under the charts and subcharts
|
||||
# directory and compares the configuration options against the index.md from
|
||||
# the previous release (It gets the previous release version from the github api:
|
||||
# https://api.github.com/repos/istio/istio/branches).
|
||||
#
|
||||
|
||||
#
|
||||
# Reads a documented Helm values.yaml file and produces a
|
||||
# MD formatted table. pip install ruamel to obtain the proper
|
||||
# YAML decoder. ruamel maintains ordering and comments. The
|
||||
# comments are needed in order to decode the commented helm
|
||||
# values.yaml file
|
||||
#
|
||||
ISTIO_CONFIG_DIR = "istio/install/kubernetes/helm/istio"
|
||||
YAML_CONFIG_DIR = ISTIO_CONFIG_DIR + "/charts"
|
||||
VALUES_YAML = "values.yaml"
|
||||
ISTIO_IO_DIR = os.path.abspath(__file__ + "/../../")
|
||||
CONFIG_INDEX_DIR = "content/docs/reference/config/installation-options/index.md"
|
||||
CONFIG_INDEX_DIFF_DIR = "content/docs/reference/config/installation-options-changes/index.md"
|
||||
CONFIG_IGNORE_LIST = ["global.hub"]
|
||||
|
||||
def endOfTheList(context, lineNum, lastLineNum, totalNum):
|
||||
flag = 0
|
||||
valueList = []
|
||||
nextLineNum = lineNum + 1
|
||||
currentLine = context[lastLineNum]
|
||||
whitespaces = (len(currentLine) - len(currentLine.lstrip())) / 2
|
||||
|
||||
if lineNum != lastLineNum:
|
||||
return False, valueList
|
||||
|
||||
for nextLineNum in range(lineNum + 1, totalNum):
|
||||
nextLine = context[nextLineNum]
|
||||
|
||||
if len(nextLine.lstrip()) != 0 and '#' != nextLine.lstrip()[0] and ':' in nextLine:
|
||||
if whitespaces >= (len(nextLine) - len(nextLine.lstrip())) / 2:
|
||||
if flag == 0:
|
||||
valueList.append(currentLine.split(':', 1)[1].strip())
|
||||
return True, valueList
|
||||
else:
|
||||
return True, valueList
|
||||
elif len(nextLine.lstrip()) != 0 and '#' != nextLine.lstrip()[0] and ':' not in nextLine and len(nextLine.strip()) != 0:
|
||||
value = nextLine.replace(' ', '')
|
||||
valueList.append(value.lstrip('-').strip())
|
||||
flag += 1;
|
||||
nextLineNum += 1
|
||||
|
||||
if lastLineNum == totalNum - 1 and len(currentLine.lstrip()) != 0 and '#' != currentLine.lstrip()[0]:
|
||||
valueList.append(currentLine.split(':', 1)[1].strip())
|
||||
|
||||
return True, valueList
|
||||
|
||||
# ordered dictionary to store the configuration options for the subcomponents of Istio. This
|
||||
# will be used to populate a new index.md
|
||||
prdict = collections.defaultdict(list)
|
||||
# ordered dictionary to store the differences of configuration options between the new
|
||||
# index.md and the previous version (i.e, configurations options already listed in the index.md).
|
||||
od_diff = collections.defaultdict(list)
|
||||
od_diff_new = collections.defaultdict(list)
|
||||
od_diff_removed = collections.defaultdict(list)
|
||||
od_diff_unchanged = collections.defaultdict(list)
|
||||
|
||||
def decode_helm_yaml(s):
|
||||
ret_val = ''
|
||||
#
|
||||
# Iterate through all the directories under /istio/install/kubernetes/heml/subcharts
|
||||
# and process the configuration options from the respective values.yaml. The
|
||||
# configuration option name is the name of the directory that contains values.yaml.
|
||||
# This name will be passed in to the the function process_helm_yaml
|
||||
#
|
||||
subchart_dir = os.path.join(ISTIO_IO_DIR, YAML_CONFIG_DIR)
|
||||
for cfile in os.listdir(subchart_dir):
|
||||
values_yaml_dir = os.path.join(subchart_dir, cfile)
|
||||
values_yaml_file = os.path.join(values_yaml_dir, VALUES_YAML)
|
||||
process_helm_yaml(values_yaml_file, cfile)
|
||||
|
||||
#
|
||||
# Process configuration options in values.yaml under istio/install/kubernetes/helm/istio.
|
||||
# The configuration option names are present in the values.yaml, hence we do not need to
|
||||
# pass it to process_helm_yaml.
|
||||
#
|
||||
istio_yaml_config_dir = os.path.join(ISTIO_IO_DIR, ISTIO_CONFIG_DIR)
|
||||
values_yaml_file = os.path.join(istio_yaml_config_dir, VALUES_YAML)
|
||||
process_helm_yaml(values_yaml_file, '')
|
||||
|
||||
return ret_val
|
||||
|
||||
def process_helm_yaml(values_yaml, option):
|
||||
ret_val = ''
|
||||
storekey = ''
|
||||
desc = ''
|
||||
newkey = ''
|
||||
whitespaces = 0
|
||||
flag = 0
|
||||
lineNum = 0
|
||||
newConfigList = []
|
||||
loaded = None
|
||||
|
||||
context = linecache.getlines(values_yaml)
|
||||
totalNum = len(context)
|
||||
lastLineNum = 0
|
||||
key = option
|
||||
|
||||
count = 0
|
||||
with open(values_yaml, 'r') as f_v:
|
||||
d_v = f_v.read()
|
||||
loaded = yaml.round_trip_load(d_v)
|
||||
|
||||
for lineNum in range(0, totalNum):
|
||||
if context[lineNum].strip().startswith('- '):
|
||||
pass
|
||||
elif '#' in context[lineNum] and '#' == context[lineNum].lstrip()[0]:
|
||||
if "Description: " in context[lineNum]:
|
||||
desc = context[lineNum].strip()
|
||||
elif ':' in context[lineNum] and '#' != context[lineNum].lstrip()[0]:
|
||||
lastLineNum = lineNum
|
||||
if flag == 1:
|
||||
whitespaces = (len(context[lineNum]) - len(context[lineNum].lstrip())) / 2
|
||||
periods = key.count('.')
|
||||
if (option == ''):
|
||||
while (whitespaces <= periods):
|
||||
key = key.rstrip(string.ascii_letters[::-1] + string.digits + '_' + '-' + '/').rstrip('.')
|
||||
whitespaces += 1
|
||||
else:
|
||||
while (whitespaces < periods) :
|
||||
key = key.rstrip(string.ascii_letters[::-1] + string.digits + '_' + '-' + '/').rstrip('.')
|
||||
whitespaces += 1
|
||||
flag = 0
|
||||
|
||||
key = key + '.' + context[lineNum].split(':', 1)[0].strip()
|
||||
isEnd, ValueList = endOfTheList(context, lineNum, lastLineNum, totalNum)
|
||||
if isEnd == True:
|
||||
flag = 1;
|
||||
|
||||
storekey = key
|
||||
sk = storekey.split('.', 2)
|
||||
if len(sk) > 1:
|
||||
storekey = '.'.join(sk[:1]).lstrip('.')
|
||||
else:
|
||||
storekey = '.'.join(sk[:0]).lstrip('.')
|
||||
|
||||
#
|
||||
# If we are processing the configurations options within the values.yaml under istio,
|
||||
# if the options have already been processed (from the subcharts directory), then we
|
||||
# do not want to process it again. If the configuration option has not been processed
|
||||
# before, then it is a new configuration option which needs to be processed (for e.g,
|
||||
# global, istiocoredns)
|
||||
#
|
||||
# option == '' - This condition means that we are looking at the values.yaml under the
|
||||
# istio directory. Hence, the configuration option names will be inside
|
||||
# the values.yaml file. (On the other hand, for the values.yaml file under
|
||||
# the subcharts directory, we get the name of the configuration option
|
||||
# from the name of the directories under the subcharts directory.)
|
||||
# newConfigList - This list is used to track configuration options in istio/values.yaml
|
||||
# that haven't been processed before (or that does not have a corresponding
|
||||
# directory under subcharts directory with values.yaml. E.g: global,
|
||||
# istiocoredns)
|
||||
#
|
||||
# This first condition checks that if this is the values.yaml file under istio directory,
|
||||
# and the configuration option to process (storekey) has not already been processed (this
|
||||
# conditions: "prdict.get(storekey) != None and (storekey in newConfigList)" together
|
||||
# makes sure that the condition where some parameters for a new configuration option like
|
||||
# 'global' has been processed and entered into the dictionary 'prdict' is still processed
|
||||
# because it is in the newConfigList. If a configuration option was processed from
|
||||
# the values.yaml under the subcharts directory, it will not be in the newConfigList.
|
||||
# subcharts directory), then go ahead and process the parameters for this option.
|
||||
#
|
||||
if option == '' and prdict.get(storekey) != None and (storekey in newConfigList):
|
||||
pass
|
||||
#
|
||||
# This second condition checks if this is the values.yaml file under istio directory, and
|
||||
# the configuration option to process (storekey) has not been processed (this could
|
||||
# happen the first time we read a configuration option from the istio/values.yaml file),
|
||||
# then add this configuration option to the newConfigList to mark it as an option that
|
||||
# needs to be processed.
|
||||
#
|
||||
elif option == '' and prdict.get(storekey) == None:
|
||||
newConfigList.append(storekey)
|
||||
#
|
||||
# This third condition checks if this is the values.yaml file under istio directory,
|
||||
# and the configuration option to process (storekey) has already been processed and if
|
||||
# this is not a new configuration option, (this could happen if we have already
|
||||
# processed the corresponding values.yaml under the subcharts directory), then ignore
|
||||
# this configuration option and do not process the values in this file.
|
||||
#
|
||||
elif option == '' and prdict.get(storekey) != None:
|
||||
continue
|
||||
|
||||
if len(context[lastLineNum].lstrip()) != 0 and '#' != context[lastLineNum].lstrip()[0]:
|
||||
isEnd, ValueList = endOfTheList(context, lineNum, lastLineNum, totalNum)
|
||||
|
||||
if (isEnd == True):
|
||||
flag = 1
|
||||
keysplit = key.split('.')
|
||||
for kv in keysplit:
|
||||
if kv != '':
|
||||
newkey = newkey + '.' + kv
|
||||
|
||||
newkey = newkey.lstrip('.')
|
||||
|
||||
# Filling Description Fields
|
||||
if ( "." in newkey):
|
||||
plist = newkey.split('.')
|
||||
da = None
|
||||
for item in plist:
|
||||
desc = ''
|
||||
# If this is the same as the configuration option name, then
|
||||
# continue to the next key in the list
|
||||
if item.rstrip() == option.rstrip():
|
||||
continue
|
||||
if da is None:
|
||||
if loaded.ca.items:
|
||||
if item in loaded.ca.items:
|
||||
desc = processComments(loaded.ca.items[item])
|
||||
da = loaded[item]
|
||||
elif isinstance(da, dict):
|
||||
if item in da.keys()[0]:
|
||||
commentTokens = da.ca.comment
|
||||
if commentTokens is not None:
|
||||
desc = processComments(commentTokens)
|
||||
|
||||
if da.ca.items:
|
||||
if item in da.ca.items:
|
||||
desc = desc + processComments(da.ca.items[item])
|
||||
da = da[item]
|
||||
else:
|
||||
if item in da.keys():
|
||||
da = da.get(item)
|
||||
else:
|
||||
da = da.values()[0]
|
||||
|
||||
ValueStr = (' ').join(ValueList)
|
||||
if ValueStr:
|
||||
if (desc in ValueStr):
|
||||
ValueStr= ValueStr.replace("#"+desc, "")
|
||||
desc = desc.replace('`','')
|
||||
desc = sanitizeValueStr(desc)
|
||||
if desc.strip():
|
||||
desc = '`' + desc.strip() + '`'
|
||||
prdict[storekey].append("| `%s` | `%s` | %s |" % (newkey, ValueStr.rstrip(), desc))
|
||||
desc = ''
|
||||
|
||||
key = newkey
|
||||
newkey = ''
|
||||
|
||||
lineNum += 1
|
||||
return ret_val
|
||||
|
||||
def processComments(comments):
|
||||
description = ''
|
||||
for c in comments:
|
||||
if c is None:
|
||||
pass
|
||||
elif isinstance(c, list):
|
||||
for comment in c:
|
||||
if (comment is None):
|
||||
pass
|
||||
else:
|
||||
# We want to avoid including commented out key: value pairs in the values.yaml as
|
||||
# part of the description/comments. For example:
|
||||
# # minAvailable: 1
|
||||
# # maxUnavailable: 1
|
||||
# # - secretName: grafana-tls
|
||||
# sessionAffinityEnabled: false
|
||||
# We do not want the commented out key-value pairs (minAvailable,maxUnavailable, secretName)
|
||||
# to be included as part of the description for 'sessionAffinityEnabled'
|
||||
#
|
||||
pattern = re.compile("#\s[-\s]*[\S]+:(?:\s(?!\S+:)\S+)*" )
|
||||
groups = pattern.match(comment.value)
|
||||
if groups:
|
||||
description=''
|
||||
break
|
||||
if comment.value.endswith('\n\n'):
|
||||
description=''
|
||||
else:
|
||||
if comment.value.rstrip() == '#':
|
||||
continue
|
||||
else:
|
||||
description = description + comment.value.replace('`','').replace("#",'').rstrip()
|
||||
elif isinstance(c, yaml.Token):
|
||||
description = description + c.value.rstrip().replace("#",'')
|
||||
|
||||
return description
|
||||
|
||||
|
||||
def sanitizeValueStr(value):
|
||||
# We can include more special characters later if they need to
|
||||
# be escaped. For now just including the 'pipe' symbol appearing
|
||||
# in the value of a configuration option.
|
||||
# e.g: | `global.tracer.lightstep.secure` | `true # example: true\|false` | |
|
||||
#
|
||||
# Without escaping the 'pipe' character, it was interpreting it as the end/start
|
||||
# of table column. Using the example above, without escaping the pipe symbol, it
|
||||
# was interpreting it as:
|
||||
# | `global.tracer.lightstep.secure` | `true # example: true |false` | |
|
||||
#
|
||||
regex = re.compile("\|")
|
||||
if value != None and regex.search(value) != None:
|
||||
value = value.replace("|", "\|");
|
||||
return value
|
||||
|
||||
# Compares the configuration option value from the newly discovered set of values (stored
|
||||
# in prdict dictionary) and its previous version (stored in index.md). If there is no
|
||||
# change in the configuration option value between the 2 versions, it will be ignored. If
|
||||
# there are any differences, we will store the differences (will track differences for key,
|
||||
# value and description of a configuration option) in the 'od_diff' dictionary. The values
|
||||
# stored in this dictionary will later be written to CONFIG_INDEX_DIFF_DIR.
|
||||
#
|
||||
# The difference between the configuration option values is stored in the CONFIG_INDEX_DIFF_DIR
|
||||
# in the format:
|
||||
# | KEY | OLD DEFAULT VALUE | NEW DEFAULT VALUE | OLD DESCRIPTION | NEW DESCRIPTION |
|
||||
# | ------ | ------------ | ------------ | ------------ | ------------ |
|
||||
# | Key | oldValue | newValue | oldDesc | newDesc |
|
||||
#
|
||||
# If a configuration option is present only in the latest version, then the oldKey, oldValue
|
||||
# and oldDescription will be represented as 'n/a' (vice-versa applies to newKey, newValue and
|
||||
# newDescription).
|
||||
#
|
||||
# oValue - configuration option from the existing index.md
|
||||
# nValue - configuration option from the current processing of configuration options to be
|
||||
# stored in a new version of index.md
|
||||
# k - istio component name for which these configuration options are being processed. This is
|
||||
# used to populate the contents of 'od_diff' dictionary.
|
||||
#
|
||||
def compareValues(oValue, nValue, k):
|
||||
# oValue and nVAlue contains configuration option in the format:
|
||||
# '| `<Key>` | `<Value>` | `<Description>` |
|
||||
# This needs to be split in order to get the Key, Value and Description values to compare.
|
||||
oldKey = ''
|
||||
oldValue = ''
|
||||
oldDesc = ''
|
||||
|
||||
newKey = ''
|
||||
newValue = ''
|
||||
newDesc = ''
|
||||
|
||||
key = None
|
||||
|
||||
if nValue is not None:
|
||||
groups = re.search("\| \`(.*)\` \| \`(.*)\` \| (.*) |", nValue.strip())
|
||||
if groups:
|
||||
newKey = groups.group(1)
|
||||
newValue = groups.group(2)
|
||||
newDesc = groups.group(3)
|
||||
|
||||
if oValue is not None and nValue is not None:
|
||||
if len(oValue) == 1:
|
||||
item = oValue[0]
|
||||
|
||||
if item == nValue:
|
||||
key = newKey
|
||||
oValue.remove(item)
|
||||
od_diff_unchanged[k].append("| `%s` | `%s` | %s |" % (newKey, newValue.rstrip(), newDesc))
|
||||
else:
|
||||
groups = re.search("\| \`(.*)\` \| \`(.*)\` \|\s*(.*)\s*\|", item.strip())
|
||||
if groups:
|
||||
oldKey = groups.group(1)
|
||||
oldValue = groups.group(2)
|
||||
oldDesc = groups.group(3)
|
||||
key = oldKey
|
||||
|
||||
if oldKey in CONFIG_IGNORE_LIST:
|
||||
oValue.remove(item)
|
||||
return key
|
||||
|
||||
if oldValue != newValue:
|
||||
if oldValue is None:
|
||||
oldValue = 'n/a'
|
||||
if newValue is None:
|
||||
newValue = 'n/a'
|
||||
|
||||
if oldDesc.strip() != newDesc.strip():
|
||||
if (newDesc == None or newDesc == '') and (oldDesc is None or oldDesc == ''):
|
||||
pass
|
||||
if oldDesc is None:
|
||||
oldDesc = 'n/a'
|
||||
if newDesc is None or newDesc == '':
|
||||
newDesc = 'n/a'
|
||||
oValue.remove(item)
|
||||
od_diff[k].append("| `%s` | `%s` | `%s` | %s | %s |" % (newKey, oldValue.rstrip(), newValue.rstrip(), oldDesc, newDesc))
|
||||
else:
|
||||
# This is the case where values are the same but descriptions are different. Right now, there is nothing more to do since
|
||||
# we do not care about displaying values that haven't changed between releases.
|
||||
oValue.remove(item)
|
||||
|
||||
#od_diff_unchanged[k].append("| `%s` | `%s` | %s |" % (newKey, newValue.rstrip(), newDesc))
|
||||
else:
|
||||
foundItem = 'false'
|
||||
for item in oValue:
|
||||
if item == nValue:
|
||||
key = newKey
|
||||
oValue.remove(item)
|
||||
od_diff_unchanged[k].append("| `%s` | `%s` | %s |" % (newKey, newValue.rstrip(), newDesc))
|
||||
foundItem = 'true'
|
||||
break
|
||||
else:
|
||||
groups = re.search("\| \`(.*)\` \| \`(.*)\` \|\s*(.*)\s*\|", item.strip())
|
||||
if groups:
|
||||
oldKey = groups.group(1)
|
||||
oldValue = groups.group(2)
|
||||
oldDesc = groups.group(3)
|
||||
|
||||
if oldKey == newKey:
|
||||
if oldValue == newValue and oldDesc != newDesc:
|
||||
key = newKey
|
||||
od_diff[k].append("| `%s` | `%s` | `%s` | %s | %s |" % (newKey, oldValue.rstrip(), newValue.rstrip(), oldDesc, newDesc))
|
||||
oValue.remove(item)
|
||||
foundItem = 'true'
|
||||
break
|
||||
|
||||
if foundItem == 'false':
|
||||
od_diff_new[k].append("| `%s` | `%s` | %s |" % (newKey, newValue.rstrip(), newDesc))
|
||||
elif oValue is None:
|
||||
key = newKey
|
||||
od_diff_new[k].append("| `%s` | `%s` | %s |" % (newKey, newValue.rstrip(), newDesc))
|
||||
elif nValue is None:
|
||||
for item in oValue:
|
||||
groups = re.search("\| \`(.*)\` \| \`(.*)\` \|\s*(.*)\s*\|", item.strip())
|
||||
if groups:
|
||||
oldKey = groups.group(1)
|
||||
oldValue = groups.group(2)
|
||||
oldDesc = groups.group(3)
|
||||
|
||||
key = oldKey
|
||||
od_diff_removed[k].append("| `%s` | `%s` | %s |" % (oldKey, oldValue.rstrip(), oldDesc))
|
||||
|
||||
return key
|
||||
|
||||
#
|
||||
# Get the previous release number so that we can retrieve the index.md for that
|
||||
# release. The release branches are tagged in the following format: release-<number>
|
||||
#
|
||||
def getPreviousRelease():
|
||||
req = requests.get("https://api.github.com/repos/istio/istio/branches")
|
||||
jsonData = req.json()
|
||||
previousRelease = 0.0
|
||||
|
||||
for x in jsonData:
|
||||
releaseName = x['name']
|
||||
if releaseName.startswith('release-'):
|
||||
releaseNum = releaseName.split('release-')
|
||||
if releaseNum[1] > previousRelease:
|
||||
previousRelease = releaseNum[1]
|
||||
return previousRelease
|
||||
|
||||
#
|
||||
# Get the index.md for the previous release.
|
||||
#
|
||||
def getContentFromPreviousRelease(releaseName):
|
||||
istio_url = 'https://raw.githubusercontent.com/istio/istio.io/release-' + releaseName +'/content/docs/reference/config/installation-options/index.md'
|
||||
req = requests.get(istio_url)
|
||||
content = req.text
|
||||
indexMap = collections.defaultdict(list)
|
||||
|
||||
# store all the configurations options from the index.md file into the indexMap
|
||||
# dictionary. This will be used to compare the values with the latest version
|
||||
# of configuration options.
|
||||
data = content.split('\n')
|
||||
for d in data:
|
||||
if d.rstrip() != '' and d != '| Key | Default Value | Description |' and d != '| --- | --- | --- |' and d[0:1] == '|' and d[-1] == '|':
|
||||
groups = re.search("\| \`(.*)\` \| \`(.*)\` \| (.*) |", d.strip())
|
||||
if groups:
|
||||
key = groups.group(1)
|
||||
if key in indexMap:
|
||||
value = indexMap.get(key)
|
||||
value.append(d.strip())
|
||||
else:
|
||||
indexMap[key].append(d.strip())
|
||||
return indexMap
|
||||
|
||||
def writeVersionDiffs(index_diff_file):
|
||||
meta = ""
|
||||
|
||||
for d in index_diff_file:
|
||||
meta = meta + d
|
||||
if "<!-- AUTO-GENERATED-START -->" in d:
|
||||
break
|
||||
|
||||
index_diff_file.seek(0)
|
||||
index_diff_file.write(meta)
|
||||
|
||||
'''
|
||||
if od_diff_unchanged:
|
||||
index_diff_file.write('\n## Unmodified configuration options\n')
|
||||
|
||||
for k, v in od_diff_unchanged.items():
|
||||
index_diff_file.write("\n### Unmodified `%s` key/value pairs\n\n" % k)
|
||||
index_diff_file.write('| Key | Default Value | Description |\n')
|
||||
index_diff_file.write('| --- | --- | --- |\n')
|
||||
|
||||
for value in v:
|
||||
index_diff_file.write('%s\n' % (value))
|
||||
'''
|
||||
|
||||
if od_diff:
|
||||
index_diff_file.write('\n## Modified configuration options\n')
|
||||
|
||||
for k, v in od_diff.items():
|
||||
index_diff_file.write("\n### Modified `%s` key/value pairs\n\n" % k)
|
||||
index_diff_file.write('| Key | Old Default Value | New Default Value | Old Description | New Description |\n')
|
||||
index_diff_file.write('| --- | --- | --- | --- | --- |\n')
|
||||
|
||||
for value in v:
|
||||
index_diff_file.write('%s\n' % (value))
|
||||
|
||||
if od_diff_new:
|
||||
index_diff_file.write('\n## New configuration options\n')
|
||||
|
||||
for k, v in od_diff_new.items():
|
||||
index_diff_file.write("\n### New `%s` key/value pairs\n\n" % k)
|
||||
index_diff_file.write('| Key | Default Value | Description |\n')
|
||||
index_diff_file.write('| --- | --- | --- |\n')
|
||||
|
||||
for value in v:
|
||||
index_diff_file.write('%s\n' % (value))
|
||||
|
||||
if od_diff_removed:
|
||||
index_diff_file.write('\n## Removed configuration options\n')
|
||||
|
||||
for k, v in od_diff_removed.items():
|
||||
index_diff_file.write("\n### Removed `%s` key/value pairs\n\n" % k)
|
||||
index_diff_file.write('| Key | Default Value | Description |\n')
|
||||
index_diff_file.write('| --- | --- | --- |\n')
|
||||
|
||||
for value in v:
|
||||
index_diff_file.write('%s\n' % (value))
|
||||
|
||||
index_diff_file.write("\n<!-- AUTO-GENERATED-END -->\n")
|
||||
index_diff_file.truncate()
|
||||
|
||||
with open(os.path.join(ISTIO_IO_DIR, CONFIG_INDEX_DIR), 'r') as f:
|
||||
endReached = False
|
||||
key = ''
|
||||
# A list used to track the configuration options that has been compared and processed when going
|
||||
# through the configurations processed in the latest version
|
||||
indexList = []
|
||||
previousRelease = getPreviousRelease()
|
||||
indexMap = getContentFromPreviousRelease(previousRelease)
|
||||
|
||||
# transform values.yaml into a encoded string dictionary
|
||||
pyaml = yaml.YAML()
|
||||
pyaml.explicit_start = True
|
||||
pyaml.dump('', sys.stdout, transform=decode_helm_yaml)
|
||||
|
||||
# Order the encoded string dictionary
|
||||
od = collections.OrderedDict(sorted(prdict.items(), key=lambda t: t[0]))
|
||||
|
||||
# Print encoded string dictionary
|
||||
for k, v in od.items():
|
||||
for value in v:
|
||||
# Compare configuration option values from the latest version
|
||||
# with the older version.
|
||||
groups = re.search("\| \`(.*)\` \| \`(.*)\` \| (.*) |", value.strip())
|
||||
if groups:
|
||||
key = groups.group(1)
|
||||
indexValue = indexMap.get(key)
|
||||
|
||||
indexList.append(compareValues(indexValue, value, k))
|
||||
|
||||
# We want to include any configuration options that was discovered in
|
||||
# the older version but not available in the current version
|
||||
for k in indexMap.keys():
|
||||
key = k.split('.')[0]
|
||||
indexList.append(compareValues(indexMap.get(k), None, key))
|
||||
|
||||
# This index.md file is used to track the differences of configuration
|
||||
# option values between the current and previous release. All the
|
||||
# differences in configuration option values between the current
|
||||
# and previous release (tracked in the 'od_diff' dictionary) will be
|
||||
# written to the index.md file
|
||||
index_diff_file = open(os.path.join(ISTIO_IO_DIR, CONFIG_INDEX_DIFF_DIR), 'r+')
|
||||
writeVersionDiffs(index_diff_file)
|
||||
index_diff_file.close()
|
Loading…
Reference in New Issue