The simplest resource class. Eventually it will function as the base class for all resource-like behaviour.
# File lib/puppet/resource.rb, line 30 def self.from_pson(pson) raise ArgumentError, "No resource type provided in pson data" unless type = pson['type'] raise ArgumentError, "No resource title provided in pson data" unless title = pson['title'] resource = new(type, title) if params = pson['parameters'] params.each { |param, value| resource[param] = value } end if tags = pson['tags'] tags.each { |tag| resource.tag(tag) } end ATTRIBUTES.each do |a| if value = pson[a.to_s] resource.send(a.to_s + "=", value) end end resource.exported ||= false resource end
Create our resource.
# File lib/puppet/resource.rb, line 188 def initialize(type, title = nil, attributes = {}) @parameters = {} # Set things like strictness first. attributes.each do |attr, value| next if attr == :parameters send(attr.to_s + "=", value) end @type, @title = extract_type_and_title(type, title) @type = munge_type_name(@type) if self.class? @title = :main if @title == "" @title = munge_type_name(@title) end if params = attributes[:parameters] extract_parameters(params) end tag(self.type) tag(self.title) if valid_tag?(self.title) @reference = self # for serialization compatibility with 0.25.x if strict? and ! resource_type if self.class? raise ArgumentError, "Could not find declared class #{title}" else raise ArgumentError, "Invalid resource type #{type}" end end end
# File lib/puppet/resource.rb, line 83 def self.value_to_pson_data(value) if value.is_a? Array value.map{|v| value_to_pson_data(v) } elsif value.is_a? Puppet::Resource value.to_s else value end end
# File lib/puppet/resource.rb, line 130 def ==(other) return false unless other.respond_to?(:title) and self.type == other.type and self.title == other.title return false unless to_hash == other.to_hash true end
Return a given parameter’s value. Converts all passed names to lower-case symbols.
# File lib/puppet/resource.rb, line 126 def [](param) parameters[parameter_name(param)] end
Set a given parameter. Converts all passed names to lower-case symbols.
# File lib/puppet/resource.rb, line 119 def []=(param, value) validate_parameter(param) if validate_parameters parameters[parameter_name(param)] = value end
Compatibility method.
# File lib/puppet/resource.rb, line 138 def builtin? builtin_type? end
Is this a builtin resource type?
# File lib/puppet/resource.rb, line 143 def builtin_type? resource_type.is_a?(Class) end
# File lib/puppet/resource.rb, line 179 def class? @is_class ||= @type == "Class" end
Iterate over each param/value pair, as required for Enumerable.
# File lib/puppet/resource.rb, line 148 def each parameters.each { |p,v| yield p, v } end
These two methods are extracted into a Helper module, but file load order prevents me from including them in the class, and I had weird behaviour (i.e., sometimes it didn’t work) when I directly extended each resource with the helper.
# File lib/puppet/resource.rb, line 161 def environment Puppet::Node::Environment.new(@environment) end
# File lib/puppet/resource.rb, line 165 def environment=(env) if env.is_a?(String) or env.is_a?(Symbol) @environment = env else @environment = env.name end end
# File lib/puppet/resource.rb, line 152 def include?(parameter) super || parameters.keys.include?( parameter_name(parameter) ) end
# File lib/puppet/resource.rb, line 55 def inspect "#{@type}[#{@title}]#{to_hash.inspect}" end
# File lib/puppet/resource.rb, line 258 def key_attributes resource_type.respond_to?(:key_attributes) ? resource_type.key_attributes : [:name] end
# File lib/puppet/resource.rb, line 296 def name # this is potential namespace conflict # between the notion of an "indirector name" # and a "resource name" [ type, title ].join('/') end
# File lib/puppet/resource.rb, line 380 def prune_parameters(options = {}) properties = resource_type.properties.map(&:name) dup.collect do |attribute, value| if value.to_s.empty? or Array(value).empty? delete(attribute) elsif value.to_s == "absent" and attribute.to_s != "ensure" delete(attribute) end parameters_to_include = options[:parameters_to_include] || [] delete(attribute) unless properties.include?(attribute) || parameters_to_include.include?(attribute) end self end
# File lib/puppet/resource.rb, line 223 def ref to_s end
Find our resource.
# File lib/puppet/resource.rb, line 228 def resolve catalog ? catalog.resource(to_s) : nil end
# File lib/puppet/resource.rb, line 232 def resource_type @rstype ||= case type when "Class"; known_resource_types.hostclass(title == :main ? "" : title) when "Node"; known_resource_types.node(title) else Puppet::Type.type(type) || known_resource_types.definition(type) end end
# File lib/puppet/resource.rb, line 332 def set_default_parameters(scope) return [] unless resource_type and resource_type.respond_to?(:arguments) unless is_a?(Puppet::Parser::Resource) fail Puppet::DevError, "Cannot evaluate default parameters for #{self} - not a parser resource" end missing_arguments.collect do |param, default| external_value = lookup_external_default_for(param, scope) if external_value.nil? && default.nil? next elsif external_value.nil? value = default.safeevaluate(scope) else value = external_value end self[param.to_sym] = value param end.compact end
# File lib/puppet/resource.rb, line 183 def stage? @is_stage ||= @type.to_s.downcase == "stage" end
Produce a simple hash of our parameters.
# File lib/puppet/resource.rb, line 242 def to_hash parse_title.merge parameters end
Convert our resource to Puppet code.
# File lib/puppet/resource.rb, line 263 def to_manifest # Collect list of attributes to align => and move ensure first attr = parameters.keys attr_max = attr.inject(0) { |max,k| k.to_s.length > max ? k.to_s.length : max } attr.sort! if attr.first != :ensure && attr.include?(:ensure) attr.delete(:ensure) attr.unshift(:ensure) end attributes = attr.collect { |k| v = parameters[k] " %-#{attr_max}s => %s,\n" % [k, Puppet::Parameter.format_value_for_display(v)] }.join "%s { '%s':\n%s}" % [self.type.to_s.downcase, self.title, attributes] end
# File lib/puppet/resource.rb, line 105 def to_pson(*args) to_pson_data_hash.to_pson(*args) end
# File lib/puppet/resource.rb, line 59 def to_pson_data_hash data = ([:type, :title, :tags] + ATTRIBUTES).inject({}) do |hash, param| next hash unless value = self.send(param) hash[param.to_s] = value hash end data["exported"] ||= false params = self.to_hash.inject({}) do |hash, ary| param, value = ary # Don't duplicate the title as the namevar next hash if param == namevar and value == title hash[param] = Puppet::Resource.value_to_pson_data(value) hash end data["parameters"] = params unless params.empty? data end
Convert our resource to a RAL resource instance. Creates component instances for resource types that don’t exist.
# File lib/puppet/resource.rb, line 288 def to_ral if typeklass = Puppet::Type.type(self.type) return typeklass.new(self) else return Puppet::Type::Component.new(self) end end
# File lib/puppet/resource.rb, line 282 def to_ref ref end
# File lib/puppet/resource.rb, line 355 def to_resource self end
# File lib/puppet/resource.rb, line 246 def to_s "#{type}[#{title}]" end
# File lib/puppet/resource.rb, line 250 def uniqueness_key # Temporary kludge to deal with inconsistant use patters h = self.to_hash h[namevar] ||= h[:name] h[:name] ||= h[namevar] h.values_at(*key_attributes.sort_by { |k| k.to_s }) end
# File lib/puppet/resource.rb, line 359 def valid_parameter?(name) resource_type.valid_parameter?(name) end
Verify that all required arguments are either present or have been provided with defaults. Must be called after ‘set_default_parameters’. We can’t join the methods because Type#set_parameters needs specifically ordered behavior.
# File lib/puppet/resource.rb, line 367 def validate_complete return unless resource_type and resource_type.respond_to?(:arguments) resource_type.arguments.each do |param, default| param = param.to_sym fail Puppet::ParseError, "Must pass #{param} to #{self}" unless parameters.include?(param) end end
# File lib/puppet/resource.rb, line 376 def validate_parameter(name) raise ArgumentError, "Invalid parameter #{name}" unless valid_parameter?(name) end
# File lib/puppet/resource.rb, line 93 def yaml_property_munge(x) case x when Hash x.inject({}) { |h,kv| k,v = kv h[k] = self.class.value_to_pson_data(v) h } else self.class.value_to_pson_data(x) end end
# File lib/puppet/resource.rb, line 417 def extract_parameters(params) params.each do |param, value| validate_parameter(param) if strict? self[param] = value end end
# File lib/puppet/resource.rb, line 424 def extract_type_and_title(argtype, argtitle) if (argtitle || argtype) =~ /^([^\[\]]+)\[(.+)\]$/ then [ $1, $2 ] elsif argtitle then [ argtype, argtitle ] elsif argtype.is_a?(Puppet::Type) then [ argtype.class.name, argtype.title ] elsif argtype.is_a?(Hash) then raise ArgumentError, "Puppet::Resource.new does not take a hash as the first argument. "+ "Did you mean (#{(argtype[:type] || argtype["type"]).inspect}, #{(argtype[:title] || argtype["title"]).inspect }) ?" else raise ArgumentError, "No title provided and #{argtype.inspect} is not a valid resource reference" end end
Consult external data bindings for class parameter values which must be namespaced in the backend.
Example:
class foo($port){ ... }
We make a request to the backend for the key ‘foo::port’ not ‘foo’
# File lib/puppet/resource.rb, line 320 def lookup_external_default_for(param, scope) if resource_type.type == :hostclass Puppet::DataBinding.indirection.find( "#{resource_type.name}::#{param}", :environment => scope.environment.to_s, :variables => Puppet::DataBinding::Variables.new(scope)) else nil end end
# File lib/puppet/resource.rb, line 303 def missing_arguments resource_type.arguments.select do |param, default| param = param.to_sym parameters[param].nil? || parameters[param].value == :undef end end
# File lib/puppet/resource.rb, line 435 def munge_type_name(value) return :main if value == :main return "Class" if value == "" or value.nil? or value.to_s.downcase == "component" value.to_s.split("::").collect { |s| s.capitalize }.join("::") end
The namevar for our resource type. If the type doesn’t exist, always use :name.
# File lib/puppet/resource.rb, line 409 def namevar if builtin_type? and t = resource_type and t.key_attributes.length == 1 t.key_attributes.first else :name end end
Produce a canonical method name.
# File lib/puppet/resource.rb, line 399 def parameter_name(param) param = param.to_s.downcase.to_sym if param == :name and n = namevar param = namevar end param end
# File lib/puppet/resource.rb, line 476 def parameters # @parameters could have been loaded from YAML, causing it to be nil (by # bypassing initialize). @parameters ||= {} end
# File lib/puppet/resource.rb, line 442 def parse_title h = {} type = resource_type if type.respond_to? :title_patterns type.title_patterns.each { |regexp, symbols_and_lambdas| if captures = regexp.match(title.to_s) symbols_and_lambdas.zip(captures[1..-1]).each do |symbol_and_lambda,capture| symbol, proc = symbol_and_lambda # Many types pass "identity" as the proc; we might as well give # them a shortcut to delivering that without the extra cost. # # Especially because the global type defines title_patterns and # uses the identity patterns. # # This was worth about 8MB of memory allocation saved in my # testing, so is worth the complexity for the API. if proc then h[symbol] = proc.call(capture) else h[symbol] = capture end end return h end } # If we've gotten this far, then none of the provided title patterns # matched. Since there's no way to determine the title then the # resource should fail here. raise Puppet::Error, "No set of title patterns matched the title \"#{title}\"." else return { :name => title.to_s } end end