summaryrefslogtreecommitdiffstats
path: root/lib/puppet/agent.rb
blob: 3add43ec1ea25f7da006668759c9fd15c1a7967b (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
require 'sync'
require 'puppet/external/event-loop'

# A general class for triggering a run of another
# class.
class Puppet::Agent
    require 'puppet/agent/locker'
    include Puppet::Agent::Locker

    require 'puppet/agent/runner'

    attr_reader :client_class, :client, :needing_restart, :splayed
    attr_accessor :stopping

    def configure_delayed_restart
        @needing_restart = true
    end

    # Just so we can specify that we are "the" instance.
    def initialize(client_class)
        @splayed = false

        @client_class = client_class
    end

    def lockfile_path
        client_class.lockfile_path
    end

    def needing_restart?
        @needing_restart
    end

    def restart
        configure_delayed_restart and return if running?
        Process.kill(:HUP, $$)
        @needing_restart = false
    end

    # Perform a run with our client.
    def run
        if running?
            Puppet.notice "Run of %s already in progress; skipping" % client_class
            return
        end
        if stopping?
            Puppet.notice "In shutdown progress; skipping run"
            return
        end
        splay
        with_client do |client|
            begin
                sync.synchronize { lock { client.run } }
            rescue => detail
                puts detail.backtrace if Puppet[:trace]
                Puppet.err "Could not run %s: %s" % [client_class, detail]
            end
        end
    end

    def stop
        if self.stopping?
            Puppet.notice "Already in shutdown"
            return
        end
        self.stopping = true
        if client and client.respond_to?(:stop)
            begin
                client.stop
            rescue
                puts detail.backtrace if Puppet[:trace]
                Puppet.err "Could not stop %s: %s" % [client_class, detail]
            end
        end
    ensure
        self.stopping = false
    end

    def stopping?
        stopping
    end

    # Have we splayed already?
    def splayed?
        splayed
    end

    # Sleep when splay is enabled; else just return.
    def splay
        return unless Puppet[:splay]
        return if splayed?

        time = rand(Integer(Puppet[:splaylimit]) + 1)
        Puppet.info "Sleeping for %s seconds (splay is enabled)" % time
        sleep(time)
        @splayed = true
    end

    # Start listening for events.  We're pretty much just listening for
    # timer events here.
    def start
        # Create our timer.  Puppet will handle observing it and such.
        timer = EventLoop::Timer.new(:interval => Puppet[:runinterval], :tolerance => 1, :start? => true) do
            run()
        end

        # Run once before we start following the timer
        timer.sound_alarm
    end

    def sync
        unless defined?(@sync) and @sync
            @sync = Sync.new
        end
        @sync
    end

    private

    # Create and yield a client instance, keeping a reference
    # to it during the yield.
    def with_client
        begin
            @client = client_class.new
        rescue => details
            puts detail.backtrace if Puppet[:trace]
            Puppet.err "Could not create instance of %s: %s" % [client_class, details]
            return
        end
        yield @client
    ensure
        @client = nil
    end
end