pagerduty 2.1.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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