summaryrefslogtreecommitdiffstats
path: root/lib/puppet/node/configuration.rb
blob: 4f93fdbe51c7f36cfc0bc5d239078b2e91eee9c9 (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
require 'puppet/external/gratr/digraph'

# This class models a node configuration.  It is the thing
# meant to be passed from server to client, and it contains all
# of the information in the configuration, including the resources
# and the relationships between them.
class Puppet::Node::Configuration < GRATR::Digraph
    attr_accessor :name, :version
    attr_reader :extraction_format

    # Add classes to our class list.
    def add_class(*classes)
        classes.each do |klass|
            @classes << klass
        end

        # Add the class names as tags, too.
        tag(*classes)
    end

    def classes
        @classes.dup
    end

    # Make sure we support the requested extraction format.
    def extraction_format=(value)
        unless respond_to?("extract_to_%s" % value)
            raise ArgumentError, "Invalid extraction format %s" % value
        end
        @extraction_format = value
    end

    # Turn our configuration graph into whatever the client is expecting.
    def extract
        send("extract_to_%s" % extraction_format)
    end

    # Create the traditional TransBuckets and TransObjects from our configuration
    # graph.  This will hopefully be deprecated soon.
    def extract_to_transportable
        top = nil
        current = nil
        buckets = {}

        unless main = vertices.find { |res| res.type == "class" and res.title == :main }
            raise Puppet::DevError, "Could not find 'main' class; cannot generate configuration"
        end

        # Create a proc for examining edges, which we'll use to build our tree
        # of TransBuckets and TransObjects.
        bucket = nil
        edges = proc do |edge|
            # The sources are always non-builtins.
            source, target = edge.source, edge.target
            unless tmp = buckets[source.to_s]
                if tmp = buckets[source.to_s] = source.to_trans
                    bucket = tmp
                else
                    # This is because virtual resources return nil.  If a virtual
                    # container resource contains realized resources, we still need to get
                    # to them.  So, we keep a reference to the last valid bucket
                    # we returned and use that if the container resource is virtual.
                end
            end
            bucket = tmp || bucket
            if child = target.to_trans
                unless bucket
                    raise "No bucket created for %s" % source
                end
                bucket.push child

                # It's important that we keep a reference to any TransBuckets we've created, so
                # we don't create multiple buckets for children.
                unless target.builtin?
                    buckets[target.to_s] = child
                end
            end
        end
        dfs(:start => main, :examine_edge => edges)

        unless main
            raise Puppet::DevError, "Could not find 'main' class; cannot generate configuration"
        end

        # Retrive the bucket for the top-level scope and set the appropriate metadata.
        unless result = buckets[main.to_s]
            raise Puppet::DevError, "Did not evaluate top scope"
        end

        result.classes = classes

        # Clear the cache to encourage the GC
        buckets.clear
        return result
    end

    def initialize(name)
        super()
        @name = name
        @extraction_format ||= :transportable
        @tags = []
        @classes = []
    end

    # Add a tag.
    def tag(*names)
        names.each do |name|
            name = name.to_s
            @tags << name unless @tags.include?(name)
            if name.include?("::")
                name.split("::").each do |sub|
                    @tags << sub unless @tags.include?(sub)
                end
            end
        end
        nil
    end

    # Return the list of tags.
    def tags
        @tags.dup
    end
end