Class: Chef::Knife

Inherits:
Object show all
Extended by:
Mixin::ConvertToClassName
Includes:
Mixlib::CLI
Defined in:
lib/chef/knife.rb,
lib/chef/knife/ssh.rb,
lib/chef/knife/search.rb,
lib/chef/knife/configure.rb,
lib/chef/knife/node_edit.rb,
lib/chef/knife/node_list.rb,
lib/chef/knife/node_show.rb,
lib/chef/knife/role_edit.rb,
lib/chef/knife/role_list.rb,
lib/chef/knife/role_show.rb,
lib/chef/knife/client_edit.rb,
lib/chef/knife/client_list.rb,
lib/chef/knife/client_show.rb,
lib/chef/knife/node_create.rb,
lib/chef/knife/node_delete.rb,
lib/chef/knife/role_create.rb,
lib/chef/knife/role_delete.rb,
lib/chef/knife/client_create.rb,
lib/chef/knife/client_delete.rb,
lib/chef/knife/cookbook_list.rb,
lib/chef/knife/cookbook_show.rb,
lib/chef/knife/cookbook_test.rb,
lib/chef/knife/data_bag_edit.rb,
lib/chef/knife/data_bag_list.rb,
lib/chef/knife/data_bag_show.rb,
lib/chef/knife/index_rebuild.rb,
lib/chef/knife/node_from_file.rb,
lib/chef/knife/role_from_file.rb,
lib/chef/knife/cookbook_delete.rb,
lib/chef/knife/cookbook_upload.rb,
lib/chef/knife/data_bag_create.rb,
lib/chef/knife/data_bag_delete.rb,
lib/chef/knife/node_bulk_delete.rb,
lib/chef/knife/role_bulk_delete.rb,
lib/chef/knife/client_reregister.rb,
lib/chef/knife/cookbook_download.rb,
lib/chef/knife/cookbook_metadata.rb,
lib/chef/knife/ec2_instance_data.rb,
lib/chef/knife/node_run_list_add.rb,
lib/chef/knife/client_bulk_delete.rb,
lib/chef/knife/cookbook_site_list.rb,
lib/chef/knife/cookbook_site_show.rb,
lib/chef/knife/cookbook_bulk_delete.rb,
lib/chef/knife/cookbook_site_search.rb,
lib/chef/knife/cookbook_site_vendor.rb,
lib/chef/knife/node_run_list_remove.rb,
lib/chef/knife/rackspace_server_list.rb,
lib/chef/knife/terremark_server_list.rb,
lib/chef/knife/cookbook_site_download.rb,
lib/chef/knife/rackspace_server_create.rb,
lib/chef/knife/rackspace_server_delete.rb,
lib/chef/knife/terremark_server_create.rb,
lib/chef/knife/terremark_server_delete.rb,
lib/chef/knife/cookbook_metadata_from_file.rb

Defined Under Namespace

Classes: ClientBulkDelete, ClientCreate, ClientDelete, ClientEdit, ClientList, ClientReregister, ClientShow, Configure, CookbookBulkDelete, CookbookDelete, CookbookDownload, CookbookList, CookbookMetadata, CookbookMetadataFromFile, CookbookShow, CookbookSiteDownload, CookbookSiteList, CookbookSiteSearch, CookbookSiteShow, CookbookSiteVendor, CookbookTest, CookbookUpload, DataBagCreate, DataBagDelete, DataBagEdit, DataBagList, DataBagShow, Ec2InstanceData, IndexRebuild, NodeBulkDelete, NodeCreate, NodeDelete, NodeEdit, NodeFromFile, NodeList, NodeRunListAdd, NodeRunListRemove, NodeShow, RackspaceServerCreate, RackspaceServerDelete, RackspaceServerList, RoleBulkDelete, RoleCreate, RoleDelete, RoleEdit, RoleFromFile, RoleList, RoleShow, Search, Ssh, TerremarkServerCreate, TerremarkServerDelete, TerremarkServerList

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Mixin::ConvertToClassName

convert_to_class_name, convert_to_snake_case, filename_to_qualified_string, snake_case_basename

Instance Attribute Details

#name_argsObject

Returns the value of attribute name_args.



30
31
32
# File 'lib/chef/knife.rb', line 30

def name_args
  @name_args
end

Class Method Details

.build_sub_class(snake_case, merge_opts = nil) ⇒ Object



55
56
57
58
59
# File 'lib/chef/knife.rb', line 55

def self.build_sub_class(snake_case, merge_opts=nil)
  klass = Chef::Knife.const_get(@sub_classes[snake_case])
  klass.options.merge!(merge_opts) if merge_opts 
  klass.new
end

.find_command(args = ARGV, merge_opts = {}) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/chef/knife.rb', line 61

