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

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

Defined Under Namespace

Classes: AlternateGemEnvironment, CurrentGemEnvironment, GemEnvironment

Constant Summary

Constants included from Mixin::ShellOut

Mixin::ShellOut::DEPRECATED_OPTIONS

Instance Attribute Summary collapse

Attributes inherited from Chef::Provider

#action, #cookbook_name, #current_resource, #new_resource, #recipe_name, #run_context

Instance Method Summary collapse

Methods inherited from Chef::Provider::Package

#action_install, #action_purge, #action_reconfig, #action_remove, #action_upgrade, #as_array, #check_resource_semantics!, #define_resource_requirements, #expand_options, #get_preseed_file, #have_any_matching_version?, #multipackage_api_adapter, #preseed_package, #preseed_resource, #reconfig_package, #removing_package?, #whyrun_supported?

Methods included from Mixin::SubclassDirective

#subclass_directive

Methods included from Mixin::ShellOut

#run_command_compatible_options, #shell_out, #shell_out!, #shell_out_with_systems_locale, #shell_out_with_systems_locale!

Methods included from Mixin::Command

#chdir_or_tmpdir, #handle_command_failures, #output_of_command, #run_command, #run_command_and_return_stdout_stderr, #run_command_with_systems_locale

Methods included from Mixin::Command::Windows

#popen4

Methods included from Mixin::Command::Unix

#popen4

Methods inherited from Chef::Provider

#action_nothing, #check_resource_semantics!, #converge_by, #converge_if_changed, #define_resource_requirements, #events, include_resource_dsl, include_resource_dsl_module, #node, #process_resource_requirements, provides, provides?, #requirements, #resource_collection, #resource_updated?, #run_action, #set_updated_status, supports?, use_inline_resources, #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 DeprecatedLWRPClass

#const_missing, #register_deprecated_lwrp_class

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

Constructor Details

#initialize(new_resource, run_context = nil) ⇒ Rubygems

Returns a new instance of Rubygems.



361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/chef/provider/package/rubygems.rb', line 361

