sui_security_watchdog/
pagerduty.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use anyhow::anyhow;
5use serde::{Deserialize, Serialize};
6use std::sync::Arc;
7use tracing::info;
8
9#[derive(Serialize, Deserialize, Debug)]
10pub struct Service {
11    pub id: String,
12    #[serde(rename = "type")]
13    pub r#type: String,
14}
15
16impl Default for Service {
17    fn default() -> Self {
18        Service {
19            id: "".to_string(),
20            r#type: "service_reference".to_string(),
21        }
22    }
23}
24
25#[derive(Serialize, Deserialize, Debug)]
26pub struct Body {
27    #[serde(rename = "type")]
28    pub r#type: String,
29    pub details: String,
30}
31
32impl Default for Body {
33    fn default() -> Self {
34        Body {
35            r#type: "incident_body".to_string(),
36            details: "".to_string(),
37        }
38    }
39}
40
41#[derive(Serialize, Deserialize, Debug)]
42pub struct Incident {
43    pub incident_key: String,
44    #[serde(rename = "type")]
45    pub r#type: String,
46    pub title: String,
47    pub service: Service,
48    pub body: Body,
49}
50
51impl Default for Incident {
52    fn default() -> Self {
53        Incident {
54            incident_key: "".to_string(),
55            r#type: "incident".to_string(),
56            title: "".to_string(),
57            service: Service::default(),
58            body: Body::default(),
59        }
60    }
61}
62
63#[derive(Serialize, Deserialize)]
64pub struct CreateIncident {
65    pub incident: Incident,
66}
67
68#[derive(Clone)]
69pub struct Pagerduty {
70    pub client: Arc<reqwest::Client>,
71    pub api_key: String,
72}
73
74impl Pagerduty {
75    pub fn new(api_key: String) -> Self {
76        Pagerduty {
77            client: Arc::new(reqwest::Client::new()),
78            api_key,
79        }
80    }
81
82    pub async fn create_incident(
83        &self,
84        from: &str,
85        incident: CreateIncident,
86    ) -> anyhow::Result<()> {
87        let token = format!("Token token={}", self.api_key);
88
89        let response = self
90            .client
91            .post("https://api.pagerduty.com/incidents")
92            .header("Authorization", token)
93            .header("Content-Type", "application/json")
94            .header("Accept", "application/json")
95            .header("From", from)
96            .json(&incident)
97            .send()
98            .await?;
99        // Check if the status code is in the range of 200-299
100        if response.status().is_success() {
101            info!(
102                "Created incident with key: {:?}",
103                incident.incident.incident_key
104            );
105            Ok(())
106        } else {
107            let status = response.status();
108            let text = response.text().await?;
109            if status.is_client_error()
110                && text.contains(
111                    "Open incident with matching dedup key already exists on this service",
112                )
113            {
114                info!(
115                    "Incident already exists with key: {}",
116                    incident.incident.incident_key
117                );
118                Ok(())
119            } else {
120                Err(anyhow!("Failed to create incident: {}", text))
121            }
122        }
123    }
124}