diff options
author | Luke Kanies <luke@madstop.com> | 2008-08-18 21:50:59 -0500 |
---|---|---|
committer | Luke Kanies <luke@madstop.com> | 2008-08-20 12:13:25 -0500 |
commit | a5ab52c628cae7ac9ed5ca1bd5de779944840802 (patch) | |
tree | d2c0db0f2859efaa25225fba82c40da94623d910 | |
parent | 4f275b669b0336c6032204bfa9694fa6522eb267 (diff) | |
download | puppet-a5ab52c628cae7ac9ed5ca1bd5de779944840802.tar.gz puppet-a5ab52c628cae7ac9ed5ca1bd5de779944840802.tar.xz puppet-a5ab52c628cae7ac9ed5ca1bd5de779944840802.zip |
Adding files temporarily, since I've decided this work is a dead-end.
I'm merely adding these so that they're in the history if I decided to
look at them again.
Signed-off-by: Luke Kanies <luke@madstop.com>
-rw-r--r-- | lib/puppet/provider/newfile/default.rb | 89 | ||||
-rw-r--r-- | lib/puppet/type/newfile.rb | 38 | ||||
-rw-r--r-- | lib/puppet/util/state_machine.rb | 52 | ||||
-rwxr-xr-x | spec/unit/provider/newfile/default.rb | 181 | ||||
-rw-r--r-- | spec/unit/type/newfile.rb | 118 | ||||
-rwxr-xr-x | spec/unit/util/state_machine.rb | 59 |
6 files changed, 537 insertions, 0 deletions
diff --git a/lib/puppet/provider/newfile/default.rb b/lib/puppet/provider/newfile/default.rb new file mode 100644 index 000000000..4da98718f --- /dev/null +++ b/lib/puppet/provider/newfile/default.rb @@ -0,0 +1,89 @@ +Puppet::Type.type(:newfile).provide(:default) do + # Remove the file. + def destroy + end + + # Does the file currently exist? + def exist? + ! stat.nil? + end + + def content + return :absent unless exist? + begin + File.read(name) + rescue => detail + fail "Could not read %s: %s" % [name, detail] + end + end + + def content=(value) + File.open(name, "w") { |f| f.print value } + end + + def flush + @stat = nil + end + + def group + return :absent unless exist? + stat.gid + end + + def group=(value) + File.chown(nil, value, name) + end + + def mkdir + begin + Dir.mkdir(name) + rescue Errno::ENOENT + fail "Cannot create %s; parent directory does not exist" % name + rescue => detail + fail "Could not create directory %s: %s" % [name, detail] + end + end + + def mkfile + end + + def mklink + end + + def mode + return :absent unless exist? + stat.mode & 007777 + end + + def mode=(value) + File.chmod(value, name) + end + + def owner + return :absent unless exist? + stat.uid + end + + def owner=(value) + File.chown(value, nil, name) + end + + def type + return :absent unless exist? + stat.ftype + end + + private + + def stat + unless defined?(@stat) and @stat + begin + @stat = File.stat(name) + # Map the property names to the stat values, yo. + rescue Errno::ENOENT + @stat = nil + end + end + @stat + end +end diff --git a/lib/puppet/type/newfile.rb b/lib/puppet/type/newfile.rb new file mode 100644 index 000000000..e8763748d --- /dev/null +++ b/lib/puppet/type/newfile.rb @@ -0,0 +1,38 @@ +Puppet::Type.newtype(:newfile) do + newparam(:path, :namevar => true) do + desc "The file path." + end + + newproperty(:ensure) do + desc "What type the file should be." + + newvalue(:absent) { provider.destroy } + newvalue(:file) { provider.mkfile } + newvalue(:directory) {provider.mkdir } + newvalue(:link) {provider.mklink} + + def retrieve + provider.type + end + end + + newproperty(:content) do + desc "The file content." + end + + newproperty(:owner) do + desc "The file owner." + end + + newproperty(:group) do + desc "The file group." + end + + newproperty(:mode) do + desc "The file mode." + end + + newproperty(:type) do + desc "The read-only file type." + end +end diff --git a/lib/puppet/util/state_machine.rb b/lib/puppet/util/state_machine.rb new file mode 100644 index 000000000..9329df3ce --- /dev/null +++ b/lib/puppet/util/state_machine.rb @@ -0,0 +1,52 @@ +require 'puppet/util' + +# A simple class for defining simple state machines. You define +# the state transitions, which translate to the states a given property +# can be in and the methods used to transition between those states. +# +# == Example +# +# newproperty(:ensure) do +# +# :absent => :present (:create) +# +# statemachine.new(:create => {:absent => :present}, :destroy => {:present => :absent}) +# +# mach.transition(:absent => :present).with :create +# mach.transition(:present => :absent).with :destroy +# +# mach.transition :create => {:absent => :present}, :destroy => {:present => :absent} + +require 'puppet/simple_graph' +require 'puppet/relationship' + +class Puppet::Util::StateMachine + class Transition < Puppet::Relationship + end + + def initialize(&block) + @graph = Puppet::SimpleGraph.new + @docs = {} + + instance_eval(&block) if block + end + + # Define a state, with docs. + def state(name, docs) + @docs[name] = docs + @graph.add_vertex(name) + end + + # Check whether a state is defined. + def state?(name) + @graph.vertex?(name) + end + + def transition(from, to) + raise ArgumentError, "Unknown starting state %s" % from unless state?(from) + raise ArgumentError, "Unknown ending state %s" % to unless state?(to) + raise ArgumentError, "Transition %s => %s already exists" % [from, to] if @graph.edge?(from, to) + transition = Transition.new(from, to) + @graph.add_edge(transition) + end +end diff --git a/spec/unit/provider/newfile/default.rb b/spec/unit/provider/newfile/default.rb new file mode 100755 index 000000000..4261e5599 --- /dev/null +++ b/spec/unit/provider/newfile/default.rb @@ -0,0 +1,181 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider = Puppet::Type.type(:newfile).provider(:default) + +describe provider do + %w{content owner group mode}.each do |attr| + it "should be able to determine the '#{attr}'" do + provider.new({}).should respond_to(attr) + end + + it "should be able to set the '#{attr}'" do + provider.new({}).should respond_to(attr + "=") + end + end + + it "should be able to determine the 'type'" do + provider.new({}).should respond_to(:type) + end + + it "should be able to tell if the file exists" do + provider.new({}).should respond_to(:exist?) + end + + it "should be able to create a file" do + provider.new({}).should respond_to(:mkfile) + end + + it "should be able to create a directory" do + provider.new({}).should respond_to(:mkdir) + end + + it "should be able to create a symlink" do + provider.new({}).should respond_to(:mklink) + end + + it "should be able to destroy the file" do + provider.new({}).should respond_to(:destroy) + end + + describe "when retrieving current state" do + before do + @file = provider.new :name => "/foo/bar" + end + + it "should not stat() the file more than once per transaction" do + stat = mock('stat') + File.expects(:stat).with("/foo/bar").returns stat + @file.exist? + @file.exist? + end + + it "should remove the cached stat after it has been flushed" do + File.expects(:stat).times(2).with("/foo/bar").returns "foo" + @file.exist? + @file.flush + @file.exist? + end + + describe "and the file does not exist" do + before do + File.stubs(:stat).with("/foo/bar").returns nil + end + + it "should correctly detect the file's absence" do + @file.should_not be_exist + end + + it "should consider the type to be absent" do + @file.type.should == :absent + end + + it "should consider the owner to be absent" do + @file.owner.should == :absent + end + + it "should consider the group to be absent" do + @file.group.should == :absent + end + + it "should consider the mode to be absent" do + @file.mode.should == :absent + end + + it "should consider the content to be absent" do + @file.content.should == :absent + end + end + + describe "and the file exists" do + before do + @stat = mock 'stat' + File.stubs(:stat).with("/foo/bar").returns @stat + end + + it "should correctly detect the file's presence" do + @file.should be_exist + end + + it "should return the filetype as the type" do + @stat.expects(:ftype).returns "file" + @file.type.should == "file" + end + + it "should return the Stat UID as the owner" do + @stat.expects(:uid).returns 50 + @file.owner.should == 50 + end + + it "should return the Stat GID as the group" do + @stat.expects(:gid).returns 50 + @file.group.should == 50 + end + + it "should return the Stat mode, shifted accordingly, as the mode" do + @stat.expects(:mode).returns 16877 + @file.mode.should == 493 # 755 in decimal + end + + it "should return the contents of the file as the content" do + File.expects(:read).with("/foo/bar").returns "fooness" + @file.content.should == "fooness" + end + + it "should fail appropriately when reading the file's content fails" do + File.expects(:read).with("/foo/bar").raises RuntimeError + lambda { @file.content }.should raise_error(Puppet::Error) + end + end + end + + describe "when creating the file" do + before do + @file = provider.new :name => "/foo/bar" + end + + it "should create a directory when asked" do + Dir.expects(:mkdir).with("/foo/bar") + @file.mkdir + end + + it "should indicate a problem with the parent directory if Errno::ENOENT is thrown when creating the directory" do + Dir.expects(:mkdir).with("/foo/bar").raises Errno::ENOENT + lambda { @file.mkdir }.should raise_error(Puppet::Error) + end + + it "should fail helpfully if a different problem creating the directory is encountered" do + Dir.expects(:mkdir).with("/foo/bar").raises RuntimeError + lambda { @file.mkdir }.should raise_error(Puppet::Error) + end + end + + describe "when changing the file" do + before do + @file = provider.new :name => "/foo/bar" + end + + it "should chown the file to the provided UID when setting the owner" do + File.expects(:chown).with(50, nil, "/foo/bar") + @file.owner = 50 + end + + it "should chown the file to the provided GID when setting the group" do + File.expects(:chown).with(nil, 50, "/foo/bar") + @file.group = 50 + end + + it "should chmod the file to the provided mode when setting the mode" do + File.expects(:chmod).with(493, "/foo/bar") # decimal, not octal + @file.mode = 493 + end + + it "should write the provided contents to the file when the content is set" do + fh = mock 'fh' + File.expects(:open).with("/foo/bar", "w").yields fh + fh.expects(:print).with "foo" + @file.content = "foo" + end + end +end diff --git a/spec/unit/type/newfile.rb b/spec/unit/type/newfile.rb new file mode 100644 index 000000000..6929d51c3 --- /dev/null +++ b/spec/unit/type/newfile.rb @@ -0,0 +1,118 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +type = Puppet::Type.type(:newfile) + +describe type do + describe "when validating attributes" do + %w{path}.each do |attr| + it "should have a '#{attr}' parameter" do + Puppet::Type.type(:newfile).attrtype(attr.intern).should == :param + end + end + + %w{content ensure owner group mode type}.each do |attr| + it "should have a '#{attr}' property" do + Puppet::Type.type(:newfile).attrtype(attr.intern).should == :property + end + end + + it "should have its 'path' attribute set as its namevar" do + Puppet::Type.type(:newfile).namevar.should == :path + end + end + + describe "when validating 'ensure'" do + before do + @file = type.create :path => "/foo/bar" + end + + it "should support 'absent' as a value" do + lambda { @file[:ensure] = :absent }.should_not raise_error + end + + it "should support 'file' as a value" do + lambda { @file[:ensure] = :file }.should_not raise_error + end + + it "should support 'directory' as a value" do + lambda { @file[:ensure] = :directory }.should_not raise_error + end + + it "should support 'link' as a value" do + lambda { @file[:ensure] = :link }.should_not raise_error + end + + it "should not support other values" do + lambda { @file[:ensure] = :foo }.should raise_error(Puppet::Error) + end + end + + describe "when managing the file's type" do + before do + @file = type.create :path => "/foo/bar", :ensure => :absent + + # We need a catalog to do our actual work; this kinda blows. + @catalog = Puppet::Node::Catalog.new + @catalog.add_resource @file + Puppet::Util::Log.newdestination :console + end + + it "should use the provider's :type method to determine the current file type" do + @file.provider.expects(:type).returns :file + @file.retrieve + end + + it "should use 'destroy' to remove the file" do + @file.provider.stubs(:type).returns :file + @file.provider.expects(:destroy) + @catalog.apply + end + + it "should use 'mkfile' to create a file when the file is absent" do + @file[:ensure] = :file + @file.provider.stubs(:type).returns :absent + @file.provider.expects(:mkfile) + @catalog.apply + end + + it "should destroy the file and then use 'mkfile' to create a file when the file is present" do + @file[:ensure] = :file + @file.provider.stubs(:type).returns :directory + @file.provider.expects(:destroy) + @file.provider.expects(:mkfile) + @catalog.apply + end + + it "should use 'mkdir' to make a directory when the file is absent" do + @file[:ensure] = :directory + @file.provider.stubs(:type).returns :absent + @file.provider.expects(:mkdir) + @catalog.apply + end + + it "should destroy the file then use 'mkdir' to make a directory when the file is present" do + @file[:ensure] = :directory + @file.provider.stubs(:type).returns :file + @file.provider.expects(:destroy) + @file.provider.expects(:mkdir) + @catalog.apply + end + + it "should use 'mklink' to make a link when the file is absent" do + @file[:ensure] = :link + @file.provider.stubs(:type).returns :absent + @file.provider.expects(:mklink) + @catalog.apply + end + + it "should destroy the file then use 'mklink' to make a link when the file is present" do + @file[:ensure] = :link + @file.provider.stubs(:type).returns :file + @file.provider.expects(:destroy) + @file.provider.expects(:mklink) + @catalog.apply + end + end +end diff --git a/spec/unit/util/state_machine.rb b/spec/unit/util/state_machine.rb new file mode 100755 index 000000000..e8ffc629d --- /dev/null +++ b/spec/unit/util/state_machine.rb @@ -0,0 +1,59 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/util/state_machine' + +describe Puppet::Util::StateMachine do + before do + @class = Puppet::Util::StateMachine + @machine = @class.new + end + + it "should instance_eval any provided block" do + f = @class.new { state(:foo, "foo") } + f.should be_state(:foo) + end + + it "should be able to declare states" do + @machine.should respond_to(:state) + end + + it "should require documentation when declaring states" do + lambda { @machine.state(:foo) }.should raise_error(ArgumentError) + end + + it "should be able to detect when states are set" do + @machine.state(:foo, "bar") + @machine.should be_state(:foo) + end + + it "should be able to declare transitions" do + @machine.should respond_to(:transition) + end + + describe "when adding transitions" do + it "should fail if the starting state of a transition is unknown" do + @machine.state(:end, "foo") + lambda { @machine.transition(:start, :end) }.should raise_error(ArgumentError) + end + + it "should fail if the ending state of a transition is unknown" do + @machine.state(:start, "foo") + lambda { @machine.transition(:start, :end) }.should raise_error(ArgumentError) + end + + it "should fail if an equivalent transition already exists" do + @machine.state(:start, "foo") + @machine.state(:end, "foo") + @machine.transition(:start, :end) + lambda { @machine.transition(:start, :end) }.should raise_error(ArgumentError) + end + end + + describe "when making a transition" do + it "should require the initial state" + + it "should return all of the transitions to be made" + end +end |