From b2a3db94c7226c7660277c746254f47ab40cde7b Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Thu, 24 Apr 2008 23:23:22 +1000 Subject: Fixed #1196 - added /sbin/service support for the redhat service provider + some doco fixes --- lib/puppet/provider/service/base.rb | 8 ++++---- lib/puppet/provider/service/init.rb | 6 +++--- lib/puppet/provider/service/redhat.rb | 25 +++++++++++++++++++++---- 3 files changed, 28 insertions(+), 11 deletions(-) (limited to 'lib/puppet') diff --git a/lib/puppet/provider/service/base.rb b/lib/puppet/provider/service/base.rb index 254b4fe4c..8964322b6 100755 --- a/lib/puppet/provider/service/base.rb +++ b/lib/puppet/provider/service/base.rb @@ -1,10 +1,10 @@ Puppet::Type.type(:service).provide :base do desc "The simplest form of service support. You have to specify enough about your service for this to work; the minimum you can specify - is a binary for starting the process, and this same binary will be searched - for in the process table to stop the service. It is preferable to - specify start, stop, and status commands, akin to how you would do - so using ``init``." + is a binary for starting the process, and this same binary will be + searched for in the process table to stop the service. It is + preferable to specify start, stop, and status commands, akin to how you + would do so using ``init``." commands :kill => "kill" diff --git a/lib/puppet/provider/service/init.rb b/lib/puppet/provider/service/init.rb index 274c334a3..3081d0eb8 100755 --- a/lib/puppet/provider/service/init.rb +++ b/lib/puppet/provider/service/init.rb @@ -2,9 +2,9 @@ # customizations of this module. Puppet::Type.type(:service).provide :init, :parent => :base do desc "Standard init service management. This provider assumes that the - init script has not ``status`` command, because so few scripts do, - so you need to either provide a status command or specify via ``hasstatus`` - that one already exists in the init script." + init script has no ``status`` command, because so few scripts do, + so you need to either provide a status command or specify via + ``hasstatus`` that one already exists in the init script." class << self attr_accessor :defpath diff --git a/lib/puppet/provider/service/redhat.rb b/lib/puppet/provider/service/redhat.rb index b013c34dc..356f6ffbe 100755 --- a/lib/puppet/provider/service/redhat.rb +++ b/lib/puppet/provider/service/redhat.rb @@ -1,11 +1,11 @@ -# Manage debian services. Start/stop is the same as InitSvc, but enable/disable -# is special. +# Manage Red Hat services. Start/stop uses /sbin/service and enable/disable uses chkconfig + Puppet::Type.type(:service).provide :redhat, :parent => :init do desc "Red Hat's (and probably many others) form of ``init``-style service management; uses ``chkconfig`` for service enabling and disabling." - commands :chkconfig => "/sbin/chkconfig" - + commands :chkconfig => "/sbin/chkconfig", :service => "/sbin/service" + defaultfor :operatingsystem => [:redhat, :fedora, :suse, :centos] def self.defpath @@ -50,5 +50,22 @@ Puppet::Type.type(:service).provide :redhat, :parent => :init do [self.name, detail] end end + + def restart + if @resource[:hasrestart] == true + service(@resource[:name], "restart") + else + return false + end + end + + def start + service(@resource[:name], "start") + end + + def stop + service(@resource[:name], "stop") + end + end -- cgit From fff6ad90d781b8fda2b93916a67ef65b3b263c66 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Wed, 30 Apr 2008 23:23:41 +1000 Subject: Fix for ticket #1209 --- lib/puppet/parser/functions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/puppet') diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index e0b60e161..93991275c 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -165,7 +165,7 @@ module Functions type is defined, either as a native type or a defined type, or whether a class is defined. This is useful for checking whether a class is defined and only including it if it is. This function can also test whether a resource has been defined, using resource references - (e.g., ``if defined(File['/tmp/myfile'] { ... }``). This function is unfortunately + (e.g., ``if defined(File['/tmp/myfile']) { ... }``). This function is unfortunately dependent on the parse order of the configuration when testing whether a resource is defined.") do |vals| result = false vals.each do |val| -- cgit From c57e194418b658ddcd82610c3c273bfa17ba5f8b Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 1 May 2008 14:04:52 -0500 Subject: Fixing an error message to be more clear --- lib/puppet/parser/compiler.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/puppet') diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index 8fba41121..d67b3d275 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -332,7 +332,7 @@ class Puppet::Parser::Compiler unless remaining.empty? fail Puppet::ParseError, - "Could not find object(s) %s" % remaining.collect { |o| + "Could not find resource(s) %s for overriding" % remaining.collect { |o| o.ref }.join(", ") end -- cgit From 2b185af97882afb4a7feab42de97771ceed80b43 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Mon, 5 May 2008 20:45:19 +1000 Subject: Add values for dump parameter for the mount type closing #1212 --- lib/puppet/type/mount.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib/puppet') diff --git a/lib/puppet/type/mount.rb b/lib/puppet/type/mount.rb index e18630cc8..1679b73a3 100755 --- a/lib/puppet/type/mount.rb +++ b/lib/puppet/type/mount.rb @@ -141,7 +141,9 @@ module Puppet newproperty(:dump) do desc "Whether to dump the mount. Not all platforms - support this." + support this. Valid values are ``1`` or ``0``. Default is ``0``." + + newvalue(%r{(0|1)}) { } defaultto { if @resource.managed? -- cgit From db8a46c605b8b4a205e65aa35a1442f2de32431b Mon Sep 17 00:00:00 2001 From: Francois Deppierraz Date: Thu, 10 Apr 2008 19:24:06 +0200 Subject: New native ssh_authorized_key type --- lib/puppet/provider/ssh_authorized_key/parsed.rb | 50 ++++++++++++++++++++++++ lib/puppet/type/ssh_authorized_key.rb | 47 ++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 lib/puppet/provider/ssh_authorized_key/parsed.rb create mode 100644 lib/puppet/type/ssh_authorized_key.rb (limited to 'lib/puppet') diff --git a/lib/puppet/provider/ssh_authorized_key/parsed.rb b/lib/puppet/provider/ssh_authorized_key/parsed.rb new file mode 100644 index 000000000..228bbc6fc --- /dev/null +++ b/lib/puppet/provider/ssh_authorized_key/parsed.rb @@ -0,0 +1,50 @@ +require 'puppet/provider/parsedfile' + +Puppet::Type.type(:ssh_authorized_key).provide(:parsed, + :parent => Puppet::Provider::ParsedFile, + :filetype => :flat, + # Ugly but the parameter is required + :default_target => '/proc/NONEXISTANT' +) do + desc "Parse and generate authorized_keys files for SSH." + + text_line :comment, :match => /^#/ + text_line :blank, :match => /^\s+/ + + record_line :parsed, + :fields => %w{options type key name}, + :optional => %w{options}, + :rts => /^\s+/, + :match => /^(?:([^ ]+) )?(ssh-dss|ssh-rsa) ([^ ]+)(?: (.+))?$/, + :post_parse => proc { |record| + if record[:options].nil? + record[:options] = [:absent] + else + record[:options] = record[:options].split(',') + end + }, + :pre_gen => proc { |record| + if record[:options].include?(:absent) + record[:options] = "" + else + record[:options] = record[:options].join(',') + end + } + + def prefetch + if not @resource.should(:target) + # + # Set default target when user is given + if val = @resource.should(:user) + target = File.expand_path("~%s/.ssh/authorized_keys" % val) + Puppet::debug("Setting target to %s" % target) + @resource[:target] = target + else + raise Puppet::Error, "Missing attribute 'user' or 'target'" + end + end + + super + end +end + diff --git a/lib/puppet/type/ssh_authorized_key.rb b/lib/puppet/type/ssh_authorized_key.rb new file mode 100644 index 000000000..97521ccab --- /dev/null +++ b/lib/puppet/type/ssh_authorized_key.rb @@ -0,0 +1,47 @@ +module Puppet + newtype(:ssh_authorized_key) do + @doc = "Manages ssh authorized keys." + + ensurable + + newparam(:name) do + desc "The ssh key comment." + + isnamevar + end + + newproperty(:type) do + desc "The encryption type used. Probably ssh-dss or ssh-rsa for + ssh version 2. Not used for ssh version 1." + + newvalue("ssh-dss") + newvalue("ssh-rsa") + newvalue("none") + + aliasvalue(:dsa, "ssh-dss") + aliasvalue(:rsa, "ssh-rsa") + + defaultto "none" + end + + newproperty(:key) do + desc "The key itself; generally a long string of hex digits." + end + + newproperty(:user) do + desc "The user account in which the ssh key should be installed." + end + + newproperty(:target) do + desc "The file in which to store the ssh key." + end + + newproperty(:options, :array_matching => :all) do + desc "Key options, see sshd(8) for possible values. Multiple values + should be specified as an array." + + defaultto do :absent end + end + end +end + -- cgit From 67dc261a764d54dd5c1b9a487b8c43d67ad4347c Mon Sep 17 00:00:00 2001 From: Francois Deppierraz Date: Fri, 9 May 2008 17:26:20 +0200 Subject: Removed "none" as a valid type attribute value, it was useless anyway --- lib/puppet/type/ssh_authorized_key.rb | 3 --- 1 file changed, 3 deletions(-) (limited to 'lib/puppet') diff --git a/lib/puppet/type/ssh_authorized_key.rb b/lib/puppet/type/ssh_authorized_key.rb index 97521ccab..e28fb7cda 100644 --- a/lib/puppet/type/ssh_authorized_key.rb +++ b/lib/puppet/type/ssh_authorized_key.rb @@ -16,12 +16,9 @@ module Puppet newvalue("ssh-dss") newvalue("ssh-rsa") - newvalue("none") aliasvalue(:dsa, "ssh-dss") aliasvalue(:rsa, "ssh-rsa") - - defaultto "none" end newproperty(:key) do -- cgit From 88ec3d808b93058f39bf0c5ac20ed1fcb9477c2b Mon Sep 17 00:00:00 2001 From: Francois Deppierraz Date: Fri, 9 May 2008 17:35:44 +0200 Subject: Cosmetic fix --- lib/puppet/provider/ssh_authorized_key/parsed.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/puppet') diff --git a/lib/puppet/provider/ssh_authorized_key/parsed.rb b/lib/puppet/provider/ssh_authorized_key/parsed.rb index 228bbc6fc..7cb6626de 100644 --- a/lib/puppet/provider/ssh_authorized_key/parsed.rb +++ b/lib/puppet/provider/ssh_authorized_key/parsed.rb @@ -3,8 +3,7 @@ require 'puppet/provider/parsedfile' Puppet::Type.type(:ssh_authorized_key).provide(:parsed, :parent => Puppet::Provider::ParsedFile, :filetype => :flat, - # Ugly but the parameter is required - :default_target => '/proc/NONEXISTANT' + :default_target => '' ) do desc "Parse and generate authorized_keys files for SSH." -- cgit From da4cdd27573a319e8fa5f89642c98377b8911fa4 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Sat, 10 May 2008 09:54:49 +1000 Subject: Fix for ticket #1218 - changed to appropriate variable name --- lib/puppet/util/settings.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/puppet') diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb index d27406d6d..0e6f91e48 100644 --- a/lib/puppet/util/settings.rb +++ b/lib/puppet/util/settings.rb @@ -122,7 +122,7 @@ class Puppet::Util::Settings if pval = self.value(varname) pval else - raise Puppet::DevError, "Could not find value for %s" % parent + raise Puppet::DevError, "Could not find value for %s" % value end end -- cgit From ae842ea8214a846624f89711d34f7481d5a8d01a Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Sun, 11 May 2008 21:23:25 +1000 Subject: Fix for urpmi provider that fixes #1217 --- lib/puppet/provider/package/urpmi.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/puppet') diff --git a/lib/puppet/provider/package/urpmi.rb b/lib/puppet/provider/package/urpmi.rb index 8adc62ab4..a95835284 100644 --- a/lib/puppet/provider/package/urpmi.rb +++ b/lib/puppet/provider/package/urpmi.rb @@ -1,6 +1,6 @@ Puppet::Type.type(:package).provide :urpmi, :parent => :rpm, :source => :rpm do desc "Support via ``urpmi``." - commands :urpmi => "urpmi", :rpm => "rpm" + commands :urpmi => "urpmi", :urpmq => "urpmq", :rpm => "rpm" if command('rpm') confine :true => begin @@ -41,9 +41,9 @@ Puppet::Type.type(:package).provide :urpmi, :parent => :rpm, :source => :rpm do # What's the latest package version available? def latest - output = urpmi "-S", :available, @resource[:name] + output = urpmq "-S", @resource[:name] - if output =~ /^#{@resource[:name]}\S+\s+(\S+)\s/ + if output =~ /^#{@resource[:name]}\s+:\s+.*\(\s+(\S+)\s+\)/ return $1 else # urpmi didn't find updates, pretend the current -- cgit From 270c007672c9a0458f14189cff3ccf1ff6311963 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 12 May 2008 15:55:02 -0500 Subject: Clarifying the exception when there's a syntax error but a valid parser. This is related to #1215. --- lib/puppet/parser/interpreter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/puppet') diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb index f27c1c5c8..04ca41494 100644 --- a/lib/puppet/parser/interpreter.rb +++ b/lib/puppet/parser/interpreter.rb @@ -62,7 +62,7 @@ class Puppet::Parser::Interpreter # exception elsewhere and reuse the parser. If one doesn't # exist, then reraise. if @parsers[environment] - Puppet.err detail + Puppet.err(detail.to_s + "; using previously parsed manifests") else raise detail end -- cgit From 20e60b111687c286737c981b13edfaac23e7b1a6 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 12 May 2008 16:37:06 -0500 Subject: Applying patch by martin to fix #1207. The -o flag is now only used when the uid is being changed. --- lib/puppet/provider/nameservice/objectadd.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/puppet') diff --git a/lib/puppet/provider/nameservice/objectadd.rb b/lib/puppet/provider/nameservice/objectadd.rb index 8ebf8924b..4682b5169 100644 --- a/lib/puppet/provider/nameservice/objectadd.rb +++ b/lib/puppet/provider/nameservice/objectadd.rb @@ -25,7 +25,7 @@ class ObjectAdd < Puppet::Provider::NameService cmd = [command(:modify), flag(param), value] - if @resource[:allowdupe] == :true + if @resource[:allowdupe] == :true && param == :uid cmd << "-o" end cmd << @resource[:name] -- cgit From 17e8158e35336291c551da03067b55dd815ab539 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 12 May 2008 17:00:48 -0500 Subject: Adding ldap providers for the user and group type. These providers use posixAccount and posixGroup. This is a collapsed merge, fwiw. --- lib/puppet/provider/group/ldap.rb | 37 +++++ lib/puppet/provider/ldap.rb | 137 ++++++++++++++++++ lib/puppet/provider/user/ldap.rb | 115 +++++++++++++++ lib/puppet/util/ldap.rb | 5 + lib/puppet/util/ldap/connection.rb | 57 ++++++++ lib/puppet/util/ldap/generator.rb | 45 ++++++ lib/puppet/util/ldap/manager.rb | 281 +++++++++++++++++++++++++++++++++++++ 7 files changed, 677 insertions(+) create mode 100644 lib/puppet/provider/group/ldap.rb create mode 100644 lib/puppet/provider/ldap.rb create mode 100644 lib/puppet/provider/user/ldap.rb create mode 100644 lib/puppet/util/ldap.rb create mode 100644 lib/puppet/util/ldap/connection.rb create mode 100644 lib/puppet/util/ldap/generator.rb create mode 100644 lib/puppet/util/ldap/manager.rb (limited to 'lib/puppet') diff --git a/lib/puppet/provider/group/ldap.rb b/lib/puppet/provider/group/ldap.rb new file mode 100644 index 000000000..632358ff1 --- /dev/null +++ b/lib/puppet/provider/group/ldap.rb @@ -0,0 +1,37 @@ +require 'puppet/provider/ldap' + +Puppet::Type.type(:group).provide :ldap, :parent => Puppet::Provider::Ldap do + desc "Group management via ``ldap``. This provider requires that you + have valid values for all of the ldap-related settings, + including ``ldapbase``. You will also almost definitely need settings + for ``ldapuser`` and ``ldappassword``, so that your clients can write + to ldap. + + Note that this provider will automatically generate a GID for you if + you do not specify one, but it is a potentially expensive operation, + as it iterates across all existing groups to pick the appropriate next + one." + + confine :true => Puppet.features.ldap? + + # We're mapping 'members' here because we want to make it + # easy for the ldap user provider to manage groups. This + # way it can just use the 'update' method in the group manager, + # whereas otherwise it would need to replicate that code. + manages(:posixGroup).at("ou=Groups").and.maps :name => :cn, :gid => :gidNumber, :members => :memberUid + + # Find the next gid after the current largest gid. + provider = self + manager.generates(:gidNumber).with do + largest = 0 + provider.manager.search.each do |hash| + next unless value = hash[:gid] + num = value[0].to_i + if num > largest + largest = num + end + end + largest + 1 + end + +end diff --git a/lib/puppet/provider/ldap.rb b/lib/puppet/provider/ldap.rb new file mode 100644 index 000000000..76834f94d --- /dev/null +++ b/lib/puppet/provider/ldap.rb @@ -0,0 +1,137 @@ +require 'puppet/provider' + +# The base class for LDAP providers. +class Puppet::Provider::Ldap < Puppet::Provider + require 'puppet/util/ldap/manager' + + class << self + attr_reader :manager + end + + # Look up all instances at our location. Yay. + def self.instances + return [] unless list = manager.search + + list.collect { |entry| new(entry) } + end + + # Specify the ldap manager for this provider, which is + # used to figure out how we actually interact with ldap. + def self.manages(*args) + @manager = Puppet::Util::Ldap::Manager.new + @manager.manages(*args) + + # Set up our getter/setter methods. + mk_resource_methods + return @manager + end + + # Query all of our resources from ldap. + def self.prefetch(resources) + resources.each do |name, resource| + if result = manager.find(name) + result[:ensure] = :present + resource.provider = new(result) + else + resource.provider = new(:ensure => :absent) + end + end + end + + attr_reader :ldap_properties + + def manager + self.class.manager + end + + def create + @property_hash[:ensure] = :present + self.class.resource_type.validproperties.each do |property| + if val = resource.should(property) + @property_hash[property] = val + end + end + end + + def delete + @property_hash[:ensure] = :absent + end + + def exists? + @property_hash[:ensure] != :absent + end + + # Apply our changes to ldap, yo. + def flush + # Just call the manager's update() method. + @property_hash.delete(:groups) + @ldap_properties.delete(:groups) + manager.update(name, ldap_properties, properties) + @property_hash.clear + @ldap_properties.clear + end + + def initialize(*args) + raise(Puppet::DevError, "No LDAP Configuration defined for %s" % self.class) unless self.class.manager + raise(Puppet::DevError, "Invalid LDAP Configuration defined for %s" % self.class) unless self.class.manager.valid? + super + + @property_hash = @property_hash.inject({}) do |result, ary| + param, values = ary + + # Skip any attributes we don't manage. + next result unless self.class.resource_type.validattr?(param) + + paramclass = self.class.resource_type.attrclass(param) + + unless values.is_a?(Array) + result[param] = values + next result + end + + # Only use the first value if the attribute class doesn't manage + # arrays of values. + if paramclass.superclass == Puppet::Parameter or paramclass.array_matching == :first + result[param] = values[0] + else + result[param] = values + end + result + end + + # Make a duplicate, so that we have a copy for comparison + # at the end. + @ldap_properties = @property_hash.dup + end + + # Return the current state of ldap. + def ldap_properties + @ldap_properties.dup + end + + # Return (and look up if necessary) the desired state. + def properties + if @property_hash.empty? + @property_hash = query || {:ensure => :absent} + if @property_hash.empty? + @property_hash[:ensure] = :absent + end + end + @property_hash.dup + end + + # Collect the current attributes from ldap. Returns + # the results, but also stores the attributes locally, + # so we have something to compare against when we update. + # LAK:NOTE This is normally not used, because we rely on prefetching. + def query + # Use the module function. + unless attributes = manager.find(name) + @ldap_properties = {} + return nil + end + + @ldap_properties = attributes + return @ldap_properties.dup + end +end diff --git a/lib/puppet/provider/user/ldap.rb b/lib/puppet/provider/user/ldap.rb new file mode 100644 index 000000000..ba91a871e --- /dev/null +++ b/lib/puppet/provider/user/ldap.rb @@ -0,0 +1,115 @@ +require 'puppet/provider/ldap' + +Puppet::Type.type(:user).provide :ldap, :parent => Puppet::Provider::Ldap do + desc "User management via ``ldap``. This provider requires that you + have valid values for all of the ldap-related settings, + including ``ldapbase``. You will also almost definitely need settings + for ``ldapuser`` and ``ldappassword``, so that your clients can write + to ldap. + + Note that this provider will automatically generate a UID for you if + you do not specify one, but it is a potentially expensive operation, + as it iterates across all existing users to pick the appropriate next + one." + + confine :true => Puppet.features.ldap? + + manages(:posixAccount, :person).at("ou=People").named_by(:uid).and.maps :name => :uid, + :password => :userPassword, + :comment => :cn, + :uid => :uidNumber, + :gid => :gidNumber, + :home => :homeDirectory, + :shell => :loginShell + + # Use the last field of a space-separated array as + # the sn. LDAP requires a surname, for some stupid reason. + manager.generates(:sn).from(:cn).with do |cn| + x = 1 + cn[0].split(/\s+/)[-1] + end + + # Find the next uid after the current largest uid. + provider = self + manager.generates(:uidNumber).with do + largest = 0 + provider.manager.search.each do |hash| + next unless value = hash[:uid] + num = value[0].to_i + if num > largest + largest = num + end + end + largest + 1 + end + + # Find all groups this user is a member of in ldap. + def groups + # We want to cache the current result, so we know if we + # have to remove old values. + unless @property_hash[:groups] + unless result = group_manager.search("memberUid=%s" % name) + return @property_hash[:groups] = :absent + end + + return @property_hash[:groups] = result.collect { |r| r[:name] }.join(",") + end + return @property_hash[:groups] + end + + # Manage the list of groups this user is a member of. + def groups=(values) + should = values.split(",") + + if groups() == :absent + is = [] + else + is = groups().split(",") + end + + modes = {} + [is, should].flatten.uniq.each do |group| + # Skip it when they're in both + next if is.include?(group) and should.include?(group) + + # We're adding a group. + modes[group] = :add and next unless is.include?(group) + + # We're removing a group. + modes[group] = :remove and next unless should.include?(group) + end + + modes.each do |group, form| + self.fail "Could not find ldap group %s" % group unless ldap_group = group_manager.find(group) + + current = ldap_group[:members] + + if form == :add + if current.is_a?(Array) and ! current.empty? + new = current + [name] + else + new = [name] + end + else + new = current - [name] + new = :absent if new.empty? + end + + group_manager.update(group, {:ensure => :present, :members => current}, {:ensure => :present, :members => new}) + end + end + + private + + def group_manager + Puppet::Type.type(:group).provider(:ldap).manager + end + + def group_properties(values) + if values.empty? or values == :absent + {:ensure => :present} + else + {:ensure => :present, :members => values} + end + end +end diff --git a/lib/puppet/util/ldap.rb b/lib/puppet/util/ldap.rb new file mode 100644 index 000000000..33f01f789 --- /dev/null +++ b/lib/puppet/util/ldap.rb @@ -0,0 +1,5 @@ +# +# Created by Luke Kanies on 2008-3-23. +# Copyright (c) 2008. All rights reserved. +module Puppet::Util::Ldap +end diff --git a/lib/puppet/util/ldap/connection.rb b/lib/puppet/util/ldap/connection.rb new file mode 100644 index 000000000..abcc07ecb --- /dev/null +++ b/lib/puppet/util/ldap/connection.rb @@ -0,0 +1,57 @@ +# +# Created by Luke Kanies on 2008-3-23. +# Copyright (c) 2008. All rights reserved. +require 'puppet/util/ldap' + +class Puppet::Util::Ldap::Connection + attr_accessor :host, :port, :user, :password, :reset, :ssl + + attr_reader :connection + + def close + connection.unbind if connection.bound? + end + + def initialize(host, port, options = {}) + raise Puppet::Error, "Could not set up LDAP Connection: Missing ruby/ldap libraries" unless Puppet.features.ldap? + + @host, @port = host, port + + options.each do |param, value| + begin + send(param.to_s + "=", value) + rescue + raise ArgumentError, "LDAP connections do not support %s parameters" % param + end + end + end + + # Create a per-connection unique name. + def name + [host, port, user, password, ssl].collect { |p| p.to_s }.join("/") + end + + # Should we reset the connection? + def reset? + reset + end + + # Start our ldap connection. + def start + begin + case ssl + when :tls: + @connection = LDAP::SSLConn.new(host, port, true) + when true: + @connection = LDAP::SSLConn.new(host, port) + else + @connection = LDAP::Conn.new(host, port) + end + @connection.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3) + @connection.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON) + @connection.simple_bind(user, password) + rescue => detail + raise Puppet::Error, "Could not connect to LDAP: %s" % detail + end + end +end diff --git a/lib/puppet/util/ldap/generator.rb b/lib/puppet/util/ldap/generator.rb new file mode 100644 index 000000000..2a868b0d9 --- /dev/null +++ b/lib/puppet/util/ldap/generator.rb @@ -0,0 +1,45 @@ +# +# Created by Luke Kanies on 2008-3-28. +# Copyright (c) 2008. All rights reserved. +require 'puppet/util/ldap' + +class Puppet::Util::Ldap::Generator + # Declare the attribute we'll use to generate the value. + def from(source) + @source = source + return self + end + + # Actually do the generation. + def generate(value = nil) + if value.nil? + @generator.call + else + @generator.call(value) + end + end + + # Initialize our generator with the name of the parameter + # being generated. + def initialize(name) + @name = name + end + + def name + @name.to_s + end + + def source + if defined?(@source) and @source + @source.to_s + else + nil + end + end + + # Provide the code that does the generation. + def with(&block) + @generator = block + return self + end +end diff --git a/lib/puppet/util/ldap/manager.rb b/lib/puppet/util/ldap/manager.rb new file mode 100644 index 000000000..9761fc753 --- /dev/null +++ b/lib/puppet/util/ldap/manager.rb @@ -0,0 +1,281 @@ +require 'puppet/util/ldap' +require 'puppet/util/ldap/connection' +require 'puppet/util/ldap/generator' + +# The configuration class for LDAP providers, plus +# connection handling for actually interacting with ldap. +class Puppet::Util::Ldap::Manager + attr_reader :objectclasses, :puppet2ldap, :location, :rdn + + # A null-op that just returns the config. + def and + return self + end + + # Set the offset from the search base and return the config. + def at(location) + @location = location + return self + end + + # The basic search base. + def base + [location, Puppet[:ldapbase]].join(",") + end + + # Convert the name to a dn, then pass the args along to + # our connection. + def create(name, attributes) + attributes = attributes.dup + + # Add the objectclasses + attributes["objectClass"] = objectclasses.collect { |o| o.to_s } + attributes["objectClass"] << "top" unless attributes["objectClass"].include?("top") + + attributes[rdn.to_s] = [name] + + # Generate any new values we might need. + generate(attributes) + + # And create our resource. + connect { |conn| conn.add dn(name), attributes } + end + + # Open, yield, and close the connection. Cannot be left + # open, at this point. + def connect + raise ArgumentError, "You must pass a block to #connect" unless block_given? + + unless defined?(@connection) and @connection + if Puppet[:ldaptls] + ssl = :tls + elsif Puppet[:ldapssl] + ssl = true + else + ssl = false + end + options = {:ssl => ssl} + if user = Puppet[:ldapuser] and user != "" + options[:user] = user + end + if password = Puppet[:ldappassword] and password != "" + options[:password] = password + end + @connection = Puppet::Util::Ldap::Connection.new(Puppet[:ldapserver], Puppet[:ldapport], options) + end + @connection.start + begin + yield @connection.connection + ensure + @connection.close + end + return nil + end + + # Convert the name to a dn, then pass the args along to + # our connection. + def delete(name) + connect { |connection| connection.delete dn(name) } + end + + # Calculate the dn for a given resource. + def dn(name) + ["#{rdn.to_s}=%s" % name, base].join(",") + end + + # Convert an ldap-style entry hash to a provider-style hash. + def entry2provider(entry) + raise ArgumentError, "Could not get dn from ldap entry" unless entry["dn"] + + # DN is always a single-entry array. Strip off the bits before the + # first comma, then the bits after the remaining equal sign. This is the + # name. + name = entry["dn"].dup.pop.split(",").shift.split("=").pop + + result = {:name => name} + + @ldap2puppet.each do |ldap, puppet| + result[puppet] = entry[ldap.to_s] || :absent + end + + result + end + + # Create our normal search filter. + def filter + return "objectclass=%s" % objectclasses[0] if objectclasses.length == 1 + return "(&(objectclass=" + objectclasses.join(")(objectclass=") + "))" + end + + # Find the associated entry for a resource. Returns a hash, minus + # 'dn', or nil if the entry cannot be found. + def find(name) + result = nil + connect do |conn| + begin + conn.search2(dn(name), 0, "objectclass=*") do |result| + # Convert to puppet-appropriate attributes + return entry2provider(result) + end + rescue => detail + return nil + end + end + end + + # Declare a new attribute generator. + def generates(parameter) + @generators << Puppet::Util::Ldap::Generator.new(parameter) + @generators[-1] + end + + # Generate any extra values we need to make the ldap entry work. + def generate(values) + return unless @generators.length > 0 + + @generators.each do |generator| + # Don't override any values that might exist. + next if values[generator.name] + + if generator.source + unless value = values[generator.source] + raise ArgumentError, "%s must be defined to generate %s" % [generator.source, generator.name] + end + result = generator.generate(value) + else + result = generator.generate + end + + result = [result] unless result.is_a?(Array) + result = result.collect { |r| r.to_s } + + values[generator.name] = result + end + end + + def initialize + @rdn = :cn + @generators = [] + end + + # Specify what classes this provider models. + def manages(*classes) + @objectclasses = classes + return self + end + + # Specify the attribute map. Assumes the keys are the puppet + # attributes, and the values are the ldap attributes, and creates a map + # for each direction. + def maps(attributes) + # The map with the puppet attributes as the keys + @puppet2ldap = attributes + + # and the ldap attributes as the keys. + @ldap2puppet = attributes.inject({}) { |map, ary| map[ary[1]] = ary[0]; map } + + return self + end + + # Return the ldap name for a puppet attribute. + def ldap_name(attribute) + @puppet2ldap[attribute].to_s + end + + # Convert the name to a dn, then pass the args along to + # our connection. + def modify(name, mods) + connect { |connection| connection.modify dn(name), mods } + end + + # Specify the rdn that we use to build up our dn. + def named_by(attribute) + @rdn = attribute + self + end + + # Return the puppet name for an ldap attribute. + def puppet_name(attribute) + @ldap2puppet[attribute] + end + + # Search for all entries at our base. A potentially expensive search. + def search(sfilter = nil) + sfilter ||= filter() + + result = [] + connect do |conn| + conn.search2(base, 1, sfilter) do |entry| + result << entry2provider(entry) + end + end + return nil if result.empty? + return result + end + + # Update the ldap entry with the desired state. + def update(name, is, should) + if should[:ensure] == :absent + Puppet.info "Removing %s from ldap" % dn(name) + delete(name) + return + end + + # We're creating a new entry + if is.empty? or is[:ensure] == :absent + Puppet.info "Creating %s in ldap" % dn(name) + # Remove any :absent params and :ensure, then convert the names to ldap names. + attrs = ldap_convert(should) + create(name, attrs) + return + end + + # We're modifying an existing entry. Yuck. + + mods = [] + # For each attribute we're deleting that is present, create a + # modify instance for deletion. + [is.keys, should.keys].flatten.uniq.each do |property| + # They're equal, so do nothing. + next if is[property] == should[property] + + attributes = ldap_convert(should) + + prop_name = ldap_name(property).to_s + + # We're creating it. + if is[property] == :absent or is[property].nil? + mods << LDAP::Mod.new(LDAP::LDAP_MOD_ADD, prop_name, attributes[prop_name]) + next + end + + # We're deleting it + if should[property] == :absent or should[property].nil? + mods << LDAP::Mod.new(LDAP::LDAP_MOD_DELETE, prop_name, []) + next + end + + # We're replacing an existing value + mods << LDAP::Mod.new(LDAP::LDAP_MOD_REPLACE, prop_name, attributes[prop_name]) + end + + modify(name, mods) + end + + # Is this a complete ldap configuration? + def valid? + location and objectclasses and ! objectclasses.empty? and puppet2ldap + end + + private + + # Convert a hash of attributes to ldap-like forms. This mostly means + # getting rid of :ensure and making sure everything's an array of strings. + def ldap_convert(attributes) + attributes.reject { |param, value| value == :absent or param == :ensure }.inject({}) do |result, ary| + value = (ary[1].is_a?(Array) ? ary[1] : [ary[1]]).collect { |v| v.to_s } + result[ldap_name(ary[0])] = value + result + end + end +end -- cgit From 49dde1187e8caea10af63ec1f92e19c0e5f1a595 Mon Sep 17 00:00:00 2001 From: Adam Jacob Date: Mon, 12 May 2008 21:41:04 -0700 Subject: Adding has_variable? support, fixing ticket #1177 --- lib/puppet/parser/templatewrapper.rb | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'lib/puppet') diff --git a/lib/puppet/parser/templatewrapper.rb b/lib/puppet/parser/templatewrapper.rb index 7a8f74156..4f044befc 100644 --- a/lib/puppet/parser/templatewrapper.rb +++ b/lib/puppet/parser/templatewrapper.rb @@ -19,6 +19,15 @@ class Puppet::Parser::TemplateWrapper @scope.parser.watch_file(@file) end end + + # Should return true if a variable is defined, false if it is not + def has_variable?(name) + if @scope.lookupvar(name.to_s, false) != :undefined + true + else + false + end + end # Ruby treats variables like methods, so we can cheat here and # trap missing vars like they were missing methods. -- cgit From 59b9958712d033bbd294ff6469ea3a242c45c82c Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 13 May 2008 11:58:33 -0500 Subject: Correcting whitespace in the templatewrapper code. Also slightly modified the wording of some of the tests. --- lib/puppet/parser/templatewrapper.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/puppet') diff --git a/lib/puppet/parser/templatewrapper.rb b/lib/puppet/parser/templatewrapper.rb index 4f044befc..4790cea30 100644 --- a/lib/puppet/parser/templatewrapper.rb +++ b/lib/puppet/parser/templatewrapper.rb @@ -19,14 +19,14 @@ class Puppet::Parser::TemplateWrapper @scope.parser.watch_file(@file) end end - + # Should return true if a variable is defined, false if it is not def has_variable?(name) - if @scope.lookupvar(name.to_s, false) != :undefined - true - else - false - end + if @scope.lookupvar(name.to_s, false) != :undefined + true + else + false + end end # Ruby treats variables like methods, so we can cheat here and -- cgit From 83ef1b0cda1b010eea3f7d001716ea52f7081c24 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Thu, 15 May 2008 17:13:30 +1000 Subject: Fix for #1219 Instead of deleting the init scripts (with --del) we should simply disable it with chkconfig service off, and respectfully do the same for enable => true; Updated CHANGELOG Fix for #1219. Instead of deleting the init scripts (with --del) we should simply disable it with chkconfig service off, and respectfully do the same for enable => true; --- lib/puppet/provider/service/redhat.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lib/puppet') diff --git a/lib/puppet/provider/service/redhat.rb b/lib/puppet/provider/service/redhat.rb index 356f6ffbe..e2d6ac947 100755 --- a/lib/puppet/provider/service/redhat.rb +++ b/lib/puppet/provider/service/redhat.rb @@ -16,7 +16,6 @@ Puppet::Type.type(:service).provide :redhat, :parent => :init do def disable begin output = chkconfig(@resource[:name], :off) - output += chkconfig("--del", @resource[:name]) rescue Puppet::ExecutionFailure raise Puppet::Error, "Could not disable %s: %s" % [self.name, output] @@ -43,8 +42,7 @@ Puppet::Type.type(:service).provide :redhat, :parent => :init do # in the init scripts. def enable begin - output = chkconfig("--add", @resource[:name]) - output += chkconfig(@resource[:name], :on) + output = chkconfig(@resource[:name], :on) rescue Puppet::ExecutionFailure => detail raise Puppet::Error, "Could not enable %s: %s" % [self.name, detail] -- cgit From 158d3df805ebe28f52db5ced928dda7129aeec1b Mon Sep 17 00:00:00 2001 From: Gunnar Wrobel Date: Thu, 15 May 2008 08:27:07 +0200 Subject: Added the ability to add arbitrary attributes to ldap. This fixes #1179. --- lib/puppet/defaults.rb | 4 ++++ lib/puppet/indirector/node/ldap.rb | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'lib/puppet') diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index eed1a00f3..df2fb9425 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -622,6 +622,10 @@ module Puppet :ldapclassattrs => ["puppetclass", "The LDAP attributes to use to define Puppet classes. Values should be comma-separated."], + :ldapstackedattrs => ["puppetvar", + "The LDAP attributes that should be stacked to arrays by adding + the values in all hierarchy elements of the tree. Values + should be comma-separated."], :ldapattrs => ["all", "The LDAP attributes to include when querying LDAP for nodes. All returned attributes are set as variables in the top-level scope. diff --git a/lib/puppet/indirector/node/ldap.rb b/lib/puppet/indirector/node/ldap.rb index 6c41c18d4..bc58908fd 100644 --- a/lib/puppet/indirector/node/ldap.rb +++ b/lib/puppet/indirector/node/ldap.rb @@ -19,6 +19,8 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap node = Puppet::Node.new(name) + information[:stacked_parameters] = {} + parent_info = nil parent = information[:parent] parents = [name] @@ -34,6 +36,10 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap raise Puppet::Error.new("Could not find parent node '%s'" % parent) end information[:classes] += parent_info[:classes] + parent_info[:stacked].each do |value| + param = value.split('=', 2) + information[:stacked_parameters][param[0]] = param[1] + end parent_info[:parameters].each do |param, value| # Specifically test for whether it's set, so false values are handled # correctly. @@ -45,6 +51,15 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap parent = parent_info[:parent] end + information[:stacked].each do |value| + param = value.split('=', 2) + information[:stacked_parameters][param[0]] = param[1] + end + + information[:stacked_parameters].each do |param, value| + information[:parameters][param] = value unless information[:parameters].include?(param) + end + node.classes = information[:classes].uniq unless information[:classes].empty? node.parameters = information[:parameters] unless information[:parameters].empty? node.environment = information[:environment] if information[:environment] @@ -62,6 +77,12 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap end end + # The attributes that Puppet will stack as array over the full + # hierarchy. + def stacked_attributes + Puppet[:ldapstackedattrs].split(/\s*,\s*/) + end + # Process the found entry. We assume that we don't just want the # ldap object. def process(name, entry) @@ -85,6 +106,14 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap end } + result[:stacked] = [] + stacked_attributes.each { |attr| + if values = entry.vals(attr) + result[:stacked] = result[:stacked] + values + end + } + + result[:parameters] = entry.to_hash.inject({}) do |hash, ary| if ary[1].length == 1 hash[ary[0]] = ary[1].shift -- cgit From c9757a689a2ecdcd044ca0355e1ebfa30bb1978f Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 15 May 2008 22:14:02 -0500 Subject: Moving the 'confine' handling to separate classes. --- lib/puppet/provider/confine.rb | 76 +++++++++++++++++++++++++++++++++++++++++ lib/puppet/provider/confiner.rb | 41 ++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 lib/puppet/provider/confine.rb create mode 100644 lib/puppet/provider/confiner.rb (limited to 'lib/puppet') diff --git a/lib/puppet/provider/confine.rb b/lib/puppet/provider/confine.rb new file mode 100644 index 000000000..e55ba02ec --- /dev/null +++ b/lib/puppet/provider/confine.rb @@ -0,0 +1,76 @@ +# The class that handles testing whether our providers +# actually work or not. +class Puppet::Provider::Confine + attr_reader :test, :values, :fact + + def exists?(value) + value and FileTest.exist?(value) + end + + # Are we a facter comparison? + def facter? + defined?(@facter) + end + + # Retrieve the value from facter + def facter_value + unless defined?(@facter_value) and @facter_value + @facter_value = Facter.value(@fact).to_s.downcase + end + @facter_value + end + + def false?(value) + ! value + end + + def initialize(test, values) + values = [values] unless values.is_a?(Array) + @values = values + + if %w{exists false true}.include?(test.to_s) + @test = test + @method = @test.to_s + "?" + else + @fact = test + @test = :facter + @method = "match?" + end + end + + def match?(value) + facter_value == value.to_s.downcase + end + + # Collect the results of all of them. + def result + values.collect { |value| send(@method, value) } + end + + def true?(value) + # Double negate, so we only get true or false. + ! ! value + end + + # Test whether our confine matches. + def valid? + values.each do |value| + unless send(@method, value) + msg = case test + when :false: "false value" + when :true: "true value" + when :exists: "file %s does not exist" % value + when :facter: "facter value '%s' for '%s' not in required list '%s'" % [value, @fact, values.join(",")] + end + Puppet.debug msg + return false + end + end + + return true + ensure + # Reset the cache. We want to cache it during a given + # run, but across runs. + @facter_value = nil + end +end diff --git a/lib/puppet/provider/confiner.rb b/lib/puppet/provider/confiner.rb new file mode 100644 index 000000000..b88f1f01b --- /dev/null +++ b/lib/puppet/provider/confiner.rb @@ -0,0 +1,41 @@ +# Manage a collection of confines, returning a boolean or +# helpful information. +require 'puppet/provider/confine' + +class Puppet::Provider::Confiner + def confine(hash) + hash.each do |test, values| + @confines << Puppet::Provider::Confine.new(test, values) + end + end + + def initialize + @confines = [] + end + + # Return a hash of the whole confine set, used for the Provider + # reference. + def result + defaults = { + :false => 0, + :true => 0, + :exists => [], + :facter => {} + } + missing = Hash.new { |hash, key| hash[key] = defaults[key] } + @confines.each do |confine| + case confine.test + when :false: missing[confine.test] += confine.result.find_all { |v| v == false }.length + when :true: missing[confine.test] += confine.result.find_all { |v| v == true }.length + when :exists: confine.result.zip(confine.values).each { |val, f| missing[:exists] << f unless val } + when :facter: missing[:facter][confine.fact] = confine.values if confine.result.include?(false) + end + end + + missing + end + + def valid? + ! @confines.detect { |c| ! c.valid? } + end +end -- cgit From 995991d8740baff52cee057752c428d0259e2be1 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 15 May 2008 22:21:50 -0500 Subject: Switching the Provider class to use the new Confiner class. --- lib/puppet/provider.rb | 79 +++++++------------------------------------------- 1 file changed, 11 insertions(+), 68 deletions(-) (limited to 'lib/puppet') diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb index e73bb0cb6..e0de9d61d 100644 --- a/lib/puppet/provider.rb +++ b/lib/puppet/provider.rb @@ -5,6 +5,8 @@ class Puppet::Provider include Puppet::Util::Warnings extend Puppet::Util::Warnings + require 'puppet/provider/confiner' + Puppet::Util.logmethods(self, true) class << self @@ -55,13 +57,14 @@ class Puppet::Provider end def self.confine(hash) - hash.each do |p,v| - if v.is_a? Array - @confines[p] += v - else - @confines[p] << v - end + confiner.confine(hash) + end + + def self.confiner + unless defined?(@confiner) + @confiner = Puppet::Provider::Confiner.new end + @confiner end # Is the provided feature a declared feature? @@ -109,9 +112,6 @@ class Puppet::Provider @defaults = {} @commands = {} @origcommands = {} - @confines = Hash.new do |hash, key| - hash[key] = [] - end end # The method for returning a list of provider instances. Note that it returns providers, preferably with values already @@ -210,65 +210,8 @@ class Puppet::Provider # Check whether this implementation is suitable for our platform. def self.suitable?(short = true) - # A single false result is sufficient to turn the whole thing down. - # We don't return 'true' until the very end, though, so that every - # confine is tested. - missing = {} - @confines.each do |check, values| - case check - when :exists: - values.each do |value| - unless value and FileTest.exists? value - debug "Not suitable: missing %s" % value - return false if short - missing[:exists] ||= [] - missing[:exists] << value - end - end - when :true: - values.each do |v| - debug "Not suitable: false value" - unless v - return false if short - missing[:true] ||= 0 - missing[:true] += 1 - end - end - when :false: - values.each do |v| - debug "Not suitable: true value" - if v and short - return false if short - missing[:false] ||= 0 - missing[:false] += 1 - end - end - else # Just delegate everything else to facter - if result = Facter.value(check) - result = result.to_s.downcase.intern - - found = values.find do |v| - result == v.to_s.downcase.intern - end - unless found - debug "Not suitable: %s not in %s" % [check, values] - return false if short - missing[:facter] ||= {} - missing[:facter][check] = values - end - else - return false if short - missing[:facter] ||= {} - missing[:facter][check] = values - end - end - end - - if short - return true - else - return missing - end + return confiner.valid? if short + return confiner.result end # Does this provider support the specified parameter? -- cgit From a1409d73b4bb8acbf5db2f8d7a244c2bca81db14 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 15 May 2008 23:09:58 -0500 Subject: Moving all confine code out of the Provider class, and fixing #1197. I created a Confiner module for the Provider class methods, and then I enhanced the interface between it and the Confine class to make sure binary paths are searched for fresh each time. This fixes #1197, which was a result of binary paths being searched for at startup, rather than at execution. --- lib/puppet/provider.rb | 43 +++++---------------------- lib/puppet/provider/confine.rb | 23 +++++++++++---- lib/puppet/provider/confine_collection.rb | 48 +++++++++++++++++++++++++++++++ lib/puppet/provider/confiner.rb | 43 +++++++-------------------- 4 files changed, 84 insertions(+), 73 deletions(-) create mode 100644 lib/puppet/provider/confine_collection.rb (limited to 'lib/puppet') diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb index e0de9d61d..c02e15029 100644 --- a/lib/puppet/provider.rb +++ b/lib/puppet/provider.rb @@ -7,6 +7,8 @@ class Puppet::Provider require 'puppet/provider/confiner' + extend Puppet::Provider::Confiner + Puppet::Util.logmethods(self, true) class << self @@ -42,31 +44,16 @@ class Puppet::Provider [name, self.name] end - if command == :missing - return nil - end - - command + return binary(command) end # Define commands that are not optional. def self.commands(hash) optional_commands(hash) do |name, path| - confine :exists => path + confine :exists => path, :for_binary => true end end - def self.confine(hash) - confiner.confine(hash) - end - - def self.confiner - unless defined?(@confiner) - @confiner = Puppet::Provider::Confiner.new - end - @confiner - end - # Is the provided feature a declared feature? def self.declared_feature?(name) defined?(@declared_features) and @declared_features.include?(name) @@ -111,7 +98,6 @@ class Puppet::Provider def self.initvars @defaults = {} @commands = {} - @origcommands = {} end # The method for returning a list of provider instances. Note that it returns providers, preferably with values already @@ -180,16 +166,7 @@ class Puppet::Provider def self.optional_commands(hash) hash.each do |name, path| name = symbolize(name) - @origcommands[name] = path - - # Try to find the full path (or verify already-full paths); otherwise - # store that the command is missing so we know it's defined but absent. - if tmp = binary(path) - path = tmp - @commands[name] = path - else - @commands[name] = :missing - end + @commands[name] = path if block_given? yield(name, path) @@ -208,12 +185,6 @@ class Puppet::Provider @source end - # Check whether this implementation is suitable for our platform. - def self.suitable?(short = true) - return confiner.valid? if short - return confiner.result - end - # Does this provider support the specified parameter? def self.supports_parameter?(param) if param.is_a?(Class) @@ -252,8 +223,8 @@ class Puppet::Provider end dochook(:commands) do - if @origcommands.length > 0 - return " Required binaries: " + @origcommands.collect do |n, c| + if @commands.length > 0 + return " Required binaries: " + @commands.collect do |n, c| "``#{c}``" end.join(", ") + "." end diff --git a/lib/puppet/provider/confine.rb b/lib/puppet/provider/confine.rb index e55ba02ec..227c923e6 100644 --- a/lib/puppet/provider/confine.rb +++ b/lib/puppet/provider/confine.rb @@ -1,9 +1,22 @@ # The class that handles testing whether our providers # actually work or not. +require 'puppet/util' + class Puppet::Provider::Confine + include Puppet::Util + attr_reader :test, :values, :fact + # Mark that this confine is used for testing binary existence. + attr_accessor :for_binary + def for_binary? + for_binary + end + def exists?(value) + if for_binary? + return false unless value = binary(value) + end value and FileTest.exist?(value) end @@ -57,11 +70,11 @@ class Puppet::Provider::Confine values.each do |value| unless send(@method, value) msg = case test - when :false: "false value" - when :true: "true value" - when :exists: "file %s does not exist" % value - when :facter: "facter value '%s' for '%s' not in required list '%s'" % [value, @fact, values.join(",")] - end + when :false: "false value when expecting true" + when :true: "true value when expecting false" + when :exists: "file %s does not exist" % value + when :facter: "facter value '%s' for '%s' not in required list '%s'" % [value, @fact, values.join(",")] + end Puppet.debug msg return false end diff --git a/lib/puppet/provider/confine_collection.rb b/lib/puppet/provider/confine_collection.rb new file mode 100644 index 000000000..f38035521 --- /dev/null +++ b/lib/puppet/provider/confine_collection.rb @@ -0,0 +1,48 @@ +# Manage a collection of confines, returning a boolean or +# helpful information. +require 'puppet/provider/confine' + +class Puppet::Provider::ConfineCollection + def confine(hash) + if hash.include?(:for_binary) + for_binary = true + hash.delete(:for_binary) + else + for_binary = false + end + hash.each do |test, values| + @confines << Puppet::Provider::Confine.new(test, values) + @confines[-1].for_binary = true if for_binary + end + end + + def initialize + @confines = [] + end + + # Return a hash of the whole confine set, used for the Provider + # reference. + def result + defaults = { + :false => 0, + :true => 0, + :exists => [], + :facter => {} + } + missing = Hash.new { |hash, key| hash[key] = defaults[key] } + @confines.each do |confine| + case confine.test + when :false: missing[confine.test] += confine.result.find_all { |v| v == false }.length + when :true: missing[confine.test] += confine.result.find_all { |v| v == true }.length + when :exists: confine.result.zip(confine.values).each { |val, f| missing[:exists] << f unless val } + when :facter: missing[:facter][confine.fact] = confine.values if confine.result.include?(false) + end + end + + missing + end + + def valid? + ! @confines.detect { |c| ! c.valid? } + end +end diff --git a/lib/puppet/provider/confiner.rb b/lib/puppet/provider/confiner.rb index b88f1f01b..3e406873b 100644 --- a/lib/puppet/provider/confiner.rb +++ b/lib/puppet/provider/confiner.rb @@ -1,41 +1,20 @@ -# Manage a collection of confines, returning a boolean or -# helpful information. -require 'puppet/provider/confine' +require 'puppet/provider/confine_collection' -class Puppet::Provider::Confiner +module Puppet::Provider::Confiner def confine(hash) - hash.each do |test, values| - @confines << Puppet::Provider::Confine.new(test, values) - end - end - - def initialize - @confines = [] + confine_collection.confine(hash) end - # Return a hash of the whole confine set, used for the Provider - # reference. - def result - defaults = { - :false => 0, - :true => 0, - :exists => [], - :facter => {} - } - missing = Hash.new { |hash, key| hash[key] = defaults[key] } - @confines.each do |confine| - case confine.test - when :false: missing[confine.test] += confine.result.find_all { |v| v == false }.length - when :true: missing[confine.test] += confine.result.find_all { |v| v == true }.length - when :exists: confine.result.zip(confine.values).each { |val, f| missing[:exists] << f unless val } - when :facter: missing[:facter][confine.fact] = confine.values if confine.result.include?(false) - end + def confine_collection + unless defined?(@confine_collection) + @confine_collection = Puppet::Provider::ConfineCollection.new end - - missing + @confine_collection end - def valid? - ! @confines.detect { |c| ! c.valid? } + # Check whether this implementation is suitable for our platform. + def suitable?(short = true) + return confine_collection.valid? if short + return confine_collection.result end end -- cgit From 38545d9a81274c16a43f7f19889cd12e52871d76 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 16 May 2008 16:49:22 +1000 Subject: Crontab provider: fix a parse error when a line begins with a space character Tests for Bug #1216 Updated CHANGELOG --- lib/puppet/provider/cron/crontab.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/puppet') diff --git a/lib/puppet/provider/cron/crontab.rb b/lib/puppet/provider/cron/crontab.rb index 7ddcc0505..e2228b15e 100755 --- a/lib/puppet/provider/cron/crontab.rb +++ b/lib/puppet/provider/cron/crontab.rb @@ -30,7 +30,7 @@ Puppet::Type.type(:cron).provide(:crontab, } crontab = record_line :crontab, :fields => %w{minute hour monthday month weekday command}, - :match => %r{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)$}, + :match => %r{^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)$}, :optional => %w{minute hour weekday month monthday}, :absent => "*" class << crontab -- cgit From 2d6a914e6071681b83e482b40448d9219d6e4d76 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 16 May 2008 17:17:34 +1000 Subject: Fix for latest method in rpm provider (fixes #1224) --- lib/puppet/provider/package/rpm.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/puppet') diff --git a/lib/puppet/provider/package/rpm.rb b/lib/puppet/provider/package/rpm.rb index 98ca1efa6..35684e11d 100755 --- a/lib/puppet/provider/package/rpm.rb +++ b/lib/puppet/provider/package/rpm.rb @@ -67,7 +67,7 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr end cmd = [command(:rpm), "-q", "--qf", "#{NEVRAFORMAT}\n", "-p", "#{@resource[:source]}"] - h = nevra_to_hash(execfail(cmd, Puppet::Error)) + h = self.class.nevra_to_hash(execfail(cmd, Puppet::Error)) return h[:ensure] end -- cgit From 8008bbc447764e0ca28169284f4d6df3a86dcdd6 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Fri, 16 May 2008 15:42:20 -0500 Subject: Modified the 'factpath' setting to automatically configure Facter to load facts there if a new enough version of Facter is used. --- lib/puppet/defaults.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lib/puppet') diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index df2fb9425..57299b7e7 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -505,9 +505,11 @@ module Puppet # Central fact information. self.setdefaults(:main, - :factpath => ["$vardir/facts", - "Where Puppet should look for facts. Multiple directories should - be colon-separated, like normal PATH variables."], + :factpath => {:default => "$vardir/facts", + :desc => "Where Puppet should look for facts. Multiple directories should + be colon-separated, like normal PATH variables.", + :call_on_define => true, # Call our hook with the default value, so we always get the value added to facter. + :hook => proc { |value| Facter.search(value) if Facter.respond_to?(:search) }}, :factdest => ["$vardir/facts", "Where Puppet should store facts that it pulls down from the central server."], -- cgit From 6a6a1d99d7cd5186b3d778007abaa54a27d5e373 Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Sun, 18 May 2008 15:30:13 -0700 Subject: Another refactor based on feedback from Luke. This includes adding an accessor for @@state to make testing a bit cleaner. --- lib/puppet/util/storage.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/puppet') diff --git a/lib/puppet/util/storage.rb b/lib/puppet/util/storage.rb index 9358a28e9..dc4e9cd71 100644 --- a/lib/puppet/util/storage.rb +++ b/lib/puppet/util/storage.rb @@ -6,6 +6,10 @@ class Puppet::Util::Storage include Singleton include Puppet::Util + def self.state + return @@state + end + def initialize self.class.load end -- cgit From 77ee4ec6ca56973ce315358864722a152557857f Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 19 May 2008 02:05:19 -0500 Subject: Refactoring how the provider confine tests work, again. Now each of the test types is a separate subclass of Confine, so that they can have all of their own logging and summarizing behaviour. Also, added a 'feature' type, which can test for the availability of Puppet features (and log their absence more usefully). --- lib/puppet/provider/confine.rb | 88 +++++++++++++------------------ lib/puppet/provider/confine/exists.rb | 22 ++++++++ lib/puppet/provider/confine/facter.rb | 37 +++++++++++++ lib/puppet/provider/confine/false.rb | 19 +++++++ lib/puppet/provider/confine/feature.rb | 17 ++++++ lib/puppet/provider/confine/true.rb | 20 +++++++ lib/puppet/provider/confine_collection.rb | 37 +++++++------ lib/puppet/provider/confiner.rb | 2 +- lib/puppet/reference/providers.rb | 2 + 9 files changed, 174 insertions(+), 70 deletions(-) create mode 100644 lib/puppet/provider/confine/exists.rb create mode 100644 lib/puppet/provider/confine/facter.rb create mode 100644 lib/puppet/provider/confine/false.rb create mode 100644 lib/puppet/provider/confine/feature.rb create mode 100644 lib/puppet/provider/confine/true.rb (limited to 'lib/puppet') diff --git a/lib/puppet/provider/confine.rb b/lib/puppet/provider/confine.rb index 227c923e6..35b80fdcf 100644 --- a/lib/puppet/provider/confine.rb +++ b/lib/puppet/provider/confine.rb @@ -5,85 +5,73 @@ require 'puppet/util' class Puppet::Provider::Confine include Puppet::Util - attr_reader :test, :values, :fact + @tests = {} - # Mark that this confine is used for testing binary existence. - attr_accessor :for_binary - def for_binary? - for_binary + class << self + attr_accessor :name end - def exists?(value) - if for_binary? - return false unless value = binary(value) - end - value and FileTest.exist?(value) - end + def self.inherited(klass) + name = klass.to_s.split("::").pop.downcase.to_sym + raise "Test %s is already defined" % name if @tests.include?(name) + + klass.name = name - # Are we a facter comparison? - def facter? - defined?(@facter) + @tests[name] = klass end - # Retrieve the value from facter - def facter_value - unless defined?(@facter_value) and @facter_value - @facter_value = Facter.value(@fact).to_s.downcase + def self.test(name) + unless @tests[name] + begin + require "puppet/provider/confine/%s" % name + rescue LoadError => detail + unless detail.to_s.include?("no such file") + warn "Could not load confine test '%s': %s" % [name, detail] + end + # Could not find file + end end - @facter_value + return @tests[name] end - def false?(value) - ! value + attr_reader :values + + # Mark that this confine is used for testing binary existence. + attr_accessor :for_binary + def for_binary? + for_binary end - def initialize(test, values) + def initialize(values) values = [values] unless values.is_a?(Array) @values = values - - if %w{exists false true}.include?(test.to_s) - @test = test - @method = @test.to_s + "?" - else - @fact = test - @test = :facter - @method = "match?" - end end - def match?(value) - facter_value == value.to_s.downcase + # Provide a hook for the message when there's a failure. + def message(value) + "" end # Collect the results of all of them. def result - values.collect { |value| send(@method, value) } - end - - def true?(value) - # Double negate, so we only get true or false. - ! ! value + values.collect { |value| pass?(value) } end # Test whether our confine matches. def valid? values.each do |value| - unless send(@method, value) - msg = case test - when :false: "false value when expecting true" - when :true: "true value when expecting false" - when :exists: "file %s does not exist" % value - when :facter: "facter value '%s' for '%s' not in required list '%s'" % [value, @fact, values.join(",")] - end - Puppet.debug msg + unless pass?(value) + Puppet.debug message(value) return false end end return true ensure - # Reset the cache. We want to cache it during a given - # run, but across runs. - @facter_value = nil + reset + end + + # Provide a hook for subclasses. + def reset end end diff --git a/lib/puppet/provider/confine/exists.rb b/lib/puppet/provider/confine/exists.rb new file mode 100644 index 000000000..1d1ed8c84 --- /dev/null +++ b/lib/puppet/provider/confine/exists.rb @@ -0,0 +1,22 @@ +require 'puppet/provider/confine' + +class Puppet::Provider::Confine::Exists < Puppet::Provider::Confine + def self.summarize(confines) + confines.inject([]) { |total, confine| total + confine.summary } + end + + def pass?(value) + if for_binary? + return false unless value = binary(value) + end + value and FileTest.exist?(value) + end + + def message(value) + "file %s does not exist" % value + end + + def summary + result.zip(values).inject([]) { |array, args| val, f = args; array << f unless val; array } + end +end diff --git a/lib/puppet/provider/confine/facter.rb b/lib/puppet/provider/confine/facter.rb new file mode 100644 index 000000000..9bb66c058 --- /dev/null +++ b/lib/puppet/provider/confine/facter.rb @@ -0,0 +1,37 @@ +require 'puppet/provider/confine' + +class Puppet::Provider::Confine::Facter < Puppet::Provider::Confine + def self.summarize(confines) + result = Hash.new { |hash, key| hash[key] = [] } + confines.inject(result) { |total, confine| total[confine.fact] += confine.values unless confine.valid?; total } + end + + attr_accessor :fact + + # Are we a facter comparison? + def facter? + defined?(@facter) + end + + # Retrieve the value from facter + def facter_value + unless defined?(@facter_value) and @facter_value + @facter_value = ::Facter.value(@fact).to_s.downcase + end + @facter_value + end + + def message(value) + "facter value '%s' for '%s' not in required list '%s'" % [value, self.fact, values.join(",")] + end + + def pass?(value) + facter_value == value.to_s.downcase + end + + def reset + # Reset the cache. We want to cache it during a given + # run, but across runs. + @facter_value = nil + end +end diff --git a/lib/puppet/provider/confine/false.rb b/lib/puppet/provider/confine/false.rb new file mode 100644 index 000000000..b5b2b51c8 --- /dev/null +++ b/lib/puppet/provider/confine/false.rb @@ -0,0 +1,19 @@ +require 'puppet/provider/confine' + +class Puppet::Provider::Confine::False < Puppet::Provider::Confine + def self.summarize(confines) + confines.inject(0) { |count, confine| count + confine.summary } + end + + def pass?(value) + ! value + end + + def message(value) + "true value when expecting false" + end + + def summary + result.find_all { |v| v == false }.length + end +end diff --git a/lib/puppet/provider/confine/feature.rb b/lib/puppet/provider/confine/feature.rb new file mode 100644 index 000000000..1d92b001a --- /dev/null +++ b/lib/puppet/provider/confine/feature.rb @@ -0,0 +1,17 @@ +require 'puppet/provider/confine' + +class Puppet::Provider::Confine::Feature < Puppet::Provider::Confine + def self.summarize(confines) + confines.collect { |c| c.values }.flatten.uniq.find_all { |value| ! confines[0].pass?(value) } + end + + # Is the named feature available? + def pass?(value) + Puppet.features.send(value.to_s + "?") + end + + def message(value) + "feature %s is missing" % value + end +end + diff --git a/lib/puppet/provider/confine/true.rb b/lib/puppet/provider/confine/true.rb new file mode 100644 index 000000000..86b3b144f --- /dev/null +++ b/lib/puppet/provider/confine/true.rb @@ -0,0 +1,20 @@ +require 'puppet/provider/confine' + +class Puppet::Provider::Confine::True < Puppet::Provider::Confine + def self.summarize(confines) + confines.inject(0) { |count, confine| count + confine.summary } + end + + def pass?(value) + # Double negate, so we only get true or false. + ! ! value + end + + def message(value) + "false value when expecting true" + end + + def summary + result.find_all { |v| v == true }.length + end +end diff --git a/lib/puppet/provider/confine_collection.rb b/lib/puppet/provider/confine_collection.rb index f38035521..0c80086c9 100644 --- a/lib/puppet/provider/confine_collection.rb +++ b/lib/puppet/provider/confine_collection.rb @@ -11,8 +11,14 @@ class Puppet::Provider::ConfineCollection for_binary = false end hash.each do |test, values| - @confines << Puppet::Provider::Confine.new(test, values) - @confines[-1].for_binary = true if for_binary + if klass = Puppet::Provider::Confine.test(test) + @confines << klass.new(values) + @confines[-1].for_binary = true if for_binary + else + confine = Puppet::Provider::Confine.test(:facter).new(values) + confine.fact = test + @confines << confine + end end end @@ -22,24 +28,17 @@ class Puppet::Provider::ConfineCollection # Return a hash of the whole confine set, used for the Provider # reference. - def result - defaults = { - :false => 0, - :true => 0, - :exists => [], - :facter => {} - } - missing = Hash.new { |hash, key| hash[key] = defaults[key] } - @confines.each do |confine| - case confine.test - when :false: missing[confine.test] += confine.result.find_all { |v| v == false }.length - when :true: missing[confine.test] += confine.result.find_all { |v| v == true }.length - when :exists: confine.result.zip(confine.values).each { |val, f| missing[:exists] << f unless val } - when :facter: missing[:facter][confine.fact] = confine.values if confine.result.include?(false) - end - end + def summary + confines = Hash.new { |hash, key| hash[key] = [] } + @confines.each { |confine| confines[confine.class] << confine } + result = {} + confines.each do |klass, list| + value = klass.summarize(list) + next if (value.respond_to?(:length) and value.length == 0) or (value == 0) + result[klass.name] = value - missing + end + result end def valid? diff --git a/lib/puppet/provider/confiner.rb b/lib/puppet/provider/confiner.rb index 3e406873b..4605523e8 100644 --- a/lib/puppet/provider/confiner.rb +++ b/lib/puppet/provider/confiner.rb @@ -15,6 +15,6 @@ module Puppet::Provider::Confiner # Check whether this implementation is suitable for our platform. def suitable?(short = true) return confine_collection.valid? if short - return confine_collection.result + return confine_collection.summary end end diff --git a/lib/puppet/reference/providers.rb b/lib/puppet/reference/providers.rb index da815ddf1..610c7550d 100644 --- a/lib/puppet/reference/providers.rb +++ b/lib/puppet/reference/providers.rb @@ -71,6 +71,8 @@ providers = Puppet::Util::Reference.newreference :providers, :title => "Provider details += " - Got %s true tests that should have been false\n" % values when :false: details += " - Got %s false tests that should have been true\n" % values + when :feature: + details += " - Missing features %s\n" % values.collect { |f| f.to_s }.join(",") end end notes << details -- cgit From ee4be4f78f7c904dbe5873ff7b44993d1440da41 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 19 May 2008 22:16:13 -0500 Subject: Removing an unused file. Closes #1229. --- lib/puppet/util/variables.rb | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 lib/puppet/util/variables.rb (limited to 'lib/puppet') diff --git a/lib/puppet/util/variables.rb b/lib/puppet/util/variables.rb deleted file mode 100644 index 1a78ef5c1..000000000 --- a/lib/puppet/util/variables.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Puppet::Util::Variables - def inithooks - @instance_init_hooks.dup - end - - def initvars - return unless defined? @class_init_hooks - self.inithooks.each do |var, value| - if value.is_a?(Class) - instance_variable_set("@" + var.to_s, value.new) - else - instance_variable_set("@" + var.to_s, value) - end - end - end - - def instancevar(hash) - @instance_init_hooks ||= {} - - unless method_defined?(:initvars) - define_method(:initvars) do - self.class.inithooks.each do |var, value| - if value.is_a?(Class) - instance_variable_set("@" + var.to_s, value.new) - else - instance_variable_set("@" + var.to_s, value) - end - end - end - end - hash.each do |var, value| - raise("Already initializing %s" % var) if @instance_init_hooks[var] - - @instance_init_hooks[var] = value - end - end -end - -- cgit