class Chef::Knife::SubcommandLoader

Attributes

chef_config_dir[R]
env[R]

Public Class Methods

new(chef_config_dir, env=ENV) click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 27
def initialize(chef_config_dir, env=ENV)
  @chef_config_dir, @env = chef_config_dir, env
  @forced_activate = {}
end

Public Instance Methods

find_subcommands_via_dirglob() click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 108
def find_subcommands_via_dirglob
  # The "require paths" of the core knife subcommands bundled with chef
  files = Dir[File.expand_path('../../../knife/*.rb', __FILE__)]
  subcommand_files = {}
  files.each do |knife_file|
    rel_path = knife_file[/#{CHEF_ROOT}#{Regexp.escape(File::SEPARATOR)}(.*)\.rb/,1]
    subcommand_files[rel_path] = knife_file
  end
  subcommand_files
end
find_subcommands_via_manifest() click to toggle source

If the user has created a ~/.chef/plugin_manifest.json file, we'll use that instead of inspecting the on-system gems to find the plugins. The file format is expected to look like:

{ "plugins": {
    "knife-ec2": {
      "paths": [
        "/home/alice/.rubymanagerthing/gems/knife-ec2-x.y.z/lib/chef/knife/ec2_server_create.rb",
        "/home/alice/.rubymanagerthing/gems/knife-ec2-x.y.z/lib/chef/knife/ec2_server_delete.rb"
      ]
    }
  }
}

Extraneous content in this file is ignored. This intentional so that we can adapt the file format for potential behavior changes to knife in the future.

# File lib/chef/knife/core/subcommand_loader.rb, line 95
def find_subcommands_via_manifest
  # Format of subcommand_files is "relative_path" (something you can
  # Kernel.require()) => full_path. The relative path isn't used
  # currently, so we just map full_path => full_path.
  subcommand_files = {}
  plugin_manifest["plugins"].each do |plugin_name, plugin_manifest|
    plugin_manifest["paths"].each do |cmd_path|
      subcommand_files[cmd_path] = cmd_path
    end
  end
  subcommand_files.merge(find_subcommands_via_dirglob)
end
find_subcommands_via_rubygems() click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 119
def find_subcommands_via_rubygems
  files = find_files_latest_gems 'chef/knife/*.rb'
  subcommand_files = {}
  files.each do |file|
    rel_path = file[/(#{Regexp.escape File.join('chef', 'knife', '')}.*)\.rb/, 1]
    subcommand_files[rel_path] = file
  end

  subcommand_files.merge(find_subcommands_via_dirglob)
end
gem_and_builtin_subcommands() click to toggle source

Returns a Hash of paths to knife commands built-in to chef, or installed via gem. If rubygems is not installed, falls back to globbing the knife directory. The Hash is of the form {“relative/path” => “/absolute/path”}

# File lib/chef/knife/core/subcommand_loader.rb, line 62
def gem_and_builtin_subcommands
  if have_plugin_manifest?
    find_subcommands_via_manifest
  else
    # search all gems for chef/knife/*.rb
    require 'rubygems'
    find_subcommands_via_rubygems
  end
rescue LoadError
  find_subcommands_via_dirglob
end
have_plugin_manifest?() click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 130
def have_plugin_manifest?
  ENV["HOME"] && File.exist?(plugin_manifest_path)
end
load_commands() click to toggle source

Load all the sub-commands

# File lib/chef/knife/core/subcommand_loader.rb, line 33
def load_commands
  subcommand_files.each { |subcommand| Kernel.load subcommand }
  true
end
plugin_manifest() click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 134
def plugin_manifest
  Chef::JSONCompat.from_json(File.read(plugin_manifest_path))
end
plugin_manifest_path() click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 138
def plugin_manifest_path
  File.join(ENV['HOME'], '.chef', 'plugin_manifest.json')
end
site_subcommands() click to toggle source

Returns an Array of paths to knife commands located in chef_config_dir/plugins/knife/ and ~/.chef/plugins/knife/

# File lib/chef/knife/core/subcommand_loader.rb, line 40
def site_subcommands
  user_specific_files = []

  if chef_config_dir
    user_specific_files.concat Dir.glob(File.expand_path("plugins/knife/*.rb", chef_config_dir))
  end

  # finally search ~/.chef/plugins/knife/*.rb
  user_specific_files.concat Dir.glob(File.join(env['HOME'], '.chef', 'plugins', 'knife', '*.rb')) if env['HOME']

  user_specific_files
end
subcommand_files() click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 74
def subcommand_files
  @subcommand_files ||= (gem_and_builtin_subcommands.values + site_subcommands).flatten.uniq
end

Private Instance Methods

check_spec_for_glob(spec, glob) click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 176
def check_spec_for_glob(spec, glob)
  dirs = if spec.require_paths.size > 1 then
    "{#{spec.require_paths.join(',')}}"
  else
    spec.require_paths.first
  end

  glob = File.join("#{spec.full_gem_path}/#{dirs}", glob)

  Dir[glob].map { |f| f.untaint }
end
find_files_latest_gems(glob, check_load_path=true) click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 144
def find_files_latest_gems(glob, check_load_path=true)
  files = []

  if check_load_path
    files = $LOAD_PATH.map { |load_path|
      Dir["#{File.expand_path glob, load_path}#{Gem.suffix_pattern}"]
    }.flatten.select { |file| File.file? file.untaint }
  end

  gem_files = latest_gem_specs.map do |spec|
    # Gem::Specification#matches_for_glob wasn't added until RubyGems 1.8
    if spec.respond_to? :matches_for_glob
      spec.matches_for_glob("#{glob}#{Gem.suffix_pattern}")
    else
      check_spec_for_glob(spec, glob)
    end
  end.flatten

  files.concat gem_files
  files.uniq! if check_load_path

  return files
end
latest_gem_specs() click to toggle source
# File lib/chef/knife/core/subcommand_loader.rb, line 168
def latest_gem_specs
  @latest_gem_specs ||= if Gem::Specification.respond_to? :latest_specs
    Gem::Specification.latest_specs(true)  # find prerelease gems
  else
    Gem.source_index.latest_specs(true)
  end
end