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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
require 'puppet/util/logging'
# Support for modules
class Puppet::Module
class Error < Puppet::Error; end
class MissingModule < Error; end
class IncompatibleModule < Error; end
class UnsupportedPlatform < Error; end
class IncompatiblePlatform < Error; end
class MissingMetadata < Error; end
class InvalidName < Error; end
include Puppet::Util::Logging
TEMPLATES = "templates"
FILES = "files"
MANIFESTS = "manifests"
PLUGINS = "plugins"
FILETYPES = [MANIFESTS, FILES, TEMPLATES, PLUGINS]
# Return an array of paths by splitting the +modulepath+ config
# parameter. Only consider paths that are absolute and existing
# directories
def self.modulepath(environment = nil)
Puppet::Node::Environment.new(environment).modulepath
end
# Find and return the +module+ that +path+ belongs to. If +path+ is
# absolute, or if there is no module whose name is the first component
# of +path+, return +nil+
def self.find(modname, environment = nil)
return nil unless modname
Puppet::Node::Environment.new(environment).module(modname)
end
attr_reader :name, :environment
attr_writer :environment
attr_accessor :source, :author, :version, :license, :puppetversion, :summary, :description, :project_page
def has_metadata?
return false unless metadata_file
FileTest.exist?(metadata_file)
end
def initialize(name, environment = nil)
@name = name
assert_validity()
if environment.is_a?(Puppet::Node::Environment)
@environment = environment
else
@environment = Puppet::Node::Environment.new(environment)
end
load_metadata if has_metadata?
validate_puppet_version
validate_dependencies
end
FILETYPES.each do |type|
# A boolean method to let external callers determine if
# we have files of a given type.
define_method(type +'?') do
return false unless path
return false unless FileTest.exist?(subpath(type))
return true
end
# A method for returning a given file of a given type.
# e.g., file = mod.manifest("my/manifest.pp")
#
# If the file name is nil, then the base directory for the
# file type is passed; this is used for fileserving.
define_method(type.to_s.sub(/s$/, '')) do |file|
return nil unless path
# If 'file' is nil then they're asking for the base path.
# This is used for things like fileserving.
if file
full_path = File.join(subpath(type), file)
else
full_path = subpath(type)
end
return nil unless FileTest.exist?(full_path)
return full_path
end
end
def exist?
! path.nil?
end
# Find the first 'files' directory. This is used by the XMLRPC fileserver.
def file_directory
subpath("files")
end
def license_file
return @license_file if defined?(@license_file)
return @license_file = nil unless path
@license_file = File.join(path, "License")
end
def load_metadata
data = PSON.parse File.read(metadata_file)
[:source, :author, :version, :license, :puppetversion].each do |attr|
unless value = data[attr.to_s]
unless attr == :puppetversion
raise MissingMetadata, "No #{attr} module metadata provided for #{self.name}"
end
end
send(attr.to_s + "=", value)
end
end
# Return the list of manifests matching the given glob pattern,
# defaulting to 'init.{pp,rb}' for empty modules.
def match_manifests(rest)
pat = File.join(path, MANIFESTS, rest || 'init')
Dir.
glob(pat + (File.extname(pat).empty? ? '.{pp,rb}' : '')).
reject { |f| FileTest.directory?(f) }
end
def metadata_file
return @metadata_file if defined?(@metadata_file)
return @metadata_file = nil unless path
@metadata_file = File.join(path, "metadata.json")
end
# Find this module in the modulepath.
def path
environment.modulepath.collect { |path| File.join(path, name) }.find { |d| FileTest.exist?(d) }
end
# Find all plugin directories. This is used by the Plugins fileserving mount.
def plugin_directory
subpath("plugins")
end
def requires(name, version = nil)
@requires ||= []
@requires << [name, version]
end
def supports(name, version = nil)
@supports ||= []
@supports << [name, version]
end
def to_s
result = "Module #{name}"
if path
result += "(#{path})"
end
result
end
def validate_dependencies
return unless defined?(@requires)
@requires.each do |name, version|
unless mod = environment.module(name)
raise MissingModule, "Missing module #{name} required by #{self.name}"
end
if version and mod.version != version
raise IncompatibleModule, "Required module #{name} is version #{mod.version} but #{self.name} requires #{version}"
end
end
end
def validate_puppet_version
return unless puppetversion and puppetversion != Puppet.version
raise IncompatibleModule, "Module #{self.name} is only compatible with Puppet version #{puppetversion}, not #{Puppet.version}"
end
private
def subpath(type)
return File.join(path, type) unless type.to_s == "plugins"
return backward_compatible_plugins_dir
end
def backward_compatible_plugins_dir
if dir = File.join(path, "plugins") and FileTest.exist?(dir)
warning "using the deprecated 'plugins' directory for ruby extensions; please move to 'lib'"
return dir
else
return File.join(path, "lib")
end
end
def assert_validity
raise InvalidName, "Invalid module name; module names must be alphanumeric (plus '-'), not '#{name}'" unless name =~ /^[-\w]+$/
end
end
|