puppet_agent_mgr 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/puppet_agent_mgr.rb +25 -0
- data/lib/puppet_agent_mgr/common.rb +195 -0
- data/lib/puppet_agent_mgr/v2/manager.rb +71 -0
- data/lib/puppet_agent_mgr/v2/unix.rb +44 -0
- data/lib/puppet_agent_mgr/v2/windows.rb +1 -0
- data/lib/puppet_agent_mgr/v3/manager.rb +75 -0
- data/lib/puppet_agent_mgr/v3/unix.rb +44 -0
- data/lib/puppet_agent_mgr/v3/windows.rb +1 -0
- data/lib/puppet_agent_mgr/version.rb +3 -0
- metadata +103 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'puppet_agent_mgr/common'
|
2
|
+
|
3
|
+
module PuppetAgentMgr
|
4
|
+
def self.manager
|
5
|
+
require 'puppet'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
if Puppet.version =~ /^(\d+)/
|
9
|
+
case $1
|
10
|
+
when "2"
|
11
|
+
require 'puppet_agent_mgr/v2/manager'
|
12
|
+
return PuppetAgentMgr::V2::Manager.new
|
13
|
+
|
14
|
+
when "3"
|
15
|
+
require 'puppet_agent_mgr/v3/manager'
|
16
|
+
return PuppetAgentMgr::V3::Manager.new
|
17
|
+
|
18
|
+
else
|
19
|
+
raise "Cannot manage Puppet version %s" % $1
|
20
|
+
end
|
21
|
+
else
|
22
|
+
raise "Cannot determine the Puppet major version"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
module PuppetAgentMgr
|
2
|
+
module Common
|
3
|
+
extend Common
|
4
|
+
|
5
|
+
# is a catalog being applied rigt now?
|
6
|
+
def stopped?
|
7
|
+
!applying?
|
8
|
+
end
|
9
|
+
|
10
|
+
# is the daemon running but not applying a catalog
|
11
|
+
def idling?
|
12
|
+
(daemon_present? && !applying?)
|
13
|
+
end
|
14
|
+
|
15
|
+
# is the agent enabled
|
16
|
+
def enabled?
|
17
|
+
!disabled?
|
18
|
+
end
|
19
|
+
|
20
|
+
# seconds since the last catalog was applied
|
21
|
+
def since_lastrun
|
22
|
+
(Time.now - lastrun).to_i
|
23
|
+
end
|
24
|
+
|
25
|
+
# if a resource is being managed, resource in the syntax File[/x] etc
|
26
|
+
def managing_resource?(resource)
|
27
|
+
managed_resources.include?(resource.downcase)
|
28
|
+
end
|
29
|
+
|
30
|
+
# how many resources are managed
|
31
|
+
def managed_resources_count
|
32
|
+
managed_resources.size
|
33
|
+
end
|
34
|
+
|
35
|
+
def run_in_foreground(clioptions)
|
36
|
+
command =["puppet", "agent", "--test", "--color=false"]
|
37
|
+
command.concat(clioptions)
|
38
|
+
|
39
|
+
%x[#{command.join(' ')}]
|
40
|
+
end
|
41
|
+
|
42
|
+
def run_in_background(clioptions)
|
43
|
+
command =["puppet", "agent", "--onetime", "--daemonize", "--color=false"]
|
44
|
+
command.concat(clioptions)
|
45
|
+
|
46
|
+
%x[#{command.join(' ')}]
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_name(name)
|
50
|
+
if name.length == 1
|
51
|
+
return false unless name =~ /\A[a-zA-Z]\Z/
|
52
|
+
else
|
53
|
+
return false unless name =~ /\A[a-zA-Z0-9_]+\Z/
|
54
|
+
end
|
55
|
+
|
56
|
+
true
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_common_puppet_cli(noop, tags, environment, server)
|
60
|
+
opts = []
|
61
|
+
|
62
|
+
(host, port) = server.to_s.split(":")
|
63
|
+
|
64
|
+
raise("Invalid hostname '%s' specified" % host) if host && !(host =~ /\A(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])\Z/)
|
65
|
+
raise("Invalid master port '%s' specified" % port) if port && !(port =~ /\A\d+\Z/)
|
66
|
+
raise("Invalid environment '%s' specified" % environment) if environment && !validate_name(environment)
|
67
|
+
|
68
|
+
unless tags.empty?
|
69
|
+
[tags].flatten.each do |tag|
|
70
|
+
tag.split("::").each do |part|
|
71
|
+
raise("Invalid tag '%s' specified" % tag) unless validate_name(part)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
opts << "--tags %s" % tags.join(",") if !tags.empty?
|
76
|
+
end
|
77
|
+
|
78
|
+
opts << "--noop" if noop
|
79
|
+
opts << "--environment %s" % environment if environment
|
80
|
+
opts << "--server %s" % host if host
|
81
|
+
opts << "--masterport %s" % port if port
|
82
|
+
|
83
|
+
opts
|
84
|
+
end
|
85
|
+
|
86
|
+
# do a run based on the following options:
|
87
|
+
#
|
88
|
+
# :foreground_run - runs in the foreground a --test run
|
89
|
+
# :signal_daemon - if the daemon is running, sends it USR1 to wake it up
|
90
|
+
# :noop - does a noop run if possible
|
91
|
+
# :tags - an array of tags to limit the run to
|
92
|
+
# :environment - the environment to run
|
93
|
+
# :server - the puppet master to use, can be some.host or some.host:port
|
94
|
+
#
|
95
|
+
# else a single background run will be attempted but this will fail if a idling
|
96
|
+
# daemon is present and :signal_daemon was false
|
97
|
+
def runonce!(options={})
|
98
|
+
valid_options = [:noop, :signal_daemon, :foreground_run, :tags, :environment, :server]
|
99
|
+
|
100
|
+
options.keys.each do |opt|
|
101
|
+
raise("Unknown option %s specified" % opt) unless valid_options.include?(opt)
|
102
|
+
end
|
103
|
+
|
104
|
+
raise "Puppet is currently applying a catalog, cannot run now" if applying?
|
105
|
+
raise "Puppet is disabled, cannot run now" if disabled?
|
106
|
+
|
107
|
+
noop = options.fetch(:noop, false)
|
108
|
+
signal_daemon = options.fetch(:signal_daemon, true)
|
109
|
+
foreground_run = options.fetch(:foreground_run, false)
|
110
|
+
environment = options[:environment]
|
111
|
+
tags = [ options[:tags] ].flatten.compact
|
112
|
+
server = options[:server]
|
113
|
+
|
114
|
+
clioptions = create_common_puppet_cli(noop, tags, environment, server)
|
115
|
+
|
116
|
+
if idling? && signal_daemon && !clioptions.empty?
|
117
|
+
raise "Cannot specify any custom puppet options when the daemon is running"
|
118
|
+
end
|
119
|
+
|
120
|
+
if foreground_run
|
121
|
+
run_in_foreground(clioptions)
|
122
|
+
elsif idling? && signal_daemon
|
123
|
+
signal_running_daemon
|
124
|
+
else
|
125
|
+
raise "Cannot run in the background if the daemon is present" if daemon_present?
|
126
|
+
run_in_background(clioptions)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# simple utility to return a hash with lots of useful information about the state of the agent
|
131
|
+
def status
|
132
|
+
status = {:applying => applying?,
|
133
|
+
:enabled => enabled?,
|
134
|
+
:daemon_present => daemon_present?,
|
135
|
+
:lastrun => lastrun,
|
136
|
+
:disable_message => lock_message,
|
137
|
+
:since_lastrun => (Time.now.to_i - lastrun)}
|
138
|
+
|
139
|
+
if !status[:enabled]
|
140
|
+
status[:status] = "disabled"
|
141
|
+
|
142
|
+
elsif status[:applying]
|
143
|
+
status[:status] = "applying a catalog"
|
144
|
+
|
145
|
+
elsif status[:daemon_present] && status[:applying]
|
146
|
+
status[:status] = "idling"
|
147
|
+
|
148
|
+
elsif !status[:applying]
|
149
|
+
status[:status] = "stopped"
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
status[:message] = "Currently %s; last completed run %s ago" % [status[:status], seconds_to_human(status[:since_lastrun])]
|
154
|
+
|
155
|
+
status
|
156
|
+
end
|
157
|
+
|
158
|
+
def atomic_file(file)
|
159
|
+
tempfile = Tempfile.new(File.basename(file), File.dirname(file))
|
160
|
+
|
161
|
+
yield(tempfile)
|
162
|
+
|
163
|
+
tempfile.close
|
164
|
+
File.rename(tempfile.path, file)
|
165
|
+
end
|
166
|
+
|
167
|
+
def seconds_to_human(seconds)
|
168
|
+
days = seconds / 86400
|
169
|
+
seconds -= 86400 * days
|
170
|
+
|
171
|
+
hours = seconds / 3600
|
172
|
+
seconds -= 3600 * hours
|
173
|
+
|
174
|
+
minutes = seconds / 60
|
175
|
+
seconds -= 60 * minutes
|
176
|
+
|
177
|
+
if days > 1
|
178
|
+
return "%d days %d hours %d minutes %02d seconds" % [days, hours, minutes, seconds]
|
179
|
+
|
180
|
+
elsif days == 1
|
181
|
+
return "%d day %d hours %d minutes %02d seconds" % [days, hours, minutes, seconds]
|
182
|
+
|
183
|
+
elsif hours > 0
|
184
|
+
return "%d hours %d minutes %02d seconds" % [hours, minutes, seconds]
|
185
|
+
|
186
|
+
elsif minutes > 0
|
187
|
+
return "%d minutes %02d seconds" % [minutes, seconds]
|
188
|
+
|
189
|
+
else
|
190
|
+
return "%02d seconds" % seconds
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module PuppetAgentMgr::V2
|
2
|
+
class Manager
|
3
|
+
include PuppetAgentMgr::Common
|
4
|
+
|
5
|
+
if Puppet.features.microsoft_windows?
|
6
|
+
require 'puppet_agent_mgr/v2/windows'
|
7
|
+
include Windows
|
8
|
+
else
|
9
|
+
require 'puppet_agent_mgr/v2/unix'
|
10
|
+
include Unix
|
11
|
+
end
|
12
|
+
|
13
|
+
# enables the puppet agent, it can now start applying catalogs again
|
14
|
+
def enable!
|
15
|
+
raise "Already enabled" if enabled?
|
16
|
+
File.unlink(Puppet[:puppetdlockfile])
|
17
|
+
end
|
18
|
+
|
19
|
+
# disable the puppet agent, on version 2.x the message is ignored
|
20
|
+
def disable!(msg=nil)
|
21
|
+
raise "Already disabled" unless enabled?
|
22
|
+
File.open(Puppet[:puppetdlockfile], "w") { }
|
23
|
+
|
24
|
+
""
|
25
|
+
end
|
26
|
+
|
27
|
+
# all the managed resources
|
28
|
+
def managed_resources
|
29
|
+
# need to add some caching here based on mtime of the resources file
|
30
|
+
return [] unless File.exist?(Puppet[:resourcefile])
|
31
|
+
|
32
|
+
File.readlines(Puppet[:resourcefile]).map do |resource|
|
33
|
+
resource.chomp
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# epoch time when the last catalog was applied
|
38
|
+
def lastrun
|
39
|
+
summary = load_summary
|
40
|
+
|
41
|
+
Integer(summary["time"].fetch("last_run", 0))
|
42
|
+
end
|
43
|
+
|
44
|
+
# the current lock message, always "" on 2.0
|
45
|
+
def lock_message
|
46
|
+
""
|
47
|
+
end
|
48
|
+
|
49
|
+
# is the agent disabled
|
50
|
+
def disabled?
|
51
|
+
if File.exist?(Puppet[:puppetdlockfile])
|
52
|
+
if File::Stat.new(Puppet[:puppetdlockfile]).zero?
|
53
|
+
return true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
60
|
+
# loads the summary file and makes sure that some keys are always present
|
61
|
+
def load_summary
|
62
|
+
summary = {"changes" => {}, "time" => {}, "resources" => {}, "version" => {}, "events" => {}}
|
63
|
+
|
64
|
+
summary.merge!(YAML.load_file(Puppet[:lastrunfile])) if File.exist?(Puppet[:lastrunfile])
|
65
|
+
|
66
|
+
summary["resources"] = {"failed"=>0, "changed"=>0, "total"=>0, "restarted"=>0, "out_of_sync"=>0}.merge!(summary["resources"])
|
67
|
+
|
68
|
+
summary
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module PuppetAgentMgr::V2
|
2
|
+
module Unix
|
3
|
+
extend Unix
|
4
|
+
# is the agent daemon currently in the unix process list?
|
5
|
+
def daemon_present?
|
6
|
+
if File.exist?(Puppet[:pidfile])
|
7
|
+
return has_process_for_pid?(File.read(Puppet[:pidfile]))
|
8
|
+
end
|
9
|
+
|
10
|
+
return false
|
11
|
+
end
|
12
|
+
|
13
|
+
# is the agent currently applying a catalog
|
14
|
+
def applying?
|
15
|
+
return false if disabled?
|
16
|
+
|
17
|
+
if File::Stat.new(Puppet[:puppetdlockfile]).size > 0
|
18
|
+
return has_process_for_pid(File.read(Puppet[:puppetdlockfile]))
|
19
|
+
end
|
20
|
+
|
21
|
+
return false
|
22
|
+
rescue
|
23
|
+
return false
|
24
|
+
end
|
25
|
+
|
26
|
+
def signal_running_daemon
|
27
|
+
pid = File.read(Puppet[:pidfile])
|
28
|
+
|
29
|
+
if has_process_for_pid?(pid)
|
30
|
+
begin
|
31
|
+
Process.kill("USR1", Integer(pid))
|
32
|
+
rescue Exception => e
|
33
|
+
raise "Failed to signal the puppet agent at pid %s: %s" % [pid, e.to_s]
|
34
|
+
end
|
35
|
+
else
|
36
|
+
run_in_background
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def has_process_for_pid?(pid)
|
41
|
+
!!Process.kill(0, Integer(pid)) rescue false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
raise "Window is not supported yet"
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module PuppetAgentMgr::V3
|
2
|
+
class Manager
|
3
|
+
if Puppet.features.microsoft_windows?
|
4
|
+
require 'puppet_agent_mgr/v3/windows'
|
5
|
+
include Windows
|
6
|
+
else
|
7
|
+
require 'puppet_agent_mgr/v3/unix'
|
8
|
+
include Unix
|
9
|
+
end
|
10
|
+
|
11
|
+
include PuppetAgentMgr::Common
|
12
|
+
|
13
|
+
# enables the puppet agent, it can now start applying catalogs again
|
14
|
+
def enable!
|
15
|
+
raise "Already enabled" if enabled?
|
16
|
+
File.unlink(Puppet[:agent_disabled_lockfile])
|
17
|
+
end
|
18
|
+
|
19
|
+
# disable the puppet agent, on version 2.x the message is ignored
|
20
|
+
def disable!(msg=nil)
|
21
|
+
raise "Already disabled" unless enabled?
|
22
|
+
|
23
|
+
msg = "Disabled using the Ruby API at %s" % Time.now.strftime("%c") unless msg
|
24
|
+
|
25
|
+
atomic_file(Puppet[:agent_disabled_lockfile]) do |f|
|
26
|
+
f.print(JSON.dump(:disabled_message => msg))
|
27
|
+
end
|
28
|
+
|
29
|
+
msg
|
30
|
+
end
|
31
|
+
|
32
|
+
# all the managed resources
|
33
|
+
def managed_resources
|
34
|
+
# need to add some caching here based on mtime of the resources file
|
35
|
+
return [] unless File.exist?(Puppet[:resourcefile])
|
36
|
+
|
37
|
+
File.readlines(Puppet[:resourcefile]).map do |resource|
|
38
|
+
resource.chomp
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# epoch time when the last catalog was applied
|
43
|
+
def lastrun
|
44
|
+
summary = load_summary
|
45
|
+
|
46
|
+
Integer(summary["time"].fetch("last_run", 0))
|
47
|
+
end
|
48
|
+
|
49
|
+
# the current lock message, always "" on 2.0
|
50
|
+
def lock_message
|
51
|
+
if disabled?
|
52
|
+
lock_data = JSON.parse(File.read(Puppet[:agent_disabled_lockfile]))
|
53
|
+
return lock_data["disabled_message"]
|
54
|
+
else
|
55
|
+
return ""
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# is the agent disabled
|
60
|
+
def disabled?
|
61
|
+
File.exist?(Puppet[:agent_disabled_lockfile])
|
62
|
+
end
|
63
|
+
|
64
|
+
# loads the summary file and makes sure that some keys are always present
|
65
|
+
def load_summary
|
66
|
+
summary = {"changes" => {}, "time" => {}, "resources" => {}, "version" => {}, "events" => {}}
|
67
|
+
|
68
|
+
summary.merge!(YAML.load_file(Puppet[:lastrunfile])) if File.exist?(Puppet[:lastrunfile])
|
69
|
+
|
70
|
+
summary["resources"] = {"failed"=>0, "changed"=>0, "total"=>0, "restarted"=>0, "out_of_sync"=>0}.merge!(summary["resources"])
|
71
|
+
|
72
|
+
summary
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module PuppetAgentMgr::V3
|
2
|
+
module Unix
|
3
|
+
extend Unix
|
4
|
+
# is the agent daemon currently in the unix process list?
|
5
|
+
def daemon_present?
|
6
|
+
if File.exist?(Puppet[:agentpidfile])
|
7
|
+
return has_process_for_pid?(File.read(Puppet[:agentpidfile]))
|
8
|
+
end
|
9
|
+
|
10
|
+
return false
|
11
|
+
end
|
12
|
+
|
13
|
+
# is the agent currently applying a catalog
|
14
|
+
def applying?
|
15
|
+
return false if disabled?
|
16
|
+
|
17
|
+
if File::Stat.new(Puppet[:pidfile]).size > 0
|
18
|
+
return has_process_for_pid(File.read(Puppet[:pidfile]))
|
19
|
+
end
|
20
|
+
|
21
|
+
return false
|
22
|
+
rescue
|
23
|
+
return false
|
24
|
+
end
|
25
|
+
|
26
|
+
def signal_running_daemon
|
27
|
+
pid = File.read(Puppet[:agentpidfile])
|
28
|
+
|
29
|
+
if has_process_for_pid?(pid)
|
30
|
+
begin
|
31
|
+
Process.kill("USR1", Integer(pid))
|
32
|
+
rescue Exception => e
|
33
|
+
raise "Failed to signal the puppet agent at pid %s: %s" % [pid, e.to_s]
|
34
|
+
end
|
35
|
+
else
|
36
|
+
run_in_background
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def has_process_for_pid?(pid)
|
41
|
+
!!Process.kill(0, Integer(pid)) rescue false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
raise "Window is not supported yet"
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: puppet_agent_mgr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- R.I.Pienaar
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-09-27 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rake
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: json
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
description: A simple library that wraps the logic around the locks, pids, JSON filesu and YAML files that makes up the Puppet Agent status. Suppots Puppet 2.7.x and 3.0.x.
|
50
|
+
email: rip@devco.net
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- lib/puppet_agent_mgr/common.rb
|
59
|
+
- lib/puppet_agent_mgr/v3/windows.rb
|
60
|
+
- lib/puppet_agent_mgr/v3/manager.rb
|
61
|
+
- lib/puppet_agent_mgr/v3/unix.rb
|
62
|
+
- lib/puppet_agent_mgr/version.rb
|
63
|
+
- lib/puppet_agent_mgr/v2/windows.rb
|
64
|
+
- lib/puppet_agent_mgr/v2/manager.rb
|
65
|
+
- lib/puppet_agent_mgr/v2/unix.rb
|
66
|
+
- lib/puppet_agent_mgr.rb
|
67
|
+
has_rdoc: true
|
68
|
+
homepage: http://devco.net/
|
69
|
+
licenses: []
|
70
|
+
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
hash: 3
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
version: "0"
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
requirements: []
|
96
|
+
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 1.3.7
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: Puppet Agent Manager
|
102
|
+
test_files: []
|
103
|
+
|