kennel 1.89.0 → 1.91.1

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: bdedd0f9bbcb9abf873cd2f9be0414700f77d2ea864e609db79773d1b3193e8e
4
- data.tar.gz: 336875475665fac019c12daced64e474fb8e85e216e158d4328b62b287785e6a
3
+ metadata.gz: 9f3417e3f9c4f8b950c89047ed25c5f44a0da8e8c7f28667f33b549b2d6fadc3
4
+ data.tar.gz: 9fec0221be92d34d7ade923a9f92bee18e9f602d719c041e21fc274fb6d10c9f
5
5
  SHA512:
6
- metadata.gz: 0041e466ce433d82f9db8252829f8c64652815d7bf932ec93596d57af76dc13aaa9dfce963bbf82888e09d424937b597364954edc1cff14344143f0661e8e274
7
- data.tar.gz: a286f227e597686085049ef0227b2e36b26d33ef41a7d96d13a535af73e1530254de6bf68ead11ebdd0c394d1562766a91a478c794ee17f35c288a7f102d46b7
6
+ metadata.gz: b31a95230656072e45d260aa549010ad5add9313af68f2490ac3d3c2220b9fe525ee6af144a3ca988103503af7f4a15cdbeb52601e1ff0427a5809714807be73
7
+ data.tar.gz: 6754253f9cc10a00b663947860527a596308856e3beca48538eef1f19f9e02aa11c98ce981b5f7f7815bb177f4b329e971decf0dd8326ccc6a767d163c2970de
data/Readme.md CHANGED
@@ -52,6 +52,7 @@ end
52
52
  ```
53
53
 
54
54
  <!-- NOT IN template/Readme.md -->
55
+
55
56
  ## Installation
56
57
 
57
58
  - create a new private `kennel` repo for your organization (do not fork this repo)
@@ -293,9 +294,12 @@ https://foo.datadog.com/monitor/123
293
294
 
294
295
  <!-- NOT IN template/Readme.md -->
295
296
 
296
-
297
297
  ## Development
298
298
 
299
+ ### Benchmarking
300
+
301
+ Setting `FORCE_GET_CACHE=true` will cache all get requests, which makes benchmarking improvements more reliable.
302
+
299
303
  ### Integration testing
300
304
 
301
305
  ```Bash
data/lib/kennel.rb CHANGED
@@ -23,6 +23,7 @@ require "kennel/models/record"
23
23
  require "kennel/models/dashboard"
24
24
  require "kennel/models/monitor"
25
25
  require "kennel/models/slo"
26
+ require "kennel/models/synthetic_test"
26
27
 
27
28
  # settings
28
29
  require "kennel/models/project"
@@ -55,7 +56,7 @@ module Kennel
55
56
 
56
57
  def store(parts)
57
58
  Progress.progress "Storing" do
58
- old = Dir["generated/**/*"]
59
+ old = Dir["generated/#{project_filter || "**"}/*"]
59
60
  used = []
60
61
 
61
62
  Utils.parallel(parts, max: 2) do |part|
@@ -83,7 +84,7 @@ module Kennel
83
84
  end
84
85
 
85
86
  def syncer
86
- @syncer ||= Syncer.new(api, generated, project: ENV["PROJECT"])
87
+ @syncer ||= Syncer.new(api, generated, project: project_filter)
87
88
  end
88
89
 
89
90
  def api
@@ -94,9 +95,21 @@ module Kennel
94
95
  @generated ||= begin
95
96
  Progress.progress "Generating" do
96
97
  load_all
98
+ known = []
97
99
  parts = Models::Project.recursive_subclasses.flat_map do |project_class|
98
- project_class.new.validated_parts
100
+ project = project_class.new
101
+ kennel_id = project.kennel_id
102
+ if project_filter
103
+ known << kennel_id
104
+ next [] if kennel_id != project_filter
105
+ end
106
+ project.validated_parts
99
107
  end
