kennel 1.114.1 → 1.116.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Readme.md +20 -5
- data/lib/kennel/models/project.rb +9 -0
- data/lib/kennel/progress.rb +3 -3
- data/lib/kennel/syncer.rb +18 -10
- data/lib/kennel/utils.rb +2 -2
- data/lib/kennel/version.rb +1 -1
- data/lib/kennel.rb +40 -22
- data/template/Readme.md +20 -5
- 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: 49677de3c690744756d70810dbe71c49cb0313014c5c325af21aa161463738bc
|
4
|
+
data.tar.gz: c52c465238c8043ba7a735712dcaf73edd589875b680b7f45f235477c696cff2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50968ff4039145263ea380d790ebd8d757afb96bd2966a8b7dddf5a58373dbf3fd2e13f0576ad44bf42ceecd7086095865d4edc7c85bc5df1b1f9cc7b9757638
|
7
|
+
data.tar.gz: 7facba1805b0d2690b4081e18d46d314161544326a8e2e772bd17284c9065d7a8d50431690ca7562425510b9a12db70726453e3a4296aac6cbfd00d3e6954172
|
data/Readme.md
CHANGED
@@ -289,23 +289,27 @@ Remove the code that created the resource. The next update will delete it (see a
|
|
289
289
|
```
|
290
290
|
|
291
291
|
### Updating existing resources with id
|
292
|
-
|
293
292
|
Setting `id` makes kennel take over a manually created datadog resource.
|
294
293
|
When manually creating to import, it is best to remove the `id` and delete the manually created resource.
|
295
294
|
|
296
295
|
When an `id` is set and the original resource is deleted, kennel will fail to update,
|
297
296
|
removing the `id` will cause kennel to create a new resource in datadog.
|
298
297
|
|
299
|
-
### Organizing many
|
300
|
-
|
298
|
+
### Organizing projects with many resources
|
299
|
+
When project files get too long, this structure can keep things bite-sized.
|
301
300
|
|
302
|
-
Use this class structure to keep things organized:
|
303
301
|
```Ruby
|
304
302
|
# projects/project_a/base.rb
|
305
303
|
module ProjectA
|
306
304
|
class Base < Kennel::Models::Project
|
307
305
|
defaults(
|
308
306
|
kennel_id: -> { "project_a" },
|
307
|
+
parts: -> {
|
308
|
+
[
|
309
|
+
Monitors::FooAlert.new(self),
|
310
|
+
...
|
311
|
+
]
|
312
|
+
}
|
309
313
|
...
|
310
314
|
|
311
315
|
# projects/project_a/monitors/foo_alert.rb
|
@@ -315,6 +319,17 @@ module ProjectA
|
|
315
319
|
...
|
316
320
|
```
|
317
321
|
|
322
|
+
### Updating a single project or resource
|
323
|
+
|
324
|
+
- Use `PROJECT=<kennel_id>` for single project:
|
325
|
+
|
326
|
+
Use the projects `kennel_id` (and if none is set then snake_case of the class name including modules)
|
327
|
+
to refer to the project. For example for `class ProjectA` use `PROJECT=project_a` but for `Foo::ProjectA` use `foo_project_a`.
|
328
|
+
|
329
|
+
- Use `TRACKING_ID=<project-kennel_id>:<resource-kennel_id>` for single resource:
|
330
|
+
|
331
|
+
Use the project kennel_id and the resources kennel_id, for example `class ProjectA` and `FooAlert` would give `project_a:foo_alert`.
|
332
|
+
|
318
333
|
### Skipping validations
|
319
334
|
Some validations might be too strict for your usecase or just wrong, please [open an issue](https://github.com/grosser/kennel/issues) and
|
320
335
|
to unblock use the `validate: -> { false }` option.
|
@@ -335,7 +350,7 @@ so they can be created in a single update and can be re-created if any of them i
|
|
335
350
|
|
336
351
|
### Debugging changes locally
|
337
352
|
- rebase on updated `master` to not undo other changes
|
338
|
-
- figure out project name by converting the class name to
|
353
|
+
- figure out project name by converting the class name to snake_case
|
339
354
|
- run `PROJECT=foo bundle exec rake kennel:update_datadog` to test changes for a single project (monitors: remove mentions while debugging to avoid alert spam)
|
340
355
|
- use `PROJECT=foo,bar,...` for multiple projects
|
341
356
|
|
@@ -19,12 +19,21 @@ module Kennel
|
|
19
19
|
|
20
20
|
def validated_parts
|
21
21
|
all = parts
|
22
|
+
unless all.is_a?(Array) && all.all? { |part| part.is_a?(Record) }
|
23
|
+
invalid! "#parts must return an array of Records"
|
24
|
+
end
|
25
|
+
|
22
26
|
validate_parts(all)
|
23
27
|
all
|
24
28
|
end
|
25
29
|
|
26
30
|
private
|
27
31
|
|
32
|
+
# let users know which project/resource failed when something happens during diffing where the backtrace is hidden
|
33
|
+
def invalid!(message)
|
34
|
+
raise ValidationError, "#{kennel_id} #{message}"
|
35
|
+
end
|
36
|
+
|
28
37
|
# hook for users to add custom validations via `prepend`
|
29
38
|
def validate_parts(parts)
|
30
39
|
end
|
data/lib/kennel/progress.rb
CHANGED
@@ -7,12 +7,12 @@ module Kennel
|
|
7
7
|
def self.progress(name)
|
8
8
|
Kennel.err.print "#{name} ... "
|
9
9
|
|
10
|
-
animation = "-\\|/"
|
11
|
-
count = 0
|
12
10
|
stop = false
|
13
11
|
result = nil
|
14
12
|
|
15
13
|
spinner = Thread.new do
|
14
|
+
animation = "-\\|/"
|
15
|
+
count = 0
|
16
16
|
loop do
|
17
17
|
break if stop
|
18
18
|
Kennel.err.print animation[count % animation.size]
|
@@ -30,7 +30,7 @@ module Kennel
|
|
30
30
|
|
31
31
|
result
|
32
32
|
ensure
|
33
|
-
stop = true
|
33
|
+
stop = true # make thread stop without killing it
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
data/lib/kennel/syncer.rb
CHANGED
@@ -4,9 +4,10 @@ module Kennel
|
|
4
4
|
DELETE_ORDER = ["dashboard", "slo", "monitor", "synthetics/tests"].freeze # dashboards references monitors + slos, slos reference monitors
|
5
5
|
LINE_UP = "\e[1A\033[K" # go up and clear
|
6
6
|
|
7
|
-
def initialize(api, expected, project_filter: nil)
|
7
|
+
def initialize(api, expected, project_filter: nil, tracking_id_filter: nil)
|
8
8
|
@api = api
|
9
9
|
@project_filter = project_filter
|
10
|
+
@tracking_id_filter = tracking_id_filter
|
10
11
|
@expected = Set.new expected # need set to speed up deletion
|
11
12
|
calculate_diff
|
12
13
|
validate_plan
|
@@ -105,7 +106,7 @@ module Kennel
|
|
105
106
|
|
106
107
|
Progress.progress "Diffing" do
|
107
108
|
populate_id_map @expected, actual
|
108
|
-
|
109
|
+
filter_actual! actual
|
109
110
|
resolve_linked_tracking_ids! @expected # resolve dependencies to avoid diff
|
110
111
|
|
111
112
|
@expected.each(&:add_tracking_id) # avoid diff with actual
|
@@ -261,11 +262,12 @@ module Kennel
|
|
261
262
|
next unless tracking_id = a.fetch(:tracking_id)
|
262
263
|
|
263
264
|
# ignore when deleted from the codebase
|
264
|
-
# (when running with
|
265
|
+
# (when running with filters we cannot see the other resources in the codebase)
|
265
266
|
api_resource = a.fetch(:klass).api_resource
|
266
267
|
next if
|
267
268
|
!@id_map.get(api_resource, tracking_id) &&
|
268
|
-
(!project_prefixes || tracking_id.start_with?(*project_prefixes))
|
269
|
+
(!project_prefixes || tracking_id.start_with?(*project_prefixes)) &&
|
270
|
+
(!@tracking_id_filter || @tracking_id_filter.include?(tracking_id))
|
269
271
|
|
270
272
|
@id_map.set(api_resource, tracking_id, a.fetch(:id))
|
271
273
|
if a[:klass].api_resource == "synthetics/tests"
|
@@ -278,12 +280,18 @@ module Kennel
|
|
278
280
|
list.each { |e| e.resolve_linked_tracking_ids!(@id_map, force: force) }
|
279
281
|
end
|
280
282
|
|
281
|
-
def
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
283
|
+
def filter_actual!(actual)
|
284
|
+
if @tracking_id_filter
|
285
|
+
actual.select! do |a|
|
286
|
+
tracking_id = a.fetch(:tracking_id)
|
287
|
+
!tracking_id || @tracking_id_filter.include?(tracking_id)
|
288
|
+
end
|
289
|
+
elsif @project_filter
|
290
|
+
project_prefixes = @project_filter.map { |p| "#{p}:" }
|
291
|
+
actual.select! do |a|
|
292
|
+
tracking_id = a.fetch(:tracking_id)
|
293
|
+
!tracking_id || tracking_id.start_with?(*project_prefixes)
|
294
|
+
end
|
287
295
|
end
|
288
296
|
end
|
289
297
|
end
|
data/lib/kennel/utils.rb
CHANGED
@@ -113,7 +113,7 @@ module Kennel
|
|
113
113
|
done[i] =
|
114
114
|
begin
|
115
115
|
yield item
|
116
|
-
rescue
|
116
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
117
117
|
work.clear
|
118
118
|
e
|
119
119
|
end
|
@@ -121,7 +121,7 @@ module Kennel
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
workers.each(&:join)
|
124
|
-
done.each { |d| raise d if d.is_a?(
|
124
|
+
done.each { |d| raise d if d.is_a?(Exception) }
|
125
125
|
end
|
126
126
|
|
127
127
|
def natural_order(name)
|
data/lib/kennel/version.rb
CHANGED
data/lib/kennel.rb
CHANGED
@@ -65,13 +65,22 @@ module Kennel
|
|
65
65
|
|
66
66
|
def store(parts)
|
67
67
|
Progress.progress "Storing" do
|
68
|
-
old = Dir[
|
68
|
+
old = Dir[[
|
69
|
+
"generated",
|
70
|
+
if project_filter || tracking_id_filter
|
71
|
+
[
|
72
|
+
"{" + (project_filter || ["*"]).join(",") + "}",
|
73
|
+
"{" + (tracking_id_filter || ["*"]).join(",") + "}.json"
|
74
|
+
]
|
75
|
+
else
|
76
|
+
"**"
|
77
|
+
end
|
78
|
+
].join("/")]
|
69
79
|
used = []
|
70
80
|
|
71
81
|
Utils.parallel(parts, max: 2) do |part|
|
72
82
|
path = "generated/#{part.tracking_id.tr("/", ":").sub(":", "/")}.json"
|
73
|
-
used
|
74
|
-
used << path
|
83
|
+
used.concat [File.dirname(path), path] # only 1 level of sub folders, so this is safe
|
75
84
|
payload = part.as_json.merge(api_resource: part.class.api_resource)
|
76
85
|
write_file_if_necessary(path, JSON.pretty_generate(payload) << "\n")
|
77
86
|
end
|
@@ -94,7 +103,7 @@ module Kennel
|
|
94
103
|
end
|
95
104
|
|
96
105
|
def syncer
|
97
|
-
@syncer ||= Syncer.new(api, generated, project_filter: project_filter)
|
106
|
+
@syncer ||= Syncer.new(api, generated, project_filter: project_filter, tracking_id_filter: tracking_id_filter)
|
98
107
|
end
|
99
108
|
|
100
109
|
def api
|
@@ -105,22 +114,12 @@ module Kennel
|
|
105
114
|
@generated ||= begin
|
106
115
|
Progress.progress "Generating" do
|
107
116
|
load_all
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
if filter
|
115
|
-
known << kennel_id
|
116
|
-
next [] unless filter.include?(kennel_id)
|
117
|
-
end
|
118
|
-
project.validated_parts
|
119
|
-
end.flatten(1)
|
120
|
-
|
121
|
-
if filter && parts.empty?
|
122
|
-
raise "#{filter.join(", ")} does not match any projects, try any of these:\n#{known.uniq.sort.join("\n")}"
|
123
|
-
end
|
117
|
+
|
118
|
+
projects = Models::Project.recursive_subclasses.map(&:new)
|
119
|
+
filter_resources!(projects, :kennel_id, project_filter, "projects", "PROJECT")
|
120
|
+
|
121
|
+
parts = Utils.parallel(projects, &:validated_parts).flatten(1)
|
122
|
+
filter_resources!(parts, :tracking_id, tracking_id_filter, "resources", "TRACKING_ID")
|
124
123
|
|
125
124
|
parts.group_by(&:tracking_id).each do |tracking_id, same|
|
126
125
|
next if same.size == 1
|
@@ -139,7 +138,25 @@ module Kennel
|
|
139
138
|
end
|
140
139
|
|
141
140
|
def project_filter
|
142
|
-
ENV["PROJECT"]
|
141
|
+
raise "either use PROJECT= or TRACKING_ID=" if ENV["PROJECT"] && ENV["TRACKING_ID"]
|
142
|
+
ENV["PROJECT"]&.split(",") || tracking_id_filter&.map { |id| id.split(":", 2).first }
|
143
|
+
end
|
144
|
+
|
145
|
+
def tracking_id_filter
|
146
|
+
(tracking_id = ENV["TRACKING_ID"]) && tracking_id.split(",")
|
147
|
+
end
|
148
|
+
|
149
|
+
def filter_resources!(resources, by, against, name, env)
|
150
|
+
return unless against
|
151
|
+
|
152
|
+
before = resources.dup
|
153
|
+
resources.select! { |p| against.include?(p.send(by)) }
|
154
|
+
return if resources.size == against.size
|
155
|
+
|
156
|
+
raise <<~MSG.rstrip
|
157
|
+
#{env}=#{against.join(",")} matched #{resources.size} #{name}, try any of these:
|
158
|
+
#{before.map(&by).sort.uniq.join("\n")}
|
159
|
+
MSG
|
143
160
|
end
|
144
161
|
|
145
162
|
def load_all
|
@@ -147,8 +164,9 @@ module Kennel
|
|
147
164
|
Dir.exist?("teams") && loader.push_dir("teams", namespace: Teams)
|
148
165
|
Dir.exist?("parts") && loader.push_dir("parts")
|
149
166
|
loader.setup
|
167
|
+
loader.eager_load # TODO: this should not be needed but we see hanging CI processes when it's not added
|
150
168
|
|
151
|
-
# TODO: also
|
169
|
+
# TODO: also auto-load projects and update expected path too
|
152
170
|
["projects"].each do |folder|
|
153
171
|
Dir["#{folder}/**/*.rb"].sort.each { |f| require "./#{f}" }
|
154
172
|
end
|
data/template/Readme.md
CHANGED
@@ -271,23 +271,27 @@ Remove the code that created the resource. The next update will delete it (see a
|
|
271
271
|
```
|
272
272
|
|
273
273
|
### Updating existing resources with id
|
274
|
-
|
275
274
|
Setting `id` makes kennel take over a manually created datadog resource.
|
276
275
|
When manually creating to import, it is best to remove the `id` and delete the manually created resource.
|
277
276
|
|
278
277
|
When an `id` is set and the original resource is deleted, kennel will fail to update,
|
279
278
|
removing the `id` will cause kennel to create a new resource in datadog.
|
280
279
|
|
281
|
-
### Organizing many
|
282
|
-
|
280
|
+
### Organizing projects with many resources
|
281
|
+
When project files get too long, this structure can keep things bite-sized.
|
283
282
|
|
284
|
-
Use this class structure to keep things organized:
|
285
283
|
```Ruby
|
286
284
|
# projects/project_a/base.rb
|
287
285
|
module ProjectA
|
288
286
|
class Base < Kennel::Models::Project
|
289
287
|
defaults(
|
290
288
|
kennel_id: -> { "project_a" },
|
289
|
+
parts: -> {
|
290
|
+
[
|
291
|
+
Monitors::FooAlert.new(self),
|
292
|
+
...
|
293
|
+
]
|
294
|
+
}
|
291
295
|
...
|
292
296
|
|
293
297
|
# projects/project_a/monitors/foo_alert.rb
|
@@ -297,6 +301,17 @@ module ProjectA
|
|
297
301
|
...
|
298
302
|
```
|
299
303
|
|
304
|
+
### Updating a single project or resource
|
305
|
+
|
306
|
+
- Use `PROJECT=<kennel_id>` for single project:
|
307
|
+
|
308
|
+
Use the projects `kennel_id` (and if none is set then snake_case of the class name including modules)
|
309
|
+
to refer to the project. For example for `class ProjectA` use `PROJECT=project_a` but for `Foo::ProjectA` use `foo_project_a`.
|
310
|
+
|
311
|
+
- Use `TRACKING_ID=<project-kennel_id>:<resource-kennel_id>` for single resource:
|
312
|
+
|
313
|
+
Use the project kennel_id and the resources kennel_id, for example `class ProjectA` and `FooAlert` would give `project_a:foo_alert`.
|
314
|
+
|
300
315
|
### Skipping validations
|
301
316
|
Some validations might be too strict for your usecase or just wrong, please [open an issue](https://github.com/grosser/kennel/issues) and
|
302
317
|
to unblock use the `validate: -> { false }` option.
|
@@ -317,7 +332,7 @@ so they can be created in a single update and can be re-created if any of them i
|
|
317
332
|
|
318
333
|
### Debugging changes locally
|
319
334
|
- rebase on updated `master` to not undo other changes
|
320
|
-
- figure out project name by converting the class name to
|
335
|
+
- figure out project name by converting the class name to snake_case
|
321
336
|
- run `PROJECT=foo bundle exec rake kennel:update_datadog` to test changes for a single project (monitors: remove mentions while debugging to avoid alert spam)
|
322
337
|
- use `PROJECT=foo,bar,...` for multiple projects
|
323
338
|
|
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.116.1
|
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-08-
|
11
|
+
date: 2022-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|