puppet 2.6.7 → 2.6.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puppet might be problematic. Click here for more details.
- data/CHANGELOG +49 -0
- data/install.rb +6 -2
- data/lib/puppet.rb +1 -1
- data/lib/puppet/application.rb +16 -8
- data/lib/puppet/application/agent.rb +2 -0
- data/lib/puppet/application/apply.rb +3 -0
- data/lib/puppet/application/master.rb +1 -1
- data/lib/puppet/configurer.rb +10 -1
- data/lib/puppet/defaults.rb +9 -0
- data/lib/puppet/file_serving/fileset.rb +1 -0
- data/lib/puppet/indirector/exec.rb +1 -2
- data/lib/puppet/indirector/report/yaml.rb +11 -0
- data/lib/puppet/node/environment.rb +1 -1
- data/lib/puppet/parameter.rb +2 -0
- data/lib/puppet/parameter/path.rb +42 -0
- data/lib/puppet/parser/compiler.rb +1 -1
- data/lib/puppet/parser/lexer.rb +3 -2
- data/lib/puppet/parser/parser_support.rb +0 -1
- data/lib/puppet/provider/exec/posix.rb +112 -0
- data/lib/puppet/provider/exec/shell.rb +17 -0
- data/lib/puppet/provider/group/groupadd.rb +3 -0
- data/lib/puppet/provider/nameservice/#directoryservice.rb# +519 -0
- data/lib/puppet/provider/package/gem.rb +2 -2
- data/lib/puppet/provider/package/macports.rb +106 -0
- data/lib/puppet/provider/service/debian.rb +6 -2
- data/lib/puppet/rails/inventory_node.rb +5 -0
- data/lib/puppet/reference/#providers.rb# +123 -0
- data/lib/puppet/resource/type_collection.rb +6 -1
- data/lib/puppet/simple_graph.rb +1 -1
- data/lib/puppet/transaction.rb +1 -1
- data/lib/puppet/transaction/report.rb +28 -10
- data/lib/puppet/type/cron.rb +3 -1
- data/lib/puppet/type/exec.rb +30 -167
- data/lib/puppet/type/file.rb +12 -1
- data/lib/puppet/type/file/source.rb +1 -0
- data/lib/puppet/type/group.rb +11 -1
- data/lib/puppet/type/service.rb +19 -11
- data/lib/puppet/util/command_line.rb +15 -12
- data/lib/puppet/util/command_line/puppetrun +0 -1
- data/lib/puppet/util/loadedfile.rb +1 -5
- data/lib/puppet/util/metric.rb +3 -5
- data/lib/puppet/util/plugins.rb +82 -0
- data/spec/integration/configurer_spec.rb +38 -5
- data/spec/integration/transaction_spec.rb +43 -42
- data/spec/lib/puppet_spec/verbose.rb +9 -0
- data/spec/shared_behaviours/path_parameters.rb +185 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/unit/application/agent_spec.rb +7 -0
- data/spec/unit/application/apply_spec.rb +6 -0
- data/spec/unit/application/master_spec.rb +2 -2
- data/spec/unit/configurer_spec.rb +48 -0
- data/spec/unit/file_serving/fileset_spec.rb +8 -0
- data/spec/unit/indirector/certificate_status/#file_spec.rb# +188 -0
- data/spec/unit/indirector/exec_spec.rb +2 -3
- data/spec/unit/indirector/facts/inventory_active_record_spec.rb +5 -1
- data/spec/unit/indirector/report/yaml_spec.rb +38 -0
- data/spec/unit/node/environment_spec.rb +15 -14
- data/spec/unit/parameter/path_spec.rb +24 -0
- data/spec/unit/parser/compiler_spec.rb +1 -2
- data/spec/unit/parser/lexer_spec.rb +12 -0
- data/spec/unit/provider/exec/posix_spec.rb +120 -0
- data/spec/unit/provider/exec/shell_spec.rb +50 -0
- data/spec/unit/provider/group/groupadd_spec.rb +11 -1
- data/spec/unit/provider/package/gem_spec.rb +11 -1
- data/spec/unit/provider/package/macports_spec.rb +122 -0
- data/spec/unit/provider/service/debian_spec.rb +14 -2
- data/spec/unit/resource/#type_collection_spec.rb# +463 -0
- data/spec/unit/resource/type_collection_spec.rb +21 -17
- data/spec/unit/transaction/report_spec.rb +13 -2
- data/spec/unit/type/cron_spec.rb +466 -18
- data/spec/unit/type/exec_spec.rb +633 -106
- data/spec/unit/type/file/source_spec.rb +1 -0
- data/spec/unit/type/group_spec.rb +8 -1
- data/spec/unit/type_spec.rb +1 -1
- data/spec/unit/util/loadedfile_spec.rb +7 -0
- data/spec/unit/util/rdoc/parser_spec.rb +2 -1
- data/tasks/rake/git_workflow.rake +3 -1
- data/test/ral/type/exec.rb +87 -176
- metadata +21 -5
- data/lib/puppet/provider/package/darwinport.rb +0 -86
@@ -22,7 +22,7 @@ Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package d
|
|
22
22
|
end
|
23
23
|
|
24
24
|
if name = hash[:justme]
|
25
|
-
command << name
|
25
|
+
command << name + "$"
|
26
26
|
end
|
27
27
|
|
28
28
|
begin
|
@@ -94,7 +94,7 @@ Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package d
|
|
94
94
|
command << "--source" << "#{source}" << resource[:name]
|
95
95
|
end
|
96
96
|
else
|
97
|
-
command << resource[:name]
|
97
|
+
command << "--no-rdoc" << "--no-ri" << resource[:name]
|
98
98
|
end
|
99
99
|
|
100
100
|
output = execute(command)
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'puppet/provider/package'
|
2
|
+
|
3
|
+
Puppet::Type.type(:package).provide :macports, :parent => Puppet::Provider::Package do
|
4
|
+
desc "Package management using MacPorts on OS X.
|
5
|
+
|
6
|
+
Supports MacPorts versions and revisions, but not variants.
|
7
|
+
Variant preferences may be specified using the MacPorts variants.conf file
|
8
|
+
http://guide.macports.org/chunked/internals.configuration-files.html#internals.configuration-files.variants-conf
|
9
|
+
|
10
|
+
When specifying a version in the Puppet DSL, only specify the version, not the revision
|
11
|
+
Revisions are only used internally for ensuring the latest version/revision of a port.
|
12
|
+
"
|
13
|
+
|
14
|
+
confine :operatingsystem => :darwin
|
15
|
+
commands :port => "/opt/local/bin/port"
|
16
|
+
|
17
|
+
has_feature :installable
|
18
|
+
has_feature :uninstallable
|
19
|
+
has_feature :upgradeable
|
20
|
+
has_feature :versionable
|
21
|
+
|
22
|
+
|
23
|
+
def self.parse_installed_query_line(line)
|
24
|
+
regex = /(\S+)\s+@(\S+)_(\S+)\s+\(active\)/
|
25
|
+
fields = [:name, :ensure, :revision]
|
26
|
+
hash_from_line(line, regex, fields)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.parse_info_query_line(line)
|
30
|
+
regex = /(\S+)\s+(\S+)/
|
31
|
+
fields = [:version, :revision]
|
32
|
+
hash_from_line(line, regex, fields)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.hash_from_line(line, regex, fields)
|
36
|
+
hash = {}
|
37
|
+
if match = regex.match(line)
|
38
|
+
fields.zip(match.captures) { |field, value|
|
39
|
+
hash[field] = value
|
40
|
+
}
|
41
|
+
hash[:provider] = self.name
|
42
|
+
return hash
|
43
|
+
end
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.instances
|
48
|
+
packages = []
|
49
|
+
port("-q", :installed).each do |line|
|
50
|
+
if hash = parse_installed_query_line(line)
|
51
|
+
packages << new(hash)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
packages
|
55
|
+
end
|
56
|
+
|
57
|
+
def install
|
58
|
+
should = @resource.should(:ensure)
|
59
|
+
if [:latest, :installed, :present].include?(should)
|
60
|
+
output = port("-q", :install, @resource[:name])
|
61
|
+
else
|
62
|
+
output = port("-q", :install, @resource[:name], "@#{should}")
|
63
|
+
end
|
64
|
+
# MacPorts now correctly exits non-zero with appropriate errors in
|
65
|
+
# situations where a port cannot be found or installed.
|
66
|
+
end
|
67
|
+
|
68
|
+
def query
|
69
|
+
return self.class.parse_installed_query_line(port("-q", :installed, @resource[:name]))
|
70
|
+
end
|
71
|
+
|
72
|
+
def latest
|
73
|
+
# We need both the version and the revision to be confident
|
74
|
+
# we've got the latest revision of a specific version
|
75
|
+
# Note we're still not doing anything with variants here.
|
76
|
+
info_line = port("-q", :info, "--line", "--version", "--revision", @resource[:name])
|
77
|
+
return nil if info_line == ""
|
78
|
+
|
79
|
+
if newest = self.class.parse_info_query_line(info_line)
|
80
|
+
current = query
|
81
|
+
# We're doing some fiddling behind the scenes here to cope with updated revisions.
|
82
|
+
# If we're already at the latest version/revision, then just return the version
|
83
|
+
# so the current and desired values match. Otherwise return version and revision
|
84
|
+
# to trigger an upgrade to the latest revision.
|
85
|
+
if newest[:version] == current[:ensure] and newest[:revision] == current[:revision]
|
86
|
+
return current[:ensure]
|
87
|
+
else
|
88
|
+
return "#{newest[:version]}_#{newest[:revision]}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def uninstall
|
95
|
+
port("-q", :uninstall, @resource[:name])
|
96
|
+
end
|
97
|
+
|
98
|
+
def update
|
99
|
+
if query[:name] == @resource[:name] # 'port upgrade' cannot install new ports
|
100
|
+
port("-q", :upgrade, @resource[:name])
|
101
|
+
else
|
102
|
+
install
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
@@ -22,8 +22,12 @@ Puppet::Type.type(:service).provide :debian, :parent => :init do
|
|
22
22
|
|
23
23
|
# Remove the symlinks
|
24
24
|
def disable
|
25
|
-
|
26
|
-
|
25
|
+
if `dpkg --compare-versions $(dpkg-query -W --showformat '${Version}' sysv-rc) ge 2.88 ; echo $?`.to_i == 0
|
26
|
+
update_rc @resource[:name], "disable"
|
27
|
+
else
|
28
|
+
update_rc "-f", @resource[:name], "remove"
|
29
|
+
update_rc @resource[:name], "stop", "00", "1", "2", "3", "4", "5", "6", "."
|
30
|
+
end
|
27
31
|
end
|
28
32
|
|
29
33
|
def enabled?
|
@@ -3,6 +3,11 @@ require 'puppet/rails/inventory_fact'
|
|
3
3
|
class Puppet::Rails::InventoryNode < ::ActiveRecord::Base
|
4
4
|
has_many :facts, :class_name => "Puppet::Rails::InventoryFact", :foreign_key => :node_id, :dependent => :delete_all
|
5
5
|
|
6
|
+
if Puppet::Util.activerecord_version >= 3.0
|
7
|
+
# Prevents "DEPRECATION WARNING: Base.named_scope has been deprecated, please use Base.scope instead"
|
8
|
+
ActiveRecord::NamedScope::ClassMethods.module_eval { alias :named_scope :scope }
|
9
|
+
end
|
10
|
+
|
6
11
|
named_scope :has_fact_with_value, lambda { |name,value|
|
7
12
|
{
|
8
13
|
:conditions => ["inventory_facts.name = ? AND inventory_facts.value = ?", name, value],
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# This doesn't get stored in trac, since it changes every time.
|
2
|
+
providers = Puppet::Util::Reference.newreference :providers, :title => "Provider Suitability Report", :depth => 1, :dynamic => true, :doc => "Which providers are valid for this machine" do
|
3
|
+
types = []
|
4
|
+
newPuppet::Type.loadall
|
5
|
+
Puppet::Type.eachtype do |klass|
|
6
|
+
next unless klass.providers.length > 0
|
7
|
+
types << klass
|
8
|
+
end
|
9
|
+
types.sort! { |a,b| a.name.to_s <=> b.name.to_s }
|
10
|
+
|
11
|
+
command_line = Puppet::Util::CommandLine.new
|
12
|
+
types.reject! { |type| ! command_line.args.include?(type.name.to_s) } unless command_line.args.empty?
|
13
|
+
|
14
|
+
ret = "Details about this host:\n\n"
|
15
|
+
|
16
|
+
# Throw some facts in there, so we know where the report is from.
|
17
|
+
["Ruby Version", "Puppet Version", "Operating System", "Operating System Release"].each do |label|
|
18
|
+
name = label.gsub(/\s+/, '')
|
19
|
+
value = Facter.value(name)
|
20
|
+
ret += option(label, value)
|
21
|
+
end
|
22
|
+
ret += "\n"
|
23
|
+
|
24
|
+
count = 1
|
25
|
+
|
26
|
+
# Produce output for each type.
|
27
|
+
types.each do |type|
|
28
|
+
features = type.features
|
29
|
+
ret += "\n" # add a trailing newline
|
30
|
+
|
31
|
+
# Now build up a table of provider suitability.
|
32
|
+
headers = %w{Provider Suitable?} + features.collect { |f| f.to_s }.sort
|
33
|
+
|
34
|
+
table_data = {}
|
35
|
+
|
36
|
+
functional = false
|
37
|
+
notes = []
|
38
|
+
begin
|
39
|
+
default = type.defaultprovider.name
|
40
|
+
rescue Puppet::DevError
|
41
|
+
default = "none"
|
42
|
+
end
|
43
|
+
type.providers.sort { |a,b| a.to_s <=> b.to_s }.each do |pname|
|
44
|
+
data = []
|
45
|
+
table_data[pname] = data
|
46
|
+
provider = type.provider(pname)
|
47
|
+
|
48
|
+
# Add the suitability note
|
49
|
+
if missing = provider.suitable?(false) and missing.empty?
|
50
|
+
data << "*X*"
|
51
|
+
suit = true
|
52
|
+
functional = true
|
53
|
+
else
|
54
|
+
data << "[#{count}]_" # A pointer to the appropriate footnote
|
55
|
+
suit = false
|
56
|
+
end
|
57
|
+
|
58
|
+
# Add a footnote with the details about why this provider is unsuitable, if that's the case
|
59
|
+
unless suit
|
60
|
+
details = ".. [#{count}]\n"
|
61
|
+
missing.each do |test, values|
|
62
|
+
case test
|
63
|
+
when :exists
|
64
|
+
details += " - Missing files #{values.join(", ")}\n"
|
65
|
+
when :variable
|
66
|
+
values.each do |name, facts|
|
67
|
+
if Puppet.settings.valid?(name)
|
68
|
+
details += " - Setting #{name} (currently #{Puppet.settings.value(name).inspect}) not in list #{facts.join(", ")}\n"
|
69
|
+
else
|
70
|
+
details += " - Fact #{name} (currently #{Facter.value(name).inspect}) not in list #{facts.join(", ")}\n"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
when :true
|
74
|
+
details += " - Got #{values} true tests that should have been false\n"
|
75
|
+
when :false
|
76
|
+
details += " - Got #{values} false tests that should have been true\n"
|
77
|
+
when :feature
|
78
|
+
details += " - Missing features #{values.collect { |f| f.to_s }.join(",")}\n"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
notes << details
|
82
|
+
|
83
|
+
count += 1
|
84
|
+
end
|
85
|
+
|
86
|
+
# Add a note for every feature
|
87
|
+
features.each do |feature|
|
88
|
+
if provider.features.include?(feature)
|
89
|
+
data << "*X*"
|
90
|
+
else
|
91
|
+
data << ""
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
ret += h(type.name.to_s + "_", 2)
|
97
|
+
|
98
|
+
ret += "[#{type.name}](#{"http://docs.puppetlabs.com/references/stable/type.html##{type.name}"})\n\n"
|
99
|
+
ret += option("Default provider", default)
|
100
|
+
ret += doctable(headers, table_data)
|
101
|
+
|
102
|
+
notes.each do |note|
|
103
|
+
ret += note + "\n"
|
104
|
+
end
|
105
|
+
|
106
|
+
ret += "\n"
|
107
|
+
end
|
108
|
+
|
109
|
+
ret += "\n"
|
110
|
+
|
111
|
+
ret
|
112
|
+
end
|
113
|
+
providers.header = "
|
114
|
+
Puppet resource types are usually backed by multiple implementations called `providers`,
|
115
|
+
which handle variance between platforms and tools.
|
116
|
+
|
117
|
+
Different providers are suitable or unsuitable on different platforms based on things
|
118
|
+
like the presence of a given tool.
|
119
|
+
|
120
|
+
Here are all of the provider-backed types and their different providers. Any unmentioned
|
121
|
+
types do not use providers yet.
|
122
|
+
|
123
|
+
"
|
@@ -162,17 +162,22 @@ class Puppet::Resource::TypeCollection
|
|
162
162
|
parser.string = code
|
163
163
|
else
|
164
164
|
file = Puppet.settings.value(:manifest, environment.to_s)
|
165
|
-
return unless File.exist?(file)
|
166
165
|
parser.file = file
|
167
166
|
end
|
168
167
|
parser.parse
|
169
168
|
rescue => detail
|
169
|
+
@parse_failed = true
|
170
|
+
|
170
171
|
msg = "Could not parse for environment #{environment}: #{detail}"
|
171
172
|
error = Puppet::Error.new(msg)
|
172
173
|
error.set_backtrace(detail.backtrace)
|
173
174
|
raise error
|
174
175
|
end
|
175
176
|
|
177
|
+
def require_reparse?
|
178
|
+
@parse_failed || stale?
|
179
|
+
end
|
180
|
+
|
176
181
|
def stale?
|
177
182
|
@watched_files.values.detect { |file| file.changed? }
|
178
183
|
end
|
data/lib/puppet/simple_graph.rb
CHANGED
@@ -329,7 +329,7 @@ class Puppet::SimpleGraph
|
|
329
329
|
children = other.adjacent(container, :direction => :out)
|
330
330
|
|
331
331
|
# MQR TODO: Luke suggests that it should be possible to refactor the system so that
|
332
|
-
# container nodes are retained, thus obviating the need for the whit.
|
332
|
+
# container nodes are retained, thus obviating the need for the whit.
|
333
333
|
children = [whit_class.new(:name => container.name, :catalog => other)] if children.empty?
|
334
334
|
|
335
335
|
# First create new edges for each of the :in edges
|
data/lib/puppet/transaction.rb
CHANGED
@@ -47,7 +47,7 @@ class Puppet::Transaction
|
|
47
47
|
def apply(resource, ancestor = nil)
|
48
48
|
status = resource_harness.evaluate(resource)
|
49
49
|
add_resource_status(status)
|
50
|
-
event_manager.queue_events(ancestor || resource, status.events)
|
50
|
+
event_manager.queue_events(ancestor || resource, status.events) unless status.failed?
|
51
51
|
rescue => detail
|
52
52
|
resource.err "Could not evaluate: #{detail}"
|
53
53
|
end
|
@@ -80,30 +80,49 @@ class Puppet::Transaction::Report
|
|
80
80
|
host
|
81
81
|
end
|
82
82
|
|
83
|
-
# Provide a summary of this report.
|
83
|
+
# Provide a human readable textual summary of this report.
|
84
84
|
def summary
|
85
|
+
report = raw_summary
|
86
|
+
|
85
87
|
ret = ""
|
88
|
+
report.keys.sort { |a,b| a.to_s <=> b.to_s }.each do |key|
|
89
|
+
ret += "#{Puppet::Util::Metric.labelize(key)}:\n"
|
86
90
|
|
87
|
-
|
88
|
-
ret += "#{metric.label}:\n"
|
89
|
-
metric.values.sort { |a,b|
|
91
|
+
report[key].keys.sort { |a,b|
|
90
92
|
# sort by label
|
91
|
-
if a
|
93
|
+
if a == :total
|
92
94
|
1
|
93
|
-
elsif b
|
95
|
+
elsif b == :total
|
94
96
|
-1
|
95
97
|
else
|
96
|
-
|
98
|
+
report[key][a].to_s <=> report[key][b].to_s
|
97
99
|
end
|
98
|
-
}.each do |
|
100
|
+
}.each do |label|
|
101
|
+
value = report[key][label]
|
99
102
|
next if value == 0
|
100
103
|
value = "%0.2f" % value if value.is_a?(Float)
|
101
|
-
ret += " %15s %s\n" % [label + ":", value]
|
104
|
+
ret += " %15s %s\n" % [Puppet::Util::Metric.labelize(label) + ":", value]
|
102
105
|
end
|
103
106
|
end
|
104
107
|
ret
|
105
108
|
end
|
106
109
|
|
110
|
+
# Provide a raw hash summary of this report.
|
111
|
+
def raw_summary
|
112
|
+
report = {}
|
113
|
+
|
114
|
+
@metrics.each do |name, metric|
|
115
|
+
key = metric.name.to_s
|
116
|
+
report[key] = {}
|
117
|
+
metric.values.each do |name, label, value|
|
118
|
+
report[key][name.to_s] = value
|
119
|
+
end
|
120
|
+
report[key]["total"] = 0 unless key == "time" or report[key].include?("total")
|
121
|
+
end
|
122
|
+
(report["time"] ||= {})["last_run"] = Time.now.tv_sec
|
123
|
+
report
|
124
|
+
end
|
125
|
+
|
107
126
|
# Based on the contents of this report's metrics, compute a single number
|
108
127
|
# that represents the report. The resulting number is a bitmask where
|
109
128
|
# individual bits represent the presence of different metrics.
|
@@ -142,7 +161,6 @@ class Puppet::Transaction::Report
|
|
142
161
|
metrics["total"] = resource_statuses.length
|
143
162
|
|
144
163
|
resource_statuses.each do |name, status|
|
145
|
-
|
146
164
|
Puppet::Resource::Status::STATES.each do |state|
|
147
165
|
metrics[state.to_s] += 1 if status.send(state)
|
148
166
|
end
|
data/lib/puppet/type/cron.rb
CHANGED
@@ -226,7 +226,9 @@ Puppet::Type.newtype(:cron) do
|
|
226
226
|
end
|
227
227
|
|
228
228
|
newproperty(:special) do
|
229
|
-
desc "
|
229
|
+
desc "A special value such as 'reboot' or 'annually'.
|
230
|
+
Only available on supported systems such as Vixie Cron.
|
231
|
+
Overrides more specific time of day/week settings."
|
230
232
|
|
231
233
|
def specials
|
232
234
|
%w{reboot yearly annually monthly weekly daily midnight hourly}
|
data/lib/puppet/type/exec.rb
CHANGED
@@ -23,17 +23,15 @@ module Puppet
|
|
23
23
|
you are doing a lot of work with `exec`, please at least notify
|
24
24
|
us at Puppet Labs what you are doing, and hopefully we can work with
|
25
25
|
you to get a native resource type for the work you are doing.
|
26
|
-
|
27
|
-
**Autorequires:** If Puppet is managing an exec's cwd or the executable file used in an exec's command, the exec resource will autorequire those files. If Puppet is managing the user that an exec should run as, the exec resource will autorequire that user."
|
28
26
|
|
29
|
-
|
27
|
+
**Autorequires:** If Puppet is managing an exec's cwd or the executable file used in an exec's command, the exec resource will autorequire those files. If Puppet is managing the user that an exec should run as, the exec resource will autorequire that user."
|
30
28
|
|
31
29
|
# Create a new check mechanism. It's basically just a parameter that
|
32
30
|
# provides one extra 'check' method.
|
33
|
-
def self.newcheck(name, &block)
|
31
|
+
def self.newcheck(name, options = {}, &block)
|
34
32
|
@checks ||= {}
|
35
33
|
|
36
|
-
check = newparam(name, &block)
|
34
|
+
check = newparam(name, options, &block)
|
37
35
|
@checks[name] = check
|
38
36
|
end
|
39
37
|
|
@@ -65,9 +63,11 @@ module Puppet
|
|
65
63
|
|
66
64
|
# First verify that all of our checks pass.
|
67
65
|
def retrieve
|
68
|
-
#
|
69
|
-
|
70
|
-
|
66
|
+
# We need to return :notrun to trigger evaluation; when that isn't
|
67
|
+
# true, we *LIE* about what happened and return a "success" for the
|
68
|
+
# value, which causes us to be treated as in_sync?, which means we
|
69
|
+
# don't actually execute anything. I think. --daniel 2011-03-10
|
70
|
+
if @resource.check_all_attributes
|
71
71
|
return :notrun
|
72
72
|
else
|
73
73
|
return self.should
|
@@ -89,7 +89,7 @@ module Puppet
|
|
89
89
|
tries.times do |try|
|
90
90
|
# Only add debug messages for tries > 1 to reduce log spam.
|
91
91
|
debug("Exec try #{try+1}/#{tries}") if tries > 1
|
92
|
-
@output, @status =
|
92
|
+
@output, @status = provider.run(self.resource[:command])
|
93
93
|
break if self.should.include?(@status.exitstatus.to_s)
|
94
94
|
if try_sleep > 0 and tries > 1
|
95
95
|
debug("Sleeping for #{try_sleep} seconds between tries")
|
@@ -139,7 +139,7 @@ module Puppet
|
|
139
139
|
newparam(:path) do
|
140
140
|
desc "The search path used for command execution.
|
141
141
|
Commands must be fully qualified if no path is specified. Paths
|
142
|
-
can be specified as an array or as a colon
|
142
|
+
can be specified as an array or as a colon separated list."
|
143
143
|
|
144
144
|
# Support both arrays and colon-separated fields.
|
145
145
|
def value=(*values)
|
@@ -176,21 +176,9 @@ module Puppet
|
|
176
176
|
# Validation is handled by the SUIDManager class.
|
177
177
|
end
|
178
178
|
|
179
|
-
newparam(:cwd) do
|
179
|
+
newparam(:cwd, :parent => Puppet::Parameter::Path) do
|
180
180
|
desc "The directory from which to run the command. If
|
181
181
|
this directory does not exist, the command will fail."
|
182
|
-
|
183
|
-
validate do |dir|
|
184
|
-
unless dir =~ /^#{File::SEPARATOR}/
|
185
|
-
self.fail("CWD must be a fully qualified path")
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
munge do |dir|
|
190
|
-
dir = dir[0] if dir.is_a?(Array)
|
191
|
-
|
192
|
-
dir
|
193
|
-
end
|
194
182
|
end
|
195
183
|
|
196
184
|
newparam(:logoutput) do
|
@@ -209,7 +197,7 @@ module Puppet
|
|
209
197
|
for refreshing."
|
210
198
|
|
211
199
|
validate do |command|
|
212
|
-
|
200
|
+
provider.validatecmd(command)
|
213
201
|
end
|
214
202
|
end
|
215
203
|
|
@@ -241,19 +229,17 @@ module Puppet
|
|
241
229
|
newparam(:timeout) do
|
242
230
|
desc "The maximum time the command should take. If the command takes
|
243
231
|
longer than the timeout, the command is considered to have failed
|
244
|
-
and will be stopped. Use
|
232
|
+
and will be stopped. Use 0 to disable the timeout.
|
245
233
|
The time is specified in seconds."
|
246
234
|
|
247
235
|
munge do |value|
|
248
236
|
value = value.shift if value.is_a?(Array)
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
Float(value)
|
254
|
-
else
|
255
|
-
value
|
237
|
+
begin
|
238
|
+
value = Float(value)
|
239
|
+
rescue ArgumentError => e
|
240
|
+
raise ArgumentError, "The timeout must be a number."
|
256
241
|
end
|
242
|
+
[value, 0.0].max
|
257
243
|
end
|
258
244
|
|
259
245
|
defaultto 300
|
@@ -333,7 +319,7 @@ module Puppet
|
|
333
319
|
end
|
334
320
|
end
|
335
321
|
|
336
|
-
newcheck(:creates) do
|
322
|
+
newcheck(:creates, :parent => Puppet::Parameter::Path) do
|
337
323
|
desc "A file that this command creates. If this
|
338
324
|
parameter is provided, then the command will only be run
|
339
325
|
if the specified file does not exist:
|
@@ -346,19 +332,7 @@ module Puppet
|
|
346
332
|
|
347
333
|
"
|
348
334
|
|
349
|
-
|
350
|
-
# fail the entire exec, right?
|
351
|
-
validate do |files|
|
352
|
-
files = [files] unless files.is_a? Array
|
353
|
-
|
354
|
-
files.each do |file|
|
355
|
-
self.fail("'creates' must be set to a fully qualified path") unless file
|
356
|
-
|
357
|
-
unless file =~ %r{^#{File::SEPARATOR}}
|
358
|
-
self.fail "'creates' files must be fully qualified."
|
359
|
-
end
|
360
|
-
end
|
361
|
-
end
|
335
|
+
accept_arrays
|
362
336
|
|
363
337
|
# If the file exists, return false (i.e., don't run the command),
|
364
338
|
# else return true
|
@@ -386,15 +360,15 @@ module Puppet
|
|
386
360
|
validate do |cmds|
|
387
361
|
cmds = [cmds] unless cmds.is_a? Array
|
388
362
|
|
389
|
-
cmds.each do |
|
390
|
-
|
363
|
+
cmds.each do |command|
|
364
|
+
provider.validatecmd(command)
|
391
365
|
end
|
392
366
|
end
|
393
367
|
|
394
368
|
# Return true if the command does not return 0.
|
395
369
|
def check(value)
|
396
370
|
begin
|
397
|
-
output, status =
|
371
|
+
output, status = provider.run(value, true)
|
398
372
|
rescue Timeout::Error
|
399
373
|
err "Check #{value.inspect} exceeded timeout"
|
400
374
|
return false
|
@@ -428,15 +402,15 @@ module Puppet
|
|
428
402
|
validate do |cmds|
|
429
403
|
cmds = [cmds] unless cmds.is_a? Array
|
430
404
|
|
431
|
-
cmds.each do |
|
432
|
-
|
405
|
+
cmds.each do |command|
|
406
|
+
provider.validatecmd(command)
|
433
407
|
end
|
434
408
|
end
|
435
409
|
|
436
410
|
# Return true if the command returns 0.
|
437
411
|
def check(value)
|
438
412
|
begin
|
439
|
-
output, status =
|
413
|
+
output, status = provider.run(value, true)
|
440
414
|
rescue Timeout::Error
|
441
415
|
err "Check #{value.inspect} exceeded timeout"
|
442
416
|
return false
|
@@ -450,7 +424,7 @@ module Puppet
|
|
450
424
|
@isomorphic = false
|
451
425
|
|
452
426
|
validate do
|
453
|
-
validatecmd(self[:command])
|
427
|
+
provider.validatecmd(self[:command])
|
454
428
|
end
|
455
429
|
|
456
430
|
# FIXME exec should autorequire any exec that 'creates' our cwd
|
@@ -503,7 +477,7 @@ module Puppet
|
|
503
477
|
# Verify that we pass all of the checks. The argument determines whether
|
504
478
|
# we skip the :refreshonly check, which is necessary because we now check
|
505
479
|
# within refresh
|
506
|
-
def
|
480
|
+
def check_all_attributes(refreshing = false)
|
507
481
|
self.class.checks.each { |check|
|
508
482
|
next if refreshing and check == :refreshonly
|
509
483
|
if @parameters.include?(check)
|
@@ -518,32 +492,6 @@ module Puppet
|
|
518
492
|
true
|
519
493
|
end
|
520
494
|
|
521
|
-
# Verify that we have the executable
|
522
|
-
def checkexe(cmd)
|
523
|
-
exe = extractexe(cmd)
|
524
|
-
|
525
|
-
if self[:path]
|
526
|
-
if Puppet.features.posix? and !File.exists?(exe)
|
527
|
-
withenv :PATH => self[:path].join(File::PATH_SEPARATOR) do
|
528
|
-
exe = which(exe) || raise(ArgumentError,"Could not find command '#{exe}'")
|
529
|
-
end
|
530
|
-
elsif Puppet.features.microsoft_windows? and !File.exists?(exe)
|
531
|
-
self[:path].each do |path|
|
532
|
-
[".exe", ".ps1", ".bat", ".com", ""].each do |extension|
|
533
|
-
file = File.join(path, exe+extension)
|
534
|
-
return if File.exists?(file)
|
535
|
-
end
|
536
|
-
end
|
537
|
-
end
|
538
|
-
end
|
539
|
-
|
540
|
-
raise ArgumentError, "Could not find executable '#{exe}'" unless FileTest.exists?(exe)
|
541
|
-
unless FileTest.executable?(exe)
|
542
|
-
raise ArgumentError,
|
543
|
-
"'#{exe}' is not executable"
|
544
|
-
end
|
545
|
-
end
|
546
|
-
|
547
495
|
def output
|
548
496
|
if self.property(:returns).nil?
|
549
497
|
return nil
|
@@ -554,98 +502,13 @@ module Puppet
|
|
554
502
|
|
555
503
|
# Run the command, or optionally run a separately-specified command.
|
556
504
|
def refresh
|
557
|
-
if self.
|
505
|
+
if self.check_all_attributes(true)
|
558
506
|
if cmd = self[:refresh]
|
559
|
-
|
507
|
+
provider.run(cmd)
|
560
508
|
else
|
561
509
|
self.property(:returns).sync
|
562
510
|
end
|
563
511
|
end
|
564
512
|
end
|
565
|
-
|
566
|
-
# Run a command.
|
567
|
-
def run(command, check = false)
|
568
|
-
output = nil
|
569
|
-
status = nil
|
570
|
-
|
571
|
-
dir = nil
|
572
|
-
|
573
|
-
checkexe(command)
|
574
|
-
|
575
|
-
if dir = self[:cwd]
|
576
|
-
unless File.directory?(dir)
|
577
|
-
if check
|
578
|
-
dir = nil
|
579
|
-
else
|
580
|
-
self.fail "Working directory '#{dir}' does not exist"
|
581
|
-
end
|
582
|
-
end
|
583
|
-
end
|
584
|
-
|
585
|
-
dir ||= Dir.pwd
|
586
|
-
|
587
|
-
if check
|
588
|
-
debug "Executing check '#{command}'"
|
589
|
-
else
|
590
|
-
debug "Executing '#{command}'"
|
591
|
-
end
|
592
|
-
begin
|
593
|
-
# Do our chdir
|
594
|
-
Dir.chdir(dir) do
|
595
|
-
environment = {}
|
596
|
-
|
597
|
-
environment[:PATH] = self[:path].join(":") if self[:path]
|
598
|
-
|
599
|
-
if envlist = self[:environment]
|
600
|
-
envlist = [envlist] unless envlist.is_a? Array
|
601
|
-
envlist.each do |setting|
|
602
|
-
if setting =~ /^(\w+)=((.|\n)+)$/
|
603
|
-
name = $1
|
604
|
-
value = $2
|
605
|
-
if environment.include? name
|
606
|
-
warning(
|
607
|
-
"Overriding environment setting '#{name}' with '#{value}'"
|
608
|
-
)
|
609
|
-
end
|
610
|
-
environment[name] = value
|
611
|
-
else
|
612
|
-
warning "Cannot understand environment setting #{setting.inspect}"
|
613
|
-
end
|
614
|
-
end
|
615
|
-
end
|
616
|
-
|
617
|
-
withenv environment do
|
618
|
-
Timeout::timeout(self[:timeout]) do
|
619
|
-
output, status = Puppet::Util::SUIDManager.run_and_capture(
|
620
|
-
[command], self[:user], self[:group]
|
621
|
-
)
|
622
|
-
end
|
623
|
-
# The shell returns 127 if the command is missing.
|
624
|
-
if status.exitstatus == 127
|
625
|
-
raise ArgumentError, output
|
626
|
-
end
|
627
|
-
end
|
628
|
-
end
|
629
|
-
rescue Errno::ENOENT => detail
|
630
|
-
self.fail detail.to_s
|
631
|
-
end
|
632
|
-
|
633
|
-
return output, status
|
634
|
-
end
|
635
|
-
|
636
|
-
def validatecmd(cmd)
|
637
|
-
exe = extractexe(cmd)
|
638
|
-
# if we're not fully qualified, require a path
|
639
|
-
self.fail "'#{cmd}' is not qualified and no path was specified. Please qualify the command or specify a path." if File.expand_path(exe) != exe and self[:path].nil?
|
640
|
-
end
|
641
|
-
|
642
|
-
def extractexe(cmd)
|
643
|
-
# easy case: command was quoted
|
644
|
-
if cmd =~ /^"([^"]+)"/
|
645
|
-
$1
|
646
|
-
else
|
647
|
-
cmd.split(/ /)[0]
|
648
|
-
end
|
649
|
-
end
|
650
513
|
end
|
651
514
|
end
|