textbook / flash_services

The services that can be shown on a Flash dashboard.
https://github.com/textbook/flash
ISC License
2 stars 0 forks source link

Cloud Foundry service #12

Open textbook opened 7 years ago

textbook commented 7 years ago

Give information on the health and metrics (if available) of any CF apps

textbook commented 7 years ago

Draft service to fetch status of all apps in a space (note need to update HeaderMixin to not mangle any Authorization token):

"""Defines the Cloud Foundry service integration."""
import logging

import requests

from .auth import HeaderMixin
from .core import CustomRootMixin, Service
from .utils import naturaldelta

logger = logging.getLogger(__name__)

class CloudFoundry(CustomRootMixin, HeaderMixin, Service):

    AUTH_HEADER = 'Authorization'
    FRIENDLY_NAME = 'CF Space'
    REQUIRED = {'space_id'}
    TEMPLATE = 'cf-status-section'

    def __init__(self, *, space_id, api_token, **kwargs):
        kwargs['api_token'] = 'bearer {}'.format(api_token)
        super().__init__(**kwargs)
        self.space_id = space_id

    def update(self):
        logger.debug('fetching Cloud Foundry space data')
        response = requests.get(
            self.url_builder(
                '/v2/spaces/{space}/summary',
                params={'space': self.space_id},
            ),
            verify=False,
            headers=self.headers,
        )
        if response.status_code == 200:
            return self.format_data(response.json())
        logger.error('failed to update Cloud Foundry space data')
        return {}

    def format_data(self, data):
        apps = [self.app_status(app) for app in data.get('apps', [])]
        while len(apps) < 4:
            apps.append(dict(name='', outcome='cancelled', uptime=None))
        return dict(
            name=data.get('name', '<no name>'),
            apps=apps[:4],
            health='error' if any(app['outcome'] == 'crashed' for app in apps) else 'ok'
        )

    def app_status(self, app):
        guid = app['guid']
        name = app['name']
        state = app['state']
        if state == 'STARTED':
            return self.detailed_app_status(guid, name)
        return dict(
            outcome='crashed',
            name=name,
            uptime=None,
        )

    def detailed_app_status(self, guid, name):
        logger.debug('fetching status of %r', name)
        response = requests.get(
            self.url_builder(
                '/v2/apps/{app}/stats',
                params={'app': guid},
            ),
            headers=self.headers,
            verify=False,
        )
        if response.status_code == 200:
            return self.format_app(response.json())
        logger.error('failed to update status of %r', name)
        return dict(name=name, outcome='cancelled', uptime=None)

    def format_app(self, data):
        first_key = list(data.keys())[0]
        stats = data[first_key]['stats']
        return dict(
            outcome='passed',
            name=stats['name'],
            uptime='up for {}'.format(naturaldelta(stats['uptime'])),
        )
<section class="pane short-pane kudos-pane">
    {% for _ in range(4) %}
    <div class="pane-item build-outcome">
        <span class="item-title name"></span>
        <br/>
        <span class="uptime"></span>
    </div>
    {% endfor %}
</section>
function cloud_foundry(pane, data) {
    if (data.apps) {
      updateItems(pane, data.apps, '.build-outcome', function (element, data) {
          element.removeClass('passed failed crashed cancelled working');
          element.addClass(data.outcome);
          ['name', 'uptime'].forEach(function (attr) {
              element.children('.' + attr).text(data[attr]);
          });
      });
    }
}
textbook commented 7 years ago

It seems like the OAuth token you can get from the CLI will expire and you'll end up with 503s. Perhaps the token can be periodically refreshed. Also it should be more obvious from the frontend that the data is now out of date (textbook/flash#34).