summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--History.txt2
-rw-r--r--Manifest.txt3
-rwxr-xr-xbin/chef-solo13
-rw-r--r--examples/config/chef-solo.rb4
-rw-r--r--examples/config/cookbooks/fakefile/recipes/default.rb7
-rw-r--r--examples/config/cookbooks/tempfile/recipes/default.rb6
-rw-r--r--examples/sample_recipe.rb5
-rw-r--r--lib/chef/compile.rb8
-rw-r--r--lib/chef/log/formatter.rb15
-rw-r--r--lib/chef/mixin/params_validate.rb57
-rw-r--r--lib/chef/node.rb26
-rw-r--r--lib/chef/platform.rb172
-rw-r--r--lib/chef/provider.rb5
-rw-r--r--lib/chef/provider/file.rb80
-rw-r--r--lib/chef/resource.rb30
-rw-r--r--lib/chef/resource/directory.rb89
-rw-r--r--lib/chef/resource/file.rb137
-rw-r--r--lib/chef/runner.rb93
-rw-r--r--spec/lib/chef/provider/easy.rb37
-rw-r--r--spec/lib/chef/provider/snakeoil.rb37
-rw-r--r--spec/lib/chef/resource/cat.rb3
-rw-r--r--spec/unit/chef_spec.rb2
-rw-r--r--spec/unit/compile_spec.rb12
-rw-r--r--spec/unit/log/formatter_spec.rb5
-rw-r--r--spec/unit/mixin/params_validate_spec.rb33
-rw-r--r--spec/unit/node_spec.rb5
-rw-r--r--spec/unit/platform_spec.rb211
-rw-r--r--spec/unit/provider/file_spec.rb19
-rw-r--r--spec/unit/provider_spec.rb4
-rw-r--r--spec/unit/resource/directory_spec.rb81
-rw-r--r--spec/unit/resource_spec.rb1
-rw-r--r--spec/unit/runner_spec.rb105
32 files changed, 1178 insertions, 129 deletions
diff --git a/History.txt b/History.txt
index d7b1defce5..af81c7d967 100644
--- a/History.txt
+++ b/History.txt
@@ -3,3 +3,5 @@
* 1 major enhancement
* Birthday!
+Sun Apr 27 20:30:43 PDT 2008
+ * Compiled first full recipes, and actually passed them to a provider. :) \ No newline at end of file
diff --git a/Manifest.txt b/Manifest.txt
index 203b0f1f63..38793f098b 100644
--- a/Manifest.txt
+++ b/Manifest.txt
@@ -4,9 +4,6 @@ Manifest.txt
README.txt
Rakefile
bin/chef-solo
-docs/chef-head.txt
-docs/design/HighLevel.graffle
-docs/recipe.rb
examples/config.rb
examples/config/chef-solo.rb
examples/config/cookbooks/fakefile/recipes/default.rb
diff --git a/bin/chef-solo b/bin/chef-solo
index 0d49414f5a..747c672b32 100755
--- a/bin/chef-solo
+++ b/bin/chef-solo
@@ -60,6 +60,9 @@ end
# Load our config file
Chef::Config.from_file(config[:config_file])
+if config[:log_level]
+ Chef::Log.level(config[:log_level].to_sym)
+end
# Find out our own hostname.
node_name = Facter["fqdn"].value
@@ -73,13 +76,7 @@ compile.load_node(node_name)
Facter.each do |field, value|
compile.node[field.to_sym] = value
end
-
-puts compile.node.to_yaml
-
compile.load_definitions
-puts compile.definitions.to_yaml
-
compile.load_recipes
-puts compile.resource_collection.to_yaml
-
-puts compile.to_yaml
+cr = Chef::Runner.new(compile.node, compile.collection)
+cr.converge \ No newline at end of file
diff --git a/examples/config/chef-solo.rb b/examples/config/chef-solo.rb
index 7270277214..6e98fbbc22 100644
--- a/examples/config/chef-solo.rb
+++ b/examples/config/chef-solo.rb
@@ -3,4 +3,6 @@
cookbook_path File.join(File.dirname(__FILE__), "cookbooks")
node_path File.join(File.dirname(__FILE__), "nodes")
-log_level :debug
+log_level :info
+
+Chef::Log::Formatter.show_time = false
diff --git a/examples/config/cookbooks/fakefile/recipes/default.rb b/examples/config/cookbooks/fakefile/recipes/default.rb
index 2f91563bfe..35d8bb828d 100644
--- a/examples/config/cookbooks/fakefile/recipes/default.rb
+++ b/examples/config/cookbooks/fakefile/recipes/default.rb
@@ -1,5 +1,6 @@
-file "foo" do
+file "/tmp/foo" do
owner "adam"
- mode 644
- action "create"
+ mode 0644
+ action :create
+ notifies :delete, resources(:file => "/tmp/glen"), :delayed
end
diff --git a/examples/config/cookbooks/tempfile/recipes/default.rb b/examples/config/cookbooks/tempfile/recipes/default.rb
index f0888ca550..7b0c3e278a 100644
--- a/examples/config/cookbooks/tempfile/recipes/default.rb
+++ b/examples/config/cookbooks/tempfile/recipes/default.rb
@@ -1,5 +1,5 @@
-file "glen" do
- owner "glen"
- mode 644
+file "/tmp/glen" do
+ owner "adam"
+ mode 0755
action "create"
end
diff --git a/examples/sample_recipe.rb b/examples/sample_recipe.rb
index 234a1587a4..4b8548cb90 100644
--- a/examples/sample_recipe.rb
+++ b/examples/sample_recipe.rb
@@ -16,6 +16,11 @@ file "/etc/nsswitch.conf" do
notifies :restart, resources("service[openldap]"), :immediately
end
+service "apache2" do
+ action "enabled"
+ subscribes :restart, resources("/etc/nsswitch.conf"), :immediately
+end
+
file "/etc/ldap.conf" do
owner "root"
group "root"
diff --git a/lib/chef/compile.rb b/lib/chef/compile.rb
index 5b1f9d756e..e974852426 100644
--- a/lib/chef/compile.rb
+++ b/lib/chef/compile.rb
@@ -25,12 +25,12 @@
class Chef
class Compile
- attr_accessor :node, :cookbook_loader, :resource_collection, :definitions
+ attr_accessor :node, :cookbook_loader, :collection, :definitions
def initialize()
@node = nil
@cookbook_loader = Chef::CookbookLoader.new
- @resource_collection = Chef::ResourceCollection.new
+ @collection = Chef::ResourceCollection.new
@definitions = Hash.new
end
@@ -50,10 +50,10 @@ class Chef
rmatch = recipe.match(/(.+?)::(.+)/)
if rmatch
cookbook = @cookbook_loader[rmatch[1]]
- cookbook.load_recipe(rmatch[2], @node, @resource_collection, @definitions, @cookbook_loader)
+ cookbook.load_recipe(rmatch[2], @node, @collection, @definitions, @cookbook_loader)
else
cookbook = @cookbook_loader[recipe]
- cookbook.load_recipe("default", @node, @resource_collection, @definitions, @cookbook_loader)
+ cookbook.load_recipe("default", @node, @collection, @definitions, @cookbook_loader)
end
end
end
diff --git a/lib/chef/log/formatter.rb b/lib/chef/log/formatter.rb
index d177060d97..1c7044bdb1 100644
--- a/lib/chef/log/formatter.rb
+++ b/lib/chef/log/formatter.rb
@@ -28,9 +28,20 @@ require 'time'
class Chef
class Log
class Formatter < Logger::Formatter
- # Prints a log message as '[time] severity: message'
+ @@show_time = true
+
+ def self.show_time=(show=false)
+ @@show_time = show
+ end
+
+ # Prints a log message as '[time] severity: message' if Chef::Log::Formatter.show_time == true.
+ # Otherwise, doesn't print the time.
def call(severity, time, progname, msg)
- sprintf("[%s] %s: %s\n", time.rfc2822(), severity, msg2str(msg))
+ if @@show_time
+ sprintf("[%s] %s: %s\n", time.rfc2822(), severity, msg2str(msg))
+ else
+ sprintf("%s: %s\n", severity, msg2str(msg))
+ end
end
# Converts some argument to a Logger.severity() call to a string. Regular strings pass through like
diff --git a/lib/chef/mixin/params_validate.rb b/lib/chef/mixin/params_validate.rb
index 4c6b5f6612..29c0b6d002 100644
--- a/lib/chef/mixin/params_validate.rb
+++ b/lib/chef/mixin/params_validate.rb
@@ -2,7 +2,7 @@
# Chef::Mixin::ParamsValidate
#
# Because I can't deal with not having named params. Strongly based on Dave Rolsky's excellent
-# Params::Validate module for Perl. Please don't blame him, though, if you hate this. :)
+# Params::Validate module for Perl. Please don't blame him, though, this is full of bugs. :)
#
# Author:: Adam Jacob (<adam@hjksolutions.com>)
# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
@@ -42,12 +42,15 @@ class Chef
# :callbacks:: Takes a hash of Procs, which should return true if the argument is valid.
# The key will be inserted into the error message if the Proc does not return true:
# "Option #{key}'s value #{value} #{message}!"
- # :kind_of:: Ensure that the value is a kind_of?(Whatever)
- # :respond_to:: Esnure that the value has a given method. Takes one method name or an array of
+ # :kind_of:: Ensure that the value is a kind_of?(Whatever). If passed an array, it will ensure
+ # that the value is one of those types.
+ # :respond_to:: Ensure that the value has a given method. Takes one method name or an array of
# method names.
# :required:: Raise an exception if this parameter is missing. Valid values are true or false,
# by default, options are not required.
# :regex:: Match the value of the paramater against a regular expression.
+ # :equal_to:: Match the value of the paramater with ==. An array means it can be equal to any
+ # of the values.
def validate(opts, map)
#--
# validate works by taking the keys in the validation map, assuming it's a hash, and
@@ -80,8 +83,19 @@ class Chef
opts
end
+ def set_or_return(instance, new_value, opts, map)
+ if new_value != nil
+ validate(opts, map)
+ instance = new_value
+ else
+ instance
+ end
+ end
+
private
+
+
# Return the value of a parameter, or nil if it doesn't exist.
def _pv_opts_lookup(opts, key)
if opts.has_key?(key.to_s)
@@ -104,12 +118,33 @@ class Chef
end
end
+ def _pv_equal_to(opts, key, to_be)
+ value = _pv_opts_lookup(opts, key)
+ if value != nil
+ passes = false
+ [ to_be ].flatten.each do |tb|
+ if value == tb
+ passes = true
+ end
+ end
+ unless passes
+ raise ArgumentError, "Option #{key} must be equal to one of: #{to_be.join(", ")}! You passed #{value.inspect}."
+ end
+ end
+ end
+
# Raise an exception if the parameter is not a kind_of?(to_be)
def _pv_kind_of(opts, key, to_be)
value = _pv_opts_lookup(opts, key)
if value != nil
- unless value.kind_of?(to_be)
- raise ArgumentError, "Option #{key} must be a kind of #{to_be}! You passed #{to_be.inspect}."
+ passes = false
+ [ to_be ].flatten.each do |tb|
+ if value.kind_of?(tb)
+ passes = true
+ end
+ end
+ unless passes
+ raise ArgumentError, "Option #{key} must be a kind of #{to_be}! You passed #{value.inspect}."
end
end
end
@@ -137,11 +172,17 @@ class Chef
# Check a parameter against a regular expression.
def _pv_regex(opts, key, regex)
value = _pv_opts_lookup(opts, key)
- if value != nil
- if regex.match(value) == nil
- raise ArgumentError, "Option #{key}'s value #{value} does not match regular expression #{regex.to_s}"
+ passes = false
+ [ regex ].flatten.each do |r|
+ if value != nil
+ if r.match(value.to_s)
+ passes = true
+ end
end
end
+ unless passes
+ raise ArgumentError, "Option #{key}'s value #{value} does not match regular expression #{regex.to_s}"
+ end
end
# Check a parameter against a hash of proc's.
diff --git a/lib/chef/node.rb b/lib/chef/node.rb
index 7cb42effd0..c5be016ec8 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -21,6 +21,7 @@
#
require File.join(File.dirname(__FILE__), "mixin", "check_helper")
+require File.join(File.dirname(__FILE__), "mixin", "params_validate")
require File.join(File.dirname(__FILE__), "mixin", "from_file")
require 'rubygems'
@@ -33,6 +34,7 @@ class Chef
include Chef::Mixin::CheckHelper
include Chef::Mixin::FromFile
+ include Chef::Mixin::ParamsValidate
# Create a new Chef::Node object.
def initialize()
@@ -79,13 +81,18 @@ class Chef
# Set the name of this Node, or return the current name.
def name(arg=nil)
- set_if_args(@name, arg) do |a|
- case a
- when String
- @name = a
- else
- raise ArgumentError, "The nodes name must be a string"
- end
+ if arg != nil
+ validate(
+ { :name => arg },
+ {
+ :name => {
+ :kind_of => String
+ }
+ }
+ )
+ @name = arg
+ else
+ @name
end
end
@@ -168,5 +175,10 @@ class Chef
result_object.to_json
end
+ # As a string
+ def to_s
+ "node[#{@name}]"
+ end
+
end
end \ No newline at end of file
diff --git a/lib/chef/platform.rb b/lib/chef/platform.rb
new file mode 100644
index 0000000000..bebdcfa297
--- /dev/null
+++ b/lib/chef/platform.rb
@@ -0,0 +1,172 @@
+#
+# 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
+#
+
+Dir[File.join(File.dirname(__FILE__), 'provider/**/*.rb')].sort.each { |lib| require lib }
+require File.join(File.dirname(__FILE__), 'mixin', 'params_validate')
+
+class Chef
+ class Platform
+
+ @platforms = {
+ :mac_os_x => {},
+ :ubuntu => {},
+ :centos => {},
+ :redhat => {},
+ :gentoo => {},
+ :solaris => {},
+ :default => {
+ :file => Chef::Provider::File
+ }
+ }
+
+ class << self
+ attr_accessor :platforms
+
+ include Chef::Mixin::ParamsValidate
+
+ def find(name, version)
+ provider_map = @platforms[:default].clone
+
+ name_sym = name
+ if name.kind_of?(String)
+ name.downcase!
+ name.gsub!(/\s/, "_")
+ name_sym = name.to_sym
+ end
+
+ if @platforms.has_key?(name_sym)
+ if @platforms[name_sym].has_key?(version)
+ Chef::Log.debug("Platform #{name.to_s} version #{version} found")
+ if @platforms[name_sym].has_key?(:default)
+ provider_map.merge!(@platforms[name_sym][:default])
+ end
+ provider_map.merge!(@platforms[name_sym][version])
+ elsif @platforms[name_sym].has_key?(:default)
+ provider_map.merge!(@platforms[name_sym][:default])
+ end
+ else
+ Chef::Log.debug("Platform #{name} not found, using all defaults. (Unsupported platform?)")
+ end
+ provider_map
+ end
+
+ def find_provider(platform, version, resource_type)
+ pmap = Chef::Platform.find(platform, version)
+ rtkey = resource_type
+ if resource_type.kind_of?(Chef::Resource)
+ rtkey = resource_type.resource_name
+ end
+ if pmap.has_key?(rtkey)
+ pmap[rtkey]
+ else
+ raise(
+ ArgumentError,
+ "Cannot find a provider for #{resource_type} on #{platform} version #{version}"
+ )
+ end
+ end
+
+ def find_provider_for_node(node, resource_type)
+ platform = nil
+ version = nil
+ if node.attribute?("lsbdistid")
+ platform = node[:lsbdistid]
+ elsif node.attribute?("macosx_productname")
+ platform = node[:macosx_productname]
+ elsif node.attribute?("operatingsystem")
+ platform = node[:operatingsystem]
+ end
+ raise ArgumentError, "Cannot find a platform for #{node}" unless platform
+
+ if node.attribute?("lsbdistrelease")
+ version = node[:lsbdistrelease]
+ elsif node.attribute?("macosx_productversion")
+ version = node[:macosx_productversion]
+ elsif node.attribute?("operatingsystemversion")
+ version = node[:operatingsystemversion]
+ end
+ raise ArgumentError, "Cannot find a version for #{node}" unless version
+
+ provider = find_provider(platform, version, resource_type)
+ end
+
+ def set(args)
+ validate(
+ args,
+ {
+ :platform => {
+ :kind_of => Symbol,
+ :required => false,
+ },
+ :version => {
+ :kind_of => String,
+ :required => false,
+ },
+ :resource => {
+ :kind_of => Symbol,
+ },
+ :provider => {
+ :kind_of => [ String, Symbol, Class ],
+ }
+ }
+ )
+ if args.has_key?(:platform)
+ if args.has_key?(:version)
+ if @platforms.has_key?(args[:platform])
+ if @platforms[args[:platform]].has_key?(args[:version])
+ @platforms[args[:platform]][args[:version]][args[:resource].to_sym] = args[:provider]
+ else
+ @platforms[args[:platform]][args[:version]] = {
+ args[:resource].to_sym => args[:provider]
+ }
+ end
+ else
+ @platforms[args[:platform]] = {
+ args[:version] => {
+ args[:resource].to_sym => args[:provider]
+ }
+ }
+ end
+ else
+ if @platforms.has_key?(args[:platform])
+ @platforms[args[:platform]][:default][args[:resource].to_sym] = args[:provider]
+ else
+ @platforms[args[:platform]] = {
+ :default => {
+ args[:resource].to_sym => args[:provider]
+ }
+ }
+ end
+ end
+ else
+ if @platforms.has_key?(:default)
+ @platforms[:default][args[:resource].to_sym] = args[:provider]
+ else
+ @platforms[:default] = {
+ args[:resource].to_sym => args[:provider]
+ }
+ end
+ end
+ end
+
+ end
+
+ end
+end \ No newline at end of file
diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb
index d296b63227..018f687069 100644
--- a/lib/chef/provider.rb
+++ b/lib/chef/provider.rb
@@ -29,5 +29,10 @@ class Chef
@current_resource = nil
end
+ def action_nothing
+ Chef::Log.debug("Doing nothing for #{self.to_s}")
+ true
+ end
+
end
end \ No newline at end of file
diff --git a/lib/chef/provider/file.rb b/lib/chef/provider/file.rb
index c0805674ec..7bb729cd87 100644
--- a/lib/chef/provider/file.rb
+++ b/lib/chef/provider/file.rb
@@ -47,58 +47,100 @@ class Chef
# 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
+ if @new_resource.owner != nil
+ 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
end
# Set the ownership on the file, assuming it is not set correctly already.
def set_owner
unless compare_owner
+ Chef::Log.info("Setting owner to #{@set_user_id} for #{@new_resource}")
::File.chown(@set_user_id, nil, @new_resource.path)
+ @new_resource.updated = true
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.getgrnam(@new_resource.group)
- @set_group_id = group_info.gid
- @set_group_id == @current_resource.group
+ if @new_resource.group != nil
+ 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.getgrnam(@new_resource.group)
+ @set_group_id = group_info.gid
+ @set_group_id == @current_resource.group
+ end
end
end
def set_group
unless compare_group
+ Chef::Log.info("Setting group to #{@set_group_id} for #{@new_resource}")
::File.chown(nil, @set_group_id, @new_resource.path)
+ @new_resource.updated = true
+ end
+ end
+
+ def compare_mode
+ if @new_resource.mode != nil
+ case @new_resource.mode
+ when /^\d+$/, Integer
+ real_mode = sprintf("%o" % (@new_resource.mode & 007777))
+ real_mode.to_i == @current_resource.mode.to_i
+ end
+ end
+ end
+
+ def set_mode
+ unless compare_mode && @new_resource.mode != nil
+ Chef::Log.info("Setting mode to #{sprintf("%o" % (@new_resource.mode & 007777))
+ } for #{@new_resource}")
+ ::File.chmod(@new_resource.mode.to_i, @new_resource.path)
+ @new_resource.updated = true
end
end
def action_create
unless ::File.exists?(@new_resource.path)
+ Chef::Log.info("Creating #{@new_resource} at #{@new_resource.path}")
::File.open(@new_resource.path, "w+") { |f| }
+ @new_resource.updated = true
end
- set_owner
- set_group
+ set_owner if @new_resource.owner != nil
+ set_group if @new_resource.group != nil
+ set_mode if @new_resource.mode != nil
end
def action_delete
if ::File.exists?(@new_resource.path) && ::File.writable?(@new_resource.path)
+ Chef::Log.info("Deleting #{@new_resource} at #{@new_resource.path}")
::File.delete(@new_resource.path)
+ @new_resource.updated = true
+ else
+ raise "Cannot delete #{@new_resource} at #{@new_resource_path}!"
end
end
+ def action_touch
+ action_create
+ time = Time.now
+ Chef::Log.info("Updating #{@new_resource} with new atime/mtime of #{time}")
+ ::File.utime(time, time, @new_resource.path)
+ @new_resource.updated = true
+ end
+
end
end
end \ No newline at end of file
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index fab6279a0e..5a5263b321 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -18,16 +18,18 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
+require File.join(File.dirname(__FILE__), "mixin", "params_validate")
require File.join(File.dirname(__FILE__), "mixin", "check_helper")
require 'yaml'
class Chef
class Resource
-
+
include Chef::Mixin::CheckHelper
+ include Chef::Mixin::ParamsValidate
- attr_accessor :tag, :actions, :params
- attr_reader :name, :noop, :resource_name, :collection, :notifies, :subscribes
+ attr_accessor :tag, :actions, :params, :provider, :updated, :allowed_actions
+ attr_reader :resource_name, :collection
def initialize(name, collection=nil)
@name = name
@@ -41,6 +43,28 @@ class Chef
@before = nil
@actions = Hash.new
@params = Hash.new
+ @provider = nil
+ @allowed_actions = [ :nothing ]
+ @action = :nothing
+ @updated = false
+ end
+
+ def action(arg=nil)
+ if arg != nil
+ arg = arg.to_sym
+ end
+ set_or_return(
+ @action,
+ arg,
+ {
+ :action => arg,
+ },
+ {
+ :action => {
+ :equal_to => @allowed_actions,
+ }
+ }
+ )
end
def name(name=nil)
diff --git a/lib/chef/resource/directory.rb b/lib/chef/resource/directory.rb
new file mode 100644
index 0000000000..b03d98fe9b
--- /dev/null
+++ b/lib/chef/resource/directory.rb
@@ -0,0 +1,89 @@
+#
+# 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 Resource
+ class Directory < Chef::Resource::File
+
+ def initialize(name, collection=nil)
+ @resource_name = :file
+ super(name, collection)
+ @path = name
+ @action = "create"
+ end
+
+ def action(arg=nil)
+ set_if_args(@action, arg) do
+ case arg
+ when "create", "delete", "touch", "nothing"
+ @action = arg
+ else
+ raise ArgumentError, "action must be create, delete, or touch!"
+ end
+ end
+ end
+
+ def group(arg=nil)
+ set_if_args(@group, arg) do
+ case arg
+ when /^([a-z]|[A-Z]|[0-9]|_|-)+$/, Integer
+ @group = arg
+ else
+ raise ArgumentError, "group must match /^([a-z]|[A-Z]|[0-9]|_|-)$/, Integer!"
+ end
+ end
+ end
+
+ def mode(arg=nil)
+ set_if_args(@mode, arg) do
+ case "#{arg.to_s}"
+ when /^\d{3,4}$/
+ @mode = arg
+ else
+ raise ArgumentError, "mode must be a valid unix file mode - 3 or 4 digets!"
+ end
+ end
+ end
+
+ def owner(arg=nil)
+ set_if_args(@owner, arg) do
+ case arg
+ when /^([a-z]|[A-Z]|[0-9]|_|-)+$/, Integer
+ @owner = arg
+ else
+ raise ArgumentError, "group must match /^([a-z]|[A-Z]|[0-9]|_|-)$/, Integer!"
+ end
+ end
+ end
+
+ def path(arg=nil)
+ set_if_args(@path, arg) do
+ case arg
+ when String
+ @path = arg
+ else
+ raise ArgumentError, "path must be a string!"
+ end
+ 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 59c51ba8d3..3ea75c106b 100644
--- a/lib/chef/resource/file.rb
+++ b/lib/chef/resource/file.rb
@@ -18,6 +18,7 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
+
class Chef
class Resource
class File < Chef::Resource
@@ -28,83 +29,97 @@ class Chef
@path = name
@backup = true
@action = "create"
+ @allowed_actions.push(:create, :delete, :touch)
end
def backup(arg=nil)
- set_if_args(@backup, arg) do
- case arg
- when true, false, Integer
- @backup = arg
- else
- raise ArgumentError, "backup must be true, false, or a number!"
- end
- end
+ set_or_return(
+ @backup,
+ arg,
+ {
+ :backup => arg,
+ },
+ {
+ :backup => {
+ :kind_of => [ Integer, TrueClass, FalseClass ],
+ }
+ }
+ )
end
-
+
def checksum(arg=nil)
- set_if_args(@checksum, arg) do
- case arg
- when /^[a-zA-Z0-9]{32}$/ # md5sum
- @checksum = arg
- else
- raise ArgumentError, "checksum must be an md5sum!"
- end
- end
+ set_or_return(
+ @checksum,
+ arg,
+ {
+ :checksum => arg,
+ },
+ {
+ :checksum => {
+ :regex => /^[a-zA-Z0-9]{32}$/,
+ },
+ }
+ )
end
-
- def action(arg=nil)
- set_if_args(@action, arg) do
- case arg
- when "create", "delete"
- @action = arg
- else
- raise ArgumentError, "action must be create or delete!"
- end
- end
- end
-
+
def group(arg=nil)
- set_if_args(@group, arg) do
- case arg
- when /^([a-z]|[A-Z]|[0-9]|_|-)+$/, Integer
- @group = arg
- else
- raise ArgumentError, "group must match /^([a-z]|[A-Z]|[0-9]|_|-)$/, Integer!"
- end
- end
+ set_or_return(
+ @group,
+ arg,
+ {
+ :group => arg
+ },
+ {
+ :group => {
+ :regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
+ }
+ }
+ )
end
def mode(arg=nil)
- set_if_args(@mode, arg) do
- case "#{arg.to_s}"
- when /^\d{3,4}$/
- @mode = arg
- else
- raise ArgumentError, "mode must be a valid unix file mode - 3 or 4 digets!"
- end
- end
+ set_or_return(
+ @mode,
+ arg,
+ {
+ :mode => arg
+ },
+ {
+ :mode => {
+ :regex => /^\d{3,4}$/
+ }
+ }
+ )
end
def owner(arg=nil)
- set_if_args(@owner, arg) do
- case arg
- when /^([a-z]|[A-Z]|[0-9]|_|-)+$/, Integer
- @owner = arg
- else
- raise ArgumentError, "group must match /^([a-z]|[A-Z]|[0-9]|_|-)$/, Integer!"
- end
- end
+ set_or_return(
+ @owner,
+ arg,
+ {
+ :owner => arg,
+ },
+ {
+ :owner => {
+ :regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
+ }
+ }
+ )
end
def path(arg=nil)
- set_if_args(@path, arg) do
- case arg
- when String
- @path = arg
- else
- raise ArgumentError, "path must be a string!"
- end
- end
+ set_or_return(
+ @path,
+ arg,
+ {
+ :path => arg,
+ },
+ {
+ :path => {
+ :kind_of => String,
+ },
+ }
+ )
end
end
diff --git a/lib/chef/runner.rb b/lib/chef/runner.rb
new file mode 100644
index 0000000000..60f3082e88
--- /dev/null
+++ b/lib/chef/runner.rb
@@ -0,0 +1,93 @@
+#
+# 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__), "mixin", "params_validate")
+
+class Chef
+ class Runner
+
+ include Chef::Mixin::ParamsValidate
+
+ def initialize(node, collection)
+ validate(
+ {
+ :node => node,
+ :collection => collection,
+ },
+ {
+ :node => {
+ :kind_of => Chef::Node,
+ },
+ :collection => {
+ :kind_of => Chef::ResourceCollection,
+ },
+ }
+ )
+ @node = node
+ @collection = collection
+ end
+
+ def build_provider(resource)
+ provider_klass = resource.provider
+ if provider_klass == nil
+ provider_klass = Chef::Platform.find_provider_for_node(@node, resource)
+ end
+ Chef::Log.debug("#{resource} using #{provider_klass.to_s}")
+ provider = provider_klass.new(@node, resource)
+ end
+
+ def converge
+ start_time = Time.now
+ Chef::Log.info("Starting Chef Run")
+ delayed_actions = Array.new
+
+ @collection.each do |resource|
+ Chef::Log.debug("Processing #{resource}")
+ provider = build_provider(resource)
+ provider.load_current_resource
+ provider.send("action_#{resource.action}")
+ if resource.updated
+ resource.actions.each_key do |action|
+ if resource.actions[action].has_key?(:immediate)
+ resource.actions[action][:immediate].each do |r|
+ Chef::Log.info("#{resource} sending action #{action} to #{r} (immediate)")
+ build_provider(r).send("action_#{action}")
+ end
+ end
+ if resource.actions[action].has_key?(:delayed)
+ resource.actions[action][:delayed].each do |r|
+ delayed_actions << lambda {
+ Chef::Log.info("#{resource} sending action #{action} to #{r} (delayed)")
+ build_provider(r).send("action_#{action}")
+ }
+ end
+ end
+ end
+ end
+ end
+
+ # Run all our :delayed actions
+ delayed_actions.each { |da| da.call }
+ end_time = Time.now
+ Chef::Log.info("Chef Run complete in #{end_time - start_time} seconds")
+ true
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/lib/chef/provider/easy.rb b/spec/lib/chef/provider/easy.rb
new file mode 100644
index 0000000000..b637efa768
--- /dev/null
+++ b/spec/lib/chef/provider/easy.rb
@@ -0,0 +1,37 @@
+#
+# 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
+ class Easy < Chef::Provider
+ def load_current_resource
+ true
+ end
+
+ def action_sell
+ true
+ end
+
+ def action_buy
+ true
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/lib/chef/provider/snakeoil.rb b/spec/lib/chef/provider/snakeoil.rb
new file mode 100644
index 0000000000..0486402686
--- /dev/null
+++ b/spec/lib/chef/provider/snakeoil.rb
@@ -0,0 +1,37 @@
+#
+# 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
+ class SnakeOil < Chef::Provider
+ def load_current_resource
+ true
+ end
+
+ def action_sell
+ true
+ end
+
+ def action_buy
+ true
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/spec/lib/chef/resource/cat.rb b/spec/lib/chef/resource/cat.rb
index 5b66c9cabd..a0456d6b45 100644
--- a/spec/lib/chef/resource/cat.rb
+++ b/spec/lib/chef/resource/cat.rb
@@ -21,9 +21,12 @@ class Chef
class Resource
class Cat < Chef::Resource
+ attr_accessor :action
+
def initialize(name, collection=nil)
@resource_name = :cat
super(name, collection)
+ @action = "sell"
end
def pretty_kitty(arg=nil)
diff --git a/spec/unit/chef_spec.rb b/spec/unit/chef_spec.rb
index cfb6cd4dac..de8042552a 100644
--- a/spec/unit/chef_spec.rb
+++ b/spec/unit/chef_spec.rb
@@ -24,4 +24,4 @@ describe Chef do
it "should have a version defined" do
Chef::VERSION.should match(/(\d+)\.(\d+)\.(\d+)/)
end
-end \ No newline at end of file
+end
diff --git a/spec/unit/compile_spec.rb b/spec/unit/compile_spec.rb
index 49d5930acb..b6cde6582c 100644
--- a/spec/unit/compile_spec.rb
+++ b/spec/unit/compile_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Compile do
end
it "should have a Chef::ResourceCollection" do
- @compile.resource_collection.should be_a_kind_of(Chef::ResourceCollection)
+ @compile.collection.should be_a_kind_of(Chef::ResourceCollection)
end
it "should have a hash of Definitions" do
@@ -58,11 +58,11 @@ describe Chef::Compile do
@compile.load_node("compile")
@compile.load_definitions
lambda { @compile.load_recipes }.should_not raise_error
- @compile.resource_collection[0].to_s.should == "cat[einstein]"
- @compile.resource_collection[1].to_s.should == "cat[loulou]"
- @compile.resource_collection[2].to_s.should == "cat[birthday]"
- @compile.resource_collection[3].to_s.should == "cat[peanut]"
- @compile.resource_collection[4].to_s.should == "cat[fat peanut]"
+ @compile.collection[0].to_s.should == "cat[einstein]"
+ @compile.collection[1].to_s.should == "cat[loulou]"
+ @compile.collection[2].to_s.should == "cat[birthday]"
+ @compile.collection[3].to_s.should == "cat[peanut]"
+ @compile.collection[4].to_s.should == "cat[fat peanut]"
end
end \ No newline at end of file
diff --git a/spec/unit/log/formatter_spec.rb b/spec/unit/log/formatter_spec.rb
index e7518ead87..6651feab2e 100644
--- a/spec/unit/log/formatter_spec.rb
+++ b/spec/unit/log/formatter_spec.rb
@@ -44,4 +44,9 @@ describe Chef::Log::Formatter do
@formatter.call("monkey", Time.new, "test", "mos def").should == "[#{time.rfc2822}] monkey: mos def\n"
end
+ it "should allow you to turn the time on and off in the output" do
+ Chef::Log::Formatter.show_time = false
+ @formatter.call("monkey", Time.new, "test", "mos def").should == "monkey: mos def\n"
+ end
+
end \ No newline at end of file
diff --git a/spec/unit/mixin/params_validate_spec.rb b/spec/unit/mixin/params_validate_spec.rb
index 3ade2b3c2d..2790f2b14a 100644
--- a/spec/unit/mixin/params_validate_spec.rb
+++ b/spec/unit/mixin/params_validate_spec.rb
@@ -295,4 +295,37 @@ describe Chef::Mixin::ParamsValidate do
@vo.validate({ "one" => "two" }, { :one => { :regex => /^two$/ }})
}.should_not raise_error(ArgumentError)
end
+
+ it "should allow an array to kind_of" do
+ lambda {
+ @vo.validate(
+ {:one => "string"},
+ {
+ :one => {
+ :kind_of => [ String, Array ]
+ }
+ }
+ )
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @vo.validate(
+ {:one => ["string"]},
+ {
+ :one => {
+ :kind_of => [ String, Array ]
+ }
+ }
+ )
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @vo.validate(
+ {:one => Hash.new},
+ {
+ :one => {
+ :kind_of => [ String, Array ]
+ }
+ }
+ )
+ }.should raise_error(ArgumentError)
+ end
end \ No newline at end of file
diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb
index c72aea0b7d..0ea4d864d4 100644
--- a/spec/unit/node_spec.rb
+++ b/spec/unit/node_spec.rb
@@ -171,4 +171,9 @@ describe Chef::Node do
list[2].should == "test"
end
+ it "should turn into a string like node[name]" do
+ @node.name("airplane")
+ @node.to_s.should eql("node[airplane]")
+ end
+
end \ No newline at end of file
diff --git a/spec/unit/platform_spec.rb b/spec/unit/platform_spec.rb
new file mode 100644
index 0000000000..4f0a5e5288
--- /dev/null
+++ b/spec/unit/platform_spec.rb
@@ -0,0 +1,211 @@
+#
+# 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.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
+
+describe Chef::Platform do
+ before(:each) do
+ Chef::Platform.platforms = {
+ :darwin => {
+ "9.2.2" => {
+ :file => "darwinian",
+ :else => "thing"
+ },
+ :default => {
+ :file => "old school",
+ :snicker => "snack"
+ }
+ },
+ :mars_volta => {
+ },
+ :default => {
+ :file => Chef::Provider::File,
+ :pax => "brittania",
+ :cat => "nice"
+ }
+ }
+ end
+
+ it "should allow you to look up a platform by name and version, returning the provider map for it" do
+ pmap = Chef::Platform.find("Darwin", "9.2.2")
+ pmap.should be_a_kind_of(Hash)
+ pmap[:file].should eql("darwinian")
+ end
+
+ it "should use the default providers for an os if the specific version does not exist" do
+ pmap = Chef::Platform.find("Darwin", "1")
+ pmap.should be_a_kind_of(Hash)
+ pmap[:file].should eql("old school")
+ end
+
+ it "should use the default providers if the os doesn't give me a default, but does exist" do
+ pmap = Chef::Platform.find("mars_volta", "1")
+ pmap.should be_a_kind_of(Hash)
+ pmap[:file].should eql(Chef::Provider::File)
+ end
+
+ it "should use the default provider if the os does not exist" do
+ pmap = Chef::Platform.find("AIX", "1")
+ pmap.should be_a_kind_of(Hash)
+ pmap[:file].should eql(Chef::Provider::File)
+ end
+
+ it "should merge the defaults for an os with the specific version" do
+ pmap = Chef::Platform.find("Darwin", "9.2.2")
+ pmap[:file].should eql("darwinian")
+ pmap[:snicker].should eql("snack")
+ end
+
+ it "should merge the defaults for an os with the universal defaults" do
+ pmap = Chef::Platform.find("Darwin", "9.2.2")
+ pmap[:file].should eql("darwinian")
+ pmap[:pax].should eql("brittania")
+ end
+
+ it "should allow you to look up a provider for a platform directly by symbol" do
+ Chef::Platform.find_provider("Darwin", "9.2.2", :file).should eql("darwinian")
+ end
+
+ it "should raise an exception if a provider cannot be found for a resource type" do
+ lambda { Chef::Platform.find_provider("Darwin", "9.2.2", :coffee) }.should raise_error(ArgumentError)
+ end
+
+ it "should look up a provider for a resource with a Chef::Resource object" do
+ kitty = Chef::Resource::Cat.new("loulou")
+ Chef::Platform.find_provider("Darwin", "9.2.2", kitty)
+ end
+
+ it "should look up a provider with a node and a Chef::Resource object" do
+ kitty = Chef::Resource::Cat.new("loulou")
+ node = Chef::Node.new
+ node.name("Intel")
+ node.operatingsystem("Darwin")
+ node.operatingsystemversion("9.2.2")
+ Chef::Platform.find_provider_for_node(node, kitty).should eql("nice")
+ end
+
+ it "should prefer lsbdistid over operatingsystem when looking up via node" do
+ kitty = Chef::Resource::Cat.new("loulou")
+ node = Chef::Node.new
+ node.name("Intel")
+ node.operatingsystem("Darwin")
+ node.operatingsystemversion("9.2.2")
+ node.lsbdistid("Not Linux")
+ Chef::Platform.set(
+ :platform => :not_linux,
+ :resource => :cat,
+ :provider => "bourbon"
+ )
+ Chef::Platform.find_provider_for_node(node, kitty).should eql("bourbon")
+ end
+
+ it "should prefer macosx_productnmae over operatingsystem when looking up via node" do
+ kitty = Chef::Resource::Cat.new("loulou")
+ node = Chef::Node.new
+ node.name("Intel")
+ node.operatingsystem("Darwin")
+ node.operatingsystemversion("9.2.2")
+ node.macosx_productname("Mac OS X")
+ Chef::Platform.set(
+ :platform => :mac_os_x,
+ :resource => :cat,
+ :provider => "bourbon"
+ )
+ Chef::Platform.find_provider_for_node(node, kitty).should eql("bourbon")
+ end
+
+ it "should prefer lsbdistrelease over operatingsystem when looking up via node" do
+ kitty = Chef::Resource::Cat.new("loulou")
+ node = Chef::Node.new
+ node.name("Intel")
+ node.operatingsystem("Darwin")
+ node.operatingsystemversion("9.2.2")
+ node.lsbdistrelease("10")
+ Chef::Platform.set(
+ :platform => :darwin,
+ :version => "10",
+ :resource => :cat,
+ :provider => "bourbon"
+ )
+ Chef::Platform.find_provider_for_node(node, kitty).should eql("bourbon")
+ end
+
+ it "should prefer macosx_productversion over operatingsystem when looking up via node" do
+ kitty = Chef::Resource::Cat.new("loulou")
+ node = Chef::Node.new
+ node.name("Intel")
+ node.operatingsystem("Darwin")
+ node.operatingsystemversion("9.2.2")
+ node.macosx_productversion("10")
+ Chef::Platform.set(
+ :platform => :darwin,
+ :version => "10",
+ :resource => :cat,
+ :provider => "bourbon"
+ )
+ Chef::Platform.find_provider_for_node(node, kitty).should eql("bourbon")
+ end
+
+ it "should update the provider map with map" do
+ Chef::Platform.set(
+ :platform => :darwin,
+ :version => "9.2.2",
+ :resource => :file,
+ :provider => "masterful"
+ )
+ Chef::Platform.platforms[:darwin]["9.2.2"][:file].should eql("masterful")
+ Chef::Platform.set(
+ :platform => :darwin,
+ :resource => :file,
+ :provider => "masterful"
+ )
+ Chef::Platform.platforms[:darwin][:default][:file].should eql("masterful")
+ Chef::Platform.set(
+ :resource => :file,
+ :provider => "masterful"
+ )
+ Chef::Platform.platforms[:default][:file].should eql("masterful")
+
+ Chef::Platform.set(
+ :platform => :hero,
+ :version => "9.2.2",
+ :resource => :file,
+ :provider => "masterful"
+ )
+ Chef::Platform.platforms[:hero]["9.2.2"][:file].should eql("masterful")
+
+ Chef::Platform.set(
+ :resource => :file,
+ :provider => "masterful"
+ )
+ Chef::Platform.platforms[:default][:file].should eql("masterful")
+
+ Chef::Platform.platforms = {}
+
+ Chef::Platform.set(
+ :resource => :file,
+ :provider => "masterful"
+ )
+ Chef::Platform.platforms[:default][:file].should eql("masterful")
+
+ end
+
+
+end \ No newline at end of file
diff --git a/spec/unit/provider/file_spec.rb b/spec/unit/provider/file_spec.rb
index 4a98e88df0..3b894ad02a 100644
--- a/spec/unit/provider/file_spec.rb
+++ b/spec/unit/provider/file_spec.rb
@@ -171,12 +171,14 @@ describe Chef::Provider::File 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!(:mode).and_return(0755)
@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(:chmod).with(0755, @provider.new_resource.path).and_return(1)
File.should_receive(:open).with(@provider.new_resource.path, "w+")
@provider.action_create
end
@@ -190,4 +192,21 @@ describe Chef::Provider::File do
@provider.action_delete
end
+ it "should raise an error if it cannot delete the file" 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(false)
+ lambda { @provider.action_delete }.should raise_error()
+ end
+
+ it "should update the atime/mtime on action_touch" do
+ @provider.load_current_resource
+ @provider.new_resource.stub!(:path).and_return("/tmp/monkeyfoo")
+ File.should_receive(:utime).once.and_return(1)
+ File.stub!(:open).and_return(1)
+ File.stub!(:chown).and_return(1)
+ File.stub!(:chmod).and_return(1)
+ @provider.action_touch
+ end
+
end \ No newline at end of file
diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb
index 71cc1a47e3..6a28120478 100644
--- a/spec/unit/provider_spec.rb
+++ b/spec/unit/provider_spec.rb
@@ -43,4 +43,8 @@ describe Chef::Provider do
it "should have nil for current_resource by default" do
@provider.current_resource.should eql(nil)
end
+
+ it "should return true for action_nothing" do
+ @provider.action_nothing.should eql(true)
+ end
end \ No newline at end of file
diff --git a/spec/unit/resource/directory_spec.rb b/spec/unit/resource/directory_spec.rb
new file mode 100644
index 0000000000..18fffb01ff
--- /dev/null
+++ b/spec/unit/resource/directory_spec.rb
@@ -0,0 +1,81 @@
+# 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.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
+
+describe Chef::Resource::Directory do
+
+ before(:each) do
+ @resource = Chef::Resource::Directory.new("fakey_fakerton")
+ end
+
+ it "should create a new Chef::Resource::Directory" do
+ @resource.should be_a_kind_of(Chef::Resource)
+ @resource.should be_a_kind_of(Chef::Resource::File)
+ @resource.should be_a_kind_of(Chef::Resource::Directory)
+ end
+
+ it "should have a name" 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 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
+ lambda { @resource.group "root" }.should_not raise_error(ArgumentError)
+ lambda { @resource.group 123 }.should_not raise_error(ArgumentError)
+ lambda { @resource.group "root*goo" }.should raise_error(ArgumentError)
+ end
+
+ it "should accept a valid unix file mode" do
+ lambda { @resource.mode 0444 }.should_not raise_error(ArgumentError)
+ lambda { @resource.mode 444 }.should_not raise_error(ArgumentError)
+ lambda { @resource.mode 4 }.should raise_error(ArgumentError)
+ end
+
+ it "should accept a user name or id for owner" do
+ lambda { @resource.owner "root" }.should_not raise_error(ArgumentError)
+ lambda { @resource.owner 123 }.should_not raise_error(ArgumentError)
+ lambda { @resource.owner "root*goo" }.should raise_error(ArgumentError)
+ end
+
+ it "should use the object name as the path by default" do
+ @resource.path.should eql("fakey_fakerton")
+ end
+
+ it "should accept a string as the path" do
+ lambda { @resource.path "/tmp" }.should_not raise_error(ArgumentError)
+ lambda { @resource.path Hash.new }.should raise_error(ArgumentError)
+ end
+
+ it "should allow you to have specify whether the action is recursive with true/false" do
+ lambda { @resource.recursive true }.should_not raise_error(ArgumentError)
+ lambda { @resource.recursive false }.should_not raise_error(ArgumentError)
+ lambda { @resource.recursive "monkey" }.should raise_error(ArgumentError)
+ end
+
+end \ No newline at end of file
diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb
index d6db1f2d74..be9a7cc3aa 100644
--- a/spec/unit/resource_spec.rb
+++ b/spec/unit/resource_spec.rb
@@ -129,6 +129,7 @@ describe Chef::Resource do
@resource.noop(@resource.is(true))
@resource.noop.should eql(true)
end
+
# it "should serialize to yaml" do
# yaml_output = <<-DESC
diff --git a/spec/unit/runner_spec.rb b/spec/unit/runner_spec.rb
new file mode 100644
index 0000000000..2a5c613cc7
--- /dev/null
+++ b/spec/unit/runner_spec.rb
@@ -0,0 +1,105 @@
+#
+# 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.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
+
+describe Chef::Runner do
+ before(:each) do
+ @mock_node = mock("Node", :null_object => true)
+ @mock_collection = mock("Resource Collection", :null_object => true)
+ @mock_provider = mock("Provider", :null_object => true)
+ @mock_resource = mock("Resource", :null_object => true)
+ new_runner
+ end
+
+ it "should require a Node and a ResourceCollection" do
+ @mock_node.should_receive(:kind_of?).once.and_return(true)
+ @mock_collection.should_receive(:kind_of?).once.and_return(true)
+ runner = Chef::Runner.new(@mock_node, @mock_collection)
+ runner.should be_a_kind_of(Chef::Runner)
+ end
+
+ it "should raise an exception if you pass the wrong kind of object to new" do
+ @mock_node.stub!(:kind_of?).and_return(false)
+ @mock_collecton.stub!(:kind_of?).and_return(false)
+ lambda { Chef::Runner.new(@mock_node, @mock_collection) }.should raise_error(ArgumentError)
+ end
+
+ it "should pass each resource in the collection to a provider" do
+ @collection.should_receive(:each).once
+ @runner.converge
+ end
+
+ it "should use the provider specified by the resource (if it has one)" do
+ provider = Chef::Provider::Easy.new(@node, @collection[0])
+ @collection[0].should_receive(:provider).once.and_return(Chef::Provider::Easy)
+ Chef::Provider::Easy.should_receive(:new).once.and_return(provider)
+ @runner.converge
+ end
+
+ it "should use the platform provider if it has one" do
+ Chef::Platform.should_receive(:find_provider_for_node).once.and_return(Chef::Provider::SnakeOil)
+ @runner.converge
+ end
+
+ it "should run the action for each resource" do
+ Chef::Platform.should_receive(:find_provider_for_node).once.and_return(Chef::Provider::SnakeOil)
+ provider = Chef::Provider::SnakeOil.new(@node, @collection[0])
+ provider.should_receive(:action_sell).once.and_return(true)
+ Chef::Provider::SnakeOil.should_receive(:new).once.and_return(provider)
+ @runner.converge
+ end
+
+ it "should execute immediate actions on changed resources" do
+ Chef::Platform.should_receive(:find_provider_for_node).exactly(3).times.and_return(Chef::Provider::SnakeOil)
+ provider = Chef::Provider::SnakeOil.new(@node, @collection[0])
+ Chef::Provider::SnakeOil.should_receive(:new).exactly(3).times.and_return(provider)
+ @collection << Chef::Resource::Cat.new("peanut", @collection)
+ @collection[1].notifies :buy, @collection[0], :immediately
+ @collection[1].updated = true
+ provider.should_receive(:action_buy).once.and_return(true)
+ @runner.converge
+ end
+
+ it "should execute delayed actions on changed resources" do
+ Chef::Platform.should_receive(:find_provider_for_node).exactly(3).times.and_return(Chef::Provider::SnakeOil)
+ provider = Chef::Provider::SnakeOil.new(@node, @collection[0])
+ Chef::Provider::SnakeOil.should_receive(:new).exactly(3).times.and_return(provider)
+ @collection << Chef::Resource::Cat.new("peanut", @collection)
+ @collection[1].notifies :buy, @collection[0], :delayed
+ @collection[1].updated = true
+ provider.should_receive(:action_buy).once.and_return(true)
+ @runner.converge
+ end
+
+ def new_runner
+ @node = Chef::Node.new
+ @node.name "latte"
+ @node.operatingsystem "mac_os_x"
+ @node.operatingsystemversion "10.5.1"
+ @collection = Chef::ResourceCollection.new()
+ @collection << Chef::Resource::Cat.new("loulou", @collection)
+ Chef::Platform.set(
+ :resource => :cat,
+ :provider => Chef::Provider::SnakeOil
+ )
+ @runner = Chef::Runner.new(@node, @collection)
+ end
+end \ No newline at end of file