summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet')
-rw-r--r--lib/puppet/application/agent.rb8
-rw-r--r--lib/puppet/application/apply.rb7
-rw-r--r--lib/puppet/application/ca.rb5
-rw-r--r--lib/puppet/application/cert.rb3
-rw-r--r--lib/puppet/application/certificate.rb5
-rw-r--r--lib/puppet/application/device.rb8
-rw-r--r--lib/puppet/application/face_base.rb3
-rw-r--r--lib/puppet/application/inspect.rb7
-rw-r--r--lib/puppet/defaults.rb2
-rw-r--r--lib/puppet/face/ca.rb233
-rw-r--r--lib/puppet/face/certificate.rb26
-rw-r--r--lib/puppet/face/certificate_request.rb20
-rw-r--r--lib/puppet/face/certificate_revocation_list.rb12
-rw-r--r--lib/puppet/face/node/clean.rb154
-rw-r--r--lib/puppet/face/status.rb1
-rw-r--r--lib/puppet/file_serving/configuration/parser.rb19
-rw-r--r--lib/puppet/indirector/face.rb24
-rw-r--r--lib/puppet/indirector/report/processor.rb45
-rw-r--r--lib/puppet/indirector/rest.rb4
-rw-r--r--lib/puppet/indirector/yaml.rb5
-rw-r--r--lib/puppet/interface.rb9
-rw-r--r--lib/puppet/interface/action.rb80
-rw-r--r--lib/puppet/interface/action_manager.rb15
-rw-r--r--lib/puppet/interface/face_collection.rb95
-rw-r--r--lib/puppet/interface/option.rb19
-rw-r--r--lib/puppet/interface/option_builder.rb13
-rw-r--r--lib/puppet/interface/option_manager.rb3
-rw-r--r--lib/puppet/module.rb5
-rw-r--r--lib/puppet/network/authconfig.rb4
-rwxr-xr-xlib/puppet/network/handler/fileserver.rb3
-rw-r--r--lib/puppet/network/http/webrick.rb2
-rw-r--r--lib/puppet/network/rest_authconfig.rb7
-rw-r--r--lib/puppet/network/rest_authorization.rb2
-rw-r--r--lib/puppet/parser/functions/create_resources.rb11
-rw-r--r--lib/puppet/reports/store.rb15
-rw-r--r--lib/puppet/resource/catalog.rb9
-rw-r--r--lib/puppet/ssl/inventory.rb2
-rwxr-xr-xlib/puppet/type/file/source.rb4
-rwxr-xr-xlib/puppet/type/user.rb8
-rw-r--r--lib/puppet/util.rb1
-rw-r--r--lib/puppet/util/settings.rb2
41 files changed, 713 insertions, 187 deletions
diff --git a/lib/puppet/application/agent.rb b/lib/puppet/application/agent.rb
index f0442648b..ea7cbdfb5 100644
--- a/lib/puppet/application/agent.rb
+++ b/lib/puppet/application/agent.rb
@@ -187,10 +187,10 @@ configuration options can also be generated by running puppet agent with
should always at least contain MD5, MD2, SHA1 and SHA256.
* --detailed-exitcodes:
- Provide transaction information via exit codes. If this is enabled, an
- exit code of '2' means there were changes, and an exit code of '4'
- means that there were failures during the transaction. This option
- only makes sense in conjunction with --onetime.
+ Provide transaction information via exit codes. If this is enabled, an exit
+ code of '2' means there were changes, an exit code of '4' means there were
+ failures during the transaction, and an exit code of '6' means there were both
+ changes and failures.
* --disable:
Disable working on the local system. This puts a lock file in place,
diff --git a/lib/puppet/application/apply.rb b/lib/puppet/application/apply.rb
index 5562a9b09..200309b7d 100644
--- a/lib/puppet/application/apply.rb
+++ b/lib/puppet/application/apply.rb
@@ -82,9 +82,10 @@ configuration options can also be generated by running puppet with
Enable full debugging.
* --detailed-exitcodes:
- Provide transaction information via exit codes. If this is enabled, an
- exit code of '2' means there were changes, and an exit code of '4'
- means that there were failures during the transaction.
+ Provide transaction information via exit codes. If this is enabled, an exit
+ code of '2' means there were changes, an exit code of '4' means there were
+ failures during the transaction, and an exit code of '6' means there were both
+ changes and failures.
* --help:
Print this help message
diff --git a/lib/puppet/application/ca.rb b/lib/puppet/application/ca.rb
new file mode 100644
index 000000000..d1ec2502e
--- /dev/null
+++ b/lib/puppet/application/ca.rb
@@ -0,0 +1,5 @@
+require 'puppet/application/face_base'
+
+class Puppet::Application::Ca < Puppet::Application::FaceBase
+ run_mode :master
+end
diff --git a/lib/puppet/application/cert.rb b/lib/puppet/application/cert.rb
index 162672b6a..330fba8bd 100644
--- a/lib/puppet/application/cert.rb
+++ b/lib/puppet/application/cert.rb
@@ -218,7 +218,8 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
if sub = self.command_line.args.shift then
self.subcommand = sub
else
- help
+ puts help
+ exit
end
end
result
diff --git a/lib/puppet/application/certificate.rb b/lib/puppet/application/certificate.rb
index eacb830b2..de5b2c499 100644
--- a/lib/puppet/application/certificate.rb
+++ b/lib/puppet/application/certificate.rb
@@ -2,11 +2,6 @@ require 'puppet/application/indirection_base'
class Puppet::Application::Certificate < Puppet::Application::IndirectionBase
def setup
- unless options[:ca_location]
- raise ArgumentError, "You must have a CA location specified;\n" +
- "use --ca-location to specify the location (remote, local, only)"
- end
-
location = Puppet::SSL::Host.ca_location
if location == :local && !Puppet::SSL::CertificateAuthority.ca?
self.class.run_mode("master")
diff --git a/lib/puppet/application/device.rb b/lib/puppet/application/device.rb
index 3e2dec98c..977c5c023 100644
--- a/lib/puppet/application/device.rb
+++ b/lib/puppet/application/device.rb
@@ -113,10 +113,10 @@ parameter, so you can specify '--server <servername>' as an argument.
Enable full debugging.
* --detailed-exitcodes:
- Provide transaction information via exit codes. If this is enabled, an
- exit code of '2' means there were changes, and an exit code of '4' means
- that there were failures during the transaction. This option only makes
- sense in conjunction with --onetime.
+ Provide transaction information via exit codes. If this is enabled, an exit
+ code of '2' means there were changes, an exit code of '4' means there were
+ failures during the transaction, and an exit code of '6' means there were both
+ changes and failures.
* --help:
Print this help message
diff --git a/lib/puppet/application/face_base.rb b/lib/puppet/application/face_base.rb
index ea5ba4aaf..a111518f1 100644
--- a/lib/puppet/application/face_base.rb
+++ b/lib/puppet/application/face_base.rb
@@ -100,7 +100,8 @@ class Puppet::Application::FaceBase < Puppet::Application
# action object it represents; if this is an invalid action name that
# will be nil, and handled later.
action_name = item.to_sym
- @action = @face.get_action(action_name)
+ @action = Puppet::Face.find_action(@face.name, action_name)
+ @face = @action.face if @action
end
end
diff --git a/lib/puppet/application/inspect.rb b/lib/puppet/application/inspect.rb
index 30865cfc1..b5a4ac872 100644
--- a/lib/puppet/application/inspect.rb
+++ b/lib/puppet/application/inspect.rb
@@ -1,6 +1,4 @@
-require 'puppet'
require 'puppet/application'
-require 'puppet/file_bucket/dipper'
class Puppet::Application::Inspect < Puppet::Application
@@ -98,6 +96,11 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
Puppet::Resource::Catalog.indirection.terminus_class = :yaml
end
+ def preinit
+ require 'puppet'
+ require 'puppet/file_bucket/dipper'
+ end
+
def run_command
benchmark(:notice, "Finished inspection") do
retrieval_starttime = Time.now
diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb
index 07442d0e9..2247634b3 100644
--- a/lib/puppet/defaults.rb
+++ b/lib/puppet/defaults.rb
@@ -466,7 +466,7 @@ module Puppet
:desc => "The directory in which to store reports
received from the client. Each client gets a separate
subdirectory."},
- :reporturl => ["http://localhost:3000/reports",
+ :reporturl => ["http://localhost:3000/reports/upload",
"The URL used by the http reports processor to send reports"],
:fileserverconfig => ["$confdir/fileserver.conf", "Where the fileserver configuration is stored."],
:strict_hostname_checking => [false, "Whether to only search for the complete
diff --git a/lib/puppet/face/ca.rb b/lib/puppet/face/ca.rb
new file mode 100644
index 000000000..00591d637
--- /dev/null
+++ b/lib/puppet/face/ca.rb
@@ -0,0 +1,233 @@
+require 'puppet/face'
+
+Puppet::Face.define(:ca, '0.1.0') do
+ copyright "Puppet Labs", 2011
+ license "Apache 2 license; see COPYING"
+
+ summary "Local Puppet Certificate Authority management."
+
+ description <<-TEXT
+ This provides local management of the Puppet Certificate Authority.
+
+ You can use this subcommand to sign outstanding certificate requests, list
+ and manage local certificates, and inspect the state of the CA.
+ TEXT
+
+ action :list do
+ summary "List certificates and/or certificate requests."
+
+ description <<-TEXT
+ This will list the current certificates and certificate signing requests
+ in the Puppet CA. You will also get the fingerprint, and any certificate
+ verification failure reported.
+ TEXT
+
+ option "--[no-]all" do
+ summary "Include all certificates and requests."
+ end
+
+ option "--[no-]pending" do
+ summary "Include pending certificate signing requests."
+ end
+
+ option "--[no-]signed" do
+ summary "Include signed certificates."
+ end
+
+ option "--subject PATTERN" do
+ summary "Only list if the subject matches PATTERN."
+
+ description <<-TEXT
+ Only include certificates or requests where subject matches PATTERN.
+
+ PATTERN is interpreted as a regular expression, allowing complex
+ filtering of the content.
+ TEXT
+ end
+
+ when_invoked do |options|
+ raise "Not a CA" unless Puppet::SSL::CertificateAuthority.ca?
+ unless ca = Puppet::SSL::CertificateAuthority.instance
+ raise "Unable to fetch the CA"
+ end
+
+ pattern = options[:subject].nil? ? nil :
+ Regexp.new(options[:subject], Regexp::IGNORECASE)
+
+ pending = options[:pending].nil? ? options[:all] : options[:pending]
+ signed = options[:signed].nil? ? options[:all] : options[:signed]
+
+ # By default we list pending, so if nothing at all was requested...
+ unless pending or signed then pending = true end
+
+ hosts = []
+
+ pending and hosts += ca.waiting?
+ signed and hosts += ca.list
+
+ pattern and hosts = hosts.select {|hostname| pattern.match hostname }
+
+ hosts.sort.map {|host| Puppet::SSL::Host.new(host) }
+ end
+
+ when_rendering :console do |hosts|
+ unless ca = Puppet::SSL::CertificateAuthority.instance
+ raise "Unable to fetch the CA"
+ end
+
+ length = hosts.map{|x| x.name.length }.max + 1
+
+ hosts.map do |host|
+ name = host.name.ljust(length)
+ if host.certificate_request then
+ " #{name} (#{host.certificate_request.fingerprint})"
+ else
+ begin
+ ca.verify(host.certificate)
+ "+ #{name} (#{host.certificate.fingerprint})"
+ rescue Puppet::SSL::CertificateAuthority::CertificateVerificationError => e
+ "- #{name} (#{host.certificate.fingerprint}) (#{e.to_s})"
+ end
+ end
+ end.join("\n")
+ end
+ end
+
+ action :destroy do
+ when_invoked do |host, options|
+ raise "Not a CA" unless Puppet::SSL::CertificateAuthority.ca?
+ unless ca = Puppet::SSL::CertificateAuthority.instance
+ raise "Unable to fetch the CA"
+ end
+
+ ca.destroy host
+ end
+ end
+
+ action :revoke do
+ when_invoked do |host, options|
+ raise "Not a CA" unless Puppet::SSL::CertificateAuthority.ca?
+ unless ca = Puppet::SSL::CertificateAuthority.instance
+ raise "Unable to fetch the CA"
+ end
+
+ begin
+ ca.revoke host
+ rescue ArgumentError => e
+ # This is a bit naff, but it makes the behaviour consistent with the
+ # destroy action. The underlying tools could be nicer for that sort
+ # of thing; they have fairly inconsistent reporting of failures.
+ raise unless e.to_s =~ /Could not find a serial number for /
+ "Nothing was revoked"
+ end
+ end
+ end
+
+ action :generate do
+ when_invoked do |host, options|
+ raise "Not a CA" unless Puppet::SSL::CertificateAuthority.ca?
+ unless ca = Puppet::SSL::CertificateAuthority.instance
+ raise "Unable to fetch the CA"
+ end
+
+ begin
+ ca.generate host
+ rescue RuntimeError => e
+ if e.to_s =~ /already has a requested certificate/
+ "#{host} already has a certificate request; use sign instead"
+ else
+ raise
+ end
+ rescue ArgumentError => e
+ if e.to_s =~ /A Certificate already exists for /
+ "#{host} already has a certificate"
+ else
+ raise
+ end
+ end
+ end
+ end
+
+ action :sign do
+ when_invoked do |host, options|
+ raise "Not a CA" unless Puppet::SSL::CertificateAuthority.ca?
+ unless ca = Puppet::SSL::CertificateAuthority.instance
+ raise "Unable to fetch the CA"
+ end
+
+ begin
+ ca.sign host
+ rescue ArgumentError => e
+ if e.to_s =~ /Could not find certificate request/
+ e.to_s
+ else
+ raise
+ end
+ end
+ end
+ end
+
+ action :print do
+ when_invoked do |host, options|
+ raise "Not a CA" unless Puppet::SSL::CertificateAuthority.ca?
+ unless ca = Puppet::SSL::CertificateAuthority.instance
+ raise "Unable to fetch the CA"
+ end
+
+ ca.print host
+ end
+ end
+
+ action :fingerprint do
+ option "--digest ALGORITHM" do
+ summary "The hash algorithm to use when displaying the fingerprint"
+ end
+
+ when_invoked do |host, options|
+ raise "Not a CA" unless Puppet::SSL::CertificateAuthority.ca?
+ unless ca = Puppet::SSL::CertificateAuthority.instance
+ raise "Unable to fetch the CA"
+ end
+
+ begin
+ # I want the default from the CA, not to duplicate it, but passing
+ # 'nil' explicitly means that we don't get that. This works...
+ if options.has_key? :digest
+ ca.fingerprint host, options[:digest]
+ else
+ ca.fingerprint host
+ end
+ rescue ArgumentError => e
+ raise unless e.to_s =~ /Could not find a certificate or csr for/
+ nil
+ end
+ end
+ end
+
+ action :verify do
+ when_invoked do |host, options|
+ raise "Not a CA" unless Puppet::SSL::CertificateAuthority.ca?
+ unless ca = Puppet::SSL::CertificateAuthority.instance
+ raise "Unable to fetch the CA"
+ end
+
+ begin
+ ca.verify host
+ { :host => host, :valid => true }
+ rescue ArgumentError => e
+ raise unless e.to_s =~ /Could not find a certificate for/
+ { :host => host, :valid => false, :error => e.to_s }
+ rescue Puppet::SSL::CertificateAuthority::CertificateVerificationError => e
+ { :host => host, :valid => false, :error => e.to_s }
+ end
+ end
+
+ when_rendering :console do |value|
+ if value[:valid]
+ nil
+ else
+ "Could not verify #{value[:host]}: #{value[:error]}"
+ end
+ end
+ end
+end
diff --git a/lib/puppet/face/certificate.rb b/lib/puppet/face/certificate.rb
index 9a306da37..8019b6bea 100644
--- a/lib/puppet/face/certificate.rb
+++ b/lib/puppet/face/certificate.rb
@@ -6,7 +6,7 @@ Puppet::Indirector::Face.define(:certificate, '0.0.1') do
license "Apache 2 license; see COPYING"
summary "Provide access to the CA for certificate management."
- description <<-'EOT'
+ description <<-EOT
This subcommand interacts with a local or remote Puppet certificate
authority. Currently, its behavior is not a full superset of `puppet
cert`; specifically, it is unable to mimic puppet cert's "clean" option,
@@ -15,8 +15,9 @@ Puppet::Indirector::Face.define(:certificate, '0.0.1') do
EOT
option "--ca-location LOCATION" do
+ required
summary "Which certificate authority to use (local or remote)."
- description <<-'EOT'
+ description <<-EOT
Whether to act on the local certificate authority or one provided by a
remote puppet master. Allowed values are 'local' and 'remote.'
@@ -24,6 +25,9 @@ Puppet::Indirector::Face.define(:certificate, '0.0.1') do
EOT
before_action do |action, args, options|
+ unless [:remote, :local, :only].include? options[:ca_location].to_sym
+ raise ArgumentError, "Valid values for ca-location are 'remote', 'local', 'only'."
+ end
Puppet::SSL::Host.ca_location = options[:ca_location].to_sym
end
end
@@ -32,7 +36,7 @@ Puppet::Indirector::Face.define(:certificate, '0.0.1') do
summary "Generate a new certificate signing request."
arguments "<host>"
returns "Nothing."
- description <<-'EOT'
+ description <<-EOT
Generates and submits a certificate signing request (CSR) for the
specified host. This CSR will then have to be signed by a user
with the proper authorization on the certificate authority.
@@ -41,7 +45,7 @@ Puppet::Indirector::Face.define(:certificate, '0.0.1') do
primarily useful for requesting certificates for individual users and
external applications.
EOT
- examples <<-'EOT'
+ examples <<-EOT
Request a certificate for "somenode" from the site's CA:
$ puppet certificate generate somenode.puppetlabs.lan --ca-location remote
@@ -56,7 +60,7 @@ Puppet::Indirector::Face.define(:certificate, '0.0.1') do
action :list do
summary "List all certificate signing requests."
- returns <<-'EOT'
+ returns <<-EOT
An array of #inspect output from CSR objects. This output is
currently messy, but does contain the names of nodes requesting
certificates. This action returns #inspect strings even when used
@@ -73,10 +77,10 @@ Puppet::Indirector::Face.define(:certificate, '0.0.1') do
action :sign do
summary "Sign a certificate signing request for HOST."
arguments "<host>"
- returns <<-'EOT'
+ returns <<-EOT
A string that appears to be (but isn't) an x509 certificate.
EOT
- examples <<-'EOT'
+ examples <<-EOT
Sign somenode.puppetlabs.lan's certificate:
$ puppet certificate sign somenode.puppetlabs.lan --ca-location remote
@@ -93,9 +97,9 @@ Puppet::Indirector::Face.define(:certificate, '0.0.1') do
find = get_action(:find)
find.summary "Retrieve a certificate."
find.arguments "<host>"
- find.returns <<-'EOT'
- An x509 SSL certificate. You will usually want to render this as a
- string (--render-as s).
+ find.render_as = :s
+ find.returns <<-EOT
+ An x509 SSL certificate.
Note that this action has a side effect of caching a copy of the
certificate in Puppet's `ssldir`.
@@ -105,7 +109,7 @@ Puppet::Indirector::Face.define(:certificate, '0.0.1') do
destroy.summary "Delete a certificate."
destroy.arguments "<host>"
destroy.returns "Nothing."
- destroy.description <<-'EOT'
+ destroy.description <<-EOT
Deletes a certificate. This action currently only works on the local CA.
EOT
diff --git a/lib/puppet/face/certificate_request.rb b/lib/puppet/face/certificate_request.rb
index 774821f12..cf342d51a 100644
--- a/lib/puppet/face/certificate_request.rb
+++ b/lib/puppet/face/certificate_request.rb
@@ -5,7 +5,7 @@ Puppet::Indirector::Face.define(:certificate_request, '0.0.1') do
license "Apache 2 license; see COPYING"
summary "Manage certificate requests."
- description <<-'EOT'
+ description <<-EOT
This subcommand retrieves and submits certificate signing requests (CSRs).
EOT
@@ -15,14 +15,12 @@ Puppet::Indirector::Face.define(:certificate_request, '0.0.1') do
find = get_action(:find)
find.summary "Retrieve a single CSR."
find.arguments "<host>"
- find.returns <<-'EOT'
+ find.render_as = :s
+ find.returns <<-EOT
A single certificate request. When used from the Ruby API, returns a
Puppet::SSL::CertificateRequest object.
-
- RENDERING ISSUES: In most cases, you will want to render this as a string
- ('--render-as s').
EOT
- find.examples <<-'EOT'
+ find.examples <<-EOT
Retrieve a single CSR from the puppet master's CA:
$ puppet certificate_request find somenode.puppetlabs.lan --terminus rest
@@ -31,10 +29,10 @@ Puppet::Indirector::Face.define(:certificate_request, '0.0.1') do
search = get_action(:search)
search.summary "Retrieve all outstanding CSRs."
search.arguments "<dummy_text>"
- search.returns <<-'EOT'
- A list of certificate requests; be sure to to render this as a string
- ('--render-as s'). When used from the Ruby API, returns an array of
- Puppet::SSL::CertificateRequest objects.
+ search.render_as = :s
+ search.returns <<-EOT
+ A list of certificate requests. When used from the Ruby API, returns an
+ array of Puppet::SSL::CertificateRequest objects.
EOT
search.short_description <<-EOT
Retrieves all outstanding certificate signing requests. Due to a known bug,
@@ -44,7 +42,7 @@ Puppet::Indirector::Face.define(:certificate_request, '0.0.1') do
Although this action always returns all CSRs, it requires a dummy search
key; this is a known bug.
EOT
- search.examples <<-'EOT'
+ search.examples <<-EOT
Retrieve all CSRs from the local CA (similar to 'puppet cert list'):
$ puppet certificate_request search x --terminus ca
diff --git a/lib/puppet/face/certificate_revocation_list.rb b/lib/puppet/face/certificate_revocation_list.rb
index f58368f75..022323b29 100644
--- a/lib/puppet/face/certificate_revocation_list.rb
+++ b/lib/puppet/face/certificate_revocation_list.rb
@@ -5,7 +5,7 @@ Puppet::Indirector::Face.define(:certificate_revocation_list, '0.0.1') do
license "Apache 2 license; see COPYING"
summary "Manage the list of revoked certificates."
- description <<-'EOT'
+ description <<-EOT
This subcommand is primarily for retrieving the certificate revocation
list from the CA.
EOT
@@ -13,12 +13,10 @@ Puppet::Indirector::Face.define(:certificate_revocation_list, '0.0.1') do
find = get_action(:find)
find.summary "Retrieve the certificate revocation list."
find.arguments "<dummy_text>"
- find.returns <<-'EOT'
+ find.render_as = :s
+ find.returns <<-EOT
The certificate revocation list. When used from the Ruby API: returns an
OpenSSL::X509::CRL object.
-
- RENDERING ISSUES: this should usually be rendered as a string
- ('--render-as s').
EOT
find.short_description <<-EOT
Retrieves the certificate revocation list. Due to a known bug, this action
@@ -28,7 +26,7 @@ Puppet::Indirector::Face.define(:certificate_revocation_list, '0.0.1') do
Although this action always returns the CRL from the specified terminus, it
requires a dummy argument; this is a known bug.
EOT
- find.examples <<-'EXAMPLES'
+ find.examples <<-EXAMPLES
Retrieve a copy of the puppet master's CRL:
$ puppet certificate_revocation_list find crl --terminus rest
@@ -38,7 +36,7 @@ Puppet::Indirector::Face.define(:certificate_revocation_list, '0.0.1') do
destroy.summary "Delete the certificate revocation list."
destroy.arguments "<dummy_text>"
destroy.returns "Nothing."
- destroy.description <<-'EOT'
+ destroy.description <<-EOT
Deletes the certificate revocation list. This cannot be done over REST, but
it is possible to delete the locally cached copy or the local CA's copy of
the CRL.
diff --git a/lib/puppet/face/node/clean.rb b/lib/puppet/face/node/clean.rb
new file mode 100644
index 000000000..d2852de04
--- /dev/null
+++ b/lib/puppet/face/node/clean.rb
@@ -0,0 +1,154 @@
+Puppet::Face.define(:node, '0.0.1') do
+ action(:clean) do
+ option "--[no-]unexport" do
+ summary "Unexport exported resources"
+ end
+
+ summary "Clean up everything a puppetmaster knows about a node"
+ arguments "<host1> [<host2> ...]"
+ description <<-EOT
+ This includes
+
+ * Signed certificates ($vardir/ssl/ca/signed/node.domain.pem)
+ * Cached facts ($vardir/yaml/facts/node.domain.yaml)
+ * Cached node stuff ($vardir/yaml/node/node.domain.yaml)
+ * Reports ($vardir/reports/node.domain)
+ * Stored configs: it can either remove all data from an host in your
+ storedconfig database, or with --unexport turn every exported resource
+ supporting ensure to absent so that any other host checking out their
+ config can remove those exported configurations.
+
+ This will unexport exported resources of a
+ host, so that consumers of these resources can remove the exported
+ resources and we will safely remove the node from our
+ infrastructure.
+ EOT
+
+ when_invoked do |*args|
+ nodes = args[0..-2]
+ options = args.last
+ raise "At least one node should be passed" if nodes.empty? || nodes == options
+
+ # TODO: this is a hack and should be removed if faces provide the proper
+ # infrastructure to set the run mode.
+ require 'puppet/util/run_mode'
+ $puppet_application_mode = Puppet::Util::RunMode[:master]
+
+ if Puppet::SSL::CertificateAuthority.ca?
+ Puppet::SSL::Host.ca_location = :local
+ else
+ Puppet::SSL::Host.ca_location = :none
+ end
+
+ Puppet::Node::Facts.indirection.terminus_class = :yaml
+ Puppet::Node::Facts.indirection.cache_class = :yaml
+ Puppet::Node.indirection.terminus_class = :yaml
+ Puppet::Node.indirection.cache_class = :yaml
+
+ nodes.each { |node| cleanup(node.downcase, options[:unexport]) }
+ end
+ end
+
+ def cleanup(node, unexport)
+ clean_cert(node)
+ clean_cached_facts(node)
+ clean_cached_node(node)
+ clean_reports(node)
+
+ # This is roughly functional, but seems to introduce order-dependent test
+ # failures; this can be re-added when those issues are resolved.
+ # clean_storeconfigs(node, unexport)
+ end
+
+ # clean signed cert for +host+
+ def clean_cert(node)
+ if Puppet::SSL::CertificateAuthority.ca?
+ Puppet::Face[:ca, :current].revoke(node)
+ Puppet::Face[:ca, :current].destroy(node)
+ Puppet.info "#{node} certificates removed from ca"
+ else
+ Puppet.info "Not managing #{node} certs as this host is not a CA"
+ end
+ end
+
+ # clean facts for +host+
+ def clean_cached_facts(node)
+ Puppet::Node::Facts.indirection.destroy(node)
+ Puppet.info "#{node}'s facts removed"
+ end
+
+ # clean cached node +host+
+ def clean_cached_node(node)
+ Puppet::Node.indirection.destroy(node)
+ Puppet.info "#{node}'s cached node removed"
+ end
+
+ # clean node reports for +host+
+ def clean_reports(node)
+ Puppet::Transaction::Report.indirection.destroy(node)
+ Puppet.info "#{node}'s reports removed"
+ end
+
+ # clean storeconfig for +node+
+ def clean_storeconfigs(node, do_unexport=false)
+ return unless Puppet[:storeconfigs] && Puppet.features.rails?
+ require 'puppet/rails'
+ Puppet::Rails.connect
+ unless rails_node = Puppet::Rails::Host.find_by_name(node)
+ Puppet.notice "No entries found for #{node} in storedconfigs."
+ return
+ end
+
+ if do_unexport
+ unexport(rails_node)
+ Puppet.notice "Force #{node}'s exported resources to absent"
+ Puppet.warning "Please wait until all other hosts have checked out their configuration before finishing the cleanup with:"
+ Puppet.warning "$ puppet node clean #{node}"
+ else
+ rails_node.destroy
+ Puppet.notice "#{node} storeconfigs removed"
+ end
+ end
+
+ def unexport(node)
+ # fetch all exported resource
+ query = {:include => {:param_values => :param_name}}
+ query[:conditions] = [ "exported=? AND host_id=?", true, node.id ]
+ Puppet::Rails::Resource.find(:all, query).each do |resource|
+ if type_is_ensurable(resource)
+ line = 0
+ param_name = Puppet::Rails::ParamName.find_or_create_by_name("ensure")
+
+ if ensure_param = resource.param_values.find(
+ :first,
+ :conditions => [ 'param_name_id = ?', param_name.id ]
+ )
+ line = ensure_param.line.to_i
+ Puppet::Rails::ParamValue.delete(ensure_param.id);
+ end
+
+ # force ensure parameter to "absent"
+ resource.param_values.create(
+ :value => "absent",
+ :line => line,
+ :param_name => param_name
+ )
+ Puppet.info("#{resource.name} has been marked as \"absent\"")
+ end
+ end
+ end
+
+ def environment
+ @environment ||= Puppet::Node::Environment.new
+ end
+
+ def type_is_ensurable(resource)
+ if (type = Puppet::Type.type(resource.restype)) && type.validattr?(:ensure)
+ return true
+ else
+ type = environment.known_resource_types.find_definition('', resource.restype)
+ return true if type && type.arguments.keys.include?('ensure')
+ end
+ return false
+ end
+end
diff --git a/lib/puppet/face/status.rb b/lib/puppet/face/status.rb
index bdb0c4d26..e8c87e98d 100644
--- a/lib/puppet/face/status.rb
+++ b/lib/puppet/face/status.rb
@@ -12,6 +12,7 @@ Puppet::Indirector::Face.define(:status, '0.0.1') do
get_action(:search).summary "Invalid for this subcommand."
find = get_action(:find)
+ find.default = true
find.summary "Check status of puppet master server."
find.arguments "<dummy_text>"
find.returns <<-'EOT'
diff --git a/lib/puppet/file_serving/configuration/parser.rb b/lib/puppet/file_serving/configuration/parser.rb
index 334201d37..83b75e28f 100644
--- a/lib/puppet/file_serving/configuration/parser.rb
+++ b/lib/puppet/file_serving/configuration/parser.rb
@@ -24,9 +24,10 @@ class Puppet::FileServing::Configuration::Parser < Puppet::Util::LoadedFile
when /^\s*$/; next # skip blank lines
when /\[([-\w]+)\]/
mount = newmount($1)
- when /^\s*(\w+)\s+(.+)$/
+ when /^\s*(\w+)\s+(.+?)(\s*#.*)?$/
var = $1
value = $2
+ value.strip!
raise(ArgumentError, "Fileserver configuration file does not use '=' as a separator") if value =~ /^=/
case var
when "path"
@@ -58,12 +59,8 @@ class Puppet::FileServing::Configuration::Parser < Puppet::Util::LoadedFile
begin
mount.info "allowing #{val} access"
mount.allow(val)
- rescue AuthStoreError => detail
-
- raise ArgumentError.new(
- detail.to_s,
-
- @count, file)
+ rescue Puppet::AuthStoreError => detail
+ raise ArgumentError.new(detail.to_s, @count, file)
end
}
end
@@ -75,12 +72,8 @@ class Puppet::FileServing::Configuration::Parser < Puppet::Util::LoadedFile
begin
mount.info "denying #{val} access"
mount.deny(val)
- rescue AuthStoreError => detail
-
- raise ArgumentError.new(
- detail.to_s,
-
- @count, file)
+ rescue Puppet::AuthStoreError => detail
+ raise ArgumentError.new(detail.to_s, @count, file)
end
}
end
diff --git a/lib/puppet/indirector/face.rb b/lib/puppet/indirector/face.rb
index ead3f4b46..adb6b688b 100644
--- a/lib/puppet/indirector/face.rb
+++ b/lib/puppet/indirector/face.rb
@@ -48,16 +48,26 @@ class Puppet::Indirector::Face < Puppet::Face
return result
end
+ option "--extra HASH" do
+ summary "Extra arguments to pass to the indirection request"
+ description <<-end
+ A terminus can take additional arguments to refine the operation, which
+ are passed as an arbitrary hash to the back-end. Anything passed as
+ the extra value is just send direct to the back-end.
+ end
+ default_to do Hash.new end
+ end
+
action :destroy do
summary "Delete an object."
arguments "<key>"
- when_invoked { |key, options| call_indirection_method(:destroy, key, options) }
+ when_invoked {|key, options| call_indirection_method :destroy, key, options[:extra] }
end
action :find do
summary "Retrieve an object by name."
arguments "<key>"
- when_invoked { |key, options| call_indirection_method(:find, key, options) }
+ when_invoked {|key, options| call_indirection_method :find, key, options[:extra] }
end
action :save do
@@ -68,13 +78,13 @@ class Puppet::Indirector::Face < Puppet::Face
currently accept data from STDIN, save actions cannot currently be invoked
from the command line.
EOT
- when_invoked { |key, options| call_indirection_method(:save, key, options) }
+ when_invoked {|key, options| call_indirection_method :save, key, options[:extra] }
end
action :search do
summary "Search for an object or retrieve multiple objects."
arguments "<query>"
- when_invoked { |key, options| call_indirection_method(:search, key, options) }
+ when_invoked {|key, options| call_indirection_method :search, key, options[:extra] }
end
# Print the configuration for the current terminus class
@@ -86,11 +96,11 @@ class Puppet::Indirector::Face < Puppet::Face
run mode with the '--mode' option.
EOT
- when_invoked do |*args|
+ when_invoked do |options|
if t = indirection.terminus_class
- puts "Run mode '#{Puppet.run_mode.name}': #{t}"
+ "Run mode '#{Puppet.run_mode.name}': #{t}"
else
- $stderr.puts "No default terminus class for run mode '#{Puppet.run_mode.name}'"
+ "No default terminus class for run mode '#{Puppet.run_mode.name}'"
end
end
end
diff --git a/lib/puppet/indirector/report/processor.rb b/lib/puppet/indirector/report/processor.rb
index 88fe4b487..7bdadcb36 100644
--- a/lib/puppet/indirector/report/processor.rb
+++ b/lib/puppet/indirector/report/processor.rb
@@ -14,28 +14,30 @@ class Puppet::Transaction::Report::Processor < Puppet::Indirector::Code
process(request.instance)
end
+ def destroy(request)
+ processors do |mod|
+ mod.destroy(request.key) if mod.respond_to?(:destroy)
+ end
+ end
+
private
# Process the report with each of the configured report types.
# LAK:NOTE This isn't necessarily the best design, but it's backward
# compatible and that's good enough for now.
def process(report)
- return if Puppet[:reports] == "none"
-
- reports.each do |name|
- if mod = Puppet::Reports.report(name)
- # We have to use a dup because we're including a module in the
- # report.
- newrep = report.dup
- begin
- newrep.extend(mod)
- newrep.process
- rescue => detail
- puts detail.backtrace if Puppet[:trace]
- Puppet.err "Report #{name} failed: #{detail}"
- end
- else
- Puppet.warning "No report named '#{name}'"
+ Puppet.debug "Recieved report to process from #{report.host}"
+ processors do |mod|
+ Puppet.debug "Processing report from #{report.host} with processor #{mod}"
+ # We have to use a dup because we're including a module in the
+ # report.
+ newrep = report.dup
+ begin
+ newrep.extend(mod)
+ newrep.process
+ rescue => detail
+ puts detail.backtrace if Puppet[:trace]
+ Puppet.err "Report #{name} failed: #{detail}"
end
end
end
@@ -45,4 +47,15 @@ class Puppet::Transaction::Report::Processor < Puppet::Indirector::Code
# LAK:NOTE See http://snurl.com/21zf8 [groups_google_com]
x = Puppet[:reports].gsub(/(^\s+)|(\s+$)/, '').split(/\s*,\s*/)
end
+
+ def processors(&blk)
+ return if Puppet[:reports] == "none"
+ reports.each do |name|
+ if mod = Puppet::Reports.report(name)
+ yield(mod)
+ else
+ Puppet.warning "No report named '#{name}'"
+ end
+ end
+ end
end
diff --git a/lib/puppet/indirector/rest.rb b/lib/puppet/indirector/rest.rb
index 8018fe8e3..19daff51d 100644
--- a/lib/puppet/indirector/rest.rb
+++ b/lib/puppet/indirector/rest.rb
@@ -93,7 +93,9 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus
http_connection.send(method, *args)
rescue OpenSSL::SSL::SSLError => error
- if error.message.include? "hostname was not match"
+ if error.message.include? "certificate verify failed"
+ raise Puppet::Error, "#{error.message}. This is often because the time is out of sync on the server or client"
+ elsif error.message.include? "hostname was not match"
raise unless cert = peer_certs.find { |c| c.name !~ /^puppet ca/i }
valid_certnames = [cert.name, *cert.alternate_names].uniq
diff --git a/lib/puppet/indirector/yaml.rb b/lib/puppet/indirector/yaml.rb
index 23997e97a..7b12d25e2 100644
--- a/lib/puppet/indirector/yaml.rb
+++ b/lib/puppet/indirector/yaml.rb
@@ -47,6 +47,11 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus
File.join(base, self.class.indirection_name.to_s, name.to_s + ext)
end
+ def destroy(request)
+ file_path = path(request.key)
+ File.unlink(file_path) if File.exists?(file_path)
+ end
+
def search(request)
Dir.glob(path(request.key,'')).collect do |file|
YAML.load_file(file)
diff --git a/lib/puppet/interface.rb b/lib/puppet/interface.rb
index 6be8b6930..eba99d6be 100644
--- a/lib/puppet/interface.rb
+++ b/lib/puppet/interface.rb
@@ -2,6 +2,7 @@ require 'puppet'
require 'puppet/util/autoload'
require 'puppet/interface/documentation'
require 'prettyprint'
+require 'semver'
class Puppet::Interface
include FullDocs
@@ -63,6 +64,10 @@ class Puppet::Interface
end
face
end
+
+ def find_action(name, action, version = :current)
+ Puppet::Interface::FaceCollection.get_action_for_face(name, action, version)
+ end
end
def set_default_format(format)
@@ -84,12 +89,12 @@ class Puppet::Interface
attr_reader :name, :version
def initialize(name, version, &block)
- unless Puppet::Interface::FaceCollection.validate_version(version)
+ unless SemVer.valid?(version)
raise ArgumentError, "Cannot create face #{name.inspect} with invalid version number '#{version}'!"
end
@name = Puppet::Interface::FaceCollection.underscorize(name)
- @version = version
+ @version = SemVer.new(version)
# The few bits of documentation we actually demand. The default license
# is a favour to our end users; if you happen to get that in a core face
diff --git a/lib/puppet/interface/action.rb b/lib/puppet/interface/action.rb
index 185302b07..60ddb2ca3 100644
--- a/lib/puppet/interface/action.rb
+++ b/lib/puppet/interface/action.rb
@@ -38,6 +38,7 @@ class Puppet::Interface::Action
def to_s() "#{@face}##{@name}" end
attr_reader :name
+ attr_reader :face
attr_accessor :default
def default?
!!@default
@@ -195,14 +196,12 @@ class Puppet::Interface::Action
wrapper = <<WRAPPER
def #{@name}(#{decl.join(", ")})
#{optn}
- args = #{args}
- options = args.last
-
- action = get_action(#{name.inspect})
- action.validate_args(args)
- __invoke_decorations(:before, action, args, options)
+ args = #{args}
+ action = get_action(#{name.inspect})
+ args << action.validate_and_clean(args.pop)
+ __invoke_decorations(:before, action, args, args.last)
rval = self.__send__(#{internal_name.inspect}, *args)
- __invoke_decorations(:after, action, args, options)
+ __invoke_decorations(:after, action, args, args.last)
return rval
end
WRAPPER
@@ -227,8 +226,9 @@ WRAPPER
end
end
+ @options << option.name
+
option.aliases.each do |name|
- @options << name
@options_hash[name] = option
end
@@ -251,27 +251,61 @@ WRAPPER
option
end
- def validate_args(args)
- # Check for multiple aliases for the same option...
- args.last.keys.each do |name|
- # #7290: If this isn't actually an option, ignore it for now. We should
- # probably fail, but that wasn't our API, and I don't want to perturb
- # behaviour this late in the RC cycle. --daniel 2011-04-29
+ def validate_and_clean(original)
+ # The final set of arguments; effectively a hand-rolled shallow copy of
+ # the original, which protects the caller from the surprises they might
+ # get if they passed us a hash and we mutated it...
+ result = {}
+
+ # Check for multiple aliases for the same option, and canonicalize the
+ # name of the argument while we are about it.
+ overlap = Hash.new do |h, k| h[k] = [] end
+ unknown = []
+ original.keys.each do |name|
if option = get_option(name) then
- overlap = (option.aliases & args.last.keys)
- unless overlap.length == 1 then
- raise ArgumentError, "Multiple aliases for the same option passed: #{overlap.join(', ')}"
+ canonical = option.name
+ if result.has_key? canonical
+ overlap[canonical] << name
+ else
+ result[canonical] = original[name]
end
+ elsif Puppet.settings.include? name
+ result[name] = original[name]
+ else
+ unknown << name
+ end
+ end
+
+ unless overlap.empty?
+ msg = overlap.map {|k, v| "(#{k}, #{v.sort.join(', ')})" }.join(", ")
+ raise ArgumentError, "Multiple aliases for the same option passed: #{msg}"
+ end
+
+ unless unknown.empty?
+ msg = unknown.sort.join(", ")
+ raise ArgumentError, "Unknown options passed: #{msg}"
+ end
+
+ # Inject default arguments and check for missing mandating options.
+ missing = []
+ options.map {|x| get_option(x) }.each do |option|
+ name = option.name
+ next if result.has_key? name
+
+ if option.has_default?
+ result[name] = option.default
+ elsif option.required?
+ missing << name
end
end
- # Check for missing mandatory options.
- required = options.map do |name|
- get_option(name)
- end.select(&:required?).collect(&:name) - args.last.keys
+ unless missing.empty?
+ msg = missing.sort.join(', ')
+ raise ArgumentError, "The following options are required: #{msg}"
+ end
- return if required.empty?
- raise ArgumentError, "The following options are required: #{required.join(', ')}"
+ # All done.
+ return result
end
########################################################################
diff --git a/lib/puppet/interface/action_manager.rb b/lib/puppet/interface/action_manager.rb
index fbf588d7d..5c9af4f96 100644
--- a/lib/puppet/interface/action_manager.rb
+++ b/lib/puppet/interface/action_manager.rb
@@ -7,13 +7,14 @@ module Puppet::Interface::ActionManager
require 'puppet/interface/action_builder'
@actions ||= {}
- @default_action ||= nil
raise "Action #{name} already defined for #{self}" if action?(name)
+
action = Puppet::Interface::ActionBuilder.build(self, name, &block)
- if action.default
- raise "Actions #{@default_action.name} and #{name} cannot both be default" if @default_action
- @default_action = action
+
+ if action.default and current = get_default_action
+ raise "Actions #{current.name} and #{name} cannot both be default"
end
+
@actions[action.name] = action
end
@@ -61,7 +62,11 @@ module Puppet::Interface::ActionManager
end
def get_default_action
- @default_action
+ default = actions.map {|x| get_action(x) }.select {|x| x.default }
+ if default.length > 1
+ raise "The actions #{default.map(&:name).join(", ")} cannot all be default"
+ end
+ default.first
end
def action?(name)
diff --git a/lib/puppet/interface/face_collection.rb b/lib/puppet/interface/face_collection.rb
index 12d3c56b1..b1f6ba398 100644
--- a/lib/puppet/interface/face_collection.rb
+++ b/lib/puppet/interface/face_collection.rb
@@ -1,8 +1,6 @@
require 'puppet/interface'
module Puppet::Interface::FaceCollection
- SEMVER_VERSION = /^(\d+)\.(\d+)\.(\d+)([A-Za-z][0-9A-Za-z-]*|)$/
-
@faces = Hash.new { |hash, key| hash[key] = {} }
def self.faces
@@ -17,55 +15,36 @@ module Puppet::Interface::FaceCollection
@faces.keys.select {|name| @faces[name].length > 0 }
end
- def self.validate_version(version)
- !!(SEMVER_VERSION =~ version.to_s)
- end
-
- def self.semver_to_array(v)
- parts = SEMVER_VERSION.match(v).to_a[1..4]
- parts[0..2] = parts[0..2].map { |e| e.to_i }
- parts
- end
-
- def self.cmp_semver(a, b)
- a, b = [a, b].map do |x| semver_to_array(x) end
-
- cmp = a[0..2] <=> b[0..2]
- if cmp == 0
- cmp = a[3] <=> b[3]
- cmp = +1 if a[3].empty? && !b[3].empty?
- cmp = -1 if b[3].empty? && !a[3].empty?
- end
- cmp
+ def self.[](name, version)
+ name = underscorize(name)
+ get_face(name, version) or load_face(name, version)
end
- def self.prefix_match?(desired, target)
- # Can't meaningfully do a prefix match with current on either side.
- return false if desired == :current
- return false if target == :current
-
- # REVISIT: Should probably fail if the matcher is not valid.
- prefix = desired.split('.').map {|x| x =~ /^\d+$/ and x.to_i }
- have = semver_to_array(target)
+ def self.get_action_for_face(name, action_name, version)
+ name = underscorize(name)
- while want = prefix.shift do
- return false unless want == have.shift
+ # If the version they request specifically doesn't exist, don't search
+ # elsewhere. Usually this will start from :current and all...
+ return nil unless face = self[name, version]
+ unless action = face.get_action(action_name)
+ # ...we need to search for it bound to an o{lder,ther} version. Since
+ # we load all actions when the face is first references, this will be in
+ # memory in the known set of versions of the face.
+ (@faces[name].keys - [ :current ]).sort.reverse.each do |version|
+ break if action = @faces[name][version].get_action(action_name)
+ end
end
- return true
- end
- def self.[](name, version)
- name = underscorize(name)
- get_face(name, version) or load_face(name, version)
+ return action
end
# get face from memory, without loading.
- def self.get_face(name, desired_version)
+ def self.get_face(name, pattern)
return nil unless @faces.has_key? name
+ return @faces[name][:current] if pattern == :current
- return @faces[name][:current] if desired_version == :current
-
- found = @faces[name].keys.select {|v| prefix_match?(desired_version, v) }.sort.last
+ versions = @faces[name].keys - [ :current ]
+ found = SemVer.find_matching(pattern, versions)
return @faces[name][found]
end
@@ -77,9 +56,7 @@ module Puppet::Interface::FaceCollection
#
# We use require to avoid executing the code multiple times, like any
# other Ruby library that we might want to use. --daniel 2011-04-06
- begin
- require "puppet/face/#{name}"
-
+ if safely_require name then
# If we wanted :current, we need to index to find that; direct version
# requests just work™ as they go. --daniel 2011-04-06
if version == :current then
@@ -108,21 +85,35 @@ module Puppet::Interface::FaceCollection
# versions here and return the last item in that set.
#
# --daniel 2011-04-06
- latest_ver = @faces[name].keys.sort {|a, b| cmp_semver(a, b) }.last
+ latest_ver = @faces[name].keys.sort.last
@faces[name][:current] = @faces[name][latest_ver]
end
- rescue LoadError => e
- raise unless e.message =~ %r{-- puppet/face/#{name}$}
- # ...guess we didn't find the file; return a much better problem.
- rescue SyntaxError => e
- raise unless e.message =~ %r{puppet/face/#{name}\.rb:\d+: }
- Puppet.err "Failed to load face #{name}:\n#{e}"
- # ...but we just carry on after complaining.
+ end
+
+ unless version == :current or get_face(name, version)
+ # Try an obsolete version of the face, if needed, to see if that helps?
+ safely_require name, version
end
return get_face(name, version)
end
+ def self.safely_require(name, version = nil)
+ path = File.join 'puppet' ,'face', version.to_s, name.to_s
+ require path
+ true
+
+ rescue LoadError => e
+ raise unless e.message =~ %r{-- #{path}$}
+ # ...guess we didn't find the file; return a much better problem.
+ nil
+ rescue SyntaxError => e
+ raise unless e.message =~ %r{#{path}\.rb:\d+: }
+ Puppet.err "Failed to load face #{name}:\n#{e}"
+ # ...but we just carry on after complaining.
+ nil
+ end
+
def self.register(face)
@faces[underscorize(face.name)][face.version] = face
end
diff --git a/lib/puppet/interface/option.rb b/lib/puppet/interface/option.rb
index 3cd930acf..01f6f2307 100644
--- a/lib/puppet/interface/option.rb
+++ b/lib/puppet/interface/option.rb
@@ -6,6 +6,7 @@ class Puppet::Interface::Option
def initialize(parent, *declaration, &block)
@parent = parent
@optparse = []
+ @default = nil
# Collect and sort the arguments in the declaration.
dups = {}
@@ -81,8 +82,26 @@ class Puppet::Interface::Option
!!@required
end
+ def has_default?
+ !!@default
+ end
+
+ def default=(proc)
+ required and raise ArgumentError, "#{self} can't be optional and have a default value"
+ proc.is_a? Proc or raise ArgumentError, "default value for #{self} is a #{proc.class.name.inspect}, not a proc"
+ @default = proc
+ end
+
+ def default
+ @default and @default.call
+ end
+
attr_reader :parent, :name, :aliases, :optparse
attr_accessor :required
+ def required=(value)
+ has_default? and raise ArgumentError, "#{self} can't be optional and have a default value"
+ @required = value
+ end
attr_accessor :before_action
def before_action=(proc)
diff --git a/lib/puppet/interface/option_builder.rb b/lib/puppet/interface/option_builder.rb
index 5676ec977..c87adc2c0 100644
--- a/lib/puppet/interface/option_builder.rb
+++ b/lib/puppet/interface/option_builder.rb
@@ -51,4 +51,17 @@ class Puppet::Interface::OptionBuilder
def required(value = true)
@option.required = value
end
+
+ def default_to(&block)
+ block or raise ArgumentError, "#{@option} default_to requires a block"
+ if @option.has_default?
+ raise ArgumentError, "#{@option} already has a default value"
+ end
+ # Ruby 1.8 treats a block without arguments as accepting any number; 1.9
+ # gets this right, so we work around it for now... --daniel 2011-07-20
+ unless block.arity == 0 or (RUBY_VERSION =~ /^1\.8/ and block.arity == -1)
+ raise ArgumentError, "#{@option} default_to block should not take any arguments"
+ end
+ @option.default = block
+ end
end
diff --git a/lib/puppet/interface/option_manager.rb b/lib/puppet/interface/option_manager.rb
index 326a91d92..a1f300e8e 100644
--- a/lib/puppet/interface/option_manager.rb
+++ b/lib/puppet/interface/option_manager.rb
@@ -26,8 +26,9 @@ module Puppet::Interface::OptionManager
end
end
+ @options << option.name
+
option.aliases.each do |name|
- @options << name
@options_hash[name] = option
end
diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb
index 059591ed8..00468df96 100644
--- a/lib/puppet/module.rb
+++ b/lib/puppet/module.rb
@@ -42,7 +42,10 @@ class Puppet::Module
def has_metadata?
return false unless metadata_file
- FileTest.exist?(metadata_file)
+ return false unless FileTest.exist?(metadata_file)
+
+ metadata = PSON.parse File.read(metadata_file)
+ return metadata.is_a?(Hash) && !metadata.keys.empty?
end
def initialize(name, environment = nil)
diff --git a/lib/puppet/network/authconfig.rb b/lib/puppet/network/authconfig.rb
index 4ba89fa71..1e486a2f9 100644
--- a/lib/puppet/network/authconfig.rb
+++ b/lib/puppet/network/authconfig.rb
@@ -102,7 +102,7 @@ module Puppet
name = $3 if $2 == "path"
name.chomp!
right = newrights.newright(name, count, @file)
- when /^\s*(allow|deny|method|environment|auth(?:enticated)?)\s+(.+)$/
+ when /^\s*(allow|deny|method|environment|auth(?:enticated)?)\s+(.+?)(\s*#.*)?$/
parse_right_directive(right, $1, $2, count)
else
raise ConfigurationError, "Invalid line #{count}: #{line}"
@@ -130,6 +130,7 @@ module Puppet
end
def parse_right_directive(right, var, value, count)
+ value.strip!
case var
when "allow"
modify_right(right, :allow, value, "allowing %s access", count)
@@ -159,6 +160,7 @@ module Puppet
def modify_right(right, method, value, msg, count)
value.split(/\s*,\s*/).each do |val|
begin
+ val.strip!
right.info msg % val
right.send(method, val)
rescue AuthStoreError => detail
diff --git a/lib/puppet/network/handler/fileserver.rb b/lib/puppet/network/handler/fileserver.rb
index 5b4b17a32..5da4cedef 100755
--- a/lib/puppet/network/handler/fileserver.rb
+++ b/lib/puppet/network/handler/fileserver.rb
@@ -269,6 +269,7 @@ class Puppet::Network::Handler
value = $2
case var
when "path"
+ raise FileServerError.new("No mount specified for argument #{var} #{value}") unless mount
if mount.name == MODULES
Puppet.warning "The '#{mount.name}' module can not have a path. Ignoring attempt to set it"
else
@@ -280,6 +281,7 @@ class Puppet::Network::Handler
end
end
when "allow"
+ raise FileServerError.new("No mount specified for argument #{var} #{value}") unless mount
value.split(/\s*,\s*/).each { |val|
begin
mount.info "allowing #{val} access"
@@ -294,6 +296,7 @@ class Puppet::Network::Handler
end
}
when "deny"
+ raise FileServerError.new("No mount specified for argument #{var} #{value}") unless mount
value.split(/\s*,\s*/).each { |val|
begin
mount.info "denying #{val} access"
diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb
index 54bcf30c2..52aec1bf1 100644
--- a/lib/puppet/network/http/webrick.rb
+++ b/lib/puppet/network/http/webrick.rb
@@ -40,7 +40,7 @@ class Puppet::Network::HTTP::WEBrick
@listening = true
@thread = Thread.new {
@server.start { |sock|
- raise "Client disconnected before connection could be established" unless IO.select([sock],nil,nil,0.1)
+ raise "Client disconnected before connection could be established" unless IO.select([sock],nil,nil,6.2)
sock.accept
@server.run(sock)
}
diff --git a/lib/puppet/network/rest_authconfig.rb b/lib/puppet/network/rest_authconfig.rb
index dfe8f85c4..7dcc81ef4 100644
--- a/lib/puppet/network/rest_authconfig.rb
+++ b/lib/puppet/network/rest_authconfig.rb
@@ -29,10 +29,15 @@ module Puppet
@main
end
+ def allowed?(request)
+ Puppet.deprecation_warning "allowed? should not be called for REST authorization - use check_authorization instead"
+ check_authorization(request)
+ end
+
# check wether this request is allowed in our ACL
# raise an Puppet::Network::AuthorizedError if the request
# is denied.
- def allowed?(indirection, method, key, params)
+ def check_authorization(indirection, method, key, params)
read
# we're splitting the request in part because
diff --git a/lib/puppet/network/rest_authorization.rb b/lib/puppet/network/rest_authorization.rb
index 50f094e3e..d636d486a 100644
--- a/lib/puppet/network/rest_authorization.rb
+++ b/lib/puppet/network/rest_authorization.rb
@@ -16,7 +16,7 @@ module Puppet::Network
# Verify that our client has access.
def check_authorization(indirection, method, key, params)
- authconfig.allowed?(indirection, method, key, params)
+ authconfig.check_authorization(indirection, method, key, params)
end
end
end
diff --git a/lib/puppet/parser/functions/create_resources.rb b/lib/puppet/parser/functions/create_resources.rb
index 430f110b4..3b8bb3543 100644
--- a/lib/puppet/parser/functions/create_resources.rb
+++ b/lib/puppet/parser/functions/create_resources.rb
@@ -27,15 +27,16 @@ Takes two parameters:
args[1].each do |title, params|
raise ArgumentError, 'params should not contain title' if(params['title'])
case type_of_resource
- when :type
- res = resource.hash2resource(params.merge(:title => title))
- catalog.add_resource(res)
- when :define
+ # JJM The only difference between a type and a define is the call to instantiate_resource
+ # for a defined type.
+ when :type, :define
p_resource = Puppet::Parser::Resource.new(type_name, title, :scope => self, :source => resource)
params.merge(:name => title).each do |k,v|
p_resource.set_parameter(k,v)
end
- resource.instantiate_resource(self, p_resource)
+ if type_of_resource == :define then
+ resource.instantiate_resource(self, p_resource)
+ end
compiler.add_resource(self, p_resource)
when :class
klass = find_hostclass(title)
diff --git a/lib/puppet/reports/store.rb b/lib/puppet/reports/store.rb
index 625a263b3..997206ec4 100644
--- a/lib/puppet/reports/store.rb
+++ b/lib/puppet/reports/store.rb
@@ -41,5 +41,20 @@ Puppet::Reports.register_report(:store) do
# Only testing cares about the return value
file
end
+
+ # removes all reports for a given host
+ def self.destroy(host)
+ client = host.gsub("..",".")
+ dir = File.join(Puppet[:reportdir], client)
+
+ if File.exists?(dir)
+ Dir.entries(dir).each do |file|
+ next if ['.','..'].include?(file)
+ file = File.join(dir, file)
+ File.unlink(file) if File.file?(file)
+ end
+ Dir.rmdir(dir)
+ end
+ end
end
diff --git a/lib/puppet/resource/catalog.rb b/lib/puppet/resource/catalog.rb
index 8eb4266db..be6302595 100644
--- a/lib/puppet/resource/catalog.rb
+++ b/lib/puppet/resource/catalog.rb
@@ -94,7 +94,7 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph
resource.ref =~ /^(.+)\[/
class_name = $1 || resource.class.name
- newref = [class_name, key]
+ newref = [class_name, key].flatten
if key.is_a? String
ref_string = "#{class_name}[#{key}]"
@@ -107,7 +107,10 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph
# isn't sufficient.
if existing = @resource_table[newref]
return if existing == resource
- raise(ArgumentError, "Cannot alias #{resource.ref} to #{key.inspect}; resource #{newref.inspect} already exists")
+ resource_definition = " at #{resource.file}:#{resource.line}" if resource.file and resource.line
+ existing_definition = " at #{existing.file}:#{existing.line}" if existing.file and existing.line
+ msg = "Cannot alias #{resource.ref} to #{key.inspect}#{resource_definition}; resource #{newref.inspect} already defined#{existing_definition}"
+ raise ArgumentError, msg
end
@resource_table[newref] = resource
@aliases[resource.ref] ||= []
@@ -436,7 +439,7 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph
res = Puppet::Resource.new(nil, type)
end
title_key = [res.type, res.title.to_s]
- uniqueness_key = [res.type, res.uniqueness_key]
+ uniqueness_key = [res.type, res.uniqueness_key].flatten
@resource_table[title_key] || @resource_table[uniqueness_key]
end
diff --git a/lib/puppet/ssl/inventory.rb b/lib/puppet/ssl/inventory.rb
index e094da100..c210fdc35 100644
--- a/lib/puppet/ssl/inventory.rb
+++ b/lib/puppet/ssl/inventory.rb
@@ -48,5 +48,7 @@ class Puppet::SSL::Inventory
return Integer($1)
end
+
+ return nil
end
end
diff --git a/lib/puppet/type/file/source.rb b/lib/puppet/type/file/source.rb
index 76c646baf..67401505d 100755
--- a/lib/puppet/type/file/source.rb
+++ b/lib/puppet/type/file/source.rb
@@ -42,7 +42,7 @@ module Puppet
on the local host, whereas `agent` will connect to the
puppet server that it received the manifest from.
- See the [fileserver configuration documentation](http://projects.puppetlabs.com/projects/puppet/wiki/File_Serving_Configuration) for information on how to configure
+ See the [fileserver configuration documentation](http://docs.puppetlabs.com/guides/file_serving.html) for information on how to configure
and use file services within Puppet.
If you specify multiple file sources for a file, then the first
@@ -154,7 +154,7 @@ module Puppet
fail detail, "Could not retrieve file metadata for #{source}: #{detail}"
end
end
- fail "Could not retrieve information from source(s) #{value.join(", ")}" unless result
+ fail "Could not retrieve information from environment #{Puppet[:environment]} source(s) #{value.join(", ")}" unless result
result
end
diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb
index 572d5796d..c64bf69e8 100755
--- a/lib/puppet/type/user.rb
+++ b/lib/puppet/type/user.rb
@@ -168,6 +168,14 @@ module Puppet
return "changed password"
end
end
+
+ def is_to_s( currentvalue )
+ return '[old password hash redacted]'
+ end
+ def should_to_s( newvalue )
+ return '[new password hash redacted]'
+ end
+
end
newproperty(:password_min_age, :required_features => :manages_password_age) do
diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb
index ce9d4642b..ff09221a2 100644
--- a/lib/puppet/util.rb
+++ b/lib/puppet/util.rb
@@ -30,7 +30,6 @@ module Util
end
end
-
def self.synchronize_on(x,type)
sync_object,users = 0,1
begin
diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb
index f243b8691..4559e9af3 100644
--- a/lib/puppet/util/settings.rb
+++ b/lib/puppet/util/settings.rb
@@ -721,7 +721,7 @@ if @config.include?(:run_mode)
end
Puppet::Util::SUIDManager.asuser(*chown) do
- mode = obj.mode || 0640
+ mode = obj.mode ? obj.mode.to_i : 0640
args << "w" if args.empty?
args << mode