kennel 1.79.0 → 1.82.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/Readme.md +13 -7
- data/lib/kennel.rb +36 -8
- data/lib/kennel/api.rb +22 -0
- data/lib/kennel/models/dashboard.rb +21 -18
- data/lib/kennel/models/monitor.rb +5 -3
- data/lib/kennel/models/record.rb +8 -10
- data/lib/kennel/models/slo.rb +1 -1
- data/lib/kennel/syncer.rb +51 -38
- data/lib/kennel/tasks.rb +8 -1
- data/lib/kennel/utils.rb +5 -0
- data/lib/kennel/version.rb +1 -1
- data/template/Readme.md +13 -7
- 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: 97ae92e1ab137731096bbedde401059fc2b8d411ff667df489056f05a3cf42aa
|
|
4
|
+
data.tar.gz: c48683c4888e16da817885a44a876638086c32e8e370aaf16553c31b7fde0136
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 89f7a44c794130ecd4a4238ace5846d050412fd923cc5ae358cb469370c3dd380569bffe991b2251bc80baf4e183bd765591295b043ffc51fd88c5593b723f21
|
|
7
|
+
data.tar.gz: 4a9eece67d3f0e0cf8dd56a21440ce585397d531c4aa82d1d4c3d43db08169b57dd1e982ba74e68326ae61ed8278ac4d5fed7eefe8f6979dcf5a3c3a3e98ceb0
|
data/Readme.md
CHANGED
|
@@ -84,7 +84,7 @@ end
|
|
|
84
84
|
- `cp .env.example .env`
|
|
85
85
|
- open [Datadog API Settings](https://app.datadoghq.com/account/settings#api)
|
|
86
86
|
- create a `API Key` or get an existing one from an admin, then add it to `.env` as `DATADOG_API_KEY`
|
|
87
|
-
-
|
|
87
|
+
- open [Datadog API Settings](https://app.datadoghq.com/access/application-keys) and create a new key, then add it to `.env` as `DATADOG_APP_KEY=`
|
|
88
88
|
- change the `DATADOG_SUBDOMAIN=app` in `.env` to your companies subdomain if you have one
|
|
89
89
|
- verify it works by running `rake plan`, it might show some diff, but should not crash
|
|
90
90
|
-->
|
|
@@ -212,14 +212,20 @@ removing the `id` will cause kennel to create a new resource in datadog.
|
|
|
212
212
|
Some validations might be too strict for your usecase or just wrong, please [open an issue](https://github.com/grosser/kennel/issues) and
|
|
213
213
|
to unblock use the `validate: -> { false }` option.
|
|
214
214
|
|
|
215
|
-
### Linking with
|
|
215
|
+
### Linking resources with kennel_id
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
Link resources with their kennel_id in the format `project kennel_id` + `:` + `resource kennel_id`,
|
|
218
|
+
this should be used to create dependent resources like monitor + slos,
|
|
219
|
+
so they can be created in a single update and can be re-created if any of them is deleted.
|
|
218
220
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
|Resource|Type|Syntax|
|
|
222
|
+
|---|---|---|
|
|
223
|
+
|Dashboard|uptime|`monitor: {id: "foo:bar"}`|
|
|
224
|
+
|Dashboard|alert_graph|`alert_id: "foo:bar"`|
|
|
225
|
+
|Dashboard|slo|`slo_id: "foo:bar"`|
|
|
226
|
+
|Monitor|composite|`query: -> { "%{foo:bar} && %{foo:baz}" }`|
|
|
227
|
+
|Monitor|slo alert|`query: -> { "error_budget(\"%{foo:bar}\").over(\"7d\") > 123.0" }`|
|
|
228
|
+
|Slo|monitor|`monitor_ids: -> ["foo:bar"]`|
|
|
223
229
|
|
|
224
230
|
### Debugging changes locally
|
|
225
231
|
|
data/lib/kennel.rb
CHANGED
|
@@ -39,12 +39,7 @@ module Kennel
|
|
|
39
39
|
attr_accessor :out, :err
|
|
40
40
|
|
|
41
41
|
def generate
|
|
42
|
-
|
|
43
|
-
generated.each do |part|
|
|
44
|
-
path = "generated/#{part.tracking_id.sub(":", "/")}.json"
|
|
45
|
-
FileUtils.mkdir_p(File.dirname(path))
|
|
46
|
-
File.write(path, JSON.pretty_generate(part.as_json) << "\n")
|
|
47
|
-
end
|
|
42
|
+
store generated
|
|
48
43
|
end
|
|
49
44
|
|
|
50
45
|
def plan
|
|
@@ -58,6 +53,35 @@ module Kennel
|
|
|
58
53
|
|
|
59
54
|
private
|
|
60
55
|
|
|
56
|
+
def store(parts)
|
|
57
|
+
Progress.progress "Storing" do
|
|
58
|
+
old = Dir["generated/**/*"]
|
|
59
|
+
used = []
|
|
60
|
+
|
|
61
|
+
Utils.parallel(parts, max: 2) do |part|
|
|
62
|
+
path = "generated/#{part.tracking_id.tr("/", ":").sub(":", "/")}.json"
|
|
63
|
+
used << File.dirname(path) # only 1 level of sub folders, so this is safe
|
|
64
|
+
used << path
|
|
65
|
+
write_file_if_necessary(path, JSON.pretty_generate(part.as_json) << "\n")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# deleting all is slow, so only delete the extras
|
|
69
|
+
(old - used).each { |p| FileUtils.rm_rf(p) }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def write_file_if_necessary(path, content)
|
|
74
|
+
# 99% case
|
|
75
|
+
begin
|
|
76
|
+
return if File.read(path) == content
|
|
77
|
+
rescue Errno::ENOENT
|
|
78
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# slow 1% case
|
|
82
|
+
File.write(path, content)
|
|
83
|
+
end
|
|
84
|
+
|
|
61
85
|
def syncer
|
|
62
86
|
@syncer ||= Syncer.new(api, generated, project: ENV["PROJECT"])
|
|
63
87
|
end
|
|
@@ -73,8 +97,12 @@ module Kennel
|
|
|
73
97
|
parts = Models::Project.recursive_subclasses.flat_map do |project_class|
|
|
74
98
|
project_class.new.validated_parts
|
|
75
99
|
end
|
|
76
|
-
parts.
|
|
77
|
-
|
|
100
|
+
parts.group_by(&:tracking_id).each do |tracking_id, same|
|
|
101
|
+
next if same.size == 1
|
|
102
|
+
raise <<~ERROR
|
|
103
|
+
#{tracking_id} is defined #{same.size} times
|
|
104
|
+
use a different `kennel_id` when defining multiple projects/monitors/dashboards to avoid this conflict
|
|
105
|
+
ERROR
|
|
78
106
|
end
|
|
79
107
|
parts
|
|
80
108
|
end
|
data/lib/kennel/api.rb
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
module Kennel
|
|
3
|
+
# encapsulates knowledge around how the api works
|
|
3
4
|
class Api
|
|
5
|
+
CACHE_FILE = "tmp/cache/details"
|
|
6
|
+
|
|
4
7
|
def initialize(app_key, api_key)
|
|
5
8
|
@app_key = app_key
|
|
6
9
|
@api_key = api_key
|
|
@@ -49,8 +52,27 @@ module Kennel
|
|
|
49
52
|
request :delete, "/api/v1/#{api_resource}/#{id}", params: { force: "true" }, ignore_404: true
|
|
50
53
|
end
|
|
51
54
|
|
|
55
|
+
def fill_details!(api_resource, list)
|
|
56
|
+
return unless api_resource == "dashboard"
|
|
57
|
+
details_cache do |cache|
|
|
58
|
+
Utils.parallel(list) { |a| fill_detail!(api_resource, a, cache) }
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
52
62
|
private
|
|
53
63
|
|
|
64
|
+
# Make diff work even though we cannot mass-fetch definitions
|
|
65
|
+
def fill_detail!(api_resource, a, cache)
|
|
66
|
+
args = [api_resource, a.fetch(:id)]
|
|
67
|
+
full = cache.fetch(args, a.fetch(:modified_at)) { show(*args) }
|
|
68
|
+
a.merge!(full)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def details_cache(&block)
|
|
72
|
+
cache = FileCache.new CACHE_FILE, Kennel::VERSION
|
|
73
|
+
cache.open(&block)
|
|
74
|
+
end
|
|
75
|
+
|
|
54
76
|
def request(method, path, body: nil, params: {}, ignore_404: false)
|
|
55
77
|
params = params.merge(application_key: @app_key, api_key: @api_key)
|
|
56
78
|
query = Faraday::FlatParamsEncoder.encode(params)
|
|
@@ -5,7 +5,6 @@ module Kennel
|
|
|
5
5
|
include TemplateVariables
|
|
6
6
|
include OptionalValidations
|
|
7
7
|
|
|
8
|
-
API_LIST_INCOMPLETE = true
|
|
9
8
|
DASHBOARD_DEFAULTS = { template_variables: [] }.freeze
|
|
10
9
|
READONLY_ATTRIBUTES = superclass::READONLY_ATTRIBUTES + [
|
|
11
10
|
:author_handle, :author_name, :modified_at, :url, :is_read_only, :notify_list
|
|
@@ -140,16 +139,16 @@ module Kennel
|
|
|
140
139
|
when "uptime"
|
|
141
140
|
if ids = definition[:monitor_ids]
|
|
142
141
|
definition[:monitor_ids] = ids.map do |id|
|
|
143
|
-
tracking_id?(id) ? resolve_link(id, :monitor, id_map, **args) : id
|
|
142
|
+
tracking_id?(id) ? (resolve_link(id, :monitor, id_map, **args) || id) : id
|
|
144
143
|
end
|
|
145
144
|
end
|
|
146
145
|
when "alert_graph"
|
|
147
146
|
if (id = definition[:alert_id]) && tracking_id?(id)
|
|
148
|
-
definition[:alert_id] = resolve_link(id, :monitor, id_map, **args).to_s
|
|
147
|
+
definition[:alert_id] = (resolve_link(id, :monitor, id_map, **args) || id).to_s
|
|
149
148
|
end
|
|
150
149
|
when "slo"
|
|
151
150
|
if (id = definition[:slo_id]) && tracking_id?(id)
|
|
152
|
-
definition[:slo_id] = resolve_link(id, :slo, id_map, **args).to_s
|
|
151
|
+
definition[:slo_id] = (resolve_link(id, :slo, id_map, **args) || id).to_s
|
|
153
152
|
end
|
|
154
153
|
end
|
|
155
154
|
end
|
|
@@ -186,22 +185,26 @@ module Kennel
|
|
|
186
185
|
end
|
|
187
186
|
|
|
188
187
|
def render_definitions(definitions)
|
|
189
|
-
definitions.map do |title, type, display_type, queries, options = {},
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
188
|
+
definitions.map do |title, type, display_type, queries, options = {}, too_many_args = nil|
|
|
189
|
+
if title.is_a?(Hash) && !type
|
|
190
|
+
title # user gave a full widget, just use it
|
|
191
|
+
else
|
|
192
|
+
# validate inputs
|
|
193
|
+
if too_many_args || (!title || !type || !queries || !options.is_a?(Hash))
|
|
194
|
+
raise ArgumentError, "Expected exactly 5 arguments for each definition (title, type, display_type, queries, options)"
|
|
195
|
+
end
|
|
196
|
+
if (SUPPORTED_DEFINITION_OPTIONS | options.keys) != SUPPORTED_DEFINITION_OPTIONS
|
|
197
|
+
raise ArgumentError, "Supported options are: #{SUPPORTED_DEFINITION_OPTIONS.map(&:inspect).join(", ")}"
|
|
198
|
+
end
|
|
197
199
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
200
|
+
# build definition
|
|
201
|
+
requests = Array(queries).map do |q|
|
|
202
|
+
request = { q: q }
|
|
203
|
+
request[:display_type] = display_type if display_type
|
|
204
|
+
request
|
|
205
|
+
end
|
|
206
|
+
{ definition: { title: title, type: type, requests: requests, **options } }
|
|
203
207
|
end
|
|
204
|
-
{ definition: { title: title, type: type, requests: requests, **options } }
|
|
205
208
|
end
|
|
206
209
|
end
|
|
207
210
|
end
|
|
@@ -112,9 +112,11 @@ module Kennel
|
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
def resolve_linked_tracking_ids!(id_map, **args)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
case as_json[:type]
|
|
116
|
+
when "composite", "slo alert"
|
|
117
|
+
type = (as_json[:type] == "composite" ? :monitor : :slo)
|
|
118
|
+
as_json[:query] = as_json[:query].gsub(/%{(.*?)}/) do
|
|
119
|
+
resolve_link($1, type, id_map, **args) || $&
|
|
118
120
|
end
|
|
119
121
|
end
|
|
120
122
|
end
|
data/lib/kennel/models/record.rb
CHANGED
|
@@ -6,7 +6,6 @@ module Kennel
|
|
|
6
6
|
READONLY_ATTRIBUTES = [
|
|
7
7
|
:deleted, :id, :created, :created_at, :creator, :org_id, :modified, :modified_at, :api_resource
|
|
8
8
|
].freeze
|
|
9
|
-
API_LIST_INCOMPLETE = false
|
|
10
9
|
|
|
11
10
|
settings :id, :kennel_id
|
|
12
11
|
|
|
@@ -65,19 +64,18 @@ module Kennel
|
|
|
65
64
|
|
|
66
65
|
private
|
|
67
66
|
|
|
68
|
-
def resolve_link(
|
|
69
|
-
|
|
70
|
-
if
|
|
67
|
+
def resolve_link(tracking_id, type, id_map, force:)
|
|
68
|
+
id = id_map[tracking_id]
|
|
69
|
+
if id == :new
|
|
71
70
|
if force
|
|
72
|
-
#
|
|
73
|
-
invalid! "#{id} needs to already exist, try again"
|
|
71
|
+
invalid! "#{type} #{tracking_id} was referenced but is also created by the current run.\nIt could not be created because of a circular dependency, try creating only some of the resources"
|
|
74
72
|
else
|
|
75
|
-
|
|
73
|
+
nil # will be re-resolved after the linked object was created
|
|
76
74
|
end
|
|
77
|
-
elsif
|
|
78
|
-
|
|
75
|
+
elsif id
|
|
76
|
+
id
|
|
79
77
|
else
|
|
80
|
-
invalid! "Unable to find #{type} #{
|
|
78
|
+
invalid! "Unable to find #{type} #{tracking_id} (does not exist and is not being created by the current run)"
|
|
81
79
|
end
|
|
82
80
|
end
|
|
83
81
|
|
data/lib/kennel/models/slo.rb
CHANGED
|
@@ -69,7 +69,7 @@ module Kennel
|
|
|
69
69
|
def resolve_linked_tracking_ids!(id_map, **args)
|
|
70
70
|
return unless as_json[:monitor_ids] # ignore_default can remove it
|
|
71
71
|
as_json[:monitor_ids] = as_json[:monitor_ids].map do |id|
|
|
72
|
-
id.is_a?(String) ? resolve_link(id, :monitor, id_map, **args) : id
|
|
72
|
+
id.is_a?(String) ? (resolve_link(id, :monitor, id_map, **args) || id) : id
|
|
73
73
|
end
|
|
74
74
|
end
|
|
75
75
|
|
data/lib/kennel/syncer.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
module Kennel
|
|
3
3
|
class Syncer
|
|
4
|
-
CACHE_FILE = "tmp/cache/details" # keep in sync with .travis.yml caching
|
|
5
4
|
TRACKING_FIELDS = [:message, :description].freeze
|
|
6
5
|
DELETE_ORDER = ["dashboard", "slo", "monitor"].freeze # dashboards references monitors + slos, slos reference monitors
|
|
7
6
|
|
|
@@ -38,23 +37,14 @@ module Kennel
|
|
|
38
37
|
end
|
|
39
38
|
|
|
40
39
|
def update
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@create.each do |_, e|
|
|
44
|
-
e.resolve_linked_tracking_ids!({}, force: true)
|
|
45
|
-
|
|
40
|
+
each_resolved @create do |_, e|
|
|
46
41
|
reply = @api.create e.class.api_resource, e.as_json
|
|
47
42
|
id = reply.fetch(:id)
|
|
48
|
-
|
|
49
|
-
# resolve ids we could previously no resolve
|
|
50
|
-
changed.delete e
|
|
51
|
-
resolve_linked_tracking_ids! from: [reply], to: changed
|
|
52
|
-
|
|
43
|
+
populate_id_map [reply] # allow resolving ids we could previously no resolve
|
|
53
44
|
Kennel.out.puts "Created #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(id)}"
|
|
54
45
|
end
|
|
55
46
|
|
|
56
|
-
@update
|
|
57
|
-
e.resolve_linked_tracking_ids!({}, force: true)
|
|
47
|
+
each_resolved @update do |id, e|
|
|
58
48
|
@api.update e.class.api_resource, id, e.as_json
|
|
59
49
|
Kennel.out.puts "Updated #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(id)}"
|
|
60
50
|
end
|
|
@@ -67,6 +57,37 @@ module Kennel
|
|
|
67
57
|
|
|
68
58
|
private
|
|
69
59
|
|
|
60
|
+
# loop over items until everything is resolved or crash when we get stuck
|
|
61
|
+
# this solves cases like composite monitors depending on each other or monitor->monitor slo->slo monitor chains
|
|
62
|
+
def each_resolved(list)
|
|
63
|
+
list = list.dup
|
|
64
|
+
loop do
|
|
65
|
+
return if list.empty?
|
|
66
|
+
list.reject! do |id, e|
|
|
67
|
+
if resolved?(e)
|
|
68
|
+
yield id, e
|
|
69
|
+
true
|
|
70
|
+
else
|
|
71
|
+
false
|
|
72
|
+
end
|
|
73
|
+
end ||
|
|
74
|
+
assert_resolved(list[0][1]) # resolve something or show a circular dependency error
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# TODO: optimize by storing an instance variable if already resolved
|
|
79
|
+
def resolved?(e)
|
|
80
|
+
assert_resolved e
|
|
81
|
+
true
|
|
82
|
+
rescue ValidationError
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# raises ValidationError when not resolved
|
|
87
|
+
def assert_resolved(e)
|
|
88
|
+
resolve_linked_tracking_ids! [e], force: true
|
|
89
|
+
end
|
|
90
|
+
|
|
70
91
|
def noop?
|
|
71
92
|
@create.empty? && @update.empty? && @delete.empty?
|
|
72
93
|
end
|
|
@@ -74,9 +95,15 @@ module Kennel
|
|
|
74
95
|
def calculate_diff
|
|
75
96
|
@update = []
|
|
76
97
|
@delete = []
|
|
98
|
+
@id_map = {}
|
|
77
99
|
|
|
78
100
|
actual = Progress.progress("Downloading definitions") { download_definitions }
|
|
79
|
-
|
|
101
|
+
|
|
102
|
+
# resolve dependencies to avoid diff
|
|
103
|
+
populate_id_map actual
|
|
104
|
+
@expected.each { |e| @id_map[e.tracking_id] ||= :new }
|
|
105
|
+
resolve_linked_tracking_ids! @expected
|
|
106
|
+
|
|
80
107
|
filter_by_project! actual
|
|
81
108
|
|
|
82
109
|
Progress.progress "Diffing" do
|
|
@@ -89,10 +116,10 @@ module Kennel
|
|
|
89
116
|
end
|
|
90
117
|
end
|
|
91
118
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
119
|
+
# fill details of things we need to compare
|
|
120
|
+
detailed = Hash.new { |h, k| h[k] = [] }
|
|
121
|
+
items.each { |e, a| detailed[a[:api_resource]] << a if e }
|
|
122
|
+
detailed.each { |api_resource, actuals| @api.fill_details! api_resource, actuals }
|
|
96
123
|
|
|
97
124
|
# pick out things to update or delete
|
|
98
125
|
items.each do |e, a|
|
|
@@ -107,27 +134,11 @@ module Kennel
|
|
|
107
134
|
|
|
108
135
|
ensure_all_ids_found
|
|
109
136
|
@create = @expected.map { |e| [nil, e] }
|
|
110
|
-
@create.sort_by! { |_, e| -DELETE_ORDER.index(e.class.api_resource) }
|
|
111
137
|
end
|
|
112
138
|
|
|
113
139
|
@delete.sort_by! { |_, _, a| DELETE_ORDER.index a.fetch(:api_resource) }
|
|
114
140
|
end
|
|
115
141
|
|
|
116
|
-
# Make diff work even though we cannot mass-fetch definitions
|
|
117
|
-
def fill_details(a, cache)
|
|
118
|
-
resource = a.fetch(:api_resource)
|
|
119
|
-
args = [resource, a.fetch(:id)]
|
|
120
|
-
full = cache.fetch(args, a[:modified] || a.fetch(:modified_at)) do
|
|
121
|
-
@api.show(*args)
|
|
122
|
-
end
|
|
123
|
-
a.merge!(full)
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def details_cache(&block)
|
|
127
|
-
cache = FileCache.new CACHE_FILE, Kennel::VERSION
|
|
128
|
-
cache.open(&block)
|
|
129
|
-
end
|
|
130
|
-
|
|
131
142
|
def download_definitions
|
|
132
143
|
Utils.parallel(Models::Record.subclasses.map(&:api_resource)) do |api_resource|
|
|
133
144
|
results = @api.list(api_resource, with_downtimes: false) # lookup monitors without adding unnecessary downtime information
|
|
@@ -214,10 +225,12 @@ module Kennel
|
|
|
214
225
|
end
|
|
215
226
|
end
|
|
216
227
|
|
|
217
|
-
def
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
228
|
+
def populate_id_map(actual)
|
|
229
|
+
actual.each { |a| @id_map[tracking_id(a)] = a.fetch(:id) }
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def resolve_linked_tracking_ids!(list, force: false)
|
|
233
|
+
list.each { |e| e.resolve_linked_tracking_ids!(@id_map, force: force) }
|
|
221
234
|
end
|
|
222
235
|
|
|
223
236
|
def filter_by_project!(definitions)
|
data/lib/kennel/tasks.rb
CHANGED
|
@@ -132,8 +132,15 @@ namespace :kennel do
|
|
|
132
132
|
else
|
|
133
133
|
Kennel::Models::Record.subclasses.map(&:api_resource)
|
|
134
134
|
end
|
|
135
|
+
api = Kennel.send(:api)
|
|
136
|
+
list = nil
|
|
137
|
+
|
|
135
138
|
resources.each do |resource|
|
|
136
|
-
Kennel.
|
|
139
|
+
Kennel::Progress.progress("Downloading #{resource}") do
|
|
140
|
+
list = api.list(resource)
|
|
141
|
+
api.fill_details!(resource, list)
|
|
142
|
+
end
|
|
143
|
+
list.each do |r|
|
|
137
144
|
Kennel.out.puts JSON.pretty_generate(r)
|
|
138
145
|
end
|
|
139
146
|
end
|
data/lib/kennel/utils.rb
CHANGED
|
@@ -23,6 +23,11 @@ module Kennel
|
|
|
23
23
|
.downcase
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
# for child projects, not used internally
|
|
27
|
+
def title_case(string)
|
|
28
|
+
string.split(/[\s_]/).map(&:capitalize) * " "
|
|
29
|
+
end
|
|
30
|
+
|
|
26
31
|
# simplified version of https://apidock.com/rails/ActiveSupport/Inflector/parameterize
|
|
27
32
|
def parameterize(string)
|
|
28
33
|
string
|
data/lib/kennel/version.rb
CHANGED
data/template/Readme.md
CHANGED
|
@@ -67,7 +67,7 @@ end
|
|
|
67
67
|
- `cp .env.example .env`
|
|
68
68
|
- open [Datadog API Settings](https://app.datadoghq.com/account/settings#api)
|
|
69
69
|
- create a `API Key` or get an existing one from an admin, then add it to `.env` as `DATADOG_API_KEY`
|
|
70
|
-
-
|
|
70
|
+
- open [Datadog API Settings](https://app.datadoghq.com/access/application-keys) and create a new key, then add it to `.env` as `DATADOG_APP_KEY=`
|
|
71
71
|
- change the `DATADOG_SUBDOMAIN=app` in `.env` to your companies subdomain if you have one
|
|
72
72
|
- verify it works by running `rake plan`, it might show some diff, but should not crash
|
|
73
73
|
|
|
@@ -194,14 +194,20 @@ removing the `id` will cause kennel to create a new resource in datadog.
|
|
|
194
194
|
Some validations might be too strict for your usecase or just wrong, please [open an issue](https://github.com/grosser/kennel/issues) and
|
|
195
195
|
to unblock use the `validate: -> { false }` option.
|
|
196
196
|
|
|
197
|
-
### Linking with
|
|
197
|
+
### Linking resources with kennel_id
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
Link resources with their kennel_id in the format `project kennel_id` + `:` + `resource kennel_id`,
|
|
200
|
+
this should be used to create dependent resources like monitor + slos,
|
|
201
|
+
so they can be created in a single update and can be re-created if any of them is deleted.
|
|
200
202
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
203
|
+
|Resource|Type|Syntax|
|
|
204
|
+
|---|---|---|
|
|
205
|
+
|Dashboard|uptime|`monitor: {id: "foo:bar"}`|
|
|
206
|
+
|Dashboard|alert_graph|`alert_id: "foo:bar"`|
|
|
207
|
+
|Dashboard|slo|`slo_id: "foo:bar"`|
|
|
208
|
+
|Monitor|composite|`query: -> { "%{foo:bar} && %{foo:baz}" }`|
|
|
209
|
+
|Monitor|slo alert|`query: -> { "error_budget(\"%{foo:bar}\").over(\"7d\") > 123.0" }`|
|
|
210
|
+
|Slo|monitor|`monitor_ids: -> ["foo:bar"]`|
|
|
205
211
|
|
|
206
212
|
### Debugging changes locally
|
|
207
213
|
|
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.82.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:
|
|
11
|
+
date: 2021-03-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|