summaryrefslogtreecommitdiff
path: root/chef/lib
diff options
context:
space:
mode:
Diffstat (limited to 'chef/lib')
-rw-r--r--chef/lib/chef.rb18
-rw-r--r--chef/lib/chef/application.rb45
-rw-r--r--chef/lib/chef/application/client.rb19
-rw-r--r--chef/lib/chef/application/knife.rb8
-rw-r--r--chef/lib/chef/application/solo.rb2
-rw-r--r--chef/lib/chef/applications.rb4
-rw-r--r--chef/lib/chef/cache/checksum.rb13
-rw-r--r--chef/lib/chef/certificate.rb4
-rw-r--r--chef/lib/chef/client.rb32
-rw-r--r--chef/lib/chef/config.rb5
-rw-r--r--chef/lib/chef/cookbook_loader.rb7
-rw-r--r--chef/lib/chef/daemon.rb2
-rw-r--r--chef/lib/chef/data_bag_item.rb3
-rw-r--r--chef/lib/chef/exceptions.rb5
-rw-r--r--chef/lib/chef/file_cache.rb36
-rw-r--r--chef/lib/chef/index_queue/indexable.rb8
-rw-r--r--chef/lib/chef/knife.rb58
-rw-r--r--chef/lib/chef/knife/client_list.rb2
-rw-r--r--chef/lib/chef/knife/client_show.rb2
-rw-r--r--chef/lib/chef/knife/configure.rb83
-rw-r--r--chef/lib/chef/knife/cookbook_list.rb2
-rw-r--r--chef/lib/chef/knife/cookbook_metadata.rb49
-rw-r--r--chef/lib/chef/knife/cookbook_metadata_from_file.rb40
-rw-r--r--chef/lib/chef/knife/cookbook_show.rb4
-rw-r--r--chef/lib/chef/knife/cookbook_site_list.rb4
-rw-r--r--chef/lib/chef/knife/cookbook_site_search.rb4
-rw-r--r--chef/lib/chef/knife/cookbook_site_show.rb2
-rw-r--r--chef/lib/chef/knife/cookbook_test.rb103
-rw-r--r--chef/lib/chef/knife/cookbook_upload.rb67
-rw-r--r--chef/lib/chef/knife/data_bag_edit.rb2
-rw-r--r--chef/lib/chef/knife/data_bag_list.rb4
-rw-r--r--chef/lib/chef/knife/data_bag_show.rb2
-rw-r--r--chef/lib/chef/knife/ec2_instance_data.rb2
-rw-r--r--chef/lib/chef/knife/index_rebuild.rb4
-rw-r--r--chef/lib/chef/knife/node_from_file.rb2
-rw-r--r--chef/lib/chef/knife/node_list.rb4
-rw-r--r--chef/lib/chef/knife/node_run_list_add.rb2
-rw-r--r--chef/lib/chef/knife/node_run_list_remove.rb2
-rw-r--r--chef/lib/chef/knife/node_show.rb2
-rw-r--r--chef/lib/chef/knife/role_from_file.rb2
-rw-r--r--chef/lib/chef/knife/role_list.rb2
-rw-r--r--chef/lib/chef/knife/role_show.rb2
-rw-r--r--chef/lib/chef/knife/search.rb2
-rw-r--r--chef/lib/chef/knife/terremark_server_create.rb152
-rw-r--r--chef/lib/chef/knife/terremark_server_delete.rb87
-rw-r--r--chef/lib/chef/knife/terremark_server_list.rb77
-rw-r--r--chef/lib/chef/mixin/command.rb20
-rw-r--r--chef/lib/chef/mixin/recipe_definition_dsl_core.rb5
-rw-r--r--chef/lib/chef/mixin/template.rb11
-rw-r--r--chef/lib/chef/mixin/xml_escape.rb6
-rw-r--r--chef/lib/chef/mixins.rb16
-rw-r--r--chef/lib/chef/node.rb126
-rw-r--r--chef/lib/chef/openid_registration.rb1
-rw-r--r--chef/lib/chef/platform.rb308
-rw-r--r--chef/lib/chef/provider.rb4
-rw-r--r--chef/lib/chef/provider/cron.rb44
-rw-r--r--chef/lib/chef/provider/deploy/revision.rb6
-rw-r--r--chef/lib/chef/provider/erl_call.rb4
-rw-r--r--chef/lib/chef/provider/file.rb1
-rw-r--r--chef/lib/chef/provider/group/dscl.rb10
-rw-r--r--chef/lib/chef/provider/mdadm.rb4
-rw-r--r--chef/lib/chef/provider/mount/mount.rb28
-rw-r--r--chef/lib/chef/provider/package.rb2
-rw-r--r--chef/lib/chef/provider/package/freebsd.rb40
-rw-r--r--chef/lib/chef/provider/package/rubygems.rb17
-rw-r--r--chef/lib/chef/provider/remote_directory.rb15
-rw-r--r--chef/lib/chef/provider/remote_file.rb123
-rw-r--r--chef/lib/chef/provider/script.rb18
-rw-r--r--chef/lib/chef/provider/service/windows.rb129
-rw-r--r--chef/lib/chef/provider/subversion.rb2
-rw-r--r--chef/lib/chef/provider/template.rb101
-rw-r--r--chef/lib/chef/providers.rb80
-rw-r--r--chef/lib/chef/recipe.rb6
-rw-r--r--chef/lib/chef/resource.rb28
-rw-r--r--chef/lib/chef/resource/cron.rb19
-rw-r--r--chef/lib/chef/resource/deploy.rb97
-rw-r--r--chef/lib/chef/resource/execute.rb2
-rw-r--r--chef/lib/chef/resource/mount.rb2
-rw-r--r--chef/lib/chef/resource/remote_directory.rb29
-rw-r--r--chef/lib/chef/resource/scm.rb39
-rw-r--r--chef/lib/chef/resource/service.rb11
-rw-r--r--chef/lib/chef/resources.rb60
-rw-r--r--chef/lib/chef/rest.rb423
-rw-r--r--chef/lib/chef/rest/auth_credentials.rb78
-rw-r--r--chef/lib/chef/rest/cookie_jar.rb (renamed from chef/lib/chef/application/server.rb)24
-rw-r--r--chef/lib/chef/rest/rest_request.rb151
-rw-r--r--chef/lib/chef/role.rb84
-rw-r--r--chef/lib/chef/streaming_cookbook_uploader.rb10
-rw-r--r--chef/lib/chef/tasks/chef_repo.rake18
-rw-r--r--chef/lib/chef/util/file_edit.rb1
-rw-r--r--chef/lib/chef/webui_user.rb1
91 files changed, 2232 insertions, 953 deletions
diff --git a/chef/lib/chef.rb b/chef/lib/chef.rb
index 0c494ef594..937e21acc9 100644
--- a/chef/lib/chef.rb
+++ b/chef/lib/chef.rb
@@ -16,22 +16,25 @@
# limitations under the License.
#
-$:.unshift(File.dirname(__FILE__)) unless
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
-
-require 'rubygems'
require 'extlib'
require 'chef/exceptions'
require 'chef/log'
require 'chef/config'
-Dir[File.join(File.dirname(__FILE__), 'chef/mixin/**/*.rb')].sort.each { |lib| require lib }
+require 'chef/providers'
+require 'chef/resources'
+
+require 'chef/compile'
+require 'chef/daemon'
+require 'chef/runner'
+require 'chef/webui_user'
+require 'chef/openid_registration'
class Chef
- VERSION = "0.8.11"
+ VERSION = "0.8.12"
end
# Adds a Dir.glob to Ruby 1.8.5, for compat
-if RUBY_VERSION < "1.8.6"
+if RUBY_VERSION < "1.8.6" || RUBY_PLATFORM =~ /mswin|mingw32|windows/
class Dir
class << self
alias_method :glob_, :glob
@@ -41,6 +44,7 @@ if RUBY_VERSION < "1.8.6"
pattern.is_a? Array and !pattern.empty?
) or pattern.is_a? String
)
+ pattern.gsub!(/\\/, "/") if RUBY_PLATFORM =~ /mswin|mingw32|windows/
[pattern].flatten.inject([]) { |r, p| r + glob_(p, flags) }
end
alias_method :[], :glob
diff --git a/chef/lib/chef/application.rb b/chef/lib/chef/application.rb
index b992bc1c59..96825a8c01 100644
--- a/chef/lib/chef/application.rb
+++ b/chef/lib/chef/application.rb
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,10 +22,10 @@ require 'mixlib/cli'
class Chef::Application
include Mixlib::CLI
-
- def initialize
+
+ def initialize
super
-
+
trap("TERM") do
Chef::Application.fatal!("SIGTERM received, stopping", 1)
end
@@ -33,23 +33,28 @@ class Chef::Application
trap("INT") do
Chef::Application.fatal!("SIGINT received, stopping", 2)
end
-
- trap("HUP") do
- Chef::Log.info("SIGHUP received, reconfiguring")
- reconfigure
+
+ unless RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ trap("HUP") do
+ Chef::Log.info("SIGHUP received, reconfiguring")
+ reconfigure
+ end
end
-
+
at_exit do
- # tear down the logger
- end
+ # tear down the logger
+ end
+
+ # Always switch to a readable directory. Keeps subsequent Dir.chdir() {}
+ # from failing due to permissions when launched as a less privileged user.
end
-
+
# Reconfigure the application. You'll want to override and super this method.
def reconfigure
configure_chef
configure_logging
end
-
+
# Get this party started
def run
reconfigure
@@ -60,25 +65,25 @@ class Chef::Application
# Parse the configuration file
def configure_chef
parse_options
-
+
Chef::Config.from_file(config[:config_file]) if !config[:config_file].nil? && File.exists?(config[:config_file]) && File.readable?(config[:config_file])
Chef::Config.merge!(config)
end
-
+
# Initialize and configure the logger
def configure_logging
Chef::Log.init(Chef::Config[:log_location])
Chef::Log.level = Chef::Config[:log_level]
end
-
+
# Called prior to starting the application, by the run method
def setup_application
- raise Chef::Exceptions::Application, "#{self.to_s}: you must override setup_application"
+ raise Chef::Exceptions::Application, "#{self.to_s}: you must override setup_application"
end
-
+
# Actually run the application
def run_application
- raise Chef::Exceptions::Application, "#{self.to_s}: you must override run_application"
+ raise Chef::Exceptions::Application, "#{self.to_s}: you must override run_application"
end
class << self
diff --git a/chef/lib/chef/application/client.rb b/chef/lib/chef/application/client.rb
index a6bd7a1003..6c265b84f6 100644
--- a/chef/lib/chef/application/client.rb
+++ b/chef/lib/chef/application/client.rb
@@ -113,12 +113,18 @@ class Chef::Application::Client < Chef::Application
:description => "The chef server URL",
:proc => nil
- option :validation_token,
- :short => "-t TOKEN",
- :long => "--token TOKEN",
- :description => "Set the openid validation token",
- :proc => nil
-
+ option :validation_key,
+ :short => "-K KEY_FILE",
+ :long => "--validation_key KEY_FILE",
+ :description => "Set the validation key file location, used for registering new clients",
+ :proc => nil
+
+ option :client_key,
+ :short => "-k KEY_FILE",
+ :long => "--client_key KEY_FILE",
+ :description => "Set the client key file location",
+ :proc => nil
+
option :version,
:short => "-v",
:long => "--version",
@@ -185,7 +191,6 @@ class Chef::Application::Client < Chef::Application
@chef_client = Chef::Client.new
@chef_client.json_attribs = @chef_client_json
- @chef_client.validation_token = Chef::Config[:validation_token]
@chef_client.node_name = Chef::Config[:node_name]
end
diff --git a/chef/lib/chef/application/knife.rb b/chef/lib/chef/application/knife.rb
index df60da4040..c5ef8f4317 100644
--- a/chef/lib/chef/application/knife.rb
+++ b/chef/lib/chef/application/knife.rb
@@ -94,7 +94,13 @@ class Chef::Application::Knife < Chef::Application
:short => "-p",
:long => "--print-after",
:description => "Show the data after a destructive operation"
-
+
+ option :format,
+ :short => "-f FORMAT",
+ :long => "--format FORMAT",
+ :description => "Which format to use for output",
+ :default => "json"
+
option :version,
:short => "-v",
:long => "--version",
diff --git a/chef/lib/chef/application/solo.rb b/chef/lib/chef/application/solo.rb
index 434638d607..8fa2b00592 100644
--- a/chef/lib/chef/application/solo.rb
+++ b/chef/lib/chef/application/solo.rb
@@ -158,7 +158,7 @@ class Chef::Application::Solo < Chef::Application
end
if Chef::Config[:recipe_url]
- cookbooks_path = Chef::Config[:cookbook_path].detect{|e| e =~ /\/cookbooks\/*$/ }
+ cookbooks_path = Array(Chef::Config[:cookbook_path]).detect{|e| e =~ /\/cookbooks\/*$/ }
recipes_path = File.expand_path(File.join(cookbooks_path, '..'))
target_file = File.join(recipes_path, 'recipes.tgz')
diff --git a/chef/lib/chef/applications.rb b/chef/lib/chef/applications.rb
new file mode 100644
index 0000000000..48fd56acf4
--- /dev/null
+++ b/chef/lib/chef/applications.rb
@@ -0,0 +1,4 @@
+require 'chef/application/agent'
+require 'chef/application/client'
+require 'chef/application/knife'
+require 'chef/application/solo'
diff --git a/chef/lib/chef/cache/checksum.rb b/chef/lib/chef/cache/checksum.rb
index 6effb3a303..e026d8875b 100644
--- a/chef/lib/chef/cache/checksum.rb
+++ b/chef/lib/chef/cache/checksum.rb
@@ -28,8 +28,9 @@ class Chef
instance.checksum_for_file(*args)
end
- def checksum_for_file(file)
- key, fstat = filename_to_key(file), File.stat(file)
+ def checksum_for_file(file, key=nil)
+ key ||= generate_key(file)
+ fstat = File.stat(file)
lookup_checksum(key, fstat) || generate_checksum(key, file, fstat)
end
@@ -48,6 +49,10 @@ class Chef
checksum
end
+ def generate_key(file, group="chef")
+ "#{group}-file-#{file.gsub(/(#{File::SEPARATOR}|\.)/, '-')}"
+ end
+
private
def file_unchanged?(cached, fstat)
@@ -59,10 +64,6 @@ class Chef
IO.foreach(file) {|line| digest.update(line) }
digest.hexdigest
end
-
- def filename_to_key(file)
- "chef-file-#{file.gsub(/(#{File::SEPARATOR}|\.)/, '-')}"
- end
end
end
diff --git a/chef/lib/chef/certificate.rb b/chef/lib/chef/certificate.rb
index 9e57c9ee89..b818b967af 100644
--- a/chef/lib/chef/certificate.rb
+++ b/chef/lib/chef/certificate.rb
@@ -128,11 +128,11 @@ class Chef
return client_cert.public_key, client_keypair
end
- def gen_validation_key(name=Chef::Config[:validation_client_name], key_file=Chef::Config[:validation_key])
+ def gen_validation_key(name=Chef::Config[:validation_client_name], key_file=Chef::Config[:validation_key], admin=false)
# Create the validation key
api_client = Chef::ApiClient.new
api_client.name(name)
- api_client.admin(true)
+ api_client.admin(admin)
begin
# If both the couch record and file exist, don't do anything. Otherwise,
diff --git a/chef/lib/chef/client.rb b/chef/lib/chef/client.rb
index 0ffe60e53a..08e2c76db0 100644
--- a/chef/lib/chef/client.rb
+++ b/chef/lib/chef/client.rb
@@ -37,12 +37,11 @@ class Chef
include Chef::Mixin::GenerateURL
include Chef::Mixin::Checksum
- attr_accessor :node, :registration, :json_attribs, :validation_token, :node_name, :ohai, :rest, :runner, :compile
+ attr_accessor :node, :registration, :json_attribs, :node_name, :ohai, :rest, :compile
# Creates a new Chef::Client.
def initialize()
@node = nil
- @validation_token = nil
@registration = nil
@json_attribs = nil
@node_name = nil
@@ -90,7 +89,6 @@ class Chef
build_node(@node_name)
save_node
sync_cookbooks
- save_node
converge
save_node
@@ -317,7 +315,7 @@ class Chef
Chef::FileCache.list.each do |cache_file|
if cache_file =~ /^cookbooks\/(.+?)\//
unless cookbook_hash.has_key?($1)
- Chef::Log.info("Removing #{cache_file} from the cache; it's cookbook is no longer needed on this client.")
+ Chef::Log.info("Removing #{cache_file} from the cache; its cookbook is no longer needed on this client.")
Chef::FileCache.delete(cache_file)
end
end
@@ -355,6 +353,22 @@ class Chef
Chef::Log.debug("Compiling recipes for node #{@node_name}")
unless solo
Chef::Config[:cookbook_path] = File.join(Chef::Config[:file_cache_path], "cookbooks")
+ Chef::Log.warn("Node #{@node_name} has an empty run list.") if @node.run_list.empty?
+ else
+ # Check for cookbooks in the path given
+ # Chef::Config[:cookbook_path] can be a string or an array
+ # if it's an array, go through it and check each one, raise error at the last one if no files are found
+ Chef::Log.fatal "BUGBUG: cookbook_path: #{Chef::Config[:cookbook_path]}"
+ Array(Chef::Config[:cookbook_path]).each_with_index do |cookbook_path, index|
+ if directory_not_empty?(cookbook_path)
+ Chef::Log.fatal "BUGBUG: cb path not empty: #{cookbook_path}"
+ break
+ else
+ msg = "No cookbook found in #{Chef::Config[:cookbook_path].inspect}, make sure cookboook_path is set correctly."
+ Chef::Log.fatal(msg)
+ raise Chef::Exceptions::CookbookNotFound, msg if is_last_element?(index, Chef::Config[:cookbook_path])
+ end
+ end
end
@compile = Chef::Compile.new(@node)
@compile.go
@@ -364,6 +378,16 @@ class Chef
@runner.converge
true
end
+
+ private
+
+ def directory_not_empty?(path)
+ File.exists?(path) && (Dir.entries(path).size > 2)
+ end
+
+ def is_last_element?(index, object)
+ object.kind_of?(Array) ? index == object.size - 1 : true
+ end
end
end
diff --git a/chef/lib/chef/config.rb b/chef/lib/chef/config.rb
index 3d90456486..6e573e7f3f 100644
--- a/chef/lib/chef/config.rb
+++ b/chef/lib/chef/config.rb
@@ -143,15 +143,14 @@ class Chef
search_url "http://localhost:4000"
solo false
splay nil
- ssl_client_cert ""
- ssl_client_key ""
+ ssl_client_cert nil
+ ssl_client_key nil
ssl_verify_mode :verify_none
ssl_ca_path nil
ssl_ca_file nil
template_url "http://localhost:4000"
umask 0022
user nil
- validation_token nil
role_path "/var/chef/roles"
role_url "http://localhost:4000"
recipe_url nil
diff --git a/chef/lib/chef/cookbook_loader.rb b/chef/lib/chef/cookbook_loader.rb
index 4a1e053425..c8591501c6 100644
--- a/chef/lib/chef/cookbook_loader.rb
+++ b/chef/lib/chef/cookbook_loader.rb
@@ -114,7 +114,12 @@ class Chef
@cookbook[cookbook].provider_files = cookbook_settings[cookbook][:provider_files].values
@metadata[cookbook] = Chef::Cookbook::Metadata.new(@cookbook[cookbook])
cookbook_settings[cookbook][:metadata_files].each do |meta_json|
- @metadata[cookbook].from_json(IO.read(meta_json))
+ begin
+ @metadata[cookbook].from_json(IO.read(meta_json))
+ rescue JSON::ParserError
+ puts "Couldn't parse JSON in " + meta_json
+ raise
+ end
end
end
end
diff --git a/chef/lib/chef/daemon.rb b/chef/lib/chef/daemon.rb
index eecdeb9607..1b9db3ba2b 100644
--- a/chef/lib/chef/daemon.rb
+++ b/chef/lib/chef/daemon.rb
@@ -121,6 +121,8 @@ class Chef
# Change process user/group to those specified in Chef::Config
#
def change_privilege
+ Dir.chdir("/")
+
if Chef::Config[:user] and Chef::Config[:group]
Chef::Log.info("About to change privilege to #{Chef::Config[:user]}:#{Chef::Config[:group]}")
_change_privilege(Chef::Config[:user], Chef::Config[:group])
diff --git a/chef/lib/chef/data_bag_item.rb b/chef/lib/chef/data_bag_item.rb
index a18649c989..0d6b1e9696 100644
--- a/chef/lib/chef/data_bag_item.rb
+++ b/chef/lib/chef/data_bag_item.rb
@@ -22,7 +22,8 @@ require 'chef/config'
require 'chef/mixin/params_validate'
require 'chef/mixin/from_file'
require 'chef/couchdb'
-require 'chef/data_bag_item'
+require 'chef/index_queue'
+require 'chef/data_bag'
require 'extlib'
require 'json'
diff --git a/chef/lib/chef/exceptions.rb b/chef/lib/chef/exceptions.rb
index f0fb60f778..d14401d3ad 100644
--- a/chef/lib/chef/exceptions.rb
+++ b/chef/lib/chef/exceptions.rb
@@ -37,5 +37,10 @@ class Chef
class CannotWritePrivateKey < RuntimeError; end
class RoleNotFound < RuntimeError; end
class ValidationFailed < ArgumentError; end
+ class InvalidPrivateKey < ArgumentError; end
+ class ConfigurationError < ArgumentError; end
+ class RedirectLimitExceeded < RuntimeError; end
+ class AmbiguousRunlistSpecification < ArgumentError; end
+ class CookbookNotFound < RuntimeError; end
end
end
diff --git a/chef/lib/chef/file_cache.rb b/chef/lib/chef/file_cache.rb
index 4e2446844a..18ecd66512 100644
--- a/chef/lib/chef/file_cache.rb
+++ b/chef/lib/chef/file_cache.rb
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -26,7 +26,7 @@ class Chef
class << self
include Chef::Mixin::ParamsValidate
include Chef::Mixin::CreatePath
-
+
# Write a file to the File Cache.
#
# === Parameters
@@ -47,7 +47,7 @@ class Chef
:contents => { :kind_of => String },
}
)
-
+
file_path_array = File.split(path)
file_name = file_path_array.pop
cache_path = create_cache_path(File.join(file_path_array))
@@ -56,7 +56,7 @@ class Chef
io.close
true
end
-
+
# Move a file in to the cache. Useful with the REST raw file output.
#
# === Parameteres
@@ -73,19 +73,19 @@ class Chef
:path => { :kind_of => String },
}
)
-
+
file_path_array = File.split(path)
file_name = file_path_array.pop
if File.exists?(file) && File.writable?(file)
FileUtils.mv(
- file,
+ file,
File.join(create_cache_path(File.join(file_path_array), true), file_name)
)
else
raise RuntimeError, "Cannot move #{file} to #{path}!"
end
end
-
+
# Read a file from the File Cache
#
# === Parameters
@@ -116,7 +116,7 @@ class Chef
cache_path
end
end
-
+
# Delete a file from the File Cache
#
# === Parameters
@@ -140,7 +140,7 @@ class Chef
end
true
end
-
+
# List all the files in the Cache
#
# === Returns
@@ -149,19 +149,19 @@ class Chef
keys = Array.new
Dir[File.join(Chef::Config[:file_cache_path], '**', '*')].each do |f|
if File.file?(f)
- path = f.match("^#{Chef::Config[:file_cache_path]}\/(.+)")[1]
+ path = f.match("^#{Dir[Chef::Config[:file_cache_path]].first}\/(.+)")[1]
keys << path
end
end
keys
end
-
+
# Whether or not this file exists in the Cache
#
# === Parameters
- # path:: The path to the file you want to check - is relative
+ # path:: The path to the file you want to check - is relative
# to Chef::Config[:file_cache_path]
- #
+ #
# === Returns
# True:: If the file exists
# False:: If it does not
@@ -181,25 +181,25 @@ class Chef
false
end
end
-
+
# Create a full path to a given file in the cache. By default,
# also creates the path if it does not exist.
#
# === Parameters
# path:: The path to create, relative to Chef::Config[:file_cache_path]
# create_if_missing:: True by default - whether to create the path if it does not exist
- #
+ #
# === Returns
# String:: The fully expanded path
def create_cache_path(path, create_if_missing=true)
cache_dir = File.expand_path(File.join(Chef::Config[:file_cache_path], path))
if create_if_missing
- create_path(cache_dir)
+ create_path(cache_dir)
else
cache_dir
end
end
-
+
end
end
end
diff --git a/chef/lib/chef/index_queue/indexable.rb b/chef/lib/chef/index_queue/indexable.rb
index 0654c6b06c..f027497f13 100644
--- a/chef/lib/chef/index_queue/indexable.rb
+++ b/chef/lib/chef/index_queue/indexable.rb
@@ -44,10 +44,12 @@ class Chef
self.class.index_object_type || Mixin::ConvertToClassName.snake_case_basename(self.class.name)
end
- def with_indexer_metadata(with_metadata={})
+ def with_indexer_metadata(indexer_metadata={})
# changing input param symbol keys to strings, as the keys in hash that goes to solr are expected to be strings [cb]
- with_metadata.each do |key,value|
- with_metadata[key.to_s] = with_metadata.delete(key)
+ # Ruby 1.9 hates you, cb [dan]
+ with_metadata = {}
+ indexer_metadata.each_key do |key|
+ with_metadata[key.to_s] = indexer_metadata[key]
end
with_metadata["type"] ||= self.index_object_type
diff --git a/chef/lib/chef/knife.rb b/chef/lib/chef/knife.rb
index 8aadb0bcd9..beaa7f8326 100644
--- a/chef/lib/chef/knife.rb
+++ b/chef/lib/chef/knife.rb
@@ -20,6 +20,8 @@
require 'mixlib/cli'
require 'chef/mixin/convert_to_class_name'
+require 'pp'
+
class Chef
class Knife
include Mixlib::CLI
@@ -96,11 +98,17 @@ class Chef
klass_instance
end
- def ask_question(q)
- print q
- a = STDIN.readline
- a.chomp!
- a
+ def ask_question(question, opts={})
+ question = question + "[#{opts[:default]}] " if opts[:default]
+
+ stdout.print question
+ a = stdin.readline.strip
+
+ if opts[:default]
+ a.empty? ? opts[:default] : a
+ else
+ a
+ end
end
def configure_chef
@@ -126,8 +134,24 @@ class Chef
puts data
end
- def json_pretty_print(data)
- puts JSON.pretty_generate(data)
+ def output(data)
+ case config[:format]
+ when "json", nil
+ puts JSON.pretty_generate(data)
+ when "yaml"
+ require 'yaml'
+ puts YAML::dump(data)
+ when "text"
+ # If you were looking for some attribute and there is only one match
+ # just dump the attribute value
+ if data.length == 1 and config[:attribute]
+ puts data.values[0]
+ else
+ pp data
+ end
+ else
+ raise ArgumentError, "Unknown output format #{config[:format]}"
+ end
end
def format_list_for_display(list)
@@ -182,7 +206,7 @@ class Chef
return true if config[:yes]
print "#{question}? (Y/N) "
- answer = STDIN.readline
+ answer = stdin.readline
answer.chomp!
case answer
when "Y", "y"
@@ -237,7 +261,7 @@ class Chef
Chef::Log.info("Saved #{output}")
- json_pretty_print(format_for_display(object)) if config[:print_after]
+ output(format_for_display(object)) if config[:print_after]
end
def create_object(object, pretty_name=nil, &block)
@@ -253,7 +277,7 @@ class Chef
Chef::Log.info("Created (or updated) #{pretty_name}")
- json_pretty_print(output) if config[:print_after]
+ output(output) if config[:print_after]
end
def delete_object(klass, name, delete_name=nil, &block)
@@ -266,7 +290,7 @@ class Chef
object.destroy
end
- json_pretty_print(format_for_display(object)) if config[:print_after]
+ output(format_for_display(object)) if config[:print_after]
obj_name = delete_name ? "#{delete_name}[#{name}]" : object
Chef::Log.warn("Deleted #{obj_name}!")
@@ -285,7 +309,7 @@ class Chef
to_delete = object_list
end
- json_pretty_print(format_list_for_display(to_delete))
+ output(format_list_for_display(to_delete))
confirm("Do you really want to delete the above items")
@@ -295,10 +319,18 @@ class Chef
else
object.destroy
end
- json_pretty_print(format_for_display(object)) if config[:print_after]
+ output(format_for_display(object)) if config[:print_after]
Chef::Log.warn("Deleted #{fancy_name} #{name}")
end
end
+
+ def stdout
+ STDOUT
+ end
+
+ def stdin
+ STDIN
+ end
def rest
@rest ||= Chef::REST.new(Chef::Config[:chef_server_url])
diff --git a/chef/lib/chef/knife/client_list.rb b/chef/lib/chef/knife/client_list.rb
index 24c23124ca..c6f3ca5136 100644
--- a/chef/lib/chef/knife/client_list.rb
+++ b/chef/lib/chef/knife/client_list.rb
@@ -32,7 +32,7 @@ class Chef
:description => "Show corresponding URIs"
def run
- json_pretty_print(format_list_for_display(Chef::ApiClient.list))
+ output(format_list_for_display(Chef::ApiClient.list))
end
end
end
diff --git a/chef/lib/chef/knife/client_show.rb b/chef/lib/chef/knife/client_show.rb
index f0efa97f10..81204547b3 100644
--- a/chef/lib/chef/knife/client_show.rb
+++ b/chef/lib/chef/knife/client_show.rb
@@ -33,7 +33,7 @@ class Chef
def run
client = Chef::ApiClient.load(@name_args[0])
- json_pretty_print(format_for_display(client))
+ output(format_for_display(client))
end
end
diff --git a/chef/lib/chef/knife/configure.rb b/chef/lib/chef/knife/configure.rb
index a362ed4465..58acbeb832 100644
--- a/chef/lib/chef/knife/configure.rb
+++ b/chef/lib/chef/knife/configure.rb
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -21,6 +21,8 @@ require 'chef/knife'
class Chef
class Knife
class Configure < Knife
+ attr_reader :chef_server, :new_client_name, :admin_client_name, :admin_client_key
+ attr_reader :chef_repo, :new_client_key, :validation_client_name, :validation_key
banner "Sub-Command: configure (options)"
@@ -37,58 +39,48 @@ class Chef
def configure_chef
# We are just faking out the system so that you can do this without a key specified
- Chef::Config[:node_name] = 'woot'
+ Chef::Config[:node_name] = 'woot'
super
Chef::Config[:node_name] = nil
end
- def run
- config[:config_file] ||= ask_question("Where should I put the config file? ")
- if File.exists?(config[:config_file])
- confirm("Overwrite #{config[:config_file]}")
- end
+ def run
+ ask_user_for_config_path
Mixlib::Log::Formatter.show_time = false
Chef::Log.init(STDOUT)
Chef::Log.level(:info)
- chef_config_path = File.dirname(config[:config_file])
- FileUtils.mkdir_p(File.dirname(config[:config_file]))
+ FileUtils.mkdir_p(chef_config_path)
- chef_server = config[:chef_server_url] || ask_question("Your chef server URL? ")
- opscode_user = config[:node_name] || ask_question("Your client user name? ")
- opscode_key = config[:client_key] || File.join(chef_config_path, "#{opscode_user}.pem")
- validation_user = config[:validation_client_name] || ask_question("Your validation client user name? ")
- validation_key = config[:validation_key] || File.join(chef_config_path, "#{validation_user}.pem")
- chef_repo = config[:repository] || ask_question("Path to a chef repository (or leave blank)? ")
-
+ ask_user_for_config
- File.open(config[:config_file], "w") do |f|
- f.puts <<EOH
+ ::File.open(config[:config_file], "w") do |f|
+ f.puts <<-EOH
log_level :info
log_location STDOUT
-node_name '#{opscode_user}'
-client_key '#{opscode_key}'
-validation_client_name '#{validation_user}'
+node_name '#{new_client_name}'
+client_key '#{new_client_key}'
+validation_client_name '#{validation_client_name}'
validation_key '#{validation_key}'
-chef_server_url '#{chef_server}'
+chef_server_url '#{chef_server}'
cache_type 'BasicFile'
cache_options( :path => '#{File.join(chef_config_path, "checksums")}' )
EOH
- unless chef_repo == ""
+ unless chef_repo.empty?
f.puts "cookbook_path [ '#{chef_repo}/cookbooks', '#{chef_repo}/site-cookbooks' ]"
- end
+ end
end
if config[:initial]
Chef::Log.warn("Creating initial API user...")
Chef::Config[:chef_server_url] = chef_server
- Chef::Config[:node_name] = 'chef-webui'
- Chef::Config[:client_key] = '/etc/chef/webui.pem'
+ Chef::Config[:node_name] = admin_client_name
+ Chef::Config[:client_key] = admin_client_key
client_create = Chef::Knife::ClientCreate.new
- client_create.name_args = [ opscode_user ]
+ client_create.name_args = [ new_client_name ]
client_create.config[:admin] = true
- client_create.config[:file] = opscode_key
+ client_create.config[:file] = new_client_key
client_create.config[:yes] = true
client_create.config[:no_editor] = true
client_create.run
@@ -96,7 +88,7 @@ EOH
Chef::Log.warn("*****")
Chef::Log.warn("")
Chef::Log.warn("You must place your client key in:")
- Chef::Log.warn(" #{opscode_key}")
+ Chef::Log.warn(" #{new_client_key}")
Chef::Log.warn("Before running commands with Knife!")
Chef::Log.warn("")
Chef::Log.warn("*****")
@@ -111,13 +103,32 @@ EOH
Chef::Log.warn("Configuration file written to #{config[:config_file]}")
end
- end
- end
-end
-
-
-
+ def ask_user_for_config_path
+ config[:config_file] ||= ask_question("Where should I put the config file? ")
+ if File.exists?(config[:config_file])
+ confirm("Overwrite #{config[:config_file]}")
+ end
+ end
+ def ask_user_for_config
+ @chef_server = config[:chef_server_url] || ask_question("Your chef server URL? ", :default => 'http://localhost:4000')
+ @new_client_name = config[:node_name] || ask_question("Select a user name for your new client: ", :default => Etc.getlogin)
+ @admin_client_name = config[:admin_client_name] || ask_question("Your existing admin client user name? ", :default => 'chef-webui')
+ @admin_client_key = config[:admin_client_key] || ask_question("The location of your existing admin key? ", :default => '/etc/chef/webui.pem')
+ @validation_client_name = config[:validation_client_name] || ask_question("Your validation client user name? ", :default => 'chef-validator')
+ @validation_key = config[:validation_key] || ask_question("The location of your validation key? ", :default => '/etc/chef/validation.pem')
+ @chef_repo = config[:repository] || ask_question("Path to a chef repository (or leave blank)? ")
+ @new_client_key = config[:client_key] || File.join(chef_config_path, "#{@new_client_name}.pem")
+ end
+ def config_file
+ config[:config_file]
+ end
+ def chef_config_path
+ File.dirname(config_file)
+ end
+ end
+ end
+end
diff --git a/chef/lib/chef/knife/cookbook_list.rb b/chef/lib/chef/knife/cookbook_list.rb
index 1b19bfb7db..7e1599dd62 100644
--- a/chef/lib/chef/knife/cookbook_list.rb
+++ b/chef/lib/chef/knife/cookbook_list.rb
@@ -31,7 +31,7 @@ class Chef
:description => "Show corresponding URIs"
def run
- json_pretty_print(format_list_for_display(rest.get_rest('cookbooks')))
+ output(format_list_for_display(rest.get_rest('cookbooks')))
end
end
end
diff --git a/chef/lib/chef/knife/cookbook_metadata.rb b/chef/lib/chef/knife/cookbook_metadata.rb
index b821b5d94b..d8a52693d5 100644
--- a/chef/lib/chef/knife/cookbook_metadata.rb
+++ b/chef/lib/chef/knife/cookbook_metadata.rb
@@ -7,9 +7,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -36,20 +36,20 @@ class Chef
:long => "--all",
:description => "Generate metadata for all cookbooks, rather than just a single cookbook"
- def run
+ def run
if config[:cookbook_path]
Chef::Config[:cookbook_path] = config[:cookbook_path]
else
config[:cookbook_path] = Chef::Config[:cookbook_path]
end
- if config[:all]
+ if config[:all]
cl = Chef::CookbookLoader.new
cl.each do |cookbook|
generate_metadata(cookbook.name.to_s)
end
else
- generate_metadata(@name_args[0])
+ generate_metadata(@name_args[0])
end
end
@@ -57,31 +57,26 @@ class Chef
Chef::Log.info("Generating metadata for #{cookbook}")
config[:cookbook_path].reverse.each do |path|
file = File.expand_path(File.join(path, cookbook, 'metadata.rb'))
- if File.exists?(file)
- Chef::Log.info("Generating from #{file}")
- md = Chef::Cookbook::Metadata.new
- md.name(cookbook)
- md.from_file(file)
- json_file = File.join(File.dirname(file), 'metadata.json')
- File.open(json_file, "w") do |f|
- f.write(JSON.pretty_generate(md))
- end
- generated = true
- Chef::Log.info("Generated #{json_file}")
- else
- Chef::Log.debug("No #{file} found; skipping!")
- end
+ generate_metadata_from_file(cookbook, file)
end
end
+ def generate_metadata_from_file(cookbook, file)
+ if File.exists?(file)
+ Chef::Log.info("Generating from #{file}")
+ md = Chef::Cookbook::Metadata.new
+ md.name(cookbook)
+ md.from_file(file)
+ json_file = File.join(File.dirname(file), 'metadata.json')
+ File.open(json_file, "w") do |f|
+ f.write(JSON.pretty_generate(md))
+ end
+ generated = true
+ Chef::Log.info("Generated #{json_file}")
+ else
+ Chef::Log.debug("No #{file} found; skipping!")
+ end
+ end
end
end
end
-
-
-
-
-
-
-
-
diff --git a/chef/lib/chef/knife/cookbook_metadata_from_file.rb b/chef/lib/chef/knife/cookbook_metadata_from_file.rb
new file mode 100644
index 0000000000..8da5773e61
--- /dev/null
+++ b/chef/lib/chef/knife/cookbook_metadata_from_file.rb
@@ -0,0 +1,40 @@
+#
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Matthew Kent (<mkent@magoazul.com>)
+# Copyright:: Copyright (c) 2009 Opscode, Inc.
+# Copyright:: Copyright (c) 2010 Matthew Kent
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/knife'
+
+class Chef
+ class Knife
+ class CookbookMetadataFromFile < Knife
+
+ banner "Sub-Command: cookbook metadata from FILE (options)"
+
+ def run
+ file = @name_args[0]
+ cookbook = File.basename(File.dirname(file))
+
+ @metadata = Chef::Knife::CookbookMetadata.new
+ @metadata.generate_metadata_from_file(cookbook, file)
+ end
+
+ end
+ end
+end
diff --git a/chef/lib/chef/knife/cookbook_show.rb b/chef/lib/chef/knife/cookbook_show.rb
index eaf23e65d3..ac255e8b13 100644
--- a/chef/lib/chef/knife/cookbook_show.rb
+++ b/chef/lib/chef/knife/cookbook_show.rb
@@ -52,9 +52,9 @@ class Chef
pretty_print(result)
when 2 # We are showing a specific part of the cookbook
result = rest.get_rest("cookbooks/#{@name_args[0]}")
- json_pretty_print(result[@name_args[1]])
+ output(result[@name_args[1]])
when 1 # We are showing the whole cookbook data
- json_pretty_print(rest.get_rest("cookbooks/#{@name_args[0]}"))
+ output(rest.get_rest("cookbooks/#{@name_args[0]}"))
end
end
diff --git a/chef/lib/chef/knife/cookbook_site_list.rb b/chef/lib/chef/knife/cookbook_site_list.rb
index 505c321f23..19d1eccbb4 100644
--- a/chef/lib/chef/knife/cookbook_site_list.rb
+++ b/chef/lib/chef/knife/cookbook_site_list.rb
@@ -29,8 +29,8 @@ class Chef
:long => "--with-uri",
:description => "Show corresponding URIs"
- def run
- json_pretty_print(format_list_for_display(get_cookbook_list))
+ def run
+ output(format_list_for_display(get_cookbook_list))
end
def get_cookbook_list(items=10, start=0, cookbook_collection={})
diff --git a/chef/lib/chef/knife/cookbook_site_search.rb b/chef/lib/chef/knife/cookbook_site_search.rb
index 1853b94209..4c4b5cc100 100644
--- a/chef/lib/chef/knife/cookbook_site_search.rb
+++ b/chef/lib/chef/knife/cookbook_site_search.rb
@@ -23,8 +23,8 @@ class Chef
banner "Sub-Command: cookbook site search QUERY (options)"
- def run
- json_pretty_print(search_cookbook(name_args[0]))
+ def run
+ output(search_cookbook(name_args[0]))
end
def search_cookbook(query, items=10, start=0, cookbook_collection={})
diff --git a/chef/lib/chef/knife/cookbook_site_show.rb b/chef/lib/chef/knife/cookbook_site_show.rb
index 2f0bcff897..128ac7bf4c 100644
--- a/chef/lib/chef/knife/cookbook_site_show.rb
+++ b/chef/lib/chef/knife/cookbook_site_show.rb
@@ -30,7 +30,7 @@ class Chef
when 2
cookbook_data = rest.get_rest("http://cookbooks.opscode.com/api/v1/cookbooks/#{@name_args[0]}/versions/#{name_args[1].gsub('.', '_')}")
end
- json_pretty_print(format_for_display(cookbook_data))
+ output(format_for_display(cookbook_data))
end
def get_cookbook_list(items=10, start=0, cookbook_collection={})
diff --git a/chef/lib/chef/knife/cookbook_test.rb b/chef/lib/chef/knife/cookbook_test.rb
new file mode 100644
index 0000000000..380739e68c
--- /dev/null
+++ b/chef/lib/chef/knife/cookbook_test.rb
@@ -0,0 +1,103 @@
+#
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Matthew Kent (<mkent@magoazul.com>)
+# Copyright:: Copyright (c) 2009 Opscode, Inc.
+# Copyright:: Copyright (c) 2010 Matthew Kent
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+require 'chef/knife'
+require 'chef/cache/checksum'
+
+class Chef
+ class Knife
+ class CookbookTest < Knife
+
+ banner "Sub-Command: cookbook test [COOKBOOKS...] (options)"
+
+ option :cookbook_path,
+ :short => "-o PATH:PATH",
+ :long => "--cookbook-path PATH:PATH",
+ :description => "A colon-separated path to look for cookbooks in",
+ :proc => lambda { |o| o.split(":") }
+
+ option :all,
+ :short => "-a",
+ :long => "--all",
+ :description => "Test all cookbooks, rather than just a single cookbook"
+
+ def run
+ if config[:cookbook_path]
+ Chef::Config[:cookbook_path] = config[:cookbook_path]
+ else
+ config[:cookbook_path] = Chef::Config[:cookbook_path]
+ end
+
+ if config[:all]
+ cl = Chef::CookbookLoader.new
+ cl.each do |cookbook|
+ test_cookbook(cookbook.name.to_s)
+ end
+ else
+ @name_args.each do |cb|
+ test_cookbook(cb)
+ end
+ end
+ end
+
+ def test_cookbook(cookbook)
+ Chef::Log.info("Running syntax check on #{cookbook}")
+ Array(config[:cookbook_path]).reverse.each do |path|
+ cookbook_dir = File.expand_path(File.join(path, cookbook))
+ test_ruby(cookbook_dir)
+ test_templates(cookbook_dir)
+ end
+ end
+
+ def test_ruby(cookbook_dir)
+ cache = Chef::Cache::Checksum.instance
+ Dir[File.join(cookbook_dir, '**', '*.rb')].each do |ruby_file|
+ key = cache.generate_key(ruby_file, "chef-test")
+ fstat = File.stat(ruby_file)
+
+ if cache.lookup_checksum(key, fstat)
+ Chef::Log.info("No change in checksum of #{ruby_file}")
+ else
+ Chef::Log.info("Testing #{ruby_file} for syntax errors...")
+ Chef::Mixin::Command.run_command(:command => "ruby -c #{ruby_file}", :output_on_failure => true)
+ cache.generate_checksum(key, ruby_file, fstat)
+ end
+ end
+ end
+
+ def test_templates(cookbook_dir)
+ cache = Chef::Cache::Checksum.instance
+ Dir[File.join(cookbook_dir, '**', '*.erb')].each do |erb_file|
+ key = cache.generate_key(erb_file, "chef-test")
+ fstat = File.stat(erb_file)
+
+ if cache.lookup_checksum(key, fstat)
+ Chef::Log.info("No change in checksum of #{erb_file}")
+ else
+ Chef::Log.info("Testing template #{erb_file} for syntax errors...")
+ Chef::Mixin::Command.run_command(:command => "sh -c 'erubis -x #{erb_file} | ruby -c'", :output_on_failure => true)
+ cache.generate_checksum(key, erb_file, fstat)
+ end
+ end
+ end
+
+ end
+ end
+end
diff --git a/chef/lib/chef/knife/cookbook_upload.rb b/chef/lib/chef/knife/cookbook_upload.rb
index 69b61d71bd..a289394ae3 100644
--- a/chef/lib/chef/knife/cookbook_upload.rb
+++ b/chef/lib/chef/knife/cookbook_upload.rb
@@ -7,9 +7,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -19,7 +19,6 @@
require 'chef/knife'
require 'chef/streaming_cookbook_uploader'
-require 'chef/cache/checksum'
class Chef
class Knife
@@ -38,14 +37,16 @@ class Chef
:long => "--all",
:description => "Upload all cookbooks, rather than just a single cookbook"
- def run
+ def run
+ Chef::Log.debug "Uploading cookbooks from #{config[:cookbook_path]}"
+
if config[:cookbook_path]
Chef::Config[:cookbook_path] = config[:cookbook_path]
else
config[:cookbook_path] = Chef::Config[:cookbook_path]
end
- if config[:all]
+ if config[:all]
cl = Chef::CookbookLoader.new
cl.each do |cookbook|
Chef::Log.info("** #{cookbook.name.to_s} **")
@@ -58,28 +59,21 @@ class Chef
end
end
end
-
- def test_ruby(cookbook_dir)
- Dir[File.join(cookbook_dir, '**', '*.rb')].each do |ruby_file|
- Chef::Log.info("Testing #{ruby_file} for syntax errors...")
- Chef::Mixin::Command.run_command(:command => "ruby -c #{ruby_file}")
- end
- end
-
- def test_templates(cookbook_dir)
- Dir[File.join(cookbook_dir, '**', '*.erb')].each do |erb_file|
- Chef::Log.info("Testing template #{erb_file} for syntax errors...")
- Chef::Mixin::Command.run_command(:command => "sh -c 'erubis -x #{erb_file} | ruby -c'")
- end
- end
def upload_cookbook(cookbook_name)
+ # Syntax check all cookbook paths rather than tmp_cookbook_dir as to
+ # take advantage of the existing cache used/generated by knife cookbook
+ # test.
+ check = Chef::Knife::CookbookTest.new
+ check.config[:cookbook_path] = config[:cookbook_path]
+ check.name_args = [ cookbook_name ]
+ check.run
if cookbook_name =~ /^#{File::SEPARATOR}/
- child_folders = cookbook_name
+ child_folders = cookbook_name
cookbook_name = File.basename(cookbook_name)
else
- child_folders = config[:cookbook_path].inject([]) do |r, e|
+ child_folders = config[:cookbook_path].inject([]) do |r, e|
r << File.join(e, cookbook_name)
r
end
@@ -98,26 +92,23 @@ class Chef
Chef::Log.debug("Staging at #{tmp_cookbook_dir}")
- found_cookbook = false
+ found_cookbook = false
child_folders.each do |file_path|
if File.directory?(file_path)
- found_cookbook = true
+ found_cookbook = true
Chef::Log.info("Copying from #{file_path} to #{tmp_cookbook_dir}")
- FileUtils.cp_r(file_path, tmp_cookbook_dir, :remove_destination => true)
+ FileUtils.cp_r(file_path, tmp_cookbook_dir, :remove_destination => true, :preserve => true)
else
Chef::Log.info("Nothing to copy from #{file_path}")
end
- end
+ end
unless found_cookbook
Chef::Log.fatal("Could not find cookbook #{cookbook_name}!")
exit 17
end
- test_ruby(tmp_cookbook_dir)
- test_templates(tmp_cookbook_dir)
-
# First, generate metadata
kcm = Chef::Knife::CookbookMetadata.new
kcm.config[:cookbook_path] = [ tmp_cookbook_dir ]
@@ -144,32 +135,32 @@ class Chef
if cookbook_uploaded
Chef::StreamingCookbookUploader.put(
- "#{Chef::Config[:chef_server_url]}/cookbooks/#{cookbook_name}/_content",
- Chef::Config[:node_name],
- Chef::Config[:client_key],
+ "#{Chef::Config[:chef_server_url]}/cookbooks/#{cookbook_name}/_content",
+ Chef::Config[:node_name],
+ Chef::Config[:client_key],
{
- :file => File.new(tarball_name),
+ :file => File.new(tarball_name),
:name => cookbook_name
}
)
else
Chef::StreamingCookbookUploader.post(
- "#{Chef::Config[:chef_server_url]}/cookbooks",
- Chef::Config[:node_name],
- Chef::Config[:client_key],
+ "#{Chef::Config[:chef_server_url]}/cookbooks",
+ Chef::Config[:node_name],
+ Chef::Config[:client_key],
{
- :file => File.new(tarball_name),
+ :file => File.new(tarball_name),
:name => cookbook_name
}
)
end
Chef::Log.info("Upload complete!")
Chef::Log.debug("Removing local tarball at #{tarball_name}")
- FileUtils.rm_rf tarball_name
+ FileUtils.rm_rf tarball_name
Chef::Log.debug("Removing local staging directory at #{tmp_cookbook_dir}")
FileUtils.rm_rf tmp_cookbook_dir
end
-
+
end
end
end
diff --git a/chef/lib/chef/knife/data_bag_edit.rb b/chef/lib/chef/knife/data_bag_edit.rb
index 16ac98dcad..50c53cc7a0 100644
--- a/chef/lib/chef/knife/data_bag_edit.rb
+++ b/chef/lib/chef/knife/data_bag_edit.rb
@@ -38,7 +38,7 @@ class Chef
Chef::Log.info("Saved data_bag_item[#{@name_args[1]}]")
- json_pretty_print(format_for_display(object)) if config[:print_after]
+ output(format_for_display(object)) if config[:print_after]
end
end
end
diff --git a/chef/lib/chef/knife/data_bag_list.rb b/chef/lib/chef/knife/data_bag_list.rb
index 42a01be1be..30ebbe8a89 100644
--- a/chef/lib/chef/knife/data_bag_list.rb
+++ b/chef/lib/chef/knife/data_bag_list.rb
@@ -30,8 +30,8 @@ class Chef
:long => "--with-uri",
:description => "Show corresponding URIs"
- def run
- json_pretty_print(format_list_for_display(Chef::DataBag.list))
+ def run
+ output(format_list_for_display(Chef::DataBag.list))
end
end
end
diff --git a/chef/lib/chef/knife/data_bag_show.rb b/chef/lib/chef/knife/data_bag_show.rb
index 977d2fcfd8..a030e56c85 100644
--- a/chef/lib/chef/knife/data_bag_show.rb
+++ b/chef/lib/chef/knife/data_bag_show.rb
@@ -32,7 +32,7 @@ class Chef
else
format_list_for_display(Chef::DataBag.load(@name_args[0]))
end
- json_pretty_print(display)
+ output(display)
end
end
end
diff --git a/chef/lib/chef/knife/ec2_instance_data.rb b/chef/lib/chef/knife/ec2_instance_data.rb
index 82acda9295..00282e7765 100644
--- a/chef/lib/chef/knife/ec2_instance_data.rb
+++ b/chef/lib/chef/knife/ec2_instance_data.rb
@@ -38,7 +38,7 @@ class Chef
"attributes" => { "run_list" => @name_args }
}
data = edit_data(data) if config[:edit]
- json_pretty_print(data)
+ output(data)
end
end
end
diff --git a/chef/lib/chef/knife/index_rebuild.rb b/chef/lib/chef/knife/index_rebuild.rb
index 4f99c3c932..b5d982be98 100644
--- a/chef/lib/chef/knife/index_rebuild.rb
+++ b/chef/lib/chef/knife/index_rebuild.rb
@@ -32,7 +32,7 @@ class Chef
def run
nag
- json_pretty_print rest.post_rest("/search/reindex", {})
+ output rest.post_rest("/search/reindex", {})
end
def nag
@@ -48,4 +48,4 @@ class Chef
end
end
-end \ No newline at end of file
+end
diff --git a/chef/lib/chef/knife/node_from_file.rb b/chef/lib/chef/knife/node_from_file.rb
index 02e33158a2..b0b5ff713c 100644
--- a/chef/lib/chef/knife/node_from_file.rb
+++ b/chef/lib/chef/knife/node_from_file.rb
@@ -31,7 +31,7 @@ class Chef
updated.save
- json_pretty_print(format_for_display(updated)) if config[:print_after]
+ output(format_for_display(updated)) if config[:print_after]
Chef::Log.warn("Updated Node #{updated.name}!")
end
diff --git a/chef/lib/chef/knife/node_list.rb b/chef/lib/chef/knife/node_list.rb
index 5239d2c29d..ad4255a938 100644
--- a/chef/lib/chef/knife/node_list.rb
+++ b/chef/lib/chef/knife/node_list.rb
@@ -31,8 +31,8 @@ class Chef
:long => "--with-uri",
:description => "Show corresponding URIs"
- def run
- json_pretty_print(format_list_for_display(Chef::Node.list))
+ def run
+ output(format_list_for_display(Chef::Node.list))
end
end
end
diff --git a/chef/lib/chef/knife/node_run_list_add.rb b/chef/lib/chef/knife/node_run_list_add.rb
index 33f3e81917..5ce61b12a8 100644
--- a/chef/lib/chef/knife/node_run_list_add.rb
+++ b/chef/lib/chef/knife/node_run_list_add.rb
@@ -41,7 +41,7 @@ class Chef
config[:run_list] = true
- json_pretty_print(format_for_display(node))
+ output(format_for_display(node))
end
def add_to_run_list(node, new_value, after=nil)
diff --git a/chef/lib/chef/knife/node_run_list_remove.rb b/chef/lib/chef/knife/node_run_list_remove.rb
index ab90015128..6bda2c21de 100644
--- a/chef/lib/chef/knife/node_run_list_remove.rb
+++ b/chef/lib/chef/knife/node_run_list_remove.rb
@@ -36,7 +36,7 @@ class Chef
config[:run_list] = true
- json_pretty_print(format_for_display(node))
+ output(format_for_display(node))
end
end
diff --git a/chef/lib/chef/knife/node_show.rb b/chef/lib/chef/knife/node_show.rb
index 1c7d29a037..e5cede73d1 100644
--- a/chef/lib/chef/knife/node_show.rb
+++ b/chef/lib/chef/knife/node_show.rb
@@ -38,7 +38,7 @@ class Chef
def run
node = Chef::Node.load(@name_args[0])
- json_pretty_print(format_for_display(node))
+ output(format_for_display(node))
end
end
end
diff --git a/chef/lib/chef/knife/role_from_file.rb b/chef/lib/chef/knife/role_from_file.rb
index 0ec9736c1c..b366e7f7f6 100644
--- a/chef/lib/chef/knife/role_from_file.rb
+++ b/chef/lib/chef/knife/role_from_file.rb
@@ -31,7 +31,7 @@ class Chef
updated.save
- json_pretty_print(format_for_display(updated)) if config[:print_after]
+ output(format_for_display(updated)) if config[:print_after]
Chef::Log.warn("Updated Role #{updated.name}!")
end
diff --git a/chef/lib/chef/knife/role_list.rb b/chef/lib/chef/knife/role_list.rb
index 6f7e8df55f..a8e127246b 100644
--- a/chef/lib/chef/knife/role_list.rb
+++ b/chef/lib/chef/knife/role_list.rb
@@ -32,7 +32,7 @@ class Chef
:description => "Show corresponding URIs"
def run
- json_pretty_print(format_list_for_display(Chef::Role.list))
+ output(format_list_for_display(Chef::Role.list))
end
end
end
diff --git a/chef/lib/chef/knife/role_show.rb b/chef/lib/chef/knife/role_show.rb
index 0ed17209c4..b5e1f0f0a3 100644
--- a/chef/lib/chef/knife/role_show.rb
+++ b/chef/lib/chef/knife/role_show.rb
@@ -33,7 +33,7 @@ class Chef
def run
role = Chef::Role.load(@name_args[0])
- json_pretty_print(format_for_display(role))
+ output(format_for_display(role))
end
end
diff --git a/chef/lib/chef/knife/search.rb b/chef/lib/chef/knife/search.rb
index cede2f16c9..f9a291c36b 100644
--- a/chef/lib/chef/knife/search.rb
+++ b/chef/lib/chef/knife/search.rb
@@ -82,7 +82,7 @@ class Chef
puts display[:rows].join("\n")
end
else
- json_pretty_print(display)
+ output(display)
end
end
end
diff --git a/chef/lib/chef/knife/terremark_server_create.rb b/chef/lib/chef/knife/terremark_server_create.rb
new file mode 100644
index 0000000000..87e933db91
--- /dev/null
+++ b/chef/lib/chef/knife/terremark_server_create.rb
@@ -0,0 +1,152 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2009 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/knife'
+require 'json'
+require 'tempfile'
+
+class Chef
+ class Knife
+ class TerremarkServerCreate < Knife
+
+ banner "Sub-Command: terremark server create NAME [RUN LIST...] (options)"
+
+ option :terremark_password,
+ :short => "-K PASSWORD",
+ :long => "--terremark-password PASSWORD",
+ :description => "Your terremark password",
+ :proc => Proc.new { |key| Chef::Config[:knife][:terremark_password] = key }
+
+ option :terremark_username,
+ :short => "-A USERNAME",
+ :long => "--terremark-username USERNAME",
+ :description => "Your terremark username",
+ :proc => Proc.new { |username| Chef::Config[:knife][:terremark_username] = username }
+
+ option :terremark_service,
+ :short => "-S SERVICE",
+ :long => "--terremark-service SERVICE",
+ :description => "Your terremark service name",
+ :proc => Proc.new { |service| Chef::Config[:knife][:terremark_service] = service }
+
+ def h
+ @highline ||= HighLine.new
+ end
+
+ def run
+ require 'fog'
+ require 'highline'
+ require 'net/ssh/multi'
+ require 'readline'
+ require 'net/scp'
+
+ server_name = @name_args[0]
+
+ terremark = Fog::Terremark.new(
+ :terremark_username => Chef::Config[:knife][:terremark_username],
+ :terremark_password => Chef::Config[:knife][:terremark_password],
+ :terremark_service => Chef::Config[:knife][:terremark_service] || :vcloud
+ )
+
+ $stdout.sync = true
+
+ puts "Instantiating vApp #{h.color(server_name, :bold)}"
+ vapp_id = terremark.instantiate_vapp_template(server_name).body['href'].split('/').last
+
+ deploy_task_id = terremark.deploy_vapp(vapp_id).body['href'].split('/').last
+ print "Waiting for deploy task [#{h.color(deploy_task_id, :bold)}]"
+ terremark.tasks.get(deploy_task_id).wait_for { print "."; ready? }
+ print "\n"
+
+ power_on_task_id = terremark.power_on(vapp_id).body['href'].split('/').last
+ print "Waiting for power on task [#{h.color(power_on_task_id, :bold)}]"
+ terremark.tasks.get(power_on_task_id).wait_for { print "."; ready? }
+ print "\n"
+
+ private_ip = terremark.get_vapp(vapp_id).body['IpAddress']
+ ssh_internet_service = terremark.create_internet_service(terremark.default_vdc_id, 'SSH', 'TCP', 22).body
+ ssh_internet_service_id = ssh_internet_service['Id']
+ public_ip = ssh_internet_service['PublicIpAddress']['Name']
+ public_ip_id = ssh_internet_service['PublicIpAddress']['Id']
+ ssh_node_service_id = terremark.add_node_service(ssh_internet_service_id, private_ip, 'SSH', 22).body['Id']
+
+ puts "\nBootstrapping #{h.color(server_name, :bold)}..."
+ password = terremark.get_vapp_template(12).body['Description'].scan(/\npassword: (.*)\n/).first.first
+
+ command = <<EOH
+bash -c '
+echo nameserver 208.67.222.222 > /etc/resolv.conf
+echo nameserver 208.67.220.220 >> /etc/resolv.conf
+
+if [ ! -f /usr/bin/chef-client ]; then
+ apt-get update
+ apt-get install -y ruby ruby1.8-dev build-essential wget libruby-extras libruby1.8-extras
+ cd /tmp
+ wget http://rubyforge.org/frs/download.php/69365/rubygems-1.3.6.tgz
+ tar xvf rubygems-1.3.6.tgz
+ cd rubygems-1.3.6
+ ruby setup.rb
+ cp /usr/bin/gem1.8 /usr/bin/gem
+ gem install chef ohai --no-rdoc --no-ri --verbose
+fi
+
+mkdir -p /etc/chef
+
+(
+cat <<'EOP'
+#{IO.read(Chef::Config[:validation_key])}
+EOP
+) > /etc/chef/validation.pem
+
+(
+cat <<'EOP'
+log_level :info
+log_location STDOUT
+chef_server_url "#{Chef::Config[:chef_server_url]}"
+validation_client_name "#{Chef::Config[:validation_client_name]}"
+EOP
+) > /etc/chef/client.rb
+
+(
+cat <<'EOP'
+#{{ "run_list" => @name_args[1..-1] }.to_json}
+EOP
+) > /etc/chef/first-boot.json
+
+/usr/bin/chef-client -j /etc/chef/first-boot.json'
+EOH
+
+ begin
+ ssh = Chef::Knife::Ssh.new
+ ssh.name_args = [ public_ip, "sudo #{command}" ]
+ ssh.config[:ssh_user] = "vcloud"
+ ssh.config[:manual] = true
+ ssh.config[:password] = password
+ ssh.password = password
+ ssh.run
+ rescue Errno::ETIMEDOUT
+ puts "Timed out on bootstrap, re-trying. Hit CTRL-C to abort."
+ puts "You probably need to log in to Terremark and powercycle #{h.color(@name_args[0], :bold)}"
+ retry
+ end
+
+ end
+ end
+ end
+end
+
diff --git a/chef/lib/chef/knife/terremark_server_delete.rb b/chef/lib/chef/knife/terremark_server_delete.rb
new file mode 100644
index 0000000000..38f98dec4b
--- /dev/null
+++ b/chef/lib/chef/knife/terremark_server_delete.rb
@@ -0,0 +1,87 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2009 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/knife'
+require 'json'
+
+class Chef
+ class Knife
+ class TerremarkServerDelete < Knife
+
+ banner "Sub-Command: terremark server delete SERVER (options)"
+
+ def h
+ @highline ||= HighLine.new
+ end
+
+ def run
+ require 'fog'
+ require 'highline'
+
+ terremark = Fog::Terremark.new(
+ :terremark_username => Chef::Config[:knife][:terremark_username],
+ :terremark_password => Chef::Config[:knife][:terremark_password],
+ :terremark_service => Chef::Config[:knife][:terremark_service] || :vcloud
+ )
+
+ $stdout.sync = true
+
+ vapp_id = terremark.servers.detect {|server| server.name == @name_args[0]}.id
+ confirm("Do you really want to delete server ID #{vapp_id} named #{@name_args[0]}")
+
+ puts "Cleaning up internet services..."
+ private_ip = terremark.servers.get(vapp_id).ip_address
+ internet_services = terremark.get_internet_services(terremark.default_vdc_id).body['InternetServices']
+ public_ip_usage = {}
+ internet_services.each do |internet_service|
+ public_ip_address = internet_service['PublicIpAddress']['Name']
+ public_ip_usage[public_ip_address] ||= []
+ public_ip_usage[public_ip_address] << internet_service['Id']
+ end
+ internet_services.each do |internet_service|
+ node_services = terremark.get_node_services(internet_service['Id']).body['NodeServices']
+ node_services.delete_if do |node_service|
+ if node_service['IpAddress'] == private_ip
+ terremark.delete_node_service(node_service['Id'])
+ end
+ end
+ if node_services.empty?
+ terremark.delete_internet_service(internet_service['Id'])
+ public_ip_usage.each_value {|internet_services| internet_services.delete(internet_service['Id'])}
+ if public_ip_usage[internet_service['PublicIpAddress']['Name']].empty?
+ terremark.delete_public_ip(internet_service['PublicIpAddress']['Id'])
+ end
+ end
+ end
+
+ power_off_task_id = terremark.power_off(vapp_id).body['href'].split('/').last
+ print "Waiting for power off task [#{h.color(power_off_task_id, :bold)}]"
+ terremark.tasks.get(power_off_task_id).wait_for { print '.'; ready? }
+ print "\n"
+
+ print "Deleting vApp #{h.color(vapp_id, :bold)}"
+ delete_vapp_task_id = terremark.delete_vapp(vapp_id).headers['Location'].split('/').last
+ terremark.tasks.get(delete_vapp_task_id).wait_for { print '.'; ready? }
+ print "\n"
+
+ Chef::Log.warn("Deleted server #{@name_args[0]}")
+ end
+ end
+ end
+end
+
diff --git a/chef/lib/chef/knife/terremark_server_list.rb b/chef/lib/chef/knife/terremark_server_list.rb
new file mode 100644
index 0000000000..3aba858570
--- /dev/null
+++ b/chef/lib/chef/knife/terremark_server_list.rb
@@ -0,0 +1,77 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2009 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/knife'
+require 'json'
+require 'tempfile'
+
+class Chef
+ class Knife
+ class TerremarkServerList < Knife
+
+ banner "Sub-Command: terremark server list (options)"
+
+ option :terremark_password,
+ :short => "-K PASSWORD",
+ :long => "--terremark-password PASSWORD",
+ :description => "Your terremark password",
+ :proc => Proc.new { |key| Chef::Config[:knife][:terremark_password] = key }
+
+ option :terremark_username,
+ :short => "-A USERNAME",
+ :long => "--terremark-username USERNAME",
+ :description => "Your terremark username",
+ :proc => Proc.new { |username| Chef::Config[:knife][:terremark_username] = username }
+
+ option :terremark_service,
+ :short => "-S SERVICE",
+ :long => "--terremark-service SERVICE",
+ :description => "Your terremark service name",
+ :proc => Proc.new { |service| Chef::Config[:knife][:terremark_service] = service }
+
+ def h
+ @highline ||= HighLine.new
+ end
+
+ def run
+ require 'fog'
+ require 'highline'
+
+ server_name = @name_args[0]
+
+ terremark = Fog::Terremark.new(
+ :terremark_username => Chef::Config[:knife][:terremark_username],
+ :terremark_password => Chef::Config[:knife][:terremark_password],
+ :terremark_service => Chef::Config[:knife][:terremark_service] || :vcloud
+ )
+
+ $stdout.sync = true
+
+ server_list = [ h.color('ID', :bold), h.color('Name', :bold) ]
+ terremark.servers.all.each do |server|
+ server_list << server.id.to_s
+ server_list << server.name
+ end
+ puts h.list(server_list, :columns_across, 2)
+
+ end
+ end
+ end
+end
+
+
diff --git a/chef/lib/chef/mixin/command.rb b/chef/lib/chef/mixin/command.rb
index eec0966189..f703674c03 100644
--- a/chef/lib/chef/mixin/command.rb
+++ b/chef/lib/chef/mixin/command.rb
@@ -97,6 +97,7 @@ class Chef
# timeout<String>: How many seconds to wait for the command to execute before timing out
# returns<String>: The single exit value command is expected to return, otherwise causes an exception
# ignore_failure<Boolean>: Whether to raise an exception on failure, or just return the status
+ # output_on_failure<Boolean>: Return output in raised exception regardless of Log.level
#
# user<String>: The UID or user name of the user to execute the command as
# group<String>: The GID or group name of the group to execute the command as
@@ -108,6 +109,7 @@ class Chef
command_output = ""
args[:ignore_failure] ||= false
+ args[:output_on_failure] ||= false
if args.has_key?(:creates)
if File.exists?(args[:creates])
@@ -165,18 +167,18 @@ class Chef
module_function :output_of_command
- def handle_command_failures(status, command_output, args={})
- unless args[:ignore_failure]
- args[:returns] ||= 0
- if status.exitstatus != args[:returns]
+ def handle_command_failures(status, command_output, opts={})
+ unless opts[:ignore_failure]
+ opts[:returns] ||= 0
+ unless Array(opts[:returns]).include?(status.exitstatus)
# if the log level is not debug, through output of command when we fail
output = ""
- if Chef::Log.level == :debug
- output << "\n---- Begin output of #{args[:command]} ----\n"
- output << "#{command_output}"
- output << "---- End output of #{args[:command]} ----\n"
+ if Chef::Log.level == :debug || opts[:output_on_failure]
+ output << "\n---- Begin output of #{opts[:command]} ----\n"
+ output << command_output.to_s
+ output << "\n---- End output of #{opts[:command]} ----\n"
end
- raise Chef::Exceptions::Exec, "#{args[:command]} returned #{status.exitstatus}, expected #{args[:returns]}#{output}"
+ raise Chef::Exceptions::Exec, "#{opts[:command]} returned #{status.exitstatus}, expected #{opts[:returns]}#{output}"
end
end
end
diff --git a/chef/lib/chef/mixin/recipe_definition_dsl_core.rb b/chef/lib/chef/mixin/recipe_definition_dsl_core.rb
index 1f27d1bd79..c8afd07574 100644
--- a/chef/lib/chef/mixin/recipe_definition_dsl_core.rb
+++ b/chef/lib/chef/mixin/recipe_definition_dsl_core.rb
@@ -17,11 +17,14 @@
# limitations under the License.
#
-require 'chef/recipe'
require 'chef/resource'
require 'chef/mixin/convert_to_class_name'
require 'chef/mixin/language'
+# UGH. this is a circular require that will cause an uninitialized constant
+# error, but this file really does depend on Chef::Recipe. oh well.
+# require 'chef/recipe'
+
class Chef
module Mixin
module RecipeDefinitionDSLCore
diff --git a/chef/lib/chef/mixin/template.rb b/chef/lib/chef/mixin/template.rb
index 0309b5994c..df6c2839df 100644
--- a/chef/lib/chef/mixin/template.rb
+++ b/chef/lib/chef/mixin/template.rb
@@ -42,15 +42,16 @@ class Chef
rescue Object => e
raise TemplateError.new(e, template, context)
end
- final_tempfile = Tempfile.new("chef-rendered-template")
- final_tempfile.print(output)
- final_tempfile.close
- final_tempfile
+ Tempfile.open("chef-rendered-template") do |tempfile|
+ tempfile.print(output)
+ tempfile.close
+ yield tempfile
+ end
end
class TemplateError < RuntimeError
attr_reader :original_exception, :context
- SOURCE_CONTEXT_WINDOW = 2 unless defined? SOURCE_CONTEXT_WINDOW
+ SOURCE_CONTEXT_WINDOW = 2
def initialize(original_exception, template, context)
@original_exception, @template, @context = original_exception, template, context
diff --git a/chef/lib/chef/mixin/xml_escape.rb b/chef/lib/chef/mixin/xml_escape.rb
index c9ef7e76f9..7ab40470b4 100644
--- a/chef/lib/chef/mixin/xml_escape.rb
+++ b/chef/lib/chef/mixin/xml_escape.rb
@@ -80,18 +80,18 @@ class Chef
156 => 339, # latin small ligature oe
158 => 382, # latin small letter z with caron
159 => 376 # latin capital letter y with diaeresis
- } unless defined?(CP1252)
+ }
# http://www.w3.org/TR/REC-xml/#dt-chardata
PREDEFINED = {
38 => '&amp;', # ampersand
60 => '&lt;', # left angle bracket
62 => '&gt;' # right angle bracket
- } unless defined?(PREDEFINED)
+ }
# http://www.w3.org/TR/REC-xml/#charsets
VALID = [[0x9, 0xA, 0xD], (0x20..0xD7FF),
- (0xE000..0xFFFD), (0x10000..0x10FFFF)] unless defined?(VALID)
+ (0xE000..0xFFFD), (0x10000..0x10FFFF)]
def xml_escape(unescaped_str)
begin
diff --git a/chef/lib/chef/mixins.rb b/chef/lib/chef/mixins.rb
new file mode 100644
index 0000000000..5c9b8f4320
--- /dev/null
+++ b/chef/lib/chef/mixins.rb
@@ -0,0 +1,16 @@
+require 'chef/mixin/check_helper'
+require 'chef/mixin/checksum'
+require 'chef/mixin/command'
+require 'chef/mixin/convert_to_class_name'
+require 'chef/mixin/create_path'
+require 'chef/mixin/deep_merge'
+require 'chef/mixin/find_preferred_file'
+require 'chef/mixin/from_file'
+require 'chef/mixin/generate_url'
+require 'chef/mixin/language'
+require 'chef/mixin/language_include_attribute'
+require 'chef/mixin/language_include_recipe'
+require 'chef/mixin/params_validate'
+require 'chef/mixin/recipe_definition_dsl_core'
+require 'chef/mixin/template'
+require 'chef/mixin/xml_escape' \ No newline at end of file
diff --git a/chef/lib/chef/node.rb b/chef/lib/chef/node.rb
index f79dd4fc87..9bdf7228e6 100644
--- a/chef/lib/chef/node.rb
+++ b/chef/lib/chef/node.rb
@@ -7,9 +7,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,6 +22,7 @@ require 'chef/mixin/check_helper'
require 'chef/mixin/params_validate'
require 'chef/mixin/from_file'
require 'chef/mixin/language_include_attribute'
+require 'chef/mixin/deep_merge'
require 'chef/couchdb'
require 'chef/rest'
require 'chef/run_list'
@@ -36,20 +37,20 @@ class Chef
attr_accessor :recipe_list, :couchdb, :couchdb_rev, :run_state, :run_list, :override_attrs, :default_attrs, :normal_attrs, :automatic_attrs, :cookbook_loader
attr_reader :node
attr_reader :couchdb_id
-
+
include Chef::Mixin::CheckHelper
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
include Chef::Mixin::LanguageIncludeAttribute
include Chef::IndexQueue::Indexable
-
+
DESIGN_DOCUMENT = {
"version" => 9,
"language" => "javascript",
"views" => {
"all" => {
"map" => <<-EOJS
- function(doc) {
+ function(doc) {
if (doc.chef_type == "node") {
emit(doc.name, doc);
}
@@ -58,7 +59,7 @@ class Chef
},
"all_id" => {
"map" => <<-EOJS
- function(doc) {
+ function(doc) {
if (doc.chef_type == "node") {
emit(doc.name, doc.name);
}
@@ -84,7 +85,7 @@ class Chef
to_emit["ohai_time"] = doc["attributes"]["ohai_time"];
} else {
to_emit["ohai_time"] = "Undefined";
- }
+ }
if (doc["attributes"]["uptime"]) {
to_emit["uptime"] = doc["attributes"]["uptime"];
} else {
@@ -125,7 +126,7 @@ class Chef
}
},
}
-
+
# Create a new Chef::Node object.
def initialize(couchdb=nil)
@name = nil
@@ -154,36 +155,36 @@ class Chef
end
def chef_server_rest
- Chef::REST.new(Chef::Config[:chef_server_url])
+ Chef::REST.new(Chef::Config[:chef_server_url])
end
def self.chef_server_rest
- Chef::REST.new(Chef::Config[:chef_server_url])
+ Chef::REST.new(Chef::Config[:chef_server_url])
end
- # Find a recipe for this Chef::Node by fqdn. Will search first for
+ # Find a recipe for this 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.
+ # Raises an ArgumentError if it cannot find the node.
def find_file(fqdn)
host_parts = fqdn.split(".")
hostname = host_parts[0]
-
+
[fqdn, hostname, "default"].each { |fname|
- node_file = File.join(Chef::Config[:node_path], "#{fname.to_s}.rb")
+ node_file = File.join(Chef::Config[:node_path], "#{fname.to_s}.rb")
return self.from_file(node_file) if File.exists?(node_file)
}
-
- raise ArgumentError, "Cannot find a node matching #{fqdn}, not even with default.rb!"
+
+ raise ArgumentError, "Cannot find a node matching #{fqdn}, not even with default.rb!"
end
-
+
# Set the name of this Node, or return the current name.
def name(arg=nil)
if arg != nil
validate(
- {:name => arg },
+ {:name => arg },
{:name => { :kind_of => String,
:cannot_be => :blank}
})
@@ -194,7 +195,7 @@ class Chef
end
def attribute
- Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attribute
end
def attribute=(value)
@@ -203,14 +204,14 @@ class Chef
# Return an attribute of this node. Returns nil if the attribute is not found.
def [](attrib)
- Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)[attrib]
+ attribute[attrib]
end
-
+
# Set an attribute of this node
def []=(attrib, value)
- Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)[attrib] = value
+ attribute[attrib] = value
end
-
+
def store(attrib, value)
self[attrib] = value
end
@@ -218,7 +219,7 @@ class Chef
# Set a normal attribute of this node, but auto-vivifiy any Mashes that
# might be missing
def normal
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :normal
attrs.auto_vivifiy_on_read = true
attrs
@@ -229,7 +230,7 @@ class Chef
# Set a normal attribute of this node, auto-vivifiying any mashes that are
# missing, but if the final value already exists, don't set it
def normal_unless
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :normal
attrs.auto_vivifiy_on_read = true
attrs.set_unless_value_present = true
@@ -240,7 +241,7 @@ class Chef
# Set a default of this node, but auto-vivifiy any Mashes that might
# be missing
def default
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :default
attrs.auto_vivifiy_on_read = true
attrs
@@ -249,7 +250,7 @@ class Chef
# Set a default attribute of this node, auto-vivifiying any mashes that are
# missing, but if the final value already exists, don't set it
def default_unless
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :default
attrs.auto_vivifiy_on_read = true
attrs.set_unless_value_present = true
@@ -259,7 +260,7 @@ class Chef
# Set an override attribute of this node, but auto-vivifiy any Mashes that
# might be missing
def override
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :override
attrs.auto_vivifiy_on_read = true
attrs
@@ -268,7 +269,7 @@ class Chef
# Set an override attribute of this node, auto-vivifiying any mashes that
# are missing, but if the final value already exists, don't set it
def override_unless
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :override
attrs.auto_vivifiy_on_read = true
attrs.set_unless_value_present = true
@@ -281,44 +282,44 @@ class Chef
# Only works on the top level. Preferred way is to use the normal [] style
# lookup and call attribute?()
def attribute?(attrib)
- Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs).attribute?(attrib)
+ attribute.attribute?(attrib)
end
-
- # Yield each key of the top level to the block.
+
+ # Yield each key of the top level to the block.
def each(&block)
- Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs).each(&block)
+ attribute.each(&block)
end
-
+
# Iterates over each attribute, passing the attribute and value to the block.
def each_attribute(&block)
- Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs).each_attribute(&block)
+ attribute.each_attribute(&block)
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)
- attrs = Chef::Node::Attribute.new(@normal_attrs, @default_attrs, @override_attrs, @automatic_attrs)
+ attrs = attribute
attrs.set_type = :normal
attrs.send(symbol, *args)
end
-
+
# Returns true if this Node expects a given recipe, false if not.
def recipe?(recipe_name)
@run_list.include?(recipe_name) || @run_state[:seen_recipes].include?(recipe_name)
end
-
+
# Returns true if this Node expects a given role, false if not.
def role?(role_name)
@run_list.include?("role[#{role_name}]")
end
# Returns an Array of roles and recipes, in the order they will be applied.
- # If you call it with arguments, they will become the new list of roles and recipes.
+ # If you call it with arguments, they will become the new list of roles and recipes.
def run_list(*args)
args.length > 0 ? @run_list.reset!(args) : @run_list
end
-
+
def recipes(*args)
Chef::Log.warn "Chef::Node#recipes method is deprecated. Please use Chef::Node#run_list"
run_list(*args)
@@ -328,22 +329,22 @@ class Chef
def run_list?(item)
@run_list.detect { |r| r == item } ? true : false
end
-
+
def consume_attributes(attrs)
attrs ||= {}
Chef::Log.debug("Adding JSON Attributes")
- attrs.each do |key, value|
- if ["recipes", "run_list"].include?(key)
- run_list(value)
- else
- Chef::Log.debug("JSON Attribute: #{key} - #{value.inspect}")
- store(key, value)
+ if new_run_list = attrs.delete("recipes") || attrs.delete("run_list")
+ if attrs.key?("recipes") || attrs.key?("run_list")
+ raise Chef::Exceptions::AmbiguousRunlistSpecification, "please set the node's run list using the 'run_list' attribute only."
end
+ Chef::Log.info("Replacing the run_list with #{new_run_list.inspect} from JSON")
+ run_list(new_run_list)
end
+ Chef::Mixin::DeepMerge.merge(@attribute, attrs)
+
self[:tags] = Array.new unless attribute?(:tags)
-
end
-
+
# Transform the node to a Hash
def to_hash
index_hash = Hash.new
@@ -357,8 +358,8 @@ class Chef
index_hash["run_list"] = @run_list.run_list if @run_list.run_list.length > 0
index_hash
end
-
- # Serialize this object as a hash
+
+ # Serialize this object as a hash
def to_json(*a)
result = {
"name" => @name,
@@ -373,11 +374,12 @@ class Chef
result["_rev"] = @couchdb_rev if @couchdb_rev
result.to_json(*a)
end
-
+
# Create a Chef::Node from JSON
def self.json_create(o)
node = new
node.name(o["name"])
+
if o.has_key?("attributes")
node.normal_attrs = o["attributes"]
end
@@ -396,7 +398,7 @@ class Chef
node.index_id = node.couchdb_id
node
end
-
+
# List all the Chef::Node objects in the CouchDB. If inflate is set to true, you will get
# the full list of all Nodes, fully inflated.
def self.cdb_list(inflate=false, couchdb=nil)
@@ -416,10 +418,10 @@ class Chef
Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("nodes")
end
end
-
+
# Load a node by name from CouchDB
def self.cdb_load(name, couchdb=nil)
- (couchdb || Chef::CouchDB.new).load("node", name)
+ (couchdb || Chef::CouchDB.new).load("node", name)
end
def self.exists?(nodename, couchdb)
@@ -429,12 +431,12 @@ class Chef
nil
end
end
-
+
# Load a node by name
def self.load(name)
Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("nodes/#{name}")
end
-
+
# Remove this node from the CouchDB
def cdb_destroy
@couchdb.delete("node", @name, @couchdb_rev)
@@ -444,7 +446,7 @@ class Chef
def destroy
chef_server_rest.delete_rest("nodes/#{@name}")
end
-
+
# Save this node to the CouchDB
def cdb_save
@couchdb_rev = @couchdb.store("node", @name, self)["rev"]
@@ -460,18 +462,18 @@ class Chef
end
self
end
-
+
# Create the node via the REST API
def create
chef_server_rest.post_rest("nodes", self)
self
- end
+ end
# Set up our CouchDB design document
def self.create_design_document(couchdb=nil)
(couchdb || Chef::CouchDB.new).create_design_document("nodes", DESIGN_DOCUMENT)
end
-
+
# As a string
def to_s
"node[#{@name}]"
diff --git a/chef/lib/chef/openid_registration.rb b/chef/lib/chef/openid_registration.rb
index fcc67dd08c..82bee0798e 100644
--- a/chef/lib/chef/openid_registration.rb
+++ b/chef/lib/chef/openid_registration.rb
@@ -21,7 +21,6 @@ require 'chef/mixin/params_validate'
require 'chef/couchdb'
require 'chef/index_queue'
require 'digest/sha1'
-require 'rubygems'
require 'json'
class Chef
diff --git a/chef/lib/chef/platform.rb b/chef/lib/chef/platform.rb
index 6d81f7a9e0..58ebc4d0fb 100644
--- a/chef/lib/chef/platform.rb
+++ b/chef/lib/chef/platform.rb
@@ -19,128 +19,162 @@
require 'chef/config'
require 'chef/log'
require 'chef/mixin/params_validate'
-require 'chef/platform'
-require 'chef/resource'
-Dir[File.join(File.dirname(__FILE__), 'provider/**/*.rb')].sort.each { |lib| require lib }
+
+# Actually, this file depends on nearly every provider in chef, but actually
+# requiring them causes circular requires resulting in uninitialized constant
+# errors.
+require 'chef/provider'
+require 'chef/provider/log'
+require 'chef/provider/user'
+require 'chef/provider/group'
+require 'chef/provider/mount'
+require 'chef/provider/service'
+require 'chef/provider/package'
+
class Chef
class Platform
- @platforms = {
- :mac_os_x => {
- :default => {
- :package => Chef::Provider::Package::Macports,
- :user => Chef::Provider::User::Dscl,
- :group => Chef::Provider::Group::Dscl
- }
- },
- :freebsd => {
- :default => {
- :group => Chef::Provider::Group::Pw,
- :package => Chef::Provider::Package::Freebsd,
- :service => Chef::Provider::Service::Freebsd,
- :user => Chef::Provider::User::Pw,
- :cron => Chef::Provider::Cron
- }
- },
- :ubuntu => {
- :default => {
- :package => Chef::Provider::Package::Apt,
- :service => Chef::Provider::Service::Debian,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :debian => {
- :default => {
- :package => Chef::Provider::Package::Apt,
- :service => Chef::Provider::Service::Debian,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :centos => {
- :default => {
- :service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
- :package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :fedora => {
- :default => {
- :service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
- :package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :suse => {
- :default => {
- :service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
- :package => Chef::Provider::Package::Zypper
- }
- },
- :redhat => {
- :default => {
- :service => Chef::Provider::Service::Redhat,
- :cron => Chef::Provider::Cron,
- :package => Chef::Provider::Package::Yum,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :gentoo => {
- :default => {
- :package => Chef::Provider::Package::Portage,
- :service => Chef::Provider::Service::Gentoo,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :arch => {
- :default => {
- :package => Chef::Provider::Package::Pacman,
- :service => Chef::Provider::Service::Arch,
- :cron => Chef::Provider::Cron,
- :mdadm => Chef::Provider::Mdadm
- }
- },
- :solaris => {},
- :default => {
- :file => Chef::Provider::File,
- :directory => Chef::Provider::Directory,
- :link => Chef::Provider::Link,
- :template => Chef::Provider::Template,
- :remote_file => Chef::Provider::RemoteFile,
- :remote_directory => Chef::Provider::RemoteDirectory,
- :execute => Chef::Provider::Execute,
- :mount => Chef::Provider::Mount::Mount,
- :script => Chef::Provider::Script,
- :service => Chef::Provider::Service::Init,
- :perl => Chef::Provider::Script,
- :python => Chef::Provider::Script,
- :ruby => Chef::Provider::Script,
- :bash => Chef::Provider::Script,
- :csh => Chef::Provider::Script,
- :user => Chef::Provider::User::Useradd,
- :group => Chef::Provider::Group::Gpasswd,
- :http_request => Chef::Provider::HttpRequest,
- :route => Chef::Provider::Route,
- :ifconfig => Chef::Provider::Ifconfig,
- :ruby_block => Chef::Provider::RubyBlock,
- :erl_call => Chef::Provider::ErlCall,
- :log => Chef::Provider::Log::ChefLog
- }
- }
-
class << self
- attr_accessor :platforms
+ attr_writer :platforms
+
+ def platforms
+ @platforms ||= {
+ :mac_os_x => {
+ :default => {
+ :package => Chef::Provider::Package::Macports,
+ :user => Chef::Provider::User::Dscl,
+ :group => Chef::Provider::Group::Dscl
+ }
+ },
+ :freebsd => {
+ :default => {
+ :group => Chef::Provider::Group::Pw,
+ :package => Chef::Provider::Package::Freebsd,
+ :service => Chef::Provider::Service::Freebsd,
+ :user => Chef::Provider::User::Pw,
+ :cron => Chef::Provider::Cron
+ }
+ },
+ :ubuntu => {
+ :default => {
+ :package => Chef::Provider::Package::Apt,
+ :service => Chef::Provider::Service::Debian,
+ :cron => Chef::Provider::Cron,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :debian => {
+ :default => {
+ :package => Chef::Provider::Package::Apt,
+ :service => Chef::Provider::Service::Debian,
+ :cron => Chef::Provider::Cron,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :centos => {
+ :default => {
+ :service => Chef::Provider::Service::Redhat,
+ :cron => Chef::Provider::Cron,
+ :package => Chef::Provider::Package::Yum,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :scientific => {
+ :default => {
+ :service => Chef::Provider::Service::Redhat,
+ :cron => Chef::Provider::Cron,
+ :package => Chef::Provider::Package::Yum,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :fedora => {
+ :default => {
+ :service => Chef::Provider::Service::Redhat,
+ :cron => Chef::Provider::Cron,
+ :package => Chef::Provider::Package::Yum,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :suse => {
+ :default => {
+ :service => Chef::Provider::Service::Redhat,
+ :cron => Chef::Provider::Cron,
+ :package => Chef::Provider::Package::Zypper
+ }
+ },
+ :redhat => {
+ :default => {
+ :service => Chef::Provider::Service::Redhat,
+ :cron => Chef::Provider::Cron,
+ :package => Chef::Provider::Package::Yum,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :gentoo => {
+ :default => {
+ :package => Chef::Provider::Package::Portage,
+ :service => Chef::Provider::Service::Gentoo,
+ :cron => Chef::Provider::Cron,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :arch => {
+ :default => {
+ :package => Chef::Provider::Package::Pacman,
+ :service => Chef::Provider::Service::Arch,
+ :cron => Chef::Provider::Cron,
+ :mdadm => Chef::Provider::Mdadm
+ }
+ },
+ :mswin => {
+ :default => {
+ :service => Chef::Provider::Service::Windows
+ }
+ },
+ :mingw32 => {
+ :default => {
+ :service => Chef::Provider::Service::Windows
+ }
+ },
+ :windows => {
+ :default => {
+ :service => Chef::Provider::Service::Windows
+ }
+ },
+ :solaris => {},
+ :default => {
+ :file => Chef::Provider::File,
+ :directory => Chef::Provider::Directory,
+ :link => Chef::Provider::Link,
+ :template => Chef::Provider::Template,
+ :remote_file => Chef::Provider::RemoteFile,
+ :remote_directory => Chef::Provider::RemoteDirectory,
+ :execute => Chef::Provider::Execute,
+ :mount => Chef::Provider::Mount::Mount,
+ :script => Chef::Provider::Script,
+ :service => Chef::Provider::Service::Init,
+ :perl => Chef::Provider::Script,
+ :python => Chef::Provider::Script,
+ :ruby => Chef::Provider::Script,
+ :bash => Chef::Provider::Script,
+ :csh => Chef::Provider::Script,
+ :user => Chef::Provider::User::Useradd,
+ :group => Chef::Provider::Group::Gpasswd,
+ :http_request => Chef::Provider::HttpRequest,
+ :route => Chef::Provider::Route,
+ :ifconfig => Chef::Provider::Ifconfig,
+ :ruby_block => Chef::Provider::RubyBlock,
+ :erl_call => Chef::Provider::ErlCall,
+ :log => Chef::Provider::Log::ChefLog
+ }
+ }
+ end
include Chef::Mixin::ParamsValidate
def find(name, version)
- provider_map = @platforms[:default].clone
+ provider_map = platforms[:default].clone
name_sym = name
if name.kind_of?(String)
@@ -149,15 +183,15 @@ class Chef
name_sym = name.to_sym
end
- if @platforms.has_key?(name_sym)
- if @platforms[name_sym].has_key?(version)
+ 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])
+ 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])
+ 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?)")
@@ -221,30 +255,30 @@ class Chef
)
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]
+ 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]] = {
+ platforms[args[:platform]][args[:version]] = {
args[:resource].to_sym => args[:provider]
}
end
else
- @platforms[args[:platform]] = {
+ platforms[args[:platform]] = {
args[:version] => {
args[:resource].to_sym => args[:provider]
}
}
end
else
- if @platforms.has_key?(args[:platform])
- if @platforms[args[:platform]].has_key?(:default)
- @platforms[args[:platform]][:default][args[:resource].to_sym] = args[:provider]
+ if platforms.has_key?(args[:platform])
+ if platforms[args[:platform]].has_key?(:default)
+ platforms[args[:platform]][:default][args[:resource].to_sym] = args[:provider]
else
- @platforms[args[:platform]] = { :default => { args[:resource].to_sym => args[:provider] } }
+ platforms[args[:platform]] = { :default => { args[:resource].to_sym => args[:provider] } }
end
else
- @platforms[args[:platform]] = {
+ platforms[args[:platform]] = {
:default => {
args[:resource].to_sym => args[:provider]
}
@@ -252,10 +286,10 @@ class Chef
end
end
else
- if @platforms.has_key?(:default)
- @platforms[:default][args[:resource].to_sym] = args[:provider]
+ if platforms.has_key?(:default)
+ platforms[:default][args[:resource].to_sym] = args[:provider]
else
- @platforms[:default] = {
+ platforms[:default] = {
args[:resource].to_sym => args[:provider]
}
end
@@ -264,8 +298,8 @@ class Chef
def find_provider(platform, version, resource_type)
pmap = Chef::Platform.find(platform, version)
- provider_klass = explicit_provider(platform, version, resource_type) ||
- platform_provider(platform, version, resource_type) ||
+ provider_klass = explicit_provider(platform, version, resource_type) ||
+ platform_provider(platform, version, resource_type) ||
resource_matching_provider(platform, version, resource_type)
raise ArgumentError, "Cannot find a provider for #{resource_type} on #{platform} version #{version}" if provider_klass.nil?
@@ -288,8 +322,8 @@ class Chef
def resource_matching_provider(platform, version, resource_type)
if resource_type.kind_of?(Chef::Resource)
begin
- Chef::Provider.const_get(resource_type.class.to_s.split('::').last)
- rescue NameError
+ Chef::Provider.const_get(resource_type.class.to_s.split('::').last)
+ rescue NameError
nil
end
else
diff --git a/chef/lib/chef/provider.rb b/chef/lib/chef/provider.rb
index 2191aabc94..bd7c50a065 100644
--- a/chef/lib/chef/provider.rb
+++ b/chef/lib/chef/provider.rb
@@ -49,10 +49,10 @@ class Chef
protected
- def recipe_eval(*args, &block)
+ def recipe_eval(&block)
provider_collection, @collection = @collection, Chef::ResourceCollection.new
- instance_eval(*args, &block)
+ instance_eval(&block)
Chef::Runner.new(@node, @collection).converge
@collection = provider_collection
diff --git a/chef/lib/chef/provider/cron.rb b/chef/lib/chef/provider/cron.rb
index 25467ffbf6..df3f9e66fe 100644
--- a/chef/lib/chef/provider/cron.rb
+++ b/chef/lib/chef/provider/cron.rb
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -35,19 +35,19 @@ class Chef
attr_accessor :cron_exists, :cron_empty
def load_current_resource
- crontab = String.new
+ crontab_lines = []
@current_resource = Chef::Resource::Cron.new(@new_resource.name)
@current_resource.user(@new_resource.user)
status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr|
- stdout.each { |line| crontab << line }
+ stdout.each_line { |line| crontab_lines << line }
end
if status.exitstatus > 1
raise Chef::Exceptions::Cron, "Error determining state of #{@new_resource.name}, exit: #{status.exitstatus}"
elsif status.exitstatus == 0
cron_found = false
- crontab.each do |line|
- case line
- when /^# Chef Name: #{@new_resource.name}/
+ crontab_lines.each do |line|
+ case line.chomp
+ when "# Chef Name: #{@new_resource.name}"
Chef::Log.debug("Found cron '#{@new_resource.name}'")
cron_found = true
@cron_exists = true
@@ -66,11 +66,11 @@ class Chef
next
when CRON_PATTERN
if cron_found
- @current_resource.minute($1)
- @current_resource.hour($2)
+ @current_resource.minute($1)
+ @current_resource.hour($2)
@current_resource.day($3)
- @current_resource.month($4)
- @current_resource.weekday($5)
+ @current_resource.month($4)
+ @current_resource.weekday($5)
@current_resource.command($6)
cron_found=false
end
@@ -84,7 +84,7 @@ class Chef
Chef::Log.debug("Cron empty for '#{@new_resource.user}'")
@cron_empty = true
end
-
+
@current_resource
end
@@ -112,8 +112,8 @@ class Chef
end
status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr|
stdout.each_line do |line|
- case line
- when /^# Chef Name: #{@new_resource.name}\n/
+ case line.chomp
+ when "# Chef Name: #{@new_resource.name}"
cron_found = true
next
when CRON_PATTERN
@@ -125,12 +125,12 @@ class Chef
else
next if cron_found
end
- crontab << line
+ crontab << line
end
end
status = popen4("crontab -u #{@new_resource.user} -", :waitlast => true) do |pid, stdin, stdout, stderr|
- crontab.each { |line| stdin.puts "#{line}" }
+ crontab.each_line { |line| stdin.puts "#{line}" }
end
Chef::Log.info("Updated cron '#{@new_resource.name}'")
@new_resource.updated = true
@@ -140,11 +140,11 @@ class Chef
stdout.each { |line| crontab << line }
end
end
-
+
crontab << newcron
status = popen4("crontab -u #{@new_resource.user} -", :waitlast => true) do |pid, stdin, stdout, stderr|
- crontab.each { |line| stdin.puts "#{line}" }
+ crontab.each_line { |line| stdin.puts "#{line}" }
end
Chef::Log.info("Added cron '#{@new_resource.name}'")
@new_resource.updated = true
@@ -157,8 +157,8 @@ class Chef
cron_found = false
status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr|
stdout.each_line do |line|
- case line
- when /^# Chef Name: #{@new_resource.name}\n/
+ case line.chomp
+ when "# Chef Name: #{@new_resource.name}"
cron_found = true
next
when CRON_PATTERN
@@ -169,12 +169,12 @@ class Chef
else
next if cron_found
end
- crontab << line
+ crontab << line
end
end
status = popen4("crontab -u #{@new_resource.user} -", :waitlast => true) do |pid, stdin, stdout, stderr|
- crontab.each { |line| stdin.puts "#{line}" }
+ crontab.each_line { |line| stdin.puts "#{line}" }
end
Chef::Log.debug("Deleted cron '#{@new_resource.name}'")
@new_resource.updated = true
diff --git a/chef/lib/chef/provider/deploy/revision.rb b/chef/lib/chef/provider/deploy/revision.rb
index aea4901f6b..00a30515ba 100644
--- a/chef/lib/chef/provider/deploy/revision.rb
+++ b/chef/lib/chef/provider/deploy/revision.rb
@@ -50,11 +50,15 @@ class Chef
cache
end
+ def sorted_releases_from_filesystem
+ Dir.glob(new_resource.deploy_to + "/releases/*").sort_by { |d| ::File.ctime(d) }
+ end
+
def load_cache
begin
JSON.parse(Chef::FileCache.load("revision-deploys/#{new_resource.name}"))
rescue Chef::Exceptions::FileNotFound
- save_cache([])
+ sorted_releases_from_filesystem
end
end
diff --git a/chef/lib/chef/provider/erl_call.rb b/chef/lib/chef/provider/erl_call.rb
index 39b2dcfab6..7d29e24081 100644
--- a/chef/lib/chef/provider/erl_call.rb
+++ b/chef/lib/chef/provider/erl_call.rb
@@ -59,11 +59,11 @@ class Chef
Chef::Log.debug("Running erl_call[#{@new_resource.name}]")
Chef::Log.debug("erl_call[#{@new_resource.name}] command: #{command}")
Chef::Log.debug("erl_call[#{@new_resource.name}] code: #{@new_resource.code}")
- @new_resource.code.each { |line| stdin.puts "#{line.chomp!}" }
+ @new_resource.code.each_line { |line| stdin.puts "#{line.chomp!}" }
stdin.close
Chef::Log.info("Ran erl_call[#{@new_resource.name}] successfully")
Chef::Log.debug("erl_call[#{@new_resource.name}] output: ")
- stdout.each { |line| Chef::Log.debug("#{line}")}
+ stdout.each_line { |line| Chef::Log.debug("#{line}")}
end
end
diff --git a/chef/lib/chef/provider/file.rb b/chef/lib/chef/provider/file.rb
index 0bb2c4529b..969c7c9703 100644
--- a/chef/lib/chef/provider/file.rb
+++ b/chef/lib/chef/provider/file.rb
@@ -46,6 +46,7 @@ class Chef
def load_current_resource
@current_resource = Chef::Resource::File.new(@new_resource.name)
+ @new_resource.path.gsub!(/\\/, "/") # for Windows
@current_resource.path(@new_resource.path)
if ::File.exist?(@current_resource.path) && ::File.readable?(@current_resource.path)
cstats = ::File.stat(@current_resource.path)
diff --git a/chef/lib/chef/provider/group/dscl.rb b/chef/lib/chef/provider/group/dscl.rb
index 905a1deaaf..4462aa28a6 100644
--- a/chef/lib/chef/provider/group/dscl.rb
+++ b/chef/lib/chef/provider/group/dscl.rb
@@ -75,11 +75,13 @@ class Chef
def set_members
unless @new_resource.append
Chef::Log.debug("#{@new_resource}: removing group members #{@current_resource.members.join(' ')}") unless @current_resource.members.empty?
- safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembers") # clear guid list
- safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembership") # clear user list
+ safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembers ''") # clear guid list
+ safe_dscl("create /Groups/#{@new_resource.group_name} GroupMembership ''") # clear user list
+ end
+ unless @new_resource.members.empty?
+ Chef::Log.debug("#{@new_resource}: setting group members #{@new_resource.members.join(', ')}")
+ safe_dscl("append /Groups/#{@new_resource.group_name} GroupMembership #{@new_resource.members.join(' ')}")
end
- Chef::Log.debug("#{@new_resource}: setting group members #{@new_resource.members.join(', ')}") unless @new_resource.members.empty?
- safe_dscl("append /Groups/#{@new_resource.group_name} GroupMembership #{@new_resource.members.join(' ')}")
end
def load_current_resource
diff --git a/chef/lib/chef/provider/mdadm.rb b/chef/lib/chef/provider/mdadm.rb
index 6a22c289b4..6df655a186 100644
--- a/chef/lib/chef/provider/mdadm.rb
+++ b/chef/lib/chef/provider/mdadm.rb
@@ -26,10 +26,6 @@ class Chef
include Chef::Mixin::Command
- def initialize(node, new_resource)
- super(node, new_resource)
- end
-
def load_current_resource
@current_resource = Chef::Resource::Mdadm.new(@new_resource.name)
@current_resource.raid_device(@new_resource.raid_device)
diff --git a/chef/lib/chef/provider/mount/mount.rb b/chef/lib/chef/provider/mount/mount.rb
index 1669ddebb5..18bc8441bc 100644
--- a/chef/lib/chef/provider/mount/mount.rb
+++ b/chef/lib/chef/provider/mount/mount.rb
@@ -64,12 +64,16 @@ class Chef
# Check to see if there is a entry in /etc/fstab. Last entry for a volume wins.
enabled = false
- ::File.read("/etc/fstab").each do |line|
+ ::File.foreach("/etc/fstab") do |line|
case line
when /^[#\s]/
next
- when /^#{device_fstab_regex}\s+#{@new_resource.mount_point}/
+ when /^#{device_fstab_regex}\s+#{@new_resource.mount_point}\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/
enabled = true
+ @current_resource.fstype($1)
+ @current_resource.options($2)
+ @current_resource.dump($3.to_i)
+ @current_resource.pass($4.to_i)
Chef::Log.debug("Found mount #{device_fstab} to #{@new_resource.mount_point} in /etc/fstab")
when /^[\/\w]+\s+#{@new_resource.mount_point}/
enabled = false
@@ -119,13 +123,21 @@ class Chef
end
def enable_fs
- unless @current_resource.enabled
- ::File.open("/etc/fstab", "a") do |fstab|
- fstab.puts("#{device_fstab} #{@new_resource.mount_point} #{@new_resource.fstype} #{@new_resource.options.nil? ? "defaults" : @new_resource.options.join(",")} #{@new_resource.dump} #{@new_resource.pass}")
- Chef::Log.info("Enabled #{@new_resource.mount_point}")
+ if @current_resource.enabled
+ if @current_resource.fstype == @new_resource.fstype and
+ @current_resource.options == @new_resource.options and
+ @current_resource.dump == @new_resource.dump and
+ @current_resource.pass == @new_resource.pass
+ Chef::Log.debug("#{@new_resource.mount_point} is already enabled.")
+ return
end
- else
- Chef::Log.debug("#{@new_resource.mount_point} is already enabled.")
+ # The current options don't match what we have, so
+ # disable, then enable.
+ disable_fs
+ end
+ ::File.open("/etc/fstab", "a") do |fstab|
+ fstab.puts("#{device_fstab} #{@new_resource.mount_point} #{@new_resource.fstype} #{@new_resource.options.nil? ? "defaults" : @new_resource.options.join(",")} #{@new_resource.dump} #{@new_resource.pass}")
+ Chef::Log.info("Enabled #{@new_resource.mount_point}")
end
end
diff --git a/chef/lib/chef/provider/package.rb b/chef/lib/chef/provider/package.rb
index bf6dd4cf47..6f0ecbbd9d 100644
--- a/chef/lib/chef/provider/package.rb
+++ b/chef/lib/chef/provider/package.rb
@@ -120,7 +120,7 @@ class Chef
raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :purge"
end
- def preseed_package(name, version, preseed)
+ def preseed_package(name, version)
raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support pre-seeding package install/upgrade instructions - don't ask it to!"
end
diff --git a/chef/lib/chef/provider/package/freebsd.rb b/chef/lib/chef/provider/package/freebsd.rb
index bfe67c8b73..18dd38107c 100644
--- a/chef/lib/chef/provider/package/freebsd.rb
+++ b/chef/lib/chef/provider/package/freebsd.rb
@@ -7,9 +7,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -24,8 +24,13 @@ require 'chef/resource/package'
class Chef
class Provider
class Package
- class Freebsd < Chef::Provider::Package
-
+ class Freebsd < Chef::Provider::Package
+
+ def initialize(*args)
+ super
+ @current_resource = Chef::Resource::Package.new(@new_resource.name)
+ end
+
def current_installed_version
command = "pkg_info -E \"#{package_name}*\""
status = popen4(command) do |pid, stdin, stdout, stderr|
@@ -41,7 +46,7 @@ class Chef
end
nil
end
-
+
def port_path
case @new_resource.package_name
# When the package name starts with a '/' treat it as the full path to the ports directory
@@ -61,10 +66,10 @@ class Chef
end
end
end
- raise Chef::Exception::Package, "Could not find port with the name #{@new_resource.package_name}"
- end
+ raise Chef::Exceptions::Package, "Could not find port with the name #{@new_resource.package_name}"
+ end
end
-
+
def ports_makefile_variable_value(variable)
command = "cd #{port_path}; make -V #{variable}"
status = popen4(command) do |pid, stdin, stdout, stderr|
@@ -75,34 +80,33 @@ class Chef
end
nil
end
-
+
def ports_candidate_version
ports_makefile_variable_value("PORTVERSION")
end
-
+
def load_current_resource
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
-
+
@current_resource.version(current_installed_version)
Chef::Log.debug("Current version is #{@current_resource.version}") if @current_resource.version
-
+
@candidate_version = ports_candidate_version
Chef::Log.debug("Ports candidate version is #{@candidate_version}") if @candidate_version
-
+
@current_resource
end
-
+
def latest_link_name
ports_makefile_variable_value("LATEST_LINK")
end
-
+
# The name of the package (without the version number) as understood by pkg_add and pkg_info
def package_name
if ports_makefile_variable_value("PKGNAME") =~ /^(.+)-[^-]+$/
$1
else
- raise Chef::Exception::Package, "Unexpected form for PKGNAME variable in #{port_path}/Makefile"
+ raise Chef::Exceptions::Package, "Unexpected form for PKGNAME variable in #{port_path}/Makefile"
end
end
@@ -134,7 +138,7 @@ class Chef
end
end
end
-
+
def remove_package(name, version)
# a version is mandatory
if version
diff --git a/chef/lib/chef/provider/package/rubygems.rb b/chef/lib/chef/provider/package/rubygems.rb
index c6392d91a3..d9c80f2e33 100644
--- a/chef/lib/chef/provider/package/rubygems.rb
+++ b/chef/lib/chef/provider/package/rubygems.rb
@@ -27,9 +27,8 @@ class Chef
def gem_list_parse(line)
installed_versions = Array.new
- if line.match("^#{@new_resource.package_name} \\((.+?)\\)$")
- installed_versions = $1.split(/, /)
- installed_versions
+ if md = line.match(/^#{@new_resource.package_name} \((.+?)(?: [^\)\.]+)?\)$/)
+ md.captures.first.split(/, /)
else
nil
end
@@ -47,9 +46,8 @@ class Chef
# First, we need to look up whether we have the local gem installed or not
status = popen4("#{gem_binary_path} list --local #{@new_resource.package_name}") do |pid, stdin, stdout, stderr|
- stdout.each do |line|
- installed_versions = gem_list_parse(line)
- next unless installed_versions
+ stdout.each_line do |line|
+ next unless installed_versions = gem_list_parse(line)
# If the version we are asking for is installed, make that our current
# version. Otherwise, go ahead and use the highest one, which
# happens to come first in the array.
@@ -75,9 +73,8 @@ class Chef
return @candidate_version if @candidate_version
status = popen4("#{gem_binary_path} list --remote #{@new_resource.package_name}#{' --source=' + @new_resource.source if @new_resource.source}") do |pid, stdin, stdout, stderr|
- stdout.each do |line|
- installed_versions = gem_list_parse(line)
- next unless installed_versions
+ stdout.each_line do |line|
+ next unless installed_versions = gem_list_parse(line)
Chef::Log.debug("candidate_version: remote rubygem(s) available: #{installed_versions.inspect}")
unless installed_versions.empty?
@@ -97,7 +94,7 @@ class Chef
def install_package(name, version)
src = nil
if @new_resource.source
- src = " --source=#{@new_resource.source} --source=http://gems.rubyforge.org"
+ src = " --source=#{@new_resource.source} --source=http://rubygems.org"
end
run_command_with_systems_locale(
:command => "#{gem_binary_path} install #{name} -q --no-rdoc --no-ri -v \"#{version}\"#{src}#{opts}"
diff --git a/chef/lib/chef/provider/remote_directory.rb b/chef/lib/chef/provider/remote_directory.rb
index b5b65367fa..2816e54d51 100644
--- a/chef/lib/chef/provider/remote_directory.rb
+++ b/chef/lib/chef/provider/remote_directory.rb
@@ -26,6 +26,7 @@ require 'chef/platform'
require 'uri'
require 'tempfile'
require 'net/https'
+require 'set'
class Chef
class Provider
@@ -47,8 +48,22 @@ class Chef
Chef::Log.debug("Doing a remote recursive directory transfer for #{@new_resource}")
end
+ existing_files = Set.new Dir[::File.join(@new_resource.path, '**', '*')]
+
files_to_transfer.each do |remote_file_source|
fetch_remote_file(remote_file_source)
+ existing_files.delete(::File.join(@new_resource.path, remote_file_source))
+ end
+ if @new_resource.purge
+ existing_files.sort { |a,b| b <=> a }.each do |f|
+ if ::File.directory?(f)
+ Chef::Log.debug("Removing directory #{f}")
+ Dir::rmdir(f)
+ else
+ Chef::Log.debug("Deleting file #{f}")
+ ::File.delete(f)
+ end
+ end
end
end
diff --git a/chef/lib/chef/provider/remote_file.rb b/chef/lib/chef/provider/remote_file.rb
index 247549ed95..c8904c2941 100644
--- a/chef/lib/chef/provider/remote_file.rb
+++ b/chef/lib/chef/provider/remote_file.rb
@@ -45,42 +45,20 @@ class Chef
def do_remote_file(source, path)
retval = true
- if(@new_resource.checksum && @current_resource.checksum && @current_resource.checksum =~ /^#{@new_resource.checksum}/)
- Chef::Log.debug("File #{@new_resource} checksum matches, not updating")
+ if current_resource_matches_target_checksum?
+ Chef::Log.debug("File #{@new_resource} checksum matches target checksum (#{@new_resource.checksum}), not updating")
else
begin
- # The remote filehandle
- raw_file = get_from_uri(source) ||
- get_from_server(source, @current_resource.checksum) ||
- get_from_local_cookbook(source)
-
- # If the file exists
- Chef::Log.debug "#{@new_resource}: Checking for file existence of #{@new_resource.path}"
- if ::File.exists?(@new_resource.path)
- # And it matches the checksum of the raw file
- @new_resource.checksum(self.checksum(raw_file.path))
- Chef::Log.debug "#{@new_resource}: File exists at #{@new_resource.path}"
- Chef::Log.debug "#{@new_resource}: Target checksum: #{@current_resource.checksum}"
- Chef::Log.debug "#{@new_resource}: Source checksum: #{@new_resource.checksum}"
- if @new_resource.checksum != @current_resource.checksum
- # Updating target file, let's perform a backup!
- Chef::Log.debug "#{@new_resource}: checksum changed from #{@current_resource.checksum} to #{@new_resource.checksum}"
- Chef::Log.info "#{@new_resource}: Updating #{@new_resource.path}"
- backup @new_resource.path
+ source_file(source, @current_resource.checksum) do |raw_file|
+ if matches_current_checksum?(raw_file)
+ Chef::Log.debug "#{@new_resource}: Target and Source checksums are the same, taking no action"
+ else
+ backup_new_resource
+ Chef::Log.debug "copying remote file from origin #{raw_file.path} to destination #{@new_resource.path}"
FileUtils.cp raw_file.path, @new_resource.path
@new_resource.updated = true
- else
- Chef::Log.debug "#{@new_resource}: Target and Source checksums are the same, taking no action"
end
- else
- # We're creating a new file
- Chef::Log.info "#{@new_resource}: Creating #{@new_resource.path}"
- FileUtils.cp raw_file.path, @new_resource.path
- @new_resource.updated = true
end
-
- # We're done with the file, so make sure to close it if it was open.
- raw_file.close unless raw_file.closed?
rescue Net::HTTPRetriableError => e
if e.response.kind_of?(Net::HTTPNotModified)
Chef::Log.debug("File #{path} is unchanged")
@@ -89,38 +67,78 @@ class Chef
raise e
end
end
+
+ Chef::Log.debug "#{@new_resource} completed"
+ retval
end
-
+ enforce_ownership_and_permissions
+
+ retval
+ end
+
+ def enforce_ownership_and_permissions
set_owner if @new_resource.owner
set_group if @new_resource.group
set_mode if @new_resource.mode
- retval
end
- def get_from_uri(source)
- begin
- uri = URI.parse(source)
- if uri.absolute
- r = Chef::REST.new(source, nil, nil)
- Chef::Log.debug("Downloading from absolute URI: #{source}")
- r.get_rest(source, true).open
- end
- rescue URI::InvalidURIError
- nil
+ def current_resource_matches_target_checksum?
+ @new_resource.checksum && @current_resource.checksum && @current_resource.checksum =~ /^#{@new_resource.checksum}/
+ end
+
+ def matches_current_checksum?(candidate_file)
+ Chef::Log.debug "#{@new_resource}: Checking for file existence of #{@new_resource.path}"
+ if ::File.exists?(@new_resource.path)
+ Chef::Log.debug "#{@new_resource}: File exists at #{@new_resource.path}"
+ @new_resource.checksum(checksum(candidate_file.path))
+ Chef::Log.debug "#{@new_resource}: Target checksum: #{@current_resource.checksum}"
+ Chef::Log.debug "#{@new_resource}: Source checksum: #{@new_resource.checksum}"
+ @new_resource.checksum == @current_resource.checksum
+ else
+ Chef::Log.info "#{@new_resource}: Creating #{@new_resource.path}"
+ false
+ end
+ end
+
+ def backup_new_resource
+ if ::File.exists?(@new_resource.path)
+ Chef::Log.debug "#{@new_resource}: checksum changed from #{@current_resource.checksum} to #{@new_resource.checksum}"
+ Chef::Log.info "#{@new_resource}: Updating #{@new_resource.path}"
+ backup @new_resource.path
end
end
- def get_from_server(source, current_checksum)
- unless Chef::Config[:solo]
- r = Chef::REST.new(Chef::Config[:remotefile_url])
- url = generate_url(source, "files", :checksum => current_checksum)
- Chef::Log.debug("Downloading from server: #{url}")
- r.get_rest(url, true).open
+ def source_file(source, current_checksum, &block)
+ if absolute_uri?(source)
+ fetch_from_uri(source, &block)
+ elsif !Chef::Config[:solo]
+ fetch_from_chef_server(source, current_checksum, &block)
+ else
+ fetch_from_local_cookbook(source, &block)
end
end
- def get_from_local_cookbook(source)
+ private
+
+ def absolute_uri?(source)
+ URI.parse(source).absolute?
+ rescue URI::InvalidURIError
+ false
+ end
+
+ def fetch_from_uri(source)
+ Chef::Log.debug("Downloading from absolute URI: #{source}")
+ Chef::REST.new(source, nil, nil).fetch(source) { |tmp_file| yield tmp_file }
+ end
+
+ def fetch_from_chef_server(source, current_checksum)
+ url = generate_url(source, "files", :checksum => current_checksum)
+ Chef::Log.debug("Downloading #{@new_resource} from server: #{url}")
+ Chef::REST.new(Chef::Config[:remotefile_url]).fetch(url) { |tmp_file| yield tmp_file }
+ end
+
+ def fetch_from_local_cookbook(source)
if Chef::Config[:solo]
cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
filename = find_preferred_file(
@@ -132,7 +150,12 @@ class Chef
@node[:platform_version]
)
Chef::Log.debug("Using local file for remote_file:#{filename}")
- ::File.open(filename)
+ begin
+ file = ::File.open(filename)
+ yield file
+ ensure
+ file.close
+ end
end
end
diff --git a/chef/lib/chef/provider/script.rb b/chef/lib/chef/provider/script.rb
index 6fd4a367a0..8a7bbb69f5 100644
--- a/chef/lib/chef/provider/script.rb
+++ b/chef/lib/chef/provider/script.rb
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,21 +22,23 @@ require 'chef/provider/execute'
class Chef
class Provider
class Script < Chef::Provider::Execute
-
- def action_run
+
+ def action_run
tf = Tempfile.new("chef-script")
tf.puts(@new_resource.code)
tf.close
-
+
fr = Chef::Resource::File.new(tf.path, nil, @node)
fr.owner(@new_resource.user)
fr.group(@new_resource.group)
fr.run_action(:create)
-
- @new_resource.command("#{@new_resource.interpreter} #{tf.path}")
+
+ @new_resource.command("#{@new_resource.interpreter} #{tf.path}")
super
+ ensure
+ tf && tf.close!
end
-
+
end
end
end \ No newline at end of file
diff --git a/chef/lib/chef/provider/service/windows.rb b/chef/lib/chef/provider/service/windows.rb
new file mode 100644
index 0000000000..69920c08eb
--- /dev/null
+++ b/chef/lib/chef/provider/service/windows.rb
@@ -0,0 +1,129 @@
+#
+# Author:: Nuo Yan <nuo@opscode.com>
+# Copyright:: Copyright (c) 2010 Opscode, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/service/init'
+
+class Chef::Provider::Service::Windows < Chef::Provider::Service::Init
+
+ def initialize(node, new_resource, collection=nil, definitions=nil, cookbook_loader=nil)
+ super(node, new_resource, collection, definitions, cookbook_loader)
+ @init_command = "sc"
+ end
+
+ def load_current_resource
+ @current_resource = Chef::Resource::Service.new(@new_resource.name)
+ @current_resource.service_name(@new_resource.service_name)
+ status = IO.popen("#{@init_command} query #{@new_resource.service_name}").entries
+ raise Chef::Exceptions::Exec, "Service #{@new_resource.service_name} does not exist.\n#{status.join}\n" if status[0].include?("FAILED 1060")
+
+ begin
+ started = status[3].include?("4")
+ @current_resource.running started
+
+ start_type = IO.popen("#{@init_command} qc #{@new_resource.service_name}").entries[4]
+ @current_resource.enabled(start_type.include?('2') || start_type.include?('3') ? true : false)
+
+ Chef::Log.debug "#{@new_resource}: running: #{@current_resource.running}"
+ rescue StandardError
+ raise Chef::Exceptions::Exec
+ rescue Chef::Exceptions::Exec
+ Chef::Log.debug "Failed to determine the current status of the service, assuming it is not running"
+ @current_resource.running false
+ nil
+ end
+ @current_resource
+ end
+
+ def start_service
+ begin
+ result = if @new_resource.start_command
+ Chef::Log.debug "starting service using the given start_command"
+ IO.popen(@new_resource.start_command).readlines
+ else
+ IO.popen("#{@init_command} start #{@new_resource.service_name}").readlines
+ end
+ Chef::Log.debug result.join
+ result[3].include?('4') || result.include?('2') ? true : false
+ rescue
+ Chef::Log.debug "Failed to start service #{@new_resource.service_name}"
+ false
+ end
+ end
+
+ def stop_service
+ begin
+ Chef::Log.debug "stopping service using the given stop_command"
+ result = if @new_resource.stop_command
+ IO.popen(@new_resource.stop_command).readlines
+ else
+ IO.popen("#{@init_command} stop #{@new_resource.service_name}").readlines
+ end
+ Chef::Log.debug result.join
+ result[3].include?('1')
+ rescue
+ Chef::Log.debug "Failed to stop service #{@new_resource.service_name}"
+ false
+ end
+ end
+
+ def restart_service
+ begin
+ if @new_resource.restart_command
+ Chef::Log.debug "restarting service using the given restart_command"
+ result = IO.popen(@new_resource.restart_command).readlines
+ Chef::Log.debug result.join
+ else
+ Chef::Log.debug IO.popen("#{@init_command} stop #{@new_resource.service_name}").readlines.join
+ sleep 1
+ result = IO.popen("#{@init_command} start #{@new_resource.service_name}").readlines
+ Chef::Log.debug result.join
+ end
+ result[3].include?('4') || result.include?('2')
+ rescue
+ Chef::Log.debug "Failed to restart service #{@new_resource.service_name}"
+ false
+ end
+ end
+
+ def enable_service()
+ begin
+ Chef::Log.debug result = IO.popen("#{@init_command} config #{@new_resource.service_name} start= #{determine_startup_type}").readlines.join
+ result.include?('SUCCESS')
+ rescue
+ Chef::Log.debug "Failed to enable service #{@new_resource.service_name}"
+ false
+ end
+ end
+
+ def disable_service()
+ begin
+ Chef::Log.debug result = IO.popen("#{@init_command} config #{@new_resource.service_name} start= disabled").readlines.join
+ result.include?('SUCCESS')
+ rescue
+ Chef::Log.debug "Failed to disable service #{@new_resource.service_name}"
+ false
+ end
+ end
+
+ private
+
+ def determine_startup_type
+ {:automatic => 'auto', :mannual => 'demand'}[@new_resource.startup_type]
+ end
+
+end \ No newline at end of file
diff --git a/chef/lib/chef/provider/subversion.rb b/chef/lib/chef/provider/subversion.rb
index 826dd9b35d..c58805e4d3 100644
--- a/chef/lib/chef/provider/subversion.rb
+++ b/chef/lib/chef/provider/subversion.rb
@@ -86,7 +86,7 @@ class Chef
if @new_resource.revision =~ /^\d+$/
@new_resource.revision
else
- command = scm(:info, @new_resource.repository, authentication, "-r#{@new_resource.revision}")
+ command = scm(:info, @new_resource.repository, @new_resource.svn_info_args, authentication, "-r#{@new_resource.revision}")
status, svn_info, error_message = output_of_command(command, run_options)
handle_command_failures(status, "STDOUT: #{svn_info}\nSTDERR: #{error_message}")
extract_revision_info(svn_info)
diff --git a/chef/lib/chef/provider/template.rb b/chef/lib/chef/provider/template.rb
index 89a51718a8..47a5ed6ca7 100644
--- a/chef/lib/chef/provider/template.rb
+++ b/chef/lib/chef/provider/template.rb
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -27,63 +27,64 @@ require 'tempfile'
class Chef
class Provider
-
+
class Template < Chef::Provider::File
-
+
include Chef::Mixin::Checksum
include Chef::Mixin::Template
include Chef::Mixin::FindPreferredFile
-
+
def action_create
raw_template_file = nil
-
+
Chef::Log.debug("looking for template #{@new_resource.source} in cookbook #{cookbook_name.inspect}")
-
+
cache_file_name = "cookbooks/#{cookbook_name}/templates/default/#{@new_resource.source}"
template_cache_name = "#{cookbook_name}_#{@new_resource.source}"
-
+
if @new_resource.local
cache_file_name = @new_resource.source
elsif Chef::Config[:solo]
cache_file_name = solo_cache_file_name
else
raw_template_file = fetch_template_via_rest(cache_file_name, template_cache_name)
- end
-
+ end
+
if template_updated?
Chef::Log.debug("Updating template for #{@new_resource} in the cache")
Chef::FileCache.move_to(raw_template_file.path, cache_file_name)
end
- template_file = render_with_context(cache_file_name)
+ render_with_context(cache_file_name) do |template_file|
- update = false
-
- if ::File.exists?(@new_resource.path)
- @new_resource.checksum(self.checksum(template_file.path))
- if @new_resource.checksum != @current_resource.checksum
- Chef::Log.debug("#{@new_resource} changed from #{@current_resource.checksum} to #{@new_resource.checksum}")
- Chef::Log.info("Updating #{@new_resource} at #{@new_resource.path}")
+ update = false
+
+ if ::File.exists?(@new_resource.path)
+ @new_resource.checksum(checksum(template_file.path))
+ if @new_resource.checksum != @current_resource.checksum
+ Chef::Log.debug("#{@new_resource} changed from #{@current_resource.checksum} to #{@new_resource.checksum}")
+ Chef::Log.info("Updating #{@new_resource} at #{@new_resource.path}")
+ update = true
+ end
+ else
+ Chef::Log.info("Creating #{@new_resource} at #{@new_resource.path}")
update = true
end
- else
- Chef::Log.info("Creating #{@new_resource} at #{@new_resource.path}")
- update = true
- end
-
- if update
- backup
- FileUtils.cp(template_file.path, @new_resource.path)
- @new_resource.updated = true
- else
- Chef::Log.debug("#{@new_resource} is unchanged")
+
+ if update
+ backup
+ FileUtils.mv(template_file.path, @new_resource.path)
+ @new_resource.updated = true
+ else
+ Chef::Log.debug("#{@new_resource} is unchanged")
+ end
end
-
+
set_owner if @new_resource.owner != nil
set_group if @new_resource.group != nil
set_mode if @new_resource.mode != nil
end
-
+
def action_create_if_missing
if ::File.exists?(@new_resource.path)
Chef::Log.debug("Template #{@new_resource} exists, taking no action.")
@@ -91,32 +92,32 @@ class Chef
action_create
end
end
-
+
private
-
+
def template_updated
@template_updated = true
end
-
+
def template_not_updated
@template_updated = false
end
-
+
def template_updated?
@template_updated
end
-
+
def cookbook_name
@cookbook_name = (@new_resource.cookbook || @new_resource.cookbook_name)
end
-
- def render_with_context(cache_file_name)
+
+ def render_with_context(cache_file_name, &block)
context = {}
context.merge!(@new_resource.variables)
context[:node] = @node
- render_template(Chef::FileCache.load(cache_file_name), context)
+ render_template(Chef::FileCache.load(cache_file_name), context, &block)
end
-
+
def solo_cache_file_name
filename = find_preferred_file(
cookbook_name,
@@ -129,32 +130,32 @@ class Chef
Chef::Log.debug("Using local file for template:#{filename}")
Pathname.new(filename).relative_path_from(Pathname.new(Chef::Config[:file_cache_path])).to_s
end
-
+
def fetch_template_via_rest(cache_file_name, template_cache_name)
if @node.run_state[:template_cache].has_key?(template_cache_name)
Chef::Log.debug("I have already fetched the template for #{@new_resource} once this run, not checking again.")
template_not_updated
return false
end
-
+
r = Chef::REST.new(Chef::Config[:template_url])
-
+
current_checksum = nil
-
+
if Chef::FileCache.has_key?(cache_file_name)
current_checksum = self.checksum(Chef::FileCache.load(cache_file_name, false))
else
Chef::Log.debug("Template #{@new_resource} is not in the template cache")
end
-
+
template_url = generate_url(
- @new_resource.source,
+ @new_resource.source,
"templates",
{
:checksum => current_checksum
}
)
-
+
begin
raw_template_file = r.get_rest(template_url, true)
template_updated
@@ -165,13 +166,13 @@ class Chef
raise e
end
end
-
+
# We have checked the cache for this template this run
@node.run_state[:template_cache][template_cache_name] = true
-
+
raw_template_file
end
-
+
end
end
end
diff --git a/chef/lib/chef/providers.rb b/chef/lib/chef/providers.rb
new file mode 100644
index 0000000000..02168b2dcd
--- /dev/null
+++ b/chef/lib/chef/providers.rb
@@ -0,0 +1,80 @@
+#
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/breakpoint'
+require 'chef/provider/cron'
+require 'chef/provider/deploy'
+require 'chef/provider/directory'
+require 'chef/provider/erl_call'
+require 'chef/provider/execute'
+require 'chef/provider/file'
+require 'chef/provider/git'
+require 'chef/provider/group'
+require 'chef/provider/http_request'
+require 'chef/provider/ifconfig'
+require 'chef/provider/link'
+require 'chef/provider/log'
+require 'chef/provider/mdadm'
+require 'chef/provider/mount'
+require 'chef/provider/package'
+require 'chef/provider/remote_directory'
+require 'chef/provider/remote_file'
+require 'chef/provider/route'
+require 'chef/provider/ruby_block'
+require 'chef/provider/script'
+require 'chef/provider/service'
+require 'chef/provider/subversion'
+require 'chef/provider/template'
+require 'chef/provider/user'
+
+require 'chef/provider/package/apt'
+require 'chef/provider/package/dpkg'
+require 'chef/provider/package/easy_install'
+require 'chef/provider/package/freebsd'
+require 'chef/provider/package/macports'
+require 'chef/provider/package/pacman'
+require 'chef/provider/package/portage'
+require 'chef/provider/package/rpm'
+require 'chef/provider/package/rubygems'
+require 'chef/provider/package/yum'
+require 'chef/provider/package/zypper'
+
+require 'chef/provider/service/arch'
+require 'chef/provider/service/debian'
+require 'chef/provider/service/freebsd'
+require 'chef/provider/service/gentoo'
+require 'chef/provider/service/init'
+require 'chef/provider/service/redhat'
+require 'chef/provider/service/simple'
+require 'chef/provider/service/upstart'
+require 'chef/provider/service/windows'
+
+require 'chef/provider/user/dscl'
+require 'chef/provider/user/pw'
+require 'chef/provider/user/useradd'
+
+require 'chef/provider/group/dscl'
+require 'chef/provider/group/gpasswd'
+require 'chef/provider/group/groupadd'
+require 'chef/provider/group/pw'
+require 'chef/provider/group/usermod'
+
+require 'chef/provider/mount/mount'
+
+require 'chef/provider/deploy/revision'
+require 'chef/provider/deploy/timestamped'
diff --git a/chef/lib/chef/recipe.rb b/chef/lib/chef/recipe.rb
index d9a2362889..da6d0c0a05 100644
--- a/chef/lib/chef/recipe.rb
+++ b/chef/lib/chef/recipe.rb
@@ -17,15 +17,13 @@
# limitations under the License.
#
-require 'chef/resource'
-Dir[File.join(File.dirname(__FILE__), 'resource/**/*.rb')].sort.each { |lib| require lib }
+
+require 'chef/mixin/recipe_definition_dsl_core'
require 'chef/mixin/from_file'
require 'chef/mixin/language'
require 'chef/mixin/language_include_recipe'
-require 'chef/mixin/recipe_definition_dsl_core'
require 'chef/resource_collection'
require 'chef/cookbook_loader'
-require 'chef/rest'
class Chef
class Recipe
diff --git a/chef/lib/chef/resource.rb b/chef/lib/chef/resource.rb
index 89f4513576..fc95abd595 100644
--- a/chef/lib/chef/resource.rb
+++ b/chef/lib/chef/resource.rb
@@ -80,7 +80,7 @@ class Chef
prior_resource = @collection.lookup(self.to_s)
Chef::Log.debug("Setting #{self.to_s} to the state of the prior #{self.to_s}")
prior_resource.instance_variables.each do |iv|
- unless iv == "@source_line" || iv == "@action"
+ unless iv.to_sym == :@source_line || iv.to_sym == :@action
self.instance_variable_set(iv, prior_resource.instance_variable_get(iv))
end
end
@@ -162,10 +162,18 @@ class Chef
if args.size > 1
notifies_helper(*args)
else
- resources_array = *args
- resources_array.each do |resource|
- resource.each do |key, value|
- notifies_helper(value[0], key, value[1])
+ # This syntax is so weird. surely people will just give us one hash?
+ notifications = args.flatten
+ notifications.each do |resources_notifications|
+ begin
+ resources_notifications.each do |resource, notification|
+ Chef::Log.error "resource KV: `#{resource.inspect}' => `#{notification.inspect}'"
+ notifies_helper(notification[0], resource, notification[1])
+ end
+ rescue NoMethodError
+ Chef::Log.fatal("encountered NME processing resource #{resources_notifications.inspect}")
+ Chef::Log.fatal("incoming args: #{args.inspect}")
+ raise
end
end
end
@@ -193,7 +201,11 @@ class Chef
end
def is(*args)
- return *args
+ if args.size == 1
+ args.first
+ else
+ return *args
+ end
end
def to_s
@@ -218,7 +230,9 @@ class Chef
def to_hash
instance_vars = Hash.new
self.instance_variables.each do |iv|
- instance_vars[iv.sub(/^@/,'').to_sym] = self.instance_variable_get(iv) unless iv == "@collection"
+ iv = iv.to_s
+ next if iv == "@collection"
+ instance_vars[iv.sub(/^@/,'').to_sym] = self.instance_variable_get(iv)
end
instance_vars
end
diff --git a/chef/lib/chef/resource/cron.rb b/chef/lib/chef/resource/cron.rb
index 2da76b62f8..206bf2c540 100644
--- a/chef/lib/chef/resource/cron.rb
+++ b/chef/lib/chef/resource/cron.rb
@@ -47,7 +47,7 @@ class Chef
converted_arg = arg
end
begin
- if Integer(arg) > 59 then raise RangeError end
+ if integerize(arg) > 59 then raise RangeError end
rescue ArgumentError
end
set_or_return(
@@ -64,7 +64,7 @@ class Chef
converted_arg = arg
end
begin
- if Integer(arg) > 23 then raise RangeError end
+ if integerize(arg) > 23 then raise RangeError end
rescue ArgumentError
end
set_or_return(
@@ -81,7 +81,7 @@ class Chef
converted_arg = arg
end
begin
- if Integer(arg) > 31 then raise RangeError end
+ if integerize(arg) > 31 then raise RangeError end
rescue ArgumentError
end
set_or_return(
@@ -98,7 +98,7 @@ class Chef
converted_arg = arg
end
begin
- if Integer(arg) > 12 then raise RangeError end
+ if integerize(arg) > 12 then raise RangeError end
rescue ArgumentError
end
set_or_return(
@@ -115,7 +115,7 @@ class Chef
converted_arg = arg
end
begin
- if Integer(arg) > 7 then raise RangeError end
+ if integerize(arg) > 7 then raise RangeError end
rescue ArgumentError
end
set_or_return(
@@ -172,6 +172,15 @@ class Chef
:kind_of => String
)
end
+
+ private
+
+ # On Ruby 1.8, Kernel#Integer will happily do this for you. On 1.9, no.
+ def integerize(integerish)
+ Integer(integerish)
+ rescue TypeError
+ 0
+ end
end
end
end
diff --git a/chef/lib/chef/resource/deploy.rb b/chef/lib/chef/resource/deploy.rb
index 327f9425c8..a513b16411 100644
--- a/chef/lib/chef/resource/deploy.rb
+++ b/chef/lib/chef/resource/deploy.rb
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -38,20 +38,20 @@ require "chef/resource/scm"
class Chef
class Resource
-
+
# Deploy: Deploy apps from a source control repository.
#
# Callbacks:
# Callbacks can be a block or a string. If given a block, the code
# is evaluated as an embedded recipe, and run at the specified
# point in the deploy process. If given a string, the string is taken as
- # a path to a callback file/recipe. Paths are evaluated relative to the
+ # a path to a callback file/recipe. Paths are evaluated relative to the
# release directory. Callback files can contain chef code (resources, etc.)
#
class Deploy < Chef::Resource
-
+
provider_base Chef::Provider::Deploy
-
+
def initialize(name, collection=nil, node=nil)
super(name, collection, node)
@resource_name = :deploy
@@ -74,26 +74,26 @@ class Chef
@provider = Chef::Provider::Deploy::Timestamped
@allowed_actions.push(:force_deploy, :deploy, :rollback)
end
-
+
# where the checked out/cloned code goes
def destination
@destination ||= shared_path + "/#{@repository_cache}"
end
-
+
# where shared stuff goes, i.e., logs, tmp, etc. goes here
def shared_path
@shared_path ||= @deploy_to + "/shared"
end
-
+
# where the deployed version of your code goes
def current_path
@current_path ||= @deploy_to + "/current"
end
-
+
def depth
@shallow_clone ? "5" : nil
end
-
+
# note: deploy_to is your application "meta-root."
def deploy_to(arg=nil)
set_or_return(
@@ -102,7 +102,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def repo(arg=nil)
set_or_return(
:repo,
@@ -111,7 +111,7 @@ class Chef
)
end
alias :repository :repo
-
+
def remote(arg=nil)
set_or_return(
:remote,
@@ -119,7 +119,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def role(arg=nil)
set_or_return(
:role,
@@ -127,7 +127,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def restart_command(arg=nil, &block)
arg ||= block
set_or_return(
@@ -137,7 +137,7 @@ class Chef
)
end
alias :restart :restart_command
-
+
def migrate(arg=nil)
set_or_return(
:migrate,
@@ -145,7 +145,7 @@ class Chef
:kind_of => [ TrueClass, FalseClass ]
)
end
-
+
def migration_command(arg=nil)
set_or_return(
:migration_command,
@@ -153,7 +153,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def user(arg=nil)
set_or_return(
:user,
@@ -161,15 +161,15 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def group(arg=nil)
set_or_return(
:group,
arg,
:kind_of => [ String ]
)
- end
-
+ end
+
def enable_submodules(arg=nil)
set_or_return(
:enable_submodules,
@@ -177,7 +177,7 @@ class Chef
:kind_of => [ TrueClass, FalseClass ]
)
end
-
+
def shallow_clone(arg=nil)
set_or_return(
:shallow_clone,
@@ -193,7 +193,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def copy_exclude(arg=nil)
set_or_return(
:copy_exclude,
@@ -201,7 +201,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def revision(arg=nil)
set_or_return(
:revision,
@@ -210,7 +210,7 @@ class Chef
)
end
alias :branch :revision
-
+
def git_ssh_wrapper(arg=nil)
set_or_return(
:git_ssh_wrapper,
@@ -219,7 +219,7 @@ class Chef
)
end
alias :ssh_wrapper :git_ssh_wrapper
-
+
def svn_username(arg=nil)
set_or_return(
:svn_username,
@@ -227,7 +227,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def svn_password(arg=nil)
set_or_return(
:svn_password,
@@ -235,7 +235,7 @@ class Chef
:kind_of => [ String ]
)
end
-
+
def svn_arguments(arg=nil)
set_or_return(
:svn_arguments,
@@ -243,7 +243,14 @@ class Chef
:kind_of => [ String ]
)
end
-
+
+ def svn_info_args(arg=nil)
+ set_or_return(
+ :svn_arguments,
+ arg,
+ :kind_of => [ String ])
+ end
+
def scm_provider(arg=nil)
klass = if arg.kind_of?(String) || arg.kind_of?(Symbol)
lookup_provider_constant(arg)
@@ -256,7 +263,7 @@ class Chef
:kind_of => [ Class ]
)
end
-
+
def svn_force_export(arg=nil)
set_or_return(
:svn_force_export,
@@ -264,7 +271,7 @@ class Chef
:kind_of => [ TrueClass, FalseClass ]
)
end
-
+
def environment(arg=nil)
if arg.is_a?(String)
Chef::Log.info "Setting RAILS_ENV, RACK_ENV, and MERB_ENV to `#{arg}'"
@@ -277,8 +284,8 @@ class Chef
:kind_of => [ Hash ]
)
end
-
- # An array of paths, relative to your app's root, to be purged from a
+
+ # An array of paths, relative to your app's root, to be purged from a
# SCM clone/checkout before symlinking. Use this to get rid of files and
# directories you want to be shared between releases.
# Default: ["log", "tmp/pids", "public/system"]
@@ -289,12 +296,12 @@ class Chef
:kind_of => Array
)
end
-
+
# An array of paths, relative to your app's root, where you expect dirs to
# exist before symlinking. This runs after #purge_before_symlink, so you
# can use this to recreate dirs that you had previously purged.
- # For example, if you plan to use a shared directory for pids, and you
- # want it to be located in $APP_ROOT/tmp/pids, you could purge tmp,
+ # For example, if you plan to use a shared directory for pids, and you
+ # want it to be located in $APP_ROOT/tmp/pids, you could purge tmp,
# then specify tmp here so that the tmp directory will exist when you
# symlink the pids directory in to the current release.
# Default: ["tmp", "public", "config"]
@@ -305,10 +312,10 @@ class Chef
:kind_of => Array
)
end
-
+
# A Hash of shared/dir/path => release/dir/path. This attribute determines
# which files and dirs in the shared directory get symlinked to the current
- # release directory, and where they go. If you have a directory
+ # release directory, and where they go. If you have a directory
# $shared/pids that you would like to symlink as $current_release/tmp/pids
# you specify it as "pids" => "tmp/pids"
# Default {"system" => "public/system", "pids" => "tmp/pids", "log" => "log"}
@@ -319,8 +326,8 @@ class Chef
:kind_of => Hash
)
end
-
- # A Hash of shared/dir/path => release/dir/path. This attribute determines
+
+ # A Hash of shared/dir/path => release/dir/path. This attribute determines
# which files in the shared directory get symlinked to the current release
# directory and where they go. Unlike map_shared_files, these are symlinked
# *before* any migration is run.
@@ -334,31 +341,31 @@ class Chef
:kind_of => Hash
)
end
-
+
# Callback fires before migration is run.
def before_migrate(arg=nil, &block)
arg ||= block
set_or_return(:before_migrate, arg, :kind_of => [Proc, String])
end
-
+
# Callback fires before symlinking
def before_symlink(arg=nil, &block)
arg ||= block
set_or_return(:before_symlink, arg, :kind_of => [Proc, String])
end
-
+
# Callback fires before restart
def before_restart(arg=nil, &block)
arg ||= block
set_or_return(:before_restart, arg, :kind_of => [Proc, String])
end
-
+
# Callback fires after restart
def after_restart(arg=nil, &block)
arg ||= block
set_or_return(:after_restart, arg, :kind_of => [Proc, String])
end
-
+
end
end
end
diff --git a/chef/lib/chef/resource/execute.rb b/chef/lib/chef/resource/execute.rb
index 3f2ba07547..46b7287d6a 100644
--- a/chef/lib/chef/resource/execute.rb
+++ b/chef/lib/chef/resource/execute.rb
@@ -100,7 +100,7 @@ class Chef
set_or_return(
:returns,
arg,
- :kind_of => [ Integer ]
+ :kind_of => [ Integer, Array ]
)
end
diff --git a/chef/lib/chef/resource/mount.rb b/chef/lib/chef/resource/mount.rb
index affedf333c..56dcfedee9 100644
--- a/chef/lib/chef/resource/mount.rb
+++ b/chef/lib/chef/resource/mount.rb
@@ -28,7 +28,7 @@ class Chef
@mount_point = name
@device = nil
@device_type = :device
- @fstype = nil
+ @fstype = "auto"
@options = ["defaults"]
@dump = 0
@pass = 2
diff --git a/chef/lib/chef/resource/remote_directory.rb b/chef/lib/chef/resource/remote_directory.rb
index 9742df5540..1920c086de 100644
--- a/chef/lib/chef/resource/remote_directory.rb
+++ b/chef/lib/chef/resource/remote_directory.rb
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -21,7 +21,7 @@ require 'chef/resource/directory'
class Chef
class Resource
class RemoteDirectory < Chef::Resource::Directory
-
+
def initialize(name, collection=nil, node=nil)
super(name, collection, node)
@resource_name = :remote_directory
@@ -30,6 +30,7 @@ class Chef
@delete = false
@action = :create
@recursive = true
+ @purge = false
@files_backup = 5
@files_owner = nil
@files_group = nil
@@ -37,7 +38,7 @@ class Chef
@allowed_actions.push(:create, :delete)
@cookbook = nil
end
-
+
def source(args=nil)
set_or_return(
:source,
@@ -45,7 +46,7 @@ class Chef
:kind_of => String
)
end
-
+
def files_backup(arg=nil)
set_or_return(
:files_backup,
@@ -53,7 +54,15 @@ class Chef
:kind_of => [ Integer, FalseClass ]
)
end
-
+
+ def purge(arg=nil)
+ set_or_return(
+ :purge,
+ arg,
+ :kind_of => [ TrueClass, FalseClass ]
+ )
+ end
+
def files_group(arg=nil)
set_or_return(
:files_group,
@@ -61,7 +70,7 @@ class Chef
:regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
)
end
-
+
def files_mode(arg=nil)
set_or_return(
:files_mode,
@@ -69,7 +78,7 @@ class Chef
:regex => /^\d{3,4}$/
)
end
-
+
def files_owner(arg=nil)
set_or_return(
:files_owner,
@@ -77,7 +86,7 @@ class Chef
:regex => [ /^([a-z]|[A-Z]|[0-9]|_|-)+$/, /^\d+$/ ]
)
end
-
+
def cookbook(args=nil)
set_or_return(
:cookbook,
@@ -85,7 +94,7 @@ class Chef
:kind_of => String
)
end
-
+
end
end
end
diff --git a/chef/lib/chef/resource/scm.rb b/chef/lib/chef/resource/scm.rb
index d444d7f6d6..215fb59d06 100644
--- a/chef/lib/chef/resource/scm.rb
+++ b/chef/lib/chef/resource/scm.rb
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,7 +22,7 @@ require 'chef/resource'
class Chef
class Resource
class Scm < Chef::Resource
-
+
def initialize(name, collection=nil, node=nil)
super(name, collection, node)
@destination = name
@@ -34,7 +34,7 @@ class Chef
@depth = nil
@allowed_actions.push(:checkout, :export, :sync, :diff, :log)
end
-
+
def destination(arg=nil)
set_or_return(
:destination,
@@ -42,7 +42,7 @@ class Chef
:kind_of => String
)
end
-
+
def repository(arg=nil)
set_or_return(
:repository,
@@ -50,7 +50,7 @@ class Chef
:kind_of => String
)
end
-
+
def revision(arg=nil)
set_or_return(
:revision,
@@ -58,7 +58,7 @@ class Chef
:kind_of => String
)
end
-
+
def user(arg=nil)
set_or_return(
:user,
@@ -66,7 +66,7 @@ class Chef
:kind_of => [String, Integer]
)
end
-
+
def group(arg=nil)
set_or_return(
:group,
@@ -74,7 +74,7 @@ class Chef
:kind_of => [String, Integer]
)
end
-
+
def svn_username(arg=nil)
set_or_return(
:svn_username,
@@ -82,7 +82,7 @@ class Chef
:kind_of => String
)
end
-
+
def svn_password(arg=nil)
set_or_return(
:svn_password,
@@ -90,7 +90,7 @@ class Chef
:kind_of => String
)
end
-
+
def svn_arguments(arg=nil)
set_or_return(
:svn_arguments,
@@ -98,7 +98,14 @@ class Chef
:kind_of => String
)
end
-
+
+ def svn_info_args(arg=nil)
+ set_or_return(
+ :svn_arguments,
+ arg,
+ :kind_of => String)
+ end
+
# Capistrano and git-deploy use ``shallow clone''
def depth(arg=nil)
set_or_return(
@@ -107,7 +114,7 @@ class Chef
:kind_of => Integer
)
end
-
+
def enable_submodules(arg=nil)
set_or_return(
:enable_submodules,
@@ -115,7 +122,7 @@ class Chef
:kind_of => [TrueClass, FalseClass]
)
end
-
+
def remote(arg=nil)
set_or_return(
:remote,
@@ -123,7 +130,7 @@ class Chef
:kind_of => String
)
end
-
+
def ssh_wrapper(arg=nil)
set_or_return(
:ssh_wrapper,
@@ -131,7 +138,7 @@ class Chef
:kind_of => String
)
end
-
+
end
end
end \ No newline at end of file
diff --git a/chef/lib/chef/resource/service.rb b/chef/lib/chef/resource/service.rb
index 1ca5ba6e71..f32d3c062b 100644
--- a/chef/lib/chef/resource/service.rb
+++ b/chef/lib/chef/resource/service.rb
@@ -35,6 +35,7 @@ class Chef
@restart_command = nil
@reload_command = nil
@action = "nothing"
+ @startup_type = :automatic
@supports = { :restart => false, :reload => false, :status => false }
@allowed_actions.push(:enable, :disable, :start, :stop, :restart, :reload)
end
@@ -127,7 +128,15 @@ class Chef
@supports
end
end
-
+
+ # This attribute applies for Windows only.
+ def startup_type(arg=nil)
+ set_or_return(
+ :startup_type,
+ arg,
+ :equal_to => [:automatic, :mannual]
+ )
+ end
end
end
diff --git a/chef/lib/chef/resources.rb b/chef/lib/chef/resources.rb
new file mode 100644
index 0000000000..8577c1b843
--- /dev/null
+++ b/chef/lib/chef/resources.rb
@@ -0,0 +1,60 @@
+#
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/resource/apt_package'
+require 'chef/resource/bash'
+require 'chef/resource/breakpoint'
+require 'chef/resource/cron'
+require 'chef/resource/csh'
+require 'chef/resource/deploy'
+require 'chef/resource/deploy_revision'
+require 'chef/resource/directory'
+require 'chef/resource/dpkg_package'
+require 'chef/resource/easy_install_package'
+require 'chef/resource/erl_call'
+require 'chef/resource/execute'
+require 'chef/resource/file'
+require 'chef/resource/freebsd_package'
+require 'chef/resource/gem_package'
+require 'chef/resource/git'
+require 'chef/resource/group'
+require 'chef/resource/http_request'
+require 'chef/resource/ifconfig'
+require 'chef/resource/link'
+require 'chef/resource/log'
+require 'chef/resource/macports_package'
+require 'chef/resource/mdadm'
+require 'chef/resource/mount'
+require 'chef/resource/package'
+require 'chef/resource/pacman_package'
+require 'chef/resource/perl'
+require 'chef/resource/portage_package'
+require 'chef/resource/python'
+require 'chef/resource/remote_directory'
+require 'chef/resource/remote_file'
+require 'chef/resource/route'
+require 'chef/resource/ruby'
+require 'chef/resource/ruby_block'
+require 'chef/resource/scm'
+require 'chef/resource/script'
+require 'chef/resource/service'
+require 'chef/resource/subversion'
+require 'chef/resource/template'
+require 'chef/resource/timestamped_deploy'
+require 'chef/resource/user'
+require 'chef/resource/yum_package'
diff --git a/chef/lib/chef/rest.rb b/chef/lib/chef/rest.rb
index 9e214a1ddb..d8723ae71a 100644
--- a/chef/lib/chef/rest.rb
+++ b/chef/lib/chef/rest.rb
@@ -10,9 +10,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,67 +20,58 @@
# limitations under the License.
#
-require 'chef/mixin/params_validate'
require 'net/https'
require 'uri'
require 'json'
require 'tempfile'
-require 'singleton'
-require 'mixlib/authentication/signedheaderauth'
require 'chef/api_client'
-
-include Mixlib::Authentication::SignedHeaderAuth
+require 'chef/rest/auth_credentials'
+require 'chef/rest/rest_request'
class Chef
class REST
+ attr_reader :auth_credentials
+ attr_accessor :url, :cookies, :sign_on_redirect, :redirect_limit
- class CookieJar < Hash
- include Singleton
- end
-
- attr_accessor :url, :cookies, :client_name, :signing_key, :signing_key_filename, :sign_on_redirect, :sign_request
-
def initialize(url, client_name=Chef::Config[:node_name], signing_key_filename=Chef::Config[:client_key], options={})
@url = url
@cookies = CookieJar.instance
- @client_name = client_name
@default_headers = options[:headers] || {}
- if signing_key_filename
- @signing_key_filename = signing_key_filename
- @signing_key = load_signing_key(signing_key_filename)
- @sign_request = true
- else
- @signing_key = nil
- @sign_request = false
- end
- @sign_on_redirect = true
+ @auth_credentials = AuthCredentials.new(client_name, signing_key_filename)
+ @sign_on_redirect, @sign_request = true, true
+ @redirects_followed = 0
+ @redirect_limit = 10
end
- def load_signing_key(key)
- begin
- IO.read(key)
- rescue StandardError=>se
- Chef::Log.error "Failed to read the private key #{key}: #{se.inspect}, #{se.backtrace}"
- raise Chef::Exceptions::PrivateKeyMissing, "I cannot read #{key}, which you told me to use to sign requests!"
- end
+ def signing_key_filename
+ @auth_credentials.key_file
+ end
+
+ def client_name
+ @auth_credentials.client_name
+ end
+
+ def signing_key
+ @auth_credentials.raw_key
end
-
- # Register the client
- def register(name=Chef::Config[:node_name], destination=Chef::Config[:client_key])
- raise Chef::Exceptions::CannotWritePrivateKey, "I cannot write your private key to #{destination} - check permissions?" if (File.exists?(destination) && !File.writable?(destination))
+ # Register the client
+ def register(name=Chef::Config[:node_name], destination=Chef::Config[:client_key])
+ if (File.exists?(destination) && !File.writable?(destination))
+ raise Chef::Exceptions::CannotWritePrivateKey, "I cannot write your private key to #{destination} - check permissions?"
+ end
nc = Chef::ApiClient.new
nc.name(name)
catch(:done) do
- retries = Chef::Config[:client_registration_retries] || 5
+ retries = config[:client_registration_retries] || 5
0.upto(retries) do |n|
begin
response = nc.save(true, true)
Chef::Log.debug("Registration response: #{response.inspect}")
raise Chef::Exceptions::CannotWritePrivateKey, "The response from the server did not include a private key!" unless response.has_key?("private_key")
# Write out the private key
- file = File.open(destination, File::WRONLY|File::EXCL|File::CREAT, 0600)
+ file = ::File.open(destination, File::WRONLY|File::EXCL|File::CREAT, 0600)
file.print(response["private_key"])
file.close
throw :done
@@ -100,27 +91,40 @@ class Chef
#
# === Parameters
# path:: The path to GET
- # raw:: Whether you want the raw body returned, or JSON inflated. Defaults
+ # raw:: Whether you want the raw body returned, or JSON inflated. Defaults
# to JSON inflated.
def get_rest(path, raw=false, headers={})
- run_request(:GET, create_url(path), headers, false, 10, raw)
- end
-
+ if raw
+ streaming_request(create_url(path), headers)
+ else
+ api_request(:GET, create_url(path), headers)
+ end
+ end
+
# Send an HTTP DELETE request to the path
- def delete_rest(path, headers={})
- run_request(:DELETE, create_url(path), headers)
- end
-
- # Send an HTTP POST request to the path
+ def delete_rest(path, headers={})
+ api_request(:DELETE, create_url(path), headers)
+ end
+
+ # Send an HTTP POST request to the path
def post_rest(path, json, headers={})
- run_request(:POST, create_url(path), headers, json)
- end
-
+ api_request(:POST, create_url(path), headers, json)
+ end
+
# Send an HTTP PUT request to the path
def put_rest(path, json, headers={})
- run_request(:PUT, create_url(path), headers, json)
+ api_request(:PUT, create_url(path), headers, json)
end
-
+
+ # Streams a download to a tempfile, then yields the tempfile to a block.
+ # After the download, the tempfile will be closed and unlinked.
+ # If you rename the tempfile, it will not be deleted.
+ # Beware that if the server streams infinite content, this method will
+ # stream it until you run out of disk space.
+ def fetch(path, headers={})
+ streaming_request(create_url(path), headers) {|tmp_file| yield tmp_file }
+ end
+
def create_url(path)
if path =~ /^(http|https):\/\//
URI.parse(path)
@@ -128,150 +132,39 @@ class Chef
URI.parse("#{@url}/#{path}")
end
end
-
- def sign_request(http_method, path, private_key, user_id, body = "", host="localhost")
- #body = "" if body == false
- timestamp = Time.now.utc.iso8601
- sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(
- :http_method=>http_method,
- :path => path,
- :body=>body,
- :user_id=>user_id,
- :timestamp=>timestamp)
- signed = sign_obj.sign(private_key).merge({:host => host})
- signed.inject({}){|memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1];memo}
+
+ def sign_requests?
+ auth_credentials.sign_requests? && @sign_request
end
-
+
# Actually run an HTTP request. First argument is the HTTP method,
# which should be one of :GET, :PUT, :POST or :DELETE. Next is the
# URL, then an object to include in the body (which will be converted with
- # .to_json) and finally, the limit of HTTP Redirects to follow (10).
+ # .to_json). The limit argument is unused, it is present for backwards
+ # compatibility. Configure the redirect limit with #redirect_limit=
+ # instead.
#
# Typically, you won't use this method -- instead, you'll use one of
# the helper methods (get_rest, post_rest, etc.)
#
# Will return the body of the response on success.
- def run_request(method, url, headers={}, data=false, limit=10, raw=false)
-
- http_retry_delay = Chef::Config[:http_retry_delay]
- http_retry_count = Chef::Config[:http_retry_count]
-
- raise ArgumentError, 'HTTP redirect too deep' if limit == 0
-
- http = Net::HTTP.new(url.host, url.port)
- if url.scheme == "https"
- http.use_ssl = true
- if Chef::Config[:ssl_verify_mode] == :verify_none
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
- elsif Chef::Config[:ssl_verify_mode] == :verify_peer
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
- end
- if Chef::Config[:ssl_ca_path] and File.exists?(Chef::Config[:ssl_ca_path])
- http.ca_path = Chef::Config[:ssl_ca_path]
- elsif Chef::Config[:ssl_ca_file] and File.exists?(Chef::Config[:ssl_ca_file])
- http.ca_file = Chef::Config[:ssl_ca_file]
- end
- if Chef::Config[:ssl_client_cert] && File.exists?(Chef::Config[:ssl_client_cert])
- http.cert = OpenSSL::X509::Certificate.new(File.read(Chef::Config[:ssl_client_cert]))
- http.key = OpenSSL::PKey::RSA.new(File.read(Chef::Config[:ssl_client_key]))
- end
- end
-
- http.read_timeout = Chef::Config[:rest_timeout]
-
- headers = @default_headers.merge(headers)
-
- unless raw
- headers = headers.merge({
- 'Accept' => "application/json",
- })
- end
+ def run_request(method, url, headers={}, data=false, limit=nil, raw=false)
+ json_body = data ? data.to_json : nil
+ headers = build_headers(method, url, headers, json_body, raw)
- headers['X-Chef-Version'] = ::Chef::VERSION
-
- if @cookies.has_key?("#{url.host}:#{url.port}")
- headers['Cookie'] = @cookies["#{url.host}:#{url.port}"]
- end
-
- json_body = data ? data.to_json : nil
-
- if @sign_request
- raise ArgumentError, "Cannot sign the request without a client name, check that :node_name is assigned" if @client_name.nil?
- Chef::Log.debug("Signing the request as #{@client_name}")
- if json_body
- headers.merge!(sign_request(method, url.path, OpenSSL::PKey::RSA.new(@signing_key), @client_name, json_body, "#{url.host}:#{url.port}"))
- else
- headers.merge!(sign_request(method, url.path, OpenSSL::PKey::RSA.new(@signing_key), @client_name, "", "#{url.host}:#{url.port}"))
- end
- end
-
- req = nil
- case method
- when :GET
- req_path = "#{url.path}"
- req_path << "?#{url.query}" if url.query
- req = Net::HTTP::Get.new(req_path, headers)
- when :POST
- headers["Content-Type"] = 'application/json' if data
- req_path = "#{url.path}"
- req_path << "?#{url.query}" if url.query
- req = Net::HTTP::Post.new(req_path, headers)
- req.body = json_body if json_body
- when :PUT
- headers["Content-Type"] = 'application/json' if data
- req_path = "#{url.path}"
- req_path << "?#{url.query}" if url.query
- req = Net::HTTP::Put.new(req_path, headers)
- req.body = json_body if json_body
- when :DELETE
- req_path = "#{url.path}"
- req_path << "?#{url.query}" if url.query
- req = Net::HTTP::Delete.new(req_path, headers)
- else
- raise ArgumentError, "You must provide :GET, :PUT, :POST or :DELETE as the method"
- end
-
- Chef::Log.debug("Sending HTTP Request via #{req.method} to #{url.host}:#{url.port}#{req.path}")
-
- # Optionally handle HTTP Basic Authentication
- req.basic_auth(url.user, url.password) if url.user
-
- res = nil
tf = nil
- http_attempts = 0
- begin
- http_attempts += 1
-
- res = http.request(req) do |response|
+ retriable_rest_request(method, url, json_body, headers) do |rest_request|
+
+ res = rest_request.call do |response|
if raw
- tf = Tempfile.new("chef-rest")
- # Stolen from http://www.ruby-forum.com/topic/166423
- # Kudos to _why!
- size, total = 0, response.header['Content-Length'].to_i
- response.read_body do |chunk|
- tf.write(chunk)
- size += chunk.size
- if size == 0
- Chef::Log.debug("#{req.path} done (0 length file)")
- elsif total == 0
- Chef::Log.debug("#{req.path} (zero content length)")
- else
- Chef::Log.debug("#{req.path}" + " %d%% done (%d of %d)" % [(size * 100) / total, size, total])
- end
- end
- tf.close
- tf
+ tf = stream_to_tempfile(url, response)
else
response.read_body
end
- response
end
-
+
if res.kind_of?(Net::HTTPSuccess)
- if res['set-cookie']
- @cookies["#{url.host}:#{url.port}"] = res['set-cookie']
- end
if res['content-type'] =~ /json/
response_body = res.body.chomp
JSON.parse(response_body)
@@ -283,37 +176,114 @@ class Chef
end
end
elsif res.kind_of?(Net::HTTPFound) or res.kind_of?(Net::HTTPMovedPermanently)
- if res['set-cookie']
- @cookies["#{url.host}:#{url.port}"] = res['set-cookie']
- end
- @sign_request = false if @sign_on_redirect == false
- run_request(:GET, create_url(res['location']), {}, false, limit - 1, raw)
+ follow_redirect {run_request(:GET, create_url(res['location']), {}, false, nil, raw)}
else
if res['content-type'] =~ /json/
exception = JSON.parse(res.body)
- Chef::Log.debug("HTTP Request Returned #{res.code} #{res.message}: #{exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"]}")
+ msg = "HTTP Request Returned #{res.code} #{res.message}: "
+ msg << (exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"].to_s)
+ Chef::Log.warn(msg)
end
res.error!
end
-
+ end
+ end
+
+ # Similar to #run_request but only supports JSON APIs. File Download not supported.
+ def api_request(method, url, headers={}, data=false)
+ json_body = data ? data.to_json : nil
+ headers = build_headers(method, url, headers, json_body)
+
+ retriable_rest_request(method, url, json_body, headers) do |rest_request|
+ response = rest_request.call {|r| r.read_body}
+
+ if response.kind_of?(Net::HTTPSuccess)
+ if response['content-type'] =~ /json/
+ JSON.parse(response.body.chomp)
+ else
+ Chef::Log.warn("Expected JSON response, but got content-type '#{response['content-type']}'")
+ response.body
+ end
+ elsif redirect_location = redirected_to(response)
+ follow_redirect {api_request(:GET, create_url(redirect_location))}
+ else
+ if response['content-type'] =~ /json/
+ exception = JSON.parse(response.body)
+ msg = "HTTP Request Returned #{response.code} #{response.message}: "
+ msg << (exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"].to_s)
+ Chef::Log.warn(msg)
+ end
+ response.error!
+ end
+ end
+ end
+
+ # similar to #run_request but only supports streaming downloads.
+ # Only supports GET, doesn't speak JSON
+ # Streams the response body to a tempfile. If a block is given, it's
+ # passed to the tempfile, which means that the tempfile will automatically
+ # be unlinked after the block is executed.
+ # If no block is given, the tempfile is returned, which means it's up to
+ # you to unlink the tempfile when you're done with it.
+ def streaming_request(url, headers, &block)
+ headers = build_headers(:GET, url, headers, nil, true)
+ retriable_rest_request(:GET, url, nil, headers) do |rest_request|
+ tempfile = nil
+ response = rest_request.call do |r|
+ if block_given? && r.kind_of?(Net::HTTPSuccess)
+ begin
+ tempfile = stream_to_tempfile(url, r, &block)
+ yield tempfile
+ ensure
+ tempfile.close!
+ end
+ else
+ tempfile = stream_to_tempfile(url, r)
+ end
+ end
+ if response.kind_of?(Net::HTTPSuccess)
+ tempfile
+ elsif redirect_location = redirected_to(response)
+ # TODO: test tempfile unlinked when following redirects.
+ tempfile && tempfile.close!
+ follow_redirect {streaming_request(create_url(redirect_location), {}, &block)}
+ else
+ tempfile && tempfile.close!
+ response.error!
+ end
+ end
+ end
+
+ def retriable_rest_request(method, url, req_body, headers)
+ rest_request = Chef::REST::RESTRequest.new(method, url, req_body, headers)
+
+ Chef::Log.debug("Sending HTTP Request via #{method} to #{url.host}:#{url.port}#{rest_request.path}")
+
+ http_attempts = 0
+
+ begin
+ http_attempts += 1
+
+ res = yield rest_request
+
rescue Errno::ECONNREFUSED
if http_retry_count - http_attempts + 1 > 0
- Chef::Log.error("Connection refused connecting to #{url.host}:#{url.port} for #{req.path}, retry #{http_attempts}/#{http_retry_count}")
+ Chef::Log.error("Connection refused connecting to #{url.host}:#{url.port} for #{rest_request.path}, retry #{http_attempts}/#{http_retry_count}")
sleep(http_retry_delay)
retry
end
- raise Errno::ECONNREFUSED, "Connection refused connecting to #{url.host}:#{url.port} for #{req.path}, giving up"
+ raise Errno::ECONNREFUSED, "Connection refused connecting to #{url.host}:#{url.port} for #{rest_request.path}, giving up"
rescue Timeout::Error
if http_retry_count - http_attempts + 1 > 0
- Chef::Log.error("Timeout connecting to #{url.host}:#{url.port} for #{req.path}, retry #{http_attempts}/#{http_retry_count}")
+ Chef::Log.error("Timeout connecting to #{url.host}:#{url.port} for #{rest_request.path}, retry #{http_attempts}/#{http_retry_count}")
sleep(http_retry_delay)
retry
end
- raise Timeout::Error, "Timeout connecting to #{url.host}:#{url.port} for #{req.path}, giving up"
+ raise Timeout::Error, "Timeout connecting to #{url.host}:#{url.port} for #{rest_request.path}, giving up"
rescue Net::HTTPServerException
if res.kind_of?(Net::HTTPForbidden)
if http_retry_count - http_attempts + 1 > 0
- Chef::Log.error("Received 403 Forbidden against #{url.host}:#{url.port} for #{req.path}, retry #{http_attempts}/#{http_retry_count}")
+ Chef::Log.error("Received 403 Forbidden against #{url.host}:#{url.port} for #{rest_request.path}, retry #{http_attempts}/#{http_retry_count}")
sleep(http_retry_delay)
retry
end
@@ -321,6 +291,81 @@ class Chef
raise
end
end
-
+
+ def authentication_headers(method, url, json_body=nil)
+ request_params = {:http_method => method, :path => url.path, :body => json_body, :host => "#{url.host}:#{url.port}"}
+ request_params[:body] ||= ""
+ auth_credentials.signature_headers(request_params)
+ end
+
+ def http_retry_delay
+ config[:http_retry_delay]
+ end
+
+ def http_retry_count
+ config[:http_retry_count]
+ end
+
+ def config
+ Chef::Config
+ end
+
+ def follow_redirect
+ raise Chef::Exceptions::RedirectLimitExceeded if @redirects_followed >= redirect_limit
+ @redirects_followed += 1
+ Chef::Log.debug("Following redirect #{@redirects_followed}/#{redirect_limit}")
+ if @sign_on_redirect
+ yield
+ else
+ @sign_request = false
+ yield
+ end
+ ensure
+ @redirects_followed = 0
+ @sign_request = true
+ end
+
+ private
+
+ def redirected_to(response)
+ if response.kind_of?(Net::HTTPFound) || response.kind_of?(Net::HTTPMovedPermanently)
+ response['location']
+ else
+ nil
+ end
+ end
+
+ def build_headers(method, url, headers={}, json_body=false, raw=false)
+ headers = @default_headers.merge(headers)
+ headers['Accept'] = "application/json" unless raw
+ headers["Content-Type"] = 'application/json' if json_body
+ headers.merge!(authentication_headers(method, url, json_body)) if sign_requests?
+ headers
+ end
+
+ def stream_to_tempfile(url, response)
+ tf = Tempfile.open("chef-rest")
+ Chef::Log.debug("Streaming download from #{url.to_s} to tempfile #{tf.path}")
+ # Stolen from http://www.ruby-forum.com/topic/166423
+ # Kudos to _why!
+ size, total = 0, response.header['Content-Length'].to_i
+ response.read_body do |chunk|
+ tf.write(chunk)
+ size += chunk.size
+ if size == 0
+ Chef::Log.debug("#{url.path} done (0 length file)")
+ elsif total == 0
+ Chef::Log.debug("#{url.path} (zero content length or no Content-Length header)")
+ else
+ Chef::Log.debug("#{url.path}" + " %d%% done (%d of %d)" % [(size * 100) / total, size, total])
+ end
+ end
+ tf.close
+ tf
+ rescue Exception
+ tf.close!
+ raise
+ end
+
end
end
diff --git a/chef/lib/chef/rest/auth_credentials.rb b/chef/lib/chef/rest/auth_credentials.rb
new file mode 100644
index 0000000000..9e2aaf8a24
--- /dev/null
+++ b/chef/lib/chef/rest/auth_credentials.rb
@@ -0,0 +1,78 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Thom May (<thom@clearairturbulence.org>)
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Author:: Christopher Brown (<cb@opscode.com>)
+# Author:: Christopher Walters (<cw@opscode.com>)
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+require 'chef/exceptions'
+require 'mixlib/authentication/signedheaderauth'
+
+class Chef
+ class REST
+ class AuthCredentials
+ attr_reader :key_file, :client_name, :key, :raw_key
+
+ def initialize(client_name=nil, key_file=nil)
+ @client_name, @key_file = client_name, key_file
+ load_signing_key if sign_requests?
+ end
+
+ def sign_requests?
+ !!key_file
+ end
+
+ def signature_headers(request_params={})
+ raise ArgumentError, "Cannot sign the request without a client name, check that :node_name is assigned" if client_name.nil?
+ Chef::Log.debug("Signing the request as #{client_name}")
+
+ # params_in = {:http_method => :GET, :path => "/clients", :body => "", :host => "localhost"}
+ request_params = request_params.dup
+ request_params[:timestamp] = Time.now.utc.iso8601
+ request_params[:user_id] = client_name
+ host = request_params.delete(:host) || "localhost"
+
+ sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(request_params)
+ signed = sign_obj.sign(key).merge({:host => host})
+ signed.inject({}){|memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1];memo}
+ end
+
+ private
+
+ def load_signing_key
+ begin
+ @raw_key = IO.read(key_file)
+ rescue SystemCallError, IOError => e
+ Chef::Log.fatal "Failed to read the private key #{key_file}: #{e.inspect}, #{e.backtrace}"
+ raise Chef::Exceptions::PrivateKeyMissing, "I cannot read #{key_file}, which you told me to use to sign requests!"
+ end
+ assert_valid_key_format!(@raw_key)
+ @key = OpenSSL::PKey::RSA.new(@raw_key)
+ end
+
+ def assert_valid_key_format!(raw_key)
+ unless (raw_key =~ /\A-----BEGIN RSA PRIVATE KEY-----$/) && (raw_key =~ /^-----END RSA PRIVATE KEY-----\Z/)
+ msg = "The file #{key_file} does not contain a correctly formatted private key.\n"
+ msg << "The key file should begin with '-----BEGIN RSA PRIVATE KEY-----' and end with '-----END RSA PRIVATE KEY-----'"
+ raise Chef::Exceptions::InvalidPrivateKey, msg
+ end
+ end
+
+ end
+ end
+end
diff --git a/chef/lib/chef/application/server.rb b/chef/lib/chef/rest/cookie_jar.rb
index a42f61dc1e..e3137708a2 100644
--- a/chef/lib/chef/application/server.rb
+++ b/chef/lib/chef/rest/cookie_jar.rb
@@ -1,19 +1,31 @@
#
-# Author:: AJ Christensen (<aj@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Thom May (<thom@clearairturbulence.org>)
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Author:: Christopher Brown (<cb@opscode.com>)
+# Author:: Christopher Walters (<cw@opscode.com>)
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+#
+require 'singleton'
-require 'chef/application'
-
+class Chef
+ class REST
+ class CookieJar < Hash
+ include Singleton
+ end
+ end
+end
diff --git a/chef/lib/chef/rest/rest_request.rb b/chef/lib/chef/rest/rest_request.rb
new file mode 100644
index 0000000000..66e2a11233
--- /dev/null
+++ b/chef/lib/chef/rest/rest_request.rb
@@ -0,0 +1,151 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Thom May (<thom@clearairturbulence.org>)
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Author:: Christopher Brown (<cb@opscode.com>)
+# Author:: Christopher Walters (<cw@opscode.com>)
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+require 'uri'
+require 'net/http'
+require 'chef/rest/cookie_jar'
+
+class Chef
+ class REST
+ class RESTRequest
+ attr_reader :method, :url, :headers, :http_client, :http_request
+
+ def initialize(method, url, req_body, base_headers={})
+ @method, @url = method, url
+ @request_body = nil
+ @cookies = CookieJar.instance
+ configure_http_client
+ build_headers(base_headers)
+ configure_http_request(req_body)
+ end
+
+ def host
+ @url.host
+ end
+
+ def port
+ @url.port
+ end
+
+ def query
+ @url.query
+ end
+
+ def path
+ @url.path.empty? ? "/" : @url.path
+ end
+
+ def call
+ http_client.request(http_request) do |response|
+ store_cookie(response)
+ yield response if block_given?
+ response
+ end
+ end
+
+ def config
+ Chef::Config
+ end
+
+ private
+
+ def store_cookie(response)
+ if response['set-cookie']
+ @cookies["#{host}:#{port}"] = response['set-cookie']
+ end
+ end
+
+ def build_headers(headers)
+ @headers = headers.dup
+ # TODO: need to set accept somewhere else
+ # headers.merge!('Accept' => "application/json") unless raw
+ @headers['X-Chef-Version'] = ::Chef::VERSION
+
+ if @cookies.has_key?("#{host}:#{port}")
+ @headers['Cookie'] = @cookies["#{host}:#{port}"]
+ end
+ end
+
+ def configure_http_client
+ @http_client = Net::HTTP.new(host, port)
+ if url.scheme == "https"
+ @http_client.use_ssl = true
+ if config[:ssl_verify_mode] == :verify_none
+ @http_client.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ elsif config[:ssl_verify_mode] == :verify_peer
+ @http_client.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ end
+ if config[:ssl_ca_path]
+ unless ::File.exist?(config[:ssl_ca_path])
+ raise Chef::Exceptions::ConfigurationError, "The configured ssl_ca_path #{config[:ssl_ca_path]} does not exist"
+ end
+ @http_client.ca_path = config[:ssl_ca_path]
+ elsif config[:ssl_ca_file]
+ unless ::File.exist?(config[:ssl_ca_file])
+ raise Chef::Exceptions::ConfigurationError, "The configured ssl_ca_file #{config[:ssl_ca_file]} does not exist"
+ end
+ @http_client.ca_file = config[:ssl_ca_file]
+ end
+ if (config[:ssl_client_cert] || config[:ssl_client_key])
+ unless (config[:ssl_client_cert] && config[:ssl_client_key])
+ raise Chef::Exceptions::ConfigurationError, "You must configure ssl_client_cert and ssl_client_key together"
+ end
+ unless ::File.exists?(config[:ssl_client_cert])
+ raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_cert #{config[:ssl_client_cert]} does not exist"
+ end
+ unless ::File.exists?(config[:ssl_client_key])
+ raise Chef::Exceptions::ConfigurationError, "The configured ssl_client_key #{config[:ssl_client_key]} does not exist"
+ end
+ @http_client.cert = OpenSSL::X509::Certificate.new(::File.read(config[:ssl_client_cert]))
+ @http_client.key = OpenSSL::PKey::RSA.new(::File.read(config[:ssl_client_key]))
+ end
+ end
+
+ @http_client.read_timeout = config[:rest_timeout]
+ end
+
+
+ def configure_http_request(request_body=nil)
+ req_path = "#{path}"
+ req_path << "?#{query}" if query
+
+ @http_request = case method.to_s.downcase
+ when "get"
+ Net::HTTP::Get.new(req_path, headers)
+ when "post"
+ Net::HTTP::Post.new(req_path, headers)
+ when "put"
+ Net::HTTP::Put.new(req_path, headers)
+ when "delete"
+ Net::HTTP::Delete.new(req_path, headers)
+ else
+ raise ArgumentError, "You must provide :GET, :PUT, :POST or :DELETE as the method"
+ end
+
+ @http_request.body = request_body if (request_body && @http_request.request_body_permitted?)
+ # Optionally handle HTTP Basic Authentication
+ @http_request.basic_auth(url.user, url.password) if url.user
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/chef/lib/chef/role.rb b/chef/lib/chef/role.rb
index 64ae7609e9..75707477f3 100644
--- a/chef/lib/chef/role.rb
+++ b/chef/lib/chef/role.rb
@@ -8,9 +8,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -28,19 +28,19 @@ require 'extlib'
require 'json'
class Chef
- class Role
-
+ class Role
+
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
include Chef::IndexQueue::Indexable
-
+
DESIGN_DOCUMENT = {
"version" => 6,
"language" => "javascript",
"views" => {
"all" => {
"map" => <<-EOJS
- function(doc) {
+ function(doc) {
if (doc.chef_type == "role") {
emit(doc.name, doc);
}
@@ -49,7 +49,7 @@ class Chef
},
"all_id" => {
"map" => <<-EOJS
- function(doc) {
+ function(doc) {
if (doc.chef_type == "role") {
emit(doc.name, doc.name);
}
@@ -61,14 +61,14 @@ class Chef
attr_accessor :couchdb_rev, :couchdb
attr_reader :couchdb_id
-
+
# Create a new Chef::Role object.
def initialize(couchdb=nil)
- @name = ''
- @description = ''
+ @name = ''
+ @description = ''
@default_attributes = Mash.new
@override_attributes = Mash.new
- @run_list = Chef::RunList.new
+ @run_list = Chef::RunList.new
@couchdb_rev = nil
@couchdb_id = nil
@couchdb = couchdb || Chef::CouchDB.new
@@ -87,7 +87,7 @@ class Chef
Chef::REST.new(Chef::Config[:chef_server_url])
end
- def name(arg=nil)
+ def name(arg=nil)
set_or_return(
:name,
arg,
@@ -95,7 +95,7 @@ class Chef
)
end
- def description(arg=nil)
+ def description(arg=nil)
set_or_return(
:description,
arg,
@@ -113,7 +113,7 @@ class Chef
# Chef::Log.warn "Chef::Role#recipes method is deprecated. Please use Chef::Role#run_list"
# run_list(*args)
# end
-
+
def default_attributes(arg=nil)
set_or_return(
:default_attributes,
@@ -144,11 +144,11 @@ class Chef
result
end
- # Serialize this object as a hash
+ # Serialize this object as a hash
def to_json(*a)
to_hash.to_json(*a)
end
-
+
# Create a Chef::Role from JSON
def self.json_create(o)
role = new
@@ -164,15 +164,15 @@ class Chef
role.couchdb_rev = o["_rev"] if o.has_key?("_rev")
role.index_id = role.couchdb_id
role.couchdb_id = o["_id"] if o.has_key?("_id")
- role
+ role
end
-
+
# List all the Chef::Role objects in the CouchDB. If inflate is set to true, you will get
# the full list of all Roles, fully inflated.
def self.cdb_list(inflate=false, couchdb=nil)
rs = (couchdb || Chef::CouchDB.new).list("roles", inflate)
lookup = (inflate ? "value" : "key")
- rs["rows"].collect { |r| r[lookup] }
+ rs["rows"].collect { |r| r[lookup] }
end
# Get the list of all roles from the API.
@@ -187,12 +187,12 @@ class Chef
chef_server_rest.get_rest("roles")
end
end
-
+
# Load a role by name from CouchDB
def self.cdb_load(name, couchdb=nil)
(couchdb || Chef::CouchDB.new).load("role", name)
end
-
+
# Load a role by name from the API
def self.load(name)
chef_server_rest.get_rest("roles/#{name}")
@@ -205,44 +205,36 @@ class Chef
nil
end
end
-
+
# Remove this role from the CouchDB
def cdb_destroy
couchdb.delete("role", @name, couchdb_rev)
- if Chef::Config[:couchdb_version] == 0.9
- rs = couchdb.get_view("nodes", "by_run_list", :startkey => "role[#{@name}]", :endkey => "role[#{@name}]", :include_docs => true)
- rs["rows"].each do |row|
- node = row["doc"]
- node.run_list.remove("role[#{@name}]")
- node.cdb_save
- end
- else
- Chef::Node.cdb_list.each do |node|
- n = Chef::Node.cdb_load(node)
- n.run_list.remove("role[#{@name}]")
- n.cdb_save
- end
+ rs = couchdb.get_view("nodes", "by_run_list", :startkey => "role[#{@name}]", :endkey => "role[#{@name}]", :include_docs => true)
+ rs["rows"].each do |row|
+ node = row["doc"]
+ node.run_list.remove("role[#{@name}]")
+ node.cdb_save
end
end
-
+
# Remove this role via the REST API
def destroy
chef_server_rest.delete_rest("roles/#{@name}")
-
+
Chef::Node.list.each do |node|
n = Chef::Node.load(node[0])
n.run_list.remove("role[#{@name}]")
n.save
end
-
+
end
-
+
# Save this role to the CouchDB
def cdb_save
self.couchdb_rev = couchdb.store("role", @name, self)["rev"]
end
-
+
# Save this role via the REST API
def save
begin
@@ -253,18 +245,18 @@ class Chef
end
self
end
-
+
# Create the role via the REST API
def create
chef_server_rest.post_rest("roles", self)
self
- end
-
+ end
+
# Set up our CouchDB design document
def self.create_design_document(couchdb=nil)
(couchdb || Chef::CouchDB.new).create_design_document("roles", DESIGN_DOCUMENT)
end
-
+
# As a string
def to_s
"role[#{@name}]"
@@ -289,14 +281,14 @@ class Chef
# Sync all the json roles with couchdb from disk
def self.sync_from_disk_to_couchdb
Dir[File.join(Chef::Config[:role_path], "*.json")].each do |role_file|
- short_name = File.basename(role_file, ".json")
+ short_name = File.basename(role_file, ".json")
Chef::Log.warn("Loading #{short_name}")
r = Chef::Role.from_disk(short_name, "json")
begin
couch_role = Chef::Role.cdb_load(short_name)
r.couchdb_rev = couch_role.couchdb_rev
Chef::Log.debug("Replacing role #{short_name} with data from #{role_file}")
- rescue Chef::Exceptions::CouchDBNotFound
+ rescue Chef::Exceptions::CouchDBNotFound
Chef::Log.debug("Creating role #{short_name} with data from #{role_file}")
end
r.cdb_save
diff --git a/chef/lib/chef/streaming_cookbook_uploader.rb b/chef/lib/chef/streaming_cookbook_uploader.rb
index f7e8b23ff9..1e1bb8f4dc 100644
--- a/chef/lib/chef/streaming_cookbook_uploader.rb
+++ b/chef/lib/chef/streaming_cookbook_uploader.rb
@@ -1,9 +1,15 @@
+# inspired by/cargo-culted from http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
+# On Apr 6, 2010, at 3:00 PM, Stanislav Vitvitskiy wrote:
+#
+# It's free to use / modify / distribute. No need to mention anything. Just copy/paste and use.
+#
+# Regards,
+# Stan
+
require 'net/http'
require 'mixlib/authentication/signedheaderauth'
require 'openssl'
-# inspired by/cargo-culted from http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
-# TODO: confirm that code is public domain
class Chef
class StreamingCookbookUploader
diff --git a/chef/lib/chef/tasks/chef_repo.rake b/chef/lib/chef/tasks/chef_repo.rake
index 58b1319d33..b22d84b25f 100644
--- a/chef/lib/chef/tasks/chef_repo.rake
+++ b/chef/lib/chef/tasks/chef_repo.rake
@@ -72,8 +72,8 @@ task :install => [ :update, :roles, :upload_cookbooks ] do
end
end
-desc "By default, run rake test"
-task :default => [ :test ]
+desc "By default, run rake test_cookbooks"
+task :default => [ :test_cookbooks ]
desc "Create a new cookbook (with COOKBOOK=name, optional CB_PREFIX=site-)"
task :new_cookbook do
@@ -213,7 +213,7 @@ EOH
end
rule(%r{\b(?:site-)?cookbooks/[^/]+/metadata\.json\Z} => [ proc { |task_name| task_name.sub(/\.[^.]+$/, '.rb') } ]) do |t|
- system("knife cookbook metadata #{t.source}")
+ system("knife cookbook metadata from file #{t.source}")
end
desc "Build cookbook metadata.json from metadata.rb"
@@ -228,7 +228,7 @@ task :roles => FileList[File.join(TOPDIR, 'roles', '**', '*.rb')].pathmap('%X.j
desc "Update a specific role"
task :role, :role_name do |t, args|
- system("knife role from file #{args.cookbook}")
+ system("knife role from file #{File.join(TOPDIR, 'roles', args.role_name)}.rb")
end
desc "Upload all cookbooks"
@@ -243,3 +243,13 @@ task :upload_cookbook, :cookbook do |t, args|
system("knife cookbook upload #{args.cookbook}")
end
+desc "Test all cookbooks"
+task :test_cookbooks do
+ system("knife cookbook test --all")
+end
+
+desc "Test a single cookbook"
+task :test_cookbook, :cookbook do |t, args|
+ system("knife cookbook test #{args.cookbook}")
+end
+
diff --git a/chef/lib/chef/util/file_edit.rb b/chef/lib/chef/util/file_edit.rb
index 2ecaa98642..83b1568f08 100644
--- a/chef/lib/chef/util/file_edit.rb
+++ b/chef/lib/chef/util/file_edit.rb
@@ -15,7 +15,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require 'ftools'
require 'fileutils'
require 'tempfile'
diff --git a/chef/lib/chef/webui_user.rb b/chef/lib/chef/webui_user.rb
index 240e119dc5..fbfe1724d1 100644
--- a/chef/lib/chef/webui_user.rb
+++ b/chef/lib/chef/webui_user.rb
@@ -22,7 +22,6 @@ require 'chef/mixin/params_validate'
require 'chef/couchdb'
require 'chef/index_queue'
require 'digest/sha1'
-require 'rubygems'
require 'json'