Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add simple integration test and misc fixes #148

Merged
merged 3 commits into from Dec 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Expand Up @@ -125,5 +125,5 @@ run: $(VIRTUALENV)

.PHONY: t
# target: t - Runs tests
t: $(VIRTUALENV)/bin/py.test
$(VIRTUALENV)/bin/py.test -xs
t: $(VIRTUALENV)/bin/py.test tests
$(VIRTUALENV)/bin/py.test -xs tests
27 changes: 10 additions & 17 deletions graphite_beacon/alerts.py
@@ -1,22 +1,17 @@
"""Implement alerts."""

import math

from collections import deque, defaultdict
from collections import defaultdict, deque
from itertools import islice

from tornado import ioloop, httpclient as hc, gen, log, escape
from tornado import httpclient as hc
from tornado import escape, gen, ioloop, log

from . import _compat as _, units
from . import _compat as _
from . import units
from .graphite import GraphiteRecord
from .units import MILLISECOND, TimeUnit
from .utils import (
HISTORICAL,
LOGICAL_OPERATORS,
convert_to_format,
parse_rule,
)

from .utils import HISTORICAL, LOGICAL_OPERATORS, convert_to_format, parse_rule

LOGGER = log.gen_log
METHODS = "average", "last_value", "sum", "minimum", "maximum"
Expand Down Expand Up @@ -115,15 +110,15 @@ def configure(self, name=None, rules=None, query=None, **options):

time_window_raw = options.get(
'time_window',
options.get('interval', interval_raw)
self.reactor.options.get('time_window', interval_raw),
)
time_window = TimeUnit.from_interval(time_window_raw)
self.time_window = TimeUnit.from_interval(time_window_raw)

until_raw = options.get('until', self.reactor.options['until'])
self.until = TimeUnit.from_interval(until_raw)

# Adjust the start time to cater for `until`
self.from_time = time_window + self.until
self.from_time = self.time_window + self.until

self._format = options.get('format', self.reactor.options['format'])
self.request_timeout = options.get(
Expand Down Expand Up @@ -165,12 +160,10 @@ def start(self):
"""Start checking."""
self.callback.start()
self.load()
return self

def stop(self):
"""Stop checking."""
self.callback.stop()
return self

def check(self, records):
"""Check current value."""
Expand Down Expand Up @@ -277,7 +270,7 @@ def load(self):
connect_timeout=self.connect_timeout,
validate_cert=self.validate_cert)
records = (
GraphiteRecord(line.decode('utf-8'), self.default_nan_value, self.ignore_nan)
GraphiteRecord(line, self.default_nan_value, self.ignore_nan)
for line in response.buffer)
data = [
(None if record.empty else getattr(record, self.method), record.target)
Expand Down
10 changes: 6 additions & 4 deletions graphite_beacon/app.py
Expand Up @@ -7,7 +7,6 @@

from .core import Reactor


LOGGER = log.gen_log
DEFAULT_CONFIG_PATH = 'config.json'

Expand All @@ -32,10 +31,13 @@ def run():

reactor = Reactor(**options_dict)

signal.signal(signal.SIGTERM, reactor.stop)
signal.signal(signal.SIGINT, reactor.stop)
stop = lambda *args: reactor.stop()
reinit = lambda *args: reactor.reinit()

signal.signal(signal.SIGTERM, stop)
signal.signal(signal.SIGINT, stop)
if hasattr(signal, 'SIGHUP'):
signal.signal(signal.SIGHUP, reactor.reinit)
signal.signal(signal.SIGHUP, reinit)

reactor.start()

Expand Down
43 changes: 29 additions & 14 deletions graphite_beacon/core.py
@@ -1,17 +1,16 @@
import sys
import os
from re import compile as re, M

import json
import logging
import os
import sys
from re import compile as re
from re import M

from tornado import ioloop, log
import yaml
from tornado import ioloop, log

from .alerts import BaseAlert
from .units import MILLISECOND, TimeUnit
from .handlers import registry

from .units import MILLISECOND, TimeUnit

LOGGER = log.gen_log

Expand Down Expand Up @@ -62,14 +61,14 @@ def __init__(self, **options):
self.callback = ioloop.PeriodicCallback(
self.repeat, repeat_interval.convert_to(MILLISECOND))

def started(self):
def is_running(self):
"""Check whether the reactor is running.

:rtype: bool
"""
return hasattr(self, 'callback') and self.callback.is_running()

def reinit(self, *args, **options): # pylint: disable=unused-argument
def reinit(self, **options): # pylint: disable=unused-argument
LOGGER.info('Read configuration')

self.options.update(options)
Expand All @@ -79,7 +78,7 @@ def reinit(self, *args, **options): # pylint: disable=unused-argument
config_valid = config_valid and self.include_config(config)

# If we haven't started the ioloop yet and config is invalid then fail fast.
if not self.started() and not config_valid:
if not self.is_running() and not config_valid:
sys.exit(1)

if not self.options['public_graphite_url']:
Expand All @@ -98,12 +97,20 @@ def reinit(self, *args, **options): # pylint: disable=unused-argument
self.alerts.remove(alert)

self.alerts = set(
BaseAlert.get(self, **opts).start() for opts in self.options.get('alerts')) # pylint: disable=no-member
BaseAlert.get(self, **opts) for opts in self.options.get('alerts')) # pylint: disable=no-member

# Only auto-start alerts if the reactor is already running
if self.is_running():
self.start_alerts()

LOGGER.debug('Loaded with options:')
LOGGER.debug(json.dumps(self.options, indent=2))
return self

def start_alerts(self):
for alert in self.alerts:
alert.start()

def include_config(self, config):
LOGGER.info('Load configuration: %s' % config)
if config:
Expand Down Expand Up @@ -136,15 +143,23 @@ def repeat(self):
for alert in self.alerts:
alert.reset()

def start(self, *args): # pylint: disable=unused-argument
def start(self, start_loop=True):
"""Start all the things.

:param start_loop bool: whether to start the ioloop. should be False if
the IOLoop is managed externally
"""
self.start_alerts()
if self.options.get('pidfile'):
with open(self.options.get('pidfile'), 'w') as fpid:
fpid.write(str(os.getpid()))
self.callback.start()
LOGGER.info('Reactor starts')
self.loop.start()

def stop(self, *args): # pylint: disable=unused-argument
if start_loop:
self.loop.start()

def stop(self):
self.callback.stop()
self.loop.stop()
if self.options.get('pidfile'):
Expand Down
8 changes: 7 additions & 1 deletion graphite_beacon/graphite.py
@@ -1,7 +1,13 @@
class GraphiteRecord(object):

def __init__(self, metric_string, default_nan_value=None, ignore_nan=False):
meta, data = metric_string.split('|')
try:
meta, data = metric_string.split('|')
except ValueError:
peek = ((metric_string[:40] + '..')
if len(metric_string) > 40 else metric_string)
raise ValueError("Unable to parse graphite record: {}".format(peek))

self.target, start_time, end_time, step = meta.rsplit(',', 3)
self.start_time = int(start_time)
self.end_time = int(end_time)
Expand Down
2 changes: 1 addition & 1 deletion graphite_beacon/handlers/cli.py
@@ -1,6 +1,6 @@
import subprocess

from graphite_beacon.handlers import AbstractHandler, LOGGER
from graphite_beacon.handlers import LOGGER, AbstractHandler


class CliHandler(AbstractHandler):
Expand Down
5 changes: 3 additions & 2 deletions graphite_beacon/handlers/hipchat.py
@@ -1,8 +1,9 @@
import json

from tornado import gen, httpclient as hc
from tornado import httpclient as hc
from tornado import gen

from graphite_beacon.handlers import AbstractHandler, LOGGER
from graphite_beacon.handlers import LOGGER, AbstractHandler


class HipChatHandler(AbstractHandler):
Expand Down
5 changes: 3 additions & 2 deletions graphite_beacon/handlers/http.py
@@ -1,8 +1,9 @@
import urllib

from tornado import gen, httpclient as hc
from tornado import httpclient as hc
from tornado import gen

from graphite_beacon.handlers import AbstractHandler, LOGGER
from graphite_beacon.handlers import LOGGER, AbstractHandler


class HttpHandler(AbstractHandler):
Expand Down
2 changes: 1 addition & 1 deletion graphite_beacon/handlers/log.py
@@ -1,4 +1,4 @@
from graphite_beacon.handlers import AbstractHandler, LOGGER
from graphite_beacon.handlers import LOGGER, AbstractHandler


class LogHandler(AbstractHandler):
Expand Down
5 changes: 3 additions & 2 deletions graphite_beacon/handlers/pagerduty.py
@@ -1,8 +1,9 @@
import json

from tornado import gen, httpclient as hc
from tornado import httpclient as hc
from tornado import gen

from graphite_beacon.handlers import AbstractHandler, LOGGER
from graphite_beacon.handlers import LOGGER, AbstractHandler


class PagerdutyHandler(AbstractHandler):
Expand Down
5 changes: 3 additions & 2 deletions graphite_beacon/handlers/slack.py
@@ -1,8 +1,9 @@
import json

from tornado import gen, httpclient as hc
from tornado import httpclient as hc
from tornado import gen

from graphite_beacon.handlers import AbstractHandler, LOGGER
from graphite_beacon.handlers import LOGGER, AbstractHandler
from graphite_beacon.template import TEMPLATES


Expand Down
4 changes: 2 additions & 2 deletions graphite_beacon/handlers/smtp.py
Expand Up @@ -3,9 +3,9 @@
from email.mime.text import MIMEText
from smtplib import SMTP

from tornado import gen, concurrent
from tornado import concurrent, gen

from graphite_beacon.handlers import AbstractHandler, TEMPLATES, LOGGER
from graphite_beacon.handlers import LOGGER, TEMPLATES, AbstractHandler


class SMTPHandler(AbstractHandler):
Expand Down
4 changes: 3 additions & 1 deletion graphite_beacon/handlers/telegram.py
Expand Up @@ -2,11 +2,13 @@

import json
from os.path import exists

from tornado import gen, httpclient

from graphite_beacon.handlers import AbstractHandler, LOGGER
from graphite_beacon.handlers import LOGGER, AbstractHandler
from graphite_beacon.template import TEMPLATES


HELP_MESSAGE = """Telegram handler for graphite-beacon
*usage* /command [parameters]
*examples*
Expand Down
10 changes: 6 additions & 4 deletions graphite_beacon/handlers/victorops.py
@@ -1,13 +1,15 @@
import json

from tornado import httpclient as hc
from tornado import gen

from graphite_beacon.handlers import LOGGER, AbstractHandler

try:
from urlparse import urljoin
except ImportError:
from urllib.parse import urljoin

from tornado import gen, httpclient as hc

from graphite_beacon.handlers import AbstractHandler, LOGGER


class VictorOpsHandler(AbstractHandler):

Expand Down
1 change: 0 additions & 1 deletion graphite_beacon/template.py
Expand Up @@ -2,7 +2,6 @@

from tornado import template


LOADER = template.Loader(op.join(op.dirname(op.abspath(__file__)), 'templates'), autoescape=None)
TEMPLATES = {
'graphite': {
Expand Down
3 changes: 1 addition & 2 deletions graphite_beacon/units.py
Expand Up @@ -2,7 +2,6 @@

import re


NUMBER_RE = re.compile(r'(?P<value>\-?\d*\.?\d*)(?P<unit>\w+)')

# Time units
Expand Down Expand Up @@ -64,7 +63,7 @@ def __init__(self, value, unit):
if self.value < 0:
raise ValueError("Negative time units are not supported: {}".format(value))
if not self.unit:
raise ValueError("Unable to parse time unit: {}".format(value))
raise ValueError("Unable to parse time unit: {}{}".format(value, unit))

def display_value(self):
return int(self.value) if self.value.is_integer() else self.value
Expand Down
7 changes: 3 additions & 4 deletions graphite_beacon/utils.py
@@ -1,9 +1,8 @@
from re import compile as re
import operator as op
from re import compile as re

from funcparserlib.lexer import make_tokenizer, Token
from funcparserlib.parser import (some, a, maybe, finished, skip, many)

from funcparserlib.lexer import Token, make_tokenizer
from funcparserlib.parser import a, finished, many, maybe, skip, some

# NOTE: the unit conversions below should be considered deprecated and migrated
# over to `unit.py` instead.
Expand Down
1 change: 0 additions & 1 deletion pytest.ini
@@ -1,3 +1,2 @@
[pytest]
norecursedirs = env
addopts = tests
Empty file added tests/__init__.py
Empty file.
Empty file added tests/integration/__init__.py
Empty file.