pager_judy 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []