kennel 2.6.1 → 2.8.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 +4 -4
- data/lib/kennel/api.rb +1 -0
- data/lib/kennel/models/dashboard.rb +4 -3
- data/lib/kennel/models/monitor.rb +5 -6
- data/lib/kennel/models/project.rb +6 -1
- data/lib/kennel/models/record.rb +2 -6
- data/lib/kennel/syncer/matched_expected.rb +7 -1
- data/lib/kennel/syncer/plan_printer.rb +1 -1
- data/lib/kennel/syncer.rb +21 -30
- data/lib/kennel/tasks.rb +1 -1
- data/lib/kennel/utils.rb +3 -2
- data/lib/kennel/version.rb +1 -1
- data/lib/kennel.rb +9 -4
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bebf47eb6fde98d7122a199324dca2c205b94a65e844193d7917f2e384fbc029
|
|
4
|
+
data.tar.gz: c3631e51b4d7ef363b7fd177e42d688e00f7ea9816b9161b27dd2f48753fff93
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7029a570da24413841e71dc380a7f07d476c9cff3b83aee05a032478ed1707771b96f071949e2284003ef51777f955174c9d241a0e11c2d6fbd6aadc1d4ccece
|
|
7
|
+
data.tar.gz: b5df9cfcd85ff76b762c9c8a96e630f1b3cf509eb8509e71a42fb2d5c8b24efd6588a88033abe38c2d1ff5b37f8087d2e0074be171f3e78066c1796df53f72b4
|
data/lib/kennel/api.rb
CHANGED
|
@@ -208,9 +208,10 @@ module Kennel
|
|
|
208
208
|
end
|
|
209
209
|
end
|
|
210
210
|
|
|
211
|
-
def
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
def allowed_update_error(actual)
|
|
212
|
+
actual_type = actual[:layout_type]
|
|
213
|
+
return if actual_type == layout_type
|
|
214
|
+
"cannot update layout_type from #{actual_type} to #{layout_type}"
|
|
214
215
|
end
|
|
215
216
|
|
|
216
217
|
private
|
|
@@ -180,12 +180,11 @@ module Kennel
|
|
|
180
180
|
end
|
|
181
181
|
end
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if
|
|
187
|
-
|
|
188
|
-
end
|
|
183
|
+
# ensure type does not change, but not if it's metric->query which is supported and used by importer.rb
|
|
184
|
+
def allowed_update_error(actual)
|
|
185
|
+
actual_type = actual[:type]
|
|
186
|
+
return if actual_type == type || (actual_type == "metric alert" && type == "query alert")
|
|
187
|
+
"cannot update type from #{actual_type} to #{type}"
|
|
189
188
|
end
|
|
190
189
|
|
|
191
190
|
def self.api_resource
|
|
@@ -18,7 +18,7 @@ module Kennel
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def validated_parts
|
|
21
|
-
all = parts
|
|
21
|
+
all = filter_parts(parts)
|
|
22
22
|
unless all.is_a?(Array) && all.all? { |part| part.is_a?(Record) }
|
|
23
23
|
raise "Project #{kennel_id} #parts must return an array of Records"
|
|
24
24
|
end
|
|
@@ -29,6 +29,11 @@ module Kennel
|
|
|
29
29
|
|
|
30
30
|
private
|
|
31
31
|
|
|
32
|
+
# hook for users to add custom filtering via `prepend`
|
|
33
|
+
def filter_parts(parts)
|
|
34
|
+
parts
|
|
35
|
+
end
|
|
36
|
+
|
|
32
37
|
# hook for users to add custom validations via `prepend`
|
|
33
38
|
def validate_parts(parts)
|
|
34
39
|
end
|
data/lib/kennel/models/record.rb
CHANGED
|
@@ -142,12 +142,8 @@ module Kennel
|
|
|
142
142
|
@as_json
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def invalid_update!(field, old_value, new_value)
|
|
150
|
-
raise DisallowedUpdateError, "#{safe_tracking_id} Datadog does not allow update of #{field} (#{old_value.inspect} -> #{new_value.inspect})"
|
|
145
|
+
def allowed_update_error(_a)
|
|
146
|
+
nil
|
|
151
147
|
end
|
|
152
148
|
|
|
153
149
|
# For use during error handling
|
|
@@ -36,7 +36,13 @@ module Kennel
|
|
|
36
36
|
|
|
37
37
|
def matching_expected(a, map)
|
|
38
38
|
klass = a.fetch(:klass)
|
|
39
|
-
|
|
39
|
+
full_id = "#{klass.api_resource}:#{a.fetch(:id)}"
|
|
40
|
+
if (e = map[full_id]) # we try to update and the user has set the id
|
|
41
|
+
return e unless (error = e.allowed_update_error(a))
|
|
42
|
+
raise DisallowedUpdateError, "#{full_id} Datadog does not allow update: #{error}"
|
|
43
|
+
elsif (e = map[a.fetch(:tracking_id)])
|
|
44
|
+
e.allowed_update_error(a) ? nil : e # force a re-create if we can't update
|
|
45
|
+
end
|
|
40
46
|
end
|
|
41
47
|
end
|
|
42
48
|
end
|
|
@@ -12,9 +12,9 @@ module Kennel
|
|
|
12
12
|
if plan.empty?
|
|
13
13
|
Kennel.out.puts Console.color(:green, "Nothing to do")
|
|
14
14
|
else
|
|
15
|
+
print_changes "Delete", plan.deletes, :red
|
|
15
16
|
print_changes "Create", plan.creates, :green
|
|
16
17
|
print_changes "Update", plan.updates, :yellow
|
|
17
|
-
print_changes "Delete", plan.deletes, :red
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
data/lib/kennel/syncer.rb
CHANGED
|
@@ -13,14 +13,13 @@ module Kennel
|
|
|
13
13
|
|
|
14
14
|
attr_reader :plan
|
|
15
15
|
|
|
16
|
-
def initialize(api, expected
|
|
16
|
+
def initialize(api, expected:, actual:, filter:, strict_imports: true)
|
|
17
17
|
@api = api
|
|
18
18
|
@strict_imports = strict_imports
|
|
19
19
|
@filter = filter
|
|
20
20
|
|
|
21
21
|
@resolver = Resolver.new(expected: expected, filter: filter)
|
|
22
22
|
@plan = Plan.new(*calculate_changes(expected: expected, actual: actual))
|
|
23
|
-
validate_changes
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
def print_plan
|
|
@@ -126,27 +125,19 @@ module Kennel
|
|
|
126
125
|
# if there is a new item that has the same name or title as an "to be deleted" item,
|
|
127
126
|
# update it instead to avoid old urls from becoming invalid
|
|
128
127
|
# - careful with unmatched_actual being huge since it has all api resources
|
|
129
|
-
# - don't do it when
|
|
130
|
-
# -
|
|
131
|
-
# - when using a filter and updating the kennel_id of an existing item, old and new must be in the filter
|
|
128
|
+
# - don't do it when update is not allowed
|
|
129
|
+
# - when using a filter and updating the kennel_id of an existing item, old and new must be in the filter (PROJECT= works, but not TRACKING_ID)
|
|
132
130
|
def convert_replace_into_update!(matching, unmatched_actual, unmatched_expected)
|
|
133
131
|
unmatched_expected.reject! do |e|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
break [field, value]
|
|
137
|
-
end
|
|
138
|
-
raise unless e_field # uncovered: should never happen ...
|
|
139
|
-
# TODO: ideally reuse invalid_update! logic and just put the fields somewhere
|
|
140
|
-
e_monitor_type = e.as_json[:type]
|
|
141
|
-
e_dashboard_layout_type = e.as_json[:layout_type]
|
|
142
|
-
|
|
132
|
+
# find actual by title
|
|
133
|
+
e_field, e_value = title_field_and_value(e)
|
|
143
134
|
actual = unmatched_actual.detect do |a|
|
|
144
135
|
a[:klass].api_resource == e.class.api_resource &&
|
|
145
|
-
a[e_field] == e_value
|
|
146
|
-
a[:type] == e_monitor_type &&
|
|
147
|
-
a[:layout_type] == e_dashboard_layout_type
|
|
136
|
+
a[e_field] == e_value
|
|
148
137
|
end
|
|
149
|
-
|
|
138
|
+
|
|
139
|
+
# keep unmatched if we could not find or can't update
|
|
140
|
+
next false if !actual || e.allowed_update_error(actual)
|
|
150
141
|
|
|
151
142
|
# add as update and remove from unmatched
|
|
152
143
|
unmatched_actual.delete(actual)
|
|
@@ -156,10 +147,18 @@ module Kennel
|
|
|
156
147
|
end
|
|
157
148
|
end
|
|
158
149
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
150
|
+
def title_field_and_value(e)
|
|
151
|
+
Kennel::Models::Record::TITLE_FIELDS.detect do |field|
|
|
152
|
+
next unless (value = e.as_json[field])
|
|
153
|
+
return [field, value]
|
|
154
|
+
end
|
|
155
|
+
raise # uncovered: should never happen ...
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# fill details of things we need to compare, so diff works even though we cannot mass-fetch definitions
|
|
159
|
+
def fill_details!(matching)
|
|
160
|
+
dashboards = matching.filter_map { |e, a| a if e && e.class.api_resource == "dashboard" }
|
|
161
|
+
@api.fill_details! "dashboard", dashboards
|
|
163
162
|
end
|
|
164
163
|
|
|
165
164
|
def validate_expected_id_not_missing(expected)
|
|
@@ -175,14 +174,6 @@ module Kennel
|
|
|
175
174
|
end
|
|
176
175
|
end
|
|
177
176
|
|
|
178
|
-
# We've already validated the desired objects ('generated') in isolation.
|
|
179
|
-
# Now that we have made the plan, we can perform some more validation.
|
|
180
|
-
def validate_changes
|
|
181
|
-
@plan.updates.each do |item|
|
|
182
|
-
item.expected.validate_update!(item.diff)
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
|
|
186
177
|
def filter_actual!(actual)
|
|
187
178
|
return unless filter.filtering? # minor optimization
|
|
188
179
|
|
data/lib/kennel/tasks.rb
CHANGED
data/lib/kennel/utils.rb
CHANGED
|
@@ -24,13 +24,14 @@ module Kennel
|
|
|
24
24
|
workers = Array.new(threads).map do
|
|
25
25
|
Thread.new do
|
|
26
26
|
loop do
|
|
27
|
-
item, i = work.
|
|
27
|
+
item, i = work.shift
|
|
28
28
|
break unless i
|
|
29
29
|
done[i] =
|
|
30
30
|
begin
|
|
31
31
|
yield item
|
|
32
32
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
33
|
-
work.clear
|
|
33
|
+
work.clear # prevent new work
|
|
34
|
+
(workers - [Thread.current]).each(&:kill) # stop ongoing work
|
|
34
35
|
e
|
|
35
36
|
end
|
|
36
37
|
end
|
data/lib/kennel/version.rb
CHANGED
data/lib/kennel.rb
CHANGED
|
@@ -56,7 +56,7 @@ module Kennel
|
|
|
56
56
|
self.err = $stderr
|
|
57
57
|
|
|
58
58
|
class Engine
|
|
59
|
-
attr_accessor :strict_imports
|
|
59
|
+
attr_accessor :strict_imports # TODO: rename to :enforce_expected_ids_exist
|
|
60
60
|
|
|
61
61
|
def initialize
|
|
62
62
|
@strict_imports = true
|
|
@@ -93,7 +93,9 @@ module Kennel
|
|
|
93
93
|
@syncer ||= begin
|
|
94
94
|
preload
|
|
95
95
|
Syncer.new(
|
|
96
|
-
api,
|
|
96
|
+
api,
|
|
97
|
+
expected: generated,
|
|
98
|
+
actual: definitions,
|
|
97
99
|
filter: filter,
|
|
98
100
|
strict_imports: strict_imports
|
|
99
101
|
)
|
|
@@ -129,7 +131,7 @@ module Kennel
|
|
|
129
131
|
end
|
|
130
132
|
end
|
|
131
133
|
|
|
132
|
-
# performance: this takes ~100ms on large codebases, tried rewriting with Set or Hash but it was slower
|
|
134
|
+
# performance: this takes ~100ms on large codebases, tried rewriting with Set or Hash, but it was slower
|
|
133
135
|
def validate_unique_tracking_ids(parts)
|
|
134
136
|
bad = parts.group_by(&:tracking_id).select { |_, same| same.size > 1 }
|
|
135
137
|
return if bad.empty?
|
|
@@ -143,7 +145,10 @@ module Kennel
|
|
|
143
145
|
def definitions(**kwargs)
|
|
144
146
|
@definitions ||= Progress.progress("Downloading definitions", **kwargs) do
|
|
145
147
|
Utils.parallel(Models::Record.subclasses) do |klass|
|
|
146
|
-
|
|
148
|
+
# lookup monitors without adding unnecessary downtime information
|
|
149
|
+
params = (klass.api_resource == "monitor" ? { with_downtimes: false } : {})
|
|
150
|
+
|
|
151
|
+
api.list(klass.api_resource, params)
|
|
147
152
|
end.flatten(1)
|
|
148
153
|
end
|
|
149
154
|
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: 2.
|
|
4
|
+
version: 2.8.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: 2025-
|
|
11
|
+
date: 2025-11-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: diff-lcs
|