feat(armada): adding helm testing framework

- added helm test framework to armada
- added helm test status

Closes #151

Change-Id: I417cae04b4595ad0d4fd05889d90c83907607c47
This commit is contained in:
Alexis Rivera De La Torre
2017-08-07 18:22:09 +00:00
committed by gardlt
parent 70e95d64f5
commit d5f4378731
11 changed files with 447 additions and 96 deletions

View File

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

View File

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

View File

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