summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2005-09-07 02:53:19 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2005-09-07 02:53:19 +0000
commitb77d29568e8adaa0104d8ee5b4c168ef6e3c4bdd (patch)
tree8372dc3bc8ed4cb293ff19e7cf6f920d8e7abb69
parent4f2812a2180894b645583c7a7c3aa8a012b2b0ac (diff)
downloadpuppet-b77d29568e8adaa0104d8ee5b4c168ef6e3c4bdd.tar.gz
puppet-b77d29568e8adaa0104d8ee5b4c168ef6e3c4bdd.tar.xz
puppet-b77d29568e8adaa0104d8ee5b4c168ef6e3c4bdd.zip
adding user and group classes (although user class is not yet functional), and added "is(state)" and "should(state)" methods for retrieving the respective values on a specified state
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@630 980ebf18-57e1-0310-9a29-db15c13687c0
-rw-r--r--lib/puppet/type.rb24
-rwxr-xr-xlib/puppet/type/group.rb156
-rwxr-xr-xlib/puppet/type/user.rb205
-rwxr-xr-xtest/types/tc_group.rb99
-rwxr-xr-xtest/types/tc_user.rb98
5 files changed, 582 insertions, 0 deletions
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb
index 509a10333..4a1afd91d 100644
--- a/lib/puppet/type.rb
+++ b/lib/puppet/type.rb
@@ -576,6 +576,28 @@ class Type < Puppet::Element
#---------------------------------------------------------------
#---------------------------------------------------------------
+ # retrieve the 'is' value for a specified state
+ def is(state)
+ if @states.include?(state)
+ return @states[state].is
+ else
+ return nil
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # retrieve the 'should' value for a specified state
+ def should(state)
+ if @states.include?(state)
+ return @states[state].should
+ else
+ return nil
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
# is the instance a managed instance? A 'yes' here means that
# the instance was created from the language, vs. being created
# in order resolve other questions, such as finding a package
@@ -1219,11 +1241,13 @@ end
require 'puppet/statechange'
require 'puppet/type/component'
require 'puppet/type/exec'
+require 'puppet/type/group'
require 'puppet/type/package'
require 'puppet/type/pfile'
require 'puppet/type/pfilebucket'
require 'puppet/type/service'
require 'puppet/type/symlink'
+require 'puppet/type/user'
require 'puppet/type/tidy'
#require 'puppet/type/typegen'
#require 'puppet/type/typegen/filetype'
diff --git a/lib/puppet/type/group.rb b/lib/puppet/type/group.rb
new file mode 100755
index 000000000..38a56dc31
--- /dev/null
+++ b/lib/puppet/type/group.rb
@@ -0,0 +1,156 @@
+# $Id$
+
+require 'etc'
+require 'facter'
+require 'puppet/type/state'
+
+module Puppet
+ class State
+ class GroupState < Puppet::State
+ class << self
+ def infomethod
+ if defined? @infomethod and @infomethod
+ return @infomethod
+ else
+ return @name
+ end
+ end
+ end
+
+ def retrieve
+ obj = @parent.getinfo(true)
+
+ method = self.class.infomethod
+
+ @is = obj.send(method)
+ end
+
+ def sync
+ obj = @parent.getinfo
+
+ cmd = nil
+ event = nil
+ if @should == :notfound
+ # we need to remove the object...
+ if obj.nil?
+ # the group already doesn't exist
+ return nil
+ end
+
+ cmd = ["groupdel", @parent.name]
+ type = "delete"
+ else
+ unless obj.nil?
+ raise Puppet::DevError,
+ "Got told to create a group that already exists"
+ end
+ # we're creating the group
+
+ # i can just tell i'm going to regret this
+ # why doesn't POSIX include interfaces for adding users
+ # and groups? it's stupid
+ cmd = ["groupadd"]
+ if gid = @parent.should(:gid)
+ cmd << "-g" << gid
+ end
+ cmd << @parent.name
+ type = "create"
+ end
+
+ output = %x{#{cmd.join(" ")} 2>&1}
+
+ unless $? == 0
+ raise Puppet::Error, "Could not %s group %s: %s" %
+ [type, @parent.name, output]
+ end
+
+ return "group_#{type}d".intern
+ end
+ end
+
+ class GroupGID < GroupState
+ @doc = "The group ID. Must be specified numerically. If not specified,
+ a number will be picked, which can result in ID differences across
+ systems and thus is not recommended. The method for picking GIDs
+ is basically to find the next GID above the highest existing GID
+ excluding those above 65000."
+ @name = :gid
+
+ def should=(gid)
+ if gid.is_a?(String)
+ if gid =~ /^[0-9]+$/
+ gid = Integer(gid)
+ end
+ end
+
+ Puppet.info "Setting gid to %s" % gid
+
+ @should = gid
+ end
+ end
+ end
+
+ class Type
+ class Group < Type
+ @states = [
+ Puppet::State::GroupGID
+ ]
+
+ @parameters = [
+ :name
+ ]
+
+ @doc = " "
+ @name = :group
+ @namevar = :name
+
+ def getinfo(refresh = false)
+ if @groupinfo.nil? or refresh == true
+ begin
+ @groupinfo = Etc.getgrnam(self[:name])
+ rescue ArgumentError => detail
+ # leave groupinfo as nil
+ end
+ end
+
+ @groupinfo
+ end
+
+ def initialize(hash)
+ @groupinfo = nil
+ super
+ end
+
+ def retrieve
+ obj = self.getinfo(true)
+
+ if obj.nil?
+ # the group does not exist
+
+ # unless we're in noop mode, we need to auto-pick a gid if there
+ # hasn't been one specified
+ unless @states.include?(:gid) or self.noop
+ highest = 0
+ Etc.group { |group|
+ if group.gid > highest
+ unless group.gid > 65000
+ highest = group.gid
+ end
+ end
+ }
+
+ self[:gid] = highest + 1
+ end
+
+ @states.each { |name, state|
+ state.is = :notfound
+ }
+
+ return
+ else
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb
new file mode 100755
index 000000000..cc7bbadaa
--- /dev/null
+++ b/lib/puppet/type/user.rb
@@ -0,0 +1,205 @@
+# $Id$
+
+require 'etc'
+require 'facter'
+require 'puppet/type/state'
+
+module Puppet
+ class State
+ class UserState < Puppet::State
+ @@userinfo = nil
+
+ class << self
+ attr_accessor :infomethod
+
+ def getinfo(refresh = false)
+ if @@userinfo.nil? or refresh == true
+ begin
+ @@userinfo = Etc.getpwnam(@parent[:name])
+ rescue ArgumentError => detail
+ @@userinfo = :notfound
+ end
+ end
+
+ @@userinfo
+ end
+ end
+
+ def retrieve
+ info = self.class.getinfo(true)
+
+ method = self.class.infomethod || self.class.name
+
+ unless method
+ raise Puppet::DevError,
+ "Could not retrieve info method for state %s" % self.class.name
+ end
+
+ unless info.respond_to?(method)
+ raise Puppet::DevError, "UserInfo object does not respond to %s" %
+ method
+ end
+
+ @is = info.send(method)
+ end
+
+ def sync
+ end
+ end
+
+ class UserUID < UserState
+ @doc = "The user ID. Must be specified numerically. For new users being
+ created, if no user ID is specified then one will be chosen
+ automatically, which will likely result in the same user having
+ different IDs on different systems, which is not recommended."
+ @name = :uid
+ end
+
+ class UserGID < UserState
+ @doc = "The user's primary group. Can be specified numerically or by name."
+ @name = :gid
+
+ def should=(gid)
+ method = :getgrgid
+ if gid.is_a?(String)
+ if gid =~ /^[0-9]+$/
+ gid = Integer(gid)
+ else
+ method = :getgrnam
+ end
+ end
+
+ # FIXME this should really check to see if we already have a group
+ # ready to be managed; if so, then we should just mark it as a prereq
+ begin
+ ginfo = Etc.send(method, gid)
+ rescue ArgumentError => detail
+ raise Puppet::Error, "Could not find group %s: %s" % [gid, detail]
+ end
+
+ @should = ginfo.gid
+ end
+
+ def sync
+ return :executed_command
+ end
+ end
+
+ class UserComment < UserState
+ @doc = "The expected return code. An error will be returned if the
+ executed command returns something else."
+ @name = :comment
+
+ def retrieve
+ end
+
+ def sync
+ return :executed_command
+ end
+ end
+
+ class UserHome < UserState
+ @doc = "The expected return code. An error will be returned if the
+ executed command returns something else."
+ @name = :home
+
+ def retrieve
+ end
+
+ def sync
+ return :executed_command
+ end
+ end
+
+ class UserShell < UserState
+ @doc = "The expected return code. An error will be returned if the
+ executed command returns something else."
+ @name = :shell
+
+ def retrieve
+ end
+
+ def sync
+ return :executed_command
+ end
+ end
+
+ class UserLocked < UserState
+ @doc = "The expected return code. An error will be returned if the
+ executed command returns something else."
+ @name = :locked
+
+ def retrieve
+ end
+
+ def sync
+ return :executed_command
+ end
+ end
+
+ class UserExpire < UserState
+ @doc = "The expected return code. An error will be returned if the
+ executed command returns something else."
+ @name = :expire
+
+ def retrieve
+ end
+
+ def sync
+ return :executed_command
+ end
+ end
+
+ class UserInactive < UserState
+ @doc = "The expected return code. An error will be returned if the
+ executed command returns something else."
+ @name = :inactive
+
+ def retrieve
+ end
+
+ def sync
+ return :executed_command
+ end
+ end
+
+ end
+
+ class Type
+ class User < Type
+ @states = [
+ Puppet::State::UserUID,
+ Puppet::State::UserGID,
+ Puppet::State::UserComment,
+ Puppet::State::UserHome,
+ Puppet::State::UserShell,
+ Puppet::State::UserLocked,
+ Puppet::State::UserExpire,
+ Puppet::State::UserInactive
+ ]
+
+ @parameters = [
+ :name
+ ]
+
+ @doc = "
+ "
+ @name = :user
+ @namevar = :name
+
+ def retrieve
+ info = Puppet::State::UserState.getinfo
+
+ if info == :notfound
+ # the user does not exist
+ @states.each { |name, state|
+ state.is = :notfound
+ }
+ return
+ else
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/test/types/tc_group.rb b/test/types/tc_group.rb
new file mode 100755
index 000000000..7f3c1d01f
--- /dev/null
+++ b/test/types/tc_group.rb
@@ -0,0 +1,99 @@
+if __FILE__ == $0
+ $:.unshift '..'
+ $:.unshift '../../lib'
+ $puppetbase = "../../../../language/trunk"
+end
+
+# $Id$
+
+require 'etc'
+require 'puppet/type'
+require 'puppettest'
+require 'test/unit'
+
+class TestGroup < TestPuppet
+ def setup
+ Puppet[:loglevel] = :debug if __FILE__ == $0
+ super
+ end
+
+ def groupnames
+ %x{groups}.chomp.split(/ /)
+ end
+
+ def groupids
+ Process.groups
+ end
+
+ def test_eachmethod
+ obj = Etc.getgrnam(groupnames()[0])
+
+ assert(obj, "Could not retrieve test group object")
+
+ Puppet::Type::Group.validstates.each { |name, state|
+ assert_nothing_raised {
+ method = state.infomethod
+ assert(method, "State %s has no infomethod" % name)
+ assert(obj.respond_to?(method), "State %s has an invalid method %s" %
+ [name, method])
+ }
+ }
+ end
+
+ def test_owngroups
+ groupnames().each { |group|
+ gobj = nil
+ comp = nil
+ assert_nothing_raised {
+ gobj = Puppet::Type::Group.new(
+ :name => group,
+ :check => [:gid]
+ )
+
+ comp = newcomp("grouptest %s" % group, gobj)
+ }
+
+ trans = nil
+ assert_nothing_raised {
+ trans = comp.evaluate
+ }
+
+ assert(gobj.is(:gid), "Failed to retrieve gid")
+ }
+ end
+
+ if Process.uid == 0
+ def test_mkgroup
+ gobj = nil
+ comp = nil
+ name = "pptestgr"
+ assert_nothing_raised {
+ gobj = Puppet::Type::Group.new(
+ :name => name
+ )
+
+ comp = newcomp("groupmaker %s" % name, gobj)
+ }
+
+ trans = nil
+ assert_nothing_raised {
+ trans = comp.evaluate
+ }
+
+ events = nil
+ assert_nothing_raised {
+ events = trans.evaluate.reject { |e| e.nil? }.collect { |e| e.event }
+ }
+
+ assert_equal([:group_created], events, "Incorrect group events")
+
+ assert_nothing_raised {
+ events = trans.rollback.reject { |e| e.nil? }.collect { |e| e.event }
+ }
+
+ assert_equal([:group_deleted], events, "Incorrect deletion group events")
+ end
+ else
+ $stderr.puts "Not running as root; skipping group creation tests."
+ end
+end
diff --git a/test/types/tc_user.rb b/test/types/tc_user.rb
new file mode 100755
index 000000000..3522539d5
--- /dev/null
+++ b/test/types/tc_user.rb
@@ -0,0 +1,98 @@
+if __FILE__ == $0
+ $:.unshift '..'
+ $:.unshift '../../lib'
+ $puppetbase = "../../../../language/trunk"
+end
+
+# $Id$
+
+require 'puppet/type'
+require 'test/unit'
+
+class TestType < Test::Unit::TestCase
+ def test_typemethods
+ assert_nothing_raised() {
+ Puppet::Type.buildstatehash
+ }
+
+ Puppet::Type.eachtype { |type|
+ name = nil
+ assert_nothing_raised() {
+ name = type.name
+ }
+
+ assert(
+ name
+ )
+
+ assert_equal(
+ type,
+ Puppet::Type.type(name)
+ )
+
+ assert(
+ type.namevar
+ )
+
+ assert_not_nil(
+ type.states
+ )
+
+ assert_not_nil(
+ type.validstates
+ )
+
+ assert(
+ type.validparameter?(type.namevar)
+ )
+ }
+ end
+
+ def test_stringvssymbols
+ file = nil
+ path = "/tmp/testfile"
+ assert_nothing_raised() {
+ system("rm -f %s" % path)
+ file = Puppet::Type::PFile.new(
+ :path => path,
+ :create => true,
+ :recurse => true,
+ :checksum => "md5"
+ )
+ }
+ assert_nothing_raised() {
+ file.retrieve
+ }
+ assert_nothing_raised() {
+ file.sync
+ }
+ Puppet::Type::PFile.clear
+ assert_nothing_raised() {
+ system("rm -f %s" % path)
+ file = Puppet::Type::PFile.new(
+ "path" => path,
+ "create" => true,
+ "recurse" => true,
+ "checksum" => "md5"
+ )
+ }
+ assert_nothing_raised() {
+ file.retrieve
+ }
+ assert_nothing_raised() {
+ file[:path]
+ }
+ assert_nothing_raised() {
+ file["path"]
+ }
+ assert_nothing_raised() {
+ file[:recurse]
+ }
+ assert_nothing_raised() {
+ file["recurse"]
+ }
+ assert_nothing_raised() {
+ file.sync
+ }
+ end
+end