datadog_backup 3.0.0.alpha.1 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/rspec_and_release.yml +6 -3
- data/.rubocop.yml +36 -77
- data/CHANGELOG.md +33 -0
- data/Gemfile.lock +2 -2
- data/README.md +2 -2
- data/bin/datadog_backup +6 -9
- data/datadog_backup.gemspec +3 -4
- data/lib/datadog_backup/cli.rb +47 -51
- data/lib/datadog_backup/core.rb +66 -38
- data/lib/datadog_backup/dashboards.rb +11 -8
- data/lib/datadog_backup/deprecations.rb +4 -5
- data/lib/datadog_backup/local_filesystem.rb +4 -5
- data/lib/datadog_backup/monitors.rb +5 -4
- data/lib/datadog_backup/options.rb +1 -0
- data/lib/datadog_backup/synthetics.rb +66 -0
- data/lib/datadog_backup/thread_pool.rb +1 -0
- data/lib/datadog_backup/version.rb +1 -1
- data/lib/datadog_backup.rb +2 -5
- data/release.config.js +4 -6
- data/spec/datadog_backup/cli_spec.rb +56 -41
- data/spec/datadog_backup/core_spec.rb +52 -31
- data/spec/datadog_backup/dashboards_spec.rb +17 -32
- data/spec/datadog_backup/deprecations_spec.rb +7 -9
- data/spec/datadog_backup/local_filesystem_spec.rb +12 -12
- data/spec/datadog_backup/monitors_spec.rb +13 -29
- data/spec/datadog_backup/synthetics_spec.rb +258 -0
- data/spec/datadog_backup_bin_spec.rb +17 -15
- data/spec/spec_helper.rb +4 -2
- metadata +12 -19
- data/spec/datadog_backup_spec.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f24893d65b7350e2c22c2f1f89068664f9d4dd932231d292a26ae4b5f0c87a57
|
4
|
+
data.tar.gz: d7a3a5e175b128a3e11bf523d5fa8fbd9b3c890260a3e14bb74ba8688f0aef06
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd41dd652e26e9d049d25439c81b29a7be45da046526188bc77e0ffd2e06607e25044d10d78d72a0c35066c4b83919f2bcfb435136f9582240f11fcf035c13d6
|
7
|
+
data.tar.gz: 256dd9b0eb76878f7966a853a644c39aede938882781f13e5b7577db4e09f97600c57748858a75aebcff0cd0a3ca32e2a4358618bb0481eea5568469bb57abe8
|
@@ -25,7 +25,6 @@ jobs:
|
|
25
25
|
run: |
|
26
26
|
bundle exec rspec --format documentation --require spec_helper
|
27
27
|
release:
|
28
|
-
if: github.event_name == 'push'
|
29
28
|
needs: rspec
|
30
29
|
runs-on: ubuntu-latest
|
31
30
|
env:
|
@@ -45,10 +44,14 @@ jobs:
|
|
45
44
|
uses: cycjimmy/semantic-release-action@v3
|
46
45
|
env:
|
47
46
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
48
|
-
GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_TOKEN }}
|
49
47
|
with:
|
50
48
|
semantic_version: 17
|
51
49
|
extra_plugins: |
|
52
50
|
@semantic-release/changelog@5
|
53
51
|
@semantic-release/git@9
|
54
|
-
|
52
|
+
- name: push to rubygems
|
53
|
+
if: ${{ steps.semantic.output.new_release_published }}
|
54
|
+
env:
|
55
|
+
GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_TOKEN }}
|
56
|
+
run: |
|
57
|
+
gem push datadog_backup.gem
|
data/.rubocop.yml
CHANGED
@@ -1,78 +1,37 @@
|
|
1
|
-
|
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
|
2
13
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
Layout/
|
8
|
-
Enabled:
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
Enabled:
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
Enabled:
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
Enabled:
|
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
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,36 @@
|
|
1
|
+
# [3.1.0](https://github.com/scribd/datadog_backup/compare/v3.0.0...v3.1.0) (2022-08-30)
|
2
|
+
|
3
|
+
|
4
|
+
### Features
|
5
|
+
|
6
|
+
* backup and restore synthetics ([#139](https://github.com/scribd/datadog_backup/issues/139)) ([a46cadc](https://github.com/scribd/datadog_backup/commit/a46cadc7d196dcb0f20bf31d06cde6a13a390835))
|
7
|
+
|
8
|
+
# [3.0.0](https://github.com/scribd/datadog_backup/compare/v2.0.2...v3.0.0) (2022-08-25)
|
9
|
+
|
10
|
+
|
11
|
+
* feat!: release 3.0 (#136) ([3d23b03](https://github.com/scribd/datadog_backup/commit/3d23b03668e888886f394de2fa4884aa1e3ca287)), closes [#136](https://github.com/scribd/datadog_backup/issues/136)
|
12
|
+
|
13
|
+
|
14
|
+
### BREAKING CHANGES
|
15
|
+
|
16
|
+
* DATADOG_API_KEY and DATADOG_APP_KEY are no longer the environment variables used to authenticate to Datadog. Instead, set the environment variables DD_API_KEY and DD_APP_KEY.
|
17
|
+
* ruby 2.6 is no longer supported. Please upgrade to ruby 2.7 or higher.
|
18
|
+
* The options `--ssh` and `--ssshh` are no longer supported. Instead, please use `--quiet` to supress logging. `--debug` remains supported.
|
19
|
+
* The environment variable `DATADOG_HOST` is no longer supported. Instead, please use `DD_SITE_URL`.
|
20
|
+
|
21
|
+
refactor: The legacy [dogapi-rb ](https://github.com/DataDog/dogapi-rb) gem is replaced with [faraday](https://lostisland.github.io/faraday/). The [official client library](https://github.com/DataDog/datadog-api-client-ruby) was considered, but was not adopted as I had a hard time grok-ing it.
|
22
|
+
|
23
|
+
* chore: permit logging from tests, but only error+
|
24
|
+
|
25
|
+
Co-authored-by: semantic-release-bot <semantic-release-bot@martynus.net>
|
26
|
+
|
27
|
+
# [3.0.0-alpha.2](https://github.com/scribd/datadog_backup/compare/v3.0.0-alpha.1...v3.0.0-alpha.2) (2022-08-25)
|
28
|
+
|
29
|
+
|
30
|
+
### Bug Fixes
|
31
|
+
|
32
|
+
* remove development pry ([611d0a6](https://github.com/scribd/datadog_backup/commit/611d0a6dd5899b0046fc00233cf679834b275089))
|
33
|
+
|
1
34
|
# [3.0.0-alpha.1](https://github.com/scribd/datadog_backup/compare/v2.0.2...v3.0.0-alpha.1) (2022-08-24)
|
2
35
|
|
3
36
|
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
datadog_backup (
|
4
|
+
datadog_backup (3.0.0)
|
5
5
|
amazing_print
|
6
6
|
concurrent-ruby
|
7
7
|
deepsort
|
@@ -62,7 +62,7 @@ GEM
|
|
62
62
|
byebug (~> 11.0)
|
63
63
|
pry (>= 0.13, < 0.15)
|
64
64
|
rainbow (3.1.1)
|
65
|
-
rb-fsevent (0.11.
|
65
|
+
rb-fsevent (0.11.2)
|
66
66
|
rb-inotify (0.10.1)
|
67
67
|
ffi (~> 1.0)
|
68
68
|
regexp_parser (2.5.0)
|
data/README.md
CHANGED
@@ -16,7 +16,7 @@ Additional features may be built out over time.
|
|
16
16
|
## Breaking Changes
|
17
17
|
v3 is a backwards incompatible change.
|
18
18
|
|
19
|
-
- [] DATADOG_API_KEY and DATADOG_APP_KEY are no longer the environment variables used to authenticate to Datadog. Instead, set the environment variables DD_API_KEY and DD_APP_KEY.
|
19
|
+
- [ ] DATADOG_API_KEY and DATADOG_APP_KEY are no longer the environment variables used to authenticate to Datadog. Instead, set the environment variables DD_API_KEY and DD_APP_KEY.
|
20
20
|
- [ ] ruby 2.6 is no longer supported. Please upgrade to ruby 2.7 or higher.
|
21
21
|
- [ ] The options `--ssh` and `--ssshh` are no longer supported. Instead, please use `--quiet` to supress logging. `--debug` remains supported.
|
22
22
|
- [ ] The environment variable `DATADOG_HOST` is no longer supported. Instead, please use `DD_SITE_URL`.
|
@@ -86,7 +86,7 @@ DD_API_KEY | The Application key for the Datadog account
|
|
86
86
|
|
87
87
|
### Usage in a Github repo
|
88
88
|
|
89
|
-
See [example/](https://github.com/scribd/datadog_backup/tree/
|
89
|
+
See [example/](https://github.com/scribd/datadog_backup/tree/main/example) for an example implementation as a repo that backs up your Datadog dashboards hourly.
|
90
90
|
|
91
91
|
# Development
|
92
92
|
|
data/bin/datadog_backup
CHANGED
@@ -11,7 +11,6 @@ LOGGER.level = Logger::INFO
|
|
11
11
|
|
12
12
|
require 'datadog_backup'
|
13
13
|
|
14
|
-
|
15
14
|
def fatal(message)
|
16
15
|
LOGGER.fatal(message)
|
17
16
|
exit 1
|
@@ -19,10 +18,10 @@ end
|
|
19
18
|
|
20
19
|
def options_valid?(options)
|
21
20
|
%w[backup diffs restore].include?(options[:action])
|
22
|
-
%w[DD_API_KEY DD_APP_KEY].all? { |key| ENV
|
21
|
+
%w[DD_API_KEY DD_APP_KEY].all? { |key| ENV.fetch(key, nil) }
|
23
22
|
end
|
24
23
|
|
25
|
-
def prereqs(defaults)
|
24
|
+
def prereqs(defaults) # rubocop:disable Metrics/AbcSize
|
26
25
|
ARGV << '--help' if ARGV.empty?
|
27
26
|
|
28
27
|
result = defaults.dup
|
@@ -49,6 +48,9 @@ def prereqs(defaults)
|
|
49
48
|
opts.on('--dashboards-only') do
|
50
49
|
result[:resources] = [DatadogBackup::Dashboards]
|
51
50
|
end
|
51
|
+
opts.on('--synthetics-only') do
|
52
|
+
result[:resources] = [DatadogBackup::Synthetics]
|
53
|
+
end
|
52
54
|
opts.on(
|
53
55
|
'--json',
|
54
56
|
'format backups as JSON instead of YAML. Does not impact `diffs` nor `restore`, but do not mix formats in the same backup-dir.'
|
@@ -78,14 +80,9 @@ defaults = {
|
|
78
80
|
action: nil,
|
79
81
|
backup_dir: File.join(ENV.fetch('PWD'), 'backup'),
|
80
82
|
diff_format: :color,
|
81
|
-
resources: [DatadogBackup::Dashboards, DatadogBackup::Monitors],
|
83
|
+
resources: [DatadogBackup::Dashboards, DatadogBackup::Monitors, DatadogBackup::Synthetics],
|
82
84
|
output_format: :yaml,
|
83
85
|
force_restore: false
|
84
86
|
}
|
85
87
|
|
86
|
-
begin
|
87
88
|
DatadogBackup::Cli.new(prereqs(defaults)).run!
|
88
|
-
rescue => e
|
89
|
-
require 'pry'
|
90
|
-
binding.pry
|
91
|
-
end
|
data/datadog_backup.gemspec
CHANGED
@@ -13,13 +13,13 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.description = 'A utility to backup and restore Datadog accounts'
|
14
14
|
spec.homepage = 'https://github.com/scribd/datadog_backup'
|
15
15
|
spec.license = 'MIT'
|
16
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
16
17
|
|
17
18
|
spec.files = `git ls-files -z`.split("\x0")
|
18
19
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
-
spec.test_files = spec.files.grep(%r{^spec/})
|
20
20
|
spec.require_paths = ['lib']
|
21
21
|
|
22
|
-
spec.required_ruby_version =
|
22
|
+
spec.required_ruby_version = '>= 2.7'
|
23
23
|
|
24
24
|
spec.add_dependency 'amazing_print'
|
25
25
|
spec.add_dependency 'concurrent-ruby'
|
@@ -28,11 +28,10 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_dependency 'faraday'
|
29
29
|
spec.add_dependency 'faraday-retry'
|
30
30
|
|
31
|
-
|
32
31
|
spec.add_development_dependency 'bundler'
|
32
|
+
spec.add_development_dependency 'guard-rspec'
|
33
33
|
spec.add_development_dependency 'pry'
|
34
34
|
spec.add_development_dependency 'pry-byebug'
|
35
|
-
spec.add_development_dependency 'guard-rspec'
|
36
35
|
spec.add_development_dependency 'rspec'
|
37
36
|
spec.add_development_dependency 'rubocop'
|
38
37
|
spec.add_development_dependency 'rubocop-rspec'
|
data/lib/datadog_backup/cli.rb
CHANGED
@@ -4,6 +4,7 @@ require 'optparse'
|
|
4
4
|
require 'amazing_print'
|
5
5
|
|
6
6
|
module DatadogBackup
|
7
|
+
# CLI is the command line interface for the datadog_backup gem.
|
7
8
|
class Cli
|
8
9
|
include ::DatadogBackup::Options
|
9
10
|
|
@@ -11,9 +12,9 @@ module DatadogBackup
|
|
11
12
|
LOGGER.info("Starting diffs on #{::DatadogBackup::ThreadPool::TPOOL.max_length} threads")
|
12
13
|
any_resource_instance
|
13
14
|
.all_file_ids_for_selected_resources
|
14
|
-
.map do |
|
15
|
-
Concurrent::Promises.future_on(::DatadogBackup::ThreadPool::TPOOL,
|
16
|
-
[
|
15
|
+
.map do |file_id|
|
16
|
+
Concurrent::Promises.future_on(::DatadogBackup::ThreadPool::TPOOL, file_id) do |fid|
|
17
|
+
[fid, getdiff(fid)]
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
@@ -32,32 +33,17 @@ module DatadogBackup
|
|
32
33
|
matching_resource_instance(any_resource_instance.class_from_id(id))
|
33
34
|
end
|
34
35
|
|
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
|
-
|
47
36
|
def getdiff(id)
|
48
37
|
result = definitive_resource_instance(id).diff(id)
|
49
38
|
case result
|
50
|
-
when ''
|
51
|
-
nil
|
52
|
-
when "\n"
|
53
|
-
nil
|
54
|
-
when '<div class="diff"></div>'
|
39
|
+
when '---' || '' || "\n" || '<div class="diff"></div>'
|
55
40
|
nil
|
56
41
|
else
|
57
42
|
result
|
58
43
|
end
|
59
44
|
end
|
60
45
|
|
46
|
+
# rubocop:disable Style/StringConcatenation
|
61
47
|
def format_diff_output(diff_output)
|
62
48
|
case diff_format
|
63
49
|
when nil, :color
|
@@ -69,58 +55,31 @@ module DatadogBackup
|
|
69
55
|
Diffy::CSS +
|
70
56
|
'</style></head><body>' +
|
71
57
|
diff_output.map do |id, diff|
|
72
|
-
"<br><br> ---<br><strong> id: #{id}</strong><br
|
58
|
+
"<br><br> ---<br><strong> id: #{id}</strong><br>#{diff}"
|
73
59
|
end.join('<br>') +
|
74
60
|
'</body></html>'
|
75
61
|
else
|
76
62
|
raise 'Unexpected diff_format.'
|
77
63
|
end
|
78
64
|
end
|
65
|
+
# rubocop:enable Style/StringConcatenation
|
79
66
|
|
80
67
|
def initialize(options)
|
81
68
|
@options = options
|
82
69
|
end
|
83
70
|
|
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
|
-
|
94
71
|
def restore
|
95
72
|
futures = all_diff_futures
|
96
73
|
watcher = ::DatadogBackup::ThreadPool.watcher
|
97
74
|
|
98
75
|
futures.each do |future|
|
99
76
|
id, diff = *future.value!
|
100
|
-
next
|
77
|
+
next if diff.nil? || diff.empty?
|
101
78
|
|
102
79
|
if @options[:force_restore]
|
103
80
|
definitive_resource_instance(id).restore(id)
|
104
81
|
else
|
105
|
-
|
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)
|
124
83
|
end
|
125
84
|
end
|
126
85
|
watcher.join if watcher.status
|
@@ -131,5 +90,42 @@ module DatadogBackup
|
|
131
90
|
rescue SystemExit, Interrupt
|
132
91
|
::DatadogBackup::ThreadPool.shutdown
|
133
92
|
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
|
134
130
|
end
|
135
131
|
end
|
data/lib/datadog_backup/core.rb
CHANGED
@@ -5,14 +5,14 @@ require 'deepsort'
|
|
5
5
|
require 'faraday'
|
6
6
|
require 'faraday/retry'
|
7
7
|
|
8
|
-
|
9
|
-
|
10
8
|
module DatadogBackup
|
9
|
+
# The default options for backing up and restores.
|
10
|
+
# This base class is meant to be extended by specific resources, such as Dashboards, Monitors, and so on.
|
11
11
|
class Core
|
12
12
|
include ::DatadogBackup::LocalFilesystem
|
13
13
|
include ::DatadogBackup::Options
|
14
14
|
|
15
|
-
|
15
|
+
@retry_options = {
|
16
16
|
max: 5,
|
17
17
|
interval: 0.05,
|
18
18
|
interval_randomness: 0.5,
|
@@ -20,27 +20,27 @@ module DatadogBackup
|
|
20
20
|
}
|
21
21
|
|
22
22
|
def api_service
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
23
|
+
@api_service ||= Faraday.new(
|
24
|
+
url: api_url,
|
25
|
+
headers: {
|
26
|
+
'DD-API-KEY' => ENV.fetch('DD_API_KEY'),
|
27
|
+
'DD-APPLICATION-KEY' => ENV.fetch('DD_APP_KEY')
|
28
|
+
}
|
29
|
+
) do |faraday|
|
30
|
+
faraday.request :json
|
31
|
+
faraday.request :retry, @retry_options
|
32
|
+
faraday.response(:logger, LOGGER, { headers: true, bodies: LOGGER.debug?, log_level: :debug }) do |logger|
|
33
|
+
logger.filter(/(DD-API-KEY:)([^&]+)/, '\1[REDACTED]')
|
34
|
+
logger.filter(/(DD-APPLICATION-KEY:)([^&]+)/, '\1[REDACTED]')
|
35
|
+
end
|
36
|
+
faraday.response :raise_error
|
37
|
+
faraday.response :json
|
38
|
+
faraday.adapter Faraday.default_adapter
|
39
|
+
end
|
40
40
|
end
|
41
41
|
|
42
42
|
def api_url
|
43
|
-
ENV.fetch('DD_SITE_URL',
|
43
|
+
ENV.fetch('DD_SITE_URL', 'https://api.datadoghq.com/')
|
44
44
|
end
|
45
45
|
|
46
46
|
def api_version
|
@@ -51,6 +51,11 @@ module DatadogBackup
|
|
51
51
|
raise 'subclass is expected to implement #api_resource_name'
|
52
52
|
end
|
53
53
|
|
54
|
+
# Some resources have a different key for the id.
|
55
|
+
def id_keyname
|
56
|
+
'id'
|
57
|
+
end
|
58
|
+
|
54
59
|
def backup
|
55
60
|
raise 'subclass is expected to implement #backup'
|
56
61
|
end
|
@@ -61,8 +66,8 @@ module DatadogBackup
|
|
61
66
|
current = except(get_by_id(id)).deep_sort.to_yaml
|
62
67
|
filesystem = except(load_from_file_by_id(id)).deep_sort.to_yaml
|
63
68
|
result = ::Diffy::Diff.new(current, filesystem, include_plus_and_minus_in_html: true).to_s(diff_format)
|
64
|
-
LOGGER.debug("Compared ID #{id} and found #{result}")
|
65
|
-
result
|
69
|
+
LOGGER.debug("Compared ID #{id} and found filesystem: #{filesystem} <=> current: #{current} == result: #{result}")
|
70
|
+
result.chomp
|
66
71
|
end
|
67
72
|
|
68
73
|
# Returns a hash with banlist elements removed
|
@@ -74,6 +79,7 @@ module DatadogBackup
|
|
74
79
|
end
|
75
80
|
end
|
76
81
|
|
82
|
+
# Fetch the specified resource from Datadog
|
77
83
|
def get(id)
|
78
84
|
params = {}
|
79
85
|
headers = {}
|
@@ -81,17 +87,25 @@ module DatadogBackup
|
|
81
87
|
body_with_2xx(response)
|
82
88
|
end
|
83
89
|
|
90
|
+
# Returns a list of all resources in Datadog
|
91
|
+
# Do not use directly, but use the child classes' #all method instead
|
84
92
|
def get_all
|
93
|
+
return @get_all if @get_all
|
94
|
+
|
85
95
|
params = {}
|
86
96
|
headers = {}
|
87
97
|
response = api_service.get("/api/#{api_version}/#{api_resource_name}", params, headers)
|
88
|
-
body_with_2xx(response)
|
98
|
+
@get_all = body_with_2xx(response)
|
89
99
|
end
|
90
100
|
|
101
|
+
# Download the resource from Datadog and write it to a file
|
91
102
|
def get_and_write_file(id)
|
92
|
-
|
103
|
+
body = get_by_id(id)
|
104
|
+
write_file(dump(body), filename(id))
|
105
|
+
body
|
93
106
|
end
|
94
107
|
|
108
|
+
# Fetch the specified resource from Datadog and remove the banlist elements
|
95
109
|
def get_by_id(id)
|
96
110
|
except(get(id))
|
97
111
|
end
|
@@ -106,43 +120,57 @@ module DatadogBackup
|
|
106
120
|
self.class.to_s.split(':').last.downcase
|
107
121
|
end
|
108
122
|
|
109
|
-
#
|
123
|
+
# Create a new resource in Datadog
|
110
124
|
def create(body)
|
111
125
|
headers = {}
|
112
|
-
response =
|
126
|
+
response = api_service.post("/api/#{api_version}/#{api_resource_name}", body, headers)
|
113
127
|
body = body_with_2xx(response)
|
114
|
-
LOGGER.warn "Successfully created #{body.fetch(
|
128
|
+
LOGGER.warn "Successfully created #{body.fetch(id_keyname)} in datadog."
|
129
|
+
LOGGER.info 'Invalidating cache'
|
130
|
+
@get_all = nil
|
115
131
|
body
|
116
132
|
end
|
117
133
|
|
118
|
-
#
|
134
|
+
# Update an existing resource in Datadog
|
119
135
|
def update(id, body)
|
120
136
|
headers = {}
|
121
137
|
response = api_service.put("/api/#{api_version}/#{api_resource_name}/#{id}", body, headers)
|
122
138
|
body = body_with_2xx(response)
|
123
|
-
LOGGER.warn
|
139
|
+
LOGGER.warn "Successfully restored #{id} to datadog."
|
140
|
+
LOGGER.info 'Invalidating cache'
|
141
|
+
@get_all = nil
|
124
142
|
body
|
125
143
|
end
|
126
144
|
|
145
|
+
# If the resource exists in Datadog, update it. Otherwise, create it.
|
127
146
|
def restore(id)
|
128
147
|
body = load_from_file_by_id(id)
|
129
148
|
begin
|
130
149
|
update(id, body)
|
131
150
|
rescue RuntimeError => e
|
132
|
-
|
133
|
-
new_id = create(body).fetch('id')
|
151
|
+
raise e.message unless e.message.include?('update failed with error 404')
|
134
152
|
|
135
|
-
|
136
|
-
get_and_write_file(new_id)
|
137
|
-
else
|
138
|
-
raise e.message
|
139
|
-
end
|
153
|
+
create_newly(id, body)
|
140
154
|
end
|
141
155
|
end
|
142
156
|
|
157
|
+
# Return the Faraday body from a response with a 2xx status code, otherwise raise an error
|
143
158
|
def body_with_2xx(response)
|
144
|
-
|
159
|
+
unless response.status.to_s =~ /^2/
|
160
|
+
raise "#{caller_locations(1,
|
161
|
+
1)[0].label} failed with error #{response.status}"
|
162
|
+
end
|
163
|
+
|
145
164
|
response.body
|
146
165
|
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
# Create a new resource in Datadog, then move the old file to the new resource's ID
|
170
|
+
def create_newly(file_id, body)
|
171
|
+
new_id = create(body).fetch(id_keyname)
|
172
|
+
FileUtils.rm(find_file_by_id(file_id))
|
173
|
+
get_and_write_file(new_id)
|
174
|
+
end
|
147
175
|
end
|
148
176
|
end
|