kennel 1.50.1 → 1.54.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc88113c58755de472140ee4c734b028f04d7222c4433192a71d8ba441f38d49
4
- data.tar.gz: 547412eb62bae5a5dc7d601a74ed7f1fd29c1e12078fe32abfb5bcf1e0a64e26
3
+ metadata.gz: ff469d7ae1bcb0d5dc6fb5d867c9dad761b5f2d07159ea147d99620544ec2580
4
+ data.tar.gz: e3322d92c13faee31f51ef53042cc0c768a0c85cfa0644d4fb2dafcf67f3e03a
5
5
  SHA512:
6
- metadata.gz: 0020656d117c83d078a1ae585ac124fea8fa2970872dca59f1c94922dba7ec5c4d4543d425fe65b91ec53a401193a9cb9baa24d949e4dca667d59381a254ad48
7
- data.tar.gz: 0312710345b73441b95283c37572033c79c43c5db194e69de1caed44403e20d345e5c541355f206f425acdd5f061581a04520614e29527f88f6f64f5a8942955
6
+ metadata.gz: 18d054b93d83f78b98dabdd584fcec0a99759f32734c2808c32bb64d51042e3087ca36722c88aa61c24707999f7dbefc1aebdd16ce996c964ba3ca2595bec7df
7
+ data.tar.gz: d1876da783f71a72287ede64c20c9865622f409ec0857a483161cb1f2914c0e40c3a232165697bee8984ea515d34abfc0beb8eede8b42164d0b324c7805e1468
data/Readme.md CHANGED
@@ -42,19 +42,23 @@ Keep datadog monitors/dashboards/etc in version control, avoid chaotic managemen
42
42
  - `gem install bundler && bundle install`
43
43
  - `cp .env.example .env`
