datadog_backup 0.10.1 → 1.0.0.alpha.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: bea05a2afebfd74199cde145437e6fdfb60bf079a2ff31eec43b51898532c72b
4
- data.tar.gz: 2eb15f9a5f94b657cce02ecfe619b92e71a7adb4edb4ded832d5f1fbad340a37
3
+ metadata.gz: 5db8a5e804f30996a50247c24352e3fc1075971d141e37a2093987c510e9c9f1
4
+ data.tar.gz: 41c6f43d1c15c7e789aabe5a5b9bd34d45e5f5712d4ada557fb32edaeac4dd11
5
5
  SHA512:
6
- metadata.gz: 86d475400eac4ae5794296748f0792cbbabc1eecf24b3f129c97300cf5a1388f478b1e378d7ac81f0e0528b7782f8e5041b8bc77f09ff77cb8bf69c41d0c9949
7
- data.tar.gz: 79c2ee0e0be5a8d49be57b5b12d33ab5a164894376669be20eecb899ecb57bd142f56242e9668463ec0c1c6f47a803c85c5cea272f3d1a13447d11da937e31d4
6
+ metadata.gz: f34c936e1a48773a4e0d600cfb23414c07b7197d7e72f9e1f7c3e1efd27c6b25b2ec9ab8643228b6b6d055f1b9c83762cf62e743418708ca8a02046405f256f7
7
+ data.tar.gz: e3f2a91bf80a2ea6c265766b55e07b01ac885a002a4be41f3f6dc7dc9574bdd394a158c1258887814b387290915f9843dc5a07759dc74d5c9d5c491c155b4ffe
@@ -11,10 +11,10 @@ jobs:
11
11
 
12
12
  steps:
13
13
  - uses: actions/checkout@v2
14
- - name: Set up Ruby 2.7.1
15
- uses: actions/setup-ruby@v1.1.1
14
+ - name: Set up Ruby 2.7.2
15
+ uses: actions/setup-ruby@v1.1.2
16
16
  with:
17
- ruby-version: 2.7.1
17
+ ruby-version: 2.7.2
18
18
  - name: Test with Rspec
19
19
  run: |
20
20
  gem install --no-document bundler
@@ -27,10 +27,10 @@ jobs:
27
27
 
28
28
  steps:
29
29
  - uses: actions/checkout@v2
30
- - name: Set up Ruby 2.7.1
31
- uses: actions/setup-ruby@v1.1.1
30
+ - name: Set up Ruby 2.7.2
31
+ uses: actions/setup-ruby@v1.1.2
32
32
  with:
33
- ruby-version: 2.7.1
33
+ ruby-version: 2.7.2
34
34
  - name: Build with bundler
35
35
  run: |
36
36
  gem install --no-document bundler
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,37 @@
1
+ # [1.0.0-alpha.1](https://github.com/scribd/datadog_backup/compare/v0.11.0...v1.0.0-alpha.1) (2021-01-17)
2
+
3
+
4
+ ### Features
5
+
6
+ * If resource doesn't exist in Datadog, the resource is recreated. ([95d9699](https://github.com/scribd/datadog_backup/commit/95d9699e3fe6a6ecae0d6690dce225680f1d6a8a))
7
+
8
+
9
+ ### BREAKING CHANGES
10
+
11
+ * `datadog-backup` used to exit with an error if a resource
12
+ wasn't found in Datadog.
13
+
14
+ # [0.11.0](https://github.com/scribd/datadog_backup/compare/v0.10.3...v0.11.0) (2021-01-12)
15
+
16
+
17
+ ### Features
18
+
19
+ * Add force-restore flag to allow running in automation ([#46](https://github.com/scribd/datadog_backup/issues/46)) ([e067386](https://github.com/scribd/datadog_backup/commit/e0673862b6f6d86297e1352faaee872f2c4884c8))
20
+
21
+ ## [0.10.3](https://github.com/scribd/datadog_backup/compare/v0.10.2...v0.10.3) (2020-12-11)
22
+
23
+
24
+ ### Performance Improvements
25
+
26
+ * coerce patch release ([bc86649](https://github.com/scribd/datadog_backup/commit/bc86649b874cd5be1da2f6bc0d1b1ecd0728676c))
27
+
28
+ ## [0.10.2](https://github.com/scribd/datadog_backup/compare/v0.10.1...v0.10.2) (2020-11-03)
29
+
30
+
31
+ ### Bug Fixes
32
+
33
+ * virtual environment updates ruby 2.7.1 -> 2.7.2 ([f950dd6](https://github.com/scribd/datadog_backup/commit/f950dd67ce989bb12de5f2dbf69c6449b91f2542))
34
+
1
35
  ## [0.10.1](https://github.com/scribd/datadog_backup/compare/v0.10.0...v0.10.1) (2020-09-08)
2
36
 
3
37
 
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'
@@ -23,6 +23,7 @@ LOGGER.level = Logger::INFO
23
23
  diff_format: :color,
24
24
  resources: [DatadogBackup::Dashboards, DatadogBackup::Monitors],
25
25
  output_format: :yaml,
26
+ force_restore: false,
26
27
  logger: LOGGER
27
28
  }
28
29
 
@@ -54,7 +55,8 @@ def prereqs
54
55
  opts.on('--dashboards-only') do
55
56
  @options[:resources] = [DatadogBackup::Dashboards]
56
57
  end
57
- opts.on('--json', 'format backups as JSON instead of YAML. Does not impact `diffs` nor `restore`, but do not mix formats in the same backup-dir.') do
58
+ opts.on('--json',
59
+ 'format backups as JSON instead of YAML. Does not impact `diffs` nor `restore`, but do not mix formats in the same backup-dir.') do
58
60
  @options[:output_format] = :json
59
61
  end
60
62
  opts.on('--no-color', 'removes colored output from diff format') do
@@ -63,6 +65,9 @@ def prereqs
63
65
  opts.on('--diff-format FORMAT', 'one of `color`, `html_simple`, `html`') do |format|
64
66
  @options[:diff_format] = format.to_sym
65
67
  end
68
+ opts.on('--force-restore', 'force restore to Datadog') do
69
+ @options[:force_restore] = true
70
+ end
66
71
  end
67
72
  options.parse!
68
73
 
@@ -19,12 +19,12 @@ Gem::Specification.new do |spec|
19
19
  spec.test_files = spec.files.grep(%r{^spec/})
20
20
  spec.require_paths = ['lib']
21
21
 
22
- spec.add_dependency 'amazing_print', '1.2.1'
22
+ spec.add_dependency 'amazing_print', '1.2.2'
23
23
  spec.add_dependency 'concurrent-ruby', '1.1.7'
24
24
  spec.add_dependency 'concurrent-ruby-edge', '0.6.0'
25
25
  spec.add_dependency 'deepsort', '0.4.5'
26
26
  spec.add_dependency 'diffy', '3.4.0'
27
- spec.add_dependency 'dogapi', '1.41.0'
27
+ spec.add_dependency 'dogapi', '1.44.0'
28
28
 
29
29
  spec.add_development_dependency 'bundler'
30
30
  spec.add_development_dependency 'pry'
@@ -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
@@ -108,27 +108,30 @@ module DatadogBackup
108
108
  id, diff = *future.value!
109
109
  next unless diff
110
110
 
111
- puts '--------------------------------------------------------------------------------'
112
- puts format_diff_output([id, diff])
113
- puts '(r)estore to Datadog, overwrite local changes and (d)ownload, (s)kip, or (q)uit?'
114
- response = $stdin.gets.chomp
115
- case response
116
- when 'q'
117
- exit
118
- when 'r'
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))
121
- when 'd'
122
- puts "Downloading #{id} from Datadog."
123
- definitive_resource_instance(id).get_and_write_file(id)
124
- when 's'
125
- next
111
+ if @options[:force_restore]
112
+ definitive_resource_instance(id).restore(id)
126
113
  else
127
- puts 'Invalid response, please try again.'
114
+ puts '--------------------------------------------------------------------------------'
115
+ puts format_diff_output([id, diff])
116
+ puts '(r)estore to Datadog, overwrite local changes and (d)ownload, (s)kip, or (q)uit?'
128
117
  response = $stdin.gets.chomp
118
+ case response
119
+ when 'q'
120
+ exit
121
+ when 'r'
122
+ puts "Restoring #{id} to Datadog."
123
+ definitive_resource_instance(id).restore(id)
124
+ when 'd'
125
+ puts "Downloading #{id} from Datadog."
126
+ definitive_resource_instance(id).get_and_write_file(id)
127
+ when 's'
128
+ next
129
+ else
130
+ puts 'Invalid response, please try again.'
131
+ response = $stdin.gets.chomp
132
+ end
129
133
  end
130
134
  end
131
-
132
135
  watcher.join if watcher.status
133
136
  end
134
137
 
@@ -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,46 @@ 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'
76
+ # Calls out to Datadog and checks for a '200' response
77
+ def create(body)
78
+ result = with_200 do
79
+ api_service.request(Net::HTTP::Post, "/api/#{api_version}/#{api_resource_name}", nil, body, true)
80
+ end
81
+ logger.warn 'Successfully created in datadog.'
82
+ result
85
83
  end
86
84
 
85
+ # Calls out to Datadog and checks for a '200' response
87
86
  def update(id, body)
88
- api_service.request(Net::HTTP::Put, "/api/#{api_version}/#{api_resource_name}/#{id}", nil, body, true)
87
+ result = with_200 do
88
+ api_service.request(Net::HTTP::Put, "/api/#{api_version}/#{api_resource_name}/#{id}", nil, body, true)
89
+ end
90
+ logger.warn 'Successfully restored to datadog.'
91
+ result
89
92
  end
90
93
 
91
- # Calls out to Datadog and checks for a '200' response
92
- def update_with_200(id, body)
94
+ def restore(id)
95
+ body = load_from_file_by_id(id)
96
+ begin
97
+ update(id, body)
98
+ rescue RuntimeError => e
99
+ if e.message.include?('Request failed with error ["404"')
100
+ new_id = create(body).fetch('id')
101
+
102
+ get_and_write_file(new_id)
103
+ FileUtils.rm(find_file_by_id(id))
104
+ else
105
+ raise e.message
106
+ end
107
+ end
108
+ end
109
+
110
+ def with_200
93
111
  max_retries = 6
94
112
  retries ||= 0
95
113
 
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."
114
+ response = yield
115
+ raise "Request failed with error #{response}" unless response[0] == '200'
102
116
 
103
117
  response[1]
104
118
  rescue ::Net::OpenTimeout => e
@@ -106,7 +120,7 @@ module DatadogBackup
106
120
  sleep(0.1 * retries**5) # 0.1, 3.2, 24.3, 102.4 seconds per retry
107
121
  retry
108
122
  else
109
- raise "Update failed with error #{e.message}"
123
+ raise "Net::OpenTimeout: #{e.message}"
110
124
  end
111
125
  end
112
126
  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'
@@ -2,11 +2,8 @@
2
2
 
3
3
  module DatadogBackup
4
4
  class Monitors < Core
5
- API_SERVICE_NAME = :@monitor_svc
6
- BANLIST = %w[overall_state overall_state_modified matching_downtimes modified]
7
-
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
@@ -42,5 +42,9 @@ module DatadogBackup
42
42
  def resources
43
43
  @options[:resources]
44
44
  end
45
+
46
+ def force_restore
47
+ @options[:force_restore]
48
+ end
45
49
  end
46
50
  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.1'
4
+ VERSION = '1.0.0.alpha.1'
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,13 @@ 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,
49
+ false).and_return(all_boards)
50
+ allow(api_service_double).to receive(:request).with(Net::HTTP::Get, '/api/v1/dashboard/stillthere', nil, nil,
51
+ false).and_return(['200', {}])
52
+ allow(api_service_double).to receive(:request).with(Net::HTTP::Get, '/api/v1/dashboard/alsostillthere', nil,
53
+ nil, false).and_return(['200', {}])
48
54
  end
49
55
 
50
56
  it 'deletes the file locally as well' do
@@ -83,17 +89,16 @@ describe DatadogBackup::Cli do
83
89
 
84
90
  example 'starts interactive restore' do
85
91
  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
92
+
93
+ expect { subject }.to(
94
+ output(/\(r\)estore to Datadog, overwrite local changes and \(d\)ownload, \(s\)kip, or \(q\)uit\?/).to_stdout
95
+ .and(raise_error(SystemExit))
96
+ )
92
97
  end
93
98
 
94
99
  example 'restore' do
95
100
  allow($stdin).to receive(:gets).and_return('r')
96
- expect(dashboards).to receive(:update_with_200).with('diffs1', { 'text' => 'diff' })
101
+ expect(dashboards).to receive(:update).with('diffs1', { 'text' => 'diff' })
97
102
  subject
98
103
  end
99
104
  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
@@ -93,15 +76,92 @@ describe DatadogBackup::Core do
93
76
  it { is_expected.to eq 'core' }
94
77
  end
95
78
 
79
+ describe '#create' do
80
+ subject { core.create({ 'a' => 'b' }) }
81
+ example 'it calls Dogapi::APIService.request' do
82
+ stub_const('Dogapi::APIService::API_VERSION', 'v1')
83
+ allow(core).to receive(:api_service).and_return(api_service_double)
84
+ allow(core).to receive(:api_version).and_return('v1')
85
+ allow(core).to receive(:api_resource_name).and_return('dashboard')
86
+ expect(api_service_double).to receive(:request).with(Net::HTTP::Post, '/api/v1/dashboard', nil, { 'a' => 'b' },
87
+ true).and_return(['200', { 'id' => 'whatever-id-abc' }])
88
+ subject
89
+ end
90
+ end
91
+
96
92
  describe '#update' do
97
- subject { core.update_with_200('abc-123-def', '{"a": "b"}') }
93
+ subject { core.update('abc-123-def', { 'a' => 'b' }) }
98
94
  example 'it calls Dogapi::APIService.request' do
99
95
  stub_const('Dogapi::APIService::API_VERSION', 'v1')
100
96
  allow(core).to receive(:api_service).and_return(api_service_double)
101
97
  allow(core).to receive(:api_version).and_return('v1')
102
98
  allow(core).to receive(:api_resource_name).and_return('dashboard')
103
- expect(api_service_double).to receive(:request).with(Net::HTTP::Put, '/api/v1/dashboard/abc-123-def', nil, '{"a": "b"}', true).and_return(%w[200 Created])
99
+ expect(api_service_double).to receive(:request).with(Net::HTTP::Put, '/api/v1/dashboard/abc-123-def', nil,
100
+ { 'a' => 'b' }, true).and_return(['200',
101
+ { 'id' => 'whataver-man-thats-like-your-opinion' }])
104
102
  subject
105
103
  end
106
104
  end
105
+
106
+ describe '#restore' do
107
+ before(:each) do
108
+ allow(core).to receive(:api_service).and_return(api_service_double)
109
+ allow(core).to receive(:api_version).and_return('api-version-string')
110
+ allow(core).to receive(:api_resource_name).and_return('api-resource-name-string')
111
+ allow(api_service_double).to receive(:request).with(
112
+ Net::HTTP::Get,
113
+ '/api/api-version-string/api-resource-name-string/abc-123-def',
114
+ nil,
115
+ nil,
116
+ false
117
+ ).and_return(['200', { test: :ok }])
118
+ allow(api_service_double).to receive(:request).with(
119
+ Net::HTTP::Get,
120
+ '/api/api-version-string/api-resource-name-string/bad-123-id',
121
+ nil,
122
+ nil,
123
+ false
124
+ ).and_return(['404', { error: :blahblah_not_found }])
125
+ allow(core).to receive(:load_from_file_by_id).and_return({ 'load' => 'ok' })
126
+ end
127
+
128
+ context 'when id exists' do
129
+ subject { core.restore('abc-123-def') }
130
+ example 'it calls out to update' do
131
+ expect(core).to receive(:update).with('abc-123-def', { 'load' => 'ok' })
132
+ subject
133
+ end
134
+ end
135
+
136
+ context 'when id does not exist' do
137
+ before(:each) do
138
+ allow(api_service_double).to receive(:request).with(
139
+ Net::HTTP::Put,
140
+ '/api/api-version-string/api-resource-name-string/bad-123-id',
141
+ nil, { 'load' => 'ok' },
142
+ true
143
+ ).and_return(
144
+ ['404', { 'Error' => 'my not found' }]
145
+ )
146
+ allow(api_service_double).to receive(:request).with(
147
+ Net::HTTP::Post,
148
+ '/api/api-version-string/api-resource-name-string',
149
+ nil,
150
+ { 'load' => 'ok' },
151
+ true
152
+ ).and_return(
153
+ ['200', { 'id' => 'my-new-id' }]
154
+ )
155
+ end
156
+
157
+ subject { core.restore('bad-123-id') }
158
+ example 'it calls out to create then saves the new file and deletes the new file' do
159
+ expect(core).to receive(:create).with({ 'load' => 'ok' }).and_return({ 'id' => 'my-new-id' })
160
+ expect(core).to receive(:get_and_write_file).with('my-new-id')
161
+ allow(core).to receive(:find_file_by_id).with('bad-123-id').and_return('/path/to/bad-123-id.json')
162
+ expect(FileUtils).to receive(:rm).with('/path/to/bad-123-id.json')
163
+ subject
164
+ end
165
+ end
166
+ end
107
167
  end
@@ -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,11 @@ 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,
65
+ false).and_return(all_boards)
66
+ allow(api_service_double).to receive(:request).with(Net::HTTP::Get, '/api/v1/dashboard/abc-123-def', nil, nil,
67
+ false).and_return(example_dashboard)
64
68
  end
65
69
 
66
70
  describe '#backup' do
@@ -79,22 +83,34 @@ describe DatadogBackup::Dashboards do
79
83
  describe '#all_boards' do
80
84
  subject { dashboards.all_boards }
81
85
 
82
- it 'calls get_all_boards' do
83
- subject
84
- expect(client_double).to have_received(:get_all_boards)
85
- end
86
-
87
86
  it { is_expected.to eq [dashboard_description] }
88
87
  end
89
88
 
90
89
  describe '#diff' do
91
90
  it 'calls the api only once' do
92
91
  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
92
+ expect(dashboards.diff('abc-123-def')).to eq(<<~EOF
93
+ ---
94
+ -description: example dashboard
95
+ -graphs:
96
+ -- definition:
97
+ - requests:
98
+ - - q: min:foo.bar{a:b}
99
+ - stacked: false
100
+ - viz: timeseries
101
+ - title: example graph
102
+ -title: example dashboard
103
+ +a: b
104
+ EOF
105
+ )
95
106
  end
96
107
  end
97
108
 
109
+ describe '#except' do
110
+ subject { dashboards.except({ :a => :b, 'modified_at' => :c, 'url' => :d }) }
111
+ it { is_expected.to eq({ a: :b }) }
112
+ end
113
+
98
114
  describe '#get_by_id' do
99
115
  subject { dashboards.get_by_id('abc-123-def') }
100
116
  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,17 @@ 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,
55
+ false).and_return(all_monitors)
56
+ allow(api_service_double).to receive(:request).with(Net::HTTP::Get, '/api/v1/dashboard/123455', nil, nil,
57
+ false).and_return(example_monitor)
53
58
  end
