1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
require_relative '../../puppet/indirector'
require_relative '../../puppet/indirector/errors'
require_relative '../../puppet/indirector/indirection'
require_relative '../../puppet/util/instance_loader'
# A simple class that can function as the base class for indirected types.
class Puppet::Indirector::Terminus
require_relative '../../puppet/util/docs'
extend Puppet::Util::Docs
class << self
include Puppet::Util::InstanceLoader
attr_accessor :name, :terminus_type
attr_reader :abstract_terminus, :indirection
# Are we an abstract terminus type, rather than an instance with an
# associated indirection?
def abstract_terminus?
abstract_terminus
end
# Convert a constant to a short name.
def const2name(const)
const.sub(/^[A-Z]/) { |i| i.downcase }.gsub(/[A-Z]/) { |i| "_#{i.downcase}" }.intern
end
# Look up the indirection if we were only provided a name.
def indirection=(name)
if name.is_a?(Puppet::Indirector::Indirection)
@indirection = name
else
ind = Puppet::Indirector::Indirection.instance(name)
if ind
@indirection = ind
else
raise ArgumentError, _("Could not find indirection instance %{name} for %{terminus}") % { name: name, terminus: self.name }
end
end
end
def indirection_name
@indirection.name
end
# Register our subclass with the appropriate indirection.
# This follows the convention that our terminus is named after the
# indirection.
def inherited(subclass)
longname = subclass.to_s
if longname =~ /#<Class/
raise Puppet::DevError, _("Terminus subclasses must have associated constants")
end
names = longname.split("::")
# Convert everything to a lower-case symbol, converting camelcase to underscore word separation.
name = names.pop.sub(/^[A-Z]/) { |i| i.downcase }.gsub(/[A-Z]/) { |i| "_#{i.downcase}" }.intern
subclass.name = name
# Short-circuit the abstract types, which are those that directly subclass
# the Terminus class.
if self == Puppet::Indirector::Terminus
subclass.mark_as_abstract_terminus
return
end
# Set the terminus type to be the name of the abstract terminus type.
# Yay, class/instance confusion.
subclass.terminus_type = self.name
# This subclass is specifically associated with an indirection.
raise("Invalid name #{longname}") unless names.length > 0
processed_name = names.pop.sub(/^[A-Z]/) { |i| i.downcase }.gsub(/[A-Z]/) { |i| "_#{i.downcase}" }
if processed_name.empty?
raise Puppet::DevError, _("Could not discern indirection model from class constant")
end
# This will throw an exception if the indirection instance cannot be found.
# Do this last, because it also registers the terminus type with the indirection,
# which needs the above information.
subclass.indirection = processed_name.intern
# And add this instance to the instance hash.
Puppet::Indirector::Terminus.register_terminus_class(subclass)
end
# Mark that this instance is abstract.
def mark_as_abstract_terminus
@abstract_terminus = true
end
def model
indirection.model
end
# Convert a short name to a constant.
def name2const(name)
name.to_s.capitalize.sub(/_(.)/) { |i| $1.upcase }
end
# Register a class, probably autoloaded.
def register_terminus_class(klass)
setup_instance_loading klass.indirection_name
instance_hash(klass.indirection_name)[klass.name] = klass
end
# Return a terminus by name, using the autoloader.
def terminus_class(indirection_name, terminus_type)
setup_instance_loading indirection_name
loaded_instance(indirection_name, terminus_type)
end
# Return all terminus classes for a given indirection.
def terminus_classes(indirection_name)
setup_instance_loading indirection_name
instance_loader(indirection_name).files_to_load(Puppet.lookup(:current_environment)).map do |file|
File.basename(file).chomp(".rb").intern
end
end
private
def setup_instance_loading(type)
instance_load type, "puppet/indirector/#{type}" unless instance_loading?(type)
end
end
def indirection
self.class.indirection
end
def initialize
raise Puppet::DevError, _("Cannot create instances of abstract terminus types") if self.class.abstract_terminus?
end
def model
self.class.model
end
def name
self.class.name
end
def require_environment?
true
end
def allow_remote_requests?
true
end
def terminus_type
self.class.terminus_type
end
def validate(request)
if request.instance
validate_model(request)
validate_key(request)
end
end
def validate_key(request)
unless request.key == request.instance.name
raise Puppet::Indirector::ValidationError, _("Instance name %{name} does not match requested key %{key}") % { name: request.instance.name.inspect, key: request.key.inspect }
end
end
def validate_model(request)
unless model === request.instance
raise Puppet::Indirector::ValidationError, _("Invalid instance type %{klass}, expected %{model_type}") % { klass: request.instance.class.inspect, model_type: model.inspect }
end
end
end
|