Module: Shell::Extensions

Defined in:
lib/chef/shell/ext.rb

Defined Under Namespace

Modules: FalseClass, ObjectCoreExtensions, String, Symbol, TrueClass Classes: Help

Constant Summary collapse

ObjectUIExtensions =

Methods that have associated help text need to be dynamically added to the main irb objects, so we define them in a proc and later instance_eval the proc in the object.

Proc.new do
  extend Shell::Extensions::ObjectCoreExtensions

  desc "prints this help message"
  explain("## SUMMARY ##\n  When called with no argument, +help+ prints a table of all\n  chef-shell commands. When called with an argument COMMAND, +help+\n  prints a detailed explanation of the command if available, or the\n  description if no explanation is available.\n  E\n  def help(commmand = nil)\n    if commmand\n      explain_command(commmand)\n    else\n      puts help_banner\n    end\n    :ucanhaz_halp\n  end\n  alias :halp :help\n\n  desc \"prints information about chef\"\n  def version\n    puts \"This is the chef-shell.\\n\" +\n      \" Chef Version: \#{::Chef::VERSION}\\n\" +\n      \" http://www.chef.io/\\n\" +\n      \" http://docs.chef.io/\"\n    :ucanhaz_automation\n  end\n  alias :shell :version\n\n  desc \"switch to recipe mode\"\n  def recipe_mode\n    find_or_create_session_for Shell.session.recipe\n    :recipe\n  end\n\n  desc \"switch to attributes mode\"\n  def attributes_mode\n    find_or_create_session_for Shell.session.node\n    :attributes\n  end\n\n  desc \"run chef using the current recipe\"\n  def run_chef\n    Chef::Log.level = :debug\n    session = Shell.session\n    runrun = Chef::Runner.new(session.run_context).converge\n    Chef::Log.level = :info\n    runrun\n  end\n\n  desc \"returns an object to control a paused chef run\"\n  subcommands :resume       => \"resume the chef run\",\n              :step         => \"run only the next resource\",\n              :skip_back    => \"move back in the run list\",\n              :skip_forward => \"move forward in the run list\"\n  def chef_run\n    Shell.session.resource_collection.iterator\n  end\n\n  desc \"resets the current recipe\"\n  def reset\n    Shell.session.reset!\n  end\n\n  desc \"assume the identity of another node.\"\n  def become_node(node_name)\n    Shell::DoppelGangerSession.instance.assume_identity(node_name)\n    :doppelganger\n  end\n  alias :doppelganger :become_node\n\n  desc \"turns printout of return values on or off\"\n  def echo(on_or_off)\n    conf.echo = on_or_off.on_off_to_bool\n  end\n\n  desc \"says if echo is on or off\"\n  def echo?\n    puts \"echo is \#{conf.echo.to_on_off_str}\"\n  end\n\n  desc \"turns on or off tracing of execution. *verbose*\"\n  def tracing(on_or_off)\n    conf.use_tracer = on_or_off.on_off_to_bool\n    tracing?\n  end\n  alias :trace :tracing\n\n  desc \"says if tracing is on or off\"\n  def tracing?\n    puts \"tracing is \#{conf.use_tracer.to_on_off_str}\"\n  end\n  alias :trace? :tracing?\n\n  desc \"simple ls style command\"\n  def ls(directory)\n    Dir.entries(directory)\n  end\nend\n")
MainContextExtensions =
Proc.new do
  desc "returns the current node (i.e., this host)"
  def node
    Shell.session.node
  end

  desc "pretty print the node's attributes"
  def ohai(key = nil)
    pp(key ? node.attribute[key] : node.attribute)
  end
