summaryrefslogtreecommitdiff
path: root/lib/chef/provider
diff options
context:
space:
mode:
authorNimishaS <nimisha.sharad@msystechnologies.com>2016-10-07 09:01:00 +0000
committerNimishaS <nimisha.sharad@msystechnologies.com>2016-11-16 10:04:58 +0000
commit767b66e13f2989693f7adf1b3cebed2e6da6a12d (patch)
tree449b11fe6c5f1c41263c9287f744bbdc67cc9e86 /lib/chef/provider
parentbedcbd5f52448d24fdd7ab26ab79185c011beee3 (diff)
downloadchef-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 'lib/chef/provider')
-rw-r--r--lib/chef/provider/package/msu.rb163
1 files changed, 163 insertions, 0 deletions
diff --git a/lib/chef/provider/package/msu.rb b/lib/chef/provider/package/msu.rb
new file mode 100644
index 0000000000..e1f0d53a9c
--- /dev/null
+++ b/lib/chef/provider/package/msu.rb
@@ -0,0 +1,163 @@
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2015-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.
+#
+
+# msu_package leverages cab_package
+# The contents of msu file are extracted, which contains one or more cab files.
+# The extracted cab files are installed using Chef::Resource::Package::CabPackage
+# Reference: https://support.microsoft.com/en-in/kb/934307
+require "chef/provider/package"
+require "chef/resource/msu_package"
+require "chef/mixin/shell_out"
+require "chef/provider/package/cab"
+require "chef/util/path_helper"
+require "chef/mixin/uris"
+require "chef/mixin/checksum"
+
+class Chef
+ class Provider
+ class Package
+ class Msu < Chef::Provider::Package
+ use_inline_resources
+ include Chef::Mixin::ShellOut
+ include Chef::Mixin::Uris
+ include Chef::Mixin::Checksum
+
+ provides :msu_package, os: "windows"
+
+ def load_current_resource
+ @current_resource = Chef::Resource::MsuPackage.new(new_resource.name)
+
+ # download file if source is a url
+ msu_file = uri_scheme?(new_resource.source) ? download_source_file : @new_resource.source
+
+ # temp directory where the contents of msu file get extracted
+ @temp_directory = Dir.mktmpdir("chef")
+ extract_msu_contents(msu_file, @temp_directory)
+ @cab_files = read_cab_files_from_xml(@temp_directory)
+
+ unless @cab_files.empty?
+ current_resource.version(get_current_versions)
+ else
+ raise Chef::Exceptions::Package, "Corrupt MSU package: MSU package XML does not contain any cab file"
+ end
+ current_resource
+ end
+
+ def get_current_versions
+ @cab_files.map do |cabfile|
+ cab_pkg = get_cab_package(cabfile)
+ cab_pkg.installed_version
+ end
+ end
+
+ def get_candidate_versions
+ @cab_files.map do |cabfile|
+ cab_pkg = get_cab_package(cabfile)
+ cab_pkg.package_version
+ end
+ end
+
+ def candidate_version
+ @candidate_version ||= get_candidate_versions
+ end
+
+ def get_cab_package(cab_file)
+ cab_resource = @new_resource
+ cab_resource.source = cab_file
+ cab_pkg = Chef::Provider::Package::Cab.new(cab_resource, nil)
+ end
+
+ def download_source_file
+ source_resource.run_action(:create)
+ Chef::Log.debug("#{new_resource} fetched source file to #{source_resource.path}")
+ source_resource.path
+ end
+
+ def source_resource
+ @source_resource ||= declare_resource(:remote_file, new_resource.name) do
+ path default_download_cache_path
+ source new_resource.source
+ checksum new_resource.checksum
+ backup false
+ end
+ end
+
+ def default_download_cache_path
+ uri = ::URI.parse(new_resource.source)
+ filename = ::File.basename(::URI.unescape(uri.path))
+ file_cache_dir = Chef::FileCache.create_cache_path("package/")
+ Chef::Util::PathHelper.cleanpath("#{file_cache_dir}/#{filename}")
+ end
+
+ def install_package(name, version)
+ #use cab_package resource to install the extracted cab packages
+ @cab_files.each do |cab_file|
+ declare_resource(:cab_package, @new_resource.name) do
+ source cab_file
+ action :install
+ end
+ end
+ end
+
+ def remove_package(name, version)
+ #use cab_package provider to remove the extracted cab packages
+ @cab_files.each do |cab_file|
+ declare_resource(:cab_package, @new_resource.name) do
+ source cab_file
+ action :remove
+ end
+ end
+ end
+
+ def extract_msu_contents(msu_file, destination)
+ shellout = Mixlib::ShellOut.new("expand -f:* #{msu_file} #{destination}", { :timeout => @new_resource.timeout })
+ with_os_architecture(nil) do
+ shellout.run_command
+ end
+ end
+
+ # msu package can contain multiple cab files
+ # Reading cab files from xml to ensure the order of installation in case of multiple cab files
+ def read_cab_files_from_xml(msu_dir)
+ # get the file with .xml extension
+ xml_files = Dir.glob("#{msu_dir}/*.xml")
+ cab_files = []
+
+ if xml_files.empty?
+ raise Chef::Exceptions::Package, "Corrupt MSU package: MSU package doesn't contain any xml file"
+ else
+ # msu package contains only single xml file. So using xml_files.first is sufficient
+ doc = ::File.open("#{xml_files.first}") { |f| REXML::Document.new f }
+ locations = doc.elements.each("unattend/servicing/package/source") { |element| puts element.attributes["location"] }
+ locations.each do |loc|
+ cab_files << msu_dir + "/" + loc.attribute("location").value.split("\\")[1]
+ end
+
+ cab_files
+ end
+ cab_files
+ end
+
+ def cleanup_after_converge
+ # delete the temp directory where the contents of msu file are extracted
+ FileUtils.rm_rf(@temp_directory) if Dir.exists?(@temp_directory)
+ end
+ end
+ end
+ end
+end