kennel 1.131.0 → 1.132.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/filter.rb +17 -2
- data/lib/kennel/models/record.rb +2 -1
- data/lib/kennel/parts_serializer.rb +9 -6
- data/lib/kennel/syncer/matched_expected.rb +44 -0
- data/lib/kennel/syncer/plan_displayer.rb +34 -0
- data/lib/kennel/syncer/resolver.rb +81 -0
- data/lib/kennel/syncer/types.rb +73 -0
- data/lib/kennel/syncer.rb +14 -232
- data/lib/kennel/version.rb +1 -1
- data/lib/kennel.rb +2 -3
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d0c423b6658afcd8aa09a7e80169f85312540c03aad0bc8c3543e76c5a9420b
|
4
|
+
data.tar.gz: 71ab44e7dc1ed76114f6a3aadfd70d79d905d673d0090f85f96e40e6c8967620
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4b6217e61f1282dd003bd2feea590b92f7dd8ddd2c7e9efc693ee58d0735baad7ea69d260ad376e581a7ff4b6364c51ddd2d42defd978928326183e5c8c5de3
|
7
|
+
data.tar.gz: ed60a47513aa4db4e1a1082cee541dbf9a1b345a512a058810eb4ad8763cc0505d3158b91f5b9df97e33a788cde84511fa0ad8b4de19aa14ca907f6944de8a2b
|
data/lib/kennel/filter.rb
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
module Kennel
|
4
4
|
class Filter
|
5
|
-
attr_reader :project_filter, :tracking_id_filter
|
6
|
-
|
7
5
|
def initialize
|
8
6
|
# build early so we fail fast on invalid user input
|
9
7
|
@tracking_id_filter = build_tracking_id_filter
|
@@ -18,8 +16,25 @@ module Kennel
|
|
18
16
|
filter_resources(parts, :tracking_id, tracking_id_filter, "resources", "TRACKING_ID")
|
19
17
|
end
|
20
18
|
|
19
|
+
def filtering?
|
20
|
+
!project_filter.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def matches_project_id?(project_id)
|
24
|
+
!filtering? || project_filter.include?(project_id)
|
25
|
+
end
|
26
|
+
|
27
|
+
def matches_tracking_id?(tracking_id)
|
28
|
+
return true unless filtering?
|
29
|
+
return tracking_id_filter.include?(tracking_id) if tracking_id_filter
|
30
|
+
|
31
|
+
project_filter.include?(tracking_id.split(":").first)
|
32
|
+
end
|
33
|
+
|
21
34
|
private
|
22
35
|
|
36
|
+
attr_reader :project_filter, :tracking_id_filter
|
37
|
+
|
23
38
|
def build_project_filter
|
24
39
|
project_names = ENV["PROJECT"]&.split(",")&.sort&.uniq
|
25
40
|
tracking_project_names = tracking_id_filter&.map { |id| id.split(":", 2).first }&.sort&.uniq
|
data/lib/kennel/models/record.rb
CHANGED
@@ -35,7 +35,8 @@ module Kennel
|
|
35
35
|
:klass, :tracking_id # added by syncer.rb
|
36
36
|
].freeze
|
37
37
|
ALLOWED_KENNEL_ID_CHARS = "a-zA-Z_\\d.-"
|
38
|
-
|
38
|
+
ALLOWED_KENNEL_ID_SEGMENT = /[#{ALLOWED_KENNEL_ID_CHARS}]+/
|
39
|
+
ALLOWED_KENNEL_ID_FULL = "#{ALLOWED_KENNEL_ID_SEGMENT}:#{ALLOWED_KENNEL_ID_SEGMENT}".freeze
|
39
40
|
ALLOWED_KENNEL_ID_REGEX = /\A#{ALLOWED_KENNEL_ID_FULL}\z/
|
40
41
|
|
41
42
|
settings :id, :kennel_id
|
@@ -35,13 +35,16 @@ module Kennel
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def existing_files_and_folders
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
38
|
+
paths = Dir["generated/**/*"]
|
39
|
+
|
40
|
+
if filter.filtering?
|
41
|
+
paths.select! do |path|
|
42
|
+
tracking_id = path.split("/")[1..2].to_a.join(":")
|
43
|
+
filter.matches_tracking_id?(tracking_id)
|
44
|
+
end
|
44
45
|
end
|
46
|
+
|
47
|
+
paths
|
45
48
|
end
|
46
49
|
|
47
50
|
def path_for_tracking_id(tracking_id)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kennel
|
4
|
+
class Syncer
|
5
|
+
module MatchedExpected
|
6
|
+
class << self
|
7
|
+
def partition(expected, actual)
|
8
|
+
lookup_map = matching_expected_lookup_map(expected)
|
9
|
+
unmatched_expected = Set.new(expected) # for efficient deletion
|
10
|
+
unmatched_actual = []
|
11
|
+
matched = []
|
12
|
+
actual.each do |a|
|
13
|
+
e = matching_expected(a, lookup_map)
|
14
|
+
if e && unmatched_expected.delete?(e)
|
15
|
+
matched << [e, a]
|
16
|
+
else
|
17
|
+
unmatched_actual << a
|
18
|
+
end
|
19
|
+
end.compact
|
20
|
+
[matched, unmatched_expected.to_a, unmatched_actual]
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# index list by all the thing we look up by: tracking id and actual id
|
26
|
+
def matching_expected_lookup_map(expected)
|
27
|
+
expected.each_with_object({}) do |e, all|
|
28
|
+
keys = [e.tracking_id]
|
29
|
+
keys << "#{e.class.api_resource}:#{e.id}" if e.id
|
30
|
+
keys.compact.each do |key|
|
31
|
+
raise "Lookup #{key} is duplicated" if all[key]
|
32
|
+
all[key] = e
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def matching_expected(a, map)
|
38
|
+
klass = a.fetch(:klass)
|
39
|
+
map["#{klass.api_resource}:#{a.fetch(:id)}"] || map[a.fetch(:tracking_id)]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kennel
|
4
|
+
class Syncer
|
5
|
+
class PlanDisplayer
|
6
|
+
def initialize
|
7
|
+
@attribute_differ = AttributeDiffer.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def display(internal_plan)
|
11
|
+
Kennel.out.puts "Plan:"
|
12
|
+
if internal_plan.empty?
|
13
|
+
Kennel.out.puts Console.color(:green, "Nothing to do")
|
14
|
+
else
|
15
|
+
print_changes "Create", internal_plan.creates, :green
|
16
|
+
print_changes "Update", internal_plan.updates, :yellow
|
17
|
+
print_changes "Delete", internal_plan.deletes, :red
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def print_changes(step, list, color)
|
24
|
+
return if list.empty?
|
25
|
+
list.each do |item|
|
26
|
+
Kennel.out.puts Console.color(color, "#{step} #{item.api_resource} #{item.tracking_id}")
|
27
|
+
if item.class::TYPE == :update
|
28
|
+
item.diff.each { |args| Kennel.out.puts @attribute_differ.format(*args) } # only for update
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../id_map"
|
4
|
+
|
5
|
+
module Kennel
|
6
|
+
class Syncer
|
7
|
+
class Resolver
|
8
|
+
def initialize(expected:, filter:)
|
9
|
+
@id_map = IdMap.new
|
10
|
+
@filter = filter
|
11
|
+
|
12
|
+
# mark everything as new
|
13
|
+
expected.each do |e|
|
14
|
+
id_map.set(e.class.api_resource, e.tracking_id, IdMap::NEW)
|
15
|
+
if e.class.api_resource == "synthetics/tests"
|
16
|
+
id_map.set(Kennel::Models::Monitor.api_resource, e.tracking_id, IdMap::NEW)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_actual(actual)
|
22
|
+
# override resources that exist with their id
|
23
|
+
actual.each do |a|
|
24
|
+
# ignore when not managed by kennel
|
25
|
+
next unless tracking_id = a.fetch(:tracking_id)
|
26
|
+
|
27
|
+
# ignore when deleted from the codebase
|
28
|
+
# (when running with filters we cannot see the other resources in the codebase)
|
29
|
+
api_resource = a.fetch(:klass).api_resource
|
30
|
+
next if !id_map.get(api_resource, tracking_id) && filter.matches_tracking_id?(tracking_id)
|
31
|
+
|
32
|
+
id_map.set(api_resource, tracking_id, a.fetch(:id))
|
33
|
+
if a.fetch(:klass).api_resource == "synthetics/tests"
|
34
|
+
id_map.set(Kennel::Models::Monitor.api_resource, tracking_id, a.fetch(:monitor_id))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def resolve_as_much_as_possible(expected)
|
40
|
+
expected.each do |e|
|
41
|
+
e.resolve_linked_tracking_ids!(id_map, force: false)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# loop over items until everything is resolved or crash when we get stuck
|
46
|
+
# this solves cases like composite monitors depending on each other or monitor->monitor slo->slo monitor chains
|
47
|
+
def each_resolved(list)
|
48
|
+
list = list.dup
|
49
|
+
loop do
|
50
|
+
return if list.empty?
|
51
|
+
list.reject! do |item|
|
52
|
+
if resolved?(item.expected)
|
53
|
+
yield item
|
54
|
+
true
|
55
|
+
else
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end ||
|
59
|
+
assert_resolved(list[0].expected) # resolve something or show a circular dependency error
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
attr_reader :id_map, :filter
|
66
|
+
|
67
|
+
# TODO: optimize by storing an instance variable if already resolved
|
68
|
+
def resolved?(e)
|
69
|
+
assert_resolved e
|
70
|
+
true
|
71
|
+
rescue UnresolvableIdError
|
72
|
+
false
|
73
|
+
end
|
74
|
+
|
75
|
+
# raises UnresolvableIdError when not resolved
|
76
|
+
def assert_resolved(e)
|
77
|
+
e.resolve_linked_tracking_ids!(id_map, force: true)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kennel
|
4
|
+
class Syncer
|
5
|
+
module Types
|
6
|
+
class PlannedChange
|
7
|
+
def initialize(klass, tracking_id)
|
8
|
+
@klass = klass
|
9
|
+
@tracking_id = tracking_id
|
10
|
+
end
|
11
|
+
|
12
|
+
def api_resource
|
13
|
+
klass.api_resource
|
14
|
+
end
|
15
|
+
|
16
|
+
def url(id = nil)
|
17
|
+
klass.url(id || self.id)
|
18
|
+
end
|
19
|
+
|
20
|
+
def change(id = nil)
|
21
|
+
Change.new(self.class::TYPE, api_resource, tracking_id, id)
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :klass, :tracking_id
|
25
|
+
end
|
26
|
+
|
27
|
+
class PlannedCreate < PlannedChange
|
28
|
+
TYPE = :create
|
29
|
+
|
30
|
+
def initialize(expected)
|
31
|
+
super(expected.class, expected.tracking_id)
|
32
|
+
@expected = expected
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :expected
|
36
|
+
end
|
37
|
+
|
38
|
+
class PlannedUpdate < PlannedChange
|
39
|
+
TYPE = :update
|
40
|
+
|
41
|
+
def initialize(expected, actual, diff)
|
42
|
+
super(expected.class, expected.tracking_id)
|
43
|
+
@expected = expected
|
44
|
+
@actual = actual
|
45
|
+
@diff = diff
|
46
|
+
@id = actual.fetch(:id)
|
47
|
+
end
|
48
|
+
|
49
|
+
def change
|
50
|
+
super(id)
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_reader :expected, :actual, :diff, :id
|
54
|
+
end
|
55
|
+
|
56
|
+
class PlannedDelete < PlannedChange
|
57
|
+
TYPE = :delete
|
58
|
+
|
59
|
+
def initialize(actual)
|
60
|
+
super(actual.fetch(:klass), actual.fetch(:tracking_id))
|
61
|
+
@actual = actual
|
62
|
+
@id = actual.fetch(:id)
|
63
|
+
end
|
64
|
+
|
65
|
+
def change
|
66
|
+
super(id)
|
67
|
+
end
|
68
|
+
|
69
|
+
attr_reader :actual, :id
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/kennel/syncer.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "./syncer/matched_expected"
|
4
|
+
require_relative "./syncer/plan_displayer"
|
5
|
+
require_relative "./syncer/resolver"
|
6
|
+
require_relative "./syncer/types"
|
7
|
+
|
3
8
|
module Kennel
|
4
9
|
class Syncer
|
5
10
|
DELETE_ORDER = ["dashboard", "slo", "monitor", "synthetics/tests"].freeze # dashboards references monitors + slos, slos reference monitors
|
@@ -15,13 +20,12 @@ module Kennel
|
|
15
20
|
|
16
21
|
Change = Struct.new(:type, :api_resource, :tracking_id, :id)
|
17
22
|
|
18
|
-
def initialize(api, expected, actual, strict_imports: true
|
23
|
+
def initialize(api, expected, actual, filter:, strict_imports: true)
|
19
24
|
@api = api
|
20
25
|
@strict_imports = strict_imports
|
21
|
-
@
|
22
|
-
@tracking_id_filter = tracking_id_filter
|
26
|
+
@filter = filter
|
23
27
|
|
24
|
-
@resolver = Resolver.new(expected: expected,
|
28
|
+
@resolver = Resolver.new(expected: expected, filter: filter)
|
25
29
|
|
26
30
|
internal_plan = calculate_changes(expected: expected, actual: actual)
|
27
31
|
validate_changes(internal_plan)
|
@@ -81,7 +85,7 @@ module Kennel
|
|
81
85
|
|
82
86
|
private
|
83
87
|
|
84
|
-
attr_reader :resolver, :internal_plan
|
88
|
+
attr_reader :filter, :resolver, :internal_plan
|
85
89
|
|
86
90
|
def calculate_changes(expected:, actual:)
|
87
91
|
@warnings = []
|
@@ -101,7 +105,7 @@ module Kennel
|
|
101
105
|
# Refuse to "adopt" existing items into kennel while running with a filter (i.e. on a branch).
|
102
106
|
# Without this, we'd adopt an item, then the next CI run would delete it
|
103
107
|
# (instead of "unadopting" it).
|
104
|
-
e.add_tracking_id unless
|
108
|
+
e.add_tracking_id unless filter.filtering? && a.fetch(:tracking_id).nil?
|
105
109
|
id = a.fetch(:id)
|
106
110
|
diff = e.diff(a)
|
107
111
|
a[:id] = id
|
@@ -150,233 +154,11 @@ module Kennel
|
|
150
154
|
end
|
151
155
|
|
152
156
|
def filter_actual!(actual)
|
153
|
-
|
154
|
-
actual.select! do |a|
|
155
|
-
tracking_id = a.fetch(:tracking_id)
|
156
|
-
!tracking_id || @tracking_id_filter.include?(tracking_id)
|
157
|
-
end
|
158
|
-
elsif @project_filter
|
159
|
-
project_prefixes = @project_filter.map { |p| "#{p}:" }
|
160
|
-
actual.select! do |a|
|
161
|
-
tracking_id = a.fetch(:tracking_id)
|
162
|
-
!tracking_id || tracking_id.start_with?(*project_prefixes)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
class PlanDisplayer
|
168
|
-
def initialize
|
169
|
-
@attribute_differ = AttributeDiffer.new
|
170
|
-
end
|
171
|
-
|
172
|
-
def display(internal_plan)
|
173
|
-
Kennel.out.puts "Plan:"
|
174
|
-
if internal_plan.empty?
|
175
|
-
Kennel.out.puts Console.color(:green, "Nothing to do")
|
176
|
-
else
|
177
|
-
print_changes "Create", internal_plan.creates, :green
|
178
|
-
print_changes "Update", internal_plan.updates, :yellow
|
179
|
-
print_changes "Delete", internal_plan.deletes, :red
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
private
|
184
|
-
|
185
|
-
def print_changes(step, list, color)
|
186
|
-
return if list.empty?
|
187
|
-
list.each do |item|
|
188
|
-
Kennel.out.puts Console.color(color, "#{step} #{item.api_resource} #{item.tracking_id}")
|
189
|
-
if item.class::TYPE == :update
|
190
|
-
item.diff.each { |args| Kennel.out.puts @attribute_differ.format(*args) } # only for update
|
191
|
-
end
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
class Resolver
|
197
|
-
def initialize(expected:, project_filter:, tracking_id_filter:)
|
198
|
-
@id_map = IdMap.new
|
199
|
-
@project_filter = project_filter
|
200
|
-
@tracking_id_filter = tracking_id_filter
|
201
|
-
|
202
|
-
# mark everything as new
|
203
|
-
expected.each do |e|
|
204
|
-
id_map.set(e.class.api_resource, e.tracking_id, IdMap::NEW)
|
205
|
-
if e.class.api_resource == "synthetics/tests"
|
206
|
-
id_map.set(Kennel::Models::Monitor.api_resource, e.tracking_id, IdMap::NEW)
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
def add_actual(actual)
|
212
|
-
# override resources that exist with their id
|
213
|
-
project_prefixes = project_filter&.map { |p| "#{p}:" }
|
214
|
-
|
215
|
-
actual.each do |a|
|
216
|
-
# ignore when not managed by kennel
|
217
|
-
next unless tracking_id = a.fetch(:tracking_id)
|
218
|
-
|
219
|
-
# ignore when deleted from the codebase
|
220
|
-
# (when running with filters we cannot see the other resources in the codebase)
|
221
|
-
api_resource = a.fetch(:klass).api_resource
|
222
|
-
next if
|
223
|
-
!id_map.get(api_resource, tracking_id) &&
|
224
|
-
(!project_prefixes || tracking_id.start_with?(*project_prefixes)) &&
|
225
|
-
(!tracking_id_filter || tracking_id_filter.include?(tracking_id))
|
226
|
-
|
227
|
-
id_map.set(api_resource, tracking_id, a.fetch(:id))
|
228
|
-
if a.fetch(:klass).api_resource == "synthetics/tests"
|
229
|
-
id_map.set(Kennel::Models::Monitor.api_resource, tracking_id, a.fetch(:monitor_id))
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
def resolve_as_much_as_possible(expected)
|
235
|
-
expected.each do |e|
|
236
|
-
e.resolve_linked_tracking_ids!(id_map, force: false)
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
# loop over items until everything is resolved or crash when we get stuck
|
241
|
-
# this solves cases like composite monitors depending on each other or monitor->monitor slo->slo monitor chains
|
242
|
-
def each_resolved(list)
|
243
|
-
list = list.dup
|
244
|
-
loop do
|
245
|
-
return if list.empty?
|
246
|
-
list.reject! do |item|
|
247
|
-
if resolved?(item.expected)
|
248
|
-
yield item
|
249
|
-
true
|
250
|
-
else
|
251
|
-
false
|
252
|
-
end
|
253
|
-
end ||
|
254
|
-
assert_resolved(list[0].expected) # resolve something or show a circular dependency error
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
private
|
259
|
-
|
260
|
-
attr_reader :id_map, :project_filter, :tracking_id_filter
|
261
|
-
|
262
|
-
# TODO: optimize by storing an instance variable if already resolved
|
263
|
-
def resolved?(e)
|
264
|
-
assert_resolved e
|
265
|
-
true
|
266
|
-
rescue UnresolvableIdError
|
267
|
-
false
|
268
|
-
end
|
269
|
-
|
270
|
-
# raises UnresolvableIdError when not resolved
|
271
|
-
def assert_resolved(e)
|
272
|
-
e.resolve_linked_tracking_ids!(id_map, force: true)
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
module MatchedExpected
|
277
|
-
class << self
|
278
|
-
def partition(expected, actual)
|
279
|
-
lookup_map = matching_expected_lookup_map(expected)
|
280
|
-
unmatched_expected = Set.new(expected) # for efficient deletion
|
281
|
-
unmatched_actual = []
|
282
|
-
matched = []
|
283
|
-
actual.each do |a|
|
284
|
-
e = matching_expected(a, lookup_map)
|
285
|
-
if e && unmatched_expected.delete?(e)
|
286
|
-
matched << [e, a]
|
287
|
-
else
|
288
|
-
unmatched_actual << a
|
289
|
-
end
|
290
|
-
end.compact
|
291
|
-
[matched, unmatched_expected.to_a, unmatched_actual]
|
292
|
-
end
|
293
|
-
|
294
|
-
private
|
295
|
-
|
296
|
-
# index list by all the thing we look up by: tracking id and actual id
|
297
|
-
def matching_expected_lookup_map(expected)
|
298
|
-
expected.each_with_object({}) do |e, all|
|
299
|
-
keys = [e.tracking_id]
|
300
|
-
keys << "#{e.class.api_resource}:#{e.id}" if e.id
|
301
|
-
keys.compact.each do |key|
|
302
|
-
raise "Lookup #{key} is duplicated" if all[key]
|
303
|
-
all[key] = e
|
304
|
-
end
|
305
|
-
end
|
306
|
-
end
|
307
|
-
|
308
|
-
def matching_expected(a, map)
|
309
|
-
klass = a.fetch(:klass)
|
310
|
-
map["#{klass.api_resource}:#{a.fetch(:id)}"] || map[a.fetch(:tracking_id)]
|
311
|
-
end
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
module Types
|
316
|
-
class PlannedChange
|
317
|
-
def initialize(klass, tracking_id)
|
318
|
-
@klass = klass
|
319
|
-
@tracking_id = tracking_id
|
320
|
-
end
|
321
|
-
|
322
|
-
def api_resource
|
323
|
-
klass.api_resource
|
324
|
-
end
|
325
|
-
|
326
|
-
def url(id = nil)
|
327
|
-
klass.url(id || self.id)
|
328
|
-
end
|
329
|
-
|
330
|
-
def change(id = nil)
|
331
|
-
Change.new(self.class::TYPE, api_resource, tracking_id, id)
|
332
|
-
end
|
333
|
-
|
334
|
-
attr_reader :klass, :tracking_id
|
335
|
-
end
|
336
|
-
|
337
|
-
class PlannedCreate < PlannedChange
|
338
|
-
TYPE = :create
|
339
|
-
|
340
|
-
def initialize(expected)
|
341
|
-
super(expected.class, expected.tracking_id)
|
342
|
-
@expected = expected
|
343
|
-
end
|
344
|
-
|
345
|
-
attr_reader :expected
|
346
|
-
end
|
347
|
-
|
348
|
-
class PlannedUpdate < PlannedChange
|
349
|
-
TYPE = :update
|
350
|
-
|
351
|
-
def initialize(expected, actual, diff)
|
352
|
-
super(expected.class, expected.tracking_id)
|
353
|
-
@expected = expected
|
354
|
-
@actual = actual
|
355
|
-
@diff = diff
|
356
|
-
@id = actual.fetch(:id)
|
357
|
-
end
|
358
|
-
|
359
|
-
def change
|
360
|
-
super(id)
|
361
|
-
end
|
362
|
-
|
363
|
-
attr_reader :expected, :actual, :diff, :id
|
364
|
-
end
|
365
|
-
|
366
|
-
class PlannedDelete < PlannedChange
|
367
|
-
TYPE = :delete
|
368
|
-
|
369
|
-
def initialize(actual)
|
370
|
-
super(actual.fetch(:klass), actual.fetch(:tracking_id))
|
371
|
-
@actual = actual
|
372
|
-
@id = actual.fetch(:id)
|
373
|
-
end
|
374
|
-
|
375
|
-
def change
|
376
|
-
super(id)
|
377
|
-
end
|
157
|
+
return unless filter.filtering? # minor optimization
|
378
158
|
|
379
|
-
|
159
|
+
actual.select! do |a|
|
160
|
+
tracking_id = a.fetch(:tracking_id)
|
161
|
+
tracking_id.nil? || filter.matches_tracking_id?(tracking_id)
|
380
162
|
end
|
381
163
|
end
|
382
164
|
end
|
data/lib/kennel/version.rb
CHANGED
data/lib/kennel.rb
CHANGED
@@ -92,9 +92,8 @@ module Kennel
|
|
92
92
|
preload
|
93
93
|
Syncer.new(
|
94
94
|
api, generated, definitions,
|
95
|
-
|
96
|
-
|
97
|
-
tracking_id_filter: filter.tracking_id_filter
|
95
|
+
filter: filter,
|
96
|
+
strict_imports: strict_imports
|
98
97
|
)
|
99
98
|
end
|
100
99
|
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.132.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: 2023-01-
|
11
|
+
date: 2023-01-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: diff-lcs
|
@@ -112,6 +112,10 @@ files:
|
|
112
112
|
- lib/kennel/string_utils.rb
|
113
113
|
- lib/kennel/subclass_tracking.rb
|
114
114
|
- lib/kennel/syncer.rb
|
115
|
+
- lib/kennel/syncer/matched_expected.rb
|
116
|
+
- lib/kennel/syncer/plan_displayer.rb
|
117
|
+
- lib/kennel/syncer/resolver.rb
|
118
|
+
- lib/kennel/syncer/types.rb
|
115
119
|
- lib/kennel/tasks.rb
|
116
120
|
- lib/kennel/template_variables.rb
|
117
121
|
- lib/kennel/unmuted_alerts.rb
|