Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A few incident trigger tweaks #228

Merged
merged 12 commits into from
Oct 1, 2024
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ Then add **hubot-pager-me** to your `external-scripts.json`:
| `HUBOT_PAGERDUTY_FROM_EMAIL` | Yes | The email of the default "actor" user for incident creation and modification. |
| `HUBOT_PAGERDUTY_USER_ID` | No`*` | The user ID of a PagerDuty user for your bot. This is only required if you want chat users to be able to trigger incidents without their own PagerDuty user.
| `HUBOT_PAGERDUTY_SERVICE_API_KEY` | No`*` | The [Incident Service Key](https://v2.developer.pagerduty.com/docs/incident-creation-api) to use when creating a new incident. This should be assigned to a dummy escalation policy that doesn't actually notify, as Hubot will trigger on this before reassigning it.
| `HUBOT_PAGERDUTY_SERVICES` | No | Provide a comma separated list of service identifiers (e.g. `PFGPBFY,AFBCGH`) to restrict queries to only those services. |
| `HUBOT_PAGERDUTY_SCHEDULES` | No | Provide a comma separated list of schedules identifiers (e.g. `PFGPBFY,AFBCGH`) to restrict queries to only those schedules. |
| `HUBOT_PAGERDUTY_SERVICES` | No | Provide a comma separated list of service identifiers (e.g. `PFGPBFY,AFBCGH`) to restrict queries to only those services.
| `HUBOT_PAGERDUTY_SCHEDULES` | No | Provide a comma separated list of schedules identifiers (e.g. `PFGPBFY,AFBCGH`) to restrict queries to only those schedules.
| `HUBOT_PAGERDUTY_DEFAULT_SCHEDULE` | No | A specific schedule that can be triggered using `pager default trigger` to reduce typing for users.

`*` - May be required for certain actions.

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "hubot-pager-me",
"description": "PagerDuty integration for Hubot",
"version": "4.0.4",
"version": "4.1.0",
"author": "Josh Nichols <[email protected]>",
"license": "MIT",
"keywords": [
Expand Down
224 changes: 126 additions & 98 deletions src/scripts/pagerduty.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
// Commands:
// hubot pager me as <email> - remember your pager email is <email>
// hubot pager forget me - forget your pager email
// hubot Am I on call - return if I'm currently on call or not
// hubot am I on call - return if I'm currently on call or not
// hubot who's on call - return a list of services and who is on call for them
// hubot who's on call for <schedule> - return the username of who's on call for any schedule matching <search>
// hubot pager trigger <user> <msg> - create a new incident with <msg> and assign it to <user>
// hubot pager default trigger <msg> - create a new incident with <msg> and assign it the user currently on call for our default schedule
// hubot pager trigger <schedule> <msg> - create a new incident with <msg> and assign it the user currently on call for <schedule>
// hubot pager incidents - return the current incidents
// hubot pager sup - return the current incidents
Expand Down Expand Up @@ -45,9 +46,12 @@ const moment = require('moment-timezone');
const pagerDutyUserId = process.env.HUBOT_PAGERDUTY_USER_ID;
const pagerDutyServiceApiKey = process.env.HUBOT_PAGERDUTY_SERVICE_API_KEY;
const pagerDutySchedules = process.env.HUBOT_PAGERDUTY_SCHEDULES;
const pagerDutyDefaultSchedule = process.env.HUBOT_PAGERDUTY_DEFAULT_SCHEDULE;


module.exports = function (robot) {
let campfireUserToPagerDutyUser;

robot.respond(/pager( me)?$/i, function (msg) {
if (pagerduty.missingEnvironmentForApi(msg)) {
return;
Expand Down Expand Up @@ -151,110 +155,42 @@ module.exports = function (robot) {
robot.respond(/(pager|major)( me)? (?:trigger|page) ([\w\-]+)$/i, (msg) =>
msg.reply("Please include a user or schedule to page, like 'hubot pager infrastructure everything is on fire'.")
);

robot.respond(
/(pager|major)( me)? (?:trigger|page) ((["'])([^\4]*?)\4|“([^”]*?)”|‘([^’]*?)’|([\.\w\-]+)) (.+)$/i,
/(pager|major)( me)? default (?:trigger|page) ?(.+)?$/i,
function (msg) {
msg.finish();

if (pagerduty.missingEnvironmentForApi(msg)) {
return;
}

if (!pagerDutyDefaultSchedule) {
msg.send("No default schedule configured! Cannot send a page! Please set HUBOT_PAGERDUTY_DEFAULT_SCHEDULE");
return;
}
const fromUserName = msg.message.user.name;
let query = msg.match[5] || msg.match[6] || msg.match[7] || msg.match[8];
const reason = msg.match[9];
const description = `${reason} - @${fromUserName}`;

// Figure out who we are
campfireUserToPagerDutyUser(msg, msg.message.user, false, function (triggerdByPagerDutyUser) {
const triggerdByPagerDutyUserId = (() => {
if (triggerdByPagerDutyUser != null) {
return triggerdByPagerDutyUser.id;
} else if (pagerDutyUserId) {
return pagerDutyUserId;
}
})();
if (!triggerdByPagerDutyUserId) {
msg.send(
`Sorry, I can't figure your PagerDuty account, and I don't have my own :( Can you tell me your PagerDuty email with \`${robot.name} pager me as [email protected]\` or make sure you've set the HUBOT_PAGERDUTY_USER_ID environment variable?`
);
return;
}

// Figure out what we're trying to page
reassignmentParametersForUserOrScheduleOrEscalationPolicy(msg, query, function (results) {
if (!(results.assigned_to_user || results.escalation_policy)) {
msg.reply(`Couldn't find a user or unique schedule or escalation policy matching ${query} :/`);
return;
}

return pagerDutyIntegrationAPI(msg, 'trigger', description, function (json) {
query = { incident_key: json.incident_key };

msg.reply(':pager: triggered! now assigning it to the right user...');

return setTimeout(
() =>
pagerduty.get('/incidents', query, function (err, json) {
if (err != null) {
robot.emit('error', err, msg);
return;
}

if ((json != null ? json.incidents.length : undefined) === 0) {
msg.reply("Couldn't find the incident we just created to reassign. Please try again :/");
} else {
}

let data = null;
if (results.escalation_policy) {
data = {
incidents: json.incidents.map((incident) => ({
id: incident.id,
type: 'incident_reference',
query = pagerDutyDefaultSchedule;
reason = msg.match[4] || "We Need Help!"
description = `${reason} - @${fromUserName}`;
robot.logger.debug(`Triggering a default page to ${pagerDutyDefaultSchedule} saying ${description}!`);
incidentTrigger(msg, query, description);
}
);

escalation_policy: {
id: results.escalation_policy,
type: 'escalation_policy_reference',
},
})),
};
} else {
data = {
incidents: json.incidents.map((incident) => ({
id: incident.id,
type: 'incident_reference',

assignments: [
{
assignee: {
id: results.assigned_to_user,
type: 'user_reference',
},
},
],
})),
};
}
robot.respond(
/(pager|major)( me)? (?:trigger|page) ((["'])([^\4]*?)\4|“([^”]*?)”|‘([^’]*?)’|([\.\w\-]+)) ?(.+)?$/i,
function (msg) {
msg.finish();

return pagerduty.put('/incidents', data, function (err, json) {
if (err != null) {
robot.emit('error', err, msg);
return;
}

if ((json != null ? json.incidents.length : undefined) === 1) {
return msg.reply(`:pager: assigned to ${results.name}!`);
} else {
return msg.reply('Problem reassigning the incident :/');
}
});
}),
10000
);
});
});
});
if (pagerduty.missingEnvironmentForApi(msg)) {
return;
}
const fromUserName = msg.message.user.name;
const query = msg.match[5] || msg.match[6] || msg.match[7] || msg.match[8];
const reason = msg.match[9] || "We Need Help!";
const description = `${reason} - @${fromUserName}`;
robot.logger.debug(`Triggering a page to ${query} saying ${description}!`);
incidentTrigger(msg, query, description);
}
);

Expand Down Expand Up @@ -722,14 +658,14 @@ module.exports = function (robot) {
);
return;
}

withScheduleMatching(msg, msg.match[2], function (matchingSchedule) {
const schedule = msg.match[2].replace(/(^"|"$)/mg, '');
const minutes = parseInt(msg.match[3]);
withScheduleMatching(msg, schedule, function (matchingSchedule) {
if (!matchingSchedule.id) {
return;
}

let start = moment().format();
const minutes = parseInt(msg.match[3]);
let end = moment().add(minutes, 'minutes').format();
const override = {
start,
Expand Down Expand Up @@ -1182,6 +1118,98 @@ module.exports = function (robot) {
}
});

var incidentTrigger = (msg, query, description) =>
// Figure out who we are
campfireUserToPagerDutyUser(msg, msg.message.user, false, function (triggerdByPagerDutyUser) {
const triggerdByPagerDutyUserId = (() => {
if (triggerdByPagerDutyUser != null) {
return triggerdByPagerDutyUser.id;
} else if (pagerDutyUserId) {
return pagerDutyUserId;
}
})();
if (!triggerdByPagerDutyUserId) {
msg.send(
`Sorry, I can't figure your PagerDuty account, and I don't have my own :( Can you tell me your PagerDuty email with \`${robot.name} pager me as [email protected]\` or make sure you've set the HUBOT_PAGERDUTY_USER_ID environment variable?`
);
return;
}

// Figure out what we're trying to page
reassignmentParametersForUserOrScheduleOrEscalationPolicy(msg, query, function (results) {
if (!(results.assigned_to_user || results.escalation_policy)) {
msg.reply(`Couldn't find a user or unique schedule or escalation policy matching ${query} :/`);
return;
}

return pagerDutyIntegrationAPI(msg, 'trigger', description, function (json) {
query = { incident_key: json.incident_key };

msg.reply(':pager: triggered! now assigning it to the right user...');

return setTimeout(
() =>
pagerduty.get('/incidents', query, function (err, json) {
if (err != null) {
robot.emit('error', err, msg);
return;
}

if ((json != null ? json.incidents.length : undefined) === 0) {
msg.reply("Couldn't find the incident we just created to reassign. Please try again :/");
} else {
}

let data = null;
if (results.escalation_policy) {
data = {
incidents: json.incidents.map((incident) => ({
id: incident.id,
type: 'incident_reference',

escalation_policy: {
id: results.escalation_policy,
type: 'escalation_policy_reference',
},
})),
};
} else {
data = {
incidents: json.incidents.map((incident) => ({
id: incident.id,
type: 'incident_reference',

assignments: [
{
assignee: {
id: results.assigned_to_user,
type: 'user_reference',
},
},
],
})),
};
}

return pagerduty.put('/incidents', data, function (err, json) {
if (err != null) {
robot.emit('error', err, msg);
return;
}

if ((json != null ? json.incidents.length : undefined) === 1) {
return msg.reply(`:pager: assigned to ${results.name}!`);
} else {
return msg.reply('Problem reassigning the incident :/');
}
});
}),
10000
);
});
});
});

const userEmail = (user) =>
user.pagerdutyEmail ||
user.email_address ||
Expand Down
6 changes: 5 additions & 1 deletion test/pager-me-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const { expect } = chai;
describe('pagerduty', function () {
before(function () {
this.triggerRegex =
/(pager|major)( me)? (?:trigger|page) ((["'])([^\4]*?)\4|“([^”]*?)”|‘([^’]*?)’|([\.\w\-]+)) (.+)$/i;
/(pager|major)( me)? (?:trigger|page) ((["'])([^\4]*?)\4|“([^”]*?)”|‘([^’]*?)’|([\.\w\-]+)) ?(.+)?$/i;
this.schedulesRegex = /(pager|major)( me)? schedules( ((["'])([^]*?)\5|(.+)))?$/i;
this.whosOnCallRegex =
/who(?:’s|'s|s| is|se)? (?:on call|oncall|on-call)(?:\?)?(?: (?:for )?((["'])([^]*?)\2|(.*?))(?:\?|$))?$/i;
Expand Down Expand Up @@ -46,6 +46,10 @@ describe('pagerduty', function () {
expect(this.robot.respond).to.have.been.calledWith(/(pager|major)( me)? (?:trigger|page) ([\w\-]+)$/i);
});

it('registers a pager default trigger with message listener', function () {
expect(this.robot.respond).to.have.been.calledWith(/(pager|major)( me)? default (?:trigger|page) ?(.+)?$/i);
});

it('registers a pager trigger with message listener', function () {
expect(this.robot.respond).to.have.been.calledWith(this.triggerRegex);
});
Expand Down