class Chef::Resource::WindowsFeatureDism

Public Instance Methods

add_to_feature_mash(feature_type, feature_string) click to toggle source

parse the feature string and add the values to the appropriate array in the strips trailing whitespace characters then split on n number of spaces + | + n number of spaces @return [void]

# File lib/chef/resource/windows_feature_dism.rb, line 197
def add_to_feature_mash(feature_type, feature_string)
  feature_details = feature_string.strip.split(/\s+[|]\s+/).first

  # dism on windows 2012+ isn't case sensitive so it's best to compare
  # lowercase lists so the user input doesn't need to be case sensitive
  # @todo when we're ready to remove windows 2008R2 the gating here can go away
  feature_details.downcase! unless node["platform_version"].to_f < 6.2
  node.override["dism_features_cache"][feature_type] << feature_details
end
fail_if_removed() click to toggle source

Fail if any of the packages are in a removed state @return [void]

# File lib/chef/resource/windows_feature_dism.rb, line 209
def fail_if_removed
  return if new_resource.source # if someone provides a source then all is well
  if node["platform_version"].to_f > 6.2
    return if registry_key_exists?('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing') && registry_value_exists?('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing', name: "LocalSourcePath") # if source is defined in the registry, still fine
  end
  removed = new_resource.feature_name & node["dism_features_cache"]["removed"]
  raise "The Windows feature#{'s' if removed.count > 1} #{removed.join(',')} #{removed.count > 1 ? 'are' : 'is'} have been removed from the host and cannot be installed." unless removed.empty?
end
fail_if_unavailable() click to toggle source

if any features are not supported on this release of Windows or have been deleted raise with a friendly message. At one point in time we just warned, but this goes against the behavior of ever other package provider in Chef and it isn't clear what you'd want if you passed an array and some features were available and others were not. @return [void]

# File lib/chef/resource/windows_feature_dism.rb, line 151
def fail_if_unavailable
  all_available = node["dism_features_cache"]["enabled"] +
    node["dism_features_cache"]["disabled"] +
    node["dism_features_cache"]["removed"]

  # the difference of desired features to install to all features is what's not available
  unavailable = (new_resource.feature_name - all_available)
  raise "The Windows feature#{'s' if unavailable.count > 1} #{unavailable.join(',')} #{unavailable.count > 1 ? 'are' : 'is'} not available on this version of Windows. Run 'dism /online /Get-Features' to see the list of available feature names." unless unavailable.empty?
end
features_to_delete() click to toggle source

@return [Array] features the user has requested to delete which need deleting

# File lib/chef/resource/windows_feature_dism.rb, line 136
def features_to_delete
  # the intersection of the features to remove & enabled/disabled features are what needs removing
  @remove ||= begin
    all_available = node["dism_features_cache"]["enabled"] +
      node["dism_features_cache"]["disabled"]
    new_resource.feature_name & all_available
  end
end
features_to_install() click to toggle source

@return [Array] features the user has requested to install which need installation

# File lib/chef/resource/windows_feature_dism.rb, line 116
def features_to_install
  @install ||= begin
    # disabled features are always available to install
    available_for_install = node["dism_features_cache"]["disabled"]

    # if the user passes a source then removed features are also available for installation
    available_for_install.concat(node["dism_features_cache"]["removed"]) if new_resource.source

    # the intersection of the features to install & disabled/removed(if passing source) features are what needs installing
    new_resource.feature_name & available_for_install
  end
end
features_to_remove() click to toggle source

@return [Array] features the user has requested to remove which need removing

# File lib/chef/resource/windows_feature_dism.rb, line 130
def features_to_remove
  # the intersection of the features to remove & enabled features are what needs removing
  @remove ||= new_resource.feature_name & node["dism_features_cache"]["enabled"]
end
raise_if_delete_unsupported() click to toggle source

Fail unless we're on windows 8+ / 2012+ where deleting a feature is supported @return [void]

# File lib/chef/resource/windows_feature_dism.rb, line 220
def raise_if_delete_unsupported
  raise Chef::Exceptions::UnsupportedAction, "#{self} :delete action not support on Windows releases before Windows 8/2012. Cannot continue!" unless node["platform_version"].to_f >= 6.2
end
reload_cached_dism_data() click to toggle source

run dism.exe to get a list of all available features and their state and save that to the node at node.override level. We do this because getting a list of features in dism takes at least a second and this data will be persisted across multiple resource runs which gives us a much faster run when no features actually need to be installed / removed. @return [void]

# File lib/chef/resource/windows_feature_dism.rb, line 167
def reload_cached_dism_data
  logger.trace("Caching Windows features available via dism.exe.")
  node.override["dism_features_cache"] = Mash.new
  node.override["dism_features_cache"]["enabled"] = []
  node.override["dism_features_cache"]["disabled"] = []
  node.override["dism_features_cache"]["removed"] = []

  # Grab raw feature information from dism command line
  raw_list_of_features = shell_out("dism.exe /Get-Features /Online /Format:Table /English").stdout

  # Split stdout into an array by windows line ending
  features_list = raw_list_of_features.split("\r\n")
  features_list.each do |feature_details_raw|
    case feature_details_raw
    when /Payload Removed/ # matches 'Disabled with Payload Removed'
      add_to_feature_mash("removed", feature_details_raw)
    when /Enable/ # matches 'Enabled' and 'Enable Pending' aka after reboot
      add_to_feature_mash("enabled", feature_details_raw)
    when /Disable/ # matches 'Disabled' and 'Disable Pending' aka after reboot
      add_to_feature_mash("disabled", feature_details_raw)
    end
  end
  logger.trace("The cache contains\n#{node['dism_features_cache']}")
end
to_lowercase_array(x) click to toggle source
# File lib/chef/resource/windows_feature_dism.rb, line 47
def to_lowercase_array(x)
  x = x.split(/\s*,\s*/) if x.is_a?(String) # split multiple forms of a comma separated list

  # dism on windows < 2012 is case sensitive so only downcase when on 2012+
  # @todo when we're really ready to remove support for Windows 2008 R2 this check can go away
  node["platform_version"].to_f < 6.2 ? x : x.map(&:downcase)
end