app/services/foreman_ansible/variables_importer.rb
# frozen_string_literal: true
module ForemanAnsible
# Methods to transform variable names coming from the proxy into
# Foreman AnsibleVariable objects
class VariablesImporter
include ::ForemanAnsible::ProxyAPI
VARIABLE_TYPES = {
'TrueClass' => 'boolean',
'FalseClass' => 'boolean',
'Integer' => 'integer',
'Fixnum' => 'integer',
'Float' => 'real',
'Array' => 'array',
'Hash' => 'hash',
'String' => 'string'
}.freeze
def initialize(proxy = nil)
@ansible_proxy = proxy
end
def import_variable_names(new_roles)
return import_variables(remote_variables, new_roles) if @ansible_proxy
import_variables(local_variables, new_roles)
end
def get_variables_names(role_name)
remote_variables[role_name]
end
def import_variables(role_variables, new_roles)
detect_changes(role_variables.map do |role_name, variables|
next if variables.blank?
role = import_new_role(role_name, new_roles)
next if role.blank?
initialize_variables(variables, role)
end.select(&:present?).flatten.compact)
end
def import_variables_roles(roles)
if roles['new']
imported_roles = roles['new'].keys
variables_to_import = {}
remote_variables.each do |role, variables|
next unless imported_roles.include? role
role_obj = AnsibleRole.find_by(:name => role)
variables_to_import[role] = {}
values = initialize_variables(variables, role_obj)
values.each do |value|
variables_to_import[role][value.key] = value.attributes.to_json
end
end
create_new_variables(variables_to_import)
end
return if roles['old'].empty?
variables_to_update = { 'new' => {}, 'obsolete' => {}, 'update' => {} }
import_variable_names([]).each do |kind, variables|
variables.each do |variable|
next unless roles['old'].values.map { |role| role['id'] }.include?(variable.ansible_role_id)
role_name = variable.ansible_role.name
variables_to_update[kind][role_name] ||= {}
variables_to_update[kind][role_name][variable.key] = variable.attributes.to_json
end
end
finish_import(variables_to_update['new'], variables_to_update['obsolete'], variables_to_update['update'])
end
def import_new_role(role_name, new_roles)
role = AnsibleRole.find_by(:name => role_name)
if role.blank? && new_roles.map(&:name).include?(role_name)
role = new_roles.find { |r| r.name == role_name }
end
role
end
def initialize_variables(variables, role)
variables.map do |variable_name, variable_default|
variable = AnsibleVariable.find_or_initialize_by(
:key => variable_name,
:ansible_role_id => role.id
)
variable.assign_attributes(:hidden_value => false,
:default_value => variable_default,
:key_type => infer_key_type(variable_default))
variable.imported = true if variable.new_record?
variable.valid? ? variable : nil
end
end
def detect_changes(imported)
changes = {}.with_indifferent_access
persisted, changes[:new] = imported.partition { |var| var.id.present? }
changed, _old = persisted.partition(&:changed?)
_overriden, changes[:update] = changed.partition(&:override?)
changes[:obsolete] = AnsibleVariable.where.not(id: persisted.pluck(:id)).
where.not(imported: false)
changes
end
def finish_import(new, obsolete, update)
results = { :added => [], :obsolete => [], :updated => [] }
results[:added] = create_new_variables(new) if new.present?
results[:obsolete] = delete_old_variables(obsolete) if obsolete.present?
results[:updated] = update_variables(update) if update.present?
results
end
def create_new_variables(variables)
iterate_over_variables(variables) do |role, memo, attrs|
variable = AnsibleVariable.new(
JSON.parse(attrs)
)
# Make sure, newly created variables are not hidden
variable.hidden_value = false
variable.ansible_role = ::AnsibleRole.find_by(:name => role)
variable.save
memo << variable
end
end
def update_variables(variables)
iterate_over_variables(variables) do |_role, memo, attrs|
attributes = JSON.parse(attrs)
var = AnsibleVariable.find attributes['id']
var.update(attributes)
memo << var
end
end
def delete_old_variables(variables)
iterate_over_variables(variables) do |_role, memo, attrs|
variable = AnsibleVariable.find(
JSON.parse(attrs)['id']
)
memo << variable.key
variable.destroy
end
end
private
def local_variables
::AnsibleVariable.all
end
def remote_variables
proxy_api.all_variables
end
def infer_key_type(value)
VARIABLE_TYPES[value.class.to_s] || 'string'
end
def iterate_over_variables(variables)
variables.reduce([]) do |memo, (role, vars)|
vars.map do |_key, attrs|
yield role, memo, attrs if block_given?
memo
end
end
end
end
end