108
+
109
+ if project_filter && parts.empty?
110
+ raise "#{project_filter} does not match any projects, try any of these:\n#{known.uniq.sort.join("\n")}"
111
+ end
112
+
100
113
  parts.group_by(&:tracking_id).each do |tracking_id, same|
101
114
  next if same.size == 1
102
115
  raise <<~ERROR
@@ -109,6 +122,10 @@ module Kennel
109
122
  end
110
123
  end
111
124
 
125
+ def project_filter
126
+ ENV["PROJECT"]
127
+ end
128
+
112
129
  def load_all
113
130
  ["teams", "parts", "projects"].each do |folder|
114
131
  Dir["#{folder}/**/*.rb"].sort.each { |f| require "./#{f}" }
data/lib/kennel/api.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ # encapsulates knowledge around how the api works
3
+ # especially 1-off weirdness that should not lak into other parts of the code
2
4
  module Kennel
3
- # encapsulates knowledge around how the api works
4
5
  class Api
5
6
  CACHE_FILE = "tmp/cache/details"
6
7
 
@@ -11,49 +12,55 @@ module Kennel
11
12
  end
12
13
 
13
14
  def show(api_resource, id, params = {})
14
- reply = request :get, "/api/v1/#{api_resource}/#{id}", params: params
15
- api_resource == "slo" ? reply[:data] : reply
15
+ response = request :get, "/api/v1/#{api_resource}/#{id}", params: params
16
+ response = response.fetch(:data) if api_resource == "slo"
17
+ response[:id] = response.delete(:public_id) if api_resource == "synthetics/tests"
18
+ response
16
19
  end
17
20
 
18
21
  def list(api_resource, params = {})
19
- if api_resource == "slo"
20
- raise ArgumentError if params[:limit] || params[:offset]
21
- limit = 1000
22
- offset = 0
23
- all = []
24
-
25
- loop do
26
- result = request :get, "/api/v1/#{api_resource}", params: params.merge(limit: limit, offset: offset)
27
- data = result.fetch(:data)
28
- all.concat data
29
- break all if data.size < limit
30
- offset += limit
22
+ with_pagination api_resource == "slo", params do |paginated_params|
23
+ response = request :get, "/api/v1/#{api_resource}", params: paginated_params
24
+ response = response.fetch(:dashboards) if api_resource == "dashboard"
25
+ response = response.fetch(:data) if api_resource == "slo"
26
+ if api_resource == "synthetics/tests"
27
+ response = response.fetch(:tests)
28
+ response.each { |r| r[:id] = r.delete(:public_id) }
31
29
  end
32
- else
33
- result = request :get, "/api/v1/#{api_resource}", params: params
34
- result = result.fetch(:dashboards) if api_resource == "dashboard"
35
- result
30
+
31
+ # ignore monitor synthetics create and that inherit the kennel_id, we do not directly manage them
32
+ response.reject! { |m| m[:type] == "synthetics alert" } if api_resource == "monitor"
33
+
34
+ response
36
35
  end
37
36
  end
38
37
 
39
38
  def create(api_resource, attributes)
40
- reply = request :post, "/api/v1/#{api_resource}", body: attributes
41
- api_resource == "slo" ? reply[:data].first : reply
39
+ response = request :post, "/api/v1/#{api_resource}", body: attributes
40
+ response = response.fetch(:data).first if api_resource == "slo"
41
+ response[:id] = response.delete(:public_id) if api_resource == "synthetics/tests"
42
+ response
42
43
  end
43
44
 
44
45
  def update(api_resource, id, attributes)
45
- request :put, "/api/v1/#{api_resource}/#{id}", body: attributes
46
+ response = request :put, "/api/v1/#{api_resource}/#{id}", body: attributes
47
+ response[:id] = response.delete(:public_id) if api_resource == "synthetics/tests"
48
+ response
46
49
  end
47
50
 
48
51
  # - force=true to not dead-lock on dependent monitors+slos
49
52
  # external dependency on kennel managed resources is their problem, we don't block on it
50
53
  # (?force=true did not work, force for dashboard is not documented but does not blow up)
51
54
  def delete(api_resource, id)