end
RESTApiExtensions =
Proc.new do
  desc "edit an object in your EDITOR"
  explain("## SUMMARY ##\n  +edit(object)+ allows you to edit any object that can be converted to JSON.\n  When finished editing, this method will return the edited object:\n\n  new_node = edit(existing_node)\n\n## EDITOR SELECTION ##\n  chef-shell looks for an editor using the following logic\n  1. Looks for an EDITOR set by Shell.editor = \"EDITOR\"\n  2. Looks for an EDITOR configured in your chef-shell config file\n  3. Uses the value of the EDITOR environment variable\n  E\n  def edit(object)\n    unless Shell.editor\n      puts \"Please set your editor with Shell.editor = \\\"vim|emacs|mate|ed\\\"\"\n      return :failburger\n    end\n\n    filename = \"chef-shell-edit-\#{object.class.name}-\"\n    if object.respond_to?(:name)\n      filename += object.name\n    elsif object.respond_to?(:id)\n      filename += object.id\n    end\n\n    edited_data = Tempfile.open([filename, \".js\"]) do |tempfile|\n      tempfile.sync = true\n      tempfile.puts Chef::JSONCompat.to_json(object)\n      system(\"\#{Shell.editor} \#{tempfile.path}\")\n      tempfile.rewind\n      tempfile.read\n    end\n\n    Chef::JSONCompat.from_json(edited_data)\n  end\n\n  desc \"Find and edit API clients\"\n  explain(<<-E)\n## SUMMARY ##\n  +clients+ allows you to query you chef server for information about your api\n  clients.\n\n## LIST ALL CLIENTS ##\n  To see all clients on the system, use\n\n  clients.all #=> [<Chef::ApiClient...>, ...]\n\n  If the output from all is too verbose, or you're only interested in a specific\n  value from each of the objects, you can give a code block to +all+:\n\n  clients.all { |client| client.name } #=> [CLIENT1_NAME, CLIENT2_NAME, ...]\n\n## SHOW ONE CLIENT ##\n  To see a specific client, use\n\n  clients.show(CLIENT_NAME)\n\n## SEARCH FOR CLIENTS ##\n  You can also search for clients using +find+ or +search+. You can use the\n  familiar string search syntax:\n\n  clients.search(\"KEY:VALUE\")\n\n  Just as the +all+ subcommand, the +search+ subcommand can use a code block to\n  filter or transform the information returned from the search:\n\n  clients.search(\"KEY:VALUE\") { |c| c.name }\n\n  You can also use a Hash based syntax, multiple search conditions will be\n  joined with AND.\n\n  clients.find :KEY => :VALUE, :KEY2 => :VALUE2, ...\n\n## BULK-EDIT CLIENTS ##\n                **BE CAREFUL, THIS IS DESTRUCTIVE**\n  You can bulk edit API Clients using the +transform+ subcommand, which requires\n  a code block. Each client will be saved after the code block is run. If the\n  code block returns +nil+ or +false+, that client will be skipped:\n\n  clients.transform(\"*:*\") do |client|\n    if client.name =~ /borat/i\n      client.admin(false)\n      true\n    else\n      nil\n    end\n  end\n\n  This will strip the admin privileges from any client named after borat.\n  E\n  subcommands :all        => \"list all api clients\",\n              :show       => \"load an api client by name\",\n              :search     => \"search for API clients\",\n              :transform  => \"edit all api clients via a code block and save them\"\n  def clients\n    @clients ||= Shell::ModelWrapper.new(Chef::ApiClient, :client)\n  end\n\n  desc \"Find and edit cookbooks\"\n  subcommands :all        => \"list all cookbooks\",\n              :show       => \"load a cookbook by name\",\n              :transform  => \"edit all cookbooks via a code block and save them\"\n  def cookbooks\n    @cookbooks ||= Shell::ModelWrapper.new(Chef::CookbookVersion)\n  end\n\n  desc \"Find and edit nodes via the API\"\n  explain(<<-E)\n## SUMMARY ##\n  +nodes+ Allows you to query your chef server for information about your nodes.\n\n## LIST ALL NODES ##\n  You can list all nodes using +all+ or +list+\n\n  nodes.all #=> [<Chef::Node...>, <Chef::Node...>, ...]\n\n  To limit the information returned for each node, pass a code block to the +all+\n  subcommand:\n\n  nodes.all { |node| node.name } #=> [NODE1_NAME, NODE2_NAME, ...]\n\n## SHOW ONE NODE ##\n  You can show the data for a single node using the +show+ subcommand:\n\n  nodes.show(\"NODE_NAME\") => <Chef::Node @name=\"NODE_NAME\" ...>\n\n## SEARCH FOR NODES ##\n  You can search for nodes using the +search+ or +find+ subcommands:\n\n  nodes.find(:name => \"app*\") #=> [<Chef::Node @name=\"app1.example.com\" ...>, ...]\n\n  Similarly to +all+, you can pass a code block to limit or transform the\n  information returned:\n\n  nodes.find(:name => \"app#\") { |node| node.ec2 }\n\n## BULK EDIT NODES ##\n          **BE CAREFUL, THIS OPERATION IS DESTRUCTIVE**\n\n  Bulk edit nodes by passing a code block to the +transform+ or +bulk_edit+\n  subcommand. The block will be applied to each matching node, and then the node\n  will be saved. If the block returns +nil+ or +false+, that node will be\n  skipped.\n\n  nodes.transform do |node|\n    if node.fqdn =~ /.*\\\\.preprod\\\\.example\\\\.com/\n      node.set[:environment] = \"preprod\"\n    end\n  end\n\n  This will assign the attribute to every node with a FQDN matching the regex.\n  E\n  subcommands :all        => \"list all nodes\",\n              :show       => \"load a node by name\",\n              :search     => \"search for nodes\",\n              :transform  => \"edit all nodes via a code block and save them\"\n  def nodes\n    @nodes ||= Shell::ModelWrapper.new(Chef::Node)\n  end\n\n  desc \"Find and edit roles via the API\"\n  explain(<<-E)\n## SUMMARY ##\n  +roles+ allows you to query and edit roles on your Chef server.\n\n## SUBCOMMANDS ##\n  * all       (list)\n  * show      (load)\n  * search    (find)\n  * transform (bulk_edit)\n\n## SEE ALSO ##\n  See the help for +nodes+ for more information about the subcommands.\n  E\n  subcommands :all        => \"list all roles\",\n              :show       => \"load a role by name\",\n              :search     => \"search for roles\",\n              :transform  => \"edit all roles via a code block and save them\"\n  def roles\n    @roles ||= Shell::ModelWrapper.new(Chef::Role)\n  end\n\n  desc \"Find and edit +databag_name+ via the api\"\n  explain(<<-E)\n## SUMMARY ##\n  +databags(DATABAG_NAME)+ allows you to query and edit data bag items on your\n  Chef server. Unlike other commands for working with data on the server,\n  +databags+ requires the databag name as an argument, for example:\ndatabags(:users).all\n\n## SUBCOMMANDS ##\n  * all       (list)\n  * show      (load)\n  * search    (find)\n  * transform (bulk_edit)\n\n## SEE ALSO ##\n  See the help for +nodes+ for more information about the subcommands.\n\n  E\n  subcommands :all        => \"list all items in the data bag\",\n              :show       => \"load a data bag item by id\",\n              :search     => \"search for items in the data bag\",\n              :transform  => \"edit all items via a code block and save them\"\n  def databags(databag_name)\n    @named_databags_wrappers ||= {}\n    @named_databags_wrappers[databag_name] ||= Shell::NamedDataBagWrapper.new(databag_name)\n  end\n\n  desc \"Find and edit environments via the API\"\n  explain(<<-E)\n## SUMMARY ##\n  +environments+ allows you to query and edit environments on your Chef server.\n\n## SUBCOMMANDS ##\n  * all       (list)\n  * show      (load)\n  * search    (find)\n  * transform (bulk_edit)\n\n## SEE ALSO ##\n  See the help for +nodes+ for more information about the subcommands.\n  E\n  subcommands :all        => \"list all environments\",\n              :show       => \"load an environment by name\",\n              :search     => \"search for environments\",\n              :transform  => \"edit all environments via a code block and save them\"\n  def environments\n    @environments ||= Shell::ModelWrapper.new(Chef::Environment)\n  end\n\n  desc \"A REST Client configured to authenticate with the API\"\n  def api\n    @rest = Chef::ServerAPI.new(Chef::Config[:chef_server_url])\n  end\n\nend\n")
