lib/puppet/resource_api.rb in puppet-resource_api-1.6.5 vs lib/puppet/resource_api.rb in puppet-resource_api-1.7.0
- old
+ new
@@ -17,58 +17,25 @@
class << self
attr_accessor :warning_count
end
def register_type(definition)
- raise Puppet::DevError, 'requires a hash as definition, not `%{other_type}`' % { other_type: definition.class } unless definition.is_a? Hash
- raise Puppet::DevError, 'requires a `:name`' unless definition.key? :name
- raise Puppet::DevError, 'requires `:attributes`' unless definition.key? :attributes
- raise Puppet::DevError, '`:attributes` must be a hash, not `%{other_type}`' % { other_type: definition[:attributes].class } unless definition[:attributes].is_a?(Hash)
- [:title, :provider, :alias, :audit, :before, :consume, :export, :loglevel, :noop, :notify, :require, :schedule, :stage, :subscribe, :tag].each do |name|
- raise Puppet::DevError, 'must not define an attribute called `%{name}`' % { name: name.inspect } if definition[:attributes].key? name
- end
- if definition.key?(:title_patterns) && !definition[:title_patterns].is_a?(Array)
- raise Puppet::DevError, '`:title_patterns` must be an array, not `%{other_type}`' % { other_type: definition[:title_patterns].class }
- end
+ # Attempt to create a TypeDefinition from the input hash
+ # This will validate and throw if its not right
+ type_def = TypeDefinition.new(definition)
- Puppet::ResourceApi::DataTypeHandling.validate_ensure(definition)
-
- definition[:features] ||= []
- supported_features = %w[supports_noop canonicalize remote_resource simple_get_filter].freeze
- unknown_features = definition[:features] - supported_features
- Puppet.warning("Unknown feature detected: #{unknown_features.inspect}") unless unknown_features.empty?
-
- # fixup desc/docs backwards compatibility
- if definition.key? :docs
- if definition[:desc]
- raise Puppet::DevError, '`%{name}` has both `desc` and `docs`, prefer using `desc`' % { name: definition[:name] }
- end
- definition[:desc] = definition[:docs]
- definition.delete(:docs)
- end
- Puppet.warning('`%{name}` has no documentation, add it using a `desc` key' % { name: definition[:name] }) unless definition.key? :desc
-
- # fixup any weird behavior ;-)
- definition[:attributes].each do |name, attr|
- next unless attr[:behavior]
- if attr[:behaviour]
- raise Puppet::DevError, "the '#{name}' attribute has both a `behavior` and a `behaviour`, only use one"
- end
- attr[:behaviour] = attr[:behavior]
- attr.delete(:behavior)
- end
-
# prepare the ruby module for the provider
# this has to happen before Puppet::Type.newtype starts autoloading providers
# it also needs to be guarded against the namespace already being defined by something
# else to avoid ruby warnings
unless Puppet::Provider.const_defined?(class_name_from_type_name(definition[:name]), false)
Puppet::Provider.const_set(class_name_from_type_name(definition[:name]), Module.new)
end
Puppet::Type.newtype(definition[:name].to_sym) do
- @docs = definition[:desc]
+ @docs = definition[:docs]
+ @type_definition = type_def
# Keeps a copy of the provider around. Weird naming to avoid clashes with puppet's own `provider` member
define_singleton_method(:my_provider) do
@my_provider ||= Hash.new { |hash, key| hash[key] = Puppet::ResourceApi.load_provider(definition[:name]).new }
@my_provider[Puppet::Util::NetworkDevice.current.class]
@@ -78,22 +45,22 @@
def my_provider
self.class.my_provider
end
define_singleton_method(:type_definition) do
- @type_definition ||= TypeDefinition.new(definition)
+ @type_definition
end
def type_definition
self.class.type_definition
end
if type_definition.feature?('remote_resource')
apply_to_device
end
- def initialize(attributes)
+ define_method(:initialize) do |attributes|
# $stderr.puts "A: #{attributes.inspect}"
if attributes.is_a? Puppet::Resource
@title = attributes.title
@catalog = attributes.catalog
sensitives = attributes.sensitive_parameters
@@ -117,11 +84,11 @@
end
# the `Puppet::Resource::Ral.find` method, when `instances` does not return a match, uses a Hash with a `:name` key to create
# an "absent" resource. This is often hit by `puppet resource`. This needs to work, even if the namevar is not called `name`.
# This bit here relies on the default `title_patterns` (see below) to match the title back to the first (and often only) namevar
- if type_definition.attributes[:name].nil? && attributes[:title].nil?
+ if definition[:attributes][:name].nil? && attributes[:title].nil?
attributes[:title] = attributes.delete(:name)
if attributes[:title].nil? && !type_definition.namevars.empty?
attributes[:title] = @title
end
end
@@ -131,29 +98,15 @@
def name
title
end
- def self.build_title(type_definition, resource_hash)
- if type_definition.namevars.size > 1
- # use a MonkeyHash to allow searching in Puppet's RAL
- Puppet::ResourceApi::MonkeyHash[type_definition.namevars.map { |attr| [attr, resource_hash[attr]] }]
- else
- resource_hash[type_definition.namevars[0]]
- end
- end
-
- def rsapi_title
- @rsapi_title ||= self.class.build_title(type_definition, self)
- @rsapi_title
- end
-
def to_resource
to_resource_shim(super)
end
- def to_resource_shim(resource)
+ define_method(:to_resource_shim) do |resource|
resource_hash = Hash[resource.keys.map { |k| [k, resource[k]] }]
resource_hash[:title] = resource.title
ResourceShim.new(resource_hash, type_definition.name, type_definition.namevars, type_definition.attributes, catalog)
end
@@ -217,18 +170,12 @@
# customizations required by the Resource API applied. Under the hood,
# this maps to the relevant DSL methods in Puppet::Type. See
# https://puppet.com/docs/puppet/6.0/custom_types.html#reference-5883
# for details.
send(param_or_property, name.to_sym, parent: parent) do
- unless options[:type]
- raise Puppet::DevError, "#{definition[:name]}.#{name} has no type"
- end
-
if options[:desc]
desc "#{options[:desc]} (a #{options[:type]})"
- else
- warn("#{definition[:name]}.#{name} has no docs")
end
# The initialize method is called when puppet core starts building up
# type objects. The core passes in a hash of shape { resource:
# #<Puppet::Type::TypeName> }. We use this to pass through the
@@ -256,11 +203,11 @@
options,
)
end
end
- def self.instances
+ define_singleton_method(:instances) do
# puts 'instances'
# force autoloading of the provider
provider(type_definition.name)
initial_fetch = if type_definition.feature?('simple_get_filter')
@@ -273,44 +220,40 @@
type_definition.check_schema(resource_hash)
# allow a :title from the provider to override the default
result = if resource_hash.key? :title
new(title: resource_hash[:title])
else
- new(title: build_title(type_definition, resource_hash))
+ new(title: resource_hash[type_definition.namevars.first])
end
result.cache_current_state(resource_hash)
result
end
end
- def refresh_current_state
+ define_method(:refresh_current_state) do
@rsapi_current_state = if type_definition.feature?('simple_get_filter')
- my_provider.get(context, [rsapi_title]).find { |h| namevar_match?(h) }
+ my_provider.get(context, [title]).find { |h| namevar_match?(h) }
else
my_provider.get(context).find { |h| namevar_match?(h) }
end
if @rsapi_current_state
type_definition.check_schema(@rsapi_current_state)
strict_check(@rsapi_current_state) if type_definition.feature?('canonicalize')
else
- @rsapi_current_state = if rsapi_title.is_a? Hash
- rsapi_title.dup
- else
- { title: rsapi_title }
- end
+ @rsapi_current_state = { title: title }
@rsapi_current_state[:ensure] = :absent if type_definition.ensurable?
end
end
# Use this to set the current state from the `instances` method
def cache_current_state(resource_hash)
@rsapi_current_state = resource_hash
strict_check(@rsapi_current_state) if type_definition.feature?('canonicalize')
end
- def retrieve
+ define_method(:retrieve) do
refresh_current_state unless @rsapi_current_state
Puppet.debug("Current State: #{@rsapi_current_state.inspect}")
result = Puppet::Resource.new(self.class, title, parameters: @rsapi_current_state)
@@ -320,17 +263,17 @@
raise_missing_attrs
result
end
- def namevar_match?(item)
+ define_method(:namevar_match?) do |item|
context.type.namevars.all? do |namevar|
item[namevar] == @parameters[namevar].value if @parameters[namevar].respond_to? :value
end
end
- def flush
+ define_method(:flush) do
raise_missing_attrs
# puts 'flush'
# skip puppet's injected metaparams
actual_params = @parameters.select { |k, _v| type_definition.attributes.key? k }
@@ -344,11 +287,11 @@
Puppet.debug("Target State: #{target_state.inspect}")
# enforce init_only attributes
if Puppet.settings[:strict] != :off && @rsapi_current_state && (@rsapi_current_state[:ensure] == 'present' && target_state[:ensure] == 'present')
target_state.each do |name, value|
- next unless type_definition.attributes[name][:behaviour] == :init_only && value != @rsapi_current_state[name]
+ next unless definition[:attributes][name][:behaviour] == :init_only && value != @rsapi_current_state[name]
message = "Attempting to change `#{name}` init_only attribute value from `#{@rsapi_current_state[name]}` to `#{value}`"
case Puppet.settings[:strict]
when :warning
Puppet.warning(message)
when :error
@@ -356,31 +299,31 @@
end
end
end
if type_definition.feature?('supports_noop')
- my_provider.set(context, { rsapi_title => { is: @rsapi_current_state, should: target_state } }, noop: noop?)
+ my_provider.set(context, { title => { is: @rsapi_current_state, should: target_state } }, noop: noop?)
else
- my_provider.set(context, rsapi_title => { is: @rsapi_current_state, should: target_state }) unless noop?
+ my_provider.set(context, title => { is: @rsapi_current_state, should: target_state }) unless noop?
end
raise 'Execution encountered an error' if context.failed?
# remember that we have successfully reached our desired state
@rsapi_current_state = target_state
end
- def raise_missing_attrs
+ define_method(:raise_missing_attrs) do
error_msg = "The following mandatory attributes were not provided:\n * " + @missing_attrs.join(", \n * ")
raise Puppet::ResourceError, error_msg if @missing_attrs.any? && (value(:ensure) != :absent && !value(:ensure).nil?)
end
- def raise_missing_params
+ define_method(:raise_missing_params) do
error_msg = "The following mandatory parameters were not provided:\n * " + @missing_params.join(", \n * ")
raise Puppet::ResourceError, error_msg
end
- def strict_check(current_state)
+ define_method(:strict_check) do |current_state|
return if Puppet.settings[:strict] == :off
# if strict checking is on we must notify if the values are changed by canonicalize
# make a deep copy to perform the operation on and to compare against later
state_clone = Marshal.load(Marshal.dump(current_state))
@@ -390,11 +333,11 @@
return unless state_clone && (current_state != state_clone)
#:nocov:
# codecov fails to register this multiline as covered, even though simplecov does.
message = <<MESSAGE.strip
-#{type_definition.name}[#{@title}]#get has not provided canonicalized values.
+#{definition[:name]}[#{@title}]#get has not provided canonicalized values.
Returned values: #{current_state.inspect}
Canonicalized values: #{state_clone.inspect}
MESSAGE
#:nocov:
@@ -403,23 +346,23 @@
Puppet.warning(message)
when :error
raise Puppet::DevError, message
end
- nil
+ return nil
end
define_singleton_method(:context) do
@context ||= PuppetContext.new(definition)
end
def context
self.class.context
end
- def self.title_patterns
- @title_patterns ||= if type_definition.definition.key? :title_patterns
- parse_title_patterns(type_definition.definition[:title_patterns])
+ define_singleton_method(:title_patterns) do
+ @title_patterns ||= if definition.key? :title_patterns
+ parse_title_patterns(definition[:title_patterns])
else
[[%r{(.*)}m, [[type_definition.namevars.first]]]]
end
end