52
- request :delete, "/api/v1/#{api_resource}/#{id}", params: { force: "true" }, ignore_404: true
55
+ if api_resource == "synthetics/tests"
56
+ # https://docs.datadoghq.com/api/latest/synthetics/#delete-tests
57
+ request :post, "/api/v1/#{api_resource}/delete", body: { public_ids: [id] }, ignore_404: true
58
+ else
59
+ request :delete, "/api/v1/#{api_resource}/#{id}", params: { force: "true" }, ignore_404: true
60
+ end
53
61
  end
54
62
 
55
63
  def fill_details!(api_resource, list)
56
- return unless api_resource == "dashboard"
57
64
  details_cache do |cache|
58
65
  Utils.parallel(list) { |a| fill_detail!(api_resource, a, cache) }
59
66
  end
@@ -61,6 +68,21 @@ module Kennel
61
68
 
62
69
  private
63
70
 
71
+ def with_pagination(enabled, params)
72
+ return yield params unless enabled
73
+ raise ArgumentError if params[:limit] || params[:offset]
74
+ limit = 1000
75
+ offset = 0
76
+ all = []
77
+
78
+ loop do
79
+ response = yield params.merge(limit: limit, offset: offset)
80
+ all.concat response
81
+ return all if response.size < limit
82
+ offset += limit
83
+ end
84
+ end
85
+
64
86
  # Make diff work even though we cannot mass-fetch definitions
65
87
  def fill_detail!(api_resource, a, cache)
66
88
  args = [api_resource, a.fetch(:id)]
@@ -74,34 +96,52 @@ module Kennel
74
96
  end
75
97
 
76
98
  def request(method, path, body: nil, params: {}, ignore_404: false)
77
- params = params.merge(application_key: @app_key, api_key: @api_key)
78
- query = Faraday::FlatParamsEncoder.encode(params)
79
- response = nil
80
- tries = 2
81
-
82
- tries.times do |i|
83
- response = Utils.retry Faraday::ConnectionFailed, Faraday::TimeoutError, times: 2 do
84
- @client.send(method, "#{path}?#{query}") do |request|
85
- request.body = JSON.generate(body) if body
86
- request.headers["Content-type"] = "application/json"
99
+ path = "#{path}?#{Faraday::FlatParamsEncoder.encode(params)}" if params.any?
100
+ with_cache ENV["FORCE_GET_CACHE"] && method == :get, path do
101
+ response = nil
102
+ tries = 2
103
+
104
+ tries.times do |i|
105
+ response = Utils.retry Faraday::ConnectionFailed, Faraday::TimeoutError, times: 2 do
106
+ @client.send(method, path) do |request|
107
+ request.body = JSON.generate(body) if body
108
+ request.headers["Content-type"] = "application/json"
109
+ request.headers["DD-API-KEY"] = @api_key
110
+ request.headers["DD-APPLICATION-KEY"] = @app_key
111
+ end
87
112
  end
113
+
114
+ break if i == tries - 1 || method != :get || response.status < 500
115
+ Kennel.err.puts "Retrying on server error #{response.status} for #{path}"
88
116
  end
89
117
 
90
- break if i == tries - 1 || method != :get || response.status < 500
91
- Kennel.err.puts "Retrying on server error #{response.status} for #{path}"
92
- end
118
+ if !response.success? && (response.status != 404 || !ignore_404)
119
+ message = +"Error #{response.status} during #{method.upcase} #{path}\n"
120
+ message << "request:\n#{JSON.pretty_generate(body)}\nresponse:\n" if body
121
+ message << response.body
122
+ raise message
123
+ end
93
124
 
94
- if !response.success? && (response.status != 404 || !ignore_404)
95
- message = +"Error #{response.status} during #{method.upcase} #{path}\n"
96
- message << "request:\n#{JSON.pretty_generate(body)}\nresponse:\n" if body
97
- message << response.body
98
- raise message
125
+ if response.body.empty?
126
+ {}
127
+ else
128
+ JSON.parse(response.body, symbolize_names: true)
129
+ end
99
130
  end
