from flask import Flask , request
import os
import incident
import datetime
import subprocess
import re
def strip_ansi ( text , escape = re . compile ( r ' \ x1B \ [[0-?]*[ -/]*[@-~] ' ) ) :
return escape . sub ( ' ' , text )
def strip_progress ( text , escape = re . compile ( r ' \ r[^ \ n]* \ n ' ) ) :
return escape . sub ( ' ' , text )
app = Flask ( __name__ )
app . config . from_envvar ( ' FIH_SETTINGS ' )
app . mm = incident . Mattermost ( url = app . config [ ' MATTERMOST_URL ' ] ,
port = app . config [ ' MATTERMOST_PORT ' ] ,
login_id = app . config [ ' MATTERMOST_USER_EMAIL ' ] ,
password = app . config [ ' MATTERMOST_USER_PASSWORD ' ] ,
) . mm
@app.route ( ' /mattermost/incident ' , methods = [ ' POST ' ] )
def mattermost_incident ( ) :
# channel_name: testing
# command: /incident
# channel_id: o3ob1utim3nummoswjyt4174mo
# user_name: marek
# test_domain: faelix
# text: test
# team_id: 3woo8mbjrbb1in53xwdzi4ynqh
# user_id: ko3uxmend7ne8ger4uw9mxkt1o
# token: XXXXXXXXXXXXXXXXXXXXXXXXXX
if request . form [ ' token ' ] in app . config [ ' MATTERMOST_COMMAND_TOKENS ' ] :
incident_number = incident . generate_incident_number ( )
fi_number = " FI# " + incident_number
channel_name = " incident " + incident_number
purpose = request . form [ ' text ' ]
channel = app . mm . channels . create_channel ( options = { " team_id " : request . form [ ' team_id ' ] ,
" name " : channel_name ,
" display_name " : fi_number ,
" purpose " : purpose ,
" header " : " " ,
" type " : " O " ,
} )
# add user to the channel we just created
app . mm . client . make_request ( ' post ' , ' /channels/ ' + channel [ ' id ' ] + ' /members ' , options = { ' user_id ' : request . form [ ' user_id ' ] ,
} )
app . mm . posts . create_post ( options = { ' channel_id ' : channel [ ' id ' ] ,
' message ' : " Incident discussion channel for [ %s ](https://status.faelix.net/incident/ %s ) created by @ %s . " % ( fi_number , incident_number , request . form [ ' user_name ' ] ) ,
} )
now = datetime . datetime . utcnow ( ) . strftime ( " % Y- % m- %d T % H: % M: % S " )
app . mm . posts . create_post ( options = { ' channel_id ' : channel [ ' id ' ] ,
' message ' : """ \
# Incident Response Communication Procedure
The contents of this channel are strictly confidential .
## Public Announcement
1. adjust the name of the incident with * * / purpose * *
2. adjust the description of the incident with * * / header * *
3. choose a front page status ( see below )
4. publish https : / / status . faelix . net / incident / % ( incident_number ) s with * * @FIH PUBLISH * *
## Status Updates
* add an update : * * @FIH TIMELINE * * _text_
* set the incident start : * * @FIH START * * _ % ( now ) s_ ( UTC )
* set the incident ETR : * * @FIH ETR * * _ % ( now ) s_ ( UTC )
* set the incident resolved time : * * @FIH RESOLVED * * _ % ( now ) s_ ( UTC )
* set the incident finished time : * * @FIH CLOSED * * _ % ( now ) s_ ( UTC )
Again , don ' t forget to **@FIH PUBLISH** once making changes.
## Front Page Status
* * * @FIH INCIDENT * *
* * * @FIH DEGRADED * *
* * * @FIH MAINTENANCE * *
* * * @FIH NOTICE * *
* * * @FIH OK * *
Again , after setting the headline status you must : * * @FIH PUBLISH * *
## Good luck, Incident Commander @%(commander_name)s!""" % { "incident_number": incident_number,
" commander_name " : request . form [ ' user_name ' ] ,
" now " : now ,
} ,
} )
app . mm . posts . create_post ( options = { ' channel_id ' : request . form [ ' channel_id ' ] ,
' message ' : ' On behalf of @ %s I have started incident ~ %s . ' % ( request . form [ ' user_name ' ] , channel_name , ) ,
} )
return " "
else :
return " Invalid command token. "
def exec_to_message ( cmdline ) :
results = " ` " + cmdline + " `: \n \n "
process = subprocess . Popen ( cmdline , shell = True , cwd = app . config [ " STATUS_ROOT " ] , stdout = subprocess . PIPE , stderr = subprocess . PIPE )
stdout = strip_progress ( strip_ansi ( process . stdout . read ( ) . decode ( ' ascii ' , ' ignore ' ) ) )
if stdout :
results + = " \n \n ``` \n " + stdout . replace ( " ``` " , " ` ` ` " ) + " ``` \n "
stderr = strip_progress ( strip_ansi ( process . stderr . read ( ) . decode ( ' ascii ' , ' ignore ' ) ) )
if stderr :
results + = " \n \n ``` \n " + stderr . replace ( " ``` " , " ` ` ` " ) + " ``` \n "
if not stdout and not stderr :
results + = ' _no output_ \n '
return results
@app.route ( ' /mattermost/fih ' , methods = [ ' POST ' ] )
def mattermost_fih ( ) :
# text: @FIH PUBLISH
# file_ids:
# user_name: marek
# trigger_word: @FIH
# channel_name: incident201806158693
# timestamp: 1529100914
# channel_id: zq61mk8ig7rmibtbyqf4babhfr
# team_id: 3woo8mbjrbb1in53xwdzi4ynqh
# token: nn54y4ozbtnzb8pri3c4gdhbce
# user_id: ko3uxmend7ne8ger4uw9mxkt1o
# team_domain: faelix
# post_id: 8cxn8yeyepyczcfbazdsd1bonw
if request . form [ ' token ' ] in app . config [ ' MATTERMOST_WEBHOOK_TOKENS ' ] :
cmd = request . form [ ' text ' ] . strip ( ) . split ( ) [ 1 ] . upper ( )
app . mm . posts . create_post ( options = { ' channel_id ' : request . form [ ' channel_id ' ] ,
' message ' : " ### Executing ` " + cmd + " `... " ,
} )
if cmd == " PUBLISH " :
for pull_from in app . config [ " MERCURIAL_PUSH_TO " ] :
cmdline = app . config [ " MERCURIAL_BIN " ] + " pull " + pull_from
message = exec_to_message ( cmdline )
app . mm . posts . create_post ( options = { ' channel_id ' : request . form [ ' channel_id ' ] ,
' message ' : message ,
} )
cmdline = app . config [ " MERCURIAL_BIN " ] + " merge "
message = exec_to_message ( cmdline )
app . mm . posts . create_post ( options = { ' channel_id ' : request . form [ ' channel_id ' ] ,
' message ' : message ,
} )
cmdline = app . config [ " MERCURIAL_BIN " ] + " commit -m ' merge from " + pull_from + " ' "
message = exec_to_message ( cmdline )
app . mm . posts . create_post ( options = { ' channel_id ' : request . form [ ' channel_id ' ] ,
' message ' : message ,
} )
for deploy_to in app . config [ " GROW_DEPLOY_TO " ] :
cmdline = app . config [ " GROW_BIN " ] + " deploy -f " + deploy_to
message = exec_to_message ( cmdline )
app . mm . posts . create_post ( options = { ' channel_id ' : request . form [ ' channel_id ' ] ,
' message ' : message ,
} )
elif cmd == " START " :
pass
elif cmd == " ETR " :
pass
elif cmd == " RESOLVED " :
pass
elif cmd == " CLOSED " :
pass
elif cmd == " TIMELINE " :
pass
elif cmd == " INCIDENT " :
pass
elif cmd == " DEGRADED " :
pass
elif cmd == " MAINTENANCE " :
pass
elif cmd == " NOTICE " :
pass
elif cmd == " OK " :
pass
else :
return " WTF command? "
app . mm . posts . create_post ( options = { ' channel_id ' : request . form [ ' channel_id ' ] ,
' message ' : " ### Finished ` " + cmd + " `! " ,
} )
return " OK "
else :
return " Invalid command token. "
@app.route ( ' / ' )
def hello_world ( ) :
return ' Hello, World! '