def uninstall_release(self, release, disable_hooks=False, purge=True): """ :params - release - helm chart release name :params - purge - deep delete of chart Deletes a helm chart from tiller """ stub = ReleaseServiceStub(self._channel) release_request = UninstallReleaseRequest(name=release, disable_hooks=disable_hooks, purge=purge) return stub.UninstallRelease(release_request, self._timeout, metadata=self.metadata)
def update_release(self, chart, namespace, dry_run=False, name=None, values=None, wait=False, disable_hooks=False, recreate=False, reset_values=False, reuse_values=False, force=False, description="", install=False): """ Update a Helm Release """ stub = ReleaseServiceStub(self._channel) if install: if not namespace: namespace = DEFAULT_NAMESPACE try: release_status = self.get_release_status(name) except grpc.RpcError as rpc_error_call: if not rpc_error_call.details() == "getting deployed release \"{}\": release: \"{}\" not found".format(name, name): raise rpc_error_call # The release doesn't exist - it's time to install self._logger.info( "Release %s does not exist. Installing it now.", name) return self.install_release(chart, namespace, dry_run, name, values, wait) if release_status.namespace != namespace: self._logger.warn("Namespace %s doesn't match with previous. Release will be deployed to %s", release_status.namespace, namespace) values = Config(raw=yaml.safe_dump(values or {})) release_request = UpdateReleaseRequest( chart=chart, dry_run=dry_run, disable_hooks=disable_hooks, values=values, name=name or '', wait=wait, recreate=recreate, reset_values=reset_values, reuse_values=reuse_values, force=force, description=description) return stub.UpdateRelease(release_request, self._timeout, metadata=self.metadata)
def install_release(self, chart, release, namespace, values=None, wait=False, timeout=None): ''' Create a Helm Release ''' timeout = self._check_timeout(wait, timeout) LOG.info('Helm install release%s: wait=%s, timeout=%s', (' (dry run)' if self.dry_run else ''), wait, timeout) if values is None: values = Config(raw='') else: values = Config(raw=values) # build release install request try: stub = ReleaseServiceStub(self.channel) release_request = InstallReleaseRequest(chart=chart, dry_run=self.dry_run, values=values, name=release, namespace=namespace, wait=wait, timeout=timeout) install_msg = stub.InstallRelease(release_request, timeout + GRPC_EPSILON, metadata=self.metadata) tiller_result = TillerResult( install_msg.release.name, install_msg.release.namespace, install_msg.release.info.status.Code.Name( install_msg.release.info.status.code), install_msg.release.info.Description, install_msg.release.version) return tiller_result except Exception: LOG.exception('Error while installing release %s', release) status = self.get_release_status(release) raise ex.ReleaseException(release, status, 'Install')
def test_release(self, release, timeout=const.DEFAULT_TILLER_TIMEOUT, cleanup=False): ''' :param release: name of release to test :param timeout: runtime before exiting :param cleanup: removes testing pod created :returns: test suite run object ''' LOG.info("Running Helm test: release=%s, timeout=%s", release, timeout) try: stub = ReleaseServiceStub(self.channel) # TODO: This timeout is redundant since we already have the grpc # timeout below, and it's actually used by tiller for individual # k8s operations not the overall request, should we: # 1. Remove this timeout # 2. Add `k8s_timeout=const.DEFAULT_K8S_TIMEOUT` arg and use release_request = TestReleaseRequest(name=release, timeout=timeout, cleanup=cleanup) test_message_stream = stub.RunReleaseTest(release_request, timeout, metadata=self.metadata) failed = 0 for test_message in test_message_stream: if test_message.status == test.TESTRUN_STATUS_FAILURE: failed += 1 LOG.info(test_message.msg) if failed: LOG.info('{} test(s) failed'.format(failed)) status = self.get_release_status(release) return status.info.status.last_test_suite_run except Exception: LOG.exception('Error while testing release %s', release) status = self.get_release_status(release) raise ex.ReleaseException(release, status, 'Test')
def list_releases(self, status_codes=None, filter="", namespace=""): """ List Helm Releases Possible status codes can be seen in the status_pb2 in part of Helm gRPC definition """ releases = [] # Convert the string status codes to the their numerical values if status_codes: codes_enum = _STATUS.enum_types_by_name.get("Code") request_status_codes = [ codes_enum.values_by_name.get(code).number for code in status_codes ] else: request_status_codes = [] offset = None stub = ReleaseServiceStub(self._channel) while True: req = ListReleasesRequest( limit=RELEASE_LIMIT, offset=offset, filter=filter, namespace=namespace, status_codes=request_status_codes, ) release_list = stub.ListReleases(req, self._timeout, metadata=self.metadata) for y in release_list: offset = str(y.next) releases.extend(y.releases) # This handles two cases: # 1. If there are no releases, offset will not be set and will remain None # 2. If there were releases, once we've fetched all of them, offset will be "" if not offset: break return releases
def tiller_version(self): ''' :returns: Tiller version ''' try: stub = ReleaseServiceStub(self.channel) release_request = GetVersionRequest() LOG.debug('Getting Tiller version, with timeout=%s', self.timeout) tiller_version = stub.GetVersion( release_request, self.timeout, metadata=self.metadata) tiller_version = getattr(tiller_version.Version, 'sem_ver', None) LOG.debug('Got Tiller version %s', tiller_version) return tiller_version except Exception: LOG.exception('Failed to get Tiller version.') raise ex.TillerVersionException()
def uninstall_release(self, release, disable_hooks=False, purge=True, timeout=None): ''' :param: release - Helm chart release name :param: purge - deep delete of chart :param: timeout - timeout for the tiller call Deletes a Helm chart from Tiller ''' if timeout is None: timeout = const.DEFAULT_DELETE_TIMEOUT # Helm client calls ReleaseContent in Delete dry-run scenario if self.dry_run: content = self.get_release_content(release) LOG.info( 'Skipping delete during `dry-run`, would have deleted ' 'release=%s from namespace=%s.', content.release.name, content.release.namespace) return # build release uninstall request try: stub = ReleaseServiceStub(self.channel) LOG.info( "Delete %s release with disable_hooks=%s, " "purge=%s, timeout=%s flags", release, disable_hooks, purge, timeout) release_request = UninstallReleaseRequest( name=release, disable_hooks=disable_hooks, purge=purge) return stub.UninstallRelease( release_request, timeout, metadata=self.metadata) except Exception: LOG.exception('Error while deleting release %s', release) status = self.get_release_status(release) raise ex.ReleaseException(release, status, 'Delete')
def install_release( self, chart, namespace, dry_run=False, name=None, values=None, wait=False, disable_hooks=False, reuse_name=False, disable_crd_hook=False, description="", ): """ Create a Helm Release """ if values: f = io.StringIO() yaml.dump(values, f) f.seek(0) raw = f.read() else: raw = "" values = Config(raw=raw) stub = ReleaseServiceStub(self._channel) release_request = InstallReleaseRequest( chart=chart, dry_run=dry_run, values=values, name=name or "", namespace=namespace, wait=wait, disable_hooks=disable_hooks, reuse_name=reuse_name, disable_crd_hook=disable_crd_hook, description=description, ) return stub.InstallRelease(release_request, self._timeout, metadata=self.metadata)
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 ''' LOG.debug("Helm test release %s, timeout=%s", release, timeout) 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: LOG.exception('Error while testing release %s', release) status = self.get_release_status(release) raise ex.ReleaseException(release, status, 'Test')
def get_release_content(self, release, version=0): ''' :param release: name of release to test :param version: version of release status ''' LOG.debug('Helm getting release content for release=%s, version=%s', release, version) try: stub = ReleaseServiceStub(self.channel) status_request = GetReleaseContentRequest(name=release, version=version) release_content = stub.GetReleaseContent(status_request, self.timeout, metadata=self.metadata) LOG.debug('GetReleaseContent= %s', release_content) return release_content except Exception: raise ex.GetReleaseContentException(release, version)
def get_release_status(self, release, version=0): ''' :param release: name of release to test :param version: version of release status ''' LOG.debug('Helm getting release status for release=%s, version=%s', release, version) try: stub = ReleaseServiceStub(self.channel) status_request = GetReleaseStatusRequest( name=release, version=version) release_status = stub.GetReleaseStatus( status_request, self.timeout, metadata=self.metadata) LOG.debug('GetReleaseStatus= %s', release_status) return release_status except Exception: LOG.exception('Cannot get tiller release status.') raise ex.GetReleaseStatusException(release, version)
def rollback_release(self, release_name, version, wait=False, timeout=None, force=False, recreate_pods=False): ''' Rollback a helm release. ''' timeout = self._check_timeout(wait, timeout) LOG.debug( 'Helm rollback%s of release=%s, version=%s, ' 'wait=%s, timeout=%s', (' (dry run)' if self.dry_run else ''), release_name, version, wait, timeout) try: stub = ReleaseServiceStub(self.channel) rollback_request = RollbackReleaseRequest( name=release_name, version=version, dry_run=self.dry_run, wait=wait, timeout=timeout, force=force, recreate=recreate_pods) rollback_msg = stub.RollbackRelease( rollback_request, timeout + GRPC_EPSILON, metadata=self.metadata) LOG.debug('RollbackRelease= %s', rollback_msg) return except Exception: LOG.exception('Error while rolling back tiller release.') raise ex.RollbackReleaseException(release_name, version)
def get_chart_templates(self, template_name, name, release_name, namespace, chart, disable_hooks, values): # returns some info LOG.info("Template( %s ) : %s ", template_name, name) stub = ReleaseServiceStub(self.channel) release_request = InstallReleaseRequest(chart=chart, dry_run=True, values=values, name=name, namespace=namespace, wait=False) templates = stub.InstallRelease(release_request, self.timeout, metadata=self.metadata) for template in yaml.load_all( getattr(templates.release, 'manifest', [])): if template_name == template.get('metadata', None).get('name', None): LOG.info(template_name) return template
def install_release(self, chart, namespace, dry_run=False, name=None, values=None, wait=False, disable_hooks=False, reuse_name=False, disable_crd_hook=False, overrides=[], description="", chartbuilder=None): """ Prepare overrides, note it only supports one file at the moment """ standard_values = {} if overrides is not None and chartbuilder is not None: standard_values = yaml.safe_load(chartbuilder.get_values().raw) for file in overrides: standard_values.update(yaml.safe_load(file.value)) """ Create a Helm Release """ # now this is where it gets tricky # since the overrides are appended 'left to right' # the later mappings override the standard values in `values.yaml` # the standard helm client does something similar but this 'works' (and is a kludge) values = Config(raw=yaml.safe_dump(standard_values)) stub = ReleaseServiceStub(self._channel) release_request = InstallReleaseRequest( chart=chart, dry_run=dry_run, values=values, name=name or '', namespace=namespace, wait=wait, disable_hooks=disable_hooks, reuse_name=reuse_name, disable_crd_hook=disable_crd_hook, description=description) return stub.InstallRelease(release_request, self._timeout, metadata=self.metadata)
def list_releases(self): ''' List Helm Releases ''' # TODO(MarshM possibly combine list_releases() with list_charts() # since they do the same thing, grouping output differently stub = ReleaseServiceStub(self.channel) # NOTE(seaneagan): Paging through releases to prevent hitting the # maximum message size limit that tiller sets for it's reponses. def get_results(): releases = [] done = False next_release_expected = "" initial_total = None while not done: req = ListReleasesRequest(offset=next_release_expected, limit=LIST_RELEASES_PAGE_SIZE, status_codes=const.STATUS_ALL) LOG.debug('Tiller ListReleases() with timeout=%s, request=%s', self.timeout, req) response = stub.ListReleases(req, self.timeout, metadata=self.metadata) found_message = False for message in response: found_message = True page = message.releases if initial_total: if message.total != initial_total: LOG.warning( 'Total releases changed between ' 'pages from (%s) to (%s)', initial_total, message.count) raise ex.TillerListReleasesPagingException() else: initial_total = message.total # Add page to results. releases.extend(page) if message.next: next_release_expected = message.next else: done = True # Ensure we break out was no message found which # is seen if there are no releases in tiller. if not found_message: done = True return releases for index in range(LIST_RELEASES_ATTEMPTS): attempt = index + 1 try: releases = get_results() except ex.TillerListReleasesPagingException: LOG.warning('List releases paging failed on attempt %s/%s', attempt, LIST_RELEASES_ATTEMPTS) if attempt == LIST_RELEASES_ATTEMPTS: raise else: # Filter out old releases, similar to helm cli: # https://github.com/helm/helm/blob/1e26b5300b5166fabb90002535aacd2f9cc7d787/cmd/helm/list.go#L196 latest_versions = {} for r in releases: max = latest_versions.get(r.name) if max is not None: if max > r.version: continue latest_versions[r.name] = r.version latest_releases = [] for r in releases: if latest_versions[r.name] == r.version: LOG.debug('Found release %s, version %s, status: %s', r.name, r.version, get_release_status(r)) latest_releases.append(r) return latest_releases
def update_release(self, chart, release, namespace, pre_actions=None, post_actions=None, disable_hooks=False, values=None, wait=False, timeout=None, force=False, recreate_pods=False): ''' Update a Helm Release ''' timeout = self._check_timeout(wait, timeout) LOG.info( 'Helm update release%s: wait=%s, timeout=%s, force=%s, ' 'recreate_pods=%s', (' (dry run)' if self.dry_run else ''), wait, timeout, force, recreate_pods) if values is None: values = Config(raw='') else: values = Config(raw=values) self._pre_update_actions(pre_actions, release, namespace, chart, disable_hooks, values, timeout) update_msg = None # build release install request try: stub = ReleaseServiceStub(self.channel) release_request = UpdateReleaseRequest( chart=chart, dry_run=self.dry_run, disable_hooks=disable_hooks, values=values, name=release, wait=wait, timeout=timeout, force=force, recreate=recreate_pods) update_msg = stub.UpdateRelease( release_request, timeout + GRPC_EPSILON, metadata=self.metadata) except Exception: LOG.exception('Error while updating release %s', release) status = self.get_release_status(release) raise ex.ReleaseException(release, status, 'Upgrade') tiller_result = TillerResult( update_msg.release.name, update_msg.release.namespace, update_msg.release.info.status.Code.Name( update_msg.release.info.status.code), update_msg.release.info.Description, update_msg.release.version) return tiller_result