Exemple #1
0
    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)
Exemple #2
0
    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)
Exemple #3
0
    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')
Exemple #4
0
    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')
Exemple #5
0
    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
Exemple #6
0
    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()
Exemple #7
0
    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')
Exemple #8
0
    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)
Exemple #9
0
    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')
Exemple #10
0
    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)
Exemple #11
0
    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)
Exemple #12
0
    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)
Exemple #13
0
    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
Exemple #14
0
    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)
Exemple #15
0
    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
Exemple #16
0
    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