summaryrefslogtreecommitdiff
path: root/lib/chef
diff options
context:
space:
mode:
authorAdam Jacob <adam@hjksolutions.com>2008-04-08 00:57:17 -0700
committerAdam Jacob <adam@hjksolutions.com>2008-04-08 00:57:17 -0700
commit95eb1375f0647accd1b1a1569a7d0e3acc4a61e4 (patch)
treea58d6fddde4d7818dc33511ff8275c7b9f69f1c6 /lib/chef
parent434f25ba07b5c0c50baa1e15b14a945bba3c3c3b (diff)
downloadchef-95eb1375f0647accd1b1a1569a7d0e3acc4a61e4.tar.gz
Adding chef-solo command, config examples, Chef::Log class, Chef::Log::Formatter, Chef::Compile, and all the tests
Diffstat (limited to 'lib/chef')
-rw-r--r--lib/chef/compile.rb62
-rw-r--r--lib/chef/config.rb5
-rw-r--r--lib/chef/cookbook.rb6
-rw-r--r--lib/chef/log.rb93
-rw-r--r--lib/chef/log/formatter.rb52
-rw-r--r--lib/chef/node.rb62
-rw-r--r--lib/chef/recipe.rb1
-rw-r--r--lib/chef/resource_collection.rb3
8 files changed, 282 insertions, 2 deletions
diff --git a/lib/chef/compile.rb b/lib/chef/compile.rb
new file mode 100644
index 0000000000..5b1f9d756e
--- /dev/null
+++ b/lib/chef/compile.rb
@@ -0,0 +1,62 @@
+#
+# Chef::Compile
+#
+# Compile a nodes resources.
+#
+# 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 Compile
+
+ attr_accessor :node, :cookbook_loader, :resource_collection, :definitions
+
+ def initialize()
+ @node = nil
+ @cookbook_loader = Chef::CookbookLoader.new
+ @resource_collection = Chef::ResourceCollection.new
+ @definitions = Hash.new
+ end
+
+ def load_node(name)
+ @node = Chef::Node.find(name)
+ end
+
+ def load_definitions()
+ @cookbook_loader.each do |cookbook|
+ hash = cookbook.load_definitions
+ @definitions.merge!(hash)
+ end
+ end
+
+ def load_recipes
+ @node.recipes.each do |recipe|
+ rmatch = recipe.match(/(.+?)::(.+)/)
+ if rmatch
+ cookbook = @cookbook_loader[rmatch[1]]
+ cookbook.load_recipe(rmatch[2], @node, @resource_collection, @definitions, @cookbook_loader)
+ else
+ cookbook = @cookbook_loader[recipe]
+ cookbook.load_recipe("default", @node, @resource_collection, @definitions, @cookbook_loader)
+ end
+ end
+ end
+
+ end
+end \ No newline at end of file
diff --git a/lib/chef/config.rb b/lib/chef/config.rb
index 191de2504c..4793011fb4 100644
--- a/lib/chef/config.rb
+++ b/lib/chef/config.rb
@@ -37,7 +37,10 @@ class Chef
include Chef::Mixin::CheckHelper
@configuration = {
- :cookbook_path => [ "/etc/chef/site-cookbook", "/etc/chef/cookbook" ]
+ :cookbook_path => [ "/etc/chef/site-cookbook", "/etc/chef/cookbook" ],
+ :node_path => "/etc/chef/node",
+ :log_level => :info,
+ :log_location => STDOUT
}
class << self
diff --git a/lib/chef/cookbook.rb b/lib/chef/cookbook.rb
index 218ecb3c10..39013069ca 100644
--- a/lib/chef/cookbook.rb
+++ b/lib/chef/cookbook.rb
@@ -30,6 +30,7 @@ class Chef
@definition_files = Array.new
@recipe_files = Array.new
@recipe_names = Hash.new
+ @loaded_attributes = false
end
def load_attributes(node)
@@ -39,6 +40,7 @@ class Chef
@attribute_files.each do |file|
node.from_file(file)
end
+ @loaded_atributes = true
node
end
@@ -95,6 +97,10 @@ class Chef
unless @recipe_names.has_key?(recipe_name)
raise ArgumentError, "Cannot find a recipe matching #{recipe_name} in cookbook #{@name}"
end
+ Chef::Log.debug("Found recipe #{recipe_name} in cookbook #{cookbook_name}") if Chef::Log.debug?
+ unless @loaded_attributes
+ load_attributes(node)
+ end
recipe = Chef::Recipe.new(cookbook_name, recipe_name, node,
collection, definitions, cookbook_loader)
recipe.from_file(@recipe_files[@recipe_names[recipe_name]])
diff --git a/lib/chef/log.rb b/lib/chef/log.rb
new file mode 100644
index 0000000000..cf8b0b45e9
--- /dev/null
+++ b/lib/chef/log.rb
@@ -0,0 +1,93 @@
+#
+# Chef::Logger
+#
+# A simple wrapper for the standard Ruby Logger.
+#
+# 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 'logger'
+
+class Chef
+ class Log
+
+ @logger = nil
+
+ class << self
+ attr_reader :logger #:nodoc
+
+ # Use Chef::Logger.init when you want to set up the logger manually. Arguments to this method
+ # get passed directly to Logger.new, so check out the documentation for the standard Logger class
+ # to understand what to do here.
+ #
+ # If this method is called with no arguments, it will log to STDOUT at the :info level.
+ #
+ # It also configures the Logger instance it creates to use the custom Chef::Log::Formatter class.
+ def init(*opts)
+ if opts.length == 0
+ @logger = Logger.new(STDOUT)
+ else
+ @logger = Logger.new(*opts)
+ end
+ @logger.formatter = Chef::Log::Formatter.new()
+ level(Chef::Config.log_level)
+ end
+
+ # Sets the level for the Logger object by symbol. Valid arguments are:
+ #
+ # :debug
+ # :info
+ # :warn
+ # :error
+ # :fatal
+ #
+ # Throws an ArgumentError if you feed it a bogus log level.
+ def level(loglevel)
+ init() unless @logger
+ case loglevel
+ when :debug
+ @logger.level = Logger::DEBUG
+ when :info
+ @logger.level = Logger::INFO
+ when :warn
+ @logger.level = Logger::WARN
+ when :error
+ @logger.level = Logger::ERROR
+ when :fatal
+ @logger.level = Logger::FATAL
+ else
+ raise ArgumentError, "Log level must be one of :debug, :info, :warn, :error, or :fatal"
+ end
+ end
+
+ # Passes any other method calls on directly to the underlying Logger object created with init. If
+ # this method gets hit before a call to Chef::Logger.init has been made, it will call
+ # Chef::Logger.init() with no arguments.
+ def method_missing(method_symbol, *args)
+ init() unless @logger
+ if args.length > 0
+ @logger.send(method_symbol, *args)
+ else
+ @logger.send(method_symbol)
+ end
+ end
+
+ end # class << self
+ end
+end \ No newline at end of file
diff --git a/lib/chef/log/formatter.rb b/lib/chef/log/formatter.rb
new file mode 100644
index 0000000000..d177060d97
--- /dev/null
+++ b/lib/chef/log/formatter.rb
@@ -0,0 +1,52 @@
+#
+# Chef::Log::Formatter
+#
+# A custom Logger::Formatter implementation for Chef.
+#
+# 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 'logger'
+require 'time'
+
+class Chef
+ class Log
+ class Formatter < Logger::Formatter
+ # Prints a log message as '[time] severity: message'
+ def call(severity, time, progname, msg)
+ sprintf("[%s] %s: %s\n", time.rfc2822(), severity, msg2str(msg))
+ end
+
+ # Converts some argument to a Logger.severity() call to a string. Regular strings pass through like
+ # normal, Exceptions get formatted as "message (class)\nbacktrace", and other random stuff gets
+ # put through "object.inspect"
+ def msg2str(msg)
+ case msg
+ when ::String
+ msg
+ when ::Exception
+ "#{ msg.message } (#{ msg.class })\n" <<
+ (msg.backtrace || []).join("\n")
+ else
+ msg.inspect
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/chef/node.rb b/lib/chef/node.rb
index d5896fbd95..7cb42effd0 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -1,7 +1,6 @@
#
# Chef::Node
#
-#
# Author:: Adam Jacob (<adam@hjksolutions.com>)
# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
# License:: GNU General Public License version 2 or later
@@ -24,6 +23,9 @@
require File.join(File.dirname(__FILE__), "mixin", "check_helper")
require File.join(File.dirname(__FILE__), "mixin", "from_file")
+require 'rubygems'
+require 'json'
+
class Chef
class Node
@@ -39,6 +41,42 @@ class Chef
@recipe_list = Array.new
end
+ # Find a Chef::Node by fqdn. Will search first for Chef::Config["node_path"]/fqdn.rb, then
+ # hostname.rb, then default.rb.
+ #
+ # Returns a new Chef::Node object.
+ #
+ # Raises an ArgumentError if it cannot find the node.
+ def self.find(fqdn)
+ node_file = nil
+ host_parts = fqdn.split(".")
+ hostname = host_parts[0]
+
+ if File.exists?(File.join(Chef::Config[:node_path], "#{fqdn}.rb"))
+ node_file = File.join(Chef::Config[:node_path], "#{fqdn}.rb")
+ elsif File.exists?(File.join(Chef::Config[:node_path], "#{hostname}.rb"))
+ node_file = File.join(Chef::Config[:node_path], "#{hostname}.rb")
+ elsif File.exists?(File.join(Chef::Config[:node_path], "default.rb"))
+ node_file = File.join(Chef::Config[:node_path], "default.rb")
+ else
+ raise ArgumentError, "Cannot find a node matching #{fqdn}, not even with default.rb!"
+ end
+ chef_node = Chef::Node.new()
+ chef_node.from_file(node_file)
+ chef_node
+ end
+
+ # Returns an array of nodes available, based on the list of files present.
+ def self.list
+ results = Array.new
+ Dir[File.join(Chef::Config[:node_path], "*.rb")].sort.each do |file|
+ mr = file.match(/^.+\/(.+)\.rb$/)
+ node_name = mr[1]
+ results << node_name
+ end
+ results
+ end
+
# Set the name of this Node, or return the current name.
def name(arg=nil)
set_if_args(@name, arg) do |a|
@@ -62,6 +100,11 @@ class Chef
end
end
+ # Set an attribute of this node
+ def []=(attrib, value)
+ @attribute[attrib] = value
+ end
+
# Iterates over each attribute, passing the attribute and value to the block.
def each_attribute(&block)
@attribute.each do |k,v|
@@ -108,5 +151,22 @@ class Chef
end
end
+ # Serialize this Node as json
+ def to_json()
+ result_object = {
+ "name" => @name,
+ "type" => "Chef::Node",
+ "attributes" => Hash.new,
+ "recipes" => Array.new
+ }
+ each_attribute do |a,v|
+ result_object["attributes"][a] = v
+ end
+ recipes.each do |r|
+ result_object["recipes"] << r
+ end
+ result_object.to_json
+ end
+
end
end \ No newline at end of file
diff --git a/lib/chef/recipe.rb b/lib/chef/recipe.rb
index af1cbd2d77..d7d1435eb4 100644
--- a/lib/chef/recipe.rb
+++ b/lib/chef/recipe.rb
@@ -81,6 +81,7 @@ class Chef
new_def.instance_eval(&block)
new_recipe = Chef::Recipe.new(@cookbook_name, @recipe_name, @node, @collection, @definitions, @cookbook_loader)
new_recipe.params = new_def.params
+ new_recipe.params[:name] = args[0]
new_recipe.instance_eval(&new_def.recipe)
else
method_name = method_symbol.to_s
diff --git a/lib/chef/resource_collection.rb b/lib/chef/resource_collection.rb
index dc93cfb4fa..5728fdd3e9 100644
--- a/lib/chef/resource_collection.rb
+++ b/lib/chef/resource_collection.rb
@@ -33,6 +33,7 @@ class Chef
def []=(index, arg)
is_chef_resource(arg)
+ raise ArgumentError, "Already have a resource named #{arg.to_s}" if @resources_by_name.has_key?(arg.to_s)
@resources[index] = arg
@resources_by_name[arg.to_s] = index
end
@@ -40,6 +41,7 @@ class Chef
def <<(*args)
args.flatten.each do |a|
is_chef_resource(a)
+ raise ArgumentError, "Already have a resource named #{a.to_s}" if @resources_by_name.has_key?(a.to_s)
@resources << a
@resources_by_name[a.to_s] = @resources.length - 1
end
@@ -48,6 +50,7 @@ class Chef
def push(*args)
args.flatten.each do |a|
is_chef_resource(a)
+ raise ArgumentError, "Already have a resource named #{a.to_s}" if @resources_by_name.has_key?(a.to_s)
@resources.push(a)
@resources_by_name[a.to_s] = @resources.length - 1
end