pager_judy 0.2.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.
@@ -0,0 +1,321 @@
1
+ require "clamp"
2
+ require "console_logger"
3
+ require "pager_judy/api/client"
4
+ require "pager_judy/cli/collection_behaviour"
5
+ require "pager_judy/cli/item_behaviour"
6
+ require "pager_judy/cli/time_filtering"
7
+ require "pager_judy/sync"
8
+ require "pager_judy/version"
9
+
10
+ module PagerJudy
11
+ module CLI
12
+
13
+ class MainCommand < Clamp::Command
14
+
15
+ option "--debug", :flag, "enable debugging"
16
+ option "--dry-run", :flag, "enable dry-run mode"
17
+
18
+ option "--version", :flag, "display version" do
19
+ puts PagerJudy::VERSION
20
+ exit 0
21
+ end
22
+
23
+ option "--api-key", "KEY", "PagerDuty API key",
24
+ environment_variable: "PAGER_DUTY_API_KEY"
25
+
26
+ subcommand ["escalation-policy", "ep"], "Display escalation policy" do
27
+
28
+ parameter "ID", "escalation_policy ID"
29
+
30
+ include ItemBehaviour
31
+
32
+ def item
33
+ client.escalation_policies[id]
34
+ end
35
+
36
+ end
37
+
38
+ subcommand ["escalation_policies", "eps"], "Display escalation policies" do
39
+
40
+ option %w[-q --query], "FILTER", "name filter"
41
+
42
+ include CollectionBehaviour
43
+
44
+ def collection
45
+ client.escalation_policies.with(query: query)
46
+ end
47
+
48
+ end
49
+
50
+ subcommand ["extension", "ext"], "Display extension" do
51
+
52
+ parameter "ID", "extension ID"
53
+
54
+ include ItemBehaviour
55
+
56
+ def item
57
+ client.extensions[id]
58
+ end
59
+
60
+ end
61
+
62
+ subcommand ["extensions", "exts"], "Display extensions" do
63
+
64
+ option %w[-q --query], "FILTER", "name filter"
65
+
66
+ include CollectionBehaviour
67
+
68
+ def collection
69
+ client.extensions.with(query: query)
70
+ end
71
+
72
+ end
73
+
74
+ subcommand ["incidents"], "Display incidents" do
75
+
76
+ option %w[-s --status], "STATUS", "status", :multivalued => true
77
+
78
+ include CollectionBehaviour
79
+ include TimeFiltering
80
+
81
+ def collection
82
+ client.incidents.with(filters)
83
+ end
84
+
85
+ def filters
86
+ time_filters.merge("statuses[]" => status_list).select { |_,v| v }
87
+ end
88
+
89
+ end
90
+
91
+ subcommand ["notifications"], "Display notifications" do
92
+
93
+ include TimeFiltering
94
+
95
+ self.default_subcommand = "data"
96
+
97
+ subcommand ["data", "d"], "Full details" do
98
+ include CollectionBehaviour::DataSubcommand
99
+ end
100
+
101
+ def collection
102
+ client.notifications.with(time_filters)
103
+ end
104
+
105
+ end
106
+
107
+ subcommand "schedule", "Display schedule" do
108
+
109
+ parameter "ID", "schedule ID"
110
+
111
+ include ItemBehaviour
112
+
113
+ def item
114
+ client.schedules[id]
115
+ end
116
+
117
+ end
118
+
119
+ subcommand "schedules", "Display schedules" do
120
+
121
+ option %w[-q --query], "FILTER", "name filter"
122
+
123
+ include CollectionBehaviour
124
+
125
+ def collection
126
+ client.schedules.with(query: query)
127
+ end
128
+
129
+ end
130
+
131
+ subcommand "service", "Display service" do
132
+
133
+ option %w[--include], "TYPE", "linked objects to include", :multivalued => true
134
+
135
+ parameter "ID", "service ID"
136
+
137
+ include ItemBehaviour
138
+
139
+ def item
140
+ client.services[id].with(
141
+ "include[]" => include_list
142
+ )
143
+ end
144
+
145
+ end
146
+
147
+ subcommand "services", "Display services" do
148
+
149
+ option %w[-q --query], "FILTER", "name filter"
150
+ option %w[--team], "ID", "team ID", :multivalued => true
151
+ option %w[--include], "TYPE", "linked objects to include", :multivalued => true
152
+
153
+ include CollectionBehaviour
154
+
155
+ def collection
156
+ client.services.with(
157
+ "query" => query,
158
+ "team_ids[]" => team_list,
159
+ "include[]" => include_list
160
+ )
161
+ end
162
+
163
+ end
164
+
165
+ subcommand "team", "Display team" do
166
+
167
+ parameter "ID", "team ID"
168
+
169
+ include ItemBehaviour
170
+
171
+ def item
172
+ client.teams[id]
173
+ end
174
+
175
+ end
176
+
177
+ subcommand "teams", "Display teams" do
178
+
179
+ option %w[-q --query], "FILTER", "name filter"
180
+
181
+ include CollectionBehaviour
182
+
183
+ def collection
184
+ client.teams.with(query: query)
185
+ end
186
+
187
+ end
188
+
189
+ subcommand "user", "Display user" do
190
+
191
+ parameter "ID", "user ID"
192
+
193
+ include ItemBehaviour
194
+
195
+ def item
196
+ client.users[id]
197
+ end
198
+
199
+ end
200
+
201
+ subcommand "users", "User operations" do
202
+
203
+ include CollectionBehaviour
204
+
205
+ def collection
206
+ client.users
207
+ end
208
+
209
+ end
210
+
211
+ subcommand "vendor", "Specific vendor" do
212
+
213
+ parameter "ID", "vendor ID"
214
+
215
+ include ItemBehaviour
216
+
217
+ def item
218
+ client.vendors[id]
219
+ end
220
+
221
+ end
222
+
223
+ subcommand "vendors", "Vendor list" do
224
+
225
+ include CollectionBehaviour
226
+
227
+ def collection
228
+ client.vendors
229
+ end
230
+
231
+ end
232
+
233
+ subcommand "viz", "Generate Graphviz Dot diagram" do
234
+
235
+ def execute
236
+ services = client.services
237
+ escalation_policies = client.escalation_policies
238
+ puts %(digraph pagerduty {)
239
+ puts %(rankdir=LR;)
240
+ integrations = []
241
+ services.each do |service|
242
+ service.fetch('integrations').each do |integration|
243
+ integrations << integration
244
+ puts %(#{integration.fetch('id')} -> #{service.fetch('id')};)
245
+ puts %(#{integration.fetch('id')} [label="#{integration.fetch('summary')}",shape=box];)
246
+ end
247
+ end
248
+ puts same_rank(integrations)
249
+ services.each do |service|
250
+ puts %(#{service.fetch('id')} [label="#{service.fetch('name')}",shape=box,style=filled,color=lightgrey];)
251
+ ep = service['escalation_policy']
252
+ if ep
253
+ puts %(#{service.fetch('id')} -> #{ep.fetch('id')};)
254
+ end
255
+ end
256
+ puts same_rank(services)
257
+ escalation_targets = {}
258
+ escalation_policies.each do |ep|
259
+ puts %{#{ep.fetch('id')} [label="#{ep.fetch('name')}",shape=box,style=filled,color=lightgrey];}
260
+ ep.fetch('escalation_rules').each do |rule|
261
+ rule.fetch('targets').each do |target|
262
+ puts %(#{ep.fetch('id')} -> #{target.fetch('id')};)
263
+ escalation_targets[target.fetch('id')] = target.fetch('summary')
264
+ end
265
+ end
266
+ end
267
+ puts same_rank(escalation_policies)
268
+ escalation_targets.each do |id, summary|
269
+ puts %(#{id} [label="#{summary}",shape=box];)
270
+ end
271
+ puts %(})
272
+ end
273
+
274
+ private
275
+
276
+ def same_rank(things)
277
+ ids = things.map { |thing| thing.fetch('id') }
278
+ "{" + ["rank=same", *ids].join(';') + "}"
279
+ end
280
+
281
+ end
282
+
283
+ subcommand "configure", "Apply config" do
284
+
285
+ option "--check", :flag, "just validate the config"
286
+
287
+ parameter "SOURCE", "config file"
288
+
289
+ def execute
290
+ config = PagerJudy::Sync::Config.from(source)
291
+ return if check?
292
+ PagerJudy::Sync.sync(client: client, config: config)
293
+ end
294
+
295
+ end
296
+
297
+ def run(*args)
298
+ super(*args)
299
+ rescue PagerJudy::API::HttpError => e
300
+ $stderr.puts e.response.body
301
+ signal_error e.message
302
+ rescue ConfigMapper::MappingError => e
303
+ signal_error e.message
304
+ end
305
+
306
+ private
307
+
308
+ def client
309
+ signal_error "no --api-key provided" unless api_key
310
+ HTTPI.log = false
311
+ @client ||= PagerJudy::API::Client.new(api_key, logger: logger, dry_run: dry_run?)
312
+ end
313
+
314
+ def logger
315
+ @logger ||= ConsoleLogger.new(STDERR, debug?)
316
+ end
317
+
318
+ end
319
+
320
+ end
321
+ end
@@ -0,0 +1,46 @@
1
+ require "clamp"
2
+ require "time"
3
+
4
+ module PagerJudy
5
+ module CLI
6
+
7
+ module TimeFiltering
8
+
9
+ DAY = 24 * 60 * 60
10
+
11
+ extend Clamp::Option::Declaration
12
+
13
+ option %w[-a --after], "DATETIME", "start date/time", default: "24 hours ago", attribute_name: :after
14
+ option %w[-b --before], "DATETIME", "end date/time", attribute_name: :before
15
+
16
+ protected
17
+
18
+ def time_filters
19
+ {
20
+ since: after,
21
+ until: before
22
+ }.select { |_,v| v }
23
+ end
24
+
25
+ private
26
+
27
+ def after=(s)
28
+ @after = Time.parse(s)
29
+ end
30
+
31
+ def before=(s)
32
+ @before = Time.parse(s)
33
+ end
34
+
35
+ def default_after
36
+ Time.now - DAY
37
+ end
38
+
39
+ def default_before
40
+ Time.now
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1 @@
1
+ require "pager_judy/cli/main_command"
@@ -0,0 +1,69 @@
1
+ require "config_hound"
2
+ require "config_mapper"
3
+ require "config_mapper/config_struct"
4
+
5
+ module PagerJudy
6
+ module Sync
7
+
8
+ # load and validate our configuration
9
+ class Config < ConfigMapper::ConfigStruct
10
+
11
+ class << self
12
+
13
+ # Load configuration.
14
+ #
15
+ # Multiple "sources" can be specified. First one wins.
16
+ # Sources may be a file-name, a URI, or a raw Ruby Hash.
17
+ #
18
+ def from(*config_sources)
19
+ config_data = data_from(*config_sources)
20
+ config_data.delete("var")
21
+ from_data(config_data)
22
+ end
23
+
24
+ # Load configuration data.
25
+ #
26
+ # Includes are processed; defaults are included, references are expanded.
27
+ #
28
+ def data_from(*sources)
29
+ ConfigHound.load(sources, expand_refs: true, include_key: "include")
30
+ end
31
+
32
+ end
33
+
34
+ component_dict :escalation_policies do
35
+
36
+ attribute :description
37
+
38
+ end
39
+
40
+ component_dict :services do
41
+
42
+ attribute :description
43
+
44
+ attribute :acknowledgement_timeout, default: 1800
45
+
46
+ attribute :auto_resolve_timeout, default: 14400
47
+
48
+ component :escalation_policy do
49
+
50
+ attribute :id
51
+
52
+ def to_h
53
+ super.merge("type" => "escalation_policy_reference")
54
+ end
55
+
56
+ end
57
+
58
+ component_dict :integrations do
59
+
60
+ attribute :type
61
+
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,23 @@
1
+ module PagerJudy
2
+ module Sync
3
+
4
+ class Syncer
5
+
6
+ def initialize(client:, config:)
7
+ @client = client
8
+ @config = config
9
+ end
10
+
11
+ attr_reader :client
12
+ attr_reader :config
13
+
14
+ def sync
15
+ config.services.each do |name, detail|
16
+ client.services.create_or_update_by_name(name, detail.to_h)
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ require "pager_judy/sync/config"
2
+ require "pager_judy/sync/syncer"
3
+
4
+ module PagerJudy
5
+ module Sync
6
+
7
+ def self.sync(*args)
8
+ Syncer.new(*args).sync
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ module PagerJudy
2
+
3
+ VERSION = '0.2.0'
4
+
5
+ end
@@ -0,0 +1,26 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "pager_judy/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pager_judy"
8
+ spec.version = PagerJudy::VERSION
9
+ spec.authors = ["Mike Williams"]
10
+ spec.email = ["mdub@dogbiscuit.org"]
11
+
12
+ spec.summary = %q{a Ruby client and CLI for PagerDuty}
13
+ spec.homepage = "https://github.com/mdub/pager_judy"
14
+ spec.license = "MIT"
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.16"
26
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pager_judy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Mike Williams
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-12-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ description:
28
+ email:
29
+ - mdub@dogbiscuit.org
30
+ executables:
31
+ - pagerjudy
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".buildkite/pipeline.yml"
36
+ - ".buildkite/upload-pipeline"
37
+ - ".dockerignore"
38
+ - ".gitignore"
39
+ - ".rspec"
40
+ - ".rubocop.yml"
41
+ - CHANGES.md
42
+ - Dockerfile
43
+ - Gemfile
44
+ - Gemfile.lock
45
+ - README.md
46
+ - Rakefile
47
+ - auto/build
48
+ - auto/dev-environment
49
+ - auto/test
50
+ - docker-compose.yml
51
+ - exe/pagerjudy
52
+ - lib/pager_judy/api/client.rb
53
+ - lib/pager_judy/api/collection.rb
54
+ - lib/pager_judy/api/errors.rb
55
+ - lib/pager_judy/api/fake_api_app.rb
56
+ - lib/pager_judy/api/item.rb
57
+ - lib/pager_judy/api/resource.rb
58
+ - lib/pager_judy/cli.rb
59
+ - lib/pager_judy/cli/collection_behaviour.rb
60
+ - lib/pager_judy/cli/data_display.rb
61
+ - lib/pager_judy/cli/item_behaviour.rb
62
+ - lib/pager_judy/cli/main_command.rb
63
+ - lib/pager_judy/cli/time_filtering.rb
64
+ - lib/pager_judy/sync.rb
65
+ - lib/pager_judy/sync/config.rb
66
+ - lib/pager_judy/sync/syncer.rb
67
+ - lib/pager_judy/version.rb
68
+ - pager_judy.gemspec
69
+ homepage: https://github.com/mdub/pager_judy
70
+ licenses:
71
+ - MIT
72
+ metadata: {}
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 2.7.6
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: a Ruby client and CLI for PagerDuty
93
+ test_files: []