Class: Chef::Provider::Package::Windows

Inherits:
Chef::Provider::Package show all
Includes:
Mixin::Checksum, Mixin::Uris
Defined in:
lib/chef/provider/package/windows.rb,
lib/chef/provider/package/windows/exe.rb,
lib/chef/provider/package/windows/msi.rb,
lib/chef/provider/package/windows/registry_uninstall_entry.rb

Defined Under Namespace

Classes: Exe, MSI, RegistryUninstallEntry

Instance Attribute Summary

Attributes inherited from Chef::Provider

#action, #after_resource, #current_resource, #logger, #new_resource, #run_context

Instance Method Summary collapse

Methods included from Mixin::Checksum

#checksum, #checksum_match?, #short_cksum

Methods included from Mixin::Uris

#as_uri, #uri_scheme?

Methods inherited from Chef::Provider::Package

#as_array, #check_resource_semantics!, #expand_options, #initialize, #lock_package, #multipackage_api_adapter, #options, #package_locked, #prepare_for_installation, #preseed_package, #purge_package, #reconfig_package, #removing_package?, #unlock_package, #upgrade_package, #version_requirement_satisfied?

Methods included from Mixin::SubclassDirective

#subclass_directive

Methods inherited from Chef::Provider

action, action_description, action_descriptions, #action_nothing, #check_resource_semantics!, #cleanup_after_converge, #compile_and_converge_action, #converge_by, #converge_if_changed, #cookbook_name, #description, #events, include_resource_dsl?, include_resource_dsl_module, #initialize, #introduced, #load_after_resource, #node, #process_resource_requirements, provides, provides?, #recipe_name, #requirements, #resource_collection, #resource_updated?, #run_action, #set_updated_status, supports?, use, use_inline_resources, #validate_required_properties!, #whyrun_mode?, #whyrun_supported?

Methods included from Mixin::Provides

#provided_as, #provides, #provides?

Methods included from Mixin::DescendantsTracker

#descendants, descendants, direct_descendants, #direct_descendants, find_descendants_by_name, #find_descendants_by_name, #inherited, store_inherited

Methods included from Mixin::LazyModuleInclude

#descendants, #include, #included

Methods included from Mixin::PowershellOut

#powershell_out, #powershell_out!

Methods included from Mixin::WindowsArchitectureHelper

#assert_valid_windows_architecture!, #disable_wow64_file_redirection, #forced_32bit_override_required?, #is_i386_process_on_x86_64_windows?, #node_supports_windows_architecture?, #node_windows_architecture, #restore_wow64_file_redirection, #valid_windows_architecture?, #with_os_architecture, #wow64_architecture_override_required?, #wow64_directory

Methods included from DSL::Secret

#default_secret_config, #default_secret_service, #secret, #with_secret_config, #with_secret_service

Methods included from DSL::RenderHelpers

#render_json, #render_toml, #render_yaml

Methods included from DSL::ReaderHelpers

#parse_file, #parse_json, #parse_toml, #parse_yaml

Methods included from DSL::Powershell

#ps_credential

Methods included from DSL::RegistryHelper

#registry_data_exists?, #registry_get_subkeys, #registry_get_values, #registry_has_subkeys?, #registry_key_exists?, #registry_value_exists?

Methods included from DSL::ChefVault

#chef_vault, #chef_vault_item, #chef_vault_item_for_environment

Methods included from DSL::DataQuery

#data_bag, #data_bag_item, #search, #tagged?

Methods included from EncryptedDataBagItem::CheckEncrypted

#encrypted?

Methods included from DSL::PlatformIntrospection

#older_than_win_2012_or_8?, #platform?, #platform_family?, #value_for_platform, #value_for_platform_family

Methods included from DSL::Recipe

#exec, #have_resource_class_for?, #resource_class_for

Methods included from DSL::Definitions

add_definition, #evaluate_resource_definition, #has_resource_definition?

Methods included from DSL::Resources

add_resource_dsl, remove_resource_dsl

Methods included from DSL::Cheffish

load_cheffish

Methods included from DSL::RebootPending

#reboot_pending?

Methods included from DSL::IncludeRecipe

#include_recipe, #load_recipe

Methods included from Mixin::NotifyingBlock

#notifying_block, #subcontext_block

Methods included from DSL::DeclareResource

#build_resource, #declare_resource, #delete_resource, #delete_resource!, #edit_resource, #edit_resource!, #find_resource, #find_resource!, #resources, #with_run_context

Methods included from DSL::Compliance

#include_input, #include_profile, #include_waiver

Constructor Details

This class inherits a constructor from Chef::Provider::Package

Instance Method Details

#candidate_versionString

Returns candidate_version.

Returns:

  • (String)

    candidate_version



157
158
159
# File 'lib/chef/provider/package/windows.rb', line 157

def candidate_version
  @candidate_version ||= (new_resource.version || "latest")
end

#current_version_arrayArray

this package provider does not support package arrays However, There may be multiple versions for a single package so the first element may be a nested array

FIXME: this breaks the semantics of the superclass and needs to get unwound. Since these package providers don’t support multipackage they can’t put multiple versions into this array. The windows package managers need this in order to uninstall multiple installed version, and they should track that in something like an ‘uninstall_version_array` of their own. The superclass does not implement this kind of feature. Doing this here breaks LSP and will create bugs since the superclass will not expect it at all. The `current_resource.version` also MUST NOT be an array if the package provider is not multipackage. The existing implementation of package_provider.installed_version should probably be what `uninstall_version_array` is, and then that list should be sorted and last/first’d into the current_resource.version. The current_version_array method was not intended to be overwritten by subclasses (but ruby provides no feature to block doing so – it is already marked as private).

Returns:

  • (Array)

    current_version(s) as an array



177
178
179
# File 'lib/chef/provider/package/windows.rb', line 177

def current_version_array
  [ current_resource.version ]
end

#define_resource_requirementsObject



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/chef/provider/package/windows.rb', line 38

def define_resource_requirements
  if new_resource.checksum
    requirements.assert(:install) do |a|
      a.assertion { checksum_match?(new_resource.checksum, checksum(source_location)) }
      a.failure_message Chef::Exceptions::Package, "Checksum on resource (#{short_cksum(new_resource.checksum)}) does not match checksum on content (#{short_cksum(source_location)})"
    end
  end

  requirements.assert(:install) do |a|
    a.assertion { new_resource.source || msi? }
    a.failure_message Chef::Exceptions::NoWindowsPackageSource, "Source for package #{new_resource.package_name} must be specified in the resource's source property for package to be installed because the package_name property is used to test for the package installation state for this package type."
  end

  unless uri_scheme?(new_resource.source)
    requirements.assert(:install) do |a|
      a.assertion { ::File.exist?(new_resource.source) }
      a.failure_message Chef::Exceptions::Package, "Source for package #{new_resource.package_name} does not exist"
      a.whyrun "Assuming source file #{new_resource.source} would have been created."
    end
  end

  requirements.assert(:all_actions) do |a|
    a.assertion { !new_resource.environment }
    a.failure_message Chef::Exceptions::Package, "The environment property is not supported for package resources on this platform"
  end
end

#have_any_matching_version?Boolean

Returns:

  • (Boolean)


198
199
200
# File 'lib/chef/provider/package/windows.rb', line 198

def have_any_matching_version?
  target_version_already_installed?(current_resource.version, new_resource.version)
end

#install_package(name, version) ⇒ Object

Chef::Provider::Package action_install + action_remove call install_package + remove_package Pass those calls to the correct sub-provider



142
143
144
# File 'lib/chef/provider/package/windows.rb', line 142

def install_package(name, version)
  package_provider.install_package
end

#installer_typeObject



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/chef/provider/package/windows.rb', line 90

def installer_type
  # Depending on the installer, we may need to examine installer_type or
  # source attributes, or search for text strings in the installer file
  # binary to determine the installer type for the user. Since the file
  # must be on disk to do so, we have to make this choice in the provider.
  @installer_type ||= begin
    return :msi if msi?

    if new_resource.installer_type
      new_resource.installer_type
    elsif source_location.nil?
      inferred_registry_type
    else
      basename = ::File.basename(source_location)
      file_extension = basename.split(".").last.downcase

      # search the binary file for installer type
      ::Kernel.open(::File.expand_path(source_location), "rb") do |io|
        filesize = io.size
        bufsize = 4096 # read 4K buffers
        overlap = 16 # bytes to overlap between buffer reads

        until io.eof
          contents = io.read(bufsize)

          case contents
          when /inno/i # Inno Setup
            return :inno
          when /wise/i # Wise InstallMaster
            return :wise
          when /nullsoft/i # Nullsoft Scriptable Install System
            return :nsis
          end

          if io.tell < filesize
            io.seek(io.tell - overlap)
          end
        end

        # if file is named 'setup.exe' assume installshield
        if basename == "setup.exe"
          :installshield
        else
          raise Chef::Exceptions::CannotDetermineWindowsInstallerType, "Installer type for Windows Package '#{new_resource.package_name}' not specified and cannot be determined from file extension '#{file_extension}'"
        end
      end
    end
  end
end

#load_current_resourceObject



65
66
67
68
69
70
71
72
73
74
75
# File 'lib/chef/provider/package/windows.rb', line 65

def load_current_resource
  if uri_scheme?(new_resource.source) && action == :install
    download_source_file
  end

  @current_resource = Chef::Resource::WindowsPackage.new(new_resource.name)
  current_resource.version(package_provider.installed_version)
  new_resource.version(package_provider.package_version) if package_provider.package_version

  current_resource
end

#new_version_arrayArray

Returns new_version(s) as an array.

Returns:

  • (Array)

    new_version(s) as an array



151
152
153
154
# File 'lib/chef/provider/package/windows.rb', line 151

def new_version_array
  # Because the one in the parent caches things
  [new_resource.version]
end

#package_providerObject



77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/chef/provider/package/windows.rb', line 77

def package_provider
  @package_provider ||= case installer_type
    when :msi
      logger.trace("#{new_resource} is MSI")
      require_relative "windows/msi"
      Chef::Provider::Package::Windows::MSI.new(resource_for_provider, uninstall_registry_entries)
    else
      logger.trace("#{new_resource} is EXE with type '#{installer_type}'")
      require_relative "windows/exe"
      Chef::Provider::Package::Windows::Exe.new(resource_for_provider, installer_type, uninstall_registry_entries)
                        end
end

#remove_package(name, version) ⇒ Object



146
147
148
# File 'lib/chef/provider/package/windows.rb', line 146

def remove_package(name, version)
  package_provider.remove_package
end

#target_version_already_installed?(current_version, new_version) ⇒ Boolean

Returns true if new_version is equal to or included in current_version.

Parameters:

  • current_version (String)

    one or more versions currently installed

  • new_version (String)

    version of the new resource

Returns:

  • (Boolean)

    true if new_version is equal to or included in current_version



185
186
187
# File 'lib/chef/provider/package/windows.rb', line 185

def target_version_already_installed?(current_version, new_version)
  version_equals?(current_version, new_version)
end

#version_equals?(current_version, new_version) ⇒ Boolean

Returns:

  • (Boolean)


189
190
191
192
193
194
195
196
# File 'lib/chef/provider/package/windows.rb', line 189

def version_equals?(current_version, new_version)
  logger.trace("Checking if #{new_resource} version '#{new_version}' is already installed. #{current_version} is currently installed")
  if current_version.is_a?(Array)
    current_version.include?(new_version)
  else
    new_version == current_version
  end
end