summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsdelano <stephen@opscode.com>2011-06-21 11:56:44 -0700
committersdelano <stephen@opscode.com>2011-06-21 11:56:44 -0700
commit074e0a123a54f152d7fb3213c351ce1190511450 (patch)
tree0a1949cce377bd9068a12bbc2fbfecbd1f571bd8
parentee88686d5fe19e306fccf060667055365bb78dfa (diff)
parentc6be7004af67e6404d68041704f325959b6984ef (diff)
downloadchef-074e0a123a54f152d7fb3213c351ce1190511450.tar.gz
Merge branch 'master' into pl-master
Conflicts: chef/lib/chef/environment.rb
-rw-r--r--README.rdoc134
-rw-r--r--chef-expander/Rakefile3
-rw-r--r--chef-server-api/Gemfile.lock4
-rw-r--r--chef-server-webui/app/controllers/databag_items.rb52
-rw-r--r--chef-solr/README.rdoc7
-rw-r--r--chef/Rakefile17
-rw-r--r--chef/chef.gemspec2
-rw-r--r--chef/distro/common/html/knife-cookbook.1.html8
-rw-r--r--chef/distro/common/man/man1/knife-cookbook.16
-rw-r--r--chef/distro/common/markdown/man1/knife-cookbook.mkd7
-rw-r--r--chef/distro/common/markdown/man1/knife-node.mkd7
-rw-r--r--chef/lib/chef/application.rb1
-rw-r--r--chef/lib/chef/cookbook_loader.rb18
-rw-r--r--chef/lib/chef/encrypted_data_bag_item.rb25
-rw-r--r--chef/lib/chef/environment.rb18
-rw-r--r--chef/lib/chef/knife.rb28
-rw-r--r--chef/lib/chef/knife/bootstrap.rb7
-rw-r--r--chef/lib/chef/knife/bootstrap/archlinux-gems.erb26
-rw-r--r--chef/lib/chef/knife/bootstrap/centos5-gems.erb13
-rw-r--r--chef/lib/chef/knife/bootstrap/fedora13-gems.erb2
-rw-r--r--chef/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb25
-rw-r--r--chef/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb10
-rw-r--r--chef/lib/chef/knife/client_bulk_delete.rb34
-rw-r--r--chef/lib/chef/knife/cookbook_upload.rb27
-rw-r--r--chef/lib/chef/knife/core/bootstrap_context.rb16
-rw-r--r--chef/lib/chef/knife/core/cookbook_scm_repo.rb2
-rw-r--r--chef/lib/chef/knife/core/node_presenter.rb7
-rw-r--r--chef/lib/chef/knife/help.rb25
-rw-r--r--chef/lib/chef/knife/help_topics.rb4
-rw-r--r--chef/lib/chef/knife/ssh.rb9
-rw-r--r--chef/lib/chef/mixin/language.rb19
-rw-r--r--chef/lib/chef/monkey_patches/numeric.rb10
-rw-r--r--chef/lib/chef/monkey_patches/string.rb21
-rw-r--r--chef/lib/chef/platform.rb3
-rw-r--r--chef/lib/chef/provider.rb2
-rw-r--r--chef/lib/chef/provider/git.rb19
-rw-r--r--chef/lib/chef/provider/group/suse.rb53
-rw-r--r--chef/lib/chef/provider/mount/mount.rb46
-rw-r--r--chef/lib/chef/provider/package/apt.rb63
-rw-r--r--chef/lib/chef/provider/package/dpkg.rb2
-rw-r--r--chef/lib/chef/provider/package/easy_install.rb4
-rw-r--r--chef/lib/chef/provider/package/freebsd.rb4
-rw-r--r--chef/lib/chef/provider/package/macports.rb8
-rw-r--r--chef/lib/chef/provider/package/yum-dump.py281
-rw-r--r--chef/lib/chef/provider/package/yum.rb1065
-rw-r--r--chef/lib/chef/provider/package/zypper.rb23
-rw-r--r--chef/lib/chef/provider/remote_directory.rb1
-rw-r--r--chef/lib/chef/provider/service/arch.rb63
-rw-r--r--chef/lib/chef/provider/service/systemd.rb102
-rw-r--r--chef/lib/chef/provider/service/upstart.rb10
-rw-r--r--chef/lib/chef/providers.rb2
-rw-r--r--chef/lib/chef/resource.rb33
-rw-r--r--chef/lib/chef/resource/git.rb9
-rw-r--r--chef/lib/chef/resource/yum_package.rb20
-rw-r--r--chef/lib/chef/rest.rb2
-rw-r--r--chef/lib/chef/run_context.rb6
-rw-r--r--chef/lib/chef/runner.rb17
-rw-r--r--chef/lib/chef/shell_out.rb2
-rw-r--r--chef/lib/chef/shell_out/windows.rb4
-rw-r--r--chef/lib/chef/tasks/chef_repo.rake2
-rw-r--r--chef/spec/data/lwrp/resources_with_default_attributes/nodeattr.rb1
-rw-r--r--chef/spec/unit/encrypted_data_bag_item_spec.rb37
-rw-r--r--chef/spec/unit/environment_spec.rb31
-rw-r--r--chef/spec/unit/knife/client_bulk_delete_spec.rb22
-rw-r--r--chef/spec/unit/knife/core/cookbook_scm_repo_spec.rb7
-rw-r--r--chef/spec/unit/knife/knife_help.rb92
-rw-r--r--chef/spec/unit/lwrp_spec.rb59
-rw-r--r--chef/spec/unit/mixin/language_spec.rb15
-rw-r--r--chef/spec/unit/provider/mount/mount_spec.rb31
-rw-r--r--chef/spec/unit/provider/package/apt_spec.rb197
-rw-r--r--chef/spec/unit/provider/package/dpkg_spec.rb26
-rw-r--r--chef/spec/unit/provider/package/easy_install_spec.rb18
-rw-r--r--chef/spec/unit/provider/package/freebsd_spec.rb12
-rw-r--r--chef/spec/unit/provider/package/macports_spec.rb31
-rw-r--r--chef/spec/unit/provider/package/rubygems_spec.rb61
-rw-r--r--chef/spec/unit/provider/package/yum_spec.rb1503
-rw-r--r--chef/spec/unit/provider/package/zypper_spec.rb36
-rw-r--r--chef/spec/unit/provider/service/systemd_service_spec.rb226
-rw-r--r--chef/spec/unit/provider/service/upstart_service_spec.rb6
-rw-r--r--chef/spec/unit/resource/yum_package_spec.rb36
-rw-r--r--chef/spec/unit/resource_spec.rb22
-rw-r--r--chef/spec/unit/runner_spec.rb15
-rw-r--r--features/chef-client/run_interval.feature17
-rw-r--r--features/data/cookbooks/scm/metadata.rb1
-rw-r--r--features/data/cookbooks/sync_library_original/README.rdoc8
-rw-r--r--features/data/cookbooks/sync_library_original/libraries/sync_library.rb27
-rw-r--r--features/data/cookbooks/sync_library_original/metadata.rb6
-rw-r--r--features/data/cookbooks/sync_library_original/recipes/default.rb30
-rw-r--r--features/data/cookbooks/sync_library_updated/libraries/sync_library.rb8
-rw-r--r--features/provider/scm/git.feature19
-rw-r--r--features/steps/cookbook_steps.rb10
-rw-r--r--features/steps/deploy_steps.rb13
-rw-r--r--features/steps/run_client_steps.rb57
93 files changed, 4412 insertions, 708 deletions
diff --git a/README.rdoc b/README.rdoc
index 214a1ed1c0..016f786753 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -2,9 +2,9 @@
= DESCRIPTION:
-Chef is a configuration management tool designed to bring automation to your entire infrastructure. Chef's development is sponsored by Opscode[http://www.opscode.com].
+Chef is a system integration framework designed to bring the benefits of configuration management to your entire infrastructure.
-The Chef Wiki is the definitive source of user documentation.
+The Chef Wiki is the definitive source of user documentation.
* http://wiki.opscode.com/display/chef/Home
@@ -16,7 +16,7 @@ This README focuses on developers who want to modify Chef source code. For users
Before working on the code, if you plan to contribute your changes, you need to read the Opscode Contributing document.
-* http://wiki.opscode.com/display/opscode/Contributing
+* http://wiki.opscode.com/display/chef/How+to+Contribute
You will also need to set up the repository with the appropriate branches. We document the process on the Chef Wiki.
@@ -26,102 +26,100 @@ Once your repository is set up, you can start working on the code. We do use BDD
= ENVIRONMENT:
-In order to have a development environment where changes to the Chef code can be tested, we'll need to install a few things after setting up the Git repository.
+Chef has two kinds of code testing:
-== Requirements:
+* Unit Tests (rspec)
+* Integration Tests (cucumber)
-Install these via your platform's preferred method; for example apt, yum, ports, emerge, etc.
+We're going to set up the local environment in two phases, one for each of these.
-* Git
-* CouchDB
-* RabbitMQ
+== Unit Tests (rspec):
-Install the following RubyGems.
+We use Rspec to test that the code does what it is supposed to (unit tests).
+
+Non-Ruby software:
+
+* Gecode (version 3.5.0)
+
+Gecode is available on Mac OS X from homebrew, for Ubuntu/Debian from apt.opscode.com, or can be compiled from source.
+
+* http://www.gecode.org/
+
+Install the Chef Gems from source in this repository.
+
+ rake install
+
+Install the following RubyGems:
-* ohai
* rake
-* rspec
-* cucumber
-* merb-core
-* merb-assets
-* merb-haml
-* merb-helpers
-* merb-param-protection
-* merb_cucumber
-* coderay
+* rspec (2.5.0)
+* dep_selector (requires Gecode to compile native extensions)
-Ohai is also by Opscode and available on GitHub, http://github.com/opscode/ohai/tree/master.
+Run rake or rake spec:
-roman-merb_cucumber is available from GitHub:
+ rake spec
+ ....
+ Finished in 17.42 seconds
+ 3823 examples, 0 failures, 3 pending
- gem install --source http://gems.github.com/ roman-merb_cucumber
+== Integration Tests (cucumber):
-== Starting the Environment:
+The integration tests require some additional software on top of what was installed for the unit tests. Most of the integration tests are for testing the Chef Server API interactions, so a Chef Server environment needs to be installed.
-Once everything is installed, run the dev:features rake task. Since the features do integration testing, root access is required.
+Non-Ruby software:
- sudo rake dev:features
+* Java
+* CouchDB (0.10+)
+* RabbitMQ (1.7.2+)
-The dev:features task:
+These are available in most common distribution packaging systems, including Debian 6.0+ and Ubuntu 10.04+. Java is installed on Mac OS X by default; CouchDB and RabbitMQ are available in homebrew.
-* Installs chef, chef-server, chef-server-slice gems. It will fail if required gems above are missing.
-* Starts chef-server on ports 4000 and 4001.
-* Starts chef-indexer.
-* Starts CouchDB on port 5984.
-* Starts the stompserver on port 61613.
+Make sure the RubyGems for the Chef Server are installed (should be from the +rake install+ earlier):
-You'll know its running when you see:
+* chef-expander
+* chef-server
+* chef-server-api
+* chef-server-webui
+* chef-solr
- ~ Activating slice 'ChefServerSlice' ...
- merb : worker (port 4000) ~ Starting Mongrel at port 4000
- merb : worker (port 4000) ~ Successfully bound to port 4000
- merb : worker (port 4001) ~ Starting Mongrel at port 4001
- merb : worker (port 4001) ~ Successfully bound to port 4001
+=== Starting the Environment:
+
+Once everything is installed, run the dev:features rake task. Since the features do integration testing, root access may be required.
+
+ (sudo) rake dev:features
+
+This rake task will do the following:
+
+* Start CouchDB, running on port 5984
+* Start and configure RabbitMQ for Chef, running on port 5672
+* Install Chef SOLR data and start chef-solr, running on port 8983
+* Start chef-server-api on port 4000
+* Start chef-server-webui on port 4001
+* Start chef-expander, the clustered search index broker
You'll want to leave this terminal running the dev environment.
== Web Interface:
-With the dev environment running, you can now access the web interface via http://localhost:4000/. Supply an OpenID to log in.
+With the dev environment running, you can now access the web interface via http://localhost:4000/. The default login is admin / p@ssw0rd1.
-== Spec testing:
+== Integration testing:
-We use RSpec for unit/spec tests.
+We test integration with Cucumber. Normally, you'll just run:
- rake spec
+ rake features
-This doesn't actually use the development environment, because it does the testing on all the Chef internals. For integration/usage testing, we use Cucumber features.
+In a second terminal, to run all the feature tests. Other feature tests are available as well, see:
-== Integration testing:
+ rake -T | grep Cucumber
-We test integration with Cucumber. The available feature tests are rake tasks:
-
- rake features # Run Features with Cucumber
- rake features:api # Run Features with Cucumber
- rake features:api:nodes # Run Features with Cucumber
- rake features:api:nodes:create # Run Features with Cucumber
- rake features:api:nodes:delete # Run Features with Cucumber
- rake features:api:nodes:list # Run Features with Cucumber
- rake features:api:nodes:show # Run Features with Cucumber
- rake features:api:nodes:update # Run Features with Cucumber
- rake features:api:roles # Run Features with Cucumber
- rake features:api:roles:create # Run Features with Cucumber
- rake features:api:roles:delete # Run Features with Cucumber
- rake features:api:roles:list # Run Features with Cucumber
- rake features:api:roles:show # Run Features with Cucumber
- rake features:api:roles:update # Run Features with Cucumber
- rake features:client # Run Features with Cucumber
- rake features:language # Run Features with Cucumber
- rake features:language:recipe_include # Run Features with Cucumber
- rake features:provider:package:macports # Run Features with Cucumber
- rake features:provider:remote_file # Run Features with Cucumber
- rake features:search # Run Features with Cucumber
+There are over 60 different sets of features that can be tested.
= LINKS:
Source:
-* http://github.com/opscode/chef/tree/master
+* https://github.com/opscode/chef/
Tickets/Issues:
@@ -136,7 +134,7 @@ Documentation:
Chef - A configuration management system
Author:: Adam Jacob (<adam@opscode.com>)
-Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
+Copyright:: Copyright (c) 2008-2011 Opscode, Inc.
License:: Apache License, Version 2.0
Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/chef-expander/Rakefile b/chef-expander/Rakefile
index 8a0644a26d..121e7f8f43 100644
--- a/chef-expander/Rakefile
+++ b/chef-expander/Rakefile
@@ -16,6 +16,7 @@
# limitations under the License.
#
+require 'lib/chef/expander/version'
require 'rake/gempackagetask'
require 'rake/rdoctask'
@@ -41,7 +42,7 @@ rescue LoadError
end
task :install => :package do
- sh %{gem install pkg/chef-expander-#{spec.version} --no-rdoc --no-ri}
+ sh %{gem install pkg/chef-expander-#{Chef::Expander::VERSION} --no-rdoc --no-ri}
end
begin
diff --git a/chef-server-api/Gemfile.lock b/chef-server-api/Gemfile.lock
index 5e631ab3e1..ff994b8f9e 100644
--- a/chef-server-api/Gemfile.lock
+++ b/chef-server-api/Gemfile.lock
@@ -12,7 +12,7 @@ PATH
mixlib-log (>= 1.3.0)
moneta
net-ssh (~> 2.1.3)
- net-ssh-multi (~> 1.0.1)
+ net-ssh-multi (~> 1.1.0)
ohai (>= 0.5.7)
rest-client (< 1.7.0, >= 1.0.4)
treetop (~> 1.4.9)
@@ -65,7 +65,7 @@ GEM
net-ssh (2.1.3)
net-ssh-gateway (1.0.1)
net-ssh (>= 1.99.1)
- net-ssh-multi (1.0.1)
+ net-ssh-multi (1.1.0)
net-ssh (>= 1.99.2)
net-ssh-gateway (>= 0.99.0)
nokogiri (1.4.4)
diff --git a/chef-server-webui/app/controllers/databag_items.rb b/chef-server-webui/app/controllers/databag_items.rb
index e08f967b7d..a5caba6148 100644
--- a/chef-server-webui/app/controllers/databag_items.rb
+++ b/chef-server-webui/app/controllers/databag_items.rb
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -19,25 +19,25 @@
require 'chef' / 'data_bag_item'
class DatabagItems < Application
-
+
provides :html, :json
- before :login_required
-
+ before :login_required
+
def edit
begin
@databag_item = Chef::DataBagItem.load(params[:databag_id], params[:id])
- @default_data = @databag_item
+ @default_data = @databag_item.raw_data
rescue => e
Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
- @_message = { :error => "Could not load the databag item" }
- end
+ @_message = { :error => "Could not load the databag item" }
+ end
render
- end
-
+ end
+
def update
begin
@databag_item = Chef::DataBagItem.new
- @databag_item.data_bag params[:databag_id]
+ @databag_item.data_bag params[:databag_id]
@databag_item.raw_data = Chef::JSONCompat.from_json(params[:json_data])
raise ArgumentError, "Updating id is not allowed" unless @databag_item.raw_data['id'] == params[:id] #to be consistent with other objects, changing id is not allowed.
@databag_item.save
@@ -47,18 +47,18 @@ class DatabagItems < Application
@_message = { :error => "Could not update the databag item" }
@databag_item = Chef::DataBagItem.load(params[:databag_id], params[:id])
@default_data = @databag_item
- render :edit
- end
- end
-
+ render :edit
+ end
+ end
+
def new
@default_data = {'id'=>''}
render
- end
-
+ end
+
def create
- begin
- @databag_name = params[:databag_id]
+ begin
+ @databag_name = params[:databag_id]
@databag_item = Chef::DataBagItem.new
@databag_item.data_bag @databag_name
@databag_item.raw_data = Chef::JSONCompat.from_json(params[:json_data])
@@ -66,11 +66,11 @@ class DatabagItems < Application
redirect(url(:databag_databag_items, :databag_id => @databag_name), :message => { :notice => "Databag item created successfully" })
rescue => e
Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
- @_message = { :error => "Could not create databag item" }
+ @_message = { :error => "Could not create databag item" }
render :new
end
end
-
+
def index
render
end
@@ -85,9 +85,9 @@ class DatabagItems < Application
rescue => e
Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
redirect(url(:databag_databag_items), {:message => { :error => "Could not show the databag item" }, :permanent => true})
- end
+ end
end
-
+
def destroy(databag_id=params[:databag_id], item_id=params[:id])
begin
@databag_item = Chef::DataBagItem.new
@@ -96,7 +96,7 @@ class DatabagItems < Application
rescue => e
Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
redirect(url(:databag_databag_items), {:message => { :error => "Could not delete databag item" }, :permanent => true})
- end
- end
-
+ end
+ end
+
end
diff --git a/chef-solr/README.rdoc b/chef-solr/README.rdoc
index 722ffd1c00..bf8b0698fb 100644
--- a/chef-solr/README.rdoc
+++ b/chef-solr/README.rdoc
@@ -1,6 +1,11 @@
= chef-solr
-Description goes here.
+chef-solr is a Ruby container for SOLR that starts up the Search Engine for the Chef Server under Jetty.
+
+For more information, see the following pages on the Chef Wiki:
+
+* http://wiki.opscode.com/display/chef/Search
+* http://wiki.opscode.com/display/chef/Chef+Indexer
== Copyright
diff --git a/chef/Rakefile b/chef/Rakefile
index 1a8a1ec09e..897b414c80 100644
--- a/chef/Rakefile
+++ b/chef/Rakefile
@@ -67,6 +67,22 @@ namespace :docs do
desc "Regenerate HTML manual from markdown"
task :html
+ desc "Regenerate help topics from man pages"
+ task :list => :man do
+ topics = Array.new
+
+ Dir['distro/common/man/man1/*.1'].each do |man|
+ topics << File.basename(man, '.1')
+ end
+
+ File.open('lib/chef/knife/help_topics.rb', 'w') do |f|
+ f.puts "# Do not edit this file by hand"
+ f.puts "# This file is autogenerated by the docs:list rake task from the available manpages\n\n"
+
+ f.puts "HELP_TOPICS = #{topics.inspect}"
+ end
+ end
+
if system('which ronn > /dev/null')
['distro/common/markdown/man1/*.mkd', 'distro/common/markdown/man8/*.mkd'].each do |dir|
Dir[dir].each do |mkd|
@@ -87,7 +103,6 @@ namespace :docs do
file(htmlfile => mkd) do
sh "ronn -5 #{RONN_OPTS} --style=toc #{mkd} --pipe > #{htmlfile}"
end
-
task :html => htmlfile
end
diff --git a/chef/chef.gemspec b/chef/chef.gemspec
index 376a488908..4bbfe1baf5 100644
--- a/chef/chef.gemspec
+++ b/chef/chef.gemspec
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
s.add_dependency "json", ">= 1.4.4", "<= 1.5.2"
s.add_dependency "treetop", "~> 1.4.9"
s.add_dependency "net-ssh", "~> 2.1.3"
- s.add_dependency "net-ssh-multi", "~> 1.0.1"
+ s.add_dependency "net-ssh-multi", "~> 1.1.0"
%w{erubis moneta highline uuidtools}.each { |gem| s.add_dependency gem }
s.bindir = "bin"
diff --git a/chef/distro/common/html/knife-cookbook.1.html b/chef/distro/common/html/knife-cookbook.1.html
index f062742922..c6ad49e1ac 100644
--- a/chef/distro/common/html/knife-cookbook.1.html
+++ b/chef/distro/common/html/knife-cookbook.1.html
@@ -143,6 +143,8 @@
<dl>
<dt><code>-a</code>, <code>--all</code></dt><dd>upload all cookbooks, rather than just a single cookbook</dd>
<dt><code>-o</code>, <code>--cookbook-path path:path</code></dt><dd>a colon-separated path to look for cookbooks in</dd>
+<dt><code>-d</code>, <code>--upload-dependencies</code></dt><dd>Uploads additional cookbooks that this cookbook lists in as
+dependencies in its metadata.</dd>
<dt><code>-E</code>, <code>--environment ENVIRONMENT</code></dt><dd>An <em>ENVIRONMENT</em> to apply the uploaded cookbooks to. Specifying this
option will cause knife to edit the <em>ENVIRONMENT</em> to place a strict
version constraint on the cookbook version(s) uploaded.</dd>
@@ -350,11 +352,11 @@ cookbook.</p>
<h2 id="AUTHOR">AUTHOR</h2>
-<p> Chef was written by Adam Jacob <a href="&#x6d;&#x61;&#105;&#108;&#x74;&#111;&#58;&#x61;&#100;&#97;&#109;&#x40;&#111;&#x70;&#x73;&#x63;&#x6f;&#100;&#101;&#x2e;&#99;&#111;&#x6d;" data-bare-link="true">&#97;&#100;&#97;&#x6d;&#x40;&#111;&#112;&#x73;&#x63;&#x6f;&#x64;&#101;&#46;&#x63;&#111;&#109;</a> with many contributions from the community.</p>
+<p> Chef was written by Adam Jacob <a href="&#x6d;&#97;&#x69;&#x6c;&#116;&#x6f;&#x3a;&#97;&#x64;&#97;&#x6d;&#64;&#111;&#x70;&#115;&#99;&#x6f;&#x64;&#101;&#46;&#99;&#111;&#109;" data-bare-link="true">&#x61;&#x64;&#x61;&#109;&#64;&#111;&#x70;&#115;&#x63;&#x6f;&#100;&#101;&#x2e;&#x63;&#x6f;&#x6d;</a> with many contributions from the community.</p>
<h2 id="DOCUMENTATION">DOCUMENTATION</h2>
-<p> This manual page was written by Joshua Timberman <a href="&#x6d;&#97;&#105;&#108;&#116;&#x6f;&#58;&#106;&#111;&#115;&#104;&#x75;&#97;&#64;&#x6f;&#x70;&#x73;&#x63;&#111;&#100;&#x65;&#46;&#x63;&#111;&#x6d;" data-bare-link="true">&#106;&#111;&#x73;&#x68;&#x75;&#x61;&#64;&#x6f;&#112;&#115;&#99;&#x6f;&#100;&#101;&#46;&#x63;&#111;&#x6d;</a>.
+<p> This manual page was written by Joshua Timberman <a href="&#109;&#x61;&#105;&#108;&#116;&#111;&#x3a;&#106;&#x6f;&#115;&#104;&#x75;&#x61;&#x40;&#x6f;&#112;&#115;&#x63;&#111;&#x64;&#x65;&#46;&#x63;&#111;&#x6d;" data-bare-link="true">&#x6a;&#111;&#x73;&#104;&#117;&#97;&#x40;&#x6f;&#x70;&#x73;&#x63;&#x6f;&#100;&#x65;&#46;&#x63;&#111;&#x6d;</a>.
Permission is granted to copy, distribute and / or modify this document under the terms of the Apache 2.0 License.</p>
<h2 id="CHEF">CHEF</h2>
@@ -364,7 +366,7 @@ cookbook.</p>
<ol class='man-decor man-foot man foot'>
<li class='tl'>Chef 0.10.0</li>
- <li class='tc'>April 2011</li>
+ <li class='tc'>May 2011</li>
<li class='tr'>knife-cookbook(1)</li>
</ol>
diff --git a/chef/distro/common/man/man1/knife-cookbook.1 b/chef/distro/common/man/man1/knife-cookbook.1
index 3571f5d7d8..624bc4fca3 100644
--- a/chef/distro/common/man/man1/knife-cookbook.1
+++ b/chef/distro/common/man/man1/knife-cookbook.1
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "KNIFE\-COOKBOOK" "1" "April 2011" "Chef 0.10.0" "Chef Manual"
+.TH "KNIFE\-COOKBOOK" "1" "May 2011" "Chef 0.10.0" "Chef Manual"
.
.SH "NAME"
\fBknife\-cookbook\fR \- upload and manage chef cookbooks
@@ -82,6 +82,10 @@ upload all cookbooks, rather than just a single cookbook
a colon\-separated path to look for cookbooks in
.
.TP
+\fB\-d\fR, \fB\-\-upload\-dependencies\fR
+Uploads additional cookbooks that this cookbook lists in as dependencies in its metadata\.
+.
+.TP
\fB\-E\fR, \fB\-\-environment ENVIRONMENT\fR
An \fIENVIRONMENT\fR to apply the uploaded cookbooks to\. Specifying this option will cause knife to edit the \fIENVIRONMENT\fR to place a strict version constraint on the cookbook version(s) uploaded\.
.
diff --git a/chef/distro/common/markdown/man1/knife-cookbook.mkd b/chef/distro/common/markdown/man1/knife-cookbook.mkd
index 32bdcfdbc5..543d6ce5f8 100644
--- a/chef/distro/common/markdown/man1/knife-cookbook.mkd
+++ b/chef/distro/common/markdown/man1/knife-cookbook.mkd
@@ -46,6 +46,9 @@ __knife cookbook upload [cookbooks...]__ _(options)_
upload all cookbooks, rather than just a single cookbook
* `-o`, `--cookbook-path path:path`:
a colon-separated path to look for cookbooks in
+ * `-d`, `--upload-dependencies`:
+ Uploads additional cookbooks that this cookbook lists in as
+ dependencies in its metadata.
* `-E`, `--environment ENVIRONMENT`:
An _ENVIRONMENT_ to apply the uploaded cookbooks to. Specifying this
option will cause knife to edit the _ENVIRONMENT_ to place a strict
@@ -63,6 +66,10 @@ Uploads one or more cookbooks from your local cookbook repository(ies)
to the Chef Server. Only files that don't yet exist on the server will
be uploaded.
+As the command parses the name args as 1..n cookbook names:
+ `knife cookbook upload COOKBOOK COOKBOOK ...`
+works for one to many cookbooks.
+
## DOWNLOAD
__knife cookbook download cookbook [version]__ _(options)_
diff --git a/chef/distro/common/markdown/man1/knife-node.mkd b/chef/distro/common/markdown/man1/knife-node.mkd
index 5937897ed8..1579052083 100644
--- a/chef/distro/common/markdown/man1/knife-node.mkd
+++ b/chef/distro/common/markdown/man1/knife-node.mkd
@@ -102,13 +102,14 @@ __knife node show__ _node name_ _(options)_
Displays the node identified by _node name_ on stdout. The amount of
content displayed and the output format are modified by the --format
-option. Valid formats are:
+option. If no alternate format is selected, the default is summary.
+Valid formats are:
* summary:
- displays the node in a custom, summarized format
+ displays the node in a custom, summarized format (default)
* text
displays the node data in its entirety using the colorized tree
-display
+display
* json:
displays the node in JSON format
* yaml:
diff --git a/chef/lib/chef/application.rb b/chef/lib/chef/application.rb
index c8f3a536fd..eeab7f7d63 100644
--- a/chef/lib/chef/application.rb
+++ b/chef/lib/chef/application.rb
@@ -15,6 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require 'socket'
require 'chef/config'
require 'chef/exceptions'
require 'chef/log'
diff --git a/chef/lib/chef/cookbook_loader.rb b/chef/lib/chef/cookbook_loader.rb
index 463dafbd7d..046561b041 100644
--- a/chef/lib/chef/cookbook_loader.rb
+++ b/chef/lib/chef/cookbook_loader.rb
@@ -30,6 +30,8 @@ class Chef
attr_accessor :metadata
attr_reader :cookbooks_by_name
+ attr_reader :merged_cookbooks
+ attr_reader :cookbook_paths
include Enumerable
@@ -39,9 +41,23 @@ class Chef
@cookbooks_by_name = Mash.new
@loaded_cookbooks = {}
@metadata = Mash.new
+ @cookbooks_paths = Hash.new {|h,k| h[k] = []} # for deprecation warnings
+
+ # Used to track which cookbooks appear in multiple places in the cookbook repos
+ # and are merged in to a single cookbook by file shadowing. This behavior is
+ # deprecated, so users of this class may issue warnings to the user by checking
+ # this variable
+ @merged_cookbooks = []
+
load_cookbooks
end
+ def merged_cookbook_paths # for deprecation warnings
+ merged_cookbook_paths = {}
+ @merged_cookbooks.each {|c| merged_cookbook_paths[c] = @cookbooks_paths[c]}
+ merged_cookbook_paths
+ end
+
def load_cookbooks
cookbook_settings = Hash.new
@repo_paths.each do |repo_path|
@@ -52,7 +68,9 @@ class Chef
loader = Cookbook::CookbookVersionLoader.new(cookbook_path, chefignore)
loader.load_cookbooks
next if loader.empty?
+ @cookbooks_paths[loader.cookbook_name] << cookbook_path # for deprecation warnings
if @loaded_cookbooks.key?(loader.cookbook_name)
+ @merged_cookbooks << loader.cookbook_name # for deprecation warnings
@loaded_cookbooks[loader.cookbook_name].merge!(loader)
else
@loaded_cookbooks[loader.cookbook_name] = loader
diff --git a/chef/lib/chef/encrypted_data_bag_item.rb b/chef/lib/chef/encrypted_data_bag_item.rb
index 87c39dd6f2..048ab8d57e 100644
--- a/chef/lib/chef/encrypted_data_bag_item.rb
+++ b/chef/lib/chef/encrypted_data_bag_item.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@opscode.com>)
-# Copyright:: Copyright 2010 Opscode, Inc.
+# Copyright:: Copyright 2010-2011 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,6 +20,7 @@ require 'base64'
require 'openssl'
require 'chef/data_bag_item'
require 'yaml'
+require 'open-uri'
# An EncryptedDataBagItem represents a read-only data bag item where
# all values, except for the value associated with the id key, have
@@ -56,7 +57,7 @@ class Chef::EncryptedDataBagItem
def [](key)
value = @enc_hash[key]
- if key == "id"
+ if key == "id" || value.nil?
value
else
self.class.decrypt_value(value, @secret)
@@ -103,10 +104,22 @@ class Chef::EncryptedDataBagItem
def self.load_secret(path=nil)
path = path || Chef::Config[:encrypted_data_bag_secret] || DEFAULT_SECRET_FILE
- if !File.exists?(path)
- raise Errno::ENOENT, "file not found '#{path}'"
- end
- secret = IO.read(path).strip
+ secret = case path
+ when /^\w+:\/\//
+ # We have a remote key
+ begin
+ Kernel.open(path).read.strip
+ rescue Errno::ECONNREFUSED
+ raise ArgumentError, "Remote key not available from '#{path}'"
+ rescue OpenURI::HTTPError
+ raise ArgumentError, "Remote key not found at '#{path}'"
+ end
+ else
+ if !File.exists?(path)
+ raise Errno::ENOENT, "file not found '#{path}'"
+ end
+ IO.read(path).strip
+ end
if secret.size < 1
raise ArgumentError, "invalid zero length secret in '#{path}'"
end
diff --git a/chef/lib/chef/environment.rb b/chef/lib/chef/environment.rb
index 6ae81c796d..a647a91f18 100644
--- a/chef/lib/chef/environment.rb
+++ b/chef/lib/chef/environment.rb
@@ -2,6 +2,7 @@
# Author:: Stephen Delano (<stephen@opscode.com>)
# Author:: Seth Falcon (<seth@opscode.com>)
# Author:: Nuo Yan (<nuo@opscode.com>)
+# Author:: John Keiser (<jkeiser@ospcode.com>)
# Copyright:: Copyright 2010-2011 Opscode, Inc.
# License:: Apache License, Version 2.0
#
@@ -405,12 +406,17 @@ class Chef
def self.cdb_load_filtered_recipe_list(name, couchdb=nil)
cdb_load_filtered_cookbook_versions(name, couchdb).map do |cb_name, cb|
- cb.first.recipe_filenames_by_name.keys.map do |recipe|
- case recipe
- when DEFAULT
- cb_name
- else
- "#{cb_name}::#{recipe}"
+ if cb.empty? # no available versions
+ [] # empty list elided with flatten
+ else
+ latest_version = cb.first
+ latest_version.recipe_filenames_by_name.keys.map do |recipe|
+ case recipe
+ when DEFAULT
+ cb_name
+ else
+ "#{cb_name}::#{recipe}"
+ end
end
end
end.flatten
diff --git a/chef/lib/chef/knife.rb b/chef/lib/chef/knife.rb
index 0279549f12..8cd1920216 100644
--- a/chef/lib/chef/knife.rb
+++ b/chef/lib/chef/knife.rb
@@ -497,34 +497,6 @@ class Chef
self.msg("Deleted #{obj_name}")
end
- def bulk_delete(klass, fancy_name, delete_name=nil, list=nil, regex=nil, &block)
- object_list = list ? list : klass.list(true)
-
- if regex
- to_delete = Hash.new
- object_list.each_key do |object|
- next if regex && object !~ /#{regex}/
- to_delete[object] = object_list[object]
- end
- else
- to_delete = object_list
- end
-
- output(format_list_for_display(to_delete))
-
- confirm("Do you really want to delete the above items")
-
- to_delete.each do |name, object|
- if Kernel.block_given?
- block.call(name, object)
- else
- object.destroy
- end
- output(format_for_display(object)) if config[:print_after]
- self.msg("Deleted #{fancy_name} #{name}")
- end
- end
-
def rest
@rest ||= begin
require 'chef/rest'
diff --git a/chef/lib/chef/knife/bootstrap.rb b/chef/lib/chef/knife/bootstrap.rb
index 4fba49f969..4431b55f17 100644
--- a/chef/lib/chef/knife/bootstrap.rb
+++ b/chef/lib/chef/knife/bootstrap.rb
@@ -72,6 +72,11 @@ class Chef
:description => "The version of Chef to install",
:proc => lambda { |v| Chef::Config[:knife][:bootstrap_version] = v }
+ option :bootstrap_proxy,
+ :long => "--bootstrap-proxy PROXY_URL",
+ :description => "The proxy server for the node being bootstrapped",
+ :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
+
option :distro,
:short => "-d DISTRO",
:long => "--distro DISTRO",
@@ -110,6 +115,7 @@ class Chef
bootstrap_files << File.join(File.dirname(__FILE__), 'bootstrap', "#{config[:distro]}.erb")
bootstrap_files << File.join(Dir.pwd, ".chef", "bootstrap", "#{config[:distro]}.erb")
bootstrap_files << File.join(ENV['HOME'], '.chef', 'bootstrap', "#{config[:distro]}.erb")
+ bootstrap_files << Gem.find_files(File.join("chef","knife","bootstrap","#{config[:distro]}.erb"))
end
template = Array(bootstrap_files).find do |bootstrap_template|
@@ -166,6 +172,7 @@ class Chef
def knife_ssh
ssh = Chef::Knife::Ssh.new
+ ssh.ui = ui
ssh.name_args = [ server_name, ssh_command ]
ssh.config[:ssh_user] = config[:ssh_user]
ssh.config[:ssh_password] = config[:ssh_password]
diff --git a/chef/lib/chef/knife/bootstrap/archlinux-gems.erb b/chef/lib/chef/knife/bootstrap/archlinux-gems.erb
index 41c3c48fad..b23a3ff3df 100644
--- a/chef/lib/chef/knife/bootstrap/archlinux-gems.erb
+++ b/chef/lib/chef/knife/bootstrap/archlinux-gems.erb
@@ -1,4 +1,6 @@
bash -c '
+<%= "export http_proxy=\"#{knife_config[:bootstrap_proxy]}\"" if knife_config[:bootstrap_proxy] -%>
+
if [ ! -f /usr/bin/chef-client ]; then
pacman -Syy
pacman -S --noconfirm ruby ntp base-devel
@@ -10,7 +12,7 @@ fi
mkdir -p /etc/chef
(
cat <<'EOP'
-<%= IO.read(Chef::Config[:validation_key]) %>
+<%= validation_key %>
EOP
) > /tmp/validation.pem
awk NF /tmp/validation.pem > /etc/chef/validation.pem
@@ -20,21 +22,22 @@ rm /tmp/validation.pem
cat <<'EOP'
log_level :info
log_location STDOUT
-chef_server_url "<%= Chef::Config[:chef_server_url] %>"
-validation_client_name "<%= Chef::Config[:validation_client_name] %>"
-<% if @config[:chef_node_name] == nil %>
-# Using default node name (fqdn)
-<% else %>
+chef_server_url "<%= @chef_config[:chef_server_url] %>"
+validation_client_name "<%= @chef_config[:validation_client_name] %>"
+<% if @config[:chef_node_name] -%>
node_name "<%= @config[:chef_node_name] %>"
-<% end %>
-<% if Chef::Config[:environment] != nil %>
-environment "<%= Chef::Config[:environment]%>"
-<% end %>
+<% else -%>
+# Using default node name (fqdn)
+<% end -%>
# ArchLinux follows the Filesystem Hierarchy Standard
file_cache_path "/var/cache/chef"
file_backup_path "/var/lib/chef/backup"
pid_file "/var/run/chef/client.pid"
cache_options({ :path => "/var/cache/chef/checksums", :skip_expires => true})
+<% if knife_config[:bootstrap_proxy] %>
+http_proxy "<%= knife_config[:bootstrap_proxy] %>"
+https_proxy "<%= knife_config[:bootstrap_proxy] %>"
+<% end -%>
EOP
) > /etc/chef/client.rb
@@ -44,5 +47,4 @@ cat <<'EOP'
EOP
) > /etc/chef/first-boot.json
-/usr/bin/chef-client -j /etc/chef/first-boot.json
-'
+<%= start_chef %>'
diff --git a/chef/lib/chef/knife/bootstrap/centos5-gems.erb b/chef/lib/chef/knife/bootstrap/centos5-gems.erb
index b51f67d9de..cfaf9876a8 100644
--- a/chef/lib/chef/knife/bootstrap/centos5-gems.erb
+++ b/chef/lib/chef/knife/bootstrap/centos5-gems.erb
@@ -1,14 +1,17 @@
bash -c '
+<%= "export http_proxy=\"#{knife_config[:bootstrap_proxy]}\"" if knife_config[:bootstrap_proxy] -%>
+
if [ ! -f /usr/bin/chef-client ]; then
- rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm
- wget -O /etc/yum.repos.d/aegis.repo http://rpm.aegisco.com/aegisco/el5/aegisco.repo
+ wget <%= "--proxy=on " if knife_config[:bootstrap_proxy] %>http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm
+ rpm -Uvh epel-release-5-4.noarch.rpm
+ wget <%= "--proxy=on " if knife_config[:bootstrap_proxy] %>-O /etc/yum.repos.d/aegis.repo http://rpm.aegisco.com/aegisco/el5/aegisco.repo
yum install -y ruby-1.8.7.334-2.el5 ruby-devel-1.8.7.334-2.el5 gcc gcc-c++ automake autoconf make
cd /tmp
- wget http://production.cf.rubygems.org/rubygems/rubygems-1.3.7.tgz
- tar zxf rubygems-1.3.7.tgz
- cd rubygems-1.3.7
+ wget <%= "--proxy=on " if knife_config[:bootstrap_proxy] %>http://production.cf.rubygems.org/rubygems/rubygems-1.6.2.tgz
+ tar zxf rubygems-1.6.2.tgz
+ cd rubygems-1.6.2
ruby setup.rb --no-format-executable
fi
diff --git a/chef/lib/chef/knife/bootstrap/fedora13-gems.erb b/chef/lib/chef/knife/bootstrap/fedora13-gems.erb
index 9318eed258..b9819f20f2 100644
--- a/chef/lib/chef/knife/bootstrap/fedora13-gems.erb
+++ b/chef/lib/chef/knife/bootstrap/fedora13-gems.erb
@@ -1,4 +1,6 @@
bash -c '
+<%= "export http_proxy=\"#{knife_config[:bootstrap_proxy]}\"" if knife_config[:bootstrap_proxy] -%>
+
yum install -y ruby ruby-devel gcc gcc-c++ automake autoconf rubygems make
gem update --system
diff --git a/chef/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb b/chef/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb
index 47690792b7..67206dfaae 100644
--- a/chef/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb
+++ b/chef/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb
@@ -1,32 +1,39 @@
bash -c '
+<%= "export http_proxy=\"#{knife_config[:bootstrap_proxy]}\"" if knife_config[:bootstrap_proxy] -%>
+
if [ ! -f /usr/bin/chef-client ]; then
- echo "chef chef/chef_server_url string <%= Chef::Config[:chef_server_url] %>" | debconf-set-selections
- [ -f /etc/apt/sources.list.d/opscode.list ] || echo "deb http://apt.opscode.com lucid main" > /etc/apt/sources.list.d/opscode.list
- wget -O- http://apt.opscode.com/packages@opscode.com.gpg.key | apt-key add -
+ echo "chef chef/chef_server_url string <%= @chef_config[:chef_server_url] %>" | debconf-set-selections
+ [ -f /etc/apt/sources.list.d/opscode.list ] || echo "deb http://apt.opscode.com <%= chef_version.to_f == 0.10 ? "lucid-0.10" : "lucid" %> main" > /etc/apt/sources.list.d/opscode.list
+ wget <%= "--proxy=on " if knife_config[:bootstrap_proxy] %>-O- http://apt.opscode.com/packages@opscode.com.gpg.key | apt-key add -
fi
apt-get update
apt-get install -y chef
-<% unless Chef::Config[:validation_client_name] == "chef-validator" -%>
-[ `grep -qx "validation_client_name \"<%= Chef::Config[:validation_client_name] %>\"" /etc/chef/client.rb` ] || echo "validation_client_name \"<%= Chef::Config[:validation_client_name] %>\"" >> /etc/chef/client.rb
-<% end -%>
-
(
cat <<'EOP'
-<%= IO.read(Chef::Config[:validation_key]) %>
+<%= validation_key %>
EOP
) > /tmp/validation.pem
awk NF /tmp/validation.pem > /etc/chef/validation.pem
rm /tmp/validation.pem
+<% unless @chef_config[:validation_client_name] == "chef-validator" -%>
+[ `grep -qx "validation_client_name \"<%= @chef_config[:validation_client_name] %>\"" /etc/chef/client.rb` ] || echo "validation_client_name \"<%= @chef_config[:validation_client_name] %>\"" >> /etc/chef/client.rb
+<% end -%>
+
<% if @config[:chef_node_name] %>
[ `grep -qx "node_name \"<%= @config[:chef_node_name] %>\"" /etc/chef/client.rb` ] || echo "node_name \"<%= @config[:chef_node_name] %>\"" >> /etc/chef/client.rb
<% end -%>
+<% if knife_config[:bootstrap_proxy] %>
+echo 'http_proxy "knife_config[:bootstrap_proxy]"' >> /etc/chef/client.rb
+echo 'https_proxy "knife_config[:bootstrap_proxy]"' >> /etc/chef/client.rb
+<% end -%>
+
(
cat <<'EOP'
<%= { "run_list" => @run_list }.to_json %>
EOP
) > /etc/chef/first-boot.json
-/usr/bin/chef-client -j /etc/chef/first-boot.json'
+<%= start_chef %>'
diff --git a/chef/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb b/chef/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb
index 7aa6758639..0182dd234f 100644
--- a/chef/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb
+++ b/chef/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb
@@ -1,14 +1,18 @@
bash -c '
+<%= "export http_proxy=\"#{knife_config[:bootstrap_proxy]}\"" if knife_config[:bootstrap_proxy] -%>
+
if [ ! -f /usr/bin/chef-client ]; then
apt-get update
apt-get install -y ruby ruby1.8-dev build-essential wget libruby-extras libruby1.8-extras
cd /tmp
- wget http://production.cf.rubygems.org/rubygems/rubygems-1.3.7.tgz
- tar zxf rubygems-1.3.7.tgz
- cd rubygems-1.3.7
+ wget <%= "--proxy=on " if knife_config[:bootstrap_proxy] %>http://production.cf.rubygems.org/rubygems/rubygems-1.6.2.tgz
+ tar zxf rubygems-1.6.2.tgz
+ cd rubygems-1.6.2
ruby setup.rb --no-format-executable
fi
+gem update --system
+gem update
gem install ohai --no-rdoc --no-ri --verbose
gem install chef --no-rdoc --no-ri --verbose <%= bootstrap_version_string %>
diff --git a/chef/lib/chef/knife/client_bulk_delete.rb b/chef/lib/chef/knife/client_bulk_delete.rb
index 1a2b3bfa97..8bf2c2f116 100644
--- a/chef/lib/chef/knife/client_bulk_delete.rb
+++ b/chef/lib/chef/knife/client_bulk_delete.rb
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -29,12 +29,34 @@ class Chef
banner "knife client bulk delete REGEX (options)"
- def run
- if @name_args.length < 1
+ def run
+ if name_args.length < 1
ui.fatal("You must supply a regular expression to match the results against")
exit 42
- else
- bulk_delete(Chef::ApiClient, "client", nil, nil, @name_args[0])
+ end
+ all_clients = Chef::ApiClient.list(true)
+
+ matcher = /#{name_args[0]}/
+ clients_to_delete = {}
+ all_clients.each do |name, client|
+ next unless name =~ matcher
+ clients_to_delete[client.name] = client
+ end
+
+ if clients_to_delete.empty?
+ ui.info "No clients match the expression /#{name_args[0]}/"
+ exit 0
+ end
+
+ ui.msg("The following clients will be deleted:")
+ ui.msg("")
+ ui.msg(ui.list(clients_to_delete.keys.sort, :columns_down))
+ ui.msg("")
+ ui.confirm("Are you sure you want to delete these clients")
+
+ clients_to_delete.sort.each do |name, client|
+ client.destroy
+ ui.msg("Deleted client #{name}")
end
end
end
diff --git a/chef/lib/chef/knife/cookbook_upload.rb b/chef/lib/chef/knife/cookbook_upload.rb
index 471def07d4..2945a9fef9 100644
--- a/chef/lib/chef/knife/cookbook_upload.rb
+++ b/chef/lib/chef/knife/cookbook_upload.rb
@@ -60,11 +60,17 @@ class Chef
:long => '--environment ENVIRONMENT',
:description => "Set ENVIRONMENT's version dependency match the version you're uploading.",
:default => nil
+
+ option :depends,
+ :short => "-d",
+ :long => "--include-dependencies",
+ :description => "Also upload cookbook dependencies"
def run
config[:cookbook_path] ||= Chef::Config[:cookbook_path]
assert_environment_valid!
+ warn_about_cookbook_shadowing
version_constraints_to_update = {}
if config[:all]
@@ -84,6 +90,11 @@ class Chef
@name_args.each do |cookbook_name|
begin
cookbook = cookbook_repo[cookbook_name]
+ if config[:depends]
+ cookbook.metadata.dependencies.each do |dep, versions|
+ @name_args.push dep
+ end
+ end
cookbook.freeze_version if config[:freeze]
upload(cookbook, justify_width)
version_constraints_to_update[cookbook_name] = cookbook.version
@@ -117,6 +128,22 @@ class Chef
@environment ||= config[:environment] ? Environment.load(config[:environment]) : nil
end
+ def warn_about_cookbook_shadowing
+ unless cookbook_repo.merged_cookbooks.empty?
+ ui.warn "* " * 40
+ ui.warn(<<-WARNING)
+The cookbooks: #{cookbook_repo.merged_cookbooks.join(', ')} exist in multiple places in your cookbook_path.
+A composite version of these cookbooks has been compiled for uploading.
+
+#{ui.color('IMPORTANT:', :red, :bold)} In a future version of Chef, this behavior will be removed and you will no longer
+be able to have the same version of a cookbook in multiple places in your cookbook_path.
+WARNING
+ ui.warn "The affected cookbooks are located:"
+ ui.output ui.format_for_display(cookbook_repo.merged_cookbook_paths)
+ ui.warn "* " * 40
+ end
+ end
+
private
def assert_environment_valid!
diff --git a/chef/lib/chef/knife/core/bootstrap_context.rb b/chef/lib/chef/knife/core/bootstrap_context.rb
index 26510851c7..f88167c5c2 100644
--- a/chef/lib/chef/knife/core/bootstrap_context.rb
+++ b/chef/lib/chef/knife/core/bootstrap_context.rb
@@ -38,8 +38,7 @@ class Chef
if @config[:prerelease]
"--prerelease"
else
- version = knife_config[:bootstrap_version] || Chef::VERSION
- "--version #{version}"
+ "--version #{chef_version}"
end
end
@@ -63,17 +62,28 @@ CONFIG
else
client_rb << "# Using default node name (fqdn)\n"
end
+
+ if knife_config[:bootstrap_proxy]
+ client_rb << %Q{http_proxy "#{knife_config[:bootstrap_proxy]}"\n}
+ client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n}
+ end
client_rb
end
def start_chef
- "/usr/bin/chef-client -j /etc/chef/first-boot.json -E #{bootstrap_environment}"
+ s = "/usr/bin/chef-client -j /etc/chef/first-boot.json"
+ s << " -E #{bootstrap_environment}" if chef_version.to_f != 0.9 # only use the -E option on Chef 0.10+
+ s
end
def knife_config
@chef_config.key?(:knife) ? @chef_config[:knife] : {}
end
+ def chef_version
+ knife_config[:bootstrap_version] || Chef::VERSION
+ end
+
end
end
end
diff --git a/chef/lib/chef/knife/core/cookbook_scm_repo.rb b/chef/lib/chef/knife/core/cookbook_scm_repo.rb
index 0b9d0e34cf..f2f8f206a0 100644
--- a/chef/lib/chef/knife/core/cookbook_scm_repo.rb
+++ b/chef/lib/chef/knife/core/cookbook_scm_repo.rb
@@ -113,7 +113,7 @@ class Chef
end
def branch_exists?(branch_name)
- git("branch --no-color").stdout.lines.any? {|l| l.include?(branch_name) }
+ git("branch --no-color").stdout.lines.any? {|l| l =~ /\s#{Regexp.escape(branch_name)}(?:\s|$)/ }
end
private
diff --git a/chef/lib/chef/knife/core/node_presenter.rb b/chef/lib/chef/knife/core/node_presenter.rb
index 4ef59df294..80239bce30 100644
--- a/chef/lib/chef/knife/core/node_presenter.rb
+++ b/chef/lib/chef/knife/core/node_presenter.rb
@@ -60,14 +60,17 @@ class Chef
def summarize(data)
if data.kind_of?(Chef::Node)
node = data
+ # special case ec2 with their split horizon whatsis.
+ ip = (node[:ec2] && node[:ec2][:public_ipv4]) || node[:ipaddress]
+
summarized=<<-SUMMARY
#{ui.color('Node Name:', :bold)} #{ui.color(node.name, :bold)}
#{key('Environment:')} #{node.chef_environment}
#{key('FQDN:')} #{node[:fqdn]}
-#{key('IP:')} #{node[:ipaddress]}
+#{key('IP:')} #{ip}
#{key('Run List:')} #{node.run_list}
#{key('Roles:')} #{Array(node[:roles]).join(', ')}
-#{key('Recipes')} #{Array(node[:recipes]).join(', ')}
+#{key('Recipes:')} #{Array(node[:recipes]).join(', ')}
#{key('Platform:')} #{node[:platform]} #{node[:platform_version]}
SUMMARY
if config[:medium_output] || config[:long_output]
diff --git a/chef/lib/chef/knife/help.rb b/chef/lib/chef/knife/help.rb
index d2d2d79c13..13fe674704 100644
--- a/chef/lib/chef/knife/help.rb
+++ b/chef/lib/chef/knife/help.rb
@@ -55,23 +55,25 @@ MOAR_HELP
@topic = find_manpages_for_query(@query)
end
- manpage_path = available_manpages_by_basename[@topic]
+ manpage_path = find_manpage_path(@topic)
exec "man #{manpage_path}"
end
def help_topics
- available_manpages_by_basename.keys.map {|c| c.sub(/^knife\-/, '')}.sort
+ # The list of help topics is generated by a rake task from the available man pages
+ # This constant is provided in help_topics.rb which is automatically required/loaded by the knife subcommand loader.
+ HELP_TOPICS
end
def print_help_topics
ui.info "Available help topics are: "
- help_topics.each do |topic|
+ help_topics.collect {|t| t.gsub(/knife-/, '') }.sort.each do |topic|
ui.msg " #{topic}"
end
end
def find_manpages_for_query(query)
- possibilities = available_manpages_by_basename.keys.select do |manpage|
+ possibilities = help_topics.select do |manpage|
::File.fnmatch("knife-#{query}*", manpage) || ::File.fnmatch("#{query}*", manpage)
end
if possibilities.empty?
@@ -87,16 +89,15 @@ MOAR_HELP
end
end
- def available_manpages_by_basename
- @available_manpages_by_basename ||= begin
- available_manpages = Dir[File.expand_path("../distro/common/man/man1/*1", CHEF_ROOT)]
- available_manpages.inject({}) do |map, manpath|
- map[::File.basename(manpath, '.1')] = manpath
- map
- end
+ def find_manpage_path(topic)
+ if ::File.exists?(::File.expand_path("../distro/common/man/man1/#{topic}.1", CHEF_ROOT))
+ # If we've provided the man page in the gem, give that
+ return ::File.expand_path("../distro/common/man/man1/#{topic}.1", CHEF_ROOT)
+ else
+ # Otherwise, we'll just be using MANPATH
+ topic
end
end
-
end
end
end
diff --git a/chef/lib/chef/knife/help_topics.rb b/chef/lib/chef/knife/help_topics.rb
new file mode 100644
index 0000000000..90f638f357
--- /dev/null
+++ b/chef/lib/chef/knife/help_topics.rb
@@ -0,0 +1,4 @@
+# Do not edit this file by hand
+# This file is autogenerated by the docs:list rake task from the available manpages
+
+HELP_TOPICS = ["knife-ssh", "knife-environment", "knife-bootstrap", "knife-cookbook", "knife-exec", "knife-tag", "knife-status", "knife", "knife-data-bag", "knife-index", "knife-configure", "knife-search", "knife-node", "knife-client", "shef", "knife-role", "knife-cookbook-site"]
diff --git a/chef/lib/chef/knife/ssh.rb b/chef/lib/chef/knife/ssh.rb
index 58c393e7e9..ca9b60fe42 100644
--- a/chef/lib/chef/knife/ssh.rb
+++ b/chef/lib/chef/knife/ssh.rb
@@ -149,9 +149,8 @@ class Chef
data.split(/\n/).each { |d| print_data(host, d) }
else
padding = @longest - host.length
- print ui.color(host, :cyan)
- padding.downto(0) { print " " }
- puts data
+ str = ui.color(host, :cyan) + (" " * (padding + 1)) + data
+ ui.msg(str)
end
end
@@ -241,6 +240,7 @@ class Chef
window = 0
session.servers_for.each do |server|
tf.print("screen -t \"#{server.host}\" #{window} ssh ")
+ tf.print("-i #{config[:identity_file]} ") if config[:identity_file]
server.user ? tf.puts("#{server.user}@#{server.host}") : tf.puts(server.host)
window += 1
end
@@ -250,8 +250,9 @@ class Chef
def tmux
ssh_dest = lambda do |server|
+ identity = "-i #{config[:identity_file]} " if config[:identity_file]
prefix = server.user ? "#{server.user}@" : ""
- "'ssh #{prefix}#{server.host}'"
+ "'ssh #{identity}#{prefix}#{server.host}'"
end
new_window_cmds = lambda do
diff --git a/chef/lib/chef/mixin/language.rb b/chef/lib/chef/mixin/language.rb
index 664d57549b..73bde116e8 100644
--- a/chef/lib/chef/mixin/language.rb
+++ b/chef/lib/chef/mixin/language.rb
@@ -69,24 +69,21 @@ class Chef
@values["default"] = value
else
assert_valid_platform_values!(platforms, value)
- Array(platforms).each { |platform| @values[platform.to_s] = format_values(value)}
+ Array(platforms).each { |platform| @values[platform.to_s] = normalize_keys(value)}
value
end
end
- def format_values(hash)
- formatted_array = flatten_one_level(hash.map { |key, value| [key.to_s, value]})
- Hash[*formatted_array]
- end
-
- def flatten_one_level(array)
- array.inject([]) do |flatter_array, values|
- Array(values).each {|value| flatter_array << value }
- flatter_array
+ def normalize_keys(hash)
+ hash.inject({}) do |h, key_value|
+ keys, value = *key_value
+ Array(keys).each do |key|
+ h[key.to_s] = value
+ end
+ h
end
end
-
def assert_valid_platform_values!(platforms, value)
unless value.kind_of?(Hash)
msg = "platform dependent values must be specified in the format :platform => {:version => value} "
diff --git a/chef/lib/chef/monkey_patches/numeric.rb b/chef/lib/chef/monkey_patches/numeric.rb
index fef1f27693..1f5ff14209 100644
--- a/chef/lib/chef/monkey_patches/numeric.rb
+++ b/chef/lib/chef/monkey_patches/numeric.rb
@@ -4,4 +4,12 @@ unless 0.respond_to?(:fdiv)
to_f / other
end
end
-end \ No newline at end of file
+end
+
+# String elements referenced with [] <= 1.8.6 return a Fixnum. Cheat to allow
+# for the simpler "test"[2].ord construct
+class Numeric
+ def ord
+ return self
+ end
+end
diff --git a/chef/lib/chef/monkey_patches/string.rb b/chef/lib/chef/monkey_patches/string.rb
index ac23f09647..46207dea27 100644
--- a/chef/lib/chef/monkey_patches/string.rb
+++ b/chef/lib/chef/monkey_patches/string.rb
@@ -21,8 +21,29 @@
# give the actual number of characters. In Chef::REST, we need the bytesize
# so we can correctly set the Content-Length headers, but ruby 1.8.6 and lower
# don't define String#bytesize. Monkey patching time!
+
+begin
+ require 'enumerator'
+rescue LoadError
+end
+
class String
unless method_defined?(:bytesize)
alias :bytesize :size
end
+
+ unless method_defined?(:lines)
+ def lines
+ enum_for(:each)
+ end
+ end
+end
+
+# <= 1.8.6 needs some ord!
+class String
+ unless method_defined?(:ord)
+ def ord
+ self.unpack('c').first
+ end
+ end
end
diff --git a/chef/lib/chef/platform.rb b/chef/lib/chef/platform.rb
index 9efba7cb75..0b91413ebd 100644
--- a/chef/lib/chef/platform.rb
+++ b/chef/lib/chef/platform.rb
@@ -126,7 +126,8 @@ class Chef
:default => {
:service => Chef::Provider::Service::Redhat,
:cron => Chef::Provider::Cron,
- :package => Chef::Provider::Package::Zypper
+ :package => Chef::Provider::Package::Zypper,
+ :group => Chef::Provider::Group::Suse
}
},
:redhat => {
diff --git a/chef/lib/chef/provider.rb b/chef/lib/chef/provider.rb
index 1b81f29578..2f681f2636 100644
--- a/chef/lib/chef/provider.rb
+++ b/chef/lib/chef/provider.rb
@@ -81,7 +81,7 @@ class Chef
class << self
include Chef::Mixin::ConvertToClassName
- def build_from_file(cookbook_name, filename)
+ def build_from_file(cookbook_name, filename, run_context)
pname = filename_to_qualified_string(cookbook_name, filename)
# Add log entry if we override an existing light-weight provider.
diff --git a/chef/lib/chef/provider/git.rb b/chef/lib/chef/provider/git.rb
index 025ba68e2d..126ea324cb 100644
--- a/chef/lib/chef/provider/git.rb
+++ b/chef/lib/chef/provider/git.rb
@@ -38,10 +38,11 @@ class Chef
def action_checkout
assert_target_directory_valid!
- if target_dir_non_existant_or_empty?
+ if target_dir_non_existent_or_empty?
clone
checkout
enable_submodules
+ add_remotes
@new_resource.updated_by_last_action(true)
else
Chef::Log.debug "#{@new_resource} checkout destination #{@new_resource.destination} already exists or is a non-empty directory"
@@ -66,7 +67,7 @@ class Chef
Chef::Log.info "#{@new_resource} updated to revision #{target_revision}"
@new_resource.updated_by_last_action(true)
end
-
+ add_remotes
else
action_checkout
@new_resource.updated_by_last_action(true)
@@ -85,7 +86,7 @@ class Chef
::File.exist?(::File.join(@new_resource.destination, ".git"))
end
- def target_dir_non_existant_or_empty?
+ def target_dir_non_existent_or_empty?
!::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == ['.','..']
end
@@ -98,6 +99,18 @@ class Chef
sha_hash?(result) ? result : nil
end
+ def add_remotes
+ if (@new_resource.additional_remotes.length > 0)
+ @new_resource.additional_remotes.each_pair do |remote_name, remote_url|
+ Chef::Log.info "#{@new_resource} adding git remote #{remote_name} = #{remote_url}"
+ command = "git remote add #{remote_name} #{remote_url}"
+ if shell_out(command, run_options(:cwd => @new_resource.destination, :command_log_level => :info)).exitstatus != 0
+ @new_resource.updated_by_last_action(true)
+ end
+ end
+ end
+ end
+
def clone
remote = @new_resource.remote
diff --git a/chef/lib/chef/provider/group/suse.rb b/chef/lib/chef/provider/group/suse.rb
new file mode 100644
index 0000000000..22486eb9d0
--- /dev/null
+++ b/chef/lib/chef/provider/group/suse.rb
@@ -0,0 +1,53 @@
+#
+# Author:: AJ Christensen (<aj@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/provider/group/groupadd'
+require 'chef/mixin/shell_out'
+
+class Chef
+ class Provider
+ class Group
+ class Suse < Chef::Provider::Group::Groupadd
+
+ include Chef::Mixin::ShellOut
+
+ def load_current_resource
+ super
+
+ raise Chef::Exceptions::Group, "Could not find binary /usr/sbin/groupmod for #{@new_resource}" unless ::File.exists?("/usr/sbin/groupmod")
+ end
+
+ def modify_group_members
+ unless @new_resource.members.empty?
+ if(@new_resource.append)
+ @new_resource.members.each do |member|
+ Chef::Log.debug("#{@new_resource} appending member #{member} to group #{@new_resource.group_name}")
+ shell_out!("groupmod -A #{member} #{@new_resource.group_name}")
+ end
+ else
+ Chef::Log.debug("#{@new_resource} setting group members to #{@new_resource.members.join(', ')}")
+ shell_out!("groupmod -A #{@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
+ end
+ end
+ end
+ end
+end
diff --git a/chef/lib/chef/provider/mount/mount.rb b/chef/lib/chef/provider/mount/mount.rb
index c0b370b02c..7419182d6e 100644
--- a/chef/lib/chef/provider/mount/mount.rb
+++ b/chef/lib/chef/provider/mount/mount.rb
@@ -36,29 +36,21 @@ class Chef
@current_resource = Chef::Resource::Mount.new(@new_resource.name)
@current_resource.mount_point(@new_resource.mount_point)
@current_resource.device(@new_resource.device)
- Chef::Log.debug("Checking for mount point #{@current_resource.mount_point}")
-
+ mounted?
+ enabled?
+ end
+
+ def mountable?
# only check for existence of non-remote devices
if (device_should_exist? && !::File.exists?(device_real) )
raise Chef::Exceptions::Mount, "Device #{@new_resource.device} does not exist"
elsif( !::File.exists?(@new_resource.mount_point) )
raise Chef::Exceptions::Mount, "Mount point #{@new_resource.mount_point} does not exist"
end
-
- # Check to see if the volume is mounted. Last volume entry wins.
- mounted = false
- shell_out!("mount").stdout.each_line do |line|
- case line
- when /^#{device_mount_regex}\s+on\s+#{Regexp.escape(@new_resource.mount_point)}/
- mounted = true
- Chef::Log.debug("Special device #{device_logstring} mounted as #{@new_resource.mount_point}")
- when /^([\/\w])+\son\s#{Regexp.escape(@new_resource.mount_point)}\s+/
- mounted = false
- Chef::Log.debug("Special device #{$~[1]} mounted as #{@new_resource.mount_point}")
- end
- end
- @current_resource.mounted(mounted)
-
+ return true
+ end
+
+ def enabled?
# Check to see if there is a entry in /etc/fstab. Last entry for a volume wins.
enabled = false
::File.foreach("/etc/fstab") do |line|
@@ -72,16 +64,33 @@ class Chef
@current_resource.dump($3.to_i)
@current_resource.pass($4.to_i)
Chef::Log.debug("Found mount #{device_fstab} to #{@new_resource.mount_point} in /etc/fstab")
- when /^[\/\w]+\s+#{Regexp.escape(@new_resource.mount_point)}/
+ next
+ when /^[\/\w]+\s+#{Regexp.escape(@new_resource.mount_point)}\s+/
enabled = false
Chef::Log.debug("Found conflicting mount point #{@new_resource.mount_point} in /etc/fstab")
end
end
@current_resource.enabled(enabled)
end
+
+ def mounted?
+ mounted = false
+ shell_out!("mount").stdout.each_line do |line|
+ case line
+ when /^#{device_mount_regex}\s+on\s+#{Regexp.escape(@new_resource.mount_point)}/
+ mounted = true
+ Chef::Log.debug("Special device #{device_logstring} mounted as #{@new_resource.mount_point}")
+ when /^([\/\w])+\son\s#{Regexp.escape(@new_resource.mount_point)}\s+/
+ mounted = false
+ Chef::Log.debug("Special device #{$~[1]} mounted as #{@new_resource.mount_point}")
+ end
+ end
+ @current_resource.mounted(mounted)
+ end
def mount_fs
unless @current_resource.mounted
+ mountable?
command = "mount -t #{@new_resource.fstype}"
command << " -o #{@new_resource.options.join(',')}" unless @new_resource.options.nil? || @new_resource.options.empty?
command << case @new_resource.device_type
@@ -112,7 +121,6 @@ class Chef
def remount_fs
if @current_resource.mounted and @new_resource.supports[:remount]
shell_out!("mount -o remount #{@new_resource.mount_point}")
-
@new_resource.updated_by_last_action(true)
Chef::Log.debug("#{@new_resource} is remounted at #{@new_resource.mount_point}")
elsif @current_resource.mounted
diff --git a/chef/lib/chef/provider/package/apt.rb b/chef/lib/chef/provider/package/apt.rb
index 5f7ff0d204..44bb0f158e 100644
--- a/chef/lib/chef/provider/package/apt.rb
+++ b/chef/lib/chef/provider/package/apt.rb
@@ -25,44 +25,57 @@ class Chef
class Package
class Apt < Chef::Provider::Package
+ include Chef::Mixin::ShellOut
+ attr_accessor :virtual
+
def load_current_resource
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
+ check_package_state(@new_resource.package_name)
+ @current_resource
+ end
+
+ def check_package_state(package)
+ Chef::Log.debug("Checking package status for #{package}")
+ installed = false
+ depends = false
- Chef::Log.debug("#{@new_resource} checking apt-cache policy")
- status = popen4("apt-cache policy #{@new_resource.package_name}") do |pid, stdin, stdout, stderr|
- stdout.each do |line|
- case line
- when /^\s{2}Installed: (.+)$/
- installed_version = $1
- if installed_version == '(none)'
- Chef::Log.debug("#{@new_resource} current version is nil")
- @current_resource.version(nil)
- else
- Chef::Log.debug("#{@new_resource} current version is #{installed_version}")
- @current_resource.version(installed_version)
- end
- when /^\s{2}Candidate: (.+)$/
- Chef::Log.debug("#{@new_resource} candidate version is #{$1}")
- @candidate_version = $1
+ shell_out!("aptitude show #{package}").stdout.each_line do |line|
+ case line
+ when /^State: installed/
+ installed = true
+ when /^Version: (.*)/
+ @candidate_version = $1
+ if installed
+ @current_resource.version($1)
+ else
+ @current_resource.version(nil)
end
+ when /Depends: ([^\s]*) /
+ depends = $1
+ when /Provided by: ([\w\d\-\.]*)/
+ next if installed
+ virtual_provider = $1
+ virtual_provider = depends if depends
+ Chef::Log.debug("Virtual package provided by #{virtual_provider}")
+ @virtual = true
+ installed = check_package_state(virtual_provider)
+ @candidate_version = virtual_provider
end
end
- unless status.exitstatus == 0
- raise Chef::Exceptions::Package, "apt-cache failed - #{status.inspect}!"
- end
-
- if @candidate_version == "(none)"
+ if @candidate_version.nil?
raise Chef::Exceptions::Package, "apt does not have a version of package #{@new_resource.package_name}"
end
- @current_resource
+ return installed
end
def install_package(name, version)
+ package_name = "#{name}=#{version}"
+ package_name = "#{name} #{@candidate_version}" if @virtual
run_command_with_systems_locale(
- :command => "apt-get -q -y#{expand_options(@new_resource.options)} install #{name}=#{version}",
+ :command => "apt-get -q -y#{expand_options(@new_resource.options)} install #{package_name}",
:environment => {
"DEBIAN_FRONTEND" => "noninteractive"
}
@@ -74,8 +87,10 @@ class Chef
end
def remove_package(name, version)
+ package_name = "#{name}"
+ package_name = "#{name} #{@candidate_version}" if @virtual
run_command_with_systems_locale(
- :command => "apt-get -q -y#{expand_options(@new_resource.options)} remove #{@new_resource.package_name}",
+ :command => "apt-get -q -y#{expand_options(@new_resource.options)} remove #{package_name}",
:environment => {
"DEBIAN_FRONTEND" => "noninteractive"
}
diff --git a/chef/lib/chef/provider/package/dpkg.rb b/chef/lib/chef/provider/package/dpkg.rb
index 8beb14af29..8ef0ead9f8 100644
--- a/chef/lib/chef/provider/package/dpkg.rb
+++ b/chef/lib/chef/provider/package/dpkg.rb
@@ -24,7 +24,7 @@ class Chef
class Provider
class Package
class Dpkg < Chef::Provider::Package::Apt
- DPKG_INFO = /([a-z\d\-\+]+)\t([\w\d.-]+)/
+ DPKG_INFO = /([a-z\d\-\+]+)\t([\w\d.~-]+)/
DPKG_INSTALLED = /^Status: install ok installed/
DPKG_VERSION = /^Version: (.+)$/
diff --git a/chef/lib/chef/provider/package/easy_install.rb b/chef/lib/chef/provider/package/easy_install.rb
index 64723db938..6c9dacc55d 100644
--- a/chef/lib/chef/provider/package/easy_install.rb
+++ b/chef/lib/chef/provider/package/easy_install.rb
@@ -115,7 +115,7 @@ class Chef
end
def install_package(name, version)
- run_command(:command => "#{easy_install_binary_path} \"#{name}==#{version}\"")
+ run_command(:command => "#{easy_install_binary_path}#{expand_options(@new_resource.options)} \"#{name}==#{version}\"")
end
def upgrade_package(name, version)
@@ -123,7 +123,7 @@ class Chef
end
def remove_package(name, version)
- run_command(:command => "#{easy_install_binary_path} -m #{name}")
+ run_command(:command => "#{easy_install_binary_path }#{expand_options(@new_resource.options)} -m #{name}")
end
def purge_package(name, version)
diff --git a/chef/lib/chef/provider/package/freebsd.rb b/chef/lib/chef/provider/package/freebsd.rb
index ed45e73d72..139bbe02d7 100644
--- a/chef/lib/chef/provider/package/freebsd.rb
+++ b/chef/lib/chef/provider/package/freebsd.rb
@@ -57,7 +57,7 @@ class Chef
end
def ports_makefile_variable_value(variable)
- make_v = shell_out!("make -V #{variable}", :cwd => port_path, :env => nil, :returns => [0,1])
+ make_v = shell_out!("make -V #{variable} -f #{port_path}/Makefile", :env => nil, :returns => [0,1])
make_v.stdout.strip.split($\).first # $\ is the line separator, i.e., newline
end
@@ -94,7 +94,7 @@ class Chef
unless @current_resource.version
case @new_resource.source
when /^ports$/
- shell_out!("make -DBATCH install", :cwd => port_path, :env => nil).status
+ shell_out!("make -DBATCH -f #{port_path}/Makefile install", :timeout => 1200, :env => nil).status
when /^http/, /^ftp/
shell_out!("pkg_add -r #{package_name}", :env => { "PACKAGESITE" => @new_resource.source, 'LC_ALL' => nil }).status
Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}")
diff --git a/chef/lib/chef/provider/package/macports.rb b/chef/lib/chef/provider/package/macports.rb
index ceee848f54..fd33788944 100644
--- a/chef/lib/chef/provider/package/macports.rb
+++ b/chef/lib/chef/provider/package/macports.rb
@@ -43,7 +43,7 @@ class Chef
def install_package(name, version)
unless @current_resource.version == version
- command = "port install #{name}"
+ command = "port#{expand_options(@new_resource.options)} install #{name}"
command << " @#{version}" if version and !version.empty?
run_command_with_systems_locale(
:command => command
@@ -52,7 +52,7 @@ class Chef
end
def purge_package(name, version)
- command = "port uninstall #{name}"
+ command = "port#{expand_options(@new_resource.options)} uninstall #{name}"
command << " @#{version}" if version and !version.empty?
run_command_with_systems_locale(
:command => command
@@ -60,7 +60,7 @@ class Chef
end
def remove_package(name, version)
- command = "port deactivate #{name}"
+ command = "port#{expand_options(@new_resource.options)} deactivate #{name}"
command << " @#{version}" if version and !version.empty?
run_command_with_systems_locale(
@@ -79,7 +79,7 @@ class Chef
install_package(name, version)
elsif current_version != version
run_command_with_systems_locale(
- :command => "port upgrade #{name} @#{version}"
+ :command => "port#{expand_options(@new_resource.options)} upgrade #{name} @#{version}"
)
end
end
diff --git a/chef/lib/chef/provider/package/yum-dump.py b/chef/lib/chef/provider/package/yum-dump.py
index 28348d58bb..818121f5db 100644
--- a/chef/lib/chef/provider/package/yum-dump.py
+++ b/chef/lib/chef/provider/package/yum-dump.py
@@ -1,6 +1,6 @@
#
# Author:: Matthew Kent (<mkent@magoazul.com>)
-# Copyright:: Copyright (c) 2009 Matthew Kent
+# Copyright:: Copyright (c) 2009, 2011 Matthew Kent
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,110 +19,231 @@
# yum-dump.py
# Inspired by yumhelper.py by David Lutterkort
#
-# Produce a list of installed and available packages using yum and dump the
-# result to stdout.
+# Produce a list of installed, available and re-installable packages using yum
+# and dump the results to stdout.
#
-# This invokes yum just as the command line would which makes it subject to
-# all the caching related configuration paramaters in yum.conf.
+# yum-dump invokes yum similarly to the command line interface which makes it
+# subject to most of the configuration paramaters in yum.conf. yum-dump will
+# also load yum plugins in the same manor as yum - these can affect the output.
#
# Can be run as non root, but that won't update the cache.
+#
+# Intended to support yum 2.x and 3.x
import os
import sys
import time
import yum
+import re
+import errno
from yum import Errors
+from optparse import OptionParser
-PIDFILE='/var/run/yum.pid'
+YUM_PID_FILE='/var/run/yum.pid'
# Seconds to wait for exclusive access to yum
-lock_timeout = 10
+LOCK_TIMEOUT = 10
-failure = False
+if re.search(r"^3\.", yum.__version__):
+ YUM_VER = 3
+elif re.search(r"^2\.", yum.__version__):
+ YUM_VER = 2
+else:
+ print >> sys.stderr, "yum-dump Error: Can't match supported yum version" \
+ " (%s)" % yum.__version__
+ sys.exit(1)
-# Can't do try: except: finally: in python 2.4 it seems, hence this fun.
-try:
- try:
- y = yum.YumBase()
+def setup(yb, options):
+ # Only want our output
+ #
+ if YUM_VER == 3:
try:
- # Only want our output
- y.doConfigSetup(errorlevel=0,debuglevel=0)
- except:
- # but of course, yum on even moderately old
- # redhat/centosen doesn't know how to do logging properly
- # so we duck punch our way to victory
- def __log(a,b): pass
- y.doConfigSetup()
- y.log = __log
- y.errorlog = __log
-
- # Yum assumes it can update the cache directory. Disable this for non root
- # users.
- y.conf.cache = os.geteuid() != 0
-
- # Override any setting in yum.conf - we only care about the newest
- y.conf.showdupesfromrepos = False
-
- # Spin up to lock_timeout.
- countdown = lock_timeout
+ yb.preconf.errorlevel=0
+ yb.preconf.debuglevel=0
+
+ # initialize the config
+ yb.conf
+ except yum.Errors.ConfigError, e:
+ # supresses an ignored exception at exit
+ yb.preconf = None
+ print >> sys.stderr, "yum-dump Config Error: %s" % e
+ return 1
+ except ValueError, e:
+ yb.preconf = None
+ print >> sys.stderr, "yum-dump Options Error: %s" % e
+ return 1
+ elif YUM_VER == 2:
+ yb.doConfigSetup()
+
+ def __log(a,b): pass
+
+ yb.log = __log
+ yb.errorlog = __log
+
+ # Give Chef every possible package version, it can decide what to do with them
+ if YUM_VER == 3:
+ yb.conf.showdupesfromrepos = True
+ elif YUM_VER == 2:
+ yb.conf.setConfigOption('showdupesfromrepos', True)
+
+ # Optionally run only on cached repositories, but non root must use the cache
+ if os.geteuid() != 0:
+ if YUM_VER == 3:
+ yb.conf.cache = True
+ elif YUM_VER == 2:
+ yb.conf.setConfigOption('cache', True)
+ else:
+ if YUM_VER == 3:
+ yb.conf.cache = options.cache
+ elif YUM_VER == 2:
+ yb.conf.setConfigOption('cache', options.cache)
+
+ return 0
+
+def dump_packages(yb, list, output_provides):
+ packages = {}
+
+ if YUM_VER == 2:
+ yb.doTsSetup()
+ yb.doRepoSetup()
+ yb.doSackSetup()
+
+ db = yb.doPackageLists(list)
+
+ for pkg in db.installed:
+ pkg.type = 'i'
+ # __str__ contains epoch, name etc
+ packages[str(pkg)] = pkg
+
+ for pkg in db.available:
+ pkg.type = 'a'
+ packages[str(pkg)] = pkg
+
+ if YUM_VER == 2:
+ # ugh - can't get the availability state of our installed rpms, lets assume
+ # they are available to install
+ for pkg in db.installed:
+ pkg.type = 'r'
+ packages[str(pkg)] = pkg
+ else:
+ # These are both installed and available
+ for pkg in db.reinstall_available:
+ pkg.type = 'r'
+ packages[str(pkg)] = pkg
+
+ unique_packages = packages.values()
+
+ unique_packages.sort(lambda x, y: cmp(x.name, y.name))
+
+ for pkg in unique_packages:
+ if output_provides == "all" or \
+ (output_provides == "installed" and (pkg.type == "i" or pkg.type == "r")):
+ provides = pkg.provides_print
+ else:
+ provides = "[]"
+
+ print '%s %s %s %s %s %s %s' % (
+ pkg.name,
+ pkg.epoch,
+ pkg.version,
+ pkg.release,
+ pkg.arch,
+ provides,
+ pkg.type )
+
+ return 0
+
+def yum_dump(options):
+ lock_obtained = False
+
+ yb = yum.YumBase()
+
+ status = setup(yb, options)
+ if status != 0:
+ return status
+
+ if options.output_options:
+ print "[option installonlypkgs] %s" % " ".join(yb.conf.installonlypkgs)
+
+ # Non root can't handle locking on rhel/centos 4
+ if os.geteuid() != 0:
+ return dump_packages(yb, options.package_list, options.output_provides)
+
+ # Wrap the collection and output of packages in yum's global lock to prevent
+ # any inconsistencies.
+ try:
+ # Spin up to LOCK_TIMEOUT
+ countdown = LOCK_TIMEOUT
while True:
try:
- y.doLock(PIDFILE)
+ yb.doLock(YUM_PID_FILE)
+ lock_obtained = True
except Errors.LockError, e:
time.sleep(1)
countdown -= 1
if countdown == 0:
- print >> sys.stderr, "Error! Couldn't obtain an exclusive yum lock in %d seconds. Giving up." % lock_timeout
- failure = True
- sys.exit(1)
+ print >> sys.stderr, "yum-dump Locking Error! Couldn't obtain an " \
+ "exclusive yum lock in %d seconds. Giving up." % LOCK_TIMEOUT
+ return 200
else:
break
-
- y.doTsSetup()
- y.doRpmDBSetup()
-
+
+ return dump_packages(yb, options.package_list, options.output_provides)
+
+ # Ensure we clear the lock and cleanup any resources
+ finally:
try:
- db = y.doPackageLists('all')
- except AttributeError:
- # some people claim that testing for yum.__version__ should be
- # enough to see if this is required, but I say they're liars.
- # the yum on 4.8 at least understands yum.__version__ but still
- # needs to get its repos and sacks set up manually.
- # Thus, we just try it, fail, and then try again. WCPGW?
- y.doRepoSetup()
- y.doSackSetup()
- db = y.doPackageLists('all')
-
- y.closeRpmDB()
-
- except Errors.YumBaseError, e:
- print >> sys.stderr, "Error! %s" % e
- failure = True
- sys.exit(1)
+ yb.closeRpmDB()
+ if lock_obtained == True:
+ yb.doUnlock(YUM_PID_FILE)
+ except Errors.LockError, e:
+ print >> sys.stderr, "yum-dump Unlock Error: %s" % e
+ return 200
+
+def main():
+ usage = "Usage: %prog [options]\n" + \
+ "Output a list of installed, available and re-installable packages via yum"
+ parser = OptionParser(usage=usage)
+ parser.add_option("-C", "--cache",
+ action="store_true", dest="cache", default=False,
+ help="run entirely from cache, don't update cache")
+ parser.add_option("-o", "--options",
+ action="store_true", dest="output_options", default=False,
+ help="output select yum options useful to Chef")
+ parser.add_option("-p", "--installed-provides",
+ action="store_const", const="installed", dest="output_provides", default="none",
+ help="output Provides for installed packages, big/wide output")
+ parser.add_option("-P", "--all-provides",
+ action="store_const", const="all", dest="output_provides", default="none",
+ help="output Provides for all package, slow, big/wide output")
+ parser.add_option("-i", "--installed",
+ action="store_const", const="installed", dest="package_list", default="all",
+ help="output only installed packages")
+ parser.add_option("-a", "--available",
+ action="store_const", const="available", dest="package_list", default="all",
+ help="output only available and re-installable packages")
+
+ (options, args) = parser.parse_args()
-# Ensure we clear the lock.
-finally:
try:
- y.doUnlock(PIDFILE)
- # Keep Unlock from raising a second exception as it does with a yum.conf
- # config error.
- except Errors.YumBaseError:
- if failure == False:
- print >> sys.stderr, "Error! %s" % e
+ return yum_dump(options)
+
+ except yum.Errors.RepoError, e:
+ print >> sys.stderr, "yum-dump Repository Error: %s" % e
+ return 1
+
+ except yum.Errors.YumBaseError, e:
+ print >> sys.stderr, "yum-dump General Error: %s" % e
+ return 1
+
+try:
+ status = main()
+# Suppress a nasty broken pipe error when output is piped to utilities like 'head'
+except IOError, e:
+ if e.errno == errno.EPIPE:
sys.exit(1)
-
-for pkg in db.installed:
- print '%s,installed,%s,%s,%s,%s' % ( pkg.name,
- pkg.epoch,
- pkg.version,
- pkg.release,
- pkg.arch )
-for pkg in db.available:
- print '%s,available,%s,%s,%s,%s' % ( pkg.name,
- pkg.epoch,
- pkg.version,
- pkg.release,
- pkg.arch )
-
-sys.exit(0)
+ else:
+ raise
+
+sys.exit(status)
diff --git a/chef/lib/chef/provider/package/yum.rb b/chef/lib/chef/provider/package/yum.rb
index f57243321f..31cd0159b7 100644
--- a/chef/lib/chef/provider/package/yum.rb
+++ b/chef/lib/chef/provider/package/yum.rb
@@ -26,148 +26,890 @@ class Chef
class Package
class Yum < Chef::Provider::Package
- class YumCache
- include Chef::Mixin::Command
- include Singleton
+ class RPMUtils
+ class << self
- def initialize
- load_data
+ # RPM::Version version_parse equivalent
+ def version_parse(evr)
+ return if evr.nil?
+
+ epoch = nil
+ # assume this is a version
+ version = evr
+ release = nil
+
+ lead = 0
+ tail = evr.size
+
+ if evr =~ %r{^([\d]+):}
+ epoch = $1.to_i
+ lead = $1.length + 1
+ elsif evr[0].ord == ":".ord
+ epoch = 0
+ lead = 1
+ end
+
+ if evr =~ %r{:?.*-(.*)$}
+ release = $1
+ tail = evr.length - release.length - lead - 1
+
+ if release.empty?
+ release = nil
+ end
+ end
+
+ version = evr[lead,tail]
+ if version.empty?
+ version = nil
+ end
+
+ [ epoch, version, release ]
+ end
+
+ # verify
+ def isalnum(x)
+ isalpha(x) or isdigit(x)
+ end
+
+ def isalpha(x)
+ v = x.ord
+ (v >= 65 and v <= 90) or (v >= 97 and v <= 122)
+ end
+
+ def isdigit(x)
+ v = x.ord
+ v >= 48 and v <= 57
+ end
+
+ # based on the reference spec in lib/rpmvercmp.c in rpm 4.9.0
+ def rpmvercmp(x, y)
+ # easy! :)
+ return 0 if x == y
+
+ if x.nil?
+ x = ""
+ end
+
+ if y.nil?
+ y = ""
+ end
+
+ # not so easy :(
+ #
+ # takes 2 strings like
+ #
+ # x = "1.20.b18.el5"
+ # y = "1.20.b17.el5"
+ #
+ # breaks into purely alpha and numeric segments and compares them using
+ # some rules
+ #
+ # * 10 > 1
+ # * 1 > a
+ # * z > a
+ # * Z > A
+ # * z > Z
+ # * leading zeros are ignored
+ # * separators (periods, commas) are ignored
+ # * "1.20.b18.el5.extrastuff" > "1.20.b18.el5"
+
+ x_pos = 0 # overall string element reference position
+ x_pos_max = x.length - 1 # number of elements in string, starting from 0
+ x_seg_pos = 0 # segment string element reference position
+ x_comp = nil # segment to compare
+
+ y_pos = 0
+ y_seg_pos = 0
+ y_pos_max = y.length - 1
+ y_comp = nil
+
+ while (x_pos <= x_pos_max and y_pos <= y_pos_max)
+ # first we skip over anything non alphanumeric
+ while (x_pos <= x_pos_max) and (isalnum(x[x_pos]) == false)
+ x_pos += 1 # +1 over pos_max if end of string
+ end
+ while (y_pos <= y_pos_max) and (isalnum(y[y_pos]) == false)
+ y_pos += 1
+ end
+
+ # if we hit the end of either we are done matching segments
+ if (x_pos == x_pos_max + 1) or (y_pos == y_pos_max + 1)
+ break
+ end
+
+ # we are now at the start of a alpha or numeric segment
+ x_seg_pos = x_pos
+ y_seg_pos = y_pos
+
+ # grab segment so we can compare them
+ if isdigit(x[x_seg_pos].ord)
+ x_seg_is_num = true
+
+ # already know it's a digit
+ x_seg_pos += 1
+
+ # gather up our digits
+ while (x_seg_pos <= x_pos_max) and isdigit(x[x_seg_pos])
+ x_seg_pos += 1
+ end
+ # copy the segment but not the unmatched character that x_seg_pos will
+ # refer to
+ x_comp = x[x_pos,x_seg_pos - x_pos]
+
+ while (y_seg_pos <= y_pos_max) and isdigit(y[y_seg_pos])
+ y_seg_pos += 1
+ end
+ y_comp = y[y_pos,y_seg_pos - y_pos]
+ else
+ # we are comparing strings
+ x_seg_is_num = false
+
+ while (x_seg_pos <= x_pos_max) and isalpha(x[x_seg_pos])
+ x_seg_pos += 1
+ end
+ x_comp = x[x_pos,x_seg_pos - x_pos]
+
+ while (y_seg_pos <= y_pos_max) and isalpha(y[y_seg_pos])
+ y_seg_pos += 1
+ end
+ y_comp = y[y_pos,y_seg_pos - y_pos]
+ end
+
+ # if y_seg_pos didn't advance in the above loop it means the segments are
+ # different types
+ if y_pos == y_seg_pos
+ # numbers always win over letters
+ return x_seg_is_num ? 1 : -1
+ end
+
+ # move the ball forward before we mess with the segments
+ x_pos += x_comp.length # +1 over pos_max if end of string
+ y_pos += y_comp.length
+
+ # we are comparing numbers - simply convert them
+ if x_seg_is_num
+ x_comp = x_comp.to_i
+ y_comp = y_comp.to_i
+ end
+
+ # compares ints or strings
+ # don't return if equal - try the next segment
+ if x_comp > y_comp
+ return 1
+ elsif x_comp < y_comp
+ return -1
+ end
+
+ # if we've reached here than the segments are the same - try again
+ end
+
+ # we must have reached the end of one or both of the strings and they
+ # matched up until this point
+
+ # segments matched completely but the segment separators were different -
+ # rpm reference code treats these as equal.
+ if (x_pos == x_pos_max + 1) and (y_pos == y_pos_max + 1)
+ return 0
+ end
+
+ # the most unprocessed characters left wins
+ if (x_pos_max - x_pos) > (y_pos_max - y_pos)
+ return 1
+ else
+ return -1
+ end
+ end
+
+ end # self
+ end # RPMUtils
+
+ class RPMVersion
+ include Comparable
+
+ def initialize(*args)
+ if args.size == 1
+ @e, @v, @r = RPMUtils.version_parse(args[0])
+ elsif args.size == 3
+ @e = args[0].to_i
+ @v = args[1]
+ @r = args[2]
+ else
+ raise ArgumentError, "Expecting either 'epoch-version-release' or 'epoch, " +
+ "version, release'"
+ end
+ end
+ attr_reader :e, :v, :r
+ alias :epoch :e
+ alias :version :v
+ alias :release :r
+
+ def self.parse(*args)
+ self.new(*args)
+ end
+
+ def <=>(y)
+ compare_versions(y)
+ end
+
+ def compare(y)
+ compare_versions(y, false)
+ end
+
+ def partial_compare(y)
+ compare_versions(y, true)
+ end
+
+ # RPM::Version rpm_version_to_s equivalent
+ def to_s
+ if @r.nil?
+ @v
+ else
+ "#{@v}-#{@r}"
+ end
+ end
+
+ def evr
+ "#{@e}:#{@v}-#{@r}"
end
+
+ private
- def stale?
- interval = Chef::Config[:interval].to_f
+ # Rough RPM::Version rpm_version_cmp equivalent - except much slower :)
+ #
+ # partial lets epoch and version segment equality be good enough to return equal, eg:
+ #
+ # 2:1.2-1 == 2:1.2
+ # 2:1.2-1 == 2:
+ #
+ def compare_versions(y, partial=false)
+ x = self
- # run once mode
- if interval == 0
+ # compare epoch
+ if (x.e.nil? == false and x.e > 0) and y.e.nil?
+ return 1
+ elsif x.e.nil? and (y.e.nil? == false and y.e > 0)
+ return -1
+ elsif x.e.nil? == false and y.e.nil? == false
+ if x.e < y.e
+ return -1
+ elsif x.e > y.e
+ return 1
+ end
+ end
+
+ # compare version
+ if partial and (x.v.nil? or y.v.nil?)
+ return 0
+ elsif x.v.nil? == false and y.v.nil?
+ return 1
+ elsif x.v.nil? and y.v.nil? == false
+ return -1
+ elsif x.v.nil? == false and y.v.nil? == false
+ cmp = RPMUtils.rpmvercmp(x.v, y.v)
+ return cmp if cmp != 0
+ end
+
+ # compare release
+ if partial and (x.r.nil? or y.r.nil?)
+ return 0
+ elsif x.r.nil? == false and y.r.nil?
+ return 1
+ elsif x.r.nil? and y.r.nil? == false
+ return -1
+ elsif x.r.nil? == false and y.r.nil? == false
+ cmp = RPMUtils.rpmvercmp(x.r, y.r)
+ return cmp
+ end
+
+ return 0
+ end
+ end
+
+ class RPMPackage
+ include Comparable
+
+ def initialize(*args)
+ if args.size == 4
+ @n = args[0]
+ @version = RPMVersion.new(args[1])
+ @a = args[2]
+ @provides = args[3]
+ elsif args.size == 6
+ @n = args[0]
+ e = args[1].to_i
+ v = args[2]
+ r = args[3]
+ @version = RPMVersion.new(e,v,r)
+ @a = args[4]
+ @provides = args[5]
+ else
+ raise ArgumentError, "Expecting either 'name, epoch-version-release, arch, provides' " +
+ "or 'name, epoch, version, release, arch, provides'"
+ end
+
+ # We always have one, ourselves!
+ if @provides.empty?
+ @provides = [ RPMProvide.new(@n, @version.evr, :==) ]
+ end
+ end
+ attr_reader :n, :a, :version, :provides
+ alias :name :n
+ alias :arch :a
+
+ def <=>(y)
+ compare(y)
+ end
+
+ def compare(y)
+ x = self
+
+ # compare name
+ if x.n.nil? == false and y.n.nil?
+ return 1
+ elsif x.n.nil? and y.n.nil? == false
+ return -1
+ elsif x.n.nil? == false and y.n.nil? == false
+ if x.n < y.n
+ return -1
+ elsif x.n > y.n
+ return 1
+ end
+ end
+
+ # compare version
+ if x.version > y.version
+ return 1
+ elsif x.version < y.version
+ return -1
+ end
+
+ # compare arch
+ if x.a.nil? == false and y.a.nil?
+ return 1
+ elsif x.a.nil? and y.a.nil? == false
+ return -1
+ elsif x.a.nil? == false and y.a.nil? == false
+ if x.a < y.a
+ return -1
+ elsif x.a > y.a
+ return 1
+ end
+ end
+
+ return 0
+ end
+
+ def to_s
+ nevra
+ end
+
+ def nevra
+ "#{@n}-#{@version.evr}.#{@a}"
+ end
+ end
+
+ # Simple implementation from rpm and ruby-rpm reference code
+ class RPMDependency
+ def initialize(*args)
+ if args.size == 3
+ @name = args[0]
+ @version = RPMVersion.new(args[1])
+ # Our requirement to other dependencies
+ @flag = args[2] || :==
+ elsif args.size == 5
+ @name = args[0]
+ e = args[1].to_i
+ v = args[2]
+ r = args[3]
+ @version = RPMVersion.new(e,v,r)
+ @flag = args[4] || :==
+ else
+ raise ArgumentError, "Expecting either 'name, epoch-version-release, flag' or " +
+ "'name, epoch, version, release, flag'"
+ end
+ end
+ attr_reader :name, :version, :flag
+
+ # Parses 2 forms:
+ #
+ # "mtr >= 2:0.71-3.0"
+ # "mta"
+ def self.parse(string)
+ if string =~ %r{^(\S+)\s+(>|>=|=|==|<=|<)\s+(\S+)$}
+ name = $1
+ if $2 == "="
+ flag = :==
+ else
+ flag = :"#{$2}"
+ end
+ version = $3
+
+ return self.new(name, version, flag)
+ else
+ name = string
+ return self.new(name, nil, nil)
+ end
+ end
+
+ # Test if another RPMDependency satisfies our requirements
+ def satisfy?(y)
+ unless y.kind_of?(RPMDependency)
+ raise ArgumentError, "Expecting an RPMDependency object"
+ end
+
+ x = self
+
+ # Easy!
+ if x.name != y.name
return false
- elsif (Time.now - @updated_at) > interval
+ end
+
+ # Partial compare
+ #
+ # eg: x.version 2.3 == y.version 2.3-1
+ sense = x.version.partial_compare(y.version)
+
+ # Thanks to rpmdsCompare() rpmds.c
+ if sense < 0 and (x.flag == :> || x.flag == :>=) || (y.flag == :<= || y.flag == :<)
+ return true
+ elsif sense > 0 and (x.flag == :< || x.flag == :<=) || (y.flag == :>= || y.flag == :>)
+ return true
+ elsif sense == 0 and (
+ ((x.flag == :== or x.flag == :<= or x.flag == :>=) and (y.flag == :== or y.flag == :<= or y.flag == :>=)) or
+ (x.flag == :< and y.flag == :<) or
+ (x.flag == :> and y.flag == :>)
+ )
return true
end
- false
+ return false
end
-
- def refresh
- if @data.empty?
- reload
- elsif stale?
- reload
- end
+ end
+
+ class RPMProvide < RPMDependency; end
+ class RPMRequire < RPMDependency; end
+
+ class RPMDbPackage < RPMPackage
+ # <rpm parts>, installed, available
+ def initialize(*args)
+ # state
+ @available = args.pop
+ @installed = args.pop
+ super(*args)
end
+ attr_reader :available, :installed
+ end
- def load_data
- @data = Hash.new
- error = String.new
+ # Simple storage for RPMPackage objects - keeps them unique and sorted
+ class RPMDb
+ def initialize
+ # package name => [ RPMPackage, RPMPackage ] of different versions
+ @rpms = Hash.new
+ # provide name (aka feature) => [RPMPackage, RPMPackage] each providing this feature
+ @provides = Hash.new
+ # RPMPackages listed as available
+ @available = Set.new
+ # RPMPackages listed as installed
+ @installed = Set.new
+ end
- helper = ::File.join(::File.dirname(__FILE__), 'yum-dump.py')
- status = popen4("python #{helper}", :waitlast => true) do |pid, stdin, stdout, stderr|
- stdout.each do |line|
- line.chomp!
- name, type, epoch, version, release, arch = line.split(',')
- type_sym = type.to_sym
- if !@data.has_key?(name)
- @data[name] = Hash.new
- end
- if !@data[name].has_key?(type_sym)
- @data[name][type_sym] = Hash.new
+ def [](package_name)
+ self.lookup(package_name)
+ end
+
+ def lookup(package_name)
+ @rpms[package_name]
+ end
+
+ def lookup_provides(provide_name)
+ @provides[provide_name]
+ end
+
+ # Using the package name as a key keep a unique, descending list of packages.
+ # The available/installed state can be overwritten for existing packages.
+ def push(*args)
+ args.flatten.each do |new_rpm|
+ unless new_rpm.kind_of?(RPMDbPackage)
+ raise ArgumentError, "Expecting an RPMDbPackage object"
+ end
+
+ @rpms[new_rpm.n] ||= Array.new
+
+ # new_rpm may be a different object but it will be compared using RPMPackages <=>
+ idx = @rpms[new_rpm.n].index(new_rpm)
+ if idx
+ # grab the existing package if it's not
+ curr_rpm = @rpms[new_rpm.n][idx]
+ else
+ @rpms[new_rpm.n] << new_rpm
+ @rpms[new_rpm.n].sort!
+ @rpms[new_rpm.n].reverse!
+
+ new_rpm.provides.each do |provide|
+ @provides[provide.name] ||= Array.new
+ @provides[provide.name] << new_rpm
end
- @data[name][type_sym][arch] = { :epoch => epoch, :version => version,
- :release => release }
+
+ curr_rpm = new_rpm
end
-
- error = stderr.readlines
- end
- unless status.exitstatus == 0
- raise Chef::Exceptions::Package, "yum failed - #{status.inspect} - returns: #{error}"
+ # these are overwritten for existing packages
+ if new_rpm.available
+ @available << curr_rpm
+ end
+ if new_rpm.installed
+ @installed << curr_rpm
+ end
end
+ end
- @updated_at = Time.now
+ def <<(*args)
+ self.push(args)
end
- alias :reload :load_data
- def version(package_name, type, arch)
- if (x = @data[package_name])
- if (y = x[type])
- if arch
- if (z = y[arch])
- return "#{z[:version]}-#{z[:release]}"
+ def clear
+ @rpms.clear
+ @provides.clear
+ clear_available
+ clear_installed
+ end
+
+ def clear_available
+ @available.clear
+ end
+
+ def clear_installed
+ @installed.clear
+ end
+
+ def size
+ @rpms.size
+ end
+ alias :length :size
+
+ def available_size
+ @available.size
+ end
+
+ def installed_size
+ @installed.size
+ end
+
+ def available?(package)
+ @available.include?(package)
+ end
+
+ def installed?(package)
+ @installed.include?(package)
+ end
+
+ def whatprovides(rpmdep)
+ unless rpmdep.kind_of?(RPMDependency)
+ raise ArgumentError, "Expecting an RPMDependency object"
+ end
+
+ what = []
+
+ packages = lookup_provides(rpmdep.name)
+ if packages
+ packages.each do |pkg|
+ pkg.provides.each do |provide|
+ if provide.satisfy?(rpmdep)
+ what << pkg
end
- else
- # no arch specified - take the first match
- z = y.to_a[0][1]
- return "#{z[:version]}-#{z[:release]}"
end
end
end
- nil
+ return what
end
+ end
- def version_available?(package_name, desired_version, arch)
- if (package_data = @data[package_name])
- if (available_versions = package_data[:available])
- if arch
- # arch gets passed like ".x86_64"
- matching_versions = [ available_versions[arch.sub(/^./, '')]]
- else
- matching_versions = available_versions.values
- end
+ # Cache for our installed and available packages, pulled in from yum-dump.py
+ class YumCache
+ include Chef::Mixin::Command
+ include Singleton
+
+ def initialize
+ @rpmdb = RPMDb.new
+
+ # Next time @rpmdb is accessed:
+ # :all - Trigger a run of "yum-dump.py --options --installed-provides", updates
+ # yum's cache and parses options from /etc/yum.conf. Pulls in Provides
+ # dependency data for installed packages only - this data is slow to
+ # gather.
+ # :provides - Same as :all but pulls in Provides data for available packages as well.
+ # Used as a last resort when we can't find a Provides match.
+ # :installed - Trigger a run of "yum-dump.py --installed", only reads the local rpm
+ # db. Used between client runs for a quick refresh.
+ # :none - Do nothing, a call to one of the reload methods is required.
+ @next_refresh = :all
+
+ @allow_multi_install = []
+
+ # these are for subsequent runs if we are on an interval
+ Chef::Client.when_run_starts do
+ YumCache.instance.reload
+ end
+ end
+
+ # Cache management
+ #
- if matching_versions.nil?
- if arch.empty?
- arch_msg = ""
+ def refresh
+ case @next_refresh
+ when :none
+ return nil
+ when :installed
+ reset_installed
+ # fast
+ opts=" --installed"
+ when :all
+ reset
+ # medium
+ opts=" --options --installed-provides"
+ when :provides
+ reset
+ # slow!
+ opts=" --options --all-provides"
+ else
+ raise ArgumentError, "Unexpected value in next_refresh: #{@next_refresh}"
+ end
+
+ one_line = false
+ error = nil
+
+ helper = ::File.join(::File.dirname(__FILE__), 'yum-dump.py')
+
+ status = popen4("/usr/bin/python #{helper}#{opts}", :waitlast => true) do |pid, stdin, stdout, stderr|
+ stdout.each do |line|
+ one_line = true
+
+ line.chomp!
+
+ if line =~ %r{\[option (.*)\] (.*)}
+ if $1 == "installonlypkgs"
+ @allow_multi_install = $2.split
else
- arch_msg = "with arch #{arch.sub(/^./, '')} "
+ raise Chef::Exceptions::Package, "Strange, unknown option line '#{line}' from yum-dump.py"
end
+ next
+ end
- raise ArgumentError, "#{package_name}: Found no available versions #{arch_msg}to match"
+ if line =~ %r{^(\S+) ([0-9]+) (\S+) (\S+) (\S+) \[(.*)\] ([i,a,r])$}
+ name = $1
+ epoch = $2
+ version = $3
+ release = $4
+ arch = $5
+ provides = parse_provides($6)
+ type = $7
+ else
+ Chef::Log.warn("Problem parsing line '#{line}' from yum-dump.py! " +
+ "Please check your yum configuration.")
+ next
end
- # Expect [ { :version => "ver", :release => "rel" }, { :version => "ver", :release => "rel" }, { :version => "ver", :release => "rel" } ] ???
- matching_versions.each do |ver|
- Chef::Log.debug("#{@new_resource} trying to match #{desired_version} to version #{ver[:version]} and release #{ver[:release]}")
- if (desired_version == "#{ver[:version]}-#{ver[:release]}")
- return true
- end
+ case type
+ when "i"
+ # if yum-dump was called with --installed this may not be true, but it's okay
+ # since we don't touch the @available Set in reload_installed
+ available = false
+ installed = true
+ when "a"
+ available = true
+ installed = false
+ when "r"
+ available = true
+ installed = true
end
+
+ pkg = RPMDbPackage.new(name, epoch, version, release, arch, provides, installed, available)
+ @rpmdb << pkg
end
+
+ error = stderr.readlines
end
- nil
+ if status.exitstatus != 0
+ raise Chef::Exceptions::Package, "Yum failed - #{status.inspect} - returns: #{error}"
+ else
+ unless one_line
+ Chef::Log.warn("Odd, no output from yum-dump.py. Please check " +
+ "your yum configuration.")
+ end
+ end
+
+ # A reload method must be called before the cache is altered
+ @next_refresh = :none
end
- def installed_version(package_name, arch)
- version(package_name, :installed, arch)
+ def reload
+ @next_refresh = :all
end
- def candidate_version(package_name, arch)
- version(package_name, :available, arch)
+ def reload_installed
+ @next_refresh = :installed
end
- def flush
- @data.clear
+ def reload_provides
+ @next_refresh = :provides
+ end
+
+ def reset
+ @rpmdb.clear
+ end
+
+ def reset_installed
+ @rpmdb.clear_installed
+ end
+
+ # Querying the cache
+ #
+
+ def package_available?(package_name)
+ refresh
+ if @rpmdb.lookup(package_name)
+ true
+ else
+ false
+ end
+ end
+
+ # Returns a array of packages satisfying an RPMDependency
+ def packages_from_require(rpmdep)
+ refresh
+ @rpmdb.whatprovides(rpmdep)
+ end
+
+ def version_available?(package_name, desired_version, arch=nil)
+ version(package_name, arch, true, false) do |v|
+ return true if desired_version == v
+ end
+
+ return false
+ end
+
+ def available_version(package_name, arch=nil)
+ version(package_name, arch, true, false)
+ end
+ alias :candidate_version :available_version
+
+ def installed_version(package_name, arch=nil)
+ version(package_name, arch, false, true)
+ end
+
+ def allow_multi_install
+ refresh
+ @allow_multi_install
+ end
+
+ private
+
+ def version(package_name, arch=nil, is_available=false, is_installed=false)
+ refresh
+ packages = @rpmdb[package_name]
+ if packages
+ packages.each do |pkg|
+ if is_available
+ next unless @rpmdb.available?(pkg)
+ end
+ if is_installed
+ next unless @rpmdb.installed?(pkg)
+ end
+ if arch
+ next unless pkg.arch == arch
+ end
+
+ if block_given?
+ yield pkg.version.to_s
+ else
+ # first match is latest version
+ return pkg.version.to_s
+ end
+ end
+ end
+
+ if block_given?
+ return self
+ else
+ return nil
+ end
+ end
+
+ # Parse provides from yum-dump.py output
+ def parse_provides(string)
+ ret = []
+ # ['atk = 1.12.2-1.fc6', 'libatk-1.0.so.0']
+ string.split(", ").each do |seg|
+ # 'atk = 1.12.2-1.fc6'
+ if seg =~ %r{^'(.*)'$}
+ ret << RPMProvide.parse($1)
+ end
+ end
+
+ return ret
end
- end
+
+ end # YumCache
def initialize(new_resource, run_context)
super
+
@yum = YumCache.instance
end
+ # Extra attributes
+ #
+
def arch
if @new_resource.respond_to?("arch")
- @new_resource.arch
+ @new_resource.arch
else
nil
end
end
+ def flush_cache
+ if @new_resource.respond_to?("flush_cache")
+ @new_resource.flush_cache
+ else
+ { :before => false, :after => false }
+ end
+ end
+
+ def allow_downgrade
+ if @new_resource.respond_to?("allow_downgrade")
+ @new_resource.allow_downgrade
+ else
+ false
+ end
+ end
+
+ # Helpers
+ #
+
def yum_arch
arch ? ".#{arch}" : nil
end
+ # Standard Provider methods for Parent
+ #
+
def load_current_resource
+ if flush_cache[:before]
+ @yum.reload
+ end
+
+ unless @yum.package_available?(@new_resource.package_name)
+ parse_dependency
+ end
+
+ # Don't overwrite an existing arch
+ unless arch
+ parse_arch
+ end
+
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
@@ -188,20 +930,26 @@ class Chef
end
end
- Chef::Log.debug("#{@new_resource} checking yum info for #{@new_resource.package_name}#{yum_arch}")
+ if @new_resource.version
+ new_resource = "#{@new_resource.package_name}-#{@new_resource.version}#{yum_arch}"
+ else
+ new_resource = "#{@new_resource.package_name}#{yum_arch}"
+ end
- @yum.refresh
+ Chef::Log.debug("#{@new_resource} checking yum info for #{new_resource}")
installed_version = @yum.installed_version(@new_resource.package_name, arch)
+ @current_resource.version(installed_version)
+
@candidate_version = @yum.candidate_version(@new_resource.package_name, arch)
- @current_resource.version(installed_version)
- if candidate_version
- @candidate_version = candidate_version
- else
- @candidate_version = installed_version
+ if @candidate_version.nil?
+ raise Chef::Exceptions::Package, "Yum installed and available lists don't have a version of package "+
+ "#{@new_resource.package_name}"
end
- Chef::Log.debug("#{@new_resource} installed version: #{installed_version} candidate version: #{candidate_version}")
+
+ Chef::Log.debug("#{@new_resource} installed version: #{installed_version || "(none)"} candidate version: " +
+ "#{@candidate_version}")
@current_resource
end
@@ -209,52 +957,155 @@ class Chef
def install_package(name, version)
if @new_resource.source
run_command_with_systems_locale(
- :command => "yum -d0 -e0 -y #{@new_resource.options} localinstall #{@new_resource.source}"
+ :command => "yum -d0 -e0 -y#{expand_options(@new_resource.options)} localinstall #{@new_resource.source}"
)
else
# Work around yum not exiting with an error if a package doesn't exist for CHEF-2062
- if @yum.version_available?(name, version, yum_arch)
+ if @yum.version_available?(name, version, arch)
+ method = "install"
+
+ # More Yum fun:
+ #
+ # yum install of an old name+version will exit(1)
+ # yum install of an old name+version+arch will exit(0) for some reason
+ #
+ # Some packages can be installed multiple times like the kernel
+ unless @yum.allow_multi_install.include?(name)
+ if RPMVersion.parse(@current_resource.version) > RPMVersion.parse(version)
+ # Unless they want this...
+ if allow_downgrade
+ method = "downgrade"
+ else
+ # we bail like yum when the package is older
+ raise Chef::Exceptions::Package, "Installed package #{name}-#{@current_resource.version} is newer " +
+ "than candidate package #{name}-#{version}"
+ end
+ end
+ end
+
run_command_with_systems_locale(
- :command => "yum -d0 -e0 -y #{@new_resource.options} install #{name}-#{version}#{yum_arch}"
+ :command => "yum -d0 -e0 -y#{expand_options(@new_resource.options)} #{method} #{name}-#{version}#{yum_arch}"
)
else
- raise ArgumentError, "#{@new_resource.name}: Version #{version} of #{name} not found. Did you specify both version and release? (version-release, e.g. 1.84-10.fc6)"
+ raise Chef::Exceptions::Package, "Version #{version} of #{name} not found. Did you specify both version " +
+ "and release? (version-release, e.g. 1.84-10.fc6)"
end
end
- @yum.flush
+ if flush_cache[:after]
+ @yum.reload
+ else
+ @yum.reload_installed
+ end
end
- def upgrade_package(name, version)
- # If we're not given a version, running update is the correct
- # option. If we are, then running install_package is right.
- unless version
- run_command_with_systems_locale(
- :command => "yum -d0 -e0 -y #{@new_resource.options} update #{name}#{yum_arch}"
- )
- @yum.flush
+ # Keep upgrades from trying to install an older candidate version. Can happen when a new
+ # version is installed then removed from a repository, now the older available version
+ # shows up as a viable install candidate.
+ #
+ # Can be done in upgrade_package but an upgraded from->to log message slips out
+ #
+ # Hacky - better overall solution? Custom compare in Package provider?
+ def action_upgrade
+ # Ensure the candidate is newer
+ if RPMVersion.parse(candidate_version) > RPMVersion.parse(@current_resource.version)
+ super
+ # Candidate is older
else
- install_package(name, version)
+ Chef::Log.debug("#{@new_resource} is at the latest version - nothing to do")
end
end
+ def upgrade_package(name, version)
+ install_package(name, version)
+ end
+
def remove_package(name, version)
if version
run_command_with_systems_locale(
- :command => "yum -d0 -e0 -y #{@new_resource.options} remove #{name}-#{version}#{yum_arch}"
+ :command => "yum -d0 -e0 -y#{expand_options(@new_resource.options)} remove #{name}-#{version}#{yum_arch}"
)
else
run_command_with_systems_locale(
- :command => "yum -d0 -e0 -y #{@new_resource.options} remove #{name}#{yum_arch}"
+ :command => "yum -d0 -e0 -y#{expand_options(@new_resource.options)} remove #{name}#{yum_arch}"
)
end
-
- @yum.flush
+ if flush_cache[:after]
+ @yum.reload
+ else
+ @yum.reload_installed
+ end
end
def purge_package(name, version)
remove_package(name, version)
end
+ private
+
+ def parse_arch
+ # Allow for foo.x86_64 style package_name like yum uses in it's output
+ #
+ if @new_resource.package_name =~ %r{^(.*)\.(.*)$}
+ new_package_name = $1
+ new_arch = $2
+ # foo.i386 and foo.beta1 are both valid package names or expressions of an arch.
+ # Ensure we don't have an existing package matching package_name, then ensure we at
+ # least have a match for the new_package+new_arch before we overwrite. If neither
+ # then fall through to standard package handling.
+ if (@yum.installed_version(@new_resource.package_name).nil? and @yum.candidate_version(@new_resource.package_name).nil?) and
+ (@yum.installed_version(new_package_name, new_arch) or @yum.candidate_version(new_package_name, new_arch))
+ @new_resource.package_name(new_package_name)
+ @new_resource.arch(new_arch)
+ end
+ end
+ end
+
+ # If we don't have the package we could have been passed a 'whatprovides' feature
+ #
+ # eg: yum install "perl(Config)"
+ # yum install "mtr = 2:0.71-3.1"
+ # yum install "mtr > 2:0.71"
+ #
+ # We support resolving these out of the Provides data imported from yum-dump.py and
+ # matching them up with an actual package so the standard resource handling can apply.
+ #
+ # There is currently no support for filename matching.
+ def parse_dependency
+ # Transform the package_name into a requirement
+ yum_require = RPMRequire.parse(@new_resource.package_name)
+ # and gather all the packages that have a Provides feature satisfying the requirement.
+ # It could be multiple be we can only manage one
+ packages = @yum.packages_from_require(yum_require)
+
+ if packages.empty?
+ Chef::Log.debug("#{@new_resource} couldn't match #{@new_resource.package_name} in " +
+ "installed Provides, loading available Provides - this may take a moment")
+ @yum.reload_provides
+ packages = @yum.packages_from_require(yum_require)
+ end
+
+ unless packages.empty?
+ new_package_name = packages.first.name
+ Chef::Log.debug("#{@new_resource} no package found for #{@new_resource.package_name} " +
+ "but matched Provides for #{new_package_name}")
+
+ # Ensure it's not the same package under a different architecture
+ unique_names = []
+ packages.each do |pkg|
+ unique_names << "#{pkg.name}-#{pkg.version.evr}"
+ end
+ unique_names.uniq!
+
+ if unique_names.size > 1
+ Chef::Log.warn("#{@new_resource} matched multiple Provides for #{@new_resource.package_name} " +
+ "but we can only use the first match: #{new_package_name}. Please use a more " +
+ "specific version.")
+ end
+
+ @new_resource.package_name(new_package_name)
+ end
+ end
+
end
end
end
diff --git a/chef/lib/chef/provider/package/zypper.rb b/chef/lib/chef/provider/package/zypper.rb
index e309abf1e3..43727466e2 100644
--- a/chef/lib/chef/provider/package/zypper.rb
+++ b/chef/lib/chef/provider/package/zypper.rb
@@ -78,9 +78,18 @@ class Chef
@current_resource
end
+
+ #Gets the zypper Version from command output (Returns Floating Point number)
+ def zypper_version()
+ `zypper -V 2>&1`.scan(/\d+/).join(".").to_f
+ end
def install_package(name, version)
- if version
+ if zypper_version < 1.0
+ run_command(
+ :command => "zypper install -y #{name}"
+ )
+ elsif version
run_command(
:command => "zypper -n --no-gpg-checks install -l #{name}=#{version}"
)
@@ -92,7 +101,11 @@ class Chef
end
def upgrade_package(name, version)
- if version
+ if zypper_version < 1.0
+ run_command(
+ :command => "zypper install -y #{name}"
+ )
+ elsif version
run_command(
:command => "zypper -n --no-gpg-checks install -l #{name}=#{version}"
)
@@ -104,7 +117,11 @@ class Chef
end
def remove_package(name, version)
- if version
+ if zypper_version < 1.0
+ run_command(
+ :command => "zypper remove -y #{name}"
+ )
+ elsif version
run_command(
:command => "zypper -n --no-gpg-checks remove #{name}=#{version}"
)
diff --git a/chef/lib/chef/provider/remote_directory.rb b/chef/lib/chef/provider/remote_directory.rb
index 2fb25ca063..61d42cae08 100644
--- a/chef/lib/chef/provider/remote_directory.rb
+++ b/chef/lib/chef/provider/remote_directory.rb
@@ -45,7 +45,6 @@ class Chef
end
purge_unmanaged_files(files_to_purge)
Chef::Log.info("#{@new_resource} created")
- @new_resource.updated_by_last_action(true)
end
def action_create_if_missing
diff --git a/chef/lib/chef/provider/service/arch.rb b/chef/lib/chef/provider/service/arch.rb
index 58fcac057a..4f8e3e0b50 100644
--- a/chef/lib/chef/provider/service/arch.rb
+++ b/chef/lib/chef/provider/service/arch.rb
@@ -20,16 +20,16 @@ require 'chef/provider/service/init'
require 'chef/mixin/command'
class Chef::Provider::Service::Arch < Chef::Provider::Service::Init
-
+
def initialize(new_resource, run_context)
super
@init_command = "/etc/rc.d/#{@new_resource.service_name}"
end
-
+
def load_current_resource
-
+
raise Chef::Exceptions::Service unless ::File.exists?("/etc/rc.conf")
- raise Chef::Exceptions::Service unless ::File.read("/etc/rc.conf").match(/DAEMONS=\((.*)\)/)
+ raise Chef::Exceptions::Service unless ::File.read("/etc/rc.conf").match(/DAEMONS=\((.*)\)/m)
super
@@ -38,38 +38,46 @@ class Chef::Provider::Service::Arch < Chef::Provider::Service::Init
@current_resource
end
+ # Get list of all daemons from the file '/etc/rc.conf'.
+ # Mutiple lines and background form are supported. Example:
+ # DAEMONS=(\
+ # foobar \
+ # @example \
+ # !net \
+ # )
def daemons
entries = []
- if ::File.read("/etc/rc.conf").match(/DAEMONS=\((.*)\)/)
- entries += $1.split(" ") if $1.length > 0
+ if ::File.read("/etc/rc.conf").match(/DAEMONS=\((.*)\)/m)
+ entries += $1.gsub(/\\?[\r\n]/, ' ').gsub(/# *[^ ]+/,' ').split(' ') if $1.length > 0
end
-
+
yield(entries) if block_given?
-
+
entries
end
-
+
+ # FIXME: Multiple entries of DAEMONS will cause very bad results :)
def update_daemons(entries)
- content = ::File.read("/etc/rc.conf").gsub(/DAEMONS=\((.*)\)/, "DAEMONS=(#{entries.join(' ')})")
+ content = ::File.read("/etc/rc.conf").gsub(/DAEMONS=\((.*)\)/m, "DAEMONS=(#{entries.join(' ')})")
::File.open("/etc/rc.conf", "w") do |f|
f.write(content)
end
end
-
+
def enable_service()
new_daemons = []
entries = daemons
-
- if entries.include?(new_resource.service_name)
- # exists and already enabled
- new_daemons += entries
+
+ if entries.include?(new_resource.service_name) or entries.include?("@#{new_resource.service_name}")
+ # exists and already enabled (or already enabled as a background service)
+ # new_daemons += entries
else
if entries.include?("!#{new_resource.service_name}")
# exists but disabled
entries.each do |daemon|
- if daemon == "!#{new_resource.service_name}"
+ if daemon == "!#{new_resource.service_name}"
new_daemons << new_resource.service_name
- else
+ else
new_daemons << daemon
end
end
@@ -78,32 +86,31 @@ class Chef::Provider::Service::Arch < Chef::Provider::Service::Init
new_daemons += entries
new_daemons << new_resource.service_name
end
+ update_daemons(new_daemons)
end
-
- update_daemons(new_daemons)
end
-
+
def disable_service()
new_daemons = []
entries = daemons
-
+
if entries.include?("!#{new_resource.service_name}")
# exists and disabled
- new_daemons += entries
+ # new_daemons += entries
else
- if entries.include?(new_resource.service_name)
- # exists but enabled
+ if entries.include?(new_resource.service_name) or entries.include?("@#{new_resource.service_name}")
+ # exists but enabled (or enabled as a back-ground service)
+ # FIXME: Does arch support !@foobar ?
entries.each do |daemon|
- if daemon == new_resource.service_name
+ if [new_resource.service_name, "@#{new_resource.service_name}"].include?(daemon)
new_daemons << "!#{new_resource.service_name}"
- else
+ else
new_daemons << daemon
end
end
end
+ update_daemons(new_daemons)
end
-
- update_daemons(new_daemons)
end
end
diff --git a/chef/lib/chef/provider/service/systemd.rb b/chef/lib/chef/provider/service/systemd.rb
new file mode 100644
index 0000000000..749cb3bfc6
--- /dev/null
+++ b/chef/lib/chef/provider/service/systemd.rb
@@ -0,0 +1,102 @@
+#
+# Author:: Stephen Haynes (<sh@nomitor.com>)
+# Copyright:: Copyright (c) 2011 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/service'
+require 'chef/provider/service/simple'
+require 'chef/mixin/command'
+
+class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
+ def load_current_resource
+ @current_resource = Chef::Resource::Service.new(@new_resource.name)
+ @current_resource.service_name(@new_resource.service_name)
+
+ if @new_resource.status_command
+ Chef::Log.debug("#{@new_resource} you have specified a status command, running..")
+
+ begin
+ if run_command_with_systems_locale(:command => @new_resource.status_command) == 0
+ @current_resource.running(true)
+ end
+ rescue Chef::Exceptions::Exec
+ @current_resource.running(false)
+ nil
+ end
+ else
+ @current_resource.running(is_active?)
+ end
+
+ @current_resource.enabled(is_enabled?)
+ @current_resource
+ end
+
+ def start_service
+ if @current_resource.running
+ Chef::Log.debug("#{@new_resource} already running, not starting")
+ else
+ if @new_resource.start_command
+ super
+ else
+ run_command_with_systems_locale(:command => "/bin/systemctl start #{@new_resource.service_name}")
+ end
+ end
+ end
+
+ def stop_service
+ unless @current_resource.running
+ Chef::Log.debug("#{@new_resource} not running, not stopping")
+ else
+ if @new_resource.stop_command
+ super
+ else
+ run_command_with_systems_locale(:command => "/bin/systemctl stop #{@new_resource.service_name}")
+ end
+ end
+ end
+
+ def restart_service
+ if @new_resource.restart_command
+ super
+ else
+ run_command_with_systems_locale(:command => "/bin/systemctl restart #{@new_resource.service_name}")
+ end
+ end
+
+ def reload_service
+ if @new_resource.reload_command
+ super
+ else
+ run_command_with_systems_locale(:command => "/bin/systemctl reload #{@new_resource.service_name}")
+ end
+ end
+
+ def enable_service
+ run_command_with_systems_locale(:command => "/bin/systemctl enable #{@new_resource.service_name}")
+ end
+
+ def disable_service
+ run_command_with_systems_locale(:command => "/bin/systemctl disable #{@new_resource.service_name}")
+ end
+
+ def is_active?
+ run_command_with_systems_locale({:command => "/bin/systemctl is-active #{@new_resource.service_name}", :ignore_failure => true}) == 0
+ end
+
+ def is_enabled?
+ run_command_with_systems_locale({:command => "/bin/systemctl is-enabled #{@new_resource.service_name}", :ignore_failure => true}) == 0
+ end
+end
diff --git a/chef/lib/chef/provider/service/upstart.rb b/chef/lib/chef/provider/service/upstart.rb
index 447e72633e..9969c99521 100644
--- a/chef/lib/chef/provider/service/upstart.rb
+++ b/chef/lib/chef/provider/service/upstart.rb
@@ -140,8 +140,14 @@ class Chef
def restart_service
if @new_resource.restart_command
super
- else
- run_command_with_systems_locale(:command => "/sbin/restart #{@new_resource.service_name}")
+ # Upstart always provides restart functionality so we don't need to mimic it with stop/sleep/start.
+ # Older versions of upstart would fail on restart if the service was currently stopped, check for that. LP:430883
+ else @new_resource.supports[:restart]
+ if @current_resource.running
+ run_command_with_systems_locale(:command => "/sbin/restart #{@new_resource.service_name}")
+ else
+ start_service
+ end
end
end
diff --git a/chef/lib/chef/providers.rb b/chef/lib/chef/providers.rb
index 9bc16470ef..d7abf0f21d 100644
--- a/chef/lib/chef/providers.rb
+++ b/chef/lib/chef/providers.rb
@@ -69,6 +69,7 @@ require 'chef/provider/service/init'
require 'chef/provider/service/insserv'
require 'chef/provider/service/redhat'
require 'chef/provider/service/simple'
+require 'chef/provider/service/systemd'
require 'chef/provider/service/upstart'
require 'chef/provider/service/windows'
require 'chef/provider/service/solaris'
@@ -83,6 +84,7 @@ require 'chef/provider/group/dscl'
require 'chef/provider/group/gpasswd'
require 'chef/provider/group/groupadd'
require 'chef/provider/group/pw'
+require 'chef/provider/group/suse'
require 'chef/provider/group/usermod'
require 'chef/provider/group/windows'
diff --git a/chef/lib/chef/resource.rb b/chef/lib/chef/resource.rb
index b92f52c8e9..7ba59ca265 100644
--- a/chef/lib/chef/resource.rb
+++ b/chef/lib/chef/resource.rb
@@ -87,6 +87,8 @@ F
attr_accessor :recipe_name
attr_accessor :enclosing_provider
attr_accessor :source_line
+ attr_accessor :retries
+ attr_accessor :retry_delay
attr_reader :updated
@@ -112,6 +114,8 @@ F
@updated_by_last_action = false
@supports = {}
@ignore_failure = false
+ @retries = 0
+ @retry_delay = 2
@not_if = nil
@not_if_args = {}
@only_if = nil
@@ -224,6 +228,22 @@ F
)
end
+ def retries(arg=nil)
+ set_or_return(
+ :retries,
+ arg,
+ :kind_of => Integer
+ )
+ end
+
+ def retry_delay(arg=nil)
+ set_or_return(
+ :retry_delay,
+ arg,
+ :kind_of => Integer
+ )
+ end
+
def epic_fail(arg=nil)
ignore_failure(arg)
end
@@ -468,8 +488,8 @@ F
set_or_return(attr_name.to_sym, arg, validation_opts)
end
end
-
- def build_from_file(cookbook_name, filename)
+
+ def build_from_file(cookbook_name, filename, run_context)
rname = filename_to_qualified_string(cookbook_name, filename)
# Add log entry if we override an existing light-weight resource.
@@ -490,6 +510,12 @@ F
class << cls
include Chef::Mixin::FromFile
+
+ attr_accessor :run_context
+
+ def node
+ self.run_context.node
+ end
def actions_to_create
@actions_to_create
@@ -500,6 +526,9 @@ F
end
end
+ # set the run context in the class instance variable
+ cls.run_context = run_context
+
# load resource definition from file
cls.class_from_file(filename)
diff --git a/chef/lib/chef/resource/git.rb b/chef/lib/chef/resource/git.rb
index a5b97f195c..774bb24f24 100644
--- a/chef/lib/chef/resource/git.rb
+++ b/chef/lib/chef/resource/git.rb
@@ -26,6 +26,15 @@ class Chef
super
@resource_name = :git
@provider = Chef::Provider::Git
+ @additional_remotes = Hash[]
+ end
+
+ def additional_remotes(arg=nil)
+ set_or_return(
+ :additional_remotes,
+ arg,
+ :kind_of => Hash
+ )
end
alias :branch :revision
diff --git a/chef/lib/chef/resource/yum_package.rb b/chef/lib/chef/resource/yum_package.rb
index 4a415fb48f..bcb1f65667 100644
--- a/chef/lib/chef/resource/yum_package.rb
+++ b/chef/lib/chef/resource/yum_package.rb
@@ -27,6 +27,8 @@ class Chef
super
@resource_name = :yum_package
@provider = Chef::Provider::Package::Yum
+ @flush_cache = { :before => false, :after => false }
+ @allow_downgrade = false
end
# Install a specific arch
@@ -38,6 +40,24 @@ class Chef
)
end
+ def flush_cache(args={})
+ if args.is_a? Array
+ args.each { |arg| @flush_cache[arg] = true }
+ elsif args.any?
+ @flush_cache = args
+ else
+ @flush_cache
+ end
+ end
+
+ def allow_downgrade(arg=nil)
+ set_or_return(
+ :allow_downgrade,
+ arg,
+ :kind_of => [ TrueClass, FalseClass ]
+ )
+ end
+
end
end
end
diff --git a/chef/lib/chef/rest.rb b/chef/lib/chef/rest.rb
index 7a180f8edf..cfb0843201 100644
--- a/chef/lib/chef/rest.rb
+++ b/chef/lib/chef/rest.rb
@@ -195,7 +195,7 @@ class Chef
end
end
elsif res.kind_of?(Net::HTTPFound) or res.kind_of?(Net::HTTPMovedPermanently)
- follow_redirect {run_request(:GET, create_url(res['location']), {}, false, nil, raw)}
+ follow_redirect {run_request(method, create_url(res['location']), headers, false, nil, raw)}
elsif res.kind_of?(Net::HTTPNotModified)
false
else
diff --git a/chef/lib/chef/run_context.rb b/chef/lib/chef/run_context.rb
index 9919888ac2..6a173f57d4 100644
--- a/chef/lib/chef/run_context.rb
+++ b/chef/lib/chef/run_context.rb
@@ -79,21 +79,21 @@ class Chef
def load_libraries
foreach_cookbook_load_segment(:libraries) do |cookbook_name, filename|
Chef::Log.debug("Loading cookbook #{cookbook_name}'s library file: #{filename}")
- require filename
+ Kernel.load(filename)
end
end
def load_lwrp_providers
foreach_cookbook_load_segment(:providers) do |cookbook_name, filename|
Chef::Log.debug("Loading cookbook #{cookbook_name}'s providers from #{filename}")
- Chef::Provider.build_from_file(cookbook_name, filename)
+ Chef::Provider.build_from_file(cookbook_name, filename, self)
end
end
def load_lwrp_resources
foreach_cookbook_load_segment(:resources) do |cookbook_name, filename|
Chef::Log.debug("Loading cookbook #{cookbook_name}'s resources from #{filename}")
- Chef::Resource.build_from_file(cookbook_name, filename)
+ Chef::Resource.build_from_file(cookbook_name, filename, self)
end
end
diff --git a/chef/lib/chef/runner.rb b/chef/lib/chef/runner.rb
index 9521f94c79..9ff775e1b2 100644
--- a/chef/lib/chef/runner.rb
+++ b/chef/lib/chef/runner.rb
@@ -74,8 +74,21 @@ class Chef
# Execute each resource.
run_context.resource_collection.execute_each_resource do |resource|
- # Execute each of this resource's actions.
- Array(resource.action).each {|action| run_action(resource, action) }
+ begin
+ Chef::Log.debug("Processing #{resource} on #{run_context.node.name}")
+
+ # Execute each of this resource's actions.
+ Array(resource.action).each {|action| run_action(resource, action)}
+ rescue => e
+ Chef::Log.error("#{resource} (#{resource.source_line}) had an error:\n#{e}\n#{e.backtrace.join("\n")}")
+ if resource.retries > 0
+ resource.retries -= 1
+ Chef::Log.info("Retrying execution of #{resource}, #{resource.retries} attempt(s) left")
+ sleep resource.retry_delay
+ retry
+ end
+ raise e unless resource.ignore_failure
+ end
end
# Run all our :delayed actions
diff --git a/chef/lib/chef/shell_out.rb b/chef/lib/chef/shell_out.rb
index afc78cceae..7cb7bfc371 100644
--- a/chef/lib/chef/shell_out.rb
+++ b/chef/lib/chef/shell_out.rb
@@ -190,7 +190,7 @@ class Chef
# Chef::Exceptions::ShellCommandFailed::: via +invalid!+
def error!
unless Array(valid_exit_codes).include?(exitstatus)
- invalid!("Expected process to exit 0, but it exited with #{exitstatus}")
+ invalid!("Expected process to exit with #{valid_exit_codes.inspect}, but received '#{exitstatus}'")
end
end
diff --git a/chef/lib/chef/shell_out/windows.rb b/chef/lib/chef/shell_out/windows.rb
index 9376d72c4a..e923fe4074 100644
--- a/chef/lib/chef/shell_out/windows.rb
+++ b/chef/lib/chef/shell_out/windows.rb
@@ -58,11 +58,11 @@ class Chef
out_reader.join
err_reader.join
-
- @status = $?
end
end
+ @status = $?
+
self
rescue Timeout::Error
diff --git a/chef/lib/chef/tasks/chef_repo.rake b/chef/lib/chef/tasks/chef_repo.rake
index afda706738..7b84cfe2ba 100644
--- a/chef/lib/chef/tasks/chef_repo.rake
+++ b/chef/lib/chef/tasks/chef_repo.rake
@@ -77,7 +77,7 @@ task :default => [ :test_cookbooks ]
desc "Create a new cookbook (with COOKBOOK=name, optional CB_PREFIX=site-)"
task :new_cookbook do
- puts "***WARN: rake new_cookbook is deprecated. Please use 'knife cookbook new COOKBOOK' command.***"
+ puts "***WARN: rake new_cookbook is deprecated. Please use 'knife cookbook create COOKBOOK' command.***"
create_cookbook(File.join(TOPDIR, "#{ENV["CB_PREFIX"]}cookbooks"))
create_readme(File.join(TOPDIR, "#{ENV["CB_PREFIX"]}cookbooks"))
create_metadata(File.join(TOPDIR, "#{ENV["CB_PREFIX"]}cookbooks"))
diff --git a/chef/spec/data/lwrp/resources_with_default_attributes/nodeattr.rb b/chef/spec/data/lwrp/resources_with_default_attributes/nodeattr.rb
new file mode 100644
index 0000000000..dece7c4eab
--- /dev/null
+++ b/chef/spec/data/lwrp/resources_with_default_attributes/nodeattr.rb
@@ -0,0 +1 @@
+attribute :penguin, :kind_of => String, :default => node[:penguin_name]
diff --git a/chef/spec/unit/encrypted_data_bag_item_spec.rb b/chef/spec/unit/encrypted_data_bag_item_spec.rb
index 7ad7c0c17e..975c1318ef 100644
--- a/chef/spec/unit/encrypted_data_bag_item_spec.rb
+++ b/chef/spec/unit/encrypted_data_bag_item_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Seth Falcon (<seth@opscode.com>)
-# Copyright:: Copyright 2010 Opscode, Inc.
+# Copyright:: Copyright 2010-2011 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -75,6 +75,10 @@ describe Chef::EncryptedDataBagItem do
it "decrypts everyting via to_hash" do
@eh.to_hash.should == @plain_data
end
+
+ it "handles missing keys gracefully" do
+ @eh["no-such-key"].should be_nil
+ end
end
describe "loading" do
@@ -84,4 +88,35 @@ describe Chef::EncryptedDataBagItem do
edbi["greeting"].should == @plain_data["greeting"]
end
end
+
+ describe "load_secret" do
+ it "should read from the default path" do
+ default_path = "/etc/chef/encrypted_data_bag_secret"
+ ::File.stub(:exists?).with(default_path).and_return(true)
+ IO.stub(:read).with(default_path).and_return("opensesame")
+ Chef::EncryptedDataBagItem.load_secret().should == "opensesame"
+ end
+
+ it "should read from Chef::Config[:encrypted_data_bag_secret]" do
+ path = "/var/mysecret"
+ Chef::Config[:encrypted_data_bag_secret] = path
+ ::File.stub(:exists?).with(path).and_return(true)
+ IO.stub(:read).with(path).and_return("opensesame")
+ Chef::EncryptedDataBagItem.load_secret().should == "opensesame"
+ end
+
+ it "should read from a specified path" do
+ path = "/var/mysecret"
+ ::File.stub(:exists?).with(path).and_return(true)
+ IO.stub(:read).with(path).and_return("opensesame")
+ Chef::EncryptedDataBagItem.load_secret(path).should == "opensesame"
+ end
+
+ it "should read from a URL" do
+ path = "http://www.opscode.com/"
+ fake_file = StringIO.new("opensesame")
+ Kernel.stub(:open).with(path).and_return(fake_file)
+ Chef::EncryptedDataBagItem.load_secret(path).should == "opensesame"
+ end
+ end
end
diff --git a/chef/spec/unit/environment_spec.rb b/chef/spec/unit/environment_spec.rb
index a799ec2585..2bb80de7c9 100644
--- a/chef/spec/unit/environment_spec.rb
+++ b/chef/spec/unit/environment_spec.rb
@@ -1,7 +1,8 @@
#
# Author:: Stephen Delano (<stephen@ospcode.com>)
# Author:: Seth Falcon (<seth@ospcode.com>)
-# Copyright:: Copyright 2010 Opscode, Inc.
+# Author:: John Keiser (<jkeiser@ospcode.com>)
+# Copyright:: Copyright 2010-2011 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -247,21 +248,25 @@ describe Chef::Environment do
@all_cookbooks << begin
cv = Chef::CookbookVersion.new("apt")
cv.version = "1.0.0"
+ cv.recipe_filenames = ["default.rb", "only-in-1-0.rb"]
cv
end
@all_cookbooks << begin
cv = Chef::CookbookVersion.new("apt")
cv.version = "1.1.0"
+ cv.recipe_filenames = ["default.rb", "only-in-1-1.rb"]
cv
end
@all_cookbooks << begin
cv = Chef::CookbookVersion.new("apache2")
cv.version = "2.0.0"
+ cv.recipe_filenames = ["default.rb", "mod_ssl.rb"]
cv
end
@all_cookbooks << begin
cv = Chef::CookbookVersion.new("god")
cv.version = "4.2.0"
+ cv.recipe_filenames = ["default.rb"]
cv
end
Chef::CookbookVersion.stub!(:cdb_list).and_return @all_cookbooks
@@ -272,9 +277,33 @@ describe Chef::Environment do
Chef::Environment.cdb_load_filtered_cookbook_versions("prod")
end
+ it "should handle cookbooks with no available version" do
+ @environment.cookbook_versions({
+ "apt" => "> 999.0.0",
+ "apache2" => "= 2.0.0"
+ })
+ Chef::Environment.should_receive(:cdb_load).with("prod", nil)
+ recipes = Chef::Environment.cdb_load_filtered_recipe_list("prod")
+ # order doesn't matter
+ recipes.should =~ ["god", "apache2", "apache2::mod_ssl"]
+ end
+
+
it "should load all the cookbook versions" do
Chef::CookbookVersion.should_receive(:cdb_list)
Chef::Environment.cdb_load_filtered_cookbook_versions("prod")
+ recipes = Chef::Environment.cdb_load_filtered_recipe_list("prod")
+ recipes.should =~ ["apache2", "apache2::mod_ssl", "apt",
+ "apt::only-in-1-0", "god"]
+ end
+
+ it "should load all the cookbook versions with no policy" do
+ @environment.cookbook_versions({})
+ Chef::CookbookVersion.should_receive(:cdb_list)
+ Chef::Environment.cdb_load_filtered_cookbook_versions("prod")
+ recipes = Chef::Environment.cdb_load_filtered_recipe_list("prod")
+ recipes.should =~ ["apache2", "apache2::mod_ssl", "apt",
+ "apt::only-in-1-1", "god"]
end
it "should restrict the cookbook versions, as specified in the environment" do
diff --git a/chef/spec/unit/knife/client_bulk_delete_spec.rb b/chef/spec/unit/knife/client_bulk_delete_spec.rb
index 10cb73ab9e..9541cadd00 100644
--- a/chef/spec/unit/knife/client_bulk_delete_spec.rb
+++ b/chef/spec/unit/knife/client_bulk_delete_spec.rb
@@ -24,12 +24,10 @@ describe Chef::Knife::ClientBulkDelete do
Chef::Config[:node_name] = "webmonkey.example.com"
@knife = Chef::Knife::ClientBulkDelete.new
- @knife.config = {
- :print_after => nil
- }
@knife.name_args = ["."]
- @knife.stub!(:output).and_return(:true)
- @knife.stub!(:confirm).and_return(true)
+ @stdout = StringIO.new
+ @knife.ui.stub!(:stdout).and_return(@stdout)
+ @knife.ui.stub!(:confirm).and_return(true)
@clients = Hash.new
%w{tim dan stephen}.each do |client_name|
client = Chef::ApiClient.new()
@@ -48,12 +46,12 @@ describe Chef::Knife::ClientBulkDelete do
end
it "should print the clients you are about to delete" do
- @knife.should_receive(:output).with(@knife.format_list_for_display(@clients))
@knife.run
+ @stdout.string.should match(/#{@knife.ui.list(@clients.keys.sort, :columns_down)}/)
end
it "should confirm you really want to delete them" do
- @knife.should_receive(:confirm)
+ @knife.ui.should_receive(:confirm)
@knife.run
end
@@ -76,15 +74,5 @@ describe Chef::Knife::ClientBulkDelete do
@knife.name_args = []
lambda { @knife.run }.should raise_error(SystemExit)
end
-
- describe "with -p or --print_after" do
- it "should pretty_print the client, formatted for display" do
- @knife.config[:print_after] = true
- @clients.each_value do |n|
- @knife.should_receive(:output).with(@knife.format_for_display(n))
- end
- @knife.run
- end
- end
end
end
diff --git a/chef/spec/unit/knife/core/cookbook_scm_repo_spec.rb b/chef/spec/unit/knife/core/cookbook_scm_repo_spec.rb
index 013f31baa4..d2b2f8f9f2 100644
--- a/chef/spec/unit/knife/core/cookbook_scm_repo_spec.rb
+++ b/chef/spec/unit/knife/core/cookbook_scm_repo_spec.rb
@@ -34,6 +34,7 @@ describe Chef::Knife::CookbookSCMRepo do
chef-vendor-ganglia
chef-vendor-graphite
chef-vendor-python
+ chef-vendor-absent-new
BRANCHES
end
@@ -116,6 +117,12 @@ DIRTY
@cookbook_repo.branch_exists?("chef-vendor-nginx").should be_false
end
+ it "determines if a the branch not exists correctly without substring search" do
+ @cookbook_repo.should_receive(:shell_out!).twice.with('git branch --no-color', :cwd => @repo_path).and_return(@branch_list)
+ @cookbook_repo.should_not be_branch_exists("chef-vendor-absent")
+ @cookbook_repo.should be_branch_exists("chef-vendor-absent-new")
+ end
+
describe "when the pristine copy branch does not exist" do
it "prepares for import by creating the pristine copy branch" do
@cookbook_repo.should_receive(:shell_out!).with('git branch --no-color', :cwd => @repo_path).and_return(@branch_list)
diff --git a/chef/spec/unit/knife/knife_help.rb b/chef/spec/unit/knife/knife_help.rb
new file mode 100644
index 0000000000..6ba585c86b
--- /dev/null
+++ b/chef/spec/unit/knife/knife_help.rb
@@ -0,0 +1,92 @@
+#
+# Author:: Bryan McLellan <btm@loftninjas.org>
+# Copyright:: Copyright (c) 2011 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::Knife::Help do
+ before(:each) do
+ # Perilously use the build in list even though it is dynamic so we don't get warnings about the constant
+ # HELP_TOPICS = [ "foo", "bar", "knife-kittens", "ceiling-cat", "shef" ]
+ @knife = Chef::Knife::Help.new
+ end
+
+ it "should return a list of help topics" do
+ @knife.help_topics.should include("knife-status")
+ end
+
+ it "should run man for you" do
+ @knife.name_args = [ "shef" ]
+ @knife.should_receive(:exec).with(/^man \/.*\/shef.1$/)
+ @knife.run
+ end
+
+ it "should suggest topics" do
+ @knife.name_args = [ "list" ]
+ @knife.ui.stub!(:msg)
+ @knife.ui.should_receive(:info).with("Available help topics are: ")
+ @knife.ui.should_receive(:msg).with(/knife/)
+ @knife.stub!(:exec)
+ @knife.should_receive(:exit).with(1)
+ @knife.run
+ end
+
+ describe "find_manpage_path" do
+ it "should find the man page in the gem" do
+ @knife.find_manpage_path("shef").should =~ /distro\/common\/man\/man1\/shef.1$/
+ end
+
+ it "should provide the man page name if not in the gem" do
+ @knife.find_manpage_path("foo").should == "foo"
+ end
+ end
+
+ describe "find_manpages_for_query" do
+ it "should error if it does not find a match" do
+ @knife.ui.stub!(:error)
+ @knife.ui.stub!(:info)
+ @knife.ui.stub!(:msg)
+ @knife.should_receive(:exit).with(1)
+ @knife.ui.should_receive(:error).with("No help found for 'chickens'")
+ @knife.ui.should_receive(:msg).with(/knife/)
+ @knife.find_manpages_for_query("chickens")
+ end
+ end
+
+ describe "print_help_topics" do
+ it "should print the known help topics" do
+ @knife.ui.stub!(:msg)
+ @knife.ui.stub!(:info)
+ @knife.ui.should_receive(:msg).with(/knife/)
+ @knife.print_help_topics
+ end
+
+ it "should shorten topics prefixed by knife-" do
+ @knife.ui.stub!(:msg)
+ @knife.ui.stub!(:info)
+ @knife.ui.should_receive(:msg).with(/node/)
+ @knife.print_help_topics
+ end
+
+ it "should not leave topics prefixed by knife-" do
+ @knife.ui.stub!(:msg)
+ @knife.ui.stub!(:info)
+ @knife.ui.should_not_receive(:msg).with(/knife-node/)
+ @knife.print_help_topics
+ end
+ end
+end
diff --git a/chef/spec/unit/lwrp_spec.rb b/chef/spec/unit/lwrp_spec.rb
index 05e1eb9c20..d18528a89e 100644
--- a/chef/spec/unit/lwrp_spec.rb
+++ b/chef/spec/unit/lwrp_spec.rb
@@ -19,26 +19,26 @@
require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
describe "override logging" do
-
+
it "should log if attempting to load resource of same name" do
Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources", "*"))].each do |file|
- Chef::Resource.build_from_file("lwrp", file)
+ Chef::Resource.build_from_file("lwrp", file, nil)
end
Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "resources", "*"))].each do |file|
Chef::Log.should_receive(:info).with(/overriding/)
- Chef::Resource.build_from_file("lwrp", file)
+ Chef::Resource.build_from_file("lwrp", file, nil)
end
end
it "should log if attempting to load provider of same name" do
Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "providers", "*"))].each do |file|
- Chef::Provider.build_from_file("lwrp", file)
+ Chef::Provider.build_from_file("lwrp", file, nil)
end
Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "providers", "*"))].each do |file|
Chef::Log.should_receive(:info).with(/overriding/)
- Chef::Provider.build_from_file("lwrp", file)
+ Chef::Provider.build_from_file("lwrp", file, nil)
end
end
@@ -54,18 +54,16 @@ describe "LWRP" do
$VERBOSE = @original_VERBOSE
end
- describe "Light-weight Chef::Resource" do
+ describe "Lightweight Chef::Resource" do
before do
-
Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources", "*"))].each do |file|
- Chef::Resource.build_from_file("lwrp", file)
+ Chef::Resource.build_from_file("lwrp", file, nil)
end
Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "resources", "*"))].each do |file|
- Chef::Resource.build_from_file("lwrp", file)
+ Chef::Resource.build_from_file("lwrp", file, nil)
end
-
end
it "should load the resource into a properly-named class" do
@@ -88,33 +86,50 @@ describe "LWRP" do
lambda { Chef::Resource::LwrpFoo.new("blah").monkey(42) }.should raise_error(ArgumentError)
end
+ it "should have access to the run context and node during class definition" do
+ node = Chef::Node.new(nil)
+ node[:penguin_name] = "jackass"
+ run_context = Chef::RunContext.new(node, Chef::CookbookCollection.new)
+
+ Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources_with_default_attributes", "*"))].each do |file|
+ Chef::Resource.build_from_file("lwrp", file, run_context)
+ end
+
+ cls = Chef::Resource.const_get("LwrpNodeattr")
+ cls.node.should be_kind_of(Chef::Node)
+ cls.run_context.should be_kind_of(Chef::RunContext)
+ cls.node[:penguin_name].should eql("jackass")
+ end
+
end
- describe "Light-weight Chef::Provider" do
+ describe "Lightweight Chef::Provider" do
+ before do
+ @node = Chef::Node.new
+ @node.platform(:ubuntu)
+ @node.platform_version('8.10')
+ @run_context = Chef::RunContext.new(@node, Chef::CookbookCollection.new({}))
+
+ @runner = Chef::Runner.new(@run_context)
+ end
+
before(:each) do
Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources", "*"))].each do |file|
- Chef::Resource.build_from_file("lwrp", file)
+ Chef::Resource.build_from_file("lwrp", file, @run_context)
end
Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "resources", "*"))].each do |file|
- Chef::Resource.build_from_file("lwrp", file)
+ Chef::Resource.build_from_file("lwrp", file, @run_context)
end
Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "providers", "*"))].each do |file|
- Chef::Provider.build_from_file("lwrp", file)
+ Chef::Provider.build_from_file("lwrp", file, @run_context)
end
Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "providers", "*"))].each do |file|
- Chef::Provider.build_from_file("lwrp", file)
+ Chef::Provider.build_from_file("lwrp", file, @run_context)
end
-
- node = Chef::Node.new
- node.platform(:ubuntu)
- node.platform_version('8.10')
- @run_context = Chef::RunContext.new(node, Chef::CookbookCollection.new({}))
-
- @runner = Chef::Runner.new(@run_context)
end
diff --git a/chef/spec/unit/mixin/language_spec.rb b/chef/spec/unit/mixin/language_spec.rb
index dc378ace3e..395b420e99 100644
--- a/chef/spec/unit/mixin/language_spec.rb
+++ b/chef/spec/unit/mixin/language_spec.rb
@@ -35,6 +35,7 @@ describe Chef::Mixin::Language do
"1.2.3" => "#{x}-1.2.3"
}
end
+ @platform_hash["debian"] = {["5", "6"] => "debian-5/6", "default" => "debian"}
@platform_hash["default"] = "default"
end
@@ -65,6 +66,20 @@ describe Chef::Mixin::Language do
@language.value_for_platform(@platform_hash).should == "openbsd"
end
+ describe "when platform versions is an array" do
+ it "returns a version-specific value based on the current platform" do
+ @node[:platform] = "debian"
+ @node[:platform_version] = "6"
+ @language.value_for_platform(@platform_hash).should == "debian-5/6"
+ end
+
+ it "returns a value based on the current platform if version not found" do
+ @node[:platform] = "debian"
+ @node[:platform_version] = "0.0.0"
+ @language.value_for_platform(@platform_hash).should == "debian"
+ end
+ end
+
# NOTE: this is a regression test for bug CHEF-1514
describe "when the value is an array" do
before do
diff --git a/chef/spec/unit/provider/mount/mount_spec.rb b/chef/spec/unit/provider/mount/mount_spec.rb
index d7f1524317..bad3ec47d5 100644
--- a/chef/spec/unit/provider/mount/mount_spec.rb
+++ b/chef/spec/unit/provider/mount/mount_spec.rb
@@ -56,11 +56,20 @@ describe Chef::Provider::Mount::Mount do
@stdout_findfs = mock("STDOUT", :first => "/dev/sdz1")
@provider.should_receive(:popen4).with("/sbin/findfs UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a").and_yield(@pid,@stdin,@stdout_findfs,@stderr).and_return(@status)
@provider.load_current_resource()
+ @provider.mountable?
end
it "should raise an error if the mount device does not exist" do
::File.stub!(:exists?).with("/dev/sdz1").and_return false
- lambda { @provider.load_current_resource() }.should raise_error(Chef::Exceptions::Mount)
+ lambda { @provider.load_current_resource();@provider.mountable? }.should raise_error(Chef::Exceptions::Mount)
+ end
+
+ it "should not call mountable? with load_current_resource - CHEF-1565" do
+ ::File.stub!(:exists?).with("/dev/sdz1").and_return false
+ @provider.should_receive(:mounted?).and_return(true)
+ @provider.should_receive(:enabled?).and_return(true)
+ @provider.should_not_receive(:mountable?)
+ @provider.load_current_resource
end
it "should raise an error if the mount device (uuid) does not exist" do
@@ -70,12 +79,12 @@ describe Chef::Provider::Mount::Mount do
stdout_findfs = mock("STDOUT", :first => nil)
@provider.should_receive(:popen4).with("/sbin/findfs UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a").and_yield(@pid,@stdin,stdout_findfs,@stderr).and_return(status_findfs)
::File.should_receive(:exists?).with("").and_return(false)
- lambda { @provider.load_current_resource() }.should raise_error(Chef::Exceptions::Mount)
+ lambda { @provider.load_current_resource();@provider.mountable? }.should raise_error(Chef::Exceptions::Mount)
end
it "should raise an error if the mount point does not exist" do
::File.stub!(:exists?).with("/tmp/foo").and_return false
- lambda { @provider.load_current_resource() }.should raise_error(Chef::Exceptions::Mount)
+ lambda { @provider.load_current_resource();@provider.mountable? }.should raise_error(Chef::Exceptions::Mount)
end
it "does not expect the device to exist when it is tmpfs" do
@@ -125,10 +134,20 @@ describe Chef::Provider::Mount::Mount do
end
it "should set enabled to true if the mount point is last in fstab" do
- fstab = "/dev/sdy1 /tmp/foo ext3 defaults 1 2\n"
- fstab << "/dev/sdz1 /tmp/foo ext3 defaults 1 2\n"
+ fstab1 = "/dev/sdy1 /tmp/foo ext3 defaults 1 2\n"
+ fstab2 = "#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n"
- ::File.stub!(:foreach).with("/etc/fstab").and_yield fstab
+ ::File.stub!(:foreach).with("/etc/fstab").and_yield(fstab1).and_yield(fstab2)
+
+ @provider.load_current_resource
+ @provider.current_resource.enabled.should be_true
+ end
+
+ it "should set enabled to true if the mount point is not last in fstab and mount_point is a substring of another mount" do
+ fstab1 = "#{@new_resource.device} #{@new_resource.mount_point} ext3 defaults 1 2\n"
+ fstab2 = "/dev/sdy1 /tmp/foo/bar ext3 defaults 1 2\n"
+
+ ::File.stub!(:foreach).with("/etc/fstab").and_yield(fstab1).and_yield(fstab2)
@provider.load_current_resource
@provider.current_resource.enabled.should be_true
diff --git a/chef/spec/unit/provider/package/apt_spec.rb b/chef/spec/unit/provider/package/apt_spec.rb
index d4298fa374..1eada8fbb3 100644
--- a/chef/spec/unit/provider/package/apt_spec.rb
+++ b/chef/spec/unit/provider/package/apt_spec.rb
@@ -23,94 +23,147 @@ describe Chef::Provider::Package::Apt do
@node = Chef::Node.new
@node.cookbook_collection = {}
@run_context = Chef::RunContext.new(@node, {})
- @new_resource = Chef::Resource::Package.new("emacs", @run_context)
- @current_resource = Chef::Resource::Package.new("emacs", @run_context)
+ @new_resource = Chef::Resource::Package.new("irssi", @run_context)
+ @current_resource = Chef::Resource::Package.new("irssi", @run_context)
@status = mock("Status", :exitstatus => 0)
@provider = Chef::Provider::Package::Apt.new(@new_resource, @run_context)
Chef::Resource::Package.stub!(:new).and_return(@current_resource)
@provider.stub!(:popen4).and_return(@status)
- @stdin = StringIO.new
- @stdout = StringIO.new(<<-SAMPLE_STDOUT)
-emacs:
- Installed: (none)
- Candidate: 0.1.1
- Version Table:
-SAMPLE_STDOUT
- @stderr = StringIO.new
- @pid = mock("PID")
+ @stdin = mock("STDIN", :null_object => true)
+ @stdout =<<-PKG_STATUS
+Package: irssi
+State: not installed
+Version: 0.8.12-7
+PKG_STATUS
+ @stderr = mock("STDERR", :null_object => true)
+ @pid = mock("PID", :null_object => true)
+ @shell_out = OpenStruct.new(:stdout => @stdout,:stdin => @stdin,:stderr => @stderr,:status => @status,:exitstatus => 0)
end
describe "when loading current resource" do
it "should create a current resource with the name of the new_resource" do
+ @provider.should_receive(:shell_out!).and_return(@shell_out)
Chef::Resource::Package.should_receive(:new).and_return(@current_resource)
@provider.load_current_resource
end
it "should set the current resources package name to the new resources package name" do
+ @provider.should_receive(:shell_out!).and_return(@shell_out)
@current_resource.should_receive(:package_name).with(@new_resource.package_name)
@provider.load_current_resource
end
- it "should run apt-cache policy with the package name" do
- @provider.should_receive(:popen4).with("apt-cache policy #{@new_resource.package_name}").and_return(@status)
+ it "should run aptitude show with the package name" do
+ @provider.should_receive(:shell_out!).with("aptitude show #{@new_resource.package_name}").and_return(@shell_out)
@provider.load_current_resource
end
- it "should read stdout on apt-cache policy" do
- @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
- @stdout.should_receive(:each).and_return(true)
- @provider.load_current_resource
- end
-
- it "should set the installed version to nil on the current resource if apt-cache policy installed version is (none)" do
- @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ it "should set the installed version to nil on the current resource if package state is not installed" do
+ @provider.should_receive(:shell_out!).and_return(@shell_out)
@current_resource.should_receive(:version).with(nil).and_return(true)
@provider.load_current_resource
end
- it "should set the installed version if apt-cache policy has one" do
- @stdout.stub!(:each).and_yield("emacs:").
- and_yield(" Installed: 0.1.1").
- and_yield(" Candidate: 0.1.1").
- and_yield(" Version Table:")
- @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
- @current_resource.should_receive(:version).with("0.1.1").and_return(true)
+ it "should set the installed version if package has one" do
+ @stdout.replace(<<-INSTALLED)
+Package: sudo
+State: installed
+Automatically installed: no
+Version: 1.7.2p1-1ubuntu5
+Priority: important
+Section: admin
+Maintainer: Ubuntu Core Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+Uncompressed Size: 602k
+Depends: libc6 (>= 2.8), libpam0g (>= 0.99.7.1), libpam-modules
+Conflicts: sudo-ldap
+Replaces: sudo-ldap
+Provided by: sudo-ldap
+Description: Provide limited super user privileges to specific users
+Sudo is a program designed to allow a sysadmin to give limited root privileges
+to users and log root activity. The basic philosophy is to give as few
+privileges as possible but still allow people to get their work done.
+INSTALLED
+ @provider.should_receive(:shell_out!).and_return(@shell_out)
@provider.load_current_resource
+ @current_resource.version.should == "1.7.2p1-1ubuntu5"
+ @provider.candidate_version.should eql("1.7.2p1-1ubuntu5")
end
- it "should set the candidate version if apt-cache policy has one" do
- @stdout.stub!(:each).and_yield("emacs:").
- and_yield(" Installed: 0.1.1").
- and_yield(" Candidate: 10").
- and_yield(" Version Table:")
- @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
- @provider.load_current_resource
- @provider.candidate_version.should eql("10")
- end
-
- it "should raise an exception if apt-cache policy fails" do
- @status.should_receive(:exitstatus).and_return(1)
+ it "should raise an exception if aptitude show does not return a candidate version" do
+ @stdout.replace("E: Unable to locate package magic")
+ @provider.should_receive(:shell_out!).and_return(@shell_out)
lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Package)
end
- it "should not raise an exception if apt-cache policy succeeds" do
- @status.should_receive(:exitstatus).and_return(0)
- lambda { @provider.load_current_resource }.should_not raise_error(Chef::Exceptions::Package)
+ it "should return the current resouce" do
+ @provider.should_receive(:shell_out!).and_return(@shell_out)
+ @provider.load_current_resource.should eql(@current_resource)
end
- it "should raise an exception if apt-cache policy does not return a candidate version" do
- @stdout.stub!(:each).and_yield("emacs:").
- and_yield(" Installed: 0.1.1").
- and_yield(" Candidate: (none)").
- and_yield(" Version Table:")
- @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
- lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Package)
+ it "should set candidate version to new package name if virtual package" do
+ @new_resource.package_name("libmysqlclient-dev")
+ virtual_package_out=<<-VPKG_STDOUT
+"No current or candidate version found for libmysqlclient-dev").
+Package: libmysqlclient-dev
+State: not a real package
+Provided by: libmysqlclient15-dev
+VPKG_STDOUT
+ virtual_package = mock(:stdout => virtual_package_out,:exitstatus => 0)
+ @provider.should_receive(:shell_out!).with("aptitude show libmysqlclient-dev").and_return(virtual_package)
+ real_package_out=mock("STDOUT", :null_object => true)
+ real_package_out =<<-REALPKG_STDOUT
+Package: libmysqlclient15-dev
+State: not installed
+Version: 5.0.51a-24+lenny4
+REALPKG_STDOUT
+ real_package = mock(:stdout => real_package_out,:exitstatus => 0)
+ @provider.should_receive(:shell_out!).with("aptitude show libmysqlclient15-dev").and_return(real_package)
+ @provider.load_current_resource
+ @provider.candidate_version.should eql("libmysqlclient15-dev")
end
- it "should return the current resouce" do
- @provider.load_current_resource.should eql(@current_resource)
+ it "should set candidate version to the first depends package name if multiple virtual package providers" do
+ @new_resource.package_name("vim")
+ virtual_package_out=<<-VPKG_STDOUT
+Package: vim
+State: not installed
+Version: 2:7.2.330-1ubuntu3
+Priority: optional
+Section: editors
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+Uncompressed Size: 1,946k
+Depends: vim-common (= 2:7.2.330-1ubuntu3), vim-runtime (= 2:7.2.330-1ubuntu3), libacl1 (>= 2.2.11-1), libc6 (>= 2.11),
+ libgpm2 (>= 1.20.4), libncurses5 (>= 5.6+20071006-3), libpython2.6 (>= 2.6), libselinux1 (>= 1.32)
+Suggests: ctags, vim-doc, vim-scripts
+Conflicts: vim-common (< 1:7.1-175+1)
+Replaces: vim-common (< 1:7.1-175+1)
+Provides: editor
+Provided by: vim-gnome, vim-gtk, vim-nox
+Description: Vi IMproved - enhanced vi editor
+ Vim is an almost compatible version of the UNIX editor Vi.
+VPKG_STDOUT
+ virtual_package = mock(:stdout => virtual_package_out,:exitstatus => 0)
+ @provider.should_receive(:shell_out!).with("aptitude show vim").and_return(virtual_package)
+ real_package_out=<<-REALPKG_STDOUT
+Package: vim-common
+State: not installed
+Automatically installed: no
+Version: 2:7.2.330-1ubuntu3
+Priority: important
+Section: editors
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+Uncompressed Size: 389k
+Depends: libc6 (>= 2.4)
+Recommends: vim | vim-gnome | vim-gtk | vim-lesstif | vim-nox | vim-tiny
+Description: Vi IMproved - Common files
+ Vim is an almost compatible version of the UNIX editor Vi.
+REALPKG_STDOUT
+ real_package = mock(:stdout => real_package_out,:exitstatus => 0)
+ @provider.should_receive(:shell_out!).with("aptitude show vim-common").and_return(real_package)
+ @provider.load_current_resource
+ @provider.candidate_version.should eql("vim-common")
end
end
@@ -119,32 +172,32 @@ SAMPLE_STDOUT
it "should run apt-get install with the package name and version" do
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "apt-get -q -y install emacs=1.0",
+ :command => "apt-get -q -y install irssi=0.8.12-7",
:environment => {
"DEBIAN_FRONTEND" => "noninteractive"
}
})
- @provider.install_package("emacs", "1.0")
+ @provider.install_package("irssi", "0.8.12-7")
end
it "should run apt-get install with the package name and version and options if specified" do
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "apt-get -q -y --force-yes install emacs=1.0",
+ :command => "apt-get -q -y --force-yes install irssi=0.8.12-7",
:environment => {
"DEBIAN_FRONTEND" => "noninteractive"
}
})
@new_resource.stub!(:options).and_return("--force-yes")
- @provider.install_package("emacs", "1.0")
+ @provider.install_package("irssi", "0.8.12-7")
end
end
describe Chef::Provider::Package::Apt, "upgrade_package" do
it "should run install_package with the name and version" do
- @provider.should_receive(:install_package).with("emacs", "1.0")
- @provider.upgrade_package("emacs", "1.0")
+ @provider.should_receive(:install_package).with("irssi", "0.8.12-7")
+ @provider.upgrade_package("irssi", "0.8.12-7")
end
end
@@ -152,24 +205,24 @@ SAMPLE_STDOUT
it "should run apt-get remove with the package name" do
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "apt-get -q -y remove emacs",
+ :command => "apt-get -q -y remove irssi",
:environment => {
"DEBIAN_FRONTEND" => "noninteractive"
}
})
- @provider.remove_package("emacs", "1.0")
+ @provider.remove_package("irssi", "0.8.12-7")
end
it "should run apt-get remove with the package name and options if specified" do
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "apt-get -q -y --force-yes remove emacs",
+ :command => "apt-get -q -y --force-yes remove irssi",
:environment => {
"DEBIAN_FRONTEND" => "noninteractive"
}
})
@new_resource.stub!(:options).and_return("--force-yes")
- @provider.remove_package("emacs", "1.0")
+ @provider.remove_package("irssi", "0.8.12-7")
end
end
@@ -177,52 +230,52 @@ SAMPLE_STDOUT
it "should run apt-get purge with the package name" do
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "apt-get -q -y purge emacs",
+ :command => "apt-get -q -y purge irssi",
:environment => {
"DEBIAN_FRONTEND" => "noninteractive"
}
})
- @provider.purge_package("emacs", "1.0")
+ @provider.purge_package("irssi", "0.8.12-7")
end
it "should run apt-get purge with the package name and options if specified" do
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "apt-get -q -y --force-yes purge emacs",
+ :command => "apt-get -q -y --force-yes purge irssi",
:environment => {
"DEBIAN_FRONTEND" => "noninteractive"
}
})
@new_resource.stub!(:options).and_return("--force-yes")
- @provider.purge_package("emacs", "1.0")
+ @provider.purge_package("irssi", "0.8.12-7")
end
end
describe "when preseeding a package" do
before(:each) do
- @provider.stub!(:get_preseed_file).and_return("/tmp/emacs-10.seed")
+ @provider.stub!(:get_preseed_file).and_return("/tmp/irssi-0.8.12-7.seed")
@provider.stub!(:run_command_with_systems_locale).and_return(true)
end
it "should get the full path to the preseed response file" do
- @provider.should_receive(:get_preseed_file).with("emacs", "10").and_return("/tmp/emacs-10.seed")
- @provider.preseed_package("emacs", "10")
+ @provider.should_receive(:get_preseed_file).with("irssi", "0.8.12-7").and_return("/tmp/irssi-0.8.12-7.seed")
+ @provider.preseed_package("irssi", "0.8.12-7")
end
it "should run debconf-set-selections on the preseed file if it has changed" do
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "debconf-set-selections /tmp/emacs-10.seed",
+ :command => "debconf-set-selections /tmp/irssi-0.8.12-7.seed",
:environment => {
"DEBIAN_FRONTEND" => "noninteractive"
}
}).and_return(true)
- @provider.preseed_package("emacs", "10")
+ @provider.preseed_package("irssi", "0.8.12-7")
end
it "should not run debconf-set-selections if the preseed file has not changed" do
@provider.stub!(:get_preseed_file).and_return(false)
@provider.should_not_receive(:run_command_with_systems_locale)
- @provider.preseed_package("emacs", "10")
+ @provider.preseed_package("irssi", "0.8.12-7")
end
end
end
diff --git a/chef/spec/unit/provider/package/dpkg_spec.rb b/chef/spec/unit/provider/package/dpkg_spec.rb
index 6293fdab8c..7c1f84f758 100644
--- a/chef/spec/unit/provider/package/dpkg_spec.rb
+++ b/chef/spec/unit/provider/package/dpkg_spec.rb
@@ -49,12 +49,26 @@ describe Chef::Provider::Package::Dpkg do
lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Package)
end
- it "gets the source package version from dpkg-deb if provided" do
- @stdout = StringIO.new("wget\t1.11.4-1ubuntu1")
- @provider.stub!(:popen4).with("dpkg-deb -W #{@new_resource.source}").and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
- @provider.load_current_resource
- @provider.current_resource.package_name.should == "wget"
- @new_resource.version.should == '1.11.4-1ubuntu1'
+ describe 'gets the source package version from dpkg-deb' do
+ def check_version(version)
+ @stdout = StringIO.new("wget\t#{version}")
+ @provider.stub!(:popen4).with("dpkg-deb -W #{@new_resource.source}").and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ @provider.load_current_resource
+ @provider.current_resource.package_name.should == "wget"
+ @new_resource.version.should == version
+ end
+
+ it 'if short version provided' do
+ check_version('1.11.4')
+ end
+
+ it 'if extended version provided' do
+ check_version('1.11.4-1ubuntu1')
+ end
+
+ it 'if distro-specific version provided' do
+ check_version('1.11.4-1ubuntu1~lucid')
+ end
end
it "gets the source package name from dpkg-deb correctly when the package name has `-' or `+' characters" do
diff --git a/chef/spec/unit/provider/package/easy_install_spec.rb b/chef/spec/unit/provider/package/easy_install_spec.rb
index 57c0044d3f..4bb0dcf9ce 100644
--- a/chef/spec/unit/provider/package/easy_install_spec.rb
+++ b/chef/spec/unit/provider/package/easy_install_spec.rb
@@ -38,7 +38,7 @@ describe Chef::Provider::Package::EasyInstall do
@pid = 2342
@provider.stub!(:popen4).and_return(@status)
end
-
+
describe "easy_install_binary_path" do
it "should return a Chef::Provider::EasyInstall object" do
@@ -70,6 +70,14 @@ describe Chef::Provider::Package::EasyInstall do
@provider.install_package("boto", "1.8d")
end
+ it "should run easy_install with the package name and version and specified options" do
+ @provider.should_receive(:run_command).with({
+ :command => "easy_install --always-unzip \"boto==1.8d\""
+ })
+ @new_resource.stub!(:options).and_return("--always-unzip")
+ @provider.install_package("boto", "1.8d")
+ end
+
it "should run easy_install with the package name and version" do
@provider.should_receive(:run_command).with({
:command => "easy_install \"boto==1.8d\""
@@ -84,6 +92,14 @@ describe Chef::Provider::Package::EasyInstall do
@provider.remove_package("boto", "1.8d")
end
+ it "should run easy_install -m with the package name and version and specified options" do
+ @provider.should_receive(:run_command).with({
+ :command => "easy_install -x -m boto"
+ })
+ @new_resource.stub!(:options).and_return("-x")
+ @provider.remove_package("boto", "1.8d")
+ end
+
it "should run easy_install -m with the package name and version" do
@provider.should_receive(:run_command).with({
:command => "easy_install -m boto"
diff --git a/chef/spec/unit/provider/package/freebsd_spec.rb b/chef/spec/unit/provider/package/freebsd_spec.rb
index 3c4ac9bd17..56dd9a6322 100644
--- a/chef/spec/unit/provider/package/freebsd_spec.rb
+++ b/chef/spec/unit/provider/package/freebsd_spec.rb
@@ -100,14 +100,14 @@ describe Chef::Provider::Package::Freebsd, "load_current_resource" do
it "should return the ports candidate version when given a valid port path" do
@provider.stub!(:port_path).and_return("/usr/ports/shells/zsh")
make_v = OpenStruct.new(:stdout => "4.3.6\n")
- @provider.should_receive(:shell_out!).with("make -V PORTVERSION", :cwd => '/usr/ports/shells/zsh', :env => nil, :returns=>[0,1]).and_return(make_v)
+ @provider.should_receive(:shell_out!).with("make -V PORTVERSION -f /usr/ports/shells/zsh/Makefile", :env => nil, :returns=>[0,1]).and_return(make_v)
@provider.ports_candidate_version.should == "4.3.6"
end
it "should figure out the package name" do
@provider.stub!(:port_path).and_return("/usr/ports/shells/zsh")
make_v = OpenStruct.new(:stdout => "zsh-4.3.6_7\n")
- @provider.should_receive(:shell_out!).with('make -V PKGNAME', :env => nil, :cwd => '/usr/ports/shells/zsh', :returns=>[0,1]).and_return(make_v)
+ @provider.should_receive(:shell_out!).with('make -V PKGNAME -f /usr/ports/shells/zsh/Makefile', :env => nil, :returns=>[0,1]).and_return(make_v)
#@provider.should_receive(:ports_makefile_variable_value).with("PKGNAME").and_return("zsh-4.3.6_7")
@provider.package_name.should == "zsh"
end
@@ -130,7 +130,7 @@ describe Chef::Provider::Package::Freebsd, "load_current_resource" do
it "should run make install when installing from ports" do
@new_resource.stub!(:source).and_return("ports")
@provider.should_receive(:port_path).and_return("/usr/ports/shells/zsh")
- @provider.should_receive(:shell_out!).with("make -DBATCH install", :env=>nil, :cwd => "/usr/ports/shells/zsh").and_return(@cmd_result)
+ @provider.should_receive(:shell_out!).with("make -DBATCH -f /usr/ports/shells/zsh/Makefile install", :timeout => 1200, :env=>nil).and_return(@cmd_result)
@provider.install_package("zsh", "4.3.6_7")
end
end
@@ -176,7 +176,7 @@ describe Chef::Provider::Package::Freebsd, "load_current_resource" do
@provider.stub!(:port_path).and_return("/usr/ports/converters/ruby-iconv")
@provider.stub!(:package_name).and_return("ruby18-iconv")
@provider.stub!(:latest_link_name).and_return("ruby18-iconv")
-
+
@install_result = OpenStruct.new(:status => true)
end
@@ -187,7 +187,7 @@ describe Chef::Provider::Package::Freebsd, "load_current_resource" do
it "should run make install when installing from ports" do
@new_resource.stub!(:source).and_return("ports")
- @provider.should_receive(:shell_out!).with("make -DBATCH install", :env=>nil, :cwd => "/usr/ports/converters/ruby-iconv").and_return(@install_result)
+ @provider.should_receive(:shell_out!).with("make -DBATCH -f /usr/ports/converters/ruby-iconv/Makefile install", :timeout => 1200, :env=>nil).and_return(@install_result)
@provider.install_package("ruby-iconv", "1.0")
end
end
@@ -239,7 +239,7 @@ describe Chef::Provider::Package::Freebsd, "load_current_resource" do
end
it "should install the mysql50-server binary package with the correct name" do
-
+
@new_resource = Chef::Resource::Package.new("mysql50-server")
@current_resource = Chef::Resource::Package.new("mysql50-server")
@provider = Chef::Provider::Package::Freebsd.new(@new_resource, @run_context)
diff --git a/chef/spec/unit/provider/package/macports_spec.rb b/chef/spec/unit/provider/package/macports_spec.rb
index 389e394095..46d61c0359 100644
--- a/chef/spec/unit/provider/package/macports_spec.rb
+++ b/chef/spec/unit/provider/package/macports_spec.rb
@@ -116,6 +116,15 @@ EOF
@provider.install_package("zsh", "4.2.7")
end
+
+ it "should add options to the port command when specified" do
+ @current_resource.should_receive(:version).and_return("4.1.6")
+ @provider.current_resource = @current_resource
+ @new_resource.stub!(:options).and_return("-f")
+ @provider.should_receive(:run_command_with_systems_locale).with(:command => "port -f install zsh @4.2.7")
+
+ @provider.install_package("zsh", "4.2.7")
+ end
end
describe "purge_package" do
@@ -128,6 +137,12 @@ EOF
@provider.should_receive(:run_command_with_systems_locale).with(:command => "port uninstall zsh")
@provider.purge_package("zsh", nil)
end
+
+ it "should add options to the port command when specified" do
+ @new_resource.stub!(:options).and_return("-f")
+ @provider.should_receive(:run_command_with_systems_locale).with(:command => "port -f uninstall zsh @4.2.7")
+ @provider.purge_package("zsh", "4.2.7")
+ end
end
describe "remove_package" do
@@ -140,6 +155,12 @@ EOF
@provider.should_receive(:run_command_with_systems_locale).with(:command => "port deactivate zsh")
@provider.remove_package("zsh", nil)
end
+
+ it "should add options to the port command when specified" do
+ @new_resource.stub!(:options).and_return("-f")
+ @provider.should_receive(:run_command_with_systems_locale).with(:command => "port -f deactivate zsh @4.2.7")
+ @provider.remove_package("zsh", "4.2.7")
+ end
end
describe "upgrade_package" do
@@ -167,5 +188,15 @@ EOF
@provider.upgrade_package("zsh", "4.2.7")
end
+
+ it "should add options to the port command when specified" do
+ @new_resource.stub!(:options).and_return("-f")
+ @current_resource.should_receive(:version).at_least(:once).and_return("4.1.6")
+ @provider.current_resource = @current_resource
+
+ @provider.should_receive(:run_command_with_systems_locale).with(:command => "port -f upgrade zsh @4.2.7")
+
+ @provider.upgrade_package("zsh", "4.2.7")
+ end
end
end
diff --git a/chef/spec/unit/provider/package/rubygems_spec.rb b/chef/spec/unit/provider/package/rubygems_spec.rb
index 9a389d1832..a5c7e88c82 100644
--- a/chef/spec/unit/provider/package/rubygems_spec.rb
+++ b/chef/spec/unit/provider/package/rubygems_spec.rb
@@ -44,18 +44,16 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
end
it "determines the installed versions of gems from Gem.source_index" do
- gems = [gemspec('rspec', Gem::Version.new('1.2.9')), gemspec('rspec', Gem::Version.new('1.3.0'))]
- Gem.source_index.should_receive(:search).with(Gem::Dependency.new('rspec', nil)).and_return(gems)
- @gem_env.installed_versions(Gem::Dependency.new('rspec', nil)).should == gems
+ gems = [gemspec('rspec-core', Gem::Version.new('1.2.9')), gemspec('rspec-core', Gem::Version.new('1.3.0'))]
+ Gem.source_index.should_receive(:search).with(Gem::Dependency.new('rspec-core', nil)).and_return(gems)
+ @gem_env.installed_versions(Gem::Dependency.new('rspec-core', nil)).should == gems
end
-
- # RSpec 2.5.0 reports its version as 2.5.1, which screws us. Thx.
- #it "determines the installed versions of gems from the source index (part2: the unmockening)" do
- #expected = ['rspec', Gem::Version.new()]
- #actual = @gem_env.installed_versions(Gem::Dependency.new('rspec', nil)).map { |spec| [spec.name, spec.version] }
- #actual.should include(expected)
- #end
+ it "determines the installed versions of gems from the source index (part2: the unmockening)" do
+ expected = ['rspec-core', Gem::Version.new(RSpec::Core::Version::STRING)]
+ actual = @gem_env.installed_versions(Gem::Dependency.new('rspec-core', nil)).map { |spec| [spec.name, spec.version] }
+ actual.should include(expected)
+ end
it "yields to a block with an alternate source list set" do
sources_in_block = nil
@@ -198,15 +196,14 @@ describe Chef::Provider::Package::Rubygems::AlternateGemEnvironment do
@gem_env.installed_versions(Gem::Dependency.new('rspec', nil)).should == gems
end
- # RSpec 2.5.0 reports its version as 2.5.1 :/
- #it "determines the installed versions of gems from the source index (part2: the unmockening)" do
- #path_to_gem = `which gem`.strip
- #pending("cant find your gem executable") if path_to_gem.empty?
- #gem_env = Chef::Provider::Package::Rubygems::AlternateGemEnvironment.new(path_to_gem)
- #expected = ['rspec', Gem::Version.new(Rspec::Core::Version::STRING)]
- #actual = gem_env.installed_versions(Gem::Dependency.new('rspec', nil)).map { |s| [s.name, s.version] }
- #actual.should include(expected)
- #end
+ it "determines the installed versions of gems from the source index (part2: the unmockening)" do
+ path_to_gem = `which gem`.strip
+ pending("cant find your gem executable") if path_to_gem.empty?
+ gem_env = Chef::Provider::Package::Rubygems::AlternateGemEnvironment.new(path_to_gem)
+ expected = ['rspec-core', Gem::Version.new(Rspec::Core::Version::STRING)]
+ actual = gem_env.installed_versions(Gem::Dependency.new('rspec-core', nil)).map { |s| [s.name, s.version] }
+ actual.should include(expected)
+ end
it "detects when the target gem environment is the jruby platform" do
gem_env_out=<<-JRUBY_GEM_ENV
@@ -302,9 +299,8 @@ end
describe Chef::Provider::Package::Rubygems do
before(:each) do
@node = Chef::Node.new
- @new_resource = Chef::Resource::GemPackage.new("rspec")
- #@spec_version = @new_resource.version RSpec::Core::Version::STRING
- @spec_version = Gem.source_index.search(Gem::Dependency.new("rspec")).last.version.to_s
+ @new_resource = Chef::Resource::GemPackage.new("rspec-core")
+ @spec_version = @new_resource.version RSpec::Core::Version::STRING
@run_context = Chef::RunContext.new(@node, {})
@provider = Chef::Provider::Package::Rubygems.new(@new_resource, @run_context)
@@ -332,9 +328,9 @@ describe Chef::Provider::Package::Rubygems do
end
it "converts the new resource into a gem dependency" do
- @provider.gem_dependency.should == Gem::Dependency.new('rspec', nil)
+ @provider.gem_dependency.should == Gem::Dependency.new('rspec-core', @spec_version)
@new_resource.version('~> 1.2.0')
- @provider.gem_dependency.should == Gem::Dependency.new('rspec', '~> 1.2.0')
+ @provider.gem_dependency.should == Gem::Dependency.new('rspec-core', '~> 1.2.0')
end
describe "when determining the currently installed version" do
@@ -361,17 +357,15 @@ describe Chef::Provider::Package::Rubygems do
describe "when determining the candidate version to install" do
it "does not query for available versions when the current version is the target version" do
- @new_resource.version("2.5.0")
@provider.current_resource = @new_resource.dup
@provider.candidate_version.should be_nil
end
it "determines the candidate version by querying the remote gem servers" do
- @new_resource.version("2.5.0")
@new_resource.source('http://mygems.example.com')
version = Gem::Version.new(@spec_version)
@provider.gem_env.should_receive(:candidate_version_from_remote).
- with(Gem::Dependency.new('rspec', @spec_version), "http://mygems.example.com").
+ with(Gem::Dependency.new('rspec-core', @spec_version), "http://mygems.example.com").
and_return(version)
@provider.candidate_version.should == @spec_version
end
@@ -387,9 +381,9 @@ describe Chef::Provider::Package::Rubygems do
describe "when installing a gem" do
before do
- @current_resource = Chef::Resource::GemPackage.new('rspec')
+ @current_resource = Chef::Resource::GemPackage.new('rspec-core')
@provider.current_resource = @current_resource
- @gem_dep = Gem::Dependency.new('rspec')
+ @gem_dep = Gem::Dependency.new('rspec-core', @spec_version)
end
describe "in the current gem environment" do
@@ -413,7 +407,7 @@ describe Chef::Provider::Package::Rubygems do
it "installs the gem by shelling out when options are provided as a String" do
@new_resource.options('-i /alt/install/location')
- expected ="gem install rspec -q --no-rdoc --no-ri -v \"#{@spec_version}\" -i /alt/install/location"
+ expected ="gem install rspec-core -q --no-rdoc --no-ri -v \"#{@spec_version}\" -i /alt/install/location"
@provider.should_receive(:shell_out!).with(expected, :env => nil)
@provider.action_install.should be_true
end
@@ -425,11 +419,10 @@ describe Chef::Provider::Package::Rubygems do
end
describe "at a specific version" do
before do
- @current_resource = Chef::Resource::GemPackage.new("rspec")
+ @current_resource = Chef::Resource::GemPackage.new("rspec-core")
@current_resource.version("2.4.0")
- @new_resource.version("2.5.0")
- @gem_dep = Gem::Dependency.new('rspec', @spec_version)
+ @gem_dep = Gem::Dependency.new('rspec-core', @spec_version)
end
it "installs the gem via the gems api" do
@@ -442,7 +435,7 @@ describe Chef::Provider::Package::Rubygems do
describe "in an alternate gem environment" do
it "installs the gem by shelling out to gem install" do
@new_resource.gem_binary('/usr/weird/bin/gem')
- @provider.should_receive(:shell_out!).with("/usr/weird/bin/gem install rspec -q --no-rdoc --no-ri -v \"#{@spec_version}\"", :env=>nil)
+ @provider.should_receive(:shell_out!).with("/usr/weird/bin/gem install rspec-core -q --no-rdoc --no-ri -v \"#{@spec_version}\"", :env=>nil)
@provider.action_install.should be_true
end
end
diff --git a/chef/spec/unit/provider/package/yum_spec.rb b/chef/spec/unit/provider/package/yum_spec.rb
index e2810523d5..a600a76fa6 100644
--- a/chef/spec/unit/provider/package/yum_spec.rb
+++ b/chef/spec/unit/provider/package/yum_spec.rb
@@ -26,11 +26,13 @@ describe Chef::Provider::Package::Yum do
@status = mock("Status", :exitstatus => 0)
@yum_cache = mock(
'Chef::Provider::Yum::YumCache',
- :refresh => true,
- :flush => true,
+ :reload_installed => true,
+ :reset => true,
:installed_version => "1.2.4-11.18.el5",
:candidate_version => "1.2.4-11.18.el5_2.3",
- :version_available? => true
+ :package_available? => true,
+ :version_available? => true,
+ :allow_multi_install => [ "kernel" ]
)
Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
@@ -39,7 +41,6 @@ describe Chef::Provider::Package::Yum do
end
describe "when loading the current system state" do
-
it "should create a current resource with the name of the new_resource" do
@provider.load_current_resource
@provider.current_resource.name.should == "cups"
@@ -69,12 +70,238 @@ describe Chef::Provider::Package::Yum do
it "should return the current resouce" do
@provider.load_current_resource.should eql(@provider.current_resource)
end
+
+ it "should raise an error if a candidate version can't be found" do
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :reload_installed => true,
+ :reset => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => nil,
+ :package_available? => true,
+ :version_available? => true
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Package, %r{don't have a version of package})
+ end
+
+ describe "when arch in package_name" do
+ it "should set the arch if no existing package_name is found and new_package_name+new_arch is available" do
+ @new_resource = Chef::Resource::YumPackage.new('testing.noarch')
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache'
+ )
+ @yum_cache.stub!(:installed_version) do |package_name, arch|
+ # nothing installed for package_name/new_package_name
+ nil
+ end
+ @yum_cache.stub!(:candidate_version) do |package_name, arch|
+ if package_name == "testing.noarch" || package_name == "testing.more.noarch"
+ nil
+ # candidate for new_package_name
+ elsif package_name == "testing" || package_name == "testing.more"
+ "1.1"
+ end
+ end
+ @yum_cache.stub!(:package_available?).and_return(true)
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ @provider.load_current_resource
+ @provider.new_resource.package_name.should == "testing"
+ @provider.new_resource.arch.should == "noarch"
+ @provider.arch.should == "noarch"
+
+ @new_resource = Chef::Resource::YumPackage.new('testing.more.noarch')
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ @provider.load_current_resource
+ @provider.new_resource.package_name.should == "testing.more"
+ @provider.new_resource.arch.should == "noarch"
+ @provider.arch.should == "noarch"
+ end
+
+ it "should not set the arch when an existing package_name is found" do
+ @new_resource = Chef::Resource::YumPackage.new('testing.beta3')
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache'
+ )
+ @yum_cache.stub!(:installed_version) do |package_name, arch|
+ # installed for package_name
+ if package_name == "testing.beta3" || package_name == "testing.beta3.more"
+ "1.1"
+ elsif package_name == "testing" || package_name = "testing.beta3"
+ nil
+ end
+ end
+ @yum_cache.stub!(:candidate_version) do |package_name, arch|
+ # no candidate for package_name/new_package_name
+ nil
+ end
+ @yum_cache.stub!(:package_available?).and_return(true)
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ # annoying side effect of the fun stub'ing above
+ lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Package, %r{don't have a version of package})
+ @provider.new_resource.package_name.should == "testing.beta3"
+ @provider.new_resource.arch.should == nil
+ @provider.arch.should == nil
+
+ @new_resource = Chef::Resource::YumPackage.new('testing.beta3.more')
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Package, %r{don't have a version of package})
+ @provider.new_resource.package_name.should == "testing.beta3.more"
+ @provider.new_resource.arch.should == nil
+ @provider.arch.should == nil
+ end
+
+ it "should not set the arch when no existing package_name or new_package_name+new_arch is found" do
+ @new_resource = Chef::Resource::YumPackage.new('testing.beta3')
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache'
+ )
+ @yum_cache.stub!(:installed_version) do |package_name, arch|
+ # nothing installed for package_name/new_package_name
+ nil
+ end
+ @yum_cache.stub!(:candidate_version) do |package_name, arch|
+ # no candidate for package_name/new_package_name
+ nil
+ end
+ @yum_cache.stub!(:package_available?).and_return(true)
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Package, %r{don't have a version of package})
+ @provider.new_resource.package_name.should == "testing.beta3"
+ @provider.new_resource.arch.should == nil
+ @provider.arch.should == nil
+
+ @new_resource = Chef::Resource::YumPackage.new('testing.beta3.more')
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Package, %r{don't have a version of package})
+ @provider.new_resource.package_name.should == "testing.beta3.more"
+ @provider.new_resource.arch.should == nil
+ @provider.arch.should == nil
+ end
+
+ it "should ensure it doesn't clobber an existing arch if passed" do
+ @new_resource = Chef::Resource::YumPackage.new('testing.i386')
+ @new_resource.arch("x86_64")
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache'
+ )
+ @yum_cache.stub!(:installed_version) do |package_name, arch|
+ # nothing installed for package_name/new_package_name
+ nil
+ end
+ @yum_cache.stub!(:candidate_version) do |package_name, arch|
+ if package_name == "testing.noarch"
+ nil
+ # candidate for new_package_name
+ elsif package_name == "testing"
+ "1.1"
+ end
+ end.and_return("something")
+ @yum_cache.stub!(:package_available?).and_return(true)
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ @provider.load_current_resource
+ @provider.new_resource.package_name.should == "testing.i386"
+ @provider.new_resource.arch.should == "x86_64"
+ end
+ end
+
+ it "should flush the cache if :before is true" do
+ @new_resource.stub!(:flush_cache).and_return({:after => false, :before => true})
+ @yum_cache.should_receive(:reload).once
+ @provider.load_current_resource
+ end
+
+ it "should flush the cache if :before is false" do
+ @new_resource.stub!(:flush_cache).and_return({:after => false, :before => false})
+ @yum_cache.should_not_receive(:reload)
+ @provider.load_current_resource
+ end
+
+ it "should search provides if package name can't be found then set package_name to match" do
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :reload_installed => true,
+ :reset => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5",
+ :package_available? => false,
+ :version_available? => true
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
+ pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "1.2.4-11.18.el5", "x86_64", [])
+ @yum_cache.should_receive(:packages_from_require).and_return([pkg])
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ @provider.load_current_resource
+ @new_resource.package_name.should == "test-package"
+ end
+
+ it "should search provides if package name can't be found, warn about multiple matches, but use the first one" do
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :reload_installed => true,
+ :reset => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5",
+ :package_available? => false,
+ :version_available? => true
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
+ pkg_x = Chef::Provider::Package::Yum::RPMPackage.new("test-package-x", "1.2.4-11.18.el5", "x86_64", [])
+ pkg_y = Chef::Provider::Package::Yum::RPMPackage.new("test-package-y", "1.2.6-11.3.el5", "i386", [])
+ @yum_cache.should_receive(:packages_from_require).and_return([pkg_x, pkg_y])
+ Chef::Log.should_receive(:warn).exactly(1).times.with(%r{matched multiple Provides})
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ @provider.load_current_resource
+ @new_resource.package_name.should == "test-package-x"
+ end
+
+ it "should search provides if no package is available - if no match in installed provides then load the complete set" do
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :reload_installed => true,
+ :reset => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5",
+ :package_available? => false,
+ :version_available? => true
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
+ @yum_cache.should_receive(:packages_from_require).twice.and_return([])
+ @yum_cache.should_receive(:reload_provides)
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ @provider.load_current_resource
+ end
+
+ it "should search provides if no package is available - if no match in provides leave the name intact" do
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :reload_provides => true,
+ :reload_installed => true,
+ :reset => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5",
+ :package_available? => false,
+ :version_available? => true
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
+ @yum_cache.should_receive(:packages_from_require).twice.and_return([])
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ @provider.load_current_resource
+ @new_resource.package_name.should == "cups"
+ end
end
describe "when installing a package" do
it "should run yum install with the package name and version" do
+ @provider.load_current_resource
+ Chef::Provider::Package::Yum::RPMUtils.stub!(:rpmvercmp).and_return(-1)
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "yum -d0 -e0 -y install emacs-1.0"
+ :command => "yum -d0 -e0 -y install emacs-1.0"
})
@provider.install_package("emacs", "1.0")
end
@@ -82,81 +309,173 @@ describe Chef::Provider::Package::Yum do
it "should run yum localinstall if given a path to an rpm" do
@new_resource.stub!(:source).and_return("/tmp/emacs-21.4-20.el5.i386.rpm")
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "yum -d0 -e0 -y localinstall /tmp/emacs-21.4-20.el5.i386.rpm"
+ :command => "yum -d0 -e0 -y localinstall /tmp/emacs-21.4-20.el5.i386.rpm"
})
@provider.install_package("emacs", "21.4-20.el5")
end
it "should run yum install with the package name, version and arch" do
+ @provider.load_current_resource
@new_resource.stub!(:arch).and_return("i386")
+ Chef::Provider::Package::Yum::RPMUtils.stub!(:rpmvercmp).and_return(-1)
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "yum -d0 -e0 -y install emacs-21.4-20.el5.i386"
+ :command => "yum -d0 -e0 -y install emacs-21.4-20.el5.i386"
})
@provider.install_package("emacs", "21.4-20.el5")
end
it "installs the package with the options given in the resource" do
+ @provider.load_current_resource
@provider.candidate_version = '11'
@new_resource.stub!(:options).and_return("--disablerepo epmd")
+ Chef::Provider::Package::Yum::RPMUtils.stub!(:rpmvercmp).and_return(-1)
@provider.should_receive(:run_command_with_systems_locale).with({
:command => "yum -d0 -e0 -y --disablerepo epmd install cups-11"
})
@provider.install_package(@new_resource.name, @provider.candidate_version)
end
- it "should fail if the package is not available" do
+ it "should raise an exception if the package is not available" do
@yum_cache = mock(
'Chef::Provider::Yum::YumCache',
- :refresh => true,
- :flush => true,
+ :reload_from_cache => true,
+ :reset => true,
:installed_version => "1.2.4-11.18.el5",
:candidate_version => "1.2.4-11.18.el5_2.3",
+ :package_available? => true,
:version_available? => nil
)
Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
- lambda { @provider.install_package("lolcats", "0.99") }.should raise_error(ArgumentError)
+ lambda { @provider.install_package("lolcats", "0.99") }.should raise_error(Chef::Exceptions::Package, %r{Version .* not found})
end
- end
- describe "when upgrading a package" do
- it "should run yum update if the package is installed and no version is given" do
+ it "should raise an exception if candidate version is older than the installed version and allow_downgrade is false" do
+ @new_resource.stub!(:allow_downgrade).and_return(false)
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :reload_installed => true,
+ :reset => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.15.el5",
+ :package_available? => true,
+ :version_available? => true,
+ :allow_multi_install => [ "kernel" ]
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ @provider.load_current_resource
+ lambda { @provider.install_package("cups", "1.2.4-11.15.el5") }.should raise_error(Chef::Exceptions::Package, %r{is newer than candidate package})
+ end
+
+ it "should not raise an exception if candidate version is older than the installed version and the package is list in yum's installonlypkg option" do
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :reload_installed => true,
+ :reset => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.15.el5",
+ :package_available? => true,
+ :version_available? => true,
+ :allow_multi_install => [ "cups" ]
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ @provider.load_current_resource
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "yum -d0 -e0 -y update cups"
+ :command => "yum -d0 -e0 -y install cups-1.2.4-11.15.el5"
})
- @provider.upgrade_package(@new_resource.name, nil)
+ @provider.install_package("cups", "1.2.4-11.15.el5")
end
- it "should run yum update with arch if the package is installed and no version is given" do
- @new_resource.stub!(:arch).and_return("i386")
+ it "should run yum downgrade if candidate version is older than the installed version and allow_downgrade is true" do
+ @new_resource.stub!(:allow_downgrade).and_return(true)
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :reload_installed => true,
+ :reset => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.15.el5",
+ :package_available? => true,
+ :version_available? => true,
+ :allow_multi_install => []
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ @provider.load_current_resource
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "yum -d0 -e0 -y update cups.i386"
+ :command => "yum -d0 -e0 -y downgrade cups-1.2.4-11.15.el5"
})
- @provider.upgrade_package(@new_resource.name, nil)
+ @provider.install_package("cups", "1.2.4-11.15.el5")
+ end
+
+ it "should run yum install then flush the cache if :after is true" do
+ @new_resource.stub!(:flush_cache).and_return({:after => true, :before => false})
+ @provider.load_current_resource
+ Chef::Provider::Package::Yum::RPMUtils.stub!(:rpmvercmp).and_return(-1)
+ @provider.should_receive(:run_command_with_systems_locale).with({
+ :command => "yum -d0 -e0 -y install emacs-1.0"
+ })
+ @yum_cache.should_receive(:reload).once
+ @provider.install_package("emacs", "1.0")
+ end
+
+ it "should run yum install then not flush the cache if :after is false" do
+ @new_resource.stub!(:flush_cache).and_return({:after => false, :before => false})
+ @provider.load_current_resource
+ Chef::Provider::Package::Yum::RPMUtils.stub!(:rpmvercmp).and_return(-1)
+ @provider.should_receive(:run_command_with_systems_locale).with({
+ :command => "yum -d0 -e0 -y install emacs-1.0"
+ })
+ @yum_cache.should_not_receive(:reload)
+ @provider.install_package("emacs", "1.0")
end
+ end
+ describe "when upgrading a package" do
it "should run yum install if the package is installed and a version is given" do
+ @provider.load_current_resource
@provider.candidate_version = '11'
+ Chef::Provider::Package::Yum::RPMUtils.stub!(:rpmvercmp).and_return(-1)
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "yum -d0 -e0 -y install cups-11"
+ :command => "yum -d0 -e0 -y install cups-11"
})
@provider.upgrade_package(@new_resource.name, @provider.candidate_version)
end
it "should run yum install if the package is not installed" do
+ @provider.load_current_resource
@current_resource = Chef::Resource::Package.new('cups')
@provider.candidate_version = '11'
+ Chef::Provider::Package::Yum::RPMUtils.stub!(:rpmvercmp).and_return(-1)
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "yum -d0 -e0 -y install cups-11"
+ :command => "yum -d0 -e0 -y install cups-11"
})
@provider.upgrade_package(@new_resource.name, @provider.candidate_version)
end
+
+ it "should raise an exception if candidate version is older than the installed version" do
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :reload_installed => true,
+ :reset => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.15.el5",
+ :package_available? => true,
+ :version_available? => true,
+ :allow_multi_install => [ "kernel" ]
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ @provider.load_current_resource
+ lambda { @provider.upgrade_package("cups", "1.2.4-11.15.el5") }.should raise_error(Chef::Exceptions::Package, %r{is newer than candidate package})
+ end
end
describe "when removing a package" do
it "should run yum remove with the package name" do
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "yum -d0 -e0 -y remove emacs-1.0"
+ :command => "yum -d0 -e0 -y remove emacs-1.0"
})
@provider.remove_package("emacs", "1.0")
end
@@ -164,7 +483,7 @@ describe Chef::Provider::Package::Yum do
it "should run yum remove with the package name and arch" do
@new_resource.stub!(:arch).and_return("x86_64")
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "yum -d0 -e0 -y remove emacs-1.0.x86_64"
+ :command => "yum -d0 -e0 -y remove emacs-1.0.x86_64"
})
@provider.remove_package("emacs", "1.0")
end
@@ -173,10 +492,1144 @@ describe Chef::Provider::Package::Yum do
describe "when purging a package" do
it "should run yum remove with the package name" do
@provider.should_receive(:run_command_with_systems_locale).with({
- :command => "yum -d0 -e0 -y remove emacs-1.0"
+ :command => "yum -d0 -e0 -y remove emacs-1.0"
})
@provider.purge_package("emacs", "1.0")
end
end
end
+
+describe Chef::Provider::Package::Yum::RPMUtils do
+ describe "version_parse" do
+ before do
+ @rpmutils = Chef::Provider::Package::Yum::RPMUtils
+ end
+
+ it "parses known good epoch strings" do
+ [
+ [ "0:3.3", [ 0, "3.3", nil ] ],
+ [ "9:1.7.3", [ 9, "1.7.3", nil ] ],
+ [ "15:20020927", [ 15, "20020927", nil ] ]
+ ].each do |x, y|
+ @rpmutils.version_parse(x).should == y
+ end
+ end
+
+ it "parses strange epoch strings" do
+ [
+ [ ":3.3", [ 0, "3.3", nil ] ],
+ [ "-1:1.7.3", [ nil, nil, "1:1.7.3" ] ],
+ [ "-:20020927", [ nil, nil, ":20020927" ] ]
+ ].each do |x, y|
+ @rpmutils.version_parse(x).should == y
+ end
+ end
+
+ it "parses known good version strings" do
+ [
+ [ "3.3", [ nil, "3.3", nil ] ],
+ [ "1.7.3", [ nil, "1.7.3", nil ] ],
+ [ "20020927", [ nil, "20020927", nil ] ]
+ ].each do |x, y|
+ @rpmutils.version_parse(x).should == y
+ end
+ end
+
+ it "parses strange version strings" do
+ [
+ [ "3..3", [ nil, "3..3", nil ] ],
+ [ "0001.7.3", [ nil, "0001.7.3", nil ] ],
+ [ "20020927,3", [ nil, "20020927,3", nil ] ]
+ ].each do |x, y|
+ @rpmutils.version_parse(x).should == y
+ end
+ end
+
+ it "parses known good version release strings" do
+ [
+ [ "3.3-0.pre3.1.60.el5_5.1", [ nil, "3.3", "0.pre3.1.60.el5_5.1" ] ],
+ [ "1.7.3-1jpp.2.el5", [ nil, "1.7.3", "1jpp.2.el5" ] ],
+ [ "20020927-46.el5", [ nil, "20020927", "46.el5" ] ]
+ ].each do |x, y|
+ @rpmutils.version_parse(x).should == y
+ end
+ end
+
+ it "parses strange version release strings" do
+ [
+ [ "3.3-", [ nil, "3.3", nil ] ],
+ [ "-1jpp.2.el5", [ nil, nil, "1jpp.2.el5" ] ],
+ [ "-0020020927-46.el5", [ nil, "-0020020927", "46.el5" ] ]
+ ].each do |x, y|
+ @rpmutils.version_parse(x).should == y
+ end
+ end
+ end
+
+ describe "rpmvercmp" do
+ before do
+ @rpmutils = Chef::Provider::Package::Yum::RPMUtils
+ end
+
+ it "should validate version compare logic for standard examples" do
+ [
+ # numeric
+ [ "0.0.2", "0.0.1", 1 ],
+ [ "0.2.0", "0.1.0", 1 ],
+ [ "2.0.0", "1.0.0", 1 ],
+ [ "0.0.1", "0.0.1", 0 ],
+ [ "0.0.1", "0.0.2", -1 ],
+ [ "0.1.0", "0.2.0", -1 ],
+ [ "1.0.0", "2.0.0", -1 ],
+ # alpha
+ [ "bb", "aa", 1 ],
+ [ "ab", "aa", 1 ],
+ [ "aa", "aa", 0 ],
+ [ "aa", "bb", -1 ],
+ [ "aa", "ab", -1 ],
+ [ "BB", "AA", 1 ],
+ [ "AA", "AA", 0 ],
+ [ "AA", "BB", -1 ],
+ [ "aa", "AA", 1 ],
+ [ "AA", "aa", -1 ],
+ # alphanumeric
+ [ "0.0.1b", "0.0.1a", 1 ],
+ [ "0.1b.0", "0.1a.0", 1 ],
+ [ "1b.0.0", "1a.0.0", 1 ],
+ [ "0.0.1a", "0.0.1a", 0 ],
+ [ "0.0.1a", "0.0.1b", -1 ],
+ [ "0.1a.0", "0.1b.0", -1 ],
+ [ "1a.0.0", "1b.0.0", -1 ],
+ # alphanumeric against alphanumeric
+ [ "0.0.1", "0.0.a", 1 ],
+ [ "0.1.0", "0.a.0", 1 ],
+ [ "1.0.0", "a.0.0", 1 ],
+ [ "0.0.a", "0.0.a", 0 ],
+ [ "0.0.a", "0.0.1", -1 ],
+ [ "0.a.0", "0.1.0", -1 ],
+ [ "a.0.0", "1.0.0", -1 ],
+ # alphanumeric against numeric
+ [ "0.0.2", "0.0.1a", 1 ],
+ [ "0.0.2a", "0.0.1", 1 ],
+ [ "0.0.1", "0.0.2a", -1 ],
+ [ "0.0.1a", "0.0.2", -1 ],
+ # length
+ [ "0.0.1aa", "0.0.1a", 1 ],
+ [ "0.0.1aa", "0.0.1aa", 0 ],
+ [ "0.0.1a", "0.0.1aa", -1 ],
+ ].each do |x, y, result|
+ @rpmutils.rpmvercmp(x,y).should == result
+ end
+ end
+
+ it "should validate version compare logic for strange examples" do
+ [
+ [ "2,0,0", "1.0.0", 1 ],
+ [ "0.0.1", "0,0.1", 0 ],
+ [ "1.0.0", "2,0,0", -1 ],
+ [ "002.0.0", "001.0.0", 1 ],
+ [ "001..0.1", "001..0.0", 1 ],
+ [ "-001..1", "-001..0", 1 ],
+ [ "1.0.1", nil, 1 ],
+ [ nil, nil, 0 ],
+ [ nil, "1.0.1", -1 ],
+ [ "1.0.1", "", 1 ],
+ [ "", "", 0 ],
+ [ "", "1.0.1", -1 ]
+ ].each do |x, y, result|
+ @rpmutils.rpmvercmp(x,y).should == result
+ end
+ end
+
+ it "tests isalnum good input" do
+ [ 'a', 'z', 'A', 'Z', '0', '9' ].each do |t|
+ @rpmutils.isalnum(t).should == true
+ end
+ end
+
+ it "tests isalnum bad input" do
+ [ '-', '.', '!', '^', ':', '_' ].each do |t|
+ @rpmutils.isalnum(t).should == false
+ end
+ end
+
+ it "tests isalpha good input" do
+ [ 'a', 'z', 'A', 'Z', ].each do |t|
+ @rpmutils.isalpha(t).should == true
+ end
+ end
+
+ it "tests isalpha bad input" do
+ [ '0', '9', '-', '.', '!', '^', ':', '_' ].each do |t|
+ @rpmutils.isalpha(t).should == false
+ end
+ end
+
+ it "tests isdigit good input" do
+ [ '0', '9', ].each do |t|
+ @rpmutils.isdigit(t).should == true
+ end
+ end
+
+ it "tests isdigit bad input" do
+ [ 'A', 'z', '-', '.', '!', '^', ':', '_' ].each do |t|
+ @rpmutils.isdigit(t).should == false
+ end
+ end
+ end
+
+end
+
+describe Chef::Provider::Package::Yum::RPMVersion do
+ describe "new - with parsing" do
+ before do
+ @rpmv = Chef::Provider::Package::Yum::RPMVersion.new("1:1.6.5-9.36.el5")
+ end
+
+ it "should expose evr (name-version-release) available" do
+ @rpmv.e.should == 1
+ @rpmv.v.should == "1.6.5"
+ @rpmv.r.should == "9.36.el5"
+
+ @rpmv.evr.should == "1:1.6.5-9.36.el5"
+ end
+
+ it "should output a version-release string" do
+ @rpmv.to_s.should == "1.6.5-9.36.el5"
+ end
+ end
+
+ describe "new - no parsing" do
+ before do
+ @rpmv = Chef::Provider::Package::Yum::RPMVersion.new("1", "1.6.5", "9.36.el5")
+ end
+
+ it "should expose evr (name-version-release) available" do
+ @rpmv.e.should == 1
+ @rpmv.v.should == "1.6.5"
+ @rpmv.r.should == "9.36.el5"
+
+ @rpmv.evr.should == "1:1.6.5-9.36.el5"
+ end
+
+ it "should output a version-release string" do
+ @rpmv.to_s.should == "1.6.5-9.36.el5"
+ end
+ end
+
+ it "should raise an error unless passed 1 or 3 args" do
+ lambda {
+ Chef::Provider::Package::Yum::RPMVersion.new()
+ }.should raise_error(ArgumentError)
+ lambda {
+ Chef::Provider::Package::Yum::RPMVersion.new("1:1.6.5-9.36.el5")
+ }.should_not raise_error
+ lambda {
+ Chef::Provider::Package::Yum::RPMVersion.new("1:1.6.5-9.36.el5", "extra")
+ }.should raise_error(ArgumentError)
+ lambda {
+ Chef::Provider::Package::Yum::RPMVersion.new("1", "1.6.5", "9.36.el5")
+ }.should_not raise_error
+ lambda {
+ Chef::Provider::Package::Yum::RPMVersion.new("1", "1.6.5", "9.36.el5", "extra")
+ }.should raise_error(ArgumentError)
+ end
+
+ # thanks version_class_spec.rb!
+ describe "compare" do
+ it "should sort based on complete epoch-version-release data" do
+ [
+ # smaller, larger
+ [ "0:1.6.5-9.36.el5",
+ "1:1.6.5-9.36.el5" ],
+ [ "0:2.3-15.el5",
+ "0:3.3-15.el5" ],
+ [ "0:alpha9.8-27.2",
+ "0:beta9.8-27.2" ],
+ [ "0:0.09-14jpp.3",
+ "0:0.09-15jpp.3" ],
+ [ "0:0.9.0-0.6.20110211.el5",
+ "0:0.9.0-0.6.20120211.el5" ],
+ [ "0:1.9.1-4.el5",
+ "0:1.9.1-5.el5" ],
+ [ "0:1.4.10-7.20090624svn.el5",
+ "0:1.4.10-7.20090625svn.el5" ],
+ [ "0:2.3.4-2.el5",
+ "0:2.3.4-2.el6" ]
+ ].each do |smaller, larger|
+ sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller)
+ lg = Chef::Provider::Package::Yum::RPMVersion.new(larger)
+ sm.should be < lg
+ lg.should be > sm
+ sm.should_not == lg
+ end
+ end
+
+ it "should sort based on partial epoch-version-release data" do
+ [
+ # smaller, larger
+ [ ":1.6.5-9.36.el5",
+ "1:1.6.5-9.36.el5" ],
+ [ "2.3-15.el5",
+ "3.3-15.el5" ],
+ [ "alpha9.8",
+ "beta9.8" ],
+ [ "14jpp",
+ "15jpp" ],
+ [ "0.9.0-0.6",
+ "0.9.0-0.7" ],
+ [ "0:1.9",
+ "3:1.9" ],
+ [ "2.3-2.el5",
+ "2.3-2.el6" ]
+ ].each do |smaller, larger|
+ sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller)
+ lg = Chef::Provider::Package::Yum::RPMVersion.new(larger)
+ sm.should be < lg
+ lg.should be > sm
+ sm.should_not == lg
+ end
+ end
+
+ it "should verify equality of complete epoch-version-release data" do
+ [
+ [ "0:1.6.5-9.36.el5",
+ "0:1.6.5-9.36.el5" ],
+ [ "0:2.3-15.el5",
+ "0:2.3-15.el5" ],
+ [ "0:alpha9.8-27.2",
+ "0:alpha9.8-27.2" ]
+ ].each do |smaller, larger|
+ sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller)
+ lg = Chef::Provider::Package::Yum::RPMVersion.new(larger)
+ sm.should be == lg
+ end
+ end
+
+ it "should verify equality of partial epoch-version-release data" do
+ [
+ [ ":1.6.5-9.36.el5",
+ "0:1.6.5-9.36.el5" ],
+ [ "2.3-15.el5",
+ "2.3-15.el5" ],
+ [ "alpha9.8-3",
+ "alpha9.8-3" ]
+ ].each do |smaller, larger|
+ sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller)
+ lg = Chef::Provider::Package::Yum::RPMVersion.new(larger)
+ sm.should be == lg
+ end
+ end
+ end
+
+ describe "partial compare" do
+ it "should compare based on partial epoch-version-release data" do
+ [
+ # smaller, larger
+ [ "0:1.1.1-1",
+ "1:" ],
+ [ "0:1.1.1-1",
+ "0:1.1.2" ],
+ [ "0:1.1.1-1",
+ "0:1.1.2-1" ],
+ [ "0:",
+ "1:1.1.1-1" ],
+ [ "0:1.1.1",
+ "0:1.1.2-1" ],
+ [ "0:1.1.1-1",
+ "0:1.1.2-1" ],
+ ].each do |smaller, larger|
+ sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller)
+ lg = Chef::Provider::Package::Yum::RPMVersion.new(larger)
+ sm.partial_compare(lg).should be == -1
+ lg.partial_compare(sm).should be == 1
+ sm.partial_compare(lg).should_not be == 0
+ end
+ end
+
+ it "should verify equality based on partial epoch-version-release data" do
+ [
+ [ "0:",
+ "0:1.1.1-1" ],
+ [ "0:1.1.1",
+ "0:1.1.1-1" ],
+ [ "0:1.1.1-1",
+ "0:1.1.1-1" ],
+ ].each do |smaller, larger|
+ sm = Chef::Provider::Package::Yum::RPMVersion.new(smaller)
+ lg = Chef::Provider::Package::Yum::RPMVersion.new(larger)
+ sm.partial_compare(lg).should be == 0
+ end
+ end
+ end
+
+end
+
+describe Chef::Provider::Package::Yum::RPMPackage do
+ describe "new - with parsing" do
+ before do
+ @rpm = Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5", "x86_64", [])
+ end
+
+ it "should expose nevra (name-epoch-version-release-arch) available" do
+ @rpm.name.should == "testing"
+ @rpm.version.e.should == 1
+ @rpm.version.v.should == "1.6.5"
+ @rpm.version.r.should == "9.36.el5"
+ @rpm.arch.should == "x86_64"
+
+ @rpm.nevra.should == "testing-1:1.6.5-9.36.el5.x86_64"
+ @rpm.to_s.should == @rpm.nevra
+ end
+
+ it "should always have at least one provide, itself" do
+ @rpm.provides.size.should == 1
+ @rpm.provides[0].name == "testing"
+ @rpm.provides[0].version.evr == "1:1.6.5-9.36.el5"
+ @rpm.provides[0].flag == :==
+ end
+ end
+
+ describe "new - no parsing" do
+ before do
+ @rpm = Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64", [])
+ end
+
+ it "should expose nevra (name-epoch-version-release-arch) available" do
+ @rpm.name.should == "testing"
+ @rpm.version.e.should == 1
+ @rpm.version.v.should == "1.6.5"
+ @rpm.version.r.should == "9.36.el5"
+ @rpm.arch.should == "x86_64"
+
+ @rpm.nevra.should == "testing-1:1.6.5-9.36.el5.x86_64"
+ @rpm.to_s.should == @rpm.nevra
+ end
+
+ it "should always have at least one provide, itself" do
+ @rpm.provides.size.should == 1
+ @rpm.provides[0].name == "testing"
+ @rpm.provides[0].version.evr == "1:1.6.5-9.36.el5"
+ @rpm.provides[0].flag == :==
+ end
+ end
+
+ it "should raise an error unless passed 4 or 6 args" do
+ lambda {
+ Chef::Provider::Package::Yum::RPMPackage.new()
+ }.should raise_error(ArgumentError)
+ lambda {
+ Chef::Provider::Package::Yum::RPMPackage.new("testing")
+ }.should raise_error(ArgumentError)
+ lambda {
+ Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5")
+ }.should raise_error(ArgumentError)
+ lambda {
+ Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5", "x86_64")
+ }.should raise_error(ArgumentError)
+ lambda {
+ Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5", "x86_64", [])
+ }.should_not raise_error
+ lambda {
+ Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64")
+ }.should raise_error(ArgumentError)
+ lambda {
+ Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64", [])
+ }.should_not raise_error
+ lambda {
+ Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64", [], "extra")
+ }.should raise_error(ArgumentError)
+ end
+
+ describe "<=>" do
+ it "should sort alphabetically based on package name" do
+ [
+ [ "a-test",
+ "b-test" ],
+ [ "B-test",
+ "a-test" ],
+ [ "A-test",
+ "B-test" ],
+ [ "Aa-test",
+ "aA-test" ],
+ [ "1test",
+ "2test" ],
+ ].each do |smaller, larger|
+ sm = Chef::Provider::Package::Yum::RPMPackage.new(smaller, "0:0.0.1-1", "x86_64", [])
+ lg = Chef::Provider::Package::Yum::RPMPackage.new(larger, "0:0.0.1-1", "x86_64", [])
+ sm.should be < lg
+ lg.should be > sm
+ sm.should_not == lg
+ end
+ end
+
+ it "should sort alphabetically based on package arch" do
+ [
+ [ "i386",
+ "x86_64" ],
+ [ "i386",
+ "noarch" ],
+ [ "noarch",
+ "x86_64" ],
+ ].each do |smaller, larger|
+ sm = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "0:0.0.1-1", smaller, [])
+ lg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "0:0.0.1-1", larger, [])
+ sm.should be < lg
+ lg.should be > sm
+ sm.should_not == lg
+ end
+ end
+ end
+
+end
+
+describe Chef::Provider::Package::Yum::RPMDbPackage do
+ before(:each) do
+ # name, version, arch, installed, available
+ @rpm_x = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "noarch", [], false, true)
+ @rpm_y = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "noarch", [], true, true)
+ @rpm_z = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "noarch", [], true, false)
+ end
+
+ describe "initialize" do
+ it "should return a Chef::Provider::Package::Yum::RPMDbPackage object" do
+ @rpm_x.should be_kind_of(Chef::Provider::Package::Yum::RPMDbPackage)
+ end
+ end
+
+ describe "available" do
+ it "should return true" do
+ @rpm_x.available.should be == true
+ @rpm_y.available.should be == true
+ @rpm_z.available.should be == false
+ end
+ end
+
+ describe "installed" do
+ it "should return true" do
+ @rpm_x.installed.should be == false
+ @rpm_y.installed.should be == true
+ @rpm_z.installed.should be == true
+ end
+ end
+
+end
+
+describe Chef::Provider::Package::Yum::RPMDependency do
+ describe "new - with parsing" do
+ before do
+ @rpmdep = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==)
+ end
+
+ it "should expose name, version, flag available" do
+ @rpmdep.name.should == "testing"
+ @rpmdep.version.e.should == 1
+ @rpmdep.version.v.should == "1.6.5"
+ @rpmdep.version.r.should == "9.36.el5"
+ @rpmdep.flag.should == :==
+ end
+ end
+
+ describe "new - no parsing" do
+ before do
+ @rpmdep = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1", "1.6.5", "9.36.el5", :==)
+ end
+
+ it "should expose name, version, flag available" do
+ @rpmdep.name.should == "testing"
+ @rpmdep.version.e.should == 1
+ @rpmdep.version.v.should == "1.6.5"
+ @rpmdep.version.r.should == "9.36.el5"
+ @rpmdep.flag.should == :==
+ end
+ end
+
+ it "should raise an error unless passed 3 or 5 args" do
+ lambda {
+ Chef::Provider::Package::Yum::RPMDependency.new()
+ }.should raise_error(ArgumentError)
+ lambda {
+ Chef::Provider::Package::Yum::RPMDependency.new("testing")
+ }.should raise_error(ArgumentError)
+ lambda {
+ Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5")
+ }.should raise_error(ArgumentError)
+ lambda {
+ Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==)
+ }.should_not raise_error
+ lambda {
+ Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==, "extra")
+ }.should raise_error(ArgumentError)
+ lambda {
+ Chef::Provider::Package::Yum::RPMDependency.new("testing", "1", "1.6.5", "9.36.el5", :==)
+ }.should_not raise_error
+ lambda {
+ Chef::Provider::Package::Yum::RPMDependency.new("testing", "1", "1.6.5", "9.36.el5", :==, "extra")
+ }.should raise_error(ArgumentError)
+ end
+
+ describe "parse" do
+ it "should parse a name, flag, version string into a valid RPMDependency object" do
+ @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing >= 1:1.6.5-9.36.el5")
+
+ @rpmdep.name.should == "testing"
+ @rpmdep.version.e.should == 1
+ @rpmdep.version.v.should == "1.6.5"
+ @rpmdep.version.r.should == "9.36.el5"
+ @rpmdep.flag.should == :>=
+ end
+
+ it "should parse a name into a valid RPMDependency object" do
+ @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing")
+
+ @rpmdep.name.should == "testing"
+ @rpmdep.version.e.should == nil
+ @rpmdep.version.v.should == nil
+ @rpmdep.version.r.should == nil
+ @rpmdep.flag.should == :==
+ end
+
+ it "should parse an invalid string into the name of a RPMDependency object" do
+ @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing blah >")
+
+ @rpmdep.name.should == "testing blah >"
+ @rpmdep.version.e.should == nil
+ @rpmdep.version.v.should == nil
+ @rpmdep.version.r.should == nil
+ @rpmdep.flag.should == :==
+ end
+
+ it "should parse various valid flags" do
+ [
+ [ ">", :> ],
+ [ ">=", :>= ],
+ [ "=", :== ],
+ [ "==", :== ],
+ [ "<=", :<= ],
+ [ "<", :< ]
+ ].each do |before, after|
+ @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing #{before} 1:1.1-1")
+ @rpmdep.flag.should == after
+ end
+ end
+
+ it "should parse various invalid flags and treat them as names" do
+ [
+ [ "<>", :== ],
+ [ "!=", :== ],
+ [ ">>", :== ],
+ [ "<<", :== ],
+ [ "!", :== ],
+ [ "~", :== ]
+ ].each do |before, after|
+ @rpmdep = Chef::Provider::Package::Yum::RPMDependency.parse("testing #{before} 1:1.1-1")
+ @rpmdep.name.should == "testing #{before} 1:1.1-1"
+ @rpmdep.flag.should == after
+ end
+ end
+ end
+
+ describe "satisfy?" do
+ it "should raise an error unless a RPMDependency is passed" do
+ @rpmprovide = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==)
+ @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :>=)
+ lambda {
+ @rpmprovide.satisfy?("hi")
+ }.should raise_error(ArgumentError)
+ lambda {
+ @rpmprovide.satisfy?(@rpmrequire)
+ }.should_not raise_error
+ end
+
+ it "should validate dependency satisfaction logic for standard examples" do
+ [
+ # names
+ [ "test", "test", true ],
+ [ "test", "foo", false ],
+ # full: epoch:version-relese
+ [ "testing = 1:1.1-1", "testing > 1:1.1-0", true ],
+ [ "testing = 1:1.1-1", "testing >= 1:1.1-0", true ],
+ [ "testing = 1:1.1-1", "testing >= 1:1.1-1", true ],
+ [ "testing = 1:1.1-1", "testing = 1:1.1-1", true ],
+ [ "testing = 1:1.1-1", "testing == 1:1.1-1", true ],
+ [ "testing = 1:1.1-1", "testing <= 1:1.1-1", true ],
+ [ "testing = 1:1.1-1", "testing <= 1:1.1-0", false ],
+ [ "testing = 1:1.1-1", "testing < 1:1.1-0", false ],
+ # partial: epoch:version
+ [ "testing = 1:1.1", "testing > 1:1.0", true ],
+ [ "testing = 1:1.1", "testing >= 1:1.0", true ],
+ [ "testing = 1:1.1", "testing >= 1:1.1", true ],
+ [ "testing = 1:1.1", "testing = 1:1.1", true ],
+ [ "testing = 1:1.1", "testing == 1:1.1", true ],
+ [ "testing = 1:1.1", "testing <= 1:1.1", true ],
+ [ "testing = 1:1.1", "testing <= 1:1.0", false ],
+ [ "testing = 1:1.1", "testing < 1:1.0", false ],
+ # partial: epoch
+ [ "testing = 1:", "testing > 0:", true ],
+ [ "testing = 1:", "testing >= 0:", true ],
+ [ "testing = 1:", "testing >= 1:", true ],
+ [ "testing = 1:", "testing = 1:", true ],
+ [ "testing = 1:", "testing == 1:", true ],
+ [ "testing = 1:", "testing <= 1:", true ],
+ [ "testing = 1:", "testing <= 0:", false ],
+ [ "testing = 1:", "testing < 0:", false ],
+ # mix and match!
+ [ "testing = 1:1.1-1", "testing == 1:1.1", true ],
+ [ "testing = 1:1.1-1", "testing == 1:", true ],
+ ].each do |prov, req, result|
+ @rpmprovide = Chef::Provider::Package::Yum::RPMDependency.parse(prov)
+ @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.parse(req)
+
+ @rpmprovide.satisfy?(@rpmrequire).should == result
+ @rpmrequire.satisfy?(@rpmprovide).should == result
+ end
+ end
+ end
+
+end
+
+# thanks resource_collection_spec.rb!
+describe Chef::Provider::Package::Yum::RPMDb do
+ before(:each) do
+ @rpmdb = Chef::Provider::Package::Yum::RPMDb.new
+ # name, version, arch, installed, available
+ deps_v = [
+ Chef::Provider::Package::Yum::RPMDependency.parse("libz.so.1()(64bit)"),
+ Chef::Provider::Package::Yum::RPMDependency.parse("test-package-a = 0:1.6.5-9.36.el5")
+ ]
+ deps_z = [
+ Chef::Provider::Package::Yum::RPMDependency.parse("libz.so.1()(64bit)"),
+ Chef::Provider::Package::Yum::RPMDependency.parse("config(test) = 0:1.6.5-9.36.el5"),
+ Chef::Provider::Package::Yum::RPMDependency.parse("test-package-c = 0:1.6.5-9.36.el5")
+ ]
+ @rpm_v = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-a", "0:1.6.5-9.36.el5", "i386", deps_v, true, false)
+ @rpm_w = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "i386", [], true, true)
+ @rpm_x = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "0:1.6.5-9.36.el5", "x86_64", [], false, true)
+ @rpm_y = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-b", "1:1.6.5-9.36.el5", "x86_64", [], true, true)
+ @rpm_z = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-c", "0:1.6.5-9.36.el5", "noarch", deps_z, true, true)
+ @rpm_z_mirror = Chef::Provider::Package::Yum::RPMDbPackage.new("test-package-c", "0:1.6.5-9.36.el5", "noarch", deps_z, true, true)
+ end
+
+ describe "initialize" do
+ it "should return a Chef::Provider::Package::Yum::RPMDb object" do
+ @rpmdb.should be_kind_of(Chef::Provider::Package::Yum::RPMDb)
+ end
+ end
+
+ describe "push" do
+ it "should accept an RPMDbPackage object through pushing" do
+ lambda { @rpmdb.push(@rpm_w) }.should_not raise_error
+ end
+
+ it "should accept multiple RPMDbPackage object through pushing" do
+ lambda { @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z) }.should_not raise_error
+ end
+
+ it "should only accept an RPMDbPackage object" do
+ lambda { @rpmdb.push("string") }.should raise_error
+ end
+
+ it "should add the package to the package db" do
+ @rpmdb.push(@rpm_w)
+ @rpmdb["test-package-b"].should_not be == nil
+ end
+
+ it "should add conditionally add the package to the available list" do
+ @rpmdb.available_size.should be == 0
+ @rpmdb.push(@rpm_v, @rpm_w)
+ @rpmdb.available_size.should be == 1
+ end
+
+ it "should add conditionally add the package to the installed list" do
+ @rpmdb.installed_size.should be == 0
+ @rpmdb.push(@rpm_w, @rpm_x)
+ @rpmdb.installed_size.should be == 1
+ end
+
+ it "should have a total of 2 packages in the RPMDb" do
+ @rpmdb.size.should be == 0
+ @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z)
+ @rpmdb.size.should be == 2
+ end
+
+ it "should keep the Array unique when a duplicate is pushed" do
+ @rpmdb.push(@rpm_z, @rpm_z_mirror)
+ @rpmdb["test-package-c"].size.should be == 1
+ end
+
+ it "should register the package provides in the provides index" do
+ @rpmdb.push(@rpm_v, @rpm_w, @rpm_z)
+ @rpmdb.lookup_provides("test-package-a")[0].should be == @rpm_v
+ @rpmdb.lookup_provides("config(test)")[0].should be == @rpm_z
+ @rpmdb.lookup_provides("libz.so.1()(64bit)")[0].should be == @rpm_v
+ @rpmdb.lookup_provides("libz.so.1()(64bit)")[1].should be == @rpm_z
+ end
+ end
+
+ describe "<<" do
+ it "should accept an RPMPackage object through the << operator" do
+ lambda { @rpmdb << @rpm_w }.should_not raise_error
+ end
+ end
+
+ describe "lookup" do
+ it "should return an Array of RPMPackage objects by index" do
+ @rpmdb << @rpm_w
+ @rpmdb.lookup("test-package-b").should be_kind_of(Array)
+ end
+ end
+
+ describe "[]" do
+ it "should return an Array of RPMPackage objects though the [index] operator" do
+ @rpmdb << @rpm_w
+ @rpmdb["test-package-b"].should be_kind_of(Array)
+ end
+
+ it "should return an Array of 3 RPMPackage objects" do
+ @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z)
+ @rpmdb["test-package-b"].size.should be == 3
+ end
+
+ it "should return an Array of RPMPackage objects sorted from newest to oldest" do
+ @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z)
+ @rpmdb["test-package-b"][0].should be == @rpm_y
+ @rpmdb["test-package-b"][1].should be == @rpm_x
+ @rpmdb["test-package-b"][2].should be == @rpm_w
+ end
+ end
+
+ describe "lookup_provides" do
+ it "should return an Array of RPMPackage objects by index" do
+ @rpmdb << @rpm_z
+ x = @rpmdb.lookup_provides("config(test)")
+ x.should be_kind_of(Array)
+ x[0].should be == @rpm_z
+ end
+ end
+
+ describe "clear" do
+ it "should clear the RPMDb" do
+ @rpmdb.should_receive(:clear_available).once
+ @rpmdb.should_receive(:clear_installed).once
+ @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z)
+ @rpmdb.size.should_not be == 0
+ @rpmdb.lookup_provides("config(test)").should be_kind_of(Array)
+ @rpmdb.clear
+ @rpmdb.lookup_provides("config(test)").should be == nil
+ @rpmdb.size.should be == 0
+ end
+ end
+
+ describe "clear_available" do
+ it "should clear the available list" do
+ @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z)
+ @rpmdb.available_size.should_not be == 0
+ @rpmdb.clear_available
+ @rpmdb.available_size.should be == 0
+ end
+ end
+
+ describe "available?" do
+ it "should return true if a package is available" do
+ @rpmdb.available?(@rpm_w).should be == false
+ @rpmdb.push(@rpm_v, @rpm_w)
+ @rpmdb.available?(@rpm_v).should be == false
+ @rpmdb.available?(@rpm_w).should be == true
+ end
+ end
+
+ describe "clear_installed" do
+ it "should clear the installed list" do
+ @rpmdb.push(@rpm_w, @rpm_x, @rpm_y, @rpm_z)
+ @rpmdb.installed_size.should_not be == 0
+ @rpmdb.clear_installed
+ @rpmdb.installed_size.should be == 0
+ end
+ end
+
+ describe "installed?" do
+ it "should return true if a package is installed" do
+ @rpmdb.installed?(@rpm_w).should be == false
+ @rpmdb.push(@rpm_w, @rpm_x)
+ @rpmdb.installed?(@rpm_w).should be == true
+ @rpmdb.installed?(@rpm_x).should be == false
+ end
+ end
+
+ describe "whatprovides" do
+ it "should raise an error unless a RPMDependency is passed" do
+ @rpmprovide = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==)
+ @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :>=)
+ lambda {
+ @rpmdb.whatprovides("hi")
+ }.should raise_error(ArgumentError)
+ lambda {
+ @rpmdb.whatprovides(@rpmrequire)
+ }.should_not raise_error
+ end
+
+ it "should return an Array of packages statisfying a RPMDependency" do
+ @rpmdb.push(@rpm_v, @rpm_w, @rpm_z)
+
+ @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.parse("test-package-a >= 1.6.5")
+ x = @rpmdb.whatprovides(@rpmrequire)
+ x.should be_kind_of(Array)
+ x[0].should be == @rpm_v
+
+ @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.parse("libz.so.1()(64bit)")
+ x = @rpmdb.whatprovides(@rpmrequire)
+ x.should be_kind_of(Array)
+ x[0].should be == @rpm_v
+ x[1].should be == @rpm_z
+ end
+ end
+
+end
+
+describe Chef::Provider::Package::Yum::YumCache do
+ # allow for the reset of a Singleton
+ # thanks to Ian White (http://blog.ardes.com/2006/12/11/testing-singletons-with-ruby)
+ class << Chef::Provider::Package::Yum::YumCache
+ def reset_instance
+ Singleton.send :__init__, self
+ self
+ end
+ end
+
+ before(:each) do
+ yum_dump_good_output = <<EOF
+[option installonlypkgs] kernel kernel-bigmem kernel-enterprise
+erlang-mochiweb 0 1.4.1 5.el5 x86_64 ['erlang-mochiweb = 1.4.1-5.el5', 'mochiweb = 1.4.1-5.el5'] i
+zip 0 2.31 2.el5 x86_64 ['zip = 2.31-2.el5'] r
+zisofs-tools 0 1.0.6 3.2.2 x86_64 [] a
+zlib 0 1.2.3 3 x86_64 ['zlib = 1.2.3-3', 'libz.so.1()(64bit)'] r
+zlib 0 1.2.3 3 i386 ['zlib = 1.2.3-3', 'libz.so.1'] r
+zlib-devel 0 1.2.3 3 i386 [] a
+zlib-devel 0 1.2.3 3 x86_64 ['zlib-devel = 1.2.3-3'] r
+znc 0 0.098 1.el5 x86_64 [] a
+znc-devel 0 0.098 1.el5 i386 [] a
+znc-devel 0 0.098 1.el5 x86_64 [] a
+znc-extra 0 0.098 1.el5 x86_64 [] a
+znc-modtcl 0 0.098 1.el5 x86_64 [] a
+EOF
+
+ yum_dump_bad_output_separators = <<EOF
+zip 0 2.31 2.el5 x86_64 ['zip = 2.31-2.el5'] r
+zlib 0 1.2.3 3 x86_64 ['zlib = 1.2.3-3', 'libz.so.1()(64bit)'] i bad
+zlib-devel 0 1.2.3 3 i386 [] a
+bad zlib-devel 0 1.2.3 3 x86_64 ['zlib-devel = 1.2.3-3'] i
+znc-modtcl 0 0.098 1.el5 x86_64 [] a bad
+EOF
+
+ yum_dump_bad_output_type = <<EOF
+zip 0 2.31 2.el5 x86_64 ['zip = 2.31-2.el5'] r
+zlib 0 1.2.3 3 x86_64 ['zlib = 1.2.3-3', 'libz.so.1()(64bit)'] c
+zlib-devel 0 1.2.3 3 i386 [] a
+zlib-devel 0 1.2.3 3 x86_64 ['zlib-devel = 1.2.3-3'] bad
+znc-modtcl 0 0.098 1.el5 x86_64 [] a
+EOF
+
+ yum_dump_error = <<EOF
+yum-dump Config Error: File contains no section headers.
+file: file://///etc/yum.repos.d/CentOS-Base.repo, line: 12
+'qeqwewe\n'
+EOF
+
+ @status = mock("Status", :exitstatus => 0)
+ @status_bad = mock("Status", :exitstatus => 1)
+ @stdin = mock("STDIN", :nil_object => true)
+ @stdout = mock("STDOUT", :nil_object => true)
+ @stdout_good = yum_dump_good_output.split("\n")
+ @stdout_bad_type = yum_dump_bad_output_type.split("\n")
+ @stdout_bad_separators = yum_dump_bad_output_separators.split("\n")
+ @stderr = mock("STDERR", :nil_object => true)
+ @stderr.stub!(:readlines).and_return(yum_dump_error.split("\n"))
+ @pid = mock("PID", :nil_object => true)
+
+ # new singleton each time
+ Chef::Provider::Package::Yum::YumCache.reset_instance
+ @yc = Chef::Provider::Package::Yum::YumCache.instance
+ # load valid data
+ @yc.stub!(:popen4).and_yield(@pid, @stdin, @stdout_good, @stderr).and_return(@status)
+ end
+
+ describe "initialize" do
+ it "should return a Chef::Provider::Package::Yum::YumCache object" do
+ @yc.should be_kind_of(Chef::Provider::Package::Yum::YumCache)
+ end
+
+ it "should register reload for start of Chef::Client runs" do
+ Chef::Provider::Package::Yum::YumCache.reset_instance
+ Chef::Client.should_receive(:when_run_starts) do |&b|
+ b.should_not be_nil
+ end
+ @yc = Chef::Provider::Package::Yum::YumCache.instance
+ end
+ end
+
+ describe "refresh" do
+ it "should implicitly call yum-dump.py only once by default after being instantiated" do
+ @yc.should_receive(:popen4).once
+ @yc.installed_version("zlib")
+ @yc.reset
+ @yc.installed_version("zlib")
+ end
+
+ it "should run yum-dump.py using the system python when next_refresh is for :all" do
+ @yc.reload
+ @yc.should_receive(:popen4).with(%r{^/usr/bin/python .*/yum-dump.py --options --installed-provides$}, :waitlast=>true)
+ @yc.refresh
+ end
+
+ it "should run yum-dump.py with the installed flag when next_refresh is for :installed" do
+ @yc.reload_installed
+ @yc.should_receive(:popen4).with(%r{^/usr/bin/python .*/yum-dump.py --installed$}, :waitlast=>true)
+ @yc.refresh
+ end
+
+ it "should run yum-dump.py with the all-provides flag when next_refresh is for :provides" do
+ @yc.reload_provides
+ @yc.should_receive(:popen4).with(%r{^/usr/bin/python .*/yum-dump.py --options --all-provides$}, :waitlast=>true)
+ @yc.refresh
+ end
+
+ it "should warn about invalid data with too many separators" do
+ @yc.stub!(:popen4).and_yield(@pid, @stdin, @stdout_bad_separators, @stderr).and_return(@status)
+ Chef::Log.should_receive(:warn).exactly(3).times.with(%r{Problem parsing})
+ @yc.refresh
+ end
+
+ it "should warn about invalid data with an incorrect type" do
+ @yc.stub!(:popen4).and_yield(@pid, @stdin, @stdout_bad_type, @stderr).and_return(@status)
+ Chef::Log.should_receive(:warn).exactly(2).times.with(%r{Problem parsing})
+ @yc.refresh
+ end
+
+ it "should warn about no output from yum-dump.py" do
+ @yc.stub!(:popen4).and_yield(@pid, @stdin, [], @stderr).and_return(@status)
+ Chef::Log.should_receive(:warn).exactly(1).times.with(%r{no output from yum-dump.py})
+ @yc.refresh
+ end
+
+ it "should raise exception yum-dump.py exits with a non zero status" do
+ @yc.stub!(:popen4).and_yield(@pid, @stdin, [], @stderr).and_return(@status_bad)
+ lambda { @yc.refresh}.should raise_error(Chef::Exceptions::Package, %r{CentOS-Base.repo, line: 12})
+ end
+
+ it "should parse type 'i' into an installed state for a package" do
+ @yc.available_version("erlang-mochiweb").should be == nil
+ @yc.installed_version("erlang-mochiweb").should_not be == nil
+ end
+
+ it "should parse type 'a' into an available state for a package" do
+ @yc.available_version("znc").should_not be == nil
+ @yc.installed_version("znc").should be == nil
+ end
+
+ it "should parse type 'r' into an installed and available states for a package" do
+ @yc.available_version("zip").should_not be == nil
+ @yc.installed_version("zip").should_not be == nil
+ end
+
+ it "should parse installonlypkgs from yum-dump.py options output" do
+ @yc.allow_multi_install.should be == %w{kernel kernel-bigmem kernel-enterprise}
+ end
+ end
+
+ describe "installed_version" do
+ it "should take one or two arguments" do
+ lambda { @yc.installed_version("zip") }.should_not raise_error(ArgumentError)
+ lambda { @yc.installed_version("zip", "i386") }.should_not raise_error(ArgumentError)
+ lambda { @yc.installed_version("zip", "i386", "extra") }.should raise_error(ArgumentError)
+ end
+
+ it "should return version-release for matching package regardless of arch" do
+ @yc.installed_version("zip", "x86_64").should be == "2.31-2.el5"
+ @yc.installed_version("zip", nil).should be == "2.31-2.el5"
+ end
+
+ it "should return version-release for matching package and arch" do
+ @yc.installed_version("zip", "x86_64").should be == "2.31-2.el5"
+ @yc.installed_version("zisofs-tools", "i386").should be == nil
+ end
+
+ it "should return nil for an unmatched package" do
+ @yc.installed_version(nil, nil).should be == nil
+ @yc.installed_version("test1", nil).should be == nil
+ @yc.installed_version("test2", "x86_64").should be == nil
+ end
+ end
+
+ describe "available_version" do
+ it "should take one or two arguments" do
+ lambda { @yc.available_version("zisofs-tools") }.should_not raise_error(ArgumentError)
+ lambda { @yc.available_version("zisofs-tools", "i386") }.should_not raise_error(ArgumentError)
+ lambda { @yc.available_version("zisofs-tools", "i386", "extra") }.should raise_error(ArgumentError)
+ end
+
+ it "should return version-release for matching package regardless of arch" do
+ @yc.available_version("zip", "x86_64").should be == "2.31-2.el5"
+ @yc.available_version("zip", nil).should be == "2.31-2.el5"
+ end
+
+ it "should return version-release for matching package and arch" do
+ @yc.available_version("zip", "x86_64").should be == "2.31-2.el5"
+ @yc.available_version("zisofs-tools", "i386").should be == nil
+ end
+
+ it "should return nil for an unmatched package" do
+ @yc.available_version(nil, nil).should be == nil
+ @yc.available_version("test1", nil).should be == nil
+ @yc.available_version("test2", "x86_64").should be == nil
+ end
+ end
+
+ describe "version_available?" do
+ it "should take two or three arguments" do
+ lambda { @yc.version_available?("zisofs-tools") }.should raise_error(ArgumentError)
+ lambda { @yc.version_available?("zisofs-tools", "1.0.6-3.2.2") }.should_not raise_error(ArgumentError)
+ lambda { @yc.version_available?("zisofs-tools", "1.0.6-3.2.2", "x86_64") }.should_not raise_error(ArgumentError)
+ end
+
+ it "should return true if our package-version-arch is available" do
+ @yc.version_available?("zisofs-tools", "1.0.6-3.2.2", "x86_64").should be == true
+ end
+
+ it "should return true if our package-version, no arch, is available" do
+ @yc.version_available?("zisofs-tools", "1.0.6-3.2.2", nil).should be == true
+ @yc.version_available?("zisofs-tools", "1.0.6-3.2.2").should be == true
+ end
+
+ it "should return false if our package-version-arch isn't available" do
+ @yc.version_available?("zisofs-tools", "1.0.6-3.2.2", "pretend").should be == false
+ @yc.version_available?("zisofs-tools", "pretend", "x86_64").should be == false
+ @yc.version_available?("pretend", "1.0.6-3.2.2", "x86_64").should be == false
+ end
+
+ it "should return false if our package-version, no arch, isn't available" do
+ @yc.version_available?("zisofs-tools", "pretend", nil).should be == false
+ @yc.version_available?("zisofs-tools", "pretend").should be == false
+ @yc.version_available?("pretend", "1.0.6-3.2.2").should be == false
+ end
+ end
+
+ describe "reset" do
+ it "should empty the installed and available packages RPMDb" do
+ @yc.available_version("zip", "x86_64").should be == "2.31-2.el5"
+ @yc.installed_version("zip", "x86_64").should be == "2.31-2.el5"
+ @yc.reset
+ @yc.available_version("zip", "x86_64").should be == nil
+ @yc.installed_version("zip", "x86_64").should be == nil
+ end
+ end
+
+ describe "package_available?" do
+ it "should return true a package is available" do
+ @yc.package_available?("zisofs-tools").should be == true
+ @yc.package_available?("moo").should be == false
+ @yc.package_available?(nil).should be == false
+ end
+ end
+
+end
diff --git a/chef/spec/unit/provider/package/zypper_spec.rb b/chef/spec/unit/provider/package/zypper_spec.rb
index 5b6e9f93b5..03207343f9 100644
--- a/chef/spec/unit/provider/package/zypper_spec.rb
+++ b/chef/spec/unit/provider/package/zypper_spec.rb
@@ -34,6 +34,7 @@ describe Chef::Provider::Package::Zypper do
@stderr = StringIO.new
@stdout = StringIO.new
@pid = mock("PID")
+ @provider.stub!(:`).and_return("2.0")
end
describe "when loading the current package state" do
@@ -89,14 +90,12 @@ describe Chef::Provider::Package::Zypper do
end
describe "install_package" do
-
it "should run zypper install with the package name and version" do
@provider.should_receive(:run_command).with({
:command => "zypper -n --no-gpg-checks install -l emacs=1.0",
})
@provider.install_package("emacs", "1.0")
end
-
end
describe "upgrade_package" do
@@ -123,4 +122,37 @@ describe Chef::Provider::Package::Zypper do
@provider.purge_package("emacs", "1.0")
end
end
+
+ describe "on an older zypper" do
+ before(:each) do
+ @provider.stub!(:`).and_return("0.11.6")
+ end
+
+ describe "install_package" do
+ it "should run zypper install with the package name and version" do
+ @provider.should_receive(:run_command).with({
+ :command => "zypper install -y emacs"
+ })
+ @provider.install_package("emacs", "1.0")
+ end
+ end
+
+ describe "upgrade_package" do
+ it "should run zypper update with the package name and version" do
+ @provider.should_receive(:run_command).with({
+ :command => "zypper install -y emacs"
+ })
+ @provider.upgrade_package("emacs", "1.0")
+ end
+ end
+
+ describe "remove_package" do
+ it "should run zypper remove with the package name" do
+ @provider.should_receive(:run_command).with({
+ :command => "zypper remove -y emacs"
+ })
+ @provider.remove_package("emacs", "1.0")
+ end
+ end
+ end
end
diff --git a/chef/spec/unit/provider/service/systemd_service_spec.rb b/chef/spec/unit/provider/service/systemd_service_spec.rb
new file mode 100644
index 0000000000..f47e73b11e
--- /dev/null
+++ b/chef/spec/unit/provider/service/systemd_service_spec.rb
@@ -0,0 +1,226 @@
+#
+# Author:: Stephen Haynes (<sh@nomitor.com>)
+# Copyright:: Copyright (c) 2011 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::Service::Systemd do
+ before(:each) do
+ @node = Chef::Node.new
+ @run_context = Chef::RunContext.new(@node, {})
+ @new_resource = Chef::Resource::Service.new('rsyslog.service')
+ @provider = Chef::Provider::Service::Systemd.new(@new_resource, @run_context)
+ end
+
+ describe "load_current_resource" do
+ before(:each) do
+ @current_resource = Chef::Resource::Service.new('rsyslog.service')
+ Chef::Resource::Service.stub!(:new).and_return(@current_resource)
+
+ @provider.stub!(:is_active?).and_return(false)
+ @provider.stub!(:is_enabled?).and_return(false)
+ end
+
+ it "should create a current resource with the name of the new resource" do
+ Chef::Resource::Service.should_receive(:new).and_return(@current_resource)
+ @provider.load_current_resource
+ end
+
+ it "should set the current resources service name to the new resources service name" do
+ @current_resource.should_receive(:service_name).with(@new_resource.service_name)
+ @provider.load_current_resource
+ end
+
+ it "should check if the service is running" do
+ @provider.should_receive(:is_active?)
+ @provider.load_current_resource
+ end
+
+ it "should set running to true if the service is running" do
+ @provider.stub!(:is_active?).and_return(true)
+ @current_resource.should_receive(:running).with(true)
+ @provider.load_current_resource
+ end
+
+ it "should set running to false if the service is not running" do
+ @provider.stub!(:is_active?).and_return(false)
+ @current_resource.should_receive(:running).with(false)
+ @provider.load_current_resource
+ end
+
+ describe "when a status command has been specified" do
+ before do
+ @new_resource.stub!(:status_command).and_return("/bin/chefhasmonkeypants status")
+ end
+
+ it "should run the services status command if one has been specified" do
+ @provider.stub!(:run_command_with_systems_locale).with({:command => "/bin/chefhasmonkeypants status"}).and_return(0)
+ @current_resource.should_receive(:running).with(true)
+ @provider.load_current_resource
+ end
+
+ it "should set running to false if it catches a Chef::Exceptions::Exec when using a status command" do
+ @provider.stub!(:run_command_with_systems_locale).and_raise(Chef::Exceptions::Exec)
+ @current_resource.should_receive(:running).with(false)
+ @provider.load_current_resource
+ end
+ end
+
+ it "should check if the service is enabled" do
+ @provider.should_receive(:is_enabled?)
+ @provider.load_current_resource
+ end
+
+ it "should set enabled to true if the service is enabled" do
+ @provider.stub!(:is_enabled?).and_return(true)
+ @current_resource.should_receive(:enabled).with(true)
+ @provider.load_current_resource
+ end
+
+ it "should set enabled to false if the service is not enabled" do
+ @provider.stub!(:is_enabled?).and_return(false)
+ @current_resource.should_receive(:enabled).with(false)
+ @provider.load_current_resource
+ end
+
+ it "should return the current resource" do
+ @provider.load_current_resource.should eql(@current_resource)
+ end
+ end
+
+ describe "start and stop service" do
+ before(:each) do
+ @current_resource = Chef::Resource::Service.new('rsyslog.service')
+ Chef::Resource::Service.stub!(:new).and_return(@current_resource)
+ @provider.current_resource = @current_resource
+ end
+
+ it "should call the start command if one is specified" do
+ @new_resource.stub!(:start_command).and_return("/sbin/rsyslog startyousillysally")
+ @provider.should_receive(:run_command).with({:command => "/sbin/rsyslog startyousillysally"}).and_return(0)
+ @provider.start_service
+ end
+
+ it "should call '/bin/systemctl start service_name' if no start command is specified" do
+ @provider.should_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl start #{@new_resource.service_name}"}).and_return(0)
+ @provider.start_service
+ end
+
+ it "should not call '/bin/systemctl start service_name' if it is already running" do
+ @current_resource.stub!(:running).and_return(true)
+ @provider.should_not_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl start #{@new_resource.service_name}"}).and_return(0)
+ @provider.start_service
+ end
+
+ it "should call the restart command if one is specified" do
+ @current_resource.stub!(:running).and_return(true)
+ @new_resource.stub!(:restart_command).and_return("/sbin/rsyslog restartyousillysally")
+ @provider.should_receive(:run_command).with({:command => "/sbin/rsyslog restartyousillysally"}).and_return(0)
+ @provider.restart_service
+ end
+
+ it "should call '/bin/systemctl restart service_name' if no restart command is specified" do
+ @current_resource.stub!(:running).and_return(true)
+ @provider.should_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl restart #{@new_resource.service_name}"}).and_return(0)
+ @provider.restart_service
+ end
+
+ it "should call the reload command if one is specified" do
+ @current_resource.stub!(:running).and_return(true)
+ @new_resource.stub!(:reload_command).and_return("/sbin/rsyslog reloadyousillysally")
+ @provider.should_receive(:run_command).with({:command => "/sbin/rsyslog reloadyousillysally"}).and_return(0)
+ @provider.reload_service
+ end
+
+ it "should call '/bin/systemctl reload service_name' if no reload command is specified" do
+ @current_resource.stub!(:running).and_return(true)
+ @provider.should_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl reload #{@new_resource.service_name}"}).and_return(0)
+ @provider.reload_service
+ end
+
+ it "should call the stop command if one is specified" do
+ @current_resource.stub!(:running).and_return(true)
+ @new_resource.stub!(:stop_command).and_return("/sbin/rsyslog stopyousillysally")
+ @provider.should_receive(:run_command).with({:command => "/sbin/rsyslog stopyousillysally"}).and_return(0)
+ @provider.stop_service
+ end
+
+ it "should call '/bin/systemctl stop service_name' if no stop command is specified" do
+ @current_resource.stub!(:running).and_return(true)
+ @provider.should_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl stop #{@new_resource.service_name}"}).and_return(0)
+ @provider.stop_service
+ end
+
+ it "should not call '/bin/systemctl stop service_name' if it is already stopped" do
+ @current_resource.stub!(:running).and_return(false)
+ @provider.should_not_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl stop #{@new_resource.service_name}"}).and_return(0)
+ @provider.stop_service
+ end
+ end
+
+ describe "enable and disable service" do
+ before(:each) do
+ @current_resource = Chef::Resource::Service.new('rsyslog.service')
+ Chef::Resource::Service.stub!(:new).and_return(@current_resource)
+ @provider.current_resource = @current_resource
+ end
+
+ it "should call '/bin/systemctl enable service_name' to enable the service" do
+ @provider.should_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl enable #{@new_resource.service_name}"}).and_return(0)
+ @provider.enable_service
+ end
+
+ it "should call '/bin/systemctl disable service_name' to disable the service" do
+ @provider.should_receive(:run_command_with_systems_locale).with({:command => "/bin/systemctl disable #{@new_resource.service_name}"}).and_return(0)
+ @provider.disable_service
+ end
+ end
+
+ describe "is_active?" do
+ before(:each) do
+ @current_resource = Chef::Resource::Service.new('rsyslog.service')
+ Chef::Resource::Service.stub!(:new).and_return(@current_resource)
+ end
+
+ it "should return true if '/bin/systemctl is-active service_name' returns 0" do
+ @provider.should_receive(:run_command_with_systems_locale).with({:command => '/bin/systemctl is-active rsyslog.service', :ignore_failure => true}).and_return(0)
+ @provider.is_active?.should be_true
+ end
+
+ it "should return false if '/bin/systemctl is-active service_name' returns anything except 0" do
+ @provider.should_receive(:run_command_with_systems_locale).with({:command => '/bin/systemctl is-active rsyslog.service', :ignore_failure => true}).and_return(1)
+ @provider.is_active?.should be_false
+ end
+ end
+
+ describe "is_enabled?" do
+ before(:each) do
+ @current_resource = Chef::Resource::Service.new('rsyslog.service')
+ Chef::Resource::Service.stub!(:new).and_return(@current_resource)
+ end
+
+ it "should return true if '/bin/systemctl is-enabled service_name' returns 0" do
+ @provider.should_receive(:run_command_with_systems_locale).with({:command => '/bin/systemctl is-enabled rsyslog.service', :ignore_failure => true}).and_return(0)
+ @provider.is_enabled?.should be_true
+ end
+
+ it "should return false if '/bin/systemctl is-enabled service_name' returns anything except 0" do
+ @provider.should_receive(:run_command_with_systems_locale).with({:command => '/bin/systemctl is-enabled rsyslog.service', :ignore_failure => true}).and_return(1)
+ @provider.is_enabled?.should be_false
+ end
+ end
+end
diff --git a/chef/spec/unit/provider/service/upstart_service_spec.rb b/chef/spec/unit/provider/service/upstart_service_spec.rb
index 2e1cd971e0..82e8c58e49 100644
--- a/chef/spec/unit/provider/service/upstart_service_spec.rb
+++ b/chef/spec/unit/provider/service/upstart_service_spec.rb
@@ -242,6 +242,12 @@ describe Chef::Provider::Service::Upstart do
@provider.restart_service()
end
+ it "should call '/sbin/start service_name' if restart_service is called for a stopped service" do
+ @current_resource.stub!(:running).and_return(false)
+ @provider.should_receive(:run_command_with_systems_locale).with({:command => "/sbin/start #{@new_resource.service_name}"}).and_return(0)
+ @provider.restart_service()
+ end
+
it "should call the reload command if one is specified" do
@current_resource.stub!(:running).and_return(true)
@new_resource.stub!(:reload_command).and_return("/sbin/rsyslog reloadyousillysally")
diff --git a/chef/spec/unit/resource/yum_package_spec.rb b/chef/spec/unit/resource/yum_package_spec.rb
index a414b6830f..ec01eb1fb3 100644
--- a/chef/spec/unit/resource/yum_package_spec.rb
+++ b/chef/spec/unit/resource/yum_package_spec.rb
@@ -47,3 +47,39 @@ describe Chef::Resource::YumPackage, "arch" do
@resource.arch.should eql("i386")
end
end
+
+describe Chef::Resource::YumPackage, "flush_cache" do
+ before(:each) do
+ @resource = Chef::Resource::YumPackage.new("foo")
+ end
+
+ it "should default the flush timing to false" do
+ flush_hash = { :before => false, :after => false }
+ @resource.flush_cache.should == flush_hash
+ end
+
+ it "should allow you to set the flush timing with an array" do
+ flush_array = [ :before, :after ]
+ flush_hash = { :before => true, :after => true }
+ @resource.flush_cache(flush_array)
+ @resource.flush_cache.should == flush_hash
+ end
+
+ it "should allow you to set the flush timing with a hash" do
+ flush_hash = { :before => true, :after => true }
+ @resource.flush_cache(flush_hash)
+ @resource.flush_cache.should == flush_hash
+ end
+end
+
+describe Chef::Resource::YumPackage, "allow_downgrade" do
+ before(:each) do
+ @resource = Chef::Resource::YumPackage.new("foo")
+ end
+
+ it "should allow you to specify whether allow_downgrade is true or false" do
+ lambda { @resource.allow_downgrade true }.should_not raise_error(ArgumentError)
+ lambda { @resource.allow_downgrade false }.should_not raise_error(ArgumentError)
+ lambda { @resource.allow_downgrade "monkey" }.should raise_error(ArgumentError)
+ end
+end
diff --git a/chef/spec/unit/resource_spec.rb b/chef/spec/unit/resource_spec.rb
index 00496d7d71..a341624278 100644
--- a/chef/spec/unit/resource_spec.rb
+++ b/chef/spec/unit/resource_spec.rb
@@ -193,7 +193,7 @@ describe Chef::Resource do
:updated, :updated_by_last_action, :before, :not_if, :supports,
:delayed_notifications, :immediate_notifications, :noop,
:ignore_failure, :name, :source_line, :action,
- :not_if_args, :only_if_args
+ :not_if_args, :only_if_args, :retries, :retry_delay
]
(hash.keys - expected_keys).should == []
(expected_keys - hash.keys).should == []
@@ -238,6 +238,26 @@ describe Chef::Resource do
end
end
+ describe "retries" do
+ it "should default to not retrying if a provider fails for a resource" do
+ @resource.retries.should == 0
+ end
+
+ it "should allow you to set how many retries a provider should attempt after a failure" do
+ @resource.retries(2)
+ @resource.retries.should == 2
+ end
+
+ it "should default to a retry delay of 2 seconds" do
+ @resource.retry_delay.should == 2
+ end
+
+ it "should allow you to set the retry delay" do
+ @resource.retry_delay(10)
+ @resource.retry_delay.should == 10
+ end
+ end
+
describe "setting the base provider class for the resource" do
it "defaults to Chef::Provider for the base class" do
diff --git a/chef/spec/unit/runner_spec.rb b/chef/spec/unit/runner_spec.rb
index 56e70e6990..a6051db2e7 100644
--- a/chef/spec/unit/runner_spec.rb
+++ b/chef/spec/unit/runner_spec.rb
@@ -6,9 +6,9 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -109,6 +109,15 @@ describe Chef::Runner do
lambda { @runner.converge }.should_not raise_error(ArgumentError)
end
+ it "should retry with the specified delay if retries are specified" do
+ @first_resource.retries 3
+ provider = Chef::Provider::SnakeOil.new(@run_context.resource_collection[0], @run_context)
+ Chef::Provider::SnakeOil.stub!(:new).once.and_return(provider)
+ provider.stub!(:action_sell).and_raise(ArgumentError)
+ @runner.should_receive(:sleep).with(2).exactly(3).times
+ lambda { @runner.converge }.should raise_error(ArgumentError)
+ end
+
it "should execute immediate actions on changed resources" do
notifying_resource = Chef::Resource::Cat.new("peanut", @run_context)
notifying_resource.action = :purr # only action that will set updated on the resource
@@ -144,7 +153,7 @@ describe Chef::Runner do
end
it "should execute delayed actions on changed resources" do
- @first_resource.action = :nothing
+ @first_resource.action = :nothing
second_resource = Chef::Resource::Cat.new("peanut", @run_context)
second_resource.action = :purr
diff --git a/features/chef-client/run_interval.feature b/features/chef-client/run_interval.feature
index db767c94d7..54046ceec6 100644
--- a/features/chef-client/run_interval.feature
+++ b/features/chef-client/run_interval.feature
@@ -10,3 +10,20 @@ Feature: Run chef-client at periodic intervals
When I run the chef-client with '-l info -i 1' for '12' seconds
Then 'INFO: Starting Chef Run' should appear on 'stdout' '2' times
+ Scenario: Run a background client for a few seconds
+ Given a validated node
+ And it includes the recipe 'run_interval'
+ When I run the chef-client in the background with '-l info -i 2'
+ And I stop the background chef-client after '10' seconds
+ Then the background chef-client should not be running
+ And 'INFO: Starting Chef Run' should appear on 'stdout' '2' times
+
+ Scenario: Run a background client with the sync_library cookbook, update sync_library between intervals and ensure updated library is run
+ Given I have restored the original 'sync_library' cookbook
+ And a validated node
+ And it includes the recipe 'sync_library'
+ When I run the chef-client in the background with '-l info -i 2'
+ And I update cookbook 'sync_library' from 'sync_library_updated' after the first run
+ And I stop the background chef-client after '10' seconds
+ Then 'INFO: First generation library' should appear on 'stdout' '1' times
+ And 'INFO: Second generation library' should appear on 'stdout' '1' times
diff --git a/features/data/cookbooks/scm/metadata.rb b/features/data/cookbooks/scm/metadata.rb
index 31aa11370f..b9961a7123 100644
--- a/features/data/cookbooks/scm/metadata.rb
+++ b/features/data/cookbooks/scm/metadata.rb
@@ -6,3 +6,4 @@ license 'Apache v2.0'
long_description "SCM integration/acceptance test recipes"
recipe "scm::git", "git awesome"
+recipe "scm::git-remotes", "git awesome repos"
diff --git a/features/data/cookbooks/sync_library_original/README.rdoc b/features/data/cookbooks/sync_library_original/README.rdoc
new file mode 100644
index 0000000000..8d774805b9
--- /dev/null
+++ b/features/data/cookbooks/sync_library_original/README.rdoc
@@ -0,0 +1,8 @@
+= DESCRIPTION:
+
+= REQUIREMENTS:
+
+= ATTRIBUTES:
+
+= USAGE:
+
diff --git a/features/data/cookbooks/sync_library_original/libraries/sync_library.rb b/features/data/cookbooks/sync_library_original/libraries/sync_library.rb
new file mode 100644
index 0000000000..659529670b
--- /dev/null
+++ b/features/data/cookbooks/sync_library_original/libraries/sync_library.rb
@@ -0,0 +1,27 @@
+
+require 'chef/index_queue/amqp_client'
+$sync_library_go_count ||= 0
+module SyncLibrary
+
+ def go
+ Chef::Log.info('First generation library')
+
+ # Publish the first run
+ $sync_library_go_count += 1
+ if $sync_library_go_count < 2
+ amqp = Chef::IndexQueue::AmqpClient.instance
+ queue = amqp.amqp_client.queue('sync_library_test')
+ queue.publish("first run complete")
+
+ # Wait until the message is consumed / the sync_library cookbook is updated
+ mcount = 1
+ while mcount > 0
+ Chef::Log.info("Sleeping while message is being consumed")
+ sleep 1
+ mcount = queue.message_count
+ end
+ end
+
+ end
+
+end \ No newline at end of file
diff --git a/features/data/cookbooks/sync_library_original/metadata.rb b/features/data/cookbooks/sync_library_original/metadata.rb
new file mode 100644
index 0000000000..0191f053a1
--- /dev/null
+++ b/features/data/cookbooks/sync_library_original/metadata.rb
@@ -0,0 +1,6 @@
+maintainer "Opscode"
+maintainer_email "do_not_reply@opscode.com"
+license "Apache 2.0"
+description "Installs/Configures sync_library"
+long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))
+version "0.1"
diff --git a/features/data/cookbooks/sync_library_original/recipes/default.rb b/features/data/cookbooks/sync_library_original/recipes/default.rb
new file mode 100644
index 0000000000..bb77f98632
--- /dev/null
+++ b/features/data/cookbooks/sync_library_original/recipes/default.rb
@@ -0,0 +1,30 @@
+#
+# Cookbook Name:: attribute_settings
+# 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.
+#
+
+class Chef::Recipe
+ include SyncLibrary
+end
+
+# Run the library code
+go
+
+$sync_library_global ||= 2
+$sync_library_global -= 1
+exit(2) if $sync_library_global == 0
+
diff --git a/features/data/cookbooks/sync_library_updated/libraries/sync_library.rb b/features/data/cookbooks/sync_library_updated/libraries/sync_library.rb
new file mode 100644
index 0000000000..4a28bec485
--- /dev/null
+++ b/features/data/cookbooks/sync_library_updated/libraries/sync_library.rb
@@ -0,0 +1,8 @@
+
+module SyncLibrary
+
+ def go
+ Chef::Log.info('Second generation library')
+ end
+
+end \ No newline at end of file
diff --git a/features/provider/scm/git.feature b/features/provider/scm/git.feature
index 29f73d85e4..f0ff2a1e04 100644
--- a/features/provider/scm/git.feature
+++ b/features/provider/scm/git.feature
@@ -13,6 +13,19 @@ Feature: Git
Then the run should exit '0'
And a file named 'gitchef/.git' should exist
And a file named 'gitchef/what_revision_am_i' should exist
-
-
-
+
+ Scenario: Clone a git repo with additional repositories
+ Given a test git repo in the temp directory
+ And a validated node
+ And it includes the recipe 'scm::git-remotes'
+ When I run the chef-client
+ Then the run should exit '0'
+ And a remote repository named 'hi' should exist in 'gitchef2'
+ And a remote repository named 'lo' should exist in 'gitchef2'
+ And a remote repository named 'waugh' should exist in 'gitchef2'
+ When I remove the remote repository named 'lo' from 'gitchef2'
+ And I run the chef-client again
+ Then the run should exit '0'
+ And a remote repository named 'hi' should exist in 'gitchef2'
+ And a remote repository named 'lo' should exist in 'gitchef2'
+ And a remote repository named 'waugh' should exist in 'gitchef2'
diff --git a/features/steps/cookbook_steps.rb b/features/steps/cookbook_steps.rb
index b97f970b44..fadfc354a4 100644
--- a/features/steps/cookbook_steps.rb
+++ b/features/steps/cookbook_steps.rb
@@ -84,6 +84,16 @@ Given /^I delete the cookbook's on disk checksum files$/ do
end
end
+Given /^I have restored the original 'sync_library' cookbook$/ do
+ # Copy the original cookbook
+ source = File.join(datadir, 'cookbooks', 'sync_library_original')
+ dest = File.join(datadir, 'cookbooks', 'sync_library')
+ FileUtils.mkdir_p(dest)
+
+ system("cp -r #{source}/. #{dest}/.")
+ shell_out!("#{KNIFE_CMD} cookbook upload -c #{KNIFE_CONFIG} -o #{INTEGRATION_COOKBOOKS} sync_library")
+end
+
When /^I run the task to generate cookbook metadata for '(.+)'$/ do |cb|
self.cookbook = cb
When('I run the task to generate cookbook metadata')
diff --git a/features/steps/deploy_steps.rb b/features/steps/deploy_steps.rb
index 65cac729a9..cdedf6534a 100644
--- a/features/steps/deploy_steps.rb
+++ b/features/steps/deploy_steps.rb
@@ -1,5 +1,6 @@
require 'chef/shell_out'
-
+require 'chef/mixin/shell_out'
+include Chef::Mixin::ShellOut
# Given /^I have a clone of typo in the data\/tmp dir$/ do
# cmd = "git clone #{datadir}/typo.bundle #{tmpdir}/gitrepo/typo"
@@ -28,6 +29,10 @@ Given /^a test git repo in the temp directory$/ do
cmd.run_command.exitstatus.should == 0
end
+When /^I remove the remote repository named '(.+)' from '(.+)'$/ do |remote_name, repository_dir|
+ shell_out!("git remote rm #{remote_name}", Hash[:cwd => File.join(tmpdir, repository_dir)])
+end
+
Then /^I should hear about it$/ do
puts "==deploy:"
puts `ls #{tmpdir}/deploy/`
@@ -81,4 +86,8 @@ Then /^the second chef run should have skipped deployment$/ do
expected_deploy = "#{tmpdir}/deploy/releases/62c9979f6694612d9659259f8a68d71048ae9a5b"
Then "'stdout' should not have 'INFO: Already deployed app at #{expected_deploy}. Rolling back to it - use action :force_deploy to re-checkout this revision.'"
end
-
+
+Then /^a remote repository named '(.*)' should exist in '(.*)'$/ do |remote_name, repository_dir|
+ remotes = shell_out!('git remote', Hash[:cwd => File.join(tmpdir, repository_dir)]).stdout.split(/\s/)
+ remotes.should include remote_name
+end
diff --git a/features/steps/run_client_steps.rb b/features/steps/run_client_steps.rb
index 0ecf85ec00..2bc517605e 100644
--- a/features/steps/run_client_steps.rb
+++ b/features/steps/run_client_steps.rb
@@ -18,6 +18,7 @@
require 'chef/shell_out'
require 'chef/mixin/shell_out'
+require 'chef/index_queue/amqp_client'
include Chef::Mixin::ShellOut
@@ -42,6 +43,38 @@ When /^I run the chef\-client$/ do
@status = status
end
+When /^I run the chef\-client in the background with '(.+)'$/ do |args|
+ @stdout_filename = "/tmp/chef.run_interval.stdout.#{$$}.txt"
+ @stderr_filename = "/tmp/chef.run_interval.stderr.#{$$}.txt"
+
+ @chef_args = "#{args}"
+ @client_pid = Process.fork do
+ STDOUT.reopen(File.open(@stdout_filename, "w"))
+ STDERR.reopen(File.open(@stderr_filename, "w"))
+ exec chef_client_command_string()
+ exit 2
+ end
+end
+
+When /^I stop the background chef\-client after '(\d+)' seconds$/ do |timeout|
+ begin
+ sleep timeout.to_i
+ Process.kill("KILL", @client_pid)
+ rescue Errno::ESRCH
+ # Kill didn't work; the process exited while we were waiting, like
+ # it's supposed to.
+ end
+
+ # Read these in so they can be used in later steps.
+ @stdout = IO.read(@stdout_filename)
+ @stderr = IO.read(@stderr_filename)
+end
+
+Then /^the background chef\-client should not be running$/ do
+ system("ps -af | grep #{@client_pid} | grep -vq grep")
+ $?.exitstatus.should == 0
+end
+
When "I run the chef-client for no more than '$timeout' seconds" do |timeout|
cmd = shell_out("#{CHEF_CLIENT} -l info -i 1 -s 1 -c #{File.expand_path(File.join(configdir, 'client.rb'))}", :timeout => timeout.to_i)
@status = cmd.status
@@ -167,6 +200,30 @@ CONFIG
end
end
+When /^I update cookbook 'sync_library' from 'sync_library_updated' after the first run$/ do
+ amqp = Chef::IndexQueue::AmqpClient.instance
+ queue = amqp.amqp_client.queue("sync_library_test")
+ queue.subscribe(:timeout => 10) do |message|
+ if "first run complete" == message[:payload]
+
+ # Copy the updated library file over
+ source = File.join(datadir, 'cookbooks', 'sync_library_updated')
+ dest = File.join(datadir, 'cookbooks', 'sync_library')
+ cmd = "cp -r #{source}/. #{dest}/."
+ system(cmd)
+
+ # Upload the updated cookbook
+ knife_cmd = "#{KNIFE_CMD} cookbook upload -c #{KNIFE_CONFIG} -o #{INTEGRATION_COOKBOOKS} sync_library"
+ shell_out!(knife_cmd)
+
+ # Ack to release the client
+ queue.delivery_tag = message[:delivery_details][:delivery_tag]
+ queue.ack
+ break
+ end
+ end
+end
+
###
# Then
###