RecipeUIExtensions =
Proc.new do
  alias :original_resources :resources

  desc "list all the resources on the current recipe"
  def resources(*args)
    if args.empty?
      pp run_context.resource_collection.keys
    else
      pp resources = original_resources(*args)
      resources
    end
  end
end

Class Method Summary collapse

Class Method Details

.extend_context_node(node_obj) ⇒ Object



567
568
569
# File 'lib/chef/shell/ext.rb', line 567

def self.extend_context_node(node_obj)
  node_obj.instance_eval(&ObjectUIExtensions)
end

.extend_context_object(obj) ⇒ Object



558
559
560
561
562
563
564
565
# File 'lib/chef/shell/ext.rb', line 558

def self.extend_context_object(obj)
  obj.instance_eval(&ObjectUIExtensions)
  obj.instance_eval(&MainContextExtensions)
  obj.instance_eval(&RESTApiExtensions)
  obj.extend(FileUtils)
  obj.extend(Chef::DSL::PlatformIntrospection)
  obj.extend(Chef::DSL::DataQuery)
end

.extend_context_recipe(recipe_obj) ⇒ Object



571
572
573
574
# File 'lib/chef/shell/ext.rb', line 571

def self.extend_context_recipe(recipe_obj)
  recipe_obj.instance_eval(&ObjectUIExtensions)
  recipe_obj.instance_eval(&RecipeUIExtensions)
end