summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Smith <tsmith@chef.io>2016-03-30 12:29:15 -0700
committerTim Smith <tsmith@chef.io>2016-03-30 12:29:15 -0700
commit02defc6ff5ce01204656ae4715c4fdce68843e2e (patch)
treeacd202778ab2c304e3caaf0426ec1a8d09fee3f0
parent438fdeba4532c78161e01ade9d23d108ff8bac33 (diff)
parente6c6d5997cccd24641ae16cfbc73fb5000dc8ad1 (diff)
downloadohai-02defc6ff5ce01204656ae4715c4fdce68843e2e.tar.gz
Merge pull request #778 from sh9189/windows_packages_fix
Windows packages plugin - Get packages from registry
-rw-r--r--lib/ohai/plugins/packages.rb105
-rw-r--r--spec/unit/plugins/packages_spec.rb356
2 files changed, 224 insertions, 237 deletions
diff --git a/lib/ohai/plugins/packages.rb b/lib/ohai/plugins/packages.rb
index b9d06451..334db7ce 100644
--- a/lib/ohai/plugins/packages.rb
+++ b/lib/ohai/plugins/packages.rb
@@ -22,63 +22,78 @@ Ohai.plugin(:Packages) do
provides "packages"
depends "platform_family"
+ WINDOWS_ATTRIBUTE_ALIASES = {
+ "DisplayVersion" => "version",
+ "Publisher" => "publisher",
+ "InstallDate" => "installdate"
+ }
+
collect_data(:linux) do
- if configuration(:enabled)
- packages Mash.new
- if %w{debian}.include? platform_family
- so = shell_out("dpkg-query -W")
- pkgs = so.stdout.lines
+ packages Mash.new
+ if %w{debian}.include? platform_family
+ so = shell_out("dpkg-query -W")
+ pkgs = so.stdout.lines
- pkgs.each do |pkg|
- name, version = pkg.split
- packages[name] = { "version" => version }
- end
+ pkgs.each do |pkg|
+ name, version = pkg.split
+ packages[name] = { "version" => version }
+ end
- elsif %w{rhel fedora suse}.include? platform_family
- require "shellwords"
- format = Shellwords.escape '%{NAME}\t%{VERSION}\t%{RELEASE}\n'
- so = shell_out("rpm -qa --queryformat #{format}")
- pkgs = so.stdout.lines
+ elsif %w{rhel fedora suse}.include? platform_family
+ require "shellwords"
+ format = Shellwords.escape '%{NAME}\t%{VERSION}\t%{RELEASE}\n'
+ so = shell_out("rpm -qa --queryformat #{format}")
+ pkgs = so.stdout.lines
- pkgs.each do |pkg|
- name, version, release = pkg.split
- packages[name] = { "version" => version, "release" => release }
- end
+ pkgs.each do |pkg|
+ name, version, release = pkg.split
+ packages[name] = { "version" => version, "release" => release }
end
end
end
- collect_data(:windows) do
- if configuration(:enabled)
- packages Mash.new
- require "wmi-lite"
-
- wmi = WmiLite::Wmi.new
- w32_product = wmi.instances_of("Win32_Product")
-
- w32_product.find_all.each do |product|
- name = product["name"]
+ def collect_programs_from_registry_key(key_path)
+ # from http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129(v=vs.85).aspx
+ if ::RbConfig::CONFIG["target_cpu"] == "i386"
+ reg_type = Win32::Registry::KEY_READ | 0x100
+ elsif ::RbConfig::CONFIG["target_cpu"] == "x86_64"
+ reg_type = Win32::Registry::KEY_READ | 0x200
+ else
+ reg_type = Win32::Registry::KEY_READ
+ end
+ Win32::Registry::HKEY_LOCAL_MACHINE.open(key_path, reg_type) do |reg|
+ reg.each_key do |key, _wtime|
+ pkg = reg.open(key)
+ name = pkg["DisplayName"] rescue nil
+ next if name.nil?
package = packages[name] = Mash.new
- %w{version vendor installdate}.each do |attr|
- package[attr] = product[attr]
+ WINDOWS_ATTRIBUTE_ALIASES.each do |registry_attr, package_attr|
+ value = pkg[registry_attr] rescue nil
+ package[package_attr] = value unless value.nil?
end
end
end
end
+ collect_data(:windows) do
+ require "win32/registry"
+ packages Mash.new
+ collect_programs_from_registry_key('Software\Microsoft\Windows\CurrentVersion\Uninstall')
+ # on 64 bit systems, 32 bit programs are stored here
+ collect_programs_from_registry_key('Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall')
+ end
+
collect_data(:aix) do
- if configuration(:enabled)
- packages Mash.new
- so = shell_out("lslpp -L -q -c")
- pkgs = so.stdout.lines
+ packages Mash.new
+ so = shell_out("lslpp -L -q -c")
+ pkgs = so.stdout.lines
- # Output format is
- # Package Name:Fileset:Level
- # On aix, filesets are packages and levels are versions
- pkgs.each do |pkg|
- _, name, version = pkg.split(":")
- packages[name] = { "version" => version }
- end
+ # Output format is
+ # Package Name:Fileset:Level
+ # On aix, filesets are packages and levels are versions
+ pkgs.each do |pkg|
+ _, name, version = pkg.split(":")
+ packages[name] = { "version" => version }
end
end
@@ -119,10 +134,8 @@ Ohai.plugin(:Packages) do
end
collect_data(:solaris2) do
- if configuration(:enabled)
- packages Mash.new
- collect_ips_packages
- collect_sysv_packages
- end
+ packages Mash.new
+ collect_ips_packages
+ collect_sysv_packages
end
end
diff --git a/spec/unit/plugins/packages_spec.rb b/spec/unit/plugins/packages_spec.rb
index 169eeac6..332b89e8 100644
--- a/spec/unit/plugins/packages_spec.rb
+++ b/spec/unit/plugins/packages_spec.rb
@@ -20,252 +20,226 @@
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper.rb")
describe Ohai::System, "plugin packages" do
- context "when the packages plugin is disabled" do
- before do
- Ohai.config[:plugin][:packages][:enabled] = false
- allow(plugin).to receive(:collect_os).and_return(platform_family.to_s)
- plugin.run
- end
-
+ context "on debian" do
let(:plugin) do
get_plugin("packages").tap do |plugin|
- plugin[:platform_family] = platform_family
+ plugin[:platform_family] = "debian"
end
end
- [:debian, :fedora, :windows, :aix, :solaris2].each do |os|
- context "on #{os}" do
- let(:platform_family) { os }
-
- it "does not enumerate the packages" do
- expect(plugin[:packages]).to eq(nil)
- end
- end
+ let(:stdout) do
+ File.read(File.join(SPEC_PLUGIN_PATH, "dpkg-query.output"))
end
- end
- context "when the packages plugin is enabled" do
- before do
- Ohai.config[:plugin][:packages][:enabled] = true
+ before(:each) do
+ allow(plugin).to receive(:collect_os).and_return(:linux)
+ allow(plugin).to receive(:shell_out)
+ .with("dpkg-query -W")
+ .and_return(mock_shell_out(0, stdout, ""))
+ plugin.run
end
- context "on debian" do
- let(:plugin) do
- get_plugin("packages").tap do |plugin|
- plugin[:platform_family] = "debian"
- end
- end
+ it "calls dpkg query" do
+ expect(plugin).to receive(:shell_out)
+ .with("dpkg-query -W")
+ .and_return(mock_shell_out(0, stdout, ""))
+ plugin.run
+ end
- let(:stdout) do
- File.read(File.join(SPEC_PLUGIN_PATH, "dpkg-query.output"))
- end
+ it "gets packages and versions" do
+ expect(plugin[:packages]["vim-common"][:version]).to eq("2:7.4.052-1ubuntu3")
+ end
+ end
- before(:each) do
- allow(plugin).to receive(:collect_os).and_return(:linux)
- allow(plugin).to receive(:shell_out)
- .with("dpkg-query -W")
- .and_return(mock_shell_out(0, stdout, ""))
- plugin.run
+ context "on fedora" do
+ let(:plugin) do
+ get_plugin("packages").tap do |plugin|
+ plugin[:platform_family] = "fedora"
end
+ end
- it "calls dpkg query" do
- expect(plugin).to receive(:shell_out)
- .with("dpkg-query -W")
- .and_return(mock_shell_out(0, stdout, ""))
- plugin.run
- end
+ let(:format) { Shellwords.escape '%{NAME}\t%{VERSION}\t%{RELEASE}\n' }
- it "gets packages and versions" do
- expect(plugin[:packages]["vim-common"][:version]).to eq("2:7.4.052-1ubuntu3")
- end
+ let(:stdout) do
+ File.read(File.join(SPEC_PLUGIN_PATH, "rpmquery.output"))
end
- context "on fedora" do
- let(:plugin) do
- get_plugin("packages").tap do |plugin|
- plugin[:platform_family] = "fedora"
- end
- end
+ before(:each) do
+ allow(plugin).to receive(:collect_os).and_return(:linux)
+ allow(plugin).to receive(:shell_out).with("rpm -qa --queryformat #{format}").and_return(mock_shell_out(0, stdout, ""))
+ plugin.run
+ end
- let(:format) { Shellwords.escape '%{NAME}\t%{VERSION}\t%{RELEASE}\n' }
+ it "calls rpm -qa" do
+ expect(plugin).to receive(:shell_out)
+ .with("rpm -qa --queryformat #{format}")
+ .and_return(mock_shell_out(0, stdout, ""))
+ plugin.run
+ end
- let(:stdout) do
- File.read(File.join(SPEC_PLUGIN_PATH, "rpmquery.output"))
- end
+ it "gets packages and versions/release" do
+ expect(plugin[:packages]["vim-common"][:version]).to eq("7.2.411")
+ expect(plugin[:packages]["vim-common"][:release]).to eq("1.8.el6")
+ end
+ end
- before(:each) do
- allow(plugin).to receive(:collect_os).and_return(:linux)
- allow(plugin).to receive(:shell_out).with("rpm -qa --queryformat #{format}").and_return(mock_shell_out(0, stdout, ""))
- plugin.run
- end
+ context "on windows", :windows_only do
- it "calls rpm -qa" do
- expect(plugin).to receive(:shell_out)
- .with("rpm -qa --queryformat #{format}")
- .and_return(mock_shell_out(0, stdout, ""))
- plugin.run
+ let(:plugin) do
+ get_plugin("packages").tap do |plugin|
+ plugin[:platform_family] = "windows"
end
+ end
- it "gets packages and versions/release" do
- expect(plugin[:packages]["vim-common"][:version]).to eq("7.2.411")
- expect(plugin[:packages]["vim-common"][:release]).to eq("1.8.el6")
- end
+ let(:win_reg_double) do
+ instance_double("Win32::Registry")
end
- context "on windows", :windows_only do
- require "wmi-lite"
+ let(:win_reg_keys) do
+ [ "{22FA28AB-3C1B-438B-A8B5-E23892C8B567}",
+ "{0D4BCDCD-6225-4BA5-91A3-54AFCECC281E}" ]
+ end
- let(:plugin) do
- get_plugin("packages").tap do |plugin|
- plugin[:platform_family] = "windows"
- end
- end
+ let(:i386_reg_type) do
+ Win32::Registry::KEY_READ | 0x100
+ end
- let(:win32_product_output) do
- [{ "assignmenttype" => 0,
- "caption" => "NXLOG-CE",
- "description" => "NXLOG-CE",
- "helplink" => nil,
- "helptelephone" => nil,
- "identifyingnumber" => "{22FA28AB-3C1B-438B-A8B5-E23892C8B567}",
- "installdate" => "20150511",
- "installdate2" => nil,
- "installlocation" => nil,
- "installsource" => 'C:\\chef\\cache\\',
- "installstate" => 5,
- "language" => "1033",
- "localpackage" => 'C:\\Windows\\Installer\\30884.msi',
- "name" => "NXLOG-CE",
- "packagecache" => 'C:\\Windows\\Installer\\30884.msi',
- "packagecode" => "{EC3A13C4-4634-47FC-9662-DC293CB96F9F}",
- "packagename" => "nexlog-ce-2.8.1248.msi",
- "productid" => nil,
- "regcompany" => nil,
- "regowner" => nil,
- "skunumber" => nil,
- "transforms" => nil,
- "urlinfoabout" => nil,
- "urlupdateinfo" => nil,
- "vendor" => "nxsec.com",
- "version" => "2.8.1248",
- "wordcount" => 2 },
- { "assignmenttype" => 1,
- "caption" => "Chef Development Kit v0.7.0",
- "description" => "Chef Development Kit v0.7.0",
- "helplink" => "http://www.getchef.com/support/",
- "helptelephone" => nil,
- "identifyingnumber" => "{90754A33-404C-4172-8F3B-7F04CE98011C}",
- "installdate" => "20150925", "installdate2" => nil,
- "installlocation" => nil,
- "installsource" => 'C:\\Users\\skhajamohid1\\Downloads\\',
- "installstate" => 5, "language" => "1033",
- "localpackage" => 'C:\\WINDOWS\\Installer\\d9e1ca7.msi',
- "name" => "Chef Development Kit v0.7.0",
- "packagecache" => 'C:\\WINDOWS\\Installer\\d9e1ca7.msi',
- "packagecode" => "{9B82FB86-40AE-4CDF-9DE8-97574F9395B9}",
- "packagename" => "chefdk-0.7.0-1 (2).msi",
- "productid" => nil,
- "regcompany" => nil,
- "regowner" => nil,
- "skunumber" => nil,
- "transforms" => nil,
- "urlinfoabout" => nil,
- "urlupdateinfo" => nil,
- "vendor" => "\"Chef Software, Inc. <maintainers@chef.io>\"",
- "version" => "0.7.0.1",
- "wordcount" => 2 }]
- end
+ let(:x86_64_reg_type) do
+ Win32::Registry::KEY_READ | 0x200
+ end
- before(:each) do
- allow(plugin).to receive(:collect_os).and_return(:windows)
- expect_any_instance_of(WmiLite::Wmi).to receive(:instances_of).with("Win32_Product").and_return(win32_product_output)
- plugin.run
- end
+ let(:win_reg_output) do
+ [{ "DisplayName" => "NXLOG-CE",
+ "DisplayVersion" => "2.8.1248",
+ "Publisher" => "nxsec.com",
+ "InstallDate" => "20150511"
+ },
+ { "DisplayName" => "Chef Development Kit v0.7.0",
+ "DisplayVersion" => "0.7.0.1",
+ "Publisher" => "\"Chef Software, Inc. <maintainers@chef.io>\"",
+ "InstallDate" => "20150925" }]
+ end
+ shared_examples "windows_package_plugin" do
it "gets package info" do
+ plugin.run
expect(plugin[:packages]["Chef Development Kit v0.7.0"][:version]).to eq("0.7.0.1")
- expect(plugin[:packages]["Chef Development Kit v0.7.0"][:vendor]).to eq("\"Chef Software, Inc. <maintainers@chef.io>\"")
+ expect(plugin[:packages]["Chef Development Kit v0.7.0"][:publisher]).to eq("\"Chef Software, Inc. <maintainers@chef.io>\"")
expect(plugin[:packages]["Chef Development Kit v0.7.0"][:installdate]).to eq("20150925")
expect(plugin[:packages]["NXLOG-CE"][:version]).to eq("2.8.1248")
- expect(plugin[:packages]["NXLOG-CE"][:vendor]).to eq("nxsec.com")
+ expect(plugin[:packages]["NXLOG-CE"][:publisher]).to eq("nxsec.com")
expect(plugin[:packages]["NXLOG-CE"][:installdate]).to eq("20150511")
end
end
- context "on aix" do
- let(:plugin) { get_plugin("packages") }
+ before(:each) do
+ allow(plugin).to receive(:collect_os).and_return(:windows)
+ allow(win_reg_double).to receive(:open).with(win_reg_keys[0]).and_return(win_reg_output[0])
+ allow(win_reg_double).to receive(:open).with(win_reg_keys[1]).and_return(win_reg_output[1])
+ allow(win_reg_double).to receive(:each_key).and_yield(win_reg_keys[0], 0).and_yield(win_reg_keys[1], 1)
+ end
- let(:stdout) do
- File.read(File.join(SPEC_PLUGIN_PATH, "lslpp.output"))
+ describe "on 32 bit ruby" do
+ before do
+ stub_const("::RbConfig::CONFIG", { "target_cpu" => "i386" } )
+ allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).with('Software\Microsoft\Windows\CurrentVersion\Uninstall', i386_reg_type).and_yield(win_reg_double)
+ allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).with('Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall', i386_reg_type).and_yield(win_reg_double)
end
+ it_behaves_like "windows_package_plugin"
+ end
- before(:each) do
- allow(plugin).to receive(:collect_os).and_return(:aix)
- allow(plugin).to receive(:shell_out).with("lslpp -L -q -c").and_return(mock_shell_out(0, stdout, ""))
- plugin.run
+ describe "on 64 bit ruby" do
+ before do
+ stub_const("::RbConfig::CONFIG", { "target_cpu" => "x86_64" } )
+ allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).with('Software\Microsoft\Windows\CurrentVersion\Uninstall', x86_64_reg_type).and_yield(win_reg_double)
+ allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).with('Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall', x86_64_reg_type).and_yield(win_reg_double)
end
+ it_behaves_like "windows_package_plugin"
+ end
- it "calls lslpp -L -q -c" do
- expect(plugin).to receive(:shell_out)
- .with("lslpp -L -q -c")
- .and_return(mock_shell_out(0, stdout, ""))
- plugin.run
+ describe "on unknown ruby" do
+ before do
+ stub_const("::RbConfig::CONFIG", { "target_cpu" => nil } )
+ allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).with('Software\Microsoft\Windows\CurrentVersion\Uninstall', Win32::Registry::KEY_READ).and_yield(win_reg_double)
+ allow(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).with('Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall', Win32::Registry::KEY_READ).and_yield(win_reg_double)
end
+ it_behaves_like "windows_package_plugin"
+ end
+ end
- it "gets packages with version" do
- expect(plugin[:packages]["chef"][:version]).to eq("12.5.1.1")
- end
+ context "on aix" do
+ let(:plugin) { get_plugin("packages") }
+
+ let(:stdout) do
+ File.read(File.join(SPEC_PLUGIN_PATH, "lslpp.output"))
end
- context "on solaris2" do
- let(:plugin) { get_plugin("packages") }
+ before(:each) do
+ allow(plugin).to receive(:collect_os).and_return(:aix)
+ allow(plugin).to receive(:shell_out).with("lslpp -L -q -c").and_return(mock_shell_out(0, stdout, ""))
+ plugin.run
+ end
- let(:pkglist_output) do
- File.read(File.join(SPEC_PLUGIN_PATH, "pkglist.output"))
- end
+ it "calls lslpp -L -q -c" do
+ expect(plugin).to receive(:shell_out)
+ .with("lslpp -L -q -c")
+ .and_return(mock_shell_out(0, stdout, ""))
+ plugin.run
+ end
- let(:pkginfo_output) do
- File.read(File.join(SPEC_PLUGIN_PATH, "pkginfo.output"))
- end
+ it "gets packages with version" do
+ expect(plugin[:packages]["chef"][:version]).to eq("12.5.1.1")
+ end
+ end
- before(:each) do
- allow(plugin).to receive(:collect_os).and_return(:solaris2)
- allow(plugin).to receive(:shell_out).with("pkg list -H").and_return(mock_shell_out(0, pkglist_output, ""))
- allow(plugin).to receive(:shell_out).with("pkginfo -l").and_return(mock_shell_out(0, pkginfo_output, ""))
- plugin.run
- end
+ context "on solaris2" do
+ let(:plugin) { get_plugin("packages") }
- it "calls pkg list -H" do
- expect(plugin).to receive(:shell_out)
- .with("pkg list -H")
- .and_return(mock_shell_out(0, pkglist_output, ""))
- plugin.run
- end
+ let(:pkglist_output) do
+ File.read(File.join(SPEC_PLUGIN_PATH, "pkglist.output"))
+ end
- it "calls pkginfo -l" do
- expect(plugin).to receive(:shell_out)
- .with("pkginfo -l")
- .and_return(mock_shell_out(0, pkginfo_output, ""))
- plugin.run
- end
+ let(:pkginfo_output) do
+ File.read(File.join(SPEC_PLUGIN_PATH, "pkginfo.output"))
+ end
- it "gets ips packages with version" do
- expect(plugin[:packages]["chef"][:version]).to eq("12.5.1")
- end
+ before(:each) do
+ allow(plugin).to receive(:collect_os).and_return(:solaris2)
+ allow(plugin).to receive(:shell_out).with("pkg list -H").and_return(mock_shell_out(0, pkglist_output, ""))
+ allow(plugin).to receive(:shell_out).with("pkginfo -l").and_return(mock_shell_out(0, pkginfo_output, ""))
+ plugin.run
+ end
- it "gets ips packages with version and publisher" do
- expect(plugin[:packages]["system/EMCpower"][:version]).to eq("6.0.0.1.0-3")
- expect(plugin[:packages]["system/EMCpower"][:publisher]).to eq("emc.com")
- end
+ it "calls pkg list -H" do
+ expect(plugin).to receive(:shell_out)
+ .with("pkg list -H")
+ .and_return(mock_shell_out(0, pkglist_output, ""))
+ plugin.run
+ end
- it "gets sysv packages with version" do
- expect(plugin[:packages]["chef"][:version]).to eq("12.5.1")
- end
+ it "calls pkginfo -l" do
+ expect(plugin).to receive(:shell_out)
+ .with("pkginfo -l")
+ .and_return(mock_shell_out(0, pkginfo_output, ""))
+ plugin.run
+ end
- it "gets sysv packages with version" do
- expect(plugin[:packages]["mqm"][:version]).to eq("7.0.1.4")
- end
+ it "gets ips packages with version" do
+ expect(plugin[:packages]["chef"][:version]).to eq("12.5.1")
+ end
+
+ it "gets ips packages with version and publisher" do
+ expect(plugin[:packages]["system/EMCpower"][:version]).to eq("6.0.0.1.0-3")
+ expect(plugin[:packages]["system/EMCpower"][:publisher]).to eq("emc.com")
+ end
+
+ it "gets sysv packages with version" do
+ expect(plugin[:packages]["chef"][:version]).to eq("12.5.1")
+ end
+
+ it "gets sysv packages with version" do
+ expect(plugin[:packages]["mqm"][:version]).to eq("7.0.1.4")
end
end
end