datadog_backup 0.10.2 → 0.10.3

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: 54c83b8ce7b212ad43f91a1619363eb683b58cbf7089a24f05b56a5a6a72d250
4
- data.tar.gz: 82986e61ff8d93fed519d03c6b38095cce0c97f0981274bb904d94f7b762e056
3
+ metadata.gz: 5833879f7fbadabad6111eae29c4e487e0d51a0b0802dcd61124985bae70cd23
4
+ data.tar.gz: 5fdc04d6d1bf28671a4a2000a20c6f1513cbf27f406def181550d956cd270d48
5
5
  SHA512:
6
- metadata.gz: ec5087654fd806a8cb562912d460dfbdf7c9e1bdb9d75728ce310b4c0b20ba690b7ccb42584c1c28dc4b5db6ba49cfe46d6cb64ad623e6dfb8939ad07dfed56c
7
- data.tar.gz: 072762f43d59b7727baccbff8a7273a84ab0b0e1fe0fbd1787486d6b1b08faad9b492b39d68a83be90637a09a6f2bbc436f386775b9da4751308253b0ff6bbc8
6
+ metadata.gz: 9cc3623d68ec1e5c5872bda3d2e341c9e5d2b1bb954e16d08a2f4d289adb01751f0b7f3f2b0549bfba2fbf91d5ab86c233b59c74f2124642c4cd52b0074a1824
7
+ data.tar.gz: 9ef0f647d84441c750a65a9cf18130fe89daad15af18ad0e07d61b74caf13ba160388551b4908609bf95a6113f3f39de854b5347becad449fcef0df70782fee2
data/.gitignore CHANGED
@@ -117,3 +117,5 @@ Gemfile.lock
117
117
 
118
118
  spec/helpers/failures.txt
119
119
  backup/