def self.find_command(args=ARGV, merge_opts={})
  load_commands

  non_dash_args = Array.new
  args.each do |arg|
    non_dash_args << arg if arg =~ /^([[:alnum:]]|_)+$/
  end

  to_try = non_dash_args.length 
  klass_instance = nil
  cli_bits = nil 

  while(to_try >= 0)
    cli_bits = non_dash_args[0..to_try]
    snake_case_class_name = cli_bits.join("_")

    if @sub_classes.has_key?(snake_case_class_name)
      klass_instance = build_sub_class(snake_case_class_name, merge_opts)
      break
    end

    to_try = to_try - 1
  end

  unless klass_instance
    Chef::Log.fatal("Cannot find sub command for: #{args.join(' ')}")
    Chef::Knife.list_commands
    exit 10
  end

  extra = klass_instance.parse_options(args)
  if klass_instance.config[:help]
    puts klass_instance.opt_parser
    exit 1
  end
  klass_instance.name_args = extra.inject([]) { |c, i| cli_bits.include?(i) ? cli_bits.delete(i) : c << i; c } 
  klass_instance.configure_chef
  klass_instance
end

.list_commandsObject



45
46
47
48
49
50
51
52
53
# File 'lib/chef/knife.rb', line 45

def self.list_commands
  load_commands
  @sub_classes.keys.sort.each do |snake_case|
    klass_instance = build_sub_class(snake_case) 
    klass_instance.parse_options
    puts klass_instance.opt_parser
    puts
  end
end

.load_commandsObject

Load all the sub-commands



33
34
35
36
37
38
39
40
41
42
43
# File 'lib/chef/knife.rb', line 33

def self.load_commands
  @sub_classes = Hash.new
  Dir[
    File.expand_path(File.join(File.dirname(__FILE__), 'knife', '*.rb'))
  ].each do |knife_file|
    require knife_file
    snake_case_file_name = File.basename(knife_file).sub(/\.rb$/, '')
    @sub_classes[snake_case_file_name] = convert_to_class_name(snake_case_file_name)
  end
  @sub_classes
end

Instance Method Details

#ask_question(question, opts = {}) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/chef/knife.rb', line 101

def ask_question(question, opts={})
  question = question + "[#{opts[:default]}] " if opts[:default]
    
  stdout.print question
  a = stdin.readline.strip

  if opts[:default]
    a.empty? ? opts[:default] : a
  else
    a
  end
end

#bulk_delete(klass, fancy_name, delete_name = nil, list = nil, regex = nil, &block) ⇒ Object



299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/chef/knife.rb', line 299

def bulk_delete(klass, fancy_name, delete_name=nil, list=nil, regex=nil, &block)
  object_list = list ? list : klass.list(true)

  if regex
    to_delete = Hash.new
    object_list.each_key do |object|
      next if regex && object !~ /#{regex}/
      to_delete[object] = object_list[object]
    end
  else
    to_delete = object_list
  end

  output(format_list_for_display(to_delete))

  confirm("Do you really want to delete the above items")

  to_delete.each do |name, object|
    if Kernel.block_given?
      block.call(name, object)
    else
      object.destroy
    end
    output(format_for_display(object)) if config[:print_after]
    Chef::Log.warn("Deleted #{fancy_name} #{name}")
  end
end

#configure_chefObject



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/chef/knife.rb', line 114

def configure_chef
  if !config[:config_file].nil? && File.exists?(config[:config_file]) && File.readable?(config[:config_file])
    Chef::Config.from_file(config[:config_file]) 
  end

  Chef::Config[:log_level] = config[:log_level] if config[:log_level]
  Chef::Config[:log_location] = config[:log_location] if config[:log_location]
  Chef::Config[:node_name] = config[:node_name] if config[:node_name]
  Chef::Config[:client_key] = config[:client_key] if config[:client_key]
  Chef::Config[:chef_server_url] = config[:chef_server_url] if config[:chef_server_url]
  Mixlib::Log::Formatter.show_time = false
  Chef::Log.init(Chef::Config[:log_location])
  Chef::Log.level(Chef::Config[:log_level])

  if Chef::Config[:node_name].nil?
    raise ArgumentError, "No user specified, pass via -u or specifiy 'node_name' in #{config[:config_file] ? config[:config_file] : "~/.chef/knife.rb"}"
  end
end

#confirm(question) ⇒ Object



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/chef/knife.rb', line 205

def confirm(question)
  return true if config[:yes]

  print "#{question}? (Y/N) "
  answer = stdin.readline
  answer.chomp!
  case answer
  when "Y", "y"
    true
  when "N", "n"
    Chef::Log.info("You said no, so I'm done here.")
    exit 3 
  else
    Chef::Log.error("I have no idea what to do with #{answer}")
    Chef::Log.error("Just say Y or N, please.")
    confirm(question)
  end
end

#create_object(object, pretty_name = nil, &block) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/chef/knife.rb', line 267

def create_object(object, pretty_name=nil, &block)
  output = edit_data(object)

  if Kernel.block_given?
    output = block.call(output)
  else
    output.save
  end

  pretty_name ||= output

  Chef::Log.info("Created (or updated) #{pretty_name}")
  
  output(output) if config[:print_after]
end

#delete_object(klass, name, delete_name = nil, &block) ⇒ Object



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/chef/knife.rb', line 283

def delete_object(klass, name, delete_name=nil, &block)
  confirm("Do you really want to delete #{name}")

  if Kernel.block_given?
    object = block.call
  else
    object = klass.load(name)
    object.destroy
  end

  output(format_for_display(object)) if config[:print_after]

  obj_name = delete_name ? "#{delete_name}[#{name}]" : object
  Chef::Log.warn("Deleted #{obj_name}!")
end

#edit_data(data, parse_output = true) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/chef/knife.rb', line 183

def edit_data(data, parse_output=true)
  output = JSON.pretty_generate(data)
  
  if (!config[:no_editor])
    filename = "knife-edit-"
    0.upto(20) { filename += rand(9).to_s }
    filename << ".js"
    filename = File.join(Dir.tmpdir, filename)
    tf = File.open(filename, "w")
    tf.sync = true
    tf.puts output
    tf.close
    raise "Please set EDITOR environment variable" unless system("#{config[:editor]} #{tf.path}") 
    tf = File.open(filename, "r")
    output = tf.gets(nil)
    tf.close
    File.unlink(filename)
  end

  parse_output ? JSON.parse(output) : output
end

#edit_object(klass, name) ⇒ Object



255
256
257
258
259
260
261
262
263
264
265
# File 'lib/chef/knife.rb', line 255

def edit_object(klass, name)
  object = klass.load(name)

  output = edit_data(object)
  
  output.save

  Chef::Log.info("Saved #{output}")

  output(format_for_display(object)) if config[:print_after]
end

#file_exists_and_is_readable?(file) ⇒ Boolean

Returns:

  • (Boolean)


251
252
253
# File 'lib/chef/knife.rb', line 251

def file_exists_and_is_readable?(file)
  File.exists?(file) && File.readable?(file)
end

#format_for_display(item) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/chef/knife.rb', line 161

def format_for_display(item)
  data = item.kind_of?(Chef::DataBagItem) ? item.raw_data : item

  if config[:attribute]
    config[:attribute].split(".").each do |attr|
      if data.respond_to?(:[])
        data = data[attr]
      else
        data = data.send(attr.to_sym)
      end
    end
    { config[:attribute] => data.kind_of?(Chef::Node::Attribute) ? data.to_hash: data }
  elsif config[:run_list]
    data = data.run_list.run_list
    { "run_list" => data }
  elsif config[:id_only]
    data.respond_to?(:name) ? data.name : data["id"]
  else
    data
  end
end

#format_list_for_display(list) ⇒ Object



157
158
159
# File 'lib/chef/knife.rb', line 157

def format_list_for_display(list)
  config[:with_uri] ? list : list.keys.sort { |a,b| a <=> b } 
end

#load_from_file(klass, from_file) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/chef/knife.rb', line 224

def load_from_file(klass, from_file) 
  from_file = @name_args[0]
  relative_file = File.expand_path(File.join(Dir.pwd, 'roles', from_file))
  filename = nil

  if file_exists_and_is_readable?(from_file)
    filename = from_file
  elsif file_exists_and_is_readable?(relative_file) 
    filename = relative_file 
  else
    Chef::Log.fatal("Cannot find file #{from_file}")
    exit 30
  end

  case from_file
  when /\.(js|json)$/
    JSON.parse(IO.read(filename))
  when /\.rb$/
    r = klass.new
    r.from_file(filename)
    r
  else
    Chef::Log.fatal("File must end in .js, .json, or .rb")
    exit 30
  end
end

#output(data) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/chef/knife.rb', line 137

def output(data)
  case config[:format]
  when "json", nil
    puts JSON.pretty_generate(data)
  when "yaml"
    require 'yaml'
    puts YAML::dump(data)
  when "text"
    # If you were looking for some attribute and there is only one match
    # just dump the attribute value
    if data.length == 1 and config[:attribute]
      puts data.values[0]
    else
      pp data
    end
  else
    raise ArgumentError, "Unknown output format #{config[:format]}"
  end
end

#pretty_print(data) ⇒ Object



133
134
135
# File 'lib/chef/knife.rb', line 133

def pretty_print(data)
  puts data
end

#restObject



335
336
337
# File 'lib/chef/knife.rb', line 335

def rest
  @rest ||= Chef::REST.new(Chef::Config[:chef_server_url])
end

#stdinObject



331
332
333
# File 'lib/chef/knife.rb', line 331

def stdin
  STDIN
end

#stdoutObject



327
328
329
# File 'lib/chef/knife.rb', line 327

def stdout
  STDOUT
end