Skip to content
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

Add rake script to export/import miq alerts and alert profiles #14126

Merged
merged 23 commits into from
May 25, 2017
Merged

Add rake script to export/import miq alerts and alert profiles #14126

merged 23 commits into from
May 25, 2017

Conversation

branic
Copy link
Contributor

@branic branic commented Mar 1, 2017

These rake scripts and classes provide functionality for exporting/importing of the following ManageIQ object types:

  • alerts (MiqAlert)
  • alert profiles (MiqAlertSet)

This PR also sets up the framework for future export/import PRs for other ManageIQ object types.

These scripts are based on the CFME RH Consulting Scripts and are used by Red Hat consultants to enable storing customizations in Git and maintaining customizations between environments (e.g. dev/qa/prod) for an SDLC lifecycle.

PR 112 in manageiq-appliance is dependant on this PR and provides a bash script wrapper for these rake scripts.

@Fryguy
Copy link
Member

Fryguy commented Mar 2, 2017

@branic First off, thanks for all of this! Just wanted to state that so you know that my comments below don't reflect that we don't appreciate all of this work 😄

  • Is the goal to just get these in as is, and then clean them up afterwards? Or do we want to clean them up in the course of this PR? For one, the Red Hat references and namings have to be removed. (e.g. rhconsulting)
  • Rubocop and style issues will probably need to be cleaned up for much stuff. If the goal is to just get the original in and iterate on that, we can let that go. Also, the lib/tasks directory is probably not the right place for most of the classes and utility modules, so those would probably have to move.
  • Why are there commented lines of code? Are those optional or just unused?
  • I see authorship at the top of these files, but we generally don't have authorship in files as per our license the authors are the "ManageIQ Authors". Is it ok, if we move that? Was there a license on the source of these that we need to adhere to? If the author wants attribution, perhaps they should make the PR?

@branic
Copy link
Contributor Author

branic commented Mar 2, 2017

Thanks for the comments @Fryguy. This first commit was just a straight copy of the files as they exist in the rhconsulting github. I'll work on cleaning up based on your comments and the Rubocop issues.

The commented code is from keeping old code when the code changed and I can remove them as part of the cleanup effort.

Are additional commits to this PR ok for the cleanups?

@Fryguy
Copy link
Member

Fryguy commented Mar 2, 2017

I think comments will come as the code progresses. I think the best approach to get this in faster and easier might be to slim this down to, say, only one of the import scripts + the accompanying helper modules. A 1700+ line PR is almost guaranteed to take a long time. However, if you trim it down to just one script, then we can iterate the review process on just that script, uncovering all of the various comments, and get it in quickly. Once it's in, now you have a foundation for more rake tasks (probably one per PR) and since all of the comments have been already iterated on and discussed, getting these new PRs in will be a LOT faster.

@Fryguy
Copy link
Member

Fryguy commented Mar 2, 2017

Are additional commits to this PR ok for the cleanups?

Yes, that is preferable

@branic branic changed the title Add rake scripts to export/import miq objects Add alert rake script to export/import miq objects Mar 11, 2017
@branic
Copy link
Contributor Author

branic commented Mar 11, 2017

@Fryguy I've removed all but the script to export/import alerts and the two helper modules from this PR. There has been some re-factoring to get rid of references to rhconsulting and general cleanup. I'm not sure what the best way to fix the last of the rubocop offenses.

