-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: use generator methods, and class instance variables to separate duties between classes #143
base: main
Are you sure you want to change the base?
refactor: use generator methods, and class instance variables to separate duties between classes #143
Changes from all commits
200b0ad
9c1a77a
7d4d7a6
93b8624
77f3d47
de7d014
9df4d97
27393e9
8169497
a003595
157dd43
91d50d6
9baaf3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,109 +6,80 @@ | |
module DatadogBackup | ||
# CLI is the command line interface for the datadog_backup gem. | ||
class Cli | ||
include ::DatadogBackup::Options | ||
|
||
def all_diff_futures | ||
LOGGER.info("Starting diffs on #{::DatadogBackup::ThreadPool::TPOOL.max_length} threads") | ||
any_resource_instance | ||
.all_file_ids_for_selected_resources | ||
.map do |file_id| | ||
Concurrent::Promises.future_on(::DatadogBackup::ThreadPool::TPOOL, file_id) do |fid| | ||
[fid, getdiff(fid)] | ||
end | ||
end | ||
end | ||
|
||
def any_resource_instance | ||
resource_instances.first | ||
def initialize(options) | ||
$options = options | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The original code stored the options in an instance variable rather than a global variable, which would be the more normal practice. This PR is a little large for me to take in all at once, so I'll just ask here: What other code needs to access the options passed in here via the global variable ...Coming back to this comment a bit later, I have some ideas for how to minimize the drawbacks of the use of a global variable. (That is, if its use can't be easily eliminated.) First, it would be cleaner if the code that instantiates the
I'd suggest instead something like:
I see also that the
(The Then in the rest of the class you can write just The above only applies if no other code is going to update the options on the fly, which appears to be the case. |
||
end | ||
|
||
def backup | ||
resource_instances.each(&:purge) | ||
resource_instances.each(&:backup) | ||
$options[:resources].each(&:purge) | ||
$options[:resources].each(&:backup_all) | ||
any_resource_instance.all_files | ||
end | ||
|
||
def definitive_resource_instance(id) | ||
matching_resource_instance(any_resource_instance.class_from_id(id)) | ||
end | ||
def diffs | ||
$options[:resources].each do |resource| | ||
resource.all.each do |resource_instance| | ||
next if resource_instance.diff.nil? || resource_instance.diff.empty? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't seem like ActiveSupport is available in this repo...? If it is, you can combine these two tests into just:
|
||
|
||
def getdiff(id) | ||
result = definitive_resource_instance(id).diff(id) | ||
case result | ||
when '---' || '' || "\n" || '<div class="diff"></div>' | ||
nil | ||
else | ||
result | ||
end | ||
end | ||
|
||
# rubocop:disable Style/StringConcatenation | ||
def format_diff_output(diff_output) | ||
case diff_format | ||
when nil, :color | ||
diff_output.map do |id, diff| | ||
" ---\n id: #{id}\n#{diff}" | ||
end.join("\n") | ||
when :html | ||
'<html><head><style>' + | ||
Diffy::CSS + | ||
'</style></head><body>' + | ||
diff_output.map do |id, diff| | ||
"<br><br> ---<br><strong> id: #{id}</strong><br>#{diff}" | ||
end.join('<br>') + | ||
'</body></html>' | ||
else | ||
raise 'Unexpected diff_format.' | ||
puts resource_instance.diff | ||
end | ||
end | ||
end | ||
# rubocop:enable Style/StringConcatenation | ||
|
||
def initialize(options) | ||
@options = options | ||
end | ||
|
||
def restore | ||
futures = all_diff_futures | ||
watcher = ::DatadogBackup::ThreadPool.watcher | ||
|
||
futures.each do |future| | ||
id, diff = *future.value! | ||
next if diff.nil? || diff.empty? | ||
|
||
if @options[:force_restore] | ||
definitive_resource_instance(id).restore(id) | ||
else | ||
ask_to_restore(id, diff) | ||
$options[:resources].each do |resource| | ||
resource.all.each do |resource_instance| | ||
next if resource_instance.diff.nil? || resource_instance.diff.empty? | ||
|
||
if $options[:force_restore] | ||
resource_instance.restore | ||
else | ||
ask_to_restore(resource_instance) | ||
end | ||
end | ||
end | ||
watcher.join if watcher.status | ||
end | ||
|
||
def run! | ||
puts(send(action.to_sym)) | ||
case $options[:action] | ||
when 'backup' | ||
LOGGER.info('Starting backup.') | ||
backup | ||
when 'restore' | ||
LOGGER.info('Starting restore.') | ||
restore | ||
when 'diffs' | ||
LOGGER.info('Starting diffs.') | ||
diffs | ||
else | ||
fatal 'No action specified.' | ||
end | ||
LOGGER.info('Done.') | ||
rescue SystemExit, Interrupt | ||
::DatadogBackup::ThreadPool.shutdown | ||
end | ||
|
||
private | ||
|
||
def ask_to_restore(id, diff) | ||
## | ||
# Interact with the user | ||
|
||
def ask_to_restore(resource_instance) | ||
puts '--------------------------------------------------------------------------------' | ||
puts format_diff_output([id, diff]) | ||
puts resource_instance.diff | ||
puts '(r)estore to Datadog, overwrite local changes and (d)ownload, (s)kip, or (q)uit?' | ||
loop do | ||
response = $stdin.gets.chomp | ||
case response | ||
when 'q' | ||
exit | ||
when 'r' | ||
puts "Restoring #{id} to Datadog." | ||
definitive_resource_instance(id).restore(id) | ||
puts "Restoring #{resource_instance.id} to Datadog." | ||
resource_instance.restore | ||
break | ||
when 'd' | ||
puts "Downloading #{id} from Datadog." | ||
definitive_resource_instance(id).get_and_write_file(id) | ||
puts "Downloading #{resource_instance.id} from Datadog." | ||
resource_instance.backup | ||
break | ||
when 's' | ||
break | ||
|
@@ -118,14 +89,10 @@ def ask_to_restore(id, diff) | |
end | ||
end | ||
|
||
def matching_resource_instance(klass) | ||
resource_instances.select { |resource_instance| resource_instance.instance_of?(klass) }.first | ||
end | ||
|
||
def resource_instances | ||
@resource_instances ||= resources.map do |resource| | ||
resource.new(@options) | ||
end | ||
## | ||
# Finding the right resource instance to use. | ||
def any_resource_instance | ||
$options[:resources].first | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# frozen_string_literal: true | ||
require 'faraday' | ||
require 'faraday/retry' | ||
|
||
module DatadogBackup | ||
class Client | ||
RETRY_OPTIONS = { | ||
max: 5, | ||
interval: 0.05, | ||
interval_randomness: 0.5, | ||
backoff_factor: 2 | ||
}.freeze | ||
|
||
def initialize | ||
@client = Faraday.new( | ||
url: ENV.fetch('DD_SITE_URL', 'https://api.datadoghq.com/'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default url might be better stored as a class constant, eg:
And later:
|
||
headers: { | ||
'DD-API-KEY' => ENV.fetch('DD_API_KEY'), | ||
'DD-APPLICATION-KEY' => ENV.fetch('DD_APP_KEY') | ||
} | ||
) do |faraday| | ||
faraday.request :json | ||
faraday.request :retry, RETRY_OPTIONS | ||
faraday.response(:logger, LOGGER, { headers: true, bodies: LOGGER.debug?, log_level: :debug }) do |logger| | ||
logger.filter(/(DD-API-KEY:)([^&]+)/, '\1[REDACTED]') | ||
logger.filter(/(DD-APPLICATION-KEY:)([^&]+)/, '\1[REDACTED]') | ||
end | ||
faraday.response :raise_error | ||
faraday.response :json | ||
faraday.adapter Faraday.default_adapter | ||
end | ||
end | ||
|
||
def get_body(path, params = {}, headers = {}) | ||
response = @client.get(path, params, headers) | ||
response.body | ||
end | ||
|
||
def post_body(path, body, headers = {}) | ||
response = @client.post(path, body, headers) | ||
response.body | ||
end | ||
|
||
def put_body(path, body, headers = {}) | ||
response = @client.put(path, body, headers) | ||
response.body | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The proscription against class and global variables is in general pretty sensible. I haven't yet digested enough of this PR to know whether they can be done without, but if not, I think the Rubocop checks should be disabled on a linewise basis rather than disabling them globally. For example, one a line with a global variable like
$options
, you can append a comment:...to turn off the warning for that line only. It's a bit noisy, but I think it's better to draw attention to places where the usual style rules are being overridden by necessity.
...Also, when I check out your branch and search the codebase for the string
@@
, I don't find any instances. I would expect to find some if Ruby class variables are being employed somewhere. Is it actually necessary to disable theStyle/ClassVars
rule at all?