Class: Chef::Provider::Package::Rubygems

Inherits:
Chef::Provider::Package show all
Includes:
Mixin::GetSourceFromPackage, Mixin::Which
Defined in:
lib/chef/provider/package/rubygems.rb

Defined Under Namespace

Classes: AlternateGemEnvironment, CurrentGemEnvironment, GemEnvironment

Instance Attribute Summary collapse

Attributes inherited from Chef::Provider

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

Instance Method Summary collapse

Methods inherited from Chef::Provider::Package

#as_array, #check_resource_semantics!, #expand_options, #have_any_matching_version?, #lock_package, #multipackage_api_adapter, #options, #package_locked, #prepare_for_installation, #preseed_package, #reconfig_package, #removing_package?, #target_version_already_installed?, #unlock_package, #version_compare, #version_equals?

Methods included from Mixin::SubclassDirective

#subclass_directive

Methods inherited from Chef::Provider

action, action_description, action_descriptions, #action_nothing, #check_resource_semantics!, #compile_and_converge_action, #converge_by, #converge_if_changed, #cookbook_name, #description, #events, include_resource_dsl?, include_resource_dsl_module, #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

#initialize(new_resource, run_context = nil) ⇒ Rubygems

Returns a new instance of Rubygems.



452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
# File 'lib/chef/provider/package/rubygems.rb', line 452

def initialize(new_resource, run_context = nil)
  super
  @cleanup_gem_env = true
  if new_resource.gem_binary
    if new_resource.options && new_resource.options.is_a?(Hash)
      msg =  "options cannot be given as a hash when using an explicit gem_binary\n"
      msg << "in #{new_resource} from #{new_resource.source_line}"
      raise ArgumentError, msg
    end
    @gem_env = AlternateGemEnvironment.new(new_resource.gem_binary)
    logger.trace("#{new_resource} using gem '#{new_resource.gem_binary}'")
  elsif is_omnibus? && (!new_resource.instance_of? Chef::Resource::ChefGem)
    # Opscode Omnibus - The ruby that ships inside omnibus is only used for Chef
    # Default to installing somewhere more functional
    if new_resource.options && new_resource.options.is_a?(Hash)
      msg = [
        "Gem options must be passed to gem_package as a string instead of a hash when",
        "using this installation of #{ChefUtils::Dist::Infra::PRODUCT} because it runs with its own packaged Ruby. A hash",
        "may only be used when installing a gem to the same Ruby installation that #{ChefUtils::Dist::Infra::PRODUCT} is",
        "running under. See https://docs.chef.io/resources/gem_package/ for more information.",
        "Error raised at #{new_resource} from #{new_resource.source_line}",
      ].join("\n")
      raise ArgumentError, msg
    end
    gem_location = find_gem_by_path
    new_resource.gem_binary gem_location
    @gem_env = AlternateGemEnvironment.new(gem_location)
    logger.trace("#{new_resource} using gem '#{gem_location}'")
  else
    @gem_env = CurrentGemEnvironment.new
    @cleanup_gem_env = false
    logger.trace("#{new_resource} using gem from running ruby environment")
  end
end

Instance Attribute Details

#cleanup_gem_envObject (readonly)

Returns the value of attribute cleanup_gem_env.



444
445
446
# File 'lib/chef/provider/package/rubygems.rb', line 444

def cleanup_gem_env
  @cleanup_gem_env
end

#gem_envObject (readonly)

Returns the value of attribute gem_env.



443
444
445
# File 'lib/chef/provider/package/rubygems.rb', line 443

def gem_env
  @gem_env
end

Instance Method Details

#all_installed_versionsObject



545
546
547
# File 'lib/chef/provider/package/rubygems.rb', line 545

def all_installed_versions
  @all_installed_versions ||= @gem_env.installed_versions(Gem::Dependency.new(gem_dependency.name, ">= 0"))
end

#candidate_versionObject



594
595
596
597
598
599
600
# File 'lib/chef/provider/package/rubygems.rb', line 594

def candidate_version
  @candidate_version ||= if source_is_remote?
                           @gem_env.candidate_version_from_remote(gem_dependency, *gem_sources).to_s
                         else
                           @gem_env.candidate_version_from_file(gem_dependency, new_resource.source).to_s
                         end
end

#cleanup_after_convergeObject



587
588
589
590
591
592
# File 'lib/chef/provider/package/rubygems.rb', line 587

def cleanup_after_converge
  if @cleanup_gem_env
    logger.trace { "#{new_resource} resetting gem environment to default" }
    Gem.clear_paths
  end
