diff options
Diffstat (limited to 'chef')
-rw-r--r-- | chef/lib/chef/knife.rb | 28 | ||||
-rw-r--r-- | chef/lib/chef/knife/client_bulk_delete.rb | 34 | ||||
-rw-r--r-- | chef/lib/chef/provider/package/apt.rb | 63 | ||||
-rw-r--r-- | chef/spec/unit/knife/client_bulk_delete_spec.rb | 22 | ||||
-rw-r--r-- | chef/spec/unit/provider/package/apt_spec.rb | 197 |
5 files changed, 197 insertions, 147 deletions
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/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/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/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/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 |