54
59
 
55
60
  describe '#all_monitors' do
56
61
  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
62
  it { is_expected.to eq [monitor_description] }
64
63
  end
65
64
 
@@ -76,10 +75,10 @@ describe DatadogBackup::Monitors do
76
75
  end
77
76
  end
78
77
 
79
- describe '#diff' do
78
+ describe '#diff and #except' do
80
79
  example 'it ignores `overall_state` and `overall_state_modified`' do
81
80
  monitors.write_file(monitors.dump(monitor_description), monitors.filename(123_455))
82
- allow(client_double).to receive(:get_all_monitors).and_return(
81
+ allow(api_service_double).to receive(:request).and_return(
83
82
  [
84
83
  '200',
85
84
  [
@@ -88,8 +87,8 @@ describe DatadogBackup::Monitors do
88
87
  'message' => 'foo',
89
88
  'id' => 123_455,
90
89
  'name' => 'foo',
91
- 'overall_state' => 'NO DATA',
92
- 'overall_state_modified' => '2020-07-27T22:55:55+00:00'
90
+ 'overall_state' => 'ZZZZZZZZZZZZZZZZZZZZZZZZZZZ',
91
+ 'overall_state_modified' => '9999-07-27T22:55:55+00:00'
93
92
  }
94
93
  ]
95
94
  ]
@@ -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.1
4
+ version: 1.0.0.alpha.1
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-09-08 00:00:00.000000000 Z
12
+ date: 2021-01-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: amazing_print
@@ -17,14 +17,14 @@ dependencies:
17
17
  requirements:
18
18
  - - '='
19
19
  - !ruby/object:Gem::Version
20
- version: 1.2.1
20
+ version: 1.2.2
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - '='
26
26
  - !ruby/object:Gem::Version
27
- version: 1.2.1
27
+ version: 1.2.2
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: concurrent-ruby
30
30
  requirement: !ruby/object:Gem::Requirement
@@ -87,14 +87,14 @@ dependencies:
87
87
  requirements:
88
88
  - - '='
89
89
  - !ruby/object:Gem::Version
90
- version: 1.41.0
90
+ version: 1.44.0
91
91
  type: :runtime
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
95
  - - '='
96
96
  - !ruby/object:Gem::Version
97
- version: 1.41.0
97
+ version: 1.44.0
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: bundler
100
100
  requirement: !ruby/object:Gem::Requirement
@@ -183,11 +183,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
183
183
  version: '0'
184
184
  required_rubygems_version: !ruby/object:Gem::Requirement
185
185
  requirements:
186
- - - ">="
186
+ - - ">"
187
187
  - !ruby/object:Gem::Version
188
- version: '0'
188
+ version: 1.3.1
189
189
  requirements: []
190
- rubygems_version: 3.1.2
190
+ rubygems_version: 3.1.4
191
191
  signing_key:
192
192
  specification_version: 4
193
193
  summary: A utility to backup and restore Datadog accounts