diff options
29 files changed, 769 insertions, 115 deletions
diff --git a/Manifest.txt b/Manifest.txt index a3bb60aaff..d21fe642aa 100644 --- a/Manifest.txt +++ b/Manifest.txt @@ -1,7 +1,101 @@ +.gitignore History.txt Manifest.txt README.txt Rakefile -bin/chef +docs/chef-head.txt +docs/design/HighLevel.graffle +docs/recipe.rb +examples/config.rb +examples/mrepo/Rakefile +examples/node.rb +examples/node.yml +examples/sample_definition.rb +examples/sample_recipe.rb lib/chef.rb -test/test_chef.rb
\ No newline at end of file +lib/chef/config.rb +lib/chef/cookbook.rb +lib/chef/cookbook_loader.rb +lib/chef/mixin/check_helper.rb +lib/chef/mixin/from_file.rb +lib/chef/mixin/params_validate.rb +lib/chef/node.rb +lib/chef/provider.rb +lib/chef/provider/file.rb +lib/chef/recipe.rb +lib/chef/resource.rb +lib/chef/resource/file.rb +lib/chef/resource_collection.rb +lib/chef/resource_definition.rb +server/chef-head/Rakefile +server/chef-head/app/controllers/application.rb +server/chef-head/app/controllers/exceptions.rb +server/chef-head/app/controllers/nodes.rb +server/chef-head/app/helpers/global_helpers.rb +server/chef-head/app/helpers/nodes_helper.rb +server/chef-head/app/models/node.rb +server/chef-head/app/views/exceptions/internal_server_error.html.erb +server/chef-head/app/views/exceptions/not_acceptable.html.erb +server/chef-head/app/views/exceptions/not_found.html.erb +server/chef-head/app/views/layout/application.html.erb +server/chef-head/app/views/nodes/delete.html.erb +server/chef-head/app/views/nodes/edit.html.erb +server/chef-head/app/views/nodes/index.html.erb +server/chef-head/app/views/nodes/new.html.erb +server/chef-head/app/views/nodes/show.html.erb +server/chef-head/config/environments/development.rb +server/chef-head/config/environments/production.rb +server/chef-head/config/environments/test.rb +server/chef-head/config/init.rb +server/chef-head/config/rack.rb +server/chef-head/config/router.rb +server/chef-head/public/images/merb.jpg +server/chef-head/public/merb.fcgi +server/chef-head/public/stylesheets/master.css +server/chef-head/spec/controllers/nodes_spec.rb +server/chef-head/spec/helpers/nodes_helpers.rb +server/chef-head/spec/spec.opts +server/chef-head/spec/spec_helper.rb +server/chef-head/spec/views/nodes/delete.html.erb_spec.rb +server/chef-head/spec/views/nodes/edit.html.erb_spec.rb +server/chef-head/spec/views/nodes/index.html.erb_spec.rb +server/chef-head/spec/views/nodes/new.html.erb_spec.rb +server/chef-head/spec/views/nodes/show.html.erb_spec.rb +spec/data/bad-config.rb +spec/data/config.rb +spec/data/cookbooks/openldap/attributes/default.rb +spec/data/cookbooks/openldap/attributes/smokey.rb +spec/data/cookbooks/openldap/definitions/client.rb +spec/data/cookbooks/openldap/definitions/server.rb +spec/data/cookbooks/openldap/ignore +spec/data/cookbooks/openldap/recipes/gigantor.rb +spec/data/definitions/test.rb +spec/data/kitchen/openldap/attributes/default.rb +spec/data/kitchen/openldap/attributes/robinson.rb +spec/data/kitchen/openldap/definitions/client.rb +spec/data/kitchen/openldap/definitions/drewbarrymore.rb +spec/data/kitchen/openldap/recipes/gigantor.rb +spec/data/kitchen/openldap/recipes/ignoreme.rb +spec/data/kitchen/openldap/recipes/woot.rb +spec/data/nodes/test.rb +spec/data/recipes/test.rb +spec/data/seattle.txt +spec/lib/chef/resource/cat.rb +spec/lib/chef/resource/zen_master.rb +spec/rcov.opts +spec/spec.opts +spec/spec_helper.rb +spec/unit/chef_spec.rb +spec/unit/config_spec.rb +spec/unit/cookbook_loader_spec.rb +spec/unit/cookbook_spec.rb +spec/unit/mixin/params_validate_spec.rb +spec/unit/node_spec.rb +spec/unit/provider/file_spec.rb +spec/unit/provider_spec.rb +spec/unit/recipe_spec.rb +spec/unit/resource/file_spec.rb +spec/unit/resource_collection_spec.rb +spec/unit/resource_definition_spec.rb +spec/unit/resource_spec.rb +tasks/rspec.rb diff --git a/examples/sample_definition.rb b/examples/sample_definition.rb index fa4ddfe120..9264a4ba3b 100644 --- a/examples/sample_definition.rb +++ b/examples/sample_definition.rb @@ -3,6 +3,12 @@ web_server "monchichi" do two "something else" end +runit_service "bobo" do + directory "monkey" + downif "/bin/false is true" + templatedir "something" +end + define :runit_service, :directory => "/etc/sv", :downif => "/bin/false", :templatedir => nil do require_recipe "runit" diff --git a/examples/sample_recipe.rb b/examples/sample_recipe.rb index 2eb4ba7f2c..234a1587a4 100644 --- a/examples/sample_recipe.rb +++ b/examples/sample_recipe.rb @@ -10,7 +10,6 @@ service "apache2" do end file "/etc/nsswitch.conf" do - insure "present" owner "root" group "root" mode 0644 @@ -18,7 +17,6 @@ file "/etc/nsswitch.conf" do end file "/etc/ldap.conf" do - insure "present" owner "root" group "root" mode 0644 diff --git a/lib/chef/config.rb b/lib/chef/config.rb index 6c0402bdda..191de2504c 100644 --- a/lib/chef/config.rb +++ b/lib/chef/config.rb @@ -19,51 +19,59 @@ # require File.join(File.dirname(__FILE__), "mixin", "check_helper") +require File.join(File.dirname(__FILE__), "mixin", "from_file") + +# Chef::Config[:variable] +# @config = Chef::Config.new() +# +# Chef::ConfigFast << Chef::Config +# +# Chef::Config.from_file(foo) +# Chef::Resource.from_file (NoMethodError) +# Chef::Config[:cookbook_path] +# Chef::Config.cookbook_path +# Chef::Config.cookbook_path "one", "two" class Chef class Config - include Chef::Mixin::CheckHelper + + @configuration = { + :cookbook_path => [ "/etc/chef/site-cookbook", "/etc/chef/cookbook" ] + } - def initialize - set_defaults - end - - def self.load_file(file) - config = Chef::Config.new - if File.exists?(file) && File.readable?(file) - begin - config.instance_eval(IO.read(file), file, 1) - rescue NoMethodError => e - new_message = "You probably tried to use a config variable that doesn't exist!\n" - new_message += e.message - raise e.exception(new_message) + class << self + include Chef::Mixin::FromFile + + def configure(&block) + yield @configuration + end + + def [](config_option) + if @configuration.has_key?(config_option.to_sym) + @configuration[config_option.to_sym] + else + raise ArgumentError, "Cannot find configuration option #{config_option.to_s}" end - else - raise IOError, "Cannot find or read #{file}!" end - config - end + + def []=(config_option, value) + @configuration[config_option.to_sym] = value + end - def cookbook_path(*args) - if args.length == 0 - @cookbook_path - else - flat_args = args.flatten - flat_args.each do |a| - unless a.kind_of?(String) - raise ArgumentError, "You must pass strings to cookbook_path!" + def method_missing(method_symbol, *args) + if @configuration.has_key?(method_symbol) + if args.length == 1 + @configuration[method_symbol] = args[0] + elsif args.length > 1 + @configuration[method_symbol] = args end + return @configuration[method_symbol] + else + raise ArgumentError, "Cannot find configuration option #{method_symbol.to_s}" end - @cookbook_path = flat_args end - end - - def set_defaults - @cookbook_path = [ - "/etc/chef/site-cookbook", - "/etc/chef/cookbook", - ] - end + + end # class << self end end
\ No newline at end of file diff --git a/lib/chef/cookbook.rb b/lib/chef/cookbook.rb index c69491c2f2..218ecb3c10 100644 --- a/lib/chef/cookbook.rb +++ b/lib/chef/cookbook.rb @@ -86,20 +86,17 @@ class Chef results end - def load_recipe(name, node, collection=nil, definitions=nil, config=nil) + def load_recipe(name, node, collection=nil, definitions=nil, cookbook_loader=nil) cookbook_name = @name recipe_name = nil - case name - when /^(.+)::(.+)$/ - recipe_name = $2 - else - recipe_name = name - end + nmatch = name.match(/^(.+)::(.+)$/) + recipe_name = nmatch ? nmatch[2] : name + unless @recipe_names.has_key?(recipe_name) raise ArgumentError, "Cannot find a recipe matching #{recipe_name} in cookbook #{@name}" end recipe = Chef::Recipe.new(cookbook_name, recipe_name, node, - collection, definitions, config) + collection, definitions, cookbook_loader) recipe.from_file(@recipe_files[@recipe_names[recipe_name]]) recipe end diff --git a/lib/chef/cookbook_loader.rb b/lib/chef/cookbook_loader.rb index d585b1e440..24ec01e574 100644 --- a/lib/chef/cookbook_loader.rb +++ b/lib/chef/cookbook_loader.rb @@ -21,19 +21,18 @@ class Chef class CookbookLoader - attr_accessor :cookbook, :config + attr_accessor :cookbook include Enumerable - def initialize(config) - @config = config + def initialize() @cookbook = Hash.new load_cookbooks end def load_cookbooks cookbook_settings = Hash.new - @config.cookbook_path.each do |cb_path| + Chef::Config.cookbook_path.each do |cb_path| Dir[File.join(cb_path, "*")].each do |cookbook| next unless File.directory?(cookbook) cookbook_name = File.basename(cookbook).to_sym diff --git a/lib/chef/mixin/from_file.rb b/lib/chef/mixin/from_file.rb index 68f614c07a..b1ad600ef8 100644 --- a/lib/chef/mixin/from_file.rb +++ b/lib/chef/mixin/from_file.rb @@ -1,3 +1,8 @@ +# +# Chef::Mixin::FromFile +# +# A mixin that adds instance_eval support to a given object. +# # Author:: Adam Jacob (<adam@hjksolutions.com>) # Copyright:: Copyright (c) 2008 HJK Solutions, LLC # License:: GNU General Public License version 2 or later @@ -19,7 +24,12 @@ class Chef module Mixin - module FromFile + module FromFile + + # Loads a given ruby file, and runs instance_eval against it in the context of the current + # object. + # + # Raises an IOError if the file cannot be found, or is not readable. def from_file(filename) if File.exists?(filename) && File.readable?(filename) self.instance_eval(IO.read(filename), filename, 1) diff --git a/lib/chef/mixin/params_validate.rb b/lib/chef/mixin/params_validate.rb new file mode 100644 index 0000000000..4c6b5f6612 --- /dev/null +++ b/lib/chef/mixin/params_validate.rb @@ -0,0 +1,162 @@ +# +# 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. :) +# +# 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 + module Mixin + module ParamsValidate + + # Takes a hash of options, along with a map to validate them. Returns the original + # options hash, plus any changes that might have been made (through things like setting + # default values in the validation map) + # + # For example: + # + # validate({ :one => "neat" }, { :one => { :kind_of => String }}) + # + # Would raise an exception if the value of :one above is not a kind_of? string. Valid + # map options are: + # + # :default:: Sets the default value for this parameter. + # :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 + # 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. + def validate(opts, map) + #-- + # validate works by taking the keys in the validation map, assuming it's a hash, and + # looking for _pv_:symbol as methods. Assuming it find them, it calls the right + # one. + #++ + raise ArgumentError, "Options must be a hash" unless opts.kind_of?(Hash) + raise ArgumentError, "Validation Map must be a hash" unless map.kind_of?(Hash) + + map.each do |key, validation| + unless key.kind_of?(Symbol) || key.kind_of?(String) + raise ArgumentError, "Validation map keys must be symbols or strings!" + end + case validation + when true + _pv_required(opts, key) + when false + true + when Hash + validation.each do |check, carg| + check_method = "_pv_#{check.to_s}" + if self.respond_to?(check_method, true) + self.send(check_method, opts, key, carg) + else + raise ArgumentError, "Validation map has unknown check: #{check}" + end + end + end + end + opts + 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) + opts[key.to_s] + elsif opts.has_key?(key.to_sym) + opts[key.to_sym] + else + nil + end + end + + # Raise an exception if the parameter is not found. + def _pv_required(opts, key, is_required=true) + if is_required + if opts.has_key?(key.to_s) || opts.has_key?(key.to_sym) + true + else + raise ArgumentError, "Required argument #{key} is missing!" + 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}." + end + end + end + + # Raise an exception if the parameter does not respond to a given set of methods. + def _pv_respond_to(opts, key, method_name_list) + value = _pv_opts_lookup(opts, key) + if value != nil + method_name_list.to_a.each do |method_name| + unless value.respond_to?(method_name) + raise ArgumentError, "Option #{key} must have a #{method_name} method!" + end + end + end + end + + # Assign a default value to a parameter. + def _pv_default(opts, key, default_value) + value = _pv_opts_lookup(opts, key) + if value == nil + opts[key] = default_value + end + end + + # 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}" + end + end + end + + # Check a parameter against a hash of proc's. + def _pv_callbacks(opts, key, callbacks) + raise ArgumentError, "Callback list must be a hash!" unless callbacks.kind_of?(Hash) + value = _pv_opts_lookup(opts, key) + if value != nil + callbacks.each do |message, zeproc| + if zeproc.call(value) != true + raise ArgumentError, "Option #{key}'s value #{value} #{message}!" + end + end + end + end + end + end +end + diff --git a/lib/chef/node.rb b/lib/chef/node.rb index 69dc6ab3f0..d5896fbd95 100644 --- a/lib/chef/node.rb +++ b/lib/chef/node.rb @@ -1,4 +1,7 @@ # +# Chef::Node +# +# # Author:: Adam Jacob (<adam@hjksolutions.com>) # Copyright:: Copyright (c) 2008 HJK Solutions, LLC # License:: GNU General Public License version 2 or later @@ -29,12 +32,14 @@ class Chef include Chef::Mixin::CheckHelper include Chef::Mixin::FromFile + # Create a new Chef::Node object. def initialize() @name = nil @attribute = Hash.new @recipe_list = Array.new end + # Set the name of this Node, or return the current name. def name(arg=nil) set_if_args(@name, arg) do |a| case a @@ -46,6 +51,7 @@ class Chef end end + # Return an attribute of this node. Returns nil if the attribute is not found. def [](attrib) if @attribute.has_key?(attrib) @attribute[attrib] @@ -56,6 +62,15 @@ class Chef end end + # Iterates over each attribute, passing the attribute and value to the block. + def each_attribute(&block) + @attribute.each do |k,v| + yield(k, v) + end + end + + # Return true if this Node has a given attribute, false if not. Takes either a symbol or + # a string. def attribute?(attrib) result = false result = @attribute.has_key?(attrib) @@ -63,10 +78,13 @@ class Chef return @attribute.has_key?(attrib.to_sym) end + # Returns true if this Node expects a given recipe, false if not. def recipe?(recipe_name) @recipe_list.detect { |r| r == recipe_name } ? true : false end + # Returns an Array of recipes. If you call it with arguments, they will become the new + # list of recipes. def recipes(*args) if args.length > 0 @recipe_list = args.flatten @@ -75,6 +93,9 @@ class Chef end end + # Set an attribute based on the missing method. If you pass an argument, we'll use that + # to set the attribute values. Otherwise, we'll wind up just returning the attributes + # value. def method_missing(symbol, *args) if args.length != 0 @attribute[symbol] = args.length == 1 ? args[0] : args diff --git a/lib/chef/recipe.rb b/lib/chef/recipe.rb index 81fb979de2..af1cbd2d77 100644 --- a/lib/chef/recipe.rb +++ b/lib/chef/recipe.rb @@ -26,30 +26,47 @@ class Chef include Chef::Mixin::FromFile attr_accessor :cookbook_name, :recipe_name, :recipe, :node, :collection, - :definitions, :config, :params + :definitions, :params, :cookbook_loader - def initialize(cookbook_name, recipe_name, node, collection=nil, definitions=nil, config=nil) + def initialize(cookbook_name, recipe_name, node, collection=nil, definitions=nil, cookbook_loader=nil) @cookbook_name = cookbook_name @recipe_name = recipe_name @node = node + if collection @collection = collection else @collection = Chef::ResourceCollection.new() end - if config - @config = config - else - @config = Chef::Config.new() - end + if definitions @definitions = definitions else @definitions = Hash.new end + + if cookbook_loader + @cookbook_loader = cookbook_loader + else + @cookbook_loader = Chef::CookbookLoader.new() + end + @params = Hash.new end + def require_recipe(*args) + args.flatten.each do |recipe| + rmatch = recipe.match(/(.+?)::(.+)/) + if rmatch + cookbook = @cookbook_loader[rmatch[1]] + cookbook.load_recipe(rmatch[2], @node, @collection, @definitions, @cookbook_loader) + else + cookbook = @cookbook_loader[recipe] + cookbook.load_recipe("default", @node, @collection, @definitions, @cookbook_loader) + end + end + end + def resources(*args) @collection.resources(*args) end @@ -58,26 +75,28 @@ class Chef resource = nil # If we have a definition that matches, we want to use that instead. This should # let you do some really crazy over-riding of "native" types, if you really want - # to. + # to. if @definitions.has_key?(method_symbol) new_def = @definitions[method_symbol].dup new_def.instance_eval(&block) - new_recipe = Chef::Recipe.new(@cookbook_name, @recipe_name, @node, @collection, @definitions, @config) + new_recipe = Chef::Recipe.new(@cookbook_name, @recipe_name, @node, @collection, @definitions, @cookbook_loader) new_recipe.params = new_def.params new_recipe.instance_eval(&new_def.recipe) else method_name = method_symbol.to_s # Otherwise, we're rocking the regular resource call route. rname = nil - case method_name - when /^(.+)_(.+)$/ - rname = "Chef::Resource::#{$1.capitalize}#{$2.capitalize}" - when /^(.+)$/ - rname = "Chef::Resource::#{$1.capitalize}" + mn = method_name.match(/^(.+)_(.+)$/) + if mn + rname = "Chef::Resource::#{mn[1].capitalize}#{mn[2].capitalize}" + else + short_match = method_name.match(/^(.+)$/) + if short_match + rname = "Chef::Resource::#{short_match[1].capitalize}" + end end begin args << @collection - args << @config resource = eval(rname).new(*args) resource.params = @params resource.instance_eval(&block) diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb index 2d7b90ed7f..fab6279a0e 100644 --- a/lib/chef/resource.rb +++ b/lib/chef/resource.rb @@ -26,21 +26,16 @@ class Chef include Chef::Mixin::CheckHelper - attr_accessor :tag, :actions, :config, :params + attr_accessor :tag, :actions, :params attr_reader :name, :noop, :resource_name, :collection, :notifies, :subscribes - def initialize(name, collection=nil, config=nil) + def initialize(name, collection=nil) @name = name if collection @collection = collection else @collection = Chef::ResourceCollection.new() end - if config - @config = config - else - @config = Chef::Config.new() - end @tag = [ name.to_s ] @noop = nil @before = nil diff --git a/lib/chef/resource/file.rb b/lib/chef/resource/file.rb index 3132aa5a1a..59c51ba8d3 100644 --- a/lib/chef/resource/file.rb +++ b/lib/chef/resource/file.rb @@ -22,9 +22,9 @@ class Chef class Resource class File < Chef::Resource - def initialize(name, collection=nil, config=nil) + def initialize(name, collection=nil) @resource_name = :file - super(name, collection, config) + super(name, collection) @path = name @backup = true @action = "create" diff --git a/spec/data/config.rb b/spec/data/config.rb index 2b1fcdc341..0b3340ce57 100644 --- a/spec/data/config.rb +++ b/spec/data/config.rb @@ -2,5 +2,5 @@ # Sample Chef Config File # -cookbook_path = "/etc/chef/cookbook", "/etc/chef/site-cookbook" +cookbook_path "/etc/chef/cookbook", "/etc/chef/site-cookbook" diff --git a/spec/data/cookbooks/openldap/recipes/default.rb b/spec/data/cookbooks/openldap/recipes/default.rb new file mode 100644 index 0000000000..0ac8a9bb4b --- /dev/null +++ b/spec/data/cookbooks/openldap/recipes/default.rb @@ -0,0 +1,3 @@ +cat "blanket" do + pretty_kitty true +end diff --git a/spec/lib/chef/resource/cat.rb b/spec/lib/chef/resource/cat.rb index d9f3c657e0..5b66c9cabd 100644 --- a/spec/lib/chef/resource/cat.rb +++ b/spec/lib/chef/resource/cat.rb @@ -21,9 +21,9 @@ class Chef class Resource class Cat < Chef::Resource - def initialize(name, collection=nil, config=nil) + def initialize(name, collection=nil) @resource_name = :cat - super(name, collection, config) + super(name, collection) end def pretty_kitty(arg=nil) diff --git a/spec/lib/chef/resource/zen_master.rb b/spec/lib/chef/resource/zen_master.rb index 3559b3272c..91ae87b246 100644 --- a/spec/lib/chef/resource/zen_master.rb +++ b/spec/lib/chef/resource/zen_master.rb @@ -22,9 +22,9 @@ class Chef class ZenMaster < Chef::Resource attr_reader :peace - def initialize(name, collection=nil, config=nil) + def initialize(name, collection=nil) @resource_name = :zen_master - super(name, collection, config) + super(name, collection) end def peace(tf) diff --git a/spec/unit/chef_spec.rb b/spec/unit/chef_spec.rb index a75e3ad548..cfb6cd4dac 100644 --- a/spec/unit/chef_spec.rb +++ b/spec/unit/chef_spec.rb @@ -18,7 +18,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -require File.join(File.dirname(__FILE__), "..", "spec_helper") +require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) describe Chef do it "should have a version defined" do diff --git a/spec/unit/config_spec.rb b/spec/unit/config_spec.rb index dec94796e0..4148336078 100644 --- a/spec/unit/config_spec.rb +++ b/spec/unit/config_spec.rb @@ -18,55 +18,70 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -require File.join(File.dirname(__FILE__), "..", "spec_helper") +require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) describe Chef::Config do - before(:each) do - @config = Chef::Config.new - end it "should load a .rb file in context" do lambda { - Chef::Config.load_file(File.join(File.dirname(__FILE__), "..", "data", "config.rb")) + Chef::Config.from_file(File.join(File.dirname(__FILE__), "..", "data", "config.rb")) }.should_not raise_error end - it "should raise a NoMethodError with an explanation if you have a bad config file" do + it "should raise an ArgumentError with an explanation if you try and set a non-existent variable" do lambda { - Chef::Config.load_file(File.join(File.dirname(__FILE__), "..", "data", "bad-config.rb")) - }.should raise_error(NoMethodError) + Chef::Config.from_file(File.join(File.dirname(__FILE__), "..", "data", "bad-config.rb")) + }.should raise_error(ArgumentError) end it "should raise an IOError if it can't find the file" do lambda { - Chef::Config.load_file("/tmp/timmytimmytimmy") + Chef::Config.from_file("/tmp/timmytimmytimmy") }.should raise_error(IOError) end it "should have a default cookbook_path" do - @config.cookbook_path.should be_kind_of(Array) + Chef::Config.cookbook_path.should be_kind_of(Array) end it "should allow you to set a cookbook_path with a string" do - @config.cookbook_path("/etc/chef/cookbook") - @config.cookbook_path.should eql(["/etc/chef/cookbook"]) + Chef::Config.cookbook_path("/etc/chef/cookbook") + Chef::Config.cookbook_path.should eql("/etc/chef/cookbook") end it "should allow you to set a cookbook_path with multiple strings" do - @config.cookbook_path("/etc/chef/cookbook", "/etc/chef/upstream-cookbooks") - @config.cookbook_path.should eql([ + Chef::Config.cookbook_path("/etc/chef/cookbook", "/etc/chef/upstream-cookbooks") + Chef::Config.cookbook_path.should eql([ "/etc/chef/cookbook", "/etc/chef/upstream-cookbooks" ]) end it "should allow you to set a cookbook_path with an array" do - @config.cookbook_path ["one", "two"] - @config.cookbook_path.should eql(["one", "two"]) + Chef::Config.cookbook_path ["one", "two"] + Chef::Config.cookbook_path.should eql(["one", "two"]) + end + + it "should allow you to reference a value by index" do + Chef::Config[:cookbook_path].should be_kind_of(Array) + end + + it "should allow you to set a value by index" do + Chef::Config[:cookbook_path] = "one" + Chef::Config[:cookbook_path].should == "one" + end + + it "should allow you to set config values with a block" do + Chef::Config.configure do |c| + c[:cookbook_path] = "monkey_rabbit" + c[:otherthing] = "boo" + end + Chef::Config.cookbook_path.should == "monkey_rabbit" + Chef::Config.otherthing.should == "boo" end - it "should not allow you to set a cookbook_path with anything else" do - lambda { @config.cookbook_path :symbol }.should raise_error(ArgumentError) + it "should raise an ArgumentError if you access a config option that does not exist" do + lambda { Chef::Config[:snob_hobbery] }.should raise_error(ArgumentError) end end
\ No newline at end of file diff --git a/spec/unit/cookbook_loader_spec.rb b/spec/unit/cookbook_loader_spec.rb index 3da3e644ec..d66034ce7d 100644 --- a/spec/unit/cookbook_loader_spec.rb +++ b/spec/unit/cookbook_loader_spec.rb @@ -18,16 +18,15 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -require File.join(File.dirname(__FILE__), "..", "spec_helper") +require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) describe Chef::CookbookLoader do before(:each) do - config = Chef::Config.new - config.cookbook_path [ + Chef::Config.cookbook_path [ File.join(File.dirname(__FILE__), "..", "data", "cookbooks"), File.join(File.dirname(__FILE__), "..", "data", "kitchen") ] - @cl = Chef::CookbookLoader.new(config) + @cl = Chef::CookbookLoader.new() end it "should be a Chef::CookbookLoader object" do @@ -56,7 +55,7 @@ describe Chef::CookbookLoader do end it "should find all the cookbooks in the cookbook path" do - @cl.config.cookbook_path << File.join(File.dirname(__FILE__), "..", "data", "hidden-cookbooks") + Chef::Config.cookbook_path << File.join(File.dirname(__FILE__), "..", "data", "hidden-cookbooks") @cl.load_cookbooks @cl.detect { |cb| cb.name == :openldap }.should_not eql(nil) @cl.detect { |cb| cb.name == :apache2 }.should_not eql(nil) diff --git a/spec/unit/cookbook_spec.rb b/spec/unit/cookbook_spec.rb index 1e44f519f7..f702fe32c1 100644 --- a/spec/unit/cookbook_spec.rb +++ b/spec/unit/cookbook_spec.rb @@ -18,7 +18,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -require File.join(File.dirname(__FILE__), "..", "spec_helper") +require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) describe Chef::Cookbook do COOKBOOK_PATH = File.join(File.dirname(__FILE__), "..", "data", "cookbooks", "openldap") diff --git a/spec/unit/mixin/params_validate_spec.rb b/spec/unit/mixin/params_validate_spec.rb new file mode 100644 index 0000000000..3ade2b3c2d --- /dev/null +++ b/spec/unit/mixin/params_validate_spec.rb @@ -0,0 +1,298 @@ +# 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")) + +class TinyClass + include Chef::Mixin::ParamsValidate + + def music(is_good=true) + is_good + end +end + +describe Chef::Mixin::ParamsValidate do + before(:each) do + @vo = TinyClass.new() + end + + it "should allow a hash and a hash as arguments to validate" do + lambda { @vo.validate({:one => "two"}, {}) }.should_not raise_error(ArgumentError) + end + + it "should raise an argument error if validate is called incorrectly" do + lambda { @vo.validate("one", "two") }.should raise_error(ArgumentError) + end + + it "should require validation map keys to be symbols or strings" do + lambda { @vo.validate({:one => "two"}, { :one => true }) }.should_not raise_error(ArgumentError) + lambda { @vo.validate({:one => "two"}, { "one" => true }) }.should_not raise_error(ArgumentError) + lambda { @vo.validate({:one => "two"}, { Hash.new => true }) }.should raise_error(ArgumentError) + end + + it "should allow options to be required with true" do + lambda { @vo.validate({:one => "two"}, { :one => true }) }.should_not raise_error(ArgumentError) + end + + it "should allow options to be optional with false" do + lambda { @vo.validate({}, {:one => false})}.should_not raise_error(ArgumentError) + end + + it "should allow you to check what kind_of? thing an argument is with kind_of" do + lambda { + @vo.validate( + {:one => "string"}, + { + :one => { + :kind_of => String + } + } + ) + }.should_not raise_error(ArgumentError) + + lambda { + @vo.validate( + {:one => "string"}, + { + :one => { + :kind_of => Array + } + } + ) + }.should raise_error(ArgumentError) + end + + it "should allow you to specify an argument is required with required" do + lambda { + @vo.validate( + {:one => "string"}, + { + :one => { + :required => true + } + } + ) + }.should_not raise_error(ArgumentError) + + lambda { + @vo.validate( + {:two => "string"}, + { + :one => { + :required => true + } + } + ) + }.should raise_error(ArgumentError) + + lambda { + @vo.validate( + {:two => "string"}, + { + :one => { + :required => false + } + } + ) + }.should_not raise_error(ArgumentError) + end + + it "should allow you to specify whether an object has a method with respond_to" do + lambda { + @vo.validate( + {:one => @vo}, + { + :one => { + :respond_to => "validate" + } + } + ) + }.should_not raise_error(ArgumentError) + + lambda { + @vo.validate( + {:one => @vo}, + { + :one => { + :respond_to => "monkey" + } + } + ) + }.should raise_error(ArgumentError) + end + + it "should allow you to specify whether an object has all the given methods with respond_to and an array" do + lambda { + @vo.validate( + {:one => @vo}, + { + :one => { + :respond_to => ["validate", "music"] + } + } + ) + }.should_not raise_error(ArgumentError) + + lambda { + @vo.validate( + {:one => @vo}, + { + :one => { + :respond_to => ["monkey", "validate"] + } + } + ) + }.should raise_error(ArgumentError) + end + + it "should let you set a default value with default => value" do + arguments = Hash.new + @vo.validate(arguments, { + :one => { + :default => "is the loneliest number" + } + }) + arguments[:one].should == "is the loneliest number" + end + + it "should let you check regular expressions" do + lambda { + @vo.validate( + { :one => "is good" }, + { + :one => { + :regex => /^is good$/ + } + } + ) + }.should_not raise_error(ArgumentError) + + lambda { + @vo.validate( + { :one => "is good" }, + { + :one => { + :regex => /^is bad$/ + } + } + ) + }.should raise_error(ArgumentError) + end + + it "should let you specify your own callbacks" do + lambda { + @vo.validate( + { :one => "is good" }, + { + :one => { + :callbacks => { + "should be equal to is good" => lambda { |a| + a == "is good" + }, + } + } + } + ) + }.should_not raise_error(ArgumentError) + + lambda { + @vo.validate( + { :one => "is bad" }, + { + :one => { + :callbacks => { + "should be equal to 'is good'" => lambda { |a| + a == "is good" + }, + } + } + } + ) + }.should raise_error(ArgumentError) + end + + it "should let you combine checks" do + args = { :one => "is good", :two => "is bad" } + lambda { + @vo.validate( + args, + { + :one => { + :kind_of => String, + :respond_to => [ :to_s, :upcase ], + :regex => /^is good/, + :callbacks => { + "should be your friend" => lambda { |a| + a == "is good" + } + }, + :required => true + }, + :two => { + :kind_of => String, + :required => false + }, + :three => { :default => "neato mosquito" } + } + ) + }.should_not raise_error(ArgumentError) + args[:three].should == "neato mosquito" + lambda { + @vo.validate( + args, + { + :one => { + :kind_of => String, + :respond_to => [ :to_s, :upcase ], + :regex => /^is good/, + :callbacks => { + "should be your friend" => lambda { |a| + a == "is good" + } + }, + :required => true + }, + :two => { + :kind_of => Hash, + :required => false + }, + :three => { :default => "neato mosquito" } + } + ) + }.should raise_error(ArgumentError) + end + + it "should raise an ArgumentError if the validation map has an unknown check" do + lambda { @vo.validate( + { :one => "two" }, + { + :one => { + :busted => "check" + } + } + ) + }.should raise_error(ArgumentError) + end + + it "should accept keys that are strings in the options" do + lambda { + @vo.validate({ "one" => "two" }, { :one => { :regex => /^two$/ }}) + }.should_not 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 e5a92486e5..a8c456e8f5 100644 --- a/spec/unit/node_spec.rb +++ b/spec/unit/node_spec.rb @@ -18,7 +18,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -require File.join(File.dirname(__FILE__), "..", "spec_helper") +require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) describe Chef::Node do before(:each) do @@ -104,5 +104,17 @@ describe Chef::Node do lambda { @node.from_file("/tmp/monkeydiving") }.should raise_error(IOError) end + it "should allow you to iterate over attributes with each_attribute" do + @node.sunshine "is bright" + @node.canada "is a nice place" + seen_attributes = Hash.new + @node.each_attribute do |a,v| + seen_attributes[a] = v + end + seen_attributes.should have_key(:sunshine) + seen_attributes.should have_key(:canada) + seen_attributes[:sunshine].should == "is bright" + seen_attributes[:canada].should == "is a nice place" + 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 2ee6b1960e..4a98e88df0 100644 --- a/spec/unit/provider/file_spec.rb +++ b/spec/unit/provider/file_spec.rb @@ -20,7 +20,7 @@ require 'ostruct' -require File.join(File.dirname(__FILE__), "..", "..", "spec_helper") +require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper")) describe Chef::Provider::File do before(:each) do diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb index 51980cbe99..71cc1a47e3 100644 --- a/spec/unit/provider_spec.rb +++ b/spec/unit/provider_spec.rb @@ -18,7 +18,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -require File.join(File.dirname(__FILE__), "..", "spec_helper") +require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) describe Chef::Provider do before(:each) do diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb index e02973d73c..3c33a6d485 100644 --- a/spec/unit/recipe_spec.rb +++ b/spec/unit/recipe_spec.rb @@ -18,7 +18,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -require File.join(File.dirname(__FILE__), "..", "spec_helper") +require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) describe Chef::Recipe do before(:each) do @@ -110,7 +110,7 @@ CODE @recipe.resources(:zen_master => "lao tzu").something.should eql(true) end - it "should load a node from a ruby file" do + it "should load a resource from a ruby file" do @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") @@ -123,5 +123,23 @@ CODE it "should raise an exception if the file cannot be found or read" do lambda { @recipe.from_file("/tmp/monkeydiving") }.should raise_error(IOError) end + + it "should evaluate another recipe with recipe_require" do + Chef::Config.cookbook_path File.join(File.dirname(__FILE__), "..", "data", "cookbooks") + @recipe.cookbook_loader.load_cookbooks + @recipe.require_recipe "openldap::gigantor" + res = @recipe.resources(:cat => "blanket") + res.name.should eql("blanket") + res.pretty_kitty.should eql(false) + end + + it "should load the default recipe for a cookbook if require_recipe is called without a ::" do + Chef::Config.cookbook_path File.join(File.dirname(__FILE__), "..", "data", "cookbooks") + @recipe.cookbook_loader.load_cookbooks + @recipe.require_recipe "openldap" + res = @recipe.resources(:cat => "blanket") + res.name.should eql("blanket") + res.pretty_kitty.should eql(true) + end end
\ No newline at end of file diff --git a/spec/unit/resource/file_spec.rb b/spec/unit/resource/file_spec.rb index 474c58f0d1..bffb4e43f8 100644 --- a/spec/unit/resource/file_spec.rb +++ b/spec/unit/resource/file_spec.rb @@ -17,7 +17,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -require File.join(File.dirname(__FILE__), "..", "..", "spec_helper") +require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper")) describe Chef::Resource::File do diff --git a/spec/unit/resource_collection_spec.rb b/spec/unit/resource_collection_spec.rb index 6f6f195513..10f7d43cde 100644 --- a/spec/unit/resource_collection_spec.rb +++ b/spec/unit/resource_collection_spec.rb @@ -17,7 +17,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -require File.join(File.dirname(__FILE__), "..", "spec_helper") +require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) describe Chef::ResourceCollection do diff --git a/spec/unit/resource_definition_spec.rb b/spec/unit/resource_definition_spec.rb index 8bcd9c52f9..8b8ee16f3a 100644 --- a/spec/unit/resource_definition_spec.rb +++ b/spec/unit/resource_definition_spec.rb @@ -18,7 +18,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -require File.join(File.dirname(__FILE__), "..", "spec_helper") +require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) describe Chef::ResourceDefinition do before(:each) do diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb index f767fe8348..d6db1f2d74 100644 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -18,7 +18,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -require File.join(File.dirname(__FILE__), "..", "spec_helper") +require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) describe Chef::Resource do before(:each) do |