248 lines
9.4 KiB
Python
248 lines
9.4 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.
|
|
from copy import deepcopy
|
|
|
|
from oslo_log import log as logging
|
|
|
|
from armada import const
|
|
from armada import exceptions
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class Manifest(object):
|
|
|
|
def __init__(self, documents, target_manifest=None):
|
|
"""Instantiates a Manifest object.
|
|
|
|
An Armada Manifest expects that at least one of each of the following
|
|
be included in ``documents``:
|
|
|
|
* A document with schema "armada/Chart/v1"
|
|
* A document with schema "armada/ChartGroup/v1"
|
|
|
|
And only one document of the following is allowed:
|
|
|
|
* A document with schema "armada/Manifest/v1"
|
|
|
|
If multiple documents with schema "armada/Manifest/v1" are provided,
|
|
specify ``target_manifest`` to select the target one.
|
|
|
|
:param List[dict] documents: Documents out of which to build the
|
|
Armada Manifest.
|
|
:param str target_manifest: The target manifest to use when multiple
|
|
documents with "armada/Manifest/v1" are contained in
|
|
``documents``. Default is None.
|
|
:raises ManifestException: If the expected number of document types
|
|
are not found or if the document types are missing required
|
|
properties.
|
|
"""
|
|
self.documents = deepcopy(documents)
|
|
self.charts, self.groups, manifests = self._find_documents(
|
|
target_manifest)
|
|
|
|
if len(manifests) > 1:
|
|
error = ('Multiple manifests are not supported. Ensure that the '
|
|
'`target_manifest` option is set to specify the target '
|
|
'manifest')
|
|
LOG.error(error)
|
|
raise exceptions.ManifestException(details=error)
|
|
else:
|
|
self.manifest = manifests[0] if manifests else None
|
|
|
|
if not all([self.charts, self.groups, self.manifest]):
|
|
expected_schemas = [const.DOCUMENT_CHART, const.DOCUMENT_GROUP]
|
|
error = ('Documents must be a list of documents with at least one '
|
|
'of each of the following schemas: %s and only one '
|
|
'manifest' % expected_schemas)
|
|
LOG.error(error)
|
|
raise exceptions.ManifestException(details=error)
|
|
|
|
def _find_documents(self, target_manifest=None):
|
|
"""Returns the chart documents, chart group documents,
|
|
and Armada manifest
|
|
|
|
If multiple documents with schema "armada/Manifest/v1" are provided,
|
|
specify ``target_manifest`` to select the target one.
|
|
|
|
:param str target_manifest: The target manifest to use when multiple
|
|
documents with "armada/Manifest/v1" are contained in
|
|
``documents``. Default is None.
|
|
:returns: Tuple of chart documents, chart groups, and manifests
|
|
found in ``self.documents``
|
|
:rtype: tuple
|
|
"""
|
|
charts = []
|
|
groups = []
|
|
manifests = []
|
|
for document in self.documents:
|
|
if document.get('schema') == const.DOCUMENT_CHART:
|
|
charts.append(document)
|
|
if document.get('schema') == const.DOCUMENT_GROUP:
|
|
groups.append(document)
|
|
if document.get('schema') == const.DOCUMENT_MANIFEST:
|
|
manifest_name = document.get('metadata', {}).get('name')
|
|
if target_manifest:
|
|
if manifest_name == target_manifest:
|
|
manifests.append(document)
|
|
else:
|
|
manifests.append(document)
|
|
return charts, groups, manifests
|
|
|
|
def find_chart_document(self, name):
|
|
"""Returns a chart document with the specified name
|
|
|
|
:param str name: name of the desired chart document
|
|
:returns: The requested chart document
|
|
:rtype: dict
|
|
:raises ManifestException: If a chart document with the
|
|
specified name is not found
|
|
"""
|
|
for chart in self.charts:
|
|
if chart.get('metadata', {}).get('name') == name:
|
|
return chart
|
|
raise exceptions.ManifestException(
|
|
details='Could not find a {} named "{}"'.format(
|
|
const.DOCUMENT_CHART, name))
|
|
|
|
def find_chart_group_document(self, name):
|
|
"""Returns a chart group document with the specified name
|
|
|
|
:param str name: name of the desired chart group document
|
|
:returns: The requested chart group document
|
|
:rtype: dict
|
|
:raises ManifestException: If a chart
|
|
group document with the specified name is not found
|
|
"""
|
|
for group in self.groups:
|
|
if group.get('metadata', {}).get('name') == name:
|
|
return group
|
|
raise exceptions.ManifestException(
|
|
details='Could not find a {} named "{}"'.format(
|
|
const.DOCUMENT_GROUP, name))
|
|
|
|
def build_charts_deps(self):
|
|
"""Build chart dependencies for every ``chart``.
|
|
|
|
:returns: None
|
|
"""
|
|
for chart in self.charts:
|
|
self.build_chart_deps(chart)
|
|
|
|
def build_chart_groups(self):
|
|
"""Build chart dependencies for every ``chart_group``.
|
|
|
|
:returns: None
|
|
"""
|
|
for chart_group in self.groups:
|
|
self.build_chart_group(chart_group)
|
|
|
|
def build_chart_deps(self, chart):
|
|
"""Recursively build chart dependencies for ``chart``.
|
|
|
|
:param dict chart: The chart whose dependencies will be recursively
|
|
built.
|
|
:returns: The chart with all dependencies.
|
|
:rtype: dict
|
|
:raises ManifestException: If a chart for a dependency name listed
|
|
under ``chart['data']['dependencies']`` could not be found.
|
|
"""
|
|
try:
|
|
dep = None
|
|
chart_dependencies = chart.get('data', {}).get('dependencies', [])
|
|
for iter, dep in enumerate(chart_dependencies):
|
|
if isinstance(dep, dict):
|
|
continue
|
|
chart_dep = self.find_chart_document(dep)
|
|
self.build_chart_deps(chart_dep)
|
|
chart['data']['dependencies'][iter] = {
|
|
'chart': chart_dep.get('data', {})
|
|
}
|
|
except Exception:
|
|
raise exceptions.ManifestException(
|
|
details="Could not find dependency chart {} in {}".format(
|
|
dep, const.DOCUMENT_CHART))
|
|
else:
|
|
return chart
|
|
|
|
def build_chart_group(self, chart_group):
|
|
"""Builds the chart dependencies for`charts`chart group``.
|
|
|
|
:param dict chart_group: The chart_group whose dependencies
|
|
will be built.
|
|
:returns: The chart_group with all dependencies.
|
|
:rtype: dict
|
|
:raises ManifestException: If a chart for a dependency name listed
|
|
under ``chart_group['data']['chart_group']`` could not be found.
|
|
"""
|
|
try:
|
|
chart = None
|
|
for iter, chart in enumerate(
|
|
chart_group.get('data', {}).get('chart_group', [])):
|
|
if isinstance(chart, dict):
|
|
continue
|
|
chart_dep = self.find_chart_document(chart)
|
|
chart_group['data']['chart_group'][iter] = {
|
|
'chart': chart_dep.get('data', {})
|
|
}
|
|
except Exception:
|
|
raise exceptions.ManifestException(
|
|
details="Could not find chart {} in {}".format(
|
|
chart, const.DOCUMENT_GROUP))
|
|
else:
|
|
return chart_group
|
|
|
|
def build_armada_manifest(self):
|
|
"""Builds the Armada manifest while pulling out data
|
|
from the chart_group.
|
|
|
|
:returns: The Armada manifest with the data of the chart groups.
|
|
:rtype: dict
|
|
:raises ManifestException: If a chart group's data listed
|
|
under ``chart_group['data']`` could not be found.
|
|
"""
|
|
try:
|
|
group = None
|
|
for iter, group in enumerate(
|
|
self.manifest.get('data', {}).get('chart_groups', [])):
|
|
if isinstance(group, dict):
|
|
continue
|
|
chart_grp = self.find_chart_group_document(group)
|
|
|
|
# Add name to chart group
|
|
ch_grp_data = chart_grp.get('data', {})
|
|
ch_grp_data['name'] = chart_grp.get('metadata', {}).get('name')
|
|
|
|
self.manifest['data']['chart_groups'][iter] = ch_grp_data
|
|
except Exception:
|
|
raise exceptions.ManifestException(
|
|
"Could not find chart group {} in {}".format(
|
|
group, const.DOCUMENT_MANIFEST))
|
|
else:
|
|
return self.manifest
|
|
|
|
def get_manifest(self):
|
|
"""Builds all of the documents including the dependencies of the
|
|
chart documents, the charts in the chart_groups, and the
|
|
Armada manifest
|
|
|
|
:returns: The Armada manifest.
|
|
:rtype: dict
|
|
"""
|
|
self.build_charts_deps()
|
|
self.build_chart_groups()
|
|
self.build_armada_manifest()
|
|
|
|
return {'armada': self.manifest.get('data', {})}
|