summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-04-20 05:14:13 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-04-20 05:14:13 +0000
commite24a299511f6a23019edfee16e2541c8078d3f5d (patch)
tree304cd64de34e628f922ba3695a67b65308562894
parentac049810032ed655b0ea32650127d38df39e1089 (diff)
downloadpuppet-e24a299511f6a23019edfee16e2541c8078d3f5d.tar.gz
A simple first version of an object (called "pelement") server is now in place. There is not yet a client, and the tests are pretty simple so far -- only files have been tested yet. I had to make a significant number of modifications to the file object in order to get this all to work, and one of the big changes I made is to the internals of the checksum state.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1123 980ebf18-57e1-0310-9a29-db15c13687c0
-rw-r--r--lib/puppet/client/pelement.rb20
-rwxr-xr-xlib/puppet/server/pelement.rb101
-rw-r--r--lib/puppet/type.rb30
-rwxr-xr-xlib/puppet/type/pfile/checksum.rb145
-rwxr-xr-xlib/puppet/type/pfile/ensure.rb4
-rwxr-xr-xlib/puppet/type/pfile/source.rb2
-rw-r--r--lib/puppet/type/pfile/target.rb65
-rw-r--r--lib/puppet/type/state.rb23
-rw-r--r--test/server/pelement.rb81
9 files changed, 378 insertions, 93 deletions
diff --git a/lib/puppet/client/pelement.rb b/lib/puppet/client/pelement.rb
new file mode 100644
index 000000000..116624003
--- /dev/null
+++ b/lib/puppet/client/pelement.rb
@@ -0,0 +1,20 @@
+class Puppet::Client::FileClient < Puppet::Client::ProxyClient
+ @drivername = :FileServer
+
+ # set up the appropriate interface methods
+ @handler = Puppet::Server::FileServer
+
+ self.mkmethods
+
+ def initialize(hash = {})
+ if hash.include?(:FileServer)
+ unless hash[:FileServer].is_a?(Puppet::Server::FileServer)
+ raise Puppet::DevError, "Must pass an actual FS object"
+ end
+ end
+
+ super(hash)
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/server/pelement.rb b/lib/puppet/server/pelement.rb
new file mode 100755
index 000000000..791576666
--- /dev/null
+++ b/lib/puppet/server/pelement.rb
@@ -0,0 +1,101 @@
+require 'puppet'
+require 'puppet/server'
+
+module Puppet
+
+class Server::PElementServer
+ attr_accessor :local
+
+ @interface = XMLRPC::Service::Interface.new("fileserver") { |iface|
+ iface.add_method("string describe(string, string, array, array)")
+ iface.add_method("string list(string, string, boolean, array)")
+ }
+
+ # Describe a given object. This returns the 'is' values for every state
+ # available on the object type.
+ def describe(type, name, retrieve = nil, ignore = [], format = "yaml", client = nil, clientip = nil)
+ @local = true unless client
+ typeklass = nil
+ unless typeklass = Puppet.type(type)
+ raise Puppet::Error, "Puppet type %s is unsupported" % type
+ end
+
+ obj = nil
+
+ retrieve ||= :all
+
+ if obj = typeklass[name]
+ obj[:check] = retrieve
+ else
+ begin
+ obj = typeklass.create(:name => name, :check => retrieve)
+ rescue Puppet::Error => detail
+ raise Puppet::Error, "%s[%s] could not be created: %s" %
+ [type, name, detail]
+ end
+ end
+
+ trans = obj.to_trans
+
+ # Now get rid of any attributes they specifically don't want
+ ignore.each do |st|
+ if trans.include? st
+ trans.delete(st)
+ end
+ end
+
+ if @local
+ return trans
+ else
+ str = nil
+ case format
+ when "yaml":
+ str = YAML.dump(trans)
+ else
+ raise XMLRPC::FaultException.new(
+ 1, "Unavailable config format %s" % format
+ )
+ end
+ return CGI.escape(str)
+ end
+ end
+
+ # Create a new fileserving module.
+ def initialize(hash = {})
+ if hash[:Local]
+ @local = hash[:Local]
+ else
+ @local = false
+ end
+ end
+
+ def list(type, name, client = nil, clientip = nil)
+ end
+
+ private
+
+ def authcheck(file, mount, client, clientip)
+ unless mount.allowed?(client, clientip)
+ mount.warning "%s cannot access %s" %
+ [client, file]
+ raise Puppet::Server::AuthorizationError, "Cannot access %s" % mount
+ end
+ end
+
+ # Deal with ignore parameters.
+ def handleignore(children, path, ignore)
+ ignore.each { |ignore|
+ Dir.glob(File.join(path,ignore), File::FNM_DOTMATCH) { |match|
+ children.delete(File.basename(match))
+ }
+ }
+ return children
+ end
+
+ def to_s
+ "pelementserver"
+ end
+end
+end
+
+# $Id$
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb
index 612c3d13f..c11d51b9a 100644
--- a/lib/puppet/type.rb
+++ b/lib/puppet/type.rb
@@ -9,7 +9,10 @@ require 'puppet/util'
# see the bottom of the file for the rest of the inclusions
module Puppet
+# The type is unknown
+class UnknownTypeError < Puppet::Error; end
class Type < Puppet::Element
+
# Types (which map to elements in the languages) are entirely composed of
# attribute value pairs. Generally, Puppet calls any of these things an
# 'attribute', but these attributes always take one of three specific
@@ -1682,6 +1685,24 @@ class Type < Puppet::Element
self.name
end
+ # Convert to a transportable object
+ def to_trans
+ # Collect all of the "is" values
+ retrieve()
+
+ trans = TransObject.new(self.name, self.class.name)
+
+ states().each do |state|
+ trans[state.name] = state.is
+ end
+
+ trans.tags = self.tags
+
+ # FIXME I'm currently ignoring 'parent' and 'path'
+
+ return trans
+ end
+
# instance methods dealing with actually doing work
public
@@ -2039,6 +2060,13 @@ class Type < Puppet::Element
on all packages."
munge do |args|
+ # If they've specified all, collect all known states
+ if args == :all
+ args = @parent.class.states.collect do |state|
+ state.name
+ end
+ end
+
unless args.is_a?(Array)
args = [args]
end
@@ -2054,6 +2082,8 @@ class Type < Puppet::Element
end
next if @parent.statedefined?(state)
+ next unless @parent.class.validstate?(state).checkable?
+
@parent.newstate(state)
}
end
diff --git a/lib/puppet/type/pfile/checksum.rb b/lib/puppet/type/pfile/checksum.rb
index d25f28c52..c1f00e5b8 100755
--- a/lib/puppet/type/pfile/checksum.rb
+++ b/lib/puppet/type/pfile/checksum.rb
@@ -16,6 +16,44 @@ module Puppet
@validtypes.include?(type)
end
+ @validtypes.each do |ctype|
+ newvalue(ctype) do
+ handlesum()
+ end
+ end
+
+ str = @validtypes.join("|")
+
+ newvalue(/^\{#{str}\}/) do
+ handlesum()
+ end
+
+ newvalue(:nosum) do
+ # nothing
+ :nochange
+ end
+
+ # Convert from the sum type to the stored checksum.
+ munge do |value|
+ unless defined? @checktypes
+ @checktypes = []
+ end
+
+ if FileTest.directory?(@parent[:path])
+ value = "time"
+ end
+
+
+ if value =~ /^\{(\w+)\}(.+)$/
+ @checktypes << $1
+ return $2
+ else
+ value = super
+ @checktypes << value
+ return getcachedsum()
+ end
+ end
+
def checktype
@checktypes[0]
end
@@ -80,15 +118,17 @@ module Puppet
# Calculate the sum from disk.
def getsum(checktype)
sum = ""
+
+ checktype = checktype.intern if checktype.is_a? String
case checktype
- when "md5", "md5lite":
+ when :md5, :md5lite:
unless FileTest.file?(@parent[:path])
@parent.info "Cannot MD5 sum directory %s" %
@parent[:path]
- # because we cannot sum directories, just delete ourselves
- # from the file so we won't sync
- @parent.delete(self[:path])
+ @should = [nil]
+ @is = nil
+ #@parent.delete(self[:path])
return
else
begin
@@ -117,35 +157,59 @@ module Puppet
@parent.delete(self.class.name)
end
end
- when "timestamp","mtime":
+ when :timestamp, :mtime:
sum = @parent.stat.mtime.to_s
#sum = File.stat(@parent[:path]).mtime.to_s
- when "time":
+ when :time:
sum = @parent.stat.ctime.to_s
#sum = File.stat(@parent[:path]).ctime.to_s
else
raise Puppet::Error, "Invalid sum type %s" % checktype
end
- return sum
+ return "{#{checktype}}" + sum.to_s
end
- # Convert from the sum type to the stored checksum.
- munge do |value|
- unless defined? @checktypes
- @checktypes = []
- end
- unless self.class.validtype?(value)
- self.fail "Invalid checksum type '%s'" % value
+ # At this point, we don't actually modify the system, we modify
+ # the stored state to reflect the current state, and then kick
+ # off an event to mark any changes.
+ def handlesum
+ if @is.nil?
+ raise Puppet::Error, "Checksum state for %s is somehow nil" %
+ @parent.name
end
- if FileTest.directory?(@parent[:path])
- value = "time"
- end
+ if @is == :absent
+ self.retrieve
- @checktypes << value
+ if self.insync?
+ self.debug "Checksum is already in sync"
+ return nil
+ end
+ #@parent.debug "%s(%s): after refresh, is '%s'" %
+ # [self.class.name,@parent.name,@is]
- return getcachedsum()
+ # If we still can't retrieve a checksum, it means that
+ # the file still doesn't exist
+ if @is == :absent
+ # if they're copying, then we won't worry about the file
+ # not existing yet
+ unless @parent.state(:source)
+ self.warning(
+ "File %s does not exist -- cannot checksum" %
+ @parent[:path]
+ )
+ end
+ return nil
+ end
+ end
+
+ # If the sums are different, then return an event.
+ if self.updatesum
+ return :file_changed
+ else
+ return nil
+ end
end
# Even though they can specify multiple checksums, the insync?
@@ -194,49 +258,6 @@ module Puppet
#@parent.debug "checksum state is %s" % self.is
end
-
- # At this point, we don't actually modify the system, we modify
- # the stored state to reflect the current state, and then kick
- # off an event to mark any changes.
- def sync
- if @is.nil?
- raise Puppet::Error, "Checksum state for %s is somehow nil" %
- @parent.name
- end
-
- if @is == :absent
- self.retrieve
-
- if self.insync?
- self.debug "Checksum is already in sync"
- return nil
- end
- #@parent.debug "%s(%s): after refresh, is '%s'" %
- # [self.class.name,@parent.name,@is]
-
- # If we still can't retrieve a checksum, it means that
- # the file still doesn't exist
- if @is == :absent
- # if they're copying, then we won't worry about the file
- # not existing yet
- unless @parent.state(:source)
- self.warning(
- "File %s does not exist -- cannot checksum" %
- @parent[:path]
- )
- end
- return nil
- end
- end
-
- # If the sums are different, then return an event.
- if self.updatesum
- return :file_changed
- else
- return nil
- end
- end
-
# Store the new sum to the state db.
def updatesum
result = false
diff --git a/lib/puppet/type/pfile/ensure.rb b/lib/puppet/type/pfile/ensure.rb
index 8a653e93b..9f44b780f 100755
--- a/lib/puppet/type/pfile/ensure.rb
+++ b/lib/puppet/type/pfile/ensure.rb
@@ -80,8 +80,9 @@ module Puppet
if state.linkmaker
self.set_directory
+ return :directory_created
else
- state.sync
+ return state.sync
end
else
self.fail "Cannot create a symlink without a target"
@@ -120,7 +121,6 @@ module Puppet
end
def retrieve
-
if stat = @parent.stat(false)
@is = stat.ftype.intern
else
diff --git a/lib/puppet/type/pfile/source.rb b/lib/puppet/type/pfile/source.rb
index c7c07d1c9..8d770890b 100755
--- a/lib/puppet/type/pfile/source.rb
+++ b/lib/puppet/type/pfile/source.rb
@@ -28,6 +28,8 @@ module Puppet
"
+ uncheckable
+
# Ask the file server to describe our file.
def describe(source)
sourceobj, path = @parent.uri2obj(source)
diff --git a/lib/puppet/type/pfile/target.rb b/lib/puppet/type/pfile/target.rb
index c136cc7ad..426c52b6d 100644
--- a/lib/puppet/type/pfile/target.rb
+++ b/lib/puppet/type/pfile/target.rb
@@ -5,31 +5,13 @@ module Puppet
desc "The target for creating a link. Currently, symlinks are the
only type supported."
- munge do |value|
- value
+ newvalue(:notlink) do
+ # We do nothing if the value is absent
+ return :nochange
end
- def retrieve
- if @parent.state(:ensure).should == :directory
- @is = self.should
- @linkmaker = true
- else
- if stat = @parent.stat
- if File.exists?(self.should) and tstat = File.lstat(self.should) and tstat.ftype == "directory" and @parent.recurse?
- @parent[:ensure] = :directory
- @is = self.should
- @linkmaker = true
- else
- @is = File.readlink(@parent[:path])
- @linkmaker = false
- end
- else
- @is = :absent
- end
- end
- end
-
- def sync
+ # Anything else, basically
+ newvalue(/./) do
target = self.should
if stat = @parent.stat
@@ -39,10 +21,9 @@ module Puppet
File.unlink(@parent[:path])
end
Dir.chdir(File.dirname(@parent[:path])) do
-
unless FileTest.exists?(target)
self.debug "Not linking to non-existent '%s'" % target
- return nil # Grrr, can't return
+ :nochange # Grrr, can't return
else
Puppet::Util.asuser(@parent.asuser()) do
mode = @parent.should(:mode)
@@ -55,9 +36,37 @@ module Puppet
end
end
- # We can't use "return" here because we're in an anonymous
- # block.
- return :link_created
+ :link_created
+ end
+ end
+ end
+
+ def retrieve
+ if @parent.state(:ensure).should == :directory
+ @is = self.should
+ @linkmaker = true
+ else
+ if stat = @parent.stat
+ # If we're just checking the value
+ if should = self.should and
+ should != :notlink
+ File.exists?(should) and
+ tstat = File.lstat(should) and
+ tstat.ftype == "directory" and
+ @parent.recurse?
+ @parent[:ensure] = :directory
+ @is = should
+ @linkmaker = true
+ else
+ if stat.ftype == "link"
+ @is = File.readlink(@parent[:path])
+ @linkmaker = false
+ else
+ @is = :notlink
+ end
+ end
+ else
+ @is = :absent
end
end
end
diff --git a/lib/puppet/type/state.rb b/lib/puppet/type/state.rb
index 05f876583..6d2b14cf2 100644
--- a/lib/puppet/type/state.rb
+++ b/lib/puppet/type/state.rb
@@ -18,6 +18,22 @@ class State < Puppet::Parameter
class << self
attr_accessor :unmanaged
attr_reader :name
+
+ def checkable
+ @checkable = true
+ end
+
+ def uncheckable
+ @checkable = false
+ end
+
+ def checkable?
+ if defined? @checkable
+ return @checkable
+ else
+ return true
+ end
+ end
end
# Create the value management variables.
@@ -82,13 +98,18 @@ class State < Puppet::Parameter
end
if event and event.is_a?(Symbol)
- return event
+ if event == :nochange
+ return nil
+ else
+ return event
+ end
else
# Return the appropriate event.
event = case self.should
when :present: (@parent.class.name.to_s + "_created").intern
when :absent: (@parent.class.name.to_s + "_removed").intern
else
+ warning self.should.inspect
(@parent.class.name.to_s + "_changed").intern
end
diff --git a/test/server/pelement.rb b/test/server/pelement.rb
new file mode 100644
index 000000000..3e55918dd
--- /dev/null
+++ b/test/server/pelement.rb
@@ -0,0 +1,81 @@
+if __FILE__ == $0
+ $:.unshift '../../lib'
+ $:.unshift '..'
+ $puppetbase = "../.."
+end
+
+require 'puppet'
+require 'puppet/server/pelement'
+require 'test/unit'
+require 'puppettest.rb'
+require 'base64'
+require 'cgi'
+
+class TestPElementServer < Test::Unit::TestCase
+ include ServerTest
+
+ def test_describe_file
+ # Make a file to describe
+ file = tempfile()
+ str = "yayness\n"
+
+ server = nil
+
+ assert_nothing_raised do
+ server = Puppet::Server::PElementServer.new()
+ end
+
+ [ [nil],
+ [[:content, :mode], []],
+ [[], [:content]],
+ [[:content], [:mode]]
+ ].each do |ary|
+ retrieve = ary[0] || []
+ ignore = ary[1] || []
+
+ File.open(file, "w") { |f| f.print str }
+
+ result = nil
+ assert_nothing_raised do
+ result = server.describe("file", file, *ary)
+ end
+
+ assert(result, "Could not retrieve file information")
+
+ assert_instance_of(Puppet::TransObject, result)
+
+ # Now we have to clear, so that the server's object gets removed
+ Puppet::Type.type(:file).clear
+
+ # And remove the file, so we can verify it gets recreated
+ File.unlink(file)
+
+ object = nil
+ assert_nothing_raised do
+ object = result.to_type
+ end
+
+ assert(object, "Could not create type")
+
+ retrieve.each do |state|
+ assert(object.should(state), "Did not retrieve %s" % state)
+ end
+
+ ignore.each do |state|
+ assert(! object.should(state), "Incorrectly retrieved %s" % state)
+ end
+
+ assert_events([:file_created], object)
+
+ assert(FileTest.exists?(file), "File did not get recreated")
+
+ if object.should(:content)
+ assert_equal(str, File.read(file), "File contents are not the same")
+ else
+ assert_equal("", File.read(file), "File content was incorrectly made")
+ end
+ end
+ end
+end
+
+# $Id$