datadog_backup 3.2.0 → 3.3.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: c069dccdefa9573c4fcd1e0e49292813203455a7b76e670c156a47936aa82b32
4
- data.tar.gz: 17c9f91c0de0a2a3975525c01f843f30bba77b43676c903a02472a1c576953ca
3
+ metadata.gz: a99c7fdf81fb1fafa9e2c8b2c4e28a7a2685d4c1a6482eede1aefd08322167bc
4
+ data.tar.gz: ac4f8c37ac22dc4c9893153afa27678e4abb98d70a4cb2a47574bf26e1b5c8da
5
5
  SHA512:
6
- metadata.gz: 7200fc88e53d944655028f91455d015a788ab4529f6cf21739dc23631ba168945d664bbcf9c000b81ce44be85ce33f2c6d964a1aa40625cbf93a601dec5d0df1
7
- data.tar.gz: b570dc952f7a9b93817970a2f897b005c6e81c1abb2d99988c32567cc7380a6042af3fb8a6e0e8d858a4d4447d681da762e389e434ee51446e7427b0d6864181
6
+ metadata.gz: ae3e46853cda4a9dc988624c169ad104c2a3737081f04ed5c33a1bfe6f24a752e6bd6fadfd0d9d1dc8545b135b95829044fd4748abd45ef6cd43c4d3258662d9
7
+ data.tar.gz: 39136571658e67c317b00a31b21b67169026e958cedf987dbb57c0b8af9a5cf8f944ed9617a1fd736a591aaa8c22559b4405fd5e17641dcf6fe911dac0484668
@@ -10,7 +10,7 @@ jobs:
10
10
  runs-on: ubuntu-20.04
11
11
  steps:
12
12
  - name: Validate PR Title
13
- uses: amannn/action-semantic-pull-request@v5.0.2
13
+ uses: amannn/action-semantic-pull-request@v5.2.0
14
14
  env:
