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
|
require 'puppet/util/cacher'
require 'monitor'
# Just define it, so this class has fewer load dependencies.
class Puppet::Node
end
# Model the environment that a node can operate in. This class just
# provides a simple wrapper for the functionality around environments.
class Puppet::Node::Environment
module Helper
def environment
Puppet::Node::Environment.new(@environment)
end
def environment=(env)
if env.is_a?(String) or env.is_a?(Symbol)
@environment = env
else
@environment = env.name
end
end
end
include Puppet::Util::Cacher
@seen = {}
# Return an existing environment instance, or create a new one.
def self.new(name = nil)
return name if name.is_a?(self)
name ||= Puppet.settings.value(:environment)
raise ArgumentError, "Environment name must be specified" unless name
symbol = name.to_sym
return @seen[symbol] if @seen[symbol]
obj = self.allocate
obj.send :initialize, symbol
@seen[symbol] = obj
end
def self.current
Thread.current[:environment] || root
end
def self.current=(env)
Thread.current[:environment] = new(env)
end
def self.root
@root
end
# This is only used for testing.
def self.clear
@seen.clear
end
attr_reader :name
# Return an environment-specific setting.
def [](param)
Puppet.settings.value(param, self.name)
end
def initialize(name)
@name = name
extend MonitorMixin
end
def known_resource_types
# This makes use of short circuit evaluation to get the right thread-safe
# per environment semantics with an efficient most common cases; we almost
# always just return our thread's known-resource types. Only at the start
# of a compilation (after our thread var has been set to nil) or when the
# environment has changed do we delve deeper.
Thread.current[:known_resource_types] = nil if (krt = Thread.current[:known_resource_types]) && krt.environment != self
Thread.current[:known_resource_types] ||= synchronize {
if @known_resource_types.nil? or @known_resource_types.require_reparse?
@known_resource_types = Puppet::Resource::TypeCollection.new(self)
@known_resource_types.import_ast(perform_initial_import, '')
end
@known_resource_types
}
end
def module(name)
mod = Puppet::Module.new(name, self)
return nil unless mod.exist?
mod
end
# Cache the modulepath, so that we aren't searching through
# all known directories all the time.
cached_attr(:modulepath, Puppet[:filetimeout]) do
dirs = self[:modulepath].split(File::PATH_SEPARATOR)
dirs = ENV["PUPPETLIB"].split(File::PATH_SEPARATOR) + dirs if ENV["PUPPETLIB"]
validate_dirs(dirs)
end
# Return all modules from this environment.
# Cache the list, because it can be expensive to create.
cached_attr(:modules, Puppet[:filetimeout]) do
module_names = modulepath.collect { |path| Dir.entries(path) }.flatten.uniq
module_names.collect do |path|
begin
Puppet::Module.new(path, self)
rescue Puppet::Module::Error => e
nil
end
end.compact
end
def to_s
name.to_s
end
def to_sym
to_s.to_sym
end
# The only thing we care about when serializing an environment is its
# identity; everything else is ephemeral and should not be stored or
# transmitted.
def to_zaml(z)
self.to_s.to_zaml(z)
end
def validate_dirs(dirs)
dir_regex = Puppet.features.microsoft_windows? ? /^[A-Za-z]:#{File::SEPARATOR}/ : /^#{File::SEPARATOR}/
dirs.collect do |dir|
if dir !~ dir_regex
File.join(Dir.getwd, dir)
else
dir
end
end.find_all do |p|
p =~ dir_regex && FileTest.directory?(p)
end
end
private
def perform_initial_import
return empty_parse_result if Puppet.settings[:ignoreimport]
parser = Puppet::Parser::Parser.new(self)
if code = Puppet.settings.uninterpolated_value(:code, name.to_s) and code != ""
parser.string = code
else
file = Puppet.settings.value(:manifest, name.to_s)
parser.file = file
end
parser.parse
rescue => detail
known_resource_types.parse_failed = true
msg = "Could not parse for environment #{self}: #{detail}"
error = Puppet::Error.new(msg)
error.set_backtrace(detail.backtrace)
raise error
end
def empty_parse_result
# Return an empty toplevel hostclass to use as the result of
# perform_initial_import when no file was actually loaded.
return Puppet::Parser::AST::Hostclass.new('')
end
@root = new(:'*root*')
end
|