Support builtin chart dependencies
This adds support for using the same builtin chart dependencies [0] as the Helm CLI would use. [0]: https://helm.sh/docs/developing_charts/#chart-dependencies Change-Id: Ifc541dc273fa2a5c5b4e43125f468ea3fdb0f379
This commit is contained in:
parent
77deecc294
commit
f502f33e9f
|
@ -76,7 +76,7 @@ class ChartDeploy(object):
|
|||
# Begin Chart timeout deadline
|
||||
deadline = time.time() + chart_wait.get_timeout()
|
||||
|
||||
chartbuilder = ChartBuilder(ch)
|
||||
chartbuilder = ChartBuilder.from_chart(ch)
|
||||
new_chart = chartbuilder.get_helm_chart()
|
||||
|
||||
# TODO(mark-burnett): It may be more robust to directly call
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
from google.protobuf.any_pb2 import Any
|
||||
from hapi.chart.chart_pb2 import Chart
|
||||
|
@ -37,38 +38,51 @@ class ChartBuilder(object):
|
|||
into proper ``protoc`` Helm charts that can be pushed to Tiller.
|
||||
'''
|
||||
|
||||
def __init__(self, chart):
|
||||
'''Initialize the :class:`ChartBuilder` class.
|
||||
@classmethod
|
||||
def from_chart(cls, chart):
|
||||
name = chart['metadata']['name']
|
||||
chart_data = chart[const.KEYWORD_DATA]
|
||||
source_dir = chart_data.get('source_dir')
|
||||
source_directory = os.path.join(*source_dir)
|
||||
dependencies = chart_data.get('dependencies')
|
||||
dependency_builders = None
|
||||
if dependencies:
|
||||
dependency_builders = []
|
||||
for chart_dep in dependencies:
|
||||
builder = ChartBuilder.from_chart(chart_dep)
|
||||
dependency_builders.append(builder)
|
||||
|
||||
:param dict chart: The document containing all intentions to pass to
|
||||
Tiller.
|
||||
'''
|
||||
return cls(name, source_directory, dependency_builders)
|
||||
|
||||
def __init__(self, name, source_directory, dependency_builders=None):
|
||||
self.name = name
|
||||
self.source_directory = source_directory
|
||||
if dependency_builders is None:
|
||||
dependency_builders = []
|
||||
charts_dir = os.path.join(source_directory, 'charts')
|
||||
if os.path.isdir(charts_dir):
|
||||
for f in os.scandir(charts_dir):
|
||||
if not f.is_dir():
|
||||
# TODO: Support ".tgz" dependency charts.
|
||||
|
||||
# Ignore regular files.
|
||||
continue
|
||||
|
||||
# Ignore directories that start with "." or "_".
|
||||
if re.match(r'^[._]', f.name):
|
||||
continue
|
||||
|
||||
source_directory = os.path.join(charts_dir, f.name)
|
||||
builder = ChartBuilder(f.name, source_directory)
|
||||
dependency_builders.append(builder)
|
||||
self.dependency_builders = dependency_builders
|
||||
|
||||
# cache for generated protoc chart object
|
||||
self._helm_chart = None
|
||||
|
||||
# store chart schema
|
||||
self.chart = chart
|
||||
self.chart_data = chart[const.KEYWORD_DATA]
|
||||
|
||||
# extract, pull, whatever the chart from its source
|
||||
self.source_directory = self.get_source_path()
|
||||
|
||||
# load ignored files from .helmignore if present
|
||||
self.ignored_files = self.get_ignored_files()
|
||||
|
||||
def get_source_path(self):
|
||||
'''Return the joined path of the source directory and subpath.
|
||||
|
||||
Returns "<source directory>/<subpath>" taken from the "source_dir"
|
||||
property from the chart, or else "" if the property isn't a 2-tuple.
|
||||
'''
|
||||
source_dir = self.chart_data.get('source_dir')
|
||||
return (
|
||||
os.path.join(*source_dir) if (
|
||||
source_dir and isinstance(source_dir, (list, tuple))
|
||||
and len(source_dir) == 2) else "")
|
||||
|
||||
def get_ignored_files(self):
|
||||
'''Load files to ignore from .helmignore if present.'''
|
||||
try:
|
||||
|
@ -209,7 +223,7 @@ class ChartBuilder(object):
|
|||
Process all files in templates/ as a template to attach to the chart,
|
||||
building a :class:`hapi.chart.template_pb2.Template` object.
|
||||
'''
|
||||
chart_name = self.chart['metadata']['name']
|
||||
chart_name = self.name
|
||||
templates = []
|
||||
if not os.path.exists(os.path.join(self.source_directory,
|
||||
'templates')):
|
||||
|
@ -238,22 +252,21 @@ class ChartBuilder(object):
|
|||
Constructs a :class:`hapi.chart.chart_pb2.Chart` object from the
|
||||
``chart`` intentions, including all dependencies.
|
||||
'''
|
||||
if self._helm_chart:
|
||||
return self._helm_chart
|
||||
if not self._helm_chart:
|
||||
self._helm_chart = self._get_helm_chart()
|
||||
|
||||
return self._helm_chart
|
||||
|
||||
def _get_helm_chart(self):
|
||||
dependencies = []
|
||||
chart_dependencies = self.chart_data.get('dependencies', [])
|
||||
chart_name = self.chart['metadata']['name']
|
||||
chart_release = self.chart_data.get('release', None)
|
||||
for dep_chart in chart_dependencies:
|
||||
dep_chart_name = dep_chart['metadata']['name']
|
||||
for dep_builder in self.dependency_builders:
|
||||
LOG.info(
|
||||
"Building dependency chart %s for release %s.", dep_chart_name,
|
||||
chart_release)
|
||||
"Building dependency chart %s for chart %s.", dep_builder.name,
|
||||
self.name)
|
||||
try:
|
||||
dependencies.append(ChartBuilder(dep_chart).get_helm_chart())
|
||||
dependencies.append(dep_builder.get_helm_chart())
|
||||
except Exception:
|
||||
raise chartbuilder_exceptions.DependencyException(chart_name)
|
||||
raise chartbuilder_exceptions.DependencyException(self.name)
|
||||
|
||||
try:
|
||||
helm_chart = Chart(
|
||||
|
@ -264,9 +277,8 @@ class ChartBuilder(object):
|
|||
files=self.get_files())
|
||||
except Exception as e:
|
||||
raise chartbuilder_exceptions.HelmChartBuildException(
|
||||
chart_name, details=e)
|
||||
self.name, details=e)
|
||||
|
||||
self._helm_chart = helm_chart
|
||||
return helm_chart
|
||||
|
||||
def dump(self):
|
||||
|
|
|
@ -169,7 +169,6 @@ data:
|
|||
- no_hooks
|
||||
additionalProperties: false
|
||||
required:
|
||||
- dependencies
|
||||
- namespace
|
||||
- chart_name
|
||||
- release
|
||||
|
|
|
@ -366,7 +366,7 @@ class ArmadaHandlerTestCase(base.ArmadaTestCase):
|
|||
|
||||
@mock.patch.object(armada.Armada, 'post_flight_ops')
|
||||
@mock.patch.object(armada.Armada, 'pre_flight_ops')
|
||||
@mock.patch('armada.handlers.chart_deploy.ChartBuilder')
|
||||
@mock.patch('armada.handlers.chart_deploy.ChartBuilder.from_chart')
|
||||
@mock.patch('armada.handlers.chart_deploy.Test')
|
||||
def _do_test(
|
||||
mock_test, mock_chartbuilder, mock_pre_flight,
|
||||
|
@ -399,7 +399,6 @@ class ArmadaHandlerTestCase(base.ArmadaTestCase):
|
|||
mock_test_release.return_value = test_success
|
||||
|
||||
# Stub out irrelevant methods called by `armada.sync()`.
|
||||
mock_chartbuilder.get_source_path.return_value = None
|
||||
mock_chartbuilder.get_helm_chart.return_value = None
|
||||
|
||||
# Simulate chart diff, upgrade should only happen if non-empty.
|
||||
|
|
|
@ -145,7 +145,7 @@ class ChartBuilderTestCase(BaseChartBuilderTestCase):
|
|||
self._write_temporary_file_contents(
|
||||
chart_dir.path, 'Chart.yaml', self.chart_yaml)
|
||||
|
||||
chartbuilder = ChartBuilder(self._get_test_chart(chart_dir))
|
||||
chartbuilder = ChartBuilder.from_chart(self._get_test_chart(chart_dir))
|
||||
|
||||
# Validate response type is :class:`hapi.chart.metadata_pb2.Metadata`
|
||||
resp = chartbuilder.get_metadata()
|
||||
|
@ -155,7 +155,7 @@ class ChartBuilderTestCase(BaseChartBuilderTestCase):
|
|||
chart_dir = self.useFixture(fixtures.TempDir())
|
||||
self.addCleanup(shutil.rmtree, chart_dir.path)
|
||||
|
||||
chartbuilder = ChartBuilder(self._get_test_chart(chart_dir))
|
||||
chartbuilder = ChartBuilder.from_chart(self._get_test_chart(chart_dir))
|
||||
|
||||
self.assertRaises(
|
||||
chartbuilder_exceptions.MetadataLoadException,
|
||||
|
@ -181,7 +181,7 @@ class ChartBuilderTestCase(BaseChartBuilderTestCase):
|
|||
for filename in ['template%d' % x for x in range(3)]:
|
||||
self._write_temporary_file_contents(templates_subdir, filename, "")
|
||||
|
||||
chartbuilder = ChartBuilder(self._get_test_chart(chart_dir))
|
||||
chartbuilder = ChartBuilder.from_chart(self._get_test_chart(chart_dir))
|
||||
|
||||
expected_files = (
|
||||
'[type_url: "%s"\n, type_url: "%s"\n]' % ('./bar', './foo'))
|
||||
|
@ -197,7 +197,7 @@ class ChartBuilderTestCase(BaseChartBuilderTestCase):
|
|||
self._write_temporary_file_contents(
|
||||
chart_dir.path, filename, "DIRC^@^@^@^B^@^@^@×Z®<86>F.1")
|
||||
|
||||
chartbuilder = ChartBuilder(self._get_test_chart(chart_dir))
|
||||
chartbuilder = ChartBuilder.from_chart(self._get_test_chart(chart_dir))
|
||||
chartbuilder.get_files()
|
||||
|
||||
def test_get_basic_helm_chart(self):
|
||||
|
@ -212,7 +212,7 @@ class ChartBuilderTestCase(BaseChartBuilderTestCase):
|
|||
ch['data']['source_dir'] = (chart_dir.path, '')
|
||||
|
||||
test_chart = ch
|
||||
chartbuilder = ChartBuilder(test_chart)
|
||||
chartbuilder = ChartBuilder.from_chart(test_chart)
|
||||
helm_chart = chartbuilder.get_helm_chart()
|
||||
|
||||
expected = inspect.cleandoc(
|
||||
|
@ -244,7 +244,7 @@ class ChartBuilderTestCase(BaseChartBuilderTestCase):
|
|||
ch['data']['source_dir'] = (chart_dir.path, '')
|
||||
|
||||
test_chart = ch
|
||||
chartbuilder = ChartBuilder(test_chart)
|
||||
chartbuilder = ChartBuilder.from_chart(test_chart)
|
||||
helm_chart = chartbuilder.get_helm_chart()
|
||||
|
||||
self.assertIsInstance(helm_chart, Chart)
|
||||
|
@ -273,7 +273,7 @@ class ChartBuilderTestCase(BaseChartBuilderTestCase):
|
|||
ch['data']['source_dir'] = (chart_dir.path, '')
|
||||
|
||||
test_chart = ch
|
||||
chartbuilder = ChartBuilder(test_chart)
|
||||
chartbuilder = ChartBuilder.from_chart(test_chart)
|
||||
helm_chart = chartbuilder.get_helm_chart()
|
||||
|
||||
expected_files = (
|
||||
|
@ -315,14 +315,14 @@ class ChartBuilderTestCase(BaseChartBuilderTestCase):
|
|||
# Files to ignore within templates/ subdirectory.
|
||||
self._write_temporary_file_contents(
|
||||
templates_subdir, file_to_ignore, "")
|
||||
# Files to ignore within charts/ subdirectory.
|
||||
self._write_temporary_file_contents(charts_subdir, file_to_ignore, "")
|
||||
# Files to ignore within templates/bin subdirectory.
|
||||
self._write_temporary_file_contents(
|
||||
templates_nested_subdir, file_to_ignore, "")
|
||||
# Files to ignore within charts/extra subdirectory.
|
||||
self._write_temporary_file_contents(
|
||||
charts_nested_subdir, file_to_ignore, "")
|
||||
self._write_temporary_file_contents(
|
||||
charts_nested_subdir, 'Chart.yaml', self.chart_yaml)
|
||||
# Files to **include** within charts/ subdirectory.
|
||||
self._write_temporary_file_contents(charts_subdir, '.prov', "xyzzy")
|
||||
|
||||
|
@ -330,7 +330,7 @@ class ChartBuilderTestCase(BaseChartBuilderTestCase):
|
|||
ch['data']['source_dir'] = (chart_dir.path, '')
|
||||
|
||||
test_chart = ch
|
||||
chartbuilder = ChartBuilder(test_chart)
|
||||
chartbuilder = ChartBuilder.from_chart(test_chart)
|
||||
helm_chart = chartbuilder.get_helm_chart()
|
||||
|
||||
expected_files = (
|
||||
|
@ -369,7 +369,7 @@ class ChartBuilderTestCase(BaseChartBuilderTestCase):
|
|||
dependency_chart = dep_ch
|
||||
main_chart['data']['dependencies'] = [dependency_chart]
|
||||
|
||||
chartbuilder = ChartBuilder(main_chart)
|
||||
chartbuilder = ChartBuilder.from_chart(main_chart)
|
||||
helm_chart = chartbuilder.get_helm_chart()
|
||||
|
||||
expected_dependency = inspect.cleandoc(
|
||||
|
@ -429,7 +429,7 @@ class ChartBuilderTestCase(BaseChartBuilderTestCase):
|
|||
ch['data']['source_dir'] = (chart_dir.path, '')
|
||||
|
||||
test_chart = ch
|
||||
chartbuilder = ChartBuilder(test_chart)
|
||||
chartbuilder = ChartBuilder.from_chart(test_chart)
|
||||
self.assertRegex(
|
||||
repr(chartbuilder.dump()),
|
||||
'hello-world-chart.*A sample Helm chart for Kubernetes.*')
|
||||
|
@ -444,7 +444,7 @@ class ChartBuilderTestCase(BaseChartBuilderTestCase):
|
|||
|
||||
dependency_chart = dep_ch
|
||||
test_chart['data']['dependencies'] = [dependency_chart]
|
||||
chartbuilder = ChartBuilder(test_chart)
|
||||
chartbuilder = ChartBuilder.from_chart(test_chart)
|
||||
|
||||
re = inspect.cleandoc(
|
||||
"""
|
||||
|
@ -473,7 +473,7 @@ class ChartBuilderNegativeTestCase(BaseChartBuilderTestCase):
|
|||
self._write_temporary_file_contents(
|
||||
chart_dir.path, filename, "DIRC^@^@^@^B^@^@^@×Z®<86>F.1")
|
||||
|
||||
chartbuilder = ChartBuilder(self._get_test_chart(chart_dir))
|
||||
chartbuilder = ChartBuilder.from_chart(self._get_test_chart(chart_dir))
|
||||
|
||||
# Confirm it failed for both encodings.
|
||||
error_re = (
|
||||
|
@ -494,7 +494,7 @@ class ChartBuilderNegativeTestCase(BaseChartBuilderTestCase):
|
|||
self._write_temporary_file_contents(
|
||||
chart_dir.path, filename, "DIRC^@^@^@^B^@^@^@×Z®<86>F.1")
|
||||
|
||||
chartbuilder = ChartBuilder(self._get_test_chart(chart_dir))
|
||||
chartbuilder = ChartBuilder.from_chart(self._get_test_chart(chart_dir))
|
||||
|
||||
side_effects = [self.exc_to_raise, "", ""]
|
||||
with mock.patch("builtins.open", mock.mock_open(read_data="")) \
|
||||
|
|
|
@ -52,9 +52,9 @@ Chart
|
|||
| ``upgrade.options.no_hooks``, | |
|
||||
| and now optional | |
|
||||
+--------------------------------+------------------------------------------------------------+
|
||||
| ``dependencies``, | Remove as desired. |
|
||||
| ``source.subpath`` | |
|
||||
| now optional | |
|
||||
| ``source.subpath`` | Remove as desired. |
|
||||
| now optional, deafults to no | |
|
||||
| subpath. | |
|
||||
+--------------------------------+------------------------------------------------------------+
|
||||
| ``wait`` improvements | See `Wait Improvements`_. |
|
||||
+--------------------------------+------------------------------------------------------------+
|
||||
|
|
|
@ -134,7 +134,9 @@ Chart
|
|||
+-----------------+----------+---------------------------------------------------------------------------------------+
|
||||
| source | object | provide a path to a ``git repo``, ``local dir``, or ``tarball url`` chart |
|
||||
+-----------------+----------+---------------------------------------------------------------------------------------+
|
||||
| dependencies | object | reference any chart dependencies before install |
|
||||
| dependencies | object | (optional) reference any chart dependencies before install. |
|
||||
| | | Defaults to using the standard `Builtin Chart Dependencies`_ as the Helm CLI does. |
|
||||
| | | NOTE: Dependencies stored as ".tgz" archives are not yet supported. |
|
||||
+-----------------+----------+---------------------------------------------------------------------------------------+
|
||||
| timeout | int | time (in seconds) allotted for chart to deploy when 'wait' flag is set (DEPRECATED) |
|
||||
+-----------------+----------+---------------------------------------------------------------------------------------+
|
||||
|
@ -589,3 +591,5 @@ References
|
|||
|
||||
For working examples please check the examples in our repo
|
||||
`here <https://opendev.org/airship/armada/src/branch/master/examples>`__.
|
||||
|
||||
.. _Builtin Chart Dependencies: https://helm.sh/docs/developing_charts/#chart-dependencies
|
||||
|
|
|
@ -121,7 +121,9 @@ Chart
|
|||
+-----------------+----------+---------------------------------------------------------------------------------------+
|
||||
| source | object | provide a path to a ``git repo``, ``local dir``, or ``tarball url`` chart |
|
||||
+-----------------+----------+---------------------------------------------------------------------------------------+
|
||||
| dependencies | object | (optional) reference any chart dependencies before install |
|
||||
| dependencies | object | (optional) reference any chart dependencies before install. |
|
||||
| | | Defaults to using the standard `Builtin Chart Dependencies`_ as the Helm CLI does. |
|
||||
| | | NOTE: Dependencies stored as ".tgz" archives are not yet supported. |
|
||||
+-----------------+----------+---------------------------------------------------------------------------------------+
|
||||
|
||||
.. _wait_v2:
|
||||
|
@ -574,4 +576,7 @@ References
|
|||
~~~~~~~~~~
|
||||
|
||||
For working examples please check the examples in our repo
|
||||
`here <https://opendev.org/airship/armada/src/branch/master/examples>`__.
|
||||
`here <https://github.com/openstack/airship-armada/tree/master/examples>`__
|
||||
|
||||
|
||||
.. _Builtin Chart Dependencies: https://helm.sh/docs/developing_charts/#chart-dependencies
|
||||
|
|
Loading…
Reference in New Issue