summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Jacob <adam@hjksolutions.com>2008-03-23 20:33:06 -0700
committerAdam Jacob <adam@hjksolutions.com>2008-03-23 20:33:06 -0700
commitc74068fffb387955cff8b32c2c984c782ee1e0a0 (patch)
treebac2087b4d045a2a5f36450fdc10d04202bf3961
parent90d083563df3fc997b8cd3790f3b0b95d0855461 (diff)
downloadchef-c74068fffb387955cff8b32c2c984c782ee1e0a0.tar.gz
Adding a File provider
-rw-r--r--README.txt2
-rw-r--r--lib/chef/provider.rb33
-rw-r--r--lib/chef/provider/file.rb103
-rw-r--r--lib/chef/resource/file.rb19
-rw-r--r--spec/data/cookbooks/openldap/recipes/gigantor.rb2
-rw-r--r--spec/data/recipes/test.rb2
-rw-r--r--spec/data/seattle.txt1
-rw-r--r--spec/unit/provider/file_spec.rb169
-rw-r--r--spec/unit/provider_spec.rb46
-rw-r--r--spec/unit/recipe_spec.rb2
-rw-r--r--spec/unit/resource/file_spec.rb23
11 files changed, 376 insertions, 26 deletions
diff --git a/README.txt b/README.txt
index 2216d88244..57ba1c4ea9 100644
--- a/README.txt
+++ b/README.txt
@@ -10,7 +10,7 @@ I'm in ur netwerk, cookin up yer servers. :)
== FEATURES/PROBLEMS:
-* Uses a Ruby DSL in place of the puppet language
+* Uses a Ruby pidgin language in place of the puppet language
* Drastically simpler implementation
== SYNOPSIS:
diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb
new file mode 100644
index 0000000000..d296b63227
--- /dev/null
+++ b/lib/chef/provider.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: GNU General Public License version 2 or later
+#
+# This program and entire repository is free software; you can
+# redistribute it and/or modify it under the terms of the GNU
+# General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+class Chef
+ class Provider
+
+ attr_accessor :node, :new_resource, :current_resource
+
+ def initialize(node, new_resource)
+ @node = node
+ @new_resource = new_resource
+ @current_resource = nil
+ end
+
+ end
+end \ No newline at end of file
diff --git a/lib/chef/provider/file.rb b/lib/chef/provider/file.rb
new file mode 100644
index 0000000000..e59fd8bb7d
--- /dev/null
+++ b/lib/chef/provider/file.rb
@@ -0,0 +1,103 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: GNU General Public License version 2 or later
+#
+# This program and entire repository is free software; you can
+# redistribute it and/or modify it under the terms of the GNU
+# General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+require 'digest/md5'
+require 'etc'
+
+class Chef
+ class Provider
+ class File < Chef::Provider
+ def load_current_resource
+ @current_resource = Chef::Resource::File.new(@new_resource.name)
+ @current_resource.path(@new_resource.path)
+ if ::File.exist?(@current_resource.path) && ::File.readable?(@current_resource.path)
+ cstats = ::File.stat(@current_resource.path)
+ @current_resource.owner(cstats.uid)
+ @current_resource.group(cstats.gid)
+ @current_resource.mode("%o" % (cstats.mode & 007777))
+ end
+ @current_resource
+ end
+
+ def checksum
+ digest = Digest::MD5.new
+ IO.foreach(@current_resource.path) do |line|
+ digest << line
+ end
+ @current_resource.checksum(digest.hexdigest)
+ end
+
+ # Compare the ownership of a file. Returns true if they are the same, false if they are not.
+ def compare_owner
+ case @new_resource.owner
+ when /^\d+$/, Integer
+ @set_user_id = @new_resource.owner.to_i
+ @set_user_id == @current_resource.owner
+ else
+ # This raises an ArugmentError if you can't find the user
+ user_info = Etc.getpwnam(@new_resource.owner)
+ @set_user_id = user_info.uid
+ @set_user_id == @current_resource.owner
+ end
+ end
+
+ # Set the ownership on the file, assuming it is not set correctly already.
+ def set_owner
+ unless compare_owner
+ ::File.chown(@set_user_id, nil, @new_resource.path)
+ end
+ end
+
+ # Compares the group of a file. Returns true if they are the same, false if they are not.
+ def compare_group
+ case @new_resource.group
+ when /^\d+$/, Integer
+ @set_group_id = @new_resource.group.to_i
+ @set_group_id == @current_resource.group
+ else
+ group_info = Etc.getpwnam(@new_resource.group)
+ @set_group_id = group_info.gid
+ @set_group_id == @current_resource.group
+ end
+ end
+
+ def set_group
+ unless compare_group
+ ::File.chown(nil, @set_group_id, @new_resource.path)
+ end
+ end
+
+ def action_create
+ unless ::File.exists?(@new_resource.path)
+ ::File.open(@new_resource.path, "w+") { |f| }
+ end
+ set_owner
+ set_group
+ end
+
+ def action_delete
+ if ::File.exists?(@new_resource.path) && ::File.writable?(@new_resource.path)
+ ::File.delete(@new_resource.path)
+ end
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/chef/resource/file.rb b/lib/chef/resource/file.rb
index 0a2edb56a5..3132aa5a1a 100644
--- a/lib/chef/resource/file.rb
+++ b/lib/chef/resource/file.rb
@@ -21,14 +21,13 @@
class Chef
class Resource
class File < Chef::Resource
- attr_reader :backup, :checksum, :insure, :group, :mode, :owner, :path
-
+
def initialize(name, collection=nil, config=nil)
@resource_name = :file
super(name, collection, config)
@path = name
@backup = true
- @checksum = "md5sum"
+ @action = "create"
end
def backup(arg=nil)
@@ -45,21 +44,21 @@ class Chef
def checksum(arg=nil)
set_if_args(@checksum, arg) do
case arg
- when "md5sum", "mtime"
+ when /^[a-zA-Z0-9]{32}$/ # md5sum
@checksum = arg
else
- raise ArgumentError, "checksum must be md5sum or mtime!"
+ raise ArgumentError, "checksum must be an md5sum!"
end
end
end
- def insure(arg=nil)
- set_if_args(@insure, arg) do
+ def action(arg=nil)
+ set_if_args(@action, arg) do
case arg
- when "absent", "present"
- @insure = arg
+ when "create", "delete"
+ @action = arg
else
- raise ArgumentError, "insure must be absent or present!"
+ raise ArgumentError, "action must be create or delete!"
end
end
end
diff --git a/spec/data/cookbooks/openldap/recipes/gigantor.rb b/spec/data/cookbooks/openldap/recipes/gigantor.rb
index 4ebbaeb22d..b450eca7cd 100644
--- a/spec/data/cookbooks/openldap/recipes/gigantor.rb
+++ b/spec/data/cookbooks/openldap/recipes/gigantor.rb
@@ -1,3 +1,3 @@
cat "blanket" do
pretty_kitty false
-end \ No newline at end of file
+end
diff --git a/spec/data/recipes/test.rb b/spec/data/recipes/test.rb
index ce79f7109f..1c94b917f0 100644
--- a/spec/data/recipes/test.rb
+++ b/spec/data/recipes/test.rb
@@ -1,6 +1,6 @@
file "/etc/nsswitch.conf" do
- insure "present"
+ action "create"
owner "root"
group "root"
mode 0644
diff --git a/spec/data/seattle.txt b/spec/data/seattle.txt
new file mode 100644
index 0000000000..19f6290939
--- /dev/null
+++ b/spec/data/seattle.txt
@@ -0,0 +1 @@
+Seattle is a great town. Next time you visit, you should buy me a beer. \ No newline at end of file
diff --git a/spec/unit/provider/file_spec.rb b/spec/unit/provider/file_spec.rb
new file mode 100644
index 0000000000..b31a4d7ef1
--- /dev/null
+++ b/spec/unit/provider/file_spec.rb
@@ -0,0 +1,169 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: GNU General Public License version 2 or later
+#
+# This program and entire repository is free software; you can
+# redistribute it and/or modify it under the terms of the GNU
+# General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+require 'ostruct'
+
+require File.join(File.dirname(__FILE__), "..", "..", "spec_helper")
+
+describe Chef::Provider::File do
+ before(:each) do
+ @resource = Chef::Resource::File.new("seattle")
+ @resource.path(File.join(File.dirname(__FILE__), "..", "..", "data", "seattle.txt"))
+ @node = Chef::Node.new
+ @node.name "latte"
+ @provider = Chef::Provider::File.new(@node, @resource)
+ end
+
+ it "should return a Chef::Provider::File" do
+ @provider.should be_a_kind_of(Chef::Provider::File)
+ end
+
+ it "should store the resource passed to new as new_resource" do
+ @provider.new_resource.should eql(@resource)
+ end
+
+ it "should store the node passed to new as node" do
+ @provider.node.should eql(@node)
+ end
+
+ it "should load a current resource based on the one specified at construction" do
+ @provider.load_current_resource
+ @provider.current_resource.should be_a_kind_of(Chef::Resource::File)
+ @provider.current_resource.name.should eql(@resource.name)
+ @provider.current_resource.path.should eql(@resource.path)
+ @provider.current_resource.owner.should_not eql(nil)
+ @provider.current_resource.group.should_not eql(nil)
+ @provider.current_resource.mode.should_not eql(nil)
+ end
+
+ it "should load a mostly blank current resource if the file specified in new_resource doesn't exist/isn't readable" do
+ resource = Chef::Resource::File.new("seattle")
+ resource.path(File.join(File.dirname(__FILE__), "..", "..", "data", "woot.txt"))
+ node = Chef::Node.new
+ node.name "latte"
+ provider = Chef::Provider::File.new(node, resource)
+ provider.load_current_resource
+ provider.current_resource.should be_a_kind_of(Chef::Resource::File)
+ provider.current_resource.name.should eql(resource.name)
+ provider.current_resource.path.should eql(resource.path)
+ provider.current_resource.owner.should eql(nil)
+ provider.current_resource.group.should eql(nil)
+ provider.current_resource.mode.should eql(nil)
+ end
+
+ it "should load the correct value for owner of the current resource" do
+ stats = File.stat(@resource.path)
+ @provider.load_current_resource
+ @provider.current_resource.owner.should eql(stats.uid)
+ end
+
+ it "should load an md5 sum for an existing file" do
+ @provider.load_current_resource
+ @provider.current_resource.checksum("8d6152c7d62ea9188eda596c4d31e732")
+ end
+
+ it "should compare the current owner with the requested owner" do
+ @provider.load_current_resource
+ @provider.new_resource.stub!(:owner).and_return("adam")
+ Etc.stub!(:getpwnam).and_return(
+ OpenStruct.new(
+ :name => "adam",
+ :passwd => "foo",
+ :uid => 501,
+ :gid => 501,
+ :gecos => "Adam Jacob",
+ :dir => "/Users/adam",
+ :shell => "/bin/zsh",
+ :change => "0",
+ :uclass => "",
+ :expire => 0
+ )
+ )
+ @provider.current_resource.owner(501)
+ @provider.compare_owner.should eql(true)
+
+ @provider.current_resource.owner(777)
+ @provider.compare_owner.should eql(false)
+
+ @provider.new_resource.stub!(:owner).and_return(501)
+ @provider.current_resource.owner(501)
+ @provider.compare_owner.should eql(true)
+
+ @provider.new_resource.stub!(:owner).and_return("501")
+ @provider.current_resource.owner(501)
+ @provider.compare_owner.should eql(true)
+ end
+
+ it "should set the ownership on the file to the requested owner" do
+ @provider.load_current_resource
+ @provider.new_resource.stub!(:owner).and_return(9982398)
+ File.stub!(:chown).and_return(1)
+ File.should_receive(:chown).with(9982398, nil, @provider.current_resource.path)
+ lambda { @provider.set_owner }.should_not raise_error
+ end
+
+ it "should raise an exception if you are not root and try to change ownership" do
+ @provider.load_current_resource
+ @provider.new_resource.stub!(:owner).and_return(0)
+ if Process.uid != 0
+ lambda { @provider.set_owner }.should raise_error
+ end
+ end
+
+ it "should set the group on the file to the requested group" do
+ @provider.load_current_resource
+ @provider.new_resource.stub!(:group).and_return(9982398)
+ File.stub!(:chown).and_return(1)
+ File.should_receive(:chown).with(nil, 9982398, @provider.current_resource.path)
+ lambda { @provider.set_group }.should_not raise_error
+ end
+
+ it "should raise an exception if you are not root and try to change ownership" do
+ @provider.load_current_resource
+ @provider.new_resource.stub!(:group).and_return(0)
+ if Process.uid != 0
+ lambda { @provider.set_group }.should raise_error
+ end
+ end
+
+ it "should create the file if it is missing, then set the attributes on action_create" do
+ @provider.load_current_resource
+ @provider.new_resource.stub!(:owner).and_return(9982398)
+ @provider.new_resource.stub!(:group).and_return(9982398)
+ @provider.new_resource.stub!(:path).and_return("/tmp/monkeyfoo")
+ File.stub!(:chown).and_return(1)
+ File.should_receive(:chown).with(nil, 9982398, @provider.new_resource.path)
+ File.stub!(:chown).and_return(1)
+ File.should_receive(:chown).with(9982398, nil, @provider.new_resource.path)
+ File.stub!(:open).and_return(1)
+ File.should_receive(:open).with(@provider.new_resource.path, "w+")
+ @provider.action_create
+ end
+
+ it "should delete the file if it exists and is writable on action_delete" do
+ @provider.load_current_resource
+ @provider.new_resource.stub!(:path).and_return("/tmp/monkeyfoo")
+ File.should_receive("exists?").with(@provider.new_resource.path).and_return(true)
+ File.should_receive("writable?").with(@provider.new_resource.path).and_return(true)
+ File.should_receive(:delete).with(@provider.new_resource.path).and_return(true)
+ @provider.action_delete
+ end
+
+end \ No newline at end of file
diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb
new file mode 100644
index 0000000000..51980cbe99
--- /dev/null
+++ b/spec/unit/provider_spec.rb
@@ -0,0 +1,46 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: GNU General Public License version 2 or later
+#
+# This program and entire repository is free software; you can
+# redistribute it and/or modify it under the terms of the GNU
+# General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+require File.join(File.dirname(__FILE__), "..", "spec_helper")
+
+describe Chef::Provider do
+ before(:each) do
+ @resource = Chef::Resource.new("funk")
+ @node = Chef::Node.new
+ @node.name "latte"
+ @provider = Chef::Provider.new(@node, @resource)
+ end
+
+ it "should return a Chef::Provider" do
+ @provider.should be_a_kind_of(Chef::Provider)
+ end
+
+ it "should store the resource passed to new as new_resource" do
+ @provider.new_resource.should eql(@resource)
+ end
+
+ it "should store the node passed to new as node" do
+ @provider.node.should eql(@node)
+ end
+
+ it "should have nil for current_resource by default" do
+ @provider.current_resource.should eql(nil)
+ end
+end \ No newline at end of file
diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb
index 11080a2d82..e02973d73c 100644
--- a/spec/unit/recipe_spec.rb
+++ b/spec/unit/recipe_spec.rb
@@ -114,7 +114,7 @@ CODE
@recipe.from_file(File.join(File.dirname(__FILE__), "..", "data", "recipes", "test.rb"))
res = @recipe.resources(:file => "/etc/nsswitch.conf")
res.name.should eql("/etc/nsswitch.conf")
- res.insure.should eql("present")
+ res.action.should eql("create")
res.owner.should eql("root")
res.group.should eql("root")
res.mode.should eql(0644)
diff --git a/spec/unit/resource/file_spec.rb b/spec/unit/resource/file_spec.rb
index 85aaac1311..474c58f0d1 100644
--- a/spec/unit/resource/file_spec.rb
+++ b/spec/unit/resource/file_spec.rb
@@ -34,6 +34,10 @@ describe Chef::Resource::File do
@resource.name.should eql("fakey_fakerton")
end
+ it "should have a default action of 'create'" do
+ @resource.action.should eql("create")
+ end
+
it "should be set to back up files by default" do
@resource.backup.should eql(true)
end
@@ -45,20 +49,15 @@ describe Chef::Resource::File do
lambda { @resource.backup "blues" }.should raise_error(ArgumentError)
end
- it "should use the md5sum for checking changes by default" do
- @resource.checksum.should eql("md5sum")
- end
-
- it "should accept md5sum or mtime for checksum" do
- lambda { @resource.checksum "md5sum" }.should_not raise_error(ArgumentError)
- lambda { @resource.checksum "mtime" }.should_not raise_error(ArgumentError)
- lambda { @resource.checksum "blues" }.should raise_error(ArgumentError)
+ it "should accept an md5sum for checksum" do
+ lambda { @resource.checksum "bfda9e7a13afb123433667c2c7801d11" }.should_not raise_error(ArgumentError)
+ lambda { @resource.checksum "monkey!" }.should raise_error(ArgumentError)
end
- it "should accept absent or present for insure" do
- lambda { @resource.insure "absent" }.should_not raise_error(ArgumentError)
- lambda { @resource.insure "present" }.should_not raise_error(ArgumentError)
- lambda { @resource.insure "blues" }.should raise_error(ArgumentError)
+ it "should accept create or delete for action" do
+ lambda { @resource.action "create" }.should_not raise_error(ArgumentError)
+ lambda { @resource.action "delete" }.should_not raise_error(ArgumentError)
+ lambda { @resource.action "blues" }.should raise_error(ArgumentError)
end
it "should accept a group name or id for group" do