131
+ end
100
132
 
101
- if response.body.empty?
102
- {}
133
+ # allow caching all requests to speedup/benchmark logic that includes repeated requests
134
+ def with_cache(enabled, key)
135
+ return yield unless enabled
136
+ dir = "tmp/cache"
137
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
138
+ file = "#{dir}/#{key.delete("/?=")}" # TODO: encode nicely
139
+ if File.exist?(file)
140
+ Marshal.load(File.read(file)) # rubocop:disable Security/MarshalLoad
103
141
  else
104
- JSON.parse(response.body, symbolize_names: true)
142
+ result = yield
143
+ File.write(file, Marshal.dump(result))
144
+ result
105
145
  end
106
146
  end
107
147
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # cache that reads everything from a single file
4
- # to avoid doing multiple disk reads while iterating all definitions
5
- # it also replaces updated keys and has an overall expiry to not keep deleted things forever
4
+ # - avoids doing multiple disk reads while iterating all definitions
5
+ # - has a global expiry to not keep deleted resources forever
6
6
  module Kennel
7
7
  class FileCache
8
8
  def initialize(file, cache_version)
@@ -22,10 +22,11 @@ module Kennel
22
22
 
23
23
  def fetch(key, key_version)
24
24
  old_value, old_version = @data[key]
25
- return old_value if old_version == [key_version, @cache_version]
25
+ expected_version = [key_version, @cache_version]
26
+ return old_value if old_version == expected_version
26
27
 
27
28
  new_value = yield
28
- @data[key] = [new_value, [key_version, @cache_version], @expires]
29
+ @data[key] = [new_value, expected_version, @expires]
29
30
  new_value
30
31
  end
31
32
 
@@ -46,8 +47,11 @@ module Kennel
46
47
  File.write(@file, Marshal.dump(@data))
47
48
  end
48
49
 
50
+ # keep the cache small to make loading it fast (5MB ~= 100ms)
51
+ # - delete expired keys
52
+ # - delete what would be deleted anyway when updating
49
53
  def expire_old_data
50
- @data.reject! { |_, (_, _, ex)| ex < @now }
54
+ @data.reject! { |_, (_, (_, cv), expires)| expires < @now || cv != @cache_version }
51
55
  end
52
56
  end
53
57
  end
@@ -15,11 +15,8 @@ module Kennel
15
15
  end
16
16
 
17
17
  model =
18
- begin
19
- Kennel::Models.const_get(resource.capitalize)
20
- rescue NameError
21
- raise ArgumentError, "#{resource} is not supported"
22
- end
18
+ Kennel::Models::Record.subclasses.detect { |c| c.api_resource == resource } ||
19
+ raise(ArgumentError, "#{resource} is not supported")
23
20
 
24
21
  data = @api.show(model.api_resource, id)
25
22
  id = data.fetch(:id) # keep native value
@@ -70,6 +67,8 @@ module Kennel
70
67
  dry_up_widget_metadata!(widget)
71
68
  (widget.dig(:definition, :markers) || []).each { |m| m[:label]&.delete! " " }
72
69
  end
70
+ when "synthetics/tests"
71
+ data[:locations] = :all if data[:locations].sort == Kennel::Models::SyntheticTest::LOCATIONS.sort
73
72
  else
74
73
  # noop
75
74
  end
@@ -113,8 +112,9 @@ module Kennel
113
112
  next if request[:formulas] && request[:formulas] != [{ formula: "query1" }]
114
113
  next if request[:queries]&.size != 1
115
114
  next if request[:queries].any? { |q| q[:data_source] != "metrics" }
115
+ next if widget.dig(:definition, :type) != request[:response_format]
116
116
  request.delete(:formulas)
117
- request[:type] = request.delete(:response_format)
117
+ request.delete(:response_format)
118
118
  request[:q] = request.delete(:queries).first.fetch(:query)
119
119
  end
120
120
  end
@@ -6,7 +6,7 @@ module Kennel
6
6
  TRACKING_FIELDS = [:message, :description].freeze
