# Support for modules class Puppet::Module TEMPLATES = "templates" FILES = "files" MANIFESTS = "manifests" # 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) dirs = Puppet.settings.value(:modulepath, environment).split(":") if ENV["PUPPETLIB"] dirs = ENV["PUPPETLIB"].split(":") + dirs else end dirs.select do |p| p =~ /^#{File::SEPARATOR}/ && File::directory?(p) end 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) if modname =~ %r/^#{File::SEPARATOR}/ return nil end modpath = all(environment).find { |f| File::directory?(f) } return nil unless modpath return self.new(modname, modpath) end # Return an array of the full path of every subdirectory in each # directory in the modulepath. def self.all(environment = nil) modulepath(environment).map do |mp| Dir.new(mp).map do |modfile| modpath = File.join(mp, modfile) unless modfile == '.' or modfile == '..' or !File.directory?(modpath) modpath else nil end end end.flatten.compact end # Instance methods # Find the concrete file denoted by +file+. If +file+ is absolute, # return it directly. Otherwise try to find it as a template in a # module. If that fails, return it relative to the +templatedir+ config # param. # In all cases, an absolute path is returned, which does not # necessarily refer to an existing file def self.find_template(template, environment = nil) if template =~ /^#{File::SEPARATOR}/ return template end path, file = split_path(template) # Because templates don't have an assumed template name, like manifests do, # we treat templates with no name as being templates in the main template # directory. if file.nil? mod = nil else mod = find(path, environment) end if mod return mod.template(file) else return File.join(Puppet.settings.value(:templatedir, environment), template) end end # Return a list of manifests (as absolute filenames) that match +pat+ # with the current directory set to +cwd+. If the first component of # +pat+ does not contain any wildcards and is an existing module, return # a list of manifests in that module matching the rest of +pat+ # Otherwise, try to find manifests matching +pat+ relative to +cwd+ def self.find_manifests(start, options = {}) cwd = options[:cwd] || Dir.getwd module_name, pattern = split_path(start) if module_name and mod = find(module_name, options[:environment]) return mod.manifests(pattern) else abspat = File::expand_path(start, cwd) files = Dir.glob(abspat).reject { |f| FileTest.directory?(f) } if files.size == 0 files = Dir.glob(abspat + ".pp").reject { |f| FileTest.directory?(f) } end return files end end # Split the path into the module and the rest of the path. # This method can and often does return nil, so anyone calling # it needs to handle that. def self.split_path(path) if path =~ %r/^#{File::SEPARATOR}/ return nil end modname, rest = path.split(File::SEPARATOR, 2) return nil if modname.nil? || modname.empty? return modname, rest end attr_reader :name, :path def initialize(name, path) @name = name @path = path end def template(file) return File::join(path, TEMPLATES, file) end def files return File::join(path, FILES) end # Return the list of manifests matching the given glob pattern, # defaulting to 'init.pp' for empty modules. def manifests(rest) rest ||= "init.pp" p = File::join(path, MANIFESTS, rest) files = Dir.glob(p) if files.size == 0 files = Dir.glob(p + ".pp") end return files end private :initialize end