kennel 1.68.0 → 1.72.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb534c9bfadda4d374201e3e6b00eddbe5596af1376b8e11e071b27c7d1eaa28
4
- data.tar.gz: 33566de8715eb98743470f5d40f9e77744a1932de3900652cf8f01b72ffbe5f6
3
+ metadata.gz: 1406c5a505ce76ac2af360ba94344a9c942b9c84381f5c695ff18015c62ca530
4
+ data.tar.gz: 04c66c7a56702edd27248c9c71a5c9aa87787c65714aa95112b8479bc5e3fc59
5
5
  SHA512:
6
- metadata.gz: 57b28c3702ce5bf31b81dcfc104e61cb4ef936564df0575ef65812dc0d2224202c71d120f53b82527464dd50f545388e7d130600941b2fb75e9e2361b1dc499a
7
- data.tar.gz: 97b1954e09f674ce834ad717fa90112f8e1e905d244040b0d2360b41f0bfde0955da5d69a86985964525efd2aa89ee13fdcbd22beffcbea2911aa5a795906788
6
+ metadata.gz: 0d0b0abc841eca42e4e13a67634c22653bb56f212304baff189344339a8aeaa877eb753dbd06c5989c938b53eb5ff5c9b3764c726556aebdf5ec20c379498922
7
+ data.tar.gz: 1458d96d7d17b04beae03734106d305e55c05006de958c0eb54b2e2ab982b7646209a8d4331604b02a754fb84ccee346525a330a18161b14fc604006478067e3
data/Readme.md CHANGED
@@ -23,7 +23,7 @@ Manage datadog monitors/dashboards/slos as code
23
23
  ```
24
24
  - add a basic projects and teams so others can copy-paste to get started
25
25
  - setup travis build for your repo
26
- - uncomment `.travis.yml` section for automated github PR feedback and datadog updates on merge
26
+ - uncomment `.travis.yml` section for automated PR planing and datadog updates on merge
27
27
  - follow `Setup` in your repos Readme.md
28
28
  <!-- NOT IN -->
29
29
 
@@ -72,7 +72,7 @@ end
72
72
  ### Updating an existing monitor
73
73
  - use [datadog monitor UI](https://app.datadoghq.com/monitors/manage) to find a monitor
74
74
  - get the `id` from the url
75
- - run `RESOURCE=monitor ID=12345 bundle exec rake kennel:import` and copy the output
75
+ - run `URL='https://app.datadoghq.com/monitors/123' bundle exec rake kennel:import` and copy the output
76
76
  - find or create a project in `projects/`
77
77
  - add the monitor to `parts: [` list, for example:
78
78
  ```Ruby
@@ -118,7 +118,7 @@ end
118
118
  ### Updating an existing dashboard
119
119
  - go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to find a dashboard
120
120
  - get the `id` from the url
121
- - run `RESOURCE=dashboard ID=abc-def-ghi bundle exec rake kennel:import` and copy the output
121
+ - run `URL='https://app.datadoghq.com/dashboard/bet-foo-bar' bundle exec rake kennel:import` and copy the output
122
122
  - find or create a project in `projects/`
123
123
  - add a dashboard to `parts: [` list, for example:
124
124
  ```Ruby
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # Not used in here, but in our templated repo ... so keeping it around for now.
2
3
  module Kennel
3
4
  class GithubReporter
4
5
  MAX_COMMENT_SIZE = 65536
@@ -16,12 +16,13 @@ module Kennel
16
16
  TIMESERIES_DEFAULTS = { show_legend: false, legend_size: "0" }.freeze
17
17
  SUPPORTED_DEFINITION_OPTIONS = [:events, :markers, :precision].freeze
18
18
 
19
- settings :title, :description, :definitions, :widgets, :layout_type
19
+ settings :title, :description, :definitions, :widgets, :layout_type, :template_variable_presets
20
20
 
21
21
  defaults(
22
22
  description: -> { "" },
23
23
  definitions: -> { [] },
24
24
  widgets: -> { [] },
25
+ template_variable_presets: -> { [] },
25
26
  id: -> { nil }
26
27
  )
27
28
 
@@ -102,6 +103,7 @@ module Kennel
102
103
  title: "#{title}#{LOCK}",
103
104
  description: description,
104
105
  template_variables: render_template_variables,
106
+ template_variable_presets: template_variable_presets,
105
107
  widgets: all_widgets
106
108
  }
107
109
 
@@ -116,6 +118,10 @@ module Kennel
116
118
  Utils.path_to_url "/dashboard/#{id}"
117
119
  end
118
120
 
121
+ def self.parse_url(url)
122
+ url[/\/dashboard\/([a-z\d-]+)/, 1]
123
+ end
124
+
119
125
  def resolve_linked_tracking_ids(id_map)
120
126
  widgets = as_json[:widgets].flat_map { |w| [w, *w.dig(:definition, :widgets) || []] }
121
127
  widgets.each do |widget|
@@ -163,6 +169,10 @@ module Kennel
163
169
  super
164
170
 
165
171
  validate_template_variables data, :widgets
172
+
173
+ # Avoid diff from datadog presets sorting.
174
+ presets = data[:template_variable_presets]
175
+ invalid! "template_variable_presets must be sorted by name" if presets != presets.sort_by { |p| p[:name] }
166
176
  end
167
177
 
168
178
  def render_definitions
@@ -120,6 +120,12 @@ module Kennel
120
120
  Utils.path_to_url "/monitors##{id}/edit"
121
121
  end
122
122
 
123
+ # datadog uses both / and # as separator in it's links
124
+ def self.parse_url(url)
125
+ return unless id = url[/\/monitors[\/#](\d+)/, 1]
126
+ Integer(id)
127
+ end
128
+
123
129
  def self.normalize(expected, actual)
124
130
  super
125
131
  options = actual.fetch(:options)
@@ -11,6 +11,14 @@ module Kennel
11
11
  settings :id, :kennel_id
12
12
 
13
13
  class << self
14
+ def parse_any_url(url)
15
+ subclasses.detect do |s|
16
+ if id = s.parse_url(url)
17
+ break s.api_resource, id
18
+ end
19
+ end
20
+ end
21
+
14
22
  private
15
23
 
16
24
  def normalize(_expected, actual)
@@ -62,6 +62,10 @@ module Kennel
62
62
  Utils.path_to_url "/slo?slo_id=#{id}"
63
63
  end
64
64
 
65
+ def self.parse_url(url)
66
+ url[/\/slo\?slo_id=([a-z\d]+)/, 1]
67
+ end
68
+
65
69
  def resolve_linked_tracking_ids(id_map)
66
70
  as_json[:monitor_ids] = as_json[:monitor_ids].map do |id|
67
71
  id.is_a?(String) ? resolve_link(id, :monitor, id_map) : id
@@ -19,6 +19,7 @@ module Kennel
19
19
  end
20
20
  @expected.each { |e| add_tracking_id e }
21
21
  calculate_diff
22
+ prevent_irreversible_partial_updates
22
23
  end
23
24
 
24
25
  def plan
@@ -42,7 +43,6 @@ module Kennel
42
43
  Kennel.out.puts "Created #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(reply.fetch(:id))}"
43
44
  end
44
45
 
45
- block_irreversible_partial_updates
46
46
  @update.each do |id, e|
47
47
  @api.update e.class.api_resource, id, e.as_json
48
48
  Kennel.out.puts "Updated #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(id)}"
@@ -151,7 +151,7 @@ module Kennel
151
151
  return if list.empty?
152
152
  list.each do |_, e, a, diff|
153
153
  api_resource = (e ? e.class.api_resource : a.fetch(:api_resource))
154
- Kennel.out.puts Utils.color(color, "#{step} #{api_resource} #{tracking_id(e&.as_json || a)}")
154
+ Kennel.out.puts Utils.color(color, "#{step} #{api_resource} #{e&.tracking_id || tracking_id(a)}")
155
155
  print_diff(diff) if diff # only for update
156
156
  end
157
157
  end
@@ -159,12 +159,12 @@ module Kennel
159
159
  def print_diff(diff)
160
160
  diff.each do |type, field, old, new|
161
161
  if type == "+"
162
- temp = new.inspect
163
- new = old.inspect
162
+ temp = Utils.pretty_inspect(new)
163
+ new = Utils.pretty_inspect(old)
164
164
  old = temp
165
165
  else # ~ and -
166
- old = old.inspect
167
- new = new.inspect
166
+ old = Utils.pretty_inspect(old)
167
+ new = Utils.pretty_inspect(new)
168
168
  end
169
169
 
170
170
  if (old + new).size > 100
@@ -177,18 +177,30 @@ module Kennel
177
177
  end
178
178
  end
179
179
 
180
- def block_irreversible_partial_updates
180
+ # Do not add tracking-id when working with existing ids on a branch,
181
+ # so resource do not get deleted from merges to master.
182
+ # Also make sure the diff still makes sense, by kicking out the now noop-update.
183
+ #
184
+ # Note: ideally we'd never add tracking in the first place, but at that point we do not know the diff yet
185
+ def prevent_irreversible_partial_updates
181
186
  return unless @project_filter
182
- return if @update.none? do |_, e, _, diff|
183
- e.id && diff.any? do |_, field, old, new = nil|
184
- TRACKING_FIELDS.include?(field.to_sym) && tracking_value(old) != tracking_value(new)
187
+ @update.select! do |_, e, _, diff|
188
+ next true unless e.id # short circuit for performance
189
+
190
+ diff.select! do |field_diff|
191
+ (_, field, old, new) = field_diff
192
+ next true unless tracking_field?(field)
193
+
194
+ if (old_tracking = tracking_value(old))
195
+ old_tracking == tracking_value(new) || raise("do not update! (atm unreachable)")
196
+ else
197
+ field_diff[3] = remove_tracking_id(e) # make plan output match update
198
+ old != field_diff[3]
199
+ end
185
200
  end
201
+
202
+ !diff.empty?
186
203
  end
187
- raise <<~TEXT
188
- Updates with PROJECT= filter should not update tracking id in #{TRACKING_FIELDS.join("/")} of resources with a set `id:`,
189
- since this makes them get deleted by a full update.
190
- Remove the `id:` to test them out, which will result in a copy being created and later deleted.
191
- TEXT
192
204
  end
193
205
 
194
206
  def resolve_linked_tracking_ids(actual)
@@ -212,6 +224,13 @@ module Kennel
212
224
  json[field] = "#{json[field]}\n-- Managed by kennel #{e.tracking_id} in #{e.project.class.file_location}, do not modify manually".lstrip
213
225
  end
214
226
 
227
+ def remove_tracking_id(e)
228
+ json = e.as_json
229
+ field = tracking_field(json)
230
+ value = json[field]
231
+ json[field] = value.dup.sub!(/\n?-- Managed by kennel .*/, "") || raise("did not find tracking id in #{value}")
232
+ end
233
+
215
234
  def tracking_id(a)
216
235
  tracking_value a[tracking_field(a)]
217
236
  end
@@ -223,5 +242,9 @@ module Kennel
223
242
  def tracking_field(a)
224
243
  TRACKING_FIELDS.detect { |f| a.key?(f) }
225
244
  end
245
+
246
+ def tracking_field?(field)
247
+ TRACKING_FIELDS.include?(field.to_sym)
248
+ end
226
249
  end
227
250
  end
@@ -68,7 +68,7 @@ namespace :kennel do
68
68
  Kennel.update
69
69
  end
70
70
 
71
- desc "update if this is a push to the default branch, otherwise plan (report to github with GITHUB_TOKEN)"
71
+ desc "update if this is a push to the default branch, otherwise plan"
72
72
  task :travis do
73
73
  on_default_branch = (ENV["TRAVIS_BRANCH"] == (ENV["DEFAULT_BRANCH"] || "master"))
74
74
  is_push = (ENV["TRAVIS_PULL_REQUEST"] == "false")
@@ -79,9 +79,7 @@ namespace :kennel do
79
79
  "kennel:plan" # show plan in travis logs
80
80
  end
81
81
 
82
- Kennel::GithubReporter.report(ENV["GITHUB_TOKEN"]) do
83
- Rake::Task[task_name].invoke
84
- end
82
+ Rake::Task[task_name].invoke
85
83
  end
86
84
 
87
85
  desc "show unmuted alerts filtered by TAG, for example TAG=team:foo"
@@ -111,12 +109,18 @@ namespace :kennel do
111
109
  end
112
110
  end
113
111
 
114
- desc "Convert existing resources to copy-pastable definitions to import existing resources RESOURCE=[dashboard,monitor,slo] ID=1234"
112
+ desc "Convert existing resources to copy-pasteable definitions to import existing resources (call with URL= or call with RESOURCE= and ID=)"
115
113
  task import: :environment do
116
- resource = ENV["RESOURCE"] || Kennel::Tasks.abort("Call with RESOURCE=dashboard or monitor or slo")
117
- id = ENV["ID"] || Kennel::Tasks.abort("Call with ID=1234")
118
- id = Integer(id) if id =~ /^\d+$/ # dashboards can have alphanumeric ids
119
- puts Kennel::Importer.new(Kennel.send(:api)).import(resource, id)
114
+ if (id = ENV["ID"]) && (resource = ENV["RESOURCE"])
115
+ id = Integer(id) if id =~ /^\d+$/ # dashboards can have alphanumeric ids
116
+ elsif (url = ENV["URL"])
117
+ resource, id = Kennel::Models::Record.parse_any_url(url) || Kennel::Tasks.abort("Unable to parse url")
118
+ else
119
+ possible_resources = Kennel::Models::Record.subclasses.map(&:api_resource)
120
+ Kennel::Tasks.abort("Call with URL= or call with RESOURCE=#{possible_resources.join(" or ")} and ID=")
121
+ end
122
+
123
+ Kennel.out.puts Kennel::Importer.new(Kennel.send(:api)).import(resource, id)
120
124
  end
121
125
 
122
126
  desc "Dump ALL of datadog config as raw json ... useful for grep/search TYPE=slo|monitor|dashboard"
@@ -19,6 +19,7 @@ module Kennel
19
19
  .gsub(/::/, "_") # Foo::Bar -> foo_bar
20
20
  .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') # FOOBar -> foo_bar
21
21
  .gsub(/([a-z\d])([A-Z])/, '\1_\2') # fooBar -> foo_bar
22
+ .tr("-", "_") # foo-bar -> foo_bar
22
23
  .downcase
23
24
  end
24
25
 
@@ -142,6 +143,17 @@ module Kennel
142
143
  else []
143
144
  end
144
145
  end
146
+
147
+ # TODO: use awesome-print or similar, but it has too many monkey-patches
148
+ # https://github.com/amazing-print/amazing_print/issues/36
149
+ def pretty_inspect(object)
150
+ string = object.inspect
151
+ string.gsub!(/:([a-z_]+)=>/, "\\1: ")
152
+ 10.times do
153
+ string.gsub!(/{(\S.*?\S)}/, "{ \\1 }") || break
154
+ end
155
+ string
156
+ end
145
157
  end
146
158
  end
147
159
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Kennel
3
- VERSION = "1.68.0"
3
+ VERSION = "1.72.0"
4
4
  end
@@ -54,7 +54,7 @@ end
54
54
  ### Updating an existing monitor
55
55
  - use [datadog monitor UI](https://app.datadoghq.com/monitors/manage) to find a monitor
56
56
  - get the `id` from the url
57
- - run `RESOURCE=monitor ID=12345 bundle exec rake kennel:import` and copy the output
57
+ - run `URL='https://app.datadoghq.com/monitors/123' bundle exec rake kennel:import` and copy the output
58
58
  - find or create a project in `projects/`
59
59
  - add the monitor to `parts: [` list, for example:
60
60
  ```Ruby
@@ -100,7 +100,7 @@ end
100
100
  ### Updating an existing dashboard
101
101
  - go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to find a dashboard
102
102
  - get the `id` from the url
103
- - run `RESOURCE=dashboard ID=abc-def-ghi bundle exec rake kennel:import` and copy the output
103
+ - run `URL='https://app.datadoghq.com/dashboard/bet-foo-bar' bundle exec rake kennel:import` and copy the output
104
104
  - find or create a project in `projects/`
105
105
  - add a dashboard to `parts: [` list, for example:
106
106
  ```Ruby
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.68.0
4
+ version: 1.72.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: 2020-05-11 00:00:00.000000000 Z
11
+ date: 2020-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday