summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorVasu1105 <vasundhara.jagdale@clogeny.com>2016-09-07 11:32:01 +0530
committerVasu1105 <vasundhara.jagdale@clogeny.com>2016-09-30 15:10:46 +0530
commit09b44d56bc3f9285360b6b93f7798e6de4dd8dfd (patch)
treef50f4ad4403c80f8bb3db301b6c5c1bab8610055 /lib
parent09886bad1411faa86b9e716712b4dad62b8330bd (diff)
downloadchef-09b44d56bc3f9285360b6b93f7798e6de4dd8dfd.tar.gz
Added resource and provider for cab package
Reverted changes Adding resource and provider for cab file support Added method identifying package version for installation and removal of package Updated code to idetitfy installed version refactored some code Refactored code for parsing dism get package output Fixed rubocop issue, working on specs created spec files Working on unit specs Added unit specs and working on functional specs Working on functional specs and fixed rubocop errors Fixed failing unit test cases Resolved rubucop style error Removed functional specs Fixed error and working on functional specs Added missing functional spec file
Diffstat (limited to 'lib')
-rw-r--r--lib/chef/provider/package/cab.rb147
-rw-r--r--lib/chef/provider/package/windows.rb13
-rw-r--r--lib/chef/provider/package/windows/cab.rb152
-rw-r--r--lib/chef/providers.rb1
-rw-r--r--lib/chef/resource/cab_package.rb44
-rw-r--r--lib/chef/resources.rb1
6 files changed, 194 insertions, 164 deletions
diff --git a/lib/chef/provider/package/cab.rb b/lib/chef/provider/package/cab.rb
new file mode 100644
index 0000000000..f5af1a86b4
--- /dev/null
+++ b/lib/chef/provider/package/cab.rb
@@ -0,0 +1,147 @@
+#
+# Author:: Vasundhara Jagdale (<vasundhara.jagdale@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.
+#
+
+require "chef/provider/package"
+require "chef/resource/cab_package"
+require "chef/mixin/powershell_out"
+
+class Chef
+ class Provider
+ class Package
+ class Cab < Chef::Provider::Package
+ include Chef::Mixin::PowershellOut
+
+ provides :cab_package, os: "windows"
+
+ def load_current_resource
+ @current_resource = Chef::Resource::CabPackage.new(new_resource.name)
+ current_resource.source(new_resource.source)
+ new_resource.version(package_version)
+ current_resource.version(installed_version)
+ current_resource
+ end
+
+ def install_package(name, version)
+ dism_command("/Add-Package /PackagePath:\"#{@new_resource.source}\"")
+ end
+
+ def remove_package(name, version)
+ dism_command("/Remove-Package /PackagePath:\"#{@new_resource.source}\"")
+ end
+
+ def dism_command(command)
+ powershell_out("dism.exe /Online #{command} /NoRestart", { :timeout => @new_resource.timeout })
+ end
+
+ def installed_version
+ stdout = dism_command("/Get-PackageInfo /PackagePath:\"#{@new_resource.source}\"").stdout
+ package_info = parse_dism_get_package_info(stdout)
+ # e.g. Package_for_KB2975719~31bf3856ad364e35~amd64~~6.3.1.8
+ package = split_package_identity(package_info["package_information"]["package_identity"])
+ # Search for just the package name to catch a different version being installed
+ Chef::Log.debug("#{@new_resource} searching for installed package #{package['name']}")
+ found_packages = installed_packages.select { |p| p["package_identity"] =~ /^#{package['name']}~/ }
+ if found_packages.length == 0
+ nil
+ elsif found_packages.length == 1
+ stdout = dism_command("/Get-PackageInfo /PackageName:\"#{found_packages.first["package_identity"]}\"").stdout
+ find_version(stdout)
+ else
+ # Presuming this won't happen, otherwise we need to handle it
+ raise Chef::Exceptions::Package, "Found mutliple packages installed matching name #{package['name']}, found: #{found_packages.length} matches"
+ end
+ end
+
+ def package_version
+ Chef::Log.debug("#{@new_resource} getting product version for package at #{@new_resource.source}")
+ stdout = dism_command("/Get-PackageInfo /PackagePath:\"#{@new_resource.source}\"").stdout
+ find_version(stdout)
+ end
+
+ def find_version(stdout)
+ package_info = parse_dism_get_package_info(stdout)
+ package = split_package_identity(package_info["package_information"]["package_identity"])
+ package["version"]
+ end
+
+ # returns a hash of package state information given the output of dism /get-packages
+ # expected keys: package_identity
+ def parse_dism_get_packages(text)
+ packages = Array.new
+ text.each_line do |line|
+ key, value = line.split(":") if line.start_with?("Package Identity")
+ unless key.nil? || value.nil?
+ package = Hash.new
+ package[key.downcase.strip.tr(" ", "_")] = value.strip.chomp
+ packages << package
+ end
+ end
+ packages
+ end
+
+ # returns a hash of package information given the output of dism /get-packageinfo
+ def parse_dism_get_package_info(text)
+ package_data = Hash.new
+ errors = Array.new
+ in_section = false
+ section_headers = [ "Package information", "Custom Properties", "Features" ]
+ text.each_line do |line|
+ if line =~ /Error: (.*)/
+ errors << $1
+ elsif section_headers.any? { |header| line =~ /^(#{header})/ }
+ in_section = $1.downcase.tr(" ", "_")
+ elsif line =~ /(.*) ?: (.*)/
+ v = $2 # has to be first or the gsub below replaces this variable
+ k = $1.downcase.strip.tr(" ", "_")
+ if in_section
+ package_data[in_section] = Hash.new unless package_data[in_section]
+ package_data[in_section][k] = v
+ else
+ package_data[k] = v
+ end
+ end
+ end
+ unless errors.empty?
+ if !(errors & ["0x80070003\r", "0x80070002\r"]).empty?
+ raise Chef::Exceptions::Package, "DISM: The system cannot find the path or file specified."
+ elsif errors.include?("740\r")
+ raise Chef::Exceptions::Package, "DISM: Error 740: Elevated permissions are required to run DISM."
+ else
+ raise Chef::Exceptions::Package, "Unknown errors encountered parsing DISM output: #{errors}"
+ end
+ end
+ package_data
+ end
+
+ def split_package_identity(identity)
+ data = Hash.new
+ data["name"], data["publisher"], data["arch"], data["resource_id"], data["version"] = identity.split("~")
+ data
+ end
+
+ def installed_packages
+ @packages ||= begin
+ output = dism_command("/Get-Packages").stdout
+ packages = parse_dism_get_packages(output)
+ packages
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/windows.rb b/lib/chef/provider/package/windows.rb
index 0a655f5238..b11bcf5192 100644
--- a/lib/chef/provider/package/windows.rb
+++ b/lib/chef/provider/package/windows.rb
@@ -21,7 +21,6 @@ require "chef/resource/windows_package"
require "chef/provider/package"
require "chef/util/path_helper"
require "chef/mixin/checksum"
-require 'pry'
class Chef
class Provider
@@ -52,6 +51,7 @@ class Chef
current_resource.version(package_provider.installed_version)
new_resource.version(package_provider.package_version) if package_provider.package_version
end
+
current_resource
end
@@ -62,10 +62,6 @@ class Chef
Chef::Log.debug("#{new_resource} is MSI")
require "chef/provider/package/windows/msi"
Chef::Provider::Package::Windows::MSI.new(resource_for_provider, uninstall_registry_entries)
- when :cab
- Chef::Log.debug("#{new_resource} is CAB")
- require "chef/provider/package/windows/cab"
- Chef::Provider::Package::Windows::CAB.new(resource_for_provider)
else
Chef::Log.debug("#{new_resource} is EXE with type '#{installer_type}'")
require "chef/provider/package/windows/exe"
@@ -82,8 +78,6 @@ class Chef
@installer_type ||= begin
return :msi if msi?
- return :cab if cab?
-
if new_resource.installer_type
new_resource.installer_type
elsif source_location.nil?
@@ -271,11 +265,6 @@ class Chef
::File.extname(source_location).casecmp(".msi").zero?
end
end
-
- def cab?
- return true if new_resource.installer_type == :cab
- ::File.extname(source_location).casecmp(".cab").zero?
- end
end
end
end
diff --git a/lib/chef/provider/package/windows/cab.rb b/lib/chef/provider/package/windows/cab.rb
deleted file mode 100644
index 856460bfc9..0000000000
--- a/lib/chef/provider/package/windows/cab.rb
+++ /dev/null
@@ -1,152 +0,0 @@
-require 'chef/mixin/windows_architecture_helper'
-require "chef/mixin/shell_out"
-require 'chef/util/path_helper'
-
-class Chef
- class Provider
- class Package
- class Windows
- class CAB
- include Chef::Mixin::ShellOut
- include Chef::Mixin::WindowsArchitectureHelper
-
- def initialize(resource)
- @new_resource = resource
- end
-
- attr_reader :new_resource
-
- def installed_version
- Chef::Log.debug("#{@new_resource} getting product version for package at #{@new_resource.source}")
- stdout = dism_command("/Get-PackageInfo /PackagePath:\"#{@new_resource.source}\"").stdout
- package_info = parse_dism_get_package_info(stdout)
- # e.g. Package_for_KB2975719~31bf3856ad364e35~amd64~~6.3.1.8
- package = split_package_identity(package_info['package_information']['package_identity'])
- # Search for just the package name to catch a different version being installed
- Chef::Log.debug("#{@new_resource} searching for installed package #{package['name']}")
- found_packages = installed_packages.select { |p| p['package_identity'] =~ /^#{package['name']}~/ }
- if found_packages.length == 0
- nil
- elsif found_packages.length == 1
- package['version']
- else
- # Presuming this won't happen, otherwise we need to handle it
- raise Chef::Exceptions::Package, "Found mutliple packages installed matching name #{package['name']}, found: #{found_packages.length} matches"
- end
- end
-
- def package_version
- Chef::Log.debug("#{@new_resource} getting product version for package at #{@new_resource.source}")
- stdout = dism_command("/Get-PackageInfo /PackagePath:\"#{@new_resource.source}\"").stdout
- package_info = parse_dism_get_package_info(stdout)
- package = split_package_identity(package_info['package_information']['package_identity'])
- package['version']
- end
-
- def install_package
- Chef::Log.info("#{@new_resource} installing Windows CAB package '#{@new_resource.source}'")
- dism_command("/Add-Package /PackagePath:\"#{@new_resource.source}\"")
- end
-
- def remove_package
- Chef::Log.debug("#{@new_resource} removing Windows CAB package '#{@new_resource.source}'")
- dism_command("/Remove-Package /PackagePath:\"#{@new_resource.source}\"")
- end
-
- # Runs a command through DISM, and returns a Mixlib::ShellOut object
- # For example, output can be accessed like dism_command("/Get-CurrentEdition").stdout
- def dism_command(command)
- shellout = Mixlib::ShellOut.new("dism.exe /Online #{command} /NoRestart", {:timeout => @new_resource.timeout, :returns => @new_resource.returns})
- with_os_architecture(@node) do
- shellout.run_command
- end
- end
-
- # returns a hash of package state information given the output of dism /get-packages
- # expected keys: package_identity, state, release_type, install_time
- def parse_dism_get_packages(text)
- packages = Array.new
- package = Hash.new
- inside_package_listing = false
- text.each_line do |line|
- # Skip the headers
- if line =~ /^Packages listing:/
- inside_package_listing = true
- next
- end
-
- next unless inside_package_listing
-
- if line.chomp.empty?
- # Between packages so save it to the collection
- unless package.empty?
- packages << package
- package = Hash.new
- end
- end
-
- if line =~ /(.*) : (.*)/
- v = $2.chomp # has to be first or the gsub below replaces this variable
- k = $1.downcase.strip.gsub(/ /,"_")
- package[k] = v
- end
- end
- packages
- end
-
- # returns a hash of package information given the output of dism /get-packageinfo
- def parse_dism_get_package_info(text)
- package_data = Hash.new
- errors = Array.new
- in_section = false
- # Version/Image Version are Windows Versions
- # TODO: Verfiy we're parsing Features in a useful way
- section_headers = [ "Package information", "Custom Properties", "Features" ]
-
- text.each_line do |line|
- if line =~ /Error: (.*)/
- errors << $1
- elsif section_headers.any? { |header| line =~ /^(#{header})/ }
- in_section = $1.downcase.gsub(/ /,"_")
- elsif line =~ /(.*) ?: (.*)/
- v = $2 # has to be first or the gsub below replaces this variable
- k = $1.downcase.strip.gsub(/ /,"_")
- if in_section
- package_data[in_section] = Hash.new unless package_data[in_section]
- package_data[in_section][k] = v
- else
- package_data[k] = v
- end
- end
- end
-
- unless errors.empty?
- if errors.include?("87")
- Chef::Log.debug("DISM: Error 87: Package unknown (not installed)")
- return nil
- else
- raise Chef::Exceptions::Package, "Unknown errors encountered parsing DISM output: #{errors}"
- end
- end
- package_data
- end
-
- def split_package_identity(identity)
- data = Hash.new
- data['name'], data['publisher'], data['arch'], data['resource_id'], data['version'] = identity.split('~')
- data
- end
-
- def installed_packages
- @@package_cache ||= begin
- # TODO: A resource attribute to invalidate the cache
- output = dism_command("/Get-Packages").stdout
- packages = parse_dism_get_packages(output)
- packages
- end
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index affa5ca2c1..596629290b 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -83,6 +83,7 @@ require "chef/provider/package/zypper"
require "chef/provider/package/solaris"
require "chef/provider/package/smartos"
require "chef/provider/package/aix"
+require "chef/provider/package/cab"
require "chef/provider/service/arch"
require "chef/provider/service/freebsd"
diff --git a/lib/chef/resource/cab_package.rb b/lib/chef/resource/cab_package.rb
new file mode 100644
index 0000000000..b7acfb0ec9
--- /dev/null
+++ b/lib/chef/resource/cab_package.rb
@@ -0,0 +1,44 @@
+#
+# Author:: Vasundhara Jagdale (<vasundhara.jagdale@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 "chef/resource/package"
+require "chef/mixin/uris"
+
+class Chef
+ class Resource
+ class CabPackage < Chef::Resource::Package
+ include Chef::Mixin::Uris
+
+ provides :cab_package, os: "windows"
+
+ allowed_actions :install, :remove
+
+ def initialize(name, run_context = nil)
+ super
+ @resource_name = :cab_package
+ end
+
+ property :source, String,
+ coerce: (proc do |s|
+ unless s.nil?
+ uri_scheme?(s) ? s : Chef::Util::PathHelper.canonical_path(s, false)
+ end
+ end)
+ end
+ end
+end
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index 2afd47a8f4..9ff772353b 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -95,3 +95,4 @@ require "chef/resource/yum_repository"
require "chef/resource/lwrp_base"
require "chef/resource/bff_package"
require "chef/resource/zypper_package"
+require "chef/resource/cab_package"