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      \#{ChefUtils::Dist::Infra::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(command = nil)\n    if command\n      explain_command(command)\n    else\n      puts help_banner\n    end\n    :help\n  end\n  alias :halp :help\n\n  desc \"prints information about \#{ChefUtils::Dist::Infra::PRODUCT}\"\n  def version\n    puts \"Welcome to the \#{ChefUtils::Dist::Infra::SHELL} \#{::Chef::VERSION}\\n\" +\n      \"For usage see https://docs.chef.io/chef_shell/\"\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 \#{ChefUtils::Dist::Infra::PRODUCT} 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 \#{ChefUtils::Dist::Infra::PRODUCT} run\"\n  subcommands resume: \"resume the \#{ChefUtils::Dist::Infra::PRODUCT} 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      \#{ChefUtils::Dist::Infra::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 \#{ChefUtils::Dist::Infra::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 = \"\#{ChefUtils::Dist::Infra::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:\n        databags(: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



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

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

.extend_context_object(obj) ⇒ Object



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

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



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

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