7
7
  READONLY_ATTRIBUTES = [
8
8
  :deleted, :id, :created, :created_at, :creator, :org_id, :modified, :modified_at,
9
- :klass # added by syncer.rb
9
+ :klass, :tracking_id # added by syncer.rb
10
10
  ].freeze
11
11
 
12
12
  settings :id, :kennel_id
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+ module Kennel
3
+ module Models
4
+ class SyntheticTest < Record
5
+ TRACKING_FIELD = :message
6
+ DEFAULTS = {
7
+ }.freeze
8
+ READONLY_ATTRIBUTES = superclass::READONLY_ATTRIBUTES + [:status, :monitor_id]
9
+ LOCATIONS = ["aws:ca-central-1", "aws:eu-north-1", "aws:eu-west-1", "aws:eu-west-3", "aws:eu-west-2", "aws:ap-south-1", "aws:us-west-2", "aws:us-west-1", "aws:sa-east-1", "aws:us-east-2", "aws:ap-northeast-1", "aws:ap-northeast-2", "aws:eu-central-1", "aws:ap-southeast-2", "aws:ap-southeast-1"].freeze
10
+
11
+ settings :tags, :config, :message, :subtype, :type, :name, :locations, :options
12
+
13
+ defaults(
14
+ id: -> { nil },
15
+ tags: -> { @project.tags },
16
+ message: -> { "\n\n#{project.mention}" }
17
+ )
18
+
19
+ def as_json
20
+ return @as_json if @as_json
21
+ locations = locations()
22
+ data = {
23
+ message: message,
24
+ tags: tags,
25
+ config: config,
26
+ type: type,
27
+ subtype: subtype,
28
+ options: options,
29
+ name: name,
30
+ locations: locations == :all ? LOCATIONS : locations
31
+ }
32
+
33
+ if v = id
34
+ data[:id] = v
35
+ end
36
+
37
+ @as_json = data
38
+ end
39
+
40
+ def self.api_resource
41
+ "synthetics/tests"
42
+ end
43
+
44
+ def self.url(id)
45
+ Utils.path_to_url "/synthetics/details/#{id}"
46
+ end
47
+
48
+ def self.parse_url(url)
49
+ url[/\/synthetics\/details\/([a-z\d-]{11,})/, 1] # id format is 1ab-2ab-3ab
50
+ end
51
+
52
+ def self.normalize(expected, actual)
53
+ super
54
+
55
+ # tags come in a semi-random order and order is never updated
56
+ expected[:tags]&.sort!
57
+ actual[:tags].sort!
58
+
59
+ ignore_default(expected, actual, DEFAULTS)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -29,6 +29,8 @@ module Kennel
29
29
  Kennel.err.print "#{time.round(2)}s\n"
30
30
 
31
31
  result
32
+ ensure
33
+ stop = true
32
34
  end
33
35
  end
34
36
  end
data/lib/kennel/syncer.rb CHANGED
@@ -1,22 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
  module Kennel
3
3
  class Syncer
4
- DELETE_ORDER = ["dashboard", "slo", "monitor"].freeze # dashboards references monitors + slos, slos reference monitors
5
- LINE_UP = "\e[1A"
4
+ DELETE_ORDER = ["dashboard", "slo", "monitor", "synthetics/tests"].freeze # dashboards references monitors + slos, slos reference monitors
5
+ LINE_UP = "\e[1A\033[K" # go up and clear
6
6
 
7
7
  def initialize(api, expected, project: nil)
8
8
  @api = api
9
9
  @project_filter = project
10
10
  @expected = expected
11
- if @project_filter
12
- original = @expected
13
- @expected = @expected.select { |e| e.project.kennel_id == @project_filter }
14
- if @expected.empty?
15
- possible = original.map { |e| e.project.kennel_id }.uniq.sort
16
- raise "#{@project_filter} does not match any projects, try any of these:\n#{possible.join("\n")}"
17
- end
18
- end
19
- @expected.each(&:add_tracking_id)
20
11
  calculate_diff
21
12
  prevent_irreversible_partial_updates