120
+
121
+ .envrc
@@ -1,3 +1,10 @@
1
+ ## [0.10.3](https://github.com/scribd/datadog_backup/compare/v0.10.2...v0.10.3) (2020-12-11)
2
+
3
+
4
+ ### Performance Improvements
5
+
6
+ * coerce patch release ([bc86649](https://github.com/scribd/datadog_backup/commit/bc86649b874cd5be1da2f6bc0d1b1ecd0728676c))
7
+
1
8
  ## [0.10.2](https://github.com/scribd/datadog_backup/compare/v0.10.1...v0.10.2) (2020-11-03)
2
9
 
3
10
 
data/Guardfile CHANGED
@@ -17,7 +17,7 @@
17
17
  #
18
18
  # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
19
19
 
20
- # Note: The cmd option is now required due to the increasing number of ways
20
+ # NOTE: The cmd option is now required due to the increasing number of ways
21
21
  # rspec may be run, below are examples of the most common uses.
22
22
  # * bundler: 'bundle exec rspec'
23
23
  # * bundler binstubs: 'bin/rspec'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
- gem 'datadog_backup', :source => 'https://scribd.jfrog.io/artifactory/gems-local'
5
+ gem 'datadog_backup'
@@ -91,7 +91,7 @@ module DatadogBackup
91
91
  end
92
92
 
93
93
  def matching_resource_instance(klass)
94
- resource_instances.select { |resource_instance| resource_instance.class == klass }.first
94
+ resource_instances.select { |resource_instance| resource_instance.instance_of?(klass) }.first
95
95
  end
96
96
 
97
97
  def resource_instances
@@ -117,7 +117,7 @@ module DatadogBackup
117
117
  exit
118
118
  when 'r'
119
119
  puts "Restoring #{id} to Datadog."
120
- definitive_resource_instance(id).update_with_200(id, definitive_resource_instance(id).load_from_file_by_id(id))
120
+ definitive_resource_instance(id).update(id, definitive_resource_instance(id).load_from_file_by_id(id))
121
121
  when 'd'
122
122
  puts "Downloading #{id} from Datadog."
123
123
  definitive_resource_instance(id).get_and_write_file(id)
@@ -24,55 +24,48 @@ module DatadogBackup
24
24
  raise 'subclass is expected to implement #backup'
25
25
  end
26
26
 
27
- # Calls out to Datadog and checks for a '200' response
28
- def client_with_200(method, *id)
29
- max_retries = 6
30
- retries ||= 0
31
-
32
- response = client.send(method, *id)
33
-
34
- # logger.debug response
35
- raise "Method #{method} failed with error #{response}" unless response[0] == '200'
36
-
37
- response[1]
38
- rescue ::Net::OpenTimeout => e
39
- if (retries += 1) <= max_retries
40
- sleep(0.1 * retries**5) # 0.1, 3.2, 24.3, 102.4 seconds per retry
41
- retry
42
- else
43
- raise "Method #{method} failed with error #{e.message}"
44
- end
45
- end
46
-
47
27
  # Returns the diffy diff.
48
28
  # Optionally, supply an array of keys to remove from comparison
49
- def diff(id, banlist = [])
50
- current = except(get_by_id(id), banlist).deep_sort.to_yaml
51
- filesystem = except(load_from_file_by_id(id), banlist).deep_sort.to_yaml
29
+ def diff(id)
30
+ current = except(get_by_id(id)).deep_sort.to_yaml
31
+ filesystem = except(load_from_file_by_id(id)).deep_sort.to_yaml
52
32
  result = ::Diffy::Diff.new(current, filesystem, include_plus_and_minus_in_html: true).to_s(diff_format)
53
33
  logger.debug("Compared ID #{id} and found #{result}")
54
34
  result
55
35
  end
56
36
 
57
37
  # Returns a hash with banlist elements removed
58
- def except(hash, banlist)
38
+ def except(hash)
59
39
  hash.tap do # tap returns self
60
- banlist.each do |key|
40
+ @banlist.each do |key|
61
41
  hash.delete(key) # delete returns the value at the deleted key, hence the tap wrapper
62
42
  end
63
43
  end
64
44
  end
65
45
 
46
+ def get(id)
47
+ with_200 do
48
+ api_service.request(Net::HTTP::Get, "/api/#{api_version}/#{api_resource_name}/#{id}", nil, nil, false)
49
+ end
50
+ end
51
+
52
+ def get_all
53
+ with_200 do
54
+ api_service.request(Net::HTTP::Get, "/api/#{api_version}/#{api_resource_name}", nil, nil, false)
55
+ end
56
+ end
57
+
66
58
  def get_and_write_file(id)
67
59
  write_file(dump(get_by_id(id)), filename(id))
68
60
  end
69
61
 
70
- def get_by_id(_id)
71
- raise 'subclass is expected to implement #get_by_id(id)'
62
+ def get_by_id(id)
63
+ except(get(id))
72
64
  end
73
65
 
74
66
  def initialize(options)
75
67
  @options = options
68
+ @banlist = []
76
69
  ::FileUtils.mkdir_p(mydir)
77
70
  end
78
71
 
@@ -80,25 +73,20 @@ module DatadogBackup
80
73
  self.class.to_s.split(':').last.downcase
81
74
  end
82
75
 
83
- def restore
84
- raise 'subclass is expected to implement #restore'
85
- end
86
-
76
+ # Calls out to Datadog and checks for a '200' response
87
77
  def update(id, body)
88
- api_service.request(Net::HTTP::Put, "/api/#{api_version}/#{api_resource_name}/#{id}", nil, body, true)
78
+ with_200 do
79
+ api_service.request(Net::HTTP::Put, "/api/#{api_version}/#{api_resource_name}/#{id}", nil, body, true)
80
+ end
81
+ logger.warn 'Successfully restored to datadog.'
89
82
  end
90
83
 
91
- # Calls out to Datadog and checks for a '200' response
92
- def update_with_200(id, body)
84
+ def with_200
93
85
  max_retries = 6
94
86
  retries ||= 0
95
87
 
96
- response = update(id, body)
97
-
98
- # logger.debug response
99
- raise "Update failed with error #{response}" unless response[0] == '200'
100
-
101
- logger.warn "Successfully restored #{id} to datadog."
88
+ response = yield
89
+ raise "Request failed with error #{response}" unless response[0] == '200'
102
90
 
103
91
  response[1]
104
92
  rescue ::Net::OpenTimeout => e
@@ -106,8 +94,9 @@ module DatadogBackup
106
94
  sleep(0.1 * retries**5) # 0.1, 3.2, 24.3, 102.4 seconds per retry
107
95
  retry
108
96
  else
109
- raise "Update failed with error #{e.message}"
97
+ raise "Request failed with error #{e.message}"
110
98
  end
111
99
  end
100
+
112
101
  end
113
102
  end
@@ -2,10 +2,8 @@
2
2
 
3
3
  module DatadogBackup
4
4
  class Dashboards < Core
5
- BANLIST = %w[modified_at url]
6
-
7
5
  def all_boards
8
- client_with_200(:get_all_boards).fetch('dashboards')
6
+ get_all.fetch('dashboards')
9
7
  end
10
8
 
11
9
  def api_service
@@ -37,14 +35,9 @@ module DatadogBackup
37
35
  Concurrent::Promises.zip(*futures).value!
38
36
  end
39
37
 
40
- def diff(id)
41
- super(id, BANLIST)
42
- end
43
-
44
- def get_by_id(id)
45
- except(client_with_200(:get_board, id), BANLIST)
38
+ def initialize(options)
39
+ super(options)
40
+ @banlist = %w[modified_at url].freeze
46
41
  end
47
-
48
- def restore!; end
49
42
  end
50
43
  end
@@ -31,9 +31,10 @@ module DatadogBackup
31
31
  end
32
32
 
33
33
  def dump(object)
34
- if output_format == :json
34
+ case output_format
35
+ when :json
35
36
  JSON.pretty_generate(object.deep_sort)
36
- elsif output_format == :yaml
37
+ when :yaml
37
38
  YAML.dump(object.deep_sort)
38
39
  else
39
40
  raise 'invalid output_format specified or not specified'
@@ -45,7 +46,7 @@ module DatadogBackup
45
46
  end
46
47
 
47
48
  def file_type(filepath)
48
- ::File.extname(filepath).strip.downcase[1..-1].to_sym
49
+ ::File.extname(filepath).strip.downcase[1..].to_sym
49
50
  end
50
51
 
51
52
  def find_file_by_id(id)
@@ -53,9 +54,10 @@ module DatadogBackup
53
54
  end
54
55
 
55
56
  def load_from_file(string, output_format)
56
- if output_format == :json
57
+ case output_format
58
+ when :json
57
59
  JSON.parse(string)
58
- elsif output_format == :yaml
60
+ when :yaml
59
61
  YAML.safe_load(string)
60
62
  else
61
63
  raise 'invalid output_format specified or not specified'
@@ -1,12 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatadogBackup
4
- class Monitors < Core
5
- API_SERVICE_NAME = :@monitor_svc
6
- BANLIST = %w[overall_state overall_state_modified matching_downtimes modified]
7
-
4
+ class Monitors < Core
8
5
  def all_monitors
9
- @all_monitors ||= client_with_200(:get_all_monitors)
6
+ @all_monitors ||= get_all
10
7
  end
11
8
 
12
9
  def api_service
@@ -29,14 +26,13 @@ module DatadogBackup
29
26
  end
30
27
  end
31
28
 
32
- def diff(id)
33
- super(id, BANLIST)
34
- end
35
-
36
29
  def get_by_id(id)
37
- except(all_monitors.select { |monitor| monitor['id'].to_s == id.to_s }.first, BANLIST)
30
+ except(all_monitors.select { |monitor| monitor['id'].to_s == id.to_s }.first)
38
31
  end
39
32
 
40
- def restore!; end
33
+ def initialize(options)
34
+ super(options)
35
+ @banlist = %w[overall_state overall_state_modified matching_downtimes modified].freeze
36
+ end
41
37
  end
42
38
  end
@@ -11,7 +11,7 @@ module DatadogBackup
11
11
 
12
12
  def self.watcher(logger)
13
13
  Thread.new(TPOOL) do |pool|
14
- while pool.queue_length > 0
14
+ while pool.queue_length.positive?
15
15
  sleep 2
16
16
  logger.info("#{pool.queue_length} tasks remaining for execution.")
17
17
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatadogBackup
4
- VERSION = '0.10.2'
4
+ VERSION = '0.10.3'
5
5
  end
@@ -3,6 +3,7 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe DatadogBackup::Cli do
6
+ let(:api_service_double) { double(Dogapi::APIService) }
6
7
  let(:client_double) { double }
7
8
  let(:tempdir) { Dir.mktmpdir }
8
9
  let(:options) do
@@ -43,8 +44,10 @@ describe DatadogBackup::Cli do
43
44
  dashboards.write_file('{"text": "diff"}', "#{tempdir}/dashboards/alsostillthere.json")
44
45
  dashboards.write_file('{"text": "diff"}', "#{tempdir}/dashboards/deleted.json")
45
46
 
46
- allow(client_double).to receive(:get_all_boards).and_return(all_boards)
47
- allow(client_double).to receive(:get_board).and_return(['200', {}])
47
+ allow(client_double).to receive(:instance_variable_get).with(:@dashboard_service).and_return(api_service_double)
48
+ allow(api_service_double).to receive(:request).with(Net::HTTP::Get, "/api/v1/dashboard", nil, nil, false).and_return(all_boards)
49
+ allow(api_service_double).to receive(:request).with(Net::HTTP::Get, "/api/v1/dashboard/stillthere", nil, nil, false).and_return(['200', {}])
50
+ allow(api_service_double).to receive(:request).with(Net::HTTP::Get, "/api/v1/dashboard/alsostillthere", nil, nil, false).and_return(['200', {}])
48
51
  end
49
52
 
50
53
  it 'deletes the file locally as well' do
@@ -83,17 +86,16 @@ describe DatadogBackup::Cli do
83
86
 
84
87
  example 'starts interactive restore' do
85
88
  allow($stdin).to receive(:gets).and_return('q')
86
- begin
87
- expect { subject }.to(
88
- output(/\(r\)estore to Datadog, overwrite local changes and \(d\)ownload, \(s\)kip, or \(q\)uit\?/).to_stdout
89
- .and(raise_error(SystemExit))
90
- )
91
- end
89
+
90
+ expect { subject }.to(
91
+ output(/\(r\)estore to Datadog, overwrite local changes and \(d\)ownload, \(s\)kip, or \(q\)uit\?/).to_stdout
92
+ .and(raise_error(SystemExit))
93
+ )
92
94
  end
93
95
 
94
96
  example 'restore' do
95
97
  allow($stdin).to receive(:gets).and_return('r')
96
- expect(dashboards).to receive(:update_with_200).with('diffs1', { 'text' => 'diff' })
98
+ expect(dashboards).to receive(:update).with('diffs1', { 'text' => 'diff' })
97
99
  subject
98
100
  end
99
101
  example 'download' do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe DatadogBackup::Core do
@@ -22,21 +24,15 @@ describe DatadogBackup::Core do
22
24
  it { is_expected.to eq client_double }
23
25
  end
24
26
 
25
- describe '#client_with_200' do
26
- subject { core.client_with_200(:get_all_boards) }
27
-
27
+ describe '#with_200' do
28
28
  context 'with 200' do
29
- before(:example) do
30
- allow(client_double).to receive(:get_all_boards).and_return(['200', { foo: :bar }])
31
- end
29
+ subject { core.with_200 {['200', { foo: :bar }]} }
32
30
 
33
31
  it { is_expected.to eq({ foo: :bar }) }
34
32
  end
35
33
 
36
34
  context 'with not 200' do
37
- before(:example) do
38
- allow(client_double).to receive(:get_all_boards).and_return(['401', {}])
39
- end
35
+ subject { core.with_200 {['400', "Error message"]} }
40
36
 
41
37
  it 'raises an error' do
42
38
  expect { subject }.to raise_error(RuntimeError)
@@ -50,34 +46,21 @@ describe DatadogBackup::Core do
50
46
  core.write_file('{"text": "diff2", "extra": "diff2"}', "#{tempdir}/core/diff.json")
51
47
  end
52
48
 
53
- context 'without banlist' do
54
- subject { core.diff('diff') }
55
- it {
56
- is_expected.to eq <<~EOF
57
- ---
58
- -extra: diff1
59
- -text: diff1
60
- +extra: diff2
61
- +text: diff2
62
- EOF
63
- }
64
- end
65
-
66
- context 'with banlist' do
67
- subject { core.diff('diff', ['extra']) }
68
- it {
69
- is_expected.to eq <<~EOF
70
- ---
71
- -text: diff1
72
- +text: diff2
73
- EOF
74
- }
75
- end
49
+ subject { core.diff('diff') }
50
+ it {
51
+ is_expected.to eq <<~EOF
52
+ ---
53
+ -extra: diff1
54
+ -text: diff1
55
+ +extra: diff2
56
+ +text: diff2
57
+ EOF
58
+ }
76
59
  end
77
60
 
78
61
  describe '#except' do
79
- subject { core.except({ a: :b, b: :c }, [:b]) }
80
- it { is_expected.to eq({ a: :b }) }
62
+ subject { core.except({ a: :b, b: :c }) }
63
+ it { is_expected.to eq({ a: :b, b: :c }) }
81
64
  end
82
65
 
83
66
  describe '#initialize' do
@@ -94,7 +77,7 @@ describe DatadogBackup::Core do
94
77
  end
95
78
 
96
79
  describe '#update' do
97
- subject { core.update_with_200('abc-123-def', '{"a": "b"}') }
80
+ subject { core.update('abc-123-def', '{"a": "b"}') }
98
81
  example 'it calls Dogapi::APIService.request' do
99
82
  stub_const('Dogapi::APIService::API_VERSION', 'v1')
100
83
  allow(core).to receive(:api_service).and_return(api_service_double)
@@ -3,6 +3,7 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe DatadogBackup::Dashboards do
6
+ let(:api_service_double) { double(Dogapi::APIService) }
6
7
  let(:client_double) { double }
7
8
  let(:tempdir) { Dir.mktmpdir }
8
9
  let(:dashboards) do
@@ -59,8 +60,9 @@ describe DatadogBackup::Dashboards do
59
60
  }
60
61
  end
61
62
  before(:example) do
62
- allow(client_double).to receive(:get_all_boards).and_return(all_boards)
63
- allow(client_double).to receive(:get_board).and_return(example_dashboard)
63
+ allow(client_double).to receive(:instance_variable_get).with(:@dashboard_service).and_return(api_service_double)
64
+ allow(api_service_double).to receive(:request).with(Net::HTTP::Get, "/api/v1/dashboard", nil, nil, false).and_return(all_boards)
65
+ allow(api_service_double).to receive(:request).with(Net::HTTP::Get, "/api/v1/dashboard/abc-123-def", nil, nil, false).and_return(example_dashboard)
64
66
  end
65
67
 
66
68
  describe '#backup' do
@@ -79,22 +81,34 @@ describe DatadogBackup::Dashboards do
79
81
  describe '#all_boards' do
80
82
  subject { dashboards.all_boards }
81
83
 
82
- it 'calls get_all_boards' do
83
- subject
84
- expect(client_double).to have_received(:get_all_boards)
85
- end
86
-
87
84
  it { is_expected.to eq [dashboard_description] }
88
85
  end
89
86
 
90
87
  describe '#diff' do
91
88
  it 'calls the api only once' do
92
89
  dashboards.write_file('{"a":"b"}', dashboards.filename('abc-123-def'))
93
- dashboards.diff('abc-123-def')
94
- expect(client_double).to have_received(:get_board).exactly(1).times
90
+ expect(dashboards.diff('abc-123-def')).to eq(<<~EOF
91
+ ---
92
+ -description: example dashboard
93
+ -graphs:
94
+ -- definition:
95
+ - requests:
96
+ - - q: min:foo.bar{a:b}
97
+ - stacked: false
98
+ - viz: timeseries
99
+ - title: example graph
100
+ -title: example dashboard
101
+ +a: b
102
+ EOF
103
+ )
95
104
  end
96
105
  end
97
106
 
107
+ describe '#except' do
108
+ subject { dashboards.except({ :a => :b, 'modified_at' => :c, 'url' => :d }) }
109
+ it { is_expected.to eq({ a: :b }) }
110
+ end
111
+
98
112
  describe '#get_by_id' do
99
113
  subject { dashboards.get_by_id('abc-123-def') }
100
114
  it { is_expected.to eq board_abc_123_def }
@@ -3,6 +3,7 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe DatadogBackup::Monitors do
6
+ let(:api_service_double) { double(Dogapi::APIService) }
6
7
  let(:client_double) { double }
7
8
  let(:tempdir) { Dir.mktmpdir }
8
9
  let(:monitors) do
@@ -47,19 +48,15 @@ describe DatadogBackup::Monitors do
47
48
  monitor_description
48
49
  ]
