[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, master, updated. debian/0.24.6-1-356-g5718585

James Turnbull james at lovedthanlost.net
Fri Jan 23 14:21:45 UTC 2009


The following commit has been merged in the master branch:
commit d978668c03f42ce314245c23b06179f6a62f3d67
Author: Nigel Kersten <nigelk at google.com>
Date:   Tue Nov 25 08:32:46 2008 -0800

    Lots of DirectoryService work. New Computer Type. Users now use password hashes. Groups now support setting members as attributes of the group for OS X.

diff --git a/lib/puppet/provider/computer/computer.rb b/lib/puppet/provider/computer/computer.rb
new file mode 100644
index 0000000..76d0f18
--- /dev/null
+++ b/lib/puppet/provider/computer/computer.rb
@@ -0,0 +1,22 @@
+require 'puppet/provider/nameservice/directoryservice'
+
+Puppet::Type.type(:computer).provide :directoryservice, :parent => Puppet::Provider::NameService::DirectoryService do
+    desc "Computer object management using DirectoryService on OS X.
+    
+    Note that these are distinctly different kinds of objects to 'hosts',
+    as they require a MAC address and can have all sorts of policy attached to
+    them.
+    
+    This provider only manages Computer objects in the local directory service
+    domain, not in remote directories.
+    
+    If you wish to manage /etc/hosts on Mac OS X, then simply use the host
+    type as per other platforms.
+    "
+
+    confine :operatingsystem => :darwin
+    defaultfor :operatingsystem => :darwin
+    
+    # hurray for abstraction. The nameservice directoryservice provider can
+    # handle everything we need. super.
+end
\ No newline at end of file
diff --git a/lib/puppet/provider/group/directoryservice.rb b/lib/puppet/provider/group/directoryservice.rb
index 4066222..2f39305 100644
--- a/lib/puppet/provider/group/directoryservice.rb
+++ b/lib/puppet/provider/group/directoryservice.rb
@@ -16,8 +16,9 @@ require 'puppet/provider/nameservice/directoryservice'
 
 Puppet::Type.type(:group).provide :directoryservice, :parent => Puppet::Provider::NameService::DirectoryService do
     desc "Group management using DirectoryService on OS X."
-    
+        
     commands :dscl => "/usr/bin/dscl"
     confine :operatingsystem => :darwin
-    #defaultfor :operatingsystem => :darwin
+    defaultfor :operatingsystem => :darwin
+    has_feature :manages_members
 end
diff --git a/lib/puppet/provider/nameservice/directoryservice.rb b/lib/puppet/provider/nameservice/directoryservice.rb
index ecd5fa6..0e8002c 100644
--- a/lib/puppet/provider/nameservice/directoryservice.rb
+++ b/lib/puppet/provider/nameservice/directoryservice.rb
@@ -16,6 +16,7 @@ require 'puppet'
 require 'puppet/provider/nameservice'
 require 'facter/util/plist'
 
+
 class Puppet::Provider::NameService
 class DirectoryService < Puppet::Provider::NameService
     # JJM: Dive into the eigenclass
@@ -27,6 +28,7 @@ class DirectoryService < Puppet::Provider::NameService
         attr_writer :ds_path
     end
 
+    
     # JJM 2007-07-24: Not yet sure what initvars() does.  I saw it in netinfo.rb
     # I do know, however, that it makes methods "work"  =)
     # e.g. addcmd isn't available if this method call isn't present.
@@ -37,6 +39,7 @@ class DirectoryService < Puppet::Provider::NameService
     initvars()
     
     commands :dscl => "/usr/bin/dscl"
+    commands :dseditgroup => "/usr/sbin/dseditgroup"
     confine :operatingsystem => :darwin
     defaultfor :operatingsystem => :darwin
 
@@ -56,6 +59,9 @@ class DirectoryService < Puppet::Provider::NameService
         'RealName' => :comment,
         'Password' => :password,
         'GeneratedUID' => :guid,
+        'IPAddress'    => :ip_address,
+        'ENetAddress'  => :en_address,
+        'GroupMembership' => :members,
     }
     # JJM The same table as above, inverted.
     @@ns_to_ds_attribute_map = {
@@ -67,6 +73,9 @@ class DirectoryService < Puppet::Provider::NameService
         :comment => 'RealName',
         :password => 'Password',
         :guid => 'GeneratedUID',
+        :en_address => 'ENetAddress',
+        :ip_address => 'IPAddress',
+        :members => 'GroupMembership',
     }
     
     @@password_hash_dir = "/var/db/shadow/hash"
