# 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 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_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 ) if variable.new_record? variable.assign_attributes(:default_value => variable_default, :key_type => infer_key_type(variable_default), :imported => true) end variable.ansible_role = role variable.valid? ? variable : nil end end def detect_changes(imported) changes = {}.with_indifferent_access persisted, changes[:new] = imported.partition { |role| role.id.present? } changes[:update], _old = persisted.partition(&:changed?) changes[:obsolete] = AnsibleVariable.where.not(:id => persisted.pluck(:id), :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)['ansible_variable'] ) 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)['ansible_variable'] 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)['ansible_variable']['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