pagerduty 2.1.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,12 +1,14 @@
1
1
  # pagerduty
2
2
 
3
+ [![License MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/envato/pagerduty/blob/HEAD/LICENSE.txt)
4
+ [![Gem Version](https://img.shields.io/gem/v/pagerduty.svg?maxAge=2592000)](https://rubygems.org/gems/pagerduty)
5
+ [![Gem Downloads](https://img.shields.io/gem/dt/pagerduty.svg?maxAge=2592000)](https://rubygems.org/gems/pagerduty)
6
+ [![Build Status](https://github.com/envato/pagerduty/workflows/build/badge.svg?branch=main)](https://github.com/envato/pagerduty/actions?query=workflow%3Abuild+branch%3Amain)
3
7
 
4
- [![License MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/envato/pagerduty/blob/master/LICENSE.txt)
5
- [![Gem Version](https://badge.fury.io/rb/pagerduty.svg)](http://badge.fury.io/rb/pagerduty)
6
- [![Build Status](https://travis-ci.org/envato/pagerduty.svg?branch=master)](https://travis-ci.org/envato/pagerduty)
8
+ Provides a lightweight Ruby interface for calling the [PagerDuty Events
9
+ API][events-v2-docs].
7
10
 
8
- Provides a lightweight Ruby interface for calling the [PagerDuty
9
- Integration API](http://developer.pagerduty.com/documentation/integration/events).
11
+ [events-v2-docs]: https://developer.pagerduty.com/docs/ZG9jOjExMDI5NTgw-events-api-v2-overview
10
12
 
11
13
  ## Installation
12
14
 
@@ -26,58 +28,178 @@ Or install it yourself as:
26
28
 
27
29
  ## Usage
28
30
 
31
+ First, obtain an Events API integration key from PagerDuty. Follow the
32
+ [instructions][integration-key-documentation] in PagerDuty's documentation to
33
+ procure one.
34
+
35
+ [integration-key-documentation]: https://support.pagerduty.com/docs/services-and-integrations#create-a-generic-events-api-integration
36
+
37
+
38
+ ### Events API V2
39
+
29
40
  ```ruby
30
- # Don't forget to require the library
31
- require "pagerduty"
41
+ # Instantiate a Pagerduty service object providing an integration key and the
42
+ # desired API version: 2
43
+ pagerduty = Pagerduty.build(
44
+ integration_key: "<integration-key>",
45
+ api_version: 2
46
+ )
32
47
 
33
- # Instantiate a Pagerduty with your specific service key
34
- pagerduty = Pagerduty.new("<my-service-key>")
48
+ # Trigger an incident providing minimal details
49
+ incident = pagerduty.trigger(
50
+ summary: "summary",
51
+ source: "source",
52
+ severity: "critical"
53
+ )
35
54
 
36
- # Trigger an incident
37
- incident = pagerduty.trigger("incident description")
55
+ # Trigger an incident providing full context
56
+ incident = pagerduty.trigger(
57
+ summary: "Example alert on host1.example.com",
58
+ source: "monitoringtool:cloudvendor:central-region-dc-01:852559987:cluster/api-stats-prod-003",
59
+ severity: %w[critical error warning info].sample,
60
+ timestamp: Time.now,
61
+ component: "postgres",
62
+ group: "prod-datapipe",
63
+ class: "deploy",
64
+ custom_details: {
65
+ ping_time: "1500ms",
66
+ load_avg: 0.75
67
+ },
68
+ images: [
69
+ {
70
+ src: "https://www.pagerduty.com/wp-content/uploads/2016/05/pagerduty-logo-green.png",
71
+ href: "https://example.com/",
72
+ alt: "Example text",
73
+ },
74
+ ],
75
+ links: [
76
+ {
77
+ href: "https://example.com/",
78
+ text: "Link text",
79
+ },
80
+ ],
81
+ client: "Sample Monitoring Service",
82
+ client_url: "https://monitoring.example.com"
83
+ )
38
84
 
39
85
  # Acknowledge and/or resolve the incident
40
86
  incident.acknowledge
41
87
  incident.resolve
42
88
 
43
- # Acknowledge and/or resolve an existing incident
44
- incident = pagerduty.get_incident("<unique-incident-key>")
89
+ # Provide a client-defined incident key
90
+ # (this can be used to update existing incidents)
91
+ incident = pagerduty.incident("<incident-key>")
92
+ incident.trigger(
93
+ summary: "summary",
94
+ source: "source",
95
+ severity: "critical"
96
+ )
45
97
  incident.acknowledge
46
98
  incident.resolve
47
99
  ```
48
100
 
49
- There are a whole bunch of properties you can send to PagerDuty when triggering
50
- an incident. See the [PagerDuty
51
- documentation](http://developer.pagerduty.com/documentation/integration/events/trigger)
52
- for the specifics.
101
+ See the [PagerDuty Events API V2 documentation][events-v2-docs] for a
102
+ detailed description on the parameters you can send when triggering an
103
+ incident.
104
+
105
+ ### Events API V1
106
+
107
+ The following code snippet shows how to use the [Pagerduty Events API version
108
+ 1](https://v2.developer.pagerduty.com/docs/events-api).
53
109
 
54
110
  ```ruby
55
- pagerduty.trigger(
56
- "incident description",
57
- incident_key: "my unique incident identifier",
58
- client: "server in trouble",
59
- client_url: "http://server.in.trouble",
60
- details: { my: "extra details" }
111
+ # Instantiate a Pagerduty with a service integration key
112
+ pagerduty = Pagerduty.build(
113
+ integration_key: "<integration-key>",
114
+ api_version: 1,
115
+ )
116
+
117
+ # Trigger an incident
118
+ incident = pagerduty.trigger(
119
+ "FAILURE for production/HTTP on machine srv01.acme.com",
120
+ )
121
+
122
+ # Trigger an incident providing context and details
123
+ incident = pagerduty.trigger(
124
+ "FAILURE for production/HTTP on machine srv01.acme.com",
125
+ client: "Sample Monitoring Service",
126
+ client_url: "https://monitoring.service.com",
127
+ contexts: [
128
+ {
129
+ type: "link",
130
+ href: "http://acme.pagerduty.com",
131
+ text: "View the incident on PagerDuty",
132
+ },
133
+ {
134
+ type: "image",
135
+ src: "https://chart.googleapis.com/chart?chs=600x400&chd=t:6,2,9,5,2,5,7,4,8,2,1&cht=lc&chds=a&chxt=y&chm=D,0033FF,0,0,5,1",
136
+ }
137
+ ],
138
+ details: {
139
+ ping_time: "1500ms",
140
+ load_avg: 0.75,
141
+ },
142
+ )
143
+
144
+ # Acknowledge the incident
145
+ incident.acknowledge
146
+
147
+ # Acknowledge, providing a description and extra details
148
+ incident.acknowledge(
149
+ "Engineers are investigating the incident",
150
+ {
151
+ ping_time: "1700ms",
152
+ load_avg: 0.71,
153
+ }
61
154
  )
155
+
156
+ # Resolve the incident
157
+ incident.resolve
158
+
159
+ # Resolve, providing a description and extra details
160
+ incident.acknowledge(
161
+ "A fix has been deployed and the service has recovered",
162
+ {
163
+ ping_time: "120ms",
164
+ load_avg: 0.23,
165
+ }
166
+ )
167
+
168
+ # Provide a client defined incident key
169
+ # (this can be used to update existing incidents)
170
+ incident = pagerduty.incident("<incident-key>")
171
+ incident.trigger("Description of the event")
172
+ incident.acknowledge
173
+ incident.resolve
62
174
  ```
63
175
 
176
+ See the [PagerDuty Events API V1
177
+ documentation](https://v2.developer.pagerduty.com/docs/trigger-events) for a
178
+ detailed description of the parameters you can send when triggering an
179
+ incident.
180
+
64
181
  ### HTTP Proxy Support
65
182
 
66
183
  One can explicitly define an HTTP proxy like this:
67
184
 
68
185
  ```ruby
69
- # Instantiate a Pagerduty with your specific service key and proxy details
70
- pagerduty = Pagerduty.new(
71
- "<my-service-key>",
72
- proxy_host: "my.http.proxy.local",
73
- proxy_port: 3128,
74
- proxy_username: "<my-proxy-username>",
75
- proxy_password: "<my-proxy-password>",
186
+ pagerduty = Pagerduty.build(
187
+ integration_key: "<integration-key>",
188
+ api_version: 2, # The HTTP proxy settings work with either API version
189
+ http_proxy: {
190
+ host: "my.http.proxy.local",
191
+ port: 3128,
192
+ username: "<my-proxy-username>",
193
+ password: "<my-proxy-password>",
194
+ }
76
195
  )
77
196
 
78
- # Then proceed to trigger your incident
79
- # (sends the request to PagerDuty via the HTTP proxy)
80
- incident = pagerduty.trigger("incident description")
197
+ # Subsequent API calls will then be sent via the HTTP proxy
198
+ incident = pagerduty.trigger(
199
+ summary: "summary",
200
+ source: "source",
201
+ severity: "critical"
202
+ )
81
203
  ```
82
204
 
83
205
  ### Debugging Error Responses
@@ -87,38 +209,18 @@ go about debugging these unhappy cases:
87
209
 
88
210
  ```ruby
89
211
  begin
90
- pagerduty.trigger("incident description")
91
- rescue Net::HTTPServerException => error
212
+ pagerduty.trigger(
213
+ summary: "summary",
214
+ source: "source",
215
+ severity: "critical"
216
+ )
217
+ rescue Net::HTTPClientException => error
92
218
  error.response.code #=> "400"
93
219
  error.response.message #=> "Bad Request"
94
220
  error.response.body #=> "{\"status\":\"invalid event\",\"message\":\"Event object is invalid\",\"errors\":[\"Service key is the wrong length (should be 32 characters)\"]}"
95
221
  end
96
222
  ```
97
223
 
98
- ### Upgrading to Version 2.0.0
99
-
100
- The API has changed in three ways that you need to be aware of:
101
-
102
- 1. `Pagerduty` class initialiser no longer accepts an `incident_key`. This
103
- attribute can now be provided when calling the `#trigger` method (see above).
104
-
105
- 2. `Pagerduty#trigger` arguments have changed to accept all available options
106
- rather than just details.
107
-
108
- ```ruby
109
- # This no longer works post v2.0.0. If you're
110
- # providing details in this form, please migrate.
111
- pagerduty.trigger("desc", key: "value")
112
-
113
- # Post v2.0.0 this is how to send details (migrate to this please).
114
- pagerduty.trigger("desc", details: { key: "value" })
115
- ```
116
-
117
- 3. `PagerdutyException` now extends from `StandardError` rather than
118
- `Exception`. This may affect how you rescue the error. i.e. `rescue
119
- StandardError` will now rescue a `PagerdutyException` where it did not
120
- before.
121
-
122
224
  ## Contributing
123
225
 
124
226
  1. Fork it ( https://github.com/envato/pagerduty/fork )
@@ -0,0 +1,279 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pagerduty
4
+ # Trigger incidents via the PagerDuty Events API version 1.
5
+ #
6
+ # @see https://v2.developer.pagerduty.com/docs/events-api PagerDuty Events
7
+ # API V1 documentation
8
+ #
9
+ # @see Pagerduty.build
10
+ #
11
+ # @see Pagerduty::EventsApiV1::Incident
12
+ #
13
+ class EventsApiV1
14
+ # Rather than using this directly, use the {Pagerduty.build} method to
15
+ # construct an instance.
16
+ #
17
+ # @option config [String] integration_key Authentication key for connecting
18
+ # to PagerDuty. A UUID expressed as a 32-digit hexadecimal number.
19
+ # Integration keys are generated by creating a new service, or creating a
20
+ # new integration for an existing service in PagerDuty, and can be found
21
+ # on a service's Integrations tab. This option is required.
22
+ #
23
+ # @option config [String] http_proxy.host The DNS name or IP address of the
24
+ # proxy host. If nil or unprovided an HTTP proxy will not be used.
25
+ #
26
+ # @option config [String] http_proxy.port The TCP port to use to access the
27
+ # proxy.
28
+ #
29
+ # @option config [String] http_proxy.username username if authorization is
30
+ # required to use the proxy.
31
+ #
32
+ # @option config [String] http_proxy.password password if authorization is
33
+ # required to use the proxy.
34
+ #
35
+ # @see Pagerduty.build
36
+ #
37
+ def initialize(config)
38
+ @config = config
39
+ end
40
+
41
+ # Send PagerDuty a trigger event to report a new or ongoing problem.
42
+ #
43
+ # @example Trigger an incident
44
+ # incident = pagerduty.trigger(
45
+ # "<A description of the event or outage>"
46
+ # )
47
+ #
48
+ # @example Trigger an incident, providing more context and details
49
+ # incident = pagerduty.trigger(
50
+ # "FAILURE for production/HTTP on machine srv01.acme.com",
51
+ # client: "Sample Monitoring Service",
52
+ # client_url: "https://monitoring.service.com",
53
+ # contexts: [
54
+ # {
55
+ # type: "link",
56
+ # href: "http://acme.pagerduty.com",
57
+ # text: "View the incident on PagerDuty",
58
+ # },
59
+ # {
60
+ # type: "image",
61
+ # src: "https://chart.googleapis.com/chart.png",
62
+ # }
63
+ # ],
64
+ # details: {
65
+ # ping_time: "1500ms",
66
+ # load_avg: 0.75,
67
+ # },
68
+ # )
69
+ #
70
+ # @param [String] description A short description of the problem that led to
71
+ # this trigger. This field (or a truncated version) will be used when
72
+ # generating phone calls, SMS messages and alert emails. It will also
73
+ # appear on the incidents tables in the PagerDuty UI. The maximum length
74
+ # is 1024 characters.
75
+ #
76
+ # @option options [String] client The name of the monitoring client that is
77
+ # triggering this event.
78
+ #
79
+ # @option options [String] client_url The URL of the monitoring client that
80
+ # is triggering this event.
81
+ #
82
+ # @option options [Array] contexts An array of objects. Contexts to be
83
+ # included with the incident trigger such as links to graphs or images.
84
+ #
85
+ # @option options [Hash] details An arbitrary hash containing any data you'd
86
+ # like included in the incident log.
87
+ #
88
+ # @return [Pagerduty::EventsApiV1::Incident] The triggered incident.
89
+ #
90
+ # @raise [PagerdutyException] If PagerDuty responds with a status that is
91
+ # not "success"
92
+ #
93
+ def trigger(description, options = {})
94
+ config = @config.merge(incident_key: options[:incident_key])
95
+ options = options.reject { |key| key == :incident_key }
96
+ Incident.new(config).trigger(description, options)
97
+ end
98
+
99
+ # @param [String] incident_key Identifies the incident to which
100
+ # this trigger event should be applied. If there's no open (i.e.
101
+ # unresolved) incident with this key, a new one will be created. If
102
+ # there's already an open incident with a matching key, this event will be
103
+ # appended to that incident's log. The event key provides an easy way to
104
+ # "de-dup" problem reports. If this field isn't provided, PagerDuty will
105
+ # automatically open a new incident with a unique key. The maximum length
106
+ # is 255 characters.
107
+ #
108
+ # @return [Pagerduty::EventsApiV1::Incident] The incident referenced by the
109
+ # key.
110
+ #
111
+ # @raise [ArgumentError] If incident_key is nil
112
+ #
113
+ def incident(incident_key)
114
+ raise ArgumentError, "incident_key is nil" if incident_key.nil?
115
+
116
+ Incident.new(@config.merge(incident_key: incident_key))
117
+ end
118
+
119
+ class Incident
120
+ attr_reader :incident_key
121
+
122
+ # @option (see Pagerduty::EventsApiV1#initialize)
123
+ #
124
+ # @option config [String] incident_key Identifies the incident to which
125
+ # this trigger event should be applied. If there's no open
126
+ # (i.e. unresolved) incident with this key, a new one will be created.
127
+ # If there's already an open incident with a matching key, this event
128
+ # will be appended to that incident's log. The event key provides an
129
+ # easy way to "de-dup" problem reports. If this field isn't provided,
130
+ # PagerDuty will automatically open a new incident with a unique key.
131
+ # The maximum length is 255 characters.
132
+ #
133
+ def initialize(config)
134
+ @integration_key = config.fetch(:integration_key) do
135
+ raise ArgumentError "integration_key not provided"
136
+ end
137
+ @incident_key = config[:incident_key]
138
+ @transport = Pagerduty::HttpTransport.new(
139
+ path: "/generic/2010-04-15/create_event.json",
140
+ proxy: config[:http_proxy],
141
+ )
142
+ end
143
+
144
+ # Send PagerDuty a trigger event to report a new or ongoing problem. When
145
+ # PagerDuty receives a trigger event, it will either open a new incident,
146
+ # or add a new trigger log entry to an existing incident, depending on the
147
+ # provided incident_key.
148
+ #
149
+ # @example Trigger or update an incident
150
+ # incident.trigger(
151
+ # "<A description of the event or outage>"
152
+ # )
153
+ #
154
+ # @example Trigger or update an incident, providing more context
155
+ # incident.trigger(
156
+ # "FAILURE for production/HTTP on machine srv01.acme.com",
157
+ # client: "Sample Monitoring Service",
158
+ # client_url: "https://monitoring.service.com",
159
+ # contexts: [
160
+ # {
161
+ # type: "link",
162
+ # href: "http://acme.pagerduty.com",
163
+ # text: "View the incident on PagerDuty",
164
+ # },
165
+ # {
166
+ # type: "image",
167
+ # src: "https://chart.googleapis.com/chart.png",
168
+ # }
169
+ # ],
170
+ # details: {
171
+ # ping_time: "1500ms",
172
+ # load_avg: 0.75,
173
+ # },
174
+ # )
175
+ #
176
+ # @param (see Pagerduty::EventsApiV1#trigger)
177
+ # @option (see Pagerduty::EventsApiV1#trigger)
178
+ def trigger(description, options = {})
179
+ if options.key?(:incident_key)
180
+ raise ArgumentError, "incident_key provided"
181
+ end
182
+
183
+ options = options.merge(description: description)
184
+ options[:incident_key] = @incident_key unless @incident_key.nil?
185
+ response = api_call("trigger", options)
186
+ @incident_key = response["incident_key"]
187
+ self
188
+ end
189
+
190
+ # Acknowledge the referenced incident. While an incident is acknowledged,
191
+ # it won't generate any additional notifications, even if it receives new
192
+ # trigger events. Send PagerDuty an acknowledge event when you know
193
+ # someone is presently working on the problem.
194
+ #
195
+ # @example Acknowledge the incident
196
+ # incident.acknowledge
197
+ #
198
+ # @example Acknowledge, providing a description and extra details
199
+ # incident.acknowledge(
200
+ # "Engineers are investigating the incident",
201
+ # {
202
+ # ping_time: "1700ms",
203
+ # load_avg: 0.71,
204
+ # }
205
+ # )
206
+ #
207
+ # @param [String] description Text that will appear in the incident's log
208
+ # associated with this event.
209
+ #
210
+ # @param [Hash] details An arbitrary hash containing any data you'd like
211
+ # included in the incident log.
212
+ #
213
+ # @return [Pagerduty::EventsApiV1::Incident] self
214
+ #
215
+ # @raise [PagerdutyException] If PagerDuty responds with a status that is
216
+ # not "success"
217
+ #
218
+ def acknowledge(description = nil, details = nil)
219
+ modify_incident("acknowledge", description, details)
220
+ end
221
+
222
+ # Resolve the referenced incident. Once an incident is resolved, it won't
223
+ # generate any additional notifications. New trigger events with the same
224
+ # incident_key as a resolved incident won't re-open the incident. Instead,
225
+ # a new incident will be created. Send PagerDuty a resolve event when the
226
+ # problem that caused the initial trigger event has been fixed.
227
+ #
228
+ # @example Resolve the incident
229
+ # incident.resolve
230
+ #
231
+ # @example Resolve, providing a description and extra details
232
+ # incident.resolve(
233
+ # "A fix has been deployed and the service has recovered",
234
+ # {
235
+ # ping_time: "130ms",
236
+ # load_avg: 0.23,
237
+ # }
238
+ # )
239
+ #
240
+ # @param [String] description Text that will appear in the incident's log
241
+ # associated with this event.
242
+ #
243
+ # @param [Hash] details An arbitrary hash containing any data you'd like
244
+ # included in the incident log.
245
+ #
246
+ # @return [Pagerduty::EventsApiV1::Incident] self
247
+ #
248
+ # @raise [PagerdutyException] If PagerDuty responds with a status that is
249
+ # not "success"
250
+ #
251
+ def resolve(description = nil, details = nil)
252
+ modify_incident("resolve", description, details)
253
+ end
254
+
255
+ private
256
+
257
+ def modify_incident(event_type, description, details)
258
+ options = { incident_key: incident_key }
259
+ options[:description] = description if description
260
+ options[:details] = details if details
261
+ api_call(event_type, options)
262
+ self
263
+ end
264
+
265
+ def api_call(event_type, args)
266
+ args = args.merge(
267
+ service_key: @integration_key,
268
+ event_type: event_type,
269
+ )
270
+ response = @transport.send_payload(args)
271
+ unless response["status"] == "success"
272
+ raise PagerdutyException.new(self, response, response["message"])
273
+ end
274
+
275
+ response
276
+ end
277
+ end
278
+ end
279
+ end