22
13
  end
@@ -41,9 +32,9 @@ module Kennel
41
32
  message = "#{e.class.api_resource} #{e.tracking_id}"
42
33
  Kennel.out.puts "Creating #{message}"
43
34
  reply = @api.create e.class.api_resource, e.as_json
44
- reply[:klass] = e.class # store api resource class for later use
35
+ cache_metadata reply, e.class
45
36
  id = reply.fetch(:id)
46
- populate_id_map [reply] # allow resolving ids we could previously no resolve
37
+ populate_id_map [], [reply] # allow resolving ids we could previously no resolve
47
38
  Kennel.out.puts "#{LINE_UP}Created #{message} #{e.class.url(id)}"
48
39
  end
49
40
 
@@ -56,8 +47,7 @@ module Kennel
56
47
 
57
48
  @delete.each do |id, _, a|
58
49
  klass = a.fetch(:klass)
59
- tracking_id = klass.parse_tracking_id(a)
60
- message = "#{klass.api_resource} #{tracking_id} #{id}"
50
+ message = "#{klass.api_resource} #{a.fetch(:tracking_id)} #{id}"
61
51
  Kennel.out.puts "Deleting #{message}"
62
52
  @api.delete klass.api_resource, id
63
53
  Kennel.out.puts "#{LINE_UP}Deleted #{message}"
@@ -108,14 +98,13 @@ module Kennel
108
98
 
109
99
  actual = Progress.progress("Downloading definitions") { download_definitions }
110
100
 
111
- # resolve dependencies to avoid diff
112
- populate_id_map actual
113
- @expected.each { |e| @id_map[e.tracking_id] ||= :new }
114
- resolve_linked_tracking_ids! @expected
101
+ Progress.progress "Diffing" do
102
+ populate_id_map @expected, actual
103
+ filter_actual_by_project! actual
104
+ resolve_linked_tracking_ids! @expected # resolve dependencies to avoid diff
115
105
 
116
- filter_by_project! actual
106
+ @expected.each(&:add_tracking_id) # avoid diff with actual
117
107
 
118
- Progress.progress "Diffing" do
119
108
  items = actual.map do |a|
120
109
  e = matching_expected(a)
121
110
  if e && @expected.delete(e)
@@ -126,9 +115,8 @@ module Kennel
126
115
  end
127
116
 
128
117
  # fill details of things we need to compare
129
- detailed = Hash.new { |h, k| h[k] = [] }
130
- items.each { |e, a| detailed[a[:klass]] << a if e }
131
- detailed.each { |klass, actuals| @api.fill_details! klass.api_resource, actuals }
118
+ details = items.map { |e, a| a if e && e.class.api_resource == "dashboard" }.compact
119
+ @api.fill_details! "dashboard", details
132
120
 
133
121
  # pick out things to update or delete
134
122
  items.each do |e, a|
@@ -136,26 +124,30 @@ module Kennel
136
124
  if e
137
125
  diff = e.diff(a)
138
126
  @update << [id, e, a, diff] if diff.any?
139
- elsif a.fetch(:klass).parse_tracking_id(a) # was previously managed
127
+ elsif a.fetch(:tracking_id) # was previously managed
140
128
  @delete << [id, nil, a]
141
129
  end
142
130
  end
143
131
 
144
132
  ensure_all_ids_found
145
133
  @create = @expected.map { |e| [nil, e] }
134
+ @delete.sort_by! { |_, _, a| DELETE_ORDER.index a.fetch(:klass).api_resource }
146
135
  end
147
-
148
- @delete.sort_by! { |_, _, a| DELETE_ORDER.index a.fetch(:klass).api_resource }
149
136
  end
150
137
 
151
138
  def download_definitions
152
139
  Utils.parallel(Models::Record.subclasses) do |klass|
153
140
  results = @api.list(klass.api_resource, with_downtimes: false) # lookup monitors without adding unnecessary downtime information
154
141
  results = results[results.keys.first] if results.is_a?(Hash) # dashboards are nested in {dashboards: []}
