From d5f43787311c25dbe80f60e5955fdd421450e912 Mon Sep 17 00:00:00 2001 From: Alexis Rivera De La Torre Date: Mon, 7 Aug 2017 18:22:09 +0000 Subject: [PATCH] feat(armada): adding helm testing framework - added helm test framework to armada - added helm test status Closes #151 Change-Id: I417cae04b4595ad0d4fd05889d90c83907607c47 --- armada/cli/test.py | 107 ++++++++++++++ armada/cli/tiller.py | 4 +- armada/exceptions/tiller_exceptions.py | 96 ++++++------- armada/handlers/armada.py | 20 ++- armada/handlers/k8s.py | 17 +++ armada/handlers/tiller.py | 136 +++++++++++++++--- docs/source/commands/index.rst | 3 +- docs/source/commands/test.rst | 21 +++ .../operations/guide-build-armada-yaml.rst | 123 +++++++++++++--- examples/armada-manifest-v1.yaml | 13 +- setup.cfg | 3 + 11 files changed, 447 insertions(+), 96 deletions(-) create mode 100644 armada/cli/test.py create mode 100644 docs/source/commands/test.rst diff --git a/armada/cli/test.py b/armada/cli/test.py new file mode 100644 index 00000000..31471756 --- /dev/null +++ b/armada/cli/test.py @@ -0,0 +1,107 @@ +# 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 yaml + +from cliff import command as cmd +from oslo_config import cfg +from oslo_log import log as logging + +from armada import const +from armada.handlers.manifest import Manifest +from armada.handlers.tiller import Tiller +from armada.utils.release import release_prefix + +LOG = logging.getLogger(__name__) + +CONF = cfg.CONF + + +def testService(args): + + tiller = Tiller(tiller_host=args.tiller_host, tiller_port=args.tiller_port) + known_release_names = [release[0] for release in tiller.list_charts()] + + if args.release: + LOG.info("RUNNING: %s tests", args.release) + resp = tiller.testing_release(args.release) + + if not resp: + LOG.info("FAILED: %s", args.release) + return + + test_status = getattr(resp.info.status, 'last_test_suite_run', + 'FAILED') + if test_status.results[0].status: + LOG.info("PASSED: %s", args.release) + else: + LOG.info("FAILED: %s", args.release) + + if args.file: + documents = yaml.safe_load_all(open(args.file).read()) + armada_obj = Manifest(documents).get_manifest() + prefix = armada_obj.get(const.KEYWORD_ARMADA).get(const.KEYWORD_PREFIX) + + for group in armada_obj.get(const.KEYWORD_ARMADA).get( + const.KEYWORD_GROUPS): + for ch in group.get(const.KEYWORD_CHARTS): + release_name = release_prefix( + prefix, ch.get('chart').get('chart_name')) + + if release_name in known_release_names: + LOG.info('RUNNING: %s tests', release_name) + resp = tiller.testing_release(release_name) + + if not resp: + continue + + test_status = getattr(resp.info.status, + 'last_test_suite_run', 'FAILED') + if test_status.results[0].status: + LOG.info("PASSED: %s", release_name) + else: + LOG.info("FAILED: %s", release_name) + + else: + LOG.info('Release %s not found - SKIPPING', release_name) + + +class TestServerCommand(cmd.Command): + def get_parser(self, prog_name): + parser = super(TestServerCommand, self).get_parser(prog_name) + parser.add_argument( + '--release', action='store', help='testing Helm in Release') + parser.add_argument( + '-f', + '--file', + type=str, + metavar='FILE', + help='testing Helm releases in Manifest') + parser.add_argument( + '--tiller-host', + action='store', + type=str, + default=None, + help='Specify the tiller host') + parser.add_argument( + '--tiller-port', + action='store', + type=int, + default=44134, + help='Specify the tiller port') + + return parser + + def take_action(self, parsed_args): + testService(parsed_args) diff --git a/armada/cli/tiller.py b/armada/cli/tiller.py index 28427b8e..22f7a663 100644 --- a/armada/cli/tiller.py +++ b/armada/cli/tiller.py @@ -28,7 +28,9 @@ def tillerServer(args): tiller = Tiller() if args.status: - LOG.info('Tiller is Active: %s', tiller.tiller_status()) + resp = tiller.tiller_version() + LOG.info('Tiller Service: %s', tiller.tiller_status()) + LOG.info('Tiller Version: %s', getattr(resp.Version, 'sem_ver', False)) if args.releases: for release in tiller.list_releases(): diff --git a/armada/exceptions/tiller_exceptions.py b/armada/exceptions/tiller_exceptions.py index 9055b3c9..ff4d6b6c 100644 --- a/armada/exceptions/tiller_exceptions.py +++ b/armada/exceptions/tiller_exceptions.py @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -import base_exception +from base_exception import ArmadaBaseException as ex -class TillerException(base_exception.ArmadaBaseException): +class TillerException(ex): '''Base class for Tiller exceptions and error handling.''' message = 'An unknown Tiller error occured.' @@ -27,10 +27,10 @@ class TillerServicesUnavailableException(TillerException): class ChartCleanupException(TillerException): '''Exception that occures during chart cleanup.''' - def __init__(self, chart_name, source_type): - super(ChartCleanupException, self).__init__('An error occred during \ - cleanup while removing \ - the chart ' + chart_name) + def __init__(self, chart_name): + message = 'An error occred during cleanup while removing {}'.format( + chart_name) + super(ChartCleanupException, self).__init__(message) class ListChartsException(TillerException): '''Exception that occurs when listing charts''' @@ -41,78 +41,76 @@ class PostUpdateJobDeleteException(TillerException): '''Exception that occurs when a job deletion''' def __init__(self, name, namespace): - self._name = name - self._namespace = namespace - self._message = 'Failed to delete k8s job ' + self._name + ' in ' + \ - self._namespace + ' namspace.' + message = 'Failed to delete k8s job {} in {}'.format( + name, namespace) - super(PostUpdateJobDeleteException, self).__init__(self._message) + super(PostUpdateJobDeleteException, self).__init__(message) class PostUpdateJobCreateException(TillerException): '''Exception that occurs when a job creation fails.''' def __init__(self, name, namespace): - self._name = name - self._namespace = namespace - self._message = 'Failed to create k8s job ' + self._name + ' in ' + \ - self._namespace + ' namespace.' + message = 'Failed to create k8s job {} in {}'.format( + name, namespace) - super(PostUpdateJobCreateException, self).__init__(self._message) + super(PostUpdateJobCreateException, self).__init__(message) class PreUpdateJobDeleteException(TillerException): '''Exception that occurs when a job deletion''' def __init__(self, name, namespace): - self._name = name - self._namespace = namespace - self._message = 'Failed to delete k8s job ' + self._name + ' in ' + \ - self._namespace + ' namspace.' + message = 'Failed to delete k8s job {} in {}'.format( + name, namespace) - super(PreUpdateJobDeleteException, self).__init__(self._message) + super(PreUpdateJobDeleteException, self).__init__(message) class PreUpdateJobCreateException(TillerException): '''Exception that occurs when a job creation fails.''' def __init__(self, name, namespace): - self._name = name - self._namespace = namespace - self._message = 'Failed to create k8s job ' + self._name + ' in ' + \ - self._namespace + ' namespace.' + message = 'Failed to create k8s job {} in {}'.format( + name, namespace) - super(PreUpdateJobCreateException, self).__init__(self._message) + super(PreUpdateJobCreateException, self).__init__(message) -class ReleaseUninstallException(TillerException): - '''Exception that occurs when a release fails to uninstall.''' - - def __init__(self, name, namespace): - self._name = name - self._message = 'Failed to uninstall release' + self._name + '.' - - super(ReleaseUninstallException, self).__init__(self._message) - -class ReleaseInstallException(TillerException): +class ReleaseException(TillerException): '''Exception that occurs when a release fails to install.''' - def __init__(self, name, namespace): - self._name = name - self._message = 'Failed to install release' + self._name + '.' + def __init__(self, name, status, action): + til_msg = getattr(status.info, 'Description').encode() + message = 'Failed to {} release: {} - Tiller Message: {}'.format( + action, name, til_msg) - super(ReleaseInstallException, self).__init__(self._message) - -class ReleaseUpdateException(TillerException): - '''Exception that occurs when a release fails to update.''' - - def __init__(self, name, namespace): - self._name = name - self._message = 'Failed to update release' + self._name + '.' - - super(ReleaseUpdateException, self).__init__(self._message) + super(ReleaseException, self).__init__(message) class ChannelException(TillerException): '''Exception that occurs during a failed GRPC channel creation''' message = 'Failed to create GRPC channel.' + +class GetReleaseStatusException(TillerException): + '''Exception that occurs during a failed Release Testing''' + + def __init__(self, release, version): + message = 'Failed to get {} status {} version'.format( + release, version) + + super(GetReleaseStatusException, self).__init__(message) + +class GetReleaseContentException(TillerException): + '''Exception that occurs during a failed Release Testing''' + + def __init__(self, release, version): + message = 'Failed to get {} content {} version {}'.format( + release, version) + + super(GetReleaseContentException, self).__init__(message) + +class TillerVersionException(TillerException): + '''Exception that occurs during a failed Release Testing''' + + message = 'Failed to get Tiller Version' diff --git a/armada/handlers/armada.py b/armada/handlers/armada.py index e00d3b28..19ef4703 100644 --- a/armada/handlers/armada.py +++ b/armada/handlers/armada.py @@ -204,8 +204,9 @@ class Armada(object): desc = entry.get('description', 'A Chart Group') chart_group = entry.get(KEYWORD_CHARTS, []) + test_charts = entry.get('test_charts', False) - if entry.get('sequenced', False): + if entry.get('sequenced', False) or test_charts: chart_wait = True LOG.info('Deploying: %s', desc) @@ -213,13 +214,18 @@ class Armada(object): for gchart in chart_group: chart = dotify(gchart['chart']) values = gchart.get('chart').get('values', {}) + test_chart = gchart.get('chart').get('test', False) pre_actions = {} post_actions = {} + LOG.info('%s', chart.release) if chart.release is None: continue + if test_chart: + chart_wait = True + # retrieve appropriate timeout value if 'wait' is specified chart_timeout = self.timeout if chart_wait: @@ -271,6 +277,7 @@ class Armada(object): continue # do actual update + LOG.info('wait: %s', chart_wait) self.tiller.update_release(protoc_chart, prefix_chart, chart.namespace, @@ -297,6 +304,17 @@ class Armada(object): LOG.debug("Cleaning up chart source in %s", chartbuilder.source_directory) + if test_charts or test_chart: + LOG.info('Testing: %s', prefix_chart) + resp = self.tiller.testing_release(prefix_chart) + test_status = getattr(resp.info.status, + 'last_test_suite_run', 'FAILED') + LOG.info("Test INFO: %s", test_status) + if resp: + LOG.info("PASSED: %s", prefix_chart) + else: + LOG.info("FAILED: %s", prefix_chart) + LOG.info("Performing Post-Flight Operations") self.post_flight_ops() diff --git a/armada/handlers/k8s.py b/armada/handlers/k8s.py index 68c8b80a..462e1782 100644 --- a/armada/handlers/k8s.py +++ b/armada/handlers/k8s.py @@ -173,3 +173,20 @@ class K8s(object): LOG.info('New pod %s deployed', new_pod_name) w.stop() + + def wait_get_completed_podphase(self, release, timeout=300): + ''' + :param release - part of namespace + :param timeout - time before disconnecting stream + ''' + + w = watch.Watch() + for event in w.stream(self.client.list_pod_for_all_namespaces, + timeout_seconds=timeout): + pod_name = event['object'].metadata.name + + if release in pod_name: + pod_state = event['object'].status.phase + if pod_state == 'Succeeded': + w.stop() + break diff --git a/armada/handlers/tiller.py b/armada/handlers/tiller.py index c04e910b..48012baf 100644 --- a/armada/handlers/tiller.py +++ b/armada/handlers/tiller.py @@ -12,26 +12,33 @@ # See the License for the specific language governing permissions and # limitations under the License. -import grpc import yaml +import grpc -from hapi.services.tiller_pb2 import ReleaseServiceStub, ListReleasesRequest, \ - InstallReleaseRequest, UpdateReleaseRequest, UninstallReleaseRequest from hapi.chart.config_pb2 import Config - -from k8s import K8s -from ..const import STATUS_DEPLOYED, STATUS_FAILED - -from ..exceptions import tiller_exceptions -from ..utils.release import release_prefix - +from hapi.services.tiller_pb2 import GetReleaseContentRequest +from hapi.services.tiller_pb2 import GetReleaseStatusRequest +from hapi.services.tiller_pb2 import GetVersionRequest +from hapi.services.tiller_pb2 import InstallReleaseRequest +from hapi.services.tiller_pb2 import ListReleasesRequest +from hapi.services.tiller_pb2 import ReleaseServiceStub +from hapi.services.tiller_pb2 import TestReleaseRequest +from hapi.services.tiller_pb2 import UninstallReleaseRequest +from hapi.services.tiller_pb2 import UpdateReleaseRequest from oslo_config import cfg from oslo_log import log as logging +from ..const import STATUS_DEPLOYED, STATUS_FAILED +from ..exceptions import tiller_exceptions as ex +from ..utils.release import release_prefix +from k8s import K8s + + TILLER_PORT = 44134 TILLER_VERSION = b'2.5.0' TILLER_TIMEOUT = 300 RELEASE_LIMIT = 64 +RUNTEST_SUCCESS = 9 # the standard gRPC max message size is 4MB # this expansion comes at a performance penalty @@ -88,7 +95,7 @@ class Tiller(object): ] ) except Exception: - raise tiller_exceptions.ChannelException() + raise ex.ChannelException() def _get_tiller_pod(self): ''' @@ -200,8 +207,8 @@ class Tiller(object): self.delete_resources( release_name, name, 'pod', labels, namespace) except Exception: - raise tiller_exceptions.PreUpdateJobDeleteException(name, - namespace) + raise ex.PreUpdateJobDeleteException(name, namespace) + LOG.debug("PRE: Could not delete anything, please check yaml") try: @@ -213,8 +220,8 @@ class Tiller(object): self.k8s.create_job_action(name, action_type) continue except Exception: - raise tiller_exceptions.PreUpdateJobCreateException(name, - namespace) + raise ex.PreUpdateJobCreateException(name, namespace) + LOG.debug("PRE: Could not create anything, please check yaml") def delete_resource(self, release_name, resource_name, resource_type, @@ -260,7 +267,7 @@ class Tiller(object): self.k8s.create_job_action(name, action_type) continue except Exception: - raise tiller_exceptions.PreUpdateJobCreateException() + raise ex.PreUpdateJobCreateException() LOG.debug("POST: Could not create anything, please check yaml") def list_charts(self): @@ -319,7 +326,8 @@ class Tiller(object): stub.UpdateRelease( release_request, self.timeout, metadata=self.metadata) except Exception: - raise tiller_exceptions.ReleaseInstallException(release, namespace) + status = self.get_release_status(release) + raise ex.ReleaseException(release, status, 'Upgrade') self._post_update_actions(post_actions, namespace) @@ -331,6 +339,7 @@ class Tiller(object): ''' Create a Helm Release ''' + LOG.debug("wait: %s", wait) LOG.debug("timeout: %s", timeout) @@ -355,7 +364,95 @@ class Tiller(object): release_request, self.timeout, metadata=self.metadata) except Exception: - raise tiller_exceptions.ReleaseInstallException(release, namespace) + status = self.get_release_status(release) + raise ex.ReleaseException(release, status, 'Install') + + def testing_release(self, release, timeout=300, cleanup=True): + ''' + :param release - name of release to test + :param timeout - runtime before exiting + :param cleanup - removes testing pod created + + :returns - results of test pod + + ''' + + try: + + stub = ReleaseServiceStub(self.channel) + release_request = TestReleaseRequest(name=release, timeout=timeout, + cleanup=cleanup) + + content = self.get_release_content(release) + + if not len(content.release.hooks): + LOG.info('No test found') + return False + + if content.release.hooks[0].events[0] == RUNTEST_SUCCESS: + test = stub.RunReleaseTest( + release_request, self.timeout, metadata=self.metadata) + + if test.running(): + self.k8s.wait_get_completed_podphase(release) + + test.cancel() + + return self.get_release_status(release) + + except Exception: + status = self.get_release_status(release) + raise ex.ReleaseException(release, status, 'Test') + + def get_release_status(self, release, version=0): + ''' + :param release - name of release to test + :param version - version of release status + + ''' + + try: + stub = ReleaseServiceStub(self.channel) + status_request = GetReleaseStatusRequest( + name=release, version=version) + + return stub.GetReleaseStatus( + status_request, self.timeout, metadata=self.metadata) + + except Exception: + raise ex.GetReleaseStatusException(release, version) + + def get_release_content(self, release, version=0): + ''' + :param release - name of release to test + :param version - version of release status + + ''' + + try: + stub = ReleaseServiceStub(self.channel) + status_request = GetReleaseContentRequest( + name=release, version=version) + + return stub.GetReleaseContent( + status_request, self.timeout, metadata=self.metadata) + + except Exception: + raise ex.GetReleaseContentException(release, version) + + def tiller_version(self): + ''' + :returns - tiller version + ''' + try: + stub = ReleaseServiceStub(self.channel) + release_request = GetVersionRequest() + + return stub.GetVersion( + release_request, self.timeout, metadata=self.metadata) + + except Exception: + raise ex.TillerVersionException() def uninstall_release(self, release, disable_hooks=False, purge=True): ''' @@ -375,7 +472,8 @@ class Tiller(object): release_request, self.timeout, metadata=self.metadata) except Exception: - raise tiller_exceptions.ReleaseUninstallException(release) + status = self.get_release_status(release) + raise ex.ReleaseException(release, status, 'Delete') def chart_cleanup(self, prefix, charts): ''' diff --git a/docs/source/commands/index.rst b/docs/source/commands/index.rst index fc2fd24b..d240d77f 100644 --- a/docs/source/commands/index.rst +++ b/docs/source/commands/index.rst @@ -11,5 +11,6 @@ Commands Guide :caption: Contents: apply.rst - validate.rst + test.rst tiller.rst + validate.rst diff --git a/docs/source/commands/test.rst b/docs/source/commands/test.rst new file mode 100644 index 00000000..345dd246 --- /dev/null +++ b/docs/source/commands/test.rst @@ -0,0 +1,21 @@ +Armada - Test +============= + + +Commands +-------- + +.. code:: bash + + Usage: armada test + + Options: + + [-h] [--release RELEASE] [--file FILE] + + +Synopsis +-------- + +The test command will perform helm test defined on the release. Test command can +test a single release or a manifest. diff --git a/docs/source/operations/guide-build-armada-yaml.rst b/docs/source/operations/guide-build-armada-yaml.rst index 9c1ecbdb..9a636338 100644 --- a/docs/source/operations/guide-build-armada-yaml.rst +++ b/docs/source/operations/guide-build-armada-yaml.rst @@ -7,16 +7,16 @@ armada/Manifest/v1 +---------------------+--------+----------------------+ | keyword | type | action | +=====================+========+======================+ -| ``release_prefix`` | string | tag appended to the | +| ``release_prefix`` | string | appends to the | | | | front of all | | | | charts | | | | released | | | | by the | -| | | yaml in | +| | | manifest in | | | | order to | -| | | manage releses | +| | | manage releases | | | | throughout their | -| | | lifecycles | +| | | lifecycle | +---------------------+--------+----------------------+ | ``chart_groups`` | array | references | | | | ChartGroup document | @@ -52,6 +52,8 @@ armada/ChartGroup/v1 +-----------------+----------+------------------------------------------------------------------------+ | sequenced | bool | enables sequenced chart deployment in a group | +-----------------+----------+------------------------------------------------------------------------+ +| test_charts | bool | run pre-defined helm tests helm in a ChartGroup | ++-----------------+----------+------------------------------------------------------------------------+ Example ~~~~~~~~ @@ -87,6 +89,8 @@ Chart +-----------------+----------+---------------------------------------------------------------------------+ | timeout | int | time (in seconds) allotted for chart to deploy when 'wait' flag is set | +-----------------+----------+---------------------------------------------------------------------------+ +| test | bool | run pre-defined helm tests helm in a chart | ++-----------------+----------+---------------------------------------------------------------------------+ | install | object | install the chart into your Kubernetes cluster | +-----------------+----------+---------------------------------------------------------------------------+ | update | object | update the chart managed by the armada yaml | @@ -135,7 +139,7 @@ Update - Actions - Update/Delete +=============+==========+===============================================================+ | name | string | name of action | +-------------+----------+---------------------------------------------------------------+ -| type | string | type of K8s kind to execute | +| type | string | type of kubernetes workload to execute | +-------------+----------+---------------------------------------------------------------+ | labels | object | array of labels to query against kinds. (key: value) | +-------------+----------+---------------------------------------------------------------+ @@ -144,6 +148,84 @@ Update - Actions - Update/Delete Update Actions only support type: 'daemonset' +.. note:: + + Delete Actions only support type: 'job' + +Example +~~~~~~~ + +:: + + # type git + --- + schema: armada/Chart/v1 + metadata: + schema: metadata/Document/v1 + name: blog-1 + data: + chart_name: blog-1 + release_name: blog-1 + namespace: default + timeout: 100 + install: + no_hook: false + upgrade: + no_hook: false + values: {} + source: + type: git + location: https://github.com/namespace/repo + subpath: . + reference: master + dependencies: [] + + # type local + --- + schema: armada/Chart/v1 + metadata: + schema: metadata/Document/v1 + name: blog-1 + data: + chart_name: blog-1 + release_name: blog-1 + namespace: default + timeout: 100 + install: + no_hook: false + upgrade: + no_hook: false + values: {} + source: + type: local + location: /path/to/charts + subpath: chart + reference: master + dependencies: [] + + # type tar + --- + schema: armada/Chart/v1 + metadata: + schema: metadata/Document/v1 + name: blog-1 + data: + chart_name: blog-1 + release_name: blog-1 + namespace: default + timeout: 100 + install: + no_hook: false + upgrade: + no_hook: false + values: {} + source: + type: tar + location: https://localhost:8879/charts/chart-0.1.0.tgz + subpath: mariadb + reference: null + dependencies: [] + Source ^^^^^^ @@ -160,6 +242,7 @@ Source | reference | string | (optional) branch of the repo (``master`` if not specified) | +-------------+----------+-------------------------------------------------------------------------------+ + Example ~~~~~~~ @@ -205,31 +288,39 @@ Example +Defining a Manifest +~~~~~~~~~~~~~~~~~~~ -Defining a Chart -~~~~~~~~~~~~~~~~ +To define your Manifest you need to define a ``armada/Manifest/v1`` document, +``armada/ChartGroup/v1`` document, ``armada/Chart/v1``. +Following the definitions above for each document you will be able to construct +an armada manifest. -To define your charts is not any different than helm. we do provide some -post/pre actions that will help us manage our charts better. +Armada - Deploy Behavior +^^^^^^^^^^^^^^^^^^^^^^^^ -Behavior -^^^^^^^^ +1. Armada will perform set of pre-flight checks to before applying the manifest + - validate input manifest + - check tiller service is Running + - check chart source locations are valid -1. will check if chart exists +2. Deploying Armada Manifest - 1. if it does not exist + 1. If the chart is not found - we will install the chart - 2. if exist then - - armada will check if there are any differences in the charts + 3. If exist then + + - Armada will check if there are any differences in the chart - if the charts are different then it will execute an upgrade - else it will not perform any actions .. note:: - You can use references in order to build your charts, this will reduce the size of the chart definition will show example in multichart below + You can use references in order to build your charts, this will reduce + the size of the chart definition will show example in multichart below Simple Example ~~~~~~~~~~~~~~ diff --git a/examples/armada-manifest-v1.yaml b/examples/armada-manifest-v1.yaml index 33be0b34..2625bab5 100644 --- a/examples/armada-manifest-v1.yaml +++ b/examples/armada-manifest-v1.yaml @@ -109,19 +109,14 @@ metadata: name: keystone data: chart_name: keystone + test: true release: keystone namespace: undercloud - timeout: 100 + timeout: 3600 install: no_hooks: false upgrade: no_hooks: false - pre: - delete: - - name: keystone-db-sync - type: job - - name: keystone-db-init - type: job values: replicas: 2 source: @@ -142,7 +137,6 @@ data: chart_group: - mariadb - etcd - - rabbitmq - memcached --- schema: armada/ChartGroup/v1 @@ -151,7 +145,8 @@ metadata: name: openstack-keystone data: description: "Deploying OpenStack Keystone" - sequenced: False + sequenced: True + test_charts: False chart_group: - keystone --- diff --git a/setup.cfg b/setup.cfg index 7dbbfe0c..d16a70b3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -40,6 +40,9 @@ armada = apply = armada.cli.apply:ApplyChartsCommand tiller = armada.cli.tiller:TillerServerCommand validate = armada.cli.validate:ValidateYamlCommand + test = armada.cli.test:TestServerCommand +oslo.config.opts = + armada.conf = armada.conf.opts:list_opts [pbr] warnerrors = True