@@ -137,7 +146,10 @@ class DirectoryService < Puppet::Provider::NameService
         dscl_plist.keys().each do |key|
             ds_attribute = key.sub("dsAttrTypeStandard:", "")
             next unless (@@ds_to_ns_attribute_map.keys.include?(ds_attribute) and type_properties.include? @@ds_to_ns_attribute_map[ds_attribute])
-            ds_value = dscl_plist[key][0]  # only care about the first entry...
+            ds_value = dscl_plist[key]
+            if not @@ds_to_ns_attribute_map[ds_attribute] == :members  # only members uses arrays so far
+                ds_value = ds_value[0]
+            end
             attribute_hash[@@ds_to_ns_attribute_map[ds_attribute]] = ds_value
         end
         
@@ -229,7 +241,6 @@ class DirectoryService < Puppet::Provider::NameService
         if ensure_value == :present
             @resource.class.validproperties.each do |name|
                 next if name == :ensure
-
                 # LAK: We use property.sync here rather than directly calling
                 # the settor method because the properties might do some kind
                 # of conversion.  In particular, the user gid property might
@@ -261,18 +272,36 @@ class DirectoryService < Puppet::Provider::NameService
       end
     end
     
-    def modifycmd(property, value)
-        # JJM: This method will assemble a exec vector which modifies
-        #    a single property and it's value using dscl.
-        # JJM: With /usr/bin/dscl, the -create option will destroy an
-        #      existing property record if it exists
-        exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
-        # JJM: The following line just maps the NS name to the DS name
-        #      e.g. { :uid => 'UniqueID' }
-        exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(property)]
-        # JJM: The following line sends the actual value to set the property to
-        exec_arg_vector << value.to_s
-        return exec_arg_vector
+    # NBK: we override @parent.set as we need to execute a series of commands
+    # to deal with array values, rather than the single command nameservice.rb
+    # expects to be returned by modifycmd. Thus we don't bother defining modifycmd.
+    
+    def set(param, value)
+        self.class.validate(param, value)
+        current_members = @property_value_cache_hash[:members]
+        if param == :members
+            # If we are meant to be authoritative for the group membership
+            # then remove all existing members who haven't been specified
+            # in the manifest.
+            if @resource[:auth_membership] and not current_members.nil?
+                remove_unwanted_members(current_members, value)
+             end
+             
+             # if they're not a member, make them one.
+             add_members(current_members, value)
+        else
+            exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
+            # JJM: The following line just maps the NS name to the DS name
+            #      e.g. { :uid => 'UniqueID' }
+            exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(param)]
+            # JJM: The following line sends the actual value to set the property to
+            exec_arg_vector << value.to_s
+            begin
+                execute(exec_arg_vector)
+            rescue Puppet::ExecutionFailure => detail
+                raise Puppet::Error, "Could not set %s on %s[%s]: %s" % [param, @resource.class.name, @resource.name, detail]
+            end
+        end
     end
     
     # NBK: we override @parent.create as we need to execute a series of commands
@@ -307,20 +336,50 @@ class DirectoryService < Puppet::Provider::NameService
         end
         
         # Now we create all the standard properties
-        Puppet::Type.type(:user).validproperties.each do |property|
+        Puppet::Type.type(@resource.class.name).validproperties.each do |property|
             next if property == :ensure
             if value = @resource.should(property) and value != ""
-                exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
-                exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(property)]
-                next if property == :password  # skip setting the password here
-                exec_arg_vector << value.to_s
+                if property == :members
+                    add_members(nil, value)
+                else
+                    exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
+                    exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(property)]
+                    next if property == :password  # skip setting the password here
+                    exec_arg_vector << value.to_s
+                    begin
+                      execute(exec_arg_vector)
+                    rescue Puppet::ExecutionFailure => detail
+                        raise Puppet::Error, "Could not create %s %s: %s" %
+                            [@resource.class.name, @resource.name, detail]
+                    end
+                end
+            end
+        end
+    end
+    
+    def remove_unwanted_members(current_members, new_members)
+        current_members.each do |member|
+            if not value.include?(member)
+                cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-d", member, @resource[:name]]
                 begin