end

#clear_sources?Boolean

If clear_sources is nil, clearing sources is implied if a source was added or if the global rubygems URL is set. If clear_sources is not nil, it has been set explicitly on the resource and its value should be used.

Returns:

  • (Boolean)


642
643
644
645
646
647
648
# File 'lib/chef/provider/package/rubygems.rb', line 642

def clear_sources?
  if new_resource.clear_sources.nil?
    !!(new_resource.source || Chef::Config[:rubygems_url])
  else
    new_resource.clear_sources
  end
end

#current_versionObject



522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# File 'lib/chef/provider/package/rubygems.rb', line 522

def current_version
  # If one or more matching versions are installed, the newest of them
  # is the current version
  if !matching_installed_versions.empty?
    gemspec = matching_installed_versions.max_by(&:version)
    logger.trace { "#{new_resource} found installed gem #{gemspec.name} version #{gemspec.version} matching #{gem_dependency}" }
    gemspec
    # If no version matching the requirements exists, the latest installed
    # version is the current version.
  elsif !all_installed_versions.empty?
    gemspec = all_installed_versions.max_by(&:version)
    logger.trace { "#{new_resource} newest installed version of gem #{gemspec.name} is #{gemspec.version}" }
    gemspec
  else
    logger.trace { "#{new_resource} no installed version found for #{gem_dependency}" }
    nil
  end
end

#define_resource_requirementsObject



578
579
580
581
582
583
584
585
# File 'lib/chef/provider/package/rubygems.rb', line 578

def define_resource_requirements
  super

  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

#find_gem_by_pathObject



501
502
503
# File 'lib/chef/provider/package/rubygems.rb', line 501

def find_gem_by_path
  which("gem", extra_path: RbConfig::CONFIG["bindir"])
end

#gem_binary_pathObject



633
634
635
# File 'lib/chef/provider/package/rubygems.rb', line 633

def gem_binary_path
  new_resource.gem_binary || "gem"
end

#gem_dependencyObject



505
506
507
# File 'lib/chef/provider/package/rubygems.rb', line 505

def gem_dependency
  Gem::Dependency.new(new_resource.package_name, new_resource.version)
end

#gem_sourcesObject



563
564
565
566
567
# File 'lib/chef/provider/package/rubygems.rb', line 563

def gem_sources
  srcs = [ new_resource.source ]
  srcs << (Chef::Config[:rubygems_url] || "https://rubygems.org") if include_default_source?
  srcs.flatten.compact
end

#include_default_source?Boolean

If include_default_source is nil, return true if the global rubygems_url was set or if clear_sources and source on the resource are not set. If include_default_source is not nil, it has been set explicitly on the resource and that value should be used.

Returns:

  • (Boolean)


555
556
557
558
559
560
561
# File 'lib/chef/provider/package/rubygems.rb', line 555

def include_default_source?
  if new_resource.include_default_source.nil?
    !!Chef::Config[:rubygems_url] || !(new_resource.source || new_resource.clear_sources)
  else
    new_resource.include_default_source
  end
end

#install_package(name, version) ⇒ Object

