summaryrefslogtreecommitdiffstats
path: root/lib/puppet/module.rb
blob: 45b40698b36694ec6e999d449ad3a591b84655a6 (plain)
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
# Support for modules
class Puppet::Module

    TEMPLATES = "templates"
    FILES = "files"
    MANIFESTS = "manifests"
    PLUGINS = "plugins"

    FILETYPES = [MANIFESTS, FILES, TEMPLATES, PLUGINS]

    # Search through a list of paths, yielding each found module in turn.
    def self.each_module(*paths)
        paths = paths.flatten.collect { |p| p.split(File::PATH_SEPARATOR) }.flatten

        yielded = {}
        paths.each do |dir|
            next unless FileTest.directory?(dir)

            Dir.entries(dir).each do |name|
                next if name =~ /^\./
                next if yielded.include?(name)

                module_path = File.join(dir, name)
                next unless FileTest.directory?(module_path)

                yielded[name] = true

                yield Puppet::Module.new(name, module_path)
            end
        end
    end
    
    # 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

    # Return an array of paths by splitting the +templatedir+ config
    # parameter.
    def self.templatepath(environment = nil)
        dirs = Puppet.settings.value(:templatedir, environment).split(":")
        dirs.select do |p|
            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)
        Puppet::Node::Environment.new(environment).module(modname)
    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

        if template_paths = templatepath(environment)
            # If we can find the template in :templatedir, we return that.
            td_file = template_paths.collect { |path|
                File::join(path, template)
            }.each do |f|
                return f if FileTest.exist?(f)
            end
        end

        # check in the default template dir, if there is one
        unless td_file = find_template_for_module(template, environment)
            raise Puppet::Error, "No valid template directory found, please check templatedir settings" if template_paths.nil?
            td_file = File::join(template_paths.first, template)
        end
        td_file
    end

    def self.find_template_for_module(template, environment = nil)
        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.
        return nil unless file

        if mod = find(path, environment) and t = mod.template(file)
            return t
        end
        nil
    end

    private_class_method :find_template_for_module

    # 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.match_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

    FILETYPES.each do |type|
        # Create a method for returning the full path to a given
        # file type's directory.
        define_method(type.to_s) do
            File.join(path, type.to_s)
        end
        # Create a boolean method for testing whether our module has
        # files of a given type.
        define_method(type.to_s + "?") do
            FileTest.exist?(send(type))
        end

        # Finally, a method for returning an individual file
        define_method(type.to_s.sub(/s$/, '')) do |file|
            if file
                path = File.join(send(type), file)
            else
                path = send(type)
            end
            return nil unless FileTest.exist?(path)
            return path
        end
    end

    # Return the list of manifests matching the given glob pattern,
    # defaulting to 'init.pp' for empty modules.
    def match_manifests(rest)
        rest ||= "init.pp"
        p = File::join(path, MANIFESTS, rest)
        files = Dir.glob(p).reject { |f| FileTest.directory?(f) }
        if files.size == 0
            files = Dir.glob(p + ".pp")
        end
        return files
    end
end