Class: Chef::Knife::SubcommandLoader

Inherits:
Object
  • Object
show all
Defined in:
lib/chef/knife/core/subcommand_loader.rb,
lib/chef/knife/core/gem_glob_loader.rb,
lib/chef/knife/core/hashed_command_loader.rb,
lib/chef/knife/core/custom_manifest_loader.rb

Overview

Public Methods of a Subcommand Loader

load_commands - loads all available subcommands load_command(args) - loads subcommands for the given args list_commands(args) - lists all available subcommands,

optionally filtering by category

subcommand_files - returns an array of all subcommand files

that could be loaded

commnad_class_from(args) - returns the subcommand class for the

user-requested command

Defined Under Namespace

Classes: CustomManifestLoader, GemGlobLoader, HashedCommandLoader

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(chef_config_dir, env = nil) ⇒ SubcommandLoader

Returns a new instance of SubcommandLoader.



81
82
83
84
85
86
87
88
89
# File 'lib/chef/knife/core/subcommand_loader.rb', line 81

def initialize(chef_config_dir, env = nil)
  @chef_config_dir = chef_config_dir

  # Deprecated and un-used instance variable.
  @env = env
  unless env.nil?
    Chef.log_deprecation("The env argument to Chef::Knife::SubcommandLoader is deprecated. If you are using env to inject/mock HOME, consider mocking Chef::Util::PathHelper.home instead.")
  end
end

Instance Attribute Details

#chef_config_dirObject (readonly)

Returns the value of attribute chef_config_dir.



40
41
42
# File 'lib/chef/knife/core/subcommand_loader.rb', line 40

def chef_config_dir
  @chef_config_dir
end

#envObject (readonly)

Returns the value of attribute env.



41
42
43
# File 'lib/chef/knife/core/subcommand_loader.rb', line 41

def env
  @env
end

Class Method Details

.autogenerated_manifest?Boolean

Returns:

  • (Boolean)


65
66
67
# File 'lib/chef/knife/core/subcommand_loader.rb', line 65

def self.autogenerated_manifest?
  plugin_manifest? && plugin_manifest.key?(HashedCommandLoader::KEY)
end

.custom_manifest?Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/chef/knife/core/subcommand_loader.rb', line 69

def self.custom_manifest?
  plugin_manifest? && plugin_manifest.key?("plugins")
end

.for_config(chef_config_dir) ⇒ Object

A small factory method. Eventually, this is the only place where SubcommandLoader should know about its subclasses, but to maintain backwards compatibility many of the instance methods in this base class contain default implementations of the functions sub classes should otherwise provide or directly instantiate the appropriate subclass



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/chef/knife/core/subcommand_loader.rb', line 49

def self.for_config(chef_config_dir)
  if autogenerated_manifest?
    Chef::Log.debug("Using autogenerated hashed command manifest #{plugin_manifest_path}")
    Knife::SubcommandLoader::HashedCommandLoader.new(chef_config_dir, plugin_manifest)
  elsif custom_manifest?
    Chef.log_deprecation("Using custom manifest #{plugin_manifest_path} is deprecated.  Please use a `knife rehash` autogenerated manifest instead.")
    Knife::SubcommandLoader::CustomManifestLoader.new(chef_config_dir, plugin_manifest)
  else
    Knife::SubcommandLoader::GemGlobLoader.new(chef_config_dir)
  end
end

.plugin_manifestObject



73
74
75
# File 'lib/chef/knife/core/subcommand_loader.rb', line 73

def self.plugin_manifest
  Chef::JSONCompat.from_json(File.read(plugin_manifest_path))
end

.plugin_manifest?Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/chef/knife/core/subcommand_loader.rb', line 61

def self.plugin_manifest?
  plugin_manifest_path && File.exist?(plugin_manifest_path)
end

.plugin_manifest_pathObject



77
78
79
# File 'lib/chef/knife/core/subcommand_loader.rb', line 77

def self.plugin_manifest_path
  Chef::Util::PathHelper.home(".chef", "plugin_manifest.json")
end

Instance Method Details

#command_class_from(args) ⇒ Object



116
117
118
119
120
121
122
# File 'lib/chef/knife/core/subcommand_loader.rb', line 116

def command_class_from(args)
  cmd_words = positional_arguments(args)
  load_command(cmd_words)
  result = Chef::Knife.subcommands[find_longest_key(Chef::Knife.subcommands,
                                                    cmd_words, "_")]
  result || Chef::Knife.subcommands[args.first.gsub("-", "_")]