Installs the gem, using either the gems API or shelling out to gem according to the following criteria:

  1. Use gems API (Gem::DependencyInstaller) by default

  2. shell out to ‘gem install` when a String of options is given

  3. use gems API with options if a hash of options is given



614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
# File 'lib/chef/provider/package/rubygems.rb', line 614

def install_package(name, version)
  if source_is_remote? && new_resource.gem_binary.nil?
    if new_resource.options.nil?
      @gem_env.install(gem_dependency, sources: gem_sources)
    elsif new_resource.options.is_a?(Hash)
      options = new_resource.options
      options[:sources] = gem_sources
      @gem_env.install(gem_dependency, options)
    else
      install_via_gem_command(name, version)
    end
  elsif new_resource.gem_binary.nil?
    @gem_env.install(new_resource.source)
  else
    install_via_gem_command(name, version)
  end
  true
end

#install_via_gem_command(name, version) ⇒ Object



650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
# File 'lib/chef/provider/package/rubygems.rb', line 650

def install_via_gem_command(name, version)
  src = []
  if new_resource.source.is_a?(String) && new_resource.source =~ /\.gem$/i
    name = new_resource.source
  else
    src << "--clear-sources" if clear_sources?
    src += gem_sources.map { |s| "--source=#{s}" }
  end
  src_str = src.empty? ? "" : " #{src.join(" ")}"
  if !version.nil? && !version.empty?
    shell_out!("#{gem_binary_path} install #{name} -q #{rdoc_string} -v \"#{version}\"#{src_str}#{opts}", env: nil)
  else
    shell_out!("#{gem_binary_path} install \"#{name}\" -q #{rdoc_string} #{src_str}#{opts}", env: nil)
  end
end

#is_omnibus?Boolean

Returns:

  • (Boolean)


487
488
489
490
491
492
493
494
495
496
497
498
499
# File 'lib/chef/provider/package/rubygems.rb', line 487

def is_omnibus?
  if %r{/(#{ChefUtils::Dist::Org::LEGACY_CONF_DIR}|#{ChefUtils::Dist::Infra::SHORT}|#{ChefUtils::Dist::Workstation::DIR_SUFFIX})/embedded/bin}.match?(RbConfig::CONFIG["bindir"])
    logger.trace("#{new_resource} detected omnibus installation in #{RbConfig::CONFIG["bindir"]}")
    # Omnibus installs to a static path because of linking on unix, find it.
    true
  elsif RbConfig::CONFIG["bindir"].sub(/^\w:/, "") == "/#{ChefUtils::Dist::Org::LEGACY_CONF_DIR}/#{ChefUtils::Dist::Infra::SHORT}/embedded/bin"
    logger.trace("#{new_resource} detected omnibus installation in #{RbConfig::CONFIG["bindir"]}")
    # windows, with the drive letter removed
    true
  else
    false
  end
end

#load_current_resourceObject



569
570
571
572
573
574
575
576
# File 'lib/chef/provider/package/rubygems.rb', line 569

def load_current_resource
  @current_resource = Chef::Resource::Package::GemPackage.new(new_resource.name)
  current_resource.package_name(new_resource.package_name)
  if current_spec = current_version
    current_resource.version(current_spec.version.to_s)
  end
  current_resource
end

#matching_installed_versionsObject



541
542
543
# File 'lib/chef/provider/package/rubygems.rb', line 541

def matching_installed_versions
  @matching_installed_versions ||= @gem_env.installed_versions(gem_dependency)
end

#purge_package(name, version) ⇒ Object



692
693
694
# File 'lib/chef/provider/package/rubygems.rb', line 692

def purge_package(name, version)
  remove_package(name, version)
end

#remove_package(name, version) ⇒ Object



670
671
672
673
674
675
676
677
678
679
680
681
682
# File 'lib/chef/provider/package/rubygems.rb', line 670

def remove_package(name, version)
  if new_resource.gem_binary.nil?
    if new_resource.options.nil?
      @gem_env.uninstall(name, version)
    elsif new_resource.options.is_a?(Hash)
      @gem_env.uninstall(name, version, new_resource.options)
    else
      uninstall_via_gem_command(name, version)
    end
  else
    uninstall_via_gem_command(name, version)
  end
end

#source_is_remote?Boolean

Returns:

  • (Boolean)


509
510
511
512
513
514
515
516
517
518
519
520
# File 'lib/chef/provider/package/rubygems.rb', line 509

def source_is_remote?
  return true if new_resource.source.nil?
  return true if new_resource.source.is_a?(Array)

  scheme = URI.parse(new_resource.source).scheme
  # URI.parse gets confused by MS Windows paths with forward slashes.
  scheme = nil if /^[a-z]$/.match?(scheme)
  %w{http https}.include?(scheme)
rescue URI::InvalidURIError
  logger.trace("#{new_resource} failed to parse source '#{new_resource.source}' as a URI, assuming a local path")
  false
end

#uninstall_via_gem_command(name, version) ⇒ Object



684
685
686
687
688
689
690
# File 'lib/chef/provider/package/rubygems.rb', line 684

def uninstall_via_gem_command(name, version)
  if version
    shell_out!("#{gem_binary_path} uninstall #{name} -q -x -I -v \"#{version}\"#{opts}", env: nil)
  else
    shell_out!("#{gem_binary_path} uninstall #{name} -q -x -I -a#{opts}", env: nil)
  end
end

#upgrade_package(name, version) ⇒ Object



666
667
668
# File 'lib/chef/provider/package/rubygems.rb', line 666

def upgrade_package(name, version)
  install_package(name, version)
end

#version_requirement_satisfied?(current_version, new_version) ⇒ Boolean

Returns:

  • (Boolean)


602
603
604
605
606
# File 'lib/chef/provider/package/rubygems.rb', line 602

def version_requirement_satisfied?(current_version, new_version)
  return false unless current_version && new_version

  Gem::Requirement.new(new_version).satisfied_by?(Gem::Version.new(current_version))
end