diff options
author | Adam Jacob <adam@hjksolutions.com> | 2009-04-24 17:20:14 -0700 |
---|---|---|
committer | Adam Jacob <adam@hjksolutions.com> | 2009-04-24 17:20:14 -0700 |
commit | d23d849e70355918be6d8915d981991249ca02d6 (patch) | |
tree | 63c2bdd2a9bdbfc6b46aa55aacb587ad0bcc42a7 /chef | |
parent | 61edf3887350274fa7204f0f51b64c59f25c51df (diff) | |
parent | c47454eff94555c0337adec7d71ced64cc1a0051 (diff) | |
download | chef-d23d849e70355918be6d8915d981991249ca02d6.tar.gz |
Merge branch 'master' into ctennis/chef-230
Diffstat (limited to 'chef')
-rwxr-xr-x | chef/bin/chef-client | 2 | ||||
-rwxr-xr-x | chef/bin/chef-solo | 2 | ||||
-rw-r--r-- | chef/contrib/el/chef-client.config | 16 | ||||
-rw-r--r-- | chef/contrib/el/chef-client.init | 74 | ||||
-rw-r--r-- | chef/lib/chef/config.rb | 4 | ||||
-rw-r--r-- | chef/lib/chef/couchdb.rb | 18 | ||||
-rw-r--r-- | chef/lib/chef/daemon.rb | 4 | ||||
-rw-r--r-- | chef/lib/chef/mixin/command.rb | 98 | ||||
-rw-r--r-- | chef/lib/chef/provider/cron.rb | 8 | ||||
-rw-r--r-- | chef/lib/chef/provider/package/yum-dump-json.py | 67 | ||||
-rw-r--r-- | chef/lib/chef/provider/package/yum.rb | 127 | ||||
-rw-r--r-- | chef/spec/unit/couchdb_spec.rb | 40 | ||||
-rw-r--r-- | chef/spec/unit/mixin/command_spec.rb | 85 | ||||
-rw-r--r-- | chef/spec/unit/mixin/template_spec.rb | 3 | ||||
-rw-r--r-- | chef/spec/unit/provider/cron_spec.rb | 31 | ||||
-rw-r--r-- | chef/spec/unit/provider/package/yum_spec.rb | 116 |
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 |