armada/armada/handlers/release_diff.py

111 lines
4.1 KiB
Python

# Copyright 2017 AT&T Intellectual Property. All other 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.
from deepdiff import DeepDiff
import yaml
from armada.exceptions import armada_exceptions
class ReleaseDiff(object):
'''
A utility for discovering diffs in helm release inputs, for example to
determine whether an upgrade is needed and what specific changes will be
applied.
Release inputs which are relevant are the override values given, and
the chart content including:
* default values (values.yaml),
* templates and their content
* files and their content
* the above for each chart on which the chart depends transitively.
This excludes Chart.yaml content as that is rarely used by the chart
via ``{{ .Chart }}``, and even when it is does not usually necessitate
an upgrade.
:param old_chart: The deployed chart.
:type old_chart: Chart
:param old_values: The deployed chart override values.
:type old_values: dict
:param new_chart: The chart to deploy.
:type new_chart: Chart
:param new_values: The chart override values to deploy.
:type new_values: dict
'''
def __init__(self, old_chart, old_values, new_chart, new_values):
self.old_chart = old_chart
self.old_values = old_values
self.new_chart = new_chart
self.new_values = new_values
def get_diff(self):
'''
Get the diff.
:return: Mapping of difference types to sets of those differences.
:rtype: dict
'''
old_input = self.make_release_input(
self.old_chart, self.old_values, 'previously deployed')
new_input = self.make_release_input(
self.new_chart, self.new_values, 'currently being deployed')
return DeepDiff(old_input, new_input, view='tree')
def make_release_input(self, chart, values, desc):
return {'chart': self.make_chart_dict(chart, desc), 'values': values}
def make_chart_dict(self, chart, desc):
try:
default_values = yaml.safe_load(chart.values.raw)
except yaml.YAMLError:
chart_desc = '{} ({})'.format(chart.metadata.name, desc)
raise armada_exceptions.InvalidValuesYamlException(chart_desc)
files = {f.type_url: f.value for f in chart.files}
# With armada/Chart/v1, Armada deployed releases with incorrect
# template names, omitting the `templates/` prefix, which is fixed in
# v2. This aligns these template names, so that the prefixes match, to
# avoid unwanted updates to releases when consuming this fix.
def fix_tpl_name(tpl_name):
CORRECT_PREFIX = 'templates/'
if tpl_name.startswith(CORRECT_PREFIX):
return tpl_name
return CORRECT_PREFIX + tpl_name
templates = {fix_tpl_name(t.name): t.data for t in chart.templates}
dependencies = {
d.metadata.name: self.make_chart_dict(
d, '{}({} dependency)'.format(desc, d.metadata.name))
for d in chart.dependencies
}
return {
# TODO(seaneagan): Are there use cases to include other
# `chart.metadata` (Chart.yaml) fields? If so, could include option
# under `upgrade` key in armada chart schema for this. Or perhaps
# can even add `upgrade.always` there to handle dynamic things
# used in charts like dates, environment variables, etc.
'name': chart.metadata.name,
'values': default_values,
'files': files,
'templates': templates,
'dependencies': dependencies
}