Class: Chef::Knife

Inherits:
Object show all
Extended by:
Mixin::ConvertToClassName
Includes:
Mixlib::CLI
Defined in:
lib/chef/knife.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/data_bag_edit.rb,
lib/chef/knife/data_bag_list.rb,
lib/chef/knife/data_bag_show.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_bulk_delete.rb,
lib/chef/knife/node_run_list_remove.rb

Defined Under Namespace

Classes: ClientBulkDelete, ClientCreate, ClientDelete, ClientEdit, ClientList, ClientReregister, ClientShow, Configure, CookbookBulkDelete, CookbookDelete, CookbookDownload, CookbookList, CookbookMetadata, CookbookShow, CookbookUpload, DataBagCreate, DataBagDelete, DataBagEdit, DataBagList, DataBagShow, Ec2InstanceData, NodeBulkDelete, NodeCreate, NodeDelete, NodeEdit, NodeFromFile, NodeList, NodeRunListAdd, NodeRunListRemove, NodeShow, RoleBulkDelete, RoleCreate, RoleDelete, RoleEdit, RoleFromFile, RoleList, RoleShow, Search

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

Instance Attribute Details

#name_argsObject

Returns the value of attribute name_args.



27
28
29
# File 'lib/chef/knife.rb', line 27

def name_args
  @name_args
end

Class Method Details

.build_sub_class(snake_case, merge_opts = nil) ⇒ Object



52
53
54
55
56
# File 'lib/chef/knife.rb', line 52

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



58
59
60
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
# File 'lib/chef/knife.rb', line 58

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 - cli_bits) || []
  klass_instance.configure_chef
  klass_instance
end

.list_commandsObject



42
43
44
45
46
47
48
49
50
# File 'lib/chef/knife.rb', line 42

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



30
31
32
33
34
35
36
37
38
39
40
# File 'lib/chef/knife.rb', line 30

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(q) ⇒ Object



98
99
100
101
102
103
# File 'lib/chef/knife.rb', line 98

def ask_question(q)
  print q 
  a = STDIN.readline
  a.chomp!
  a
end

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



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/chef/knife.rb', line 266

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

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

  json_pretty_print(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
    json_pretty_print(format_for_display(object)) if config[:print_after]
    Chef::Log.warn("Deleted #{fancy_name} #{name}")
  end
end

#configure_chefObject



105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/chef/knife.rb', line 105

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]
  Chef::Log::Formatter.show_time = false
  Chef::Log.init(Chef::Config[:log_location])
  Chef::Log.level(Chef::Config[:log_level])
end

#confirm(question) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/chef/knife.rb', line 172

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



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/chef/knife.rb', line 234

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}")
  
  json_pretty_print(output) if config[:print_after]
end

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



250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/chef/knife.rb', line 250

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

  json_pretty_print(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



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/chef/knife.rb', line 154

def edit_data(data, parse_output=true)
  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 JSON.pretty_generate(data)
  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)

  parse_output ? JSON.parse(output) : output
end

#edit_object(klass, name) ⇒ Object



222
223
224
225
226
227
228
229
230
231
232
# File 'lib/chef/knife.rb', line 222

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

  output = edit_data(object)
  
  output.save

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

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

#file_exists_and_is_readable?(file) ⇒ Boolean

Returns:

  • (Boolean)


218
219
220
# File 'lib/chef/knife.rb', line 218

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

#format_for_display(item) ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/chef/knife.rb', line 132

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



128
129
130
# File 'lib/chef/knife.rb', line 128

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

#json_pretty_print(data) ⇒ Object



124
125
126
# File 'lib/chef/knife.rb', line 124

def json_pretty_print(data)
  puts JSON.pretty_generate(data)
end

#load_from_file(klass, from_file) ⇒ Object



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/chef/knife.rb', line 191

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

#pretty_print(data) ⇒ Object



120
121
122
# File 'lib/chef/knife.rb', line 120

def pretty_print(data)
  puts data
end

#restObject



294
295
296
# File 'lib/chef/knife.rb', line 294

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