-                  execute(exec_arg_vector)
+                     execute(cmd)
                 rescue Puppet::ExecutionFailure => detail
-                    raise Puppet::Error, "Could not create %s %s: %s" %
-                        [@resource.class.name, @resource.name, detail]
-                end  
-            end
+                     raise Puppet::Error, "Could not set %s on %s[%s]: %s" % [param, @resource.class.name, @resource.name, detail]
+                end
+             end
+         end
+    end
+    
+    def add_members(current_members, new_members)
+        new_members.each do |user|
+           if current_members.nil? or not current_members.include?(user)
+               cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-a", user, @resource[:name]]
+               begin
+                    execute(cmd)
+               rescue Puppet::ExecutionFailure => detail
+                    raise Puppet::Error, "Could not set %s on %s[%s]: %s" % [param, @resource.class.name, @resource.name, detail]
+               end
+           end
         end
     end
     
@@ -376,4 +435,4 @@ class DirectoryService < Puppet::Provider::NameService
         return @property_value_cache_hash
     end
 end
-end
+end
\ No newline at end of file
diff --git a/lib/puppet/type/computer.rb b/lib/puppet/type/computer.rb
new file mode 100644
index 0000000..0c0a709
--- /dev/null
+++ b/lib/puppet/type/computer.rb
@@ -0,0 +1,59 @@
+Puppet::Type.newtype(:computer) do
+    
+    @doc = "Computer object management using DirectoryService on OS X.
+    
+    Note that these are distinctly different kinds of objects to 'hosts',
+    as they require a MAC address and can have all sorts of policy attached to
+    them.
+    
+    This provider only manages Computer objects in the local directory service
+    domain, not in remote directories.
+    
+    If you wish to manage /etc/hosts on Mac OS X, then simply use the host
+    type as per other platforms.
+    
+    This type primarily exists to create localhost Computer objects that MCX
+    policy can then be attached to."
+    
+    # ensurable
+    
+    # We autorequire the computer object in case it is being managed at the
+    # file level by Puppet.
+    
+    autorequire(:file) do
+        if self[:name]
+            "/var/db/dslocal/nodes/Default/computers/#{self[:name]}.plist"
+        else
+            nil
+        end
+    end
+    
+    newproperty(:ensure, :parent => Puppet::Property::Ensure) do
+        newvalue(:present) do
+            provider.create
+        end
+
+        newvalue(:absent) do
+            Puppet.notice "prop ensure = absent"
+            provider.delete
+        end
+    end
+    
+    newparam(:name) do
+        desc "The "
+        isnamevar
+    end
+    
+    newparam(:realname) do
+        desc "realname"
+    end
+        
+    newproperty(:en_address) do
+        desc "The MAC address of the primary network interface. Must match en0."
+    end
+    
+    newproperty(:ip_address) do
+        desc "The IP Address of the Computer object."
+    end
+    
+end
\ No newline at end of file
diff --git a/lib/puppet/type/group.rb b/lib/puppet/type/group.rb
index 29486d3..1167962 100755
--- a/lib/puppet/type/group.rb
+++ b/lib/puppet/type/group.rb
@@ -11,15 +11,22 @@ require 'facter'
 
 module Puppet
     newtype(:group) do
-        @doc = "Manage groups.  This type can only create groups.  Group
-            membership must be managed on individual users.  This resource type
-            uses the prescribed native tools for creating groups and generally
-            uses POSIX APIs for retrieving information about them.  It does
-            not directly modify ``/etc/group`` or anything.
+        @doc = "Manage groups. On most platforms this can only create groups.
+            Group membership must be managed on individual users.  
+            
+            On OS X, group membership is managed as an attribute of the group.
+            This resource type uses the prescribed native tools for creating 
+            groups and generally uses POSIX APIs for retrieving information
+            about them.  It does not directly modify ``/etc/group`` or anything.
             
             For most platforms, the tools used are ``groupadd`` and its ilk;
