kennel 1.123.0 → 1.124.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/kennel/api.rb +4 -4
- data/lib/kennel/importer.rb +1 -1
- data/lib/kennel/models/monitor.rb +7 -1
- data/lib/kennel/models/record.rb +44 -12
- data/lib/kennel/optional_validations.rb +58 -1
- data/lib/kennel/syncer.rb +39 -28
- data/lib/kennel/tasks.rb +16 -12
- data/lib/kennel/utils.rb +6 -0
- data/lib/kennel/version.rb +1 -1
- data/lib/kennel.rb +30 -29
- metadata +2 -3
- data/lib/kennel/compatibility.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31edba322106d5b2f1942c57640f19eff6a265ea970b5bb2cc7f4ef02d1889fd
|
4
|
+
data.tar.gz: f47474f8b7d745714fb49231755ed2bf41fae62cf8b5ff3817017c8cd8277713
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3dc6b1b72e834b7b7663b577c830dfa23da87a197752b04988edd2366ac9089685ff01e19a1e3c8244ac9a41f4217c75ec34bc6198ca83cf545bf6079069d71e
|
7
|
+
data.tar.gz: 1dffcf6adbd06fe80117d9a5f9bc3b55fac90bc21abb4fdd4ebeb5b726ade944c799edbbcca3eabbc5a28beff6a302011ecfcce18f041b4ad2228076e164c2d6
|
data/lib/kennel/api.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
# encapsulates knowledge around how the api works
|
3
|
-
# especially 1-off weirdness that should not
|
3
|
+
# especially 1-off weirdness that should not leak into other parts of the code
|
4
4
|
module Kennel
|
5
5
|
class Api
|
6
6
|
CACHE_FILE = "tmp/cache/details"
|
7
7
|
|
8
|
-
def initialize(app_key, api_key)
|
9
|
-
@app_key = app_key
|
10
|
-
@api_key = api_key
|
8
|
+
def initialize(app_key = nil, api_key = nil)
|
9
|
+
@app_key = app_key || ENV.fetch("DATADOG_APP_KEY")
|
10
|
+
@api_key = api_key || ENV.fetch("DATADOG_API_KEY")
|
11
11
|
url = Utils.path_to_url("", subdomain: "app")
|
12
12
|
@client = Faraday.new(url: url) { |c| c.adapter :net_http_persistent }
|
13
13
|
end
|
data/lib/kennel/importer.rb
CHANGED
@@ -98,7 +98,7 @@ module Kennel
|
|
98
98
|
def link_composite_monitors(data)
|
99
99
|
if data[:type] == "composite"
|
100
100
|
data[:query].gsub!(/\d+/) do |id|
|
101
|
-
object =
|
101
|
+
object = @api.show("monitor", id)
|
102
102
|
tracking_id = Kennel::Models::Monitor.parse_tracking_id(object)
|
103
103
|
tracking_id ? "%{#{tracking_id}}" : id
|
104
104
|
rescue StandardError # monitor not found
|
@@ -39,7 +39,7 @@ module Kennel
|
|
39
39
|
renotify_interval: -> { project.team.renotify_interval },
|
40
40
|
warning: -> { nil },
|
41
41
|
ok: -> { nil },
|
42
|
-
notify_no_data: -> { true }, # datadog sets this to false by default, but true is
|
42
|
+
notify_no_data: -> { true }, # datadog UI sets this to false by default, but true is safer
|
43
43
|
no_data_timeframe: -> { 60 },
|
44
44
|
notify_audit: -> { MONITOR_OPTION_DEFAULTS.fetch(:notify_audit) },
|
45
45
|
new_host_delay: -> { MONITOR_OPTION_DEFAULTS.fetch(:new_host_delay) },
|
@@ -99,6 +99,12 @@ module Kennel
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
+
# setting this via the api breaks the UI with
|
103
|
+
# "The no_data_timeframe option is not allowed for log alert monitors"
|
104
|
+
if data.fetch(:type) == "log alert"
|
105
|
+
options.delete :no_data_timeframe
|
106
|
+
end
|
107
|
+
|
102
108
|
if windows = threshold_windows
|
103
109
|
options[:threshold_windows] = windows
|
104
110
|
end
|
data/lib/kennel/models/record.rb
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
module Kennel
|
3
3
|
module Models
|
4
4
|
class Record < Base
|
5
|
+
class PrepareError < StandardError
|
6
|
+
def initialize(tracking_id)
|
7
|
+
super("Error while preparing #{tracking_id}")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
UnvalidatedRecordError = Class.new(StandardError)
|
12
|
+
|
5
13
|
include OptionalValidations
|
6
14
|
|
7
15
|
# Apart from if you just don't like the default for some reason,
|
@@ -76,7 +84,7 @@ module Kennel
|
|
76
84
|
end
|
77
85
|
end
|
78
86
|
|
79
|
-
attr_reader :project
|
87
|
+
attr_reader :project, :unfiltered_validation_errors, :filtered_validation_errors
|
80
88
|
|
81
89
|
def initialize(project, *args)
|
82
90
|
raise ArgumentError, "First argument must be a project, not #{project.class}" unless project.is_a?(Project)
|
@@ -100,7 +108,7 @@ module Kennel
|
|
100
108
|
@tracking_id ||= begin
|
101
109
|
id = "#{project.kennel_id}:#{kennel_id}"
|
102
110
|
unless id.match?(ALLOWED_KENNEL_ID_REGEX) # <-> parse_tracking_id
|
103
|
-
raise "#{id} must match #{ALLOWED_KENNEL_ID_REGEX}"
|
111
|
+
raise "Bad kennel/tracking id: #{id.inspect} must match #{ALLOWED_KENNEL_ID_REGEX}"
|
104
112
|
end
|
105
113
|
id
|
106
114
|
end
|
@@ -112,7 +120,7 @@ module Kennel
|
|
112
120
|
def add_tracking_id
|
113
121
|
json = as_json
|
114
122
|
if self.class.parse_tracking_id(json)
|
115
|
-
raise "#{
|
123
|
+
raise "#{safe_tracking_id} Remove \"-- #{MARKER_TEXT}\" line from #{self.class::TRACKING_FIELD} to copy a resource"
|
116
124
|
end
|
117
125
|
json[self.class::TRACKING_FIELD] =
|
118
126
|
"#{json[self.class::TRACKING_FIELD]}\n" \
|
@@ -129,13 +137,31 @@ module Kennel
|
|
129
137
|
}.compact
|
130
138
|
end
|
131
139
|
|
140
|
+
def build
|
141
|
+
@unfiltered_validation_errors = []
|
142
|
+
json = nil
|
143
|
+
|
144
|
+
begin
|
145
|
+
json = build_json
|
146
|
+
(id = json.delete(:id)) && json[:id] = id
|
147
|
+
validate_json(json)
|
148
|
+
rescue StandardError
|
149
|
+
if unfiltered_validation_errors.empty?
|
150
|
+
@unfiltered_validation_errors = nil
|
151
|
+
raise PrepareError, safe_tracking_id
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
@filtered_validation_errors = filter_validation_errors
|
156
|
+
@as_json = json # Only valid if filtered_validation_errors.empty?
|
157
|
+
end
|
158
|
+
|
132
159
|
def as_json
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
end
|
160
|
+
# A courtesy to those tests that still expect as_json to perform validation and raise on error
|
161
|
+
build if @unfiltered_validation_errors.nil?
|
162
|
+
raise UnvalidatedRecordError, "#{safe_tracking_id} as_json called on invalid part" unless filtered_validation_errors.empty?
|
163
|
+
|
164
|
+
@as_json
|
139
165
|
end
|
140
166
|
|
141
167
|
# Can raise DisallowedUpdateError
|
@@ -143,7 +169,14 @@ module Kennel
|
|
143
169
|
end
|
144
170
|
|
145
171
|
def invalid_update!(field, old_value, new_value)
|
146
|
-
raise DisallowedUpdateError, "#{
|
172
|
+
raise DisallowedUpdateError, "#{safe_tracking_id} Datadog does not allow update of #{field} (#{old_value.inspect} -> #{new_value.inspect})"
|
173
|
+
end
|
174
|
+
|
175
|
+
# For use during error handling
|
176
|
+
def safe_tracking_id
|
177
|
+
tracking_id
|
178
|
+
rescue StandardError
|
179
|
+
"<unknown; #tracking_id crashed>"
|
147
180
|
end
|
148
181
|
|
149
182
|
private
|
@@ -178,9 +211,8 @@ module Kennel
|
|
178
211
|
end
|
179
212
|
end
|
180
213
|
|
181
|
-
# let users know which project/resource failed when something happens during diffing where the backtrace is hidden
|
182
214
|
def invalid!(message)
|
183
|
-
|
215
|
+
unfiltered_validation_errors << ValidationMessage.new(message)
|
184
216
|
end
|
185
217
|
|
186
218
|
def raise_with_location(error, message)
|
@@ -1,15 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Kennel
|
3
3
|
module OptionalValidations
|
4
|
+
ValidationMessage = Struct.new(:text)
|
5
|
+
|
4
6
|
def self.included(base)
|
5
7
|
base.settings :validate
|
6
8
|
base.defaults(validate: -> { true })
|
7
9
|
end
|
8
10
|
|
11
|
+
def self.valid?(parts)
|
12
|
+
parts_with_errors = parts.reject do |part|
|
13
|
+
part.filtered_validation_errors.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
return true if parts_with_errors.empty?
|
17
|
+
|
18
|
+
Kennel.err.puts
|
19
|
+
parts_with_errors.sort_by(&:safe_tracking_id).each do |part|
|
20
|
+
part.filtered_validation_errors.each do |err|
|
21
|
+
Kennel.err.puts "#{part.safe_tracking_id} #{err.text}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
Kennel.err.puts
|
25
|
+
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
9
29
|
private
|
10
30
|
|
11
31
|
def validate_json(data)
|
12
|
-
bad = Kennel::Utils.all_keys(data).grep_v(Symbol)
|
32
|
+
bad = Kennel::Utils.all_keys(data).grep_v(Symbol).sort.uniq
|
13
33
|
return if bad.empty?
|
14
34
|
invalid!(
|
15
35
|
"Only use Symbols as hash keys to avoid permanent diffs when updating.\n" \
|
@@ -17,5 +37,42 @@ module Kennel
|
|
17
37
|
"#{bad.map(&:inspect).join("\n")}"
|
18
38
|
)
|
19
39
|
end
|
40
|
+
|
41
|
+
def filter_validation_errors
|
42
|
+
if validate
|
43
|
+
unfiltered_validation_errors
|
44
|
+
elsif unfiltered_validation_errors.empty?
|
45
|
+
msg = "`validate` is set to false, but there are no validation errors to suppress. Remove `validate: false`"
|
46
|
+
|
47
|
+
mode = ENV.fetch("UNNECESSARY_VALIDATE_FALSE") do
|
48
|
+
if ENV.key?("PROJECT") || ENV.key?("TRACKING_ID")
|
49
|
+
"fail"
|
50
|
+
else
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
if mode == "fail"
|
56
|
+
[ValidationMessage.new(msg)]
|
57
|
+
else
|
58
|
+
Kennel.out.puts "#{safe_tracking_id} #{msg}" if mode == "show"
|
59
|
+
[]
|
60
|
+
end
|
61
|
+
else
|
62
|
+
mode = ENV.fetch("SUPPRESSED_ERRORS", "ignore")
|
63
|
+
|
64
|
+
if mode == "fail"
|
65
|
+
unfiltered_validation_errors
|
66
|
+
else
|
67
|
+
if mode == "show"
|
68
|
+
unfiltered_validation_errors.each do |err|
|
69
|
+
Kennel.out.puts "#{safe_tracking_id} `validate: false` suppressing error: #{err.text.gsub("\n", " ")}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
[]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
20
77
|
end
|
21
78
|
end
|
data/lib/kennel/syncer.rb
CHANGED
@@ -6,12 +6,11 @@ module Kennel
|
|
6
6
|
class Syncer
|
7
7
|
DELETE_ORDER = ["dashboard", "slo", "monitor", "synthetics/tests"].freeze # dashboards references monitors + slos, slos reference monitors
|
8
8
|
LINE_UP = "\e[1A\033[K" # go up and clear
|
9
|
+
Plan = Struct.new(:changes, keyword_init: true)
|
9
10
|
|
10
|
-
|
11
|
-
Update = Struct.new(:update_log, keyword_init: true)
|
12
|
-
|
13
|
-
def initialize(api, expected, project_filter: nil, tracking_id_filter: nil)
|
11
|
+
def initialize(api, expected, kennel:, project_filter: nil, tracking_id_filter: nil)
|
14
12
|
@api = api
|
13
|
+
@kennel = kennel
|
15
14
|
@project_filter = project_filter
|
16
15
|
@tracking_id_filter = tracking_id_filter
|
17
16
|
@expected = Set.new expected # need set to speed up deletion
|
@@ -32,11 +31,10 @@ module Kennel
|
|
32
31
|
end
|
33
32
|
|
34
33
|
Plan.new(
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
delete: @delete
|
34
|
+
changes:
|
35
|
+
@create.map { |_id, e| [:create, e.class.api_resource, e.tracking_id, nil] } +
|
36
|
+
@update.map { |_id, e| [:create, e.class.api_resource, e.tracking_id, nil] } +
|
37
|
+
@delete.map { |_id, _e, a| [:delete, a.fetch(:klass).api_resource, a.fetch(:tracking_id), a.fetch(:id)] }
|
40
38
|
)
|
41
39
|
end
|
42
40
|
|
@@ -47,7 +45,7 @@ module Kennel
|
|
47
45
|
end
|
48
46
|
|
49
47
|
def update
|
50
|
-
|
48
|
+
changes = []
|
51
49
|
|
52
50
|
each_resolved @create do |_, e|
|
53
51
|
message = "#{e.class.api_resource} #{e.tracking_id}"
|
@@ -55,7 +53,7 @@ module Kennel
|
|
55
53
|
reply = @api.create e.class.api_resource, e.as_json
|
56
54
|
cache_metadata reply, e.class
|
57
55
|
id = reply.fetch(:id)
|
58
|
-
|
56
|
+
changes << [:create, e.class.api_resource, e.tracking_id, id]
|
59
57
|
populate_id_map [], [reply] # allow resolving ids we could previously no resolve
|
60
58
|
Kennel.out.puts "#{LINE_UP}Created #{message} #{e.class.url(id)}"
|
61
59
|
end
|
@@ -64,7 +62,7 @@ module Kennel
|
|
64
62
|
message = "#{e.class.api_resource} #{e.tracking_id} #{e.class.url(id)}"
|
65
63
|
Kennel.out.puts "Updating #{message}"
|
66
64
|
@api.update e.class.api_resource, id, e.as_json
|
67
|
-
|
65
|
+
changes << [:update, e.class.api_resource, e.tracking_id, id]
|
68
66
|
Kennel.out.puts "#{LINE_UP}Updated #{message}"
|
69
67
|
end
|
70
68
|
|
@@ -73,15 +71,17 @@ module Kennel
|
|
73
71
|
message = "#{klass.api_resource} #{a.fetch(:tracking_id)} #{id}"
|
74
72
|
Kennel.out.puts "Deleting #{message}"
|
75
73
|
@api.delete klass.api_resource, id
|
76
|
-
|
74
|
+
changes << [:delete, klass.api_resource, a.fetch(:tracking_id), id]
|
77
75
|
Kennel.out.puts "#{LINE_UP}Deleted #{message}"
|
78
76
|
end
|
79
77
|
|
80
|
-
|
78
|
+
Plan.new(changes: changes)
|
81
79
|
end
|
82
80
|
|
83
81
|
private
|
84
82
|
|
83
|
+
attr_reader :kennel
|
84
|
+
|
85
85
|
# loop over items until everything is resolved or crash when we get stuck
|
86
86
|
# this solves cases like composite monitors depending on each other or monitor->monitor slo->slo monitor chains
|
87
87
|
def each_resolved(list)
|
@@ -121,7 +121,6 @@ module Kennel
|
|
121
121
|
@warnings = []
|
122
122
|
@update = []
|
123
123
|
@delete = []
|
124
|
-
@no_change = []
|
125
124
|
@id_map = IdMap.new
|
126
125
|
|
127
126
|
actual = Progress.progress("Downloading definitions") { download_definitions }
|
@@ -154,8 +153,6 @@ module Kennel
|
|
154
153
|
diff = e.diff(a) # slow ...
|
155
154
|
if diff.any?
|
156
155
|
@update << [id, e, a, diff]
|
157
|
-
else
|
158
|
-
@no_change << [id, e, a]
|
159
156
|
end
|
160
157
|
elsif a.fetch(:tracking_id) # was previously managed
|
161
158
|
@delete << [id, nil, a]
|
@@ -186,7 +183,7 @@ module Kennel
|
|
186
183
|
@expected.each do |e|
|
187
184
|
next unless id = e.id
|
188
185
|
resource = e.class.api_resource
|
189
|
-
if
|
186
|
+
if kennel.strict_imports
|
190
187
|
raise "Unable to find existing #{resource} with id #{id}\nIf the #{resource} was deleted, remove the `id: -> { #{id} }` line."
|
191
188
|
else
|
192
189
|
@warnings << "#{resource} #{e.tracking_id} specifies id #{id}, but no such #{resource} exists. 'id' will be ignored. Remove the `id: -> { #{id} }` line."
|
@@ -234,16 +231,19 @@ module Kennel
|
|
234
231
|
new = Utils.pretty_inspect(new)
|
235
232
|
end
|
236
233
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
234
|
+
message =
|
235
|
+
if use_diff
|
236
|
+
" #{type}#{field}\n" +
|
237
|
+
diff(old, new).map { |l| " #{l}" }.join("\n")
|
238
|
+
elsif (old + new).size > 100
|
239
|
+
" #{type}#{field}\n" \
|
240
|
+
" #{old} ->\n" \
|
241
|
+
" #{new}"
|
242
|
+
else
|
243
|
+
" #{type}#{field} #{old} -> #{new}"
|
244
|
+
end
|
245
|
+
|
246
|
+
Kennel.out.puts truncate_diff(message)
|
247
247
|
end
|
248
248
|
end
|
249
249
|
|
@@ -267,6 +267,17 @@ module Kennel
|
|
267
267
|
end
|
268
268
|
end
|
269
269
|
|
270
|
+
def truncate_diff(message)
|
271
|
+
# min '2' because: -1 makes no sense, 0 does not work with * 2 math, 1 says '1 lines'
|
272
|
+
@max_diff_lines ||= [Integer(ENV.fetch("MAX_DIFF_LINES", "50")), 2].max
|
273
|
+
warning = Utils.color(
|
274
|
+
:magenta,
|
275
|
+
" (Diff for this item truncated after #{@max_diff_lines} lines. " \
|
276
|
+
"Rerun with MAX_DIFF_LINES=#{@max_diff_lines * 2} to see more)"
|
277
|
+
)
|
278
|
+
Utils.truncate_lines(message, to: @max_diff_lines, warning: warning)
|
279
|
+
end
|
280
|
+
|
270
281
|
# We've already validated the desired objects ('generated') in isolation.
|
271
282
|
# Now that we have made the plan, we can perform some more validation.
|
272
283
|
def validate_plan
|
data/lib/kennel/tasks.rb
CHANGED
@@ -8,6 +8,10 @@ require "json"
|
|
8
8
|
module Kennel
|
9
9
|
module Tasks
|
10
10
|
class << self
|
11
|
+
def kennel
|
12
|
+
@kennel ||= Kennel::Engine.new
|
13
|
+
end
|
14
|
+
|
11
15
|
def abort(message = nil)
|
12
16
|
Kennel.err.puts message if message
|
13
17
|
raise SystemExit.new(1), message
|
@@ -35,10 +39,10 @@ module Kennel
|
|
35
39
|
load_environment
|
36
40
|
|
37
41
|
if on_default_branch? && git_push?
|
38
|
-
Kennel.strict_imports = false
|
39
|
-
Kennel.update
|
42
|
+
Kennel::Tasks.kennel.strict_imports = false
|
43
|
+
Kennel::Tasks.kennel.update
|
40
44
|
else
|
41
|
-
Kennel.plan # show plan in CI logs
|
45
|
+
Kennel::Tasks.kennel.plan # show plan in CI logs
|
42
46
|
end
|
43
47
|
end
|
44
48
|
|
@@ -66,7 +70,7 @@ namespace :kennel do
|
|
66
70
|
# https://help.datadoghq.com/hc/en-us/requests/254114 for automatic validation
|
67
71
|
desc "Verify that all used monitor mentions are valid"
|
68
72
|
task validate_mentions: :environment do
|
69
|
-
known = Kennel.
|
73
|
+
known = Kennel::Api.new
|
70
74
|
.send(:request, :get, "/monitor/notifications")
|
71
75
|
.fetch(:handles)
|
72
76
|
.values
|
@@ -93,18 +97,18 @@ namespace :kennel do
|
|
93
97
|
|
94
98
|
desc "generate local definitions"
|
95
99
|
task generate: :environment do
|
96
|
-
Kennel.generate
|
100
|
+
Kennel::Tasks.kennel.generate
|
97
101
|
end
|
98
102
|
|
99
103
|
# also generate parts so users see and commit updated generated automatically
|
100
104
|
desc "show planned datadog changes (scope with PROJECT=name)"
|
101
105
|
task plan: :generate do
|
102
|
-
Kennel.plan
|
106
|
+
Kennel::Tasks.kennel.plan
|
103
107
|
end
|
104
108
|
|
105
109
|
desc "update datadog (scope with PROJECT=name)"
|
106
110
|
task update_datadog: :environment do
|
107
|
-
Kennel.update
|
111
|
+
Kennel::Tasks.kennel.update
|
108
112
|
end
|
109
113
|
|
110
114
|
desc "update on push to the default branch, otherwise show plan"
|
@@ -115,13 +119,13 @@ namespace :kennel do
|
|
115
119
|
desc "show unmuted alerts filtered by TAG, for example TAG=team:foo"
|
116
120
|
task alerts: :environment do
|
117
121
|
tag = ENV["TAG"] || Kennel::Tasks.abort("Call with TAG=foo:bar")
|
118
|
-
Kennel::UnmutedAlerts.print(Kennel.
|
122
|
+
Kennel::UnmutedAlerts.print(Kennel::Api.new, tag)
|
119
123
|
end
|
120
124
|
|
121
125
|
desc "show monitors with no data by TAG, for example TAG=team:foo [THRESHOLD_DAYS=7] [FORMAT=json]"
|
122
126
|
task nodata: :environment do
|
123
127
|
tag = ENV["TAG"] || Kennel::Tasks.abort("Call with TAG=foo:bar")
|
124
|
-
monitors = Kennel.
|
128
|
+
monitors = Kennel::Api.new.list("monitor", monitor_tags: tag, group_states: "no data")
|
125
129
|
monitors.select! { |m| m[:overall_state] == "No Data" }
|
126
130
|
monitors.reject! { |m| m[:tags].include? "nodata:ignore" }
|
127
131
|
if monitors.any?
|
@@ -179,7 +183,7 @@ namespace :kennel do
|
|
179
183
|
Kennel::Tasks.abort("Call with URL= or call with RESOURCE=#{possible_resources.join(" or ")} and ID=")
|
180
184
|
end
|
181
185
|
|
182
|
-
Kennel.out.puts Kennel::Importer.new(Kennel.
|
186
|
+
Kennel.out.puts Kennel::Importer.new(Kennel::Api.new).import(resource, id)
|
183
187
|
end
|
184
188
|
|
185
189
|
desc "Dump ALL of datadog config as raw json ... useful for grep/search [TYPE=slo|monitor|dashboard]"
|
@@ -190,7 +194,7 @@ namespace :kennel do
|
|
190
194
|
else
|
191
195
|
Kennel::Models::Record.api_resource_map.keys
|
192
196
|
end
|
193
|
-
api = Kennel.
|
197
|
+
api = Kennel::Api.new
|
194
198
|
list = nil
|
195
199
|
first = true
|
196
200
|
|
@@ -240,7 +244,7 @@ namespace :kennel do
|
|
240
244
|
klass =
|
241
245
|
Kennel::Models::Record.subclasses.detect { |s| s.api_resource == resource } ||
|
242
246
|
raise("resource #{resource} not know")
|
243
|
-
object = Kennel.
|
247
|
+
object = Kennel::Api.new.show(resource, id)
|
244
248
|
Kennel.out.puts klass.parse_tracking_id(object)
|
245
249
|
end
|
246
250
|
|
data/lib/kennel/utils.rb
CHANGED
@@ -57,6 +57,12 @@ module Kennel
|
|
57
57
|
"\e[#{COLORS.fetch(color)}m#{text}\e[0m"
|
58
58
|
end
|
59
59
|
|
60
|
+
def truncate_lines(text, to:, warning:)
|
61
|
+
lines = text.split(/\n/, to + 1)
|
62
|
+
lines[-1] = warning if lines.size > to
|
63
|
+
lines.join("\n")
|
64
|
+
end
|
65
|
+
|
60
66
|
def capture_stdout
|
61
67
|
old = Kennel.out
|
62
68
|
Kennel.out = StringIO.new
|
data/lib/kennel/version.rb
CHANGED
data/lib/kennel.rb
CHANGED
@@ -5,7 +5,6 @@ require "zeitwerk"
|
|
5
5
|
require "English"
|
6
6
|
|
7
7
|
require "kennel/version"
|
8
|
-
require "kennel/compatibility"
|
9
8
|
require "kennel/utils"
|
10
9
|
require "kennel/progress"
|
11
10
|
require "kennel/filter"
|
@@ -40,22 +39,24 @@ module Teams
|
|
40
39
|
end
|
41
40
|
|
42
41
|
module Kennel
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
UnresolvableIdError = Class.new(StandardError)
|
43
|
+
DisallowedUpdateError = Class.new(StandardError)
|
44
|
+
GenerationAbortedError = Class.new(StandardError)
|
45
|
+
UpdateResult = Struct.new(:plan, :update, keyword_init: true)
|
46
46
|
|
47
|
-
|
47
|
+
class << self
|
48
|
+
attr_accessor :out, :err
|
49
|
+
end
|
48
50
|
|
49
|
-
|
51
|
+
self.out = $stdout
|
52
|
+
self.err = $stderr
|
50
53
|
|
51
54
|
class Engine
|
52
55
|
def initialize
|
53
|
-
@out = $stdout
|
54
|
-
@err = $stderr
|
55
56
|
@strict_imports = true
|
56
57
|
end
|
57
58
|
|
58
|
-
attr_accessor :
|
59
|
+
attr_accessor :strict_imports
|
59
60
|
|
60
61
|
def generate
|
61
62
|
parts = generated
|
@@ -68,12 +69,8 @@ module Kennel
|
|
68
69
|
end
|
69
70
|
|
70
71
|
def update
|
71
|
-
|
72
|
-
|
73
|
-
UpdateResult.new(
|
74
|
-
plan: the_plan,
|
75
|
-
update: the_update
|
76
|
-
)
|
72
|
+
syncer.plan
|
73
|
+
syncer.update if syncer.confirm
|
77
74
|
end
|
78
75
|
|
79
76
|
private
|
@@ -83,11 +80,11 @@ module Kennel
|
|
83
80
|
end
|
84
81
|
|
85
82
|
def syncer
|
86
|
-
@syncer ||= Syncer.new(api, generated, project_filter: filter.project_filter, tracking_id_filter: filter.tracking_id_filter)
|
83
|
+
@syncer ||= Syncer.new(api, generated, kennel: self, project_filter: filter.project_filter, tracking_id_filter: filter.tracking_id_filter)
|
87
84
|
end
|
88
85
|
|
89
86
|
def api
|
90
|
-
@api ||= Api.new
|
87
|
+
@api ||= Api.new
|
91
88
|
end
|
92
89
|
|
93
90
|
def projects_provider
|
@@ -100,26 +97,30 @@ module Kennel
|
|
100
97
|
|
101
98
|
def generated
|
102
99
|
@generated ||= begin
|
103
|
-
Progress.progress "
|
100
|
+
parts = Progress.progress "Finding parts" do
|
104
101
|
projects = projects_provider.projects
|
105
102
|
projects = filter.filter_projects projects
|
106
103
|
|
107
104
|
parts = Utils.parallel(projects, &:validated_parts).flatten(1)
|
108
|
-
|
105
|
+
filter.filter_parts parts
|
106
|
+
end
|
109
107
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
108
|
+
parts.group_by(&:tracking_id).each do |tracking_id, same|
|
109
|
+
next if same.size == 1
|
110
|
+
raise <<~ERROR
|
111
|
+
#{tracking_id} is defined #{same.size} times
|
112
|
+
use a different `kennel_id` when defining multiple projects/monitors/dashboards to avoid this conflict
|
113
|
+
ERROR
|
114
|
+
end
|
117
115
|
|
116
|
+
Progress.progress "Building json" do
|
118
117
|
# trigger json caching here so it counts into generating
|
119
|
-
Utils.parallel(parts, &:
|
120
|
-
|
121
|
-
parts
|
118
|
+
Utils.parallel(parts, &:build)
|
122
119
|
end
|
120
|
+
|
121
|
+
OptionalValidations.valid?(parts) or raise GenerationAbortedError
|
122
|
+
|
123
|
+
parts
|
123
124
|
end
|
124
125
|
end
|
125
126
|
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.
|
4
|
+
version: 1.124.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: 2022-
|
11
|
+
date: 2022-11-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: diff-lcs
|
@@ -89,7 +89,6 @@ files:
|
|
89
89
|
- Readme.md
|
90
90
|
- lib/kennel.rb
|
91
91
|
- lib/kennel/api.rb
|
92
|
-
- lib/kennel/compatibility.rb
|
93
92
|
- lib/kennel/file_cache.rb
|
94
93
|
- lib/kennel/filter.rb
|
95
94
|
- lib/kennel/github_reporter.rb
|
data/lib/kennel/compatibility.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Kennel
|
4
|
-
module Compatibility
|
5
|
-
def self.included(into)
|
6
|
-
class << into
|
7
|
-
%I[out out= err err= strict_imports strict_imports= generate plan update].each do |sym|
|
8
|
-
define_method(sym) { |*args| instance.public_send(sym, *args) }
|
9
|
-
end
|
10
|
-
|
11
|
-
def build_default
|
12
|
-
Kennel::Engine.new
|
13
|
-
end
|
14
|
-
|
15
|
-
def instance
|
16
|
-
@instance ||= build_default
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def api
|
22
|
-
instance.send(:api)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|