Skip to content

Commit

Permalink
Initial work on removing v1 rules
Browse files Browse the repository at this point in the history
  • Loading branch information
kfdm committed May 29, 2019
1 parent f08d5ae commit 4904948
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 63 deletions.
7 changes: 1 addition & 6 deletions promgen/management/commands/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('--reload', action='store_true', help='Trigger Prometheus Reload')
parser.add_argument(
'--format', type=int, dest='version',
default=settings.PROMGEN['prometheus'].get('version', 1),
help='Prometheus rule format. Defaults to promgen.yml version (%(default)s)')
parser.add_argument(
'out',
nargs='?',
Expand All @@ -28,10 +24,9 @@ def handle(self, **kwargs):
tasks.write_rules(
path=kwargs['out'],
reload=kwargs['reload'],
version=kwargs['version']
)
else:
# Since we're already working with utf8 encoded data, we can skip
# the newline ending here
self.stdout.ending = None
self.stdout.write(prometheus.render_rules(version=kwargs['version']))
self.stdout.write(prometheus.render_rules())
50 changes: 19 additions & 31 deletions promgen/prometheus.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,32 @@
import tempfile
from urllib.parse import urljoin

import promgen.templatetags.promgen as macro
import pytz
import yaml
from dateutil import parser

import promgen.templatetags.promgen as macro
from promgen import models, util

from django.conf import settings
from django.core.exceptions import ValidationError
from django.db.models import prefetch_related_objects
from django.template.loader import render_to_string
from django.utils import timezone
from promgen import models, util

logger = logging.getLogger(__name__)


def check_rules(rules):
'''
"""
Use promtool to check to see if a rule is valid or not
The command name changed slightly from 1.x -> 2.x but this uses promtool
to verify if the rules are correct or not. This can be bypassed by setting
a dummy command such as /usr/bin/true that always returns true
'''
"""

with tempfile.NamedTemporaryFile(mode='w+b') as fp:
logger.debug('Rendering to %s', fp.name)
with tempfile.NamedTemporaryFile(mode="w+b") as fp:
logger.debug("Rendering to %s", fp.name)
# Normally we wouldn't bother saving a copy to a variable here and would
# leave it in the fp.write() call, but saving a copy in the variable
# means we can see the rendered output in a Sentry stacktrace
Expand All @@ -42,53 +43,40 @@ def check_rules(rules):
fp.flush()

# This command changed to be without a space in 2.x
cmd = [settings.PROMGEN['prometheus']['promtool']]
if settings.PROMGEN['prometheus'].get('version') == 2:
cmd += ['check', 'rules']
else:
cmd += ['check-rules']
cmd += [fp.name]
cmd = [settings.PROMGEN["prometheus"]["promtool"], "check", "rules", fp.name]

try:
subprocess.check_output(cmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
raise ValidationError(rendered.decode('utf8') + e.output.decode('utf8'))
raise ValidationError(rendered.decode("utf8") + e.output.decode("utf8"))


def render_rules(rules=None, version=None):
'''
def render_rules(rules=None):
"""
Render rules in a format that Prometheus understands
:param rules: List of rules
:type rules: list(Rule)
:param int version: Prometheus rule format (1 or 2)
:return: Returns rules in yaml or Prometheus v1 format
:rtype: bytes
This function can render in either v1 or v2 format
We call prefetch_related_objects within this function to populate the
other related objects that are mostly used for the sub lookups.
'''
"""
if rules is None:
rules = models.Rule.objects.filter(enabled=True)
if version is None:
version = settings.PROMGEN['prometheus'].get('version', 1)

prefetch_related_objects(
rules,
'content_object',
'content_type',
'overrides__content_object',
'overrides__content_type',
'ruleannotation_set',
'rulelabel_set',
"content_object",
"content_type",
"overrides__content_object",
"overrides__content_type",
"ruleannotation_set",
"rulelabel_set",
)

# V1 format is a custom format which we render through django templates
# See promgen/tests/examples/import.rule
if version == 1:
return render_to_string('promgen/prometheus.rule', {'rules': rules}).encode('utf-8')

# V2 format is a yaml dictionary which we build and then render
# See promgen/tests/examples/import.rule.yml
rule_list = collections.defaultdict(list)
Expand Down
11 changes: 3 additions & 8 deletions promgen/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,10 @@ def services(self, request, name):

class SharedViewSet:
def format(self, rules=None, name='promgen'):
version = settings.PROMGEN['prometheus'].get('version', 1)
content = prometheus.render_rules(rules, version=version)
content = prometheus.render_rules(rules)
response = HttpResponse(content)
if version == 1:
response['Content-Type'] = 'text/plain; charset=utf-8'
response['Content-Disposition'] = 'attachment; filename=%s.rule' % name
else:
response['Content-Type'] = 'application/x-yaml'
response['Content-Disposition'] = 'attachment; filename=%s.rule.yml' % name
response['Content-Type'] = 'application/x-yaml'
response['Content-Disposition'] = 'attachment; filename=%s.rule.yml' % name
return response


Expand Down
4 changes: 2 additions & 2 deletions promgen/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ def write_config(path=None, reload=True, chmod=0o644):


@shared_task
def write_rules(path=None, reload=True, chmod=0o644, version=None):
def write_rules(path=None, reload=True, chmod=0o644):
if path is None:
path = settings.PROMGEN["prometheus"]["rules"]
with atomic_write(path, mode="wb", overwrite=True) as fp:
# Set mode on our temporary file before we write and move it
os.chmod(fp.name, chmod)
fp.write(prometheus.render_rules(version=version))
fp.write(prometheus.render_rules())
if reload:
reload_prometheus()
9 changes: 2 additions & 7 deletions promgen/tests/test_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,8 @@ def setUp(self, mock_signal):
models.RuleAnnotation.objects.create(name='summary', value='Test case', rule=self.rule)

@mock.patch('django.dispatch.dispatcher.Signal.send')
def test_write_old(self, mock_post):
result = prometheus.render_rules(version=1)
self.assertEqual(result, _RULES % self.rule.id)

@mock.patch('django.dispatch.dispatcher.Signal.send')
def test_write_new(self, mock_post):
result = prometheus.render_rules(version=2)
def test_write_rule_v2(self, mock_post):
result = prometheus.render_rules()
self.assertEqual(result, _RULE_NEW % self.rule.id)

@mock.patch('django.dispatch.dispatcher.Signal.send')
Expand Down
13 changes: 4 additions & 9 deletions promgen/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -993,16 +993,11 @@ def post(self, request):


class _ExportRules(View):
def format(self, rules=None, name='promgen'):
version = settings.PROMGEN['prometheus'].get('version', 1)
content = prometheus.render_rules(rules, version=version)
def format(self, rules=None, name="promgen"):
content = prometheus.render_rules(rules)
response = HttpResponse(content)
if version == 1:
response['Content-Type'] = 'text/plain; charset=utf-8'
response['Content-Disposition'] = 'attachment; filename=%s.rule' % name
else:
response['Content-Type'] = 'application/x-yaml'
response['Content-Disposition'] = 'attachment; filename=%s.rule.yml' % name
response["Content-Type"] = "application/x-yaml"
response["Content-Disposition"] = "attachment; filename=%s.rule.yml" % name
return response


Expand Down

0 comments on commit 4904948

Please sign in to comment.