-
Notifications
You must be signed in to change notification settings - Fork 900
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
Pluggable ScanItems #19388
Merged
Merged
Pluggable ScanItems #19388
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
module ScanItem::Seeding | ||
extend ActiveSupport::Concern | ||
|
||
SCAN_ITEMS_DIR = Rails.root.join("product", "scan_items") | ||
|
||
# Default ScanItemSets | ||
SAMPLE_VM_PROFILE = {:name => "sample", :description => "VM Sample", :mode => 'Vm', :read_only => true}.freeze | ||
SAMPLE_HOST_PROFILE = {:name => "host sample", :description => "Host Sample", :mode => 'Host', :read_only => true}.freeze | ||
DEFAULT_HOST_PROFILE = {:name => "host default", :description => "Host Default", :mode => 'Host'}.freeze | ||
|
||
module ClassMethods | ||
def seed | ||
transaction do | ||
seed_all_scan_items | ||
seed_scan_item_sets | ||
end | ||
end | ||
|
||
# Used for seeding a specific scan_item for test purposes | ||
def seed_scan_item(name) | ||
path = seed_files.detect { |f| File.basename(f).include?(name) } | ||
raise "scan item #{name.inspect} not found" if path.nil? | ||
|
||
seed_record(path, ScanItem.find_by(:filename => seed_filename(path))) | ||
end | ||
|
||
private | ||
|
||
def seed_all_scan_items | ||
scan_items = where(:prod_default => 'Default').index_by do |f| | ||
seed_filename(f.filename) | ||
end | ||
seed_files.each do |f| | ||
seed_record(f, scan_items.delete(seed_filename(f))) | ||
end | ||
|
||
if scan_items.any? | ||
_log.info("Deleting the following ScanItems(s) as they no longer exist: #{scan_items.keys.sort.collect(&:inspect).join(", ")}") | ||
ScanItem.destroy(scan_items.values.map(&:id)) | ||
end | ||
end | ||
|
||
def seed_scan_item_sets | ||
vm_profile = ScanItemSet.find_or_initialize_by(:name => SAMPLE_VM_PROFILE[:name]) | ||
vm_profile.update!(SAMPLE_VM_PROFILE) | ||
|
||
# Create sample Host scan profiles | ||
host_profile = ScanItemSet.find_or_initialize_by(:name => SAMPLE_HOST_PROFILE[:name]) | ||
host_profile.update!(SAMPLE_HOST_PROFILE) | ||
|
||
# Create default Host scan profiles | ||
host_default = ScanItemSet.find_or_initialize_by(:name => DEFAULT_HOST_PROFILE[:name]) | ||
load_host_default = host_default.new_record? | ||
host_default.update!(DEFAULT_HOST_PROFILE) | ||
|
||
where(:prod_default => 'Default').each do |s| | ||
case s.mode | ||
when "Host" | ||
host_profile.add_member(s) | ||
host_default.add_member(s) if load_host_default | ||
when "Vm" | ||
vm_profile.add_member(s) | ||
end | ||
end | ||
end | ||
|
||
def seed_record(path, scan_item) | ||
scan_item ||= ScanItem.new | ||
|
||
# DB and filesystem have different precision so calling round is done in | ||
# order to eliminate the second fractions diff otherwise the comparison | ||
# of the file time and the scan_item time from db will always be different. | ||
mtime = File.mtime(path).utc.round | ||
scan_item.file_mtime = mtime | ||
|
||
if scan_item.new_record? || scan_item.changed? | ||
filename = seed_filename(path) | ||
|
||
_log.info("#{scan_item.new_record? ? "Creating" : "Updating"} ScanItem #{filename.inspect}") | ||
|
||
yml = YAML.load_file(path).symbolize_keys | ||
name = yml[:name].strip | ||
|
||
attrs = yml.slice(*column_names_symbols) | ||
attrs.delete(:id) | ||
attrs[:filename] = filename | ||
attrs[:file_mtime] = mtime | ||
attrs[:prod_default] = "Default" | ||
attrs[:name] = name | ||
|
||
begin | ||
scan_item.update!(attrs) | ||
rescue ActiveRecord::RecordInvalid | ||
duplicate = find_by(:name => name) | ||
if duplicate&.prod_default == "Custom" | ||
_log.warn("A custom scan_item already exists with the name #{duplicate.name.inspect}. Skipping...") | ||
elsif duplicate | ||
_log.warn("A default scan_item named '#{duplicate.name.inspect}' loaded from '#{duplicate.filename}' already exists. Updating attributes of existing report...") | ||
duplicate.update!(attrs) | ||
else | ||
raise | ||
end | ||
end | ||
end | ||
end | ||
|
||
def seed_files | ||
Dir.glob(SCAN_ITEMS_DIR.join("*.{yml,yaml}")).sort + seed_plugin_files | ||
end | ||
|
||
def seed_plugin_files | ||
Vmdb::Plugins.flat_map do |plugin| | ||
Dir.glob(plugin.root.join("content/scan_items/*.{yml,yaml}")).sort | ||
end | ||
end | ||
|
||
def seed_filename(path) | ||
File.basename(path) | ||
end | ||
end | ||
end |
13 changes: 13 additions & 0 deletions
13
spec/models/scan_item/data/product/scan_items/testing_scan_item.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
--- | ||
:item_type: category | ||
:definition: | ||
content: | ||
- target: vmconfig | ||
- target: vmevents | ||
- target: accounts | ||
- target: software | ||
- target: services | ||
- target: system | ||
:name: testing_scan_item | ||
:description: Testing ScanItem | ||
:mode: Vm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
require 'fileutils' | ||
|
||
describe ScanItem do | ||
describe "::Seeding" do | ||
include_examples(".seed called multiple times", 6) | ||
|
||
describe ".seed" do | ||
let(:tmpdir) { Pathname.new(Dir.mktmpdir) } | ||
let(:scan_item_dir) { tmpdir.join("product/scan_items") } | ||
let(:scan_item_yml) { scan_item_dir.join("testing_scan_item.yaml") } | ||
let(:data_dir) { Pathname.new(__dir__).join("data/product") } | ||
|
||
before do | ||
FileUtils.mkdir_p(scan_item_dir) | ||
FileUtils.cp_r(Rails.root.join("product", "scan_items", "scan_item_cat.yaml"), scan_item_dir, :preserve => true) | ||
|
||
stub_const("ScanItem::Seeding::SCAN_ITEMS_DIR", scan_item_dir) | ||
expect(Vmdb::Plugins).to receive(:flat_map).at_least(:once) { [] } | ||
end | ||
|
||
after do | ||
FileUtils.rm_rf(tmpdir) | ||
end | ||
|
||
it "creates, updates, and changes records" do | ||
described_class.seed | ||
|
||
orig_scan_item = ScanItem.find_by(:name => "sample_category") | ||
expect(orig_scan_item).to_not be_nil | ||
|
||
expect(ScanItem.where(:name => "testing_scan_item")).to_not exist | ||
|
||
# Add new records | ||
FileUtils.cp_r(data_dir, tmpdir, :preserve => true) | ||
|
||
described_class.seed | ||
|
||
scan_item = ScanItem.find_by(:name => "testing_scan_item") | ||
|
||
expect(scan_item).to have_attributes( | ||
:name => "testing_scan_item", | ||
:description => "Testing ScanItem", | ||
:filename => "testing_scan_item.yaml", | ||
:file_mtime => File.mtime(scan_item_yml).utc.round, | ||
:prod_default => "Default", | ||
:mode => "Vm", | ||
:definition => a_hash_including("content" => include("target" => "vmconfig")) | ||
) | ||
|
||
# Update reports | ||
orig_scan_item_mtime = orig_scan_item.file_mtime | ||
scan_item_mtime = scan_item.file_mtime | ||
|
||
# The mtime rounding is granular to the second, so need to be higher | ||
# than that for test purposes | ||
FileUtils.touch(scan_item_yml, :mtime => 1.second.from_now.to_time) | ||
|
||
described_class.seed | ||
|
||
expect(orig_scan_item.reload.file_mtime).to eq(orig_scan_item_mtime) | ||
expect(scan_item.reload.file_mtime).to_not eq(scan_item_mtime) | ||
|
||
# Delete reports | ||
FileUtils.rm_f(scan_item_yml) | ||
|
||
described_class.seed | ||
|
||
expect { scan_item.reload }.to raise_error(ActiveRecord::RecordNotFound) | ||
end | ||
end | ||
end | ||
|
||
describe ".seed_files (private)" do | ||
it "will include files from core" do | ||
expect(described_class.send(:seed_files)).to include( | ||
a_string_starting_with(Rails.root.join("product", "scan_items").to_s) | ||
) | ||
end | ||
|
||
it "will include files from plugins" do | ||
skip "Until a plugin brings a scan item" | ||
expect(described_class.send(:seed_files)).to include( | ||
a_string_matching(%r{#{Bundler.install_path}/.+/content/scan_items/}) | ||
) | ||
end | ||
end | ||
end |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
@pemcg Just wanted to clue you into this one. When you end up adding your scan item to v2v, please submit a PR to remove this skip line.
For now, I couldn't figure out a way to ensure this works without having a plugin bring a scan item. In the future @bdunne suggested we move our default scan items to manageiq-content, drop the core bringing scan items, then this test will just work.