Skip to content

Commit a46cadc

Browse files
authored
feat: backup and restore synthetics (#139)
* feat: backup and restore synthetic tests * refactor: apply linter recommendations from rubocop * refactor: rubocop pass * chore: enable rubygems mfa * refactor: respond_with200 to reduce a bit of visual noise * fix: diffs generating false positives * fix(deps): upgrade rb-fsevent
1 parent 2f4413b commit a46cadc

24 files changed

+685
-356
lines changed

.rubocop.yml

Lines changed: 36 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,37 @@
1-
require: rubocop-rspec
1+
# The behavior of RuboCop can be controlled via the .rubocop.yml
2+
# configuration file. It makes it possible to enable/disable
3+
# certain cops (checks) and to alter their behavior if they accept
4+
# any parameters. The file can be placed either in your home
5+
# directory or in some project directory.
6+
#
7+
# RuboCop will start looking for the configuration file in the directory
8+
# where the inspected file is and continue its way up to the root directory.
9+
#
10+
# See https://docs.rubocop.org/rubocop/configuration
11+
require:
12+
- rubocop-rspec
213

3-
Gemspec/DateAssignment: # (new in 1.10)
4-
Enabled: true
5-
Layout/LineEndStringConcatenationIndentation: # (new in 1.18)
6-
Enabled: true
7-
Layout/SpaceBeforeBrackets: # (new in 1.7)
8-
Enabled: true
9-
Lint/AmbiguousAssignment: # (new in 1.7)
10-
Enabled: true
11-
Lint/DeprecatedConstants: # (new in 1.8)
12-
Enabled: true
13-
Lint/DuplicateBranch: # (new in 1.3)
14-
Enabled: true
15-
Lint/DuplicateRegexpCharacterClassElement: # (new in 1.1)
16-
Enabled: true
17-
Lint/EmptyBlock: # (new in 1.1)
18-
Enabled: true
19-
Lint/EmptyClass: # (new in 1.3)
20-
Enabled: true
21-
Lint/EmptyInPattern: # (new in 1.16)
22-
Enabled: true
23-
Lint/LambdaWithoutLiteralBlock: # (new in 1.8)
24-
Enabled: true
25-
Lint/NoReturnInBeginEndBlocks: # (new in 1.2)
26-
Enabled: true
27-
Lint/NumberedParameterAssignment: # (new in 1.9)
28-
Enabled: true
29-
Lint/OrAssignmentToConstant: # (new in 1.9)
30-
Enabled: true
31-
Lint/RedundantDirGlobSort: # (new in 1.8)
32-
Enabled: true
33-
Lint/SymbolConversion: # (new in 1.9)
34-
Enabled: true
35-
Lint/ToEnumArguments: # (new in 1.1)
36-
Enabled: true
37-
Lint/TripleQuotes: # (new in 1.9)
38-
Enabled: true
39-
Lint/UnexpectedBlockArity: # (new in 1.5)
40-
Enabled: true
41-
Lint/UnmodifiedReduceAccumulator: # (new in 1.1)
42-
Enabled: true
43-
Naming/InclusiveLanguage: # (new in 1.18)
44-
Enabled: true
45-
Style/ArgumentsForwarding: # (new in 1.1)
46-
Enabled: true
47-
Style/CollectionCompact: # (new in 1.2)
48-
Enabled: true
49-
Style/DocumentDynamicEvalDefinition: # (new in 1.1)
50-
Enabled: true
51-
Style/EndlessMethod: # (new in 1.8)
52-
Enabled: true
53-
Style/HashConversion: # (new in 1.10)
54-
Enabled: true
55-
Style/HashExcept: # (new in 1.7)
56-
Enabled: true
57-
Style/IfWithBooleanLiteralBranches: # (new in 1.9)
58-
Enabled: true
59-
Style/InPatternThen: # (new in 1.16)
60-
Enabled: true
61-
Style/MultilineInPatternThen: # (new in 1.16)
62-
Enabled: true
63-
Style/NegatedIfElseCondition: # (new in 1.2)
64-
Enabled: true
65-
Style/NilLambda: # (new in 1.3)
66-
Enabled: true
67-
Style/QuotedSymbols: # (new in 1.16)
68-
Enabled: true
69-
Style/RedundantArgument: # (new in 1.4)
70-
Enabled: true
71-
Style/StringChars: # (new in 1.12)
72-
Enabled: true
73-
Style/SwapValues: # (new in 1.1)
74-
Enabled: true
75-
RSpec/IdenticalEqualityAssertion: # (new in 2.4)
76-
Enabled: true
77-
RSpec/Rails/AvoidSetupHook: # (new in 2.4)
78-
Enabled: true
14+
AllCops:
15+
TargetRubyVersion: 2.7
16+
NewCops: enable
17+
18+
Layout/LineLength:
19+
Enabled: false
20+
21+
Metrics/BlockLength:
22+
Enabled: false
23+
24+
Metrics/ClassLength:
25+
Enabled: false
26+
27+
Metrics/MethodLength:
28+
Enabled: false
29+
30+
Naming/AccessorMethodName:
31+
Enabled: false
32+
33+
RSpec/MultipleMemoizedHelpers:
34+
Enabled: false
35+
36+
RSpec/ExampleLength:
37+
Enabled: false

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ GEM
6262
byebug (~> 11.0)
6363
pry (>= 0.13, < 0.15)
6464
rainbow (3.1.1)
65-
rb-fsevent (0.11.1)
65+
rb-fsevent (0.11.2)
6666
rb-inotify (0.10.1)
6767
ffi (~> 1.0)
6868
regexp_parser (2.5.0)

bin/datadog_backup

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,17 @@ LOGGER.level = Logger::INFO
1111

1212
require 'datadog_backup'
1313

14-
1514
def fatal(message)
1615
LOGGER.fatal(message)
1716
exit 1
1817
end
1918

2019
def options_valid?(options)
2120
%w[backup diffs restore].include?(options[:action])
22-
%w[DD_API_KEY DD_APP_KEY].all? { |key| ENV[key] }
21+
%w[DD_API_KEY DD_APP_KEY].all? { |key| ENV.fetch(key, nil) }
2322
end
2423

25-
def prereqs(defaults)
24+
def prereqs(defaults) # rubocop:disable Metrics/AbcSize
2625
ARGV << '--help' if ARGV.empty?
2726

2827
result = defaults.dup
@@ -49,6 +48,9 @@ def prereqs(defaults)
4948
opts.on('--dashboards-only') do
5049
result[:resources] = [DatadogBackup::Dashboards]
5150
end
51+
opts.on('--synthetics-only') do
52+
result[:resources] = [DatadogBackup::Synthetics]
53+
end
5254
opts.on(
5355
'--json',
5456
'format backups as JSON instead of YAML. Does not impact `diffs` nor `restore`, but do not mix formats in the same backup-dir.'
@@ -78,9 +80,9 @@ defaults = {
7880
action: nil,
7981
backup_dir: File.join(ENV.fetch('PWD'), 'backup'),
8082
diff_format: :color,
81-
resources: [DatadogBackup::Dashboards, DatadogBackup::Monitors],
83+
resources: [DatadogBackup::Dashboards, DatadogBackup::Monitors, DatadogBackup::Synthetics],
8284
output_format: :yaml,
8385
force_restore: false
8486
}
8587

86-
DatadogBackup::Cli.new(prereqs(defaults)).run!
88+
DatadogBackup::Cli.new(prereqs(defaults)).run!

datadog_backup.gemspec

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
1919
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
2020
spec.require_paths = ['lib']
2121

22-
spec.required_ruby_version = ['>= 2.7']
22+
spec.required_ruby_version = '>= 2.7'
2323

2424
spec.add_dependency 'amazing_print'
2525
spec.add_dependency 'concurrent-ruby'
@@ -28,11 +28,10 @@ Gem::Specification.new do |spec|
2828
spec.add_dependency 'faraday'
2929
spec.add_dependency 'faraday-retry'
3030

31-
3231
spec.add_development_dependency 'bundler'
32+
spec.add_development_dependency 'guard-rspec'
3333
spec.add_development_dependency 'pry'
3434
spec.add_development_dependency 'pry-byebug'
35-
spec.add_development_dependency 'guard-rspec'
3635
spec.add_development_dependency 'rspec'
3736
spec.add_development_dependency 'rubocop'
3837
spec.add_development_dependency 'rubocop-rspec'

lib/datadog_backup.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
require_relative 'datadog_backup/core'
99
require_relative 'datadog_backup/dashboards'
1010
require_relative 'datadog_backup/monitors'
11+
require_relative 'datadog_backup/synthetics'
1112
require_relative 'datadog_backup/thread_pool'
1213
require_relative 'datadog_backup/version'
1314
require_relative 'datadog_backup/deprecations'
1415
DatadogBackup::Deprecations.check
1516

16-
17+
# DatadogBackup is a gem for backing up and restoring Datadog monitors and dashboards.
1718
module DatadogBackup
1819
end
19-

lib/datadog_backup/cli.rb

Lines changed: 47 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@
44
require 'amazing_print'
55

66
module DatadogBackup
7+
# CLI is the command line interface for the datadog_backup gem.
78
class Cli
89
include ::DatadogBackup::Options
910

1011
def all_diff_futures
1112
LOGGER.info("Starting diffs on #{::DatadogBackup::ThreadPool::TPOOL.max_length} threads")
1213
any_resource_instance
1314
.all_file_ids_for_selected_resources
14-
.map do |id|
15-
Concurrent::Promises.future_on(::DatadogBackup::ThreadPool::TPOOL, id) do |id|
16-
[id, getdiff(id)]
15+
.map do |file_id|
16+
Concurrent::Promises.future_on(::DatadogBackup::ThreadPool::TPOOL, file_id) do |fid|
17+
[fid, getdiff(fid)]
1718
end
1819
end
1920
end
@@ -32,32 +33,17 @@ def definitive_resource_instance(id)
3233
matching_resource_instance(any_resource_instance.class_from_id(id))
3334
end
3435

35-
def diffs
36-
futures = all_diff_futures
37-
::DatadogBackup::ThreadPool.watcher.join
38-
39-
format_diff_output(
40-
Concurrent::Promises
41-
.zip(*futures)
42-
.value!
43-
.compact
44-
)
45-
end
46-
4736
def getdiff(id)
4837
result = definitive_resource_instance(id).diff(id)
4938
case result
50-
when ''
51-
nil
52-
when "\n"
53-
nil
54-
when '<div class="diff"></div>'
39+
when '---' || '' || "\n" || '<div class="diff"></div>'
5540
nil
5641
else
5742
result
5843
end
5944
end
6045

46+
# rubocop:disable Style/StringConcatenation
6147
def format_diff_output(diff_output)
6248
case diff_format
6349
when nil, :color
@@ -69,58 +55,31 @@ def format_diff_output(diff_output)
6955
Diffy::CSS +
7056
'</style></head><body>' +
7157
diff_output.map do |id, diff|
72-
"<br><br> ---<br><strong> id: #{id}</strong><br>" + diff
58+
"<br><br> ---<br><strong> id: #{id}</strong><br>#{diff}"
7359
end.join('<br>') +
7460
'</body></html>'
7561
else
7662
raise 'Unexpected diff_format.'
7763
end
7864
end
65+
# rubocop:enable Style/StringConcatenation
7966

8067
def initialize(options)
8168
@options = options
8269
end
8370

84-
def matching_resource_instance(klass)
85-
resource_instances.select { |resource_instance| resource_instance.instance_of?(klass) }.first
86-
end
87-
88-
def resource_instances
89-
@resource_instances ||= resources.map do |resource|
90-
resource.new(@options)
91-
end
92-
end
93-
9471
def restore
9572
futures = all_diff_futures
9673
watcher = ::DatadogBackup::ThreadPool.watcher
9774

9875
futures.each do |future|
9976
id, diff = *future.value!
100-
next unless diff
77+
next if diff.nil? || diff.empty?
10178

10279
if @options[:force_restore]
10380
definitive_resource_instance(id).restore(id)
10481
else
105-
puts '--------------------------------------------------------------------------------'
106-
puts format_diff_output([id, diff])
107-
puts '(r)estore to Datadog, overwrite local changes and (d)ownload, (s)kip, or (q)uit?'
108-
response = $stdin.gets.chomp
109-
case response
110-
when 'q'
111-
exit
112-
when 'r'
113-
puts "Restoring #{id} to Datadog."
114-
definitive_resource_instance(id).restore(id)
115-
when 'd'
116-
puts "Downloading #{id} from Datadog."
117-
definitive_resource_instance(id).get_and_write_file(id)
118-
when 's'
119-
next
120-
else
121-
puts 'Invalid response, please try again.'
122-
response = $stdin.gets.chomp
123-
end
82+
ask_to_restore(id, diff)
12483
end
12584
end
12685
watcher.join if watcher.status
@@ -131,5 +90,42 @@ def run!
13190
rescue SystemExit, Interrupt
13291
::DatadogBackup::ThreadPool.shutdown
13392
end
93+
94+
private
95+
96+
def ask_to_restore(id, diff)
97+
puts '--------------------------------------------------------------------------------'
98+
puts format_diff_output([id, diff])
99+
puts '(r)estore to Datadog, overwrite local changes and (d)ownload, (s)kip, or (q)uit?'
100+
loop do
101+
response = $stdin.gets.chomp
102+
case response
103+
when 'q'
104+
exit
105+
when 'r'
106+
puts "Restoring #{id} to Datadog."
107+
definitive_resource_instance(id).restore(id)
108+
break
109+
when 'd'
110+
puts "Downloading #{id} from Datadog."
111+
definitive_resource_instance(id).get_and_write_file(id)
112+
break
113+
when 's'
114+
break
115+
else
116+
puts 'Invalid response, please try again.'
117+
end
118+
end
119+
end
120+
121+
def matching_resource_instance(klass)
122+
resource_instances.select { |resource_instance| resource_instance.instance_of?(klass) }.first
123+
end
124+
125+
def resource_instances
126+
@resource_instances ||= resources.map do |resource|
127+
resource.new(@options)
128+
end
129+
end
134130
end
135131
end

0 commit comments

Comments
 (0)