Skip to content

Commit 425638f

Browse files
authored
Merge pull request #61 from totem/develop
0.5.1 Release: Slack notifications
2 parents 67b3925 + 1154d20 commit 425638f

File tree

7 files changed

+179
-2
lines changed

7 files changed

+179
-2
lines changed

conf/appconfig.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,13 @@
165165
.strip().lower() in BOOLEAN_TRUE_VALUES,
166166
'token': '',
167167
'level': LEVEL_PENDING
168+
},
169+
'slack': {
170+
'enabled': os.getenv('SLACK_ENABLED', 'false').strip()
171+
.lower() in BOOLEAN_TRUE_VALUES,
172+
'level': LEVEL_FAILED,
173+
'channel': os.getenv('SLACK_CHANNEL', '#totem'),
174+
'url': os.getenv('SLACK_URL'),
168175
}
169176
}
170177
}

orchestrator/services/job.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,10 @@ def get_template_variables(owner, repo, ref, commit=None):
229229
:return: Template variables
230230
:rtype: dict
231231
"""
232-
ref_number = ref.lower().replace('feature_', '').replace('patch_', '')
232+
ref_number = ref.lower()\
233+
.replace('feature_', '')\
234+
.replace('patch_', '') \
235+
.replace('develop_', '')
233236
commit = commit or 'na'
234237
return {
235238
'owner': owner,

orchestrator/tasks/notification.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Tasks for notification
33
"""
44
import json
5+
6+
import time
57
from future.builtins import ( # noqa
68
bytes, dict, int, list, object, range, str,
79
ascii, chr, hex, input, next, oct, open,
@@ -64,6 +66,25 @@ def notify_hipchat(obj, ctx, level, config, security_profile):
6466
headers=headers).raise_for_status()
6567

6668

69+
@app.task
70+
def notify_slack(obj, ctx, level, config, security_profile):
71+
config = decrypt_config(config, profile=security_profile)
72+
ctx.setdefault('github', True)
73+
url = config.get('url')
74+
notification = util.as_dict(obj)
75+
notification['channel'] = config.get('channel')
76+
notification['date'] = int(time.time())
77+
msg = templatefactory.render_template(
78+
'slack.json.jinja', notification=notification, ctx=ctx,
79+
level=level)
80+
headers = {
81+
'content-type': 'application/json',
82+
}
83+
if url:
84+
requests.post(url, data=msg, headers=headers)\
85+
.raise_for_status()
86+
87+
6788
@app.task
6889
def notify_github(obj, ctx, level, config, security_profile):
6990
config = decrypt_config(config, profile=security_profile)
@@ -96,5 +117,5 @@ def notify_github(obj, ctx, level, config, security_profile):
96117
requests.post(status_url, data=json.dumps(data),
97118
headers=headers).raise_for_status()
98119
else:
99-
# Github notification not sent
120+
# Github notification is not sent
100121
pass
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"username": "Orchestrator ({{ctx.env}}-{{ctx.operation}})",
3+
"channel": "{{ notification.channel or '#totem' }}",
4+
"text": " ",
5+
6+
"attachments": [
7+
{
8+
"text": "{% if notification.code %}{{ notification.code}}: {% endif %}{{ notification.message | truncate(1000) }} {%if notification.code == 'CONFIG_VALIDATION_ERROR' -%}
9+
<https://github.com/totem/cluster-orchestrator/blob/master/schemas/job-config-v1.json | Click here to view the job-config-v1 schema.>
10+
{%- endif %} {% if ctx['job-id'] -%}
11+
(job-id: {{ ctx['job-id'] }})
12+
{%- endif -%}",
13+
"color":
14+
{% if level == 1 %}
15+
"danger"
16+
{% elif level == 2 %}
17+
"warning"
18+
{% elif level == 3 %}
19+
"good"
20+
{% else %}
21+
"#439FE0"
22+
{% endif %},
23+
"footer":
24+
{% if ctx.github %}
25+
"<https://github.com/{{ctx.owner}}|{{ctx.owner or 'NA'}}> / <https://github.com/{{ctx.owner}}/{{ctx.repo}}|{{ctx.repo or 'NA'}}> / <https://github.com/{{ctx.owner}}/{{ctx.repo}}/tree/{{ctx.ref}}|{{ctx.ref | truncate(30, True) or 'NA'}}> / <https://github.com/{{ctx.owner}}/{{ctx.repo}}/commit/{{(ctx.commit or ctx.ref or 'NA')[0:7]}}|{{(ctx.commit or ctx.ref or 'NA')[0:7]}}>\n"
26+
{% else %}
27+
"{{ctx.owner or 'NA'}}/{{ctx.repo or 'NA'}}/{{ctx.ref | truncate(30, True) or 'NA' }}/{{(ctx.commit or 'NA')[0:7]}}"
28+
{% endif %},
29+
"ts": "{{ notification.date }}"
30+
}
31+
]
32+
33+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import json
2+
3+
from nose.tools import eq_, ok_
4+
5+
from orchestrator import templatefactory
6+
7+
"""
8+
Tests for Slack Jinja templates
9+
"""
10+
11+
12+
def test_slack_template():
13+
"""
14+
should render json output for slack API.
15+
"""
16+
17+
output = templatefactory.render_template(
18+
'slack.json.jinja',
19+
ctx={
20+
"env": "local",
21+
"operation": "test",
22+
"owner": "test-owner",
23+
"repo": "test-repo",
24+
"ref": "test-ref",
25+
"github": True,
26+
"job-id": "test-job"
27+
},
28+
notification={
29+
"message": "test message",
30+
"code": "CONFIG_VALIDATION_ERROR"
31+
}, level=1)
32+
slack_dict = json.loads(output)
33+
34+
eq_(slack_dict.get("username"), "Orchestrator (local-test)")
35+
eq_(slack_dict.get("channel"), "#totem")
36+
ok_(slack_dict.get("text"))
37+
ok_(slack_dict.get("attachments"))