def initialize(new_resource, run_context = nil)
  super
  @cleanup_gem_env = true
  if new_resource.gem_binary
    if new_resource.options && new_resource.options.kind_of?(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)
    Chef::Log.debug("#{@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.kind_of?(Hash)
      msg = [
        "Gem options must be passed to gem_package as a string instead of a hash when",
        "using this installation of Chef because it runs with its own packaged Ruby. A hash",
        "may only be used when installing a gem to the same Ruby installation that Chef is",
        "running under.  See https://docs.chef.io/resource_gem_package.html 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)
    Chef::Log.debug("#{@new_resource} using gem '#{gem_location}'")
  else
    @gem_env = CurrentGemEnvironment.new
    @cleanup_gem_env = false
    Chef::Log.debug("#{@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.



350
351
352
# File 'lib/chef/provider/package/rubygems.rb', line 350

def cleanup_gem_env
  @cleanup_gem_env
end

#gem_envObject (readonly)

Returns the value of attribute gem_env.



349
350
351
# File 'lib/chef/provider/package/rubygems.rb', line 349

def gem_env
  @gem_env
end

Instance Method Details

#all_installed_versionsObject



457
458
459
460
461
# File 'lib/chef/provider/package/rubygems.rb', line 457

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

#candidate_versionObject



483
484
485
486
487
488
489
490
491
492
493
# File 'lib/chef/provider/package/rubygems.rb', line 483

def candidate_version
  @candidate_version ||= begin
                           if target_version_already_installed?(@current_resource.version, @new_resource.version)
                             nil
                           elsif 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
end

#cleanup_after_convergeObject



476
477
478
479
480
481
# File 'lib/chef/provider/package/rubygems.rb', line 476

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

#current_versionObject



433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/chef/provider/package/rubygems.rb', line 433

def current_version
  #raise 'todo'
  # 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.last
    logger.debug { "#{@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.last
    logger.debug { "#{@new_resource} newest installed version of gem #{gemspec.name} is #{gemspec.version}" }
    gemspec
  else
    logger.debug { "#{@new_resource} no installed version found for #{gem_dependency}" }
    nil
  end
end

#find_gem_by_pathObject



410
411
412
413
414
415
416
# File 'lib/chef/provider/package/rubygems.rb', line 410

def find_gem_by_path
  Chef::Log.debug("#{@new_resource} searching for 'gem' binary in path: #{ENV['PATH']}")
  separator = ::File::ALT_SEPARATOR ? ::File::ALT_SEPARATOR : ::File::SEPARATOR
  path_to_first_gem = ENV["PATH"].split(::File::PATH_SEPARATOR).find { |path| ::File.exists?(path + separator + "gem") }
  raise Chef::Exceptions::FileNotFound, "Unable to find 'gem' binary in path: #{ENV['PATH']}" if path_to_first_gem.nil?
  path_to_first_gem + separator + "gem"
end

#gem_binary_pathObject



527
528
529
# File 'lib/chef/provider/package/rubygems.rb', line 527

def gem_binary_path
  @new_resource.gem_binary || "gem"
end

#gem_dependencyObject



418
419
420
# File 'lib/chef/provider/package/rubygems.rb', line 418

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

#gem_sourcesObject



463
464
465
# File 'lib/chef/provider/package/rubygems.rb', line 463

def gem_sources
  @new_resource.source ? Array(@new_resource.source) : nil
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



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

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.kind_of?(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



531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
# File 'lib/chef/provider/package/rubygems.rb', line 531

def install_via_gem_command(name, version)
  if @new_resource.source =~ /\.gem$/i
    name = @new_resource.source
  elsif @new_resource.clear_sources
    src = " --clear-sources"
    src << (@new_resource.source && " --source=#{@new_resource.source}" || "")
  else
    src = @new_resource.source && " --source=#{@new_resource.source} --source=#{Chef::Config[:rubygems_url]}"
  end
  if !version.nil? && version.length > 0
    shell_out_with_timeout!("#{gem_binary_path} install #{name} -q --no-rdoc --no-ri -v \"#{version}\"#{src}#{opts}", :env => nil)
  else
    shell_out_with_timeout!("#{gem_binary_path} install \"#{name}\" -q --no-rdoc --no-ri #{src}#{opts}", :env => nil)
  end
end

#is_omnibus?Boolean

Returns:

  • (Boolean)


396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/chef/provider/package/rubygems.rb', line 396

def is_omnibus?
  if RbConfig::CONFIG["bindir"] =~ %r{/(opscode|chef|chefdk)/embedded/bin}
    Chef::Log.debug("#{@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]:/, "") == "/opscode/chef/embedded/bin"
    Chef::Log.debug("#{@new_resource} detected omnibus installation in #{RbConfig::CONFIG['bindir']}")
    # windows, with the drive letter removed
    true
  else
    false
  end
end

#load_current_resourceObject



467
468
469
470
471
472
473
474
# File 'lib/chef/provider/package/rubygems.rb', line 467

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

#loggerObject



352
353
354
# File 'lib/chef/provider/package/rubygems.rb', line 352

def logger
  Chef::Log.logger
end

#matching_installed_versionsObject



453
454
455
# File 'lib/chef/provider/package/rubygems.rb', line 453

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

#purge_package(name, version) ⇒ Object



573
574
575
# File 'lib/chef/provider/package/rubygems.rb', line 573

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

#remove_package(name, version) ⇒ Object



551
552
553
554
555
556
557
558
559
560
561
562
563
# File 'lib/chef/provider/package/rubygems.rb', line 551

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.kind_of?(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)


422
423
424
425
426
427
428
429
430
431
# File 'lib/chef/provider/package/rubygems.rb', line 422

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

#target_version_already_installed?(current_version, new_version) ⇒ Boolean

Returns:

  • (Boolean)


495
496
497
498
499
500
# File 'lib/chef/provider/package/rubygems.rb', line 495

def target_version_already_installed?(current_version, new_version)
  return false unless current_version
  return false if new_version.nil?

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

#uninstall_via_gem_command(name, version) ⇒ Object



565
566
567
568
569
570
571
# File 'lib/chef/provider/package/rubygems.rb', line 565

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

#upgrade_package(name, version) ⇒ Object



547
548
549
# File 'lib/chef/provider/package/rubygems.rb', line 547

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