Hopefully this makes it easier to review and can be used as a model for getting the other rake scripts added (in seperate PR's).

All feedback is appreciated.

@branic branic changed the title Add alert rake script to export/import miq objects Add rake script to export/import miq alerts and alert profiles Mar 11, 2017
Copy link
Member

@carbonin carbonin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking these helper classes should probably be namespaced a bit. Maybe Vmdb::Serializer::Alert (naming is hard). That would allow us to create a new Vmdb::Serializer for whatever we needed to add. Then common functionality like the illegal character and option parsing can go in the Vmdb::Serializer module itself. Then they would live in lib/vmdb/serializer.rb and lib/vmdb/serializer/alert.rb.

Judging from the amount of common code here, it feels like we could do even better with the design, but we can see how it evolves from there. What do you think @Fryguy ?

@@regex_remove_spaces = %r{[/| ]}

# Replacement character
@@replacement = '_'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These class variables should be constants.

@@ -0,0 +1,100 @@
# @class EvmExportImportOptions
# @brief Simple class to parse options passed in to a rake task.
class EvmImportExportOptions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class can probably be replaced by Trollop using the rake task options like

opts = Trollop.options(EvmRakeHelper.extract_command_options) do

require_relative 'evm_import_export_illegal_chars'
require_relative 'evm_import_export_options'

class EvmImportExportAlerts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These classes should probably live in lib/task_helpers.

@branic
Copy link
Contributor Author

branic commented Mar 23, 2017

@Fryguy, @carbonin,

I've refactored this PR to break out the classes and functions. I also implemented Trollop for processing the arguments and removed positional arguments (for example the destination directory for an export has to be specified as an option.

Copy link
Member

@carbonin carbonin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few of these comments apply to multiple places. I want to make suggestions about deduplicating some logic, but I'm not sure what will be common to all of the objects that you want to eventually add so I'll hold off for now.

module TaskHelpers
class Imports
class Alerts
class ParsedNonDialogYamlError < StandardError; end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this get used? I don't see it in any of these files. If it is used it should live in one place rather than in every class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is defined in all of the rhconsulting rake scripts, but only used in one of them. I'll remove this class as it is not used.

Trollop.die :directory, 'Destination directory must already exist' unless File.directory?(options[:directory])
Trollop.die :directory, 'Destination directory must be writeable' unless File.writable?(options[:directory])

options.delete_if { |_k, v| v.nil? || v == false }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why this is needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it is necessary either. It came as part of the copying code from https://github.com/ManageIQ/manageiq/blob/22efb0914107963b95e064c9d7adb2aa6d905e2a/lib/tasks/evm_dba.rake for Trollop.


private

def export_alert_sets(options)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be a separate method if the options are getting passed right through from #export.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I'll change it.

alertsets = YAML.load_file(fname)
import_alert_sets(alertsets)
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dir.glob will work if given a file name, so this can be simplified:

glob = File.file?(options[:source]) ? options[:source] : "#{options[:source]}/*.yaml"
Dir.glob(glob) do |fname|
  alertsets = YAML.load_file(fname)
  import_alert_sets(alertsets)
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I'll change to the simplified version.

# @param [Hash] options If options[:keep_spaces] == true, then keep spaces in
# \p str , otherwise replace spaces. (default = false, replace spaces)
def self.replace_chars(str, options = {})
if options.key?(:keep_spaces) && options[:keep_spaces] == true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A hash will return nil for a key that doesn't exist, so you don't need this check.

You can just check if options[:keep_spaces]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed this as well.

# @param [String] str string to replace
# @param [Hash] options If options[:keep_spaces] == true, then keep spaces in
# \p str , otherwise replace spaces. (default = false, replace spaces)
def self.replace_chars(str, options = {})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like if this had a better name we wouldn't need the comment.

Maybe something like safe_filename?

Also it would document itself better if we passed the option specifically rather than passing the whole options hash for one value. Then the method would look like def self.safe_filename(filename, keep_spaces = false) which is a lot more clear.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I'll change the method name and definition.

@branic
Copy link
Contributor Author

branic commented Mar 23, 2017

@carbonin I've made the changes you requested. I've also adjusted the error handling and added some error handling when performing the imports.

options
end

def self.validate_directory(directory)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all of this should be done right after parsing the options.

Also this might work better if it just returned the string then you could do something like:

error = validate_directory(options[:directory])
Trollop.die :directory, error if error

Then you don't have to write to standard out and you also don't need to define the exceptions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed this as suggested.

end

def self.validate_directory(directory)
unless directory
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this check is necessary because directory is a required argument.

@branic
Copy link
Contributor Author

branic commented Apr 5, 2017

@carbonin @Fryguy

Are there other changes you would like to see with this PR?

The travis-cli failure was caused by a larger issue with travis when I pushed the last commit. I'm not sure how to clear the failure.

Copy link
Member

@carbonin carbonin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the only other thing would be some specs. It would be great to see tests at least for the Exports.safe_filename and the validate_* methods.

Ideally we would have tests around the whole thing with simple data files to import from and export to. Then we would compare the output against the "right" answer ...

@carbonin carbonin closed this Apr 5, 2017
@carbonin carbonin reopened this Apr 5, 2017
@branic
Copy link
Contributor Author

branic commented Apr 6, 2017

@carbonin

I've added tests for the safe_filename method, but I could use some guidance on how to handle the tests for the validate_* methods if you have any ideas or can point me to some sample code.

Also, I had to rebase my branch to be able to get a working environment which looks like it messed up the commit logs and such. I'm not sure how to fix this or what I need to do.

As always any help and feedback is appreciated.

@@ -0,0 +1,44 @@
describe '#safe_filename' do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The top level block here should be:

describe TaskHelpers::Exports do
  ...
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Changed.

module TaskHelpers
class Exports
def self.new_filename(filename, keep_spaces = false)
new_filename = filename
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you changed the method name and the variable name here @branic

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this particular line isn't needed because String#gsub returns a copy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops I did change both trying to be too efficient with search and replace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had that line because the gsub returned nil if keep_spaces was true. I've changed the code to eliminate this assignment by using a ternary operator.

new_filename = filename
new_filename = new_filename.gsub(%r{[ ]}, '_') unless keep_spaces
new_filename = new_filename.gsub(%r{[/]}, 'slash')
new_filename.gsub(%r{[|]}, 'pipe')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should be able to combine these by using a hash as the second parameter (see https://ruby-doc.org/core-2.3.3/String.html#method-i-gsub)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed that when I read the doc page the first time. I've changed to use a hash.

export_dir = options[:directory]

MiqAlertSet.order(:id).all.each do |a|
# Replace characters in the description that are not allowed in filenames
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed the comment.

task :alertprofiles => :environment do
options = TaskHelpers::Imports.parse_options
TaskHelpers::Imports::AlertSets.new.import(options)
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this task need the exit also?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct. I've added the missing line.

Copy link
Member

@carbonin carbonin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one minor nit with the specs.

I'm okay with this. I would really like the main import/export methods to also have specs, but that may be asking a bit much for the first pass.

I'll leave the final determination to @Fryguy though.

@@ -0,0 +1,44 @@
describe TaskHelpers::Exports do
context 'with filename' do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be a describe ".safe_filename" do

The convention in rspec is to describe the class/module under test then describe each method you are testing then use it ".." do blocks for each test case on a particular method.

Then context can be used for shared setup between it or describe blocks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. I'll add another describe block for the method.

@branic
Copy link
Contributor Author

branic commented Apr 10, 2017

I'd like to be able to have tests for the import/export methods (and maybe even the rake tasks), but I'm not sure how to setup the tests so I need to do some more research in how to create a file to import and write and test the export within RSpec.

@branic
Copy link
Contributor Author

branic commented Apr 10, 2017

I've thought about using FakeFS [1] to be able to temporarily create the files for the RSpec tests, but the gem isn't included in ManageIQ and I don't know if a gem should be brought in just for running tests.

[1] https://github.com/fakefs/fakefs

@Fryguy
Copy link
Member

Fryguy commented Apr 17, 2017

I'm ok with a gem that's just for tests. Just be sure to put it into the test group in the Gemfile.

@Fryguy Fryguy assigned chessbyte and unassigned gmcculloug Apr 17, 2017
Copy link
Member

@carbonin carbonin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Just one other comment.

@@ -0,0 +1,54 @@
describe 'TaskHelpers::Imports::AlertSets' do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a constant, not a string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, I thought i got all of those. It's fixed now.

Copy link
Member

@carbonin carbonin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great @branic. Will merge when it goes green 😄

@miq-bot
Copy link
Member

miq-bot commented May 25, 2017

Some comments on commits https://github.com/branic/manageiq/compare/1c2d26a1930bfa362d928bcb0a9d09c60cf5a976~...042108573513b66e81121cc09e71febffbafa885

lib/task_helpers/imports/alert_sets.rb

  • ⚠️ - 11 - Detected puts. Remove all debugging statements.

lib/task_helpers/imports/alerts.rb

  • ⚠️ - 11 - Detected puts. Remove all debugging statements.

@miq-bot
Copy link
Member

miq-bot commented May 25, 2017

Checked commits https://github.com/branic/manageiq/compare/1c2d26a1930bfa362d928bcb0a9d09c60cf5a976~...042108573513b66e81121cc09e71febffbafa885 with ruby 2.2.6, rubocop 0.47.1, and haml-lint 0.20.0
13 files checked, 0 offenses detected
Everything looks fine. 🍰

@carbonin carbonin added this to the Sprint 62 Ending Jun 5, 2017 milestone May 25, 2017
@carbonin carbonin merged commit cb5f57d into ManageIQ:master May 25, 2017
@branic branic deleted the rhconsulting_scripts branch May 25, 2017 21:04
@gmcculloug
Copy link
Member

@branic Can you modify the description of this PR to match the object type that is supported with the final version of the PR.

I would also suggest creating a git issue with a check list to track all the of the types so we can link to the PRs and check them off as we go.

@branic
Copy link
Contributor Author

branic commented Jun 9, 2017

@gmcculloug I've updated the description as well as opened an issue to track all of the object types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants