Skip to content

Commit 01db61e

Browse files
committed
(maint) Refactor call paths for loading resource types
This commit: * Adds Puppet::Pops::Evaluator::Runtime3ResourceSupport - a module with helper methods for loading / finding resource types, resources and hostclasses. * Changes calls to various Scope methods to use the new resoure support. * Refactors calls to known_resource_types to always go to environment instead of via Scope (or elsewhere). * Adds Puppet::Pops::Resource namespace with WIP for resource_impl, and param (to be loaded from Pcore), and the type implementation Puppet::Pops::Resource::ResourceTypeImpl. * Adds module Puppet::CompilableResourceType as a module common to both implementations of resource type (Puppet::Type, and Puppet::Pops::Resource::ResourceTypeImpl). * A WIP Mocking Type (to be removed when real implementation is ready) * The `create_resources` function is modified as it used AST::Resource to do its work. * The `defined` function made calls to Scope methods that are now moved to Runtime3ResourceSupport. * Runtime3Support is modified to use Runtime3ResourceSupport instead of directly containing the methods for resource search and creation. * Runtime3Support now adds to the puppet call stack when calling a function from puppet logic. * Puppet::Resource is modified to use CompilableResource and adjusted to work with the API Puppet::Pops::Resource::ResourceTypeImpl. * Puppet::Type is modified to compare against CompilableResourceType instead of just Puppet::Type (this is important for ordering) * The `create_resources` function and spec is modified as error messages were improved, and because of the inclusion of a puppet call stack, the created resources now gets file/line information.
1 parent 2211c64 commit 01db61e

File tree

18 files changed

+558
-111
lines changed

18 files changed

+558
-111
lines changed

lib/puppet.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ def self.rollback_context(name)
273273
# effort, but I think we should strive for it and revisit this at some point. --cprice 2012-03-16
274274

275275
require 'puppet/indirector'
276+
require 'puppet/compilable_resource_type'
276277
require 'puppet/type'
277278
require 'puppet/resource'
278279
require 'puppet/parser'
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
require 'puppet'
2+
# The CompilableResourceType module should be either included in a class or used as a class extension
3+
# to mark that the instance used as the 'resource type' of a resource instance
4+
# is an object that is compatible with Puppet::Type's API wrt. compiling.
5+
# Puppet Resource Types written in Ruby use a meta programmed Ruby Class as the type. Those classes
6+
# are subtypes of Puppet::Type. Meta data (Pcore/puppet language) based resource types uses instances of
7+
# a class instead.
8+
#
9+
module Puppet::CompilableResourceType
10+
# All 3.x resource types implemented in Ruby using Puppet::Type respond true.
11+
# Other kinds of implementations should reimplement and return false.
12+
def is_3x_ruby_plugin?
13+
true
14+
end
15+
end

lib/puppet/functions/defined.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
end
106106

107107
def is_defined(scope, *vals)
108+
env = scope.environment
108109
vals.any? do |val|
109110
case val
110111
when String
@@ -116,11 +117,11 @@ def is_defined(scope, *vals)
116117
next nil
117118
when 'main'
118119
# Find the main class (known as ''), it does not have to be in the catalog
119-
scope.environment.known_resource_types.find_hostclass('')
120+
Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_main_class(env)
120121
else
121122
# Find a resource type, definition or class definition
122123
krt = scope.environment.known_resource_types
123-
scope.find_resource_type(val) || krt.find_definition(val) || krt.find_hostclass(val)
124+
Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_resource_type_or_class(env, val)
124125
end
125126
end
126127
when Puppet::Resource
@@ -130,7 +131,7 @@ def is_defined(scope, *vals)
130131
when Puppet::Pops::Types::PResourceType
131132
raise ArgumentError, 'The given resource type is a reference to all kind of types' if val.type_name.nil?
132133
if val.title.nil?
133-
scope.find_builtin_resource_type(val.type_name) || scope.environment.known_resource_types.find_definition(val.type_name)
134+
Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_resource_type(env, val.type_name)
134135
else
135136
scope.compiler.findresource(val.type_name, val.title)
136137
end
@@ -151,7 +152,8 @@ def is_defined(scope, *vals)
151152
# (this is the same as asking for just the class' name, but with the added certainty that it cannot be a defined type.
152153
#
153154
raise ArgumentError, 'The given class type is a reference to all classes' if val.type.class_name.nil?
154-
scope.environment.known_resource_types.find_hostclass(val.type.class_name)
155+
Puppet::Pops::Evaluator::Runtime3ResourceSupport.find_hostclass(env, val.type.class_name)
156+
#scope.environment.known_resource_types.find_hostclass(val.type.class_name)
155157
end
156158
else
157159
raise ArgumentError, "Invalid argument of type '#{val.class}' to 'defined'"

lib/puppet/metatype/manager.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,13 @@ def type(name)
183183
# Defaults to an instance of {Puppet::Util::Autoload} if no other auto loader has been set.
184184
# @return [Puppet::Util::Autoload] the loader to use.
185185
# @api private
186-
def typeloader
187-
unless defined?(@typeloader)
188-
@typeloader = Puppet::Util::Autoload.new(self, "puppet/type")
189-
end
190-
191-
@typeloader
186+
def typeloader
187+
unless defined?(@typeloader)
188+
@typeloader = Puppet::Util::Autoload.new(self, "puppet/type")
192189
end
190+
191+
@typeloader
192+
end
193193
end
194194
end
195195

lib/puppet/parser/functions/create_resources.rb

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -53,35 +53,46 @@
5353
raise ArgumentError, ('create_resources(): third argument, if provided, must be a hash') unless args[2].is_a?(Hash)
5454
end
5555

56-
5756
type, instances, defaults = args
5857
defaults ||= {}
59-
60-
resource = Puppet::Parser::AST::Resource.new(:type => type.sub(/^@{1,2}/, '').downcase, :instances =>
61-
instances.collect do |title, params|
62-
Puppet::Parser::AST::ResourceInstance.new(
63-
:title => Puppet::Parser::AST::Leaf.new(:value => title),
64-
:parameters => defaults.merge(params).collect do |name, value|
65-
next if (value == :undef || value.nil?)
66-
Puppet::Parser::AST::ResourceParam.new(
67-
:param => name,
68-
:value => Puppet::Parser::AST::Leaf.new(:value => value))
69-
end.compact)
70-
end)
58+
type_name = type.sub(/^@{1,2}/, '').downcase
59+
60+
# Get file/line information from the puppet stack (where call comes from in puppet source)
61+
# If relayed via other puppet functions in ruby that do not nest their calls, the source position
62+
# will be in the original puppet source.
63+
#
64+
stacktrace = Puppet::Pops::PuppetStack.stacktrace()
65+
if stacktrace.size > 0
66+
file, line = stacktrace[0]
67+
else
68+
file = nil
69+
line = nil
70+
end
7171

7272
if type.start_with? '@@'
73-
resource.exported = true
73+
exported = true
7474
elsif type.start_with? '@'
75-
resource.virtual = true
75+
virtual = true
7676
end
7777

78-
begin
79-
resource.safeevaluate(self)
80-
rescue Puppet::ParseError => internal_error
81-
if internal_error.original.nil?
82-
raise internal_error
83-
else
84-
raise internal_error.original
85-
end
86-
end
78+
instances.map do |title, params|
79+
# Add support for iteration if title is an array
80+
resource_titles = title.is_a?(Array) ? title : [title]
81+
Puppet::Pops::Evaluator::Runtime3ResourceSupport.create_resources(
82+
file, line,
83+
self,
84+
virtual, exported,
85+
type_name,
86+
resource_titles,
87+
defaults.merge(params).map do |name, value|
88+
next if (value == :undef || value.nil?)
89+
Puppet::Parser::Resource::Param.new(
90+
:name => name,
91+
:value => value, # wide open to various data types, must be correct
92+
:source => self.source, # TODO: support :line => line, :file => file,
93+
:add => false
94+
)
95+
end.compact
96+
)
97+
end.flatten.compact
8798
end

lib/puppet/parser/resource.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def evaluate
7575
return if evaluated?
7676
Puppet::Util::Profiler.profile("Evaluated resource #{self}", [:compiler, :evaluate_resource, self]) do
7777
@evaluated = true
78-
if builtin?
78+
if builtin_type?
7979
devfail "Cannot evaluate a builtin type (#{type})"
8080
elsif resource_type.nil?
8181
self.fail "Cannot find definition #{type}"
@@ -256,7 +256,7 @@ def add_parameters_from_consume
256256
scope.with_global_scope do |global_scope|
257257
cns_scope = global_scope.newscope(:source => self, :resource => self)
258258
cns.to_hash.each { |name, value| cns_scope[name.to_s] = value }
259-
259+
260260
# evaluate mappings in that scope
261261
resource_type.arguments.keys.each do |name|
262262
if expr = blueprint[:mappings][name]

lib/puppet/parser/scope.rb

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -976,18 +976,19 @@ def ephemeral_from(match, file = nil, line = nil)
976976
end
977977
end
978978

979+
# @api private
979980
def find_resource_type(type)
980-
# It still works fine without the type == 'class' short-cut, but it is a lot slower.
981-
return nil if ["class", "node"].include? type.to_s.downcase
982-
find_builtin_resource_type(type) || find_defined_resource_type(type)
981+
raise Puppet::DevError, "Scope#find_resource_type() is no longer supported, use Puppet::Pops::Evaluator::Runtime3ResourceSupport instead"
983982
end
984983

984+
# @api private
985985
def find_builtin_resource_type(type)
986-
Puppet::Type.type(type.to_s.downcase.to_sym)
986+
raise Puppet::DevError, "Scope#find_builtin_resource_type() is no longer supported, use Puppet::Pops::Evaluator::Runtime3ResourceSupport instead"
987987
end
988988

989+
# @api private
989990
def find_defined_resource_type(type)
990-
environment.known_resource_types.find_definition(type.to_s.downcase)
991+
raise Puppet::DevError, "Scope#find_defined_resource_type() is no longer supported, use Puppet::Pops::Evaluator::Runtime3ResourceSupport instead"
991992
end
992993

993994

@@ -1005,25 +1006,33 @@ def method_missing(method, *args, &block)
10051006
end
10061007
end
10071008

1009+
# Called from two places:
1010+
# runtime3support when creating resources
1011+
# ast::resource - used by create resources ?
1012+
# @api private
10081013
def resolve_type_and_titles(type, titles)
1009-
raise ArgumentError, "titles must be an array" unless titles.is_a?(Array)
1010-
1011-
case type.downcase
1012-
when "class"
1013-
# resolve the titles
1014-
titles = titles.collect do |a_title|
1015-
hostclass = find_hostclass(a_title)
1016-
hostclass ? hostclass.name : a_title
1017-
end
1018-
when "node"
1019-
# no-op
1020-
else
1021-
# resolve the type
1022-
resource_type = find_resource_type(type)
1023-
type = resource_type.name if resource_type
1024-
end
1014+
raise Puppet::DevError, "Scope#resolve_type_and_title() is no longer supported, use Puppet::Pops::Evaluator::Runtime3ResourceSupport instead"
10251015

1026-
return [type, titles]
1016+
# Puppet.deprecation_warning('Scope#resolve_type_and_titles is deprecated.')
1017+
#
1018+
# raise ArgumentError, "titles must be an array" unless titles.is_a?(Array)
1019+
#
1020+
# case type.downcase
1021+
# when "class"
1022+
# # resolve the titles
1023+
# titles = titles.collect do |a_title|
1024+
# hostclass = find_hostclass(a_title)
1025+
# hostclass ? hostclass.name : a_title
1026+
# end
1027+
# when "node"
1028+
# # no-op
1029+
# else
1030+
# # resolve the type
1031+
# resource_type = find_resource_type(type)
1032+
# type = resource_type.name if resource_type
1033+
# end
1034+
#
1035+
# return [type, titles]
10271036
end
10281037

10291038
# Transforms references to classes to the form suitable for

lib/puppet/pops.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,14 @@ module Config
8282
end
8383
end
8484

85+
module Resource
86+
require 'puppet/pops/resource/resource_type_impl'
87+
end
8588
module Evaluator
8689
require 'puppet/pops/evaluator/literal_evaluator'
8790
require 'puppet/pops/evaluator/callable_signature'
8891
require 'puppet/pops/evaluator/runtime3_converter'
92+
require 'puppet/pops/evaluator/runtime3_resource_support'
8993
require 'puppet/pops/evaluator/runtime3_support'
9094
require 'puppet/pops/evaluator/evaluator_impl'
9195
require 'puppet/pops/evaluator/epp_evaluator'

lib/puppet/pops/evaluator/collector_transformer.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ def transform(o, scope)
1919
fail "Classes cannot be collected"
2020
end
2121

22-
resource_type = scope.find_resource_type(type)
22+
resource_type = Runtime3ResourceSupport.find_resource_type(scope.environment, type)
23+
2324
fail "Resource type #{type} doesn't exist" unless resource_type
2425

2526
adapter = Adapters::SourcePosAdapter.adapt(o)
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
module Puppet::Pops
2+
module Evaluator
3+
4+
# @api private
5+
module Runtime3ResourceSupport
6+
CLASS_STRING = 'class'.freeze
7+
8+
def self.create_resources(file, line, scope, virtual, exported, type_name, resource_titles, evaluated_parameters)
9+
10+
env = scope.environment
11+
# loader = Adapters::LoaderAdapter.loader_for_model_object(o, scope)
12+
13+
if type_name.is_a?(String) && type_name.casecmp(CLASS_STRING) == 0
14+
# Resolve a 'class' and its titles
15+
resource_titles = resource_titles.collect do |a_title|
16+
hostclass = env.known_resource_types.find_hostclass(a_title)
17+
hostclass ? hostclass.name : a_title
18+
end
19+
# resolved type is just the string CLASS
20+
resolved_type = CLASS_STRING
21+
else
22+
# resolve a resource type - pcore based, ruby impl, user defined, or application
23+
fully_qualified_type = find_resource_type(env, type_name)
24+
type = fully_qualified_type.name if fully_qualified_type
25+
resolved_type = fully_qualified_type
26+
end
27+
28+
# TODO: Unknown resource causes creation of Resource to fail with ArgumentError, should give
29+
# a proper Issue. Now the result is "Error while evaluating a Resource Statement" with the message
30+
# from the raised exception. (It may be good enough).
31+
unless resolved_type
32+
# TODO: do this the right way
33+
raise ArgumentError, "Unknown resource type: '#{type_name}'"
34+
end
35+
36+
# Build a resource for each title - use the resolved *type* as opposed to a reference
37+
# as this makes the created resource retain the type instance.
38+
#
39+
resource_titles.map do |resource_title|
40+
resource = Puppet::Parser::Resource.new(
41+
resolved_type, resource_title,
42+
:parameters => evaluated_parameters,
43+
:file => file,
44+
:line => line,
45+
:exported => exported,
46+
:virtual => virtual,
47+
# WTF is this? Which source is this? The file? The name of the context ?
48+
:source => scope.source,
49+
:scope => scope,
50+
:strict => true
51+
)
52+
53+
# If this resource type supports inheritance (e.g. 'class') the parent chain must be walked
54+
# This impl delegates to the resource type to figure out what is needed.
55+
#
56+
if resource.resource_type.is_a? Puppet::Resource::Type
57+
resource.resource_type.instantiate_resource(scope, resource)
58+
end
59+
60+
scope.compiler.add_resource(scope, resource)
61+
62+
# Classes are evaluated immediately
63+
scope.compiler.evaluate_classes([resource_title], scope, false) if fully_qualified_type == CLASS_STRING
64+
65+
# Turn the resource into a PType (a reference to a resource type)
66+
# weed out nil's
67+
resource_to_ptype(resource)
68+
end
69+
end
70+
71+
def self.find_resource_type(env, type_name)
72+
type_name = type_name.to_s.downcase
73+
find_builtin_resource_type(env, type_name) || find_defined_resource_type(env, type_name)
74+
end
75+
76+
def self.find_resource_type_or_class(env, name)
77+
type_name = type_name.to_s.downcase
78+
find_builtin_resource_type(env, name) || find_defined_resource_type(env, name) || find_hostclass(env, name)
79+
end
80+
81+
def self.resource_to_ptype(resource)
82+
nil if resource.nil?
83+
# inference returns the meta type since the 3x Resource is an alternate way to describe a type
84+
Puppet::Pops::Types::TypeCalculator.singleton().infer(resource).type
85+
end
86+
87+
def self.find_main_class(env)
88+
# Find the main class (known as ''), it does not have to be in the catalog
89+
env.known_resource_types.find_hostclass('')
90+
end
91+
92+
def self.find_hostclass(env, class_name)
93+
env.known_resource_types.find_hostclass(class_name)
94+
end
95+
96+
private
97+
98+
def self.find_builtin_resource_type(env, type_name)
99+
# TODO: IF 4.X LOADER FINDS TYPE - USE IT
100+
# Step 1 - use cheat for notify by doing return Puppet::Pops::Resource::ResourceTypeImpl.notify_cheat()
101+
102+
# horrible
103+
Puppet::Type.type(type_name)
104+
end
105+
106+
def self.find_defined_resource_type(env, type_name)
107+
krt = env.known_resource_types
108+
krt.find_definition(type_name) || krt.application(type_name)
109+
end
110+
111+
end
112+
end
113+
end

0 commit comments

Comments
 (0)