-            for Mac OS X, NetInfo is used.  This is currently unconfigurable,
-            but if you desperately need it to be so, please contact us."
+            for Mac OS X, dscl/dseditgroup are used.
+                
+            This is currently unconfigurable, but if you desperately need it
+            to be so, please contact us."
+        
+        feature :manages_members,
+            "For directories where membership is an attribute of groups not users."
 
         ensurable do
             desc "Create or remove the group."
@@ -73,13 +80,28 @@ module Puppet
                 return gid
             end
         end
+        
+        newproperty(:members, :array_matching => :all, :required_features => :manages_members) do
+            desc "The members of the group. For directory services where group
+            membership is stored in the group objects, not the users."
+            
+            def change_to_s(currentvalue, newvalue)
+                currentvalue = currentvalue.join(",") if currentvalue != :absent
+                newvalue = newvalue.join(",")
+                super(currentvalue, newvalue)
+            end
+        end
+        
+        newparam(:auth_membership) do
+            desc "whether the provider is authoritative for group membership."
+            defaultto true
+        end
 
         newparam(:name) do
             desc "The group name.  While naming limitations vary by
                 system, it is advisable to keep the name to the degenerate
                 limitations, which is a maximum of 8 characters beginning with
                 a letter."
-
             isnamevar
         end
 
diff --git a/spec/unit/type/computer.rb b/spec/unit/type/computer.rb
new file mode 100755
index 0000000..ec3c797
--- /dev/null
+++ b/spec/unit/type/computer.rb
@@ -0,0 +1,86 @@
+#!/usr/bin/env ruby
+ 
+require File.dirname(__FILE__) + '/../../spec_helper'
+ 
+require 'puppet/type/computer'
+
+computer = Puppet::Type.type(:computer)
+
+describe Puppet.type(:computer), " when checking computer objects" do
+    before :each do
+        provider_class = Puppet::Type::Computer.provider(Puppet::Type::Computer.providers[0])
+        Puppet::Type::Computer.expects(:defaultprovider).returns provider_class
+        @resource = Puppet::Type::Computer.create(
+                        :name => "puppetcomputertest",
+                        :en_address => "aa:bb:cc:dd:ee:ff",
+                        :ip_address => "1.2.3.4")
+        @properties = {}
+        @ensure = Puppet::Type::Computer.attrclass(:ensure).new(:resource => @resource)
+    end
+  
+    after :each do
+        computer.clear
+        provider_class = nil
+        Puppet::Type::Computer.provider(nil)
+    end
+    
+    it "should be able to create a instance" do
+        provider_class = Puppet::Type::Computer.provider(Puppet::Type::Computer.providers[0])
+        Puppet::Type::Computer.expects(:defaultprovider).returns provider_class
+        computer.create(:name => "bar").should_not be_nil
+    end
+  
+    properties = [:en_address, :ip_address]
+    params = [:name]
+  
+    properties.each do |property|
+        it "should have a %s property" % property do
+            computer.attrclass(property).ancestors.should be_include(Puppet::Property)
+        end
+
+        it "should have documentation for its %s property" % property do
+            computer.attrclass(property).doc.should be_instance_of(String)
+        end
+        
+        it "should accept :absent as a value" do
+            prop = computer.attrclass(property).new(:resource => @resource)
+            prop.should = :absent
+            prop.should.must == :absent
+        end
+    end
+    
+    params.each do |param|
+        it "should have a %s parameter" % param do
+            computer.attrclass(param).ancestors.should be_include(Puppet::Parameter)
+        end
+
+        it "should have documentation for its %s parameter" % param do
+            computer.attrclass(param).doc.should be_instance_of(String)
+        end
+    end
+    
+    describe "default values" do
+        before do
+            provider_class = computer.provider(computer.providers[0])
+            computer.expects(:defaultprovider).returns provider_class
+        end
+
+        it "should be nil for en_address" do
+            computer.create(:name => :en_address)[:en_address].should == nil
+        end
+        
+        it "should be nil for ip_address" do
+            computer.create(:name => :ip_address)[:ip_address].should == nil
+        end
+    end
+        
+    describe "when managing the ensure property" do    
+        it "should support a :present value" do
+            lambda { @ensure.should = :present }.should_not raise_error
+        end
+    
+        it "should support an :absent value" do
+            lambda { @ensure.should = :absent }.should_not raise_error
+        end
+    end
+end
\ No newline at end of file

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list