Fixing up notification testing (#575)

This commit is contained in:
kevgliss
2016-12-08 11:33:40 -08:00
committed by GitHub
parent be1415fbd4
commit a4b32b0d31
16 changed files with 173 additions and 99 deletions

View File

@ -16,7 +16,7 @@ class NotificationPlugin(Plugin):
"""
type = 'notification'
def send(self):
def send(self, notification_type, message, targets, options, **kwargs):
raise NotImplementedError
@ -48,5 +48,5 @@ class ExpirationNotificationPlugin(NotificationPlugin):
def options(self):
return list(self.default_options) + self.additional_options
def send(self):
def send(self, notification_type, message, targets, options, **kwargs):
raise NotImplementedError

View File

@ -85,10 +85,10 @@ class EmailNotificationPlugin(ExpirationNotificationPlugin):
raise InvalidConfiguration('Email sender type {0} is not recognized.')
@staticmethod
def send(template_name, message, targets, **kwargs):
subject = 'Lemur: Expiration Notification'
def send(notification_type, message, targets, options, **kwargs):
subject = 'Lemur: {0} Notification'.format(notification_type.capitalize())
body = render_html(template_name, message)
body = render_html(notification_type, message)
s_type = current_app.config.get("LEMUR_EMAIL_SENDER", 'ses').lower()

View File

@ -79,18 +79,18 @@
<table border="0" cellspacing="0" cellpadding="0"
style="margin-top:48px;margin-bottom:48px">
<tbody>
{% for message in messages %}
{% for certificate in certificates %}
<tr valign="middle">
<td width="32px"></td>
<td width="16px"></td>
<td style="line-height:1.2">
<span style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:20px;color:#202020">{{ message.name }}</span>
<span style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:20px;color:#202020">{{ certificate.name }}</span>
<br>
<span style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:13px;color:#727272">
{{ message.endpoints | length }} Endpoints
<br>{{ message.owner }}
<br>{{ message.not_after | time }}
<a href="https://{{ hostname }}/#/certificates/{{ message.name }}" target="_blank">Details</a>
{{ certificate.endpoints | length }} Endpoints
<br>{{ certificate.owner }}
<br>{{ certificate.validityEnd | time }}
<a href="https://{{ hostname }}/#/certificates/{{ certificate.name }}" target="_blank">Details</a>
</span>
</td>
</tr>

View File

@ -43,7 +43,7 @@
<tr>
<td width="32px"></td>
<td style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:24px;color:#ffffff;line-height:1.25">
Your certificate(s) have been rotated!
Your certificate has been rotated!
</td>
<td width="32px"></td>
</tr>
@ -74,44 +74,31 @@
<br>This is a Lemur certificate rotation notice. Your certificate has been re-issued and re-provisioned.
</td>
</tr>
<tr>
<td style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:20px;color:#202020;line-height:1.5">
Impacted Certificate:
</td>
</tr>
<tr>
<td style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:13px;color:#202020;line-height:1.5">
<table border="0" cellspacing="0" cellpadding="0"
style="margin-top:48px;margin-bottom:48px">
<tbody>
{% for message in messages %}
<tr valign="middle">
<td width="32px"></td>
<td width="16px"></td>
<td style="line-height:1.2">
<span style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:20px;color:#202020">{{ message.name }}</span>
<span style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:20px;color:#202020">{{ certificate.name }}</span>
<br>
<span style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:13px;color:#727272">
{{ message.endpoints | length }} Endpoints
<br>{{ message.owner }}
<br>{{ message.not_after | time }}
<a href="https://{{ hostname }}/#/certificates/{{ message.name }}" target="_blank">Details</a>
<br>{{ certificate.owner }}
<br>{{ certificate.validityEnd | time }}
<a href="https://{{ hostname }}/#/certificates/{{ certificate.name }}" target="_blank">Details</a>
</span>
</td>
</tr>
{% if not loop.last %}
<tr valign="middle">
<td width="32px" height="24px"></td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</td>
</tr>
<tr>
<td style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:20px;color:#202020;line-height:1.5">
Impacted Endpoints:
<td style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:15px;color:#202020;line-height:1.5">
Replaced by:
</td>
</tr>
<tr>
@ -119,18 +106,43 @@
<table border="0" cellspacing="0" cellpadding="0"
style="margin-top:48px;margin-bottom:48px">
<tbody>
{% for message in messages %}
<tr valign="middle">
<td width="32px"></td>
<td width="16px"></td>
<td style="line-height:1.2">
<span style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:20px;color:#202020">{{ certificate.replaced.name }}</span>
<br>
<span style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:13px;color:#727272">
<br>{{ certificate.replaced[0].owner }}
<br>{{ certificate.replaced[0].validityEnd | time }}
<a href="https://{{ hostname }}/#/certificates/{{ certificate.replaced[0].name }}" target="_blank">Details</a>
</span>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:15px;color:#202020;line-height:1.5">
Endpoints Rotated:
</td>
</tr>
<tr>
<td style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:13px;color:#202020;line-height:1.5">
<table border="0" cellspacing="0" cellpadding="0"
style="margin-top:48px;margin-bottom:48px">
<tbody>
{% for endpoint in certificate.endpoints %}
<tr valign="middle">
<td width="32px"></td>
<td width="16px"></td>
<td style="line-height:1.2">
<span style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:20px;color:#202020">{{ message.name }}</span>
<span style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:20px;color:#202020">{{ endpoint.name }}</span>
<br>
<span style="font-family:Roboto-Regular,Helvetica,Arial,sans-serif;font-size:13px;color:#727272">
{{ message.endpoints | length }} Endpoints
<br>{{ message.owner }}
<br>{{ message.not_after | time }}
<a href="https://{{ hostname }}/#/certificates/{{ message.name }}" target="_blank">Details</a>
<br>{{ endpoint.dnsname }} | {{ endpoint.port }}
<a href="https://{{ hostname }}/#/endpoints/{{ endpoint.name }}" target="_blank">Details</a>
</span>
</td>
</tr>

View File

@ -0,0 +1 @@
from lemur.tests.conftest import * # noqa

View File

@ -1,12 +1,34 @@
import os
from lemur.plugins.lemur_email.templates.config import env
from lemur.tests.factories import CertificateFactory
def test_render():
messages = [{
'name': 'a-really-really-long-certificate-name',
'owner': 'bob@example.com',
'not_after': '2015-12-14 23:59:59'
}] * 10
dir_path = os.path.dirname(os.path.realpath(__file__))
def test_render(certificate, endpoint):
from lemur.certificates.schemas import certificate_notification_output_schema
new_cert = CertificateFactory()
new_cert.replaces.append(certificate)
certificates = [certificate_notification_output_schema.dump(certificate).data]
template = env.get_template('{}.html'.format('expiration'))
body = template.render(dict(messages=messages, hostname='lemur.test.example.com'))
with open(os.path.join(dir_path, 'expiration-rendered.html'), 'w') as f:
body = template.render(dict(certificates=certificates, hostname='lemur.test.example.com'))
f.write(body)
template = env.get_template('{}.html'.format('rotation'))
certificate.endpoints.append(endpoint)
with open(os.path.join(dir_path, 'rotation-rendered.html'), 'w') as f:
body = template.render(
dict(
certificate=certificate_notification_output_schema.dump(certificate).data,
hostname='lemur.test.example.com'
)
)
f.write(body)

View File

@ -23,28 +23,28 @@ def create_certificate_url(name):
)
def create_expiration_attachments(messages):
def create_expiration_attachments(certificates):
attachments = []
for message in messages:
for certificate in certificates:
attachments.append({
'title': message['name'],
'title_link': create_certificate_url(message['name']),
'title': certificate['name'],
'title_link': create_certificate_url(certificate['name']),
'color': 'danger',
'fallback': '',
'fields': [
{
'title': 'Owner',
'value': message['owner'],
'value': certificate['owner'],
'short': True
},
{
'title': 'Expires',
'value': arrow.get(message['not_after']).format('dddd, MMMM D, YYYY'),
'value': arrow.get(certificate['validityEnd']).format('dddd, MMMM D, YYYY'),
'short': True
},
{
'title': 'Endpoints Detected',
'value': len(message['endpoints']),
'value': len(certificate['endpoints']),
'short': True
}
],
@ -54,6 +54,37 @@ def create_expiration_attachments(messages):
return attachments
def create_rotation_attachments(certificate):
return {
'title': certificate['name'],
'title_link': create_certificate_url(certificate['name']),
'fields': [
{
{
'title': 'Owner',
'value': certificate['owner'],
'short': True
},
{
'title': 'Expires',
'value': arrow.get(certificate['validityEnd']).format('dddd, MMMM D, YYYY'),
'short': True
},
{
'title': 'Replaced By',
'value': len(certificate['replaced'][0]['name']),
'short': True
},
{
'title': 'Endpoints Rotated',
'value': len(certificate['endpoints']),
'short': True
}
}
]
}
class SlackNotificationPlugin(ExpirationNotificationPlugin):
title = 'Slack'
slug = 'slack-notification'
@ -85,25 +116,31 @@ class SlackNotificationPlugin(ExpirationNotificationPlugin):
},
]
def send(self, event_type, message, targets, options, **kwargs):
def send(self, notification_type, message, targets, options, **kwargs):
"""
A typical check can be performed using the notify command:
`lemur notify`
"""
if event_type == 'expiration':
attachments = None
if notification_type == 'expiration':
attachments = create_expiration_attachments(message)
elif notification_type == 'rotation':
attachments = create_rotation_attachments(message)
if not attachments:
raise Exception('Unable to create message attachments')
body = {
'text': 'Lemur Expiration Notification',
'text': 'Lemur {0} Notification'.format(notification_type.capitalize()),
'attachments': attachments,
'channel': self.get_option('recipients', options),
'username': self.get_option('username', options)
}
r = requests.post(self.get_option('webhook', options), json.dumps(body))
if r.status_code not in [200]:
raise Exception('Failed to send message')
current_app.logger.error("Slack response: {0} Message Body: {1}".format(r.status_code, body))

View File

@ -2,8 +2,8 @@
def test_formatting(certificate):
from lemur.plugins.lemur_slack.plugin import create_expiration_attachments
from lemur.notifications.service import _get_message_data
data = [_get_message_data(certificate)]
from lemur.certificates.schemas import certificate_notification_output_schema
data = [certificate_notification_output_schema.dump(certificate).data]
attachment = {
'title': certificate.name,