Class: Chef::Provider::Package::Chocolatey

Inherits:
Chef::Provider::Package show all
Includes:
ReservedNames::Win32::API::CommandLineHelper
Defined in:
lib/chef/provider/package/chocolatey.rb

Constant Summary collapse

PATHFINDING_POWERSHELL_COMMAND =
"[System.Environment]::GetEnvironmentVariable('ChocolateyInstall', 'MACHINE')".freeze
CHOCO_MISSING_MSG =
<<~EOS.freeze
  Could not locate your Chocolatey install. To install chocolatey, we recommend
  the 'chocolatey_installer' resource.
  If Chocolatey is installed, ensure that the 'ChocolateyInstall' environment
  variable is correctly set. You can verify this with the PowerShell command
  '#{PATHFINDING_POWERSHELL_COMMAND}'.
EOS
@@choco_available_packages =

initialize our cache on load

nil
@@choco_config =
nil

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 ReservedNames::Win32::API::CommandLineHelper

#command_line_to_argv_w_helper

Methods inherited from Chef::Provider::Package

#as_array, #expand_options, #have_any_matching_version?, #initialize, #lock_package, #multipackage_api_adapter, #options, #package_locked, #prepare_for_installation, #preseed_package, #reconfig_package, #removing_package?, #target_version_already_installed?, #unlock_package, #version_equals?, #version_requirement_satisfied?

Methods included from Mixin::SubclassDirective

#subclass_directive

Methods inherited from Chef::Provider

action, action_description, action_descriptions, #action_nothing, #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

#cache_is_valid?Boolean

This checks that the repo list has not changed between now and when we last checked the cache



173
174
175
176
177
# File 'lib/chef/provider/package/chocolatey.rb', line 173

def cache_is_valid?
  return false if @@choco_config.nil? || (actual_config != @@choco_config)

  true
end

#candidate_versionArray

Lazy initializer for candidate_version. A nil value means that there is no candidate version and the package is not installable (generally an error).



88
89
90
# File 'lib/chef/provider/package/chocolatey.rb', line 88

def candidate_version
  @candidate_version ||= build_candidate_versions
end

#check_resource_semantics!Object

Override the superclass check. The semantics for our new_resource.source is not files to install from, but like the rubygem provider’s sources which are more like repos.



149
# File 'lib/chef/provider/package/chocolatey.rb', line 149

def check_resource_semantics!; end

#collect_package_requests(ignore_list: [], walk_resource_tree: false) ⇒ Array

Find the set of packages to ask the chocolatey server about

if walk_resource_tree is true, this finds all of the packages that we have referenced anywhere in our recipes - this is so we can attempt to query them all in a single transaction. However, currently we don’t do that - see the comment on available_packages for details of the why, but the TL;DR is that the public chocolatey servers do not support ‘or` type queries properly.

If walk_resource_tree is false, we don’t do any of that - we just filter the package list based on cache data. This is the default due to reasons explained in the comment on available_packages - the goal is to eventually turn this back on, hence the default false parameter here.



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/chef/provider/package/chocolatey.rb', line 194

def collect_package_requests(ignore_list: [], walk_resource_tree: false)
  return ["*"] if new_resource.bulk_query || Chef::Config[:always_use_bulk_chocolatey_package_list]

  if walk_resource_tree
    # Get to the root of the resource collection
    rc = run_context.parent_run_context || run_context
    rc = rc.parent_run_context while rc.parent_run_context

    package_collection = package_name_array
    package_collection += nested_package_resources(rc.resource_collection)
  else
    package_collection = package_name_array
  end
  # downcase the array and uniq.  sorted for easier testing...
  package_collection.uniq.sort.filter { |pkg| !ignore_list.include?(pkg) }
end

#define_resource_requirementsObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/chef/provider/package/chocolatey.rb', line 64

def define_resource_requirements
  super

  # The check that Chocolatey is installed is in #choco_exe.

  # Chocolatey source property points to an alternate feed
  # and not a package specific alternate source like other providers
  # so we want to assert candidates exist for the alternate source
  requirements.assert(:upgrade, :install) do |a|
    a.assertion { candidates_exist_for_all_uninstalled? }
    a.failure_message Chef::Exceptions::Package, "No candidate version available for #{packages_missing_candidates.join(", ")}"
    a.whyrun("Assuming a repository that offers #{packages_missing_candidates.join(", ")} would have been configured")
  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

#get_choco_versionObject



151
152
153
154
155
156
157
# File 'lib/chef/provider/package/chocolatey.rb', line 151

def get_choco_version
  # We need a different way to get the version than by simply calling "choco --version".
  # If the license file is installed (for business customers) but not the Chocolatey.Extension (because you're using the choco resource to install it)
  # then you get a license error. This method bypasses that by getting the version from the exe directly instead of invoking it.
  # deprecated: @get_choco_version ||= powershell_exec!("#{choco_exe} --version").result
  @get_choco_version ||= powershell_exec!("Get-ItemProperty #{choco_exe} | select-object -expandproperty versioninfo | select-object -expandproperty productversion").result
end

#install_package(names, versions) ⇒ Object

Install multiple packages via choco.exe



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/chef/provider/package/chocolatey.rb', line 96

def install_package(names, versions)
  name_versions_to_install = desired_name_versions.select { |n, v| lowercase_names(names).include?(n) }

  name_nil_versions = name_versions_to_install.select { |n, v| v.nil? }
  name_has_versions = name_versions_to_install.compact

  # choco does not support installing multiple packages with version pins
  name_has_versions.each do |name, version|
    choco_command("install", "-y", "--version", version, cmd_args, name)
  end

  # but we can do all the ones without version pins at once
  unless name_nil_versions.empty?
    cmd_names = name_nil_versions.keys
    choco_command("install", "-y", cmd_args, *cmd_names)
  end
end

#invalidate_cacheObject

invalidate cache for testing purposes



167
168
169
# File 'lib/chef/provider/package/chocolatey.rb', line 167

def invalidate_cache
  @@choco_config = nil
end

#load_current_resourceChef::Resource::ChocolateyPackage

Responsible for building the current_resource.



53
54
55
56
57
58
59
60
61
62
# File 'lib/chef/provider/package/chocolatey.rb', line 53

def load_current_resource
  @current_resource = Chef::Resource::ChocolateyPackage.new(new_resource.name)
  current_resource.package_name(new_resource.package_name)
  current_resource.version(build_current_versions)
  # Ensure that we have a working chocolatey executable - this used to be
  # covered off by loading the resource, but since that's no longer required,
  # we're going to put a quick check here to fail early!
  choco_exe
  current_resource
end

#query_commandObject

Choco V2 uses ‘Search’ for remote repositories and ‘List’ for local packages



160
161
162
163
164
# File 'lib/chef/provider/package/chocolatey.rb', line 160

def query_command
  return "list" if Gem::Dependency.new("", "< 1.4.0").match?("", get_choco_version)

  "search"
end

#remove_package(names, versions) ⇒ Object Also known as: purge_package

Remove multiple packages via choco.exe



140
141
142
# File 'lib/chef/provider/package/chocolatey.rb', line 140

def remove_package(names, versions)
  choco_command("uninstall", "-y", cmd_args(include_source: false), *names)
end

#upgrade_package(names, versions) ⇒ Object

Upgrade multiple packages via choco.exe



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/chef/provider/package/chocolatey.rb', line 118

def upgrade_package(names, versions)
  name_versions_to_install = desired_name_versions.select { |n, v| lowercase_names(names).include?(n) }

  name_nil_versions = name_versions_to_install.select { |n, v| v.nil? }
  name_has_versions = name_versions_to_install.compact

  # choco does not support installing multiple packages with version pins
  name_has_versions.each do |name, version|
    choco_command("upgrade", "-y", "--version", version, cmd_args, name)
  end

  # but we can do all the ones without version pins at once
  unless name_nil_versions.empty?
    cmd_names = name_nil_versions.keys
    choco_command("upgrade", "-y", cmd_args, *cmd_names)
  end
end