end

#find_longest_key(hash, words, sep = "_") ⇒ Object

Utility function for finding an element in a hash given an array of words and a separator. We find the the longest key in the hash composed of the given words joined by the separator.



165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/chef/knife/core/subcommand_loader.rb', line 165

def find_longest_key(hash, words, sep = "_")
  match = nil
  until match || words.empty?
    candidate = words.join(sep)
    if hash.key?(candidate)
      match = candidate
    else
      words.pop
    end
  end
  match
end

#find_subcommands_via_dirglobObject

This is shared between the custom_manifest_loader and the gem_glob_loader



134
135
136
137
138
139
140
141
142
143
# File 'lib/chef/knife/core/subcommand_loader.rb', line 134

def find_subcommands_via_dirglob
  # The "require paths" of the core knife subcommands bundled with chef
  files = Dir[File.join(Chef::Util::PathHelper.escape_glob(File.expand_path("../../../knife", __FILE__)), "*.rb")]
  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

#force_loadObject



98
99
100
101
# File 'lib/chef/knife/core/subcommand_loader.rb', line 98

def force_load
  @loaded = false
  load_commands
end

#guess_category(args) ⇒ Object



124
125
126
127
128
129
# File 'lib/chef/knife/core/subcommand_loader.rb', line 124

def guess_category(args)
  category_words = positional_arguments(args)
  category_words.map! { |w| w.split("-") }.flatten!
  find_longest_key(Chef::Knife.subcommands_by_category,
                   category_words, " ")
end

#list_commands(pref_cat = nil) ⇒ Object



107
108
109
110
111
112
113
114
# File 'lib/chef/knife/core/subcommand_loader.rb', line 107

def list_commands(pref_cat = nil)
  load_commands
  if pref_cat && Chef::Knife.subcommands_by_category.key?(pref_cat)
    { pref_cat => Chef::Knife.subcommands_by_category[pref_cat] }
  else
    Chef::Knife.subcommands_by_category
  end
end

#load_command(_command_args) ⇒ Object



103
104
105
# File 'lib/chef/knife/core/subcommand_loader.rb', line 103

def load_command(_command_args)
  load_commands
end

#load_commandsObject

Load all the sub-commands



92
93
94
95
96
# File 'lib/chef/knife/core/subcommand_loader.rb', line 92

def load_commands
  return true if @loaded
  subcommand_files.each { |subcommand| Kernel.load subcommand }
  @loaded = true
end

#positional_arguments(args) ⇒ Array<String>

The positional arguments from the argument list provided by the users. Used to search for subcommands and categories.

Returns:



184
185
186
# File 'lib/chef/knife/core/subcommand_loader.rb', line 184

def positional_arguments(args)
  args.select { |arg| arg =~ /^(([[:alnum:]])[[:alnum:]\_\-]+)$/ }
end

#site_subcommandsObject

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



190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/chef/knife/core/subcommand_loader.rb', line 190

def site_subcommands
  user_specific_files = []

  if chef_config_dir
    user_specific_files.concat Dir.glob(File.expand_path("plugins/knife/*.rb", Chef::Util::PathHelper.escape_glob(chef_config_dir)))
  end

  # finally search ~/.chef/plugins/knife/*.rb
  Chef::Util::PathHelper.home(".chef", "plugins", "knife") do |p|
    user_specific_files.concat Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(p), "*.rb"))
  end

  user_specific_files
end

#subcommand_filesObject

Subclassses should define this themselves. Eventually, this will raise a NotImplemented error, but for now, we mimic the behavior the user was likely to get in the past.



150
151
152
153
154
155
156
157
158
# File 'lib/chef/knife/core/subcommand_loader.rb', line 150

def subcommand_files
  Chef.log_deprecation "Using Chef::Knife::SubcommandLoader directly is deprecated.
Please use Chef::Knife::SubcommandLoader.for_config(chef_config_dir, env)"
  @subcommand_files ||= if Chef::Knife::SubcommandLoader.plugin_manifest?
                          Chef::Knife::SubcommandLoader::CustomManifestLoader.new(chef_config_dir, env).subcommand_files
                        else
                          Chef::Knife::SubcommandLoader::GemGlobLoader.new(chef_config_dir, env).subcommand_files
                        end
end