/lib/puppet/indirector/terminus.rb

https://gitlab.com/gutocarvalho/puppet · Ruby · 169 lines · 113 code · 33 blank · 23 comment · 9 complexity · 27a1cce0fa2e99fced0430bd53bc3698 MD5 · raw file

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