bug(manifest): Allow specific manifest to be specified
This PS allows users to specify the manifest file to use by the Armada handler by introducing a new flag called `target_manifest`. This flag was added to the API and CLI. A foundation of unit tests for the manifest handler is included in this PS. Most of the coverage is aimed at checking the various success and failure cases surrounding the new target_manifest feature. Also updates documentation to convey information about the new flag and clean up some documentation formatting inconsistencies and typos. Change-Id: I1d5a3ecc1e99b6479438d0ee5490610178be34fe
This commit is contained in:
parent
62338b394f
commit
093b5d2296
@ -19,19 +19,19 @@ import falcon
|
||||
|
||||
from armada import api
|
||||
from armada.common import policy
|
||||
from armada import exceptions
|
||||
from armada.handlers.armada import Armada
|
||||
from armada.handlers.document import ReferenceResolver
|
||||
from armada.handlers.override import Override
|
||||
|
||||
|
||||
class Apply(api.BaseResource):
|
||||
'''
|
||||
apply armada endpoint service
|
||||
'''
|
||||
"""Controller for installing and updating charts defined in an Armada
|
||||
manifest file.
|
||||
"""
|
||||
|
||||
@policy.enforce('armada:create_endpoints')
|
||||
def on_post(self, req, resp):
|
||||
try:
|
||||
|
||||
# Load data from request and get options
|
||||
if req.content_type == 'application/x-yaml':
|
||||
data = list(self.req_yaml(req))
|
||||
@ -66,10 +66,7 @@ class Apply(api.BaseResource):
|
||||
falcon.HTTP_415,
|
||||
message="Request must be in application/x-yaml"
|
||||
"or application/json")
|
||||
|
||||
opts = req.params
|
||||
|
||||
# Encode filename
|
||||
try:
|
||||
armada = Armada(
|
||||
documents,
|
||||
disable_update_pre=req.get_param_as_bool(
|
||||
@ -80,9 +77,10 @@ class Apply(api.BaseResource):
|
||||
'enable_chart_cleanup'),
|
||||
dry_run=req.get_param_as_bool('dry_run'),
|
||||
wait=req.get_param_as_bool('wait'),
|
||||
timeout=int(opts.get('timeout', 3600)),
|
||||
tiller_host=opts.get('tiller_host', None),
|
||||
tiller_port=int(opts.get('tiller_port', 44134)),
|
||||
timeout=req.get_param_as_int('timeout') or 3600,
|
||||
tiller_host=req.get_param('tiller_host', default=None),
|
||||
tiller_port=req.get_param_as_int('tiller_port') or 44134,
|
||||
target_manifest=req.get_param('target_manifest')
|
||||
)
|
||||
|
||||
msg = armada.sync()
|
||||
@ -95,6 +93,8 @@ class Apply(api.BaseResource):
|
||||
|
||||
resp.content_type = 'application/json'
|
||||
resp.status = falcon.HTTP_200
|
||||
except exceptions.ManifestException as e:
|
||||
self.return_error(resp, falcon.HTTP_400, message=str(e))
|
||||
except Exception as e:
|
||||
err_message = 'Failed to apply manifest: {}'.format(e)
|
||||
self.error(req.context, err_message)
|
||||
|
@ -77,12 +77,13 @@ class Tests(api.BaseResource):
|
||||
@policy.enforce('armada:tests_manifest')
|
||||
def on_post(self, req, resp):
|
||||
try:
|
||||
opts = req.params
|
||||
tiller = Tiller(tiller_host=opts.get('tiller_host', None),
|
||||
tiller_port=opts.get('tiller_port', None))
|
||||
tiller = Tiller(tiller_host=req.get_param('tiller_host', None),
|
||||
tiller_port=req.get_param('tiller_port', None))
|
||||
|
||||
documents = self.req_yaml(req)
|
||||
armada_obj = Manifest(documents).get_manifest()
|
||||
target_manifest = req.get_param('target_manifest', None)
|
||||
armada_obj = Manifest(
|
||||
documents, target_manifest=target_manifest).get_manifest()
|
||||
prefix = armada_obj.get(const.KEYWORD_ARMADA).get(
|
||||
const.KEYWORD_PREFIX)
|
||||
known_releases = [release[0] for release in tiller.list_charts()]
|
||||
|
@ -23,8 +23,7 @@ from armada.handlers.document import ReferenceResolver
|
||||
|
||||
|
||||
class Validate(api.BaseResource):
|
||||
'''
|
||||
apply armada endpoint service
|
||||
'''Controller for validating an Armada manifest.
|
||||
'''
|
||||
|
||||
@policy.enforce('armada:validate_manifest')
|
||||
|
@ -65,46 +65,76 @@ To obtain override manifest:
|
||||
SHORT_DESC = "command install manifest charts"
|
||||
|
||||
|
||||
@apply.command(name='apply', help=DESC, short_help=SHORT_DESC)
|
||||
@apply.command(name='apply',
|
||||
help=DESC,
|
||||
short_help=SHORT_DESC)
|
||||
@click.argument('locations', nargs=-1)
|
||||
@click.option('--api', help="Contacts service endpoint", is_flag=True)
|
||||
@click.option(
|
||||
'--disable-update-post', help="run charts without install", is_flag=True)
|
||||
@click.option(
|
||||
'--disable-update-pre', help="run charts without install", is_flag=True)
|
||||
@click.option('--dry-run', help="run charts without install", is_flag=True)
|
||||
@click.option(
|
||||
'--enable-chart-cleanup', help="Clean up Unmanaged Charts", is_flag=True)
|
||||
@click.option('--set', multiple=True, type=str, default=[])
|
||||
@click.option('--tiller-host', help="Tiller host ip")
|
||||
@click.option(
|
||||
'--tiller-port', help="Tiller host port", type=int, default=44134)
|
||||
@click.option(
|
||||
'--timeout',
|
||||
help="specifies time to wait for charts",
|
||||
@click.option('--api',
|
||||
help="Contacts service endpoint.",
|
||||
is_flag=True)
|
||||
@click.option('--disable-update-post',
|
||||
help="Disable post-update Tiller operations.",
|
||||
is_flag=True)
|
||||
@click.option('--disable-update-pre',
|
||||
help="Disable pre-update Tiller operations.",
|
||||
is_flag=True)
|
||||
@click.option('--dry-run',
|
||||
help="Run charts without installing them.",
|
||||
is_flag=True)
|
||||
@click.option('--enable-chart-cleanup',
|
||||
help="Clean up unmanaged charts.",
|
||||
is_flag=True)
|
||||
@click.option('--set',
|
||||
help=("Use to override Armada Manifest values. Accepts "
|
||||
"overrides that adhere to the format <key>=<value>"),
|
||||
multiple=True,
|
||||
type=str,
|
||||
default=[])
|
||||
@click.option('--tiller-host',
|
||||
help="Tiller host IP.")
|
||||
@click.option('--tiller-port',
|
||||
help="Tiller host port.",
|
||||
type=int,
|
||||
default=44134)
|
||||
@click.option('--timeout',
|
||||
help="Specifies time to wait for charts to deploy.",
|
||||
type=int,
|
||||
default=3600)
|
||||
@click.option('--values', '-f', multiple=True, type=str, default=[])
|
||||
@click.option('--wait', help="wait until all charts deployed", is_flag=True)
|
||||
@click.option(
|
||||
'--debug/--no-debug', help='Enable or disable debugging', default=False)
|
||||
@click.option('--values',
|
||||
'-f',
|
||||
help=("Use to override multiple Armada Manifest values by "
|
||||
"reading overrides from a values.yaml-type file."),
|
||||
multiple=True,
|
||||
type=str,
|
||||
default=[])
|
||||
@click.option('--wait',
|
||||
help="Wait until all charts deployed.",
|
||||
is_flag=True)
|
||||
@click.option('--target-manifest',
|
||||
help=('The target manifest to run. Required for specifying '
|
||||
'which manifest to run when multiple are available.'),
|
||||
default=None)
|
||||
@click.option('--debug/--no-debug',
|
||||
help='Enable or disable debugging.',
|
||||
default=False)
|
||||
@click.pass_context
|
||||
def apply_create(ctx, locations, api, disable_update_post, disable_update_pre,
|
||||
dry_run, enable_chart_cleanup, set, tiller_host, tiller_port,
|
||||
timeout, values, wait, debug):
|
||||
timeout, values, wait, target_manifest, debug):
|
||||
|
||||
if debug:
|
||||
CONF.debug = debug
|
||||
|
||||
ApplyManifest(ctx, locations, api, disable_update_post, disable_update_pre,
|
||||
dry_run, enable_chart_cleanup, set, tiller_host, tiller_port,
|
||||
timeout, values, wait).invoke()
|
||||
timeout, values, wait, target_manifest).invoke()
|
||||
|
||||
|
||||
class ApplyManifest(CliAction):
|
||||
def __init__(self, ctx, locations, api, disable_update_post,
|
||||
disable_update_pre, dry_run, enable_chart_cleanup, set,
|
||||
tiller_host, tiller_port, timeout, values, wait):
|
||||
tiller_host, tiller_port, timeout, values, wait,
|
||||
target_manifest):
|
||||
super(ApplyManifest, self).__init__()
|
||||
self.ctx = ctx
|
||||
# Filename can also be a URL reference
|
||||
@ -120,6 +150,7 @@ class ApplyManifest(CliAction):
|
||||
self.timeout = timeout
|
||||
self.values = values
|
||||
self.wait = wait
|
||||
self.target_manifest = target_manifest
|
||||
|
||||
def output(self, resp):
|
||||
for result in resp:
|
||||
@ -153,7 +184,8 @@ class ApplyManifest(CliAction):
|
||||
armada = Armada(
|
||||
documents, self.disable_update_pre, self.disable_update_post,
|
||||
self.enable_chart_cleanup, self.dry_run, self.set, self.wait,
|
||||
self.timeout, self.tiller_host, self.tiller_port, self.values)
|
||||
self.timeout, self.tiller_host, self.tiller_port, self.values,
|
||||
self.target_manifest)
|
||||
|
||||
resp = armada.sync()
|
||||
self.output(resp)
|
||||
|
@ -56,14 +56,19 @@ SHORT_DESC = "command test releases"
|
||||
@click.option('--tiller-host', help="Tiller Host IP")
|
||||
@click.option(
|
||||
'--tiller-port', help="Tiller host Port", type=int, default=44134)
|
||||
@click.option('--target-manifest',
|
||||
help=('The target manifest to run. Required for specifying '
|
||||
'which manifest to run when multiple are available.'),
|
||||
default=None)
|
||||
@click.pass_context
|
||||
def test_charts(ctx, file, release, tiller_host, tiller_port):
|
||||
def test_charts(ctx, file, release, tiller_host, tiller_port, target_manifest):
|
||||
TestChartManifest(
|
||||
ctx, file, release, tiller_host, tiller_port).invoke()
|
||||
|
||||
|
||||
class TestChartManifest(CliAction):
|
||||
def __init__(self, ctx, file, release, tiller_host, tiller_port):
|
||||
def __init__(self, ctx, file, release, tiller_host, tiller_port,
|
||||
target_manifest):
|
||||
|
||||
super(TestChartManifest, self).__init__()
|
||||
self.ctx = ctx
|
||||
@ -71,6 +76,7 @@ class TestChartManifest(CliAction):
|
||||
self.release = release
|
||||
self.tiller_host = tiller_host
|
||||
self.tiller_port = tiller_port
|
||||
self.target_manifest = target_manifest
|
||||
|
||||
def invoke(self):
|
||||
tiller = Tiller(
|
||||
@ -107,7 +113,9 @@ class TestChartManifest(CliAction):
|
||||
if self.file:
|
||||
if not self.ctx.obj.get('api', False):
|
||||
documents = yaml.safe_load_all(open(self.file).read())
|
||||
armada_obj = Manifest(documents).get_manifest()
|
||||
armada_obj = Manifest(
|
||||
documents,
|
||||
target_manifest=self.target_manifest).get_manifest()
|
||||
prefix = armada_obj.get(const.KEYWORD_ARMADA).get(
|
||||
const.KEYWORD_PREFIX)
|
||||
|
||||
|
@ -75,13 +75,13 @@ class ArmadaClient(object):
|
||||
values=None,
|
||||
set=None,
|
||||
query=None):
|
||||
"""Call the Armada API to apply a manifest.
|
||||
"""Call the Armada API to apply a Manifest.
|
||||
|
||||
If manifest is not None, then the request body will be a fully
|
||||
If ``manifest`` is not None, then the request body will be a fully
|
||||
rendered set of YAML documents including overrides and
|
||||
values-files application.
|
||||
|
||||
If manifest is None and manifest_ref is not, then the request
|
||||
If ``manifest`` is None and ``manifest_ref`` is not, then the request
|
||||
body will be a JSON structure providing a list of references
|
||||
to Armada manifest documents and a list of overrides. Local
|
||||
values files are not supported when using the API with references.
|
||||
|
@ -191,7 +191,7 @@ class AppError(Exception):
|
||||
"""
|
||||
:param description: The internal error description
|
||||
:param error_list: The list of errors
|
||||
:param status: The desired falcon HTTP resposne code
|
||||
:param status: The desired falcon HTTP response code
|
||||
:param title: The title of the error message
|
||||
:param error_list: A list of errors to be included in output
|
||||
messages list
|
||||
|
@ -0,0 +1,18 @@
|
||||
# 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 armada.exceptions.manifest_exceptions import ManifestException
|
||||
|
||||
|
||||
__all__ = ['ManifestException']
|
@ -27,8 +27,12 @@ CONF = cfg.CONF
|
||||
class ArmadaBaseException(Exception):
|
||||
'''Base class for Armada exception and error handling.'''
|
||||
|
||||
def __init__(self, message=None):
|
||||
def __init__(self, message=None, **kwargs):
|
||||
self.message = message or self.message
|
||||
try:
|
||||
self.message = self.message % kwargs
|
||||
except TypeError:
|
||||
pass
|
||||
super(ArmadaBaseException, self).__init__(self.message)
|
||||
|
||||
|
||||
|
19
armada/exceptions/manifest_exceptions.py
Normal file
19
armada/exceptions/manifest_exceptions.py
Normal file
@ -0,0 +1,19 @@
|
||||
# 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 armada.exceptions import base_exception as base
|
||||
|
||||
|
||||
class ManifestException(base.ArmadaBaseException):
|
||||
message = 'An error occurred while generating the manifest: %(details)s.'
|
@ -54,10 +54,23 @@ class Armada(object):
|
||||
timeout=DEFAULT_TIMEOUT,
|
||||
tiller_host=None,
|
||||
tiller_port=44134,
|
||||
values=None):
|
||||
values=None,
|
||||
target_manifest=None):
|
||||
'''
|
||||
Initialize the Armada Engine and establish
|
||||
a connection to Tiller
|
||||
Initialize the Armada engine and establish a connection to Tiller.
|
||||
|
||||
:param List[dict] file: Armada documents.
|
||||
:param bool disable_update_pre: Disable pre-update Tiller operations.
|
||||
:param bool disable_update_post: Disable post-update Tiller
|
||||
operations.
|
||||
:param bool enable_chart_cleanup: Clean up unmanaged charts.
|
||||
:param bool dry_run: Run charts without installing them.
|
||||
:param bool wait: Wait until all charts are deployed.
|
||||
:param int timeout: Specifies time to wait for charts to deploy.
|
||||
:param str tiller_host: Tiller host IP.
|
||||
:param int tiller_port: Tiller host port.
|
||||
:param str target_manifest: The target manifest to run. Useful for
|
||||
specifying which manifest to run when multiple are available.
|
||||
'''
|
||||
self.disable_update_pre = disable_update_pre
|
||||
self.disable_update_post = disable_update_post
|
||||
@ -70,9 +83,13 @@ class Armada(object):
|
||||
self.values = values
|
||||
self.documents = file
|
||||
self.config = None
|
||||
self.target_manifest = target_manifest
|
||||
|
||||
def get_armada_manifest(self):
|
||||
return Manifest(self.documents).get_manifest()
|
||||
return Manifest(
|
||||
self.documents,
|
||||
target_manifest=self.target_manifest
|
||||
).get_manifest()
|
||||
|
||||
def find_release_chart(self, known_releases, name):
|
||||
'''
|
||||
@ -303,7 +320,7 @@ class Armada(object):
|
||||
timeout=chart_timeout)
|
||||
|
||||
if chart_wait:
|
||||
# TODO(gardlt): after v0.7.1 depricate timeout values
|
||||
# TODO(gardlt): after v0.7.1 deprecate timeout values
|
||||
if not wait_values.get('timeout', None):
|
||||
wait_values['timeout'] = chart_timeout
|
||||
|
||||
|
@ -12,44 +12,97 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from armada.const import DOCUMENT_CHART, DOCUMENT_GROUP, DOCUMENT_MANIFEST
|
||||
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):
|
||||
|
||||
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.config = None
|
||||
self.documents = documents
|
||||
self.charts = []
|
||||
self.groups = []
|
||||
self.manifest = None
|
||||
self.get_documents()
|
||||
self.charts, self.groups, manifests = self._find_documents(
|
||||
target_manifest)
|
||||
|
||||
def get_documents(self):
|
||||
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, expected_schemas)
|
||||
raise exceptions.ManifestException(
|
||||
details=error % expected_schemas)
|
||||
|
||||
def _find_documents(self, target_manifest=None):
|
||||
charts = []
|
||||
groups = []
|
||||
manifests = []
|
||||
for document in self.documents:
|
||||
if document.get('schema') == DOCUMENT_CHART:
|
||||
self.charts.append(document)
|
||||
if document.get('schema') == DOCUMENT_GROUP:
|
||||
self.groups.append(document)
|
||||
if document.get('schema') == DOCUMENT_MANIFEST:
|
||||
self.manifest = document
|
||||
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):
|
||||
try:
|
||||
for chart in self.charts:
|
||||
if chart.get('metadata').get('name') == name:
|
||||
if chart.get('metadata', {}).get('name') == name:
|
||||
return chart
|
||||
except Exception:
|
||||
raise Exception(
|
||||
"Could not find {} in {}".format(name, DOCUMENT_CHART))
|
||||
raise exceptions.ManifestException(
|
||||
details='Could not find a {} named "{}"'.format(
|
||||
const.DOCUMENT_CHART, name))
|
||||
|
||||
def find_chart_group_document(self, name):
|
||||
try:
|
||||
for group in self.groups:
|
||||
if group.get('metadata').get('name') == name:
|
||||
if group.get('metadata', {}).get('name') == name:
|
||||
return group
|
||||
except Exception:
|
||||
raise Exception(
|
||||
"Could not find {} in {}".format(name, DOCUMENT_GROUP))
|
||||
raise exceptions.ManifestException(
|
||||
details='Could not find a {} named "{}"'.format(
|
||||
const.DOCUMENT_GROUP, name))
|
||||
|
||||
def build_charts_deps(self):
|
||||
for chart in self.charts:
|
||||
@ -71,9 +124,9 @@ class Manifest(object):
|
||||
'chart': chart_dep.get('data')
|
||||
}
|
||||
except Exception:
|
||||
raise Exception(
|
||||
"Could not find dependency chart {} in {}".format(
|
||||
dep, DOCUMENT_CHART))
|
||||
raise exceptions.ManifestException(
|
||||
details="Could not find dependency chart {} in {}".format(
|
||||
dep, const.DOCUMENT_CHART))
|
||||
|
||||
def build_chart_group(self, chart_group):
|
||||
try:
|
||||
@ -87,9 +140,9 @@ class Manifest(object):
|
||||
'chart': chart_dep.get('data')
|
||||
}
|
||||
except Exception:
|
||||
raise Exception(
|
||||
"Could not find chart {} in {}".format(
|
||||
chart, DOCUMENT_GROUP))
|
||||
raise exceptions.ManifestException(
|
||||
details="Could not find chart {} in {}".format(
|
||||
chart, const.DOCUMENT_GROUP))
|
||||
|
||||
def build_armada_manifest(self):
|
||||
try:
|
||||
@ -106,9 +159,9 @@ class Manifest(object):
|
||||
|
||||
self.manifest['data']['chart_groups'][iter] = ch_grp_data
|
||||
except Exception:
|
||||
raise Exception(
|
||||
raise exceptions.ManifestException(
|
||||
"Could not find chart group {} in {}".format(
|
||||
group, DOCUMENT_MANIFEST))
|
||||
group, const.DOCUMENT_MANIFEST))
|
||||
|
||||
def get_manifest(self):
|
||||
self.build_charts_deps()
|
||||
|
@ -52,6 +52,7 @@ class ArmadaControllerTest(base.BaseControllerTest):
|
||||
'timeout': 100,
|
||||
'tiller_host': None,
|
||||
'tiller_port': 44134,
|
||||
'target_manifest': None
|
||||
}
|
||||
|
||||
payload_url = 'http://foo.com/test.yaml'
|
||||
|
191
armada/tests/unit/handlers/test_manifest.py
Normal file
191
armada/tests/unit/handlers/test_manifest.py
Normal file
@ -0,0 +1,191 @@
|
||||
# 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 copy
|
||||
import os
|
||||
import yaml
|
||||
|
||||
import testtools
|
||||
|
||||
from armada import const
|
||||
from armada import exceptions
|
||||
from armada.handlers import manifest
|
||||
|
||||
|
||||
class ManifestTestCase(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ManifestTestCase, self).setUp()
|
||||
examples_dir = os.path.join(
|
||||
os.getcwd(), 'armada', 'tests', 'unit', 'resources')
|
||||
with open(os.path.join(examples_dir, 'keystone-manifest.yaml')) as f:
|
||||
self.documents = list(yaml.safe_load_all(f.read()))
|
||||
|
||||
def test_get_documents(self):
|
||||
armada_manifest = manifest.Manifest(self.documents)
|
||||
|
||||
self.assertIsInstance(armada_manifest.charts, list)
|
||||
self.assertIsInstance(armada_manifest.groups, list)
|
||||
self.assertIsNotNone(armada_manifest.manifest)
|
||||
|
||||
self.assertEqual(4, len(armada_manifest.charts))
|
||||
self.assertEqual(2, len(armada_manifest.groups))
|
||||
|
||||
self.assertEqual([self.documents[x] for x in range(4)],
|
||||
armada_manifest.charts)
|
||||
self.assertEqual([self.documents[x] for x in range(4, 6)],
|
||||
armada_manifest.groups)
|
||||
self.assertEqual(self.documents[-1], armada_manifest.manifest)
|
||||
|
||||
def test_get_documents_with_target_manifest(self):
|
||||
# Validate that specifying `target_manifest` flag returns the correct
|
||||
# manifest.
|
||||
armada_manifest = manifest.Manifest(
|
||||
self.documents, target_manifest='armada-manifest')
|
||||
|
||||
self.assertIsInstance(armada_manifest.charts, list)
|
||||
self.assertIsInstance(armada_manifest.groups, list)
|
||||
self.assertIsNotNone(armada_manifest.manifest)
|
||||
|
||||
self.assertEqual(4, len(armada_manifest.charts))
|
||||
self.assertEqual(2, len(armada_manifest.groups))
|
||||
|
||||
self.assertEqual([self.documents[x] for x in range(4)],
|
||||
armada_manifest.charts)
|
||||
self.assertEqual([self.documents[x] for x in range(4, 6)],
|
||||
armada_manifest.groups)
|
||||
self.assertEqual(self.documents[-1], armada_manifest.manifest)
|
||||
self.assertEqual('armada-manifest',
|
||||
self.documents[-1]['metadata']['name'])
|
||||
|
||||
def test_get_documents_with_multi_manifest_and_target_manifest(self):
|
||||
# Validate that specifying `target_manifest` flag returns the correct
|
||||
# manifest even if there are multiple existing manifests. (Only works
|
||||
# when the manifest names are distinct or else should raise error.)
|
||||
documents = copy.deepcopy(self.documents)
|
||||
other_manifest = copy.deepcopy(self.documents[-1])
|
||||
other_manifest['metadata']['name'] = 'alt-armada-manifest'
|
||||
documents.append(other_manifest)
|
||||
|
||||
# Specify the "original" manifest and verify it works.
|
||||
armada_manifest = manifest.Manifest(
|
||||
documents, target_manifest='armada-manifest')
|
||||
|
||||
self.assertIsInstance(armada_manifest.charts, list)
|
||||
self.assertIsInstance(armada_manifest.groups, list)
|
||||
self.assertIsNotNone(armada_manifest.manifest)
|
||||
|
||||
self.assertEqual(4, len(armada_manifest.charts))
|
||||
self.assertEqual(2, len(armada_manifest.groups))
|
||||
|
||||
self.assertEqual([self.documents[x] for x in range(4)],
|
||||
armada_manifest.charts)
|
||||
self.assertEqual([self.documents[x] for x in range(4, 6)],
|
||||
armada_manifest.groups)
|
||||
self.assertEqual(armada_manifest.manifest, self.documents[-1])
|
||||
self.assertEqual('armada-manifest',
|
||||
armada_manifest.manifest['metadata']['name'])
|
||||
|
||||
# Specify the alternative manifest and verify it works.
|
||||
armada_manifest = manifest.Manifest(
|
||||
documents, target_manifest='alt-armada-manifest')
|
||||
self.assertIsNotNone(armada_manifest.manifest)
|
||||
self.assertEqual(other_manifest, armada_manifest.manifest)
|
||||
self.assertEqual('alt-armada-manifest',
|
||||
armada_manifest.manifest['metadata']['name'])
|
||||
|
||||
def test_find_chart_document(self):
|
||||
armada_manifest = manifest.Manifest(self.documents)
|
||||
chart = armada_manifest.find_chart_document('helm-toolkit')
|
||||
self.assertEqual(self.documents[0], chart)
|
||||
|
||||
def test_find_group_document(self):
|
||||
armada_manifest = manifest.Manifest(self.documents)
|
||||
chart = armada_manifest.find_chart_group_document('openstack-keystone')
|
||||
self.assertEqual(self.documents[-2], chart)
|
||||
|
||||
|
||||
class ManifestNegativeTestCase(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ManifestNegativeTestCase, self).setUp()
|
||||
examples_dir = os.path.join(
|
||||
os.getcwd(), 'armada', 'tests', 'unit', 'resources')
|
||||
with open(os.path.join(examples_dir, 'keystone-manifest.yaml')) as f:
|
||||
self.documents = list(yaml.safe_load_all(f.read()))
|
||||
|
||||
def test_get_documents_multi_manifests_raises_value_error(self):
|
||||
# Validates that finding multiple manifests without `target_manifest`
|
||||
# flag raises exceptions.ManifestException.
|
||||
documents = copy.deepcopy(self.documents)
|
||||
documents.append(documents[-1]) # Copy the last manifest.
|
||||
|
||||
error_re = r'Multiple manifests are not supported.*'
|
||||
self.assertRaisesRegexp(
|
||||
exceptions.ManifestException, error_re, manifest.Manifest,
|
||||
documents)
|
||||
|
||||
def test_get_documents_multi_target_manifests_raises_value_error(self):
|
||||
# Validates that finding multiple manifests with `target_manifest`
|
||||
# flag raises exceptions.ManifestException.
|
||||
documents = copy.deepcopy(self.documents)
|
||||
documents.append(documents[-1]) # Copy the last manifest.
|
||||
|
||||
error_re = r'Multiple manifests are not supported.*'
|
||||
self.assertRaisesRegexp(
|
||||
exceptions.ManifestException, error_re, manifest.Manifest,
|
||||
documents, target_manifest='armada-manifest')
|
||||
|
||||
def test_get_documents_missing_manifest(self):
|
||||
# Validates exceptions.ManifestException is thrown if no manifest is
|
||||
# found. Manifest is last document in sample YAML.
|
||||
error_re = ('Documents must be a list of documents with at least one '
|
||||
'of each of the following schemas: .*')
|
||||
self.assertRaisesRegexp(
|
||||
exceptions.ManifestException, error_re, manifest.Manifest,
|
||||
self.documents[:-1])
|
||||
|
||||
def test_get_documents_missing_charts(self):
|
||||
# Validates exceptions.ManifestException is thrown if no chart is
|
||||
# found. Charts are first 4 documents in sample YAML.
|
||||
error_re = ('Documents must be a list of documents with at least one '
|
||||
'of each of the following schemas: .*')
|
||||
self.assertRaisesRegexp(
|
||||
exceptions.ManifestException, error_re, manifest.Manifest,
|
||||
self.documents[4:])
|
||||
|
||||
def test_get_documents_missing_chart_groups(self):
|
||||
# Validates exceptions.ManifestException is thrown if no chart is
|
||||
# found. ChartGroups are 5-6 documents in sample YAML.
|
||||
documents = self.documents[:4] + [self.documents[-1]]
|
||||
error_re = ('Documents must be a list of documents with at least one '
|
||||
'of each of the following schemas: .*')
|
||||
self.assertRaisesRegexp(
|
||||
exceptions.ManifestException, error_re, manifest.Manifest,
|
||||
documents)
|
||||
|
||||
def test_find_chart_document_negative(self):
|
||||
armada_manifest = manifest.Manifest(self.documents)
|
||||
error_re = r'Could not find a %s named "%s"' % (
|
||||
const.DOCUMENT_CHART, 'invalid')
|
||||
self.assertRaisesRegexp(exceptions.ManifestException, error_re,
|
||||
armada_manifest.find_chart_document, 'invalid')
|
||||
|
||||
def test_find_group_document_negative(self):
|
||||
armada_manifest = manifest.Manifest(self.documents)
|
||||
error_re = r'Could not find a %s named "%s"' % (
|
||||
const.DOCUMENT_GROUP, 'invalid')
|
||||
self.assertRaisesRegexp(exceptions.ManifestException, error_re,
|
||||
armada_manifest.find_chart_group_document,
|
||||
'invalid')
|
135
armada/tests/unit/resources/keystone-manifest.yaml
Normal file
135
armada/tests/unit/resources/keystone-manifest.yaml
Normal file
@ -0,0 +1,135 @@
|
||||
---
|
||||
schema: armada/Chart/v1
|
||||
metadata:
|
||||
schema: metadata/Document/v1
|
||||
name: helm-toolkit
|
||||
data:
|
||||
chart_name: helm-toolkit
|
||||
release: helm-toolkit
|
||||
namespace: helm-tookit
|
||||
values: {}
|
||||
source:
|
||||
type: git
|
||||
location: https://git.openstack.org/openstack/openstack-helm
|
||||
subpath: helm-toolkit
|
||||
reference: master
|
||||
dependencies: []
|
||||
---
|
||||
schema: armada/Chart/v1
|
||||
metadata:
|
||||
schema: metadata/Document/v1
|
||||
name: mariadb
|
||||
data:
|
||||
chart_name: mariadb
|
||||
release: mariadb
|
||||
namespace: openstack
|
||||
timeout: 3600
|
||||
wait:
|
||||
timeout: 3600
|
||||
labels:
|
||||
release_group: armada-mariadb
|
||||
install:
|
||||
no_hooks: false
|
||||
upgrade:
|
||||
no_hooks: false
|
||||
values: {}
|
||||
source:
|
||||
type: git
|
||||
location: https://git.openstack.org/openstack/openstack-helm
|
||||
subpath: mariadb
|
||||
reference: master
|
||||
dependencies:
|
||||
- helm-toolkit
|
||||
---
|
||||
schema: armada/Chart/v1
|
||||
metadata:
|
||||
schema: metadata/Document/v1
|
||||
name: memcached
|
||||
data:
|
||||
chart_name: memcached
|
||||
release: memcached
|
||||
namespace: openstack
|
||||
timeout: 100
|
||||
wait:
|
||||
timeout: 100
|
||||
labels:
|
||||
release_group: armada-memcached
|
||||
install:
|
||||
no_hooks: false
|
||||
upgrade:
|
||||
no_hooks: false
|
||||
values: {}
|
||||
source:
|
||||
type: git
|
||||
location: https://git.openstack.org/openstack/openstack-helm
|
||||
subpath: memcached
|
||||
reference: master
|
||||
dependencies:
|
||||
- helm-toolkit
|
||||
---
|
||||
schema: armada/Chart/v1
|
||||
metadata:
|
||||
schema: metadata/Document/v1
|
||||
name: keystone
|
||||
data:
|
||||
chart_name: keystone
|
||||
test: true
|
||||
release: keystone
|
||||
namespace: openstack
|
||||
timeout: 100
|
||||
wait:
|
||||
timeout: 100
|
||||
labels:
|
||||
release_group: armada-keystone
|
||||
install:
|
||||
no_hooks: false
|
||||
upgrade:
|
||||
no_hooks: false
|
||||
pre:
|
||||
delete:
|
||||
- name: keystone-bootstrap
|
||||
type: job
|
||||
labels:
|
||||
application: keystone
|
||||
component: bootstrap
|
||||
values:
|
||||
replicas: 3
|
||||
source:
|
||||
type: git
|
||||
location: https://git.openstack.org/openstack/openstack-helm
|
||||
subpath: keystone
|
||||
reference: master
|
||||
dependencies:
|
||||
- helm-toolkit
|
||||
---
|
||||
schema: armada/ChartGroup/v1
|
||||
metadata:
|
||||
schema: metadata/Document/v1
|
||||
name: keystone-infra-services
|
||||
data:
|
||||
description: "Keystone Infra Services"
|
||||
sequenced: True
|
||||
chart_group:
|
||||
- mariadb
|
||||
- memcached
|
||||
---
|
||||
schema: armada/ChartGroup/v1
|
||||
metadata:
|
||||
schema: metadata/Document/v1
|
||||
name: openstack-keystone
|
||||
data:
|
||||
description: "Deploying OpenStack Keystone"
|
||||
sequenced: True
|
||||
test_charts: False
|
||||
chart_group:
|
||||
- keystone
|
||||
---
|
||||
schema: armada/Manifest/v1
|
||||
metadata:
|
||||
schema: metadata/Document/v1
|
||||
name: armada-manifest
|
||||
data:
|
||||
release_prefix: armada
|
||||
chart_groups:
|
||||
- keystone-infra-services
|
||||
- openstack-keystone
|
@ -31,17 +31,22 @@ Commands
|
||||
$ armada apply examples/simple.yaml --values examples/simple-ovr-values.yaml
|
||||
|
||||
Options:
|
||||
--api Contacts service endpoint
|
||||
--disable-update-post run charts without install
|
||||
--disable-update-pre run charts without install
|
||||
--dry-run run charts without install
|
||||
--enable-chart-cleanup Clean up Unmanaged Charts
|
||||
--set TEXT
|
||||
--tiller-host TEXT Tiller host ip
|
||||
--tiller-port INTEGER Tiller host port
|
||||
--timeout INTEGER specifies time to wait for charts
|
||||
-f, --values TEXT
|
||||
--wait wait until all charts deployed
|
||||
--api Contacts service endpoint.
|
||||
--disable-update-post Disable post-update Tiller operations.
|
||||
--disable-update-pre Disable pre-update Tiller operations.
|
||||
--dry-run Run charts without installing them.
|
||||
--enable-chart-cleanup Clean up unmanaged charts.
|
||||
--set TEXT Use to override Armada Manifest values. Accepts
|
||||
overrides that adhere to the format <key>=<value>
|
||||
--tiller-host TEXT Tiller host IP.
|
||||
--tiller-port INTEGER Tiller host port.
|
||||
--timeout INTEGER Specifies time to wait for charts to deploy.
|
||||
-f, --values TEXT Use to override multiple Armada Manifest values by
|
||||
reading overrides from a values.yaml-type file.
|
||||
--wait Wait until all charts deployed.
|
||||
--target-manifest TEXT The target manifest to run. Required for specifying
|
||||
which manifest to run when multiple are available.
|
||||
--debug / --no-debug Enable or disable debugging.
|
||||
--help Show this message and exit.
|
||||
|
||||
Synopsis
|
||||
|
@ -29,6 +29,8 @@ Commands
|
||||
--tiller-host TEXT Tiller Host IP
|
||||
--tiller-port INTEGER Tiller host Port
|
||||
--help Show this message and exit.
|
||||
--target-manifest TEXT The target manifest to run. Required for specifying
|
||||
which manifest to run when multiple are available.
|
||||
|
||||
|
||||
Synopsis
|
||||
|
@ -89,3 +89,13 @@ Lint Exceptions
|
||||
+----------------------------------+------------------------------+
|
||||
| InvalidArmadaObjectException | Armada object not declared. |
|
||||
+----------------------------------+------------------------------+
|
||||
|
||||
Manifest Exceptions
|
||||
===================
|
||||
+----------------------------------+------------------------------------------------+
|
||||
| Exception | Error Description |
|
||||
+==================================+================================================+
|
||||
| ManifestException | An exception occurred while attempting to build|
|
||||
| | an Armada manifest. The exception will return |
|
||||
| | with details as to why. |
|
||||
+----------------------------------+------------------------------------------------+
|
||||
|
Loading…
x
Reference in New Issue
Block a user