armada/armada/handlers/chartbuilder.py

114 lines
4.3 KiB
Python

# Copyright 2017 The Armada Authors.
#
# 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 os
from pathlib import Path
from shutil import rmtree
from oslo_config import cfg
from oslo_log import log as logging
from armada import const
from armada.exceptions import chartbuilder_exceptions
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
class ChartBuilder(object):
'''
This class handles taking chart intentions as a parameter and turning those
into proper Helm chart metadata.
'''
@classmethod
def from_chart_doc(cls, chart, helm):
'''
Returns a ChartBuilder defined by an Armada Chart doc.
:param chart: Armada Chart doc for which to build the Helm chart.
'''
name = chart['metadata']['name']
chart_data = chart[const.KEYWORD_DATA]
source_dir = chart_data['source_dir']
source_directory = os.path.join(*source_dir)
dependencies = chart_data.get('dependencies')
if dependencies is not None:
# Ensure `charts` dir exists and is empty.
charts_dir = os.path.join(source_directory, 'charts')
charts_path = Path(charts_dir)
if charts_path.is_dir():
# NOTE: Ideally we would only delete the subcharts being
# overridden, and leave the others in place, but we delete all
# for backward compatibility with the Helm 2 based Armada.
(rmtree(d) for d in charts_path.iterdir() if d.is_dir())
else:
if charts_path.exists():
# NOTE: Ideally we would throw an error if `charts` is a
# non-directory, but we don't for backward compatibility
# with the Helm 2 based Armada.
charts_path.unlink()
charts_path.mkdir()
# Add symlinks to dependencies into `charts` dir.
for chart_dep in dependencies:
# Handle any recursive dependencies.
ChartBuilder.from_chart_doc(chart_dep, helm)
dep_data = chart_dep[const.KEYWORD_DATA]
dep_source_dir = dep_data['source_dir']
dep_source_directory = os.path.join(*dep_source_dir)
dep_charts_yaml = helm.show_chart(dep_source_directory)
dep_name = dep_charts_yaml['name']
dep_target_directory = os.path.join(charts_dir, dep_name)
Path(dep_target_directory).symlink_to(dep_source_directory)
return cls(name, source_directory, helm)
def __init__(self, name, source_directory, helm):
'''
:param name: A name to use for the chart.
:param source_directory: The source directory of the Helm chart.
:param helm: Helm client to calculate the helm chart object.
'''
self.name = name
self.source_directory = source_directory
self.helm = helm
# cache for generated chart object
self._helm_chart = None
# We do a dry-run upgrade here to get the helm chart metadata.
# Ideally helm would support an explicit machine readable way to
# get that data so we don't need the dry run upgrade which could
# fail for other reasons than not being able to get the chart
# metadata, see:
# https://github.com/helm/helm/issues/9968
def get_helm_chart(self, release_id, values):
'''Return a Helm chart object.
'''
LOG.debug(
"Building chart %s from path %s", self.name, self.source_directory)
try:
result = self.helm.upgrade_release(
self.source_directory, release_id, values=values, dry_run=True)
return result['chart']
except Exception as e:
raise chartbuilder_exceptions.HelmChartBuildException(
self.name, details=e)