kennel 1.123.0 → 1.124.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: f09678f23d63da04d4b5db81eaef471bc0aa437b62ac75082f7bd161f8cd38e4
4
- data.tar.gz: a570cc2617295581459a5977727049c243d492312554496f0b1beb432ad8efe8
3
+ metadata.gz: 31edba322106d5b2f1942c57640f19eff6a265ea970b5bb2cc7f4ef02d1889fd
4
+ data.tar.gz: f47474f8b7d745714fb49231755ed2bf41fae62cf8b5ff3817017c8cd8277713
5
5
  SHA512:
6
- metadata.gz: 560a1daae6cb797b0e53e06a694e5624667b5597d3d6fb5c783e0166154a8f594064d7bfc1f64fb541723e2b633130d4e8039a8caa3434145552a63114a88678
7
- data.tar.gz: '091c93ee61b87146c69e6f8782aab7a3cf2c349a03936f686a099eaca1a96d762193ee8385220fcc9d8c694edacf89aa57c6751e6e8a17bcffb37b1f913f9591'
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 lak into other parts of the code
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
@@ -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 = Kennel.send(:api).show("monitor", id)
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 the safer
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
@@ -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 "#{tracking_id} Remove \"-- #{MARKER_TEXT}\" line from #{self.class::TRACKING_FIELD} to copy a resource"
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
- @as_json ||= begin
134
- json = build_json
135
- (id = json.delete(:id)) && json[:id] = id
136
- validate_json(json) if validate
137
- json
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, "#{tracking_id} Datadog does not allow update of #{field} (#{old_value.inspect} -> #{new_value.inspect})"
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
- raise ValidationError, "#{tracking_id} #{message}"
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
- Plan = Struct.new(:noop?, :no_change, :create, :update, :delete, keyword_init: true)
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
- noop?: noop?,
36
- no_change: @no_change,
37
- create: @create,
38
- update: @update,
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
- update_log = []
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
- update_log << [:create, e.class.api_resource, id]
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
- update_log << [:update, e.class.api_resource, id]
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
- update_log << [:delete, klass.api_resource, id]
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
- Update.new(update_log: update_log)
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 Kennel.strict_imports
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
- if use_diff
238
- Kennel.out.puts " #{type}#{field}"
239
- Kennel.out.puts(diff(old, new).map { |l| " #{l}" })
240
- elsif (old + new).size > 100
241
- Kennel.out.puts " #{type}#{field}"
242
- Kennel.out.puts " #{old} ->"
243
- Kennel.out.puts " #{new}"
244
- else
245
- Kennel.out.puts " #{type}#{field} #{old} -> #{new}"
246
- end
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.send(:api)
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.send(:api), tag)
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.send(:api).list("monitor", monitor_tags: tag, group_states: "no data")
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.send(:api)).import(resource, id)
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.send(:api)
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.send(:api).show(resource, id)
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
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Kennel
3
- VERSION = "1.123.0"
3
+ VERSION = "1.124.0"
4
4
  end
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
- ValidationError = Class.new(RuntimeError)
44
- UnresolvableIdError = Class.new(RuntimeError)
45
- DisallowedUpdateError = Class.new(RuntimeError)
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
- include Kennel::Compatibility
47
+ class << self
48
+ attr_accessor :out, :err
49
+ end
48
50
 
49
- UpdateResult = Struct.new(:plan, :update, keyword_init: true)
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 :out, :err, :strict_imports
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
- the_plan = syncer.plan
72
- the_update = syncer.update if syncer.confirm
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(ENV.fetch("DATADOG_APP_KEY"), ENV.fetch("DATADOG_API_KEY"))
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 "Generating" do
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
- parts = filter.filter_parts parts
105
+ filter.filter_parts parts
106
+ end
109
107
 
110
- parts.group_by(&:tracking_id).each do |tracking_id, same|
111
- next if same.size == 1
112
- raise <<~ERROR
113
- #{tracking_id} is defined #{same.size} times
114
- use a different `kennel_id` when defining multiple projects/monitors/dashboards to avoid this conflict
115
- ERROR
116
- end
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, &:as_json)
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.123.0
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-10-21 00:00:00.000000000 Z
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
@@ -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