tests/unit/orchestrator/services/test_job.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,28 @@ def test_template_variables():
345345
})
346346

347347

348+
def test_template_variables_for_develop_branch():
349+
"""
350+
Should return default template variables for job config
351+
"""
352+
353+
# When: I get template variables for given repository
354+
# owner, repo, ref and commit
355+
variables = get_template_variables(
356+
MOCK_OWNER, MOCK_REPO, 'develop_test', MOCK_COMMIT)
357+
358+
# Then: Default variables for the config template are returned
359+
dict_compare(variables, {
360+
'owner': MOCK_OWNER,
361+
'repo': MOCK_REPO,
362+
'ref': 'develop_test',
363+
'commit': MOCK_COMMIT,
364+
'ref_number': 'test',
365+
'cluster': CLUSTER_NAME,
366+
'env': TOTEM_ENV
367+
})
368+
369+
348370
def test_create_search_params():
349371
"""
350372
Should return search parameters for given job

tests/unit/orchestrator/tasks/test_notification.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,60 @@ def test_notify_hipchat_for_level_success(m_json, m_templatefactory,
150150
})
151151

152152

153+
@patch('orchestrator.tasks.notification.requests')
154+
@patch('orchestrator.tasks.notification.templatefactory')
155+
@patch('orchestrator.tasks.notification.json')
156+
def test_notify_slack(m_json, m_templatefactory, m_requests):
157+
"""
158+
Should send slack notification
159+
:return:
160+
"""
161+
# Given: Template factory that renders html template for notification
162+
m_templatefactory.render_template.return_value = '{}'
163+
164+
# And: Mock implementation for jsonify (for validating data)
165+
m_json.dumps.side_effect = lambda data: data
166+
167+
# When: I send message using slack
168+
notification.notify_slack(
169+
{'message': 'mock'}, {}, LEVEL_FAILED,
170+
{'url': 'http://mockslackurl'},
171+
'default')
172+
173+
# Then: Notification gets send successfully
174+
m_requests.post.assert_called_once_with(
175+
'http://mockslackurl',
176+
headers={
177+
'content-type': 'application/json'
178+
},
179+
data='{}')
180+
181+
182+
@patch('orchestrator.tasks.notification.requests')
183+
@patch('orchestrator.tasks.notification.templatefactory')
184+
@patch('orchestrator.tasks.notification.json')
185+
def test_notify_slack_when_url_is_not_set(m_json, m_templatefactory,
186+
m_requests):
187+
"""
188+
Should not send slack notification
189+
:return:
190+
"""
191+
# Given: Template factory that renders html template for notification
192+
m_templatefactory.render_template.return_value = '{}'
193+
194+
# And: Mock implementation for jsonify (for validating data)
195+
m_json.dumps.side_effect = lambda data: data
196+
197+
# When: I send message using slack
198+
notification.notify_slack(
199+
{'message': 'mock'}, {}, LEVEL_FAILED,
200+
{},
201+
'default')
202+
203+
# Then: Notification is not sent
204+
m_requests.post.assert_not_called()
205+
206+
153207
@patch('orchestrator.tasks.notification.requests')
154208
@patch('orchestrator.tasks.notification.json')
155209
def test_github(m_json, m_requests):

0 commit comments

Comments
 (0)