lib/seed_helper.rb
require Rails.root + 'db/seeds.d/020-roles_list.rb'
# methods which are used in seeds and migrations
class SeedHelper
class << self
# Check if audits show an object was renamed or deleted
# additional attributes may be specified for narrowing the scope but note
# that it can be slow if there's high number of audits for the specified type
def audit_modified?(type, name, attributes = {})
audits = Audit.where(:auditable_type => type.base_class.name, :auditable_name => name)
return true if filter_destroy_audits(audits.where(:action => :destroy), attributes).present?
audits.where(:action => :update).each do |audit|
return true if audit_changed(audit, name) && audited_matches_attributes?(audit, attributes)
end
false
end
def filter_destroy_audits(audits, attributes)
return audits unless attributes.present?
audits.select do |audit|
attributes.all? do |attribute, value|
audit.audited_changes[attribute] == value
end
end
end
def audited_matches_attributes?(audit, attributes)
return true unless attributes.present?
matching_attributes_on = audit.auditable
matching_attributes_on ||= Audit.find_by(:auditable_type => audit.auditable_type, :auditable_id => audit.auditable_id, :action => :destroy)&.audited_changes
matching_attributes_on.nil? || attributes.all? { |attr, val| matching_attributes_on[attr.to_s] == val }
end
def audit_changed(audit, name)
audit.audited_changes['name'].is_a?(Array) && audit.audited_changes['name'].first == name
end
def create_filters(role, collection)
collection.group_by(&:resource_type).each do |resource, permissions|
filter = Filter.new
filter.role = role
permissions.each do |permission|
filtering = filter.filterings.build
filtering.permission = permission
end
filter.save!
end
end
def create_role(role_name, options, builtin, check_audit = true)
description = options[:description]
if (existing = Role.find_by_name(role_name))
if existing.description != description
existing.update_attribute :description, description
end
# to allow create roles without updating permission
# the only usage we're aware of is in migration that cleans up custom roles in
# 20170221195674_tidy_current_roles because permissions are not present yet at this time
update_permissions = options[:update_permissions].nil? || options[:update_permissions]
update_role_permissions(existing, options) if update_permissions
return
end
return if check_audit && audit_modified?(Role, role_name) && (builtin == 0)
role = Role.new(:name => role_name, :builtin => builtin, :description => description)
if role.respond_to? :origin
role.origin = "foreman"
role.modify_locked = true
end
role.save!
permissions = Permission.where(:name => options[:permissions])
create_filters(role, permissions)
end
def update_role_permissions(role, options)
desired_permissions = options[:permissions].map(&:to_s)
existing_permissions = role.permissions.where(:name => PermissionsList.permissions.map(&:last)).pluck(:name)
role.ignore_locking do
missing_permissions = desired_permissions - existing_permissions
if missing_permissions.present?
role.add_permissions(missing_permissions, :save! => true)
end
# The built in role may have additional permissions added to it by users.
# To remove permissions from the default role use an explicit migration.
return if role.builtin == Role::BUILTIN_DEFAULT_ROLE
extra_permissions = existing_permissions - desired_permissions
if extra_permissions.present?
role.remove_permissions!(extra_permissions)
end
end
end
def import_raw_template(contents, vendor = 'Foreman')
metadata = Template.parse_metadata(contents)
raise "Attribute 'name' is required in metadata in order to seed the template" if metadata['name'].nil?
raise "Attribute 'model' is required in metadata in order to seed the template" if metadata['model'].nil?
name = metadata['name']
requirements = metadata['require'] || []
begin
model = metadata['model'].constantize
rescue NameError
logger.info("Unknown model #{metadata['model']} in template #{name}, skipping import.")
return
end
# Skip templates with custom changes
return if audit_modified?(model, name)
# Skip templates that don't match requirements
return unless test_template_requirements(name, requirements)
t = model.import_without_save(name, contents, { :default => true, :lock => true, :associate => 'new' })
t.vendor = vendor
if !t.persisted?
t.organizations = Organization.unscoped.all if t.respond_to?(:organizations=)
t.locations = Location.unscoped.all if t.respond_to?(:locations=)
raise "Unable to create template #{t.name}: #{format_errors t}" unless t.valid?
else
raise "Unable to update template #{t.name}: #{format_errors t}" unless t.ignore_locking { t.valid? }
end
t.ignore_locking { t.save! }
t
end
def import_templates(template_paths, vendor = 'Foreman')
template_paths.each do |path|
import_raw_template(File.read(path), vendor)
end
end
def partition_tables_templates
Dir["#{Rails.root}/app/views/unattended/partition_tables_templates/*.erb"]
end
def provisioning_templates
Dir["#{Rails.root}/app/views/unattended/provisioning_templates/**/*.erb"]
end
def report_templates
Dir["#{Rails.root}/app/views/unattended/report_templates/*.erb"]
end
def format_errors(model = nil)
return '(nil found)' if model.nil?
model.errors.full_messages.join(';')
end
def test_template_requirements(template_name, requirements)
requirements.each do |r|
plugin = Foreman::Plugin.find(r['plugin'])
if plugin.nil?
logger.info("Template #{template_name} requires plugin #{r['plugin']}, skipping import.")
return false
elsif r['version'] && (Gem::Version.new(plugin.version).release < Gem::Version.new(r['version']))
logger.info("Template #{template_name} requires plugin #{r['plugin']} >= #{r['version']}, skipping import.")
return false
end
end
true
end
private
def logger
Foreman::Logging.logger('app')
end
end
end