155
- results.each { |c| c[:klass] = klass } # store api resource for later diffing
142
+ results.each { |a| cache_metadata(a, klass) }
156
143
  end.flatten(1)
157
144
  end
158
145
 
146
+ def cache_metadata(a, klass)
147
+ a[:klass] = klass
148
+ a[:tracking_id] = a.fetch(:klass).parse_tracking_id(a)
149
+ end
150
+
159
151
  def ensure_all_ids_found
160
152
  @expected.each do |e|
161
153
  next unless id = e.id
@@ -176,14 +168,14 @@ module Kennel
176
168
  end
177
169
 
178
170
  klass = a.fetch(:klass)
179
- @lookup_map["#{klass.api_resource}:#{a.fetch(:id)}"] || @lookup_map[klass.parse_tracking_id(a)]
171
+ @lookup_map["#{klass.api_resource}:#{a.fetch(:id)}"] || @lookup_map[a.fetch(:tracking_id)]
180
172
  end
181
173
 
182
174
  def print_plan(step, list, color)
183
175
  return if list.empty?
184
176
  list.each do |_, e, a, diff|
185
177
  klass = (e ? e.class : a.fetch(:klass))
186
- Kennel.out.puts Utils.color(color, "#{step} #{klass.api_resource} #{e&.tracking_id || klass.parse_tracking_id(a)}")
178
+ Kennel.out.puts Utils.color(color, "#{step} #{klass.api_resource} #{e&.tracking_id || a.fetch(:tracking_id)}")
187
179
  print_diff(diff) if diff # only for update
188
180
  end
189
181
  end
@@ -232,19 +224,23 @@ module Kennel
232
224
  end
233
225
  end
234
226
 
235
- def populate_id_map(actual)
236
- actual.each { |a| @id_map[a.fetch(:klass).parse_tracking_id(a)] = a.fetch(:id) }
227
+ def populate_id_map(expected, actual)
228
+ actual.each do |a|
229
+ next unless tracking_id = a.fetch(:tracking_id)
230
+ @id_map[tracking_id] = a.fetch(:id)
231
+ end
232
+ expected.each { |e| @id_map[e.tracking_id] ||= :new }
237
233
  end
238
234
 
239
235
  def resolve_linked_tracking_ids!(list, force: false)
240
236
  list.each { |e| e.resolve_linked_tracking_ids!(@id_map, force: force) }
241
237
  end
242
238
 
243
- def filter_by_project!(definitions)
239
+ def filter_actual_by_project!(actual)
244
240
  return unless @project_filter
245
- definitions.select! do |a|
246
- id = a.fetch(:klass).parse_tracking_id(a)
247
- !id || id.start_with?("#{@project_filter}:")
241
+ actual.select! do |a|
242
+ tracking_id = a.fetch(:tracking_id)
243
+ !tracking_id || tracking_id.start_with?("#{@project_filter}:")
248
244
  end
249
245
  end
250
246
  end
data/lib/kennel/tasks.rb CHANGED
@@ -138,7 +138,7 @@ namespace :kennel do
138
138
  resources.each do |resource|
139
139
  Kennel::Progress.progress("Downloading #{resource}") do
140
140
  list = api.list(resource)
141
- api.fill_details!(resource, list)
141
+ api.fill_details!(resource, list) if resource == "dashboard"
142
142
  end
143
143
  list.each do |r|
144
144
  r[:api_resource] = resource
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Kennel
3
- VERSION = "1.89.0"
3
+ VERSION = "1.91.1"
4
4
  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.89.0
4
+ version: 1.91.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: 2021-06-25 00:00:00.000000000 Z
11
+ date: 2021-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -70,6 +70,7 @@ files:
70
70
  - lib/kennel/models/project.rb
71
71
  - lib/kennel/models/record.rb
72
72
  - lib/kennel/models/slo.rb
73
+ - lib/kennel/models/synthetic_test.rb
73
74
  - lib/kennel/models/team.rb
74
75
  - lib/kennel/optional_validations.rb
75
76
  - lib/kennel/progress.rb