44
44
  - open [Datadog API Settings](https://app.datadoghq.com/account/settings#api)
45
- - find or create your personal "Application Key" and add it to `.env` as `DATADOG_APP_KEY=` (will be on the last page if new)
46
45
  - copy any `API Key` and add it to `.env` as `DATADOG_API_KEY`
46
+ - find or create your personal "Application Key" and add it to `.env` as `DATADOG_APP_KEY=` (will be on the last page if new)
47
+ - change the `DATADOG_SUBDOMAIN=app` in `.env` to your companies subdomain if you have one
47
48
  -->
48
49
 
49
50
  ### Adding a team
50
51
 
52
+ - `mention` is used for all team monitors via `super()`
53
+ - `renotify_interval` is used for all team monitors (defaults to `0` / off)
54
+ - `tags` is used for all team monitors/dashboards (defaults to `team:<team-name>`)
55
+
51
56
  ```Ruby
52
57
  # teams/my_team.rb
53
58
  module Teams
54
59
  class MyTeam < Kennel::Models::Team
55
60
  defaults(
56
- slack: -> { "my-alerts" },
57
- email: -> { "my-team@example.com" }
61
+ mention: -> { "@slack-my-team" }
58
62
  )
59
63
  end
60
64
  end
@@ -62,13 +66,14 @@ end
62
66
 
63
67
  ### Adding a new monitor
64
68
  - use [datadog monitor UI](https://app.datadoghq.com/monitors#create/metric) to create a monitor
65
- - get the `id` from the url
66
- - `RESOURCE=monitor ID=12345 bundle exec rake kennel:import`
67
69
  - see below
68
70
 
69
71
  ### Updating an existing monitor
72
+ - use [datadog monitor UI](https://app.datadoghq.com/monitors#create/metric) to find a monitor
73
+ - get the `id` from the url
74
+ - run `RESOURCE=monitor ID=12345 bundle exec rake kennel:import` and copy the output
70
75
  - find or create a project in `projects/`
71
- - add a monitor to `parts: [` list
76
+ - add the monitor to `parts: [` list, for example:
72
77
  ```Ruby
73
78
  # projects/my_project.rb
74
79
  class MyProject < Kennel::Models::Project
@@ -83,7 +88,8 @@ end
83
88
  kennel_id: -> { "load-too-high" }, # make up a unique name
84
89
  name: -> { "Foobar Load too high" }, # nice descriptive name that will show up in alerts and emails
85
90
  message: -> {
86
- # Explain what behavior to expect and how to fix the cause. Use #{super()} to add team notifications.
91
+ # Explain what behavior to expect and how to fix the cause
92
+ # Use #{super()} to add team notifications.
87
93
  <<~TEXT
88
94
  Foobar will be slow and that could cause Barfoo to go down.
89
95
  Add capacity or debug why it is suddenly slow.
@@ -98,21 +104,22 @@ end
98
104
  )
99
105
  end
100
106
  ```
101
- - `bundle exec rake plan` update to existing should be shown (not Create / Delete)
102
- - alternatively: `bundle exec rake generate` to only update the generated `json` files
107
+ - run `PROJECT=my_project bundle exec rake plan`, an Update to the existing monitor should be shown (not Create / Delete)
108
+ - alternatively: `bundle exec rake generate` to only locally update the generated `json` files
103
109
  - review changes then `git commit`
104
110
  - make a PR ... get reviewed ... merge
105
111
  - datadog is updated by travis
106
112
 
107
113
  ### Adding a new dashboard
108
114
  - go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to create a dashboard
109
- - get the `id` from the url
110
- - `RESOURCE=dashboard ID=abc-def-ghi bundle exec rake kennel:import`
111
115
  - see below
112
116
 
113
117
  ### Updating an existing dashboard
118
+ - go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to find a dashboard
119
+ - get the `id` from the url
120
+ - run `RESOURCE=dashboard ID=abc-def-ghi bundle exec rake kennel:import` and copy the output
114
121
  - find or create a project in `projects/`
115
- - add a dashboard to `parts: [` list
122
+ - add a dashboard to `parts: [` list, for example:
116
123
  ```Ruby
117
124
  class MyProject < Kennel::Models::Project
118
125
  defaults(
@@ -160,6 +167,7 @@ To link to existing monitors via their kennel_id
160
167
 
161
168
  - Screens `uptime` widgets can use `monitor: {id: "foo:bar"}`
162
169
  - Screens `alert_graph` widgets can use `alert_id: "foo:bar"`
170
+ - Monitors `composite` can use `query: -> { "%{foo:bar} || %{foo:baz}" }`
163
171
 
164
172
  ### Debugging changes locally
165
173
 
@@ -195,7 +203,7 @@ Reuse it in multiple projects.
195
203
  ```Ruby
196
204
  class Database < Kennel::Models::Project
197
205
  defaults(
198
- team: -> { Kennel::Models::Team.new(slack: -> { 'foo' }, kennel_id: -> { 'foo' }) },
206
+ team: -> { Kennel::Models::Team.new(mention: -> { '@slack-foo' }, kennel_id: -> { 'foo' }) },
199
207
  parts: -> { [Monitors::LoadTooHigh.new(self, critical: -> { 13 })] }
200
208
  )
201
209
  end
@@ -53,6 +53,9 @@ module Kennel
53
53
  if query && critical
54
54
  query.sub!(/([><=]) (#{Regexp.escape(critical.to_f.to_s)}|#{Regexp.escape(critical.to_i.to_s)})$/, "\\1 \#{critical}")
55
55
  end
56
+ elsif resource == "dashboard"
57
+ widgets = data[:widgets]&.flat_map { |widget| widget.dig(:definition, :widgets) || [widget] }
58
+ widgets&.each { |widget| dry_up_query!(widget) }
56
59
  end
57
60
 
58
61
  # simplify template_variables to array of string when possible
@@ -71,6 +74,20 @@ module Kennel
71
74
 
72
75
  private
73
76
 
77
+ # reduce duplication in imports by using dry `q: :metadata` when possible
78
+ def dry_up_query!(widget)
79
+ (widget.dig(:definition, :requests) || []).each do |request|
80
+ next unless request.is_a?(Hash)
81
+ next unless metadata = request[:metadata]
82
+ next unless query = request[:q]&.dup
83
+ metadata.each do |m|
84
+ next unless exp = m[:expression]
85
+ query.sub!(exp, "")
86
+ end
87
+ request[:q] = :metadata if query.delete(", ") == ""
88
+ end
89
+ end
90
+
74
91
  def pretty_print(hash)
75
92
  sort_widgets hash
76
93
 
@@ -84,6 +101,7 @@ module Kennel
84
101
  .gsub(/(^\s*)"([a-zA-Z][a-zA-Z\d_]*)":/, "\\1\\2:") # "foo": 1 -> foo: 1
85
102
  .gsub(/: \[\n\s+\]/, ": []") # empty arrays on a single line
86
103
  .gsub(/^/, " ") # indent
104
+ .gsub('q: "metadata"', "q: :metadata") # bring symbols back
87
105
 
88
106
  "\n#{pretty}\n "
89
107
  elsif k == :message
@@ -148,6 +148,13 @@ module Kennel
148
148
 
149
149
  private
150
150
 
151
+ def resolve_link(id, id_map, force:)
152
+ id_map[id] || begin
153
+ message = "Unable to find #{id} in existing monitors (they need to be created first to link them)"
154
+ force ? invalid!(message) : Kennel.err.puts(message)
155
+ end
156
+ end
157
+
151
158
  # let users know which project/resource failed when something happens during diffing where the backtrace is hidden
152
159
  def invalid!(message)
153
160
  raise ValidationError, "#{tracking_id} #{message}"
@@ -101,11 +101,13 @@ module Kennel
101
101
  case definition[:type]
102
102
  when "uptime"
103
103
  if ids = definition[:monitor_ids]
104
- definition[:monitor_ids] = ids.map { |id| resolve_link(id, id_map) }
104
+ definition[:monitor_ids] = ids.map do |id|
105
+ tracking_id?(id) ? resolve_link(id, id_map, force: false) : id
106
+ end
105
107
  end
106
108
  when "alert_graph"
107
- if id = definition[:alert_id]
108
- definition[:alert_id] = resolve_link(id, id_map).to_s
109
+ if (id = definition[:alert_id]) && tracking_id?(id)
110
+ definition[:alert_id] = resolve_link(id, id_map, force: false).to_s
109
111
  end
110
112
  end
111
113
  end
@@ -113,6 +115,10 @@ module Kennel
113
115
 
114
116
  private
115
117
 
118
+ def tracking_id?(id)
119
+ id.is_a?(String) && !id.match?(/\A\d+\z/)
120
+ end
121
+
116
122
  # creates queries from metadata to avoid having to keep q and expression in sync
117
123
  #
118
124
  # {q: :metadata, metadata: [{expression: "sum:bar", alias_name: "foo"}, ...], }
@@ -127,16 +133,6 @@ module Kennel
127
133
  end
128
134
  end
129
135
 
130
- def resolve_link(id, id_map)
131
- return id unless tracking_id?(id)
132
- id_map[id] ||
133
- Kennel.err.puts("Unable to find #{id} in existing monitors (they need to be created first to link them)")
134
- end
135
-
136
- def tracking_id?(id)
137
- id.is_a?(String) && !id.match?(/\A\d+\z/)
138
- end
139
-
140
136
  def validate_json(data)
141
137
  super
142
138
 
@@ -15,7 +15,7 @@ module Kennel
15
15
  evaluation_delay: nil,
16
16
  new_host_delay: 300,
17
17
  timeout_h: 0,
18
- renotify_interval: 120,
18
+ renotify_interval: 0,
19
19
  no_data_timeframe: nil # this works out ok since if notify_no_data is on, it would never be nil
20
20
  }.freeze
21
21
  DEFAULT_ESCALATION_MESSAGE = ["", nil].freeze
@@ -27,9 +27,9 @@ module Kennel
27
27
  )
28
28
 
29
29
  defaults(
30
- message: -> { "\n\n@slack-#{project.slack}" },
30
+ message: -> { "\n\n#{project.mention}" },
31
31
  escalation_message: -> { DEFAULT_ESCALATION_MESSAGE.first },
32
- renotify_interval: -> { MONITOR_OPTION_DEFAULTS.fetch(:renotify_interval) },
32
+ renotify_interval: -> { project.team.renotify_interval },
33
33
  warning: -> { nil },
34
34
  ok: -> { nil },
35
35
  id: -> { nil },
@@ -108,6 +108,16 @@ module Kennel
108
108
  @as_json = data
109
109
  end
110
110
 
111
+ # resolve composite monitors ... only works when referenced monitors already exist
112
+ # since leaving names or bad ids in the query breaks the monitor update
113
+ def resolve_linked_tracking_ids(id_map)
114
+ if as_json[:type] == "composite"
115
+ as_json[:query] = as_json[:query].gsub(/%\{(.*?)\}/) do
116
+ resolve_link($1, id_map, force: true)
117
+ end
118
+ end
119
+ end
120
+
111
121
  def self.api_resource
112
122
  "monitor"
113
123
  end
@@ -2,10 +2,10 @@
2
2
  module Kennel
3
3
  module Models
4
4
  class Project < Base
5
- settings :team, :parts, :tags, :slack
5
+ settings :team, :parts, :tags, :mention
6
6
  defaults(
7
7
  tags: -> { ["service:#{kennel_id}"] + team.tags },
8
- slack: -> { team.slack }
8
+ mention: -> { team.mention }
9
9
  )
10
10
 
11
11
  def self.file_location
@@ -2,19 +2,11 @@
2
2
  module Kennel
3
3
  module Models
4
4
  class Team < Base
5
- settings :slack, :email, :tags, :kennel_id
5
+ settings :mention, :tags, :renotify_interval, :kennel_id
6
6
  defaults(
7
- tags: -> { ["team:#{kennel_id.sub(/^teams_/, "")}"] }
7
+ tags: -> { ["team:#{kennel_id.sub(/^teams_/, "")}"] },
8
+ renotify_interval: -> { 0 }
8
9
  )
9
-
10
- def initialize(*)
11
- super
12
- invalid! "remove leading # from slack" if slack.to_s.start_with?("#")
13
- end
14
-
15
- def tracking_id
16
- kennel_id
17
- end
18
10
  end
19
11
  end
20
12
  end
@@ -72,11 +72,22 @@ module Kennel
72
72
  filter_by_project! actual
73
73
 
74
74
  details_cache do |cache|
75
- actual.each do |a|
76
- id = a.fetch(:id)
75
+ items = actual.map do |a|
77
76
  e = matching_expected(a)
78
77
  if e && @expected.delete(e)
79
- fill_details(a, cache) if e.class::API_LIST_INCOMPLETE
78
+ [e, a]
79
+ else
80
+ [nil, a]
81
+ end
82
+ end
83
+
84
+ # fill details of things we need to compare (only do this part in parallel for safety & balancing)
85
+ Utils.parallel(items.select { |e, _| e && e.class::API_LIST_INCOMPLETE }) { |_, a| fill_details(a, cache) }
86
+
87
+ # pick out things to update or delete
88
+ items.each do |e, a|
89
+ id = a.fetch(:id)
90
+ if e
80
91
  diff = e.diff(a)
81
92
  @update << [id, e, a, diff] if diff.any?
82
93
  elsif tracking_id(a) # was previously managed
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Kennel
3
- VERSION = "1.50.1"
3
+ VERSION = "1.54.0"
4
4
  end
@@ -25,18 +25,22 @@ Keep datadog monitors/dashboards/etc in version control, avoid chaotic managemen
25
25
  - `gem install bundler && bundle install`
26
26
  - `cp .env.example .env`
27
27
  - open [Datadog API Settings](https://app.datadoghq.com/account/settings#api)
28
- - find or create your personal "Application Key" and add it to `.env` as `DATADOG_APP_KEY=` (will be on the last page if new)
29
28
  - copy any `API Key` and add it to `.env` as `DATADOG_API_KEY`
29
+ - find or create your personal "Application Key" and add it to `.env` as `DATADOG_APP_KEY=` (will be on the last page if new)
30
+ - change the `DATADOG_SUBDOMAIN=app` in `.env` to your companies subdomain if you have one
30
31
 
31
32
  ### Adding a team
32
33
 
34
+ - `mention` is used for all team monitors via `super()`
35
+ - `renotify_interval` is used for all team monitors (defaults to `0` / off)
36
+ - `tags` is used for all team monitors/dashboards (defaults to `team:<team-name>`)
37
+
33
38
  ```Ruby
34
39
  # teams/my_team.rb
35
40
  module Teams
36
41
  class MyTeam < Kennel::Models::Team
37
42
  defaults(
38
- slack: -> { "my-alerts" },
39
- email: -> { "my-team@example.com" }
43
+ mention: -> { "@slack-my-team" }
40
44
  )
41
45
  end
42
46
  end
@@ -44,13 +48,14 @@ end
44
48
 
45
49
  ### Adding a new monitor
46
50
  - use [datadog monitor UI](https://app.datadoghq.com/monitors#create/metric) to create a monitor
47
- - get the `id` from the url
48
- - `RESOURCE=monitor ID=12345 bundle exec rake kennel:import`
49
51
  - see below
50
52
 
51
53
  ### Updating an existing monitor
54
+ - use [datadog monitor UI](https://app.datadoghq.com/monitors#create/metric) to find a monitor
55
+ - get the `id` from the url
56
+ - run `RESOURCE=monitor ID=12345 bundle exec rake kennel:import` and copy the output
52
57
  - find or create a project in `projects/`
53
- - add a monitor to `parts: [` list
58
+ - add the monitor to `parts: [` list, for example:
54
59
  ```Ruby
55
60
  # projects/my_project.rb
56
61
  class MyProject < Kennel::Models::Project
@@ -65,7 +70,8 @@ end
65
70
  kennel_id: -> { "load-too-high" }, # make up a unique name
66
71
  name: -> { "Foobar Load too high" }, # nice descriptive name that will show up in alerts and emails
67
72
  message: -> {
68
- # Explain what behavior to expect and how to fix the cause. Use #{super()} to add team notifications.
73
+ # Explain what behavior to expect and how to fix the cause
74
+ # Use #{super()} to add team notifications.
69
75
  <<~TEXT
70
76
  Foobar will be slow and that could cause Barfoo to go down.
71
77
  Add capacity or debug why it is suddenly slow.
@@ -80,21 +86,22 @@ end
80
86
  )
81
87
  end
82
88
  ```
83
- - `bundle exec rake plan` update to existing should be shown (not Create / Delete)
84
- - alternatively: `bundle exec rake generate` to only update the generated `json` files
89
+ - run `PROJECT=my_project bundle exec rake plan`, an Update to the existing monitor should be shown (not Create / Delete)
90
+ - alternatively: `bundle exec rake generate` to only locally update the generated `json` files
85
91
  - review changes then `git commit`
86
92
  - make a PR ... get reviewed ... merge
87
93
  - datadog is updated by travis
88
94
 
89
95
  ### Adding a new dashboard
90
96
  - go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to create a dashboard
91
- - get the `id` from the url
92
- - `RESOURCE=dashboard ID=abc-def-ghi bundle exec rake kennel:import`
93
97
  - see below
94
98
 
95
99
  ### Updating an existing dashboard
100
+ - go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to find a dashboard
101
+ - get the `id` from the url
102
+ - run `RESOURCE=dashboard ID=abc-def-ghi bundle exec rake kennel:import` and copy the output
96
103
  - find or create a project in `projects/`
97
- - add a dashboard to `parts: [` list
104
+ - add a dashboard to `parts: [` list, for example:
98
105
  ```Ruby
99
106
  class MyProject < Kennel::Models::Project
100
107
  defaults(
@@ -142,6 +149,7 @@ To link to existing monitors via their kennel_id
142
149
 
143
150
  - Screens `uptime` widgets can use `monitor: {id: "foo:bar"}`
144
151
  - Screens `alert_graph` widgets can use `alert_id: "foo:bar"`
152
+ - Monitors `composite` can use `query: -> { "%{foo:bar} || %{foo:baz}" }`
145
153
 
146
154
  ### Debugging changes locally
147
155
 
@@ -177,7 +185,7 @@ Reuse it in multiple projects.
177
185
  ```Ruby
178
186
  class Database < Kennel::Models::Project
179
187
  defaults(
180
- team: -> { Kennel::Models::Team.new(slack: -> { 'foo' }, kennel_id: -> { 'foo' }) },
188
+ team: -> { Kennel::Models::Team.new(mention: -> { '@slack-foo' }, kennel_id: -> { 'foo' }) },
181
189
  parts: -> { [Monitors::LoadTooHigh.new(self, critical: -> { 13 })] }
182
190
  )
183
191
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kennel
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.50.1
4
+ version: 1.54.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-23 00:00:00.000000000 Z
11
+ date: 2019-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday