diff options
author | NimishaS <nimisha.sharad@msystechnologies.com> | 2016-10-07 09:01:00 +0000 |
---|---|---|
committer | NimishaS <nimisha.sharad@msystechnologies.com> | 2016-11-16 10:04:58 +0000 |
commit | 767b66e13f2989693f7adf1b3cebed2e6da6a12d (patch) | |
tree | 449b11fe6c5f1c41263c9287f744bbdc67cc9e86 /spec | |
parent | bedcbd5f52448d24fdd7ab26ab79185c011beee3 (diff) | |
download | chef-767b66e13f2989693f7adf1b3cebed2e6da6a12d.tar.gz |
msu_package resource for Microsoft Update packages
Signed-off-by: NimishaS <nimisha.sharad@msystechnologies.com>
Signed-off-by: NimishaS <nimisha.sharad@msystechnologies.com>
Diffstat (limited to 'spec')
-rw-r--r-- | spec/data/sample_msu1.xml | 10 | ||||
-rw-r--r-- | spec/data/sample_msu2.xml | 14 | ||||
-rw-r--r-- | spec/data/sample_msu3.xml | 16 | ||||
-rw-r--r-- | spec/functional/resource/msu_package_spec.rb | 84 | ||||
-rw-r--r-- | spec/spec_helper.rb | 1 | ||||
-rw-r--r-- | spec/support/platform_helpers.rb | 24 | ||||
-rw-r--r-- | spec/unit/provider/package/msu_spec.rb | 284 | ||||
-rw-r--r-- | spec/unit/resource/msu_package_spec.rb | 49 |
8 files changed, 474 insertions, 8 deletions
diff --git a/spec/data/sample_msu1.xml b/spec/data/sample_msu1.xml new file mode 100644 index 0000000000..cc68dbf060 --- /dev/null +++ b/spec/data/sample_msu1.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<unattend xmlns="urn:schemas-microsoft-com:unattend"> + <servicing> + <package action="install"> + <assemblyIdentity name="Package_for_KB2859903" version="10.2.1.0" language="neutral" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35"/> + <source location="%configsetroot%\IE10-Windows6.1-KB2859903-x86.CAB" /> + </package> + </servicing> +</unattend> + diff --git a/spec/data/sample_msu2.xml b/spec/data/sample_msu2.xml new file mode 100644 index 0000000000..6f95e04f93 --- /dev/null +++ b/spec/data/sample_msu2.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<unattend xmlns="urn:schemas-microsoft-com:unattend"> + <servicing> + <package action="install"> + <assemblyIdentity name="Package_for_KB2859903" version="10.2.1.0" language="neutral" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35"/> + <source location="%configsetroot%\IE10-Windows6.1-KB2859903-x86.CAB" /> + </package> + <package action="install"> + <assemblyIdentity name="Package_for_abc" version="10.2.1.0" language="neutral" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35"/> + <source location="%configsetroot%\abc.CAB" /> + </package> + </servicing> +</unattend> + diff --git a/spec/data/sample_msu3.xml b/spec/data/sample_msu3.xml new file mode 100644 index 0000000000..0ef09da206 --- /dev/null +++ b/spec/data/sample_msu3.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<unattend xmlns="urn:schemas-microsoft-com:unattend"> + <servicing> + <package action="install"> + <assemblyIdentity name="Package_for_KB2859903" version="10.2.1.0" language="neutral" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35"/> + <source location="%configsetroot%\IE10-Windows6.1-KB2859903-x86.CAB" /> + </package> + </servicing> + <servicing> + <package action="install"> + <assemblyIdentity name="Package_for_abc" version="10.2.1.0" language="neutral" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35"/> + <source location="%configsetroot%\abc.CAB" /> + </package> + </servicing> +</unattend> + diff --git a/spec/functional/resource/msu_package_spec.rb b/spec/functional/resource/msu_package_spec.rb new file mode 100644 index 0000000000..23342be6ae --- /dev/null +++ b/spec/functional/resource/msu_package_spec.rb @@ -0,0 +1,84 @@ +# +# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>) +# Copyright:: Copyright (c) 2016 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" +require "chef/provider/package/cab" + +describe Chef::Resource::MsuPackage, :win2012r2_only do + + let(:package_name) { "Package_for_KB2959977" } + let(:package_source) { "https://download.microsoft.com/download/3/B/3/3B320C07-B7B1-41E5-81F4-79EBC02DF7D3/Windows8.1-KB2959977-x64.msu" } + + let(:new_resource) { Chef::Resource::CabPackage.new("windows_test_pkg") } + let(:cab_provider) do + node = Chef::Node.new + events = Chef::EventDispatch::Dispatcher.new + run_context = Chef::RunContext.new(node, {}, events) + Chef::Provider::Package::Cab.new(new_resource, run_context) + end + + subject do + new_resource = Chef::Resource::MsuPackage.new("test msu package", run_context) + new_resource.package_name package_name + new_resource.source package_source + new_resource + end + + context "installing package" do + after { remove_package } + + it "installs the package successfully" do + subject.run_action(:install) + found_packages = cab_provider.installed_packages.select { |p| p["package_identity"] =~ /^#{package_name}~/ } + expect(found_packages.length).to be == 1 + end + end + + context "removing a package" do + it "removes an installed package" do + subject.run_action(:install) + remove_package + found_packages = cab_provider.installed_packages.select { |p| p["package_identity"] =~ /^#{package_name}~/ } + expect(found_packages.length).to be == 0 + end + end + + context "when an invalid msu package is given" do + def package_name + "Package_for_KB2859903" + end + + def package_source + "https://download.microsoft.com/download/5/2/B/52BE95BF-22D8-4415-B644-0FDF398F6D96/IE10-Windows6.1-KB2859903-x86.msu" + end + + it "raises error while installing" do + expect { subject.run_action(:install) }.to raise_error(Chef::Exceptions::Package) + end + + it "raises error while removing" do + expect { subject.run_action(:remove) }.to raise_error(Chef::Exceptions::Package) + end + end + + def remove_package + pkg_to_remove = Chef::Resource::MsuPackage.new(package_name, run_context) + pkg_to_remove.source = package_source + pkg_to_remove.run_action(:remove) + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7559e797bc..aac5a82f2a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -139,6 +139,7 @@ RSpec.configure do |config| config.filter_run_excluding :not_supported_on_gce => true if gce? config.filter_run_excluding :not_supported_on_nano => true if windows_nano_server? config.filter_run_excluding :win2k3_only => true unless windows_win2k3? + config.filter_run_excluding :win2012r2_only => true unless windows_2012r2? config.filter_run_excluding :windows_2008r2_or_later => true unless windows_2008r2_or_later? config.filter_run_excluding :windows64_only => true unless windows64? config.filter_run_excluding :windows32_only => true unless windows32? diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb index 62b262b8a7..14f883da74 100644 --- a/spec/support/platform_helpers.rb +++ b/spec/support/platform_helpers.rb @@ -50,23 +50,31 @@ end def windows_win2k3? return false unless windows? - wmi = WmiLite::Wmi.new - host = wmi.first_of("Win32_OperatingSystem") - (host["version"] && host["version"].start_with?("5.2")) + (host_version && host_version.start_with?("5.2")) end def windows_2008r2_or_later? return false unless windows? - wmi = WmiLite::Wmi.new - host = wmi.first_of("Win32_OperatingSystem") - version = host["version"] - return false unless version - components = version.split(".").map do |component| + return false unless host_version + components = host_version.split(".").map do |component| component.to_i end components.length >= 2 && components[0] >= 6 && components[1] >= 1 end +def windows_2012r2? + return false unless windows? + (host_version && host_version.start_with?("6.3")) +end + +def host_version + @host_version ||= begin + wmi = WmiLite::Wmi.new + host = wmi.first_of("Win32_OperatingSystem") + host["version"] + end +end + def windows_powershell_dsc? return false unless windows? supports_dsc = false diff --git a/spec/unit/provider/package/msu_spec.rb b/spec/unit/provider/package/msu_spec.rb new file mode 100644 index 0000000000..d9b97aaee6 --- /dev/null +++ b/spec/unit/provider/package/msu_spec.rb @@ -0,0 +1,284 @@ +# +# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>) +# Copyright:: Copyright 2008-2016, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Chef::Provider::Package::Msu, :windows_only do + let(:timeout) {} + + let(:new_resource) { Chef::Resource::MsuPackage.new("windows_test_pkg") } + + let(:provider) do + node = Chef::Node.new + events = Chef::EventDispatch::Dispatcher.new + run_context = Chef::RunContext.new(node, {}, events) + Chef::Provider::Package::Msu.new(new_resource, run_context) + end + + let(:installed_package_list_stdout) do + <<-EOF +Packages listing: +Package Identity : Package_for_KB2999486~31bf3856ad364e35~amd64~~6.1.9768.0 +Package Identity : Package_for_KB2994825~31bf3856ad364e35~amd64~~6.1.7601.0 + EOF + end + + let(:package_version_stdout) do + <<-EOF +Package information: +Package Identity : Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0 +State : Installed +Dependency : Language Pack +The operation completed successfully + EOF + end + + let(:get_package_info_stdout) do + <<-EOF +Deployment Image Servicing and Management tool +Version: 6.1.7600.16385 + +Image Version: 6.1.7600.16385 + +Package information: +Package Identity : Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0 +Applicable : Yes +Copyright : Microsoft Corporation +Company : Microsoft Corporation +State : Installed +Dependency : Language Pack +The operation completed successfully + EOF + end + + def allow_get_packages + get_packages_stdout = <<-EOF +Deployment Image Servicing and Management tool +Version: 6.1.7600.16385 + +Image Version: 6.1.7600.16385 + +Packages listing: + +Package Identity : Package_for_KB2999486~31bf3856ad364e35~amd64~~6.1.9768.0 +State : Installed +Release Type : Language Pack +Install Time : 2/11/2015 11:33 PM + +Package Identity : Package_for_KB2994825~31bf3856ad364e35~amd64~~6.1.7601.0 +State : Installed +Release Type : Language Pack +Install Time : 2/11/2015 11:33 PM + +Package Identity : Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0 +State : Installed +Release Type : Feature Pack +Install Time : 11/21/2010 3:40 AM + +The operation completed successfully. + EOF + get_packages_obj = double(stdout: get_packages_stdout) + allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-Packages").and_return(get_packages_obj) + end + + before do + allow(Dir).to receive(:mktmpdir) + allow(provider).to receive(:cleanup_after_converge) + end + + describe "#initialize" do + it "returns the correct class" do + expect(provider).to be_kind_of(Chef::Provider::Package::Msu) + end + end + + describe "#load_current_resource" do + before do + new_resource.source = "C:\\Temp\\Test6.1-KB2664825-v3-x64.msu" + cab_file = "c:\\temp\\test6.1-kb2664825-v3-x64.cab" + allow(provider).to receive(:extract_msu_contents) + allow(provider).to receive(:read_cab_files_from_xml).and_return([cab_file]) + installed_package_list_obj = double(stdout: installed_package_list_stdout) + allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-Packages").and_return(installed_package_list_obj) + package_version_obj = double(stdout: package_version_stdout) + allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-PackageInfo /PackagePath:\"#{cab_file}\"").and_return(package_version_obj) + end + + it "returns a current_resource" do + expect(provider.load_current_resource).to be_kind_of(Chef::Resource::MsuPackage) + end + + it "sets the current_resource.version to nil when the package is not installed" do + provider.load_current_resource + expect(provider.current_resource.version).to eql([nil]) + end + + it "calls download_source_file method if source is a URL" do + new_resource.source = "https://www.something.com/Test6.1-KB2664825-v3-x64.msu" + expect(provider).to receive(:download_source_file) + provider.load_current_resource + end + end + + describe "#source_resource" do + before do + new_resource.source = "C:\\Temp\\Test6.1-KB2664825-v3-x64.msu" + new_resource.cookbook_name = "Msu_package" + end + + it "sets the desired parameters of downloades msu file" do + allow(provider).to receive(:default_download_cache_path).and_return("C:\\chef\\cache\\package") + source_resource = provider.source_resource + expect(source_resource.path).to be == "C:\\chef\\cache\\package" + expect(source_resource.name).to be == "windows_test_pkg" + expect(source_resource.source).to be == [new_resource.source] + expect(source_resource.cookbook_name).to be == "Msu_package" + expect(source_resource.checksum).to be == nil + end + end + + describe "#default_download_cache_path" do + before do + new_resource.source = "https://www.something.com/Test6.1-KB2664825-v3-x64.msu" + end + + it "returns a clean cache path where the msu file is downloaded" do + allow(Chef::FileCache).to receive(:create_cache_path).and_return("C:\\chef\\abc\\package") + path = provider.default_download_cache_path + expect(path).to be == "C:\\chef\\abc\\package\\Test6.1-KB2664825-v3-x64.msu" + end + end + + describe "action specs" do + before do + new_resource.source = "C:\\Temp\\Test6.1-KB2664825-v3-x64.msu" + cab_file = "c:\\temp\\test6.1-kb2664825-v3-x64.cab" + allow(provider).to receive(:extract_msu_contents) + allow(provider).to receive(:read_cab_files_from_xml).and_return([cab_file]) + installed_package_list_obj = double(stdout: installed_package_list_stdout) + allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-Packages").and_return(installed_package_list_obj) + package_version_obj = double(stdout: package_version_stdout) + allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-PackageInfo /PackagePath:\"#{cab_file}\"").and_return(package_version_obj) + end + + describe "#action_install" do + it "installs package if not already installed" do + provider.load_current_resource + expect(provider.current_resource.version).to eql([nil]) + expect(provider).to receive(:install_package) + provider.run_action(:install) + expect(new_resource).to be_updated_by_last_action + end + + it "does not install package if already installed" do + get_package_info_obj = double(stdout: get_package_info_stdout) + allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-PackageInfo /PackagePath:\"#{new_resource.source}\"").and_return(get_package_info_obj) + allow_get_packages + allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-PackageInfo /PackageName:\"Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0\"").and_return(get_package_info_obj) + provider.load_current_resource + expect(provider.current_resource.version).to eql(["6.1.3.0"]) + expect(provider).not_to receive(:install_package) + provider.run_action(:install) + expect(new_resource).not_to be_updated_by_last_action + end + end + + describe "#action_remove" do + it "does nothing when the package is not present" do + provider.load_current_resource + expect(provider).not_to receive(:remove_package) + provider.run_action(:remove) + expect(new_resource).not_to be_updated_by_last_action + end + + it "removes packages if package is installed" do + get_package_info_obj = double(stdout: get_package_info_stdout) + allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-PackageInfo /PackagePath:\"#{new_resource.source}\"").and_return(get_package_info_obj) + allow_get_packages + allow_any_instance_of(Chef::Provider::Package::Cab).to receive(:dism_command).with("/Get-PackageInfo /PackageName:\"Package_for_KB2664825~31bf3856ad364e35~amd64~~6.1.3.0\"").and_return(get_package_info_obj) + provider.load_current_resource + expect(provider.current_resource.version).to eql(["6.1.3.0"]) + expect(provider).to receive(:remove_package) + provider.run_action(:remove) + expect(new_resource).to be_updated_by_last_action + end + end + + context "Invalid package source" do + def package_version_stdout + package_version_stdout = <<-EOF + + Deployment Image Servicing and Management tool + Version: 6.1.7600.16385 + + Image Version: 6.1.7600.16385 + + An error occurred trying to open - c:\\temp\\test6.1-KB2664825-v3-x64.cab Error: 0x80070003 + Error: 3 + The system cannot find the path specified. + The DISM log file can be found at C:\\Windows\\Logs\\DISM\\dism.log. + EOF + end + + it "raises error for invalid source path or file" do + expect { provider.load_current_resource }.to raise_error(Chef::Exceptions::Package, "DISM: The system cannot find the path or file specified.") + end + end + end + + describe "#extract_msu_contents" do + it "extracts the msu contents by using mixlib shellout" do + expect(Mixlib::ShellOut).to receive(:new).with("expand -f:* msu_file destination", { :timeout => new_resource.timeout }) + expect(provider).to receive(:with_os_architecture) + provider.extract_msu_contents("msu_file", "destination") + end + end + + describe "#read_cab_files_from_xml" do + it "raises error if the xml file is not present" do + allow(Dir).to receive(:glob).and_return([]) + expect { provider.read_cab_files_from_xml("msu_dir") }.to raise_error(Chef::Exceptions::Package) + end + + it "parses xml file with single cab file" do + xml_file = File.join(CHEF_SPEC_DATA, "sample_msu1.xml") + allow(Dir).to receive(:glob).and_return([xml_file]) + cab_files = provider.read_cab_files_from_xml("msu_dir") + expect(cab_files).to eql(["msu_dir/IE10-Windows6.1-KB2859903-x86.CAB"]) + end + +# We couldn't find any msu file with multiple cab files in it. +# So we are not 100% sure about the structure of XML file in this case +# The specs below cover 2 possible XML formats + context "handles different xml formats for multiple cab files in the msu package" do + it "parses xml file with multiple <package> tags" do + xml_file = File.join(CHEF_SPEC_DATA, "sample_msu2.xml") + allow(Dir).to receive(:glob).and_return([xml_file]) + cab_files = provider.read_cab_files_from_xml("msu_dir") + expect(cab_files).to eql(["msu_dir/IE10-Windows6.1-KB2859903-x86.CAB", "msu_dir/abc.CAB"]) + end + + it "parses xml file with multiple <servicing> tags" do + xml_file = File.join(CHEF_SPEC_DATA, "sample_msu3.xml") + allow(Dir).to receive(:glob).and_return([xml_file]) + cab_files = provider.read_cab_files_from_xml("msu_dir") + expect(cab_files).to eql(["msu_dir/IE10-Windows6.1-KB2859903-x86.CAB", "msu_dir/abc.CAB"]) + end + end + end +end diff --git a/spec/unit/resource/msu_package_spec.rb b/spec/unit/resource/msu_package_spec.rb new file mode 100644 index 0000000000..349a382b31 --- /dev/null +++ b/spec/unit/resource/msu_package_spec.rb @@ -0,0 +1,49 @@ +# +# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>) +# Copyright:: Copyright 2008-2016, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Chef::Resource::MsuPackage do + let(:resource) { Chef::Resource::MsuPackage.new("test_pkg") } + + it "creates a new Chef::Resource::MsuPackage" do + expect(resource).to be_a_kind_of(Chef::Resource) + expect(resource).to be_a_kind_of(Chef::Resource::Package) + expect(resource).to be_a_instance_of(Chef::Resource::MsuPackage) + end + + it "sets resource name as :msu_package" do + expect(resource.resource_name).to eql(:msu_package) + end + + it "sets the source as it's name" do + expect(resource.source).to eql("test_pkg") + end + + it "sets the default action as :install" do + expect(resource.action).to eql(:install) + end + + it "raises error if invalid action is given" do + expect { resource.action "abc" }.to raise_error(Chef::Exceptions::ValidationFailed) + end + + it "coerce its name to a package_name" do + expect(resource.package_name).to eql("test_pkg") + end +end |