Skip to content

Commit 3040cff

Browse files
Matthew Brownjonathanballs
Matthew Brown
authored andcommitted
Updated models to show jenkins job number in StatusCheckResult. Job number and link is also reported in hipchat template.
Tests were not merged because alert testing has already been implemented. Conflicts: cabot/cabotapp/tests/tests_basic.py
1 parent d33c57a commit 3040cff

File tree

5 files changed

+183
-5
lines changed

5 files changed

+183
-5
lines changed

cabot/cabotapp/alert.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
{% endif %}
2424
"""
2525

26-
hipchat_template = "Service {{ service.name }} {% if service.overall_status == service.PASSING_STATUS %}is back to normal{% else %}reporting {{ service.overall_status }} status{% endif %}: {{ scheme }}://{{ host }}{% url 'service' pk=service.id %}. {% if service.overall_status != service.PASSING_STATUS %}Checks failing:{% for check in service.all_failing_checks %} {{ check.name }}{% if check.last_result.error %} ({{ check.last_result.error|safe }}){% endif %}{% endfor %}{% endif %}{% if alert %}{% for alias in users %} @{{ alias }}{% endfor %}{% endif %}"
26+
hipchat_template = "Service {{ service.name }} {% if service.overall_status == service.PASSING_STATUS %}is back to normal{% else %}reporting {{ service.overall_status }} status{% endif %}: {{ scheme }}://{{ host }}{% url 'service' pk=service.id %}. {% if service.overall_status != service.PASSING_STATUS %}Checks failing:{% for check in service.all_failing_checks %}{% if check.check_category == 'Jenkins check' %}{% if check.last_result.error %} {{ check.name }} ({{ check.last_result.error|safe }}) {{jenkins_api}}job/{{ check.name }}/{{ check.last_result.job_number }}/console{% else %} {{ check.name }} {{jenkins_api}}/job/{{ check.name }}/{{check.last_result.job_number}}/console {% endif %}{% else %} {{ check.name }} {% if check.last_result.error %} ({{ check.last_result.error|safe }}){% endif %}{% endif %}{% endfor %}{% endif %}{% if alert %}{% for alias in users %} @{{ alias }}{% endfor %}{% endif %}"
2727

2828
sms_template = "Service {{ service.name }} {% if service.overall_status == service.PASSING_STATUS %}is back to normal{% else %}reporting {{ service.overall_status }} status{% endif %}: {{ scheme }}://{{ host }}{% url 'service' pk=service.id %}"
2929

@@ -91,6 +91,7 @@ def send_hipchat_alert(service, users, duty_officers):
9191
'host': settings.WWW_HTTP_HOST,
9292
'scheme': settings.WWW_SCHEME,
9393
'alert': alert,
94+
'jenkins_api': settings.JENKINS_API,
9495
})
9596
message = Template(hipchat_template).render(c)
9697
_send_hipchat_alert(message, color=color, sender='Cabot/%s' % service.name)

cabot/cabotapp/jenkins.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def get_job_status(jobname):
2222
resp = requests.get(endpoint, auth=auth, verify=True)
2323
status = resp.json
2424
ret['status_code'] = resp.status_code
25+
ret['job_number'] = status['lastBuild'].get('number', None)
2526
if status['color'].startswith('blue'):
2627
ret['active'] = True
2728
ret['succeeded'] = True
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# -*- coding: utf-8 -*-
2+
import datetime
3+
from south.db import db
4+
from south.v2 import SchemaMigration
5+
from django.db import models
6+
7+
8+
class Migration(SchemaMigration):
9+
10+
def forwards(self, orm):
11+
# Adding field 'StatusCheckResult.job_number'
12+
db.add_column('cabotapp_statuscheckresult', 'job_number',
13+
self.gf('django.db.models.fields.PositiveIntegerField')(null=True),
14+
keep_default=False)
15+
16+
17+
def backwards(self, orm):
18+
# Deleting field 'StatusCheckResult.job_number'
19+
db.delete_column('cabotapp_statuscheckresult', 'job_number')
20+
21+
22+
models = {
23+
'auth.group': {
24+
'Meta': {'object_name': 'Group'},
25+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
26+
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
27+
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
28+
},
29+
'auth.permission': {
30+
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
31+
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
32+
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
33+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
34+
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
35+
},
36+
'auth.user': {
37+
'Meta': {'object_name': 'User'},
38+
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
39+
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
40+
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
41+
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
42+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
43+
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
44+
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
45+
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
46+
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
47+
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
48+
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
49+
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
50+
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
51+
},
52+
'cabotapp.instance': {
53+
'Meta': {'ordering': "['name']", 'object_name': 'Instance'},
54+
'address': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
55+
'alerts_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
56+
'email_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
57+
'hackpad_id': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
58+
'hipchat_alert': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
59+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
60+
'last_alert_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
61+
'name': ('django.db.models.fields.TextField', [], {}),
62+
'old_overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
63+
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
64+
'sms_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
65+
'status_checks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cabotapp.StatusCheck']", 'symmetrical': 'False', 'blank': 'True'}),
66+
'telephone_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
67+
'users_to_notify': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
68+
},
69+
'cabotapp.instancestatussnapshot': {
70+
'Meta': {'object_name': 'InstanceStatusSnapshot'},
71+
'did_send_alert': ('django.db.models.fields.IntegerField', [], {'default': 'False'}),
72+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
73+
'instance': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'snapshots'", 'to': "orm['cabotapp.Instance']"}),
74+
'num_checks_active': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
75+
'num_checks_failing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
76+
'num_checks_passing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
77+
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
78+
'time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'})
79+
},
80+
'cabotapp.service': {
81+
'Meta': {'ordering': "['name']", 'object_name': 'Service'},
82+
'alerts_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
83+
'email_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
84+
'hackpad_id': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
85+
'hipchat_alert': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
86+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
87+
'instances': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cabotapp.Instance']", 'symmetrical': 'False', 'blank': 'True'}),
88+
'last_alert_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
89+
'name': ('django.db.models.fields.TextField', [], {}),
90+
'old_overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
91+
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
92+
'sms_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
93+
'status_checks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cabotapp.StatusCheck']", 'symmetrical': 'False', 'blank': 'True'}),
94+
'telephone_alert': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
95+
'url': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
96+
'users_to_notify': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
97+
},
98+
'cabotapp.servicestatussnapshot': {
99+
'Meta': {'object_name': 'ServiceStatusSnapshot'},
100+
'did_send_alert': ('django.db.models.fields.IntegerField', [], {'default': 'False'}),
101+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
102+
'num_checks_active': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
103+
'num_checks_failing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
104+
'num_checks_passing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
105+
'overall_status': ('django.db.models.fields.TextField', [], {'default': "'PASSING'"}),
106+
'service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'snapshots'", 'to': "orm['cabotapp.Service']"}),
107+
'time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'})
108+
},
109+
'cabotapp.shift': {
110+
'Meta': {'object_name': 'Shift'},
111+
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
112+
'end': ('django.db.models.fields.DateTimeField', [], {}),
113+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
114+
'start': ('django.db.models.fields.DateTimeField', [], {}),
115+
'uid': ('django.db.models.fields.TextField', [], {}),
116+
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
117+
},
118+
'cabotapp.statuscheck': {
119+
'Meta': {'ordering': "['name']", 'object_name': 'StatusCheck'},
120+
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
121+
'cached_health': ('django.db.models.fields.TextField', [], {'null': 'True'}),
122+
'calculated_status': ('django.db.models.fields.CharField', [], {'default': "'passing'", 'max_length': '50', 'blank': 'True'}),
123+
'check_type': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
124+
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
125+
'debounce': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True'}),
126+
'endpoint': ('django.db.models.fields.TextField', [], {'null': 'True'}),
127+
'expected_num_hosts': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True'}),
128+
'frequency': ('django.db.models.fields.IntegerField', [], {'default': '5'}),
129+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
130+
'importance': ('django.db.models.fields.CharField', [], {'default': "'ERROR'", 'max_length': '30'}),
131+
'last_run': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
132+
'max_queued_build_time': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
133+
'metric': ('django.db.models.fields.TextField', [], {'null': 'True'}),
134+
'name': ('django.db.models.fields.TextField', [], {}),
135+
'password': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
136+
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_cabotapp.statuscheck_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
137+
'status_code': ('django.db.models.fields.TextField', [], {'default': '200', 'null': 'True'}),
138+
'text_match': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
139+
'timeout': ('django.db.models.fields.IntegerField', [], {'default': '30', 'null': 'True'}),
140+
'username': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
141+
'value': ('django.db.models.fields.TextField', [], {'null': 'True'}),
142+
'verify_ssl_certificate': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
143+
},
144+
'cabotapp.statuscheckresult': {
145+
'Meta': {'object_name': 'StatusCheckResult'},
146+
'check': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cabotapp.StatusCheck']"}),
147+
'error': ('django.db.models.fields.TextField', [], {'null': 'True'}),
148+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
149+
'job_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
150+
'raw_data': ('django.db.models.fields.TextField', [], {'null': 'True'}),
151+
'succeeded': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
152+
'time': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
153+
'time_complete': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'})
154+
},
155+
'cabotapp.userprofile': {
156+
'Meta': {'object_name': 'UserProfile'},
157+
'fallback_alert_user': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
158+
'hipchat_alias': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
159+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
160+
'mobile_number': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20', 'blank': 'True'}),
161+
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"})
162+
},
163+
'contenttypes.contenttype': {
164+
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
165+
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
166+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
167+
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
168+
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
169+
}
170+
}
171+
172+
complete_apps = ['cabotapp']

cabot/cabotapp/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,7 @@ def _run(self):
687687
try:
688688
status = get_job_status(self.name)
689689
active = status['active']
690+
result.job_number = status['job_number']
690691
if status['status_code'] == 404:
691692
result.error = u'Job %s not found on Jenkins' % self.name
692693
result.succeeded = False
@@ -744,6 +745,9 @@ class StatusCheckResult(models.Model):
744745
succeeded = models.BooleanField(default=False)
745746
error = models.TextField(null=True)
746747

748+
# Jenkins specific
749+
job_number = models.PositiveIntegerField(null=True)
750+
747751
def __unicode__(self):
748752
return '%s: %s @%s' % (self.status, self.check.name, self.time)
749753

cabot/cabotapp/tests/tests_basic.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22

33
import requests
4+
from django.conf import settings
45
from django.utils import timezone
56
from django.core.urlresolvers import reverse
67
from django.test import TestCase
@@ -12,15 +13,14 @@
1213
from rest_framework.reverse import reverse as api_reverse
1314
from cabot.cabotapp.models import (
1415
GraphiteStatusCheck, JenkinsStatusCheck,
15-
HttpStatusCheck, ICMPStatusCheck, Service, Instance, StatusCheckResult,
16-
UserProfile)
16+
HttpStatusCheck, ICMPStatusCheck, Service, Instance,
17+
StatusCheckResult, UserProfile)
1718
from cabot.cabotapp.views import StatusCheckReportForm
1819
from cabot.cabotapp.alert import (send_hipchat_alert, send_alert)
1920
from mock import Mock, patch
2021
from twilio import rest
21-
from django.utils import timezone
2222
from django.core import mail
23-
from datetime import timedelta, date
23+
from datetime import timedelta, date, datetime
2424
import json
2525
import os
2626
import base64

0 commit comments

Comments
 (0)