Merge "bug(manifest): Allow specific manifest to be specified"

This commit is contained in:
Marshall Margenau 2018-01-26 09:20:17 -05:00 committed by Gerrit Code Review
commit 4bc40c2318
18 changed files with 634 additions and 139 deletions

View File

@ -19,57 +19,54 @@ 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))
if type(data[0]) is list:
documents = list(data[0])
else:
documents = data
elif req.content_type == 'application/json':
self.logger.debug("Applying manifest based on reference.")
req_body = self.req_json(req)
doc_ref = req_body.get('hrefs', None)
if not doc_ref:
self.logger.info("Request did not contain 'hrefs'.")
resp.status = falcon.HTTP_400
return
data = ReferenceResolver.resolve_reference(doc_ref)
documents = list()
for d in data:
documents.extend(list(yaml.safe_load_all(d.decode())))
if req_body.get('overrides', None):
overrides = Override(documents,
overrides=req_body.get('overrides'))
documents = overrides.update_manifests()
# Load data from request and get options
if req.content_type == 'application/x-yaml':
data = list(self.req_yaml(req))
if type(data[0]) is list:
documents = list(data[0])
else:
self.error(req.context, "Unknown content-type %s"
% req.content_type)
self.return_error(
resp,
falcon.HTTP_415,
message="Request must be in application/x-yaml"
"or application/json")
documents = data
elif req.content_type == 'application/json':
self.logger.debug("Applying manifest based on reference.")
req_body = self.req_json(req)
doc_ref = req_body.get('hrefs', None)
opts = req.params
if not doc_ref:
self.logger.info("Request did not contain 'hrefs'.")
resp.status = falcon.HTTP_400
return
# Encode filename
data = ReferenceResolver.resolve_reference(doc_ref)
documents = list()
for d in data:
documents.extend(list(yaml.safe_load_all(d.decode())))
if req_body.get('overrides', None):
overrides = Override(documents,
overrides=req_body.get('overrides'))
documents = overrides.update_manifests()
else:
self.error(req.context, "Unknown content-type %s"
% req.content_type)
self.return_error(
resp,
falcon.HTTP_415,
message="Request must be in application/x-yaml"
"or application/json")
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)

View File

@ -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()]

View File

@ -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')

View File

@ -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",
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('--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',
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)

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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']

View File

@ -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)

View 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.'

View File

@ -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

View File

@ -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:
return chart
except Exception:
raise Exception(
"Could not find {} in {}".format(name, DOCUMENT_CHART))
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):
try:
for group in self.groups:
if group.get('metadata').get('name') == name:
return group
except Exception:
raise Exception(
"Could not find {} in {}".format(name, DOCUMENT_GROUP))
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):
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()

View File

@ -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'

View 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')

View 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

View File

@ -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

View File

@ -24,11 +24,13 @@ Commands
$ armada test --release blog-1
Options:
--file TEXT armada manifest
--release TEXT helm release
--tiller-host TEXT Tiller Host IP
--tiller-port INTEGER Tiller host Port
--help Show this message and exit.
--file TEXT armada manifest
--release TEXT helm release
--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

View File

@ -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. |
+----------------------------------+------------------------------------------------+