49
50
  end
51
+
50
52
  before(:example) do
51
- allow(client_double).to receive(:get_all_monitors).and_return(all_monitors)
52
- allow(client_double).to receive(:get_monitor).and_return(example_monitor)
53
+ allow(client_double).to receive(:instance_variable_get).with(:@monitor_svc).and_return(api_service_double)
54
+ allow(api_service_double).to receive(:request).with(Net::HTTP::Get, "/api/v1/monitor", nil, nil, false).and_return(all_monitors)
55
+ allow(api_service_double).to receive(:request).with(Net::HTTP::Get, "/api/v1/dashboard/123455", nil, nil, false).and_return(example_monitor)
53
56
  end
54
57
 
55
58
  describe '#all_monitors' do
56
59
  subject { monitors.all_monitors }
57
-
58
- it 'calls get_all_monitors' do
59
- subject
60
- expect(client_double).to have_received(:get_all_monitors)
61
- end
62
-
63
60
  it { is_expected.to eq [monitor_description] }
64
61
  end
65
62
 
@@ -76,10 +73,10 @@ describe DatadogBackup::Monitors do
76
73
  end
77
74
  end
78
75
 
79
- describe '#diff' do
76
+ describe '#diff and #except' do
80
77
  example 'it ignores `overall_state` and `overall_state_modified`' do
81
78
  monitors.write_file(monitors.dump(monitor_description), monitors.filename(123_455))
82
- allow(client_double).to receive(:get_all_monitors).and_return(
79
+ allow(api_service_double).to receive(:request).and_return(
83
80
  [
84
81
  '200',
85
82
  [
@@ -88,8 +85,8 @@ describe DatadogBackup::Monitors do
88
85
  'message' => 'foo',
89
86
  'id' => 123_455,
90
87
  'name' => 'foo',
91
- 'overall_state' => 'NO DATA',
92
- 'overall_state_modified' => '2020-07-27T22:55:55+00:00'
88
+ 'overall_state' => 'ZZZZZZZZZZZZZZZZZZZZZZZZZZZ',
89
+ 'overall_state_modified' => '9999-07-27T22:55:55+00:00'
93
90
  }
94
91
  ]
95
92
  ]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  $LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
2
4
 
3
5
  require 'datadog_backup'
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: 0.10.2
4
+ version: 0.10.3
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: 2020-11-03 00:00:00.000000000 Z
12
+ date: 2020-12-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: amazing_print