kennel 1.39.0 → 1.40.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.rb +1 -0
- data/lib/kennel/importer.rb +17 -2
- data/lib/kennel/models/base.rb +1 -1
- data/lib/kennel/models/dash.rb +1 -14
- data/lib/kennel/models/dashboard.rb +93 -0
- data/lib/kennel/syncer.rb +12 -12
- data/lib/kennel/template_variables.rb +17 -1
- data/lib/kennel/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 48d299703094c1309aec2c1e902a6c0e8a37174395a9fccd233cf72f3b75b255
|
|
4
|
+
data.tar.gz: 72f45df5f14f29c97f4a906d6f59cc9a70acc26040fed0a36520e8c675dbacb0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ef6beaa1c5ffa0c2b1c6d534e1fd30cd3276d29bf1a16074e06f1499b76466b187c0694e5bc50ca9c9e8bb814cccfec05f477bee16b15b34067df3c78c83d2d3
|
|
7
|
+
data.tar.gz: eeabb869d2528613cbb1d18b92fe603404c160fc88212b76f97a02d4a77e8262b930fde867ae3fb33e13c0ce330b8998d5494ee233655b294b37461d6e10e5f3
|
data/lib/kennel.rb
CHANGED
data/lib/kennel/importer.rb
CHANGED
|
@@ -64,8 +64,9 @@ module Kennel
|
|
|
64
64
|
private
|
|
65
65
|
|
|
66
66
|
def pretty_print(hash)
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
sort_widgets hash
|
|
68
|
+
|
|
69
|
+
sort_hash(hash).map do |k, v|
|
|
69
70
|
pretty_value =
|
|
70
71
|
if v.is_a?(Hash) || (v.is_a?(Array) && !v.all? { |e| e.is_a?(String) })
|
|
71
72
|
# update answer here when changing https://stackoverflow.com/questions/8842546/best-way-to-pretty-print-a-hash
|
|
@@ -85,5 +86,19 @@ module Kennel
|
|
|
85
86
|
" #{k}: -> {#{pretty_value}}"
|
|
86
87
|
end.join(",\n")
|
|
87
88
|
end
|
|
89
|
+
|
|
90
|
+
# sort dashboard widgets + nesting
|
|
91
|
+
def sort_widgets(outer)
|
|
92
|
+
outer[:widgets]&.each do |widgets|
|
|
93
|
+
definition = widgets[:definition]
|
|
94
|
+
definition.replace sort_hash(definition)
|
|
95
|
+
sort_widgets definition
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# important to the front and rest deterministic
|
|
100
|
+
def sort_hash(hash)
|
|
101
|
+
Hash[hash.sort_by { |k, _| [SORT_ORDER.index(k) || 999, k] }]
|
|
102
|
+
end
|
|
88
103
|
end
|
|
89
104
|
end
|
data/lib/kennel/models/base.rb
CHANGED
|
@@ -74,7 +74,7 @@ module Kennel
|
|
|
74
74
|
[expected.size.to_i, actual.size.to_i].max.times do |i|
|
|
75
75
|
a_r = actual.dig(i, level2, :requests) || []
|
|
76
76
|
e_r = expected.dig(i, level2, :requests) || []
|
|
77
|
-
ignore_defaults e_r, a_r, REQUEST_DEFAULTS
|
|
77
|
+
ignore_defaults e_r, a_r, self::REQUEST_DEFAULTS
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
|
data/lib/kennel/models/dash.rb
CHANGED
|
@@ -75,14 +75,7 @@ module Kennel
|
|
|
75
75
|
def validate_json(data)
|
|
76
76
|
super
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
# TODO: do the same check for apm_query and their group_by
|
|
80
|
-
variables = (data[:template_variables] || []).map { |v| "$#{v.fetch(:name)}" }
|
|
81
|
-
queries = data[:graphs].flat_map { |g| (g[:definition][:requests] || []).map { |r| r[:q] }.compact }
|
|
82
|
-
bad = queries.grep_v(/(#{variables.map { |v| Regexp.escape(v) }.join("|")})\b/)
|
|
83
|
-
if bad.any?
|
|
84
|
-
invalid! "queries #{bad.join(", ")} must use the template variables #{variables.join(", ")}"
|
|
85
|
-
end
|
|
78
|
+
validate_template_variables data, :graphs
|
|
86
79
|
|
|
87
80
|
# check for fields that are unsettable
|
|
88
81
|
data[:graphs].each do |g|
|
|
@@ -92,12 +85,6 @@ module Kennel
|
|
|
92
85
|
end
|
|
93
86
|
end
|
|
94
87
|
|
|
95
|
-
def render_template_variables
|
|
96
|
-
(template_variables || []).map do |v|
|
|
97
|
-
v.is_a?(String) ? { default: "*", prefix: v, name: v } : v
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
88
|
def render_graphs
|
|
102
89
|
definitions.map do |title, viz, type, queries, options = {}, ignored = nil|
|
|
103
90
|
# validate inputs
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Kennel
|
|
3
|
+
module Models
|
|
4
|
+
class Dashboard < Base
|
|
5
|
+
include TemplateVariables
|
|
6
|
+
include OptionalValidations
|
|
7
|
+
|
|
8
|
+
API_LIST_INCOMPLETE = true
|
|
9
|
+
DASHBOARD_DEFAULTS = { template_variables: [] }.freeze
|
|
10
|
+
READONLY_ATTRIBUTES = Base::READONLY_ATTRIBUTES + [
|
|
11
|
+
:author_handle, :author_name, :modified_at, :url, :is_read_only, :notify_list
|
|
12
|
+
]
|
|
13
|
+
REQUEST_DEFAULTS = {
|
|
14
|
+
style: { line_width: "normal", palette: "dog_classic", line_type: "solid" }
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
settings :id, :title, :description, :widgets, :kennel_id, :layout_type
|
|
18
|
+
|
|
19
|
+
defaults(
|
|
20
|
+
description: -> { "" },
|
|
21
|
+
widgets: -> { [] },
|
|
22
|
+
id: -> { nil }
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
def normalize(expected, actual)
|
|
27
|
+
super
|
|
28
|
+
|
|
29
|
+
ignore_default expected, actual, DASHBOARD_DEFAULTS
|
|
30
|
+
|
|
31
|
+
base_pairs(expected, actual).each do |pair|
|
|
32
|
+
ignore_request_defaults(*pair, :widgets, :definition)
|
|
33
|
+
pair.each { |dash| dash[:widgets]&.each { |w| w.delete(:id) } }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# expand nested widgets into expected/actual pairs for default resolution
|
|
40
|
+
# [a, e] -> [[a, e], [aw1, ew1], ...]
|
|
41
|
+
def base_pairs(*pair)
|
|
42
|
+
result = [pair]
|
|
43
|
+
slots = pair.map { |d| d[:widgets]&.size }.compact.max.to_i
|
|
44
|
+
slots.times do |i|
|
|
45
|
+
nested = pair.map { |d| d.dig(:widgets, i, :definition) || {} }
|
|
46
|
+
result << nested if nested.any? { |d| d.key?(:widgets) }
|
|
47
|
+
end
|
|
48
|
+
result
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
attr_reader :project
|
|
53
|
+
|
|
54
|
+
def initialize(project, *args)
|
|
55
|
+
@project = project
|
|
56
|
+
super(*args)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.api_resource
|
|
60
|
+
"dashboard"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def as_json
|
|
64
|
+
return @json if @json
|
|
65
|
+
@json = {
|
|
66
|
+
layout_type: layout_type,
|
|
67
|
+
title: "#{title}#{LOCK}",
|
|
68
|
+
description: description,
|
|
69
|
+
template_variables: render_template_variables,
|
|
70
|
+
widgets: widgets
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@json[:id] = id if id
|
|
74
|
+
|
|
75
|
+
validate_json(@json) if validate
|
|
76
|
+
|
|
77
|
+
@json
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def url(id)
|
|
81
|
+
Utils.path_to_url "/dashboard/#{id}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def validate_json(data)
|
|
87
|
+
super
|
|
88
|
+
|
|
89
|
+
validate_template_variables data, :widgets
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
data/lib/kennel/syncer.rb
CHANGED
|
@@ -74,8 +74,12 @@ module Kennel
|
|
|
74
74
|
details_cache do |cache|
|
|
75
75
|
actual.each do |a|
|
|
76
76
|
id = a.fetch(:id)
|
|
77
|
+
e = matching_expected(a)
|
|
77
78
|
|
|
78
|
-
|
|
79
|
+
# check correct class, since we will have multiple actuals for each flavor (dash/screen/dashboard)
|
|
80
|
+
next if e && e.class.api_resource != a[:api_resource]
|
|
81
|
+
|
|
82
|
+
if e && @expected.delete(e)
|
|
79
83
|
fill_details(a, cache) if e.class::API_LIST_INCOMPLETE
|
|
80
84
|
diff = e.diff(a)
|
|
81
85
|
@update << [id, e, a, diff] if diff.any?
|
|
@@ -94,7 +98,7 @@ module Kennel
|
|
|
94
98
|
def fill_details(a, cache)
|
|
95
99
|
resource = a.fetch(:api_resource)
|
|
96
100
|
args = [resource, a.fetch(:id)]
|
|
97
|
-
full = cache.fetch(args, a.fetch(:
|
|
101
|
+
full = cache.fetch(args, a[:modified] || a.fetch(:modified_at)) do
|
|
98
102
|
unnest(resource, @api.show(*args))
|
|
99
103
|
end
|
|
100
104
|
a.merge!(full)
|
|
@@ -117,13 +121,10 @@ module Kennel
|
|
|
117
121
|
end
|
|
118
122
|
|
|
119
123
|
Utils.parallel(api_resources.compact.uniq) do |api_resource|
|
|
120
|
-
# lookup monitors without adding unnecessary downtime information
|
|
121
|
-
results =
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
results.each { |r| r[:id] = Integer(r.fetch(:id)) }
|
|
125
|
-
end
|
|
126
|
-
results.each { |c| c[:api_resource] = api_resource }
|
|
124
|
+
results = @api.list(api_resource, with_downtimes: false) # lookup monitors without adding unnecessary downtime information
|
|
125
|
+
results = results[results.keys.first] if results.is_a?(Hash) # dashes/screens are nested in {dash: {}}
|
|
126
|
+
results.each { |r| r[:id] = Integer(r[:id]) if r[:id] =~ /\A\d+\z/ } # screen ids are integers as strings
|
|
127
|
+
results.each { |c| c[:api_resource] = api_resource } # store api resource for later diffing
|
|
127
128
|
end.flatten(1)
|
|
128
129
|
end
|
|
129
130
|
|
|
@@ -134,7 +135,7 @@ module Kennel
|
|
|
134
135
|
end
|
|
135
136
|
end
|
|
136
137
|
|
|
137
|
-
def
|
|
138
|
+
def matching_expected(a)
|
|
138
139
|
# index list by all the thing we look up by: tracking id and actual id
|
|
139
140
|
@lookup_map ||= @expected.each_with_object({}) do |e, all|
|
|
140
141
|
keys = [tracking_id(e.as_json)]
|
|
@@ -145,8 +146,7 @@ module Kennel
|
|
|
145
146
|
end
|
|
146
147
|
end
|
|
147
148
|
|
|
148
|
-
|
|
149
|
-
@expected.delete(e) if e
|
|
149
|
+
@lookup_map["#{a.fetch(:api_resource)}:#{a.fetch(:id)}"] || @lookup_map[tracking_id(a)]
|
|
150
150
|
end
|
|
151
151
|
|
|
152
152
|
def print_plan(step, list, color)
|
|
@@ -9,9 +9,25 @@ module Kennel
|
|
|
9
9
|
private
|
|
10
10
|
|
|
11
11
|
def render_template_variables
|
|
12
|
-
template_variables.map do |v|
|
|
12
|
+
(template_variables || []).map do |v|
|
|
13
13
|
v.is_a?(String) ? { default: "*", prefix: v, name: v } : v
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
|
+
|
|
17
|
+
# check for queries that do not use the variables and would be misleading
|
|
18
|
+
# TODO: do the same check for apm_query and their group_by
|
|
19
|
+
def validate_template_variables(data, key)
|
|
20
|
+
variables = (data[:template_variables] || []).map { |v| "$#{v.fetch(:name)}" }
|
|
21
|
+
queries = data[key].flat_map do |g|
|
|
22
|
+
requests =
|
|
23
|
+
(g.dig(:definition, :requests) || []) +
|
|
24
|
+
(g.dig(:definition, :widgets) || []).flat_map { |w| w.dig(:definition, :requests) || [] }
|
|
25
|
+
requests.map { |r| r[:q] }
|
|
26
|
+
end.compact
|
|
27
|
+
bad = queries.grep_v(/(#{variables.map { |v| Regexp.escape(v) }.join("|")})\b/)
|
|
28
|
+
if bad.any?
|
|
29
|
+
invalid! "queries #{bad.join(", ")} must use the template variables #{variables.join(", ")}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
16
32
|
end
|
|
17
33
|
end
|
data/lib/kennel/version.rb
CHANGED
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.40.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: 2019-06-
|
|
11
|
+
date: 2019-06-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -66,6 +66,7 @@ files:
|
|
|
66
66
|
- lib/kennel/importer.rb
|
|
67
67
|
- lib/kennel/models/base.rb
|
|
68
68
|
- lib/kennel/models/dash.rb
|
|
69
|
+
- lib/kennel/models/dashboard.rb
|
|
69
70
|
- lib/kennel/models/monitor.rb
|
|
70
71
|
- lib/kennel/models/project.rb
|
|
71
72
|
- lib/kennel/models/screen.rb
|