theforeman/foreman_remote_execution

View on GitHub
app/models/targeting.rb

Summary

Maintainability
A
25 mins
Test Coverage
class Targeting < ApplicationRecord

  STATIC_TYPE = 'static_query'.freeze
  DYNAMIC_TYPE = 'dynamic_query'.freeze
  TYPES = { STATIC_TYPE => N_('Static Query'), DYNAMIC_TYPE => N_('Dynamic Query') }.freeze
  RESOLVE_PERMISSION = :view_hosts

  ORDERED = 'ordered_execution'.freeze
  RANDOMIZED = 'randomized_execution'.freeze
  ORDERINGS = { ORDERED => N_('Alphabetical'), RANDOMIZED => N_('Randomized') }.freeze

  belongs_to :user
  belongs_to :bookmark

  has_many :targeting_hosts, :dependent => :destroy
  has_many :hosts, -> { order TargetingHost.table_name + '.id' }, :through => :targeting_hosts
  has_one :job_invocation, :dependent => :delete
  has_many :template_invocations, :through => :job_invocation

  validates :targeting_type, :presence => true, :inclusion => Targeting::TYPES.keys
  validate :bookmark_or_query_is_present

  before_create :assign_search_query, :if => :static?

  def clone
    if static?
      self.dup
    else
      Targeting.new(
        :user => self.user,
        :bookmark_id => self.bookmark.try(:id),
        :targeting_type => self.targeting_type,
        :search_query => self.search_query
      )
    end.tap(&:save)
  end

  def resolve_hosts!
    raise ::Foreman::Exception, _('Cannot resolve hosts without a user') if user.nil?
    raise ::Foreman::Exception, _('Cannot resolve hosts without a bookmark or search query') if bookmark.nil? && search_query.blank?

    self.search_query = bookmark.query if dynamic? && bookmark.present?
    mark_resolved!
    self.validate!
    # avoid validation of hosts objects - they will be loaded for no reason.
    #   pluck(:id) returns duplicate results for HostCollections
    host_ids = User.as(user.login) { Host.execution_scope.authorized(RESOLVE_PERMISSION, Host).search_for(search_query).order(:name, :id).pluck(:id).uniq }
    host_ids.shuffle!(random: Random.new) if randomized_ordering
    self.assign_host_ids(host_ids)
    self.save(:validate => false)
  end

  def assign_host_ids(host_ids)
    # this can be optimized even more, by introducing bulk insert
    self.targeting_hosts.build(host_ids.map { |id| { :host_id => id } })
  end

  def dynamic?
    targeting_type == DYNAMIC_TYPE
  end

  def static?
    targeting_type == STATIC_TYPE
  end

  def self.build_query_from_hosts(ids)
    return '' if ids.empty?

    hosts = Host.execution_scope.where(:id => ids).distinct.pluck(:name)
    hosts.any? ? "name ^ (#{hosts.join(', ')})" : ''
  end

  def resolved?
    self.resolved_at.present?
  end

  def mark_resolved!
    self.resolved_at = Time.zone.now
  end

  private

  def bookmark_or_query_is_present
    if bookmark.blank? && search_query.blank?
      errors.add :bookmark_id, _('Must select a bookmark or enter a search query')
      errors.add :search_query, _('Must select a bookmark or enter a search query')
    end
  end

  def assign_search_query
    self.search_query = bookmark.query if static? && bookmark
  end
end