Skip to content

Commit 651c8fb

Browse files
committed
Add infrastructure for pagerduty integration
Right our pagerduty notifications come from heroku email alerts and a pingdom integration. We want to start paging on more specific metrics. So far we've discussed: - When no crate is uploaded for N period of time (likely 4 hours) - When a background job has failed N times (likely 5) - When the background queue has N jobs (likely 5) In order to do this we need to have some code in place that actually starts an incident and pages whoever is on call. We'd also like to test this code before we start relying on it, so I've added a binary we can use to make sure everything is configured correctly.
1 parent 0d5ed7a commit 651c8fb

File tree

4 files changed

+136
-29
lines changed

4 files changed

+136
-29
lines changed

Cargo.lock

Lines changed: 30 additions & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/bin/test-pagerduty.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//! Send a test event to pagerduty
2+
//!
3+
//! Usage:
4+
//! cargo run --bin test-pagerduty event_type [description]
5+
//!
6+
//! Event type can be trigger, acknowledge, or resolve
7+
8+
#![deny(warnings)]
9+
10+
extern crate cargo_registry;
11+
12+
use std::env::args;
13+
14+
use cargo_registry::on_call;
15+
16+
fn main() {
17+
let args = args().collect::<Vec<_>>();
18+
19+
let event_type = &*args[1];
20+
let description = args.get(2).cloned();
21+
22+
let event = match event_type {
23+
"trigger" => on_call::Event::Trigger {
24+
incident_key: Some("test".into()),
25+
description: description.unwrap_or_else(|| "Test event".into()),
26+
},
27+
"acknowledge" => on_call::Event::Acknowledge {
28+
incident_key: "test".into(),
29+
description,
30+
},
31+
"resolve" => on_call::Event::Resolve {
32+
incident_key: "test".into(),
33+
description,
34+
},
35+
_ => panic!("Event type must be trigger, acknowledge, or resolve"),
36+
};
37+
event.send().unwrap()
38+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ pub mod email;
6969
pub mod git;
7070
pub mod github;
7171
pub mod middleware;
72+
pub mod on_call;
7273
pub mod render;
7374
pub mod schema;
7475
pub mod uploaders;

src/on_call.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use reqwest::{self, header, StatusCode as Status};
2+
use std::env;
3+
4+
use util::*;
5+
6+
#[derive(Serialize, Debug)]
7+
#[serde(rename_all = "snake_case", tag = "event_type")]
8+
pub enum Event {
9+
Trigger {
10+
incident_key: Option<String>,
11+
description: String,
12+
},
13+
Acknowledge {
14+
incident_key: String,
15+
description: Option<String>,
16+
},
17+
Resolve {
18+
incident_key: String,
19+
description: Option<String>,
20+
},
21+
}
22+
23+
impl Event {
24+
/// Sends the event to pagerduty.
25+
///
26+
/// If the variant is `Trigger`, this will page whoever is on call
27+
/// (potentially waking them up at 3 AM).
28+
pub fn send(self) -> CargoResult<()> {
29+
let api_token = env::var("PAGERDUTY_API_TOKEN")?;
30+
let service_key = env::var("PAGERDUTY_INTEGRATION_KEY")?;
31+
32+
let mut response = reqwest::Client::new()
33+
.post("https://events.pagerduty.com/generic/2010-04-15/create_event.json")
34+
.header(header::ACCEPT, "application/vnd.pagerduty+json;version=2")
35+
.header(header::AUTHORIZATION, format!("Token token={}", api_token))
36+
.json(&FullEvent {
37+
service_key,
38+
event: self,
39+
}).send()?;
40+
41+
match response.status() {
42+
s if s.is_success() => Ok(()),
43+
Status::BAD_REQUEST => {
44+
let error = response.json::<InvalidEvent>()?;
45+
Err(internal(&format_args!("pagerduty error: {:?}", error)))
46+
}
47+
Status::FORBIDDEN => Err(internal("rate limited by pagerduty")),
48+
n => Err(internal(&format_args!(
49+
"Got a non 200 response code from pagerduty: {} with {:?}",
50+
n, response
51+
))),
52+
}
53+
}
54+
}
55+
56+
#[derive(Serialize, Debug)]
57+
struct FullEvent {
58+
service_key: String,
59+
#[serde(flatten)]
60+
event: Event,
61+
}
62+
63+
#[derive(Deserialize, Debug)]
64+
struct InvalidEvent {
65+
message: String,
66+
errors: Vec<String>,
67+
}

0 commit comments

Comments
 (0)