class Chef::Provider::Package::Cab

Public Instance Methods

cab_file_source() click to toggle source
# File lib/chef/provider/package/cab.rb, line 43
def cab_file_source
  @cab_file_source ||= uri_scheme?(new_resource.source) ? download_source_file : new_resource.source
end
cab_identity_from_cab_file() click to toggle source
# File lib/chef/provider/package/cab.rb, line 108
def cab_identity_from_cab_file
  stdout = dism_command("/Get-PackageInfo /PackagePath:\"#{cab_file_source}\"").stdout
  package_info = parse_dism_get_package_info(stdout)
  split_package_identity(package_info["package_information"]["package_identity"])
end
default_download_cache_path() click to toggle source
# File lib/chef/provider/package/cab.rb, line 61
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
dism_command(command) click to toggle source
# File lib/chef/provider/package/cab.rb, line 76
def dism_command(command)
  with_os_architecture(nil) do
    result = shell_out("dism.exe /Online /English #{command} /NoRestart", { timeout: new_resource.timeout })
    if result.exitstatus == -2146498530
      raise Chef::Exceptions::Package, "The specified package is not applicable to this image." if result.stdout.include?("0x800f081e")
      result.error!
    end
    result
  end
end
download_source_file() click to toggle source
# File lib/chef/provider/package/cab.rb, line 47
def download_source_file
  source_resource.run_action(:create)
  logger.trace("#{new_resource} fetched source file to #{source_resource.path}")
  source_resource.path
end
install_package(name, version) click to toggle source
# File lib/chef/provider/package/cab.rb, line 68
def install_package(name, version)
  dism_command("/Add-Package /PackagePath:\"#{cab_file_source}\"")
end
installed_packages() click to toggle source
# File lib/chef/provider/package/cab.rb, line 177
def installed_packages
  @packages ||= begin
    output = dism_command("/Get-Packages").stdout
    packages = parse_dism_get_packages(output)
    packages
  end
end
installed_version() click to toggle source
# File lib/chef/provider/package/cab.rb, line 87
def installed_version
  # e.g. Package_for_KB2975719~31bf3856ad364e35~amd64~~6.3.1.8
  package = new_cab_identity
  # Search for just the package name to catch a different version being installed
  logger.trace("#{new_resource} searching for installed package #{package['name']}")
  existing_package_identities = installed_packages.map do |p|
    split_package_identity(p["package_identity"])
  end
  found_packages = existing_package_identities.select do |existing_package_ident|
    existing_package_ident["name"] == package["name"]
  end
  if found_packages.empty?
    nil
  elsif found_packages.length == 1
    found_packages.first["version"]
  else
    # Presuming this won't happen, otherwise we need to handle it
    raise Chef::Exceptions::Package, "Found multiple packages installed matching name #{package['name']}, found: #{found_packages.length} matches"
  end
end
load_current_resource() click to toggle source
# File lib/chef/provider/package/cab.rb, line 35
def load_current_resource
  @current_resource = Chef::Resource::CabPackage.new(new_resource.name)
  current_resource.source(cab_file_source)
  new_resource.version(package_version)
  current_resource.version(installed_version)
  current_resource
end
new_cab_identity() click to toggle source
# File lib/chef/provider/package/cab.rb, line 114
def new_cab_identity
  logger.trace("#{new_resource} getting product version for package at #{cab_file_source}")
  @new_cab_identity ||= cab_identity_from_cab_file
end
package_version() click to toggle source
# File lib/chef/provider/package/cab.rb, line 119
def package_version
  new_cab_identity["version"].chomp
end
parse_dism_get_package_info(text) click to toggle source

returns a hash of package information given the output of dism /get-packageinfo

# File lib/chef/provider/package/cab.rb, line 138
def parse_dism_get_package_info(text)
  package_data = {}
  errors = []
  in_section = false
  section_headers = [ "Package information", "Custom Properties", "Features" ]
  text.each_line do |line|
    if line =~ /Error: (.*)/
      errors << $1.strip
    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] = {} 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?("0x80070003") || errors.include?("0x80070002")
      raise Chef::Exceptions::Package, "DISM: The system cannot find the path or file specified."
    elsif errors.include?("740")
      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
parse_dism_get_packages(text) click to toggle source

returns a hash of package state information given the output of dism /get-packages expected keys: package_identity

# File lib/chef/provider/package/cab.rb, line 125
def parse_dism_get_packages(text)
  packages = []
  text.each_line do |line|
    key, value = line.split(":") if line.start_with?("Package Identity")
    next if key.nil? || value.nil?
    package = {}
    package[key.downcase.strip.tr(" ", "_")] = value.strip.chomp
    packages << package
  end
  packages
end
remove_package(name, version) click to toggle source
# File lib/chef/provider/package/cab.rb, line 72
def remove_package(name, version)
  dism_command("/Remove-Package /PackagePath:\"#{cab_file_source}\"")
end
source_resource() click to toggle source
# File lib/chef/provider/package/cab.rb, line 53
def source_resource
  @source_resource ||= declare_resource(:remote_file, new_resource.name) do
    path default_download_cache_path
    source new_resource.source
    backup false
  end
end
split_package_identity(identity) click to toggle source
# File lib/chef/provider/package/cab.rb, line 171
def split_package_identity(identity)
  data = {}
  data["name"], data["publisher"], data["arch"], data["resource_id"], data["version"] = identity.split("~")
  data
end