app/models/provider_tag_mapping.rb
# A mapping matches labels on `resource_type` (NULL means any), `name` (required),
# and `value` (NULL means any).
#
# Note: `labeled_resource_type` doesn't really need to match the actual label's `resource_type`;
# it's simply matched against the type argument to Mapper#map_labels,
# and sometimes is a fake string like 'Vm' or 'Image' instead of a model name.
#
# Different labels might map to same tag, and one label might map to multiple tags.
#
# There are 2 kinds of rows:
# - When `label_value` is specified, we map only this value to a specific `tag`.
# TODO: drop this. This was never exposed in UI and is dead code to maintain.
#
# - When `label_value` is NULL, we map this name with any value to per-value tags.
# In this case, `tag` specifies the category under which to create
# the value-specific tag (and classification) on demand.
#
# All involved tags must also have a Classification.
#
class ProviderTagMapping < ApplicationRecord
belongs_to :tag
TAG_PREFIXES = ExtManagementSystem.label_mapping_prefixes.map { |name| "/managed/#{name}:" }.freeze
validate :validate_tag_prefix
# Return ProviderTagMapping::Mapper instance that holds all current mappings,
# can compute applicable tags, and create/find Tag records.
def self.mapper(mapper_parameters = {})
ProviderTagMapping::Mapper.new(in_my_region.all, mapper_parameters)
end
# Assigning/unassigning should be possible without Mapper instance, perhaps in another process.
# Checks whether a Tag record is under mapping control.
# TODO: expensive.
def self.controls_tag?(tag)
return false unless tag.classification.try(:read_only) # never touch user-assignable tags.
tag_ids = [tag.id, tag.category.tag_id].uniq
where(:tag_id => tag_ids).any?
end
# Assign/unassign mapping-controlled tags, preserving user-assigned tags.
# All tag references must have been resolved first by Mapper#find_or_create_tags.
def self.retag_entity(entity, tag_references)
mapped_tags = Mapper.references_to_tags(tag_references)
existing_tags = entity.tags.controlled_by_mapping
Tagging.transaction do
(mapped_tags - existing_tags).each do |tag|
Tagging.create!(:taggable => entity, :tag => tag)
end
(existing_tags - mapped_tags).tap do |tags|
Tagging.where(:taggable => entity, :tag => tags.collect(&:id)).destroy_all
end
end
entity.tags.reset
entity.taggings.reset
end
def validate_tag_prefix
return if labeled_resource_type == "_all_entities_"
unless TAG_PREFIXES.any? { |prefix| tag.name.start_with?(prefix) }
errors.add(:tag_id, "tag category name #{tag.name} doesn't start with any of #{TAG_PREFIXES}")
end
end
end