From 6a4d291fa72a3f37f88e3c18c97f919830761863 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Tue, 15 Mar 2011 11:22:23 -0700 Subject: (#4884) Get rid of open3 require since it wasn't being used Paired-with: Max Martin --- lib/puppet/type/exec.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb index 5ed2b104c..387c592a6 100755 --- a/lib/puppet/type/exec.rb +++ b/lib/puppet/type/exec.rb @@ -23,10 +23,8 @@ module Puppet you are doing a lot of work with `exec`, please at least notify us at Puppet Labs what you are doing, and hopefully we can work with you to get a native resource type for the work you are doing. - - **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." - require 'open3' + **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." # Create a new check mechanism. It's basically just a parameter that # provides one extra 'check' method. -- cgit From c86a980fe9b2f2e109fe7956a1be2705f3fc2ade Mon Sep 17 00:00:00 2001 From: Daniel Pittman Date: Tue, 15 Mar 2011 11:45:02 -0700 Subject: (#4884) Add consistent path validation and behavior Many path parameters were implementing their own inconsistent validation and behavior. Now those parameters can have a parent class that makes things a lot more consistent. Reviewed-by: Matt Robinson and Max Martin --- lib/puppet/parameter.rb | 2 ++ lib/puppet/parameter/path.rb | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 lib/puppet/parameter/path.rb (limited to 'lib') diff --git a/lib/puppet/parameter.rb b/lib/puppet/parameter.rb index ff7cab22b..29d60fc66 100644 --- a/lib/puppet/parameter.rb +++ b/lib/puppet/parameter.rb @@ -300,3 +300,5 @@ class Puppet::Parameter name.to_s end end + +require 'puppet/parameter/path' diff --git a/lib/puppet/parameter/path.rb b/lib/puppet/parameter/path.rb new file mode 100644 index 000000000..44886afd0 --- /dev/null +++ b/lib/puppet/parameter/path.rb @@ -0,0 +1,42 @@ +require 'puppet/parameter' + +class Puppet::Parameter::Path < Puppet::Parameter + def self.accept_arrays(bool = true) + @accept_arrays = !!bool + end + def self.arrays? + @accept_arrays + end + + def validate_path(paths) + if paths.is_a?(Array) and ! self.class.arrays? then + fail "#{name} only accepts a single path, not an array of paths" + end + + # We *always* support Unix path separators, as Win32 does now too. + absolute = "[/#{::Regexp.quote(::File::SEPARATOR)}]" + win32 = Puppet.features.microsoft_windows? + + Array(paths).each do |path| + next if path =~ %r{^#{absolute}} + next if win32 and path =~ %r{^(?:[a-zA-Z]:)?#{absolute}} + fail("#{name} must be a fully qualified path") + end + + paths + end + + # This will be overridden if someone uses the validate option, which is why + # it just delegates to the other, useful, method. + def unsafe_validate(paths) + validate_path(paths) + end + + # Likewise, this might be overridden, but by default... + def unsafe_munge(paths) + if paths.is_a?(Array) and ! self.class.arrays? then + fail "#{name} only accepts a single path, not an array of paths" + end + paths + end +end -- cgit From fa0cfc60ccf43fcf9b4339b63b748b5f177c1e75 Mon Sep 17 00:00:00 2001 From: Daniel Pittman Date: Tue, 15 Mar 2011 11:50:29 -0700 Subject: (#4884) Break the exec type out to have a posix provider This is in preparation for allowing other new providers to handle exec commands differently. Reviewed-by: Max Martin and Matt Robinson --- lib/puppet/provider/exec/posix.rb | 112 ++++++++++++++++++++++++ lib/puppet/type/exec.rb | 179 +++++--------------------------------- 2 files changed, 135 insertions(+), 156 deletions(-) create mode 100644 lib/puppet/provider/exec/posix.rb (limited to 'lib') diff --git a/lib/puppet/provider/exec/posix.rb b/lib/puppet/provider/exec/posix.rb new file mode 100644 index 000000000..92dbd8c98 --- /dev/null +++ b/lib/puppet/provider/exec/posix.rb @@ -0,0 +1,112 @@ +Puppet::Type.type(:exec).provide :posix do + include Puppet::Util::Execution + + confine :feature => :posix + defaultfor :feature => :posix + + desc "Execute external binaries directly, on POSIX systems. +This does not pass through a shell, or perform any interpolation, but +only directly calls the command with the arguments given." + + def run(command, check = false) + output = nil + status = nil + dir = nil + + checkexe(command) + + if dir = resource[:cwd] + unless File.directory?(dir) + if check + dir = nil + else + self.fail "Working directory '#{dir}' does not exist" + end + end + end + + dir ||= Dir.pwd + + debug "Executing#{check ? " check": ""} '#{command}'" + begin + # Do our chdir + Dir.chdir(dir) do + environment = {} + + environment[:PATH] = resource[:path].join(":") if resource[:path] + + if envlist = resource[:environment] + envlist = [envlist] unless envlist.is_a? Array + envlist.each do |setting| + if setting =~ /^(\w+)=((.|\n)+)$/ + env_name = $1 + value = $2 + if environment.include?(env_name) || environment.include?(env_name.to_sym) + warning "Overriding environment setting '#{env_name}' with '#{value}'" + end + environment[env_name] = value + else + warning "Cannot understand environment setting #{setting.inspect}" + end + end + end + + withenv environment do + Timeout::timeout(resource[:timeout]) do + output, status = Puppet::Util::SUIDManager. + run_and_capture([command], resource[:user], resource[:group]) + end + # The shell returns 127 if the command is missing. + if status.exitstatus == 127 + raise ArgumentError, output + end + end + end + rescue Errno::ENOENT => detail + self.fail detail.to_s + end + + return output, status + end + + # Verify that we have the executable + def checkexe(command) + exe = extractexe(command) + + if resource[:path] + if Puppet.features.posix? and !File.exists?(exe) + withenv :PATH => resource[:path].join(File::PATH_SEPARATOR) do + exe = which(exe) || raise(ArgumentError,"Could not find command '#{exe}'") + end + elsif Puppet.features.microsoft_windows? and !File.exists?(exe) + resource[:path].each do |path| + [".exe", ".ps1", ".bat", ".com", ""].each do |extension| + file = File.join(path, exe+extension) + return if File.exists?(file) + end + end + end + end + + raise ArgumentError, "Could not find command '#{exe}'" unless File.exists?(exe) + unless File.executable?(exe) + raise ArgumentError, + "'#{exe}' is not executable" + end + end + + def extractexe(command) + # easy case: command was quoted + if command =~ /^"([^"]+)"/ + $1 + else + command.split(/ /)[0] + end + end + + def validatecmd(command) + exe = extractexe(command) + # if we're not fully qualified, require a path + self.fail "'#{command}' is not qualified and no path was specified. Please qualify the command or specify a path." if File.expand_path(exe) != exe and resource[:path].nil? + end +end diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb index 387c592a6..67d40a7cc 100755 --- a/lib/puppet/type/exec.rb +++ b/lib/puppet/type/exec.rb @@ -28,10 +28,10 @@ module Puppet # Create a new check mechanism. It's basically just a parameter that # provides one extra 'check' method. - def self.newcheck(name, &block) + def self.newcheck(name, options = {}, &block) @checks ||= {} - check = newparam(name, &block) + check = newparam(name, options, &block) @checks[name] = check end @@ -63,9 +63,11 @@ module Puppet # First verify that all of our checks pass. def retrieve - # Default to somethinng - - if @resource.check + # We need to return :notrun to trigger evaluation; when that isn't + # true, we *LIE* about what happened and return a "success" for the + # value, which causes us to be treated as in_sync?, which means we + # don't actually execute anything. I think. --daniel 2011-03-10 + if @resource.check_all_attributes return :notrun else return self.should @@ -87,7 +89,7 @@ module Puppet tries.times do |try| # Only add debug messages for tries > 1 to reduce log spam. debug("Exec try #{try+1}/#{tries}") if tries > 1 - @output, @status = @resource.run(self.resource[:command]) + @output, @status = provider.run(self.resource[:command]) break if self.should.include?(@status.exitstatus.to_s) if try_sleep > 0 and tries > 1 debug("Sleeping for #{try_sleep} seconds between tries") @@ -137,7 +139,7 @@ module Puppet newparam(:path) do desc "The search path used for command execution. Commands must be fully qualified if no path is specified. Paths - can be specified as an array or as a colon-separated list." + can be specified as an array or as a colon or semi-colon separated list." # Support both arrays and colon-separated fields. def value=(*values) @@ -174,21 +176,9 @@ module Puppet # Validation is handled by the SUIDManager class. end - newparam(:cwd) do + newparam(:cwd, :parent => Puppet::Parameter::Path) do desc "The directory from which to run the command. If this directory does not exist, the command will fail." - - validate do |dir| - unless dir =~ /^#{File::SEPARATOR}/ - self.fail("CWD must be a fully qualified path") - end - end - - munge do |dir| - dir = dir[0] if dir.is_a?(Array) - - dir - end end newparam(:logoutput) do @@ -207,7 +197,7 @@ module Puppet for refreshing." validate do |command| - @resource.validatecmd(command) + provider.validatecmd(command) end end @@ -331,7 +321,7 @@ module Puppet end end - newcheck(:creates) do + newcheck(:creates, :parent => Puppet::Parameter::Path) do desc "A file that this command creates. If this parameter is provided, then the command will only be run if the specified file does not exist: @@ -344,19 +334,7 @@ module Puppet " - # FIXME if they try to set this and fail, then we should probably - # fail the entire exec, right? - validate do |files| - files = [files] unless files.is_a? Array - - files.each do |file| - self.fail("'creates' must be set to a fully qualified path") unless file - - unless file =~ %r{^#{File::SEPARATOR}} - self.fail "'creates' files must be fully qualified." - end - end - end + accept_arrays # If the file exists, return false (i.e., don't run the command), # else return true @@ -384,15 +362,15 @@ module Puppet validate do |cmds| cmds = [cmds] unless cmds.is_a? Array - cmds.each do |cmd| - @resource.validatecmd(cmd) + cmds.each do |command| + provider.validatecmd(command) end end # Return true if the command does not return 0. def check(value) begin - output, status = @resource.run(value, true) + output, status = provider.run(value, true) rescue Timeout::Error err "Check #{value.inspect} exceeded timeout" return false @@ -426,15 +404,15 @@ module Puppet validate do |cmds| cmds = [cmds] unless cmds.is_a? Array - cmds.each do |cmd| - @resource.validatecmd(cmd) + cmds.each do |command| + provider.validatecmd(command) end end # Return true if the command returns 0. def check(value) begin - output, status = @resource.run(value, true) + output, status = provider.run(value, true) rescue Timeout::Error err "Check #{value.inspect} exceeded timeout" return false @@ -448,7 +426,7 @@ module Puppet @isomorphic = false validate do - validatecmd(self[:command]) + provider.validatecmd(self[:command]) end # FIXME exec should autorequire any exec that 'creates' our cwd @@ -501,7 +479,7 @@ module Puppet # Verify that we pass all of the checks. The argument determines whether # we skip the :refreshonly check, which is necessary because we now check # within refresh - def check(refreshing = false) + def check_all_attributes(refreshing = false) self.class.checks.each { |check| next if refreshing and check == :refreshonly if @parameters.include?(check) @@ -516,32 +494,6 @@ module Puppet true end - # Verify that we have the executable - def checkexe(cmd) - exe = extractexe(cmd) - - if self[:path] - if Puppet.features.posix? and !File.exists?(exe) - withenv :PATH => self[:path].join(File::PATH_SEPARATOR) do - exe = which(exe) || raise(ArgumentError,"Could not find command '#{exe}'") - end - elsif Puppet.features.microsoft_windows? and !File.exists?(exe) - self[:path].each do |path| - [".exe", ".ps1", ".bat", ".com", ""].each do |extension| - file = File.join(path, exe+extension) - return if File.exists?(file) - end - end - end - end - - raise ArgumentError, "Could not find executable '#{exe}'" unless FileTest.exists?(exe) - unless FileTest.executable?(exe) - raise ArgumentError, - "'#{exe}' is not executable" - end - end - def output if self.property(:returns).nil? return nil @@ -552,98 +504,13 @@ module Puppet # Run the command, or optionally run a separately-specified command. def refresh - if self.check(true) + if self.check_all_attributes(true) if cmd = self[:refresh] - self.run(cmd) + provider.run(cmd) else self.property(:returns).sync end end end - - # Run a command. - def run(command, check = false) - output = nil - status = nil - - dir = nil - - checkexe(command) - - if dir = self[:cwd] - unless File.directory?(dir) - if check - dir = nil - else - self.fail "Working directory '#{dir}' does not exist" - end - end - end - - dir ||= Dir.pwd - - if check - debug "Executing check '#{command}'" - else - debug "Executing '#{command}'" - end - begin - # Do our chdir - Dir.chdir(dir) do - environment = {} - - environment[:PATH] = self[:path].join(":") if self[:path] - - if envlist = self[:environment] - envlist = [envlist] unless envlist.is_a? Array - envlist.each do |setting| - if setting =~ /^(\w+)=((.|\n)+)$/ - name = $1 - value = $2 - if environment.include? name - warning( - "Overriding environment setting '#{name}' with '#{value}'" - ) - end - environment[name] = value - else - warning "Cannot understand environment setting #{setting.inspect}" - end - end - end - - withenv environment do - Timeout::timeout(self[:timeout]) do - output, status = Puppet::Util::SUIDManager.run_and_capture( - [command], self[:user], self[:group] - ) - end - # The shell returns 127 if the command is missing. - if status.exitstatus == 127 - raise ArgumentError, output - end - end - end - rescue Errno::ENOENT => detail - self.fail detail.to_s - end - - return output, status - end - - def validatecmd(cmd) - exe = extractexe(cmd) - # if we're not fully qualified, require a path - 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? - end - - def extractexe(cmd) - # easy case: command was quoted - if cmd =~ /^"([^"]+)"/ - $1 - else - cmd.split(/ /)[0] - end - end end end -- cgit From 0d2d6f3f005ee99658ff86b79749f0ba7949beab Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Tue, 15 Mar 2011 11:52:10 -0700 Subject: (#4884) Add an shell provider for execs This makes it possible to use shell builtins when the exec is inline bash commands. Paired-with: Max Martin --- lib/puppet/provider/exec/shell.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 lib/puppet/provider/exec/shell.rb (limited to 'lib') diff --git a/lib/puppet/provider/exec/shell.rb b/lib/puppet/provider/exec/shell.rb new file mode 100644 index 000000000..98f309e8f --- /dev/null +++ b/lib/puppet/provider/exec/shell.rb @@ -0,0 +1,17 @@ +Puppet::Type.type(:exec).provide :shell, :parent => :posix do + include Puppet::Util::Execution + + confine :feature => :posix + + desc "Execute external binaries directly, on POSIX systems. +passing through a shell so that shell built ins are available." + + def run(command, check = false) + command = %Q{/bin/sh -c "#{command.gsub(/"/,'\"')}"} + super(command, check) + end + + def validatecmd(command) + true + end +end -- cgit From ec1aa192825f17afbe4dc12be1e0f2d644d74155 Mon Sep 17 00:00:00 2001 From: Max Martin Date: Thu, 17 Mar 2011 12:14:49 -0700 Subject: (#4884) Revise new exec tests, add a few more Revised a few of the new tests for the exec type and provider to ensure that they were testing what they meant to, and added in a couple of new tests. Reviewed-by:Daniel Pittman --- lib/puppet/type/exec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb index 67d40a7cc..4458bf081 100755 --- a/lib/puppet/type/exec.rb +++ b/lib/puppet/type/exec.rb @@ -139,7 +139,7 @@ module Puppet newparam(:path) do desc "The search path used for command execution. Commands must be fully qualified if no path is specified. Paths - can be specified as an array or as a colon or semi-colon separated list." + can be specified as an array or as a colon separated list." # Support both arrays and colon-separated fields. def value=(*values) -- cgit From df20513122354deccd4ee4477a15cb70c5d605c6 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Fri, 18 Mar 2011 16:04:42 -0700 Subject: (#6658) Propagate ENC connection errors to the agent When the master failed to run the External Node Classifier script it would log an error on the master, but the agent didn't get back the full error or the stack trace for it's logs. By raising when there's an ENC script problem on the master, this causes the error messages to propagate to the agent. Paired-with: Jacob Helwig --- lib/puppet/indirector/exec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/puppet/indirector/exec.rb b/lib/puppet/indirector/exec.rb index fa789442b..4683eda0f 100644 --- a/lib/puppet/indirector/exec.rb +++ b/lib/puppet/indirector/exec.rb @@ -35,8 +35,7 @@ class Puppet::Indirector::Exec < Puppet::Indirector::Terminus begin output = execute(external_command) rescue Puppet::ExecutionFailure => detail - Puppet.err "Failed to find #{name} via exec: #{detail}" - return nil + raise Puppet::Error, "Failed to find #{name} via exec: #{detail}" end if output =~ /\A\s*\Z/ # all whitespace -- cgit From 66a4f361502864e8ea6ca3a11181851b7c313664 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Thu, 3 Mar 2011 06:54:19 +1100 Subject: Fixed #6555 - Ruby 1.9.x warning: class variable access from toplevel This came from the use of the @@colormap class variable. The variables has been changed to a constant. --- lib/puppet/util/log/destinations.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/puppet/util/log/destinations.rb b/lib/puppet/util/log/destinations.rb index dd0d996bf..9550e2c3b 100644 --- a/lib/puppet/util/log/destinations.rb +++ b/lib/puppet/util/log/destinations.rb @@ -96,7 +96,7 @@ Puppet::Util::Log.newdesttype :console do HWHITE = {:console => "", :html => "FFFFFF"} RESET = {:console => "", :html => "" } - @@colormap = { + Colormap = { :debug => WHITE, :info => GREEN, :notice => CYAN, @@ -117,11 +117,11 @@ Puppet::Util::Log.newdesttype :console do end def console_color(level, str) - @@colormap[level][:console] + str + RESET[:console] + Colormap[level][:console] + str + RESET[:console] end def html_color(level, str) - %{%s} % [@@colormap[level][:html], str] + %{%s} % [Colormap[level][:html], str] end def initialize -- cgit From 923d613bb5d96b1898a718bc37842531124b9b89 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Thu, 3 Mar 2011 06:57:17 +1100 Subject: Fixed #6555 - Ruby 1.9.x returning Invalid next (SyntaxError) The kick application has the option to ping hosts. On 1.9.x this code was returning "Invalid next". The next in this code has been replaced with an exit. Testing has confirmed this doesn't change the behavior of puppet kick. --- lib/puppet/application/kick.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/puppet/application/kick.rb b/lib/puppet/application/kick.rb index 604132818..da93c0182 100644 --- a/lib/puppet/application/kick.rb +++ b/lib/puppet/application/kick.rb @@ -245,7 +245,7 @@ License out = %x{ping -c 1 #{host}} unless $CHILD_STATUS == 0 $stderr.print "Could not contact #{host}\n" - next + exit($CHILD_STATUS) end end -- cgit From 0844a17f695b7db098f7a9e59da6f4559dc9131e Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Thu, 3 Mar 2011 10:30:02 +1100 Subject: Fixed #6555 - Fixed two more when then colon issues Ruby 1.9.x doesn't support when foo: Replaced in the launchd provider and the Oracle rails ORM --- lib/puppet/provider/service/launchd.rb | 2 +- lib/puppet/rails.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/puppet/provider/service/launchd.rb b/lib/puppet/provider/service/launchd.rb index 07c549a8b..9d813bd5a 100644 --- a/lib/puppet/provider/service/launchd.rb +++ b/lib/puppet/provider/service/launchd.rb @@ -211,7 +211,7 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do job_path, job_plist = plist_from_label(resource[:name]) job_plist_disabled = job_plist["Disabled"] if job_plist.has_key?("Disabled") - if self.class.get_macosx_version_major == "10.6": + if self.class.get_macosx_version_major == "10.6" if FileTest.file?(Launchd_Overrides) and overrides = self.class.read_plist(Launchd_Overrides) if overrides.has_key?(resource[:name]) overrides_disabled = overrides[resource[:name]]["Disabled"] if overrides[resource[:name]].has_key?("Disabled") diff --git a/lib/puppet/rails.rb b/lib/puppet/rails.rb index 74805bb6f..f74e63f20 100644 --- a/lib/puppet/rails.rb +++ b/lib/puppet/rails.rb @@ -58,7 +58,7 @@ module Puppet::Rails socket = Puppet[:dbsocket] args[:socket] = socket unless socket.to_s.empty? - when "oracle_enhanced": + when "oracle_enhanced" args[:database] = Puppet[:dbname] unless Puppet[:dbname].to_s.empty? args[:username] = Puppet[:dbuser] unless Puppet[:dbuser].to_s.empty? args[:password] = Puppet[:dbpassword] unless Puppet[:dbpassword].to_s.empty? -- cgit From ad8cc54d8022de6213a4141d4c8ba9387ce4c62e Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Tue, 22 Mar 2011 00:11:38 -0700 Subject: (#6555) Fix another ruby 1.9 incompatible case statement --- lib/puppet/parser/lexer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb index 9a25263f6..4050adeb8 100644 --- a/lib/puppet/parser/lexer.rb +++ b/lib/puppet/parser/lexer.rb @@ -532,7 +532,7 @@ class Puppet::Parser::Lexer when 'n'; "\n" when 't'; "\t" when 's'; " " - when "\n": '' + when "\n"; '' else ch end else -- cgit From a29c7fd408b8b1e00410f5bbd82ff5e626381667 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Thu, 3 Mar 2011 07:03:51 +1100 Subject: Fixed #6562 - Minor kick documentation fix --- lib/puppet/util/command_line/puppetrun | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/puppet/util/command_line/puppetrun b/lib/puppet/util/command_line/puppetrun index 7eba3b2c4..3437405b0 100755 --- a/lib/puppet/util/command_line/puppetrun +++ b/lib/puppet/util/command_line/puppetrun @@ -107,7 +107,6 @@ # option requires LDAP support at this point. # # ping:: -# # Do a ICMP echo against the target host. Skip hosts that don't respond to ping. # # = Example -- cgit From 307df20d7dfd4f5a2a3b67fc2587491560ee951c Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Tue, 22 Mar 2011 01:28:45 -0700 Subject: Fix error "invalid multibyte char (US-ASCII)" under Ruby 1.9 Reviewed-by: Nick Lewis --- lib/puppet/type/macauthorization.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/puppet/type/macauthorization.rb b/lib/puppet/type/macauthorization.rb index e89aa7c89..b16ab6dde 100644 --- a/lib/puppet/type/macauthorization.rb +++ b/lib/puppet/type/macauthorization.rb @@ -2,7 +2,7 @@ Puppet::Type.newtype(:macauthorization) do @doc = "Manage the Mac OS X authorization database. See the [Apple developer site](http://developer.apple.com/documentation/Security/Conceptual/Security_Overview/Security_Services/chapter_4_section_5.html) for more information. - + **Autorequires:** If Puppet is managing the `/etc/authorization` file, each macauthorization resource will autorequire it." @@ -33,8 +33,8 @@ Puppet::Type.newtype(:macauthorization) do desc "The name of the right or rule to be managed. Corresponds to 'key' in Authorization Services. The key is the name of a rule. A key uses the same naming conventions as a right. The - Security Server uses a rule’s key to match the rule with a right. - Wildcard keys end with a ‘.’. The generic rule has an empty key value. + Security Server uses a rule's key to match the rule with a right. + Wildcard keys end with a '.'. The generic rule has an empty key value. Any rights that do not match a specific rule use the generic rule." isnamevar -- cgit From af42367dce6ab1ad18a1a8d10e4d54b5accae449 Mon Sep 17 00:00:00 2001 From: Richard Crowley Date: Tue, 1 Mar 2011 20:44:11 +0000 Subject: (#6527) Added pip package provider. Python's pip package manager is analogous to RubyGems and should be included in Puppet. Reviewed-by: Matt Robinson --- lib/puppet/provider/package/pip.rb | 115 +++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 lib/puppet/provider/package/pip.rb (limited to 'lib') diff --git a/lib/puppet/provider/package/pip.rb b/lib/puppet/provider/package/pip.rb new file mode 100644 index 000000000..00797563a --- /dev/null +++ b/lib/puppet/provider/package/pip.rb @@ -0,0 +1,115 @@ +# Puppet package provider for Python's `pip` package management frontend. +# + +require 'puppet/provider/package' +require 'xmlrpc/client' + +Puppet::Type.type(:package).provide :pip, + :parent => ::Puppet::Provider::Package do + + desc "Python packages via `pip`." + + has_feature :installable, :uninstallable, :upgradeable, :versionable + + # Parse lines of output from `pip freeze`, which are structured as + # _package_==_version_. + def self.parse(line) + if line.chomp =~ /^([^=]+)==([^=]+)$/ + {:ensure => $2, :name => $1, :provider => name} + else + nil + end + end + + # Return an array of structured information about every installed package + # that's managed by `pip` or an empty array if `pip` is not available. + def self.instances + packages = [] + execpipe "#{command :pip} freeze" do |process| + process.collect do |line| + next unless options = parse(line) + packages << new(options) + end + end + packages + rescue Puppet::DevError + [] + end + + # Return structured information about a particular package or `nil` if + # it is not installed or `pip` itself is not available. + def query + execpipe "#{command :pip} freeze" do |process| + process.each do |line| + options = self.class.parse(line) + return options if options[:name] == @resource[:name] + end + end + nil + rescue Puppet::DevError + nil + end + + # Ask the PyPI API for the latest version number. There is no local + # cache of PyPI's package list so this operation will always have to + # ask the web service. + def latest + client = XMLRPC::Client.new2("http://pypi.python.org/pypi") + client.http_header_extra = {"Content-Type" => "text/xml"} + result = client.call("package_releases", @resource[:name]) + result.first + end + + # Install a package. The ensure parameter may specify installed, + # latest, a version number, or, in conjunction with the source + # parameter, an SCM revision. In that case, the source parameter + # gives the fully-qualified URL to the repository. + def install + args = %w{install -q} + if @resource[:source] + args << "-e" + if String === @resource[:ensure] + args << "#{@resource[:source]}@#{@resource[:ensure]}#egg=#{ + @resource[:name]}" + else + args << "#{@resource[:source]}#egg=#{@resource[:name]}" + end + else + case @resource[:ensure] + when String + args << "#{@resource[:name]}==#{@resource[:ensure]}" + when :latest + args << "--upgrade" << @resource[:name] + else + args << @resource[:name] + end + end + lazy_pip *args + end + + # Uninstall a package. Uninstall won't work reliably on Debian/Ubuntu + # unless this issue gets fixed. + # + def uninstall + lazy_pip "uninstall", "-y", "-q", @resource[:name] + end + + def update + install + end + + # Execute a `pip` command. If Puppet doesn't yet know how to do so, + # try to teach it and if even that fails, raise the error. + private + def lazy_pip(*args) + pip *args + rescue NoMethodError => e + if pathname = `which pip`.chomp + self.class.commands :pip => pathname + pip *args + else + raise e + end + end + +end -- cgit From 0170cebba039378597fdf9f0086339c2766df408 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Tue, 22 Mar 2011 15:28:52 -0700 Subject: (#6527) Fix uninstall problem and refactor Uninstall wasn't working properly because the instances method relied on the pip command having been defined by calling lazy_pip. This lazy_pip pattern is a nice way of getting around Puppet's problem of disqualifying providers at the beginning of a run because the command doesn't yet exist even though part way through the run the command might exist. Really, we need to fix puppet to lazily evaluate a provider command for validity only at the time the provider is needed. The refactoring also pointed out that query could just reuse the logic from instances. The tests were also refactored to use real resources instead of stubbed ones, and they reflect the implementation changes to instances. Paired-with: Richard Crowley --- lib/puppet/provider/package/pip.rb | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/puppet/provider/package/pip.rb b/lib/puppet/provider/package/pip.rb index 00797563a..5abbc135a 100644 --- a/lib/puppet/provider/package/pip.rb +++ b/lib/puppet/provider/package/pip.rb @@ -25,29 +25,23 @@ Puppet::Type.type(:package).provide :pip, # that's managed by `pip` or an empty array if `pip` is not available. def self.instances packages = [] - execpipe "#{command :pip} freeze" do |process| + pip_cmd = which('pip') or return [] + execpipe "#{pip_cmd} freeze" do |process| process.collect do |line| next unless options = parse(line) packages << new(options) end end packages - rescue Puppet::DevError - [] end # Return structured information about a particular package or `nil` if # it is not installed or `pip` itself is not available. def query - execpipe "#{command :pip} freeze" do |process| - process.each do |line| - options = self.class.parse(line) - return options if options[:name] == @resource[:name] - end + self.class.instances.each do |provider_pip| + return provider_pip.properties if @resource[:name] == provider_pip.name end - nil - rescue Puppet::DevError - nil + return nil end # Ask the PyPI API for the latest version number. There is no local @@ -104,7 +98,7 @@ Puppet::Type.type(:package).provide :pip, def lazy_pip(*args) pip *args rescue NoMethodError => e - if pathname = `which pip`.chomp + if pathname = which('pip') self.class.commands :pip => pathname pip *args else -- cgit From 3d43d866d6629863d91861f29638c1a31750ba57 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Tue, 22 Mar 2011 22:28:27 -0700 Subject: (#2782) Fix constant_defined? Thanks to Al Hoang for the bit of code for choosing how to use constant_defined? depending on the version of Ruby. In Ruby 1.9 const_defined? has a new parameter for inherit (from Ruby docs) mod.const_defined?(sym, inherit=true) -> true or false Returns true if a constant with the given name is defined by mod, or its ancestors if inherit is not false. Unfortunately, the documentation isn't terribly clear about the behavior if inherit=false. In Ruby 1.8 the inherit parameter doesn't exist. It appears that setting inherit=false makes it behave like it used to in Ruby 1.8, but there may be sublties of autoloading that prove this wrong or ways in which were setting constants that changed and cause problems regardless of the behavior of const_defined? Ruby 1.8.7: irb(main):001:0> module Foo irb(main):002:1> end => nil irb(main):003:0> A = 'find_me?' => "find_me?" irb(main):004:0> Foo.const_defined?('A') => false Ruby 1.9.2: ruby-1.9.2-p136 :001 > module Foo ruby-1.9.2-p136 :002?> end => nil ruby-1.9.2-p136 :003 > A = 'find_me?' => "find_me?" ruby-1.9.2-p136 :004 > Foo.const_defined?('A') => true ruby-1.9.2-p136 :005 > Foo.const_defined?('A', false) => false Also noteworthy is that something about constants behavior changed between 1.9.1 and 1.9.2, though not in regard to the little test above, but we should only be testing against 1.9.2 anyway. At least with this change in we'll be able to start debugging test failures instead of just getting failures at the level of syntax errors. Reviewed-by: Jacob Helwig --- lib/puppet/util/classgen.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/puppet/util/classgen.rb b/lib/puppet/util/classgen.rb index ed69c5878..1e99aa873 100644 --- a/lib/puppet/util/classgen.rb +++ b/lib/puppet/util/classgen.rb @@ -124,11 +124,23 @@ module Puppet::Util::ClassGen klass end + # const_defined? in Ruby 1.9 behaves differently in terms + # of which class hierarchy it polls for nested namespaces + # + # See http://redmine.ruby-lang.org/issues/show/1915 + def is_constant_defined?(const) + if ::RUBY_VERSION =~ /1.9/ + const_defined?(const, false) + else + const_defined?(const) + end + end + # Handle the setting and/or removing of the associated constant. def handleclassconst(klass, name, options) const = genconst_string(name, options) - if const_defined?(const) + if is_constant_defined?(const) if options[:overwrite] Puppet.info "Redefining #{name} in #{self}" remove_const(const) -- cgit From 341654ea16e662e31bb97ca260397939ac34692e Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Tue, 22 Mar 2011 22:31:47 -0700 Subject: (#6820) Fix invalid next that should be a return The old code under ruby 1.8.7 if it was hit would result in: unexpected next (LocalJumpError) In Ruby 1.9 it doesn't even give you the chance to run the code since you get: compile error (SyntaxError) The code isn't tested so the intented behavior isn't clear, but in context this looks like the right change. Reviewed-by: Jacob Helwig --- lib/puppet/provider/service/daemontools.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/puppet/provider/service/daemontools.rb b/lib/puppet/provider/service/daemontools.rb index bbb962a71..f5a073329 100644 --- a/lib/puppet/provider/service/daemontools.rb +++ b/lib/puppet/provider/service/daemontools.rb @@ -67,7 +67,7 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do path = self.defpath unless FileTest.directory?(path) Puppet.notice "Service path #{path} does not exist" - next + return end # reject entries that aren't either a directory -- cgit From 7a4fcf2835ac414fe3c5b1b4e4b16c13a2c92d09 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Tue, 22 Mar 2011 22:48:17 -0700 Subject: (#6820) Fix RDOC parser to work with Ruby 1.9 Lovely RDOC changed where it put everything in Ruby 1.9. Now there's some conditional logic depending on Ruby version to determine which files to requrie. The tests still fail, but at least they run now. Reviewed-by: Jacob Helwig --- lib/puppet/util/rdoc/parser.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/puppet/util/rdoc/parser.rb b/lib/puppet/util/rdoc/parser.rb index 0f746e2ea..762ce25f0 100644 --- a/lib/puppet/util/rdoc/parser.rb +++ b/lib/puppet/util/rdoc/parser.rb @@ -7,13 +7,19 @@ require "rdoc/code_objects" require "puppet/util/rdoc/code_objects" require "rdoc/tokenstream" -require "rdoc/markup/simple_markup/preprocess" -require "rdoc/parsers/parserfactory" + +if ::RUBY_VERSION =~ /1.9/ + require "rdoc/markup/preprocess" + require "rdoc/parser" +else + require "rdoc/markup/simple_markup/preprocess" + require "rdoc/parsers/parserfactory" +end module RDoc class Parser - extend ParserFactory + extend ParserFactory unless ::RUBY_VERSION =~ /1.9/ SITE = "__site__" -- cgit From 8c32db76f68148b2e58ba744b965a3c8cd8f1663 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Tue, 22 Mar 2011 22:56:46 -0700 Subject: (#6820) Fix nagios parser to use proper hash syntax for Ruby 1.9 Reviewed-by: Jacob Helwig --- lib/puppet/external/nagios/parser.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/puppet/external/nagios/parser.rb b/lib/puppet/external/nagios/parser.rb index 5504f5818..17db5e307 100644 --- a/lib/puppet/external/nagios/parser.rb +++ b/lib/puppet/external/nagios/parser.rb @@ -753,7 +753,7 @@ module_eval <<'.,.,', 'grammar.ry', 40 module_eval <<'.,.,', 'grammar.ry', 42 def _reduce_10( val, _values, result ) -result = {val[0],val[1]} +result = {val[0] => val[1]} result end .,., -- cgit From 36a5665f3e0aeb8b9141cb8be2f69f8568078986 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Tue, 22 Mar 2011 23:07:56 -0700 Subject: (#6820) Fix File class lookup in the file type for Ruby 1.9 Was getting the following error when we simply called File.dirname err: Got an uncaught exception of type NoMethodError: undefined method `dirname' for Puppet::Type::File:Class The constant lookup has changed in Ruby 1.9 to look at local scopes first, so we need to make it clearer that File is the Ruby File class and not something scoped in Puppet. Reviewed-by: Jacob Helwig --- lib/puppet/type/file.rb | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb index 16b1f962d..1a6d0c3ac 100644 --- a/lib/puppet/type/file.rb +++ b/lib/puppet/type/file.rb @@ -44,7 +44,7 @@ Puppet::Type.newtype(:file) do # convert the current path in an index into the collection and the last # path name. The aim is to use less storage for all common paths in a hierarchy munge do |value| - path, name = File.split(value.gsub(/\/+/,'/')) + path, name = ::File.split(value.gsub(/\/+/,'/')) { :index => Puppet::FileCollection.collection.index(path), :name => name } end @@ -55,7 +55,7 @@ Puppet::Type.newtype(:file) do if value[:name] == '/' basedir else - File.join( basedir, value[:name] ) + ::File.join( basedir, value[:name] ) end end end @@ -248,7 +248,7 @@ Puppet::Type.newtype(:file) do # Autorequire any parent directories. autorequire(:file) do - basedir = File.dirname(self[:path]) + basedir = ::File.dirname(self[:path]) if basedir != self[:path] basedir else @@ -309,7 +309,7 @@ Puppet::Type.newtype(:file) do def asuser if self.should(:owner) and ! self.should(:owner).is_a?(Symbol) writeable = Puppet::Util::SUIDManager.asuser(self.should(:owner)) { - FileTest.writable?(File.dirname(self[:path])) + FileTest.writable?(::File.dirname(self[:path])) } # If the parent directory is writeable, then we execute @@ -412,7 +412,7 @@ Puppet::Type.newtype(:file) do # Create a new file or directory object as a child to the current # object. def newchild(path) - full_path = File.join(self[:path], path) + full_path = ::File.join(self[:path], path) # Add some new values to our original arguments -- these are the ones # set at initialization. We specifically want to exclude any param @@ -486,16 +486,16 @@ Puppet::Type.newtype(:file) do # not likely to have many actual conflicts, which is good, because # this is a pretty inefficient implementation. def remove_less_specific_files(files) - mypath = self[:path].split(File::Separator) + mypath = self[:path].split(::File::Separator) other_paths = catalog.vertices. select { |r| r.is_a?(self.class) and r[:path] != self[:path] }. - collect { |r| r[:path].split(File::Separator) }. + collect { |r| r[:path].split(::File::Separator) }. select { |p| p[0,mypath.length] == mypath } return files if other_paths.empty? files.reject { |file| - path = file[:path].split(File::Separator) + path = file[:path].split(::File::Separator) other_paths.any? { |p| path[0,p.length] == p } } end @@ -612,7 +612,7 @@ Puppet::Type.newtype(:file) do end when "link", "file" debug "Removing existing #{s.ftype} for replacement with #{should}" - File.unlink(self[:path]) + ::File.unlink(self[:path]) else self.fail "Could not back up files of type #{s.ftype}" end @@ -677,7 +677,7 @@ Puppet::Type.newtype(:file) do path = self[:path] begin - File.send(method, self[:path]) + ::File.send(method, self[:path]) rescue Errno::ENOENT => error return nil rescue Errno::EACCES => error @@ -703,7 +703,7 @@ Puppet::Type.newtype(:file) do use_temporary_file = write_temporary_file? if use_temporary_file path = "#{self[:path]}.puppettmp_#{rand(10000)}" - path = "#{self[:path]}.puppettmp_#{rand(10000)}" while File.exists?(path) or File.symlink?(path) + path = "#{self[:path]}.puppettmp_#{rand(10000)}" while ::File.exists?(path) or ::File.symlink?(path) else path = self[:path] end @@ -712,18 +712,18 @@ Puppet::Type.newtype(:file) do umask = mode ? 000 : 022 mode_int = mode ? mode.to_i(8) : nil - content_checksum = Puppet::Util.withumask(umask) { File.open(path, 'w', mode_int ) { |f| write_content(f) } } + content_checksum = Puppet::Util.withumask(umask) { ::File.open(path, 'w', mode_int ) { |f| write_content(f) } } # And put our new file in place if use_temporary_file # This is only not true when our file is empty. begin fail_if_checksum_is_wrong(path, content_checksum) if validate_checksum? - File.rename(path, self[:path]) + ::File.rename(path, self[:path]) rescue => detail fail "Could not rename temporary file #{path} to #{self[:path]}: #{detail}" ensure # Make sure the created file gets removed - File.unlink(path) if FileTest.exists?(path) + ::File.unlink(path) if FileTest.exists?(path) end end -- cgit