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 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