summaryrefslogtreecommitdiff
path: root/chef
diff options
context:
space:
mode:
authorAdam Jacob <adam@hjksolutions.com>2009-04-24 17:20:14 -0700
committerAdam Jacob <adam@hjksolutions.com>2009-04-24 17:20:14 -0700
commitd23d849e70355918be6d8915d981991249ca02d6 (patch)
tree63c2bdd2a9bdbfc6b46aa55aacb587ad0bcc42a7 /chef
parent61edf3887350274fa7204f0f51b64c59f25c51df (diff)
parentc47454eff94555c0337adec7d71ced64cc1a0051 (diff)
downloadchef-d23d849e70355918be6d8915d981991249ca02d6.tar.gz
Merge branch 'master' into ctennis/chef-230
Diffstat (limited to 'chef')
-rwxr-xr-xchef/bin/chef-client2
-rwxr-xr-xchef/bin/chef-solo2
-rw-r--r--chef/contrib/el/chef-client.config16
-rw-r--r--chef/contrib/el/chef-client.init74
-rw-r--r--chef/lib/chef/config.rb4
-rw-r--r--chef/lib/chef/couchdb.rb18
-rw-r--r--chef/lib/chef/daemon.rb4
-rw-r--r--chef/lib/chef/mixin/command.rb98
-rw-r--r--chef/lib/chef/provider/cron.rb8
-rw-r--r--chef/lib/chef/provider/package/yum-dump-json.py67
-rw-r--r--chef/lib/chef/provider/package/yum.rb127
-rw-r--r--chef/spec/unit/couchdb_spec.rb40
-rw-r--r--chef/spec/unit/mixin/command_spec.rb85
-rw-r--r--chef/spec/unit/mixin/template_spec.rb3
-rw-r--r--chef/spec/unit/provider/cron_spec.rb31
-rw-r--r--chef/spec/unit/provider/package/yum_spec.rb116
16 files changed, 425 insertions, 270 deletions
diff --git a/chef/bin/chef-client b/chef/bin/chef-client
index df06e068fd..1c1b38bd19 100755
--- a/chef/bin/chef-client
+++ b/chef/bin/chef-client
@@ -79,6 +79,8 @@ end
Chef::Config.from_file(config[:config_file])
Chef::Config.configure { |c| c.merge!(config) }
+Chef::Daemon.change_privilege
+
if Chef::Config[:daemonize]
unless Chef::Config[:log_location].is_a? IO
Chef::Log.init(Chef::Config[:log_location])
diff --git a/chef/bin/chef-solo b/chef/bin/chef-solo
index 5543faf9c3..ecc2c57f8a 100755
--- a/chef/bin/chef-solo
+++ b/chef/bin/chef-solo
@@ -34,7 +34,7 @@ config = {
Chef::Config[:solo] = true
opts = OptionParser.new do |opts|
- opts.banner = "Usage: #{$0} [-d DIR|-r FILE] (options)"
+ opts.banner = "Usage: #{$0} (options)"
opts.on("-c CONFIG", "--config CONFIG", "The Chef Config file to use") do |c|
config[:config_file] = c
end
diff --git a/chef/contrib/el/chef-client.config b/chef/contrib/el/chef-client.config
new file mode 100644
index 0000000000..5be5f35114
--- /dev/null
+++ b/chef/contrib/el/chef-client.config
@@ -0,0 +1,16 @@
+#
+# Chef Client Config File
+#
+
+log_level :info
+ssl_verify_mode :verify_none
+registration_url "http://127.0.0.1:4000"
+openid_url "http://127.0.0.1:4001"
+template_url "http://127.0.0.1:4000"
+remotefile_url "http://127.0.0.1:4000"
+search_url "http://127.0.0.1:4000"
+
+pid_file "/var/run/chef/chef-client.pid"
+
+#interval 1800
+#splay 0
diff --git a/chef/contrib/el/chef-client.init b/chef/contrib/el/chef-client.init
new file mode 100644
index 0000000000..332e9606d9
--- /dev/null
+++ b/chef/contrib/el/chef-client.init
@@ -0,0 +1,74 @@
+#!/bin/bash
+# Startup script for chef-client
+#
+# chkconfig: - 98 02
+# description: Client component of the Chef systems integration framework.
+# processname: chef-client
+#
+# config: /etc/sysconfig/chef-client
+# pidfile: /var/run/chef/chef-client.pid
+
+# Source function library
+. /etc/init.d/functions
+
+[ -f /etc/sysconfig/chef-client ] && . /etc/sysconfig/chef-client
+
+prog="chef-client"
+pidfile=${PIDFILE-/var/run/chef/chef-client.pid}
+lockfile=${LOCKFILE-/var/lock/subsys/$prog}
+config=${CONFIG-/etc/chef/client.rb}
+logfile=${LOGFILE-/var/log/chef/chef-client.log}
+OPTIONS=
+
+start() {
+ echo -n "Starting $prog:"
+ daemon chef-client -d -c "$config" -L "$logfile" "$OPTIONS" ">/dev/null"
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && touch ${lockfile}
+ return $RETVAL
+}
+
+stop() {
+ echo -n "Stopping $prog: "
+ if [ -f $pidfile ]; then
+ killproc chef-client
+ RETVAL=$?
+ if [ $RETVAL -ne 0 ]; then
+ failure;
+ fi;
+ else
+ RETVAL=1
+ failure;
+ fi
+ rm -f $lockfile
+ echo
+ return $RETVAL
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ condrestart)
+ if [ -f $lockfile ]; then
+ stop
+ start
+ fi
+ ;;
+ status)
+ status chef-client
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|condrestart|status}"
+ exit 1
+esac
+
+exit $RETVAL
diff --git a/chef/lib/chef/config.rb b/chef/lib/chef/config.rb
index 9c33ccff53..f9fda13cf0 100644
--- a/chef/lib/chef/config.rb
+++ b/chef/lib/chef/config.rb
@@ -41,8 +41,6 @@ class Chef
:interval => nil,
:splay => nil,
:solo => false,
- :user => nil,
- :group => nil,
:json_attribs => nil,
:cookbook_path => [ "/var/chef/site-cookbooks", "/var/chef/cookbooks" ],
:validation_token => nil,
@@ -62,8 +60,8 @@ class Chef
:template_url => "http://localhost:4000",
:remotefile_url => "http://localhost:4000",
:search_url => "http://localhost:4000",
- :couchdb_database => "chef",
:couchdb_version => nil,
+ :couchdb_database => "chef",
:openid_store_couchdb => false,
:openid_cstore_couchdb => false,
:openid_store_path => "/var/chef/openid/db",
diff --git a/chef/lib/chef/couchdb.rb b/chef/lib/chef/couchdb.rb
index 2c488b2ec2..a374205021 100644
--- a/chef/lib/chef/couchdb.rb
+++ b/chef/lib/chef/couchdb.rb
@@ -25,11 +25,10 @@ require 'json'
class Chef
class CouchDB
include Chef::Mixin::ParamsValidate
-
+
def initialize(url=nil)
url ||= Chef::Config[:couchdb_url]
@rest = Chef::REST.new(url)
- Chef::Config[:couchdb_version] ||= @rest.run_request(:GET, URI.parse(@rest.url + "/"), false, 10, false)["version"].gsub(/-.+/,"").to_f
end
def create_db
@@ -145,13 +144,8 @@ class Chef
end
end
- private
-
- def safe_name(name)
- name.gsub(/\./, "_")
- end
-
def view_uri(design, view)
+ Chef::Config[:couchdb_version] ||= @rest.run_request(:GET, URI.parse(@rest.url + "/"), false, 10, false)["version"].gsub(/-.+/,"").to_f
case Chef::Config[:couchdb_version]
when 0.9
"#{Chef::Config[:couchdb_database]}/_design/#{design}/_view/#{view}"
@@ -159,6 +153,12 @@ class Chef
"#{Chef::Config[:couchdb_database]}/_view/#{design}/#{view}"
end
end
-
+
+ private
+
+ def safe_name(name)
+ name.gsub(/\./, "_")
+ end
+
end
end
diff --git a/chef/lib/chef/daemon.rb b/chef/lib/chef/daemon.rb
index 7ab66ab710..2b6e6db3ea 100644
--- a/chef/lib/chef/daemon.rb
+++ b/chef/lib/chef/daemon.rb
@@ -40,12 +40,12 @@ class Chef
exit if fork
Process.setsid
exit if fork
- change_privilege
Chef::Log.info("Forked, in #{Process.pid}. Priveleges: #{Process.euid} #{Process.egid}")
File.umask 0000
$stdin.reopen("/dev/null")
$stdout.reopen("/dev/null", "a")
$stdout.reopen($stdout)
+ save_pid_file
at_exit { remove_pid_file }
rescue NotImplementedError => e
Chef.fatal!("There is no fork: #{e.message}")
@@ -167,4 +167,4 @@ class Chef
end
end
end
-end \ No newline at end of file
+end
diff --git a/chef/lib/chef/mixin/command.rb b/chef/lib/chef/mixin/command.rb
index d787059548..f0c76b09e9 100644
--- a/chef/lib/chef/mixin/command.rb
+++ b/chef/lib/chef/mixin/command.rb
@@ -113,35 +113,11 @@ class Chef
end
exec_processing_block = lambda do |pid, stdin, stdout, stderr|
- stdout.sync = true
- stderr.sync = true
-
Chef::Log.debug("---- Begin output of #{args[:command]} ----")
-
- stdout_finished = false
- stderr_finished = false
-
- while !stdout_finished || !stderr_finished
- ready = IO.select([stdout, stderr], nil, nil, 1.0)
- if ready && ready.first.include?(stdout)
- line = stdout.gets
- if line
- command_output << "STDOUT: #{line.strip}\n"
- Chef::Log.debug("STDOUT: #{line.strip}")
- else
- stdout_finished = true
- end
- end
- if ready && ready.first.include?(stderr)
- line = stderr.gets
- if line
- command_output << "STDERR: #{line.strip}\n"
- Chef::Log.debug("STDERR: #{line.strip}")
- else
- stderr_finished = true
- end
- end
- end
+ Chef::Log.debug("STDOUT: #{stdout.string.chomp!}")
+ Chef::Log.debug("STDERR: #{stderr.string.chomp!}")
+ command_output << "STDOUT: #{stdout.string.chomp!}"
+ command_output << "STDERR: #{stderr.string.chomp!}"
Chef::Log.debug("---- End output of #{args[:command]} ----")
end
@@ -154,10 +130,6 @@ class Chef
status = nil
- # I don't understand what this :waitlast argument is doing, but setting it to true is causing the block in popen4
- # not to wait until the command is finished to execute, kind of the opposite of what I would guess from the name
- args[:waitlast] ||= true
-
Dir.chdir(args[:cwd]) do
if args[:timeout]
begin
@@ -200,8 +172,13 @@ class Chef
#
# Thanks Ara!
def popen4(cmd, args={}, &b)
-
+
# Waitlast - this is magic.
+ #
+ # Do we wait for the child process to die before we yield
+ # to the block, or after? That is the magic of waitlast.
+ #
+ # By default, we are waiting before we yield the block.
args[:waitlast] ||= false
args[:user] ||= nil
@@ -298,11 +275,58 @@ class Chef
# wants to do must be done - it's dead. If it isn't,
# it's because something totally skanky is happening,
# and we don't care.
+ o = StringIO.new
+ e = StringIO.new
+
pi[0].close
- pi[1].fcntl(Fcntl::F_SETFL, pi[1].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
- pi[2].fcntl(Fcntl::F_SETFL, pi[2].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
- results = Process.waitpid2(cid).last
- b[cid, *pi]
+
+ stdout = pi[1]
+ stderr = pi[2]
+
+ stdout.sync = true
+ stderr.sync = true
+
+ stdout.fcntl(Fcntl::F_SETFL, pi[1].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
+ stderr.fcntl(Fcntl::F_SETFL, pi[2].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
+
+
+ stdout_finished = false
+ stderr_finished = false
+
+ results = nil
+
+ while !stdout_finished || !stderr_finished
+ begin
+ ready = IO.select([stdout, stderr], nil, nil, 1.0)
+ rescue Errno::EAGAIN
+ results = Process.waitpid2(cid, Process::WNOHANG)
+ if results
+ stdout_finished = true
+ stderr_finished = true
+ end
+ end
+
+ if ready && ready.first.include?(stdout)
+ line = results ? stdout.gets(nil) : stdout.gets
+ if line
+ o.write(line)
+ else
+ stdout_finished = true
+ end
+ end
+ if ready && ready.first.include?(stderr)
+ line = results ? stderr.gets(nil) : stderr.gets
+ if line
+ e.write(line)
+ else
+ stderr_finished = true
+ end
+ end
+ end
+ results = Process.waitpid2(cid).last unless results
+ o.rewind
+ e.rewind
+ b[cid, pi[0], o, e]
results
end
ensure
diff --git a/chef/lib/chef/provider/cron.rb b/chef/lib/chef/provider/cron.rb
index 88fd75e2d3..9e633053ce 100644
--- a/chef/lib/chef/provider/cron.rb
+++ b/chef/lib/chef/provider/cron.rb
@@ -64,7 +64,12 @@ class Chef
status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr|
stdout.each_line do |line|
if cron_found
- crontab << "#{@new_resource.minute} #{@new_resource.hour} #{@new_resource.day} #{@new_resource.month} #{@new_resource.weekday} #{@new_resource.command}\n"
+ cronline = "#{@new_resource.minute} #{@new_resource.hour} #{@new_resource.day} #{@new_resource.month} #{@new_resource.weekday} #{@new_resource.command}\n"
+ if (line == cronline)
+ Chef::Log.debug("Skipping existing cron entry '#{@new_resource.name}'")
+ return
+ end
+ crontab << cronline
cron_found = false
next
end
@@ -76,6 +81,7 @@ class Chef
end
end
+
status = popen4("crontab -u #{@new_resource.user} -", :waitlast => true) do |pid, stdin, stdout, stderr|
crontab.each { |line| stdin.puts "#{line}" }
stdin.close
diff --git a/chef/lib/chef/provider/package/yum-dump-json.py b/chef/lib/chef/provider/package/yum-dump-json.py
new file mode 100644
index 0000000000..115265e5aa
--- /dev/null
+++ b/chef/lib/chef/provider/package/yum-dump-json.py
@@ -0,0 +1,67 @@
+#
+# Author:: Matthew Kent (<mkent@magoazul.com>)
+# Copyright:: Copyright (c) 2009 Matthew Kent
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# yum-dump-json.py
+# Inspired by yumhelper.py by David Lutterkort
+#
+# Produce a list of installed and available packages using yum and dump the
+# result as json 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.
+#
+# Can be run as non root, but that won't update the cache.
+
+import os
+import sys
+import yum
+import json
+
+y = yum.YumBase()
+# Only want json in output
+y.doConfigSetup(debuglevel=0, errorlevel=0)
+
+# yum assumes it can update the cache directory. Disable this for non root
+# users.
+y.conf.cache = os.geteuid() != 0
+
+y.doTsSetup()
+y.doRpmDBSetup()
+
+db = y.doPackageLists('all')
+
+y.closeRpmDB()
+
+combined = {}
+
+for pkg in db.installed:
+ combined[pkg.name] = {}
+ combined[pkg.name]["installed"] = { "epoch": pkg.epoch,
+ "version": pkg.version,
+ "release": pkg.release,
+ "arch": pkg.arch }
+for pkg in db.available:
+ if not combined.has_key(pkg.name):
+ combined[pkg.name] = {}
+ combined[pkg.name]["available"] = { "epoch": pkg.epoch,
+ "version": pkg.version,
+ "release": pkg.release,
+ "arch": pkg.arch }
+print json.write( combined )
+
+sys.exit(0)
diff --git a/chef/lib/chef/provider/package/yum.rb b/chef/lib/chef/provider/package/yum.rb
index 7e4a6e96ba..5eab817f0e 100644
--- a/chef/lib/chef/provider/package/yum.rb
+++ b/chef/lib/chef/provider/package/yum.rb
@@ -19,63 +19,114 @@
require 'chef/provider/package'
require 'chef/mixin/command'
require 'chef/resource/package'
+require 'singleton'
class Chef
class Provider
class Package
class Yum < Chef::Provider::Package
+ class YumCache
+ include Chef::Mixin::Command
+ include Singleton
+
+ def initialize
+ @created_at = Time.now
+ load_data
+ end
+
+ def stale?
+ interval = Chef::Config[:interval].to_f
+
+ # run once mode
+ if interval == 0
+ return false
+ elsif (Time.now - @created_at) > interval
+ return true
+ end
+
+ false
+ end
+
+ def refresh
+ if @data.empty?
+ reload
+ elsif stale?
+ reload
+ end
+ end
+
+ def load_data
+ parsed = String.new
+ helper = ::File.join(::File.dirname(__FILE__), 'yum-dump-json.py')
+ status = popen4("python #{helper}", :waitlast => true) do |pid, stdin, stdout, stderr|
+ stdout.each do |line|
+ parsed << line
+ end
+ end
+
+ unless status.exitstatus == 0
+ raise Chef::Exceptions::Package, "yum failed - #{status.inspect}!"
+ end
+
+ @data = JSON.parse(parsed)
+ end
+ alias :reload :load_data
+
+ def version(package_name, type)
+ if (x = @data[package_name])
+ if (y = x[type])
+ return "#{y["version"]}-#{y["release"]}"
+ end
+ end
+
+ nil
+ end
+
+ def installed_version(package_name)
+ version(package_name, "installed")
+ end
+
+ def candidate_version(package_name)
+ version(package_name, "available")
+ end
+
+ def flush
+ @data.clear
+ end
+ end
+
+ def initialize(node, new_resource)
+ @yum = YumCache.instance
+ super(node, new_resource)
+ end
+
def load_current_resource
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
Chef::Log.debug("Checking yum info for #{@new_resource.package_name}")
- status = popen4("yum info -q -y #{@new_resource.package_name}") do |pid, stdin, stdout, stderr|
- package_type = nil
- installed_version = nil
- candidate_version = nil
- stdout.each do |line|
- case line
- when /^Installed Packages$/
- package_type = :installed
- when /^Available Packages$/
- package_type = :available
- when /^Version\s*: (.+)$/
- if package_type == :installed
- installed_version = $1
- elsif package_type == :available
- candidate_version = $1
- end
- when /^Release: (.+)$/
- if package_type == :installed
- installed_version += "-#{$1}"
- Chef::Log.debug("Installed release is #{installed_version}")
- elsif package_type == :available
- candidate_version += "-#{$1}"
- Chef::Log.debug("Candidate version is #{candidate_version}")
- end
- end
- end
-
- @current_resource.version(installed_version)
- if candidate_version
- @candidate_version = candidate_version
- else
- @candidate_version = installed_version
- end
- end
+
+ @yum.refresh
- unless status.exitstatus == 0
- raise Chef::Exceptions::Package, "yum failed - #{status.inspect}!"
+ installed_version = @yum.installed_version(@new_resource.package_name)
+ @candidate_version = @yum.candidate_version(@new_resource.package_name)
+
+ @current_resource.version(installed_version)
+ if candidate_version
+ @candidate_version = candidate_version
+ else
+ @candidate_version = installed_version
end
@current_resource
end
-
+
def install_package(name, version)
run_command(
:command => "yum -q -y install #{name}-#{version}"
)
+ @yum.flush
end
def upgrade_package(name, version)
@@ -84,6 +135,7 @@ class Chef
run_command(
:command => "yum -q -y update #{name}-#{version}"
)
+ @yum.flush
else
install_package(name, version)
end
@@ -93,6 +145,7 @@ class Chef
run_command(
:command => "yum -q -y remove #{name}-#{version}"
)
+ @yum.flush
end
def purge_package(name, version)
diff --git a/chef/spec/unit/couchdb_spec.rb b/chef/spec/unit/couchdb_spec.rb
index fe7a80235f..62ba85e33d 100644
--- a/chef/spec/unit/couchdb_spec.rb
+++ b/chef/spec/unit/couchdb_spec.rb
@@ -31,15 +31,11 @@ describe Chef::CouchDB, "new" do
Chef::REST.should_receive(:new).with("http://monkey")
Chef::CouchDB.new
end
-
+
it "should create a new Chef::REST object from a provided url" do
Chef::REST.should_receive(:new).with("http://monkeypants")
Chef::CouchDB.new("http://monkeypants")
end
-
- it "should parse the CouchDB version number" do
-
- end
end
describe Chef::CouchDB, "create_db" do
@@ -252,23 +248,27 @@ describe Chef::CouchDB, "has_key?" do
end
end
-describe Chef::CouchDB, "safe_name" do
+describe Chef::CouchDB, "view_uri" do
before do
- @couchdb = mock("Chef::CouchDB", :null_object => true)
- Chef::CouchDB.stub!(:new).and_return(@couchdb)
+ @mock_rest = mock("Chef::REST", :null_object => true, :url => "http://monkeypants")
+ Chef::REST.stub!(:new).and_return(@mock_rest)
+ @couchdb = Chef::CouchDB.new("http://localhost")
end
- it "should convert the name to a safe name" do
- @couchdb.should_receive(:safe_name).with("asdf.lol.com").and_return("asdf_lol_com")
- @couchdb.safe_name("asdf.lol.com")
- end
-end
-
-describe Chef::CouchDB, "view_uri" do
- before do
- @couchdb = mock("Chef::CouchDB", :null_object => true)
- Chef::CouchDB.stub!(:new).and_return(@couchdb)
- Chef::Config.stub!(:[]).with(:couchdb_database).and_return("chef")
+ describe "when the couchdb version is unknown" do
+ it "should set the couchdb version appropriately" do
+ ov = Chef::Config[:couchdb_version]
+ Chef::Config[:couchdb_version] = nil
+ @mock_rest.should_receive(:run_request).with(
+ :GET,
+ URI.parse("http://monkeypants/"),
+ false,
+ 10,
+ false
+ ).and_return({ "version" => "0.9" })
+ @couchdb.view_uri("nodes", "all")
+ Chef::Config[:couchdb_version] = ov
+ end
end
describe "on couchdb 0.8" do
@@ -292,4 +292,4 @@ describe Chef::CouchDB, "view_uri" do
@couchdb.view_uri("nodes", "all")
end
end
-end \ No newline at end of file
+end
diff --git a/chef/spec/unit/mixin/command_spec.rb b/chef/spec/unit/mixin/command_spec.rb
deleted file mode 100644
index d35712bab5..0000000000
--- a/chef/spec/unit/mixin/command_spec.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-#
-# Author:: Matthew Landauer (<matthew@openaustralia.org>)
-# Copyright:: Copyright (c) 2008 Matthew Landauer
-# 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::Mixin::Command do
- before :each do
- Chef::Log.init
- end
-
- # Reset the logger for other tests
- after :each do
- Chef::Log.init
- end
-
- it "should log the command's standard output at debug log level" do
- command = "ruby -e 'puts 5'"
- Chef::Log.should_receive(:debug).with("Executing #{command}").ordered
- Chef::Log.should_receive(:debug).with("---- Begin output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("STDOUT: 5").ordered
- Chef::Log.should_receive(:debug).with("---- End output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("Ran #{command} returned 0").ordered
- Chef::Mixin::Command.run_command(:command => command)
- end
-
- it "should log the command's standard error at debug log level" do
- command = "ruby -e 'STDERR.puts 5'"
- Chef::Log.should_receive(:debug).with("Executing #{command}").ordered
- Chef::Log.should_receive(:debug).with("---- Begin output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("STDERR: 5").ordered
- Chef::Log.should_receive(:debug).with("---- End output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("Ran #{command} returned 0").ordered
- Chef::Mixin::Command.run_command(:command => command)
- end
-
- it "should log the command's standard out and error at the same time" do
- command = "ruby -e 'STDERR.puts 1; puts 2; STDERR.puts 3; puts 4'"
- Chef::Log.should_receive(:debug).with("Executing #{command}").ordered
- Chef::Log.should_receive(:debug).with("---- Begin output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("STDERR: 1").ordered
- Chef::Log.should_receive(:debug).with("STDOUT: 2").ordered
- Chef::Log.should_receive(:debug).with("STDERR: 3").ordered
- Chef::Log.should_receive(:debug).with("STDOUT: 4").ordered
- Chef::Log.should_receive(:debug).with("---- End output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("Ran #{command} returned 0").ordered
- Chef::Mixin::Command.run_command(:command => command)
- end
-
- it "should throw an exception if the command returns a bad exit value" do
- command = "ruby -e 'puts 1; exit 1'"
- Chef::Log.level :debug
- # Stub out Chef::Log.debug to avoid messages going to console
- Chef::Log.stub!(:debug)
- lambda {Chef::Mixin::Command.run_command(:command => command)}.should raise_error(Chef::Exceptions::Exec, "#{command} returned 1, expected 0")
- end
-
- it "should include the command output in the exception if the log level is not at debug" do
- command = "ruby -e 'puts 1; exit 1'"
- Chef::Log.level :info
- lambda {Chef::Mixin::Command.run_command(:command => command)}.should raise_error(Chef::Exceptions::Exec, "#{command} returned 1, expected 0\n---- Begin output of #{command} ----\nSTDOUT: 1\n---- End output of #{command} ----\n")
- end
-
- it "should log the output as the command is executing" do
- command = "ruby -e 'STDOUT.sync = true; puts 1; sleep 2; puts 2'"
- Chef::Log.should_receive(:debug).with("Executing #{command}").ordered
- Chef::Log.should_receive(:debug).with("---- Begin output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("STDOUT: 1").ordered
- lambda {Chef::Mixin::Command.run_command(:command => command, :timeout => 1)}.should raise_error(Timeout::Error)
- end
-end
diff --git a/chef/spec/unit/mixin/template_spec.rb b/chef/spec/unit/mixin/template_spec.rb
index 86e0b59232..05fee6820a 100644
--- a/chef/spec/unit/mixin/template_spec.rb
+++ b/chef/spec/unit/mixin/template_spec.rb
@@ -31,7 +31,8 @@ describe Chef::Mixin::Template, "render_template" do
end
it "should return a file" do
- @template.render_template("abcdef", {}).should be_kind_of(File)
+ f = @template.render_template("abcdef", {})
+ @template.render_template("abcdef", {}).should be_kind_of(Tempfile)
end
describe "when an exception is raised in the template" do
diff --git a/chef/spec/unit/provider/cron_spec.rb b/chef/spec/unit/provider/cron_spec.rb
index d09d14b93c..eda942e14b 100644
--- a/chef/spec/unit/provider/cron_spec.rb
+++ b/chef/spec/unit/provider/cron_spec.rb
@@ -128,7 +128,7 @@ describe Chef::Provider::Cron, "action_create" do
@provider.action_create
end
- it "should update the cron entry if it exists" do
+ it "should update the cron entry if it exists and has changed" do
@status = mock("Status", :exitstatus => 0)
@stdin = mock("STDIN", :null_object => true)
@stdout = mock("STDOUT", :null_object => true)
@@ -142,7 +142,36 @@ describe Chef::Provider::Cron, "action_create" do
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
Chef::Log.should_receive(:info).with("Updated cron '#{@new_resource.name}'")
@provider.action_create
+ end
+
+ it "should not update the cron entry if it exists and has not changed" do
+ resource = mock("Chef::Resource::Cron",
+ :null_object => true,
+ :name => "foo",
+ :minute => "30",
+ :hour => "*",
+ :day => "*",
+ :month => "*",
+ :weekday => "*",
+ :command => "/bin/true"
+ )
+ provider = Chef::Provider::Cron.new(@node, resource)
+
+ @status = mock("Status", :exitstatus => 0)
+ @stdin = mock("STDIN", :null_object => true)
+ @stdout = mock("STDOUT", :null_object => true)
+ @stderr = mock("STDERR", :null_object => true)
+ @pid = mock("PID", :null_object => true)
+ @stdout.stub!(:each_line).and_yield("# Chef Name: bar").
+ and_yield("* 10 * * * /bin/false").
+ and_yield("# Chef Name: foo\n").
+ and_yield("30 * * * * /bin/true\n")
+ provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ Chef::Log.should_not_receive(:info).with("Updated cron '#{@new_resource.name}'")
+ Chef::Log.should_receive(:debug).with("Skipping existing cron entry '#{@new_resource.name}'")
+ provider.cron_exists = true
+ provider.action_create
end
end
diff --git a/chef/spec/unit/provider/package/yum_spec.rb b/chef/spec/unit/provider/package/yum_spec.rb
index 6a554c812f..2e8ddb9971 100644
--- a/chef/spec/unit/provider/package/yum_spec.rb
+++ b/chef/spec/unit/provider/package/yum_spec.rb
@@ -36,55 +36,16 @@ describe Chef::Provider::Package::Yum, "load_current_resource" do
:updated => nil
)
@status = mock("Status", :exitstatus => 0)
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :refresh => true,
+ :flush => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5_2.3"
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
@provider = Chef::Provider::Package::Yum.new(@node, @new_resource)
Chef::Resource::Package.stub!(:new).and_return(@current_resource)
- @provider.stub!(:popen4).and_return(@status)
- @stdin = mock("STDIN", :null_object => true)
- @stdout = mock("STDOUT", :null_object => true)
- @stdout.stub!(:each).and_yield("Installed Packages").
- and_yield("Name : cups").
- and_yield("Arch : i386").
- and_yield("Epoch : 1").
- and_yield("Version: 1.2.4").
- and_yield("Release: 11.18.el5").
- and_yield("Size : 7.8 M").
- and_yield("Repo : installed").
- and_yield("Summary: Common Unix Printing System").
- and_yield("Description:").
- and_yield("The Common UNIX Printing System provides a portable printing layer for").
- and_yield("UNIX® operating systems. It has been developed by Easy Software Products").
- and_yield("to promote a standard printing solution for all UNIX vendors and users.").
- and_yield("CUPS provides the System V and Berkeley command-line interfaces.").
- and_yield("").
- and_yield("Available Packages").
- and_yield("Name : cups").
- and_yield("Arch : i386").
- and_yield("Epoch : 1").
- and_yield("Version: 1.2.4").
- and_yield("Release: 11.18.el5_2.3").
- and_yield("Size : 2.7 M").
- and_yield("Repo : updates").
- and_yield("Summary: Common Unix Printing System").
- and_yield("Description:").
- and_yield("The Common UNIX Printing System provides a portable printing layer for").
- and_yield("UNIX® operating systems. It has been developed by Easy Software Products").
- and_yield("to promote a standard printing solution for all UNIX vendors and users.").
- and_yield("CUPS provides the System V and Berkeley command-line interfaces.")
- @stdout_available = mock("STDOUT AVAILABLE", :null_object => true)
- @stdout_available.stub!(:each).and_yield("Available Packages").
- and_yield("Name : cups").
- and_yield("Arch : i386").
- and_yield("Epoch : 1").
- and_yield("Version: 1.2.4").
- and_yield("Release: 11.18.el5_2.3").
- and_yield("Size : 2.7 M").
- and_yield("Repo : updates").
- and_yield("Summary: Common Unix Printing System").
- and_yield("Description:").
- and_yield("The Common UNIX Printing System provides a portable printing layer for").
- and_yield("UNIX® operating systems. It has been developed by Easy Software Products").
- and_yield("to promote a standard printing solution for all UNIX vendors and users.").
- and_yield("CUPS provides the System V and Berkeley command-line interfaces.")
@stderr = mock("STDERR", :null_object => true)
@pid = mock("PID", :null_object => true)
end
@@ -99,45 +60,22 @@ describe Chef::Provider::Package::Yum, "load_current_resource" do
@provider.load_current_resource
end
- it "should run yum info with the package name" do
- @provider.should_receive(:popen4).with("yum info -q -y #{@new_resource.package_name}").and_return(@status)
- @provider.load_current_resource
- end
-
- it "should read stdout on yum info" 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 no installed package" do
- @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout_available, @stderr).and_return(@status)
+ @yum_cache.stub!(:installed_version).and_return(nil)
@current_resource.should_receive(:version).with(nil).and_return(true)
@provider.load_current_resource
end
- it "should set the installed version if yum info has one" do
- @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ it "should set the installed version if yum has one" do
@current_resource.should_receive(:version).with("1.2.4-11.18.el5").and_return(true)
@provider.load_current_resource
end
it "should set the candidate version if yum info has one" do
- @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
@provider.load_current_resource
@provider.candidate_version.should eql("1.2.4-11.18.el5_2.3")
end
- it "should raise an exception if yum info fails" do
- @status.should_receive(:exitstatus).and_return(1)
- lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Package)
- end
-
- it "should not raise an exception if yum info succeeds" do
- @status.should_receive(:exitstatus).and_return(0)
- lambda { @provider.load_current_resource }.should_not raise_error(Chef::Exceptions::Package)
- end
-
it "should return the current resouce" do
@provider.load_current_resource.should eql(@current_resource)
end
@@ -154,6 +92,14 @@ describe Chef::Provider::Package::Yum, "install_package" do
:package_name => "emacs",
:updated => nil
)
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :refresh => true,
+ :flush => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5_2.3"
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
@provider = Chef::Provider::Package::Yum.new(@node, @new_resource)
end
@@ -169,13 +115,21 @@ describe Chef::Provider::Package::Yum, "upgrade_package" do
before(:each) do
@node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Package",
+ @new_resource = mock("Chef::Resource::Package",
:null_object => true,
:name => "emacs",
:version => nil,
:package_name => "emacs",
:updated => nil
)
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :refresh => true,
+ :flush => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5_2.3"
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
@current_resource = mock("Chef::Resource::Package",
:null_object => true,
:name => "emacs",
@@ -214,6 +168,14 @@ describe Chef::Provider::Package::Yum, "remove_package" do
:package_name => "emacs",
:updated => nil
)
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :refresh => true,
+ :flush => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5_2.3"
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
@provider = Chef::Provider::Package::Yum.new(@node, @new_resource)
end
@@ -235,6 +197,14 @@ describe Chef::Provider::Package::Yum, "purge_package" do
:package_name => "emacs",
:updated => nil
)
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :refresh => true,
+ :flush => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5_2.3"
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
@provider = Chef::Provider::Package::Yum.new(@node, @new_resource)
end