15
15
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16
16
  with:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [3.3.0](https://github.com/scribd/datadog_backup/compare/v3.2.1...v3.3.0) (2023-08-17)
2
+
3
+
4
+ ### Features
5
+
6
+ * backup SLOs ([#155](https://github.com/scribd/datadog_backup/issues/155)) ([6cca6e7](https://github.com/scribd/datadog_backup/commit/6cca6e7567895673e94c7de80022c821553698ee)), closes [#1](https://github.com/scribd/datadog_backup/issues/1)
7
+
8
+ ## [3.2.1](https://github.com/scribd/datadog_backup/compare/v3.2.0...v3.2.1) (2023-02-11)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * update error handling for restore ([233b1b2](https://github.com/scribd/datadog_backup/commit/233b1b27d485e7502dd47ca01670cea0576e920d))
14
+
1
15
  # [3.2.0](https://github.com/scribd/datadog_backup/compare/v3.1.1...v3.2.0) (2023-02-10)
2
16
 
3
17
 
data/bin/datadog_backup CHANGED
@@ -48,6 +48,9 @@ def prereqs(defaults) # rubocop:disable Metrics/AbcSize
48
48
  opts.on('--dashboards-only') do
49
49
  result[:resources] = [DatadogBackup::Dashboards]
50
50
  end
51
+ opts.on('--slos-only') do
52
+ result[:resources] = [DatadogBackup::SLOs]
53
+ end
51
54
  opts.on('--synthetics-only') do
52
55
  result[:resources] = [DatadogBackup::Synthetics]
53
56
  end
@@ -83,7 +86,7 @@ defaults = {
83
86
  action: nil,
84
87
  backup_dir: File.join(ENV.fetch('PWD'), 'backup'),
85
88
  diff_format: :color,
86
- resources: [DatadogBackup::Dashboards, DatadogBackup::Monitors, DatadogBackup::Synthetics],
89
+ resources: [DatadogBackup::Dashboards, DatadogBackup::Monitors, DatadogBackup::SLOs, DatadogBackup::Synthetics],
87
90
  output_format: :yaml,
88
91
  force_restore: false,
89
92
  disable_array_sort: false
@@ -22,6 +22,15 @@ module DatadogBackup
22
22
  Concurrent::Promises.zip(*futures).value!
23
23
  end
24
24
 
25
+ def get_by_id(id)
26
+ begin
27
+ dashboard = except(get(id))
28
+ rescue Faraday::ResourceNotFound => e
29
+ dashboard = {}
30
+ end
31
+ except(dashboard)
32
+ end
33
+
25
34
  def initialize(options)
26
35
  super(options)
27
36
  @banlist = %w[modified_at url].freeze
@@ -110,9 +110,7 @@ module DatadogBackup
110
110
  body = load_from_file_by_id(id)
111
111
  begin
112
112
  update(id, body)
113
- rescue RuntimeError => e
114
- raise e.message unless e.message.include?('update failed with error 404')
115
-
113
+ rescue Faraday::ResourceNotFound => e
116
114
  create_newly(id, body)
117
115
  end
118
116
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DatadogBackup
4
+ # SLO specific overrides for backup and restore.
5
+ class SLOs < Resources
6
+ def all
7
+ get_all
8
+ end
9
+
10
+ def backup
11
+ LOGGER.info("Starting diffs on #{::DatadogBackup::ThreadPool::TPOOL.max_length} threads")
12
+ futures = all.map do |slo|
13
+ Concurrent::Promises.future_on(::DatadogBackup::ThreadPool::TPOOL, slo) do |board|
14
+ id = board[id_keyname]
15
+ get_and_write_file(id)
16
+ end
17
+ end
18
+
19
+ watcher = ::DatadogBackup::ThreadPool.watcher
20
+ watcher.join if watcher.status
21
+
22
+ Concurrent::Promises.zip(*futures).value!
23
+ end
24
+
25
+ def get_by_id(id)
26
+ begin
27
+ slo = except(get(id))
28
+ rescue Faraday::ResourceNotFound => e
29
+ slo = {}
30
+ end
31
+ except(slo)
32
+ end
33
+
34
+ def initialize(options)
35
+ super(options)
36
+ @banlist = %w[modified_at url].freeze
37
+ end
38
+
39
+ # Return the Faraday body from a response with a 2xx status code, otherwise raise an error
40
+ def body_with_2xx(response)
41
+ unless response.status.to_s =~ /^2/
42
+ raise "#{caller_locations(1,
43
+ 1)[0].label} failed with error #{response.status}"
44
+ end
45
+
46
+ response.body.fetch('data')
47
+ end
48
+
49
+ private
50
+
51
+ def api_version
52
+ 'v1'
53
+ end
54
+
55
+ def api_resource_name
56
+ 'slo'
57
+ end
58
+
59
+ def id_keyname
60
+ 'id'
61
+ end
62
+ end
63
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatadogBackup
4
- VERSION = '3.2.0'
4
+ VERSION = '3.3.0'
5
5
  end
@@ -8,6 +8,7 @@ require_relative 'datadog_backup/cli'
8
8
  require_relative 'datadog_backup/resources'
9
9
  require_relative 'datadog_backup/dashboards'
10
10
  require_relative 'datadog_backup/monitors'
11
+ require_relative 'datadog_backup/slos'
11
12
  require_relative 'datadog_backup/synthetics'
12
13
  require_relative 'datadog_backup/thread_pool'
13
14
  require_relative 'datadog_backup/version'
@@ -103,7 +103,7 @@ describe DatadogBackup::Resources do
103
103
  allow(resources).to receive(:api_resource_name).and_return('api-resource-name-string')
104
104
  stubs.get('/api/api-version-string/api-resource-name-string/abc-123-def') { respond_with200({ 'test' => 'ok' }) }
105
105
  stubs.get('/api/api-version-string/api-resource-name-string/bad-123-id') do
106
- [404, {}, { 'error' => 'blahblah_not_found' }]
106
+ raise Faraday::ResourceNotFound
107
107
  end
108
108
  allow(resources).to receive(:load_from_file_by_id).and_return({ 'load' => 'ok' })
109
109
  end
@@ -126,7 +126,7 @@ describe DatadogBackup::Resources do
126
126
  before do
127
127
  allow(resources).to receive(:load_from_file_by_id).and_return({ 'load' => 'ok' })
128
128
  stubs.put('/api/api-version-string/api-resource-name-string/bad-123-id') do
129
- [404, {}, { 'error' => 'id not found' }]
129
+ raise Faraday::ResourceNotFound
130
130
  end
131
131
  stubs.post('/api/api-version-string/api-resource-name-string', { 'load' => 'ok' }) do
132
132
  respond_with200({ 'id' => 'my-new-id' })
@@ -0,0 +1,207 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe DatadogBackup::SLOs do
6
+ let(:stubs) { Faraday::Adapter::Test::Stubs.new }
7
+ let(:api_client_double) { Faraday.new { |f| f.adapter :test, stubs } }
8
+ let(:tempdir) { Dir.mktmpdir }
9
+ let(:slos) do
10
+ slos = described_class.new(
11
+ action: 'backup',
12
+ backup_dir: tempdir,
13
+ output_format: :json,
14
+ resources: []
15
+ )
16
+ allow(slos).to receive(:api_service).and_return(api_client_double)
17
+ return slos
18
+ end
19
+ let(:fetched_slos) do
20
+ {
21
+ "data"=>[
22
+ {"id"=>"abc-123", "name"=>"CI Stability", "tags"=>["kind:availability", "team:my_team"], "monitor_tags"=>[], "thresholds"=>[{"timeframe"=>"7d", "target"=>98.0, "target_display"=>"98."}, {"timeframe"=>"30d", "target"=>98.0, "target_display"=>"98."}, {"timeframe"=>"90d", "target"=>98.0, "target_display"=>"98."}], "type"=>"metric", "type_id"=>1, "description"=>"something helpful", "timeframe"=>"30d", "target_threshold"=>98.0, "query"=>{"denominator"=>"sum:metric.ci_things{*}.as_count()", "numerator"=>"sum:metric.ci_things{*}.as_count()-sum:metric.ci_things{infra_failure}.as_count()"}, "creator"=>{"name"=>"Thelma Patterson", "handle"=>"thelma.patterson@example.com", "email"=>"thelma.patterson@example.com"}, "created_at"=>1571335531, "modified_at"=>1687844157},
23
+ {"id"=>"sbc-124", "name"=>"A Latency SLO", "tags"=>["team:my_team", "kind:latency"], "monitor_tags"=>[], "thresholds"=>[{"timeframe"=>"7d", "target"=>95.0, "target_display"=>"95."}, {"timeframe"=>"30d", "target"=>95.0, "target_display"=>"95."}, {"timeframe"=>"90d", "target"=>95.0, "target_display"=>"95."}], "type"=>"monitor", "type_id"=>0, "description"=>"", "timeframe"=>"30d", "target_threshold"=>95.0, "monitor_ids"=>[13158755], "creator"=>{"name"=>"Louise Montague", "handle"=>"louise.montague@example.com", "email"=>"louise.montague@example.com"}, "created_at"=>1573162531, "modified_at"=>1685819875}
24
+ ],
25
+ "errors"=>[],
26
+ "metadata"=>{"page"=>{"total_count"=>359, "total_filtered_count"=>359}}
27
+ }
28
+ end
29
+ let(:slo_abc_123) do
30
+ {
31
+ "id" => "abc-123",
32
+ "name" => "CI Stability",
33
+ "tags" => [
34
+ "kind:availability",
35
+ "team:my_team",
36
+ ],
37
+ "monitor_tags" => [],
38
+ "thresholds" => [
39
+ {
40
+ "timeframe" => "7d",
41
+ "target" => 98.0,
42
+ "target_display" => "98."
43
+ },
44
+ {
45
+ "timeframe" => "30d",
46
+ "target" => 98.0,
47
+ "target_display" => "98."
48
+ },
49
+ {
50
+ "timeframe" => "90d",
51
+ "target" => 98.0,
52
+ "target_display" => "98."
53
+ }
54
+ ],
55
+ "type" => "metric",
56
+ "type_id" => 1,
57
+ "description" => "something helpful",
58
+ "timeframe" => "30d",
59
+ "target_threshold" => 98.0,
60
+ "query" => {
61
+ "denominator" => "sum:metric.ci_things{*}.as_count()",
62
+ "numerator" => "sum:metric.ci_things{*}.as_count()-sum:metric.ci_things{infra_failure}.as_count()"
63
+ },
64
+ "creator" => {
65
+ "name" => "Thelma Patterson",
66
+ "handle" => "thelma.patterson@example.com",
67
+ "email" => "thelma.patterson@example.com"
68
+ },
69
+ "created_at" => 1571335531,
70
+ "modified_at" => 1687844157
71
+ }
72
+ end
73
+ let(:slo_sbc_124) do
74
+ {
75
+ "id" => "sbc-124",
76
+ "name" => "A Latency SLO",
77
+ "tags" => [
78
+ "kind:latency",
79
+ "team:my_team",
80
+ ],
81
+ "monitor_tags" => [],
82
+ "thresholds" => [
83
+ {
84
+ "timeframe" => "7d",
85
+ "target" => 98.0,
86
+ "target_display" => "98."
87
+ },
88
+ {
89
+ "timeframe" => "30d",
90
+ "target" => 98.0,
91
+ "target_display" => "98."
92
+ },
93
+ {
94
+ "timeframe" => "90d",
95
+ "target" => 98.0,
96
+ "target_display" => "98."
97
+ }
98
+ ],
99
+ "type" => "monitor",
100
+ "type_id"=>0,
101
+ "description"=>"",
102
+ "timeframe"=>"30d",
103
+ "target_threshold"=>95.0,
104
+ "monitor_ids"=>[ 13158755 ],
105
+ "creator"=>{
106
+ "name"=>"Louise Montague",
107
+ "handle"=>"louise.montague@example.com",
108
+ "email"=>"louise.montague@example.com"
109
+ },
110
+ "created_at"=>1573162531,
111
+ "modified_at"=>1685819875
112
+ }
113
+ end
114
+ let(:slo_abc_123_response) do
115
+ { "data" => slo_abc_123, "errors" => [] }
116
+ end
117
+ let(:slo_sbc_124_response) do
118
+ { "data" => slo_sbc_124, "errors" => [] }
119
+ end
120
+ let(:all_slos) { respond_with200(fetched_slos) }
121
+ let(:example_slo1) { respond_with200(slo_abc_123_response) }
122
+ let(:example_slo2) { respond_with200(slo_sbc_124_response) }
123
+
124
+ before do
125
+ stubs.get('/api/v1/slo') { all_slos }
126
+ stubs.get('/api/v1/slo/abc-123') { example_slo1 }
127
+ stubs.get('/api/v1/slo/sbc-124') { example_slo2 }
128
+ end
129
+
130
+ describe '#backup' do
131
+ subject { slos.backup }
132
+
133
+ it 'is expected to create two files' do
134
+ file1 = instance_double(File)
135
+ allow(File).to receive(:open).with(slos.filename('abc-123'), 'w').and_return(file1)
136
+ allow(file1).to receive(:write)
137
+ allow(file1).to receive(:close)
138
+
139
+ file2 = instance_double(File)
140
+ allow(File).to receive(:open).with(slos.filename('sbc-124'), 'w').and_return(file2)
141
+ allow(file2).to receive(:write)
142
+ allow(file2).to receive(:close)
143
+
144
+ slos.backup
145
+ expect(file1).to have_received(:write).with(::JSON.pretty_generate(slo_abc_123.deep_sort))
146
+ expect(file2).to have_received(:write).with(::JSON.pretty_generate(slo_sbc_124.deep_sort))
147
+ end
148
+ end
149
+
150
+ describe '#filename' do
151
+ subject { slos.filename('abc-123') }
152
+
153
+ it { is_expected.to eq("#{tempdir}/slos/abc-123.json") }
154
+ end
155
+
156
+ describe '#get_by_id' do
157
+ subject { slos.get_by_id('abc-123') }
158
+
159
+ it { is_expected.to eq slo_abc_123 }
160
+ end
161
+
162
+ describe '#diff' do
163
+ it 'calls the api only once' do
164
+ slos.write_file('{"a":"b"}', slos.filename('abc-123'))
165
+ expect(slos.diff('abc-123')).to eq(<<~EODASH
166
+ ---
167
+ -created_at: 1571335531
168
+ -creator:
169
+ - email: thelma.patterson@example.com
170
+ - handle: thelma.patterson@example.com
171
+ - name: Thelma Patterson
172
+ -description: something helpful
173
+ -id: abc-123
174
+ -monitor_tags: []
175
+ -name: CI Stability
176
+ -query:
177
+ - denominator: sum:metric.ci_things{*}.as_count()
178
+ - numerator: sum:metric.ci_things{*}.as_count()-sum:metric.ci_things{infra_failure}.as_count()
179
+ -tags:
180
+ -- kind:availability
181
+ -- team:my_team
182
+ -target_threshold: 98.0
183
+ -thresholds:
184
+ -- target: 98.0
185
+ - target_display: '98.'
186
+ - timeframe: 30d
187
+ -- target: 98.0
188
+ - target_display: '98.'
189
+ - timeframe: 7d
190
+ -- target: 98.0
191
+ - target_display: '98.'
192
+ - timeframe: 90d
193
+ -timeframe: 30d
194
+ -type: metric
195
+ -type_id: 1
196
+ +a: b
197
+ EODASH
198
+ .chomp)
199
+ end
200
+ end
201
+
202
+ describe '#except' do
203
+ subject { slos.except({ :a => :b, 'modified_at' => :c, 'url' => :d }) }
204
+
205
+ it { is_expected.to eq({ a: :b }) }
206
+ end
207
+ end
@@ -231,7 +231,7 @@ describe DatadogBackup::Synthetics do
231
231
 
232
232
  before do
233
233
  synthetics.write_file(synthetics.dump({ 'name' => 'restore-invalid-id', 'type' => 'api' }), synthetics.filename('restore-invalid-id'))
234
- stubs.put('/api/v1/synthetics/tests/api/restore-invalid-id') { [404, {}, ''] }
234
+ stubs.put('/api/v1/synthetics/tests/api/restore-invalid-id') { raise Faraday::ResourceNotFound }
235
235
  stubs.post('/api/v1/synthetics/tests/api') { respond_with200({ 'public_id' => 'restore-valid-id' }) }
236
236
  allow(synthetics).to receive(:create).and_call_original
237
237
  allow(synthetics).to receive(:all).and_return([api_test, browser_test, { 'public_id' => 'restore-valid-id', 'type' => 'api' }])
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datadog_backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kamran Farhadi
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-02-10 00:00:00.000000000 Z
12
+ date: 2023-08-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: amazing_print
@@ -229,6 +229,7 @@ files:
229
229
  - lib/datadog_backup/monitors.rb
230
230
  - lib/datadog_backup/options.rb
231
231
  - lib/datadog_backup/resources.rb
232
+ - lib/datadog_backup/slos.rb
232
233
  - lib/datadog_backup/synthetics.rb
233
234
  - lib/datadog_backup/thread_pool.rb
234
235
  - lib/datadog_backup/version.rb
@@ -239,6 +240,7 @@ files:
239
240
  - spec/datadog_backup/deprecations_spec.rb
240
241
  - spec/datadog_backup/local_filesystem_spec.rb
241
242
  - spec/datadog_backup/monitors_spec.rb
243
+ - spec/datadog_backup/slos_spec.rb
242
244
  - spec/datadog_backup/synthetics_spec.rb
243
245
  - spec/datadog_backup_bin_spec.rb
244
246
  - spec/spec_helper.rb