summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Jacob <adam@opscode.com>2009-05-18 14:49:53 -0700
committerAdam Jacob <adam@opscode.com>2009-05-18 14:49:53 -0700
commit4faba23f2367e15e196abef45d9e904530d7b9a8 (patch)
treeebe2020891c3fec0c28bea90153fe31faa845b96
parentb745621c8e40ff74acafeb47cca20b6edc5193e3 (diff)
parent63f026077329adaa5a59a000482ec0ed145498aa (diff)
downloadchef-4faba23f2367e15e196abef45d9e904530d7b9a8.tar.gz
Merge branch 'master' into mdkent/chef-272
-rw-r--r--CHANGELOG12
-rw-r--r--chef-server-slice/Rakefile2
-rw-r--r--chef-server-slice/app/controllers/cookbook_attributes.rb2
-rw-r--r--chef-server-slice/app/controllers/cookbook_definitions.rb2
-rw-r--r--chef-server-slice/app/controllers/cookbook_files.rb1
-rw-r--r--chef-server-slice/app/controllers/cookbook_libraries.rb1
-rw-r--r--chef-server-slice/app/controllers/cookbook_recipes.rb1
-rw-r--r--chef-server-slice/app/controllers/cookbook_templates.rb1
-rw-r--r--chef-server-slice/app/controllers/cookbooks.rb1
-rw-r--r--chef-server-slice/app/controllers/nodes.rb2
-rw-r--r--chef-server-slice/app/controllers/search.rb1
-rw-r--r--chef-server-slice/app/controllers/search_entries.rb1
-rw-r--r--chef-server-slice/app/controllers/status.rb1
-rw-r--r--chef-server-slice/config/init.rb2
-rw-r--r--chef-server/Rakefile2
-rw-r--r--chef-server/config.ru16
-rw-r--r--chef-server/config/environments/production.rb5
-rw-r--r--chef-server/config/init.rb8
-rw-r--r--chef-server/lib/tasks/package.rake1
-rw-r--r--chef/Rakefile4
-rw-r--r--[-rwxr-xr-x]chef/examples/user_index.pl0
-rw-r--r--[-rwxr-xr-x]chef/examples/user_index.rb0
-rw-r--r--chef/lib/chef.rb2
-rw-r--r--chef/lib/chef/client.rb33
-rw-r--r--chef/lib/chef/cookbook/metadata.rb398
-rw-r--r--chef/lib/chef/cookbook_loader.rb2
-rw-r--r--chef/lib/chef/platform.rb17
-rw-r--r--chef/lib/chef/provider/group.rb14
-rw-r--r--chef/lib/chef/provider/group/groupadd.rb11
-rw-r--r--chef/lib/chef/provider/ifconfig.rb131
-rw-r--r--chef/lib/chef/provider/package.rb50
-rw-r--r--chef/lib/chef/provider/package/apt.rb12
-rw-r--r--chef/lib/chef/provider/package/freebsd.rb8
-rw-r--r--chef/lib/chef/provider/package/macports.rb105
-rw-r--r--chef/lib/chef/provider/package/portage.rb63
-rw-r--r--chef/lib/chef/provider/package/rpm.rb13
-rw-r--r--chef/lib/chef/provider/package/rubygems.rb10
-rw-r--r--chef/lib/chef/provider/package/yum.rb13
-rw-r--r--chef/lib/chef/provider/remote_directory.rb160
-rw-r--r--chef/lib/chef/provider/route.rb24
-rw-r--r--chef/lib/chef/provider/service/redhat.rb4
-rw-r--r--chef/lib/chef/recipe.rb2
-rw-r--r--chef/lib/chef/resource.rb44
-rw-r--r--chef/lib/chef/resource/group.rb8
-rw-r--r--chef/lib/chef/resource/ifconfig.rb134
-rw-r--r--chef/lib/chef/resource/macports_package.rb29
-rw-r--r--chef/lib/chef/resource/route.rb45
-rw-r--r--chef/lib/chef/rest.rb7
-rw-r--r--chef/lib/chef/tasks/chef_repo.rake199
-rw-r--r--chef/spec/unit/cookbook/metadata_spec.rb444
-rw-r--r--chef/spec/unit/platform_spec.rb6
-rw-r--r--chef/spec/unit/provider/group/groupadd_spec.rb16
-rw-r--r--chef/spec/unit/provider/group_spec.rb18
-rw-r--r--chef/spec/unit/provider/package/macports_spec.rb180
-rw-r--r--chef/spec/unit/provider/package/portage_spec.rb158
-rw-r--r--chef/spec/unit/provider/package/rpm_spec.rb2
-rw-r--r--chef/spec/unit/provider/package_spec.rb31
-rw-r--r--chef/spec/unit/provider/service/redhat_spec.rb8
-rw-r--r--chef/spec/unit/resource/group_spec.rb22
-rw-r--r--chef/spec/unit/resource/macports_package_spec.rb37
-rw-r--r--features/cookbooks/metadata.feature20
-rw-r--r--features/data/Rakefile158
-rw-r--r--features/data/cookbooks/delayed_notifications/recipes/notify_different_resources_for_different_actions.rb31
-rw-r--r--features/data/cookbooks/execute_commands/metadata.rb14
-rw-r--r--features/data/cookbooks/metadata/metadata.rb14
-rw-r--r--features/data/cookbooks/metadata/recipes/default.rb18
-rw-r--r--features/data/cookbooks/packages/recipes/default.rb0
-rw-r--r--features/data/cookbooks/packages/recipes/macports_install_bad_package.rb3
-rw-r--r--features/data/cookbooks/packages/recipes/macports_install_yydecode.rb3
-rw-r--r--features/data/cookbooks/packages/recipes/macports_purge_yydecode.rb3
-rw-r--r--features/data/cookbooks/packages/recipes/macports_remove_yydecode.rb3
-rw-r--r--features/data/cookbooks/packages/recipes/macports_upgrade_yydecode.rb7
-rw-r--r--features/language/delayed_notifications.feature8
-rw-r--r--features/provider/package/macports.feature18
-rw-r--r--features/steps/cookbooks.rb53
-rw-r--r--features/steps/node_steps.rb (renamed from features/steps/nodes.rb)18
-rw-r--r--features/steps/packages.rb20
-rw-r--r--features/steps/run_client.rb6
-rw-r--r--features/steps/run_solo.rb36
-rw-r--r--features/support/env.rb3
-rw-r--r--features/support/packages.rb12
81 files changed, 2645 insertions, 329 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 7679205187..a41a25611e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,15 @@
+Wed Apr 29 16:08:40 PDT 2009
+Release Notes - Chef - Version 0.6.2
+http://tickets.opscode.com
+
+** Bug
+ * [CHEF-255] - chef-client doesn't respect interval and stay running in foreground.
+ * [CHEF-257] - Rake test does not execute tests against the site-cookbooks directory
+
+** Improvement
+ * [CHEF-249] - can I has open-uri supported "-j http://blah.com/some.json" for chef-solo/client?
+ * [CHEF-258] - gem_package doesn't allow you to point at a custom gem binary outside of your $PATH
+
Tue Apr 28 16:43:43 PDT 2009
Release Notes - Chef - Version 0.6.0
http://tickets.opscode.com
diff --git a/chef-server-slice/Rakefile b/chef-server-slice/Rakefile
index 665681c2ed..44734103c5 100644
--- a/chef-server-slice/Rakefile
+++ b/chef-server-slice/Rakefile
@@ -5,7 +5,7 @@ require 'merb-core'
require 'merb-core/tasks/merb'
GEM_NAME = "chef-server-slice"
-CHEF_SERVER_VERSION="0.6.2"
+CHEF_SERVER_VERSION="0.6.3"
AUTHOR = "Opscode"
EMAIL = "chef@opscode.com"
HOMEPAGE = "http://wiki.opscode.com/display/chef"
diff --git a/chef-server-slice/app/controllers/cookbook_attributes.rb b/chef-server-slice/app/controllers/cookbook_attributes.rb
index 3072358a67..fd888448b0 100644
--- a/chef-server-slice/app/controllers/cookbook_attributes.rb
+++ b/chef-server-slice/app/controllers/cookbook_attributes.rb
@@ -22,6 +22,8 @@ require 'chef' / 'mixin' / 'checksum'
class ChefServerSlice::CookbookAttributes < ChefServerSlice::Application
provides :html, :json
+
+ before :login_required
include Chef::Mixin::Checksum
diff --git a/chef-server-slice/app/controllers/cookbook_definitions.rb b/chef-server-slice/app/controllers/cookbook_definitions.rb
index 0205cb1111..da5654fbcc 100644
--- a/chef-server-slice/app/controllers/cookbook_definitions.rb
+++ b/chef-server-slice/app/controllers/cookbook_definitions.rb
@@ -22,6 +22,8 @@ require 'chef' / 'mixin' / 'checksum'
class ChefServerSlice::CookbookDefinitions < ChefServerSlice::Application
provides :html, :json
+
+ before :login_required
include Chef::Mixin::Checksum
diff --git a/chef-server-slice/app/controllers/cookbook_files.rb b/chef-server-slice/app/controllers/cookbook_files.rb
index e70f853d87..b739366d83 100644
--- a/chef-server-slice/app/controllers/cookbook_files.rb
+++ b/chef-server-slice/app/controllers/cookbook_files.rb
@@ -24,6 +24,7 @@ require 'chef' / 'mixin' / 'find_preferred_file'
class ChefServerSlice::CookbookFiles < ChefServerSlice::Application
provides :html, :json
+ before :login_required
include Chef::Mixin::Checksum
include Chef::Mixin::FindPreferredFile
diff --git a/chef-server-slice/app/controllers/cookbook_libraries.rb b/chef-server-slice/app/controllers/cookbook_libraries.rb
index 88d1500a92..72b296f351 100644
--- a/chef-server-slice/app/controllers/cookbook_libraries.rb
+++ b/chef-server-slice/app/controllers/cookbook_libraries.rb
@@ -23,6 +23,7 @@ require 'chef' / 'cookbook_loader'
class ChefServerSlice::CookbookLibraries < ChefServerSlice::Application
provides :html, :json
+ before :login_required
include Chef::Mixin::Checksum
diff --git a/chef-server-slice/app/controllers/cookbook_recipes.rb b/chef-server-slice/app/controllers/cookbook_recipes.rb
index cb969e6415..fbcb427eb3 100644
--- a/chef-server-slice/app/controllers/cookbook_recipes.rb
+++ b/chef-server-slice/app/controllers/cookbook_recipes.rb
@@ -22,6 +22,7 @@ require 'chef' / 'mixin' / 'checksum'
class ChefServerSlice::CookbookRecipes < ChefServerSlice::Application
provides :html, :json
+ before :login_required
include Chef::Mixin::Checksum
diff --git a/chef-server-slice/app/controllers/cookbook_templates.rb b/chef-server-slice/app/controllers/cookbook_templates.rb
index 10cc5731c9..bce79bd573 100644
--- a/chef-server-slice/app/controllers/cookbook_templates.rb
+++ b/chef-server-slice/app/controllers/cookbook_templates.rb
@@ -24,6 +24,7 @@ require 'chef' / 'mixin' / 'find_preferred_file'
class ChefServerSlice::CookbookTemplates < ChefServerSlice::Application
provides :html, :json
+ before :login_required
include Chef::Mixin::Checksum
include Chef::Mixin::FindPreferredFile
diff --git a/chef-server-slice/app/controllers/cookbooks.rb b/chef-server-slice/app/controllers/cookbooks.rb
index c1ef997bd7..a19440937a 100644
--- a/chef-server-slice/app/controllers/cookbooks.rb
+++ b/chef-server-slice/app/controllers/cookbooks.rb
@@ -22,6 +22,7 @@ require 'chef' / 'cookbook_loader'
class ChefServerSlice::Cookbooks < ChefServerSlice::Application
provides :html, :json
+ before :login_required
def index
@cl = Chef::CookbookLoader.new
diff --git a/chef-server-slice/app/controllers/nodes.rb b/chef-server-slice/app/controllers/nodes.rb
index 03b343767a..472b33a453 100644
--- a/chef-server-slice/app/controllers/nodes.rb
+++ b/chef-server-slice/app/controllers/nodes.rb
@@ -24,7 +24,7 @@ class ChefServerSlice::Nodes < ChefServerSlice::Application
provides :html, :json
before :fix_up_node_id
- before :login_required, :only => [ :create, :update, :destroy ]
+ before :login_required
before :authorized_node, :only => [ :update, :destroy ]
def index
diff --git a/chef-server-slice/app/controllers/search.rb b/chef-server-slice/app/controllers/search.rb
index bd1232062c..0f56106492 100644
--- a/chef-server-slice/app/controllers/search.rb
+++ b/chef-server-slice/app/controllers/search.rb
@@ -23,6 +23,7 @@ require 'chef' / 'queue'
class ChefServerSlice::Search < ChefServerSlice::Application
provides :html, :json
+ before :login_required
def index
@s = Chef::Search.new
diff --git a/chef-server-slice/app/controllers/search_entries.rb b/chef-server-slice/app/controllers/search_entries.rb
index dfbc724ad2..dfb90e0b27 100644
--- a/chef-server-slice/app/controllers/search_entries.rb
+++ b/chef-server-slice/app/controllers/search_entries.rb
@@ -23,6 +23,7 @@ require 'chef' / 'queue'
class ChefServerSlice::SearchEntries < ChefServerSlice::Application
provides :html, :json
+ before :login_required
def index
@s = Chef::Search.new
diff --git a/chef-server-slice/app/controllers/status.rb b/chef-server-slice/app/controllers/status.rb
index 58562672e7..30f656c76b 100644
--- a/chef-server-slice/app/controllers/status.rb
+++ b/chef-server-slice/app/controllers/status.rb
@@ -21,6 +21,7 @@ require 'chef' / 'node'
class ChefServerSlice::Status < ChefServerSlice::Application
provides :html, :json
+ before :login_required
def index
@node_list = Chef::Node.list
diff --git a/chef-server-slice/config/init.rb b/chef-server-slice/config/init.rb
index d8faac2b81..8ae08ff1f9 100644
--- a/chef-server-slice/config/init.rb
+++ b/chef-server-slice/config/init.rb
@@ -39,5 +39,7 @@ Merb::Config.use do |c|
c[:session_store] = 'cookie'
c[:exception_details] = true
c[:reload_classes] = false
+ c[:log_level] = Chef::Config[:log_level]
+ c[:log_file] = Chef::Config[:log_location]
end
diff --git a/chef-server/Rakefile b/chef-server/Rakefile
index 4215e4a4ad..39bac5ee69 100644
--- a/chef-server/Rakefile
+++ b/chef-server/Rakefile
@@ -18,7 +18,7 @@ require 'chef' unless defined?(Chef)
include FileUtils
GEM = "chef-server"
-CHEF_SERVER_VERSION = "0.6.2"
+CHEF_SERVER_VERSION = "0.6.3"
AUTHOR = "Opscode"
EMAIL = "chef@opscode.com"
HOMEPAGE = "http://wiki.opscode.com/display/chef"
diff --git a/chef-server/config.ru b/chef-server/config.ru
new file mode 100644
index 0000000000..0cd42885ed
--- /dev/null
+++ b/chef-server/config.ru
@@ -0,0 +1,16 @@
+require 'rubygems'
+require 'merb-core'
+
+Merb::Config.setup(:merb_root => File.expand_path(File.dirname(__FILE__)),
+ :environment => ENV['RACK_ENV'], :init_file => File.dirname(__FILE__) / "config/init.rb")
+Merb.environment = Merb::Config[:environment]
+Merb.root = Merb::Config[:merb_root]
+Merb::BootLoader.run
+
+# Uncomment if your app is mounted at a suburi
+#if prefix = ::Merb::Config[:path_prefix]
+# use Merb::Rack::PathPrefix, prefix
+#end
+
+run Merb::Rack::Application.new
+
diff --git a/chef-server/config/environments/production.rb b/chef-server/config/environments/production.rb
index b10ca094dc..aa27f7a695 100644
--- a/chef-server/config/environments/production.rb
+++ b/chef-server/config/environments/production.rb
@@ -2,9 +2,8 @@ Merb.logger.info("Loaded PRODUCTION Environment...")
Merb::Config.use { |c|
c[:exception_details] = false
c[:reload_classes] = false
- c[:log_level] = :error
- c[:log_stream] = Chef::Config[:log_location]
- c[:log_file] = nil
+ c[:log_level] = Chef::Config[:log_level]
+ c[:log_file] = Chef::Config[:log_location]
# or redirect logger using IO handle
# c[:log_stream] = STDOUT
}
diff --git a/chef-server/config/init.rb b/chef-server/config/init.rb
index 767b068675..b40cb7c233 100644
--- a/chef-server/config/init.rb
+++ b/chef-server/config/init.rb
@@ -2,10 +2,12 @@
require 'config/dependencies.rb'
require 'chef' unless defined?(Chef)
-
+
+Chef::Config.from_file(File.join("/etc", "chef", "server.rb"))
+
use_test :rspec
use_template_engine :haml
-
+
Merb::Config.use do |c|
c[:use_mutex] = false
c[:session_id_key] = '_chef_server_session_id'
@@ -14,7 +16,7 @@ Merb::Config.use do |c|
c[:exception_details] = true
c[:reload_classes] = false
c[:log_level] = Chef::Config[:log_level]
- c[:log_stream] = Chef::Config[:log_location]
+ c[:log_file] = Chef::Config[:log_location]
end
Merb::BootLoader.before_app_loads do
diff --git a/chef-server/lib/tasks/package.rake b/chef-server/lib/tasks/package.rake
index 3722a2da16..f8df8096f0 100644
--- a/chef-server/lib/tasks/package.rake
+++ b/chef-server/lib/tasks/package.rake
@@ -32,6 +32,7 @@ spec = Gem::Specification.new do |s|
[ "README.txt",
"LICENSE",
"NOTICE",
+ "config.ru",
"{app}/**/*",
"{config}/**/*",
"{contrib}/**/*",
diff --git a/chef/Rakefile b/chef/Rakefile
index 3164223944..63cdec3638 100644
--- a/chef/Rakefile
+++ b/chef/Rakefile
@@ -4,7 +4,7 @@ require 'rake/rdoctask'
require './tasks/rspec.rb'
GEM = "chef"
-CHEF_VERSION = "0.6.2"
+CHEF_VERSION = "0.6.3"
AUTHOR = "Adam Jacob"
EMAIL = "adam@opscode.com"
HOMEPAGE = "http://wiki.opscode.com/display/chef"
@@ -25,7 +25,7 @@ spec = Gem::Specification.new do |s|
# Uncomment this to add a dependency
s.add_dependency "ruby-openid"
s.add_dependency "json"
- s.add_dependency "erubis"
+ s.add_dependency "erubis"
s.add_dependency "extlib"
s.add_dependency "stomp"
s.add_dependency "ohai"
diff --git a/chef/examples/user_index.pl b/chef/examples/user_index.pl
index e78a8125f4..e78a8125f4 100755..100644
--- a/chef/examples/user_index.pl
+++ b/chef/examples/user_index.pl
diff --git a/chef/examples/user_index.rb b/chef/examples/user_index.rb
index 485cff81b8..485cff81b8 100755..100644
--- a/chef/examples/user_index.rb
+++ b/chef/examples/user_index.rb
diff --git a/chef/lib/chef.rb b/chef/lib/chef.rb
index 509353d625..02a1e1d8c7 100644
--- a/chef/lib/chef.rb
+++ b/chef/lib/chef.rb
@@ -27,7 +27,7 @@ require 'chef/config'
Dir[File.join(File.dirname(__FILE__), 'chef/mixin/**/*.rb')].sort.each { |lib| require lib }
class Chef
- VERSION = '0.6.2'
+ VERSION = '0.6.3'
class << self
def fatal!(msg, err = -1)
diff --git a/chef/lib/chef/client.rb b/chef/lib/chef/client.rb
index cc53d3c397..560b7e306b 100644
--- a/chef/lib/chef/client.rb
+++ b/chef/lib/chef/client.rb
@@ -34,7 +34,7 @@ class Chef
include Chef::Mixin::GenerateURL
include Chef::Mixin::Checksum
- attr_accessor :node, :registration, :safe_name, :json_attribs, :validation_token, :node_name
+ attr_accessor :node, :registration, :safe_name, :json_attribs, :validation_token, :node_name, :ohai, :ohai_has_run
# Creates a new Chef::Client.
def initialize()
@@ -44,6 +44,9 @@ class Chef
@registration = nil
@json_attribs = nil
@node_name = nil
+ Ohai::Log.logger = Chef::Log.logger
+ @ohai = Ohai::System.new
+ @ohai_has_run = false
@rest = Chef::REST.new(Chef::Config[:registration_url])
end
@@ -65,9 +68,9 @@ class Chef
start_time = Time.now
Chef::Log.info("Starting Chef Run")
- build_node(@node_name)
register
authenticate
+ build_node(@node_name)
sync_library_files
sync_attribute_files
sync_definitions
@@ -99,7 +102,21 @@ class Chef
Chef::Log.info("Chef Run complete in #{end_time - start_time} seconds")
true
end
-
+
+ def run_ohai
+ @ohai.all_plugins unless @ohai_has_run
+ @ohai_has_run = true
+ end
+
+ def determine_node_name
+ run_ohai
+ unless @safe_name && @node_name
+ @node_name ||= @ohai[:fqdn] ? @ohai[:fqdn] : @ohai[:hostname]
+ @safe_name = @node_name.gsub(/\./, '_')
+ end
+ @node_name
+ end
+
# Builds a new node object for this client. Starts with querying for the FQDN of the current
# host (unless it is supplied), then merges in the facts from Ohai.
#
@@ -109,13 +126,9 @@ class Chef
# === Returns
# node<Chef::Node>:: Returns the created node object, also stored in @node
def build_node(node_name=nil, solo=false)
- Ohai::Log.logger = Chef::Log.logger
- ohai = Ohai::System.new
- ohai.all_plugins
-
- node_name ||= ohai[:fqdn] ? ohai[:fqdn] : ohai[:hostname]
+ node_name ||= determine_node_name
+
raise RuntimeError, "Unable to determine node name from ohai" unless node_name
- @safe_name = node_name.gsub(/\./, '_')
Chef::Log.debug("Building node object for #{@safe_name}")
unless solo
begin
@@ -166,6 +179,7 @@ class Chef
# === Returns
# true:: Always returns true
def register
+ determine_node_name
Chef::Log.debug("Registering #{@safe_name} for an openid")
@registration = nil
begin
@@ -201,6 +215,7 @@ class Chef
# === Returns
# true:: Always returns true
def authenticate
+ determine_node_name
Chef::Log.debug("Authenticating #{@safe_name} via openid")
response = @rest.post_rest('openid/consumer/start', {
"openid_identifier" => "#{Chef::Config[:openid_url]}/openid/server/node/#{@safe_name}",
diff --git a/chef/lib/chef/cookbook/metadata.rb b/chef/lib/chef/cookbook/metadata.rb
new file mode 100644
index 0000000000..1d4165604c
--- /dev/null
+++ b/chef/lib/chef/cookbook/metadata.rb
@@ -0,0 +1,398 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 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/mixin/from_file'
+require 'chef/mixin/params_validate'
+require 'chef/mixin/check_helper'
+require 'chef/log'
+
+class Chef
+ class Cookbook
+ class Metadata
+
+ include Chef::Mixin::CheckHelper
+ include Chef::Mixin::ParamsValidate
+ include Chef::Mixin::FromFile
+
+ attr_accessor :cookbook, :platforms, :dependencies, :recommendations, :suggestions, :conflicting, :providing, :replacing, :attributes, :recipes
+
+ # Builds a new Chef::Cookbook::Metadata object.
+ #
+ # === Parameters
+ # cookbook<String>:: An optional cookbook object
+ # maintainer<String>:: An optional maintainer
+ # maintainer_email<String>:: An optional maintainer email
+ # license<String>::An optional license. Default is Apache v2.0
+ #
+ # === Returns
+ # metadata<Chef::Cookbook::Metadata>
+ def initialize(cookbook=nil, maintainer='Your Name', maintainer_email='youremail@example.com', license='Apache v2.0')
+ @cookbook = cookbook
+ @name = cookbook ? cookbook.name : ""
+ @long_description = ""
+ self.maintainer(maintainer)
+ self.maintainer_email(maintainer_email)
+ self.license(license)
+ self.description('A fabulous new cookbook')
+ @platforms = Mash.new
+ @dependencies = Mash.new
+ @recommendations = Mash.new
+ @suggestions = Mash.new
+ @conflicting = Mash.new
+ @providing = Mash.new
+ @replacing = Mash.new
+ @attributes = Mash.new
+ @recipes = Mash.new
+ if cookbook
+ @recipes = cookbook.recipes.inject({}) do |r, e|
+ e = self.name if e =~ /::default$/
+ r[e] = ""
+ self.provides e
+ r
+ end
+ end
+ end
+
+ # Sets the cookbooks maintainer, or returns it.
+ #
+ # === Parameters
+ # maintainer<String>:: The maintainers name
+ #
+ # === Returns
+ # maintainer<String>:: Returns the current maintainer.
+ def maintainer(arg=nil)
+ set_or_return(
+ :maintainer,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ # Sets the maintainers email address, or returns it.
+ #
+ # === Parameters
+ # maintainer_email<String>:: The maintainers email address
+ #
+ # === Returns
+ # maintainer_email<String>:: Returns the current maintainer email.
+ def maintainer_email(arg=nil)
+ set_or_return(
+ :maintainer_email,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ # Sets the current license, or returns it.
+ #
+ # === Parameters
+ # license<String>:: The current license.
+ #
+ # === Returns
+ # license<String>:: Returns the current license
+ def license(arg=nil)
+ set_or_return(
+ :license,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ # Sets the current description, or returns it. Should be short - one line only!
+ #
+ # === Parameters
+ # description<String>:: The new description
+ #
+ # === Returns
+ # description<String>:: Returns the description
+ def description(arg=nil)
+ set_or_return(
+ :description,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ # Sets the current long description, or returns it. Might come from a README, say.
+ #
+ # === Parameters
+ # long_description<String>:: The new long description
+ #
+ # === Returns
+ # long_description<String>:: Returns the long description
+ def long_description(arg=nil)
+ set_or_return(
+ :long_description,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ # Sets the current cookbook version, or returns it. Must be two digets, seperated
+ # by a dot. ie: '2.1', or '0.9'.
+ #
+ # === Parameters
+ # version<String>:: The curent version, as a string
+ #
+ # === Returns
+ # version<String>:: Returns the current version
+ def version(arg=nil)
+ set_or_return(
+ :version,
+ arg,
+ :regex => /^\d+\.\d+$/
+ )
+ end
+
+ # Sets the name of the cookbook, or returns it.
+ #
+ # === Parameters
+ # name<String>:: The curent cookbook name.
+ #
+ # === Returns
+ # name<String>:: Returns the current cookbook name.
+ def name(arg=nil)
+ set_or_return(
+ :name,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ # Adds a supported platform, with version checking strings.
+ #
+ # === Parameters
+ # platform<String>,<Symbol>:: The platform (like :ubuntu or :mac_os_x)
+ # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
+ #
+ # === Returns
+ # versions<Array>:: Returns the list of versions for the platform
+ def supports(platform, *versions)
+ versions.each { |v| _check_version_expression(v) }
+ @platforms[platform] = versions
+ @platforms[platform]
+ end
+
+ # Adds a dependency on another cookbook, with version checking strings.
+ #
+ # === Parameters
+ # cookbook<String>:: The cookbook
+ # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
+ #
+ # === Returns
+ # versions<Array>:: Returns the list of versions for the platform
+ def depends(cookbook, *versions)
+ versions.each { |v| _check_version_expression(v) }
+ @dependencies[cookbook] = versions
+ @dependencies[cookbook]
+ end
+
+ # Adds a recommendation for another cookbook, with version checking strings.
+ #
+ # === Parameters
+ # cookbook<String>:: The cookbook
+ # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
+ #
+ # === Returns
+ # versions<Array>:: Returns the list of versions for the platform
+ def recommends(cookbook, *versions)
+ versions.each { |v| _check_version_expression(v) }
+ @recommendations[cookbook] = versions
+ @recommendations[cookbook]
+ end
+
+ # Adds a suggestion for another cookbook, with version checking strings.
+ #
+ # === Parameters
+ # cookbook<String>:: The cookbook
+ # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
+ #
+ # === Returns
+ # versions<Array>:: Returns the list of versions for the platform
+ def suggests(cookbook, *versions)
+ versions.each { |v| _check_version_expression(v) }
+ @suggestions[cookbook] = versions
+ @suggestions[cookbook]
+ end
+
+ # Adds a conflict for another cookbook, with version checking strings.
+ #
+ # === Parameters
+ # cookbook<String>:: The cookbook
+ # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
+ #
+ # === Returns
+ # versions<Array>:: Returns the list of versions for the platform
+ def conflicts(cookbook, *versions)
+ versions.each { |v| _check_version_expression(v) }
+ @conflicting[cookbook] = versions
+ @conflicting[cookbook]
+ end
+
+ # Adds a recipe, definition, or resource provided by this cookbook.
+ #
+ # Recipes are specified as normal
+ # Definitions are followed by (), and can include :params for prototyping
+ # Resources are the stringified version (service[apache2])
+ #
+ # === Parameters
+ # recipe, definition, resource<String>:: The thing we provide
+ # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
+ #
+ # === Returns
+ # versions<Array>:: Returns the list of versions for the platform
+ def provides(cookbook, *versions)
+ versions.each { |v| _check_version_expression(v) }
+ @providing[cookbook] = versions
+ @providing[cookbook]
+ end
+
+ # Adds a cookbook that is replaced by this one, with version checking strings.
+ #
+ # === Parameters
+ # cookbook<String>:: The cookbook we replace
+ # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
+ #
+ # === Returns
+ # versions<Array>:: Returns the list of versions for the platform
+ def replaces(cookbook, *versions)
+ versions.each { |v| _check_version_expression(v) }
+ @replacing[cookbook] = versions
+ @replacing[cookbook]
+ end
+
+ # Adds a cookbook that is replaced by this one, with version checking strings.
+ #
+ # === Parameters
+ # recipe<String>:: The recipe
+ # description<String>:: The description of the recipe
+ #
+ # === Returns
+ # description<String>:: Returns the current description
+ def recipe(name, description)
+ @recipes[name] = description
+ end
+
+ # Adds an attribute that a user needs to configure for this cookbook. Takes
+ # a name (with the / notation for a nested attribute), followed by any of
+ # these options
+ #
+ # display_name<String>:: What a UI should show for this attribute
+ # description<String>:: A hint as to what this attr is for
+ # multiple_values<True>,<False>:: Whether it supports multiple values
+ # type<String>:: "string", "hash" or "array" - default is "string"
+ # required<True>,<False>:: Whether this attr is required - default false
+ # recipes<Array>:: An array of recipes which need this attr set.
+ # default<String>,<Array>,<Hash>:: The default value
+ #
+ # === Parameters
+ # name<String>:: The name of the attribute ('foo', or 'apache2/log_dir')
+ # options<Hash>:: The description of the options
+ #
+ # === Returns
+ # options<Hash>:: Returns the current options hash
+ def attribute(name, options)
+ validate(
+ options,
+ {
+ :display_name => { :kind_of => String },
+ :description => { :kind_of => String },
+ :multiple_values => { :equal_to => [ true, false ], :default => false },
+ :type => { :equal_to => [ "string", "array", "hash" ], :default => "string" },
+ :required => { :equal_to => [ true, false ], :default => false },
+ :recipes => { :kind_of => [ Array ], :default => [] },
+ :default => { :kind_of => [ String, Array, Hash ] }
+ }
+ )
+ @attributes[name] = options
+ @attributes[name]
+ end
+
+ def _check_version_expression(version_string)
+ if version_string =~ /^(>>|>=|=|<=|<<) (.+)$/
+ [ $1, $2 ]
+ else
+ raise ArgumentError, "Version expression #{version_string} is invalid!"
+ end
+ end
+
+ def _check_valid_version(to_check, version_string)
+ (selector, version) = _check_version_expression(version_string)
+ case selector
+ when "<<"
+ to_check < version
+ when "<="
+ to_check <= version
+ when "="
+ to_check == version
+ when ">="
+ to_check >= version
+ when ">>"
+ to_check > version
+ end
+ end
+
+ def to_json(*a)
+ result = {
+ :name => self.name,
+ :description => self.description,
+ :long_description => self.long_description,
+ :maintainer => self.maintainer,
+ :maintainer_email => self.maintainer_email,
+ :license => self.license,
+ :platforms => self.platforms,
+ :dependencies => self.dependencies,
+ :recommendations => self.recommendations,
+ :suggestions => self.suggestions,
+ :conflicting => self.conflicting,
+ :providing => self.providing,
+ :replacing => self.replacing,
+ :attributes => self.attributes,
+ :recipes => self.recipes
+ }
+ result.to_json(*a)
+ end
+
+ def self.from_hash(o)
+ cm = self.new()
+ cm.name o['name']
+ cm.description o['description']
+ cm.long_description o['long_description']
+ cm.maintainer o['maintainer']
+ cm.maintainer_email o['maintainer_email']
+ cm.license o['license']
+ cm.platforms = o['platforms']
+ cm.dependencies = o['dependencies']
+ cm.recommendations = o['recommendations']
+ cm.suggestions = o['suggestions']
+ cm.conflicting = o['conflicting']
+ cm.providing = o['providing']
+ cm.replacing = o['replacing']
+ cm.attributes = o['attributes']
+ cm.recipes = o['recipes']
+ cm
+ end
+
+ def self.from_json(string)
+ o = JSON.parse(string)
+ self.from_hash(o)
+ end
+
+ end
+ end
+end
diff --git a/chef/lib/chef/cookbook_loader.rb b/chef/lib/chef/cookbook_loader.rb
index c0a8e3a1f0..d93d272087 100644
--- a/chef/lib/chef/cookbook_loader.rb
+++ b/chef/lib/chef/cookbook_loader.rb
@@ -158,4 +158,4 @@ class Chef
end
end
-end \ No newline at end of file
+end
diff --git a/chef/lib/chef/platform.rb b/chef/lib/chef/platform.rb
index 4aa2096574..d987541aa6 100644
--- a/chef/lib/chef/platform.rb
+++ b/chef/lib/chef/platform.rb
@@ -27,13 +27,15 @@ class Chef
class Platform
@platforms = {
- :mac_os_x => {},
+ :mac_os_x => {
+ :default => {
+ :package => Chef::Provider::Package::Macports
+ }
+ },
:freebsd => {
:default => {
- :group => Chef::Provider::Group::Pw,
:package => Chef::Provider::Package::Freebsd,
:service => Chef::Provider::Service::Freebsd,
- :user => Chef::Provider::User::Pw
}
},
:ubuntu => {
@@ -91,7 +93,8 @@ class Chef
:user => Chef::Provider::User::Useradd,
:group => Chef::Provider::Group::Groupadd,
:http_request => Chef::Provider::HttpRequest,
- :route => Chef::Provider::Route
+ :route => Chef::Provider::Route,
+ :ifconfig => Chef::Provider::Ifconfig
}
}
@@ -213,7 +216,11 @@ class Chef
end
else
if @platforms.has_key?(args[:platform])
- @platforms[args[:platform]][:default][args[:resource].to_sym] = args[:provider]
+ 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] } }
+ end
else
@platforms[args[:platform]] = {
:default => {
diff --git a/chef/lib/chef/provider/group.rb b/chef/lib/chef/provider/group.rb
index 5552744e45..30bfcb487c 100644
--- a/chef/lib/chef/provider/group.rb
+++ b/chef/lib/chef/provider/group.rb
@@ -45,6 +45,7 @@ class Chef
end
if group_info
+ @new_resource.gid(group_info.gid)
@current_resource.gid(group_info.gid)
@current_resource.members(group_info.mem)
end
@@ -58,9 +59,18 @@ class Chef
# <true>:: If a change is required
# <false>:: If a change is not required
def compare_group
- !![ :gid, :members ].find do |group_attrib|
- @new_resource.send(group_attrib) != @current_resource.send(group_attrib)
+ return true if @new_resource.gid != @current_resource.gid
+
+ if(@new_resource.append)
+ @new_resource.members.each do |member|
+ next if @current_resource.members.include?(member)
+ return true
+ end
+ else
+ return true if @new_resource.members != @current_resource.members
end
+
+ return false
end
def action_create
diff --git a/chef/lib/chef/provider/group/groupadd.rb b/chef/lib/chef/provider/group/groupadd.rb
index 88dce3303f..54ea73702a 100644
--- a/chef/lib/chef/provider/group/groupadd.rb
+++ b/chef/lib/chef/provider/group/groupadd.rb
@@ -55,8 +55,15 @@ class Chef
def modify_group_members
unless @new_resource.members.empty?
- Chef::Log.debug("#{@new_resource}: setting group members to #{@new_resource.members.join(', ')}")
- run_command(:command => "gpasswd -M #{@new_resource.members.join(',')} #{@new_resource.group_name}")
+ if(@new_resource.append)
+ @new_resource.members.each do |member|
+ Chef::Log.debug("#{@new_resource}: appending member #{member} to group #{@new_resource.group_name}")
+ run_command(:command => "gpasswd -a #{member} #{@new_resource.group_name}")
+ end
+ else
+ Chef::Log.debug("#{@new_resource}: setting group members to #{@new_resource.members.join(', ')}")
+ run_command(:command => "gpasswd -M #{@new_resource.members.join(',')} #{@new_resource.group_name}")
+ end
else
Chef::Log.debug("#{@new_resource}: not changing group members, the group has no members")
end
diff --git a/chef/lib/chef/provider/ifconfig.rb b/chef/lib/chef/provider/ifconfig.rb
new file mode 100644
index 0000000000..30fc41228d
--- /dev/null
+++ b/chef/lib/chef/provider/ifconfig.rb
@@ -0,0 +1,131 @@
+#
+# Author:: Jason Jackson (jason.jackson@monster.com)
+# Copyright:: Copyright (c) 2009 Jason Jackson
+# 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/log'
+require 'chef/mixin/command'
+require 'chef/provider'
+
+class Chef
+ class Provider
+ class Ifconfig < Chef::Provider
+ include Chef::Mixin::Command
+
+ def load_current_resource
+ @current_resource = Chef::Resource::Ifconfig.new(@new_resource.name)
+
+ @interfaces = {}
+
+ status = popen4("ifconfig") do |pid, stdin, stdout, stderr|
+ stdout.each do |line|
+
+ if !line[0..9].strip.empty?
+ @int_name = line[0..9].strip
+ @interfaces[@int_name] = {"hwaddr" => (line =~ /(HWaddr)/ ? ($') : "nil").strip.chomp }
+ else
+ @interfaces[@int_name]["inet_addr"] = (line =~ /inet addr:(\S+)/ ? ($1) : "nil") if line =~ /inet addr:/
+ @interfaces[@int_name]["bcast"] = (line =~ /Bcast:(\S+)/ ? ($1) : "nil") if line =~ /Bcast:/
+ @interfaces[@int_name]["mask"] = (line =~ /Mask:(\S+)/ ? ($1) : "nil") if line =~ /Mask:/
+ @interfaces[@int_name]["mtu"] = (line =~ /MTU:(\S+)/ ? ($1) : "nil") if line =~ /MTU:/
+ @interfaces[@int_name]["metric"] = (line =~ /Metric:(\S+)/ ? ($1) : "nil") if line =~ /Metric:/
+ end
+
+ if @interfaces.has_key?(@new_resource.device)
+ @interface = @interfaces.fetch(@new_resource.device)
+
+ @current_resource.target(@new_resource.target)
+ @current_resource.device(@int_name)
+ @current_resource.inet_addr(@interface["inet_addr"])
+ @current_resource.hwaddr(@interface["hwaddr"])
+ @current_resource.bcast(@interface["bcast"])
+ @current_resource.mask(@interface["mask"])
+ @current_resource.mtu(@interface["mtu"])
+ @current_resource.metric(@interface["metric"])
+ end
+ end
+ end
+
+ unless status.exitstatus == 0
+ raise Chef::Exception::Ifconfig, "ifconfig failed - #{status.inspect}!"
+ end
+
+ @current_resource
+ end
+
+ def action_add
+ # check to see if load_current_resource found ifconfig
+ unless @current_resource.inet_addr
+ unless @new_resource.device == "lo"
+ command = "ifconfig #{@new_resource.device} #{@new_resource.name}"
+ command << " netmask #{@new_resource.mask}" if @new_resource.mask
+ command << " metric #{@new_resource.metric}" if @new_resource.metric
+ command << " mtu #{@new_resource.mtu}" if @new_resource.mtu
+ end
+
+ run_command(
+ :command => command
+ )
+ @new_resource.updated = true
+
+ end
+
+ # Write out the config files
+ generate_config
+ end
+
+ def action_delete
+ # check to see if load_current_resource found the interface
+ if @current_resource.device
+ command = "ifconfig #{@new_resource.device} down"
+
+ run_command(
+ :command => command
+ )
+ @new_resource.updated = true
+ else
+ Chef::Log.debug("Ifconfig #{@current_resource} does not exist")
+ end
+ end
+
+ # This is a little lame of me, as if any of these values aren't filled out it leaves blank lines
+ # in the file. Can refactor later to have this nice and tight.
+ def generate_config
+ b = binding
+ case node[:platform]
+ when ("centos" || "redhat" || "fedora")
+ content = %{
+<% if @new_resource.device %>DEVICE=<%= @new_resource.device %><% end %>
+<% if @new_resource.onboot %>ONBOOT=<%= @new_resource.onboot %><% end %>
+<% if @new_resource.bootproto %>BOOTPROTO=<%= @new_resource.bootproto %><% end %>
+<% if @new_resource.target %>IPADDR=<%= @new_resource.target %><% end %>
+<% if @new_resource.mask %>NETMASK=<%= @new_resource.mask %><% end %>
+<% if @new_resource.network %>NETWORK=<%= @new_resource.network %><% end %>
+<% if @new_resource.bcast %>BROADCAST=<%= @new_resource.bcast %><% end %>
+ }
+ template = ::ERB.new(content)
+ network_file = ::File.new("/etc/sysconfig/network-scripts/ifcfg-#{@new_resource.device}", "w")
+ network_file.puts(template.result(b))
+ network_file.close
+ when ("debian" || "ubantu")
+ # template
+ when ("slackware")
+ # template
+ end
+ end
+ end
+ end
+end
diff --git a/chef/lib/chef/provider/package.rb b/chef/lib/chef/provider/package.rb
index 33b31dd848..e68248bc0d 100644
--- a/chef/lib/chef/provider/package.rb
+++ b/chef/lib/chef/provider/package.rb
@@ -36,44 +36,38 @@ class Chef
end
def action_install
- # First, select what version we should be using
- install_version = @new_resource.version
- install_version ||= @candidate_version
-
+ # If we specified a version, and it's not the current version, move to the current version
+ if @new_resource.version != nil && @new_resource.version != @current_resource.version
+ install_version = @new_resource.version
+ # If it's not installed at all, install it
+ elsif @current_resource.version == nil
+ install_version = candidate_version
+ else
+ return
+ end
+
unless install_version
raise(Chef::Exceptions::Package, "No version specified, and no candidate version available!")
end
-
- do_package = false
- # If it's not installed at all, install it
- if @current_resource.version == nil
- do_package = true
- # If we specified a version, and it's not the current version, move to the current version
- elsif @new_resource.version != nil
- if @new_resource.version != @current_resource.version
- do_package = true
- end
- end
- if do_package
- Chef::Log.info("Installing #{@new_resource} version #{install_version}")
+
+ Chef::Log.info("Installing #{@new_resource} version #{install_version}")
- # We need to make sure we handle the preseed file
- if @new_resource.response_file
- preseed_package(@new_resource.package_name, install_version)
- end
+ # We need to make sure we handle the preseed file
+ if @new_resource.response_file
+ preseed_package(@new_resource.package_name, install_version)
+ end
- status = install_package(@new_resource.package_name, install_version)
- if status
- @new_resource.updated = true
- end
+ status = install_package(@new_resource.package_name, install_version)
+ if status
+ @new_resource.updated = true
end
end
def action_upgrade
- if @current_resource.version != @candidate_version
+ if @current_resource.version != candidate_version
orig_version = @current_resource.version || "uninstalled"
- Chef::Log.info("Upgrading #{@new_resource} version from #{orig_version} to #{@candidate_version}")
- status = upgrade_package(@new_resource.package_name, @candidate_version)
+ Chef::Log.info("Upgrading #{@new_resource} version from #{orig_version} to #{candidate_version}")
+ status = upgrade_package(@new_resource.package_name, candidate_version)
if status
@new_resource.updated = true
end
diff --git a/chef/lib/chef/provider/package/apt.rb b/chef/lib/chef/provider/package/apt.rb
index b9a6e2b43e..5abe1df13a 100644
--- a/chef/lib/chef/provider/package/apt.rb
+++ b/chef/lib/chef/provider/package/apt.rb
@@ -64,8 +64,7 @@ class Chef
run_command(
:command => "apt-get -q -y install #{name}=#{version}",
:environment => {
- "DEBIAN_FRONTEND" => "noninteractive",
- "LANG" => "en_US"
+ "DEBIAN_FRONTEND" => "noninteractive"
}
)
end
@@ -78,8 +77,7 @@ class Chef
run_command(
:command => "apt-get -q -y remove #{@new_resource.package_name}",
:environment => {
- "DEBIAN_FRONTEND" => "noninteractive",
- "LANG" => "en_US"
+ "DEBIAN_FRONTEND" => "noninteractive"
}
)
end
@@ -88,8 +86,7 @@ class Chef
run_command(
:command => "apt-get -q -y purge #{@new_resource.package_name}",
:environment => {
- "DEBIAN_FRONTEND" => "noninteractive",
- "LANG" => "en_US"
+ "DEBIAN_FRONTEND" => "noninteractive"
}
)
end
@@ -101,8 +98,7 @@ class Chef
run_command(
:command => "debconf-set-selections #{preseed_file}",
:environment => {
- "DEBIAN_FRONTEND" => "noninteractive",
- "LANG" => "en_US"
+ "DEBIAN_FRONTEND" => "noninteractive"
}
)
end
diff --git a/chef/lib/chef/provider/package/freebsd.rb b/chef/lib/chef/provider/package/freebsd.rb
index a2755de2af..5d8f5a40cb 100644
--- a/chef/lib/chef/provider/package/freebsd.rb
+++ b/chef/lib/chef/provider/package/freebsd.rb
@@ -136,11 +136,15 @@ class Chef
end
def remove_package(name, version)
- if @current_resource.version
+ # a version is mandatory
+ if version
+ run_command(
+ :command => "pkg_delete #{package_name}-#{version}"
+ )
+ else
run_command(
:command => "pkg_delete #{package_name}-#{@current_resource.version}"
)
- Chef::Log.info("Removed package #{package_name}-#{@current_resource.version}")
end
end
end
diff --git a/chef/lib/chef/provider/package/macports.rb b/chef/lib/chef/provider/package/macports.rb
new file mode 100644
index 0000000000..071d52939d
--- /dev/null
+++ b/chef/lib/chef/provider/package/macports.rb
@@ -0,0 +1,105 @@
+class Chef
+ class Provider
+ class Package
+ class Macports < Chef::Provider::Package
+ 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 = macports_candidate_version
+
+ if !@new_resource.version and !@candidate_version
+ raise Chef::Exceptions::Package, "Could not get a candidate version for this package -- #{@new_resource.name} does not seem to be a valid package!"
+ end
+
+ Chef::Log.debug("MacPorts candidate version is #{@candidate_version}") if @candidate_version
+
+ @current_resource
+ end
+
+ def current_installed_version
+ command = "port installed #{@new_resource.package_name}"
+ output = get_response_from_command(command)
+
+ response = nil
+ output.each_line do |line|
+ match = line.match(/^.+ @([^\s]+) \(active\)$/)
+ response = match[1] if match
+ end
+ response
+ end
+
+ def macports_candidate_version
+ command = "port info --version #{@new_resource.package_name}"
+ output = get_response_from_command(command)
+
+ match = output.match(/^version: (.+)$/)
+
+ match ? match[1] : nil
+ end
+
+ def install_package(name, version)
+ unless @current_resource.version == version
+ command = "port install #{name}"
+ command << " @#{version}" if version and !version.empty?
+ run_command(
+ :command => command
+ )
+ end
+ end
+
+ def purge_package(name, version)
+ command = "port uninstall #{name}"
+ command << " @#{version}" if version and !version.empty?
+ run_command(
+ :command => command
+ )
+ end
+
+ def remove_package(name, version)
+ command = "port deactivate #{name}"
+ command << " @#{version}" if version and !version.empty?
+
+ run_command(
+ :command => command
+ )
+ end
+
+ def upgrade_package(name, version)
+ # Saving this to a variable -- weird rSpec behavior
+ # happens otherwise...
+ current_version = @current_resource.version
+
+ if current_version.nil? or current_version.empty?
+ # Macports doesn't like when you upgrade a package
+ # that hasn't been installed.
+ install_package(name, version)
+ elsif current_version != version
+ run_command(
+ :command => "port upgrade #{name} @#{version}"
+ )
+ end
+ end
+
+ private
+ def get_response_from_command(command)
+ output = nil
+ status = popen4(command) do |pid, stdin, stdout, stderr|
+ begin
+ output = stdout.read
+ rescue Exception
+ raise Chef::Exceptions::Package, "Could not read from STDOUT on command: #{command}"
+ end
+ end
+ unless status.exitstatus == 0 || status.exitstatus == 1
+ raise Chef::Exceptions::Package, "#{command} failed - #{status.insect}!"
+ end
+ output
+ end
+ end
+ end
+ end
+end
diff --git a/chef/lib/chef/provider/package/portage.rb b/chef/lib/chef/provider/package/portage.rb
index e1ff63f3e4..07e19ff755 100644
--- a/chef/lib/chef/provider/package/portage.rb
+++ b/chef/lib/chef/provider/package/portage.rb
@@ -28,22 +28,24 @@ class Chef
def load_current_resource
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
-
- status = popen4("emerge --color n --nospinner --search #{@new_resource.package_name.split('/').last}") do |pid, stdin, stdout, stderr|
- available, installed = parse_emerge(@new_resource.package_name, stdout.read)
-
- if installed == "[ Not Installed ]"
- @current_resource.version(nil)
- else
- @current_resource.version(installed)
- end
- @candidate_version = available
- end
- unless status.exitstatus == 0
- raise Chef::Exceptions::Package, "emerge --search failed - #{status.inspect}!"
+ category = @new_resource.package_name.split('/').first
+ pkg = @new_resource.package_name.split('/').last
+
+ @current_resource.version(nil)
+
+ catdir = "/var/db/pkg/#{category}"
+
+ if( ::File.exists?(catdir) )
+ Dir.entries(catdir).each do |entry|
+ if(entry =~ /^#{Regexp.escape(pkg)}\-(.+)/)
+ @current_resource.version($1)
+ Chef::Log.debug("Got current version #{$1}")
+ break
+ end
+ end
end
-
+
@current_resource
end
@@ -65,11 +67,34 @@ class Chef
available = installed unless available
[available, installed]
end
+
+ def candidate_version
+ return @candidate_version if @candidate_version
+
+ status = popen4("emerge --color n --nospinner --search #{@new_resource.package_name.split('/').last}") do |pid, stdin, stdout, stderr|
+ available, installed = parse_emerge(@new_resource.package_name, stdout.read)
+ @candidate_version = available
+ end
+
+ unless status.exitstatus == 0
+ raise Chef::Exceptions::Package, "emerge --search failed - #{status.inspect}!"
+ end
+
+ @candidate_version
+
+ end
def install_package(name, version)
+ pkg = "=#{name}-#{version}"
+
+ if(version =~ /^\~(.+)/)
+ # If we start with a tilde
+ pkg = "~#{name}-#{$1}"
+ end
+
run_command(
- :command => "emerge -g --color n --nospinner --quiet =#{name}-#{version}"
+ :command => "emerge -g --color n --nospinner --quiet #{pkg}"
)
end
@@ -78,8 +103,14 @@ class Chef
end
def remove_package(name, version)
+ if(version)
+ pkg = "=#{@new_resource.package_name}-#{version}"
+ else
+ pkg = "#{@new_resource.package_name}"
+ end
+
run_command(
- :command => "emerge --unmerge --color n --nospinner --quiet #{@new_resource.package_name}"
+ :command => "emerge --unmerge --color n --nospinner --quiet #{pkg}"
)
end
diff --git a/chef/lib/chef/provider/package/rpm.rb b/chef/lib/chef/provider/package/rpm.rb
index 4477a542ed..ab39f47573 100644
--- a/chef/lib/chef/provider/package/rpm.rb
+++ b/chef/lib/chef/provider/package/rpm.rb
@@ -81,10 +81,17 @@ class Chef
end
def remove_package(name, version)
- run_command(
- :command => "rpm -e #{@new_resource.name}"
- )
+ if version
+ run_command(
+ :command => "rpm -e #{name}-#{version}"
+ )
+ else
+ run_command(
+ :command => "rpm -e #{name}"
+ )
+ end
end
+
end
end
end
diff --git a/chef/lib/chef/provider/package/rubygems.rb b/chef/lib/chef/provider/package/rubygems.rb
index c237f3e438..443085d8f9 100644
--- a/chef/lib/chef/provider/package/rubygems.rb
+++ b/chef/lib/chef/provider/package/rubygems.rb
@@ -68,6 +68,12 @@ class Chef
raise Chef::Exceptions::Package, "#{gem_binary_path} list --local failed - #{status.inspect}!"
end
+ @current_resource
+ end
+
+ def candidate_version
+ 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)
@@ -79,13 +85,13 @@ class Chef
@candidate_version = installed_versions.first
end
end
+
+ @candidate_version
end
unless status.exitstatus == 0
raise Chef::Exceptions::Package, "#{gem_binary_path} list --remote failed - #{status.inspect}!"
end
-
- @current_resource
end
def install_package(name, version)
diff --git a/chef/lib/chef/provider/package/yum.rb b/chef/lib/chef/provider/package/yum.rb
index 5eab817f0e..93eb37d542 100644
--- a/chef/lib/chef/provider/package/yum.rb
+++ b/chef/lib/chef/provider/package/yum.rb
@@ -142,9 +142,16 @@ class Chef
end
def remove_package(name, version)
- run_command(
- :command => "yum -q -y remove #{name}-#{version}"
- )
+ if version
+ run_command(
+ :command => "yum -q -y remove #{name}-#{version}"
+ )
+ else
+ run_command(
+ :command => "yum -q -y remove #{name}"
+ )
+ end
+
@yum.flush
end
diff --git a/chef/lib/chef/provider/remote_directory.rb b/chef/lib/chef/provider/remote_directory.rb
index 5725be7296..438176f59a 100644
--- a/chef/lib/chef/provider/remote_directory.rb
+++ b/chef/lib/chef/provider/remote_directory.rb
@@ -19,6 +19,7 @@
require 'chef/provider/file'
require 'chef/provider/directory'
require 'chef/rest'
+require 'chef/mixin/find_preferred_file'
require 'chef/resource/directory'
require 'chef/resource/remote_file'
require 'chef/platform'
@@ -29,6 +30,8 @@ require 'net/https'
class Chef
class Provider
class RemoteDirectory < Chef::Provider::Directory
+
+ include ::Chef::Mixin::FindPreferredFile
def action_create
super
@@ -37,51 +40,130 @@ class Chef
do_recursive
end
- def action_create_if_missing
- raise Chef::Exceptions::UnsupportedAction, "Remote Directories do not support create_if_missing."
- end
-
- def do_recursive
- Chef::Log.debug("Doing a recursive directory transfer for #{@new_resource}")
+ protected
- r = Chef::REST.new(Chef::Config[:remotefile_url])
+ def do_recursive
+ if Chef::Config[:solo]
+ Chef::Log.debug("Doing a local recursive directory copy for #{@new_resource}")
+ files_to_transfer = files_for_directory(@new_resource.source)
+ else
+ Chef::Log.debug("Doing a remote recursive directory transfer for #{@new_resource}")
+ r = Chef::REST.new(Chef::Config[:remotefile_url])
+ files_to_transfer = r.get_rest(generate_url(@new_resource.source, "files", { :recursive => "true" }))
+ end
+
+ files_to_transfer.each do |remote_file_source|
+ fetch_remote_file(remote_file_source)
+ end
+ end
- files_to_transfer = r.get_rest(generate_url(@new_resource.source, "files", { :recursive => "true" }))
+ def fetch_remote_file(remote_file_source)
+ full_path = ::File.join(@new_resource.path, remote_file_source)
+ full_dir = ::File.dirname(full_path)
+
+ if !::File.directory?(full_dir)
+ create_directory(full_dir)
+ end
+
+ remote_file = Chef::Resource::RemoteFile.new(full_path, nil, @node)
+ remote_file.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
+ remote_file.source(::File.join(@new_resource.source, remote_file_source))
+ remote_file.mode(@new_resource.files_mode) if @new_resource.files_mode
+ remote_file.group(@new_resource.files_group) if @new_resource.files_group
+ remote_file.owner(@new_resource.files_owner) if @new_resource.files_owner
+ remote_file.backup(@new_resource.files_backup) if @new_resource.files_backup
+
+ rf_provider_class = Chef::Platform.find_provider_for_node(@node, remote_file)
+ rf_provider = rf_provider_class.new(@node, remote_file)
+ rf_provider.load_current_resource
+ rf_provider.action_create
+ @new_resource.updated = true if rf_provider.new_resource.updated
+ end
- files_to_transfer.each do |remote_file_source|
- full_path = ::File.join(@new_resource.path, remote_file_source)
- full_dir = ::File.dirname(full_path)
- unless ::File.directory?(full_dir)
- new_dir = Chef::Resource::Directory.new(full_dir, nil, @node)
- new_dir.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
- new_dir.mode(@new_resource.mode)
- new_dir.group(@new_resource.group)
- new_dir.owner(@new_resource.owner)
- new_dir.recursive(true)
+ def create_directory
+ new_dir = Chef::Resource::Directory.new(full_dir, nil, @node)
+ new_dir.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
+ new_dir.mode(@new_resource.mode)
+ new_dir.group(@new_resource.group)
+ new_dir.owner(@new_resource.owner)
+ new_dir.recursive(true)
+
+ d_provider_class = Chef::Platform.find_provider_for_node(@node, new_dir)
+ d_provider = d_provider_class.new(@node, new_dir)
+ d_provider.load_current_resource
+ d_provider.action_create
+ @new_resource.updated = true if d_provider.new_resource.updated
+ end
- d_provider_class = Chef::Platform.find_provider_for_node(@node, new_dir)
- d_provider = d_provider_class.new(@node, new_dir)
- d_provider.load_current_resource
- d_provider.action_create
- @new_resource.updated = true if d_provider.new_resource.updated
- end
-
- remote_file = Chef::Resource::RemoteFile.new(full_path, nil, @node)
- remote_file.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
- remote_file.source(::File.join(@new_resource.source, remote_file_source))
- remote_file.mode(@new_resource.files_mode) if @new_resource.files_mode
- remote_file.group(@new_resource.files_group) if @new_resource.files_group
- remote_file.owner(@new_resource.files_owner) if @new_resource.files_owner
- remote_file.backup(@new_resource.files_backup) if @new_resource.files_backup
-
- rf_provider_class = Chef::Platform.find_provider_for_node(@node, remote_file)
- rf_provider = rf_provider_class.new(@node, remote_file)
- rf_provider.load_current_resource
- rf_provider.action_create
- @new_resource.updated = true if rf_provider.new_resource.updated
+ def action_create_if_missing
+ raise Chef::Exceptions::UnsupportedAction, "Remote Directories do not support create_if_missing."
+ end
+ # Pulled from chef-server-slice files controller
+
+ def files_for_directory(path)
+ directory = find_preferred_file(
+ @new_resource.cookbook_name,
+ :remote_file,
+ path,
+ @node[:fqdn],
+ @node[:platform],
+ @node[:platform_version]
+ )
+
+ unless (directory && ::File.directory?(directory))
+ raise NotFound, "Cannot find a suitable directory"
+ end
+
+ directory_listing = Array.new
+ Dir[::File.join(directory, '**', '*')].sort { |a,b| b <=> a }.each do |file|
+ next if ::File.directory?(file)
+ file =~ /^#{directory}\/(.+)$/
+ directory_listing << $1
end
+ directory_listing
end
-
end
+
+
+ def do_recursive_old
+ Chef::Log.debug("Doing a recursive directory transfer for #{@new_resource}")
+
+ r = Chef::REST.new(Chef::Config[:remotefile_url])
+
+ files_to_transfer = r.get_rest(generate_url(@new_resource.source, "files", { :recursive => "true" }))
+
+ files_to_transfer.each do |remote_file_source|
+ full_path = ::File.join(@new_resource.path, remote_file_source)
+ full_dir = ::File.dirname(full_path)
+ unless ::File.directory?(full_dir)
+ new_dir = Chef::Resource::Directory.new(full_dir, nil, @node)
+ new_dir.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
+ new_dir.mode(@new_resource.mode)
+ new_dir.group(@new_resource.group)
+ new_dir.owner(@new_resource.owner)
+ new_dir.recursive(true)
+
+ d_provider_class = Chef::Platform.find_provider_for_node(@node, new_dir)
+ d_provider = d_provider_class.new(@node, new_dir)
+ d_provider.load_current_resource
+ d_provider.action_create
+ @new_resource.updated = true if d_provider.new_resource.updated
+ end
+
+ remote_file = Chef::Resource::RemoteFile.new(full_path, nil, @node)
+ remote_file.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
+ remote_file.source(::File.join(@new_resource.source, remote_file_source))
+ remote_file.mode(@new_resource.files_mode) if @new_resource.files_mode
+ remote_file.group(@new_resource.files_group) if @new_resource.files_group
+ remote_file.owner(@new_resource.files_owner) if @new_resource.files_owner
+ remote_file.backup(@new_resource.files_backup) if @new_resource.files_backup
+
+ rf_provider_class = Chef::Platform.find_provider_for_node(@node, remote_file)
+ rf_provider = rf_provider_class.new(@node, remote_file)
+ rf_provider.load_current_resource
+ rf_provider.action_create
+ @new_resource.updated = true if rf_provider.new_resource.updated
+ end
+ end
end
end
diff --git a/chef/lib/chef/provider/route.rb b/chef/lib/chef/provider/route.rb
index 16d7d9c43d..b45813b0c4 100644
--- a/chef/lib/chef/provider/route.rb
+++ b/chef/lib/chef/provider/route.rb
@@ -19,6 +19,7 @@
require 'chef/log'
require 'chef/mixin/command'
require 'chef/provider'
+require 'erb'
class Chef
class Provider
@@ -46,7 +47,7 @@ class Chef
end
unless status.exitstatus == 0
- raise Chef::Exceptions::Route, "route failed - #{status.inspect}!"
+ raise Chef::Exception::Route, "route failed - #{status.inspect}!"
end
@current_resource
@@ -72,6 +73,8 @@ class Chef
else
Chef::Log.debug("Route #{@current_resource} already exists")
end
+ # Write out the config files
+ generate_config
end
def action_delete
@@ -91,6 +94,25 @@ class Chef
Chef::Log.debug("Route #{@current_resource} does not exist")
end
end
+
+ def generate_config
+ b = binding
+ case node[:platform]
+ when ("centos" || "redhat" || "fedora")
+ content = %{
+<% if @new_resource.networking %>NETWORKING=<%= @new_resource.networking %><% end %>
+<% if @new_resource.networking_ipv6 %>NETWORKING_IPV6=<%= @new_resource.networking_ipv6 %><% end %>
+<% if @new_resource.hostname %>HOSTNAME=<%= @new_resource.hostname %><% end %>
+<% if @new_resource.name %>GATEWAY=<%= @new_resource.name %><% end %>
+<% if @new_resource.domainname %>DOMAINNAME=<%= @new_resource.domainname %><% end %>
+<% if @new_resource.domainname %>DOMAIN=<%= @new_resource.domainname %><% end %>
+ }
+ template = ::ERB.new(content)
+ network_file = ::File.new("/etc/sysconfig/network", "w")
+ network_file.puts(template.result(b))
+ network_file.close
+ end
+ end
end
end
end
diff --git a/chef/lib/chef/provider/service/redhat.rb b/chef/lib/chef/provider/service/redhat.rb
index 73e691a148..64bfdc4076 100644
--- a/chef/lib/chef/provider/service/redhat.rb
+++ b/chef/lib/chef/provider/service/redhat.rb
@@ -49,11 +49,11 @@ class Chef
end
def enable_service()
- run_command(:command => "/sbin/chkconfig --add #{@new_resource.service_name}")
+ run_command(:command => "/sbin/chkconfig #{@new_resource.service_name} on")
end
def disable_service()
- run_command(:command => "/sbin/chkconfig --del #{@new_resource.service_name}")
+ run_command(:command => "/sbin/chkconfig #{@new_resource.service_name} off")
end
end
diff --git a/chef/lib/chef/recipe.rb b/chef/lib/chef/recipe.rb
index 96e6430620..7417407924 100644
--- a/chef/lib/chef/recipe.rb
+++ b/chef/lib/chef/recipe.rb
@@ -203,4 +203,4 @@ class Chef
end
end
end
-end \ No newline at end of file
+end
diff --git a/chef/lib/chef/resource.rb b/chef/lib/chef/resource.rb
index dc915cac14..e2bf9fb302 100644
--- a/chef/lib/chef/resource.rb
+++ b/chef/lib/chef/resource.rb
@@ -136,23 +136,20 @@ class Chef
ignore_failure(arg)
end
- def notifies(action, resources, timing=:delayed)
- timing = check_timing(timing)
- rarray = resources.kind_of?(Array) ? resources : [ resources ]
- rarray.each do |resource|
- action_sym = action.to_sym
- if @actions.has_key?(action_sym)
- @actions[action_sym][timing] << resource
- else
- @actions[action_sym] = Hash.new
- @actions[action_sym][:delayed] = Array.new
- @actions[action_sym][:immediate] = Array.new
- @actions[action_sym][timing] << resource
- end
+ def notifies(*args)
+ raise ArgumentError, "Wrong number of arguments (should be 1, 2, or 3)" unless ( args.size > 0 && args.size < 4)
+ 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])
+ end
+ end
end
- true
end
-
+
def resources(*args)
@collection.resources(*args)
end
@@ -239,5 +236,22 @@ class Chef
end
timing
end
+
+ def notifies_helper(action, resources, timing=:delayed)
+ timing = check_timing(timing)
+ rarray = resources.kind_of?(Array) ? resources : [ resources ]
+ rarray.each do |resource|
+ action_sym = action.to_sym
+ if @actions.has_key?(action_sym)
+ @actions[action_sym][timing] << resource
+ else
+ @actions[action_sym] = Hash.new
+ @actions[action_sym][:delayed] = Array.new
+ @actions[action_sym][:immediate] = Array.new
+ @actions[action_sym][timing] << resource
+ end
+ end
+ true
+ end
end
end
diff --git a/chef/lib/chef/resource/group.rb b/chef/lib/chef/resource/group.rb
index ddf583198c..25d8598f42 100644
--- a/chef/lib/chef/resource/group.rb
+++ b/chef/lib/chef/resource/group.rb
@@ -27,6 +27,7 @@ class Chef
@gid = nil
@members = []
@action = :create
+ @append = false
@allowed_actions.push(:create, :remove, :modify, :manage)
end
@@ -55,6 +56,13 @@ class Chef
)
end
+ def append(arg=nil)
+ set_or_return(
+ :append,
+ arg,
+ :kind_of => [ TrueClass, FalseClass ]
+ )
+ end
end
end
end
diff --git a/chef/lib/chef/resource/ifconfig.rb b/chef/lib/chef/resource/ifconfig.rb
new file mode 100644
index 0000000000..22096f486b
--- /dev/null
+++ b/chef/lib/chef/resource/ifconfig.rb
@@ -0,0 +1,134 @@
+#
+# Author:: Jason K. Jackson (jason.jackson@monster.com)
+# Copyright:: Copyright (c) 2009 Jason K. Jackson
+# 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'
+
+class Chef
+ class Resource
+ class Ifconfig < Chef::Resource
+
+ def initialize(name, collection=nil, node=nil)
+ super(name, collection, node)
+ @resource_name = :ifconfig
+ @target = name
+ @action = :add
+ @allowed_actions.push(:add, :delete)
+ @hwaddr = nil
+ @mask = nil
+ @inet_addr = nil
+ @bcast = nil
+ @mtu = nil
+ @metric = nil
+ @device = nil
+ @onboot = nil
+ @network = nil
+ @bootproto = nil
+ end
+
+ def target(arg=nil)
+ set_or_return(
+ :target,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def device(arg=nil)
+ set_or_return(
+ :device,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def hwaddr(arg=nil)
+ set_or_return(
+ :hwaddr,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def inet_addr(arg=nil)
+ set_or_return(
+ :inet_addr,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def bcast(arg=nil)
+ set_or_return(
+ :bcast,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def mask(arg=nil)
+ set_or_return(
+ :mask,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def mtu(arg=nil)
+ set_or_return(
+ :mtu,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def metric(arg=nil)
+ set_or_return(
+ :metric,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def onboot(arg=nil)
+ set_or_return(
+ :onboot,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def network(arg=nil)
+ set_or_return(
+ :network,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def bootproto(arg=nil)
+ set_or_return(
+ :bootproto,
+ arg,
+ :kind_of => String
+ )
+ end
+ end
+ end
+end
+
+
diff --git a/chef/lib/chef/resource/macports_package.rb b/chef/lib/chef/resource/macports_package.rb
new file mode 100644
index 0000000000..b72832260e
--- /dev/null
+++ b/chef/lib/chef/resource/macports_package.rb
@@ -0,0 +1,29 @@
+#
+# Author:: David Balatero (<dbalatero@gmail.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.
+#
+
+class Chef
+ class Resource
+ class MacportsPackage < Chef::Resource::Package
+ def initialize(name, collection = nil, node = nil)
+ super(name, collection, node)
+ @resource_name = :macports_package
+ @provider = Chef::Provider::Package::Macports
+ end
+ end
+ end
+end
diff --git a/chef/lib/chef/resource/route.rb b/chef/lib/chef/resource/route.rb
index 0c124b587a..276dbedb31 100644
--- a/chef/lib/chef/resource/route.rb
+++ b/chef/lib/chef/resource/route.rb
@@ -33,6 +33,51 @@ class Chef
@metric = nil
@device = nil
@route_type = :host
+ @networking = nil
+ @networking_ipv6 = nil
+ @hostname = nil
+ @domainname = nil
+ @domain = nil
+ end
+
+ def networking(arg=nil)
+ set_or_return(
+ :networking,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def networking_ipv6(arg=nil)
+ set_or_return(
+ :networking_ipv6,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def hostname(arg=nil)
+ set_or_return(
+ :hostname,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def domainname(arg=nil)
+ set_or_return(
+ :domainname,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ def domain(arg=nil)
+ set_or_return(
+ :domain,
+ arg,
+ :kind_of => String
+ )
end
def target(arg=nil)
diff --git a/chef/lib/chef/rest.rb b/chef/lib/chef/rest.rb
index f712d02ab7..9c51e7b9df 100644
--- a/chef/lib/chef/rest.rb
+++ b/chef/lib/chef/rest.rb
@@ -23,15 +23,20 @@ require 'net/https'
require 'uri'
require 'json'
require 'tempfile'
+require 'singleton'
class Chef
class REST
+
+ class CookieJar < Hash
+ include Singleton
+ end
attr_accessor :url, :cookies
def initialize(url)
@url = url
- @cookies = Hash.new
+ @cookies = CookieJar.instance
end
# Register for an OpenID
diff --git a/chef/lib/chef/tasks/chef_repo.rake b/chef/lib/chef/tasks/chef_repo.rake
new file mode 100644
index 0000000000..0d824e2bd6
--- /dev/null
+++ b/chef/lib/chef/tasks/chef_repo.rake
@@ -0,0 +1,199 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008, 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 'rubygems'
+require 'json'
+require 'chef'
+require 'chef/cookbook/metadata'
+require 'tempfile'
+
+desc "Update your repository from source control"
+task :update do
+ puts "** Updating your repository"
+
+ case $vcs
+ when :svn
+ sh %{svn up}
+ when :git
+ pull = false
+ pull = true if File.join(TOPDIR, ".git", "remotes", "origin")
+ IO.foreach(File.join(TOPDIR, ".git", "config")) do |line|
+ pull = true if line =~ /\[remote "origin"\]/
+ end
+ if pull
+ sh %{git pull}
+ else
+ puts "* Skipping git pull, no origin specified"
+ end
+ else
+ puts "* No SCM configured, skipping update"
+ end
+end
+
+desc "Test your cookbooks for syntax errors"
+task :test do
+ puts "** Testing your cookbooks for syntax errors"
+
+ recipes = ["*cookbooks"].map { |folder|
+ Dir[File.join(TOPDIR, folder, "**", "*.rb")]
+ }.flatten
+
+ recipes.each do |recipe|
+ print "Testing recipe #{recipe}: "
+ sh %{ruby -c #{recipe}} do |ok, res|
+ if ! ok
+ raise "Syntax error in #{recipe}"
+ end
+ end
+ end
+end
+
+desc "Install the latest copy of the repository on this Chef Server"
+task :install => [ :update, :test ] do
+ puts "** Installing your cookbooks"
+ directories = [
+ COOKBOOK_PATH,
+ SITE_COOKBOOK_PATH,
+ CHEF_CONFIG_PATH
+ ]
+ puts "* Creating Directories"
+ directories.each do |dir|
+ sh "sudo mkdir -p #{dir}"
+ sh "sudo chown root #{dir}"
+ end
+ puts "* Installing new Cookbooks"
+ sh "sudo rsync -rlP --delete --exclude '.svn' cookbooks/ #{COOKBOOK_PATH}"
+ puts "* Installing new Site Cookbooks"
+ sh "sudo rsync -rlP --delete --exclude '.svn' cookbooks/ #{COOKBOOK_PATH}"
+ puts "* Installing new Chef Server Config"
+ sh "sudo cp config/server.rb #{CHEF_SERVER_CONFIG}"
+ puts "* Installing new Chef Client Config"
+ sh "sudo cp config/client.rb #{CHEF_CLIENT_CONFIG}"
+end
+
+desc "By default, run rake test"
+task :default => [ :test ]
+
+desc "Create a new cookbook (with COOKBOOK=name)"
+task :new_cookbook do
+ create_cookbook(File.join(TOPDIR, "cookbooks"))
+end
+
+def create_cookbook(dir)
+ raise "Must provide a COOKBOOK=" unless ENV["COOKBOOK"]
+ puts "** Creating cookbook #{ENV["COOKBOOK"]}"
+ sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "attributes")}"
+ sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "recipes")}"
+ sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "definitions")}"
+ sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "libraries")}"
+ sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "files", "default")}"
+ sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "templates", "default")}"
+ unless File.exists?(File.join(dir, ENV["COOKBOOK"], "recipes", "default.rb"))
+ open(File.join(dir, ENV["COOKBOOK"], "recipes", "default.rb"), "w") do |file|
+ file.puts <<-EOH
+#
+# Cookbook Name:: #{ENV["COOKBOOK"]}
+# Recipe:: default
+#
+# Copyright #{Time.now.year}, #{COMPANY_NAME}
+#
+EOH
+ case NEW_COOKBOOK_LICENSE
+ when :apachev2
+ file.puts <<-EOH
+# 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.
+#
+EOH
+ when :none
+ file.puts <<-EOH
+# All rights reserved - Do Not Redistribute
+#
+EOH
+ end
+ end
+ end
+end
+
+desc "Create a new self-signed SSL certificate for FQDN=foo.example.com"
+task :ssl_cert do
+ $expect_verbose = true
+ fqdn = ENV["FQDN"]
+ fqdn =~ /^(.+?)\.(.+)$/
+ hostname = $1
+ domain = $2
+ raise "Must provide FQDN!" unless fqdn && hostname && domain
+ puts "** Creating self signed SSL Certificate for #{fqdn}"
+ sh("(cd #{CADIR} && openssl genrsa 2048 > #{fqdn}.key)")
+ sh("(cd #{CADIR} && chmod 644 #{fqdn}.key)")
+ puts "* Generating Self Signed Certificate Request"
+ tf = Tempfile.new("#{fqdn}.ssl-conf")
+ ssl_config = <<EOH
+[ req ]
+distinguished_name = req_distinguished_name
+prompt = no
+
+[ req_distinguished_name ]
+C = #{SSL_COUNTRY_NAME}
+ST = #{SSL_STATE_NAME}
+L = #{SSL_LOCALITY_NAME}
+O = #{COMPANY_NAME}
+OU = #{SSL_ORGANIZATIONAL_UNIT_NAME}
+CN = #{fqdn}
+emailAddress = #{SSL_EMAIL_ADDRESS}
+EOH
+ tf.puts(ssl_config)
+ tf.close
+ sh("(cd #{CADIR} && openssl req -config '#{tf.path}' -new -x509 -nodes -sha1 -days 3650 -key #{fqdn}.key > #{fqdn}.crt)")
+ sh("(cd #{CADIR} && openssl x509 -noout -fingerprint -text < #{fqdn}.crt > #{fqdn}.info)")
+ sh("(cd #{CADIR} && cat #{fqdn}.crt #{fqdn}.key > #{fqdn}.pem)")
+ sh("(cd #{CADIR} && chmod 644 #{fqdn}.pem)")
+end
+
+desc "Build cookbook metadata"
+task :metadata do
+ Chef::Config[:cookbook_path] = [ COOKBOOK_PATH, SITE_COOKBOOK_PATH ]
+ cl = Chef::CookbookLoader.new
+ cl.each do |cookbook|
+ if ENV['COOKBOOK']
+ next unless cookbook.name.to_s == ENV['COOKBOOK']
+ end
+ cook_meta = Chef::Cookbook::Metadata.new(cookbook)
+ Chef::Config.cookbook_path.each do |cdir|
+ metadata_rb_file = File.join(cdir, cookbook.name.to_s, 'metadata.rb')
+ metadata_json_file = File.join(cdir, cookbook.name.to_s, 'metadata.json')
+ if File.exists?(metadata_rb_file)
+ puts "Generating metadata for #{cookbook.name}"
+ cook_meta.from_file(metadata_rb_file)
+ File.open(metadata_json_file, "w") do |f|
+ f.write(JSON.pretty_generate(cook_meta))
+ end
+ end
+ end
+ end
+end
+
diff --git a/chef/spec/unit/cookbook/metadata_spec.rb b/chef/spec/unit/cookbook/metadata_spec.rb
new file mode 100644
index 0000000000..b3242600c2
--- /dev/null
+++ b/chef/spec/unit/cookbook/metadata_spec.rb
@@ -0,0 +1,444 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
+require 'chef/cookbook/metadata'
+
+describe Chef::Cookbook::Metadata do
+ before(:each) do
+ @cookbook = Chef::Cookbook.new('test_cookbook')
+ @meta = Chef::Cookbook::Metadata.new(@cookbook)
+ end
+
+ describe "initialize" do
+ it "should return a Chef::Cookbook::Metadata object" do
+ @meta.should be_a_kind_of(Chef::Cookbook::Metadata)
+ end
+
+ it "should allow a cookbook as the first argument" do
+ lambda { Chef::Cookbook::Metadata.new(@cookbook) }.should_not raise_error
+ end
+
+ it "should allow an maintainer name for the second argument" do
+ lambda { Chef::Cookbook::Metadata.new(@cookbook, 'Bobo T. Clown') }.should_not raise_error
+ end
+
+ it "should set the maintainer name from the second argument" do
+ md = Chef::Cookbook::Metadata.new(@cookbook, 'Bobo T. Clown')
+ md.maintainer.should == 'Bobo T. Clown'
+ end
+
+ it "should allow an maintainer email for the third argument" do
+ lambda { Chef::Cookbook::Metadata.new(@cookbook, 'Bobo T. Clown', 'bobo@clown.co') }.should_not raise_error
+ end
+
+ it "should set the maintainer email from the third argument" do
+ md = Chef::Cookbook::Metadata.new(@cookbook, 'Bobo T. Clown', 'bobo@clown.co')
+ md.maintainer_email.should == 'bobo@clown.co'
+ end
+
+ it "should allow a license for the fourth argument" do
+ lambda { Chef::Cookbook::Metadata.new(@cookbook, 'Bobo T. Clown', 'bobo@clown.co', 'Clown License v1') }.should_not raise_error
+ end
+
+ it "should set the license from the fourth argument" do
+ md = Chef::Cookbook::Metadata.new(@cookbook, 'Bobo T. Clown', 'bobo@clown.co', 'Clown License v1')
+ md.license.should == 'Clown License v1'
+ end
+ end
+
+ describe "cookbook" do
+ it "should return the cookbook we were initialized with" do
+ @meta.cookbook.should eql(@cookbook)
+ end
+ end
+
+ describe "name" do
+ it "should return the name of the cookbook" do
+ @meta.name.should eql(@cookbook.name)
+ end
+ end
+
+ describe "platforms" do
+ it "should return the current platform hash" do
+ @meta.platforms.should be_a_kind_of(Hash)
+ end
+ end
+
+ describe "adding a supported platform" do
+ it "should support adding a supported platform with a single expression" do
+ @meta.supports("ubuntu", ">= 8.04")
+ @meta.platforms["ubuntu"].should == [ '>= 8.04' ]
+ end
+
+ it "should support adding a supported platform with multiple expressions" do
+ @meta.supports("ubuntu", ">= 8.04", "= 9.04")
+ @meta.platforms["ubuntu"].should == [ '>= 8.04', "= 9.04" ]
+ end
+ end
+
+ describe "meta-data attributes" do
+ params = {
+ :maintainer => "Adam Jacob",
+ :maintainer_email => "adam@opscode.com",
+ :license => "Apache v2.0",
+ :description => "Foobar!",
+ :long_description => "Much Longer\nSeriously",
+ :version => "0.6"
+ }
+ params.sort { |a,b| a.to_s <=> b.to_s }.each do |field, field_value|
+ describe field do
+ it "should be set-able via #{field}" do
+ @meta.send(field, field_value).should eql(field_value)
+ end
+ it "should be get-able via #{field}" do
+ @meta.send(field, field_value)
+ @meta.send(field).should eql(field_value)
+ end
+ end
+ end
+ end
+
+ describe "dependency specification" do
+ dep_types = {
+ :depends => [ :dependencies, "foo::bar", ">> 0.2" ],
+ :recommends => [ :recommendations, "foo::bar", ">> 0.2" ],
+ :suggests => [ :suggestions, "foo::bar", ">> 0.2" ],
+ :conflicts => [ :conflicting, "foo::bar", ">> 0.2" ],
+ :provides => [ :providing, "foo::bar", ">> 0.2" ],
+ :replaces => [ :replacing, "foo::bar", ">> 0.2" ],
+ }
+ dep_types.sort { |a,b| a.to_s <=> b.to_s }.each do |dep, dep_args|
+ check_with = dep_args.shift
+ describe dep do
+ it "should be set-able via #{dep}" do
+ @meta.send(dep, *dep_args).should == [dep_args[1]]
+ end
+ it "should be get-able via #{check_with}" do
+ @meta.send(dep, *dep_args)
+ @meta.send(check_with).should == { dep_args[0] => [dep_args[1]] }
+ end
+ end
+ end
+ end
+
+ describe "cookbook attributes" do
+ it "should allow you set an attributes metadata" do
+ attrs = {
+ "display_name" => "MySQL Databases",
+ "multiple_values" => true,
+ "type" => 'string',
+ "required" => false,
+ "recipes" => [ "mysql::server", "mysql::master" ],
+ "default" => [ ]
+ }
+ @meta.attribute("/db/mysql/databases", attrs).should == attrs
+ end
+
+ it "should not accept anything but a string for display_name" do
+ lambda {
+ @meta.attribute("db/mysql/databases", :display_name => "foo")
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @meta.attribute("db/mysql/databases", :display_name => Hash.new)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "should not accept anything but a string for the description" do
+ lambda {
+ @meta.attribute("db/mysql/databases", :description => "foo")
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @meta.attribute("db/mysql/databases", :description => Hash.new)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "should let multiple_values be true or false" do
+ lambda {
+ @meta.attribute("db/mysql/databases", :multiple_values => true)
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @meta.attribute("db/mysql/databases", :multiple_values => false)
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @meta.attribute("db/mysql/databases", :multiple_values => Hash.new)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "should set multiple_values to false by default" do
+ @meta.attribute("db/mysql/databases", {})
+ @meta.attributes["db/mysql/databases"][:multiple_values].should == false
+ end
+
+ it "should let type be string, array or hash" do
+ lambda {
+ @meta.attribute("db/mysql/databases", :type => "string")
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @meta.attribute("db/mysql/databases", :type => "array")
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @meta.attribute("db/mysql/databases", :type => "hash")
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @meta.attribute("db/mysql/databases", :type => Array.new)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "should let required be true or false" do
+ lambda {
+ @meta.attribute("db/mysql/databases", :required => true)
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @meta.attribute("db/mysql/databases", :required => false)
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @meta.attribute("db/mysql/databases", :required => Hash.new)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "should set required to false by default" do
+ @meta.attribute("db/mysql/databases", {})
+ @meta.attributes["db/mysql/databases"][:required].should == false
+ end
+
+ it "should make sure recipes is an array" do
+ lambda {
+ @meta.attribute("db/mysql/databases", :recipes => [])
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @meta.attribute("db/mysql/databases", :required => Hash.new)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "should set recipes to an empty array by default" do
+ @meta.attribute("db/mysql/databases", {})
+ @meta.attributes["db/mysql/databases"][:recipes].should == []
+ end
+
+ it "should allow the default value to be a string, array, or hash" do
+ lambda {
+ @meta.attribute("db/mysql/databases", :default => [])
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @meta.attribute("db/mysql/databases", :default => {})
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @meta.attribute("db/mysql/databases", :default => "alice in chains")
+ }.should_not raise_error(ArgumentError)
+ lambda {
+ @meta.attribute("db/mysql/databases", :required => :not_gonna_do_it)
+ }.should raise_error(ArgumentError)
+ end
+
+ end
+
+ describe "checking version expression" do
+ it "should accept >> 8.04" do
+ @meta._check_version_expression(">> 8.04").should == [ ">>", "8.04" ]
+ end
+
+ it "should accept >= 8.04" do
+ @meta._check_version_expression(">= 8.04").should == [ ">=", "8.04" ]
+ end
+
+ it "should accept = 8.04" do
+ @meta._check_version_expression("= 8.04").should == [ "=", "8.04" ]
+ end
+
+ it "should accept <= 8.04" do
+ @meta._check_version_expression("<= 8.04").should == [ "<=", "8.04" ]
+ end
+
+ it "should accept << 8.04" do
+ @meta._check_version_expression("<< 8.04").should == [ "<<", "8.04" ]
+ end
+
+ it "should raise an exception on an invalid version expression" do
+ lambda {
+ @meta._check_version_expression("tried to << love you")
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "check for a valid version" do
+ it "should think 8.00 is << 8.04" do
+ @meta._check_valid_version("8.00", "<< 8.04").should == true
+ end
+
+ it "should think 9.04 is not << 8.04" do
+ @meta._check_valid_version("9.04", "<< 8.04").should == false
+ end
+
+ it "should think 8.00 is <= 8.04" do
+ @meta._check_valid_version("8.00", "<= 8.04").should == true
+ end
+
+ it "should think 8.04 is <= 8.04" do
+ @meta._check_valid_version("8.04", "<= 8.04").should == true
+ end
+
+ it "should think 9.04 is not <= 8.04" do
+ @meta._check_valid_version("9.04", "<= 8.04").should == false
+ end
+
+ it "should think 8.00 is not = 8.04" do
+ @meta._check_valid_version("8.00", "= 8.04").should == false
+ end
+
+ it "should think 8.04 is = 8.04" do
+ @meta._check_valid_version("8.04", "= 8.04").should == true
+ end
+
+ it "should think 8.00 is not >= 8.04" do
+ @meta._check_valid_version("8.00", ">= 8.04").should == false
+ end
+
+ it "should think 9.04 is >= 8.04" do
+ @meta._check_valid_version("9.04", ">= 8.04").should == true
+ end
+
+ it "should think 8.04 is >= 8.04" do
+ @meta._check_valid_version("8.04", ">= 8.04").should == true
+ end
+
+ it "should think 8.00 is not >> 8.04" do
+ @meta._check_valid_version("8.00", ">> 8.04").should == false
+ end
+
+ it "should think 8.04 is not >> 8.04" do
+ @meta._check_valid_version("8.04", ">> 8.04").should == false
+ end
+
+ it "should think 9.04 is >> 8.04" do
+ @meta._check_valid_version("9.04", ">> 8.04").should == true
+ end
+ end
+
+ describe "recipes" do
+ before(:each) do
+ @cookbook.recipe_files = [ "default.rb", "enlighten.rb" ]
+ @meta = Chef::Cookbook::Metadata.new(@cookbook)
+ end
+
+ it "should have the names of the recipes" do
+ @meta.recipes["test_cookbook"].should == ""
+ @meta.recipes["test_cookbook::enlighten"].should == ""
+ end
+
+ it "should let you set the description for a recipe" do
+ @meta.recipe "test_cookbook", "It, um... tests stuff?"
+ @meta.recipes["test_cookbook"].should == "It, um... tests stuff?"
+ end
+
+ it "should automatically provide each recipe" do
+ @meta.providing.has_key?("test_cookbook").should == true
+ @meta.providing.has_key?("test_cookbook::enlighten").should == true
+ end
+
+ end
+
+ describe "json" do
+ before(:each) do
+ @cookbook.recipe_files = [ "default.rb", "enlighten.rb" ]
+ @meta = Chef::Cookbook::Metadata.new(@cookbook)
+ @meta.version "1.0"
+ @meta.maintainer "Bobo T. Clown"
+ @meta.maintainer_email "bobo@example.com"
+ @meta.long_description "I have a long arm!"
+ @meta.supports :ubuntu, ">> 8.04"
+ @meta.depends "bobo", "= 1.0"
+ @meta.depends "bobotclown", "= 1.1"
+ @meta.recommends "snark", "<< 3.0"
+ @meta.suggests "kindness", ">> 2.0", "<< 4.0"
+ @meta.conflicts "hatred"
+ @meta.provides "foo(:bar, :baz)"
+ @meta.replaces "snarkitron"
+ @meta.recipe "test_cookbook::enlighten", "is your buddy"
+ @meta.attribute "bizspark/has_login",
+ :display_name => "You have nothing"
+ end
+
+ describe "serialize" do
+ before(:each) do
+ @serial = JSON.parse(@meta.to_json)
+ end
+
+ it "should serialize to a json hash" do
+ JSON.parse(@meta.to_json).should be_a_kind_of(Hash)
+ end
+
+ %w{
+ name
+ description
+ long_description
+ maintainer
+ maintainer_email
+ license
+ platforms
+ dependencies
+ suggestions
+ recommendations
+ conflicting
+ providing
+ replacing
+ attributes
+ recipes
+ }.each do |t|
+ it "should include '#{t}'" do
+ @serial[t].should == @meta.send(t.to_sym)
+ end
+ end
+ end
+
+ describe "deserialize" do
+ before(:each) do
+ @deserial = Chef::Cookbook::Metadata.from_json(@meta.to_json)
+ end
+
+ it "should deserialize to a Chef::Cookbook::Metadata object" do
+ @deserial.should be_a_kind_of(Chef::Cookbook::Metadata)
+ end
+
+ %w{
+ name
+ description
+ long_description
+ maintainer
+ maintainer_email
+ license
+ platforms
+ dependencies
+ suggestions
+ recommendations
+ conflicting
+ providing
+ replacing
+ attributes
+ recipes
+ }.each do |t|
+ it "should match '#{t}'" do
+ @deserial.send(t.to_sym).should == @meta.send(t.to_sym)
+ end
+ end
+
+ end
+
+ end
+
+end
diff --git a/chef/spec/unit/platform_spec.rb b/chef/spec/unit/platform_spec.rb
index 299ecd327b..f7f7e6801a 100644
--- a/chef/spec/unit/platform_spec.rb
+++ b/chef/spec/unit/platform_spec.rb
@@ -140,8 +140,12 @@ describe Chef::Platform do
:provider => "masterful"
)
Chef::Platform.platforms[:default][:file].should eql("masterful")
+
+ Chef::Platform.platforms = { :neurosis => {} }
+ Chef::Platform.set(:platform => :neurosis, :resource => :package, :provider => "masterful")
+ Chef::Platform.platforms[:neurosis][:default][:package].should eql("masterful")
end
-end \ No newline at end of file
+end
diff --git a/chef/spec/unit/provider/group/groupadd_spec.rb b/chef/spec/unit/provider/group/groupadd_spec.rb
index ccd150391f..6e8b11a663 100644
--- a/chef/spec/unit/provider/group/groupadd_spec.rb
+++ b/chef/spec/unit/provider/group/groupadd_spec.rb
@@ -129,7 +129,8 @@ describe Chef::Provider::Group::Groupadd, "modify_group_members" do
@new_resource = mock("Chef::Resource::Group",
:null_object => true,
:group_name => "aj",
- :members => [ "all", "your", "base" ]
+ :members => [ "all", "your", "base" ],
+ :append => false
)
@new_resource.stub!(:to_s).and_return("group[aj]")
@provider = Chef::Provider::Group::Groupadd.new(@node, @new_resource)
@@ -158,9 +159,18 @@ describe Chef::Provider::Group::Groupadd, "modify_group_members" do
end
it "should run gpasswd with the members joined by ',' followed by the target group" do
- @provider.should_receive(:run_command).with({:command => "gpasswd -M all,your,base aj"})
- @provider.modify_group_members
+ @provider.should_receive(:run_command).with({:command => "gpasswd -M all,your,base aj"})
+ @provider.modify_group_members
end
+
+ it "should run gpasswd individually for each user when the append option is set" do
+ @new_resource.stub!(:append).and_return(true)
+ @provider.should_receive(:run_command).with({:command => "gpasswd -a all aj"})
+ @provider.should_receive(:run_command).with({:command => "gpasswd -a your aj"})
+ @provider.should_receive(:run_command).with({:command => "gpasswd -a base aj"})
+ @provider.modify_group_members
+ end
+
end
end
diff --git a/chef/spec/unit/provider/group_spec.rb b/chef/spec/unit/provider/group_spec.rb
index 5d2643b0ee..7b1f7a9524 100644
--- a/chef/spec/unit/provider/group_spec.rb
+++ b/chef/spec/unit/provider/group_spec.rb
@@ -109,7 +109,8 @@ describe Chef::Provider::Group, "compare_group" do
:null_object => true,
:group_name => "aj",
:gid => 50,
- :members => [ "root", "aj"]
+ :members => [ "root", "aj"],
+ :append => false
)
@current_resource = mock("Chef::Resource::Group",
:null_object => true,
@@ -128,9 +129,22 @@ describe Chef::Provider::Group, "compare_group" do
end
end
- it "should return false if no change is required" do
+ it "should return false if gid and members are equal" do
+ @provider.compare_group.should be_false
+ end
+
+ it "should return false if append is true and the group member(s) already exists" do
+ @current_resource.members << "extra_user"
+ @new_resource.stub!(:append).and_return(true)
@provider.compare_group.should be_false
end
+
+ it "should return true if append is true and the group member(s) do not already exist" do
+ @new_resource.members << "extra_user"
+ @new_resource.stub!(:append).and_return(true)
+ @provider.compare_group.should be_true
+ end
+
end
describe Chef::Provider::Group, "action_create" do
diff --git a/chef/spec/unit/provider/package/macports_spec.rb b/chef/spec/unit/provider/package/macports_spec.rb
new file mode 100644
index 0000000000..ca004116ac
--- /dev/null
+++ b/chef/spec/unit/provider/package/macports_spec.rb
@@ -0,0 +1,180 @@
+#
+# Author:: David Balatero (<dbalatero@gmail.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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec_helper"))
+
+describe Chef::Provider::Package::Macports do
+ before(:each) do
+ @node = mock("Chef::Node", :null_object => true)
+ @new_resource = mock("Chef::Resource::Package",
+ :null_object => true,
+ :name => "zsh",
+ :package_name => "zsh",
+ :version => nil
+ )
+ @current_resource = mock("Chef::Resource::Package",
+ :null_object => true,
+ :name => "zsh",
+ :package_name => "zsh",
+ :version => nil
+ )
+
+ @provider = Chef::Provider::Package::Macports.new(@node, @new_resource)
+ Chef::Resource::Package.stub!(:new).and_return(@current_resource)
+
+ @status = mock("Status", :exitstatus => 0)
+ @stdin = mock("STDIN", :null_object => true)
+ @stdout = mock("STDOUT", :null_object => true)
+ @stderr = mock("STDERR", :null_object => true)
+ @pid = mock("PID", :null_object => true)
+ end
+
+ describe "load_current_resource" do
+ it "should create a current resource with the name of the new_resource" do
+ @provider.should_receive(:current_installed_version).and_return(nil)
+ @provider.should_receive(:macports_candidate_version).and_return("4.2.7")
+
+ @provider.load_current_resource
+ @provider.current_resource.name.should == "zsh"
+ end
+
+ it "should create a current resource with the version if the package is installed" do
+ @provider.should_receive(:macports_candidate_version).and_return("4.2.7")
+ @provider.should_receive(:current_installed_version).and_return("4.2.7")
+
+ @provider.load_current_resource
+ @provider.candidate_version.should == "4.2.7"
+ end
+
+ it "should create a current resource with a nil version if the package is not installed" do
+ @provider.should_receive(:current_installed_version).and_return(nil)
+ @provider.should_receive(:macports_candidate_version).and_return("4.2.7")
+ @provider.load_current_resource
+ @provider.current_resource.version.should be_nil
+ end
+
+ it "should set a candidate version if one exists" do
+ @provider.should_receive(:current_installed_version).and_return(nil)
+ @provider.should_receive(:macports_candidate_version).and_return("4.2.7")
+ @provider.load_current_resource
+ @provider.candidate_version.should == "4.2.7"
+ end
+ end
+
+ describe "current_installed_version" do
+ it "should return the current version if the package is installed" do
+ @stdout.should_receive(:read).and_return(<<EOF
+The following ports are currently installed:
+ openssl @0.9.8k_0 (active)
+EOF
+ )
+
+ @provider.should_receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ @provider.current_installed_version.should == "0.9.8k_0"
+ end
+
+ it "should return nil if a package is not currently installed" do
+ @stdout.should_receive(:read).and_return(" \n")
+ @provider.should_receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ @provider.current_installed_version.should be_nil
+ end
+ end
+
+ describe "macports_candidate_version" do
+ it "should return the latest available version of a given package" do
+ @stdout.should_receive(:read).and_return("version: 4.2.7\n")
+ @provider.should_receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ @provider.macports_candidate_version.should == "4.2.7"
+ end
+
+ it "should return nil if there is no version for a given package" do
+ @stdout.should_receive(:read).and_return("Error: port fadsfadsfads not found\n")
+ @provider.should_receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ @provider.macports_candidate_version.should be_nil
+ end
+ end
+
+ describe "install_package" do
+ it "should run the port install command with the correct version" do
+ @current_resource.should_receive(:version).and_return("4.1.6")
+ @provider.current_resource = @current_resource
+ @provider.should_receive(:run_command).with(:command => "port install zsh @4.2.7")
+
+ @provider.install_package("zsh", "4.2.7")
+ end
+
+ it "should not do anything if a package already exists with the same version" do
+ @current_resource.should_receive(:version).and_return("4.2.7")
+ @provider.current_resource = @current_resource
+ @provider.should_not_receive(:run_command)
+
+ @provider.install_package("zsh", "4.2.7")
+ end
+ end
+
+ describe "purge_package" do
+ it "should run the port uninstall command with the correct version" do
+ @provider.should_receive(:run_command).with(:command => "port uninstall zsh @4.2.7")
+ @provider.purge_package("zsh", "4.2.7")
+ end
+
+ it "should purge the currently active version if no explicit version is passed in" do
+ @provider.should_receive(:run_command).with(:command => "port uninstall zsh")
+ @provider.purge_package("zsh", nil)
+ end
+ end
+
+ describe "remove_package" do
+ it "should run the port deactivate command with the correct version" do
+ @provider.should_receive(:run_command).with(:command => "port deactivate zsh @4.2.7")
+ @provider.remove_package("zsh", "4.2.7")
+ end
+
+ it "should remove the currently active version if no explicit version is passed in" do
+ @provider.should_receive(:run_command).with(:command => "port deactivate zsh")
+ @provider.remove_package("zsh", nil)
+ end
+ end
+
+ describe "upgrade_package" do
+ it "should run the port upgrade command with the correct version" do
+ @current_resource.should_receive(:version).at_least(:once).and_return("4.1.6")
+ @provider.current_resource = @current_resource
+
+ @provider.should_receive(:run_command).with(:command => "port upgrade zsh @4.2.7")
+
+ @provider.upgrade_package("zsh", "4.2.7")
+ end
+
+ it "should not run the port upgrade command if the version is already installed" do
+ @current_resource.should_receive(:version).at_least(:once).and_return("4.2.7")
+ @provider.current_resource = @current_resource
+ @provider.should_not_receive(:run_command)
+
+ @provider.upgrade_package("zsh", "4.2.7")
+ end
+
+ it "should call install_package if the package isn't currently installed" do
+ @current_resource.should_receive(:version).at_least(:once).and_return(nil)
+ @provider.current_resource = @current_resource
+ @provider.should_receive(:install_package).and_return(true)
+
+ @provider.upgrade_package("zsh", "4.2.7")
+ end
+ end
+end
diff --git a/chef/spec/unit/provider/package/portage_spec.rb b/chef/spec/unit/provider/package/portage_spec.rb
new file mode 100644
index 0000000000..f4aa4f10bb
--- /dev/null
+++ b/chef/spec/unit/provider/package/portage_spec.rb
@@ -0,0 +1,158 @@
+#
+# Author:: Caleb Tennis (<caleb.tennis@gmail.com>)
+# Copyright:: Copyright (c) 2008 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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec_helper"))
+
+describe Chef::Provider::Package::Portage, "load_current_resource" do
+ before(:each) do
+ @node = mock("Chef::Node", :null_object => true)
+
+ @new_resource = mock("Chef::Resource::Package",
+ :null_object => true,
+ :name => "dev-util/git",
+ :version => nil,
+ :package_name => "dev-util/git",
+ :updated => nil
+ )
+ @current_resource = mock("Chef::Resource::Package",
+ :null_object => true,
+ :name => "dev-util/git",
+ :version => nil,
+ :package_name => nil,
+ :updated => nil
+ )
+
+ @provider = Chef::Provider::Package::Portage.new(@node, @new_resource)
+ Chef::Resource::Package.stub!(:new).and_return(@current_resource)
+
+ ::File.stub!(:exists?).and_return(true)
+ end
+
+ it "should create a current resource with the name of new_resource" do
+ Chef::Resource::Package.should_receive(:new).and_return(@current_resource)
+ @provider.load_current_resource
+ end
+
+ it "should set the current resource package name to the new resource package name" do
+ @current_resource.should_receive(:package_name).with(@new_resource.package_name)
+ @provider.load_current_resource
+ end
+
+ it "should return a current resource with a nil version if the package is not found" do
+ ::Dir.stub!(:entries).and_return("git-1.0.0")
+ @current_resource.should_receive(:version).with("1.0.0")
+ @provider.load_current_resource
+ end
+
+ it "should return a current resource with the correct version if the package is found" do
+ ::Dir.stub!(:entries).and_return("notgit-1.0.0")
+ @current_resource.should_receive(:version).with(nil)
+ @provider.load_current_resource
+ end
+
+end
+
+describe Chef::Provider::Package::Portage, "candidate_version" do
+ before(:each) do
+ @node = mock("Chef::Node", :null_object => true)
+ @new_resource = mock("Chef::Resource::Package",
+ :null_object => true,
+ :name => "dev-util/git",
+ :version => nil,
+ :package_name => "dev-util/git",
+ :updated => nil
+ )
+ @provider = Chef::Provider::Package::Portage.new(@node, @new_resource)
+ end
+
+ it "should return the candidate_version variable if already setup" do
+ @provider.candidate_version = "1.0.0"
+ @provider.should_not_receive(:popen4)
+ @provider.candidate_version
+ end
+
+ it "should lookup the candidate_version if the variable is not already set" do
+ @status = mock("Status", :exitstatus => 0)
+ @provider.stub!(:popen4).and_return(@status)
+ @provider.should_receive(:popen4)
+ @provider.candidate_version
+ end
+
+ it "should throw and exception if the exitstatus is not 0" do
+ @status = mock("Status", :exitstatus => 1)
+ @provider.stub!(:popen4).and_return(@status)
+ lambda { @provider.candidate_version }.should raise_error(Chef::Exceptions::Package)
+ end
+
+end
+
+describe Chef::Provider::Package::Portage, "install_package" do
+ before(:each) do
+ @node = mock("Chef::Node", :null_object => true)
+ @new_resource = mock("Chef::Resource::Package",
+ :null_object => true,
+ :name => "dev-util/git",
+ :version => nil,
+ :package_name => "dev-util/git",
+ :updated => nil
+ )
+ @provider = Chef::Provider::Package::Portage.new(@node, @new_resource)
+ end
+
+ it "should install a normally versioned package using portage" do
+ @provider.should_receive(:run_command).with({
+ :command => "emerge -g --color n --nospinner --quiet =dev-util/git-1.0.0"
+ })
+ @provider.install_package("dev-util/git", "1.0.0")
+ end
+
+ it "should install a tilde versioned package using portage" do
+ @provider.should_receive(:run_command).with({
+ :command => "emerge -g --color n --nospinner --quiet ~dev-util/git-1.0.0"
+ })
+ @provider.install_package("dev-util/git", "~1.0.0")
+ end
+end
+
+describe Chef::Provider::Package::Portage, "remove_package" do
+ before(:each) do
+ @node = mock("Chef::Node", :null_object => true)
+ @new_resource = mock("Chef::Resource::Package",
+ :null_object => true,
+ :name => "dev-util/git",
+ :version => nil,
+ :package_name => "dev-util/git",
+ :updated => nil
+ )
+ @provider = Chef::Provider::Package::Portage.new(@node, @new_resource)
+ end
+
+ it "should un-emerge the package with no version specified" do
+ @provider.should_receive(:run_command).with({
+ :command => "emerge --unmerge --color n --nospinner --quiet dev-util/git"
+ })
+ @provider.remove_package("dev-util/git", nil)
+ end
+
+ it "should un-emerge the package with a version specified" do
+ @provider.should_receive(:run_command).with({
+ :command => "emerge --unmerge --color n --nospinner --quiet =dev-util/git-1.0.0"
+ })
+ @provider.remove_package("dev-util/git", "1.0.0")
+ end
+end
+
diff --git a/chef/spec/unit/provider/package/rpm_spec.rb b/chef/spec/unit/provider/package/rpm_spec.rb
index dede03a664..8b999f8751 100644
--- a/chef/spec/unit/provider/package/rpm_spec.rb
+++ b/chef/spec/unit/provider/package/rpm_spec.rb
@@ -143,7 +143,7 @@ describe Chef::Provider::Package::Rpm, "remove" do
it "should run rpm -e to remove the package" do
@provider.should_receive(:run_command).with({
- :command => "rpm -e emacs"
+ :command => "rpm -e emacs-21.4-20.el5"
})
@provider.remove_package("emacs", "21.4-20.el5")
end
diff --git a/chef/spec/unit/provider/package_spec.rb b/chef/spec/unit/provider/package_spec.rb
index d9a89d607d..d60beb364f 100644
--- a/chef/spec/unit/provider/package_spec.rb
+++ b/chef/spec/unit/provider/package_spec.rb
@@ -111,6 +111,37 @@ describe Chef::Provider::Package, "action_install" do
@provider.should_not_receive(:install_package)
@provider.action_install
end
+
+ it "should call the candidate_version accessor if the package is not currently installed" do
+ @provider.should_receive(:candidate_version).and_return(true)
+ @provider.action_install
+ end
+
+ it "should not call the candidate_version accessor if the package is already installed and no version is specified" do
+ @current_resource.stub!(:version).and_return("1.0")
+ @provider.should_not_receive(:candidate_version)
+ @provider.action_install
+ end
+
+ it "should not call the candidate_version accessor if the package is already installed at the version specified" do
+ @current_resource.stub!(:version).and_return("1.0")
+ @new_resource.stub!(:version).and_return("1.0")
+ @provider.should_not_receive(:candidate_version)
+ @provider.action_install
+ end
+
+ it "should not call the candidate_version accessor if the package is not installed new package's version is specified" do
+ @new_resource.stub!(:version).and_return("1.0")
+ @provider.should_not_receive(:candidate_version)
+ @provider.action_install
+ end
+
+ it "should not call the candidate_version accessor if the package at the version specified is a different version than installed" do
+ @new_resource.stub!(:version).and_return("1.0")
+ @current_resource.stub!(:version).and_return("0.99")
+ @provider.should_not_receive(:candidate_version)
+ @provider.action_install
+ end
it "should set the resource to updated if it installs the package" do
@new_resource.should_recieve(:updated=).with(true)
diff --git a/chef/spec/unit/provider/service/redhat_spec.rb b/chef/spec/unit/provider/service/redhat_spec.rb
index 11a808be98..e69e19229c 100644
--- a/chef/spec/unit/provider/service/redhat_spec.rb
+++ b/chef/spec/unit/provider/service/redhat_spec.rb
@@ -98,8 +98,8 @@ describe Chef::Provider::Service::Redhat, "enable_service" do
Chef::Resource::Service.stub!(:new).and_return(@current_resource)
end
- it "should call chkconfig --add 'service_name'" do
- @provider.should_receive(:run_command).with({:command => "/sbin/chkconfig --add #{@new_resource.service_name}"})
+ it "should call chkconfig to add 'service_name'" do
+ @provider.should_receive(:run_command).with({:command => "/sbin/chkconfig #{@new_resource.service_name} on"})
@provider.enable_service()
end
end
@@ -117,8 +117,8 @@ describe Chef::Provider::Service::Redhat, "disable_service" do
Chef::Resource::Service.stub!(:new).and_return(@current_resource)
end
- it "should call chkconfig --del 'service_name'" do
- @provider.should_receive(:run_command).with({:command => "/sbin/chkconfig --del #{@new_resource.service_name}"})
+ it "should call chkconfig to del 'service_name'" do
+ @provider.should_receive(:run_command).with({:command => "/sbin/chkconfig #{@new_resource.service_name} off"})
@provider.disable_service()
end
end
diff --git a/chef/spec/unit/resource/group_spec.rb b/chef/spec/unit/resource/group_spec.rb
index 585fcfc4d0..d110f5a45f 100644
--- a/chef/spec/unit/resource/group_spec.rb
+++ b/chef/spec/unit/resource/group_spec.rb
@@ -103,4 +103,24 @@ describe Chef::Resource::Group, "members" do
it "should not allow a hash" do
lambda { @resource.send(:members, { :aj => "is freakin awesome" }) }.should raise_error(ArgumentError)
end
-end \ No newline at end of file
+end
+
+describe Chef::Resource::Group, "append" do
+ before(:each) do
+ @resource = Chef::Resource::Group.new("admin")
+ end
+
+ it "should default to false" do
+ @resource.append.should eql(false)
+ end
+
+ it "should allow a boolean" do
+ @resource.append true
+ @resource.append.should eql(true)
+ end
+
+ it "should not allow a hash" do
+ lambda { @resource.send(:gid, { :aj => "is freakin awesome" }) }.should raise_error(ArgumentError)
+ end
+
+end
diff --git a/chef/spec/unit/resource/macports_package_spec.rb b/chef/spec/unit/resource/macports_package_spec.rb
new file mode 100644
index 0000000000..c2dabb089f
--- /dev/null
+++ b/chef/spec/unit/resource/macports_package_spec.rb
@@ -0,0 +1,37 @@
+#
+# Author:: David Balatero (<dbalatero@gmail.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 File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
+
+describe Chef::Resource::MacportsPackage, "initialize" do
+ before(:each) do
+ @resource = Chef::Resource::MacportsPackage.new("foo")
+ end
+
+ it "should return a Chef::Resource::MacportsPackage" do
+ @resource.should be_a_kind_of(Chef::Resource::MacportsPackage)
+ end
+
+ it "should set the resource_name to :macports_package" do
+ @resource.resource_name.should eql(:macports_package)
+ end
+
+ it "should set the provider to Chef::Provider::Package::Macports" do
+ @resource.provider.should eql(Chef::Provider::Package::Macports)
+ end
+end
diff --git a/features/cookbooks/metadata.feature b/features/cookbooks/metadata.feature
new file mode 100644
index 0000000000..7e1c26c9f0
--- /dev/null
+++ b/features/cookbooks/metadata.feature
@@ -0,0 +1,20 @@
+Feature: Cookbook Metadata
+ In order to understand cookbooks without evaluating them
+ As an Administrator
+ I want to automatically generate metadata about cookbooks
+
+ Scenario: Generate metadata for all cookbooks
+ Given a local cookbook repository
+ When I run the rake task to generate cookbook metadata
+ Then the run should exit '0'
+ And 'stdout' should have 'Generating metadata for metadata'
+ And 'stdout' should have 'Generating metadata for execute_commands'
+ And a file named 'cookbooks_dir/cookbooks/metadata/metadata.json' should exist
+
+ Scenario: Generate metadata for a specific cookbook
+ Given a local cookbook repository
+ When I run the rake task to generate cookbook metadata for 'metadata'
+ Then the run should exit '0'
+ And 'stdout' should have 'Generating metadata for metadata'
+ And a file named 'cookbooks_dir/cookbooks/metadata/metadata.json' should exist
+
diff --git a/features/data/Rakefile b/features/data/Rakefile
index d0a3d16913..27e98cfd98 100644
--- a/features/data/Rakefile
+++ b/features/data/Rakefile
@@ -18,164 +18,18 @@
# limitations under the License.
#
-require File.join(File.dirname(__FILE__), 'config', 'rake')
+require 'rubygems'
+require 'json'
-require 'tempfile'
+# Make sure you have loaded constants first
+require File.join(File.dirname(__FILE__), 'config', 'rake')
+# And choosen a VCS
if File.directory?(File.join(TOPDIR, ".svn"))
$vcs = :svn
elsif File.directory?(File.join(TOPDIR, ".git"))
$vcs = :git
end
-desc "Update your repository from source control"
-task :update do
- puts "** Updating your repository"
-
- case $vcs
- when :svn
- sh %{svn up}
- when :git
- pull = false
- pull = true if File.join(TOPDIR, ".git", "remotes", "origin")
- IO.foreach(File.join(TOPDIR, ".git", "config")) do |line|
- pull = true if line =~ /\[remote "origin"\]/
- end
- if pull
- sh %{git pull}
- else
- puts "* Skipping git pull, no origin specified"
- end
- else
- puts "* No SCM configured, skipping update"
- end
-end
-
-desc "Test your cookbooks for syntax errors"
-task :test do
- puts "** Testing your cookbooks for syntax errors"
+require 'chef/tasks/chef_repo.rake'
- recipes = ["*cookbooks"].map { |folder|
- Dir[File.join(TOPDIR, folder, "**", "*.rb")]
- }.flatten
-
- recipes.each do |recipe|
- print "Testing recipe #{recipe}: "
- sh %{ruby -c #{recipe}} do |ok, res|
- if ! ok
- raise "Syntax error in #{recipe}"
- end
- end
- end
-end
-
-desc "Install the latest copy of the repository on this Chef Server"
-task :install => [ :update, :test ] do
- puts "** Installing your cookbooks"
- directories = [
- COOKBOOK_PATH,
- SITE_COOKBOOK_PATH,
- CHEF_CONFIG_PATH
- ]
- puts "* Creating Directories"
- directories.each do |dir|
- sh "sudo mkdir -p #{dir}"
- sh "sudo chown root #{dir}"
- end
- puts "* Installing new Cookbooks"
- sh "sudo rsync -rlP --delete --exclude '.svn' cookbooks/ #{COOKBOOK_PATH}"
- puts "* Installing new Site Cookbooks"
- sh "sudo rsync -rlP --delete --exclude '.svn' cookbooks/ #{COOKBOOK_PATH}"
- puts "* Installing new Chef Server Config"
- sh "sudo cp config/server.rb #{CHEF_SERVER_CONFIG}"
- puts "* Installing new Chef Client Config"
- sh "sudo cp config/client.rb #{CHEF_CLIENT_CONFIG}"
-end
-
-desc "By default, run rake test"
-task :default => [ :test ]
-
-desc "Create a new cookbook (with COOKBOOK=name)"
-task :new_cookbook do
- create_cookbook(File.join(TOPDIR, "cookbooks"))
-end
-
-def create_cookbook(dir)
- raise "Must provide a COOKBOOK=" unless ENV["COOKBOOK"]
- puts "** Creating cookbook #{ENV["COOKBOOK"]}"
- sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "attributes")}"
- sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "recipes")}"
- sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "definitions")}"
- sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "libraries")}"
- sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "files", "default")}"
- sh "mkdir -p #{File.join(dir, ENV["COOKBOOK"], "templates", "default")}"
- unless File.exists?(File.join(dir, ENV["COOKBOOK"], "recipes", "default.rb"))
- open(File.join(dir, ENV["COOKBOOK"], "recipes", "default.rb"), "w") do |file|
- file.puts <<-EOH
-#
-# Cookbook Name:: #{ENV["COOKBOOK"]}
-# Recipe:: default
-#
-# Copyright #{Time.now.year}, #{COMPANY_NAME}
-#
-EOH
- case NEW_COOKBOOK_LICENSE
- when :apachev2
- file.puts <<-EOH
-# 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.
-#
-EOH
- when :none
- file.puts <<-EOH
-# All rights reserved - Do Not Redistribute
-#
-EOH
- end
- end
- end
-end
-
-desc "Create a new self-signed SSL certificate for FQDN=foo.example.com"
-task :ssl_cert do
- $expect_verbose = true
- fqdn = ENV["FQDN"]
- fqdn =~ /^(.+?)\.(.+)$/
- hostname = $1
- domain = $2
- raise "Must provide FQDN!" unless fqdn && hostname && domain
- puts "** Creating self signed SSL Certificate for #{fqdn}"
- sh("(cd #{CADIR} && openssl genrsa 2048 > #{fqdn}.key)")
- sh("(cd #{CADIR} && chmod 644 #{fqdn}.key)")
- puts "* Generating Self Signed Certificate Request"
- tf = Tempfile.new("#{fqdn}.ssl-conf")
- ssl_config = <<EOH
-[ req ]
-distinguished_name = req_distinguished_name
-prompt = no
-
-[ req_distinguished_name ]
-C = #{SSL_COUNTRY_NAME}
-ST = #{SSL_STATE_NAME}
-L = #{SSL_LOCALITY_NAME}
-O = #{COMPANY_NAME}
-OU = #{SSL_ORGANIZATIONAL_UNIT_NAME}
-CN = #{fqdn}
-emailAddress = #{SSL_EMAIL_ADDRESS}
-EOH
- tf.puts(ssl_config)
- tf.close
- sh("(cd #{CADIR} && openssl req -config '#{tf.path}' -new -x509 -nodes -sha1 -days 3650 -key #{fqdn}.key > #{fqdn}.crt)")
- sh("(cd #{CADIR} && openssl x509 -noout -fingerprint -text < #{fqdn}.crt > #{fqdn}.info)")
- sh("(cd #{CADIR} && cat #{fqdn}.crt #{fqdn}.key > #{fqdn}.pem)")
- sh("(cd #{CADIR} && chmod 644 #{fqdn}.pem)")
-end
diff --git a/features/data/cookbooks/delayed_notifications/recipes/notify_different_resources_for_different_actions.rb b/features/data/cookbooks/delayed_notifications/recipes/notify_different_resources_for_different_actions.rb
new file mode 100644
index 0000000000..3180bbe4c2
--- /dev/null
+++ b/features/data/cookbooks/delayed_notifications/recipes/notify_different_resources_for_different_actions.rb
@@ -0,0 +1,31 @@
+#
+# Cookbook Name:: delayed_notifications
+# Recipe:: notify_different_resources_for_different_actions
+#
+# Copyright 2009, Opscode
+#
+# 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.
+#
+
+file "#{node[:tmpdir]}/notified_file_2.txt" do
+ action :nothing
+end
+
+file "#{node[:tmpdir]}/notified_file_3.txt" do
+ action :nothing
+end
+
+execute "echo foo" do
+ notifies([{resources("file[#{node[:tmpdir]}/notified_file_2.txt]") => [:create, :delayed]},
+ {resources("file[#{node[:tmpdir]}/notified_file_3.txt]") => [:create, :delayed]}])
+end
diff --git a/features/data/cookbooks/execute_commands/metadata.rb b/features/data/cookbooks/execute_commands/metadata.rb
new file mode 100644
index 0000000000..4b0c820de6
--- /dev/null
+++ b/features/data/cookbooks/execute_commands/metadata.rb
@@ -0,0 +1,14 @@
+
+version "1.0"
+maintainer "Bobo T. Clown"
+maintainer_email "bobo@example.com"
+long_description "I have a long arm!"
+supports :ubuntu, ">> 8.04"
+depends "bobo", "= 1.0"
+depends "bobotclown", "= 1.1"
+recommends "snark", "<< 3.0"
+suggests "kindness", ">> 2.0", "<< 4.0"
+conflicts "hatred"
+provides "foo(:bar, :baz)"
+replaces "snarkitron"
+
diff --git a/features/data/cookbooks/metadata/metadata.rb b/features/data/cookbooks/metadata/metadata.rb
new file mode 100644
index 0000000000..4b0c820de6
--- /dev/null
+++ b/features/data/cookbooks/metadata/metadata.rb
@@ -0,0 +1,14 @@
+
+version "1.0"
+maintainer "Bobo T. Clown"
+maintainer_email "bobo@example.com"
+long_description "I have a long arm!"
+supports :ubuntu, ">> 8.04"
+depends "bobo", "= 1.0"
+depends "bobotclown", "= 1.1"
+recommends "snark", "<< 3.0"
+suggests "kindness", ">> 2.0", "<< 4.0"
+conflicts "hatred"
+provides "foo(:bar, :baz)"
+replaces "snarkitron"
+
diff --git a/features/data/cookbooks/metadata/recipes/default.rb b/features/data/cookbooks/metadata/recipes/default.rb
new file mode 100644
index 0000000000..541b504f0b
--- /dev/null
+++ b/features/data/cookbooks/metadata/recipes/default.rb
@@ -0,0 +1,18 @@
+#
+# Cookbook Name:: metadata
+# Recipe:: default
+#
+# Copyright 2009, Opscode
+#
+# 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.
+#
diff --git a/features/data/cookbooks/packages/recipes/default.rb b/features/data/cookbooks/packages/recipes/default.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/packages/recipes/default.rb
diff --git a/features/data/cookbooks/packages/recipes/macports_install_bad_package.rb b/features/data/cookbooks/packages/recipes/macports_install_bad_package.rb
new file mode 100644
index 0000000000..40ac646761
--- /dev/null
+++ b/features/data/cookbooks/packages/recipes/macports_install_bad_package.rb
@@ -0,0 +1,3 @@
+package "fdsafdsa" do
+ action :install
+end
diff --git a/features/data/cookbooks/packages/recipes/macports_install_yydecode.rb b/features/data/cookbooks/packages/recipes/macports_install_yydecode.rb
new file mode 100644
index 0000000000..7c2475db37
--- /dev/null
+++ b/features/data/cookbooks/packages/recipes/macports_install_yydecode.rb
@@ -0,0 +1,3 @@
+package "yydecode" do
+ action :install
+end
diff --git a/features/data/cookbooks/packages/recipes/macports_purge_yydecode.rb b/features/data/cookbooks/packages/recipes/macports_purge_yydecode.rb
new file mode 100644
index 0000000000..e7ad245dfb
--- /dev/null
+++ b/features/data/cookbooks/packages/recipes/macports_purge_yydecode.rb
@@ -0,0 +1,3 @@
+package "yydecode" do
+ action :purge
+end
diff --git a/features/data/cookbooks/packages/recipes/macports_remove_yydecode.rb b/features/data/cookbooks/packages/recipes/macports_remove_yydecode.rb
new file mode 100644
index 0000000000..da1acca7ec
--- /dev/null
+++ b/features/data/cookbooks/packages/recipes/macports_remove_yydecode.rb
@@ -0,0 +1,3 @@
+package "yydecode" do
+ action :remove
+end
diff --git a/features/data/cookbooks/packages/recipes/macports_upgrade_yydecode.rb b/features/data/cookbooks/packages/recipes/macports_upgrade_yydecode.rb
new file mode 100644
index 0000000000..3d008abdec
--- /dev/null
+++ b/features/data/cookbooks/packages/recipes/macports_upgrade_yydecode.rb
@@ -0,0 +1,7 @@
+package "yydecode" do
+ action :remove
+end
+
+package "yydecode" do
+ action :upgrade
+end
diff --git a/features/language/delayed_notifications.feature b/features/language/delayed_notifications.feature
index de86f4a240..bc874f0f5e 100644
--- a/features/language/delayed_notifications.feature
+++ b/features/language/delayed_notifications.feature
@@ -17,5 +17,11 @@ Feature: Delayed Notifications
Then the run should exit '0'
And a file named 'notified_file.txt' should contain 'bob dylan' only '1' time
-
+ Scenario: Notify different resources for different actions
+ Given a validated node
+ And it includes the recipe 'delayed_notifications::notify_different_resources_for_different_actions'
+ When I run the chef-client
+ Then the run should exit '0'
+ And a file named 'notified_file_2.txt' should exist
+ And a file named 'notified_file_3.txt' should exist
diff --git a/features/provider/package/macports.feature b/features/provider/package/macports.feature
new file mode 100644
index 0000000000..111fb00e0a
--- /dev/null
+++ b/features/provider/package/macports.feature
@@ -0,0 +1,18 @@
+Feature: Macports integration
+ In order to easily manage my OS X machines
+ As a Developer
+ I want to manage packages installed on OS X machines
+
+ Scenario Outline: OS X package management
+ Given that I have the MacPorts package system installed
+ When I run chef-solo with the '<recipe>' recipe
+ Then the run should exit '<exitcode>'
+ And there <should> be a binary on the path called '<binary>'
+
+ Examples:
+ | recipe | binary | should | exitcode |
+ | packages::macports_install_yydecode | yydecode | should | 0 |
+ | packages::macports_remove_yydecode | yydecode | should not | 0 |
+ | packages::macports_upgrade_yydecode | yydecode | should | 0 |
+ | packages::macports_purge_yydecode | yydecode | should not | 0 |
+ | packages::macports_install_bad_package | fdsafdsa | should not | 1 |
diff --git a/features/steps/cookbooks.rb b/features/steps/cookbooks.rb
new file mode 100644
index 0000000000..53f42100ab
--- /dev/null
+++ b/features/steps/cookbooks.rb
@@ -0,0 +1,53 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 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.
+#
+
+Given /^a local cookbook repository$/ do
+ Dir.mkdir(File.join(@tmpdir, 'cookbooks_dir'))
+ Dir.mkdir(File.join(@tmpdir, 'cookbooks_dir', 'cookbooks'))
+ Dir.mkdir(File.join(@tmpdir, 'cookbooks_dir', 'config'))
+ system("cp #{@datadir}/Rakefile #{@tmpdir}/cookbooks_dir")
+ system("cp -r #{@datadir}/config/* #{@tmpdir}/cookbooks_dir/config")
+ system("cp -r #{@datadir}/cookbooks/* #{@tmpdir}/cookbooks_dir/cookbooks")
+ @cleanup_dirs = "#{@tmpdir}/cookbooks_dir"
+end
+
+Given /^a local cookbook named '(.+)'$/ do |cb|
+ Dir.mkdir(File.join(@tmpdir, 'cookbooks_dir'))
+ Dir.mkdir(File.join(@tmpdir, 'cookbooks_dir', 'cookbooks'))
+ Dir.mkdir(File.join(@tmpdir, 'cookbooks_dir', 'config'))
+ system("cp #{@datadir}/Rakefile #{@tmpdir}/cookbooks_dir")
+ system("cp -r #{@datadir}/config/* #{@tmpdir}/cookbooks_dir/config")
+ system("cp -r #{@datadir}/cookbooks/#{cb} #{@tmpdir}/cookbooks_dir/cookbooks")
+ @cleanup_dirs = "#{@tmpdir}/cookbooks_dir"
+end
+
+When /^I run the rake task to generate cookbook metadata for '(.+)'$/ do |cb|
+ @cookbook = cb
+ When('I run the rake task to generate cookbook metadata')
+end
+
+When /^I run the rake task to generate cookbook metadata$/ do
+ to_run = "rake metadata"
+ to_run += " COOKBOOK=#{@cookbook}" if @cookbook
+ Dir.chdir(File.join(@tmpdir, 'cookbooks_dir')) do
+ @status = Chef::Mixin::Command.popen4(to_run) do |p, i, o, e|
+ @stdout = o.gets(nil)
+ @stderr = o.gets(nil)
+ end
+ end
+end
diff --git a/features/steps/nodes.rb b/features/steps/node_steps.rb
index 36cfb76914..033cea4a12 100644
--- a/features/steps/nodes.rb
+++ b/features/steps/node_steps.rb
@@ -20,23 +20,23 @@
# Given
###
Given /^a validated node$/ do
- @client.validation_token = Chef::Config[:validation_token] = 'ceelo'
- @client.build_node
- @client.node.recipes = "integration_setup"
- @client.register
- @client.authenticate
+ client.validation_token = Chef::Config[:validation_token] = 'ceelo'
+ client.register
+ client.authenticate
+ client.build_node
+ client.node.recipes = "integration_setup"
end
Given /^it includes the recipe '(.+)'$/ do |recipe|
- @recipe = recipe
- @client.node.recipes << recipe
- @client.save_node
+ self.recipe = recipe
+ client.node.recipes << recipe
+ client.save_node
end
###
# When
###
When /^the node is converged$/ do
- @client.run
+ client.run
end
diff --git a/features/steps/packages.rb b/features/steps/packages.rb
new file mode 100644
index 0000000000..178a171c0a
--- /dev/null
+++ b/features/steps/packages.rb
@@ -0,0 +1,20 @@
+Given /^that I have the (.+) package system installed$/ do |package_system|
+ unless package_system_available?(package_system)
+ pending "This Cucumber feature will not execute, as it is missing the #{package_system} packaging system."
+ end
+end
+
+Then /^there should be a binary on the path called '(.+)'$/ do |binary_name|
+ binary_name.strip!
+ result = `which #{binary_name}`
+ result.should_not =~ /not found/
+end
+
+Then /^there should not be a binary on the path called '(.+)'$/ do |binary_name|
+ binary_name.strip!
+ result = `which #{binary_name}`.strip
+
+ unless result.empty?
+ result.should =~ /not found/
+ end
+end
diff --git a/features/steps/run_client.rb b/features/steps/run_client.rb
index 62249daa7d..62db06523a 100644
--- a/features/steps/run_client.rb
+++ b/features/steps/run_client.rb
@@ -98,8 +98,10 @@ end
# Then
###
Then /^the run should exit '(.+)'$/ do |exit_code|
- puts @status.inspect
- puts @status.exitstatus
+ if ENV['LOG_LEVEL'] == 'debug'
+ puts @status.inspect
+ puts @status.exitstatus
+ end
begin
@status.exitstatus.should eql(exit_code.to_i)
rescue
diff --git a/features/steps/run_solo.rb b/features/steps/run_solo.rb
new file mode 100644
index 0000000000..f31749f524
--- /dev/null
+++ b/features/steps/run_solo.rb
@@ -0,0 +1,36 @@
+# This is kind of a crazy-ass setup, but it works.
+When /^I run chef-solo with the '(.+)' recipe$/ do |recipe_name|
+ # Set up the JSON file with the recipe we want to run.
+ dna_file = "/tmp/chef-solo-features-dna.json"
+ File.open(dna_file, "w") do |fp|
+ fp.write("{ \"recipes\": [\"#{recipe_name}\"] }")
+ end
+
+ # Set up the cache dir.
+ cache_dir = "/tmp/chef-solo-cache-features"
+ result = `rm -fr #{cache_dir}; mkdir #{cache_dir}; chmod 777 #{cache_dir}`
+
+ # Cookbook dir
+ cookbook_dir ||= File.expand_path(File.join(File.dirname(__FILE__), '..', 'data', 'cookbooks'))
+ result = `ln -sf #{cookbook_dir} #{cache_dir}/cookbooks`
+
+ # Config file
+ config_file = "/tmp/chef-solo-config-features.rb"
+ File.open(config_file, "w") do |fp|
+ fp.write("file_cache_path \"#{cache_dir}\"\n")
+ end
+
+ binary_path = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'chef', 'bin', 'chef-solo'))
+ command = "#{binary_path} -c #{config_file} -j #{dna_file}"
+
+ # Run it
+ puts "Running solo: #{command}" if ENV['LOG_LEVEL'] == 'debug'
+
+ status = Chef::Mixin::Command.popen4(command) do |p, i, o, e|
+ @stdout = o.gets(nil)
+ @stderr = o.gets(nil)
+ end
+ @status = status
+
+ print_output if ENV['LOG_LEVEL'] == 'debug'
+end
diff --git a/features/support/env.rb b/features/support/env.rb
index 978ce818bf..3b4a2caaf9 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -30,11 +30,12 @@ Chef::Config.from_file(File.join(File.dirname(__FILE__), '..', 'data', 'config',
Ohai::Config[:log_level] = :error
class ChefWorld
- attr_accessor :client, :tmpdir
+ attr_accessor :client, :tmpdir, :recipe
def initialize
@client = Chef::Client.new
@tmpdir = File.join(Dir.tmpdir, "chef_integration")
+ @datadir = File.join(File.dirname(__FILE__), "..", "data")
@cleanup_files = Array.new
@cleanup_dirs = Array.new
@recipe = nil
diff --git a/features/support/packages.rb b/features/support/packages.rb
new file mode 100644
index 0000000000..90272e543c
--- /dev/null
+++ b/features/support/packages.rb
@@ -0,0 +1,12 @@
+# Provides a method to quickly lookup whether we have
+# a given packaging system installed.
+def package_system_available?(name)
+ case name
+ when 'MacPorts'
+ uname = `uname`
+ port = `which port`
+ (uname =~ /Darwin/ and !port.match(/not found/))
+ else
+ false
+ end
+end