forked from theforeman/foreman
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes #17527 - Add support for hostgroup facets
- Loading branch information
1 parent
addd8e4
commit 3927dc2
Showing
15 changed files
with
608 additions
and
214 deletions.
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
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,35 @@ | ||
require 'facets' | ||
|
||
module Facets | ||
module HostgroupExtensions | ||
extend ActiveSupport::Concern | ||
include Facets::ModelExtensionsBase | ||
|
||
included do | ||
configure_facet(:hostgroup, :hostgroup, :hostgroup_id) | ||
|
||
Facets.after_entry_created do |entry| | ||
register_facet_relation(entry) if entry.has_hostgroup_configuration? | ||
end | ||
end | ||
|
||
def hostgroup_ancestry_cache | ||
@hostgroup_ancestry_cache ||= begin | ||
hostgroup_facets = Facets.registered_facets.select { |_, facet| facet.has_hostgroup_configuration? } | ||
# return sorted list of ancestors with all facets in place | ||
ancestors.includes(hostgroup_facets.keys) | ||
end | ||
end | ||
|
||
def inherited_facet_attributes(facet_config) | ||
inherited_attributes = send(facet_config.name).inherited_attributes | ||
hostgroup_ancestry_cache.reverse_each do |hostgroup| | ||
hg_facet = hostgroup.send(facet_config.name) | ||
next unless hg_facet | ||
inherited_attributes.merge!(hg_facet.inherited_attributes) { |_, left, right| left || right } | ||
end | ||
|
||
inherited_attributes | ||
end | ||
end | ||
end |
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,32 @@ | ||
require 'facets' | ||
|
||
module Facets | ||
module HostgroupFacet | ||
extend ActiveSupport::Concern | ||
|
||
included do | ||
belongs_to :hostgroup, class_name: "::Hostgroup", foreign_key: :hostgroup_id | ||
end | ||
|
||
module ClassMethods | ||
def inherit_attributes(*attributes) | ||
attributes_to_inherit.concat(attributes).uniq! | ||
end | ||
|
||
def attributes_to_inherit | ||
@attributes_to_inherit ||= begin | ||
_, facet_config = Facets.find_facet_by_class(self, :hostgroup) | ||
if facet_config.has_host_configuration? && facet_config.host_configuration.model == self | ||
attribute_names - ['id', 'created_at', 'updated_at'] | ||
else | ||
[] | ||
end | ||
end | ||
end | ||
end | ||
|
||
def inherited_attributes | ||
attributes.slice(*self.class.attributes_to_inherit) | ||
end | ||
end | ||
end |
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,135 @@ | ||
require 'facets' | ||
|
||
module Facets | ||
module ModelExtensionsBase | ||
extend ActiveSupport::Concern | ||
|
||
included do | ||
include InstanceMethods | ||
|
||
refresh_facet_relations | ||
end | ||
|
||
module ClassMethods | ||
def configure_facet(facet_type, base_model_symbol, base_model_id_field, &block) | ||
config = OpenStruct.new( | ||
facet_type: facet_type, | ||
base_model_symbol: base_model_symbol, | ||
base_model_id_field: base_model_id_field) | ||
config.callback = block | ||
|
||
facet_configurations << config | ||
end | ||
|
||
def facet_configurations | ||
@facet_configurations ||= [] | ||
end | ||
|
||
def refresh_facet_relations | ||
Facets.registered_facets.values.each do |facet_config| | ||
register_facet_relation(facet_config) | ||
end | ||
end | ||
|
||
# This method is used to add all relation objects necessary for accessing facet from the host object. | ||
# It: | ||
# 1. Adds active record one to one association | ||
# 2. Adds the ability to set facet's attributes via Host#attributes= method | ||
# 3. Extends Host::Managed model with extension module defined by facet's configuration | ||
# 4. Includes facet in host's cloning mechanism | ||
# 5. Adds compatibility properties forwarders so old property calls will still work after moving them to a facet: | ||
# host.foo # => will call Host.my_facet.foo | ||
def register_facet_relation(facet_config) | ||
facet_configurations.each do |extension_config| | ||
return unless facet_config.has_configuration(extension_config.facet_type) | ||
type_config = facet_config.send "#{extension_config.facet_type}_configuration" | ||
facet_name = facet_config.name | ||
|
||
extend_model_attributes(type_config, facet_name, extension_config) | ||
extend_model(type_config, facet_name) | ||
handle_migrations(type_config, facet_name) | ||
|
||
extension_config.callback&.call(facet_config) | ||
end | ||
end | ||
|
||
def handle_migrations(type_config, facet_name) | ||
return unless Foreman.in_setup_db_rake? | ||
# To prevent running into issues in old migrations when new facet is defined but not migrated yet. | ||
# We define it only when in migration to avoid this unnecessary checks outside for the migration | ||
@facet_relation_db_migrate_extensions ||= {} # prevent duplicates | ||
return if @facet_relation_db_migrate_extensions.key?(facet_name) | ||
@facet_relation_db_migrate_extensions[facet_name] = Module.new do | ||
define_method(facet_name) do | ||
if type_config.model.table_exists? | ||
super() | ||
else | ||
logger.warn("Table for #{facet_name} not defined yet: skipping the facet data") | ||
nil | ||
end | ||
end | ||
end | ||
prepend @facet_relation_db_migrate_extensions[facet_name] | ||
end | ||
|
||
def extend_model(type_config, facet_name) | ||
include type_config.extension if type_config.extension | ||
|
||
type_config.compatibility_properties&.each do |prop| | ||
define_method(prop) { |*args| forward_property_call(prop, args, facet_name) } | ||
end | ||
end | ||
|
||
def extend_model_attributes(type_config, facet_name, extension_config) | ||
has_one facet_name, | ||
:class_name => type_config.model.name, | ||
:foreign_key => extension_config.base_model_id_field, | ||
:inverse_of => extension_config.base_model_symbol, | ||
:dependent => type_config.dependent | ||
accepts_nested_attributes_for facet_name, :update_only => true, :reject_if => :all_blank | ||
|
||
alias_method "#{facet_name}_attributes", facet_name | ||
end | ||
end | ||
|
||
# define instance methods in a module, so they will be set | ||
# even for models that do not include this module directly | ||
# like in case Hostgroup -> HostgroupExtensions -> ModelExtensionsBase | ||
module InstanceMethods | ||
def attributes | ||
hash = super | ||
|
||
# include all facet attributes by default | ||
facets_with_definitions.each do |facet, facet_definition| | ||
hash["#{facet_definition.name}_attributes"] = facet.attributes.reject { |key| %w(created_at updated_at).include? key } | ||
end | ||
hash | ||
end | ||
|
||
def facets | ||
facets_with_definitions.keys | ||
end | ||
|
||
# This method will return a hash of facets for a specific host including the coresponding definitions. | ||
# The output should look like this: | ||
# { host.puppet_aspect => Facets.registered_facets[:puppet_aspect] } | ||
def facets_with_definitions | ||
facet_types = self.class.facet_configurations.map(&:facet_type) | ||
tuples = Facets.registered_facets(facet_types).values.map do |facet_config| | ||
facet = send(facet_config.name) | ||
[facet, facet_config] if facet | ||
end | ||
Hash[tuples.compact] | ||
end | ||
end | ||
|
||
private | ||
|
||
def forward_property_call(property, args, facet) | ||
facet_instance = send(facet) | ||
return nil unless facet_instance | ||
|
||
facet_instance.send(property, *args) | ||
end | ||
end | ||
end |
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
Oops, something went wrong.